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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。
; l' X, g& B; z8 F( \0 P! E3 C- t4 {- l( G
SEC/CEI:
6 _8 o+ X9 D. m& D- N% a" ]4 f2 J  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。
' Q4 C2 ~+ [% q* P+ S) \在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).0 q& U# m7 f( h- [
9 ~3 C+ M3 q0 k0 r6 w
PEI:
4 m2 i0 I  k  R! Y; b. ], ]$ W   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行# c/ Y$ _; T4 M/ Q, y% j3 a
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。: h( n; H# x3 ^! E+ x  g# i1 l) C
      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.
5 l2 ^9 ]  H7 Y4 }' e          InitializeSercurityService函数,将Notify队列清空。
( ~! i$ q1 ?1 B1 _. w1 p( T          InitializeDispatcherData函数,将Dispatcher队列清空。, B: J  X7 s3 h! N  r& a3 G* E$ I
  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。
$ L- ~& p) U' q. o$ X$ X; p    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:( s' }) C- h1 o# t! S
      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {" o$ k6 S1 C" }5 D
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},# w9 O; X+ V1 U, {! E
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},5 E% z+ \$ q7 h
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},8 z" s2 w) R0 y4 `+ K" }) S. c( O0 i
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},  ~( N3 e0 H  X! d0 D
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},$ m9 J  ~6 U; R3 k
       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}
