From 428afca32b7a919d08c695f4c347453383bd2a85 Mon Sep 17 00:00:00 2001 From: Liav A Date: Fri, 9 Jun 2023 22:50:11 +0300 Subject: [PATCH] Kernel/ACPI: Make most of StaticParsing methods to be platform-agnostic Most of the ACPI static parsing methods (methods that can be called without initializing a full AML parser) are not tied to any specific platform or CPU architecture. The only method that is platform-specific is the one that finds the RSDP structure. Thus, each CPU architecture/platform needs to implement it. This means that now aarch64 can implement its own method to find the ACPI RSDP structure, which would be hooked into the rest of the ACPI code elegantly, but for now I just added a FIXME and that method returns empty value of Optional. --- .../aarch64/Firmware/ACPI/StaticParsing.cpp | 17 +++ .../x86_64/Firmware/ACPI/StaticParsing.cpp | 57 ++++++++++ Kernel/Arch/x86_64/InterruptManagement.cpp | 28 ++--- Kernel/Arch/x86_64/InterruptManagement.h | 8 +- Kernel/Arch/x86_64/Interrupts/APIC.cpp | 22 +++- Kernel/CMakeLists.txt | 5 + Kernel/Firmware/ACPI/Definitions.h | 7 -- Kernel/Firmware/ACPI/Initialize.cpp | 5 +- Kernel/Firmware/ACPI/Parser.cpp | 102 ------------------ Kernel/Firmware/ACPI/StaticParsing.cpp | 69 ++++++++++++ Kernel/Firmware/ACPI/StaticParsing.h | 24 +++++ 11 files changed, 211 insertions(+), 133 deletions(-) create mode 100644 Kernel/Arch/aarch64/Firmware/ACPI/StaticParsing.cpp create mode 100644 Kernel/Arch/x86_64/Firmware/ACPI/StaticParsing.cpp create mode 100644 Kernel/Firmware/ACPI/StaticParsing.cpp create mode 100644 Kernel/Firmware/ACPI/StaticParsing.h diff --git a/Kernel/Arch/aarch64/Firmware/ACPI/StaticParsing.cpp b/Kernel/Arch/aarch64/Firmware/ACPI/StaticParsing.cpp new file mode 100644 index 0000000000..ba0d9bc13e --- /dev/null +++ b/Kernel/Arch/aarch64/Firmware/ACPI/StaticParsing.cpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2023, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Kernel::ACPI::StaticParsing { + +ErrorOr> find_rsdp_in_platform_specific_memory_locations() +{ + // FIXME: Implement finding RSDP for aarch64. + return Optional {}; +} + +} diff --git a/Kernel/Arch/x86_64/Firmware/ACPI/StaticParsing.cpp b/Kernel/Arch/x86_64/Firmware/ACPI/StaticParsing.cpp new file mode 100644 index 0000000000..3ae2a4866b --- /dev/null +++ b/Kernel/Arch/x86_64/Firmware/ACPI/StaticParsing.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Kernel::ACPI::StaticParsing { + +// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#finding-the-rsdp-on-ia-pc-systems +ErrorOr> find_rsdp_in_platform_specific_memory_locations() +{ + constexpr auto signature = "RSD PTR "sv; + auto ebda_or_error = map_ebda(); + if (!ebda_or_error.is_error()) { + auto rsdp = ebda_or_error.value().find_chunk_starting_with(signature, 16); + if (rsdp.has_value()) + return rsdp; + } + auto bios_or_error = map_bios(); + if (!bios_or_error.is_error()) { + auto rsdp = bios_or_error.value().find_chunk_starting_with(signature, 16); + if (rsdp.has_value()) + return rsdp; + } + + // On some systems the RSDP may be located in ACPI NVS or reclaimable memory regions + Optional rsdp; + MM.for_each_physical_memory_range([&](auto& memory_range) { + if (!(memory_range.type == Memory::PhysicalMemoryRangeType::ACPI_NVS || memory_range.type == Memory::PhysicalMemoryRangeType::ACPI_Reclaimable)) + return IterationDecision::Continue; + + Memory::MappedROM mapping; + auto region_size_or_error = Memory::page_round_up(memory_range.length); + if (region_size_or_error.is_error()) + return IterationDecision::Continue; + auto region_or_error = MM.allocate_kernel_region(memory_range.start, region_size_or_error.value(), {}, Memory::Region::Access::Read); + if (region_or_error.is_error()) + return IterationDecision::Continue; + mapping.region = region_or_error.release_value(); + mapping.offset = memory_range.start.offset_in_page(); + mapping.size = memory_range.length; + mapping.paddr = memory_range.start; + + rsdp = mapping.find_chunk_starting_with(signature, 16); + if (rsdp.has_value()) + return IterationDecision::Break; + + return IterationDecision::Continue; + }); + return rsdp; +} + +} diff --git a/Kernel/Arch/x86_64/InterruptManagement.cpp b/Kernel/Arch/x86_64/InterruptManagement.cpp index f392eff467..cd6d063038 100644 --- a/Kernel/Arch/x86_64/InterruptManagement.cpp +++ b/Kernel/Arch/x86_64/InterruptManagement.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -118,20 +119,19 @@ NonnullLockRefPtr InterruptManagement::get_responsible_irq_contro VERIFY_NOT_REACHED(); } -UNMAP_AFTER_INIT PhysicalAddress InterruptManagement::search_for_madt() +UNMAP_AFTER_INIT ErrorOr> InterruptManagement::find_madt_physical_address() { dbgln("Early access to ACPI tables for interrupt setup"); - auto rsdp = ACPI::StaticParsing::find_rsdp(); - if (!rsdp.has_value()) - return {}; - auto apic = ACPI::StaticParsing::find_table(rsdp.value(), "APIC"sv); - if (!apic.has_value()) - return {}; - return apic.value(); + auto possible_rsdp_physical_address = TRY(ACPI::StaticParsing::find_rsdp_in_platform_specific_memory_locations()); + if (!possible_rsdp_physical_address.has_value()) + return Optional {}; + auto possible_apic_physical_address = TRY(ACPI::StaticParsing::find_table(possible_rsdp_physical_address.value(), "APIC"sv)); + if (!possible_apic_physical_address.has_value()) + return Optional {}; + return possible_apic_physical_address.value(); } UNMAP_AFTER_INIT InterruptManagement::InterruptManagement() - : m_madt(search_for_madt()) { } @@ -151,13 +151,15 @@ UNMAP_AFTER_INIT void InterruptManagement::switch_to_ioapic_mode() dmesgln("Interrupts: Switch to IOAPIC mode"); InterruptDisabler disabler; - if (m_madt.is_null()) { + m_madt_physical_address = MUST(find_madt_physical_address()); + + if (!m_madt_physical_address.has_value()) { dbgln("Interrupts: ACPI MADT is not available, reverting to PIC mode"); switch_to_pic_mode(); return; } - dbgln("Interrupts: MADT @ P {}", m_madt.as_ptr()); + dbgln("Interrupts: MADT @ P {}", m_madt_physical_address.value().as_ptr()); locate_apic_data(); if (m_interrupt_controllers.size() == 1) { if (get_interrupt_controller(0).type() == IRQControllerType::i8259) { @@ -187,8 +189,8 @@ UNMAP_AFTER_INIT void InterruptManagement::switch_to_ioapic_mode() UNMAP_AFTER_INIT void InterruptManagement::locate_apic_data() { - VERIFY(!m_madt.is_null()); - auto madt = Memory::map_typed(m_madt).release_value_but_fixme_should_propagate_errors(); + VERIFY(m_madt_physical_address.has_value()); + auto madt = Memory::map_typed(m_madt_physical_address.value()).release_value_but_fixme_should_propagate_errors(); if (madt->flags & PCAT_COMPAT_FLAG) m_interrupt_controllers.append(adopt_lock_ref(*new PIC())); diff --git a/Kernel/Arch/x86_64/InterruptManagement.h b/Kernel/Arch/x86_64/InterruptManagement.h index 684e5f951e..129dc454d0 100644 --- a/Kernel/Arch/x86_64/InterruptManagement.h +++ b/Kernel/Arch/x86_64/InterruptManagement.h @@ -48,8 +48,8 @@ public: static u8 acquire_mapped_interrupt_number(u8 original_irq); static u8 acquire_irq_number(u8 mapped_interrupt_vector); - virtual void switch_to_pic_mode(); - virtual void switch_to_ioapic_mode(); + void switch_to_pic_mode(); + void switch_to_ioapic_mode(); NonnullLockRefPtr get_responsible_irq_controller(u8 interrupt_vector); NonnullLockRefPtr get_responsible_irq_controller(IRQControllerType controller_type, u8 interrupt_vector); @@ -67,12 +67,12 @@ protected: private: InterruptManagement(); - PhysicalAddress search_for_madt(); + ErrorOr> find_madt_physical_address(); void locate_apic_data(); Vector> m_interrupt_controllers; Vector m_isa_interrupt_overrides; Vector m_pci_interrupt_overrides; - PhysicalAddress m_madt; + Optional m_madt_physical_address; }; } diff --git a/Kernel/Arch/x86_64/Interrupts/APIC.cpp b/Kernel/Arch/x86_64/Interrupts/APIC.cpp index e29a30e36b..b746fd1bf2 100644 --- a/Kernel/Arch/x86_64/Interrupts/APIC.cpp +++ b/Kernel/Arch/x86_64/Interrupts/APIC.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -261,19 +262,30 @@ UNMAP_AFTER_INIT bool APIC::init_bsp() m_apic_base = region_or_error.release_value(); } - auto rsdp = ACPI::StaticParsing::find_rsdp(); - if (!rsdp.has_value()) { + auto possible_rsdp_physical_address_or_error = ACPI::StaticParsing::find_rsdp_in_platform_specific_memory_locations(); + if (possible_rsdp_physical_address_or_error.is_error()) { + dbgln("APIC: Failed to map RSDP"); + return false; + } + auto possible_rsdp_physical_address = possible_rsdp_physical_address_or_error.release_value(); + if (!possible_rsdp_physical_address.has_value()) { dbgln("APIC: RSDP not found"); return false; } - auto madt_address = ACPI::StaticParsing::find_table(rsdp.value(), "APIC"sv); - if (!madt_address.has_value()) { + + auto possible_apic_physical_address_or_error = ACPI::StaticParsing::find_table(possible_rsdp_physical_address.value(), "APIC"sv); + if (possible_apic_physical_address_or_error.is_error()) { + dbgln("APIC: Failed to map RSDT/XSDT"); + return false; + } + auto possible_apic_physical_address = possible_apic_physical_address_or_error.release_value(); + if (!possible_apic_physical_address.has_value()) { dbgln("APIC: MADT table not found"); return false; } if (kernel_command_line().is_smp_enabled()) { - auto madt_or_error = Memory::map_typed(madt_address.value()); + auto madt_or_error = Memory::map_typed(possible_apic_physical_address.value()); if (madt_or_error.is_error()) { dbgln("APIC: Failed to map MADT table"); return false; diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 99cec35d13..5da5389a8d 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -209,6 +209,7 @@ set(KERNEL_SOURCES Firmware/BIOS.cpp Firmware/ACPI/Initialize.cpp Firmware/ACPI/Parser.cpp + Firmware/ACPI/StaticParsing.cpp Firmware/MultiProcessor/Parser.cpp Interrupts/GenericInterruptHandler.cpp Interrupts/IRQHandler.cpp @@ -370,6 +371,8 @@ if ("${SERENITY_ARCH}" STREQUAL "x86_64") Arch/x86_64/DebugOutput.cpp Arch/x86_64/Delay.cpp + Arch/x86_64/Firmware/ACPI/StaticParsing.cpp + Arch/x86_64/Hypervisor/BochsDisplayConnector.cpp Arch/x86_64/Hypervisor/VMWareBackdoor.cpp @@ -460,6 +463,8 @@ elseif("${SERENITY_ARCH}" STREQUAL "aarch64") ${SOURCES_RUNNING_WITHOUT_MMU} Arch/Processor.cpp + Arch/aarch64/Firmware/ACPI/StaticParsing.cpp + Arch/aarch64/boot.S Arch/aarch64/BootPPMParser.cpp Arch/aarch64/CPUID.cpp diff --git a/Kernel/Firmware/ACPI/Definitions.h b/Kernel/Firmware/ACPI/Definitions.h index 438a8ce6c9..08c45341b4 100644 --- a/Kernel/Firmware/ACPI/Definitions.h +++ b/Kernel/Firmware/ACPI/Definitions.h @@ -327,11 +327,4 @@ struct [[gnu::packed]] DSDT { }; } -class Parser; - -namespace StaticParsing { -Optional find_rsdp(); -Optional find_table(PhysicalAddress rsdp, StringView signature); -} - } diff --git a/Kernel/Firmware/ACPI/Initialize.cpp b/Kernel/Firmware/ACPI/Initialize.cpp index 85c0b30e38..77e06e1855 100644 --- a/Kernel/Firmware/ACPI/Initialize.cpp +++ b/Kernel/Firmware/ACPI/Initialize.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -18,11 +19,11 @@ UNMAP_AFTER_INIT void initialize() if (feature_level == AcpiFeatureLevel::Disabled) return; - auto rsdp = StaticParsing::find_rsdp(); + auto rsdp = MUST(StaticParsing::find_rsdp_in_platform_specific_memory_locations()); if (!rsdp.has_value()) return; - auto facp = StaticParsing::find_table(rsdp.value(), "FACP"sv); + auto facp = MUST(StaticParsing::find_table(rsdp.value(), "FACP"sv)); if (!facp.has_value()) return; auto facp_table_or_error = Memory::map_typed(facp.value()); diff --git a/Kernel/Firmware/ACPI/Parser.cpp b/Kernel/Firmware/ACPI/Parser.cpp index 98863f7355..d69c41e024 100644 --- a/Kernel/Firmware/ACPI/Parser.cpp +++ b/Kernel/Firmware/ACPI/Parser.cpp @@ -120,9 +120,6 @@ void Parser::enumerate_static_tables(Function search_table_in_xsdt(PhysicalAddress xsdt, StringView signature); -static Optional search_table_in_rsdt(PhysicalAddress rsdt, StringView signature); static bool validate_table(Structures::SDTHeader const&, size_t length); UNMAP_AFTER_INIT void Parser::locate_static_data() @@ -417,103 +414,4 @@ static bool validate_table(Structures::SDTHeader const& v_header, size_t length) return false; } -// https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#finding-the-rsdp-on-ia-pc-systems -UNMAP_AFTER_INIT Optional StaticParsing::find_rsdp() -{ - constexpr auto signature = "RSD PTR "sv; - auto ebda_or_error = map_ebda(); - if (!ebda_or_error.is_error()) { - auto rsdp = ebda_or_error.value().find_chunk_starting_with(signature, 16); - if (rsdp.has_value()) - return rsdp; - } - auto bios_or_error = map_bios(); - if (!bios_or_error.is_error()) { - auto rsdp = bios_or_error.value().find_chunk_starting_with(signature, 16); - if (rsdp.has_value()) - return rsdp; - } - - // On some systems the RSDP may be located in ACPI NVS or reclaimable memory regions - Optional rsdp; - MM.for_each_physical_memory_range([&](auto& memory_range) { - if (!(memory_range.type == Memory::PhysicalMemoryRangeType::ACPI_NVS || memory_range.type == Memory::PhysicalMemoryRangeType::ACPI_Reclaimable)) - return IterationDecision::Continue; - - Memory::MappedROM mapping; - auto region_size_or_error = Memory::page_round_up(memory_range.length); - if (region_size_or_error.is_error()) - return IterationDecision::Continue; - auto region_or_error = MM.allocate_kernel_region(memory_range.start, region_size_or_error.value(), {}, Memory::Region::Access::Read); - if (region_or_error.is_error()) - return IterationDecision::Continue; - mapping.region = region_or_error.release_value(); - mapping.offset = memory_range.start.offset_in_page(); - mapping.size = memory_range.length; - mapping.paddr = memory_range.start; - - rsdp = mapping.find_chunk_starting_with(signature, 16); - if (rsdp.has_value()) - return IterationDecision::Break; - - return IterationDecision::Continue; - }); - return rsdp; -} - -UNMAP_AFTER_INIT Optional StaticParsing::find_table(PhysicalAddress rsdp_address, StringView signature) -{ - // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables. - VERIFY(signature.length() == 4); - - auto rsdp = Memory::map_typed(rsdp_address).release_value_but_fixme_should_propagate_errors(); - - if (rsdp->base.revision == 0) - return search_table_in_rsdt(PhysicalAddress(rsdp->base.rsdt_ptr), signature); - - if (rsdp->base.revision >= 2) { - if (rsdp->xsdt_ptr) - return search_table_in_xsdt(PhysicalAddress(rsdp->xsdt_ptr), signature); - return search_table_in_rsdt(PhysicalAddress(rsdp->base.rsdt_ptr), signature); - } - VERIFY_NOT_REACHED(); -} - -UNMAP_AFTER_INIT static Optional search_table_in_xsdt(PhysicalAddress xsdt_address, StringView signature) -{ - // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables. - VERIFY(signature.length() == 4); - - auto xsdt = Memory::map_typed(xsdt_address).release_value_but_fixme_should_propagate_errors(); - - for (size_t i = 0; i < ((xsdt->h.length - sizeof(Structures::SDTHeader)) / sizeof(u64)); ++i) { - if (match_table_signature(PhysicalAddress((PhysicalPtr)xsdt->table_ptrs[i]), signature)) - return PhysicalAddress((PhysicalPtr)xsdt->table_ptrs[i]); - } - return {}; -} - -static bool match_table_signature(PhysicalAddress table_header, StringView signature) -{ - // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables. - VERIFY(signature.length() == 4); - - auto table = Memory::map_typed(table_header).release_value_but_fixme_should_propagate_errors(); - return !strncmp(table->h.sig, signature.characters_without_null_termination(), 4); -} - -UNMAP_AFTER_INIT static Optional search_table_in_rsdt(PhysicalAddress rsdt_address, StringView signature) -{ - // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables. - VERIFY(signature.length() == 4); - - auto rsdt = Memory::map_typed(rsdt_address).release_value_but_fixme_should_propagate_errors(); - - for (u32 i = 0; i < ((rsdt->h.length - sizeof(Structures::SDTHeader)) / sizeof(u32)); i++) { - if (match_table_signature(PhysicalAddress((PhysicalPtr)rsdt->table_ptrs[i]), signature)) - return PhysicalAddress((PhysicalPtr)rsdt->table_ptrs[i]); - } - return {}; -} - } diff --git a/Kernel/Firmware/ACPI/StaticParsing.cpp b/Kernel/Firmware/ACPI/StaticParsing.cpp new file mode 100644 index 0000000000..2b967f01bd --- /dev/null +++ b/Kernel/Firmware/ACPI/StaticParsing.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Kernel::ACPI::StaticParsing { + +static bool match_table_signature(PhysicalAddress table_header, StringView signature) +{ + // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables. + VERIFY(signature.length() == 4); + + auto table = Memory::map_typed(table_header).release_value_but_fixme_should_propagate_errors(); + return !strncmp(table->h.sig, signature.characters_without_null_termination(), 4); +} + +ErrorOr> search_table_in_xsdt(PhysicalAddress xsdt_address, StringView signature) +{ + // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables. + VERIFY(signature.length() == 4); + + auto xsdt = TRY(Memory::map_typed(xsdt_address)); + + for (size_t i = 0; i < ((xsdt->h.length - sizeof(Structures::SDTHeader)) / sizeof(u64)); ++i) { + if (match_table_signature(PhysicalAddress((PhysicalPtr)xsdt->table_ptrs[i]), signature)) + return PhysicalAddress((PhysicalPtr)xsdt->table_ptrs[i]); + } + return Optional {}; +} + +ErrorOr> find_table(PhysicalAddress rsdp_address, StringView signature) +{ + // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables. + VERIFY(signature.length() == 4); + + auto rsdp = TRY(Memory::map_typed(rsdp_address)); + + if (rsdp->base.revision == 0) + return search_table_in_rsdt(PhysicalAddress(rsdp->base.rsdt_ptr), signature); + + if (rsdp->base.revision >= 2) { + if (rsdp->xsdt_ptr) + return search_table_in_xsdt(PhysicalAddress(rsdp->xsdt_ptr), signature); + return search_table_in_rsdt(PhysicalAddress(rsdp->base.rsdt_ptr), signature); + } + VERIFY_NOT_REACHED(); +} + +ErrorOr> search_table_in_rsdt(PhysicalAddress rsdt_address, StringView signature) +{ + // FIXME: There's no validation of ACPI tables here. Use the checksum to validate the tables. + VERIFY(signature.length() == 4); + + auto rsdt = TRY(Memory::map_typed(rsdt_address)); + + for (u32 i = 0; i < ((rsdt->h.length - sizeof(Structures::SDTHeader)) / sizeof(u32)); i++) { + if (match_table_signature(PhysicalAddress((PhysicalPtr)rsdt->table_ptrs[i]), signature)) + return PhysicalAddress((PhysicalPtr)rsdt->table_ptrs[i]); + } + return Optional {}; +} + +} diff --git a/Kernel/Firmware/ACPI/StaticParsing.h b/Kernel/Firmware/ACPI/StaticParsing.h new file mode 100644 index 0000000000..fec03ff1f4 --- /dev/null +++ b/Kernel/Firmware/ACPI/StaticParsing.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Kernel::ACPI::StaticParsing { + +ErrorOr> find_table(PhysicalAddress rsdp, StringView signature); +ErrorOr> search_table_in_xsdt(PhysicalAddress xsdt, StringView signature); +ErrorOr> search_table_in_rsdt(PhysicalAddress rsdt, StringView signature); + +// NOTE: This function is implemented for each CPU architecture in a subdirectory +// under the Kernel/Arch directory. +ErrorOr> find_rsdp_in_platform_specific_memory_locations(); + +}