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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。
0 v, }- g9 p+ j. e+ L5 ^
5 v- I: p# X/ p' ESEC/CEI:
- a( |& y4 E* a5 I: u( h  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。/ E7 I- Y/ c6 K# q
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).
& p7 L* G- W; A. Z3 R& K$ w+ n: f
# m! n' P& v1 O% @" vPEI:8 X  x7 ?+ W- ]$ ?3 X
   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行% _/ J8 d# [9 `  K
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。/ J. ~& a/ i/ C" G/ K
      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.# n9 ^& F0 a, D: R' _( m( f& B7 n. z
          InitializeSercurityService函数,将Notify队列清空。
3 K: }; a2 s) q* J$ e          InitializeDispatcherData函数,将Dispatcher队列清空。
0 m% K' w/ v& t+ S$ x8 m" G9 g4 Y  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。5 J2 ^  B2 ~% r* c- \8 ]$ `0 M' f# g) Y
    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:
, M+ H; `& Y6 w' B* }' k      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {
1 r. z- x" s+ ], D! E' h1 ~       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},  `3 F( T* h: }! S- _
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},4 Y* u4 n: V6 M0 |3 E+ a" V3 p
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},9 h/ s: y3 T2 i+ M) {( d3 q
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},9 m3 l7 h1 w. E. T2 C- W/ a" I7 z
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},: |) ]# Q. D7 S! x! C7 `" B' d2 }
       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}) ~) W5 c2 n: Z8 X
     };
