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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。/ X$ t+ v0 F# r: s5 F1 Z" W# ~

- w6 Y. I" M3 [2 P+ Y& F, t' CSEC/CEI:
8 q8 t0 D* R; v  z$ T8 M, Y- i  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。' x3 y$ c1 C6 |* e/ v$ o+ ^4 F4 {$ k
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).3 t  V( A1 `$ K- P9 l

" _  }8 G: x7 |) E5 ~$ d) A$ H' L1 ?. tPEI:" I8 {8 A- h. ?' r+ ]0 i$ G& J! \
   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行
6 A% y" V% |! b* p' W  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。' F: F& ~! G; ]& G- L2 x' I+ v. _
      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.+ b4 E$ T4 k1 L( T+ I3 e
          InitializeSercurityService函数,将Notify队列清空。
1 M/ t* {* ?( F, q: i- J          InitializeDispatcherData函数,将Dispatcher队列清空。
7 i/ p2 `8 Z' O# I" i  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。
- W  Z, M# q4 z% c    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:! V9 s3 t1 m% W# J9 {' n* Z$ e  m# @
      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {
+ ?5 t: N4 Z$ I4 k6 t2 i, X       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},
% J8 D) w: Y8 Q6 T8 a# {       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},
/ S+ a* i3 v; g       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},3 V1 D  w- K& K: n% v0 M4 g
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},
! b' Y. w$ o, h$ K# K2 N0 Y       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},
4 s/ ~/ r2 {. E       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}
; i. h% v% y! @     };
+ ^* c8 A" |) z& h6 g    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。
1 t, H: J9 y: t5 A0 m* _& [2 s   这些PPI会在PEIDispatcher中用到。, b; g% t- V- z9 i8 |7 o; D
   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。0 s8 o% n! h+ E
   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
3 p/ B& G' w1 z! v" v* b5 {2 O   SwitchStacks (
2 L  S, B+ |/ |* K, z2 t       (VOID *) (UINTN) DxeCoreEntryPoint,) R% j/ `" l+ J3 z4 ]6 i3 m
       (UINTN) (HobList.Raw),
7 \# z, @: w4 m% j" Z+ O       (VOID *) (UINTN) TopOfStack,
+ y4 f/ T1 P- T% R! Z# m7 T       (VOID *) (UINTN) BspStore0 A; R4 L, c4 H8 v+ U+ P. l9 L$ K% j
    );9 }+ [5 x1 d9 p+ E
  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)
+ ~3 |/ t0 J; S' g/ P* C7 r+ e& w4 X
DXE:$ Q+ z: `0 M; o" \! t
     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。1 x) X3 V2 O8 _
