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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。( O3 I, |5 _# f1 l9 c+ m; ]
  _# y$ Z5 r+ H  K* C: o; w) X8 L% y
SEC/CEI:8 m' \: w' h1 a; h2 ]* F
  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。' f& a5 n, _  S+ T3 k: m- \9 Y
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).
5 n/ B1 p0 w; H: `& u3 |& u) c0 U  w% ^! N$ `
PEI:
% h( Q1 \- E/ R2 C+ B* B   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行
! J) _% x2 h& C) t5 D  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。
; u; }6 b& o  s) R; G      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.4 q8 p$ V, R$ e; `
          InitializeSercurityService函数,将Notify队列清空。. d' y! V" l" x4 J# L; q3 c
          InitializeDispatcherData函数,将Dispatcher队列清空。
- v' }& f6 P- b+ _+ y+ r  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。7 |! j" C, r0 o- L
    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:
' z6 R, G. H' L8 M# Z( _- Z7 ^      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {* q6 m* s. Q! x- X, T" E
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},
5 i4 @9 T$ l& M9 |) t       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},  n' Y9 C1 l) @* n
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},9 Y& D3 v# x; W6 N
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},+ |  H- X. w4 h$ _7 b0 s
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},* g  S7 M/ _3 X/ O+ f; U: ~
       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}
3 A" L8 P2 m& m* R; L     };
; ^( I$ Q! e, b    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。
) G3 ]8 o: B  \% o& \# f" h   这些PPI会在PEIDispatcher中用到。
8 {( I: {' u1 w0 M6 U/ d   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。
( R: I3 ~) G* v1 U4 j# G0 s   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
% b) m2 K+ E' _$ w) H   SwitchStacks (
2 P1 r' o3 k- e# S, f/ }) N8 B       (VOID *) (UINTN) DxeCoreEntryPoint,
: H# H, F1 `8 r, q       (UINTN) (HobList.Raw),
% r5 ?- f: L. d8 a       (VOID *) (UINTN) TopOfStack,
2 q" H% g  F- a: u) d       (VOID *) (UINTN) BspStore1 M! @- _8 [& R; }8 X4 `% }4 T. N
    );
' p+ w2 [* ]2 ~7 u- A3 ~+ E/ i# x% O  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)
, ?  o* B5 Z# L) ~" K8 F8 B
  \% U* J5 h# ]4 s2 ^' T/ dDXE:
3 ^8 P! H9 N& @0 @5 c     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。
2 h/ S8 f$ v  Y5 t接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。
1 P( q/ K1 E! G, d7 s  x0 @+ M   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
* I# ^, J. J4 O$ V中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
+ t; b) [1 }/ R4 W   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。0 F, W, o! R! q
   7 a# h5 C' f* h" U4 a( O
Driver:' c  o3 d* a7 h& N# p9 |( f
    我们的驱动什么为在PEI和DXE等不同阶段执行呢?/ g2 h+ I3 `, r
  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
4 B0 g2 N$ L+ E% i  C& y  ^  [defines]: n/ H, l) b2 k5 ^  {( p6 P0 |3 Z
  BASE_NAME            = OWEN
9 _1 T1 b+ u5 W, A  p  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110
. I/ d) b0 ^- N. {3 X$ Q) a  COMPONENT_TYPE       = BS_DRIVER8 W9 v& ~$ G4 u5 U2 r: [5 {- F2 _

, X( W: b7 X' m  BASE_NAME告诉编译器最终生成的驱动的名字。' ?1 j* l2 ]! M* c) S
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。4 W3 ]/ E/ P3 k  E
  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。
* b4 b( P  k5 T" v3 K4 ?" Y  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名
  D. j, ~: ], o9 e+ ]4 L1 A  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {
9 R2 D' c, V' ^+ i  {"bs_driver",  ".dxe" },
& ?6 ]# ^7 r. P( P5 ~9 ^  {"rt_driver",  ".dxe" },
& Q% ^# n, Z( s% P+ b  {"sal_rt_driver", ".dxe"},9 @8 B6 o0 y' j  D0 _4 J
  {"security_core", ".sec"},
: ]' _8 \( f6 b2 N# j6 I! C* {; t  {"pei_core", ".pei"},
- `2 ?. Y. ]8 Z! A  {"pic_peim", ".pei"},# [  b% e2 i8 t
  {"pe32_peim", ".pei"},
; L' w. L$ f+ _" k" B; f  {"relocatable_peim", ".pei"},9 [- }, ?% ~: H2 o
  {"binary", ".ffs"},6 ]# M' H! l: C% h  i$ J
  {"application", ".app"},/ [; ^  e$ t1 J- o6 _& _, h
  {"file", ".ffs"},6 C$ K; ^. d7 R, ~0 ]  H
  {"fvimagefile", ".fvi"},
' i# w  W* x6 `- a4 w" m1 v, L  ]  {"rawfile", ".raw"},: x- \0 v! v* P" K6 t/ \
  {"apriori", ".ffs"},
$ q' B5 h, g5 d' y) s  f  W* I' I  {"combined_peim_driver", ".pei"},
1 K! c8 _1 \4 U7 l, c  { NULL,  NULL }1 w2 j$ X. R, \: u' M
};
7 N: A; Y; K4 I. |9 c2 q, x' Z4 n9 X: ^' i2 b! F0 m
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)2 a% z! Y+ J8 k
   + L- N1 u4 [1 b
         
" ^. W2 l) m  {- u  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
: T' P2 m) G* M4 H. ]9 p支持( R& {# Y5 i( ~2 Q3 q) `
继续
3 ~3 E$ p. t$ G" P$ A, u加油
3 I) c3 b! A3 `5 f4 t. n9 f
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?# F" L' T# \7 |5 o8 b4 t  l
有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。% `5 i! a7 O; v; g. T; m
还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。
- O7 w0 E) o$ \% s
$ G4 |, }' ~/ L& B) Z支持!
回复