# i) S. r2 x# i  a% @% q8 k    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。
  N2 {3 a. s1 n5 f( K2 t   这些PPI会在PEIDispatcher中用到。
, q# |$ \) v6 J( O; a, X   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。, z+ Q2 N6 X/ O6 [0 t' Z& d
   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
. D% n0 S8 K% [- Y   SwitchStacks (/ S8 \; Q" W5 E( p  k' Q4 t/ s
       (VOID *) (UINTN) DxeCoreEntryPoint,  M9 X) U) h  N2 |5 t+ b
       (UINTN) (HobList.Raw),
1 {- k1 M6 U  P# d$ n       (VOID *) (UINTN) TopOfStack,, f, d  K! g/ U" j& h' D
       (VOID *) (UINTN) BspStore
# B  ~0 w, I3 J8 Y( E+ `/ X    );7 S9 z* h& K6 Y8 W. h
  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)
1 F; w. u3 i9 |" L% r9 ^/ N# {( p- R/ x( p/ `" @9 a. j
DXE:
6 X, }8 a& ^5 a2 l/ H% Z0 \. i     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。
+ \. ^% M& V' w8 f" p$ `7 d接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。* J8 e- S1 f4 W) x& K! _0 T
   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
( l6 I' q6 i7 q8 z1 Y中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
; K5 ^, U: w# K0 M   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。
0 I& ~+ G+ J; f9 W   & O& ~% A" Y4 i0 d& z. j
Driver:
, @9 w: ^; \# c    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
! v% A; n, `- r' b2 z  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
5 X8 `$ f$ b  g* E  [defines]' F& P( V( N, V1 p
  BASE_NAME            = OWEN
- i/ b+ T# N7 P0 v$ N" g  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110, i$ H. N/ u2 v' R1 l
  COMPONENT_TYPE       = BS_DRIVER$ R; W- s9 N$ K8 c, O3 k
  g; Y. T; H( Q; O
  BASE_NAME告诉编译器最终生成的驱动的名字。
4 Z/ [" h  T. F" l8 D  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。' W8 p" O+ _1 N- R1 w% _
  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。
, T0 f, @/ Z/ a9 x& p  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名; q% D- H6 \& z; x* E" n6 O
  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {5 ~9 J; O# |  Z, l
  {"bs_driver",  ".dxe" },$ |+ I+ @. C9 K4 I* n  |$ D
  {"rt_driver",  ".dxe" },
0 X/ b8 T7 r! }' Z# q# c/ J- w  {"sal_rt_driver", ".dxe"},( g# D0 ~7 h$ R: A  ~
  {"security_core", ".sec"},
1 B. l: q: h3 ^/ O* f  {"pei_core", ".pei"},  p( v0 K! S- |- b: j. z- G# J
  {"pic_peim", ".pei"},
/ p0 u* [6 J) G% W  M  {"pe32_peim", ".pei"}," x) u2 W* g* Y0 I* [) H
  {"relocatable_peim", ".pei"},
+ \) w3 i  g& d  {"binary", ".ffs"},
* V# z. Z( s$ i5 h# }6 N  {"application", ".app"},  @* g! J0 ?+ [
  {"file", ".ffs"},* d+ r2 {4 L1 t
  {"fvimagefile", ".fvi"},
) A2 L. r" k; B; l  {"rawfile", ".raw"},/ Z! P( u) J) w
  {"apriori", ".ffs"},7 T1 x1 y- s- k6 {* _! i+ D
  {"combined_peim_driver", ".pei"},  ?- P7 i! M1 s; O, O3 J" u
  { NULL,  NULL }& h; V1 ?* E" r1 c  u7 _
};8 F* K6 v' T# z" x

4 u) L0 y* ~) t, v+ E7 k了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)( h. c) i8 H% [# l, c
   ( T3 `0 |) a9 P! B8 v, {
         
% }. [! \3 \6 s# S: y2 |! E) z- g  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!9 F1 j6 l  L8 j5 a
支持- X5 F) I9 T  l' G
继续! @- c- b) J# U6 \# k
加油( G8 N* k* R' @
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?
3 U' `! p, y7 _3 J- a有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。0 {! K. C/ g" U
还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。
0 P: ]& l: f) Q# V/ t. Y  E% f; `
支持!
回复

使用道具 举报

 楼主| 发表于 2008-7-28 18:52:31 | 显示全部楼层
3 ?; l) @" `7 n" k
小弟还在学习阶段,目前的目标是知道执行的流程。- v  N# |0 x2 @, Y1 v3 [
这里面有很多细节没有写,能力有限,只能自己知道,不能表达。
' M- {! W9 |! b' {) z/ b嘿。。。。
* ]. \  L! N3 n) n所有大侠们如果有好东西能给小弟共享一份。
- s# {/ e' `! Q" z6 F5 H3 u1 T$ Y( c# W
& K$ }5 X2 W. d) L$ ]; B( Q4 `: ^谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 : X1 N/ E2 I! {
  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver, \1 ^* ~( R  \0 G& @$ `% ]- v
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...
9 C/ t5 o3 X/ t( t7 \  K. W# t5 C

5 {2 Y9 ~: i/ XPPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol, & [/ K& n6 i' F/ Z) L" @0 p8 a
For more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".
; F# M7 z; f: E; |$ j& c6 ?% @
+ j( m, Q6 j  S% _4 W" ^[ 本帖最后由 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:
- S6 ^8 }7 b- }   Yes. I make a mistake.
2 A1 G- q8 {. X4 E      PPI:     A PEIM to PEIM interface.$ N3 f  J% ^% g0 D+ ~
      PROTOCL: A Interface between Hardware(or firmware) and software.' t- o4 E+ l  _2 Z5 W
      reference[http://www.biosren.com/viewthread.php?tid=207]+ n/ q" `' B2 I+ I$ ~" ~; j
   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.
) h6 H5 E* C5 E0 n8 u   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:
1 ]3 {) I7 J6 K' B9 c     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。! i: X/ s; ]' ^$ i0 n" w) s7 n
接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。
( l  {/ j+ P) w$ u, u   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver& {9 y# F, W- \
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。
5 i2 v# I! q7 P1 g# H   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.
  k( I& r! X; h" h! W: e, a

, |) p$ X% o. t9 A- QThere are mistakes,
) D- G/ q' h! |8 y. I1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.) h4 d" z) G& j

3 S7 Y* _4 H9 _1 V% U2 Z0 @5 x+ 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.
1 r7 v4 d  M; @3 K9 g6 w/ K; F& ?The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
4 o6 I# l( e. V4 a- A- Z' AFurthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
6 w/ }! q7 X% P% u% S; x0 \$ q7 U
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 大侠的指点。1 {/ b. q5 b% B. P2 P) K0 D& f& \8 \$ G
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
0 U% o# S* h/ w) {- [9 F) N...
6 B6 S! P7 j4 O* W3 a2.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.
) c/ F; R2 W" e8 ?- n1 }The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
5 K' C9 @) H; s9 r+ ~/ V" bFurthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().

1 y' q# z0 Z) d' x9 T    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().
0 r3 E- W+ F7 s4 w& h    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)
' C3 J1 m0 v1 N- v! E5 A  //6 s& {" u7 I$ ~0 S; F7 f
  // Go connect any handles that were created or modified while the image executed.
