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

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

[复制链接]
发表于 2008-7-27 00:11:28 | 显示全部楼层 |阅读模式
  最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。
" l0 g  ^1 S- d4 e3 d7 @
1 u) i+ H$ L8 @, H  SSEC/CEI:5 l$ ?( ]# U: ?" e* Q; ~
  UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。
: N& L0 L3 F( J& Z, ^) v在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).1 [1 d8 {( I3 O3 t5 h0 B. ~1 I
4 ]. @4 b7 K+ r% \  p
PEI:
* O1 d& _# d0 s& I7 f- G6 U   从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行. f( {. f" G' S! D: d  E4 @
  EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。
6 l9 i7 k* N/ a: X! o      InitializePPIService函数,将PPI队列清空,这个队列长0x3F.3 b- e) E# d5 b4 }& e$ o' @/ {
          InitializeSercurityService函数,将Notify队列清空。
. @6 W* c( X1 Q* s          InitializeDispatcherData函数,将Dispatcher队列清空。
  K( C  N+ f/ v* e# p& y5 f  接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。) ]* X0 b; r; e$ }' [% Y
    由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:1 d% ]! d& f8 p' R* }! f, T
      EFI_PEI_PPI_DESCRIPTOR    gPrivateDispatchTable[] = {
/ C  }, h# v4 k. |; e       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},
3 P8 z* Y5 Z6 J; z" k' U5 `       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},7 J; f7 F% V; N) r: U% v; w
       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},
) r6 D4 \6 E7 }+ {0 `) v       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},
; o2 s5 L/ M0 x8 M+ u8 }( n1 e       {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi}," w4 `2 _* X# [7 \! T
       {EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}
" r$ e: K+ Z) R! j% S     };* p( }8 e6 N) k5 d* I! m5 x
    每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。1 u/ p1 M) F8 a( W3 k
   这些PPI会在PEIDispatcher中用到。, A0 _- z0 H8 a& Y- m# H+ ^$ I- @/ L
   安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。! s& q# r( @0 k4 c8 H4 ~2 n  O
   最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。/ e' D5 j9 m0 R) f
   SwitchStacks (1 p- B' @6 F7 a; x4 N
       (VOID *) (UINTN) DxeCoreEntryPoint,8 N/ [2 E# d# J, g0 @) f! W6 F
       (UINTN) (HobList.Raw),
9 ?  u4 U$ U+ A6 W       (VOID *) (UINTN) TopOfStack,
) a$ s# f3 f* J* e       (VOID *) (UINTN) BspStore
: p; B. W6 z) |9 O  }    );
# O( i# j2 e7 x8 O' \3 k% r( I  用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)
* W/ l, D/ i! D. P- @# t9 R. Y8 [) J6 _0 z( P. i
DXE:
7 Y+ e: k" I1 C: L0 s+ _     从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。5 T. A5 j' g+ |- C9 {$ \& D
接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。' k' E0 T7 [$ x6 H) Q
   等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
- K7 {7 T* ]) s中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
$ C7 o& S! N; S* v$ ~8 M$ G! A   到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。
# |( W3 o. [) W   ; p2 n0 u" G* `; m/ E
Driver:
8 u' r( `& _. T, o1 e    我们的驱动什么为在PEI和DXE等不同阶段执行呢?
; l3 W7 j7 E- B6 d% L  大家请看一下我们的驱动的makefile.(EDK中的*.inf)
0 U" G7 j$ }  Q  [defines]: H2 n+ c; Y: N$ P
  BASE_NAME            = OWEN
' X) ^0 u2 Y" J' G# H6 p  FILE_GUID            = 1EDD13C1-62EF-4262-A1AA-0040D0830110( _( k- f0 `! h: t$ I5 a
  COMPONENT_TYPE       = BS_DRIVER2 K2 N4 D' i7 J/ v( _
+ k7 e2 V$ u4 O6 p  i
  BASE_NAME告诉编译器最终生成的驱动的名字。/ R1 Y% G. T& x2 L+ M1 }- W
  FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。
