接着上一篇, 这次还是从0xFFFFFFF0这个位置开始,在开始之前先声明下,我要分析的ROM大小为8MByte,所以从下图1的左边的地址列可以看出最大的地址是0x007ffff0这个地址就是前面说的0xfffffff0,可以把这两个地址完全对等来看待,同理0x007fffff就等于0xfffffff。下面的计算和地址(32bit)都是基于这个逻辑,不再赘述。我们这次关注的点不是第一条指令,而重点关注FwVol所以我们需要从0xfffffffc(0x007ffffc)开始,我们获取到它的值是0xfff40000,然后再找 【0xfff40000】 是什么?答案在下面,请接着图2往下看:
图1
计算公式是这样的:【0xfff40000】=0x007fffff - (0xffffffff-【0xfffffffc】)=0x007fffff-(0xffffffff-0xfff40000)=0x00740000,其中【xxxxxxxx】表示取值的意思,其他的是基本的四则运算,实在不会算的话,你可以回去找那些曾经教过你数学的体育老师把你的学费要回来了^_^。图2是我们从【0xfff40000】读到的东西,这个是什么呢,大家可以仔细看下,跟上一篇讲的很类似,这里其实就是一个FV的开始的文件头,一个完整的BIOS应该是由N多个的FV组成的,这一个应该算是最重要的一个,我们把他称之为BFV,是不是很熟悉,上一篇我们提到了一个叫做VTF的东西,这次我们要说说这个BFV,好开始了,我们先打开那一堆的intel大叔们写的E文文档,一个一个字节来对着看,顺便检验下那堆文档是否真的是对的,或许他们写的文档和code不匹配也说不准,只有自己真正的读了才知道。
对着下面的FV header/File header/Section header的结构和BIOS rom我们很容易就能读出端倪来,从下图我们大概可以读出如下信息:
1.该FV是基于FFS2的文件格式。
2.该FV的长度是0xc0000Byte
3.该FV的文件属性是0xfffe0300
4.该FV的文件头长度为0x48
5.该FV包含的logical block 数量为0x0c
6.该FV的logical block大小为0x1000(4KByte,LBAS xLBAN=4K x 0x0c =0xc0000)
7.0x007400408开始就是这个FV里面包含的第一个File,可以根据上一篇的方法对照EFI_FFS_FILE_HEADER和EFI_COMMON_SECTION_HEADER来一个一个递归的找出来,细节可看PI 1.3
8.疑问:标号为1的位置是,ZeroVector在spc上The first 16 bytes are reserved to allow for the reset vector of processors whose reset vector is at address 0,看了其他的几个FV这个位置都是0,但是BFV这里是非零值,不知道用意何在,或许是记录的某个地址,供PEICore来参考,或许是一个jmp的目标地址???
typedef struct {
UINT8 ZeroVector[16];
EFI_GUID FileSystemGuid;
UINT64 FvLength;
UINT32 Signature;
EFI_FVB_ATTRIBUTES_2 Attributes;
UINT16 HeaderLength;
UINT16 Checksum;
UINT16 ExtHeaderOffset;
UINT8 Reserved[1];
UINT8 Revision;
EFI_FV_BLOCK_MAP BlockMap[];
}
EFI_FIRMWARE_VOLUME_HEADER;
typedef struct {
EFI_GUID Name;
EFI_FFS_INTEGRITY_CHECK IntegrityCheck;
EFI_FV_FILETYPE Type;
EFI_FFS_FILE_ATTRIBUTES Attributes;
UINT8 Size[3];
EFI_FFS_FILE_STATE State;
}
EFI_FFS_FILE_HEADER;
typedef struct {
UINT8 Size[3];
EFI_SECTION_TYPE Type;
}
EFI_COMMON_SECTION_HEADER;
图2
至此,我们对BIOS ROM的分析算是告一段落,分析ROM只是一个手段,下面我们看下通过分析ROM我们能得到那些其他的信息。我们来看SECcore和PEICore是如果使用这些信息的。
/// EFI_SEC_PEI_HAND_OFF structure holds information about
/// PEI core's operating environment, such as the size of location of
/// temporary RAM, the stack location and BFV location.
typedef struct
_EFI_SEC_PEI_HAND_OFF {
/// Size of the data structure.
UINT16 DataSize;
/// Points to the first byte of the boot firmware volume,
/// which the PEI Dispatcher should search for
/// PEI modules.
VOID *BootFirmwareVolumeBase;
/// Size of the boot firmware volume, in bytes.
UINTN BootFirmwareVolumeSize;
/// Points to the first byte of the temporary RAM.
VOID *TemporaryRamBase;
/// Size of the temporary RAM, in bytes.
UINTN TemporaryRamSize;
/// Points to the first byte of the temporary RAM
/// available for use by the PEI Foundation. The area
/// described by PeiTemporaryRamBase and PeiTemporaryRamSize
/// must not extend outside beyond the area described by
/// TemporaryRamBase & TemporaryRamSize. This area should not
/// overlap with the area reported by StackBase and
/// StackSize.
VOID *PeiTemporaryRamBase;
/// The size of the available temporary RAM available for
/// use by the PEI Foundation, in bytes.
UINTN PeiTemporaryRamSize;
/// Points to the first byte of the stack.
/// This are may be part of the memory described by
/// TemporaryRamBase and TemporaryRamSize
/// or may be an entirely separate area.
VOID *StackBase;
/// Size of the stack, in bytes.
UINTN StackSize;
} EFI_SEC_PEI_HAND_OFF;
上面有一个数据结构EFI_SEC_PEI_HAND_OFF,这个是UEFI当中在SEC阶段最最重要的一个数据结构,他完成了从汇编语言执行环境向C语言执行环境的蜕变。在此之前,我们的SECCore完成了cpu,chipset的最基本的初始化包括但不限于:cpu模式的切换,RCBA,PCIEXBAR,MCHBAR,DMIBAR,Interrupt,SPI,ROM Decode,PMBase,GPIOBase,CMOS,Microcode的加载,CAR的设置等,甚至还可以在里面完成一些OEM的特殊功能,比如设置一个PCH的引脚为GPO,然后在POST过程中用示波器来测量信号变化,以此来测量POST的各个阶段的耗时;对各种需要上电就比较早做设定的外设进行设定等等。
这个数据结构里面有一项很重要的部分就是用来指示BFV的位置的(这个就是了
BootFirmwareVolumeBase),SECCore调用
call dword PTR DataSector:[0FFFFFFE0h]服务,完成从SEC阶段到PEI阶段的正式权利交接,然后大权就由PEICore来掌控,当然在交接之前必须的的一些前提条件是要必须完成的,就类似于新旧政府权利交接之前,先的把各种绊脚石移除了。这个相信大家都懂的。
好了SEC的部分就先这样,暂时先这样,下面来研究PEICore。