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

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

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

" p" a) a( S9 y& I$ YSEC/CEI:- R4 J2 Q* }* F0 o
  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。/ o5 {% e4 l* D0 i8 {; O6 x; ]
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).
1 f5 o+ @% J+ i( m4 S4 D: W+ f) G" b+ F
PEI:
5 }% K" S4 Y% N' z( ]& h/ r1 K8 v) T   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行
2 R$ k) c+ \  j' p6 x  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。
$ a$ M1 }! p1 n. a3 T, [      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.
$ b$ C( @5 Q, S( A8 ^0 C          InitializeSercurityService函数,将Notify队列清空。# |( t/ F/ u- m& |: O  E
          InitializeDispatcherData函数,将Dispatcher队列清空。
$ f, [5 ^5 Z% M/ D9 B: ]" C" B# x: \  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。
- W. \! N! W1 D; E8 V    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:
6 b6 o! @6 p+ R      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {
9 S$ ], K/ J' o) c  P       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},) @2 X  f2 @: r5 m+ M0 n
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},! B! K& N! t1 b
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},& v' G6 m- d/ I$ V
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},3 k: ~. ~2 x: S
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},
" R- ], L- ~! |& g0 A6 K( A       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}
) f' B- L3 a) W" l: e/ d* w: `/ H     };
& T5 `0 Z8 ^7 v8 ?' Y. a    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。/ x6 p1 R3 c; I$ V
   这些PPI会在PEIDispatcher中用到。
" Z3 a' i) o7 U, o4 \1 r. v   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。
/ ^" v$ e. A' G& _& l   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
  Y) g  v' P! P: B- E; C9 Y- S   SwitchStacks (8 c( ^# f% D7 F+ [- ~5 t
       (VOID *) (UINTN) DxeCoreEntryPoint,; ~% o% |4 K% C1 b1 J9 n  P: k
       (UINTN) (HobList.Raw),2 I1 P" v$ v! K) V, d  K+ B6 e5 j  F
       (VOID *) (UINTN) TopOfStack,
$ u) @) {. E3 j8 E, O4 U       (VOID *) (UINTN) BspStore
. V3 j- X, g* Q1 l4 R; H7 a    );9 n, Q6 U& ~5 x8 p% [
  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)( |, m  E1 W5 Z4 m( _9 `

$ E4 d6 r" ~$ ^1 m( sDXE:% L% p2 {2 n8 ~# y# F8 h
     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。- m" u( _: @: V
接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。: k8 O7 H% P& h$ }
   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver" j; S) u/ ^- |! @; d: _, R
中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。& }% A7 K2 _0 R7 T1 B  y" W( A
   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。