接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。: R" e: b9 u; k" [# L
   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver+ I! e) [% ^" W6 m7 ?& t
中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
6 K- z3 c, U- K+ n   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。5 `. j) y1 P# x" b. `8 H8 ?( @
   - ]1 i, v( m- T4 I8 t$ s% Q- m+ S
Driver:
" R' a4 [0 X# N4 n: w9 Q    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
' e1 J! P' w, D4 D; {5 f% G  Z  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
. G( @8 K. |7 v$ w4 Y4 W  [defines]
% N4 M1 d0 @" o; b  BASE_NAME            = OWEN" E  i7 B( L- M7 U
  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110
' O, k9 n% S$ e: g$ O  COMPONENT_TYPE       = BS_DRIVER
5 S; b8 A: P+ g! D0 P6 s6 X; G- \0 w: {) `
  BASE_NAME告诉编译器最终生成的驱动的名字。, P7 Y( o8 \' l) p/ l! Y& A% ~
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。8 C8 w0 M8 {" Z7 \' X/ ]1 z' {+ I
  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。
" c' X4 b. _9 h: I  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名
% b. A) r  Q! f  c6 \+ k  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {
% g' q) K! J% `+ @# A) J) s# {+ Q  {"bs_driver",  ".dxe" },$ B, d+ ~5 U4 p2 s* P( \, T* {( O
  {"rt_driver",  ".dxe" },
8 D; R/ G# a# F0 E0 d! `' `  {"sal_rt_driver", ".dxe"},! K: b: B/ ]1 I6 }. P( b( ?1 F
  {"security_core", ".sec"},
8 O5 V2 R4 Z( F$ X( S9 i7 A$ @  {"pei_core", ".pei"},6 V8 N' D% S* h2 j
  {"pic_peim", ".pei"},; k- z. |; b0 d
  {"pe32_peim", ".pei"},7 S8 X$ v2 {4 A" K7 P: W
  {"relocatable_peim", ".pei"},
2 Y1 |% A4 L, J" D+ K) V  h8 l% N  {"binary", ".ffs"},0 t! N" ?; Z/ ?3 s) T) z: Q
  {"application", ".app"},8 r7 p) a( e# a; ^/ \
  {"file", ".ffs"},
  Q3 }7 K. u0 J* R! C& o6 Q  {"fvimagefile", ".fvi"},
( ~, a  y0 C- s5 F  {"rawfile", ".raw"},, \" v# B( F6 o$ H: d
  {"apriori", ".ffs"},: _( g0 ^0 I2 X5 L
  {"combined_peim_driver", ".pei"},4 @! J0 P. U% D4 D
  { NULL,  NULL }
% E8 M. |/ Y+ {1 i0 f};
% P# y; E' K: K0 A# p
# N4 Q4 Y: V% C; S了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)+ ~) G& ]6 f0 R' m5 A
   
# L( R! d7 u6 ?3 T' D0 n$ F         7 g; |$ H! x' ?
  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
7 j# R1 W% D" a6 g支持
, v0 b7 [$ A! v# z继续
# [9 n5 Q6 ]3 Y+ M" R/ \加油# e% w9 U* Y; \, t5 G
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?
3 @9 d( _  i( I* G8 p! B有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。) |5 A+ v7 w9 G/ C
还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。9 K1 K3 d6 B* [: {& P8 Z# x
8 N3 V6 P# T1 n5 `( H) w$ i
支持!
回复

使用道具 举报

 楼主| 发表于 2008-7-28 18:52:31 | 显示全部楼层
9 g3 G7 w5 ^- m
小弟还在学习阶段,目前的目标是知道执行的流程。2 I0 C" N# K. B& ]/ K9 t( @; v
这里面有很多细节没有写,能力有限,只能自己知道,不能表达。
: q  ~6 B. F4 l: q+ j" X" Q嘿。。。。& d. u4 \0 L5 |/ u0 h" i1 j
所有大侠们如果有好东西能给小弟共享一份。
  Q4 @$ q3 O, i- f$ h
1 _& Y. C0 L' n- R) t4 Y0 w8 G$ T谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
& }. x7 a, A5 H7 w* ^9 j* S  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver4 `7 B1 ?) ^' P2 L- J( C
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...

3 j/ q4 v6 G  ]1 X0 W$ M3 |
& \* `$ S1 l: u. k) m8 `PPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol, 7 I6 v5 g% g# m
For more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".3 r0 A6 m* y( G+ l  |

* o4 u1 v* Y+ B6 g) K! 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:
/ S; d  m* p) o8 g0 M8 A   Yes. I make a mistake.1 Y1 [2 j  l+ P- q" G/ @
      PPI:     A PEIM to PEIM interface.
% ?+ F( b& u" D      PROTOCL: A Interface between Hardware(or firmware) and software.
( D% V# k7 _: {" L  f; i" w/ q      reference[http://www.biosren.com/viewthread.php?tid=207]' z6 L! O0 C4 H. n- P
   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.& M6 x5 p( C; x' r; o0 N* d# V+ y
   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:
" W6 R7 o& \2 _4 s" Z& k% \% ]     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。- G) h( ^" Z% i$ U. E: M2 |
接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。
+ I% c, r. P; _   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver0 x/ c# l! S, l( B
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。! _" V) A! T+ S; I
   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.

0 R  s! Z- X  }1 o: f% l3 ^
5 X  W* M! r0 H5 qThere are mistakes,; j! X, _8 g- h: s5 x; n( z
1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.- d( a9 c+ H( i& y/ t" @

$ K( X2 W: L9 Q- u5 ], f/ x8 ?1 U: r: e2.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.
( \8 Q# j" f; n3 q1 Q1 V# Q- JThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.1 ~0 L8 E# G$ Z8 b4 A. c
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().8 P  }0 U1 e9 K/ U
3 @* Z1 v! X9 p0 J
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 大侠的指点。
) _7 A+ A4 l4 h2 t2 m
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
& m# P: s& c$ L  J: ^% V' i...
$ ^1 \8 M4 C  z6 b& ^# O' u4 I& c2.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 m7 H3 a, p, D+ v  D+ lThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
7 G" W' B/ \- K; C0 h0 dFurthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().

/ z% {# \- k! ^5 ~2 Y7 g    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().$ Y+ m) @- P% m3 r* ~0 w4 L- b3 z- O
    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c); @) {+ T  s' i# U- |9 I
  //3 t  s$ c4 ]% u
  // Go connect any handles that were created or modified while the image executed.
2 f1 Y4 U& S9 C% J' q7 |$ O2 y4 F  //- x) H/ \4 e& u, f$ U
  CoreConnectHandlesByKey (HandleDatabaseKey);
8 a, y" u5 G  v$ G! h; X
这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey. m0 |* q2 Q8 k. ?5 M) \: {4 g
而CoreConnectHandlesByKey会调用到:
  S: U. H! d9 W  //
! `$ O2 {, k  y7 N  // Connect all handles whose Key value is greater than Key
9 M2 [6 C: w2 p3 M# L$ E  //
4 Y* k0 C$ o8 Y' }" f. s  for (Index = 0; Index < Count; Index++) {% u' K. V+ i* A, e
    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);1 P7 i3 e- a/ O/ [
  }

- t7 N& j% M" m( j" _所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)
$ H+ F: k* Q& R, u' f5 c) b是会去ConnectController的,也会执行对应的Support()和Start()才对!!+ G) L6 z1 i5 ^0 ^

& P* J! Z( Z0 @, c; D' w0 J/ d! l不知道我想的哪里有问题???欢迎大家指正.
  F, I3 B9 d1 f; K) w+ `# a
$ F( C! x: A/ |; H- o7 b( U8 [2 A[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。
5 e( l8 r8 g! e7 i2 c$ C一个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 | 显示全部楼层
了解了.) k8 t- \5 r6 R: U3 u
非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,
1 ?% h* ?+ [# _& G3 |- e' [Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,. ^# E! `; p1 c% e: {# K$ I
//5 f8 P- m8 z* h- m! i
// Notify the notification list for this protocol.
4 D) @! H6 f6 a//- e, z5 B# c$ {; L$ Q, B  k6 {3 j' S
if (Notify) {
  q  b3 C/ k% P# O( |2 G' d  CoreNotifyProtocolEntry(ProtEntry);* g$ e" k1 |5 x" d0 i: P
}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.9 J, |( q! J- B: n5 o* R
有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表 3 l& \+ p$ c7 m7 G% E/ s& Y
请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?' _! n$ i6 H5 _
谢谢!

' v' X6 p" b# M......
4 [" T" w- F; p' u$ U' m, U- m, g; q! ]

6 O- w3 V8 I1 ~7 L& l5 I[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表
6 o$ \0 L  H* x3 h
5 {% `5 j% `  Q, X9 a......, q( C: w$ g# n. X; P

! o3 h/ F! F/ A) fSignal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?
+ P5 X$ }; A& }. f如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()* s4 k. {: h4 U' \
TPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-5-23 10:34 , Processed in 0.045985 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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