#pragma once #include "memory/pointer.h" #include "util/number.h" namespace acpi { constexpr uint32_t fold_signature(const char (&text)[5]) { return static_cast(static_cast(text[0])) | static_cast(static_cast(text[1])) << 8 | static_cast(static_cast(text[2])) << 16 | static_cast(static_cast(text[3])) << 24; } enum class signature : uint32_t { fadt = fold_signature("FACP"), madt = fold_signature("APIC"), hpet = fold_signature("HPET"), mcfg = fold_signature("MCFG"), srat = fold_signature("SRAT"), ssdt = fold_signature("SSDT"), }; struct signature_text_t { char text[5]; }; inline signature_text_t unfold_signature(uint32_t folded) { signature_text_t result{}; memcpy(result.text, &folded, 4); result.text[4] = '\0'; return result; } struct sdt_header_t { uint32_t signature; uint32_t length; uint8_t revision; uint8_t checksum; char oem_id[6]; char oem_table_id[8]; uint32_t oem_revision; uint32_t creator_id; uint32_t creator_revision; } __attribute__((packed)); static_assert(sizeof(sdt_header_t) == 36); struct rsdp_t { char signature[8]; uint8_t checksum; char oem_id[6]; uint8_t revision; uint32_t rsdt_address; uint32_t length; uint64_t xsdt_address; uint8_t extended_checksum; uint8_t reserved[3]; } __attribute__((packed)); static_assert(sizeof(rsdp_t) == 36); struct madt_t; struct fadt_t; struct Rsdp { explicit Rsdp(paddr_t rsdp_physical) : physical(rsdp_physical) {} template void for_each_table(Fn&& callback) { auto root = root_table().access(); auto entry_size = uses_xsdt() ? 8 : 4; auto count = (root->length - sizeof(sdt_header_t)) / entry_size; auto entries = reinterpret_cast(root) + sizeof(sdt_header_t); for (auto index = 0u; index < count; index++) { uint64_t table_physical = 0; memcpy(&table_physical, entries + index * entry_size, entry_size); dispatch(paddr_t{table_physical}.access(), callback); } } private: template static void dispatch(const sdt_header_t* header, Fn&& fn) { #define ACPI_DISPATCH(signature_enum, TableType) \ case signature::signature_enum: \ if constexpr (requires { fn(static_cast(nullptr)); }) { \ fn(reinterpret_cast(header)); \ return; \ } \ break; switch (static_cast(header->signature)) { ACPI_DISPATCH(madt, madt_t) ACPI_DISPATCH(fadt, fadt_t) default: break; } #undef ACPI_DISPATCH if constexpr (requires { fn(static_cast(nullptr)); }) { fn(header); } } bool uses_xsdt() { return physical.access()->revision >= 2; } paddr_t root_table() { auto rsdp = physical.access(); return uses_xsdt() ? paddr_t{rsdp->xsdt_address} : paddr_t{rsdp->rsdt_address}; } paddr_t physical; }; } // namespace acpi