* V! ^# z( R! t9 p; O1 _  COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。
$ j5 f8 e$ }$ n( V# R  在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名
/ }7 `3 S- e1 l0 p9 v  COMP_TYPE_EXTENSION  mCompTypeExtension[] = {
9 R3 j& u% G+ C: H  {"bs_driver",  ".dxe" },4 W& u; }( U2 z9 p% F$ R
  {"rt_driver",  ".dxe" },2 Q' n- r! `" k! Y4 s1 R
  {"sal_rt_driver", ".dxe"},- a, H. S5 n7 [, @& A9 N
  {"security_core", ".sec"},. {2 d' B. _( b% v3 ?
  {"pei_core", ".pei"},
. a% {' D1 l! T8 {  {"pic_peim", ".pei"},
5 t8 X7 o- J% d6 i5 E  {"pe32_peim", ".pei"},' |' a' g  b8 V2 I* I/ u
  {"relocatable_peim", ".pei"},
! f  \0 i+ @+ `* O2 x$ X% w  {"binary", ".ffs"},+ D/ K3 Q7 K$ Z8 o: j- ~  \
  {"application", ".app"},/ i( ^6 c; F( W" a8 V4 H& s- N8 V
  {"file", ".ffs"},( s  ~2 T( S2 z! z2 l5 j
  {"fvimagefile", ".fvi"},6 U; _8 u& }5 \  d& Q
  {"rawfile", ".raw"},' ~' ?" W, h+ ]' v/ d4 }# w
  {"apriori", ".ffs"},
' A+ o( F2 e* V  {"combined_peim_driver", ".pei"},
5 ~- _; }) M7 u: o4 k  { NULL,  NULL }5 b# Y8 s9 i+ ]
};" Q9 k7 h. U) \# G; s, f! V
" {; L$ [# ^7 Z. a6 n- s
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)- @& @2 s3 Q- L+ Q
   - Z9 f0 `2 A; e% i1 Q
         5 c9 P" G, v4 E! z! @( j" N
  
发表于 2008-7-27 12:30:15 | 显示全部楼层
不错!
9 N; ^) V; w* f1 ], P8 M& J支持
8 H% L) Z% {% d& x' M4 d继续) A3 k( ^6 w6 k
加油
% X! f& G5 T0 H7 b3 g9 }- ]
回复

使用道具 举报

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

回复 1# 的帖子

gPrivateDispatchTable 和COMP_TYPE_EXTENSION  没有搜到啊  是EDK专有的么?
/ k+ q  P  `( V有看过跟gPrivateDispatchTable 类似的,但是里面的PPI不同。; }, e8 w" V  c6 U3 V
还有这个COMP_TYPE_EXTENSION  没有找到过。FWVolume.c这个文件也没有发现。。
回复

使用道具 举报

发表于 2008-7-28 13:24:20 | 显示全部楼层
这东西虽说没有什么技术含量,但是总结一下还是非常好的。2 d2 n. L- u1 ]2 `
: @3 L5 J- V4 P6 t+ C; u7 x
支持!
回复

使用道具 举报

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

