diff --git a/kernel/include/init/acpi.h b/kernel/include/init/acpi.h index 39675b1..9561506 100644 --- a/kernel/include/init/acpi.h +++ b/kernel/include/init/acpi.h @@ -12,9 +12,6 @@ constexpr uint32_t fold_signature(const char (&text)[5]) { | 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"), @@ -24,7 +21,6 @@ enum class signature : uint32_t { ssdt = fold_signature("SSDT"), }; -// A folded signature, unfolded back into a null-terminated 4-character string. struct signature_text_t { char text[5]; }; @@ -63,6 +59,8 @@ struct rsdp_t { } __attribute__((packed)); static_assert(sizeof(rsdp_t) == 36); +struct madt_t; + struct Rsdp { explicit Rsdp(paddr_t rsdp_physical) : physical(rsdp_physical) {} @@ -75,20 +73,33 @@ struct Rsdp { 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}); + dispatch(paddr_t{table_physical}.access(), callback); } } +private: template - void find_table(signature sig, Fn&& callback) { - for_each_table([&](paddr_t table) { - if (table.access()->signature == static_cast(sig)) { - callback(table); - } - }); + 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) + default: break; + } + +#undef ACPI_DISPATCH + + if constexpr (requires { fn(static_cast(nullptr)); }) { + fn(header); + } } -private: bool uses_xsdt() { return physical.access()->revision >= 2; } diff --git a/kernel/include/init/apic.h b/kernel/include/init/apic.h new file mode 100644 index 0000000..d969639 --- /dev/null +++ b/kernel/include/init/apic.h @@ -0,0 +1,193 @@ +#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 diff --git a/kernel/src/init/init.cpp b/kernel/src/init/init.cpp index 07d2ed6..b4839dd 100644 --- a/kernel/src/init/init.cpp +++ b/kernel/src/init/init.cpp @@ -4,6 +4,7 @@ #include "init/multiboot2.h" #include "init/print.h" #include "init/acpi.h" +#include "init/apic.h" #include "memory/allocator.h" [[maybe_unused]] [[noreturn]] __attribute__((used)) static void halt() { @@ -179,12 +180,18 @@ static acpi::Rsdp findRsdp() { return acpi::Rsdp(*rsdp); } +struct AcpiDiscoveryResult { + optional rsdp; + optional madt; +}; + static void setupAcpi() { auto rsdp = findRsdp(); - rsdp.for_each_table([](paddr_t table) { - auto header = table.access(); - print(acpi::unfold_signature(header->signature).text); - print("\n"); + rsdp.for_each_table(overloaded{ + [&](const acpi::sdt_header_t* header) { + print(acpi::unfold_signature(header->signature).text); + print("\n"); + } }); } diff --git a/kernel/src/init/init.o b/kernel/src/init/init.o deleted file mode 100644 index 5387e41..0000000 Binary files a/kernel/src/init/init.o and /dev/null differ