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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。& E, j& U' A* [+ d: O: E

+ p) i7 y' z6 N$ q3 |; `3 {SEC/CEI:
; b& G5 \2 U6 U' I8 n2 K+ ^: d. t  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。
4 z) b7 F% i0 h7 m在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).
: [2 u1 j# E( B* f7 r$ U1 c, D! U& _5 p% B4 R* p5 b7 o- [
PEI:) H" _$ Q& S& i  R3 j8 Y
   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行
4 N% Y9 K4 N/ i: G% y4 _  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。
2 P8 M/ h2 Y6 y; T6 d      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.  ?0 w8 b. |# W0 k4 B8 ~
          InitializeSercurityService函数,将Notify队列清空。
) z+ s* L6 C/ C2 R. f          InitializeDispatcherData函数,将Dispatcher队列清空。
9 Z4 z  n7 f9 _# u9 _  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。) V2 Z9 L6 x2 M7 ^
    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:
" O% q9 T; E( D1 R  u2 z6 c$ _      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {
; ~' a+ G- ]1 T; K       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},: G8 F1 h6 i/ b
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},
  R3 c, u9 S; C7 E+ f1 x* r5 z       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},: g) |  E" n. g2 J
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},
1 U* o4 J% q4 [' n0 P       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},
& I; t$ r& u4 l       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}3 q7 i/ ], a. E* D- L2 P/ l' ~1 {
     };9 }# q% U2 a. S5 y
    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。
  K! e, L5 `; G. R4 {, n   这些PPI会在PEIDispatcher中用到。* c/ _. X( @( W2 k) T. h* h! {$ A. Z
   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。8 p7 J0 T# Z* j4 }& v1 }( H
   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。, T* u6 l) A4 i% q2 n1 N# R* o
   SwitchStacks (6 I5 ]+ `" t+ T  l4 ~8 p# v
       (VOID *) (UINTN) DxeCoreEntryPoint,
, ?: N3 D: C. j; ^8 S' o8 e" ]       (UINTN) (HobList.Raw),
* i  X1 O% X7 W. J7 w       (VOID *) (UINTN) TopOfStack,
* w* [: m8 Q  W( f  w( x* M       (VOID *) (UINTN) BspStore
, g" z2 o) ~, b" `& _$ t. k) i    );
1 C( L- d0 P( l  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)
4 x7 \8 w) q8 ~/ W6 }8 u
6 W7 l' {2 E9 x- WDXE:
" j& e! f* |7 _, {3 F     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。1 [. }; S5 Z6 |6 l' }
接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。
: x4 M! h( G) w3 w+ w, ]' M   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver' L8 ^* K7 r' w" Y8 `
中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。  I7 o: B" Y* L/ E! g7 z2 f
   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。
) C* |: B8 `$ v  F   7 ]5 b) |/ w9 o8 s* H0 }1 R
Driver:
. w' P: d  l  y) g0 ?: a3 F2 U    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
( j3 a" U. h+ |  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
# {* R9 R0 s) z  [defines]
, l3 L/ z+ m# s8 A& F2 j  BASE_NAME            = OWEN
# B) T2 X* V4 c7 g  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110
3 O: ~2 c  K: `( I7 Q  COMPONENT_TYPE       = BS_DRIVER
+ o, `. D0 {' p3 y1 _1 i4 ?" E; ]5 z. P6 M
  BASE_NAME告诉编译器最终生成的驱动的名字。% I9 t& q7 n6 V$ e" g
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。
4 y+ W4 [  V& q, E5 y8 ^  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。3 e% u* e) G8 Y* j) {) T
  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名+ Y$ D8 Z5 n, G) \
  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {0 [- o0 N% \6 n9 H- Y8 C1 x( t% }
  {"bs_driver",  ".dxe" },1 T) r0 J2 W/ z  M1 w
  {"rt_driver",  ".dxe" },
8 ^* F8 R, ~& X% @. ]( a4 U  v  {"sal_rt_driver", ".dxe"},; L" A  L: U: {( k. |
  {"security_core", ".sec"},$ K" i' f  T/ d, N# l, S
  {"pei_core", ".pei"},
! e, {2 [& R- Z+ d  P5 P  {"pic_peim", ".pei"},
; j5 c( l, c7 L& h; Q7 o! J7 L) D  {"pe32_peim", ".pei"},
- Q; h% w$ Z- F6 {% Q, }" W; }  i  {"relocatable_peim", ".pei"},: |2 r3 e7 Y" {* U
  {"binary", ".ffs"},# P% H' Y- p7 y' c% d( r
  {"application", ".app"},
/ a( S$ a+ r0 c3 \& c/ i: I8 n  {"file", ".ffs"},
: o+ |* d7 Z2 _6 }" I& g  {"fvimagefile", ".fvi"},
: }( @5 G% b5 z- f7 c+ A' Q0 N4 i; |  {"rawfile", ".raw"},4 K1 j, R+ P$ \# e
  {"apriori", ".ffs"},, C, Q& }. O4 f7 _
  {"combined_peim_driver", ".pei"},! p' u: j: B+ j
  { NULL,  NULL }5 Q) N: R  s  x% u
};
  v# a- h0 B1 ]" r  o  `. q; g+ r5 M# j7 S5 K0 N
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)
# ~5 `% C4 s. o2 b   
0 r% z* |% [) V$ S         
" G; Z/ b% E, A8 T2 Q  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
, S& v. M! W6 L9 o3 E# n支持
: f" b  A, P7 B. \继续
$ \0 O2 }  R- u6 \9 t加油
; x: D9 y: j# y
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?
: r3 e; }0 q5 K9 y" |- z% c有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。8 `9 C4 E0 t7 Q! r3 ]5 x
还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。, }8 H, ^1 P  R

