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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。
, a2 T: p" h/ z" F4 A
& [  ]3 h+ X$ u1 `SEC/CEI:
) z) ~) |; @0 D/ O0 T+ o# `. L# g3 p  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。9 ], f6 b2 y! b( A
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).
! i1 }5 @; J$ ]% o  X& h/ R* [' b
PEI:8 e! D# D% x% {# j0 q4 {
   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行: E. D0 L6 P' ~. E! C- K' Y- {5 @+ u0 c
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。
& h* V, d& _! v) V/ Y+ ]      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.
* c: U/ [4 I' g+ j' v2 Z          InitializeSercurityService函数,将Notify队列清空。
# }8 C. m' n! \& W$ t3 I          InitializeDispatcherData函数,将Dispatcher队列清空。
4 [$ u% ?  x9 v4 {. q  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。/ _: W% l3 w/ U" I. c: p! g8 `
    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:
  o, H: e  w9 J& x5 h      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {
9 K" G8 z* [4 {  x; w       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},/ g* C' n, i& E! M1 \+ W( M1 _
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},! E2 Z  N/ e$ T6 d: l* N
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},
6 M: s; h8 l6 I  |       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},
2 }. E8 r$ G+ }       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},' n7 C9 S* l( ~2 N$ d4 H: H
       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}* n# s" a. G0 \" r( N
     };+ b$ {9 s" \: V
    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。0 d, @" ?. d: |% J3 ^! r' Y' |
   这些PPI会在PEIDispatcher中用到。3 ]1 U; l2 V* [& o, x
   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。
" }6 e9 @6 e; F. M   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
) I, T0 `" k- }2 }- @6 Z   SwitchStacks (
7 D2 F5 P& r* ?6 t1 U" l+ Z) R       (VOID *) (UINTN) DxeCoreEntryPoint,0 R( u4 R* o5 j  z4 ]2 S
       (UINTN) (HobList.Raw),
/ K9 t( q8 h! z       (VOID *) (UINTN) TopOfStack,# X  S. v, Y6 q$ c
       (VOID *) (UINTN) BspStore
+ B6 ?1 V5 u1 k9 d5 L2 t    );
* `4 O$ L: m3 v" m" f6 H  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)
) B2 I. y/ h8 h5 e; I- h5 `+ w' j3 H) q0 z
DXE:' L6 m7 H' m8 V) T& I' m+ B
     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。$ @$ q, k2 u+ A2 m6 |5 d
接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。& o- y, E8 v) k2 i0 L: F# a
   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
" i& u1 C+ _# b0 A: P9 b1 {中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
1 j+ e% ?, t& Y8 ]  M) s' k   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。
/ G! X5 M. j- v3 b* x" O, C; ^   / E: ]. d( r& D) c4 T* X0 y
Driver:7 j( p: \" e; o  l' S0 ~
    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
# k- z7 C( B' e  M  大家请看一下我们的驱动的makefile.(EDK中的*.inf). [% q( J4 K, q7 K5 ^& j
  [defines]
  t4 K" j1 Y7 \2 `  BASE_NAME            = OWEN: |+ t" B8 Y% v0 r$ ^
  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110
