Step to UEFI (293)DSDT 是如何打包到 UEFI 中的

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文件:

参考:

  1. https://uefi.org/specs/PI/1.8/V3_Code_Definitions.html#firmware-file