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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。
" f2 }0 N$ Y7 o4 A( v( `
* k! _, u: M1 J7 p. d# vSEC/CEI:
/ A( `; k) G( |& {+ M. B1 M- _  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。  @; t+ |) z# m3 z4 }# @8 ?
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).+ w$ `% w# ^: `3 ^  s$ Z* H
! L+ {2 n( K0 W# a& P% c
PEI:
. K2 w6 }- W8 i* B   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行
2 a( L6 w7 o% }  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。& z; f" B. a$ a) w; d* e& F
      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.( p- \7 S+ q0 V2 t, |
          InitializeSercurityService函数,将Notify队列清空。  w. l3 H2 U5 ^$ c( N
          InitializeDispatcherData函数,将Dispatcher队列清空。
2 i/ o1 x+ r# B/ z! ~( D  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。9 z1 h7 S5 W; }% b7 @( W
    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:1 V9 W1 U+ x0 Y5 Z8 h! h
      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {. M) |3 x2 D( \+ W
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},! V. y" H7 E( p( e8 K- e
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},7 E1 D- d) Q2 o& p0 @8 @- u& P: L6 ~
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},
  v0 a+ l' G; K" Q' @' F       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},
; h7 r1 h3 P7 k- ]# d9 m       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},& D, Q( w; t0 b: O$ S
       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}, ?- z* n/ C/ R9 y
     };9 M- w5 D3 Y6 Z+ V2 }: n
    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。/ M( u+ E; e& j' M6 A% p
   这些PPI会在PEIDispatcher中用到。
6 h+ c( L( v0 E  }5 ~   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。
6 u0 \; D3 \) l3 r   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
% C% D# H% H  T  j7 E, B   SwitchStacks (
" x1 [8 x  ^8 }* Q0 e& E       (VOID *) (UINTN) DxeCoreEntryPoint,& r: W9 j* }5 S1 f
       (UINTN) (HobList.Raw),
0 Z  F1 C2 U+ d& i9 V. E( t       (VOID *) (UINTN) TopOfStack,+ h# H0 |: [1 t1 Z
       (VOID *) (UINTN) BspStore
. S/ T# [! X) f( `" C1 ~! ]" L+ @    );% @: H& K9 W# D+ ^$ z5 i% M+ v5 j
  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)
7 g9 E' m* e& Y& i- @6 g$ l$ i8 q+ Q3 _) q7 H5 B& V3 o/ c4 N
DXE:6 j0 A6 i6 D2 ^9 ^2 l( O
     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。: @/ {; g" Q( ?
接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。
+ V$ ~7 v- x- N1 P- x' `9 v   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
2 z6 b4 }& q4 i中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
8 A9 l/ @/ N$ P   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。
' x; F1 Z! k% }* d   
. m( f& O" Q* L$ Z4 tDriver:
/ C8 T/ H6 {  w+ T. s    我们的驱动什么为在PEI和DXE等不同阶段执行呢?' u9 B: U, X$ ~, b
  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
) z. M# p9 Z/ C; ^  [defines]* N% l; X% S0 i, `3 _, a1 g. D, a4 X
  BASE_NAME            = OWEN1 p# @0 b% r3 \! l( m0 u
  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110
. N5 T8 r- i$ ^* Z; n8 o  COMPONENT_TYPE       = BS_DRIVER1 s/ R  O$ {& w$ G& N" L; f

. a. V0 @" Y& E" ?$ [+ T- y3 H: O  BASE_NAME告诉编译器最终生成的驱动的名字。2 @: G2 |  i9 |2 E( h7 J; [: }
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。  R5 @+ l8 k5 T; b% _
  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。3 M3 L/ D' m6 S0 R3 b" a) K$ `% Z
  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名
* f9 g7 {! ?# x: b4 _" G$ u  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {
  k1 E' n: s0 C# h  {"bs_driver",  ".dxe" },1 B( i( T8 h# {+ }1 o
  {"rt_driver",  ".dxe" },% S8 U5 y6 P5 ~) E6 u7 g/ S
  {"sal_rt_driver", ".dxe"},
7 y: \2 r- ?3 Z; f" A! L% {, T  {"security_core", ".sec"},& f% a9 K, r8 H! \3 x
  {"pei_core", ".pei"},( e+ ]0 Z  S5 t7 `1 W: b* F' T& g
  {"pic_peim", ".pei"},
  p0 o/ b% v. \7 z  h$ u" I2 N  {"pe32_peim", ".pei"},, ?: p6 K+ w/ a! o5 J
  {"relocatable_peim", ".pei"},
. }6 l# O; b* u6 i  {"binary", ".ffs"},' r4 u( X+ _1 O' Z( \
  {"application", ".app"},
6 d! N1 Z2 v: r2 V  {"file", ".ffs"},
3 K9 S) E5 A% c- T5 y  n  {"fvimagefile", ".fvi"},4 i, i- ^: r. {9 L4 I
  {"rawfile", ".raw"},+ C3 H; U) @, f( C$ \: G
  {"apriori", ".ffs"},
7 z9 T/ D; v8 j- d# ~. m6 \  {"combined_peim_driver", ".pei"},% c1 L/ D! Q. G1 F+ C
  { NULL,  NULL }; W7 a  `& J. R4 P- Q3 B% ^
};
' n6 T& {& ]! ^: G4 p" v2 t9 [, C0 O5 N% K8 s+ I
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)' w3 p0 e' _& C% j' |+ j2 J
   ( q/ D/ Z: `( V( w0 w8 ^
         
: e' c( N; C* R# b  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
6 O: O4 N3 i' U支持5 y. ~0 ~" ?3 V( N& \$ C
继续# M' R; T4 o2 o
加油
& Q; X+ g. i8 d! l/ J) X  l; k; d7 j
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?
" L+ k/ L  E5 m# {有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。
! ?- h" D4 g2 X( L6 a5 r6 N还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。
) q/ l9 r9 [, `* ^7 H5 c4 u9 A$ l6 j) B; \/ ^# w
支持!
回复

使用道具 举报

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

0 h: D0 w) J, y. a* k小弟还在学习阶段,目前的目标是知道执行的流程。5 P9 l. O' C2 s' P
这里面有很多细节没有写,能力有限,只能自己知道,不能表达。3 |- M9 n+ m3 |% N1 K
嘿。。。。
" e' j: Y$ I' m& M1 S8 x- j- v所有大侠们如果有好东西能给小弟共享一份。% F5 \; `! o. X
( G7 s' C. t0 _1 W. V
谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
1 L( L' A: \) P$ S  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver( [3 o8 c3 q$ }' O; m9 k) E
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...
% h+ i1 P' T! G, r+ |
8 ?/ J5 {6 n7 b  b" P
PPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol,
- {5 N3 d9 B6 v3 cFor more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI"., Y* Z* ?: i* B1 y9 L

" k1 v4 ?! {$ E) F[ 本帖最后由 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:
* J! n3 ?+ E, G& `2 K2 |& I   Yes. I make a mistake.
2 N! O: E1 M6 i6 [/ `4 c      PPI:     A PEIM to PEIM interface.& x5 t: g2 a: z
      PROTOCL: A Interface between Hardware(or firmware) and software.
8 D2 `6 j/ t  M$ e# L      reference[http://www.biosren.com/viewthread.php?tid=207]
5 {: I6 L" A! e( k* x   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.8 `2 N1 F0 I" W* F! I; \+ }- d9 }
   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:
* R/ t) x! c1 _, J0 ^* i; h6 r     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。, s$ T3 e( n8 Z$ {8 A3 ]7 \
接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。
/ p1 l6 e" q% i' P$ X$ g: m   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver! H& }" {5 ^+ s3 @% C
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。: Z# _- g# o: ^: {
   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.
, ^( G1 z4 W5 i: T
+ Z1 C7 B6 [" B* F* |; s
There are mistakes,0 c% n% d3 y1 j0 U$ O- k
1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.
; T3 ~# S. I& I. I
" v8 W2 v! i& J0 m8 @3 ?# ]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.
6 f9 {, D7 U, ~. gThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.. f( A6 }$ M% K# t" X
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
, Y2 H& J, |& e& p! t, [% e2 C% i: K! y; k% N+ E7 e
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 E3 j; e0 r
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
$ A' o3 }8 o# x- f; t! I...
$ Z1 \1 h) X2 @3 ^- r2.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 J$ w% z$ z3 G/ M
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.3 e, ]7 G0 I  ]7 b( ~+ n
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().

% Q, `# h, K9 u( b4 H& r: i    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().1 \7 w* F$ k2 D
    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)6 G5 m, h2 k5 r# A+ d
  //
' L" n( i7 _2 B& _  // Go connect any handles that were created or modified while the image executed.+ U7 [% T" n" ]6 t4 d9 r( K3 r
  //
/ z+ U/ x! t# C* G+ T! E7 V6 A  CoreConnectHandlesByKey (HandleDatabaseKey);
& G' \; c; t( o% {9 q. L, T: i8 H
这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey
9 [% z+ C0 F+ C. g# x$ o% A) U而CoreConnectHandlesByKey会调用到:
- T  S0 V9 C) x+ o+ q% j  Q( l  //
) k/ ~, f) S; }% I# \5 ?. w  // Connect all handles whose Key value is greater than Key
  P; \' n5 `8 d2 u, P4 T! p3 g  M5 ]  //1 x/ S4 l' N/ ?/ a' G
  for (Index = 0; Index < Count; Index++) {
( d  n  {4 q9 H+ j1 C    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
4 J& j5 M, t8 Z$ ?$ m- `  }
3 A. U  Z8 U) ~2 {
所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey): F6 o/ n! K' p  P
是会去ConnectController的,也会执行对应的Support()和Start()才对!!
9 s; D+ \! f( h* o6 A/ P' S1 K
3 p2 v1 Q. p3 K9 f- W9 O% H不知道我想的哪里有问题???欢迎大家指正.
$ M2 r* z- J0 r0 F
7 U  c4 K+ s+ f% d9 o[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。- z1 E, i  K6 y' _) 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 | 显示全部楼层
了解了.# }& }* `2 h+ G) N
非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,* w' v2 Y8 C2 e9 `
Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中," t) p4 z* J8 H( \  d
//" r, w- C! X8 Y
// Notify the notification list for this protocol.3 ~! Q, K5 |, ?6 n; {% Z) x
//
1 J& k6 ^- a% cif (Notify) {+ n+ Y% r; G+ b  |1 r9 p
  CoreNotifyProtocolEntry(ProtEntry);4 s% T# d* m" H, Q& k
}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.
% |- J/ d! l, w, ^. ?有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表
) C( {3 L$ D; n请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?
) E# I% e1 |( J* {3 l4 Y谢谢!

( }5 R4 G: v: c6 X: K0 @......0 f8 V0 S8 x& r. V1 C
, R! m; ^% O- y
' ~% J+ u  @% t! U* A& q1 L
[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表
! U9 \0 |( n  E* a& g; [6 q: u: _1 `
......
3 L% `1 {8 @0 Y  W4 r+ B- t" D

$ z" M( E1 s  W; P: m5 SSignal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?( a: T6 [/ h( U0 n
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()
+ Z/ U0 m7 r* h0 X. HTPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-2-1 12:04 , Processed in 0.248757 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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