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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。% p. k$ |# Q6 A0 a% \

0 q  y$ a2 x4 f$ `4 K( _' WSEC/CEI:4 S1 M# x4 `/ B: I  T5 ~
  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。# S' S: j6 X8 m5 u2 ]" ]
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).
- s' u8 K# h4 G
  p* {: w- ^: I0 E2 }+ _' PPEI:& I) n( l; T, v9 k
   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行! f4 P# h, w8 E& D1 B
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。
5 y3 I& H& j4 c; n      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.2 L5 \  H5 Q' J4 ]- E# j' z; h
          InitializeSercurityService函数,将Notify队列清空。* [# p6 P9 E! \' `
          InitializeDispatcherData函数,将Dispatcher队列清空。
+ Z4 z" `0 ]8 Y! W, |  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。
) X7 Z5 h* R# g. W& w: Z    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:
8 ?& D; k+ {1 K      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {
. o1 K+ w2 r8 q+ [* {4 Q% Y0 D       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},
0 @8 M. I* D, b+ J. M       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},! d" P3 d+ j% X5 @; |
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},1 a; }8 H/ \7 r/ C4 h- ?* E) k% F
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},
* Z  X7 K7 m1 z5 r       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},  O! B" k9 w6 L6 L9 {. g
       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}4 E1 r5 F( L1 J
     };
