找回密码
 加入计匠网
搜索
热搜: BIOS ACPI CPU Windows
查看: 53222|回复: 23

UEFI 正常启动过程--与EDK为例,大家一起讨论吧!

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。
7 \% p6 k+ E( P" S% X6 b6 O  `( h, V  }7 r6 p/ Y5 O+ O- v
SEC/CEI:+ ]+ O8 P9 \9 w. x) j
  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。5 N( H; a- ~. T2 A- \
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).
9 V" f6 l2 D8 `% S  ?  [8 I; T" }7 b
PEI:
! G7 L( O8 M) n8 g# s- b* _   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行1 k, x, x- b8 q" v
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。: _; C; ?& y: ^1 L7 w1 o9 L
      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.
4 ^( E( ?% X. R          InitializeSercurityService函数,将Notify队列清空。
5 Y3 j) M' w5 U          InitializeDispatcherData函数,将Dispatcher队列清空。. R! p1 W- [) i/ E0 M& z: J
  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。& K  k6 a% z( D! j% }& ]8 ~' B. s
    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:
0 ]' s9 w9 W1 L      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {2 S5 \. ?: |% F
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},
7 [2 K. P$ D  X% x/ w  Q7 k, ?       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},1 ]4 o* b/ `9 a9 Z
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},
0 S# t8 m- ?/ @( w! M/ k* Y# z       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},  n8 f: l- x* W# L. I
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},5 Q) v; j' y% M" [$ V9 F
       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}
) M4 M& n* [& c% L6 T" H     };
; N' S, [7 D0 L; |    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。3 n6 R# W* v. M1 p
   这些PPI会在PEIDispatcher中用到。
# B  F( X! M! Q" r) g, g   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。  F, K7 \( a  Y/ W7 v
   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
* q% i9 Q3 j) m% O0 q5 ~8 ^   SwitchStacks (7 Y* U* k* r/ d, L
       (VOID *) (UINTN) DxeCoreEntryPoint,
9 b3 S) r( }" Q2 a       (UINTN) (HobList.Raw),1 ^2 V1 O" p. ]6 s6 ~
       (VOID *) (UINTN) TopOfStack,
6 d  J! @+ u1 `& h       (VOID *) (UINTN) BspStore" W; E3 C7 P0 K, r0 q7 {% p
    );' e3 ]) D& s4 A; y8 d5 r" j
  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)+ F6 g- _8 a" f, ~, Y8 W4 s
/ q6 q  Y% n; [  W# k8 K" ?
DXE:
% k) Z' V# z( u- Z2 N0 q     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。6 ^1 T& L2 k7 u1 Y$ b0 \& F
接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。
2 V; y& B: P" ~   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
( R2 g7 f& y9 l! X6 h) F; U中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。: c0 W3 ^: m% p" s' D
   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。9 w0 R, s) l8 k
   7 L  f% L. Q+ U; i
Driver:
, A5 o7 e& ~9 Z/ G7 ?9 ?0 v    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
! @: u/ [5 z# c; C  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
' X% R- C& C5 N3 W) B  [defines]  O. I7 `, I+ P: M; C
  BASE_NAME            = OWEN
/ g) L, D8 p$ \* ]2 G  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110
3 C8 Z7 Q& {+ U; }$ b+ A) t6 ~  COMPONENT_TYPE       = BS_DRIVER% h/ z! X6 H5 p) Z
+ M& u- Q$ ?4 g8 x4 `
  BASE_NAME告诉编译器最终生成的驱动的名字。( R$ _# c8 D6 C% Z! L
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。1 }7 t4 M, ^- |: v! R7 J# x
  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。/ f  X" V  n* w% u  U! ]) q* O6 Z- A
  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名( A7 B: {9 e5 p. _, t) [
  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {6 v# ?( q/ K& |4 v
  {"bs_driver",  ".dxe" },7 h9 }2 J! g% i3 J! t
  {"rt_driver",  ".dxe" },
! {6 o' j- d, \  `4 H, \  {"sal_rt_driver", ".dxe"},
3 ?1 ]7 g, W, p+ s+ C' Y; E9 Q  {"security_core", ".sec"},
. d6 ~1 c; Y4 V+ T' ~3 h. n  {"pei_core", ".pei"},, ]& o5 ?( y& `5 ]- ^
  {"pic_peim", ".pei"},
