如果对ACPI概念不熟悉,可以先参考这篇【UEFI基础】ACPI基础
解析代码github地址: TestPkg
ACPI 表结构
ACPI的第一张表示RSDP(Root System Description Pointer),它里面存放了R(X)DST的地址。RDST是32位地址,XDST是64位地址,其功能是一样的。后面用的是AARCH64架构,所以用的64位地址的XDST。
根据RSDP找到XDST后,就可以根据XDST找出一个个表项Entry,每一个Entry记录了对应表的地址。表项的类型有很多,这里可以参考ACPI 6.3 SPEC。并且Entry的类型会随着ACPI版本增加或者改动。
注意XDST第一个Entry一定是FADT。
这里只以QEMU AARCH64上用到的类型举例。QEMU上一共有8张表如下:
- FADT (Fixed ACPI Description Table) ,主要放了一些硬件信息和DSDT的地址。
- MADT(Multiple APIC Description Table ),描述了中断硬件相关的信息。
- PPTT(Processor Properties Topology Table), 描述了CPU相关的信息。
- MCFG(PCI Express memory mapped configuration space base address Description Table) PCIE内存空间先关的地址。
- GTDT(Generic Timer Description Table), 描述了timer相关的信息。
- SPCR(Serial Port Console Redirection Table), 描述了串口相关的信息。
- DBG2(Debug Port Table), 描述了Debug口相关信息。
- IORT(I/O Remapping Table), 描述了IO Remap相关信息。
因此,在QEMU上XSDT的布局如下。Header下面就放了8个表的地址。
FADT中包含了一些硬件相关的信息BLKs和DSDT的地址。而DSDT中包含许多硬件的定义块,定义块描述了硬件的静态和动态信息。
RSDP
首先来看RSDP的定义。
MdePkg/Include/IndustryStandard/Acpi63.h
typedef struct {
UINT64 Signature; --> 签名为"RSD PTR "
UINT8 Checksum; --> 此校验和仅包含此表的前20个字节,即从0~19,并且包含此字段本身。前20个字节的总和必须是零
UINT8 OemId[6]; --> OEM ID字符串
UINT8 Revision; --> 版本号
UINT32 RsdtAddress; --> 32位RSDT地址
UINT32 Length; --> RSDP表长度
UINT64 XsdtAddress; --> 64位XSDT地址
UINT8 ExtendedChecksum; --> 这是整个表的校验和,包含两个校验和字段
UINT8 Reserved[3];
} EFI_ACPI_6_3_ROOT_SYSTEM_DESCRIPTION_POINTER;
RSDP存放在UEFI的configuration table中,它的guid是gEfiAcpi20TableGuid。
我们可以用如下代码来查找到RSDP的地址,原理很简单,就是遍历UEFI的configuration table,如果GUID是ACPI表,就把它拿出来。找到configuration table的VendorTable字段,它就是RSDP地址
TestPkg/TestDxeAcpi.c
EFIAPI
EFI_STATUS
TestDxeDumpAcpi()
{
UINTN i, j = 0, EntryCount;
UINT8 Temp = 0;
EFI_CONFIGURATION_TABLE *configTab = NULL;
EFI_ACPI_DESCRIPTION_HEADER *XSDT, *Entry, *DSDT;
UINT64 *EntryPtr;
EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE *FADT;
EFI_ACPI_6_3_ROOT_SYSTEM_DESCRIPTION_POINTER *Root;
DEBUG((DEBUG_INFO, "Dump ACPI\n"));
configTab = gST->ConfigurationTable;
//遍历系统配置表
for(i=0; i<gST->NumberOfTableEntries;i++)
{
if ((CompareGuid(&configTab->VendorGuid, &gEfiAcpiTableGuid) == TRUE) ||
(CompareGuid(&configTab->VendorGuid, &gEfiAcpi20TableGuid) == TRUE))
{
//如果是ACPI表就拿出它的地址
DEBUG((DEBUG_INFO, "Found ACPI table\n"));
DEBUG((DEBUG_INFO, "Address: @[0x%p]\n",configTab));
Root = configTab->VendorTable; -->找到RSDP
//打印RSDP相关字段
DEBUG((DEBUG_INFO, "RSDP @[0x%p]\n", Root));
DEBUG((DEBUG_INFO, " Signature:"));
for(j = 0; j < 8; j++) {
Temp = (Root->Signature >> (j * 8)) & 0xff;
DEBUG((DEBUG_INFO, "%c", Temp));
}
DEBUG((DEBUG_INFO, "\n"));
DEBUG((DEBUG_INFO, " Revision:%d\n", Root->Revision));
DEBUG((DEBUG_INFO, " OEM_ID:"));
for (j = 0; j < 6; j++) {
DEBUG((DEBUG_INFO, "%c", Root->OemId[j]));
}
DEBUG((DEBUG_INFO, "\n"));
DEBUG((DEBUG_INFO, " Length=[0x%x]\n", Root->Length));
DEBUG((DEBUG_INFO, " XSDT address=[0x%p]\n", Root->XsdtAddress));
DEBUG((DEBUG_INFO, " RSDT address=[0x%p]\n", Root->RsdtAddress));
...
}
Dump ACPI
Found ACPI table
Address: @[0x43FDFBD8]
RSDP @[0x405C0018]
Signature:RSD PTR --> Singautre符合RSD PTR
Revision:2
OEM_ID:BOCHS
Length=[0x24] -->长度就是EFI_ACPI_6_3_ROOT_SYSTEM_DESCRIPTION_POINTER的长度
XSDT address=[0x405CFE98] -->使用的是XSDT
RSDT address=[0x0] -->RSDT不存在
XSDT
XSDT没有专有的结构体定义,它只有APCI Header,然后更着一串Entry地址。XSDT的Header定义如下。后面其他的表也是用这个Header,它是一个ACPI通用的描述头。
MdePkg/Include/IndustryStandard/Acpi10.h
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;
当找到XSDT后,可以遍历它后面的Entry地址,然后找到对应的Entry
TestPkg/TestDxeAcpi.c
if (Root->Revision >= EFI_ACPI_6_3_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION) {
//从RSDP中拿到XSDT的地址
XSDT = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN) Root->XsdtAddress;
//计算有多少个Entry,这里有8个Entry
EntryCount = (XSDT->Length - sizeof(EFI_ACPI_DESCRIPTION_HEADER))
/ sizeof(UINT64);
DEBUG((DEBUG_INFO, " XSDT @[0x%p]\n", XSDT));
//打印XSDT的签名应该就是字符串"XSDT"
DEBUG((DEBUG_INFO, " Signature:"));
for(j = 0; j < 4; j++) {
Temp = (XSDT->Signature >> (j * 8)) & 0xff;
DEBUG((DEBUG_INFO, "%c", Temp));
}
DEBUG((DEBUG_INFO, "\n"));
DEBUG((DEBUG_INFO, " Length=[%d]\n", XSDT->Length));
DEBUG((DEBUG_INFO, " Entry Count=[%d]\n", EntryCount));
DEBUG((DEBUG_INFO, " OEM_ID:"));
for (j = 0; j < 6; j++) {
DEBUG((DEBUG_INFO, "%c", Root->OemId[j]));
}
DEBUG((DEBUG_INFO, "\n"));
EntryPtr=(UINT64 *)(XSDT+1);
//遍历XSDT中的Entry,把Entry的签名打印出来
for (j = 0; j < EntryCount; j++, EntryPtr++) {
Entry = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)(*EntryPtr));
if (Entry == NULL) {
continue;
}
DEBUG((DEBUG_INFO, " Signature:"));
for(j = 0; j < 4; j++) {
Temp = (Entry->Signature >> (j * 8)) & 0xff;
DEBUG((DEBUG_INFO, "%c", Temp));
}
DEBUG((DEBUG_INFO, "\n"));
}
}
可以看到最后的打印如下
XSDT @[0x405CFE98]
Signature:XSDT --> 签名是"XSDT"
Length=[100] --> 长度为100 = 表头长度36 + 表项Entry长度64
Entry Count=[8] --> Entry Count = 8 = 64 / 8
OEM_ID:BOCHS --> OEM ID和RSDP一样都是BOCHS
Signature:FACP --> 第一张表FADT的签名"FACP"
Signature:APIC --> 第二张表MADT的签名"APIC"
Signature:PPTT --> .
Signature:GTDT .
Signature:MCFG .
Signature:SPCR .
Signature:DBG2 --> .
Signature:IORT --> 最后一张表的签名"IORT"
FADT
FADT的定义很长, 如下
MdePkg/Include/IndustryStandard/Acpi63.h
typedef struct {
EFI_ACPI_DESCRIPTION_HEADER Header; --> 通用Header
UINT32 FirmwareCtrl; --> FACS的地址, OSPM和固件利用此字段交换控制信息。
UINT32 Dsdt; --> DSDT的地址
UINT8 Reserved0;
----------------------
*电源管理类型
*0 - Unspecified
*1 - Desktp
*2 - Mobile
*3 - Wokrstation
*4 - Enterprise Server
*5 - SOHO Server
*6 - Applicance PC
*7 - Performance Server
*8 - Tablet
*>8 Reserved
-----------------------
UINT8 PreferredPmProfile;
-->在8259模式下SCI中断绑定的系统向量。在不包含8269的系统中,此字段包含SCI中断的全局系统中断号。
-->OSPM需要把ACPI SCI中断看成是可共享、电平触发、低有效的中断。
UINT16 SciInt;
UINT32 SmiCmd; --> SMI命令所在的端口
UINT8 AcpiEnable; --> 将此值写到SMI_CMD来重新使能ACPI硬件寄存器SMI所有权。在不支持遗留模式的系统中,此字段被保留。
UINT8 AcpiDisable; --> 将此值写到SMI_CMD来重新使屏蔽ACPI硬件寄存器SMI所有权。在不支持遗留模式的系统中,此字段被保留。
UINT8 S4BiosReq; --> 为了进入S4BIOS状态需要将此值写到SM1_CMD中。
UINT8 PstateCnt; --> 如果此字段值为非零,那么OPSM将此值写到SM1_CMD寄存器后会承担处理器性能状态控制责任
UINT32 Pm1aEvtBlk; --> PM1a事件寄存器块的系统端口地址
UINT32 Pm1bEvtBlk; --> PM1b事件寄存器块的系统端口地址
UINT32 Pm1aCntBlk; --> PM1a控制寄存器块的系统端口地址
UINT32 Pm1bCntBlk; --> PM1b控制寄存器块的系统端口地址
UINT32 Pm2CntBlk; --> PM2事件寄存器块的系统端口地址
UINT32 PmTmrBlk; --> 电源管理定时器控制寄存器的系统端口地址
UINT32 Gpe0Blk; --> 通用目的事件0寄存器块的系统端口地址
UINT32 Gpe1Blk; --> 通用目的事件1寄存器块的系统端口地址
UINT8 Pm1EvtLen; --> 由PM1a_EVT_BLK和PM1b_EVT_BLK解码的字节数
UINT8 Pm1CntLen; --> 由PM1a_CNT_BLK和PM1b_CNT_BLK解码的字节数
UINT8 Pm2CntLen; --> 由PM2_CNT_BLK解码的字节数
UINT8 PmTmrLen; --> 由PM_TMR_BLK解码的字节数
UINT8 Gpe0BlkLen; --> 由GEP0_BLK解码的字节数
UINT8 Gpe1BlkLen; --> 由GPE1_BLK解码的字节数
UINT8 Gpe1Base; --> 在ACPI通用的事件模型中GPE1事件开始处的偏移
UINT8 CstCnt; --> OSPM将此值写到SMI_CMD寄存器向OS表明对_CST对象和C状态改变通知的支持
UINT16 PLvl2Lat; --> 进入和退出C2状态的最长硬件延时
UINT16 PLvl3Lat; --> 进入和退出C3状态的最长硬件延时
UINT16 FlushSize; --> 缓存刷新步幅大小
UINT16 FlushStride; --> 处理器内存缓存的缓存行宽度
UINT8 DutyOffset; --> 在处理器的P_CNT寄存器中,表示处理器职守周期所在位置基于零的索引
UINT8 DutyWidth;
UINT8 DayAlrm; --> 日期警告值所在对应的RTC_CMOS_RAM索引。
UINT8 MonAlrm; --> 月份警告值所在对应的RTC_CMOS_RAM索引。
UINT8 Century; --> 日期世纪值所对应的RTC_CMOS_RAM索引。
UINT16 IaPcBootArch; --> IA-PC启动架构标志
UINT8 Reserved1;
UINT32 Flags; --> 固定特征标志
EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE ResetReg; --> 重置寄存器的地址
UINT8 ResetValue; --> 为了重启系统,此值被写到RESET_REG端口
UINT16 ArmBootArch; --> ARM架构启动标志
UINT8 MinorVersion; --> 此版本号
UINT64 XFirmwareCtrl; --> EACS的64位物理地址
UINT64 XDsdt; --> DSDT的64位物理地址
EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm1aEvtBlk; --> PM1a 事件寄存器块的扩展地址
EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm1bEvtBlk; --> PM1b 事件寄存器块的扩展地址
EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm1aCntBlk; --> PM1a 控制寄存器块的扩展地址
EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm1bCntBlk; --> PM1b 控制寄存器块的扩展地址
EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPm2CntBlk; --> PM2 控制寄存器块的扩展地址
EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XPmTmrBlk; --> PM TMR 寄存器块的扩展地址
EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XGpe0Blk; --> GEP0 寄存器块的扩展地址
EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE XGpe1Blk; --> GEP1 寄存器块的扩展地址
EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE SleepControlReg; --> 睡眠控制寄存器的地址
EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE SleepStatusReg; --> 睡眠状态寄存器的地址
UINT64 HypervisorVendorIdentity; --> Hypevisor ID
} EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE;
解析FADT代码, 就是根据表的定义解析其结构并打印
TestPkg/TestDxeAcpi.c
switch (Entry->Signature) {
case EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE:
FADT = (EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE *)(UINTN) Entry;
DumpFADT(FADT);
DSDT = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)(FADT->Dsdt);
VOID
DumpFADT(EFI_ACPI_6_3_FIXED_ACPI_DESCRIPTION_TABLE *FADT)
{
char *PMProfiles[] = {
"Unspecified",
"Desktop",
"Mobile",
"WorkStation",
"Enterprise Server",
"SOHO Server",
"Applicance PC",
"Performance Server",
"Tablet"
};
DEBUG((DEBUG_INFO, "\n#########Start Dump FADT##########\n"));
DumpACPIHeader(&FADT->Header);
DEBUG((DEBUG_INFO, "FACS:0x%x\n", FADT->FirmwareCtrl));
DEBUG((DEBUG_INFO, "Dsdt:0x%x\n", FADT->Dsdt));
DEBUG((DEBUG_INFO, "", FADT->PreferredPmProfile));
if (FADT->PreferredPmProfile > 8) {
DEBUG((DEBUG_INFO, "PreferredPmProfile: Reserved\n"));
} else {
DEBUG((DEBUG_INFO, "PreferredPmProfile: %a\n", PMProfiles[FADT->PreferredPmProfile]));
}
DEBUG((DEBUG_INFO, "SCI INT:0x%04x\n", FADT->SciInt));
DEBUG((DEBUG_INFO, "SMI CMD:0x%08x\n", FADT->SmiCmd));
DEBUG((DEBUG_INFO, "ACPI ENABLE:0x%02x\n", FADT->AcpiEnable));
DEBUG((DEBUG_INFO, "ACPI DISABLE:0x%02x\n", FADT->AcpiDisable));
DEBUG((DEBUG_INFO, "S4BIOS REQ:0x%02x\n", FADT->S4BiosReq));
DEBUG((DEBUG_INFO, "PSTATE CNT:0x%02x\n", FADT->PstateCnt));
DEBUG((DEBUG_INFO, "PM1a EVT BLK:0x%08x\n", FADT->Pm1aEvtBlk));
DEBUG((DEBUG_INFO, "PM1b EVT BLK:0x%08x\n", FADT->Pm1bEvtBlk));
DEBUG((DEBUG_INFO, "PM1a CNT BLK:0x%08x\n", FADT->Pm1aCntBlk));
DEBUG((DEBUG_INFO, "PM1b CNT BLK:0x%08x\n", FADT->Pm1bCntBlk));
DEBUG((DEBUG_INFO, "PM TMR BLK:0x%08x\n", FADT->PmTmrBlk));
DEBUG((DEBUG_INFO, "GPE0 BLK:0x%08x\n", FADT->Gpe0Blk));
DEBUG((DEBUG_INFO, "GPE1 BLK:0x%08x\n", FADT->Gpe1Blk));
DEBUG((DEBUG_INFO, "PM1 EVT LEN:0x%02x\n", FADT->Pm1EvtLen));
DEBUG((DEBUG_INFO, "PM1 CNT LEN:0x%02x\n", FADT->Pm1CntLen));
DEBUG((DEBUG_INFO, "PM2 CNT LEN:0x%02x\n", FADT->Pm2CntLen));
DEBUG((DEBUG_INFO, "PM TMR LEN:0x%02x\n", FADT->PmTmrLen));
DEBUG((DEBUG_INFO, "GPE0 BLK LEN:0x%02x\n", FADT->Gpe0BlkLen));
DEBUG((DEBUG_INFO, "GPE1 BLK LEN:0x%02x\n", FADT->Gpe1BlkLen));
DEBUG((DEBUG_INFO, "GPE1 BASE:0x%02x\n", FADT->Gpe1Base));
DEBUG((DEBUG_INFO, "CST CNT:0x%02x\n", FADT->CstCnt));
DEBUG((DEBUG_INFO, "P LVL2 LAT:0x%04x\n", FADT->PLvl2Lat));
DEBUG((DEBUG_INFO, "P LVL3 LAT:0x%04x\n", FADT->PLvl3Lat));
DEBUG((DEBUG_INFO, "Flush Size:0x%04x\n", FADT->FlushSize));
DEBUG((DEBUG_INFO, "FLUSH STRIDE:0x%04x\n", FADT->FlushStride));
DEBUG((DEBUG_INFO, "DUTY OFFSET:0x%02x\n", FADT->DutyOffset));
DEBUG((DEBUG_INFO, "DUTY WIDTH:0x%02x\n", FADT->DutyWidth));
DEBUG((DEBUG_INFO, "DAY ALARM:0x%02x\n", FADT->DayAlrm));
DEBUG((DEBUG_INFO, "MON ALARM:0x%02x\n", FADT->MonAlrm));
DEBUG((DEBUG_INFO, "CENTURY:0x%02x\n", FADT->Century));
DEBUG((DEBUG_INFO, "IAPC_BOOT_ARCH:0x%04x\n", FADT->IaPcBootArch));
DEBUG((DEBUG_INFO, "Flags:0x%08x\n", FADT->Flags));
DumpGenericAddress("Reset Reg", &FADT->ResetReg);
DEBUG((DEBUG_INFO, "Reset Value:0x%02x\n", FADT->ResetValue));
DEBUG((DEBUG_INFO, "ARM BOOT ARCH:0x%04x\n", FADT->ArmBootArch));
DEBUG((DEBUG_INFO, "FADT MINOR VERSION:0x%02x\n", FADT->MinorVersion));
DEBUG((DEBUG_INFO, "X FIRMWARE CTRL:0x%p", FADT->XFirmwareCtrl));
DEBUG((DEBUG_INFO, "X DSDT:0x%p\n", FADT->XDsdt));
DumpGenericAddress("X PM1a EVT BLK", &FADT->XPm1aEvtBlk);
DumpGenericAddress("X PM1b EVT BLK", &FADT->XPm1bEvtBlk);
DumpGenericAddress("X PM1a CNT BLK", &FADT->XPm1aCntBlk);
DumpGenericAddress("X PM1b CNT BLK", &FADT->XPm1bCntBlk);
DumpGenericAddress("X PM TMR BLK", &FADT->XPmTmrBlk);
DumpGenericAddress("GPE0 BLK", &FADT->XGpe0Blk);
DumpGenericAddress("GPE1 BLK", &FADT->XGpe1Blk);
DumpGenericAddress("SLEEP CONTROL", &FADT->SleepControlReg);
DumpGenericAddress("SLEEP STATUS", &FADT->SleepStatusReg);
DEBUG((DEBUG_INFO, "###########End Dump FADT###########\n\n"));
}
log分析
#########Start Dump FADT##########
Signature:FACP -->签名为"FACP"
Length:0x10C -->长度就是FADT结构体的长度
Revision:0x05 -->下面是一些版本信息
Checksum:0x03
OemID:BOCHS
OemTableId:0x2020202043505842 -
OemRevision:0x1
CreatorId:0x43505842
CreatorRevision:0x1
FACS:0x0 -->FACS地址为0,就是不存在
Dsdt:0x405C7518 -->DSDT的地址
PreferredPmProfile: Unspecified -->没有指定电源管理的Profile
-->由于这里用的ARM AARCH64的结构,以下硬件在AARCH64架构上都是不存在的的, 所以下面这些硬件信息AARCH64都不需要
-->因此下面这些硬件信息都是0,实际上就是不存在。对于X86/X64架构是需要的。
SCI INT:0x0000
SMI CMD:0x00000000
ACPI ENABLE:0x00
ACPI DISABLE:0x00
S4BIOS REQ:0x00
PSTATE CNT:0x00
PM1a EVT BLK:0x00000000
PM1b EVT BLK:0x00000000
PM1a CNT BLK:0x00000000
PM1b CNT BLK:0x00000000
PM TMR BLK:0x00000000
GPE0 BLK:0x00000000
GPE1 BLK:0x00000000
PM1 EVT LEN:0x00
PM1 CNT LEN:0x00
PM2 CNT LEN:0x00
PM TMR LEN:0x00
GPE0 BLK LEN:0x00
GPE1 BLK LEN:0x00
GPE1 BASE:0x00
CST CNT:0x00
P LVL2 LAT:0x0000
P LVL3 LAT:0x0000
Flush Size:0x0000
FLUSH STRIDE:0x0000
DUTY OFFSET:0x00
DUTY WIDTH:0x00
DAY ALARM:0x00
MON ALARM:0x00
CENTURY:0x00
IAPC_BOOT_ARCH:0x0000
Flags:0x00100000
Reset Reg: AddressSpaceId:0x00, RegisterBitWidth:0x00, RegisterBitOffset:0x00, AccessSize:0x00, Address:0x00000000000
Reset Value:0x00
ARM BOOT ARCH:0x0003 --> ARM BOOT架构
FADT MINOR VERSION:0x01 --> 次版本号
X FIRMWARE CTRL:0x0
X DSDT:0x405C7518 --> XDSDT的地址
X PM1a EVT BLK: AddressSpaceId:0x00, RegisterBitWidth:0x00, RegisterBitOffset:0x00, AccessSize:0x00, Address:0x00000000000
X PM1b EVT BLK: AddressSpaceId:0x00, RegisterBitWidth:0x00, RegisterBitOffset:0x00, AccessSize:0x00, Address:0x00000000000
X PM1a CNT BLK: AddressSpaceId:0x00, RegisterBitWidth:0x00, RegisterBitOffset:0x00, AccessSize:0x00, Address:0x00000000000
X PM1b CNT BLK: AddressSpaceId:0x00, RegisterBitWidth:0x00, RegisterBitOffset:0x00, AccessSize:0x00, Address:0x00000000000
X PM TMR BLK: AddressSpaceId:0x00, RegisterBitWidth:0x00, RegisterBitOffset:0x00, AccessSize:0x00, Address:0x00000000000
GPE0 BLK: AddressSpaceId:0x00, RegisterBitWidth:0x00, RegisterBitOffset:0x00, AccessSize:0x00, Address:0x00000000000
GPE1 BLK: AddressSpaceId:0x00, RegisterBitWidth:0x00, RegisterBitOffset:0x00, AccessSize:0x00, Address:0x00000000000
SLEEP CONTROL: AddressSpaceId:0x00, RegisterBitWidth:0x00, RegisterBitOffset:0x00, AccessSize:0x00, Address:0x00000000000
SLEEP STATUS: AddressSpaceId:0x00, RegisterBitWidth:0x00, RegisterBitOffset:0x00, AccessSize:0x00, Address:0x00000000000
###########End Dump FADT###########
DSDT
上面已经根据FADT找到了DSDT的地址。然后我们可以把解析出来。之前讲过DSDT就只有一个头,然后下面放的就是一个个AML定义块。而AML定义块是以裸数据存在的,所以要dump出这个AML的数据,然后用工具解析。打印代码如下。
TestPkg/TestDxeAcpi.c
DSDT = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)(FADT->Dsdt);
DEBUG((DEBUG_INFO, "\n#########Start Dump DSDT##########\n"));
UINT32 *temp = (UINT32 *)DSDT;
for (UINTN k = 0; k < DSDT->Length; k += 4) {
DEBUG((DEBUG_INFO, "0x%08x\n", *temp++));
}
DEBUG((DEBUG_INFO, "#########End Dump DSDT##########\n\n"));
break;
在EDK2上我也没找到现成的可以把AML解析成字符串的代码,所以这边我将DSDT和AML定义块的数据以16进制打印出来,然后手动保存到一个文件dsdt.txt里,再利用脚本在ubuntu上调用iasl来解析DSDT。
0x54445344 --> DSDT的裸数据如下
0x0000144C
0x4F429F02
0x20534843
...
0x00444955
解析脚本
import os
import sys
f = open('dsdt.txt')
out = open('dsdt.dat', 'wb+')
for line in f:
line = line.replace('\n', '')
val = int(line, 16)
print(val)
out.write(val.to_bytes(4, byteorder='little'))
f.close()
out.close()
os.system('iasl -d dsdt.dat')
最后解析出来的dsdt.dsl如下。
/*
* Intel ACPI Component Architecture
* AML/ASL+ Disassembler version 20200925 (64-bit version)
* Copyright (c) 2000 - 2020 Intel Corporation
*
* Disassembling to symbolic ASL+ operators
*
* Disassembly of dsdt.dat, Wed May 25 09:14:56 2022
*/
--------------->DSDT的Header
* Original Table Header:
* Signature "DSDT"
* Length 0x0000144C (5196)
* Revision 0x02
* Checksum 0x9F
* OEM ID "BOCHS "
* OEM Table ID "BXPC "
* OEM Revision 0x00000001 (1)
* Compiler ID "BXPC"
* Compiler Version 0x00000001 (1)
*
DefinitionBlock ("", "DSDT", 2, "BOCHS ", "BXPC ", 0x00000001)
{
Scope (\_SB)
{
// CPU Device的信息
Device (C000)
{
Name (_HID, "ACPI0007" /* Processor Device */) // _HID: Hardware ID
Name (_UID, Zero) // _UID: Unique ID
}
//串口的信息
Device (COM0)
{
Name (_HID, "ARMH0011") ARMH0011代表ARM PL011 IP // _HID: Hardware ID
Name (_UID, Zero) // _UID: Unique ID
Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings
{
Memory32Fixed (ReadWrite,
0x09000000, // 串口寄存器的起始地址
0x00001000, // 串口寄存器的长度
)
//中断相关信息。
Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, )
{
0x00000021,
}
})
}
// FW CFG的信息
Device (FWCF)
{
Name (_HID, "QEMU0002") QEMU0002代表QMEU FW CFG设备 // _HID: Hardware ID
Name (_STA, 0x0B) // _STA: Status
Name (_CCA, One) // _CCA: Cache Coherency Attribute
Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings
{
Memory32Fixed (ReadWrite,
0x09020000, // FW CFG 的起始地址
0x00000018, // FW CFG 的长度
)
})
}
// 下面一堆VIRT IO的信息,这里只留下头和尾巴, 都是一样的重复的东西
Device (VR00)
{
Name (_HID, "LNRO0005") // _HID: Hardware ID
Name (_UID, Zero) // _UID: Unique ID
Name (_CCA, One) // _CCA: Cache Coherency Attribute
Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings
{
Memory32Fixed (ReadWrite,
0x0A000000, // VIRT IO0 的起始地址
0x00000200, // VIRT IO0 的长度
)
Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, )
{
0x00000030,
}
})
}
...
Device (VR31)
{
...
}
//PCI0的信息 不是很了解QEMU上PCI的用法,这里就不解释了
Device (PCI0)
{
Name (_HID, "PNP0A08" /* PCI Express Bus */) // _HID: Hardware ID
Name (_CID, "PNP0A03" /* PCI Bus */) // _CID: Compatible ID
Name (_SEG, Zero) // _SEG: PCI Segment
Name (_BBN, Zero) // _BBN: BIOS Bus Number
Name (_UID, Zero) // _UID: Unique ID
Name (_STR, Unicode ("PCIe 0 Device")) // _STR: Description String
Name (_CCA, One) // _CCA: Cache Coherency Attribute
Name (_PRT, Package (0x80) // _PRT: PCI Routing Table
{
Package (0x04)
{
0xFFFF,
Zero,
GSI0,
Zero
},
...
Package (0x04)
{
0x001FFFFF,
One,
GSI0,
Zero
},
Package (0x04)
{
0x001FFFFF,
0x02,
GSI1,
Zero
},
Package (0x04)
{
0x001FFFFF,
0x03,
GSI2,
Zero
}
})
...
}
// 描述Generic Evenet Device的信息
Device (\_SB.GED)
{
Name (_HID, "ACPI0013" /* Generic Event Device */) // _HID: Hardware ID
Name (_UID, "GED") // _UID: Unique ID
Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings
{
//中断信息
Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
{
0x00000029,
}
})
//寄存器地址和长度
OperationRegion (EREG, SystemMemory, 0x09080000, 0x04)
//32位寄存器定义
Field (EREG, DWordAcc, NoLock, WriteAsZeros)
{
ESEL, 32
}
//操作的方法
Method (_EVT, 1, Serialized) // _EVT: Event
{
Local0 = ESEL /* \_SB_.GED_.ESEL */
If (((Local0 & 0x02) == 0x02))
{
Notify (PWRB, 0x80) // Status Change
}
}
}
//按键的信息
Device (PWRB)
{
Name (_HID, "PNP0C0C" /* Power Button Device */) // _HID: Hardware ID
Name (_UID, Zero) // _UID: Unique ID
}
}
}
MADT
MADT用于描述中断硬件信息。
MdePkg/Include/IndustryStandard/Acpi63.h
typedef struct {
EFI_ACPI_DESCRIPTION_HEADER Header; --> ACPI Header
UINT32 LocalApicAddress; --> 处理器本地中断处理器所在的地址
UINT32 Flags; --> 标志位。
} EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER;
在MADT(EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER)的后面紧跟着的是一系列的中断控制器结构体,它的大小不是恒定的,需要动态解析出来。下面的代码可以解析MADT的信息。
switch (Entry->Signature) {
...
case EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE:
DumpMADT((EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *)Entry);
break;
...
DumpMADT首先解析出Header中的信息,然后遍历中断控制器结构体,分别解析对应的中断硬件信息。AARCH64上只有GIC,GICD和GIC MSI。所以只实现dump了这三种中断控制器。
TestPkg/TestDxeAcpi.c
VOID
DumpMADT(
IN EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *MADT
)
{
UINT8 *TempPtr;
UINT8 Type, Length;
UINTN Len = 0;
DEBUG((DEBUG_INFO, "\n###########Start Dump MADT###########\n"));
DumpACPIHeader(&MADT->Header); //解析ACPI 头
DEBUG((DEBUG_INFO, "Local APIC Address:0x%08x\n", MADT->LocalApicAddress));
DEBUG((DEBUG_INFO, "Flags:0x%08x\n", MADT->Flags));
TempPtr = (UINT8 *)(MADT + 1);
Len = MADT->Header.Length - sizeof(EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER);
//遍历中断控制器结构。
while (Len > 0) {
Type = *TempPtr; //每一个中断控制器结构第一个字节是类型
Length = *(TempPtr + 1); //第二个字节是长度
switch(Type)
{
//根据不同的类型解析相对应的中断结构体信息。
case EFI_ACPI_6_3_PROCESSOR_LOCAL_APIC:
case EFI_ACPI_6_3_IO_APIC:
case EFI_ACPI_6_3_INTERRUPT_SOURCE_OVERRIDE:
case EFI_ACPI_6_3_NON_MASKABLE_INTERRUPT_SOURCE:
case EFI_ACPI_6_3_LOCAL_APIC_NMI:
case EFI_ACPI_6_3_LOCAL_APIC_ADDRESS_OVERRIDE:
case EFI_ACPI_6_3_IO_SAPIC:
case EFI_ACPI_6_3_LOCAL_SAPIC:
case EFI_ACPI_6_3_PROCESSOR_LOCAL_X2APIC:
case EFI_ACPI_6_3_LOCAL_X2APIC_NMI:
case EFI_ACPI_6_3_GICR:
case EFI_ACPI_6_3_GIC_ITS:
DEBUG((DEBUG_INFO, "Parser not implemented for Type:0x%02x\n", Type));
break;
case EFI_ACPI_6_3_GIC:
DumpMadtGIC((EFI_ACPI_6_3_GIC_STRUCTURE *)TempPtr);
break;
case EFI_ACPI_6_3_GICD:
DumpMadtGICD((EFI_ACPI_6_3_GIC_DISTRIBUTOR_STRUCTURE *)TempPtr);
break;
case EFI_ACPI_6_3_GIC_MSI_FRAME:
DumpMadtGICMSI((EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE *)TempPtr);
break;
default:
DEBUG((DEBUG_INFO, "Unknown Type:0x%02x\n", Type));
break;
}
Len = Len - Length;
TempPtr = TempPtr + Length;
}
DEBUG((DEBUG_INFO, "###########End Dump MADT###########\n\n"));
}
VOID
DumpMadtGIC(EFI_ACPI_6_3_GIC_STRUCTURE *GIC)
{
DEBUG((DEBUG_INFO, "GIC Information:\n"));
DEBUG((DEBUG_INFO, " CPU Interface Number:%d\n", GIC->CPUInterfaceNumber));
DEBUG((DEBUG_INFO, " Acpi Processor Uid:0x%08x\n", GIC->AcpiProcessorUid));
DEBUG((DEBUG_INFO, " Flags:0x%08x\n", GIC->Flags));
DEBUG((DEBUG_INFO, " Parking Protoco lVersion:0x%08x\n", GIC->ParkingProtocolVersion));
DEBUG((DEBUG_INFO, " Performance Interrupt Gsiv:0x%08x\n", GIC->PerformanceInterruptGsiv));
DEBUG((DEBUG_INFO, " Parked Address:0x%p\n", GIC->ParkedAddress));
DEBUG((DEBUG_INFO, " Physical Base Address:0x%p\n", GIC->PhysicalBaseAddress));
DEBUG((DEBUG_INFO, " GICV:0x%11p\n", GIC->GICV));
DEBUG((DEBUG_INFO, " GICH:0x%11p\n", GIC->GICH));
DEBUG((DEBUG_INFO, " VGIC Maintenance Interrupt:0x%x\n", GIC->VGICMaintenanceInterrupt));
DEBUG((DEBUG_INFO, " GICR Base Address:0x%11p\n", GIC->GICRBaseAddress));
DEBUG((DEBUG_INFO, " MPIDR:%11p\n", GIC->MPIDR));
DEBUG((DEBUG_INFO, " Processor Power Efficiency Class:0x%02x\n", GIC->ProcessorPowerEfficiencyClass));
DEBUG((DEBUG_INFO, " Spe Overflow Interrupt:0x%04x\n", GIC->SpeOverflowInterrupt));
}
VOID
DumpMadtGICD(
IN EFI_ACPI_6_3_GIC_DISTRIBUTOR_STRUCTURE *GICD
)
{
DEBUG((DEBUG_INFO, "GIC Distributor Information:\n"));
DEBUG((DEBUG_INFO, " Gic Id:0x%08x\n", GICD->GicId ));
DEBUG((DEBUG_INFO, " Physical Base Address:0x%11p\n", GICD->PhysicalBaseAddress));
DEBUG((DEBUG_INFO, " System Vector Base:0x%x\n", GICD->SystemVectorBase));
DEBUG((DEBUG_INFO, " Gic Version:0x%02x\n", GICD->GicVersion));
}
VOID
DumpMadtGICMSI(EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE *GICMSI)
{
DEBUG((DEBUG_INFO, "GIC MSI Information:\n"));
DEBUG((DEBUG_INFO, " Gic Msi Frame Id:0x%08x\n", GICMSI->GicMsiFrameId));
DEBUG((DEBUG_INFO, " Physical Base Address:0x%11p\n", GICMSI->PhysicalBaseAddress));
DEBUG((DEBUG_INFO, " Flags:0x%08x\n", GICMSI->Flags));
DEBUG((DEBUG_INFO, " SPI Count:0x%04x\n", GICMSI->SPICount));
DEBUG((DEBUG_INFO, " SPI Base:0x%04x\n", GICMSI->SPIBase));
}
最终dump出来的log如下
###########Start Dump MADT###########
Signature:APIC -->头部信息
Length:0xA8
Revision:0x03
Checksum:0x50
OemID:BOCHS
OemTableId:0x2020202043505842
OemRevision:0x1
CreatorId:0x43505842
CreatorRevision:0x1
Local APIC Address:0x00000000 --> 不存在Local APIC
Flags:0x00000000
GIC Distributor Information:
Gic Id:0x00000000 --> GIC Distrubtor ID为0
Physical Base Address:0x00008000000 --> GIC Distributor的物理地址
System Vector Base:0x0 --> 必须为0
Gic Version:0x02 --> GIC V2
GIC Information:
CPU Interface Number:0 --> CPU接口不存在,为0
Acpi Processor Uid:0x00000000 --> 不存在
Flags:0x00000001 --> GIC可以使能
Parking Protoco lVersion:0x00000000 --> ARM-Processor Parking Protocol 版本号
Performance Interrupt Gsiv:0x00000017 --> 性能监控的GIV中断号
Parked Address:0x0
Physical Base Address:0x8010000 --> GIC的物理地址
GICV:0x00008040000 --> GIC virtual CPU interface寄存器地址
GICH:0x00008030000 --> GIC virtual interface control block寄存器地址
VGIC Maintenance Interrupt:0x0 --> 不存在
GICR Base Address:0x00000000000 --> 这里是GIC V2,所以GICR不存在
MPIDR:00000000000
Processor Power Efficiency Class:0x0D --> 功耗级别
Spe Overflow Interrupt:0x0000 --> 为0, 不支持Spe Overflow
GIC MSI Information:
Gic Msi Frame Id:0x00000000 --> ID为0
Physical Base Address:0x00008020000 --> GIC MSI的物理地址
Flags:0x00000001 --> The SPI Count and Base values override the values specified in the MSI_TYPER register in the associated GIC MSI frame.
SPI Count:0x0040 --> SPI个数
SPI Base:0x0050
###########End Dump MADT###########
SPCR
SPCR(Serial Port Console Redirection)提供了有关串口或非遗留UART接口的配置和使用信息.。在BIOS使用此串口作为控制台输入/输出的系统中,应该使用此表来传递关于这些设置的信息,确保BIOS控制台输出和Windows EMS(Emergency Management Serivces)输出之间的平滑转换。
MdePkg/Include/IndustryStandard/SerialPortConsoleRedirection.h
typedef struct {
EFI_ACPI_DESCRIPTION_HEADER Header;
UINT8 InterfaceType;
UINT8 Reserved1[3];
EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE BaseAddress;
UINT8 InterruptType;
UINT8 Irq;
UINT32 GlobalSystemInterrupt;
UINT8 BaudRate;
UINT8 Parity;
UINT8 StopBits;
UINT8 FlowControl;
UINT8 TerminalType;
UINT8 Reserved2;
UINT16 PciDeviceId;
UINT16 PciVendorId;
UINT8 PciBusNumber;
UINT8 PciDeviceNumber;
UINT8 PciFunctionNumber;
UINT32 PciFlags;
UINT8 PciSegment;
UINT32 Reserved3;
} EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE;
Dump SPCR的代码如下
switch (Entry->Signature) {
...
case EFI_ACPI_6_3_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_SIGNATURE:
DumpSPCR((EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE *)Entry);
break;
...
}
VOID
DumpSPCR(
IN EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE *SPCR
)
{
char *TempStr;
DEBUG((DEBUG_INFO, "\n#########Start Dump SPCR##########\n"));
DumpACPIHeader(&SPCR->Header);
switch(SPCR->InterfaceType) {
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERFACE_TYPE_16550:
TempStr = "16550";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERFACE_TYPE_16450:
TempStr = "16450";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERFACE_TYPE_ARM_PL011_UART:
TempStr = "ARM PL011 UART";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERFACE_TYPE_NVIDIA_16550_UART:
TempStr = "NVIDIA 16550 UART";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERFACE_TYPE_ARM_SBSA_GENERIC_UART_2X:
TempStr = "ARM SBSA Generic UART 2.x";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERFACE_TYPE_ARM_SBSA_GENERIC_UART:
TempStr = "ARM SBSA Generic UART";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERFACE_TYPE_DCC:
TempStr = "ARM DCC";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERFACE_TYPE_BCM2835_UART:
TempStr = "BCM2835";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERFACE_TYPE_16550_WITH_GAS:
TempStr = "16550-compatible";
break;
default:
TempStr = "Reserved";
break;
}
DEBUG((DEBUG_INFO, "Interface:%a\n", TempStr));
DumpGenericAddress("Base Address", (EFI_ACPI_6_3_GENERIC_ADDRESS_STRUCTURE *)&SPCR->BaseAddress);
switch (SPCR->InterruptType)
{
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERRUPT_TYPE_8259:
TempStr = "8259";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERRUPT_TYPE_APIC:
TempStr = "APIC";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERRUPT_TYPE_SAPIC:
TempStr = "SAPIC";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_INTERRUPT_TYPE_GIC:
TempStr = "GIC";
break;
default:
TempStr = "Not Supported";
break;
}
DEBUG((DEBUG_INFO, "Interrupt Type: %a\n", TempStr));
DEBUG((DEBUG_INFO, "IRQ:%d\n", SPCR->Irq));
DEBUG((DEBUG_INFO, "Global System Interrupt:0x%x\n", SPCR->GlobalSystemInterrupt));
switch (SPCR->BaudRate)
{
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_BAUD_RATE_9600:
TempStr = "9600";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_BAUD_RATE_19200:
TempStr = "19200";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_BAUD_RATE_57600:
TempStr = "57600";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_BAUD_RATE_115200:
TempStr = "115200";
break;
default:
TempStr = "Reserved";
break;
}
DEBUG((DEBUG_INFO, "Baud Rate:%a\n", TempStr));
switch (SPCR->Parity)
{
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_PARITY_NO_PARITY:
DEBUG((DEBUG_INFO, "Parity: No Parity\n"));
break;
default:
DEBUG((DEBUG_INFO, "Parity: Reserved\n"));
break;
}
switch (SPCR->StopBits)
{
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_STOP_BITS_1:
DEBUG((DEBUG_INFO, "Stop Bits:%d\n", SPCR->StopBits));
break;
default:
DEBUG((DEBUG_INFO, "Stop Bits: No Stop Bits\n"));
break;
}
DEBUG((DEBUG_INFO, "Flow Control: \n"));
if (SPCR->FlowControl & EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_FLOW_CONTROL_DCD) {
DEBUG((DEBUG_INFO, " Support DCD\n"));
}
if (SPCR->FlowControl & EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_FLOW_CONTROL_RTS_CTS) {
DEBUG((DEBUG_INFO, " Support RTS/CTS\n"));
}
if (SPCR->FlowControl & EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_FLOW_CONTROL_XON_XOFF) {
DEBUG((DEBUG_INFO, " Support XON/XOFF\n"));
}
switch (SPCR->TerminalType)
{
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_TERMINAL_TYPE_VT100:
TempStr = "VT100";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_TERMINAL_TYPE_VT100_PLUS:
TempStr = "VT100+";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_TERMINAL_TYPE_VT_UTF8:
TempStr = "VI-UTF8";
break;
case EFI_ACPI_SERIAL_PORT_CONSOLE_REDIRECTION_TABLE_TERMINAL_TYPE_ANSI:
TempStr = "ANSI";
break;
default:
TempStr = "Reserved";
break;
}
DEBUG((DEBUG_INFO, "Termial Type:%a\n", TempStr));
DEBUG((DEBUG_INFO, "PCI Device ID:0x%04x\n", SPCR->PciDeviceId));
DEBUG((DEBUG_INFO, "PCI Vendor ID:0x%04x\n", SPCR->PciVendorId));
DEBUG((DEBUG_INFO, "PCI Bus Number:0x%02x\n", SPCR->PciBusNumber));
DEBUG((DEBUG_INFO, "PCI Device Number:0x%02x\n", SPCR->PciDeviceNumber));
DEBUG((DEBUG_INFO, "PCI Function Number:0x%02x\n", SPCR->PciFunctionNumber));
DEBUG((DEBUG_INFO, "PCI Flags:0x%08x\n", SPCR->PciFlags));
DEBUG((DEBUG_INFO, "PCI Segment:0x%02x\n", SPCR->PciSegment));
DEBUG((DEBUG_INFO, "###########End Dump SPCR###########\n\n"));
}
dump出的log
#########Start Dump SPCR##########
Signature:SPCR
Length:0x50
Revision:0x02
Checksum:0xCB
OemID:BOCHS
OemTableId:0x2020202043505842
OemRevision:0x1
CreatorId:0x43505842
CreatorRevision:0x1
Interface:ARM PL011 UART --> 类型是ARM PL011 IP
--> 串口的物理地址在0x00009000000。
Base Address: AddressSpaceId:0x00, RegisterBitWidth:0x08, RegisterBitOffset:0x00, AccessSize:0x01, Address:0x00009000000
Interrupt Type: GIC --> 中断类型是ARM GIC
IRQ:0
Global System Interrupt:0x21 --> 中断号是0x21
Baud Rate:9600 --> 波特率是9600
Parity: No Parity --> 没有奇偶校验
Stop Bits:1 --> 停止位是1位
Flow Control: --> 支持RTS/CTS
Support RTS/CTS
Termial Type:VT100 --> Console类型是VT100
PCI Device ID:0xFFFF --> 不是PCI 设备,下面字段全是无效
PCI Vendor ID:0xFFFF
PCI Bus Number:0x00
PCI Device Number:0x00
PCI Function Number:0x00
PCI Flags:0x00000000
PCI Segment:0x00
###########End Dump SPCR###########