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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。2 |3 D3 q* `, r& E

3 ~& H& N5 u1 X. ?1 G  ~SEC/CEI:, Y3 o- p8 s$ C) d2 `6 H4 r
  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。
) W* l% f! a8 ?在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).$ S2 R9 C2 X0 I: A1 p
% h( q: h# c1 M8 G$ D' Y1 _) J* o8 v
PEI:
" W1 ?5 [0 U# ^- Q/ X2 L& ?   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行7 E5 E/ b5 i8 Y# K$ s9 o
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。! C' w% ^( f+ y. D1 K; F0 ^: L
      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.
5 D. Q% X! [) o          InitializeSercurityService函数,将Notify队列清空。
  T8 ^4 V, b1 x. d7 K0 x9 `          InitializeDispatcherData函数,将Dispatcher队列清空。
! @0 G' K- \- d: p0 |2 {. n  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。& s: p4 r7 y6 T7 x7 ^2 [
    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:6 |7 a* y+ x6 Y' J1 B
      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {
' t' H) P5 Q; P, p3 h" }       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},5 e" U) A, G0 b/ t
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},+ }, f. p! K7 @. |; u
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},
) E2 J$ W& i( _* [/ Z8 V. |9 t& A% c       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},5 I1 Z2 `7 m$ f6 u: D) O* |
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},1 H; U2 k' b- [- C, m1 N& g5 w& P
       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}( b0 ~7 D$ v$ d8 B
     };6 t' R: A) o: j' ]% J* Z
    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。
$ _: r1 ^) M: A: Y; H" ?+ o   这些PPI会在PEIDispatcher中用到。& ~4 A& H8 i9 W' g$ Q$ o) `6 ]8 J
   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。, S+ P; k/ T- _
   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
) P) N" _: o$ v  c1 s8 t   SwitchStacks (
8 c. N0 N; z. ~$ ]! j1 J       (VOID *) (UINTN) DxeCoreEntryPoint,
5 z1 |1 |: i; k% x9 |       (UINTN) (HobList.Raw),
' L+ [/ d) w% Q1 W8 g       (VOID *) (UINTN) TopOfStack,! B7 X: ?3 J+ U3 }
       (VOID *) (UINTN) BspStore
1 `* C) ]0 ^4 D0 [: W" U    );. |7 O( |) i4 v' Y+ ~1 r
  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)
7 l; c. O# c: N4 o3 _5 N4 L7 l! {# R# C" H, b- }
DXE:; R, \, ?! @, A& Y  H, P
     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。; X- p* \; u" i2 e& H, e4 {
接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。+ y/ t  ^. c8 O5 ~
   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver" k/ o! T; [& g$ P, b- O$ Q% y
中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
( h: q; G4 J4 x. \8 z# U9 N   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。* c8 L2 ^& U4 B, E( r0 k2 c4 g, }; A
   
, U, O6 j; O. i+ y! iDriver:
! S6 z# K8 a, {- m    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
" o8 j9 N* F) A5 y0 s; e  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
& e7 _8 s4 |( Y$ D  [defines]
( H: u& P, {# Q- J( ]+ J' Y  BASE_NAME            = OWEN
& m3 I+ _& B8 d7 u3 _7 G  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110' X) }+ ~  U4 f  k
  COMPONENT_TYPE       = BS_DRIVER
! a# |, {$ ~5 U7 ~) n$ W
) y# d& q5 w, p% Y  BASE_NAME告诉编译器最终生成的驱动的名字。) o5 B+ Z$ l( N9 }9 E4 L
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。
0 V6 C6 `* V$ J# T  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。
5 x5 R% _* w  ]9 X0 J" u  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名
' T  G( k/ s7 e3 T  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {
6 Y% h6 J0 H- Y( Z  {"bs_driver",  ".dxe" },& d9 ?1 h4 _5 Q5 Z8 l8 |3 B) p; B
  {"rt_driver",  ".dxe" },