使用道具 举报

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

2 D: A, W! U- c, ]$ Y9 o小弟还在学习阶段,目前的目标是知道执行的流程。! w+ v( Z+ @( {0 N7 o, s
这里面有很多细节没有写,能力有限,只能自己知道,不能表达。
  ]+ i1 z. ]( ^. j/ T2 U- O嘿。。。。8 b/ r$ ~% z6 D
所有大侠们如果有好东西能给小弟共享一份。/ H+ }2 r4 M1 R9 Q+ c, Q

9 W% `' p1 ]" D4 U谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表
5 J$ ?; N5 n1 G  G1 g  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver$ z% N) z2 E, e% {0 h
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...
) m6 C/ J: B2 P0 H
* S! |+ H" t/ v" r5 e/ z
PPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol,
1 X% e; y/ H* ~# `) CFor more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".2 ~: q$ t, m  {& L9 ]5 s' S

! u0 \/ R$ S4 q[ 本帖最后由 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:3 x" L0 Z0 b2 x/ k5 L+ @1 l) @
   Yes. I make a mistake.( M/ P* f2 w, P( b+ d
      PPI:     A PEIM to PEIM interface.% E) E6 f5 v3 P  p/ |! H
      PROTOCL: A Interface between Hardware(or firmware) and software.
3 S; v* Z* k& [; W- i4 q: e! a. t      reference[http://www.biosren.com/viewthread.php?tid=207]' z0 S/ P! h3 q- g$ }, |. F; N
   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.( J1 B8 T0 q: @, Q4 o
   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:
% `+ X1 [7 ?1 e     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。
, M' K0 B$ N- P6 R6 C: ?1 T; h接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。
' x7 A5 {0 R# P. ~   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver( z' x! W7 P& h
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。
3 _% e  v% G7 v) I; o/ L% N   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.

$ z- [$ R; t2 P# A% S. N3 V
9 E  G/ s- ~+ M1 V7 c' QThere are mistakes,
0 x1 [2 c$ }1 o% u4 q) y) @' b6 N1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.  |! Y0 f: Z5 U

2 B: D; B4 b5 J" B. w/ L4 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.
3 v1 Q% b  z$ e+ B- K; S- jThe CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.& ~& \, `) q/ w: G
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher()." n# e1 h4 o& R3 o( a- W
9 K) B) i# e' ^; q7 G: |
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 大侠的指点。
, j- b+ U9 Z6 `5 q
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
1 w4 ?6 D5 H: W1 _5 e+ P...2 w$ ~, u) c& g3 j& Y( s/ v9 A" |' v
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.) Y: t& T5 q' z+ y( d- W
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.3 m$ G3 p% l! M
Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().
* I$ E( P& L, l% A
    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().0 x, W; k1 m! m: \  e! ?
    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)
/ r. {6 u  f8 U* g2 f  //! a& H5 g, {9 V! |' [6 S
  // Go connect any handles that were created or modified while the image executed.
# R1 ?% {* Y/ a( L/ P0 e" I; f% J  //
) o0 H, I7 n5 [- X  CoreConnectHandlesByKey (HandleDatabaseKey);
0 d) F* D& `8 Y, I2 t) i
这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey
* ~- y- s, |; C而CoreConnectHandlesByKey会调用到:
: H. z1 {0 \1 X) V! k  //! P3 E1 R+ W( U- N3 k
  // Connect all handles whose Key value is greater than Key
+ H' T% I! e6 [, U- z2 U  //: s9 a2 Q' _# [
  for (Index = 0; Index < Count; Index++) {
4 c) x. C5 z# U7 X# A9 w" ~& ?    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);' c) }$ p# e& ?  d' |5 b
  }
! a9 j8 v2 v. s  v* m7 J  n6 t
所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)
) S, w( y/ b1 \! O+ w, y是会去ConnectController的,也会执行对应的Support()和Start()才对!!' {1 t6 m" X4 e: H9 C' K4 z

+ t+ x* k7 i! }不知道我想的哪里有问题???欢迎大家指正.& }2 r; A0 v  ^; V: ~, ]9 i% J1 D
7 |* S, n* r# |* y, H9 a
[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。
0 R+ B- A. P4 m/ T3 V; k一个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 | 显示全部楼层
了解了.0 l- K) B9 Y. f2 y) ]
非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,. [5 F* w# T9 f8 x: \2 X
Status = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,
+ T8 G' |3 N  e1 b//$ I  v" i. r; s3 \5 _
// Notify the notification list for this protocol.
1 b+ q3 M4 Q9 }1 c+ J; a//
1 O$ L6 S8 U7 Lif (Notify) {9 S* O) t! O( S0 {9 y9 O
  CoreNotifyProtocolEntry(ProtEntry);
$ {% J9 Q. \! X7 n}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.
) ]. a7 q6 X& u: c有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表
- [9 w! w: H  F/ M请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?+ e4 K# B7 ?' R; u
谢谢!
1 q7 ^/ R# |# Y7 ^2 m
......: [: W8 f& t0 O8 h. j, V6 \' }' E
+ N) }+ q3 C* I
& ?) j' c! ~$ p) C
[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表 * A, `; ~" H2 @3 U, L+ |

$ q/ |* s* d- u3 [' h1 f, z5 A( z......
) m( k; ^  [1 W  P$ q
. b( h  A1 z. R+ U
Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?
" ~! g* ^, \3 O如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()
6 P  j/ p. P9 p+ U/ YTPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-5-19 04:53 , Processed in 0.023536 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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