' P& n% ]( J; z  {"pe32_peim", ".pei"},
! m- G4 L7 d0 v; |0 ?& w8 Q5 V  {"relocatable_peim", ".pei"},
$ s5 b7 @* F5 `5 O- L3 D  {"binary", ".ffs"},
$ P% C: ?' r+ }; @; d5 n  {"application", ".app"},
" k$ A! x. v$ ~4 i  L# `  {"file", ".ffs"},
3 ]/ i' ~, t6 K% s" G8 {: h4 W  {"fvimagefile", ".fvi"},
' o: v$ I# x, ]* x* E  {"rawfile", ".raw"},
5 a0 V6 V4 e, T* w% q) \9 z- M  {"apriori", ".ffs"},
1 L7 m% l  ^. S  {"combined_peim_driver", ".pei"},6 s" u! i; l, j# f4 K& h: y# r
  { NULL,  NULL }$ |- }, s. x5 o4 E
};9 j. C* f" u# d9 e2 i9 {: N8 G
* X5 {6 W: M, v+ p
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)
+ E! W1 X0 Q1 d7 g: `   
6 W+ z6 r2 p+ e, R5 s9 x         ( y0 y% X. }: [+ {3 u
  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!* X: b3 C9 w, g9 x
支持
' e, k' n# K% ]; o; s0 `# H/ ?6 L继续
. Z& d+ p0 P- [- @2 k+ u& v, v: ^加油- J9 c  c* F2 S9 j1 \% v# U8 ]
回复

使用道具 举报

发表于 2008-7-27 18:54:53 | 显示全部楼层

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?) p1 S' I, n! O& ^
有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。8 A: {$ V2 f8 \
还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。+ i, r2 O5 Q/ c* d! @& [
# @  l7 _. E3 T& i
支持!
回复

使用道具 举报

 楼主| 发表于 2008-7-28 18:52:31 | 显示全部楼层
- ~" s- G' ?- z% I, I
小弟还在学习阶段,目前的目标是知道执行的流程。4 R0 d! r- B8 \. z6 h9 \: y
这里面有很多细节没有写,能力有限,只能自己知道,不能表达。$ G) E/ L% D) d! ^! v% j  F0 x% J5 `
嘿。。。。
4 G) ~% e4 n; O7 j( Q所有大侠们如果有好东西能给小弟共享一份。
4 W0 e  L0 t: Y7 R8 a9 k$ X" o 1 n: E6 D( O4 N2 |( P2 d8 u& J
谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
# c2 U3 o' Q; F5 B  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver/ p0 S% p- W# K0 v6 f2 k/ ?  f6 J
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...
8 h+ c: u, {" p% J5 a( ~: s; m
/ G6 _8 n* ^* D4 ]* s; \8 Z9 y
PPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol,
  c3 x5 r4 g: w+ \% K+ x% W* w( eFor more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI"./ u  @  V. Y& `0 V

! ^# D9 {( M3 E4 @[ 本帖最后由 ichirohiro 于 2008-8-13 13:32 编辑 ]
回复

使用道具 举报

发表于 2008-8-16 11:08:26 | 显示全部楼层
学习心得写了这么多 支持一下~!
回复

使用道具 举报

发表于 2008-8-17 09:48:13 | 显示全部楼层

回复 3# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION是EDK专有
回复

使用道具 举报

 楼主| 发表于 2008-8-17 09:53:39 | 显示全部楼层
To ichirohiro:0 |9 p1 K0 P- V
   Yes. I make a mistake.7 s8 c, b9 _0 o9 t# C0 M
      PPI:     A PEIM to PEIM interface.
: U& P) Z& J1 O0 h( j2 `      PROTOCL: A Interface between Hardware(or firmware) and software.! S+ m1 W3 L) J  J5 f
      reference[http://www.biosren.com/viewthread.php?tid=207]
& Z* z2 C2 T& {: ?: z   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.6 a* s# l& U+ G7 S
   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:2 M% _" w+ k: M  a# y( _- }
     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。
/ F# B- b( P4 Y; x/ B- ~( k$ I接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。9 k: a+ z4 Q5 z: \
   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver$ w3 q2 c& W6 |; F& c+ y+ u( o
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。
% ]. W, B  b! ]# S. A; [# x   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.
, M. n; B: \: J9 ]' f) G; O
/ b* R# O$ h/ P
There are mistakes,9 `) e; Z; e* J" s& i" r
1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation./ f  M/ W0 T1 y