: ^1 L5 S4 M$ E5 [) H     };
6 ?* }+ q+ q- j4 @8 ?- ^3 {2 l5 u    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。
  A3 F. u. @5 {2 u* \   这些PPI会在PEIDispatcher中用到。- x+ l7 J1 K" a) F6 [
   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。
9 D2 @. z- o& {: [9 s   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
9 T+ D1 B5 N8 ~& u" r$ }0 |# r   SwitchStacks (1 X& i' [5 C( r
       (VOID *) (UINTN) DxeCoreEntryPoint,: V# q. \) `& \) l  U+ G
       (UINTN) (HobList.Raw),
5 c  d2 p* H# T! T( S& ?       (VOID *) (UINTN) TopOfStack,! g) O' z1 J- v- i
       (VOID *) (UINTN) BspStore: m0 Z% i- Z  k2 F" b- s9 o% @! y
    );: u3 O$ B; V, t" m! q6 D- H( f
  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)
3 c- I1 s1 J4 B6 h- w
+ ~: B$ W# ^; g! ADXE:; [, r' Y$ W& ~7 L3 c
     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。
- c* N1 Y# N: O接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。* l& x, u' s/ Q+ \2 @5 V& t8 w
   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver6 r. c& `  _$ L: G5 p5 J9 S! v
中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
0 i7 B5 ?1 W: |" x  e   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。
6 u% D6 E: D: }6 _, W/ Z1 s   5 V+ w$ o8 N  ^6 g
Driver:" ~& {+ r! p' N) h  p5 b
    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
) I6 i6 \9 G; ]# d# F0 f  大家请看一下我们的驱动的makefile.(EDK中的*.inf)  I" u3 d( x+ x9 Q- Y: P- G/ T
  [defines]/ P" l9 \7 _2 g4 O+ o6 h# D- e
  BASE_NAME            = OWEN
$ R1 k9 f9 S) e) [" w0 M  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110
6 v% x. L' [( C) W# W7 c+ ?  COMPONENT_TYPE       = BS_DRIVER
5 ?7 W) T' x& o6 u/ B- {# H! @2 r1 ^- `5 c
  BASE_NAME告诉编译器最终生成的驱动的名字。: R( x! H1 C2 K( F
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。
' g5 n5 D; r5 @% m& ~& B6 k  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。! A) o# \8 d- t! }% n
  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名$ Q0 l/ {) [2 f+ n6 W$ e
  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {9 U9 c+ p, s, l) a: |6 w
  {"bs_driver",  ".dxe" },
8 Q" J. y( F8 u8 v. H  {"rt_driver",  ".dxe" },! n+ E# E# s* c3 z+ W5 H3 S
  {"sal_rt_driver", ".dxe"},
% p2 R* t  ?2 p0 c/ S9 \: T: ^9 t1 w  {"security_core", ".sec"},  ~: C2 g* {$ V. Y7 d
  {"pei_core", ".pei"},
! e/ J. e- v1 w8 M$ M2 m  {"pic_peim", ".pei"},
' k. Q2 j8 l( d3 F( a  {"pe32_peim", ".pei"},; O5 J) E6 X! `& V4 {( @2 C/ O
  {"relocatable_peim", ".pei"},
2 K, @/ l- C8 Y3 l) g2 ^+ h8 e  {"binary", ".ffs"},# Z% G* m5 ?# j3 Z  O
  {"application", ".app"},
  K& z/ t( a  e$ H; x, B4 q  k  {"file", ".ffs"},7 J5 T; b# Z0 B/ b% P
  {"fvimagefile", ".fvi"},7 L3 O1 r- S/ S/ h0 L8 e! w7 o) p
  {"rawfile", ".raw"},! M' Y$ Q8 A% h3 ^) Y/ P
  {"apriori", ".ffs"},# j5 ^9 g- h- _" v) j2 ~8 I
  {"combined_peim_driver", ".pei"},! I+ W. S: g, H9 R  U
  { NULL,  NULL }
: t  D6 u* W8 n# x};
" A- D0 c, J, w+ {' `: ]4 \7 `8 }1 w  B# W5 ]0 ?
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)2 ^6 }/ I# Q& b6 c- C1 H
   
; U) ]3 H& I( a$ s1 {! h           S" X/ k8 j/ X$ h1 P, A1 J4 g
  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
6 ?. h& F8 t" t, P# N, o支持9 a# R3 V2 c  x9 }5 ^7 Q0 u
继续/ I# i; ^2 Z: C- u) n) b; U( Q6 _
加油$ N: s' t8 `+ W6 x: L
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?- l( }7 ?5 X: C0 n: C3 i& d
有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。2 s- Y! {* u8 t0 \
还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。. ^; l* ?1 m8 d: P3 W, |

( x" q) F2 J0 {, D" }支持!
回复

使用道具 举报

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

% i( Y3 s7 ?% E. a3 j小弟还在学习阶段,目前的目标是知道执行的流程。7 ^9 h1 Y. Y6 y7 K1 a) O% }; j
这里面有很多细节没有写,能力有限,只能自己知道,不能表达。3 @. N$ n; Z) P) l/ {% {
嘿。。。。
9 q" p% d# o- {所有大侠们如果有好东西能给小弟共享一份。
; B6 ~+ ?0 v5 ~% ]  x ) k# T0 P2 |5 j) |. q# O
谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
& m' P( @) A. \0 m8 o  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver* M% q; t# n  X; {* G- g) S
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...
  I7 b8 L# a- T
$ V3 l% J! J) |& S
PPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol,
9 d- I- c/ @2 \/ WFor more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".# Y. i7 u4 |9 C6 N
  V, b  \' L# [. m) b7 a. G* 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:
* i. _- s7 u- q9 S# c( @' A   Yes. I make a mistake.
( m0 {$ \/ P# q6 z      PPI:     A PEIM to PEIM interface.) a( |& y7 ]7 ?
      PROTOCL: A Interface between Hardware(or firmware) and software.
) d( q- ?* \5 O3 E      reference[http://www.biosren.com/viewthread.php?tid=207]
! h& ?4 ^3 g  Z% k8 w/ q   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.
8 R$ S  L: n# `, Q- h, @- O   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:
; y2 w, ]) S6 U  l5 w/ W9 J     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。4 P4 \1 |! T! Y! E- W
接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。
% R; b# }5 ~) b$ o   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
% c2 O6 m9 ]8 c/ C3 c) Z中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。  H& ~, W1 h0 w& p8 z8 w1 G5 d
   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.

