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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。- b0 R& i: S* I  R3 \5 m' }
, y5 H& P3 X* x9 k* z1 @# x6 i
SEC/CEI:
  l- h+ X) Z% q7 m  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。
" w/ X. a+ [% ?5 s& u- U/ H在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).
" E, b) s4 ?2 P6 l- I$ }2 \/ t2 n# c
5 \7 ~# i1 _+ o9 TPEI:
5 n( x6 o% E3 M8 b) j   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行
0 {" C9 s. N6 ?  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。
$ @; }2 G! r7 T' _/ M      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.
: U- N& Y! u( D! `# u5 B          InitializeSercurityService函数,将Notify队列清空。# [( L& N+ B- u: }; d
          InitializeDispatcherData函数,将Dispatcher队列清空。
( T& t6 N! {& p. V$ W0 e; A! m  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。
9 J7 T8 F  v+ E8 w    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:
5 e- E, Z* l% D9 g+ P; c+ m3 q      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {9 l; }! m, W! e" A5 M* @
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},
6 H, y1 y7 Y6 Y' k" x+ w       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},
4 G" d( }9 \! p# t5 g: n% ~       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},
: F. I- z# R- `7 R       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},3 M2 `2 Y% W1 y  S5 z0 ?
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},5 V4 g- Q! ^* Q
       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}
/ S$ }* l' f; u5 c0 r# w     };
. j4 D0 l2 j/ E# M, {& D    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。
" }9 a8 _  J8 d   这些PPI会在PEIDispatcher中用到。
- E# \' i1 z! c2 ^   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。( S6 z9 F3 O: [/ E0 f
   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。) |: S+ d# B) F* o) V! I$ M. R! ]9 x
   SwitchStacks (
, e2 |' a1 M) l  T/ E       (VOID *) (UINTN) DxeCoreEntryPoint,: H$ Q. w! G9 C- x0 e* Q) L
       (UINTN) (HobList.Raw),
) n4 a& v: ^5 J* r" U: p       (VOID *) (UINTN) TopOfStack,
. N0 Z9 L+ i8 g7 J' l- E       (VOID *) (UINTN) BspStore( [. l# k& F3 i# {- {; T
    );
" I7 O# w4 ~' [$ G. u' a  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。). D. p" v0 m! X* H' r& `0 w
% P* ?- }: P6 H+ O
DXE:. e; U2 T& A  H; f8 p
     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。2 j/ p4 i' b, |
接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。# n1 a8 P0 S% V$ Z  L
   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
