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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。
3 C& e* F) _6 C8 U" _& e6 V" @5 E8 k7 G- N/ o: p
SEC/CEI:" Y. A& z8 }; I' v, S
  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。
5 Z. [* w8 \# A, H# W! o在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).+ ]3 ?0 p! J5 |1 R! S4 H9 H
4 g' O9 ~6 s9 j0 W
PEI:
0 F3 k5 E* l6 P  |0 M   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行% d. _* S- E6 y* Q4 T
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。
1 n) N& [# y7 B* b+ m9 U2 u      InitializePPIService函数,将PPI队列清空,这个队列长0x3F./ k; [9 C5 q. R! |& w% E* L
          InitializeSercurityService函数,将Notify队列清空。% Y2 l! i" N" n3 V7 n
          InitializeDispatcherData函数,将Dispatcher队列清空。$ b5 v; M# a- c5 g
  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。
3 n# g" d+ c6 r. U$ ?    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:
1 E4 ?' O7 J* ]: D      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {
# g$ T8 k) v1 ]6 U. E) m- ]       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},7 g2 E9 ^8 `4 O" ]) P4 C5 X/ o- e2 c$ v
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},
. k! A1 U/ K- k- `8 W% }       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},$ u  n9 o8 x6 E6 K
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},5 S& F& v& O: X7 Y' J( E
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},) `; }. t# j  M' U( J% i. _& V& u, Z
       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}) W; J; l& c, ~1 ?4 A5 D$ X
     };
' B, }1 i" L, C1 }8 A    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。- O& S. O. y" s1 u  T
   这些PPI会在PEIDispatcher中用到。! m! |' E" h8 I$ R. p3 y) Y' C6 X
   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。
8 p0 x5 _3 x+ t- P% t# T   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
! K. `. F3 w' A4 g$ r' O   SwitchStacks (
' i4 `: P" c% y) d; X+ }       (VOID *) (UINTN) DxeCoreEntryPoint,
7 v; m) L: {, N  _       (UINTN) (HobList.Raw),
1 R# q" K. X6 D' X0 m. Y       (VOID *) (UINTN) TopOfStack,
* m+ H, z8 A' j       (VOID *) (UINTN) BspStore# u7 z( W& k/ y! e
    );- Y( ?! o; y+ k/ ?& }, M' l
  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)( z! c; v% A7 l8 g

3 q) V. j& I. @# W' F, N: cDXE:
; C" T( {- P" w- e' |/ `3 Y     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。
% {4 ]6 h; u3 i, A, a接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。& |4 |: d, q# ]5 z: X9 s/ ]+ N
   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
