#pragma once #include "init/acpi.h" #include "memory/pointer.h" #include "util/number.h" namespace acpi { enum class madt_entry_type : uint8_t { local_apic = 0, io_apic = 1, interrupt_source_override = 2, nmi_source = 3, local_apic_nmi = 4, local_apic_address_override = 5, }; inline constexpr const char* get_madt_entry_type_name(madt_entry_type type) { switch (type) { case madt_entry_type::local_apic: return "local_apic"; case madt_entry_type::io_apic: return "io_apic"; case madt_entry_type::interrupt_source_override: return "interrupt_source_override"; case madt_entry_type::nmi_source: return "nmi_source"; case madt_entry_type::local_apic_nmi: return "local_apic_nmi"; case madt_entry_type::local_apic_address_override: return "local_apic_address_override"; } return "unknown"; } struct madt_flags_t { uint32_t dual_8259_present : 1; uint32_t reserved : 31; }; static_assert(sizeof(madt_flags_t) == 4); struct local_apic_flags_t { uint32_t enabled : 1; uint32_t online_capable : 1; uint32_t reserved : 30; }; static_assert(sizeof(local_apic_flags_t) == 4); enum class inti_polarity : uint16_t { bus_default = 0, active_high = 1, active_low = 3, }; enum class inti_trigger_mode : uint16_t { bus_default = 0, edge = 1, level = 3, }; struct inti_flags_t { inti_polarity polarity : 2; inti_trigger_mode trigger_mode : 2; uint16_t reserved : 12; }; static_assert(sizeof(inti_flags_t) == 2); struct madt_t { sdt_header_t header; uint32_t local_apic_address; madt_flags_t flags; uint8_t entries[0]; } __attribute__((packed)); static_assert(sizeof(madt_t) == 44); struct madt_entry_t { madt_entry_type type; uint8_t length; } __attribute__((packed)); static_assert(sizeof(madt_entry_t) == 2); struct madt_local_apic_t { madt_entry_type type; uint8_t length; uint8_t acpi_processor_uid; uint8_t apic_id; local_apic_flags_t flags; } __attribute__((packed)); static_assert(sizeof(madt_local_apic_t) == 8); struct madt_io_apic_t { madt_entry_type type; uint8_t length; uint8_t io_apic_id; uint8_t reserved; uint32_t io_apic_address; uint32_t global_system_interrupt_base; } __attribute__((packed)); static_assert(sizeof(madt_io_apic_t) == 12); struct madt_interrupt_source_override_t { madt_entry_type type; uint8_t length; uint8_t bus; // always 0 (ISA) uint8_t source; // ISA IRQ number uint32_t global_system_interrupt; inti_flags_t flags; } __attribute__((packed)); static_assert(sizeof(madt_interrupt_source_override_t) == 10); struct madt_nmi_source_t { madt_entry_type type; uint8_t length; inti_flags_t flags; uint32_t global_system_interrupt; } __attribute__((packed)); static_assert(sizeof(madt_nmi_source_t) == 8); struct madt_local_apic_nmi_t { madt_entry_type type; uint8_t length; uint8_t acpi_processor_uid; // 0xFF: applies to all processors inti_flags_t flags; uint8_t lint; // local APIC LINT# the NMI is wired to } __attribute__((packed)); static_assert(sizeof(madt_local_apic_nmi_t) == 6); struct madt_local_apic_address_override_t { madt_entry_type type; uint8_t length; uint16_t reserved; uint64_t local_apic_address; } __attribute__((packed)); static_assert(sizeof(madt_local_apic_address_override_t) == 12); struct Madt { explicit Madt(paddr_t madt_physical) : table(madt_physical.access()) {} explicit Madt(const madt_t* madt_table) : table(madt_table) {} paddr_t local_apic_address() { uint64_t address = table->local_apic_address; for_each_entry([&](const madt_local_apic_address_override_t* override_entry) { address = override_entry->local_apic_address; }); return paddr_t{address}; } bool has_legacy_pic() { return table->flags.dual_8259_present != 0; } template void for_each_entry(Fn&& callback) { auto ptr = table->entries; const auto end = reinterpret_cast(table) + table->header.length; while (ptr < end) { auto entry = reinterpret_cast(ptr); if (entry->length < sizeof(madt_entry_t)) { break; } dispatch(entry, callback); ptr += entry->length; } } private: template static void dispatch(const madt_entry_t* entry, Fn&& fn) { #define MADT_DISPATCH(entry_enum, EntryType) \ case madt_entry_type::entry_enum: \ if constexpr (requires { fn(static_cast(nullptr)); }) \ fn(reinterpret_cast(entry)); \ break; switch (entry->type) { MADT_DISPATCH(local_apic, madt_local_apic_t) MADT_DISPATCH(io_apic, madt_io_apic_t) MADT_DISPATCH(interrupt_source_override, madt_interrupt_source_override_t) MADT_DISPATCH(nmi_source, madt_nmi_source_t) MADT_DISPATCH(local_apic_nmi, madt_local_apic_nmi_t) MADT_DISPATCH(local_apic_address_override, madt_local_apic_address_override_t) default: break; } #undef MADT_DISPATCH } const madt_t* table; }; } // namespace acpi