- J) E2 C1 z" |( N   
& n5 g+ g- f6 V: [9 s! j$ u2 X4 S9 A' hDriver:
6 M* \' q. C) }+ _    我们的驱动什么为在PEI和DXE等不同阶段执行呢?$ H8 H% D& B& R* B. S+ V# z& V- G
  大家请看一下我们的驱动的makefile.(EDK中的*.inf)9 M8 f& N! `! _7 J- F9 ], q
  [defines]
* h2 I; [; m2 O9 S+ Q+ J0 v3 t& ]  BASE_NAME            = OWEN* `3 ^' S5 w4 d$ h* ]5 n- c
  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110
7 m# A4 M$ o% L( d1 l  COMPONENT_TYPE       = BS_DRIVER: b1 I) G* o! C+ v2 E; p7 T* d' V

! A; N1 I- X/ H; J$ s/ j( H  BASE_NAME告诉编译器最终生成的驱动的名字。+ {: L: z: V1 E& }* n4 q8 J, v
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。; L, g( v: |" C' h: ~, J
  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。; l. x" o; M" e) Q' ?1 o
  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名  o# H' y* [$ {
  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {4 A& ~$ s/ C5 G4 A
  {"bs_driver",  ".dxe" },
! g8 B+ B- v. B6 j: w3 t9 p/ y  {"rt_driver",  ".dxe" },5 L" x4 i5 \1 |. J4 @
  {"sal_rt_driver", ".dxe"},
. L. V6 w3 \, M( M  {"security_core", ".sec"},
  }) \" T9 ?- m, L: L3 T* z7 `  {"pei_core", ".pei"},' o$ O3 W3 @% t+ q7 _
  {"pic_peim", ".pei"},& s" e8 G# S; e) W, K
  {"pe32_peim", ".pei"},2 ?$ U' t) x1 M8 ]" o* \7 j
  {"relocatable_peim", ".pei"},
+ q6 q& b5 J/ i9 q6 ^# u  {"binary", ".ffs"},0 y" f1 _3 Q$ [' U
  {"application", ".app"},/ h. j/ |- {5 f, M1 C4 B
  {"file", ".ffs"},
% ~# u0 I. O' A" b* k  {"fvimagefile", ".fvi"},
' Q( S/ f& `$ {  {"rawfile", ".raw"},! \, p5 X, c# j0 y
  {"apriori", ".ffs"},
2 d3 R- t7 g+ n) p5 J8 G- R. ]. d  {"combined_peim_driver", ".pei"},
$ Y/ |" M- f# e, f- \3 x  { NULL,  NULL }
" N1 M; m* z& W! k  \};
: w  D8 C# q4 b6 ~: [% p8 z: S/ _  _7 y
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)
  M5 d# X& L+ k8 W$ D/ L% K   