0 f' T7 ~5 s  ~  l9 I/ }  {"sal_rt_driver", ".dxe"},
* Z! }$ S6 z& E: |& Q' B$ ]  {"security_core", ".sec"},
' p/ b+ T% y9 d6 e  {"pei_core", ".pei"},
+ Q& N4 A4 r! V  _  `' [5 P* W0 P  {"pic_peim", ".pei"},
1 v  k0 p: X4 L& v# ~. |  {"pe32_peim", ".pei"},
$ f; u5 `% ^4 @' N# f6 Q2 H$ _  {"relocatable_peim", ".pei"},
! f' V( _# v8 Z' {  {"binary", ".ffs"},
; D6 g- O) b) e* m, H  {"application", ".app"},
2 C. z8 H# `  {' M  {"file", ".ffs"},
+ j. G* Y; G. R! l  {"fvimagefile", ".fvi"},
7 R5 s' d' q6 Y) E( D: H  E* C  {"rawfile", ".raw"},* X; R" P: E. f$ c
  {"apriori", ".ffs"},
' l8 ~1 H! N  @  {"combined_peim_driver", ".pei"},
& k# R& \  ~1 c" c4 x1 a; `' E  { NULL,  NULL }9 `( U8 e  }  a5 @, @: Z' `
};. O" l$ f5 O2 }2 Q% D
7 \. _, ~- H0 M" m* ~
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)
: d: ^' ^: i9 D& |   3 {) ]3 w9 @4 D
         
  f! x4 z! x! F5 J" Y" y( L  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!, O( Y1 J2 y' X& v% [  K2 n( n
支持
: M2 b- Z( c& R& H2 A# k* N2 u继续0 R5 N  K+ g: Y' k+ F6 d
加油; E* y3 U! v; L0 ]0 C: x
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?# Y3 \  B/ R4 ^5 ?- D4 n% k; r
有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。" O6 C! S, ]; J6 [
还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。( x/ c0 D4 w9 ]% _1 q- _0 u

1 X  \! u- Q1 [' f5 ?: E支持!
回复

使用道具 举报

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

& T. O% s4 q" R小弟还在学习阶段,目前的目标是知道执行的流程。
' x; Z: M9 h2 H5 y6 r这里面有很多细节没有写,能力有限,只能自己知道,不能表达。  p# p% ?" y/ s; s4 x( ?4 D
嘿。。。。
% _* w4 o9 ?; {( s! S所有大侠们如果有好东西能给小弟共享一份。3 \0 J$ P* B' J! k

$ {9 {; T6 f& y& j谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 ( |7 j8 g" y3 P
  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
$ O/ h5 k) v- T& d中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...

* G  \# l6 f2 u* B1 `' @8 X' n. Z) e; ^. ?7 W
PPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol,
7 W; K5 d7 D5 c9 w' VFor more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".
2 I; V' }: s) \$ e* R0 @
2 m% o2 Y; t$ [7 I% L3 c$ C[ 本帖最后由 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:
* R/ t& ?7 [8 u6 |( \1 U4 T8 b4 B   Yes. I make a mistake.# M# h* |. F. K2 F0 J0 ^3 j
      PPI:     A PEIM to PEIM interface.; c/ C1 W1 @# Z0 {7 b- {) U0 y" l
      PROTOCL: A Interface between Hardware(or firmware) and software.' m. K* X) b( D) t, x" i
      reference[http://www.biosren.com/viewthread.php?tid=207]
  [9 n$ O- E/ ^& x2 }- L6 h: h   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.$ t/ k" c7 s# `( P7 l0 J; _3 P
   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:! u' R7 J1 Q4 W4 \8 L( y
     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。# R+ O$ A. }5 B6 n, }
接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。
9 s/ J" h& }: `8 M. D3 S   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
+ Q2 [2 I; M) r9 p) }, \: H中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。
+ j( }4 [7 T2 p# |   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.
2 V' l5 F1 E! u2 t2 W# R
4 k+ Y; K9 F+ |- ]0 u
There are mistakes,
3 M6 }$ r7 q- ^$ l1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.
! ~0 C1 ~) y) W9 d
! Z, F% M' u7 M8 u# h/ Y2.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.. g) _7 h% c4 _
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.# @7 R3 K) h6 H0 W* t
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().: p0 n7 ^2 Y: R3 `* }  M

' s2 G/ D* w/ i  x5 D" z  `' I4 `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 大侠的指点。
) d8 Q$ n. _+ K7 Y
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
' Q( ]* X( h2 }4 h" |  N...
+ e* h7 g$ I+ 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.2 \, V( E% I( B3 M- s5 ]# d# w5 i
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
! c: }3 w/ B/ n5 fFurthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
& [% |( K$ L' A* k- K! R9 D* q. h: B; a
    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().
2 k9 o1 {2 n6 s! Y3 Y, d    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)1 w- z- {/ d- |* l4 s
  //' i2 G9 |" L! V4 L
  // Go connect any handles that were created or modified while the image executed.
6 }) D6 I1 I# X, f, e' e$ N  //+ Z* q( ?0 g  @; u; N& d
  CoreConnectHandlesByKey (HandleDatabaseKey);
# a% l4 ]0 N6 X3 m, }* |: P. Y
这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey0 O# E6 o  i( r8 f( U& d
而CoreConnectHandlesByKey会调用到:
* V, H( n+ K$ d" t3 ]/ l( e; }+ v  //
' V- k6 p  ^$ h* V; P  // Connect all handles whose Key value is greater than Key" ^  {9 _2 ~7 ^- X  k& E; R# |
  //
- \" t, M* j& t4 L3 e/ s( O" N  for (Index = 0; Index < Count; Index++) {) W) e$ c# Y  t% A- E& ?0 N( {
    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);; ^- i, n+ b$ J
  }
4 t% d2 O9 N# p; M
所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)
6 o& D5 j+ l( t$ H; X9 A1 H是会去ConnectController的,也会执行对应的Support()和Start()才对!!
' n+ \. C- A8 F  C# M' k# `6 o) |) Y6 x. G" |8 t* n2 e
不知道我想的哪里有问题???欢迎大家指正.
5 ~# N& S; C6 }- r0 t  v4 B* d
; t6 _1 q+ U8 k! @7 F$ [[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。2 `, V3 Y7 A( t  R5 L* k/ g& y
一个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 | 显示全部楼层
了解了.
* H: G' ]- t- J4 v/ L非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时," |) n3 I7 k' k
Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,
& k& ?' H8 s6 k& ^# `//
; H: Q) _9 i, R) q( h// Notify the notification list for this protocol.
+ S+ A! M, w6 ?! W0 W9 V//
/ [! c+ W0 k4 p3 hif (Notify) {' u+ {1 A1 u2 {2 B1 R0 [
  CoreNotifyProtocolEntry(ProtEntry);9 ]) k. F0 \6 w# u+ ]$ F: ^! L8 f  \
}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.# K. S( e# I# r. Y3 T
有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表 ! u' S+ X" c2 P( ?- J% Y
请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?5 v& O6 y+ K. v
谢谢!
4 a3 H; s* ~2 x- E3 o
......
; I$ z9 h5 B! Q' v9 a
/ M$ e5 e( B4 A7 X5 K1 R
6 N6 [* s, z9 k1 [: ]! O9 [1 S[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表
/ W. z  y1 M! r& Z" m0 t+ r$ i' W/ g  w
......
/ b9 u( F2 C9 b. w0 I
% y* f# Y9 ^8 b/ [) ?' f) V
Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?3 H1 d* `9 g6 O: R- F) C# v3 Z
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()1 J* i0 z6 I$ ]0 ^1 f+ d( ^$ M) ?1 r
TPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-7-19 21:25 , Processed in 0.208688 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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