=====================================================================
固件C字营·版权所有·欢迎转载
敬请关注微信公众号:“固件C字营”
敬请关注QQ群:1052307
敬请关注优快云博客:Cstyle_0x007

=====================================================================
所谓”固件“原始的定义是被固化的软件。历史上存储固件的介质有很多类似EEROM、Flash等,目前在UEFI固件领域基本都是用的NorFlash,它最大的特点是支持XIP也即片上执行。UEFI BIOS固件基本都是使用SPI接口的NorFlash来存储。目标二进制固件文件通常包括代码段(code)、只读数据段(RO Data)、已初始化读写数据段(RW Data)、BSS段、堆、栈、以及其他的资源段。固件在XIP阶段的代码在链接的时候,链接地址与存储地址是相同的都会是0x00000000,在C代码开始之前通过crt0库把读写数据段(RW Data)拷贝到可读可写的内存、在内存中创建并初始化BSS段,堆、栈,C代码提供基本的运行环境。下表是固件在XIP执行的时候每一个段的状态,RO Data不需要写因此不需要拷贝到RAM当中,同理在RAM没有准备好之前即使是读写数据段也是只读的,再有就是堆段与栈段在内存没有准备好之前也是不可用。
|
资源类型 |
状态 | ||
|
固化阶段 |
初始化阶段 |
运行阶段 | |
|
代码段(Code) |
存储在flash |
存储在flash |
存储在flash |
|
只读数据段(RO Data) |
存储在flash |
存储在flash |
存储在flash |
|
读写数据段(RW Data) |
存储在flash |
拷贝到RAM |
在RAM中读写 |
|
未初始化数据段(BSS) |
存储在flash |
在RAM中创建并清零 |
在RAM中读写 |
|
堆(heap) |
存储在flash |
在RAM中创建并清零 |
在RAM中读写 |
|
栈(stack) |
存储在flash |
在RAM中创建并清零 |
在RAM中读写 |
在X86当中,SEC阶段及PEI阶段的第一阶段都是XIP模式,也就意味着:
- 代码是直接在flash上XIP执行
- 早期内存不可用
- 堆、栈都不可用,无法通过栈来作为调用栈使用
- C语言代码不可执行
- 全局的非静态变量(读写数据段)不可写
之前介绍过UEFI BIOS是99%的C代码加1%的汇编代码组成的,那么C代码是如何跑起来的呢?主要是通过以下几个方式来提供C语言运行环境。
- 早期使用寄存器作为汇编及汇编与C直接的调用栈
- 使用CAR来作为零时RAM,这样就等同于拥有了一块很小的RAM
- 在#2的基础上调用MRC初始化DDR-RAM
- 把固件拷贝到DDR-RAM
- 切换堆、栈及PC指针到新的RAM地址
- 至此,拥有完整的执行环境
在MdeModulePkg\Core\Pei\Image\Image.c函数PeiImageRead ()提供了从flash读取固件到RAM的服务,使用在SEC阶段传递给的EFI_SEC_PEI_HAND_OFF数据中有包含几个重要信息如:BFV地址及容量,零时RAM地址及容量,PEI阶段可用RAM地址及容量,栈地址及容量,细节可以参考《UEFI内核导读》的“SEC Core简介”相关章节。
/**
Support routine for the PE/COFF Loader that reads a buffer from a PE/COFF file.
The function is used for XIP code to have optimized memory copy.
@param FileHandle - The handle to the PE/COFF file
@param FileOffset - The offset, in bytes, into the file to read
@param ReadSize - The number of bytes to read from the file starting at FileOffset
@param Buffer - A pointer to the buffer to read the data into.
@return EFI_SUCCESS - ReadSize bytes of data were read into Buffer from the PE/COFF file starting at FileOffset
**/
EFI_STATUS EFIAPI PeiImageRead (
IN VOID *FileHandle,
IN UINTN FileOffset,
IN UINTN *ReadSize,
OUT VOID *Buffer
)
{
CHAR8 *Destination8;
CHAR8 *Source8;
Destination8 = Buffer;
Source8 = (CHAR8 *) ((UINTN) FileHandle + FileOffset);
if (Destination8 != Source8) {
CopyMem (Destination8, Source8, *ReadSize);
}
return EFI_SUCCESS;
}
综上述代码可见:
- PEI Core及部分PEIM在PEI第一阶段被复制到RAM当中,PEI第二阶段代码在RAM中被执行。
- 使用内存拷贝的方法,在X86架构中使用的是MOV类指令,而不是IN/OUT类似的IO指令。
- PEI Core的Image service基于PeiImageRead()从flash的读取BFV内的FFS文件并从中查找PE/TE格式可执行文件,relocates到永久DDR RAM当中以备PEI第二阶段执行,或读取查找其他类型文件。
参考代码实现:
\MdeModulePkg\Core\Pei\Image\Image.c
\MdeModulePkg\Core\Dxe\Image\Image.c
转载请注明出处,或可关注微信公众号:固件C字营
=====================================================================
固件C字营·版权所有·欢迎转载
敬请关注微信公众号:“固件C字营”
敬请关注QQ群:1052307
敬请关注优快云博客:Cstyle_0x007

=====================================================================
@微信公众号《固件C字营》不定期更新状态,关注&订阅公众号不迷路。
完整PDF版整理中,可以在优快云下载频道搜索”UEFI内核导读“下载样张......
本文介绍了UEFI BIOS固件在XIP模式下的执行环境,特别是如何在PEI阶段通过PEI Core的Image Service将代码从Flash读取到RAM中,并在RAM中执行。固件的各个段在不同阶段的状态、如何初始化RAM以及如何使用PeiImageRead函数进行内存拷贝等关键点进行了阐述。

4317

被折叠的 条评论
为什么被折叠?



