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

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

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

) ^9 l7 c5 T  V" O& {& iSEC/CEI:
7 M& p) `) p; r# \  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。0 H% M/ \. L7 K8 w7 M2 p
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).
5 ~. r. ?% z, a, a" |$ z8 \
% F& u/ ~2 g: D0 F7 iPEI:
) `3 v9 u: X( T# M1 k- K1 p- M   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行: ^2 c8 J+ e5 e; ^7 i4 W% s
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。$ f5 ^& H' V7 P- G6 B
      InitializePPIService函数,将PPI队列清空,这个队列长0x3F., q5 O8 k* J0 }& z/ g" F
          InitializeSercurityService函数,将Notify队列清空。
' I: M: Q8 D7 J4 Z  i/ n0 _          InitializeDispatcherData函数,将Dispatcher队列清空。
0 p- ]. J: `" ~- e0 L  \  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。
0 ~9 b* \4 I) K7 [    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:
" m, j, U# S' P# O. o      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {: b0 `, {" C0 M* P5 `/ s
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},* o0 X% v8 k  w3 s2 s5 v
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},3 J: }: n! X; U, p3 P0 V; o" t
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},
& S! g" v$ @* b* _       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},
5 J9 i' |) z: d! c' j3 _: h1 ^       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},+ Z- U& R) J5 Y8 t' `& A
       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}5 k; `5 m+ Y  v8 d" r3 o" g: x2 P, ^
     };
! t2 c& ~+ ^3 V    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。1 r% @; x. A( S* t" @# j; b
   这些PPI会在PEIDispatcher中用到。: B, ~3 y. z' O4 O, Y( e
   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。1 y3 N- _1 E, x* @. l& D- M
   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
* {) O/ t; j0 G/ u   SwitchStacks (
: j% `% s( z0 k! d0 N       (VOID *) (UINTN) DxeCoreEntryPoint,; v1 J* M5 |8 g
       (UINTN) (HobList.Raw),
: @% Q: j& H/ t6 D8 ~       (VOID *) (UINTN) TopOfStack,/ d1 G; j% q/ ~5 n9 v
       (VOID *) (UINTN) BspStore
$ {: r5 U5 p* p4 k    );% m7 M5 Z# a/ j; W! F
  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)0 P; }% \; a- e0 n  L: @; r1 M

1 d# ?) N# O# _% @4 k0 \DXE:
$ U7 g& i/ j% }; P/ V     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。
0 J" r0 r1 g# _6 @' G) g; Q接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。% ^" R9 m) P6 ^" V
   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver: i0 }8 R: [7 E/ }. Y( x
中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
1 l2 J0 g5 r) ^6 q/ n7 j   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。' K" a  ]5 [. b
   1 g0 u8 E4 W' r9 ]( }6 e; A
Driver:
6 r( b: X& G1 G- W( q    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
* E2 V+ E, K/ d8 y# Q% n1 y  大家请看一下我们的驱动的makefile.(EDK中的*.inf)8 m" ]# s0 X& H/ P
  [defines]- A5 z. f8 m( n2 B
  BASE_NAME            = OWEN
& C0 T3 p- F% S2 ^) V5 _; e! L% y  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110  b5 B) ~4 S; J+ ?5 R) R' s
  COMPONENT_TYPE       = BS_DRIVER# S* T9 i: }- {

# q! Q* [! a3 Q) T  BASE_NAME告诉编译器最终生成的驱动的名字。
; T) a' c0 p: A+ u9 y5 [. W; I  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。
2 o0 I5 K# T' {- s% Z2 J- i+ c  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。8 l; z2 A# I" x
  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名
& c2 D+ s3 K6 {+ h  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {
  n/ Y- \$ }& ]; Y2 S  {"bs_driver",  ".dxe" },
