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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。5 u$ w. o; W; m( h" O6 }# w
) B: k- k( S" I
SEC/CEI:
# q% i0 a8 l% D5 P  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。3 h  X4 Q0 f( [/ f. d# |, W" B& [% r
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).
0 A3 r! S1 D7 ?1 a- a. f; Q3 l% a0 y1 T! k6 m3 \
PEI:
$ R4 ~( u3 F% X- J( f1 R   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行; I& J3 }: l2 X! Z: n$ f0 U" Y
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。5 ^5 R: O( k% i2 M. [7 O! m  _
      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.) K7 M' R& E- Q6 z* O
          InitializeSercurityService函数,将Notify队列清空。: o8 l0 I' Q1 L; l) I" k: G
          InitializeDispatcherData函数,将Dispatcher队列清空。
/ Z, F" ~3 X, K' x$ _% ^  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。9 E; I0 ~, Y4 `5 |
    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:
* {1 U  f  A* c9 |  F      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {( @6 S* d) F+ J6 d4 {6 t; }' q- h6 d
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},$ w2 t: l. Q" _" u  w# t* i+ c
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},
! \$ E5 ?9 J+ M* k$ `' ?       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},2 i, h/ S4 J" f# I
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},/ O2 u, Z) Q! g, X" o/ p& R
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},
3 u. P: r* _5 a' ^       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}
( {- j6 i/ I" o: q6 a# F. u# R3 {     };
) ~7 Y& X9 e: |1 q, [/ p& m5 h    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。
5 G0 A# b9 d* |% j   这些PPI会在PEIDispatcher中用到。$ I3 `' S0 W$ g# ~( k
   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。
8 p; f, @/ Y+ y1 }% e9 j' e# g7 w, k   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
" R7 l- R, @5 C6 w1 U" R0 S   SwitchStacks (/ e4 x6 @/ n% ?1 S* R" A1 W
       (VOID *) (UINTN) DxeCoreEntryPoint,
! R4 p0 r9 O8 B       (UINTN) (HobList.Raw)," R1 U: K6 N+ X$ O3 W3 {" S
       (VOID *) (UINTN) TopOfStack,
+ x, U2 ^) B3 S, `       (VOID *) (UINTN) BspStore
5 @: n: z( N3 ~  V, K    );8 ^: Z3 |3 e: l9 R; w
  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)5 V+ [% P0 N! x8 N; ], ~9 n
' i' [9 e5 q. m9 j8 ~
DXE:% }6 n" M4 ?. i. `$ b! C9 g6 P, J4 z
     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。6 {: _# b. z& |* ]
接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。  i: n: U) U9 Z/ b6 K; j1 ~  ?
   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
+ T! `0 W5 G! f5 x- v中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。: S' z" M7 @5 ~6 n* |2 M
   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。1 e3 x' T( E: c/ {6 v) i
     l) _) ^. V6 P; |, k9 q) k3 L- _
Driver:
% `! m/ s/ u3 J  q    我们的驱动什么为在PEI和DXE等不同阶段执行呢?+ ~; v- `6 \: F3 W
  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
1 u2 y; y# ?) C2 G+ r1 i* e  [defines]
( X6 C5 Y/ ]9 `( p- g) a* L  BASE_NAME            = OWEN8 B; O  [- N. T4 ~
  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110+ p( V9 i* p$ a0 c; X, k
  COMPONENT_TYPE       = BS_DRIVER9 Q: d5 }- N% L% x' t& j* _" G

% ^6 k$ \1 I! _9 d8 o  BASE_NAME告诉编译器最终生成的驱动的名字。& f- q4 k' X' E4 _+ [, R
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。
  y) V. k3 o# ]  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。5 G  M8 x2 ^4 X1 s7 F
  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名5 C% Q  [* |* K( }& i+ z1 k
  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {
. L$ I! J! k; C1 d5 R' Z8 H4 g9 ]: a1 _  {"bs_driver",  ".dxe" },4 F: Z5 A6 a# b5 M' ~" s! F9 f
  {"rt_driver",  ".dxe" },
. C9 G7 t9 y& z- t' E0 C  {"sal_rt_driver", ".dxe"},2 c) w0 K0 l8 I9 l( v4 u" o
  {"security_core", ".sec"},
7 U) L& U: G3 J- }) h  {"pei_core", ".pei"},) U3 t+ y- W6 T" o, e
  {"pic_peim", ".pei"},% a! ^2 g9 g# G3 {6 w
  {"pe32_peim", ".pei"},
