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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。( x  k$ ^5 ~( s0 p% G7 k
* W2 |- f$ ?$ K5 p" u* ]) n
SEC/CEI:
8 H- n" @' u9 y+ J  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。8 F- {& {" l( m( U5 H# h$ t
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).* U+ r6 Y5 S* G7 I- F

* s! {0 ~2 ]& k2 {. _& `& xPEI:% T0 A% T$ p# y" k% S$ m" s
   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行
# `4 }- }% m: p  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。
; Z# R+ H, F2 N3 `4 r, B) \& W      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.( F* h9 w) [! ^; d
          InitializeSercurityService函数,将Notify队列清空。
. l! C# ]; \4 L          InitializeDispatcherData函数,将Dispatcher队列清空。5 R* X; y. B* U9 _+ J9 e
  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。
; g9 h) ^2 U8 ^1 }- m    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:
: a2 C; U. i" L" D, b) D      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {  |2 d  _! k) |; \# L
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},
5 J# C$ n: D- o9 F# F( S! C       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},
: k& y8 f2 Y# ]" K/ q       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},
$ N6 X; }7 a( H6 x5 w5 X& B       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},
$ V0 [: o8 U4 A& m- N       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},
. `) Q9 _+ j. a9 X4 T. i       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}0 l( {; N5 A6 |+ {  r
     };+ b4 |3 L0 J+ B2 B. N
    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。
7 {4 @* n3 T1 h5 E3 v& C   这些PPI会在PEIDispatcher中用到。; X7 M0 \% n: q6 ~7 O3 ?
   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。
' b8 R* r! W+ D0 L   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。7 f+ ?( n* c: K, N6 W6 i- V- J3 t0 U+ ]7 l
   SwitchStacks (
/ v3 S3 ]; d* J7 I% R! \0 N  j) ~0 F" g       (VOID *) (UINTN) DxeCoreEntryPoint,
" D) a8 ~$ M' i7 Y$ I0 a       (UINTN) (HobList.Raw),
$ h2 j& K8 T" B9 X/ Z$ n0 [' I       (VOID *) (UINTN) TopOfStack,6 Y. Z. S) U* i0 t6 |7 e1 E
       (VOID *) (UINTN) BspStore
; `, Y$ {  U+ j    );
! k5 n; ^' J: c4 U5 W4 a  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)
9 m7 Z* L, f) D' r% J3 E. m! u% r0 e
DXE:7 g: f1 j. b* I+ z9 ^" K; o; C
     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。
: A3 A6 y, P5 f0 d6 M接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。
' B2 e3 C+ k* v  N   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
2 J. W) f* V3 e中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
' e' X+ _+ f" z) J# A# @( C   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。
9 |6 J4 _( L) a' @, K# ]6 P( v3 T2 E   
- v. B; i1 g* O7 ^Driver:* D& w1 ^6 P, Q8 O
    我们的驱动什么为在PEI和DXE等不同阶段执行呢?2 ^6 k* n, A# o# B$ k8 q
  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