1 ^+ P" e; K, W1 H6 I  COMPONENT_TYPE       = BS_DRIVER  e" @( [8 o, m* t! ]) P
( w3 a  X$ `' f2 r) C/ f4 O7 d
  BASE_NAME告诉编译器最终生成的驱动的名字。6 u7 k2 M: A$ I" T3 }1 W; M" c6 x
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。  Z6 O* \" k& i$ d0 _/ v
  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。
/ H  C# S! K' m( ]  ]3 E$ }7 {  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名6 H* H' r, M6 {) H1 S, Q9 g
  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {) W+ i/ E8 p+ }2 t# f" W1 |
  {"bs_driver",  ".dxe" },
3 u5 b6 C) E& \* @" }3 ?! w1 W  {"rt_driver",  ".dxe" },0 e* Q) S' x/ M& X. A( m
  {"sal_rt_driver", ".dxe"},' U6 W( n6 f# ?$ G1 Y0 @
  {"security_core", ".sec"},3 n/ M! {8 X* A& e1 r
  {"pei_core", ".pei"},
& E, Q% y% h, G; U8 B  {"pic_peim", ".pei"},6 s# |# y, L- E. `0 A
  {"pe32_peim", ".pei"},; y. y- T. U2 w1 x% `/ c
  {"relocatable_peim", ".pei"}," C1 t4 |& U1 f
  {"binary", ".ffs"},6 J2 ?* B  u! \! E' W
  {"application", ".app"},
' |. M4 q8 C, k( g  {"file", ".ffs"},3 `- q7 @. D* }
  {"fvimagefile", ".fvi"},
$ W4 ?' r7 ^8 y3 H  {"rawfile", ".raw"},# z, A9 C$ q* Y7 g5 j: L
  {"apriori", ".ffs"},
/ j: M$ N, n2 t* N( H6 H8 a  {"combined_peim_driver", ".pei"},: G+ e& @+ R; Z4 `& r! b
  { NULL,  NULL }
0 Z1 e; @7 E( i: N9 m/ X6 B& d};
' O1 I/ v! N) {$ N
: b9 d+ k# z6 J6 x- l& T了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)% R) ^( [; V- ^; B9 e
   7 }4 L! a% i3 H; n
         ! E8 L. i1 I% m6 Q' Y
  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
9 w, a" Q$ t3 c1 n7 e支持* `  h) }" R0 S6 P: m5 x' I
继续+ k# q  _3 @  i* `9 ~; @
加油
/ ~, G5 w- T( M3 |7 n
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?: a# F+ D* s. v' |8 E
有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。, T; c, i" |" k. H$ I5 a4 e7 p  C; c1 `- @
还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。, W3 [0 U5 k8 Q& E: F- u) j  L6 s
3 d/ A- R: T# L  a0 j/ k5 R
支持!
回复

使用道具 举报

 楼主| 发表于 2008-7-28 18:52:31 | 显示全部楼层
  t) @! ?5 x8 f/ q  D
小弟还在学习阶段,目前的目标是知道执行的流程。
- Z# J% n# Y. e这里面有很多细节没有写,能力有限,只能自己知道,不能表达。: {4 @" Q! J. M) y4 P3 u7 Z1 F
嘿。。。。( g2 R5 Y4 P5 `( _' j2 c7 z- R
所有大侠们如果有好东西能给小弟共享一份。, i# N- c  q9 B; a) i6 b+ T

* w( ^9 B5 m& N# w/ F. h谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
* h# j& x9 e) I) ?6 X# d6 r2 _  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
1 L$ z  }2 D& o中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...
: r5 {) a8 e; ]7 v- S7 c

$ \* l  r7 M% fPPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol, ( _4 J( @, O- n$ u. |" v  M: G
For more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".6 L' L7 P5 {0 I/ q0 v& }

+ w, Y& E& I: _[ 本帖最后由 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:
' O7 W( [8 D, I$ J6 N& o   Yes. I make a mistake.
6 P1 v0 z0 `( N9 x5 L- R      PPI:     A PEIM to PEIM interface.. Y5 `% M$ B% D4 C1 ~  ]( C, X
      PROTOCL: A Interface between Hardware(or firmware) and software.5 ?* ]  z( a6 }% a) p0 y& J2 a. B. r7 d. s
      reference[http://www.biosren.com/viewthread.php?tid=207]! |6 U. H& b; f
   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step./ m* L: N( ]$ z  s, N: \. x9 ~
   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:: e8 P. [. D4 o1 ]
     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。
, F/ \1 b" U! @接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。1 V4 \+ U2 D. c7 D# H4 u
   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
! L+ v  V! o6 o8 a中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。. ~& p; O) s. _3 f
   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.
9 r9 Y# o' E! Q  Y

! D9 B7 \: p+ N) J  kThere are mistakes,  v; B8 e( W$ T& ?0 j
1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.- L/ F. v! D# O0 K0 @! u8 C

% `- Q7 s6 ~; ]  R2.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 m4 R) r" K5 b5 FThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.! g/ k3 g- s8 W+ N1 c9 I  e
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().& z$ O2 x! a2 y9 _, `

% j3 h0 J4 K0 q5 \. F  VBTW, 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 大侠的指点。
* C( {- t( I. W% ~. p5 u
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表 ; ]! q7 K2 i, x3 ^% M) ?+ r
...
* t' \" J9 U% m  l3 q) i. i5 F3 G; S2.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.
- M8 v4 J; C% W# d8 [3 V  lThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.- p6 U; Y( ?3 k  z) [4 x+ c
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().

% ]& _) F# z9 r- A; j3 @0 W' R    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start()., S2 |! a. t# p% u9 ?" x
    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)
