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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。& ]4 o8 V2 Y9 Y; o/ b
5 K3 Z% C/ n4 m, Y1 r! Y
SEC/CEI:2 E) t  O4 P# f6 c1 l1 V
  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。
7 I' Y9 D  G) D$ V" Z- c6 _在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).3 h2 W% X7 t0 J5 F$ V5 \* }

  |9 ?- C, t$ F3 U  ePEI:, c; E2 ^( C6 L# }5 [* P
   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行/ c! h1 m& t2 V; S
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。
( ~% {; X- _! A1 y      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.
# s& ]  Z2 p2 O2 ?1 c* a2 h0 V          InitializeSercurityService函数,将Notify队列清空。
* n% w4 }7 C8 W8 s0 f  P! \          InitializeDispatcherData函数,将Dispatcher队列清空。% `$ H4 x, P" P9 }9 `7 }
  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。
% x+ j2 B3 w6 Y# `8 h) O    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:! z( E/ j2 V$ i6 L4 j7 z9 k
      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {% j* ^+ A" q: L- v: I9 B' G
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},
( E7 R# o" E( @       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},
1 g/ s( X1 E. {( Y) H3 x. g       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},
3 `( U" |! n6 E. |) }8 q$ a       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},
; x) B* u1 s+ o$ I0 t3 X7 Q       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},& F. C% R5 E( d( v4 W9 }1 q- [
       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}
0 i2 a$ s; Y' ^8 X9 G     };
4 j6 [/ z; |  `( x, t2 E    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。1 ?# G, ^1 M" f  {$ H/ I
   这些PPI会在PEIDispatcher中用到。
2 t6 ^- U# P9 e. ^& L% U   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。
! x" B# q* y9 Z' d2 g* _   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。( d, }# g/ E" E% _* @: L. Q
   SwitchStacks (
% Y; J# U8 X7 t6 V       (VOID *) (UINTN) DxeCoreEntryPoint,
6 T& _: G  y  E       (UINTN) (HobList.Raw),; v- M8 m! u- @/ j$ q& Q% y( i1 G# j
       (VOID *) (UINTN) TopOfStack,$ `# W/ m+ A& J8 l
       (VOID *) (UINTN) BspStore
1 o( u. n" m& M8 `6 n% a    );  l4 v) M. Z6 N6 K
  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。). q. e" ^( ^1 S! m! N' n

6 @3 p% ^" m( j; x: _+ ^. lDXE:* n# B) _4 j! F  d# r3 k
     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。
2 ^" p  H3 C; |1 m接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。
$ a, n5 l5 r# C5 o   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
- t& c+ \& ^9 I, J! ?6 A中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
5 L* y1 F3 ?2 _, }$ }   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。
% I: @2 t. U- }; Q$ h+ ]   
4 {- \6 ]0 k6 n6 v: BDriver:5 O+ p3 v1 a0 p( M
    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
$ K/ v' c$ X& k8 ]/ [1 [  大家请看一下我们的驱动的makefile.(EDK中的*.inf)! C3 n6 N$ A0 [" v, A
  [defines]8 S" W+ G9 ?% B4 B) A
  BASE_NAME            = OWEN
+ K! J% G8 @% p1 r  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D08301102 w  B1 Q, b4 j- M; G) k
  COMPONENT_TYPE       = BS_DRIVER1 Y" p0 @7 @9 R, v& C0 u; h
