Root ACPI table contains non canonical addresses.
Posted: Sat Oct 05, 2024 4:14 am
Hello everyone!
I encountered a problem while trying to parse ACPI tables from the root ACPI table in my OS.
Following the ACPI 6.5 spec (https://uefi.org/sites/default/files/re ... _Aug29.pdf) and the tutorial on OS dev wiki I came up with the following code for the definitions of ACPI Root System Description Pointer, ACPI System Description Table Header and ACPI Extended System Description Table:
Since I am using an UEFI bootloader, I did the following to get the address of the XSDT.
Running the function above crashed the code with a page fault exception.
Using a debugger for a further inspection, I noticed that entries in the XSDT contain non canonical address. What I mean by that is if I place a breakpoint at the start of the last for loop and inspect the contents of the entries array in XSDT I get the following:
If I calculated the number of entries correctly, there is 6 of them. Why are there entries on addresses between canonical x86_64 address range? Namely entry number 2, 3, 4, 5 and 6?
I thought I parsed the table incorrectly or that I didn't send proper information to the OS when exiting a boot loader. Since I wrote my own boot loader (UEFI) I went back to it, commented out jumping to kernel and just printed out addresses in XSDT table. I get the same exact values as I did from using a debugger (given above).
Does anyone know what I'm doing wrong?
My host Linux kernel is: 6.11.1-arch1-1
My QEMU version is: 9.1.0.
I downloaded an OVMF file from EDK II nighlty build repository (I'm using a version from yesterday, October 4th, 2024) Link to nightly build repository: https://retrage.github.io/edk2-nightly/
I run QEMU with the following command:
I encountered a problem while trying to parse ACPI tables from the root ACPI table in my OS.
Following the ACPI 6.5 spec (https://uefi.org/sites/default/files/re ... _Aug29.pdf) and the tutorial on OS dev wiki I came up with the following code for the definitions of ACPI Root System Description Pointer, ACPI System Description Table Header and ACPI Extended System Description Table:
Code: Select all
//ACPI.h file
struct __attribute__((packed)) ACPI_RSDP {
int8_t m_signature[8];
uint8_t m_checksum;
int8_t m_OEMID[6];
uint8_t m_revision;
uint32_t m_rsdt_address;
uint32_t m_length;
uint64_t m_xsdt_address;
uint8_t m_extended_checksum;
int8_t m_reserved[3];
};
struct __attribute__((packed)) ACPI_SDTH {
uint8_t m_signature[4];
uint32_t m_length;
uint8_t m_revision;
uint8_t m_checksum;
int8_t m_OEMID[6];
uint64_t m_OEM_Table_ID;
uint32_t m_OEM_revision;
uint32_t m_creator_id;
uint32_t m_creator_revision;
};
struct __attribute__((packed)) ACPI_XSDT {
ACPI_SDTH m_header;
uint64_t* m_entries;
};
inline ACPI_RSDP* g_rsdp = nullptr;
inline ACPI_XSDT* g_xsdt = nullptr;
Since I am using an UEFI bootloader, I did the following to get the address of the XSDT.
Code: Select all
// ACPI.cpp file
// Global variables for RSDP and XSDT are defined in ACPI.h file
bool initialize_ACPI(EFI_SYSTEM_TABLE* system_table) noexcept {
for (uint64_t i = 0; i < system_table->NumberOfTableEntries; i++) {
const auto& table = system_table->ConfigurationTable[i];
if (table.VendorGuid == ACPI_2_RSDP_GUID) {
g_rsdp = static_cast<ACPI_RSDP*>(table.VendorTable);
g_xsdt = reinterpret_cast<ACPI_XSDT*>(g_rsdp->m_xsdt_address);
break;
}
}
if (!g_acpi_rsdt || !g_acpi_xsdt) {
return false;
}
size_t number_of_xsdt_entries = (g_xsdt->m_header.m_length - sizeof(ACPI_SDTH)) / 8;
for (size_t i = 0; i < number_of_xsdt_entries; i++) {
ACPI_SDTH* entry = reinterpret_cast<ACPI_SDTH*>(g_xsdt->m_entries[i]);
// Check if entry is 'MADT' or 'FADT' or something else...
// The rest of the code is omitted for brevity....
}
}
Using a debugger for a further inspection, I noticed that entries in the XSDT contain non canonical address. What I mean by that is if I place a breakpoint at the start of the last for loop and inspect the contents of the entries array in XSDT I get the following:
Code: Select all
(gdb) p *g_rsdp
$1 = {m_signature = "RSD PTR ", m_checksum = 54 '6', m_OEMID = "BOCHS ", m_revision = 2 '\002', m_rsdt_address = 532140148, m_length = 36, m_xsdt_address = 532140264, m_extended_checksum = 78 'N', m_reserved = "\000\000"}
(gdb) p *g_xsdt
$2 = {m_header = {m_signature = "XSDT", m_length = 84, m_revision = 1 '\001', m_checksum = 35 '#', m_OEMID = "BOCHS ", m_OEM_Table_ID = 2314885531408816194, m_OEM_revision = 1, m_creator_id = 538976288, m_creator_revision = 16777235}, m_entries = 0x1fb79000}
(gdb) p (g_xsdt->m_header - sizeof(ACPI_SDTH)) / 8
$3 = 6
(gdb) x /8xg 0x1fb79000
0x1fb79000: 0x000000f450434146 0x205348434f421f03
0x1fb79010: 0x2020202043505842 0x4350584200000001
0x1fb79020: 0x1fbdd00000000001 0x000900011fb7a000
0x1fb79030: 0x00000302000000b2 0x0000000000000600
I thought I parsed the table incorrectly or that I didn't send proper information to the OS when exiting a boot loader. Since I wrote my own boot loader (UEFI) I went back to it, commented out jumping to kernel and just printed out addresses in XSDT table. I get the same exact values as I did from using a debugger (given above).
Does anyone know what I'm doing wrong?
My host Linux kernel is: 6.11.1-arch1-1
My QEMU version is: 9.1.0.
I downloaded an OVMF file from EDK II nighlty build repository (I'm using a version from yesterday, October 4th, 2024) Link to nightly build repository: https://retrage.github.io/edk2-nightly/
I run QEMU with the following command:
Code: Select all
qemu-system-x86_64 -s -S --machine q35 -m 512 -cpu Skylake-Client-v3 -bios path_to_OVMF.fd_file -drive file=path_to_UEFI_file,if=ide -d cpu_reset