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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。
; g! E: V/ H+ I6 h
% @4 c' D4 @* j0 M" XSEC/CEI:
# y2 c; w5 O0 j9 L0 v, J  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。
) n8 N: E% g6 A; T0 ~: A在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).6 f' y% c2 N6 J7 G1 K7 V* V( m' W
) ]2 T( Y. J/ b, J! d: K
PEI:
6 h' x8 |' E% P" D) Z7 D" B   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行+ O& `  K4 I- {  k. I
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。
' F. E& O& h4 e% e      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.- i3 u/ H; a+ N2 A# z8 J
          InitializeSercurityService函数,将Notify队列清空。
7 H( J% l( g+ E# b# `' Y          InitializeDispatcherData函数,将Dispatcher队列清空。8 \: V/ d: M) J8 O& j( x# d- d
  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。
1 Z4 O0 L5 d( v# y% q    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:0 y5 D" T& m' U  g- ^; a: r* _
      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {9 \6 l9 t6 E6 F! s
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},( `5 n  k5 ~7 B9 N4 Q$ R2 J+ X1 G
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi}," G& B* {" h9 Q; I
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},! l  w2 P; G+ U
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},
. ]1 K  x9 O9 M5 j0 v! t/ ]$ U! R       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},
- f, {% Y, Y0 h; r8 Y* J       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}
, D  o9 m! M6 f3 t9 S     };
% m, X4 u" i8 E5 _    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。$ o2 k5 ?' T  \: H3 |0 l7 ]2 b" e' [
   这些PPI会在PEIDispatcher中用到。% P# u) s1 N* ?7 `( U* M5 y3 t
   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。
  d" _  d. _/ Z. |" B! Q   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
0 L- n6 I* T3 J0 y! s2 I   SwitchStacks (
, i2 i5 M; B, s; q, `* m4 u5 F       (VOID *) (UINTN) DxeCoreEntryPoint,
/ T6 K$ f. G- J: O" `       (UINTN) (HobList.Raw),
& E: Z5 j0 S- ?2 q* z       (VOID *) (UINTN) TopOfStack,
( U8 {, ]! P. r3 F& S! r/ v% j       (VOID *) (UINTN) BspStore; u( s) O/ ]+ X+ i$ o
    );' h2 R, k' }- |$ X0 C
  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)
1 \7 g( _( k& W
& R! _3 `% H$ K5 _DXE:& b; e1 g4 k$ R2 G, A3 ?
     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。
5 y' Q: I! S+ I- R* l% g接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。
' v/ o& V! i( Z: J+ \! B3 a3 @   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
; a- V! _! |: q5 c& l中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。  B! ~( r* x! n9 g+ L- f
   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。4 L5 P/ q$ [. [: W3 W3 u' ^
     U6 L- ~, `! Q& i, M4 ]
Driver:- z3 P2 s* p* U. ]' ^
    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
" d3 _5 t8 ?! ?% b  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
7 ]  @- q, W1 s" [. f  U  [defines]2 o7 N8 Q5 n9 ~% s
  BASE_NAME            = OWEN1 w) r& H) }8 G% P. r. ^
  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110( X7 D' k: M* A" N) O
  COMPONENT_TYPE       = BS_DRIVER# V+ ?) R% A& v. g5 e- {/ }' r$ E
6 l2 o( n% S; N! h7 w
  BASE_NAME告诉编译器最终生成的驱动的名字。6 g1 Z- m& X, F+ M  J8 \& v
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。
9 ^) w! f- l4 i3 R  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。
, d8 o# z) e4 j5 I5 N  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名2 m2 L9 N; v% G7 D
  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {  H. Y* @2 n- l8 o
  {"bs_driver",  ".dxe" },
! P: T# g7 R7 C# |2 T: L  {"rt_driver",  ".dxe" },
2 b! x8 n0 K& [  {"sal_rt_driver", ".dxe"},
- m' ]4 J% h; F% o; G0 j$ f  {"security_core", ".sec"},
6 a, A+ b; P; v; _3 `# a* E1 x* E# b& ]  {"pei_core", ".pei"},) C, a" w) x& x6 Q& k' ]1 I* R
  {"pic_peim", ".pei"},, V3 E) s- E. z7 e. k2 ~" j( M
  {"pe32_peim", ".pei"},9 A) s& b: Y1 N  \8 \
  {"relocatable_peim", ".pei"},7 y+ @4 H7 i! s0 ?8 O  [$ u
  {"binary", ".ffs"},
! Q- ^5 o; h4 \4 ~  {"application", ".app"},( h  r  W* m. B, d/ A0 Y8 v
  {"file", ".ffs"},
) ?6 ~3 b$ D% F8 {  {"fvimagefile", ".fvi"},
/ \- w5 k4 ~  [% A' v9 F& P  {"rawfile", ".raw"},
5 [5 N8 J% o  Z" T3 e0 {  {"apriori", ".ffs"},
3 x6 Y5 s% g0 ]% \3 B" c  {"combined_peim_driver", ".pei"},
# \; a+ P5 |) x  { NULL,  NULL }
( D+ [1 [3 w7 }2 G$ |};$ A/ R1 n: k, ~2 O1 S
; @+ S' s- m5 s- W- y/ s  D* u3 u
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)
! j# g: o/ I0 S" |* ^0 C' H' M& {/ U7 G   
) S0 W' b  x) B. h- }         
- g1 a& @( d9 d$ |$ {& O! j! t* T  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
; K* w7 B, \8 h$ r支持; Z" r9 }$ I, p" z5 z; O; p
继续
- K9 f7 N. q, T8 _5 K5 I加油
0 c+ r2 B: ?) t, q
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?
  E  L( i9 G6 k& O3 y& R有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。
! E4 J! U3 \. Q  b) X/ e  I) E还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。
6 k9 w/ e! a& S( s. i8 R% z& \6 ~: E+ V% j3 K
支持!
回复

使用道具 举报

 楼主| 发表于 2008-7-28 18:52:31 | 显示全部楼层
! |& o  c: u! d- D  Q
小弟还在学习阶段,目前的目标是知道执行的流程。0 P0 j* {7 c+ h, a0 Z5 T* _( c" r
这里面有很多细节没有写,能力有限,只能自己知道,不能表达。5 b1 S  l/ A$ V* Y; ^3 h
嘿。。。。" M# Q4 B# l0 J, [6 U  S7 \# q
所有大侠们如果有好东西能给小弟共享一份。1 ?" t+ Y0 {: a! r4 R, m
! d1 o2 x5 T# x/ m0 |8 f% r
谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
2 C" ^. U5 V; E' S  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
) I2 X3 K8 J" e% u2 R中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...
1 G  a4 \* ?/ O2 ?; c" I+ t* y" u

4 n5 n6 S6 S9 O5 D2 N9 XPPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol, 6 F, G0 ~8 g/ E
For more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".% Z* D. h' M( e6 E
; f+ a+ F+ C6 [8 A; h8 s8 L5 C
[ 本帖最后由 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* |& O8 x7 S" Q$ a1 a( w
   Yes. I make a mistake.. m% J5 s1 {; s
      PPI:     A PEIM to PEIM interface.
; x3 e' c; v0 N% y3 {  W  D- ^      PROTOCL: A Interface between Hardware(or firmware) and software., e8 h3 ]& N4 h& I7 c, \
      reference[http://www.biosren.com/viewthread.php?tid=207]
8 x' N; y/ K; T+ [+ g   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.
% r) U( {2 ]9 F4 y7 i. Y. z   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:  Y6 @$ r$ q: X! o, }- b* s9 s6 T
     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。
( X2 N6 i; J) H! Y# N, g% _接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。
$ c9 M% u# D# o' y, Y8 Z   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
/ g2 ^3 y. G6 r% O+ f% Q- @中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。0 R$ a( w& r* O- ^$ w; O
   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.
4 g( V! `$ ]9 \

5 q( b  W# i9 J8 f3 XThere are mistakes,  v$ g& g, d* ]/ X! ^8 P% W
1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.0 V  N' O: X( O( k' v: D  O

' r5 U9 v4 f; Y* w2.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: H$ Y8 Z, r# |. M
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
& _5 Z) p# I2 \4 i$ v" Z5 K$ M9 TFurthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().3 M* u6 H' K$ E& ?7 ^

1 ^+ ~: X; r# r& B# T4 YBTW, 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 大侠的指点。) \1 B  Q& K" z! O2 U* ]4 v
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
" I9 T% S3 Q' n  A# e...
0 V7 R/ _& P9 w" G) o! n- B) M2.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.  V' O" J3 k5 C
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.' U6 c& h2 u: n
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
+ t) K( ~$ f! t6 F
    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().
0 p9 q1 K$ \/ x# |0 L    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)
, i. u& i; w2 m- ^3 U. a9 d  //
9 s, u% X+ H0 S9 Q  // Go connect any handles that were created or modified while the image executed.$ J: G( _3 x  F9 i
  //$ d7 w* c5 P2 s% L( t
  CoreConnectHandlesByKey (HandleDatabaseKey);
; V; y, m# D/ z- x) z8 ~
这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey) q# B* j  ^$ l- R3 W
而CoreConnectHandlesByKey会调用到:
, m1 v  L$ C! e6 Y  //
! g" v* J- {# b  // Connect all handles whose Key value is greater than Key
4 q: @. S! E- N/ l! ?5 L  //8 v% A- i1 P) C) b+ r  [( W
  for (Index = 0; Index < Count; Index++) {6 K1 q- t4 c" @
    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
7 Z' d& r" v0 x/ l# y  }

% g- ^: m& W/ i# s) _/ n$ Q所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)
7 i' Z1 ]) Z. {+ m4 r# ?( c是会去ConnectController的,也会执行对应的Support()和Start()才对!!
+ [9 ~* m$ L: s) L- T1 [2 E) N# Y! }, @2 I. Q8 B: P. h9 ^
不知道我想的哪里有问题???欢迎大家指正.; f) Y! f" \. b5 [

: S3 ]+ B3 |. U$ d3 t- Z2 c6 d[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。
; z/ x' X" Q- ~一个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 | 显示全部楼层
了解了.# L/ H+ X8 B) m5 a- u6 ~
非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,
6 q( _* `6 [/ T7 ?0 CStatus = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,
$ n: b4 Z2 H9 e; [! j7 n: D//
5 @+ [7 ]1 y) z* L' U// Notify the notification list for this protocol.0 q( Y; h, \( y5 p, v6 F+ O2 L
//
: f: q$ g/ H2 d' [if (Notify) {+ T( {. d* a2 V# u" Y
  CoreNotifyProtocolEntry(ProtEntry);
4 ~# b" s7 U" Q! _}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.
, n9 X( u2 x1 `2 V* T! b有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表
# I3 q/ m2 |8 _* z7 H请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?. l* S6 m2 Y4 ^
谢谢!

/ n, o5 `- O$ P: ]% e......
8 T& Z7 T) D; ^; e( G' V; ]9 o% k9 u' D- k5 o3 U( }8 {! q

9 F3 J6 y& Y" `1 s( b# T5 J. `[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表 7 D  v% {4 P6 X6 Z: M

8 F5 |( N: t; s6 U......
/ |3 T+ _  o7 e" ^& U

3 D) U- {  `1 _: l+ ?3 DSignal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?4 }* L9 n6 N& T( C6 z; }- F
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers(), S/ a! U- u+ T6 A5 ]" r% C9 |
TPL=30
回复

使用道具 举报

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

本版积分规则

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

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

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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