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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。
* `) }; f) [# P$ T4 ^: p
' N" g- l/ f# {0 _+ {SEC/CEI:# g3 o6 A: Y# J5 o5 O& K  H
  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。7 ?3 x4 P  t" Y, I
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).
1 p( U& I2 O" H! x! i5 c1 J3 O7 D$ o0 E; L; d. F2 c
PEI:
# I8 x* y' {4 \' A   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行+ x! W! r0 S8 r4 ^  e
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。- }& |4 t+ {( ~. P& L
      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.
" S' `, Q/ d# h3 l. p- B! c          InitializeSercurityService函数,将Notify队列清空。
% D8 W4 Q/ i/ c* x1 Z1 C& W. r7 Z          InitializeDispatcherData函数,将Dispatcher队列清空。
" q9 Q$ _5 H; h/ Q# ?  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。
- \, q! h4 w, ^  D6 k- O7 D    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:& t8 Z, {. s/ y  v' D0 r, ~$ G
      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {
) W- e' ~4 k& j1 }) r( H" h       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},6 j  L2 _& q8 U$ D  m# F  j! S1 i
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},
" K/ e2 d7 |4 ^5 L9 _3 I       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},
, D+ B7 |6 S; A9 m6 C/ F       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},* |% G# N; }" Z9 S
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},
! ^+ B$ I1 N5 |/ C( D& M" M) d       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}
* D5 m. w, Y. i6 W: ?     };
% \& N  B2 T8 Y7 o+ {2 Z. a: Y    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。) G" b- i- E. d. e3 R
   这些PPI会在PEIDispatcher中用到。) C; v4 R% k6 i. F
   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。5 d- H2 o$ _, O! l  W
   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
# o& g+ u9 y" J  @5 r. U/ |   SwitchStacks (3 s5 K' d4 h. Y" g! t" m7 f
       (VOID *) (UINTN) DxeCoreEntryPoint,
$ g" q0 w9 e( c' U, [' i       (UINTN) (HobList.Raw),3 u  A: H& |7 @; d% |+ j/ w& u
       (VOID *) (UINTN) TopOfStack,5 d# }6 l# X4 f6 @$ e3 o. J
       (VOID *) (UINTN) BspStore
. {' e% ]+ W6 \+ E( J" L8 P/ ~    );0 v, i$ R) g2 M# B, B6 d- T+ A7 q
  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)& ~& c: Z. N9 r

- A( W. J: j/ P& r5 rDXE:
2 E& ^: n) v+ Z9 ]     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。
$ D+ F9 o+ `1 Y& Z' E3 ?& z接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。2 o! W4 |# N( K& m
   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
* u/ u4 a5 g5 D; j中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
0 b* s' P. f6 T6 i) u- O   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。
- j, e7 S( G- W2 C# v$ x   
) h- F/ i, [& e; v) x  Y+ YDriver:
( ~4 M" o" b# u& ?6 Q8 N    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
5 }- {4 l" f* F; O8 {& j  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
/ t5 \' S  U- E/ C9 N, Q) V  [defines]# f4 }4 a6 K5 S  _
  BASE_NAME            = OWEN
4 z+ Q, r9 n; ~5 G$ G  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110) I9 @7 l1 U; B% Z( S5 Q8 e9 W; V
  COMPONENT_TYPE       = BS_DRIVER% S+ Z4 i- J: n; K  U, m
" w: A  R7 a& s% `/ p) V2 e5 Z
  BASE_NAME告诉编译器最终生成的驱动的名字。
  h3 d2 i8 `5 {/ f% K  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。4 q9 a3 l# K) k' S8 Y7 @
  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。: X' H; d: g/ p* ~
  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名  U  K( }! X& x) ^; |
  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {0 N' ?6 T4 G, E; `! J, E
  {"bs_driver",  ".dxe" }," R. T5 V5 e" W4 o( W! s" i3 n
  {"rt_driver",  ".dxe" },
& ^9 }- H- c* Z; [+ l  {"sal_rt_driver", ".dxe"},% g& L' f/ }5 ~+ N* \% v5 Q# p5 t
  {"security_core", ".sec"},( b# c  `: q* ~% l+ l% f
  {"pei_core", ".pei"},% G/ V" Q5 n; D8 H1 Y6 b- M/ g: [
  {"pic_peim", ".pei"},) N0 A+ z3 h) E  D* X4 }) p
  {"pe32_peim", ".pei"},; ?+ _: {2 W5 s% h
  {"relocatable_peim", ".pei"},
' ?: U8 }( O" A* |3 z* m  {"binary", ".ffs"},1 d6 U4 E5 A; Q8 i; y) Q
  {"application", ".app"},! V' j! k7 _  ]0 Q9 _4 S
  {"file", ".ffs"},
% b6 L: Z' n6 @/ d& K: U  {"fvimagefile", ".fvi"},' e5 E" U" ]7 a6 m& U7 b+ p9 n# y
  {"rawfile", ".raw"},
  T8 B6 c' G4 f* d# f. V3 ]  {"apriori", ".ffs"},
6 K1 @6 C* U: M1 R  {"combined_peim_driver", ".pei"},
' L4 O* M; ^$ F  B( {  { NULL,  NULL }
( M- x# k4 j9 l% z- W};- z4 D% ^0 L2 x; K! b. r
! \2 m" W6 l$ C6 h- |! c
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)
1 }5 B) g" l; Q7 T" A1 B# B   6 m' |% L. r0 L" P
         
