UEFI 中的 AML 文件是如何打包的
以OvmfPkg 中的 Bhyve 为研究对象,编译命令:
build -a X64 -p OvmfPkg\Bhyve\BhyveX64.dsc -t VS2019
在\OvmfPkg\Bhyve\AcpiTables\ 目录下我们能看到 DSDT.ASL 这样的文件。对应的,在 \Build\BhyveX64\DEBUG_VS2019\X64\OvmfPkg\Bhyve\AcpiTables\AcpiTables\Makefile 中可以看到编译方法。首先是从 asl生成 aml, 然后是使用 GenSec 成成 RAW 的 SECTION
if exist $(OUTPUT_DIR)\Dsdt.aml GenSec -s EFI_SECTION_RAW -o c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC2.1.raw $(OUTPUT_DIR)\Dsdt.aml
例如:
7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.2.raw : $(OUTPUT_DIR)\Facs.acpi
7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.3.raw : $(OUTPUT_DIR)\Hpet.acpi
7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.4.raw : $(OUTPUT_DIR)\Madt.acpi
7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.5.raw : $(OUTPUT_DIR)\Mcfg.acpi
7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.6.raw : $(OUTPUT_DIR)\Spcr.acpi
7E374E25-8E01-4FEE-87F2-390C23C606CDSEC2.1.raw $(OUTPUT_DIR)\Dsdt.aml
最终使用 GenFFs 把他们打包到一个 FFS 中
GenFfs -t EFI_FV_FILETYPE_FREEFORM -g 7E374E25-8E01-4FEE-87F2-390C23C606CD -o c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CD.ffs -oi c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.1.raw -oi c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.2.raw -oi c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.3.raw -oi c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.4.raw -oi c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.5.raw -oi c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.6.raw -oi c:\buildbs\edk2202302\edk2\Build\BhyveX64\DEBUG_VS2019\FV\Ffs\7E374E25-8E01-4FEE-87F2-390C23C606CDPlatformAcpiTables\7E374E25-8E01-4FEE-87F2-390C23C606CDSEC2.1.raw
3.2.3.1. EFI_FFS_FILE_HEADER
Summary
Each file begins with a header that describes the state and contents of the file. The header is 8-byte aligned with respect to the beginning of the firmware volume.
Prototype
typedef struct {
EFI_GUID Name;
EFI_FFS_INTEGRITY_CHECK IntegrityCheck; //UINT16
EFI_FV_FILETYPE Type; //UINT8
EFI_FFS_FILE_ATTRIBUTES Attributes; //UINT8
UINT8 Size[3];
EFI_FFS_FILE_STATE State; //UINT8
} EFI_FFS_FILE_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;
UINT64 ExtendedSize;
} EFI_FFS_FILE_HEADER2;
上述的区别在于“EFI_FIRMWARE_FILE_SYSTEM3_GUID indicates support for FFS_ATTRIB_LARGE_SIZE and thus support for files 16MB or larger. EFI_FIRMWARE_FILE_SYSTEM2_GUID volume does not contain large files. Files 16 MB or larger use a EFI_FFS_FILE_HEADER2 and smaller files use EFI_FFS_FILE_HEADER.EFI_FIRMWARE_FILE_SYSTEM2_GUID allows backward compatibility with previous versions of this specification”,这里因为文件小,肯定是EFI_FFS_FILE_HEADER。
其中 0x1B92=7058 就是这个FFS 文件的大小。接下来的就是一个 Section 的内容了。
typedef struct {
UINT8 Size[3];
EFI_SECTION_TYPE Type;
} EFI_COMMON_SECTION_HEADER;
typedef struct {
UINT8 Size[3];
EFI_SECTION_TYPE Type;
UINT32 ExtendedSize;
} EFI_COMMON_SECTION_HEADER2;
从内容上来说,就是7E374E25-8E01-4FEE-87F2-390C23C606CDSEC1.1.raw 这个文件的内容:
有了上述的分析,这里编写一个从 FFS 解析 AML 的工具。使用 VC 编写,VS2019 编译通过
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#pragma warning(disable:4996)
#pragma pack(1)
//
// Basical data type definitions introduced in UEFI.
//
typedef struct {
UINT32 Data1;
UINT16 Data2;
UINT16 Data3;
UINT8 Data4[8];
} EFI_GUID;
//
// Used to verify the integrity of the file.
//
typedef union {
struct {
UINT8 Header;
UINT8 File;
} Checksum;
UINT16 Checksum16;
} EFI_FFS_INTEGRITY_CHECK;
typedef UINT8 EFI_FV_FILETYPE;
typedef UINT8 EFI_FFS_FILE_ATTRIBUTES;
typedef UINT8 EFI_FFS_FILE_STATE;
typedef struct {
EFI_GUID Name;
EFI_FFS_INTEGRITY_CHECK IntegrityCheck; //UINT16
EFI_FV_FILETYPE Type; //UINT8
EFI_FFS_FILE_ATTRIBUTES Attributes; //UINT8
UINT8 Size[3];
EFI_FFS_FILE_STATE State; //UINT8
} EFI_FFS_FILE_HEADER;
typedef UINT8 EFI_SECTION_TYPE;
typedef struct {
UINT8 Size[3];
EFI_SECTION_TYPE Type;
} EFI_COMMON_SECTION_HEADER;
#pragma pack()
void SaveBufferToFile(UINT8 Index, char* p, UINT16 Len) {
FILE* SaveTo;
char NumBuf[20];
sprintf(NumBuf, "%d.rom",Index);
SaveTo=fopen(NumBuf, "wb");
fwrite(p,1,Len, SaveTo);
fclose(SaveTo);
}
int main(int argc, char* argv[])
{
if (argc != 2) {
printf("Please input the FFS name\n");
return 1;
}
FILE* file;
char* buffer;
long fileLen;
// 打开文件
file = fopen(argv[1], "rb"); // 以二进制模式读取文件
if (!file) {
printf("Unable to open file %s\n", argv[1]);
return 2;
}
// 获取文件大小
fseek(file, 0, SEEK_END); // 移动到文件末尾
fileLen = ftell(file); // 当前位置即文件大小
fseek(file, 0, SEEK_SET); // 移动回文件开头
printf("%s file size is %d\n", argv[1], fileLen);
// 分配内存
buffer = (char*)malloc(fileLen);
if (!buffer) {
printf("Memory allocation failed\n");
fclose(file);
return 3;
}
// 读取文件内容到内存
fread(buffer, fileLen, 1, file);
fclose(file); // 关闭文件
char* pFile= &buffer[sizeof(EFI_FFS_FILE_HEADER)];
EFI_COMMON_SECTION_HEADER* pSection;
UINT8 Index = 0;
while (pFile - buffer<fileLen) {
pSection = (EFI_COMMON_SECTION_HEADER*) pFile;
// 输出起始位置,长度
printf("Section start at %x %x\n",
pFile- buffer,
(pSection->Size[0])+ (pSection->Size[1]<<8)+(pSection->Size[2]<<16));
// 这里我们要保存去掉头的 Section
SaveBufferToFile(Index, &buffer[pFile - buffer]+4, (pSection->Size[0]) + (pSection->Size[1] << 8) + (pSection->Size[2] << 16)-4);
Index++;
if ((pFile - buffer + (pSection->Size[0]) + (pSection->Size[1] << 8) + (pSection->Size[2] << 16)) % 4 != 0) {
pFile = &buffer[((pFile - buffer + (pSection->Size[0]) + (pSection->Size[1] << 8) + (pSection->Size[2] << 16))/4+1)*4];
}
else {
pFile = &buffer[pFile - buffer + (pSection->Size[0]) + (pSection->Size[1] << 8) + (pSection->Size[2] << 16)];
}
}
return 0;
}
例如,使用这个工具分解 QEMU中Bhyve 的7E374E25-8E01-4FEE-87F2-390C23C606CD.ffs:
可以生成 0.rom-6.rom,就是打包起来的 AML 文件:
本文提到的可执行程序源代码:
本文提到的编译后的EXE 和FFS文件:
参考:
https://uefi.org/specs/PI/1.8/V3_Code_Definitions.html#firmware-file