- q) n& I( ~# F% k! D. {$ ?" }
" W% Z! X  [4 i. `" DThere are mistakes,
. R2 m" u( k- j* {4 ^1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.# q( K) C4 H4 J; v
3 M+ i1 |8 [- M. K; q& o: D
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.3 b" @0 X: G1 ^
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.- O% ]; X% O  f  r* M
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
. i& ]' D2 K+ o% i# d
9 }* C5 O5 ]7 [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 大侠的指点。
4 m4 @1 }, @. Q, |8 G: }* H. I
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表 5 ]5 {& ^' w' A6 |% u$ e. F4 U) i
...6 c# y7 o7 N) D8 L
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.
2 @, l/ A0 a. @7 g4 @' z) B0 w  ZThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
  S: \9 j6 e8 h" E9 v. @; KFurthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().

+ ]/ x: G( ^; |    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().
8 M( T& W# [9 O$ {$ ~$ Q" |    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)
+ T- p# {/ b+ @* v; Q+ r4 t3 Q7 A  //6 I- J# ]4 a( o3 T' Z* j
  // Go connect any handles that were created or modified while the image executed.
% d5 n4 c; M( X  //
+ `. F7 o2 _. J2 ?) E  CoreConnectHandlesByKey (HandleDatabaseKey);

8 R# X& G% t! B$ U% L这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey
9 Q& c' @" ~' [而CoreConnectHandlesByKey会调用到:
  o& y. b% s- w& @- a$ H1 @7 l  //
. h, n. S$ c- h& X4 \6 U0 i  E. s1 }  // Connect all handles whose Key value is greater than Key/ H7 O% q) T9 W" @6 ]: t0 b
  //
! o; |! J1 U9 y  for (Index = 0; Index < Count; Index++) {$ M2 w3 G; T9 U3 m4 U3 \5 y4 D! n8 S
    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
7 O# `$ w- E6 m0 U; x  }
5 t0 v3 j' N) P0 P0 C/ D8 o
所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)  E0 |! }- c  z
是会去ConnectController的,也会执行对应的Support()和Start()才对!!3 G+ ]$ B- Y6 b* q* |& ^. Y
: w; H- Y; M' N; h) \
不知道我想的哪里有问题???欢迎大家指正.% p8 K2 Q, l, ^: Y, |! R- f5 n: r/ F
& o/ h& G& _' B
[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。) ]" X/ A  z6 |: s) k- ?4 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 | 显示全部楼层
了解了.
# {* b1 Q/ |1 G非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,8 n$ S5 y  d2 Z" z! I) J# M
Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,
( @- \: A8 k& F0 Y- e+ ]6 K//! q8 y% s& f8 g4 T+ v
// Notify the notification list for this protocol.% \' y' ]1 q9 x$ {
//
2 E% `2 g" b8 ~2 G. _! w5 mif (Notify) {. ^+ A+ R, F+ a" M; {( A. M
  CoreNotifyProtocolEntry(ProtEntry);1 P2 q4 |! j& y2 b- v
}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.. I$ e; A3 }: q: \/ M
有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表
( L0 S/ y* b0 q请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?+ ~# ?. e3 ]4 H3 M3 n
谢谢!

  y: F8 f* x+ G7 |: t......' I# H: X1 `: C/ Q5 L( m

% G5 \) ^9 U/ L) B7 g/ o+ H! P( t, X, S1 C2 Q4 x
[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表 . t$ o% ^9 F, q

: A* d5 Q: v& f  Q0 Y0 y6 m( p......
2 }4 F1 n  w6 k1 k7 z
, l5 e' _. m* p, r3 D
Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?9 K( U1 w  _0 W2 z( z# V4 i
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()1 Z0 {$ ^3 P2 r2 G7 d5 m; J* u
TPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-12-17 05:19 , Processed in 0.152127 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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