# @. I. Z. t3 @' d( X  [) Q$ H         
4 ~/ S% W5 m) Q6 q( ~& ~  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!/ c* w/ a& C/ y- y; J7 J0 d. b) I
支持& L0 @3 c0 U# v( M5 Z. f- c* X
继续
" D  T5 ^1 {. |+ b' t加油
: ?9 j8 e# w6 a3 j
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?  a: e3 \7 W/ q
有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。
/ I7 y1 T5 U( S# \& o" g9 O9 U还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。% K' i5 R. O: A- ]
9 {% I' v$ b; F
支持!
回复

使用道具 举报

 楼主| 发表于 2008-7-28 18:52:31 | 显示全部楼层
2 M7 \: J4 L. l2 H  t
小弟还在学习阶段,目前的目标是知道执行的流程。
. S8 I" S; g) j/ B4 {% c这里面有很多细节没有写,能力有限,只能自己知道,不能表达。
# j8 `2 d1 _: V- @嘿。。。。, S3 Z  Q$ ?1 M$ S6 @# Z; {1 }& T
所有大侠们如果有好东西能给小弟共享一份。
6 _/ s" b+ n6 x1 t% i: r
/ g& P$ W! u5 o8 R谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 ' {% U9 b7 m% c. |
  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver0 |5 @3 F" c6 \
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...
) Z  l# v/ r6 G: M' y: s. n9 f

- O6 X9 k- [. z& O/ F9 A* sPPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol,
+ R3 v- Q' E( b+ hFor more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".
6 \0 _$ H% X5 Z# s6 I1 z( Y8 o
6 l& W7 A& ~( V; m, V[ 本帖最后由 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:
$ Y, w% j+ I( x1 v- E   Yes. I make a mistake.
* [- Q- i4 i2 _& r9 ~) {. T9 Q, I2 U      PPI:     A PEIM to PEIM interface.
* V" c( e7 g2 O      PROTOCL: A Interface between Hardware(or firmware) and software.
2 }6 y/ Q# t' z- W! C$ G      reference[http://www.biosren.com/viewthread.php?tid=207]
4 H5 P3 V# J" _/ a( T8 ~, i   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.
4 K) q2 z8 _, H$ ^. A- G: s   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:
. Z6 w# d4 `. N9 L. o/ ^: P9 t     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。
3 p8 n2 o" W( F' f0 L+ U接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。
5 p. u; c( Y' s- h+ G# K   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
0 ]* ^0 t) i/ X8 \! B5 L3 F; c中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。
  l2 v* s6 U# b3 _4 H4 [9 ]* _   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.
1 ]& M- R: n, J  S! {6 F

0 h, ?; t1 b/ N+ S! cThere are mistakes,
# r& O# ]' i4 a1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.
( _/ N# \+ [" }+ h; t# J5 {7 g- E# H2 `2 a  s1 }
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.
9 q3 {5 C& |' d! D/ _The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.( u9 z+ Y7 U' b
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
0 M  Q" P, r) R& g1 _2 R6 I# {  S. ?. x
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 ]# s- V* Z% _) v3 ^6 e
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
% Q( w* }1 P1 ?8 A( W' Q...
( i# K' ?+ B3 a, f  _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.
8 {9 D+ j) i( ^- lThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.3 R. D1 B* u. M$ r
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
% z( d  N1 X6 \7 S% h. o& Y
    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().
2 _2 P/ M8 Y4 L, M1 z$ n* n# Z, [    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)  p1 R1 \# w, [
  //
+ B( D2 t- k. c# p  // Go connect any handles that were created or modified while the image executed.
; E$ ^$ s/ L8 @# T  u2 z* i6 S% k  //
* s( o9 m) x7 x9 @7 n0 J  CoreConnectHandlesByKey (HandleDatabaseKey);
, x) p2 n; F8 I0 L, d
这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey
  `; w1 O5 K6 P. V而CoreConnectHandlesByKey会调用到:9 M+ J* x: H1 K( G
  //; Q& e" v* a# M" n& |5 v
  // Connect all handles whose Key value is greater than Key
0 X! w. e2 q% C* m, @3 t8 y  //8 P' I7 _' E+ f! i; D
  for (Index = 0; Index < Count; Index++) {7 Z8 X1 P8 r# s4 O0 v+ m, [
    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);# U! I: T% J+ i+ a
  }
$ \* S; P9 t* `$ k
所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)
* P4 Y$ D& q. o6 P# n, @- T是会去ConnectController的,也会执行对应的Support()和Start()才对!!& c+ `/ r' ?( V2 c8 ~' L  p$ g

0 X8 P) o2 a6 ?5 a1 c不知道我想的哪里有问题???欢迎大家指正.* m2 Z% n4 s* G# r, S1 i- f
) P4 G$ x/ v% g. x
[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。
5 d2 c! d' L9 E. I8 x- t一个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 | 显示全部楼层
了解了.
+ w) n# ~' O/ M5 o5 u7 {非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,! @" D" q7 S# ~  l
Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,  f6 d$ N6 }* m! M( S; h6 C$ I2 m
//
2 }, a- [+ u3 M+ B  p( z9 B+ P( W# G// Notify the notification list for this protocol.  G; \- n0 V4 ~! S" \1 n, Q- B
//
6 p& Y3 i5 @' G$ L" W: X9 {9 xif (Notify) {
/ |8 }0 V6 I3 X1 E3 J; n$ D4 |2 g  CoreNotifyProtocolEntry(ProtEntry);
# T8 }6 E$ }1 k0 R, i8 v/ F$ @}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.
$ B3 a2 Z, C- W6 `有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表
( ]5 g9 N8 r) i& ^2 P0 c2 u: ]. e请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?
8 C% I6 T5 |0 \" ^9 `7 ]谢谢!
/ M: M, A& w  M( T0 |$ n6 y+ A1 Z
......
  Q$ I3 ~# ]' S7 M$ Q% H$ }
9 F" l3 _7 R/ T/ I8 ?& }( C3 o0 H7 B8 e
[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表 7 s8 \" \6 u3 |
3 [) p% Y( F: ~% H3 p# }. }5 q
......
% t0 T8 }. g2 @6 b- y: S
# n) q$ p" p; c% `
Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?. D; n3 \9 h0 s$ D
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()- z. E3 h  p% s: l
TPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-12-24 01:10 , Processed in 0.058474 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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