From c7286396d8fcb78633c8a374f9c93be9a7b17ba0 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Mon, 27 Mar 2023 03:38:55 +0300 Subject: [PATCH] Kernel: Move ACPI to its own file --- kernel/Makefile | 1 + kernel/include/kernel/ACPI.h | 43 +++++++++ kernel/kernel/ACPI.cpp | 172 +++++++++++++++++++++++++++++++++++ kernel/kernel/APIC.cpp | 153 ++----------------------------- kernel/kernel/kernel.cpp | 4 + 5 files changed, 229 insertions(+), 144 deletions(-) create mode 100644 kernel/include/kernel/ACPI.h create mode 100644 kernel/kernel/ACPI.cpp diff --git a/kernel/Makefile b/kernel/Makefile index 2932e2ff..041e2364 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -36,6 +36,7 @@ BUILDDIR=$(abspath build) KERNEL_OBJS= \ $(KERNEL_ARCH_OBJS) \ font/prefs.o \ +kernel/ACPI.o \ kernel/APIC.o \ kernel/build_libc.o \ kernel/CPUID.o \ diff --git a/kernel/include/kernel/ACPI.h b/kernel/include/kernel/ACPI.h new file mode 100644 index 00000000..00bc05ee --- /dev/null +++ b/kernel/include/kernel/ACPI.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +namespace Kernel +{ + + class ACPI + { + public: + struct SDTHeader + { + uint8_t signature[4]; + uint32_t length; + uint8_t revision; + uint8_t checksum; + uint8_t oemid[6]; + uint64_t oem_table_id; + uint32_t oem_revision; + uint32_t creator_id; + uint32_t creator_revision; + } __attribute__((packed)); + + public: + static BAN::ErrorOr initialize(); + static ACPI& get(); + + BAN::ErrorOr get_header(const char[4]); + void unmap_header(const SDTHeader*); + + private: + ACPI() = default; + BAN::ErrorOr initialize_impl(); + + const SDTHeader* get_header_from_index(size_t); + + private: + uintptr_t m_header_table = 0; + uint32_t m_entry_size = 0; + uint32_t m_entry_count = 0; + }; + +} \ No newline at end of file diff --git a/kernel/kernel/ACPI.cpp b/kernel/kernel/ACPI.cpp new file mode 100644 index 00000000..6031aca5 --- /dev/null +++ b/kernel/kernel/ACPI.cpp @@ -0,0 +1,172 @@ +#include +#include +#include +#include + +#define RSPD_SIZE 20 +#define RSPDv2_SIZE 36 + +namespace Kernel +{ + + struct RSDP + { + uint8_t signature[8]; + uint8_t checksum; + uint8_t oemid[6]; + uint8_t revision; + uint32_t rsdt_address; + + // only in revision >= 2 + uint32_t length; + uint64_t xsdt_address; + uint8_t extended_checksum; + uint8_t reserved[3]; + }; + + struct RSDT : public ACPI::SDTHeader + { + uint32_t entries[]; + } __attribute__((packed)); + + struct XSDT : public ACPI::SDTHeader + { + uint64_t entries[]; + } __attribute__((packed)); + + static ACPI* s_instance = nullptr; + + BAN::ErrorOr ACPI::initialize() + { + ASSERT(s_instance == nullptr); + s_instance = new ACPI; + if (s_instance == nullptr) + return BAN::Error::from_errno(ENOMEM); + TRY(s_instance->initialize_impl()); + return {}; + } + + ACPI& ACPI::get() + { + ASSERT(s_instance != nullptr); + return *s_instance; + } + + static bool is_rsdp(uintptr_t rsdp_addr) + { + const RSDP* rsdp = (const RSDP*)rsdp_addr; + + if (memcmp(rsdp->signature, "RSD PTR ", 8) != 0) + return false; + + { + uint8_t checksum = 0; + for (uint32_t i = 0; i < RSPD_SIZE; i++) + checksum += ((const uint8_t*)rsdp)[i]; + if (checksum != 0) + return false; + } + + if (rsdp->revision == 2) + { + uint8_t checksum = 0; + for (uint32_t i = 0; i < RSPDv2_SIZE; i++) + checksum += ((const uint8_t*)rsdp)[i]; + if (checksum != 0) + return false; + } + + return true; + } + + static const RSDP* locate_rsdp() + { + // Look in main BIOS area below 1 MB + for (uintptr_t addr = 0x000E0000; addr < 0x000FFFFF; addr += 16) + if (is_rsdp(addr)) + return (const RSDP*)addr; + return nullptr; + } + + static bool is_valid_std_header(const ACPI::SDTHeader* header) + { + uint8_t sum = 0; + for (uint32_t i = 0; i < header->length; i++) + sum += ((uint8_t*)header)[i]; + return sum == 0; + } + + BAN::ErrorOr ACPI::initialize_impl() + { + const RSDP* rsdp = locate_rsdp(); + if (rsdp == nullptr) + return BAN::Error::from_c_string("Could not find RSDP"); + + if (rsdp->revision >= 2) + { + const XSDT* xsdt = (const XSDT*)rsdp->xsdt_address; + MMU::get().allocate_page((uintptr_t)xsdt, MMU::Flags::Present); + BAN::ScopeGuard _([xsdt] { MMU::get().unallocate_page((uintptr_t)xsdt); }); + + if (memcmp(xsdt->signature, "XSDT", 4) != 0) + return BAN::Error::from_c_string("XSDT has invalid signature"); + if (!is_valid_std_header(xsdt)) + return BAN::Error::from_c_string("XSDT has invalid checksum"); + + m_header_table = (uintptr_t)xsdt->entries; + m_entry_size = 8; + m_entry_count = (xsdt->length - sizeof(SDTHeader)) / 8; + } + else + { + const RSDT* rsdt = (const RSDT*)(uintptr_t)rsdp->rsdt_address; + MMU::get().allocate_page((uintptr_t)rsdt, MMU::Flags::Present); + BAN::ScopeGuard _([rsdt] { MMU::get().unallocate_page((uintptr_t)rsdt); }); + + if (memcmp(rsdt->signature, "RSDT", 4) != 0) + return BAN::Error::from_c_string("RSDT has invalid signature"); + if (!is_valid_std_header(rsdt)) + return BAN::Error::from_c_string("RSDT has invalid checksum"); + + m_header_table = (uintptr_t)rsdt->entries; + m_entry_size = 4; + m_entry_count = (rsdt->length - sizeof(SDTHeader)) / 4; + } + + MMU::get().allocate_range(m_header_table, m_entry_count * m_entry_size, MMU::Flags::Present); + + return {}; + } + + BAN::ErrorOr ACPI::get_header(const char signature[4]) + { + for (uint32_t i = 0; i < m_entry_count; i++) + { + const SDTHeader* header = get_header_from_index(i); + MMU::get().allocate_range((uintptr_t)header, header->length, MMU::Flags::Present); + if (!is_valid_std_header(header)) + { + unmap_header(header); + continue; + } + if (memcmp(header->signature, signature, 4) == 0) + return header; + } + return BAN::Error::from_format("Could not find ACPI header '{}'", BAN::StringView(signature, 4)); + } + + void ACPI::unmap_header(const ACPI::SDTHeader* header) + { + MMU::get().unallocate_range((uintptr_t)header, header->length); + } + + const ACPI::SDTHeader* ACPI::get_header_from_index(size_t index) + { + ASSERT(index < m_entry_count); + ASSERT(m_entry_size == 4 || m_entry_size == 8); + + uintptr_t header_address = (m_entry_size == 4) ? ((uint32_t*)m_header_table)[index] : ((uint64_t*)m_header_table)[index]; + return (SDTHeader*)header_address; + } + +} \ No newline at end of file diff --git a/kernel/kernel/APIC.cpp b/kernel/kernel/APIC.cpp index 33d8870c..ad6632bc 100644 --- a/kernel/kernel/APIC.cpp +++ b/kernel/kernel/APIC.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -18,38 +19,8 @@ // https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#multiple-apic-description-table-madt-format -static constexpr uint32_t RSPD_SIZE = 20; -static constexpr uint32_t RSPDv2_SIZE = 36; - -struct RSDP +struct MADT : public Kernel::ACPI::SDTHeader { - char signature[8]; - uint8_t checksum; - char OEMID[6]; - uint8_t revision; - uint32_t rsdt_address; - uint32_t v2_length; - uint64_t v2_xsdt_address; - uint8_t v2_extended_checksum; - uint8_t v2_reserved[3]; -} __attribute__ ((packed)); - -struct SDTHeader -{ - char signature[4]; - uint32_t length; - uint8_t revision; - uint8_t checksum; - char OEMID[6]; - char OEM_table_id[8]; - uint32_t OEM_revision; - uint32_t creator_id; - uint32_t creator_revision; -} __attribute__((packed)); - -struct MADT -{ - SDTHeader header; uint32_t local_apic; uint32_t flags; } __attribute__((packed)); @@ -110,104 +81,6 @@ union RedirectionEntry }; }; -static bool is_rsdp(uintptr_t rsdp_addr) -{ - const RSDP* rsdp = (const RSDP*)rsdp_addr; - - if (memcmp(rsdp->signature, "RSD PTR ", 8) != 0) - return false; - - { - uint8_t checksum = 0; - for (uint32_t i = 0; i < RSPD_SIZE; i++) - checksum += ((uint8_t*)rsdp)[i]; - if (checksum != 0) - return false; - } - - if (rsdp->revision == 2) - { - uint8_t checksum = 0; - for (uint32_t i = 0; i < RSPDv2_SIZE; i++) - checksum += ((uint8_t*)rsdp)[i]; - if (checksum != 0) - return false; - } - - return true; -} - -static uintptr_t locate_rsdp() -{ - // Look in main BIOS area below 1 MB - for (uintptr_t addr = 0x000E0000; addr < 0x000FFFFF; addr += 16) - if (is_rsdp(addr)) - return addr; - return 0; -} - -static bool is_valid_std_header(const SDTHeader* header) -{ - uint8_t sum = 0; - for (uint32_t i = 0; i < header->length; i++) - sum += ((uint8_t*)header)[i]; - return sum == 0; -} - -uintptr_t locate_madt(uintptr_t rsdp_addr) -{ - uintptr_t entry_address_base = 0; - ptrdiff_t entry_pointer_size = 0; - uint32_t entry_count = 0; - - const RSDP* rsdp = (const RSDP*)rsdp_addr; - if (rsdp->revision == 2) - { - uintptr_t xsdt_addr = rsdp->v2_xsdt_address; - MMU::get().allocate_page(xsdt_addr, MMU::Flags::ReadWrite | MMU::Flags::Present); - entry_address_base = xsdt_addr + sizeof(SDTHeader); - entry_count = (((const SDTHeader*)xsdt_addr)->length - sizeof(SDTHeader)) / 8; - entry_pointer_size = 8; - MMU::get().unallocate_page(xsdt_addr); - } - else - { - uintptr_t rsdt_addr = rsdp->rsdt_address; - MMU::get().allocate_page(rsdt_addr, MMU::Flags::ReadWrite | MMU::Flags::Present); - entry_address_base = rsdt_addr + sizeof(SDTHeader); - entry_count = (((const SDTHeader*)rsdt_addr)->length - sizeof(SDTHeader)) / 4; - entry_pointer_size = 4; - MMU::get().unallocate_page(rsdt_addr); - } - - for (uint32_t i = 0; i < entry_count; i++) - { - uintptr_t entry_addr_ptr = entry_address_base + i * entry_pointer_size; - MMU::get().allocate_page(entry_addr_ptr, MMU::Flags::ReadWrite | MMU::Flags::Present); - - union dummy { uint32_t addr32; uint64_t addr64; } __attribute__((aligned(1), packed)); - - uintptr_t entry_addr; - if (entry_pointer_size == 4) - entry_addr = ((dummy*)entry_addr_ptr)->addr32; - else - entry_addr = ((dummy*)entry_addr_ptr)->addr64; - - MMU::get().allocate_page(entry_addr, MMU::Flags::ReadWrite | MMU::Flags::Present); - - BAN::ScopeGuard _([&]() { - MMU::get().unallocate_page(entry_addr); - MMU::get().unallocate_page(entry_addr_ptr); - }); - - const SDTHeader* entry = (const SDTHeader*)entry_addr; - if (memcmp(entry->signature, "APIC", 4) == 0 && is_valid_std_header(entry)) - return entry_addr; - } - - return 0; -} - APIC* APIC::create() { uint32_t ecx, edx; @@ -218,23 +91,14 @@ APIC* APIC::create() return nullptr; } - uintptr_t rsdp_addr = locate_rsdp(); - if (!rsdp_addr) + auto header_or_error = Kernel::ACPI::get().get_header("APIC"); + if (header_or_error.is_error()) { - dprintln("Could not locate RSDP"); + dprintln("{}", header_or_error.error()); return nullptr; } - uintptr_t madt_addr = locate_madt(rsdp_addr); - if (!madt_addr) - { - dprintln("Could not find MADT in RSDP"); - return nullptr; - } - - MMU::get().allocate_page(madt_addr, MMU::Flags::ReadWrite | MMU::Flags::Present); - - const MADT* madt = (const MADT*)madt_addr; + const MADT* madt = (const MADT*)header_or_error.value(); APIC* apic = new APIC; apic->m_local_apic = madt->local_apic; @@ -242,7 +106,7 @@ APIC* APIC::create() apic->m_irq_overrides[i] = i; uintptr_t madt_entry_addr = (uintptr_t)madt + sizeof(MADT); - while (madt_entry_addr < (uintptr_t)madt + madt->header.length) + while (madt_entry_addr < (uintptr_t)madt + madt->length) { const MADTEntry* entry = (const MADTEntry*)madt_entry_addr; switch (entry->type) @@ -274,7 +138,8 @@ APIC* APIC::create() } madt_entry_addr += entry->length; } - MMU::get().unallocate_page((uintptr_t)madt); + + Kernel::ACPI::get().unmap_header(madt); if (apic->m_local_apic == 0 || apic->m_io_apics.empty()) { diff --git a/kernel/kernel/kernel.cpp b/kernel/kernel/kernel.cpp index 5cec6939..99fb7563 100644 --- a/kernel/kernel/kernel.cpp +++ b/kernel/kernel/kernel.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -126,6 +127,9 @@ extern "C" void kernel_main() TTY* tty1 = new TTY(terminal_driver); ASSERT(tty1); + MUST(ACPI::initialize()); + dprintln("ACPI initialized"); + InterruptController::initialize(cmdline.force_pic); dprintln("Interrupt controller initialized");