- {3 U& c3 j1 Q- [3 Z' O3 \- h
  BASE_NAME告诉编译器最终生成的驱动的名字。, t8 i' k" p2 k/ B7 T& s6 z
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。+ z/ x; w- N/ b  d
  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。4 W. p" K$ O! j" P  G5 _  v& x
  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名6 W9 V1 o- ^4 u% z: q
  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {3 }: G- J' q5 ~
  {"bs_driver",  ".dxe" },, Z: c# C9 E5 B
  {"rt_driver",  ".dxe" },* o* m. D8 s: q9 @" M. ?) L
  {"sal_rt_driver", ".dxe"},: T+ p( L0 j5 V( a$ K5 c  a
  {"security_core", ".sec"},7 v7 u; O( ]8 o. T  E9 u1 F
  {"pei_core", ".pei"},3 N; y' \! y% e) O
  {"pic_peim", ".pei"},
5 C. G: k" o# X7 J# }& k9 O  {"pe32_peim", ".pei"},- L. v( \4 A: G( v% ~
  {"relocatable_peim", ".pei"},
! Q7 S  ?5 u: C9 l' z, u3 J6 w  {"binary", ".ffs"},3 d) P% ^( r* J  _  e
  {"application", ".app"},; S; V' j/ O$ l) \" h
  {"file", ".ffs"},+ E# m9 x! \# t0 K
  {"fvimagefile", ".fvi"},
% R: w) W+ j* \! W  {"rawfile", ".raw"},# J5 H4 f0 ?9 R2 f$ g  O
  {"apriori", ".ffs"},
) z4 H! E/ C4 H& u/ o; \  {"combined_peim_driver", ".pei"},# f* \0 D; Y4 J, W" H' t
  { NULL,  NULL }* E  n# m. ?: t3 P
};
! {9 L' ~! w! Q" f4 s+ ^! A; ?3 J' v
4 p2 r1 ?% Y7 p) y' |了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)2 i: c7 O0 x5 B. A# R# L5 `* j, ?
   
# S0 ^$ C2 Q# P, [) p- x1 o         % F% c# `0 T9 f, R5 g0 g
  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
$ h, s* u* Q4 u& T% }1 V' t支持7 j/ _7 E4 ~$ Z
继续
# u# ^! u4 r) [) t( K* c加油- z$ n7 O: h9 w+ Z1 ?: `$ R5 v
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?/ F: k  d/ h! B/ l
有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。( d- b; G; d7 ~0 h7 P$ O
还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。
  a% f, v+ H/ k. V% R) L. x$ r( y5 j7 ]0 t8 i
支持!
回复

使用道具 举报

 楼主| 发表于 2008-7-28 18:52:31 | 显示全部楼层
  N# A9 d) z9 d  D
小弟还在学习阶段,目前的目标是知道执行的流程。
, O. f5 d; |) A2 H这里面有很多细节没有写,能力有限,只能自己知道,不能表达。0 l2 m2 ]! J8 j9 t: A  e
嘿。。。。
% T0 q* W8 E4 Z- x. y所有大侠们如果有好东西能给小弟共享一份。
. _; R2 [: Q5 e$ ]& H
1 e+ b; G# P' Z  [8 T谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
, t9 S* I  E+ ?' {; r5 @+ N7 \; A  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver. ?; A# ^2 ~) ]# _+ }- g: V3 n
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...