; d% T; Y& a' P6 A; Q  {"rt_driver",  ".dxe" },
5 O, J6 V- G) d$ p7 z, Q  {"sal_rt_driver", ".dxe"},/ G/ T/ h, O; K. [4 @
  {"security_core", ".sec"},' v5 _  x  D. b1 u; ^. O$ w
  {"pei_core", ".pei"},
7 `$ d+ l& I5 X' b$ Q7 ~, t9 ]  {"pic_peim", ".pei"},
0 M5 _4 w2 _- K# e* S# F" o  {"pe32_peim", ".pei"},/ D5 U" I5 v8 Y
  {"relocatable_peim", ".pei"},  U( L8 f, T3 b1 n' }
  {"binary", ".ffs"},/ q/ _! C9 V8 G, g3 w: q' z
  {"application", ".app"},
+ i$ W# E" f$ ]: l  {"file", ".ffs"},
9 ~: ]5 W# P3 L8 u9 C+ y) X6 k  {"fvimagefile", ".fvi"},
! B' j; r4 W5 t2 r, U6 _- g7 M  {"rawfile", ".raw"}," W3 {. e/ J+ ]7 A7 [$ s+ F
  {"apriori", ".ffs"},
7 k( P5 A  s8 a- v  {"combined_peim_driver", ".pei"},
9 l; b& c. F7 d6 s) G( e7 J$ F. q  { NULL,  NULL }5 j7 b6 [7 m$ D! d5 I( r" A
};
, K. c4 ~& V' l: f0 @% J0 F0 G* ~! q- i! `
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)+ H& C7 ]0 f0 V0 y) F
   - v9 @6 m7 I0 m6 b' [3 R) K
         + M0 h: j3 j; w3 \
  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
4 S5 i$ d  R5 A* i( q( @支持2 T1 I2 R5 R! L% X7 ~; m
继续
! j9 v* m7 Z' k2 m9 y7 h2 Y加油1 u: x, N4 D5 a  S  E; i4 Q# d' ~
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?
' }7 |: W! w3 C! a; H6 U* b5 c有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。5 J7 v3 P1 Z9 D3 }* f/ n  j. ^  {& j
还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。( I3 u2 b* ]& }! _* ?, ?* ]) ~

, A7 t( f$ b% Q- ]' s+ w支持!
回复

使用道具 举报

 楼主| 发表于 2008-7-28 18:52:31 | 显示全部楼层
