From f456f1674d48932846eb7b5ec1df630ad67e7e3d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 17:24:36 +0200 Subject: kernel/acpi: discover local interrupt controllers --- .vscode/settings.json | 6 +- arch/x86_64/CMakeLists.txt | 2 + arch/x86_64/include/arch/devices/local_apic.hpp | 26 +++++++ arch/x86_64/kapi/acpi.cpp | 43 +++++++++++ arch/x86_64/src/devices/init.cpp | 32 +------- arch/x86_64/src/devices/local_apic.cpp | 28 +++++++ kapi/include/kapi/acpi.hpp | 91 +++++++++++++++++------ kapi/include/kapi/devices/bus.hpp | 2 + kapi/include/kapi/devices/device.hpp | 2 + kernel/CMakeLists.txt | 1 + kernel/include/kernel/acpi/manager.hpp | 3 + kernel/include/kernel/devices/cpu.hpp | 33 +++++++++ kernel/kapi/acpi.cpp | 43 ++++++++++- kernel/kapi/devices.cpp | 10 +++ kernel/src/acpi/manager.cpp | 15 +++- kernel/src/devices/cpu.cpp | 97 +++++++++++++++++++++++++ kernel/src/main.cpp | 10 +++ 17 files changed, 386 insertions(+), 58 deletions(-) create mode 100644 arch/x86_64/include/arch/devices/local_apic.hpp create mode 100644 arch/x86_64/kapi/acpi.cpp create mode 100644 arch/x86_64/src/devices/local_apic.cpp create mode 100644 kernel/include/kernel/devices/cpu.hpp create mode 100644 kernel/src/devices/cpu.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 6ea3c79..bebda51 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -26,6 +26,7 @@ }, "cSpell.words": [ "acpi", + "APIC", "bugprone", "cppcoreguidelines", "crtc", @@ -41,6 +42,8 @@ "iwyu", "kapi", "kstd", + "lapic", + "madt", "malloc", "memcmp", "memset", @@ -60,7 +63,8 @@ "teachos", "undelegated", "wrmsr", - "xsdp" + "xsdp", + "xsdt" ], "testMate.cpp.debug.configTemplate": { "type": "cppdbg", diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index f77eec5..62a2aad 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -12,6 +12,7 @@ target_link_libraries("x86_64" PUBLIC target_sources("x86_64" PRIVATE # Platform-dependent KAPI implementation + "kapi/acpi.cpp" "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" @@ -40,6 +41,7 @@ target_sources("x86_64" PRIVATE # Devices "src/devices/init.cpp" "src/devices/legacy_pit.cpp" + "src/devices/local_apic.cpp" # Memory management "src/memory/kernel_mapper.cpp" diff --git a/arch/x86_64/include/arch/devices/local_apic.hpp b/arch/x86_64/include/arch/devices/local_apic.hpp new file mode 100644 index 0000000..71e9758 --- /dev/null +++ b/arch/x86_64/include/arch/devices/local_apic.hpp @@ -0,0 +1,26 @@ +#ifndef TEACHOS_ARCH_X86_64_DEVICES_LOCAL_APIC_HPP +#define TEACHOS_ARCH_X86_64_DEVICES_LOCAL_APIC_HPP + +#include "kapi/devices/device.hpp" +#include "kapi/memory.hpp" + +#include +#include + +namespace arch::devices +{ + + struct local_apic : kapi::devices::device + { + local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id, kapi::memory::physical_address base); + + auto init() -> bool override; + + private: + std::uint64_t m_hardware_id{}; + kapi::memory::physical_address m_base{}; + }; + +} // namespace arch::devices + +#endif \ No newline at end of file diff --git a/arch/x86_64/kapi/acpi.cpp b/arch/x86_64/kapi/acpi.cpp new file mode 100644 index 0000000..9766154 --- /dev/null +++ b/arch/x86_64/kapi/acpi.cpp @@ -0,0 +1,43 @@ +#include "kapi/acpi.hpp" + +#include "kapi/devices.hpp" +#include "kapi/memory.hpp" + +#include "arch/boot/boot.hpp" +#include "arch/devices/local_apic.hpp" + +#include + +#include +#include + +namespace kapi::acpi +{ + + auto get_root_pointer() -> kstd::observer_ptr + { + auto const & mbi = kapi::boot::bootstrap_information.mbi; + auto system_description_pointer = static_cast(nullptr); + + if (auto const & xsdp = mbi->maybe_acpi_xsdp()) + { + auto data = xsdp->pointer().data(); + + system_description_pointer = reinterpret_cast(data); + } + else if (auto const & rsdp = mbi->maybe_acpi_rsdp()) + { + auto data = rsdp->pointer().data(); + system_description_pointer = reinterpret_cast(data); + } + + return kstd::make_observer(system_description_pointer); + } + + auto create_local_interrupt_controller(std::size_t major, std::size_t minor, std::uint64_t hardware_id, + memory::physical_address address) -> kstd::unique_ptr + { + return kstd::make_unique(major, minor, hardware_id, address); + } + +} // namespace kapi::acpi \ No newline at end of file diff --git a/arch/x86_64/src/devices/init.cpp b/arch/x86_64/src/devices/init.cpp index 1099ad6..6cba986 100644 --- a/arch/x86_64/src/devices/init.cpp +++ b/arch/x86_64/src/devices/init.cpp @@ -1,10 +1,7 @@ #include "arch/devices/init.hpp" -#include "kapi/acpi.hpp" -#include "kapi/boot.hpp" #include "kapi/devices.hpp" -#include "arch/boot/boot.hpp" #include "arch/bus/isa.hpp" #include "arch/devices/legacy_pit.hpp" @@ -22,34 +19,7 @@ namespace arch::devices constexpr auto pit_frequency_in_hz = std::uint32_t{100u}; } - auto init_acpi_devices() -> void - { - auto const & mbi = kapi::boot::bootstrap_information.mbi; - auto system_description_pointer = static_cast(nullptr); - - if (auto const & xsdp = mbi->maybe_acpi_xsdp()) - { - auto data = xsdp->pointer().data(); - system_description_pointer = reinterpret_cast(data); - } - else if (auto const & rsdp = mbi->maybe_acpi_rsdp()) - { - auto data = rsdp->pointer().data(); - system_description_pointer = reinterpret_cast(data); - } - - if (system_description_pointer) - { - if (!kapi::acpi::init(*system_description_pointer)) - { - kstd::println(kstd::print_sink::stderr, "[x86_64:DEV] ACPI initialization failed. No tables loaded."); - } - } - else - { - kstd::println(kstd::print_sink::stderr, "[x86_64:DEV] No ACPI RSDP found. Most devices will not be available."); - } - } + auto init_acpi_devices() -> void {} auto init_legacy_devices() -> void { diff --git a/arch/x86_64/src/devices/local_apic.cpp b/arch/x86_64/src/devices/local_apic.cpp new file mode 100644 index 0000000..beb75ef --- /dev/null +++ b/arch/x86_64/src/devices/local_apic.cpp @@ -0,0 +1,28 @@ +#include "arch/devices/local_apic.hpp" + +#include "kapi/devices.hpp" +#include "kapi/memory.hpp" + +#include + +#include +#include + +namespace arch::devices +{ + + local_apic::local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id, + kapi::memory::physical_address base) + : kapi::devices::device{major, minor, "lapic"} + , m_hardware_id{hardware_id} + , m_base{base} + {} + + auto local_apic::init() -> bool + { + kstd::println("[x86_64:DEV] Initializing local APIC on core {}", m_hardware_id); + + return true; + } + +} // namespace arch::devices \ No newline at end of file diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index 11068d1..5d55d08 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -1,8 +1,10 @@ #ifndef TEACHOS_KAPI_ACPI_HPP #define TEACHOS_KAPI_ACPI_HPP +#include "kapi/devices.hpp" #include "kapi/memory.hpp" +#include #include #include @@ -14,9 +16,38 @@ namespace kapi::acpi { - //! @addtogroup kapi-acpi + //! @addtogroup kapi-acpi-kernel-defined //! @{ + struct [[gnu::packed]] root_system_description_pointer + { + [[nodiscard]] auto oem_id() const noexcept -> std::string_view; + [[nodiscard]] auto revision() const noexcept -> std::uint8_t; + [[nodiscard]] auto signature() const noexcept -> std::string_view; + [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; + [[nodiscard]] auto validate() const noexcept -> bool; + + private: + std::array m_signature; + [[maybe_unused]] std::uint8_t m_checksum; + std::array m_oem_id; + std::uint8_t m_revision; + std::array m_rsdt_address; + }; + + struct [[gnu::packed]] extended_system_description_pointer : root_system_description_pointer + { + [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; + [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; + [[nodiscard]] auto validate() const noexcept -> bool; + + private: + std::uint32_t m_length; + std::array m_xsdt_address; + [[maybe_unused]] std::uint8_t m_extended_checksum; + [[maybe_unused]] std::array m_reserved; + }; + struct [[gnu::packed]] system_description_table_header { [[nodiscard]] auto checksum() const noexcept -> std::uint8_t; @@ -41,38 +72,36 @@ namespace kapi::acpi std::uint32_t m_creator_revision; }; - //! @} + struct [[gnu::packed]] madt_header : system_description_table_header + { + [[nodiscard]] auto local_interrupt_controller_address() const noexcept -> memory::physical_address; + [[nodiscard]] auto flags() const noexcept -> std::uint32_t; - //! @addtogroup kapi-acpi-kernel-defined - //! @{ + private: + std::uint32_t m_local_interrupt_controller_address; + std::uint32_t m_flags; + }; - struct [[gnu::packed]] root_system_description_pointer + struct [[gnu::packed]] madt_subtable_header { - [[nodiscard]] auto oem_id() const noexcept -> std::string_view; - [[nodiscard]] auto revision() const noexcept -> std::uint8_t; - [[nodiscard]] auto signature() const noexcept -> std::string_view; - [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; - [[nodiscard]] auto validate() const noexcept -> bool; + [[nodiscard]] auto type() const noexcept -> std::uint8_t; + [[nodiscard]] auto length() const noexcept -> std::size_t; private: - std::array m_signature; - [[maybe_unused]] std::uint8_t m_checksum; - std::array m_oem_id; - std::uint8_t m_revision; - std::array m_rsdt_address; + std::uint8_t m_type; + std::uint8_t m_length; }; - struct [[gnu::packed]] extended_system_description_pointer : root_system_description_pointer + struct [[gnu::packed]] madt_local_apic : madt_subtable_header { - [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; - [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; - [[nodiscard]] auto validate() const noexcept -> bool; + [[nodiscard]] auto apic_id() const noexcept -> std::uint8_t; + [[nodiscard]] auto flags() const noexcept -> std::uint32_t; + [[nodiscard]] auto processor_id() const noexcept -> std::uint32_t; private: - std::uint32_t m_length; - std::array m_xsdt_address; - [[maybe_unused]] std::uint8_t m_extended_checksum; - [[maybe_unused]] std::array m_reserved; + std::uint8_t m_processor_id; + std::uint8_t m_apic_id; + std::uint32_t m_flags; }; //! Initialize the ACPI subsystem and discover the available tables. @@ -86,6 +115,22 @@ namespace kapi::acpi //! @return true iff. the checksum is valid, false otherwise. auto validate_checksum(std::span data) -> bool; + //! Get an ACPI table by its signature. + //! + //! @param signature The signature of the table to get. + //! @return A pointer to the table if found, nullptr otherwise. + auto get_table(std::string_view signature) -> kstd::observer_ptr; + + //! @} + + //! @addtogroup kapi-acpi-platform-defined + //! @{ + + auto get_root_pointer() -> kstd::observer_ptr; + + auto create_local_interrupt_controller(std::size_t major, std::size_t minor, std::uint64_t hardware_id, + memory::physical_address address) -> kstd::unique_ptr; + //! @} } // namespace kapi::acpi diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index 052c062..a384291 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -1,6 +1,8 @@ #ifndef TEACHOS_KAPI_DEVICES_BUS_HPP #define TEACHOS_KAPI_DEVICES_BUS_HPP +// IWYU pragma: private, include "kapi/devices.hpp" + #include "kapi/devices/device.hpp" #include "kapi/devices/manager.hpp" #include "kapi/system.hpp" diff --git a/kapi/include/kapi/devices/device.hpp b/kapi/include/kapi/devices/device.hpp index b7b6bba..ca5033e 100644 --- a/kapi/include/kapi/devices/device.hpp +++ b/kapi/include/kapi/devices/device.hpp @@ -1,6 +1,8 @@ #ifndef TEACH_OS_KAPI_DEVICES_DEVICE_HPP #define TEACH_OS_KAPI_DEVICES_DEVICE_HPP +// IWYU pragma: private, include "kapi/devices.hpp" + #include #include diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index fc01723..92e2bda 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -20,6 +20,7 @@ add_library("kernel_objs" OBJECT "src/memory.cpp" "src/devices/block_device.cpp" "src/devices/block_device_utils.cpp" + "src/devices/cpu.cpp" "src/devices/root_bus.cpp" "src/devices/storage/controller.cpp" "src/devices/storage/management.cpp" diff --git a/kernel/include/kernel/acpi/manager.hpp b/kernel/include/kernel/acpi/manager.hpp index 437d4c8..fae59a6 100644 --- a/kernel/include/kernel/acpi/manager.hpp +++ b/kernel/include/kernel/acpi/manager.hpp @@ -4,6 +4,7 @@ #include "kapi/acpi.hpp" #include +#include #include #include @@ -17,6 +18,8 @@ namespace kernel::acpi auto load_tables() -> bool; + auto get_table(std::string_view signature) -> kstd::observer_ptr; + private: kapi::acpi::root_system_description_pointer const * m_sdp{}; kapi::acpi::system_description_table_header const * m_rsdt{}; diff --git a/kernel/include/kernel/devices/cpu.hpp b/kernel/include/kernel/devices/cpu.hpp new file mode 100644 index 0000000..b056665 --- /dev/null +++ b/kernel/include/kernel/devices/cpu.hpp @@ -0,0 +1,33 @@ +#ifndef TEACHOS_KERNEL_DEVICES_CPU_HPP +#define TEACHOS_KERNEL_DEVICES_CPU_HPP + +#include "kapi/devices.hpp" + +#include +#include + +namespace kernel::devices +{ + + struct cpu final : kapi::devices::bus + { + struct core final : kapi::devices::bus + { + explicit core(std::size_t major_number, std::size_t minor_number, std::uint64_t hardware_id, bool is_bsp); + + [[nodiscard]] auto hardware_id() const -> std::uint64_t; + [[nodiscard]] auto is_bsp() const -> bool; + + private: + std::uint64_t m_hardware_id; + bool m_is_bsp; + }; + + explicit cpu(std::size_t major_number); + + auto probe() -> bool final; + }; + +} // namespace kernel::devices + +#endif \ No newline at end of file diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp index 998d7a0..1283d7e 100644 --- a/kernel/kapi/acpi.cpp +++ b/kernel/kapi/acpi.cpp @@ -5,6 +5,7 @@ #include "kernel/acpi/manager.hpp" +#include #include #include @@ -110,12 +111,47 @@ namespace kapi::acpi return {m_signature.data(), m_signature.size()}; } + [[nodiscard]] auto madt_header::local_interrupt_controller_address() const noexcept -> memory::physical_address + { + return memory::physical_address{static_cast(m_local_interrupt_controller_address)}; + } + + [[nodiscard]] auto madt_header::flags() const noexcept -> std::uint32_t + { + return m_flags; + } + + [[nodiscard]] auto madt_subtable_header::type() const noexcept -> std::uint8_t + { + return m_type; + } + + [[nodiscard]] auto madt_subtable_header::length() const noexcept -> std::size_t + { + return m_length; + } + + [[nodiscard]] auto madt_local_apic::apic_id() const noexcept -> std::uint8_t + { + return m_apic_id; + } + + [[nodiscard]] auto madt_local_apic::flags() const noexcept -> std::uint32_t + { + return m_flags; + } + + [[nodiscard]] auto madt_local_apic::processor_id() const noexcept -> std::uint32_t + { + return m_processor_id; + } + auto init(root_system_description_pointer const & sdp) -> bool { auto static constinit initialized = std::atomic_flag{}; if (initialized.test_and_set()) { - system::panic("[OS::ACPI] The ACPI manager has already been initialized!"); + system::panic("[OS:ACPI] The ACPI manager has already been initialized!"); } manager.emplace(sdp); @@ -130,4 +166,9 @@ namespace kapi::acpi return sum == 0; } + auto get_table(std::string_view signature) -> kstd::observer_ptr + { + return manager->get_table(signature); + } + }; // namespace kapi::acpi diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index 031f2c9..b2911b0 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -1,7 +1,9 @@ #include "kapi/devices.hpp" +#include "kapi/acpi.hpp" #include "kapi/system.hpp" +#include "kernel/devices/cpu.hpp" #include "kernel/devices/root_bus.hpp" #include @@ -35,6 +37,14 @@ namespace kapi::devices auto & bus = root_bus.emplace(); register_device(bus); bus.init(); + + auto madt = kapi::acpi::get_table("APIC"); + if (madt) + { + auto cpu_major = allocate_major_number(); + auto cpu = kstd::make_unique(cpu_major); + bus.add_child(std::move(cpu)); + } } auto get_root_bus() -> bus & diff --git a/kernel/src/acpi/manager.cpp b/kernel/src/acpi/manager.cpp index 180d458..300b85e 100644 --- a/kernel/src/acpi/manager.cpp +++ b/kernel/src/acpi/manager.cpp @@ -4,10 +4,12 @@ #include "kapi/memory.hpp" #include "kapi/system.hpp" +#include #include #include #include +#include namespace kernel::acpi { @@ -80,8 +82,7 @@ namespace kernel::acpi } else { - kstd::println("[OS:ACPI] Found table: {}@{:#x}/{:#x} OEM: {} Rev: {}", table->signature(), linear_table_address, - physical_table_address, table->oem_id(), table->creator_revision()); + kstd::println("[OS:ACPI] Found '{}' ACPI table", table->signature()); m_tables.emplace(table->signature(), table); } } @@ -89,4 +90,14 @@ namespace kernel::acpi return !m_tables.empty(); } + auto manager::get_table(std::string_view signature) + -> kstd::observer_ptr + { + if (m_tables.contains(signature)) + { + return kstd::make_observer(m_tables.at(signature)); + } + return nullptr; + } + } // namespace kernel::acpi diff --git a/kernel/src/devices/cpu.cpp b/kernel/src/devices/cpu.cpp new file mode 100644 index 0000000..7147368 --- /dev/null +++ b/kernel/src/devices/cpu.cpp @@ -0,0 +1,97 @@ +#include "kernel/devices/cpu.hpp" + +#include "kapi/acpi.hpp" +#include "kapi/devices.hpp" + +#include +#include + +#include +#include +#include + +namespace kernel::devices +{ + + namespace + { + + auto process_madt(kstd::observer_ptr madt, kapi::devices::bus & cpu) -> void + { + auto static const core_major = kapi::devices::allocate_major_number(); + auto static const lapic_major = kapi::devices::allocate_major_number(); + + auto lapic_address = madt->local_interrupt_controller_address(); + + auto const * current = reinterpret_cast(madt.get()) + sizeof(kapi::acpi::madt_header); + auto const * end = reinterpret_cast(madt.get()) + madt->length(); + + auto bsp_found = false; + auto core_index = 0; + + while (current < end) + { + auto const * sub_table = reinterpret_cast(current); + if (sub_table->type() == 0) + { + auto const * local_apic = reinterpret_cast(sub_table); + if (local_apic->flags() & 0b11) + { + auto is_bsp = !bsp_found; + bsp_found = true; + + auto apic_id = local_apic->apic_id(); + auto core = kstd::make_unique(core_major, core_index, apic_id, is_bsp); + auto lapic = kapi::acpi::create_local_interrupt_controller(lapic_major, core_index, apic_id, lapic_address); + + core->add_child(std::move(lapic)); + cpu.add_child(std::move(core)); + + ++core_index; + } + } + + current += sub_table->length(); + } + + kstd::println("[OS:DEV] Discovered {} CPU cores", core_index); + } + + } // namespace + + cpu::core::core(std::size_t major_number, std::size_t minor_number, std::uint64_t hardware_id, bool is_bsp) + : kapi::devices::bus{major_number, minor_number, "cpu_core"} + , m_hardware_id{hardware_id} + , m_is_bsp{is_bsp} + {} + + auto cpu::core::hardware_id() const -> std::uint64_t + { + return m_hardware_id; + } + + auto cpu::core::is_bsp() const -> bool + { + return m_is_bsp; + } + + cpu::cpu(std::size_t major_number) + : kapi::devices::bus{major_number, 0, "cpu"} + {} + + auto cpu::probe() -> bool + { + auto madt = kapi::acpi::get_table("APIC"); + if (!madt) + { + kstd::println("[OS:DEV] Failed to find ACPI APIC table"); + return false; + } + + auto madt_header = static_cast(madt.get()); + process_madt(kstd::make_observer(madt_header), *this); + + return true; + } + +} // namespace kernel::devices \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 2eaa2d8..e704955 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,3 +1,4 @@ +#include "kapi/acpi.hpp" #include "kapi/boot_modules.hpp" #include "kapi/cio.hpp" #include "kapi/cpu.hpp" @@ -179,6 +180,15 @@ auto main() -> int kstd::println("[OS] Memory subsystem initialized."); kapi::system::memory_initialized(); + auto acpi_root_pointer = kapi::acpi::get_root_pointer(); + if (acpi_root_pointer && acpi_root_pointer->validate()) + { + if (kapi::acpi::init(*acpi_root_pointer)) + { + kstd::println("[OS] ACPI subsystem initialized."); + } + } + kapi::devices::init(); kstd::println("[OS] System root bus initialized."); -- cgit v1.2.3