|
|
最近在工作看到机台有启动过程中把SPI ROM数据清空,但苦与没有办法很好的Debug(PCA没有架起来),所有与EDK为对象好好的读了一把。
1 ^' X- H4 Z6 p6 d/ }% v. O% x% j9 j2 d) @
SEC/CEI:# H* q9 M. L/ i7 c7 r* u
UEFI BIOS启动时先会执行SEC/CEI,这个阶段在实现的BIOS Code中初始化Debug Port,进入Big Mode,CPU的MicroCode,CacheToRAM的转换.然后跳转到PEI阶段。' s1 e4 K& t0 ~2 c6 K
在EDK中这部分被成了Load FvRecovery.fd文件,这个文件类似与我们的BIOS ROM.里面是我们编译后生成的二进制代码。在这个阶段最主要的是将这个文件加载到内存中(Windows API将文件加载到内存,看API函数).
1 k5 t! h3 H; _2 W! T( Q0 |/ _, C/ Q: L. ^6 v! A; B( T! d3 c9 S
PEI:7 W; P8 }+ g& Y# r5 i4 H }
从这里开始UEFI BIOS和EDK执行基本相同,只是一个是从SPI ROM中定位一个地址(PEIM的开始地址),一个是从内存中定位一个地址(PEIM的开始地址)。从这里开始只讲EDK的执行
5 [# x+ h* G5 E' ?& o EDK调用InitializeMemoryService函数,将HobList清空,peiservice清空。. v5 Y" A1 r' ^$ u# e+ w
InitializePPIService函数,将PPI队列清空,这个队列长0x3F.
6 q' ?$ a# A8 e& n InitializeSercurityService函数,将Notify队列清空。
! O) i$ X" Y: ?, c6 r InitializeDispatcherData函数,将Dispatcher队列清空。4 u: J/ b2 @8 |, Z+ z( n
接着由PeiBuildHobGuid来建立一个HOB(S3返回时这时应该有这个HOB,不用建立,直接使用了,这样就会进入另外一个流程,可以这个EDK不能调试S3,不知道怎么走)。然后由(*PeiServices)->InstallPpi()将这个新HOB加入到PPI中。0 T" j a4 ~3 ~/ P( H
由于在SEC阶段转了以下这几具PPI,所以在执行PEI的Dispatcher之前会先安装东西:
$ A# z" \) J1 @ W7 X EFI_PEI_PPI_DESCRIPTOR gPrivateDispatchTable[] = {
( @0 C9 G) Y7 F+ h {EFI_PEI_PPI_DESCRIPTOR_PPI, &gEfiNtLoadAsDllPpiGuid, &mSecNtLoadAsDllPpi},! t- L- ~. D0 M9 g! h
{EFI_PEI_PPI_DESCRIPTOR_PPI, &gNtPeiLoadFileGuid, &mSecNtLoadFilePpi},
# x: ~: l1 n( V* T {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtAutoScanPpiGuid, &mSecNtAutoScanPpi},
& a( v! F6 d/ n- Z7 _9 m& j {EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiNtThunkPpiGuid, &mSecWinNtThunkPpi},; E) H* k0 @# c7 x/ O
{EFI_PEI_PPI_DESCRIPTOR_PPI, &gPeiStatusCodePpiGuid, &mSecStatusCodePpi},7 m: }+ i9 H/ I0 A; K _& Q. K$ E
{EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST, &gNtFwhPpiGuid, &mSecFwhInformationPpi}
9 K9 c0 ^# ~3 Y2 M8 w) Q };1 t5 E4 s& [, I
每个PPI是由{类型,GUID(名字,以后就根据这个来找到它的),Function_Entry_Address}组成。5 @) h. v% u; N
这些PPI会在PEIDispatcher中用到。
3 |8 B5 _1 Y2 _, H& N 安装完这此东西这开始执行PEIDispatcher函数,函数从BIOS ROM文件中找出PEI的Image(怎么找到,请读一下FV_HEAD(EFI_HEAD),里面讲到了如何区分Image类型),然后定位到PEI Image的入口地址,执行他。在我们的的PEI中,我们一般会申明一个PPI,这个就是这个PEI提供一些服务,PEIDispatcher会将他们加入到PPI-List中,以后其他Image也可以调用他的提供的功能。
% }, T$ H4 U: l0 Z: W 最后EDK会加载一个叫DXEIPL的PEI,DXEIPL提供一个PPI服务,这个PPI的功能是实现从PEI到DXE的切换,这个PPI里面为DXE做了很多的预准备,加载了很多PPI,如对BIOS的解压方法等。# c1 b( D' h% B) @
SwitchStacks () k/ f( k+ r, z0 z8 N7 f6 S
(VOID *) (UINTN) DxeCoreEntryPoint,
5 n- U# C4 N) a- R (UINTN) (HobList.Raw),
% E5 h: Q% b& L+ W' d. { (VOID *) (UINTN) TopOfStack,9 j* I- e) U: \, [3 A. b
(VOID *) (UINTN) BspStore
# J o! Y( R7 S$ i3 n- P );
4 l7 \" h8 x( H5 {4 B 用过汇编的人对这个技术一定很熟悉,不多说了(我别不清,咯。。。)7 i: O, n9 d6 w" @! q
1 d' [6 l& t" @( F2 G
DXE:5 Z3 m, W F) V0 O' ~# ~! B
从PEI到DXE切换时转过来一个HOBLIST参数,DXE会在这个HOB中找到Memory的使用情况,然后根据这些情况将BIOS引到内存(这是EFI的做法)。在EDK在DXE时重新定位一下内存。
* Y0 N% Q5 ^# |接着就会定义我们经常使用到的gST表,gRT表。接着是申明一些Protocol(先不关心这些事)。6 u. X1 ~+ w% t
等这些该加的PPI,Protocol加完了,CoreDispatcher()就出场了。他会的功能类似PEIDispatcher()。从我们BIOS ROM中将DXE的驱动读出,执行执行他们,这时会执行到Driver
! P- n, ^0 |) x0 ?( c0 R中的Support(),Start()两个功能函数。在这两个函数中你可以注册自己的PPI,为其他驱动提供服务。1 t& [8 g2 t) r* p- ?
到些BIOS的引导其他完成。接着该进OS了,看Linux 0.11吧,操作系统是怎么做事情的。0 N: v. k' s! b C+ m7 \
9 n; Y9 D3 Q& s- w
Driver:
4 n9 B* K) t+ k& q+ c& }2 p0 P 我们的驱动什么为在PEI和DXE等不同阶段执行呢?
. \6 V" ^ E% D+ r# L5 j; s5 _$ } 大家请看一下我们的驱动的makefile.(EDK中的*.inf)
- `9 r# m' f0 H# F5 s [defines]& Y2 y# J1 `( S& y1 M6 b. r- z! w
BASE_NAME = OWEN+ ]8 f0 X# J$ r% N, {2 z
FILE_GUID = 1EDD13C1-62EF-4262-A1AA-0040D0830110
2 f5 a7 [7 P: }! X3 |2 Z# f( c COMPONENT_TYPE = BS_DRIVER
) z6 |% x& y' C2 i8 B2 D' R8 o ?# y9 g( x; M
BASE_NAME告诉编译器最终生成的驱动的名字。
& Q8 p: A; a& c+ m: k* _4 | FILE_GUID就是这个驱动的GUID名字,在BIOS中引用某个驱动就是根据它来调用和识别。
Y1 K& r" h9 M$ A COMONENT_TYPE会告诉编译器生成驱动的类型,是PEI,DXE,等。
; k& }# e& F+ a- j 在EDK中有一个FWVolume.c实现的功能就是帮我们把这个TYPE转换面相应的扩展名( y1 x# G8 d1 V: P! d1 h" C* J
COMP_TYPE_EXTENSION mCompTypeExtension[] = {
" M" E- o/ M6 w# s/ q% T {"bs_driver", ".dxe" },
3 Q' }* C: S0 I( I1 y {"rt_driver", ".dxe" },
3 J$ U9 u2 r& z H/ R! y {"sal_rt_driver", ".dxe"},. o( {1 `; R4 y1 o' ~7 o
{"security_core", ".sec"},
+ Z5 I) G* `% K B7 h& m {"pei_core", ".pei"},
) g! T/ v/ }: T) S7 T& _+ s+ n# S' P {"pic_peim", ".pei"},3 I. v4 ^, ?$ N' _; t
{"pe32_peim", ".pei"},
5 d1 U$ ^+ n+ ^ {"relocatable_peim", ".pei"},
& Z. t& d' O) K( T$ ^5 j {"binary", ".ffs"},
3 |: |9 H+ @+ | {"application", ".app"},
8 d, H# I3 J0 a' U. q4 j8 Z {"file", ".ffs"},
# r" ?3 T, Z$ Z: s. E {"fvimagefile", ".fvi"},
1 X/ R0 n& |: v) c+ H {"rawfile", ".raw"},
+ s% X; @% b7 J; d3 a) T: ^1 p+ A+ s {"apriori", ".ffs"},
% [( } M* M7 S- f' U7 E {"combined_peim_driver", ".pei"},/ F3 ?/ s1 F- c x9 N3 {1 N( f( j
{ NULL, NULL }/ w& `1 I+ i$ O& w) ^' `5 S1 a
};, u# k* d( ^' L) [
# e* z& H4 ^3 {7 }% r" D8 a% n
了解了这些,接下我们可以看驱动篇了。(Go On Study... Forever)
" ^2 R- S2 Q% j9 |. q# V1 C {0 K. A+ m- m/ e6 n8 O
" ^) r# r- j6 m) a
|
|