& ]5 i  t+ J# F/ e% r支持!
回复

使用道具 举报

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

. N/ E& \) S# z0 s2 q( J+ a# z$ n小弟还在学习阶段,目前的目标是知道执行的流程。
$ u2 w# e& j: h4 i4 {这里面有很多细节没有写,能力有限,只能自己知道,不能表达。
# Z* u/ B& d, Z* w6 ~2 v嘿。。。。
1 O- Y( p) J. `所有大侠们如果有好东西能给小弟共享一份。
: o6 Y6 R2 F. K' V% n4 K
# G& Z/ |) Q4 y7 b谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
! {( O$ v% i1 U9 ~: x9 {, m  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
0 }. j6 q' u- d7 A3 w4 w1 @中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...

: v" _  `+ h/ b* u; J0 _# }
% I6 b4 C9 ^) [PPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol, ' ]: |% ~) _! t4 j1 j% R
For more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".1 G+ W0 M" h7 R/ p! O2 _
. q0 o2 [" @: Y4 W' G7 L, Q3 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:
+ i0 A# H- N' |5 Z   Yes. I make a mistake.
0 k3 }, p. p) E      PPI:     A PEIM to PEIM interface.
/ w5 t1 u- D( m2 Q      PROTOCL: A Interface between Hardware(or firmware) and software.. p+ e: r7 f9 }. l: R
      reference[http://www.biosren.com/viewthread.php?tid=207]
. H; K! d) L. t/ R' f- g! s   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.
) Q" I9 W+ m) f1 g* T% k" X   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:
: b: L/ c0 C5 Q     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。
/ b2 t  o  }0 n接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。
# }* c& e0 t* P  I  w! l) q; m) A9 C   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
2 ]: l8 E- M% E0 |中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。& ]- P, l) a6 M) y
   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.
+ d8 \) I. V0 e0 p( G5 L( M

' |5 Q9 [! Q* o! S4 QThere are mistakes,# l9 ?9 H1 i; V' [8 C* _8 Q2 l
1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.9 ?0 Y/ b2 g4 r; K) L

) ?$ x: q; |: J$ c! g2.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.: ]. A" ]5 |, T8 |% ]
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.; S' `4 a# Y# W3 c% E
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
; V; M$ ]" n+ D) ^8 D( }( B! n, O; e9 \: q
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 大侠的指点。
( ]% t( t) }+ v# }# g
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表 * N# x+ ^" @) ~% `
...4 V+ U' ?: u- N4 M& x6 m
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.
- e9 h. l5 \1 @; ~, ?& }0 _0 KThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.6 x$ l7 T5 \" y) G
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().

0 i8 U) z: T# Z: u3 N0 O# t5 l    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().
6 {& j; s3 G% N3 ]3 R( ~1 P    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)- `; N* n- J' v% s$ H4 \, ]
  //; m6 x2 g) |9 ~
  // Go connect any handles that were created or modified while the image executed.5 [! b0 B+ X! c! c  B( }* p' \- E
  //
9 }( x0 y  C5 K. f1 i  CoreConnectHandlesByKey (HandleDatabaseKey);
% s! T1 y& _% }0 O/ b  }
这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey3 ?/ x8 u7 f* B- O
而CoreConnectHandlesByKey会调用到:
2 l# ?  p) p' {$ G0 Z  //
7 d' ]. b# v& A/ j: ~  // Connect all handles whose Key value is greater than Key
5 \$ Q5 t% t1 t8 T- A  e5 u  /// q  W2 o& R. M- l
  for (Index = 0; Index < Count; Index++) {
) _$ T9 D! W7 ]  y    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
0 w$ T. W0 |$ B* |- Q* r" q  `! F. t  }
4 N5 p0 h- t2 a  Y( z
所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)
' p; H; F0 `* z5 F4 P+ a! |是会去ConnectController的,也会执行对应的Support()和Start()才对!!
' X# d" P; q4 b% B/ J" u' \! V# M- M  W1 O
不知道我想的哪里有问题???欢迎大家指正.- ]0 M) X" h* v! R& O) T
% H) H1 R/ G6 h  v
[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。2 M" K5 D; Z( `5 l& C( I9 h9 F
一个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 | 显示全部楼层
了解了.
! P% s; L9 I6 B$ I( t* [非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,) {; [/ \7 y0 J% P
Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,
% U) q! |- t7 m//) u2 A% B2 R0 H2 t* b, m9 I; F
// Notify the notification list for this protocol.
( N- u+ s- {) w0 C//0 T% ?9 b* s/ w4 f" d
if (Notify) {2 K0 y/ `( @6 I% j2 W# }
  CoreNotifyProtocolEntry(ProtEntry);' e( U# N* Z8 t8 a
}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.& S+ s9 `4 T( [3 K# [
有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表 $ G. I3 K4 @0 _9 K3 X
请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?' t! E; P7 l  k8 b: j4 M# G* ^
谢谢!

5 s; w% E4 X& r1 j- c......
  H  |* V& s  A, V& @1 J6 r4 W
, h& e3 B& Q9 W, u5 R. Y) K% [! Q/ S7 x
[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表
$ o; Y% I/ z# v
6 F1 b; B# E  z& |5 `1 ~/ a# N......
0 L, W7 V; \; {3 W' S

; ^- \7 m4 I* C- e' [6 nSignal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?
& b+ F! Z& i/ b( o  ^$ p. O如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()& {2 F0 [, h7 C) t5 ^9 F! N
TPL=30
回复

使用道具 举报

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

本版积分规则

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

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

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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