菜单

kaoqi
kaoqi
发布于 2026-09-15 / 5 阅读
0
0

RSDP与XSDT主要结构

这两个表在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做更详尽的字节码编排。


评论