- ?  P, A* G( `中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
, P  U4 u1 C' X( o2 |$ y   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。
% Q" c2 n# K7 N6 t   
) l) c+ ^; y  gDriver:1 G; M1 l! x8 @& M: K* N
    我们的驱动什么为在PEI和DXE等不同阶段执行呢?! m' n, u. A9 m* h0 L& s6 Z  e1 n
  大家请看一下我们的驱动的makefile.(EDK中的*.inf)( c2 G+ b# G  u
  [defines]
' o# x7 r3 w0 I& L  BASE_NAME            = OWEN
2 h: u3 s" N9 }  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110
/ h; {7 I* v+ c: O; o% r, V( W" x1 ~  COMPONENT_TYPE       = BS_DRIVER) P" U, {( y% Q, N7 J! n
# E' K' F* x* F
  BASE_NAME告诉编译器最终生成的驱动的名字。) h" q$ M/ F6 z& _' b
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。
' Q, n4 _. N5 n* n' l+ U  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。
$ d; ~+ ~- [" D9 o2 k9 P; u  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名
' ~4 {  U* p* K" k  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {3 Q) F0 a6 N1 j8 m
  {"bs_driver",  ".dxe" },: ~: {* |' I1 h9 s$ Q
  {"rt_driver",  ".dxe" },# A& p) @6 \" `% E2 S( T! O' z5 ^
  {"sal_rt_driver", ".dxe"},6 }$ O- g( F3 {& @3 F2 Z
  {"security_core", ".sec"},
0 z0 @6 o! R; O7 @8 b- h+ V  {"pei_core", ".pei"},
8 H( j. N6 \" g. b5 @4 V; E/ M  {"pic_peim", ".pei"},& h7 _* Z( Q+ Y( E
  {"pe32_peim", ".pei"},+ I! E  A0 e5 G" H
  {"relocatable_peim", ".pei"},
  D. [. n6 o; N" c7 P; `. Z, D6 ]( {  {"binary", ".ffs"},
' }5 _* _) s8 v  {"application", ".app"},; Q) v0 f# ~1 p( {% }
  {"file", ".ffs"},
+ H4 m* \' A  t% i  {"fvimagefile", ".fvi"},* {) s& u* [9 T% O
  {"rawfile", ".raw"},
) f( q8 l: e& ]) h1 B  e4 n) g  {"apriori", ".ffs"},
7 E  @  k% T# {9 N# U" t  {"combined_peim_driver", ".pei"},% t* Z* P9 j+ Y3 U
  { NULL,  NULL }
6 f1 u- Y, M/ b8 F. w; a9 b};
0 I4 ^  V  V8 P5 Q" h! F: J" o5 X$ [8 c* c6 J, I/ @
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)$ ]2 V9 o2 X1 G: {7 m' ?# w0 C
   7 f& ~3 X+ |' Y; \
         
. M2 w& L' W# N. |1 ~  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!' W# D) {! s/ z' R/ L8 L2 \. }2 G
支持& E5 V) s# i4 j: \0 _3 V4 a
继续5 e! u8 |  }9 I3 U, ~% o7 ?
加油' j6 Y7 O/ D/ I) g9 _
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?5 \0 J8 S9 w. k; _6 C. u* l1 U
有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。: l3 i; {' W$ \
还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。0 t, s9 w9 h: J! e8 R

% J3 o# x: f( B* q3 w" a支持!
回复

使用道具 举报

 楼主| 发表于 2008-7-28 18:52:31 | 显示全部楼层
% i. B. i% }- B5 O
小弟还在学习阶段,目前的目标是知道执行的流程。
, Q0 [6 s# X0 A# _2 ]! h  C  Y; |这里面有很多细节没有写,能力有限,只能自己知道,不能表达。
0 `& f0 G( z! k  p; i嘿。。。。: X( f# z+ w" [8 v
所有大侠们如果有好东西能给小弟共享一份。" M2 t! a& S; @% s, m" ~) z
: F7 r) z9 x2 N1 o% f
谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
* A) v  X9 J7 h' w/ @+ E+ v% y  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver7 K. A2 T$ q: w. F
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...

7 K. [8 I. o& I4 M  G* n# p, ^, ~+ u* {
& Q/ J! F4 W) D; o: @6 MPPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol,   M' F! c* o4 V: v5 j9 ?. J1 v7 c# F
For more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".
1 J9 `8 i) J9 t9 t0 L" W: |
; c" s8 N% V7 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:
+ F: N# V$ ~3 `* x$ W$ ^2 Q   Yes. I make a mistake.' A1 Q0 V# ^3 r/ K6 q$ V" R$ G% k
      PPI:     A PEIM to PEIM interface.; i/ [, G4 ]" N8 \9 z
      PROTOCL: A Interface between Hardware(or firmware) and software.3 X% L$ ^1 X. m1 I% H7 U7 |& w
      reference[http://www.biosren.com/viewthread.php?tid=207]$ Z1 C  S' D0 X4 J$ L
   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.
* m7 f, u0 b- P1 G: m   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:
$ r2 W! m/ c7 Z% `* X) g+ |# B     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。
* E7 J' ]6 Y8 f' }, _( I5 E接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。
' X* Y7 x' \& \" k  f% v   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver+ T1 H$ o6 T# ]/ W" U) u
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。. m5 _) t( X+ l0 K7 R
   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.
6 Z" J8 R; k) y

8 P+ W( B5 T4 J% L3 TThere are mistakes,  R7 ]/ s5 a8 Z- v% \
1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.1 k+ u% f1 F% r" N

7 x$ z0 Y  J! J% d; [' t2.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.
& J  R: ^6 g0 i7 hThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
3 w- m* t! u! k- f4 R; n* bFurthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
; j9 @: t6 \. d1 o
4 l: _0 ]& ~) ^. ?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 大侠的指点。
; s8 Y2 J$ Q- z
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表 0 P5 p: e  N5 n$ G/ |
...4 @8 E& a  a+ |6 t, \4 v
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.
. _2 k6 u0 T" i8 c6 ]  X  |: PThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
* z, L4 E- {$ k6 o/ oFurthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
9 s- |8 z1 A8 z
    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().
' d+ P- L4 R! z  Y- L, z/ [- p" A, ~    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)5 G9 m2 K/ [3 G
  //5 _4 h# v/ y/ C! A0 l8 e7 U
  // Go connect any handles that were created or modified while the image executed.; A6 H! Q# \- \/ m, y) n  x
  //
5 w: I: J* @1 b5 T2 r  CoreConnectHandlesByKey (HandleDatabaseKey);

; m1 {2 [2 ~) u) O0 o2 A/ p, j7 @这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey+ ~. c0 s- x1 r4 o: t! ?
而CoreConnectHandlesByKey会调用到:
  l/ B0 U- {4 Z  //
2 ~4 ^, e' d+ }" H, z: E3 b  // Connect all handles whose Key value is greater than Key
* k7 f4 u+ X1 S8 P. n- A  //) V. O; w5 B( d5 A
  for (Index = 0; Index < Count; Index++) {9 A- i( @- z' H3 ~3 f( p# Z0 B- W
    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);/ i! a; X2 f# V0 ?" i" ]
  }

/ A' D* n! W5 F/ K$ @- `, o2 c所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey): h. ?  }$ [* w7 j
是会去ConnectController的,也会执行对应的Support()和Start()才对!!
. @$ J  G1 \! A9 S( R, R
1 l. z) h% \+ v不知道我想的哪里有问题???欢迎大家指正.
9 P# E6 X" b# N/ U$ ?6 Z* @  N) V8 K" X) b
[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。
" e3 G' {$ v- m8 s1 L6 L一个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 | 显示全部楼层
了解了.- G: i, k- T( A; z
非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,/ V; F$ r) C* `- q! f1 h
Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,
# e: X2 S/ i2 |" @8 l" V//4 G- o; J! r# ]( K; i( v& u* C
// Notify the notification list for this protocol.
) |3 A& E  w" A* s6 t6 m//) ?  Q1 Y6 U+ H- I$ z3 g7 P
if (Notify) {
8 O. y8 [3 @, C5 j* D  CoreNotifyProtocolEntry(ProtEntry);
$ C  k; H5 R  t; t$ @}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.9 J' g2 L5 e# F1 [4 {& Z" ^9 Y
有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表
# F: k. ]" l' {/ z; I9 c1 m请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?" y+ k1 ]8 k# u+ g
谢谢!

3 v9 k$ G5 ~( K! k; o4 E......
' y7 v0 q  U9 I
7 x- {1 O% i1 ^4 \+ h( I% P
1 [; J' N8 X. j* Y5 Y; K[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表
. j0 W0 `4 n5 _- |# y/ @) N6 o" f1 U& ~. s7 X$ j  H) A& n
......
% m) t: m& E7 T7 T, G  o, H

' ~# ]+ B2 x; w7 e4 vSignal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?5 w! ]+ f7 \) I6 t) Z5 r
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()
/ k6 N, a' F) ^$ ~# G9 M2 {TPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-7-22 03:58 , Processed in 0.052207 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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