$ ^( ]  I- x6 o+ x中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
/ {8 z* a: {* z+ M( n. e   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。+ O/ ?' ?5 m8 ]  f. I
   
( s2 M$ V( r7 f% SDriver:
: b+ Q( {. L5 Z5 B' x9 W* @    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
9 ^- N1 g! f' Z2 S  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
9 }/ R& V" @$ p+ D' K  h' E4 u% y  [defines]" i& {) Q) [$ W% z& f9 f
  BASE_NAME            = OWEN
& _; F' B( M# g& x  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110
  X  Y2 c% |/ F! Y- m% F. v( P% I2 I  COMPONENT_TYPE       = BS_DRIVER# K8 n- n5 S) T! F

! ~1 r, o- p! N0 [  BASE_NAME告诉编译器最终生成的驱动的名字。: i# f( E5 X  E$ s" c+ D& ]
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。
: [4 @' I1 o- m# s* p  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。
. u6 }4 c0 V8 D9 H+ ]4 E  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名
2 u' o, C& J- [/ o1 m  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {
4 q1 a( k  w2 |  {"bs_driver",  ".dxe" },
1 }* A& Y( e  S1 ?: v5 o0 g  {"rt_driver",  ".dxe" },
- I* M' }! B1 V% x0 C) Y; m  {"sal_rt_driver", ".dxe"},- V  U9 |; b! j9 W
  {"security_core", ".sec"},
; E5 Z  \- C3 u* z; F: t  {"pei_core", ".pei"},
: m  j: X  L( g9 U! F0 @9 ~& p  {"pic_peim", ".pei"},- K5 m+ U. H# K3 p* h( }
  {"pe32_peim", ".pei"},
6 n2 z$ o! T, `) y. ]2 Y& j  {"relocatable_peim", ".pei"},$ z* a' e7 ]7 f. h) s4 q
  {"binary", ".ffs"},
# ^0 t% [- }3 R5 U  {"application", ".app"},$ U/ ?* o8 o6 _& b
  {"file", ".ffs"}," N% z2 r$ h. O) n0 F7 _6 M
  {"fvimagefile", ".fvi"},* Y5 ?4 o9 i$ D2 s, d& P
  {"rawfile", ".raw"},4 L( x) p" x0 Z1 f! \! b" @
  {"apriori", ".ffs"},
5 m7 w" q9 Z8 h  {"combined_peim_driver", ".pei"},7 J( K! a# s3 N9 I6 d4 C8 `
  { NULL,  NULL }9 \( ?3 W( W4 V& C* u/ H4 r% j
};
5 G$ k6 V% T5 k7 \6 b
; G4 B5 V7 x5 X3 F! E5 f/ A( e了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)
4 k/ X( L2 R7 a0 W! B1 r   ! O( J$ }* W0 c3 d6 W; V4 Y3 A2 D
         
* T; _! y6 @& O, l% c; I  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
6 h8 c" m2 O* a. }支持
% D* t5 r& Y+ F" G  b2 C  H继续
* k; j! g- o4 p0 u. b加油$ L: {* U- W0 v- H# Y
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?
; F6 ~% b/ w6 }有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。
5 o2 H+ \( C/ l, s0 j还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。
/ S1 ~2 l' J! l3 l  V8 ^# P& E( m' Z1 d+ q
支持!
回复

使用道具 举报

 楼主| 发表于 2008-7-28 18:52:31 | 显示全部楼层

& _* V2 B1 r( p) T$ _5 g8 c* e小弟还在学习阶段,目前的目标是知道执行的流程。% q: s3 ~. `1 `: j1 J' R4 V
这里面有很多细节没有写,能力有限,只能自己知道,不能表达。" ?: P  j. m" u3 t# s
嘿。。。。1 H& M. l8 K* S3 Z5 X
所有大侠们如果有好东西能给小弟共享一份。- @5 m5 H0 X( t' W
1 H4 ?+ D! E; A6 Q
谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
4 M$ W% r9 \0 N& h2 a# S  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
' z& |: X! \- ]8 E中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...
+ m3 N& T  P" ]! B2 r

5 B7 ]" A5 U( R  u0 {1 vPPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol,
+ v4 v( h! H5 `) r, M5 A* {; z8 @For more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".+ d1 X1 T) Z, f  l) m, U0 _( I

8 U" k0 {0 Q  O& w( ^[ 本帖最后由 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:
# l0 ~9 E# f5 n( c2 ^2 ?   Yes. I make a mistake.
! T6 a9 m1 Z( E, D      PPI:     A PEIM to PEIM interface.2 D( [& ]% i: ]6 m) @! H
      PROTOCL: A Interface between Hardware(or firmware) and software.' j' s" o1 Q. n( M4 n" |/ |3 x! p3 n
      reference[http://www.biosren.com/viewthread.php?tid=207]
0 t9 V6 A& F/ D/ u' x" F   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.( U) b( j  e8 S  q& K8 F& K
   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:9 H* A# K4 E' Y5 ^  @  \  |
     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。
& j- b3 k! ~% z* y; f5 V  [接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。; L2 |) o9 \+ x& J
   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
$ i2 g8 O% T/ y( B中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。
1 |- t% {7 E: i   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.
* p. `7 w  _" |( r$ `8 Q
8 ?, y% P  Y1 A* `
There are mistakes,
; u( L) v+ |' _1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.; g4 y% G3 U; D: ]; I
$ F0 P4 _, a4 V8 t4 P
2.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.6 \( {" ~- F  J/ }% p
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.' ^1 p$ X  L0 e) K$ u
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
. P$ E4 ^, N5 B4 M7 e$ _, _
  e- }3 D- M. _" u' `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 大侠的指点。
* R0 b% }3 X( |5 X: L( a2 K
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
+ g' g3 V7 `( @% S* [* [...
: l6 a5 _# w/ o  H2.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., D- S% L! Y+ |! _
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
) ]' c3 Y6 n! O* A( {# rFurthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().

5 o7 D1 C3 O/ i2 p+ l9 E    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().8 k$ ^4 i1 H2 j. H5 z9 C6 j) M
    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c), S( L: ]- c8 o) Z4 S0 |/ \
  //' i- ]' S4 M$ |
  // Go connect any handles that were created or modified while the image executed.
8 \3 Y' t/ p+ O* e  [4 o0 u  //
) r. B3 j$ P5 f3 ^9 b+ d) k  CoreConnectHandlesByKey (HandleDatabaseKey);

- l$ g9 _1 g# X/ X7 \这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey+ H! n! T$ E( w! ~+ k+ e( A  D
而CoreConnectHandlesByKey会调用到:% `! }" }$ ?, ?! ?# c: L4 r
  //
+ L1 B: a. Q! K' ?) k  // Connect all handles whose Key value is greater than Key
% |0 [% v% n* j& L! y1 k* h  //
+ s+ n1 N3 k/ _/ n6 u6 [  for (Index = 0; Index < Count; Index++) {* E& m: a4 U( T7 s& Q
    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
7 Z8 i) Q. G  a% _1 R) l7 H  }

8 w; D. p# s2 o9 ]: {所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)
& P0 j# j3 n1 U7 m4 W8 v7 j是会去ConnectController的,也会执行对应的Support()和Start()才对!!( i' r/ P3 N  [1 k8 e

7 |1 f. w6 s8 a: [不知道我想的哪里有问题???欢迎大家指正.+ k( e( z" e7 Y3 z# j+ g

! \+ f: w3 S! z[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。/ @; k( y1 T0 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 | 显示全部楼层
了解了.
0 }' f4 V) p$ k- x8 {非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,
) d/ F" d+ E% x. o1 q* PStatus = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,
2 I4 y' a! r7 i& R//
: S/ z9 L/ e+ u6 f. e// Notify the notification list for this protocol.) L- @3 |  b7 B3 w9 f1 H: G
//
4 [) Z8 H0 p8 ?' _* Z- ]5 k, Oif (Notify) {
. a6 ~1 p! u& Q& ]0 j  CoreNotifyProtocolEntry(ProtEntry);! ^0 h* |- X' d1 {# H
}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.2 `2 |2 V0 `% E6 J! b" N
有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表
; K* @$ _! Q  P0 B0 a0 d# H请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?
% j: `* Y( s) c: U" Z! C/ x谢谢!

) _% V# G1 _" w; _' z- f7 N5 b9 v......; @& S2 l% u& ~) u9 k, o
$ s# y: X. X/ y+ c0 U; z: K) E
+ x  H7 F" V# J+ |9 s9 h
[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表
9 u1 B" u! f$ i9 |; ~% A" T( p% d1 ]3 j# B8 P
......  a- G- X7 Z: J4 C8 R8 a
: Q# ?. D* a4 r0 J+ e
Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?' Z# [4 |* {' i4 y4 F
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()) B7 b0 P' D* y2 J( D8 g, G: u( t
TPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-7-21 22:11 , Processed in 0.092070 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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