#pragma once #include "util/number.h" #include "memory/pointer.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; } // Values match the folded signature stored in sdt_header_t::signature. DSDT and // FACS are absent on purpose: they are reached through the FADT, not listed in // the RSDT/XSDT. 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"), }; // A folded signature, unfolded back into a null-terminated 4-character string. 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 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); callback(paddr_t{table_physical}); } } template void find_table(signature sig, Fn&& callback) { for_each_table([&](paddr_t table) { if (table.access()->signature == static_cast(sig)) { callback(table); } }); } private: 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