4 B" q+ Y( A0 R1 @$ I# ~4 Q  {"relocatable_peim", ".pei"},
2 {/ e3 b9 @. a' ]$ F  {"binary", ".ffs"},
# e& Y+ B: Y. n( m1 N( B. e  {"application", ".app"},
0 F$ e4 L2 b- c. ?- X2 Z  {"file", ".ffs"},
: y' [5 |) C, ~, m) L/ R' v! {1 S# Y  {"fvimagefile", ".fvi"},5 c1 J$ P8 i3 J8 G8 O; f' }+ V
  {"rawfile", ".raw"},
3 U, J2 A3 t6 ~; i! q4 `  {"apriori", ".ffs"},& ~# l4 Q9 N% s2 z/ |, G
  {"combined_peim_driver", ".pei"},/ `# P5 x7 R' c6 l1 P
  { NULL,  NULL }: e0 e8 W' E) N( H6 n
};
  W( R6 }! Q& k9 j" K0 P. o
; F4 C9 P& Q- |$ {7 M了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)
4 [8 ]$ u7 W7 O  Q& ]   
& T* }1 J$ a  d/ J         
8 c8 w( g& N' @7 C) x0 r, S  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!# H, H( r% P- g1 P& a4 J& |
支持$ k. U) D0 E7 i7 L" |
继续
7 ?; t! i1 I5 F5 v  W加油
4 n. I, \$ G8 C: d: S
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?
6 U; L0 r0 G4 j% `9 ]' o有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。
- t* c2 m: x) }6 ~2 S3 c& K  K还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。: r0 R$ \- f" S4 F9 m& m6 P

) ?# e* h3 m- X支持!
回复

使用道具 举报

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

! L- j" O# V% L小弟还在学习阶段,目前的目标是知道执行的流程。8 ^- i! A' X1 z" M# B& c; F- u' |
这里面有很多细节没有写,能力有限,只能自己知道,不能表达。
5 l, b* E% R5 D嘿。。。。+ R0 m/ d2 E5 ]) ]
所有大侠们如果有好东西能给小弟共享一份。  j5 M0 B! |# B0 H
) Y+ B0 C' O( i
谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
! P! b: u9 u! a  _! E* X  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver6 G! |' B4 C% v) a" m
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...

( ]2 E" N5 I: H  J4 O5 U' b5 e+ a- G* {0 i  A( e/ M
PPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol, $ ~' v% U1 b4 S) H9 h
For more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".# ?/ h: n3 ?$ u# M% U3 x& o( h; I
; L$ Q1 r# T7 b4 |0 P% X
[ 本帖最后由 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:
/ D0 k0 z1 v1 i( p   Yes. I make a mistake.+ f9 V1 W+ l8 N8 m5 d- U, ?7 N
      PPI:     A PEIM to PEIM interface., W) f: |% E6 N4 o
      PROTOCL: A Interface between Hardware(or firmware) and software.* P. Y! {7 k' B- F& Y
      reference[http://www.biosren.com/viewthread.php?tid=207]
2 h+ d, t4 D. [   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.
% D; E0 Q% }4 m, K, D. I5 P+ a. }, h   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:- Q/ ], @1 c7 e5 D! }8 V6 E
     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。. v' B- D; ~& W
接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。9 U) j5 Y2 a( O: T0 k0 ?
   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
% e& g9 g, z7 Q9 f9 A9 h8 B中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。
) d' ^3 H* n& w8 q   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.

) \( e! ]9 m. o
, C' K6 [. U% N9 E( TThere are mistakes,9 o& }$ n. M# R  B
1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.
1 l5 u7 @9 X0 ?5 A% S* L7 m" `
& t$ G8 F3 i6 [- A$ U3 `9 N" K' l2.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; c3 ?0 F' A, SThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.4 U5 S1 Z. a0 l3 [: C8 C' w
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().0 y+ p# X3 D# ~! k. p4 Y

& O& M' H: g& pBTW, 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 大侠的指点。$ M1 R4 O6 Y5 L# t+ V( y
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
* N! W* x% q5 F/ \* E% W...
. L! _+ Z( l1 t  g3 _: k3 H2.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.5 H" t& N  E- X0 j: |, v2 f
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
: t9 O' S/ w2 B9 OFurthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().

0 S0 A/ |. R1 \1 K% y. Q    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().
* F0 F0 |* Y& H* p) C$ v    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)1 Z" E, _! b# W
  //
, g1 y- M* A* _* L0 Y7 N8 r* @  // Go connect any handles that were created or modified while the image executed.+ o3 C8 w1 I/ |3 l1 x2 E2 r& d
  //1 ]5 e! y4 @; ^. W
  CoreConnectHandlesByKey (HandleDatabaseKey);

+ q; _2 C0 @0 O: v& X$ Z  j这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey
+ ?$ J- ?7 B  |: p0 R5 G0 D而CoreConnectHandlesByKey会调用到:
* x8 H" K/ w% S# a2 Z. l* Z  //1 F* N4 ?- L5 F; \
  // Connect all handles whose Key value is greater than Key
: l1 s! W3 u& m! L5 X  //- M- J) l# T( f* }8 y
  for (Index = 0; Index < Count; Index++) {
; w+ Y) V; S. q: E& W6 b& i    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);4 ~8 Z7 ]7 A: _( A) r9 b
  }

8 c* L* m. F, z( D& k  m) B所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)
3 d/ F2 |4 B% K0 \, }4 Q是会去ConnectController的,也会执行对应的Support()和Start()才对!!# A8 U1 o0 w  v9 I
0 S* P. e8 k# K) r* c
不知道我想的哪里有问题???欢迎大家指正.
. [- P# c* z- w3 e! n2 R. f, j- j
; A) K8 l5 j7 W4 s+ l. m9 B  f[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。; I* s/ s% {/ x( G
一个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 | 显示全部楼层
了解了.% D6 n. c/ L# f4 G+ J: E
非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,5 Q5 v" h2 @$ s" k+ v
Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,
# h& t3 K" ~, o//, g1 Q1 x7 ]) B
// Notify the notification list for this protocol.5 n2 F" d( j# T4 n3 D3 J
//
) {* I) P5 K% _7 @if (Notify) {
9 l- O& F. E( n) Z  CoreNotifyProtocolEntry(ProtEntry);$ T4 @% s3 g2 k6 H/ ]/ a
}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.
4 p# ?# b8 |. n  A. e1 B! [4 W有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表
5 \5 d  i4 G( U: h请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?- W: q. O0 Y5 X4 E
谢谢!
3 c$ T2 _5 E% j! m
......& P0 I( t1 Z, h* R+ u

1 E! \3 s$ E" n* r7 i: C& @
. @% u6 U, k' G# w) J- }  I, {[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表 3 p2 d8 ~9 P* H. p3 y3 s/ Y- M
) U8 s0 F4 {7 P* h" u) q" @
......
) \! M* ]+ q3 z2 b, ~9 x
7 d' Y2 d8 X- [1 B' ^" K/ Z
Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?
- y1 o6 U4 Z7 U! C- ?- ?/ r如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()
) {/ A1 b( Z( y5 CTPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-6-4 17:32 , Processed in 1.215256 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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