: a" O/ C5 C( ?2 P3 F7 l9 j  //) L5 I; }  Y5 P+ W$ Z# v
  // Go connect any handles that were created or modified while the image executed.! M' u& T1 T5 h0 N; l: }
  //5 z* _  n: H; y& d, I. k$ A% K- G7 d
  CoreConnectHandlesByKey (HandleDatabaseKey);
1 w& j! `9 q2 X, w1 e% S
这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey
" u7 o) X  ?6 h3 z: N" O, I4 o! W而CoreConnectHandlesByKey会调用到:
  M; d' t% N' I# U  //
9 v! k6 |" [/ y) g6 O6 d6 o8 C  // Connect all handles whose Key value is greater than Key: g! m% p/ o2 x, P4 y
  //% z2 ~! J0 ~, Z
  for (Index = 0; Index < Count; Index++) {( k& b, ~( H" {$ T: p8 k
    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);, a- R9 X8 Y  N, x
  }
* e, V2 }. h$ z" Z
所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)
: y9 s0 [  U6 }% P3 h7 o是会去ConnectController的,也会执行对应的Support()和Start()才对!!; d3 V  N4 D1 U" _1 A- }

+ h& O0 V1 S' U; J不知道我想的哪里有问题???欢迎大家指正.
2 A3 N7 G& p; {# t& R7 @# E' O7 W; _  N/ k- }  ^- e, W. S1 V
[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。
* C' \3 }; e- ?0 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 | 显示全部楼层
了解了.. o5 n; h) u5 c" i7 f% d  a. @
非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,
: b4 r2 ]" v1 P6 ]1 LStatus = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,3 q5 N. ]: E: _  j$ _% \
//) a4 \/ C' e* q4 t2 [/ y/ e5 @
// Notify the notification list for this protocol.1 ~! o/ i5 u8 U* F# V) x
//
' J2 Q6 D3 e8 w2 g3 Oif (Notify) {
9 q% B8 K4 m7 w( Q1 L& E  CoreNotifyProtocolEntry(ProtEntry);
6 U' p3 f* B# o) m9 y1 t}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.
* Q2 b  e& e; _有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表
' n1 U' e! P* [请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?
. i) b+ m- @$ A谢谢!
- A) N2 M0 k% [
......3 C' r; c1 P, ~6 t. {" J, |
" K9 Q: T$ C! C

7 W) P! f) u' w( |* b) b[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表 1 D- [6 l: Q  @9 V9 g+ t

+ J4 |" O: p: g  m......
, _/ `% r' ~3 Z' v# T1 q+ v7 O  `; k

$ W7 I) b& c" }% }Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?" a, z- Z% a# _5 u& |
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()
2 o1 F5 ^- J$ ?! _  iTPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-12-1 10:29 , Processed in 0.113135 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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