|
|
最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。2 |3 D3 q* `, r& E
3 ~& H& N5 u1 X. ?1 G ~SEC/CEI:, Y3 o- p8 s$ C) d2 `6 H4 r
UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。
) W* l% f! a8 ?在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).$ S2 R9 C2 X0 I: A1 p
% h( q: h# c1 M8 G$ D' Y1 _) J* o8 v
PEI:
" W1 ?5 [0 U# ^- Q/ X2 L& ? 从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行7 E5 E/ b5 i8 Y# K$ s9 o
EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。! C' w% ^( f+ y. D1 K; F0 ^: L
InitializePPIService函数,将PPI队列清空,这个队列长0x3F.
5 D. Q% X! [) o InitializeSercurityService函数,将Notify队列清空。
T8 ^4 V, b1 x. d7 K0 x9 ` InitializeDispatcherData函数,将Dispatcher队列清空。
! @0 G' K- \- d: p0 |2 {. n 接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。& s: p4 r7 y6 T7 x7 ^2 [
由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:6 |7 a* y+ x6 Y' J1 B
EFI_PEI_PPI_DESCRIPTOR gPrivateDispatchTable[] = {
' t' H) P5 Q; P, p3 h" } {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},5 e" U) A, G0 b/ t
{EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},+ }, f. p! K7 @. |; u
{EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},
) E2 J$ W& i( _* [/ Z8 V. |9 t& A% c {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},5 I1 Z2 `7 m$ f6 u: D) O* |
{EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},1 H; U2 k' b- [- C, m1 N& g5 w& P
{EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}( b0 ~7 D$ v$ d8 B
};6 t' R: A) o: j' ]% J* Z
每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。
$ _: r1 ^) M: A: Y; H" ?+ o 这些PPI会在PEIDispatcher中用到。& ~4 A& H8 i9 W' g$ Q$ o) `6 ]8 J
安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。, S+ P; k/ T- _
最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。
) P) N" _: o$ v c1 s8 t SwitchStacks (
8 c. N0 N; z. ~$ ]! j1 J (VOID *) (UINTN) DxeCoreEntryPoint,
5 z1 |1 |: i; k% x9 | (UINTN) (HobList.Raw),
' L+ [/ d) w% Q1 W8 g (VOID *) (UINTN) TopOfStack,! B7 X: ?3 J+ U3 }
(VOID *) (UINTN) BspStore
1 `* C) ]0 ^4 D0 [: W" U );. |7 O( |) i4 v' Y+ ~1 r
用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)
7 l; c. O# c: N4 o3 _5 N4 L7 l! {# R# C" H, b- }
DXE:; R, \, ?! @, A& Y H, P
从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。; X- p* \; u" i2 e& H, e4 {
接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。+ y/ t ^. c8 O5 ~
等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver" k/ o! T; [& g$ P, b- O$ Q% y
中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。
( h: q; G4 J4 x. \8 z# U9 N 到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。* c8 L2 ^& U4 B, E( r0 k2 c4 g, }; A
, U, O6 j; O. i+ y! iDriver:
! S6 z# K8 a, {- m 我们的驱动什么为在PEI和DXE等不同阶段执行呢?
" o8 j9 N* F) A5 y0 s; e 大家请看一下我们的驱动的makefile.(EDK中的*.inf)
& e7 _8 s4 |( Y$ D [defines]
( H: u& P, {# Q- J( ]+ J' Y BASE_NAME = OWEN
& m3 I+ _& B8 d7 u3 _7 G FILE_GUID = 1EDD13C1-62EF-4262-A1AA-0040D0830110' X) }+ ~ U4 f k
COMPONENT_TYPE = BS_DRIVER
! a# |, {$ ~5 U7 ~) n$ W
) y# d& q5 w, p% Y BASE_NAME告诉编译器最终生成的驱动的名字。) o5 B+ Z$ l( N9 }9 E4 L
FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。
0 V6 C6 `* V$ J# T COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。
5 x5 R% _* w ]9 X0 J" u 在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名
' T G( k/ s7 e3 T COMP_TYPE_EXTENSION mCompTypeExtension[] = {
6 Y% h6 J0 H- Y( Z {"bs_driver", ".dxe" },& d9 ?1 h4 _5 Q5 Z8 l8 |3 B) p; B
{"rt_driver", ".dxe" },
0 f' T7 ~5 s ~ l9 I/ } {"sal_rt_driver", ".dxe"},
* Z! }$ S6 z& E: |& Q' B$ ] {"security_core", ".sec"},
' p/ b+ T% y9 d6 e {"pei_core", ".pei"},
+ Q& N4 A4 r! V _ `' [5 P* W0 P {"pic_peim", ".pei"},
1 v k0 p: X4 L& v# ~. | {"pe32_peim", ".pei"},
$ f; u5 `% ^4 @' N# f6 Q2 H$ _ {"relocatable_peim", ".pei"},
! f' V( _# v8 Z' { {"binary", ".ffs"},
; D6 g- O) b) e* m, H {"application", ".app"},
2 C. z8 H# ` {' M {"file", ".ffs"},
+ j. G* Y; G. R! l {"fvimagefile", ".fvi"},
7 R5 s' d' q6 Y) E( D: H E* C {"rawfile", ".raw"},* X; R" P: E. f$ c
{"apriori", ".ffs"},
' l8 ~1 H! N @ {"combined_peim_driver", ".pei"},
& k# R& \ ~1 c" c4 x1 a; `' E { NULL, NULL }9 `( U8 e } a5 @, @: Z' `
};. O" l$ f5 O2 }2 Q% D
7 \. _, ~- H0 M" m* ~
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)
: d: ^' ^: i9 D& | 3 {) ]3 w9 @4 D
f! x4 z! x! F5 J" Y" y( L |
|