3 k$ q3 z' V. B: _5 g9 C3 [! X6 s- m8 j! [& ?
PPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol,
1 h6 u& L+ S) B" m& S. X) uFor more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".
7 Y6 D$ L5 r+ g! M1 x* e/ t" u' T4 W+ k7 Y# C8 V; h9 g
[ 本帖最后由 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:
9 T5 k1 t/ P* f   Yes. I make a mistake.% I5 P1 L4 D6 x. F8 i
      PPI:     A PEIM to PEIM interface.+ B7 `7 |( c& u2 O. |: o
      PROTOCL: A Interface between Hardware(or firmware) and software.
; ]+ r6 S6 ?$ i/ l3 ~( Q! e      reference[http://www.biosren.com/viewthread.php?tid=207]! S3 I* [: l" |+ r; {4 @- o  g  p
   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.3 K% N) A0 w6 x, K
   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:
  ?5 c2 B* [* j3 \2 P  ^     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。
7 v- B& Z" z( F% r* u接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。
' J# \3 w( T4 a   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver9 E8 T% a/ l: k" @# I$ Y- B
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。- `5 ~1 c$ a2 ?: s
   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.

, ]' ]; h1 j" ?7 c/ i& y- H7 A3 _( ?( r2 c8 w' C
There are mistakes,
8 Q* x& Q& U+ e0 z0 d% a1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.9 b0 u, I* Q9 ~4 k( C$ j

5 F, \  {, b' }, s, [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.: B$ c+ c* L. b7 g* [% T
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.3 Z) V1 }5 @! o
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
# s( o, `  I& A
3 K* s, {0 N; tBTW, 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 大侠的指点。! u' c; T* i) p, q1 B& M2 P
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
# ~% ]  C8 R( Q! n, w...
  q  r  ]1 u  g/ J5 @- 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.$ v; [/ A- d- G: m' }
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.$ Q" @9 i9 F$ M; h9 v' z
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().

1 D3 H. m" q& m. v: q    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().- K; {7 Z4 V4 G/ i; k% ?
    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)1 S+ N2 R3 L  o6 z' v4 O, v. w
  //2 N( g' ^. d) {! a7 g4 A: H
  // Go connect any handles that were created or modified while the image executed.' T( I, Z# ^1 V( j, f" Q1 n. X3 v
  /// w$ s$ P7 _5 a  L1 H
  CoreConnectHandlesByKey (HandleDatabaseKey);

$ A! B* e8 C+ }  C) j2 u, x这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey
9 U. l# {9 `& Q- x而CoreConnectHandlesByKey会调用到:6 m6 [2 [! T- [$ d  e1 H
  //
4 g  y2 L2 f; D- `8 t4 O# b+ U  // Connect all handles whose Key value is greater than Key
. x) w) K) e( z& X' r/ n  //5 i% H" D, X' H9 {7 V: k* F
  for (Index = 0; Index < Count; Index++) {
4 U1 X' R( N! E% t0 G+ o( F    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
  n4 N8 @, x3 Y& F( D& C7 A  }
3 C6 v9 [8 f) U$ K- g. l" h. g
所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)
/ G# v7 r. i6 ~5 s4 ~- ~0 D是会去ConnectController的,也会执行对应的Support()和Start()才对!!
- Y& l9 z3 {6 n3 I0 B* R8 h! n
不知道我想的哪里有问题???欢迎大家指正.
; z) b8 s+ @, g* O" L4 ]/ S% m& h7 S6 X, W8 M5 D. j1 g
[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。) b2 v( K; E! i* `1 H! a  Y# j
一个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 | 显示全部楼层
了解了.5 v; x5 d2 u% V- Y
非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,1 d5 T" p3 K2 b  z3 w% a
Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,
3 \; y; ^) g+ d6 s  ^; _1 c//
! Q$ ]6 Z8 n8 |  z  y- y// Notify the notification list for this protocol.
1 b. K6 F# t  R8 p//+ L; S3 l. h! h1 R5 [2 m( v' [
if (Notify) {
+ f- S/ \; K2 T& c& A( G  CoreNotifyProtocolEntry(ProtEntry);/ c  C5 q+ Z; o2 S0 @
}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.
" }! w, o; d9 m) j, v- i有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表 ( e' l9 M- }7 d$ L1 F* f
请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?+ s: D# Y( D) R6 P
谢谢!

# D# y7 F- P: \......
# r( C( f/ S0 |* k$ `3 O3 n
4 J3 S: N$ X5 w  k; z
+ {* m: T6 L; ~) w; f8 I$ a) z1 b[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表 3 l& T5 v# L8 Y0 `$ E6 a
* e& O) T' ~( x- s7 I2 U
......1 k4 B, ]& x- ^5 u6 j
8 I; q& s* p, j0 t& O: t- K( H
Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?
+ S; ]" m& {( h$ ^2 j6 S如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()' N' N$ X/ E: X9 u' u, O% j7 E
TPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-11-4 01:46 , Processed in 0.150861 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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