5 e0 |4 M# C9 m7 X( i  //
; j+ j1 F3 j' I$ k$ k$ u  CoreConnectHandlesByKey (HandleDatabaseKey);
3 z% O% O* X2 M6 a
这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey
2 y5 t0 \6 x8 j而CoreConnectHandlesByKey会调用到:
/ K- F, l; w# v, u1 ]  }* n  //0 C4 q2 y  q* m& J+ o6 [
  // Connect all handles whose Key value is greater than Key  N" v# N2 G- Q
  //+ w% l& R4 V: e1 C
  for (Index = 0; Index < Count; Index++) {! D; u: i8 g- J6 T2 r3 r
    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
% F4 j% `  ^2 m  E5 ~  }
7 R4 D8 a- K+ D2 r4 Q! T0 K& b0 K
所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey), w+ |5 d* z8 G: D! L3 Q
是会去ConnectController的,也会执行对应的Support()和Start()才对!!& g% w1 U% ]7 a6 Y

* L; h, Y9 D- W# h6 U/ ]/ q不知道我想的哪里有问题???欢迎大家指正.
$ H" N" w8 ], M. P) v% `/ Z, E) J" d0 {' ]8 ]7 I2 j
[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。
) U0 J* _- ]- L. O* {2 v一个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 | 显示全部楼层
了解了.
' I/ u6 T# h$ i2 g0 V非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,
+ h2 I0 i1 f/ Q  _* X: k" NStatus = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,& s$ \$ g0 T8 t7 v8 l- d: u6 @' `+ ~
//, A& U/ @, u' {
// Notify the notification list for this protocol.
7 o) V+ q' i* W; l4 L//, V6 h& X. Q1 a/ U2 X8 k* O0 L
if (Notify) {
) e- t  g+ z$ @( |+ |( B" b  CoreNotifyProtocolEntry(ProtEntry);" O1 ]& E' X% S; j
}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.
- @5 \* Y/ ^! W: Q, \/ k" n有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表
- A5 v0 x3 p9 ?" ~2 e# y请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?
0 A2 n0 l; T) m/ }- o: V. N8 k+ `% z谢谢!

/ q- M, I$ L  B  `......
/ n8 J. H  h! x: h0 A' W. h- Y4 \# V2 a* D) N3 Z! z% J1 ?

+ q6 n3 H3 _0 O; a6 X+ m6 D: J7 U6 ][ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表 , \! N4 g! F! L( f4 i8 p9 s! v

/ y9 V4 G* W8 M" c  b% M8 \  a......& O" @) a  @; P7 n7 M
  c7 _! E7 h1 Y- l! f; q: ?+ X
Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?: L# x! A8 h* J4 j3 g. I( ]
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers(): z8 A+ n4 A3 T( k+ V5 d( J; A
TPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-4-20 05:42 , Processed in 0.043933 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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