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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。
% @+ T4 C4 F! L: f5 P+ o  a* K6 M. a3 j$ ?; J6 s
SEC/CEI:% H; Z( C, z9 N5 U
  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。: D+ Y2 @; a/ j* P0 p6 {* L1 g
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).3 ]* e$ ?! e& ]( l  Y) r

/ G' |7 A0 D8 C8 p3 U7 W6 RPEI:9 e4 h! h; c1 F+ V
   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行! A3 L  {) z, w) J
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。
! \2 a+ W; N# P+ r5 S4 z5 n8 u      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.2 a3 c5 W' |. o% H
          InitializeSercurityService函数,将Notify队列清空。% L* l1 [8 R5 q8 n" J
          InitializeDispatcherData函数,将Dispatcher队列清空。# X& u8 v" D( o2 M
  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。
. @4 X7 O5 W0 m9 p4 ^. ]5 E  ?    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:0 X% i8 u$ s5 @( h( f( P
      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {
1 \% `6 `  F4 q+ u       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},
' k9 R6 d, T1 p, `       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},
' r$ x6 ^; {% m/ G( }       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},- g6 D/ C$ ^( E! a, @& ^  X
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},
+ W; Z+ k; Z2 j       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},
/ e) A( I" |( `6 q       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}
$ @' s2 |! K; j3 k+ y. M. V     };, {2 k! Q& H0 F8 f( p' q; D% z
    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。( }9 T- X# N+ _$ G  `
   这些PPI会在PEIDispatcher中用到。1 o, L* S( _. Q  I* Q9 `
   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。
' O1 D' b4 E3 z3 g4 Y# X2 N& A   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。% u3 ~2 W$ l8 o- \: Q
   SwitchStacks (
& y+ }8 D3 K7 t' [- p       (VOID *) (UINTN) DxeCoreEntryPoint,
, R5 A% ~: W+ K7 P# h2 v       (UINTN) (HobList.Raw),
& ~$ a5 A) C) P0 K" \! a/ Q" x       (VOID *) (UINTN) TopOfStack,; u/ j3 |) S* V1 Z& T; t
       (VOID *) (UINTN) BspStore/ @8 H8 @/ i% @' W
    );
% Z  D5 [  ?4 f* S( K9 M8 h  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)) F3 E5 o- X2 k0 s7 a

+ e- H, J8 {. F" fDXE:
0 g7 ]; X6 I7 h. u  Q     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。4 t3 B- Z! n! Y) P& a* ?$ r
接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。
. B2 o/ y' O4 G% e4 V8 ?   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver+ c6 E1 n& I; D9 _, G1 x& p! C( B  m
中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
/ @5 W7 ]5 A6 H9 s   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。
0 Z3 W4 c3 b7 |0 V% U4 b7 ?1 r$ {   
2 n. u7 `! ?6 c+ ~  qDriver:$ F9 P3 r' I( u* @% m
    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
$ Q/ `6 a( T5 R2 U! u% J& L6 @5 n" C  大家请看一下我们的驱动的makefile.(EDK中的*.inf)$ L; H( a1 [. K9 n
  [defines]
7 l8 |: u& k- e% b  BASE_NAME            = OWEN
7 {% r/ t3 \# o; q  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110
1 w  w8 F% `, c  g7 w# ^  COMPONENT_TYPE       = BS_DRIVER
7 {- e. R9 s, @  o
/ t( m7 y3 I" w) e! H) w  BASE_NAME告诉编译器最终生成的驱动的名字。/ x% V3 m& w) q3 K$ P8 Z2 _* A
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。+ O2 X" q; K0 a4 k: Z% H3 e2 i
  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。3 L4 c$ b) c/ B; W$ f: `( K
  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名
! I9 m) ~- L7 }: o5 V  y+ _  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {
" t3 @6 K5 S0 t( L. u- k' \  {"bs_driver",  ".dxe" },
  N: o# }. B) }% J, e0 |  {"rt_driver",  ".dxe" },
" J: P  V% s' R6 D# w  {"sal_rt_driver", ".dxe"},4 S, W* ]3 E: N3 X
  {"security_core", ".sec"},( {3 a/ R) p) u1 L
  {"pei_core", ".pei"},. P' C5 A8 c9 M0 d+ v( ?
  {"pic_peim", ".pei"},
- k  R! u1 Q- {& J! c4 a+ Q& k6 h( p2 w  {"pe32_peim", ".pei"},1 |7 h9 V0 `, Z6 ]. j" x; J, s
  {"relocatable_peim", ".pei"},  A: y! p2 r7 n
  {"binary", ".ffs"},8 q% H5 X) ^7 W0 z- G
  {"application", ".app"},! F( a% U5 @* |  u7 N
  {"file", ".ffs"},
2 r7 G2 }6 k4 {  h# Y  {"fvimagefile", ".fvi"},+ q3 t' v, I7 P+ p6 k: M; d& N
  {"rawfile", ".raw"},
0 S! \9 q/ {0 O6 e  {"apriori", ".ffs"},
5 I- F: b) S+ |$ C7 M. Q  {"combined_peim_driver", ".pei"},
7 c( F! |; o3 J% S/ p+ p0 n* D' ^  { NULL,  NULL }
+ n% a9 t% B5 J/ G9 a7 Z3 ^: g};) a. I2 n' ^) f: O, ?/ J
& t+ x* f5 d8 T8 N& @
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever): a5 ]0 l5 X0 `5 f0 q
   
$ z3 {5 z$ X9 l7 x: L  w         
! |- Q1 h3 k/ Z# V* E  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
# o. y! N4 S+ ?: T5 X支持8 Q! m5 L+ Y# e
继续
/ G" b: a4 D' y" x0 Y" m: {加油) A  O1 _. Y: z  `0 \
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?) u1 C3 U( U6 K/ ^* O
有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。
& m, {* \5 C+ i' i% X7 B还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。
: v3 F) f5 F& s8 h  }0 l
& a+ ]) z! ]  |$ |$ j+ l+ W/ L支持!
回复

