这两个表在EDK2、GRUB2、KERNAL等多个项目中都会涉及,且同时还存在于一份标准的ACPI文档之中。
ACPI规范本体的5.2章节详细规范了这一系列的表格事宜:5. ACPI Software Programming Model — ACPI Specification 6.6 documentation
Root System Description Pointer为主要表项,下面是以EDK2项目中的MdePkg为参考的结构体定义:
typedef struct {
UINT64 Signature;
UINT8 Checksum;
UINT8 OemId[6];
UINT8 Revision;
UINT32 RsdtAddress;
UINT32 Length;
UINT64 XsdtAddress;
UINT8 ExtendedChecksum;
UINT8 Reserved[3];
} EFI_ACPI_6_6_ROOT_SYSTEM_DESCRIPTION_POINTER;Extended System Description Table作为一系列描述表的目录,由于其不定长,多以在EDK2中没有直接的结构体可以描述这个struct。具体的做法是,首先定义一个EFI_ACPI_DESCRIPTION_HEADER结构的表头指针,然后根据RSDP中的地址指向这个指针,再然后读取这个表头的Length字段。由Length获取总长度后,减去本体偏移再除以64位的长度,就可以得到存在多少个表的数目。
typedef struct {
UINT32 Signature;
UINT32 Length;
UINT8 Revision;
UINT8 Checksum;
UINT8 OemId[6];
UINT64 OemTableId;
UINT32 OemRevision;
UINT32 CreatorId;
UINT32 CreatorRevision;
} EFI_ACPI_DESCRIPTION_HEADER;可以使用如下的方式来进行entry的端口:
// 使用RSDP表获取XSDT表的地址
Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)Rsdp->XsdtAddress;
// 计算出XSDT表的数目
NumberOfTableEntries =(Xsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof (UINT64);
// 使用For循环来进行entry的遍历
for (Index = 0; Index < NumberOfTableEntries; Index++) {
// 根据当前Index,计算当前entry位置
CurrentTableEntry =
(VOID *)((UINT8 *)Xsdt + sizeof (EFI_ACPI_DESCRIPTION_HEADER) + Index * sizeof (UINT64));
// 根据当前entry值,转换为当前表指针
CurrentTablePointer = (UINTN)*(UINT64 *)CurrentTableEntry;
// 根据当前表指针,进行表头的struct解析
CurrentTable = (EFI_ACPI_DESCRIPTION_HEADER *)CurrentTablePointer;
// 使用一个ASCII字符比对函数,完成对FACP表的抓取
if (!AsciiStrnCmp ((CHAR8 *)&CurrentTable->Signature, "FACP", 4)) {
Fadt2Table = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *)(UINTN)
CurrentTablePointer;
Facs2Table = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)
Fadt2Table->FirmwareCtrl;
DsdtTable = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)Fadt2Table->Dsdt;
}
}这里比较重要的是,由XSDT表来确定FACP表,然后再根据FACP表确定FADT、FACS、DSDT这三张表。
这里需要用到一个ASCII比对函数,以及需要一个FADT、FACS的表struct描述(DSDT表由于也是可扩展的,所以也使用EFI_ACPI_DESCRIPTION_HEADER)。
字符对比函数:
// 这是一个删除了断言函数的非DEBUG版本,需要使用者自己来确保正确性哦
INTN
EFIAPI
AsciiStrnCmp (
IN CONST CHAR8 *FirstString,
IN CONST CHAR8 *SecondString,
IN UINTN Length
)
{
if (Length == 0) {
return 0;
}
while ((*FirstString != '\0') &&
(*SecondString != '\0') &&
(*FirstString == *SecondString) &&
(Length > 1))
{
FirstString++;
SecondString++;
Length--;
}
return *FirstString - *SecondString;
}FADT以及FACS的表项结构:
// GAS头
typedef struct {
UINT8 AddressSpaceId;
UINT8 RegisterBitWidth;
UINT8 RegisterBitOffset;
UINT8 Reserved;
UINT64 Address;
} EFI_ACPI_2_0_GENERIC_ADDRESS_STRUCTURE;
// FADT表结构
typedef struct {
EFI_ACPI_DESCRIPTION_HEADER Header;
UINT32 FirmwareCtrl;
UINT32 Dsdt;
UINT8 Reserved0;
UINT8 PreferredPmProfile;
UINT16 SciInt;
UINT32 SmiCmd;
UINT8 AcpiEnable;
UINT8 AcpiDisable;
UINT8 S4BiosReq;
UINT8 PstateCnt;
UINT32 Pm1aEvtBlk;
UINT32 Pm1bEvtBlk;
UINT32 Pm1aCntBlk;
UINT32 Pm1bCntBlk;
UINT32 Pm2CntBlk;
UINT32 PmTmrBlk;
UINT32 Gpe0Blk;
UINT32 Gpe1Blk;
UINT8 Pm1EvtLen;
UINT8 Pm1CntLen;
UINT8 Pm2CntLen;
UINT8 PmTmrLen;
UINT8 Gpe0BlkLen;
UINT8 Gpe1BlkLen;
UINT8 Gpe1Base;
UINT8 CstCnt;
UINT16 PLvl2Lat;
UINT16 PLvl3Lat;
UINT16 FlushSize;
UINT16 FlushStride;
UINT8 DutyOffset;
UINT8 DutyWidth;
UINT8 DayAlrm;
UINT8 MonAlrm;
UINT8 Century;
UINT16 IaPcBootArch;
UINT8 Reserved1;
UINT32 Flags;
EFI_ACPI_2_0_GENERIC_ADDRESS_STRUCTURE ResetReg;
UINT8 ResetValue;
UINT8 Reserved2[3];
UINT64 XFirmwareCtrl;
UINT64 XDsdt;
EFI_ACPI_2_0_GENERIC_ADDRESS_STRUCTURE XPm1aEvtBlk;
EFI_ACPI_2_0_GENERIC_ADDRESS_STRUCTURE XPm1bEvtBlk;
EFI_ACPI_2_0_GENERIC_ADDRESS_STRUCTURE XPm1aCntBlk;
EFI_ACPI_2_0_GENERIC_ADDRESS_STRUCTURE XPm1bCntBlk;
EFI_ACPI_2_0_GENERIC_ADDRESS_STRUCTURE XPm2CntBlk;
EFI_ACPI_2_0_GENERIC_ADDRESS_STRUCTURE XPmTmrBlk;
EFI_ACPI_2_0_GENERIC_ADDRESS_STRUCTURE XGpe0Blk;
EFI_ACPI_2_0_GENERIC_ADDRESS_STRUCTURE XGpe1Blk;
} EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE;
// FACS表结构
typedef struct {
UINT32 Signature;
UINT32 Length;
UINT32 HardwareSignature;
UINT32 FirmwareWakingVector;
UINT32 GlobalLock;
UINT32 Flags;
UINT64 XFirmwareWakingVector;
UINT8 Version;
UINT8 Reserved[31];
} EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE;最后,有必要补充解释一下DSDT表在ACPI中的实际样子,DSDT表本身由一个表描述头+n个Definition Block组成,具体的Block由AML做更详尽的字节码编排。