% N& \0 v3 I: {1 ^& e# f. |
小弟还在学习阶段,目前的目标是知道执行的流程。) D* p4 U9 A" v/ Q% ?
这里面有很多细节没有写,能力有限,只能自己知道,不能表达。
& l  z3 @3 V! y2 ?! K嘿。。。。
5 p( P( N1 z4 j+ V; P. T4 H所有大侠们如果有好东西能给小弟共享一份。9 ?% q' P" h- N

, T" M  V7 a% y& H& [! c7 v! L谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 4 j; _' t  v" j2 H
  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver( Z8 M1 K4 R( N6 C
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...

$ g7 z! B, u8 Q$ l8 X# i+ u& o7 n
PPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol, : F* m0 y" o) u! a3 d4 U
For more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".( P6 }9 d0 n/ L. E& ^5 r

6 q3 c3 ?. A" P/ L0 K* P8 x1 s[ 本帖最后由 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:
& s% O6 {. L1 Q5 B   Yes. I make a mistake.! r4 H9 `/ d  {( V% S8 q' N6 d  l
      PPI:     A PEIM to PEIM interface., {8 z# ~! K5 O! v, A& c
      PROTOCL: A Interface between Hardware(or firmware) and software." k9 |0 K' J6 O: }9 ^" @3 o
      reference[http://www.biosren.com/viewthread.php?tid=207]+ f; G8 ^% _$ H; B( T
   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.: j0 L2 _7 P' E' o4 b: B9 Q' t+ m
   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:' Q2 X2 U) D+ s( g& b
     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。' i7 |% L6 @1 ^3 C! E& N
接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。8 j2 Z; x$ T* D3 z* O2 e6 z# v
   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
; ]: y; d% `/ n中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。5 [" _) K. c# F4 ~( g
   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.

/ M- r/ e$ H2 C& d$ t
! x3 [6 }6 {" O5 eThere are mistakes,; l. @6 d+ l- x) K- v4 Y
1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.
5 v1 q9 z5 w6 L9 \( |5 O. A& u3 s/ ^- g1 ?+ s% Z6 a7 |
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.
! O* _6 K) H/ f; R& d7 o5 PThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.5 v( P; \; u9 o0 X+ V
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
9 D/ j. p3 }1 h, e. t3 n5 \! o/ p+ q3 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 大侠的指点。
0 ?' h+ F; A7 f. f0 v
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
8 m1 N& A# c1 T$ C1 o2 O+ ?% E...
/ ], c( ?  o! w6 B4 d6 Z6 k2.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.
& R4 l* x2 g" p4 m3 E" wThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
3 o0 }0 t0 ^: E3 [8 S4 B& \Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
% ]6 r* k% H5 z, }  G- T
    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().) b1 R# l8 q  d
    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c); s& W. p; G9 a  p8 @+ G
  /// A  F8 O% O2 ?) ]1 q- Z
  // Go connect any handles that were created or modified while the image executed.7 Y, |0 Q# o8 x7 l' y
  //
* y- u5 D0 v' D7 x  CoreConnectHandlesByKey (HandleDatabaseKey);
5 [7 a8 k' k- y2 g, _/ C
这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey
1 p: ^: P: {. R而CoreConnectHandlesByKey会调用到:
9 O6 ]. d. j0 D. f8 ~" q& n  //7 O( S+ [4 D0 g2 A- p1 H
  // Connect all handles whose Key value is greater than Key
; c0 M( }* a3 ^  //
/ m) a8 n' V& ]9 d' y6 K4 d! g  for (Index = 0; Index < Count; Index++) {& Z, K  E9 P. f
    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
  k+ y3 V& h) B/ E/ O0 l  }

; g- t. a# \$ }( z9 b6 ]  i1 y+ _" S5 }所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)
- Y& q, c- Y! ~5 ~" b! N是会去ConnectController的,也会执行对应的Support()和Start()才对!!3 N0 s* A6 V: o' x0 v
6 n) `, l" M9 D* `
不知道我想的哪里有问题???欢迎大家指正.
. b2 x8 o, z9 Z% u- w% \# ^
1 e; {1 p  T" K; w/ d6 ]; @[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。
& D" X5 @( w- z* l# T: o( r7 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 | 显示全部楼层
了解了.
& a4 d! c* C# ^8 H) w- J非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,9 E, N$ I4 ?9 l8 C" F. o+ b- ]2 N8 H
Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,9 f# f  f5 |, a6 r6 d) B, a8 n, {
//* Q. W* ~5 P7 ]
// Notify the notification list for this protocol.
% A8 y7 \  W; ?) @//! x. h8 l. A7 c
if (Notify) {
) o/ m& A) w; W! |% P8 i& s  CoreNotifyProtocolEntry(ProtEntry);! J6 |/ F2 G6 c. j
}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.
3 R( @: H- j7 ^) W3 d, F有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表
6 o( \2 K, Q& [& k请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?
0 D" g( o- m( E1 x, [谢谢!

. r8 ~  \, {) V. O6 L......' N2 j4 M; T# |' ?0 i

: Y. o0 ~) t* m- {% o- M7 `2 y; F
# h. T0 {: k- _8 b: p: L[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表
$ f! t+ c$ d& @+ z( R
9 q+ x7 B% b: l5 l+ w/ D......" I* Q" {2 C  A4 i

1 q0 \; X: {+ Q: w  {Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?. b; H% Z: Z0 a9 \9 B
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()$ b' s: F: i" B: k! t
TPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-5-16 07:33 , Processed in 1.582500 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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