使用道具 举报

 楼主| 发表于 2008-7-28 18:52:31 | 显示全部楼层
) u7 }/ |- t/ l2 T" z3 Z
小弟还在学习阶段,目前的目标是知道执行的流程。! Z# R  f' W2 U. W$ b$ B9 R7 q
这里面有很多细节没有写,能力有限,只能自己知道,不能表达。  r5 Y, `9 t/ t6 _
嘿。。。。
( i& h0 F6 J; O2 ^5 `9 a" H所有大侠们如果有好东西能给小弟共享一份。
" W8 f$ L" D/ B0 X9 I) \  {& c
% L5 ^7 U( M! ^) N谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
& @% R. m' W+ T' m  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver5 {* w" m7 u: }5 r
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...

+ S! e( |1 p& x% ~/ e1 M% x; t! A6 V8 o1 A4 W
PPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol,
  A, ?0 |# k, V$ C0 ~For more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".8 r# j( M: E$ J! p$ U, v- \

% u  j: z7 I* q5 Y, x& [5 e  R[ 本帖最后由 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:- S- a9 w" f9 I4 H- s  t8 l- ]
   Yes. I make a mistake.
6 x# s5 t) d7 X* a      PPI:     A PEIM to PEIM interface.
* Z' C+ A2 A, ]9 j* M4 s      PROTOCL: A Interface between Hardware(or firmware) and software.' M+ o- ^4 p$ ~- v; L% m
      reference[http://www.biosren.com/viewthread.php?tid=207]
7 _8 M2 n4 t5 v" |$ M( I0 |+ g   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.% Q( V* [' ^; f1 T
   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:9 g/ {' V8 j  {: S( x5 ~0 D
     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。
& {& {0 R8 M( m9 S/ M接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。
* X$ B% t. O- F. O0 L2 ]   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
* P% T! t( Q+ v4 K; f+ L! \8 W中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。9 C8 G7 V6 _2 Z6 g
   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.

+ z3 u" m! O! F9 t* X8 w7 _% c/ a0 t
/ \* B1 D% j# e- X% O% A- r3 @9 u# ?6 S5 kThere are mistakes,: S& R$ r% _+ O' H7 M* n4 o
1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.
  Y" c2 M* |2 L6 T+ \: r* q( W0 d$ Z7 X0 t5 U
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.4 T5 g% ?: i# m1 M/ H
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
8 B) ^0 K2 @# B, P0 e2 H7 {Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
9 s& m' P+ ]& V! h" T1 k  c# ?) v6 H4 U2 R6 \
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 大侠的指点。
8 X  v" u+ ^) |  `7 Y6 f: n$ I- N
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
1 f% Q* A: p4 V0 `! J...7 o/ N6 T' P2 }: E( R6 c  l+ ]
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.& r% g# _# ^7 o
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
! U* [" ?2 [+ |" V: Q& K# oFurthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
6 W. A" S' ]0 \2 m) i7 M! C6 O
    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().
; B; S1 d- x- [8 X# A, Z' d    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)2 \, |2 Q. l' c( J
  //$ G& W, n! \% n4 t& s
  // Go connect any handles that were created or modified while the image executed.
' e& z& n2 B  J* D$ Q- D) ?. H  //  @1 M& O) Z, B
  CoreConnectHandlesByKey (HandleDatabaseKey);

$ o! l9 B. f3 V4 a, F. {6 |5 n这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey
  l& {1 z( e% t8 a, |1 v( k3 A而CoreConnectHandlesByKey会调用到:
+ P8 C! R. H# |' u2 M  //
/ k( w6 a5 |" b: B, o5 Q/ k- }  // Connect all handles whose Key value is greater than Key( B% e; K; \5 b: @, F% F
  //
3 e2 j3 B& D* q# K2 ^' k4 Z  for (Index = 0; Index < Count; Index++) {
1 C( I8 s/ Z  ?. D1 w4 A    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);% H4 S/ ^8 V- X$ n" R
  }
( u8 n7 c4 r) C8 o& |, ?
所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)8 o/ k: g5 h7 i+ y: r
是会去ConnectController的,也会执行对应的Support()和Start()才对!!
( `+ U, d; M5 u$ N# \+ n. c, y& d% ?) K' b' H( w% r
不知道我想的哪里有问题???欢迎大家指正.5 }3 T7 C( E; z0 u& a8 v5 g

( L6 w9 L( A# X* S, b7 a0 x[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。4 v8 }, i; k* \6 g
一个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 | 显示全部楼层
了解了.
, i0 m2 h1 \. z( }非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,9 Q) Z' f% Z4 B! |
Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,
* D5 Z; P$ p3 n/ Q) i2 D& _2 u//& h8 z! v3 k$ P* \- n$ x. _
// Notify the notification list for this protocol.6 @0 m+ U/ a- @' P9 G+ O
//- _$ C7 V( F; U1 x; Q: }( d
if (Notify) {# g5 x; }0 ^! w& h% ^/ ~4 s6 ]6 v% D4 p
  CoreNotifyProtocolEntry(ProtEntry);
4 k& a) H$ e  D5 |% h  Z}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.
4 g. U4 u  \  f7 K) x! p& f有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表 ' l3 n/ E$ r& y2 B( a
请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?
! v/ m" N0 r9 U) V谢谢!
2 W' ?  u4 t3 W! H' t# ^
......
! t- A$ l# J% Q: `5 I/ ~% ^
% Z  W8 Y# q* A4 J; H  d# ]. e& @9 Y2 c" r7 [) @* ~- ^
[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表
" ]0 A+ @0 y* U! A1 ]9 i" \5 @" Q$ V. |: j
......
; q% V, K9 `6 }1 Q
& J7 ]$ |3 h+ A( p
Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?$ \/ g9 m% O7 }# G* k
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()) l8 C) n* {4 z# \
TPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-1-19 02:23 , Processed in 0.070022 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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