4 |1 X8 J8 C( b% k$ _* G3 u9 I' |  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!: `0 U" r. ^2 G5 h9 }- R! |
支持
/ `5 f4 ~) c+ Q! `+ f8 f继续" O1 ~/ }# n( ]: g. J, A
加油( k* J  Q8 a. K0 {
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?$ H1 D% v: {9 f! j3 r, x
有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。) D% Z" ^* x6 C
还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。
5 T5 G- q! _' q. X% o2 V+ s4 u! l) I' F, {: l
支持!
回复

使用道具 举报

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

$ k, X1 B1 f! }7 ?, x小弟还在学习阶段,目前的目标是知道执行的流程。
) j& P* O7 j; y: ?! \9 p9 B这里面有很多细节没有写,能力有限,只能自己知道,不能表达。$ {8 R7 }6 r9 P6 y$ c9 \! {! U: S
嘿。。。。
! E0 Y8 z& y0 U所有大侠们如果有好东西能给小弟共享一份。7 K4 S/ G0 `1 t7 z
- [7 O- X' X# u3 Y
谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 / V+ t$ W4 `/ S; K( G5 \) n
  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver4 h- H' F; T( k* i  J
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...
9 p+ d, _9 N' R% v, [; }
2 l! J1 m) h7 z7 g
PPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol,
* f1 M3 ~  L0 K( U7 `0 H* cFor more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".
. X- ?" z- e# n3 Z
; q( B' z" E5 Q5 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:
8 v  j9 k: q+ V* q/ \   Yes. I make a mistake.
" n) L/ k# t9 Q- z3 U      PPI:     A PEIM to PEIM interface.
6 G2 O5 K8 y( ?; n      PROTOCL: A Interface between Hardware(or firmware) and software.
: s* v' M6 Z- N' O( `4 `3 `+ p0 v      reference[http://www.biosren.com/viewthread.php?tid=207]9 M1 j% V: c1 q
   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.
4 `3 Y* g2 J0 a3 A3 _9 O   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:
0 o! O9 u. R0 m: _  M# \     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。
8 e* {  }5 `( |; g接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。7 z( T/ q) v$ n1 T' |
   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver
: e# j6 F: _7 ]" C- Q中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。
; y' a: x2 y3 C9 H   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.

0 f, k- j) j. @5 {9 X! ]/ Y% o) T
, K: d9 K! N: U5 o6 T, l3 j# xThere are mistakes,
% {, I1 Q- ?; s1 a1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.
5 n) I7 I( D& D# q: p, N
  D( L2 u$ S/ k, w* g. g2.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.7 f; I7 E+ ~5 K# m+ ^* e- T
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
2 ?1 {; J; N4 n( M* {% `Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().4 f# \2 z6 K6 {  T: E

0 h9 W5 _0 v" ?5 Z$ j5 C/ J+ e3 n/ qBTW, 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 大侠的指点。, _' J' a7 u! K
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
/ O" s0 Z! ^3 w, X6 W.../ X: y' C9 g1 T/ C  \
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.9 u; Q6 A! s* A) ^
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.8 x; f' e) k# l; U6 b
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
- e: y, V9 ?4 Y8 q: h& `, `
    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().
- a; c8 x  h# Z    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)' ~: e7 l1 e, h
  //$ P! ]' l7 v% s
  // Go connect any handles that were created or modified while the image executed.  N! E) {7 Z+ M/ s% b3 B
  //4 Z% {- `, v  E) }
  CoreConnectHandlesByKey (HandleDatabaseKey);

! S# S& \) [  {1 q* S' D' C这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey
- q  T* @' }, N/ I& T而CoreConnectHandlesByKey会调用到:
  \' P3 Z% J  T7 e* C; k  //5 \0 W8 X8 ]2 b
  // Connect all handles whose Key value is greater than Key$ j6 ^+ o+ u* c) b
  //5 s5 j' k2 `! o' S  R0 h6 N) U
  for (Index = 0; Index < Count; Index++) {4 u. Y) u% M7 v5 L: }  ?# [- H4 T
    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);  @" `4 B; Z& ^& [2 i) R
  }
+ N2 Z9 W& z$ V. ]
所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)5 d8 `' f- ]; M+ c, r5 I0 T
是会去ConnectController的,也会执行对应的Support()和Start()才对!!% n" `  r) H+ z9 R! d

0 e' C- J6 V9 X- {& e1 ]不知道我想的哪里有问题???欢迎大家指正.4 m) L( M+ [2 n& Q. m$ z
& e, ?8 p4 ~  r
[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。% v* e3 J& F/ a1 z/ R
一个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 | 显示全部楼层
了解了.5 P# i( H6 w6 \  }
非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,& R: L2 m! O' h% ~
Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,) t& c4 l9 z9 I/ J" N
//
! f& V; A- x5 K// Notify the notification list for this protocol.
; _% x+ a: V+ \  r* I% K//
: T; x+ {6 y: u/ G7 {. a/ oif (Notify) {
$ u- @9 M; J% ?  CoreNotifyProtocolEntry(ProtEntry);3 E0 J6 a; q( f8 Z! r3 H
}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次." o  O, T) }# I! v5 H$ V0 E
有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表 ( D5 s" c* }# a. P3 m
请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?
7 s6 d" V) e6 X8 G6 V0 l谢谢!

+ R- e# l9 N( Z. o* a......
' j6 n8 l8 a- a. X# J. A
# V* T$ `9 j1 w; J% q/ H* P3 q- M% ~1 |" N4 H& B1 ^# l- l; i4 ^
[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表
7 M2 Z- j8 h) d: X# E6 G7 T: C  ^+ u4 D, ]5 O+ t6 N* o
......
3 S3 K% I4 E4 N: X* h( M9 G/ m2 G
2 {0 u4 J6 |) C2 F+ L8 Y: @, @) H2 i
Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?- X; C' x' w& N
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()) }8 W2 P  j7 I4 ~' H9 i
TPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-9-19 21:02 , Processed in 0.077826 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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