2 i2 p- A8 j( Z! t& q. V  [defines]
' X7 x; D1 F. a$ T" |" y6 h  BASE_NAME            = OWEN. t& q" t, K$ h2 N
  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D08301100 W$ ^8 H" V3 e/ K1 A
  COMPONENT_TYPE       = BS_DRIVER
; T% A; G* J. c0 ~
1 d' [1 }1 T% ]) l3 j  BASE_NAME告诉编译器最终生成的驱动的名字。
% Y, @: B& p4 l. S) N( `  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。$ n% {3 w; y4 Z
  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。
5 t0 K. X5 g9 L6 Y# ?' J- y* K' V  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名
+ l8 ^" t+ `$ r0 U$ x  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {8 ~1 s6 n1 k0 n: J  |% D' v
  {"bs_driver",  ".dxe" },6 n+ C1 ^3 f* N7 V  E7 p
  {"rt_driver",  ".dxe" },
$ @( x% k- I6 ]1 I7 c2 A! ^  {"sal_rt_driver", ".dxe"},
* T9 R& e9 j+ b* }9 k  {"security_core", ".sec"},
3 P& c! B, W: D8 H  {"pei_core", ".pei"},
! d! h5 `$ |7 P% k# P5 x  {"pic_peim", ".pei"},( Z9 j; F# z$ r1 Q$ G0 I
  {"pe32_peim", ".pei"},
9 w% i' D! s% V) B& Y$ H7 V  {"relocatable_peim", ".pei"},
1 y% F; ^3 r$ Y+ K# ?0 z  {"binary", ".ffs"},
1 M3 ^! c+ r& w7 q( R" L7 S  {"application", ".app"},
8 r& c' a7 w* g5 b4 A  {"file", ".ffs"},
% u$ u" q% F- N( q0 f3 e/ |- O  {"fvimagefile", ".fvi"},2 E* M9 \5 v# j" N; O
  {"rawfile", ".raw"},
4 O- P/ a* t  O' ?  {"apriori", ".ffs"},
( C$ i* h6 J8 W7 Y" ?& @( Z. k  {"combined_peim_driver", ".pei"},+ A  J5 n9 ?/ }& F& G
  { NULL,  NULL }
* Z0 m; }8 j/ @/ X9 x2 I) ]( A};
+ y  T4 r. A7 H& c; b/ n8 X" T1 i# R! U$ h  K% h, z
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)
1 K+ \: l" f# i& C2 n3 P% N3 o) \1 J   
* N$ d0 {8 T) ]0 `6 {- L8 v8 f         
. }0 A( n$ F1 G! [* p1 ~$ y  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
2 C: g4 e* P( Y( \$ v0 h支持; e7 o' r: C; x1 C) S% \( }
继续+ ^- J9 {, k+ S. h$ d, J
加油0 f8 W" d& \& q; f  y
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?
& Y4 T5 g" p( m4 }有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。
# V2 u, D9 b% c还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。9 M1 \+ E* U' H6 k% K2 n

- G' S# U. w' f+ E% D" m! X支持!
回复

使用道具 举报

 楼主| 发表于 2008-7-28 18:52:31 | 显示全部楼层
- u! G; E8 b+ [  e
小弟还在学习阶段,目前的目标是知道执行的流程。3 u9 a, f4 ~' S( k& Q7 y8 x- i& D: f
这里面有很多细节没有写,能力有限,只能自己知道,不能表达。- g/ W( b/ G* `; P# k
嘿。。。。
8 ^+ N1 D* I: t; z, k/ f' D所有大侠们如果有好东西能给小弟共享一份。
% {) S5 `+ Z) _% j, R# ~8 f1 ` ; u% S( H# j5 K( G' M, N% ~& c( d
谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 9 Z: @8 x( Q: ?+ w: g
  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver- `- w1 F9 Z9 w/ Y: V: \$ h
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...

' J/ V- G% c8 y5 L
* {! T% ]& i0 D9 l# LPPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol, " q1 H' e, J4 L6 S& b/ V
For more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".
" D1 @* r8 U+ n2 c# E- a1 T/ f. T' c; w) M; K4 Q* v* a) o
[ 本帖最后由 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 Z/ W1 C! B. R7 A
   Yes. I make a mistake.
3 m: v% X8 t9 e7 B      PPI:     A PEIM to PEIM interface.
3 [8 E/ F, O8 Y/ B1 r, z! \7 x( ?& E      PROTOCL: A Interface between Hardware(or firmware) and software.
$ b6 `2 u# N9 j/ q' u+ u      reference[http://www.biosren.com/viewthread.php?tid=207]6 d$ E8 G1 ]' `$ d6 E6 ?' y* x+ B
   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.& {# ^3 h5 ^% L* K1 n2 a, q- Q4 Y$ \
   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:  e: I$ u% ?0 [0 n" s3 ^0 R
     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。
1 H# f- g$ G- r4 Y3 F2 {* m& ^8 s3 L; N接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。" Q9 g! J: h! @: o
   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
& g% B+ _+ e' u4 G0 g5 S' e中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。
) j# H% Q( }3 _4 {3 z% ~; p   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.
2 P+ U; ^  i( \3 |' t9 {
7 E9 g: A' `2 P% n4 E- Y
There are mistakes,' Y! j8 S% f% c9 w
1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.
! j" h0 Q3 {( D
3 ?* z4 M" J) I2.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.
7 ]8 b9 x, z" Z0 h6 TThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model., J7 ~+ L1 T# D0 s
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().3 q8 ^) b+ b% F* N8 f( B/ Y

3 `" a8 Y* S- N; f& iBTW, 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 大侠的指点。# e# _( j3 u- t4 T% X" v% A9 k
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表 : N) j& r; E1 N* Z1 e# [
...
* O6 e% ]' V" 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.
) y; m8 f- z/ ^6 UThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.$ D) B# p/ D9 ~7 k  C. E$ k. A
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
9 b8 ^5 W+ D% v/ r- J$ d/ Q
    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().7 b( X% x4 {1 {6 Y0 O- `
    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)
3 Y+ H" i7 ~6 L  //
9 Y: F/ B- R3 f) ~  l" i6 h  // Go connect any handles that were created or modified while the image executed.$ F% r" h8 v* P  W6 k5 k  `$ [
  //# b+ G: k, V. k2 p; V
  CoreConnectHandlesByKey (HandleDatabaseKey);

/ d$ q, `. m% z6 ~. p4 h; {* u这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey
6 S: Q" u& M9 C# k而CoreConnectHandlesByKey会调用到:
9 N( {% a% r' [" Y0 J/ Z- m  //
& I3 D6 _- }9 Z# Z* L4 C: S0 W  // Connect all handles whose Key value is greater than Key- F- {/ O: ~( c# L2 I: F
  //7 P: ~' {0 ?6 p: j, w
  for (Index = 0; Index < Count; Index++) {
. a% b3 l) M7 B! i9 v    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);: F, T6 |8 P! X4 b% x  y
  }

* D8 C! |. [; N2 P, q; }& b所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)) X+ o5 d7 R/ I# V
是会去ConnectController的,也会执行对应的Support()和Start()才对!!% j# f8 \! H- a) B3 {- D
0 E3 R% u% s/ Y$ m
不知道我想的哪里有问题???欢迎大家指正.- ]  Z. g5 A+ g0 w8 ?7 _" K" X

0 ^$ W2 z6 T2 n3 R[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。! T2 O; P( I2 G% [+ m
一个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 | 显示全部楼层
了解了.
! |; S9 ?! v/ p( L+ r/ l6 s非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,% z& n. L3 Q; c5 h2 ^$ V
Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,( o/ u3 W5 p) w6 z2 b
//
6 O8 L3 X! \5 @7 D4 `// Notify the notification list for this protocol.
8 t0 N, N, a3 d$ J2 s. S9 ?//
1 l# T6 g4 O9 tif (Notify) {( S, x( }- h9 X
  CoreNotifyProtocolEntry(ProtEntry);1 S" {- v  y% @6 Y
}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.: Y! ?7 u0 `- d. L5 Z! K* Q, }
有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表 / p. g8 T+ e0 ~& h0 k) v
请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?$ J: O8 ~3 _4 Y. f; w
谢谢!
4 M9 h3 j  }+ J3 B
......0 |. `* \5 F% e0 c& {5 r

4 R5 d% z- h! x- E
2 l, V3 \7 b6 f$ w5 [8 g. T[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表 * c) v) ^2 y. e. v0 Q
  P* j( U" E. H' a$ ^1 q
......
. M: l4 @/ P3 g- R& y, H  Y( V

1 L& c) @$ h) l% c& BSignal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?- v9 r! _% R& Q; W
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()
  q; t2 U& M2 O' OTPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-6-29 06:01 , Processed in 0.107231 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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