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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。% U1 c/ g/ _- |- U; t% t
( H  G0 _& e( @! ]' i1 k
SEC/CEI:
6 u* t' b" ?! y' g' I6 q+ J  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。, T5 \% D5 d! X7 ~# N8 }( u# q( v% m! K
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).
: x1 L2 I* u3 L4 E6 [. }. i3 \1 C; P/ ]
PEI:
; P0 b# w9 [: E7 i1 p% e  b   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行. u. W) D; R/ L- y
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。! C, Q! U6 t$ d5 A$ P0 d
      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.2 m- a0 c6 z* ~% z% x* I! X
          InitializeSercurityService函数,将Notify队列清空。' [) O1 Y! `1 F+ U/ q& u0 y+ e" Q$ @
          InitializeDispatcherData函数,将Dispatcher队列清空。
- z7 p3 H2 Z; E# o6 }: p4 b  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。
* c; ?7 g& ?& Z    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:% C! `% y/ q7 }7 Y
      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {
0 F4 Z5 r# d8 ~; l) t; _       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},6 R$ `5 g, `, X0 L
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},) D1 E, z/ a% w# Y) u4 ]' [
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},
& w3 ]! @1 J" K. a( E% ~       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},; R" T: p9 K. y  V4 l$ _
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},
$ I; l* f4 a( o  Y$ ]6 F) _$ g" Y6 C6 Q       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}+ @2 J8 D4 M7 J  A
     };. l. F' M' B! Z
    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。
6 k$ f. q* F' b+ d# h0 m   这些PPI会在PEIDispatcher中用到。7 y+ ^  B4 a/ T' N8 C8 p4 t: {
   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。: b" \) B% o$ s
   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
7 T- P  o9 C  Y" Z   SwitchStacks (4 ^7 K5 l) h" [7 B# w" @; D
       (VOID *) (UINTN) DxeCoreEntryPoint,
4 y  y! g3 R+ w: o% H) g) ]       (UINTN) (HobList.Raw),
9 Y' K' ~+ {/ \& m/ p       (VOID *) (UINTN) TopOfStack,! Q. m" d- I- V7 O% z# v- f
       (VOID *) (UINTN) BspStore) r1 q$ ^$ K; k
    );  Z* q4 a# ^4 J1 n: a$ e  ]) y
  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)0 H" }0 }/ h/ e' k+ a; t, `( U
6 `7 |0 F$ i) e" i  U
DXE:
; L% @6 m( v3 [4 ?     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。
5 p1 y* |, q% j接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。1 F. L4 m/ W5 v3 M/ C9 g
   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver. o& ^( \2 M0 h0 {' r8 [& L; {( q# s
中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。# k' e6 O6 j- ~& U0 H8 U+ M$ x
   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。% Q& d* d. q3 z, S4 T! n. \
   / M& z- i, [5 i; o
Driver:
4 q. {, y, o9 q0 n, k2 }    我们的驱动什么为在PEI和DXE等不同阶段执行呢?9 }- e( j3 N( f+ D5 D1 ]
  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
* _' ~! X; W2 P  [defines]
$ @2 S. @0 u7 D% P2 {9 Q  BASE_NAME            = OWEN
6 y4 ?3 A- W$ V- R/ \: x  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110
5 N7 o' {& y9 d( \4 f5 E  COMPONENT_TYPE       = BS_DRIVER
7 b7 f: ^3 X0 N# u* i2 T" t" X# ^1 E8 [6 V
  BASE_NAME告诉编译器最终生成的驱动的名字。# N2 K0 ?: H2 e- Y" N
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。
' Z' q3 s- E% Y4 N0 |& z+ s+ @/ y. N  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。
  A$ Q- H/ P2 ^- G* w& e% i  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名
5 N8 [, o2 b( e' z, T& b  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {
. ]% U  w& I0 {& q  {"bs_driver",  ".dxe" },
& p2 L$ x6 H8 u7 Q% g  {"rt_driver",  ".dxe" },
6 w9 T" c- h: C2 a* Z$ _. Z  {"sal_rt_driver", ".dxe"},+ u* _1 g: ~) ]; J4 h
  {"security_core", ".sec"},
% h( Z& l4 ?6 L  {"pei_core", ".pei"},9 t  T# F$ E) E. k5 f) J
  {"pic_peim", ".pei"},
6 ]5 B0 L' v8 b  }- Y  {"pe32_peim", ".pei"},) \2 b- L& l) ?) f0 E$ N, l# ^
  {"relocatable_peim", ".pei"},9 ]6 A, s% y; i3 ~+ L0 U
  {"binary", ".ffs"},
0 ~6 M' A  z6 d+ R' ?  {"application", ".app"},
7 t& T# A; o* r  {"file", ".ffs"},
  M* b0 t+ t# U2 W  {"fvimagefile", ".fvi"},* S- w/ C) q; b) X( \, F
  {"rawfile", ".raw"},
# Z. t5 |/ ?! M6 O) x6 @3 ?  {"apriori", ".ffs"},* U' _' I9 w% @& \9 [
  {"combined_peim_driver", ".pei"},
5 S1 d5 C; w, O0 \# v  c( {  { NULL,  NULL }- @6 r! f& W/ b# h8 d/ A
};2 q2 A/ s' @: H! v6 ^
0 U; N6 o* u0 d6 m/ j
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)5 _4 ~9 _: }( z
   
& b% f+ L( R/ T- R( z+ P         
8 _4 g4 C8 M  z  c  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
! }0 j+ [% ?2 k. I* l8 B; c支持1 \) D+ P: l' {0 k+ _# E
继续
0 c' i% K6 F7 b1 \5 ]4 {0 @加油! R0 N1 `( J4 A5 U( v
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?! g8 ]# l: Q" j. _
有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。
8 \  n( Y3 y, ^9 F% f0 M还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。4 d6 v+ A3 L+ k( p" i

/ f* [) R- l2 G$ m3 ]8 @: x! {* r支持!
回复

使用道具 举报

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

0 A9 b  G) o2 O, j8 k2 Z小弟还在学习阶段,目前的目标是知道执行的流程。
' i$ ^' Q, B/ G8 Q( j这里面有很多细节没有写,能力有限,只能自己知道,不能表达。
; c( p8 d3 `3 V( v! @嘿。。。。: v$ N* x6 r# Z# F# r
所有大侠们如果有好东西能给小弟共享一份。
( I: `: L' P* @& f9 b- N 3 c( X7 o7 h- S
谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
6 E+ D$ M' l+ S, c3 _0 V. l  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
3 v! |3 Z# Q6 q中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...
1 J9 N9 H# H, R, Q; x" i
9 E+ L0 f9 N$ S  X/ j" L
PPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol, 0 Q; b& h. r/ [5 V$ k
For more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".) _3 z1 W( `0 ?# J4 a

! C: C3 v6 A. A7 N# \[ 本帖最后由 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:1 Z* m: i/ h* {& E
   Yes. I make a mistake.
) n2 w  t; }( v      PPI:     A PEIM to PEIM interface.
6 v% |$ k0 O6 u6 d" v* V6 N! f      PROTOCL: A Interface between Hardware(or firmware) and software.( J  t( |% B' R: e4 [4 w
      reference[http://www.biosren.com/viewthread.php?tid=207]
9 K% K& r" K9 D7 M5 {   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.! n9 d, k* O8 x6 E0 j0 n
   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:
4 W6 e/ C! X. e4 O' X     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。
5 ]4 h$ G9 C  t$ X) @- S接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。% j0 e  [' T- S1 S
   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
. }  X: B* i2 m3 l中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。; ~2 \6 O% q: V0 T. r
   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.

& N4 J: A5 O, k% K* }9 @; |( H/ T
There are mistakes,9 X% j# v3 U' l5 w& k) h
1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.4 O2 Q" O9 |- j; l  d  v  H% m+ j
+ `  V  |5 m/ v7 D: U6 `* J& Q
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.
3 [" f9 p, u) ?0 \6 m2 g4 Q9 b( V3 |The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model." }% N: C2 a$ l4 ?- y# J( ~% o1 t
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
' H" O5 l) y6 w; X: g5 Z2 N/ ^5 L" S( Z  S
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 大侠的指点。: q8 U* B# _1 }6 A4 u& h. R
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
) X' C) U: F, O  p...
0 G# @0 t1 ^: m: T7 {/ M' Z& 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.
- Z( b$ s! j" K% G& ?: ^The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.5 g# l$ F" C3 s9 O" @
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().

7 q# a. O# O; a! T1 i+ G: \    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().
- t% k/ F& \$ n! w/ \    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c), v/ X# C( |$ K( m- B
  //8 M0 ^0 `8 J* b' |2 _. k! c3 A: b
  // Go connect any handles that were created or modified while the image executed.
) t+ v; p: d5 y: g9 E/ n6 d  //
0 B6 u% Z# t* X9 `% u! Q) ^2 d  CoreConnectHandlesByKey (HandleDatabaseKey);

9 I8 E7 g4 r! z+ u' L" ~6 N这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey8 J+ b3 m. B" d
而CoreConnectHandlesByKey会调用到:
2 ]4 n: O1 `- W0 _7 A  //
2 u! W( {! A$ v  // Connect all handles whose Key value is greater than Key9 F6 N3 S" A8 |7 z
  //
& D; n6 {7 y$ W. F: C% b: L$ X, _  for (Index = 0; Index < Count; Index++) {
- y2 N- U5 y* U, k: o$ C    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
3 _- n: V; Z0 i& A  }

2 y! S/ {0 ~) f& N0 L! z所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)
) a4 N6 n! f; o1 G# p是会去ConnectController的,也会执行对应的Support()和Start()才对!!
* I( T; j  [" M: F2 s; t6 V
' n( L. X6 Z1 L- O不知道我想的哪里有问题???欢迎大家指正.
; T: N1 j7 i& c+ n+ T2 Z* s
1 k) c' q, m8 l; z[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。
5 r$ p7 ~& m& Z8 w一个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 | 显示全部楼层
了解了.6 ^% ~/ A0 r, Y
非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,1 g- ?. T: N& U0 H0 t6 ~0 l
Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,8 P. S2 e1 L! z( K( L
//
+ L% z+ e$ i/ N$ R1 {( ^// Notify the notification list for this protocol.
) Z3 Y6 W3 N' i, M; T/ D//2 x) L4 C4 D: Y8 I) T$ L
if (Notify) {! w' E3 Q; D$ h. K+ x; }$ V9 i
  CoreNotifyProtocolEntry(ProtEntry);( I* G: |. O; P! _9 ]; z
}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.
* d2 v! [$ ~: I( }( @! A, d有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表
& R/ Q* K. P2 s: w  ?请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?
$ }! _9 _8 J" I8 _4 k! m# x谢谢!
: n+ N# P* Y% p6 f) N9 e
......
. T! j! z' e, c$ l6 ~
6 s7 ]2 g2 j! E0 K4 g
( C( Z. r# s! s2 h[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表 1 w' d3 u) J6 E; x3 _

: K7 c" y+ ^% U......
& W" `8 T1 Q, L4 Z
, ~3 y& n/ ?8 g& R! v  Q
Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?! {4 B3 v8 n/ }% ^7 U$ ]! W
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()- M0 t) @4 C" Q- X6 K
TPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-6-8 08:29 , Processed in 0.102045 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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