0 b3 }- e7 u9 a- O$ m# e4 |! ^3 A4 i2.The Dxe drivers have two subclasses : Dxe driver that execute very early in the Dxe phase and Dxe drivers that comply with the EFI1.1 driver model.
" j3 B2 h& \6 pThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
  `; B2 o% U$ X  s# E9 [) y; AFurthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
8 p. t3 O2 ~' b/ O* [; E8 c( P6 Q* g' h, S
BTW, please excuse my English, since I come from Taiwan and without Simplified Chinese typing interface.
回复

使用道具 举报

发表于 2008-8-22 14:25:59 | 显示全部楼层
请问各位大虾,EDK从哪下阿?
回复

使用道具 举报

发表于 2008-8-23 09:00:12 | 显示全部楼层
回复

使用道具 举报

 楼主| 发表于 2008-8-25 23:02:01 | 显示全部楼层
感谢 ichirohiro 大侠的指点。
) v8 f2 t8 i3 Q% r' \
回复

使用道具 举报

发表于 2008-9-18 15:22:34 | 显示全部楼层

CoreDispatcher()时,真的不会执行"Support()/Start()"吗?

原帖由 ichirohiro 于 2008-8-20 17:47 发表 + S4 b& D0 J6 b, I3 d4 d/ H
...
2 M: Z: A6 y; q9 e( B) M2.The Dxe drivers have two subclasses : Dxe driver that execute very early in the Dxe phase and Dxe drivers that comply with the EFI1.1 driver model.
, Y4 W; }5 N8 |' uThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.3 a2 X5 C% ?  q: e6 N
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().

  x& v8 `; ]" c) K( d- T    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().
/ \$ b" L) p2 ^) D4 R. d" q& \    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)+ h2 L+ Q. Q, {( m4 \. o& p
  //. W4 G1 y8 i* x. ~
  // Go connect any handles that were created or modified while the image executed.9 x/ m% A2 @1 l8 F7 J5 X
  //
3 F% Y, K' [8 y- M" L' k9 e9 @9 E  CoreConnectHandlesByKey (HandleDatabaseKey);
9 ~8 @. q8 p' z  r, r3 h
这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey
* Y$ k' u4 h) j3 a# ?而CoreConnectHandlesByKey会调用到:, \7 B: U% E) m+ C* b
  //
) F" P/ L  f/ q4 c/ x7 M+ m  // Connect all handles whose Key value is greater than Key
& B% v: e& q2 d( O4 ~" L  //+ @8 p1 T" ]1 F! M7 L" }' |
  for (Index = 0; Index < Count; Index++) {
" G& P5 U7 R& _    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
" A8 T* p' m: t9 W  }

( w4 R8 U; y0 R5 `& U所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)
# c$ J& d1 q9 J9 v/ m( l0 ~是会去ConnectController的,也会执行对应的Support()和Start()才对!!
* f8 v  N/ }" o2 |2 L( K8 M( D: I
. R* C% ~$ ]% i4 R不知道我想的哪里有问题???欢迎大家指正.
% @; Z; y( O. V1 v% o
, m! E( w: j1 L9 k[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。" {( r& A* M5 g" a$ A. K
一个UEFI driver model driver一般只会在ImageHandle上装EFI Driver Binding Protocol,而ImageHandle在CoreStartImage()之前已经存在,所以不会导致Handle database key增加,所以不会触发ConnectController()。当然,如果一个UEFI driver model driver在入口函数中创建了新的handle,是会触发CoreConnectController()的。
回复

使用道具 举报

发表于 2008-9-19 18:10:21 | 显示全部楼层
了解了.1 v( N" C& N; B% H% N
非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,
7 p( ?+ `2 N% M8 [, RStatus = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,3 A$ K% x1 N* |6 E6 P4 {6 S$ f
//
, ]% j/ ?# N+ u) u// Notify the notification list for this protocol.1 m0 S1 x% U/ A: {
//) K% T/ b1 [, }1 R$ Y" R
if (Notify) {
' y5 H1 t! Y- Z: _  CoreNotifyProtocolEntry(ProtEntry);
* U& H! p+ B* ~- Z# Z+ `}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.
$ i, ?, g0 U1 V% g5 r有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表
6 m5 B0 u  A5 Z3 F  m: k请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?3 ?7 {- I; S! F" W( j6 l/ _
谢谢!
& ^5 n. F: s1 e' ^6 s: u  }; n
......- ?" f  Q! W* z4 R

. m0 r, p! X- p# I6 S( C
& ^: p2 D& P! _[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表
5 l% \2 t6 x9 {: P3 x- e, V/ [2 E' q1 y
......
$ q. m% b$ x1 b/ u5 W- L* ?7 g) |
( Z6 K" }- p) y" {0 M" n0 _- O
Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?
5 X6 q8 q* }$ C0 Y+ f3 W: y5 j如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()3 }# ?: }7 L7 |$ {" Y
TPL=30
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

Archiver|手机版|小黑屋|计匠网

GMT+8, 2026-11-4 00:33 , Processed in 0.136343 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表