% z( k, Q, W; v0 l! q小弟还在学习阶段,目前的目标是知道执行的流程。/ X8 h6 ?0 M2 \
这里面有很多细节没有写,能力有限,只能自己知道,不能表达。: M4 M* V( Q* r: M4 n$ \0 N4 I. h
嘿。。。。' P% Z) V, M2 J0 b1 G/ a
所有大侠们如果有好东西能给小弟共享一份。
- n) P' o$ o- m: g, \) _
* v+ M* X1 e6 c: M谢谢!
回复

使用道具 举报

发表于 2008-8-12 14:44:11 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 0 |- V. T, O$ {. X! _. {3 U! U
  ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver' U0 M  X' x0 ^) X2 U
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI ...
- A& n# i. N, G9 J  K4 \

$ ~' z$ k! A- d# Q4 R' M8 NPPIs are registered during PEI phase, but Support()/Start() are invoked in DXE driver binding protocol,
4 R- X1 U- a( y+ Q1 @/ `! KFor more precisely, I think the "PPI" you mentioned is the "PROTOCOL" rather than "PPI".
+ P3 }5 M7 U8 O) J! d
1 B  j: K$ h7 H) ~3 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:# B9 [2 ?5 @1 H6 B
   Yes. I make a mistake.
9 @$ [# T6 H  \      PPI:     A PEIM to PEIM interface.1 p7 Q8 V: Q5 s( L2 V$ ]  _  E
      PROTOCL: A Interface between Hardware(or firmware) and software.. U$ V3 C! [7 |# R
      reference[http://www.biosren.com/viewthread.php?tid=207]
0 i, D8 s! M( ?# C   so, PPI execute at PEI step and Initialize hardware. PROTOCOL execute by DXE step.' d  s6 G8 b, g) N
   Thanks.
回复

使用道具 举报

发表于 2008-8-20 17:47:21 | 显示全部楼层
原帖由 winbondowen 于 2008-7-27 00:11 发表 DXE:
3 R7 H) t3 S! F     從PEI到DXE切換時轉過來一個HOBLIST參數,DXE會在這個HOB中找到Memory的使用情況,然後根據這些情況將BIOS引到內存(這是EFI的做法)。在EDK在DXE時重新定位一下內存。0 L! p4 \! i+ S
接著就會定義我們經常使用到的gST表,gRT表。接著是申明一些Protocol(先不關心這些事)。, l: g+ F7 M( z! X9 Q- T( X( q
   等這些該加的PPI,Protocol加完了,CoreDispatcher()就出場了。他會的功能類似PEIDispatcher()。從我們BIOS ROM中將DXE的驅動讀出,執行執行他們,這時會執行到Driver# t: w+ A/ m* S/ m
中的Support(),Start()兩個功能函數。在這兩個函數中你可以註冊自己的PPI,為其他驅動提供服務。
  Q) G" O) \% U: W   到些BIOS的引導其他完成。接著該進OS了,看Linux 0.11吧,操作系統是怎麼做事情的。.

" K) N4 m( e# w0 _7 K/ g- I
, h9 O, H" O0 N! E: @! ^5 X6 QThere are mistakes,8 [2 b6 l8 x  k6 x
1.gST and gRT are init after the DXE architectural protocols have been loaded, those protocols response for creating Dxe foundation.
4 }1 J- `5 r0 f$ {& ~  f( t& w$ c, b9 h
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.6 D7 ]- D0 f! f
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
) A, R6 I  W2 r9 {! hFurthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().1 _4 U, E: K' N& {) g0 T( g/ e* L
2 `+ W8 p# R: \8 O! z  Z0 }. ^7 c: 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 大侠的指点。. M$ w' k$ ?0 ?; _% n- F& i$ X
回复

使用道具 举报

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

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

原帖由 ichirohiro 于 2008-8-20 17:47 发表
) b4 \' c* X# o1 d& {4 v6 C0 h...8 K5 F5 U" V& s$ q0 Y8 E2 x
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.  e2 n; K6 \) X- c, `7 a
The CoreDispatcher() executes all trusted Dxe drivers and just register the Binding protocol if the driver is EFI1.1 driver model.
& a- K" ]9 K% I8 S9 P7 @Furthermore, Support()/Start() are invoked in BDS phase over the CoreConnectController() rather than CoreDispatcher().

2 s0 y1 G+ D) c0 c0 W7 H# O9 q    在Framework的spec DxeCis.pdf里面也是这么说的,DXE CoreDispatcher()里面,对于EFI1.1 driver model的driver只会安装Handler和Interface到Binding protocol,到BDS才会去执行Support()和Start().' h/ f- v# ]/ q, `' D0 l  _9 e% J* M
    但是,我在看code时,发现在CoreDispatcher()-->CoreStartImage()里面call image之后有这么一句:  (file:image.c)
" `& ~( k/ u& r3 Q1 L  //
( e7 ~5 r1 G, ?  // Go connect any handles that were created or modified while the image executed.
( j3 a4 d: A7 s& m  //
1 V3 R9 K9 Q, {* i: v8 q  CoreConnectHandlesByKey (HandleDatabaseKey);

9 d/ b! D, Q! A' w5 M% P这里的HandleDatabaseKey是call image之前由CoreGetHandleDatabaseKey()得到的gHandleDatabaseKey2 m# A7 f( C/ ]" |0 C5 N
而CoreConnectHandlesByKey会调用到:$ ^# u' j4 N7 _
  //
' C: `9 Z  I4 j$ l  // Connect all handles whose Key value is greater than Key5 m) C& `- {/ F& K( V
  //$ s( |7 ?$ h8 J  `& c" \
  for (Index = 0; Index < Count; Index++) {
* g8 K1 ]' A  m    CoreConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
& b) ^- J. {' w/ @- N; U$ @* E% J3 @1 c  }
# D0 }) Z( w) I+ H3 a5 i5 H, M
所以,照code看,当在Driver中安装一个Handle和Interface到Binding Protocol后(gHandleDatabaseKey会++,IHandle的Key=gHandleDatabaseKey)- B4 X% q. V. b5 h5 U: T# H  }
是会去ConnectController的,也会执行对应的Support()和Start()才对!!
' @; q& I0 ]; p7 O, a
: q% }: p4 G  Q% N& _& u9 l$ L不知道我想的哪里有问题???欢迎大家指正.
3 ~' }+ p  N/ f- V# i3 L  q
5 u2 W' F0 o6 l7 ?) U/ Y[ 本帖最后由 xtdumpling 于 2008-9-18 15:25 编辑 ]
回复

使用道具 举报

发表于 2008-9-19 14:44:41 | 显示全部楼层
这段code似乎是一个向后兼容的行为,不必太care。) D  N6 l0 w7 K& e" ~# G7 \
一个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 | 显示全部楼层
了解了.
! }) l0 ^. H8 E0 L4 E非常 感谢!!!
回复

使用道具 举报

发表于 2008-9-23 16:48:56 | 显示全部楼层
在读DXEmain时,
0 K, ?; U6 u- [( ~1 D! AStatus = CoreInitializeImageServices(HobStart);--->CoreInstallProtocolInterface();--->CoreInstallProtocolInterfaceNotify();中,! N3 q& M0 I! L$ A
//
( w8 @: M0 u- D4 o3 H' _" O' e5 g// Notify the notification list for this protocol.. R6 E3 l% E' v# }9 u# |7 R' a
//
0 y( ?2 b5 H" H2 Z+ B0 Uif (Notify) {
. H& k8 K' N, _  CoreNotifyProtocolEntry(ProtEntry);
& i$ R4 Y; z8 Q: s}    里Signal了Event. MS是说一个handle安装了一个protocol后就signal一次.$ F6 B% a8 J& {/ ]9 q
有个问题请教一下大家: 这段是在DXE很靠前的位置执行的,但是在它之前我没有看到DXE中有相关的CreateEvent出现?哪位高手能说说这部分代码的流程呢?
回复

使用道具 举报

发表于 2008-9-23 17:04:14 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-16 13:34 发表 3 o0 q: H6 i2 }: Y/ |
请楼上的兄弟分析下BS的RegisterProtocolNotify也就是CoreRegisterProtocolNotify是做什么用的?怎么用?
0 J: R1 u) e: Y. H; c7 D谢谢!

  \0 _6 P5 n) q1 F......
: B* S/ M: R! h5 j4 S8 X3 u
9 y! [0 r1 J' `  N9 Y* g2 K
! c" w, W* ^: d* ]) O3 {/ G6 |# C[ 本帖最后由 xtdumpling 于 2008-9-23 17:24 编辑 ]
回复

使用道具 举报

发表于 2008-9-24 12:43:45 | 显示全部楼层
原帖由 xtdumpling 于 2008-9-23 17:04 发表
; @2 t- F4 v0 [' J" q6 S- z$ N( Q8 \& h" A: U4 m
....... h& U) T- f* Y+ S
7 v) X- }- G7 Z
Signal的Event是不是在各个TPL级上挂载一些待处理的事件,一旦restore(TPL)的话,比当前TPL级高的pending事件就回被处理掉?$ j& r0 H2 p7 \, n* U8 r9 A
如果是这样的话,Timer事件是如何处理的呢,没有找到相关的代码呀?Xt指点一下再~
回复

使用道具 举报

发表于 2008-9-24 19:00:46 | 显示全部楼层
Timer是挂在8259的IRQ0的中断处理程序上面的, 大概每秒18.3次调用CoreTimerTick()-->CoreCheckTimers()
. W6 {( j( Y* s- S: N9 `6 P% j  H( xTPL=30
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-6-17 15:09 , Processed in 0.116850 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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