6 h7 k( Y4 K6 i$ y( j2 G- L  z    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。
% l5 J% r: v* ]& d/ e   这些PPI会在PEIDispatcher中用到。
/ r: s* F. y$ f$ K   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。
, _# D$ t* B! @3 Q' g7 o1 y4 ?   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。3 |- B$ u) Z, n; G& t: H+ `
   SwitchStacks (( ~3 l2 y& N) X
       (VOID *) (UINTN) DxeCoreEntryPoint,
' I3 l! r* X" C' ^$ ~( C) g; O       (UINTN) (HobList.Raw),# t1 _% W+ c- S& j9 z/ o
       (VOID *) (UINTN) TopOfStack,
0 C1 Z, m8 M- ]2 ^5 u) |8 ]2 y       (VOID *) (UINTN) BspStore
* s: }8 \3 A' E. G  |/ `    );
3 i; J8 s3 U) V& d6 j  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)5 u4 m- L( d, Z

4 l; g4 G0 c; g0 mDXE:
  j- }/ P: R1 ^* M, r) G( r' l+ I     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。/ W. {6 _' \1 R: m% t  X1 `
接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。
1 G) F0 D  N/ w- J   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
) J  A# l- Y& h( Q% J中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。# A; y# U$ Z3 t  |3 Y' e' U
   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。
6 l2 u1 s& U) E6 o% J5 Z   ) d1 \+ I$ ?5 ~/ I8 s; b" y
Driver:
6 S, m1 y! d3 ?9 u2 U( N    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
6 ?- U0 e3 }& j; G3 {( K1 S' W  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
% o( g9 i5 v7 l- G1 N0 N8 }8 Z: [  [defines]. i9 j" v4 Z" P5 l, T: W
  BASE_NAME            = OWEN8 ]( E4 S1 U# Q3 I
  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110, w3 I9 W5 G# \7 B( `; M2 E
  COMPONENT_TYPE       = BS_DRIVER
, S0 u% S+ C2 r0 {' o% _* J0 A+ O5 Z+ g3 p( {, X% v! O6 T% }, W/ g
  BASE_NAME告诉编译器最终生成的驱动的名字。
, s3 g, q% S% n1 o0 E% R  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。
* T( U' Y( V- e+ e, t& ^  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。( X, K" L' s, o0 W1 ~" B2 N  G6 v
  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名
1 f, w4 D$ u1 p, r; h- \7 N/ Q/ D  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {
# j+ l5 {1 b# R- @: ?  {"bs_driver",  ".dxe" },' |: d' ~* s; q  t( `; _: Q
  {"rt_driver",  ".dxe" },2 Y2 j4 U8 F( J" C) O6 \
  {"sal_rt_driver", ".dxe"},7 X0 V6 F6 Y& P. [3 {5 S
  {"security_core", ".sec"},# G* M! i& @3 f
  {"pei_core", ".pei"},: p3 c# F+ Q8 j. Z* ]
  {"pic_peim", ".pei"},2 P; a4 Q% T4 l$ ~* Z
  {"pe32_peim", ".pei"},# T+ C8 d0 D/ o" n% H0 o* o: m
  {"relocatable_peim", ".pei"},
- M+ n# K9 U9 A  t* c6 M2 h  {"binary", ".ffs"},# J# Y8 I6 x% x4 r/ N
  {"application", ".app"},6 M. c3 j; [6 c4 A8 L) ~* C
  {"file", ".ffs"},
- i, E+ @' H  F  O# D" v. h% m3 `6 ], F' E  {"fvimagefile", ".fvi"},  r& J2 @7 B5 I1 j0 _6 q; S; e) a
  {"rawfile", ".raw"},7 q4 {/ f9 C7 _
  {"apriori", ".ffs"},$ g7 |. \7 C0 h% d% |* A
  {"combined_peim_driver", ".pei"},
5 ^9 P0 v9 F1 |9 m" G" O  { NULL,  NULL }" W) k0 |1 B9 S- H  V1 y
};4 W. {. k) Y8 A/ m! j

2 J  X) l9 F7 u# J了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)5 F8 Y  Z  F2 E( }$ e
   ) K# \. t. s! b2 t1 t: E& d, H
         
5 a  F. [( @9 O* Y  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
) E$ T8 k! Y! [' }  L9 F支持
$ d- a( z. T, L1 m继续
- ]' H9 A7 h4 w% G: A( a: k5 h加油
3 w3 `& {6 }7 z) R! h
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?
; O# m/ U, F" q$ k; n有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。. E9 l. f$ W: P8 e3 w
还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。
* s/ g3 a( m/ u7 _3 y+ B5 l- }- o9 q
支持!
回复

使用道具 举报

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

( |+ B7 g* r' S) A小弟还在学习阶段,目前的目标是知道执行的流程。
# ?2 A9 r/ S; M  P这里面有很多细节没有写,能力有限,只能自己知道,不能表达。: K8 A+ z0 T9 g) ?, y
嘿。。。。
: n  Y3 t5 E1 \7 P/ k6 ]  }所有大侠们如果有好东西能给小弟共享一份。/ I0 s5 w/ }/ v0 T& v; f$ d

5 y; V& e. ]% h/ [谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
1 m# B6 M% ]# C  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver0 p6 _6 J; I. t/ z( s$ T) W1 }
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...
1 ^: ]0 L; X# e5 |8 j& J
* q" E* s/ p) B7 J- h: p
PPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol,
# H+ @" m0 L7 O0 t+ f6 A! SFor more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".7 R6 X9 K; N6 x' q$ f

  g# V: d& i: P* |. @. q& A[ 本帖最后由 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:- u& W0 n6 q# x3 ]
   Yes. I make a mistake.
; Z9 T; F, w; I% U      PPI:     A PEIM to PEIM interface.
3 [" _; Q, Q. k! R. q0 t      PROTOCL: A Interface between Hardware(or firmware) and software.+ x* Q3 C, b6 h; B, I
      reference[http://www.biosren.com/viewthread.php?tid=207]
  ]: V% I3 u0 M& d" t   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.' A3 h. A7 Y6 v9 U1 C; b9 L9 F
   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:
' `1 l2 y  e+ E6 i     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。$ M1 u( n/ R* u: k' D
接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。
& L2 D+ N4 t$ w% g$ V/ u6 l   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
6 Q7 O& ~8 S/ d0 N中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。% j% v5 V+ P: n9 Z9 v
   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.
8 e& f* ?$ U' K) V" z) ?' _  _! h
! f4 Y# B3 T& N8 Q& y) D
There are mistakes,6 _. d" |; c( j  _( r
1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.
5 R- N& V& h8 a1 Q9 u- Y5 J  I! x4 b. T* s. S
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.' L/ H/ c6 n" R$ l
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.5 r% x/ [+ J% I$ A- `
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().4 c" U3 \- v0 g! l! R. @3 N+ L

+ L5 }6 p* |7 J$ Y1 @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 大侠的指点。
6 w5 u" t0 Y; P, {1 c1 |
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
. d5 H) x; @# S( V9 I) s0 T...7 |9 M% q! R9 D$ W
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.
( L; B  e; o; |The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
' G# \' z7 M, W* W. N5 |% dFurthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
, f3 ]' z9 z% ~5 }( b9 r" C2 a6 r5 c
    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().( |3 w" j( S; H, x4 H
    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)
3 S& b; G2 Q  A  o  //5 \! F  K7 c. a& x$ {/ S5 c: I& a
  // Go connect any handles that were created or modified while the image executed.0 s( C& d1 B. U
  //% e# }1 J% E8 B4 P$ F; \4 W
  CoreConnectHandlesByKey (HandleDatabaseKey);

' y3 B9 S  P+ |这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey
2 C9 R! ~. T+ Y) `/ j而CoreConnectHandlesByKey会调用到:5 T2 X) [- p2 @! b. C
  //- d6 L& L: \, r% |. H2 S
  // Connect all handles whose Key value is greater than Key
, ~: m% U' H: t3 I8 P5 v  //
8 o& x0 n# K( D% |0 v  for (Index = 0; Index < Count; Index++) {
( z! K. W" N0 X$ B0 q    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);1 |% `7 Y2 S& H: y  g' N
  }
) u  W) q/ e3 Q# `8 P
所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)" l; S5 n6 N7 |$ l$ Q& t
是会去ConnectController的,也会执行对应的Support()和Start()才对!!: m8 n; O$ O" R' W
& O: @. e$ ?8 ]4 `$ E
不知道我想的哪里有问题???欢迎大家指正.1 c4 p3 x+ B3 F  |. B# t

2 D* q3 X  r7 \" J2 y# F[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。
3 R8 `  Z# b' q7 w# p. c2 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 | 显示全部楼层
了解了.# @: w3 ~# S! o8 ]" B+ W
非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,
% `% k4 P4 |: N5 E! E/ K  `Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,
" n9 R- u8 T) f1 b' _//* G' ^: O7 p1 o
// Notify the notification list for this protocol.6 ]% L; X+ g- N
//6 v. l; t' [& h# O6 v  n6 o4 b
if (Notify) {
/ r& H$ F& S2 ]5 Q" k( K" z8 i  CoreNotifyProtocolEntry(ProtEntry);
# ?# o+ C& o; m7 b5 Z- i}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.
0 Q! q- @2 ~: _! j7 |4 y7 |1 }有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表 , k3 L: ~" m# b+ s- G6 ]" s( g
请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?
& k& A6 Y1 Y. |4 X# V  n- M: ?1 Z谢谢!

* ?" @+ A! h& j- K9 v0 ]......7 }# g  C3 i# ^2 s

; Y4 `9 H: k# U. U+ j! X
" m$ I% t+ H: U& ^" q# }[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表 + H$ l- }/ h: L' S+ F
& J1 y$ D: S. {# s$ T! R
......6 I" X2 l' R# F! m3 e
. U8 M) U: w/ G  c
Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?6 C4 G  m, _% _- l0 l4 {* v
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()3 ~& a. ]: a/ j! _# J
TPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-4-29 04:51 , Processed in 0.061484 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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