From bd585306e31889ee4fce60abb79bc3b3a58e2b84 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 13:11:15 +0200 Subject: kapi: add basic ACPI support --- kapi/CMakeLists.txt | 8 ++- kapi/include/kapi/acpi.hpp | 64 ++++++++++++++++++++ kapi/src/acpi.cpp | 68 ++++++++++++++++++++++ .../include/kernel/memory/block_list_allocator.hpp | 10 ++-- libs/kstd/include/kstd/units | 7 ++- .../multiboot2/constants/information_id.hpp | 8 +-- libs/multiboot2/include/multiboot2/information.hpp | 43 +++++++++++++- .../include/multiboot2/information/data.hpp | 12 +++- 8 files changed, 205 insertions(+), 15 deletions(-) create mode 100644 kapi/include/kapi/acpi.hpp create mode 100644 kapi/src/acpi.cpp diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index c9aa23f..eeda158 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -1,4 +1,6 @@ -add_library("kapi" INTERFACE) +add_library("kapi" STATIC + "src/acpi.cpp" +) add_library("os::kapi" ALIAS "kapi") file(GLOB_RECURSE KAPI_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") @@ -10,11 +12,11 @@ target_sources("kapi" PUBLIC ${KAPI_HEADERS} ) -target_include_directories("kapi" INTERFACE +target_include_directories("kapi" PUBLIC "include" ) -target_link_libraries("kapi" INTERFACE +target_link_libraries("kapi" PUBLIC "libs::kstd" "gcc" diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp new file mode 100644 index 0000000..20e5e77 --- /dev/null +++ b/kapi/include/kapi/acpi.hpp @@ -0,0 +1,64 @@ +#ifndef TEACHOS_KAPI_ACPI_HPP +#define TEACHOS_KAPI_ACPI_HPP + +#include "kapi/memory.hpp" +#include + +#include +#include +#include +#include + +namespace kapi::acpi +{ + + //! @addtogroup kapi-acpi-api-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 + { + std::array signature; + std::uint32_t length; + std::uint8_t revision; + std::uint8_t checksum; + std::array oem_id; + std::array oem_table_id; + std::uint32_t oem_revision; + std::uint32_t creator_id; + std::uint32_t create_revision; + }; + +} // namespace kapi::acpi + +#endif diff --git a/kapi/src/acpi.cpp b/kapi/src/acpi.cpp new file mode 100644 index 0000000..aa0066d --- /dev/null +++ b/kapi/src/acpi.cpp @@ -0,0 +1,68 @@ +#include "kapi/acpi.hpp" + +#include "kapi/memory.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +namespace kapi::acpi +{ + + namespace + { + constexpr auto validate_checksum(std::span data) -> bool + { + auto sum = std::ranges::fold_left( + data, std::uint8_t{}, [](std::uint8_t acc, auto byte) { return static_cast(byte) + acc; }); + return sum == 0; + } + } // namespace + + auto root_system_description_pointer::oem_id() const noexcept -> std::string_view + { + return {m_oem_id.data(), m_oem_id.size()}; + } + + auto root_system_description_pointer::revision() const noexcept -> std::uint8_t + { + return m_revision; + } + + auto root_system_description_pointer::signature() const noexcept -> std::string_view + { + return {m_signature.data(), m_signature.size()}; + } + + auto root_system_description_pointer::table_address() const noexcept -> memory::physical_address + { + auto raw = std::bit_cast(m_rsdt_address); + return memory::physical_address{static_cast(raw)}; + } + + auto root_system_description_pointer::validate() const noexcept -> bool + { + return validate_checksum({reinterpret_cast(this), sizeof(root_system_description_pointer)}); + } + + auto extended_system_description_pointer::length() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{m_length}; + } + + auto extended_system_description_pointer::table_address() const noexcept -> memory::physical_address + { + return memory::physical_address{std::bit_cast(m_xsdt_address)}; + } + + auto extended_system_description_pointer::validate() const noexcept -> bool + { + return validate_checksum({reinterpret_cast(this), m_length}); + } + +}; // namespace kapi::acpi diff --git a/kernel/include/kernel/memory/block_list_allocator.hpp b/kernel/include/kernel/memory/block_list_allocator.hpp index f319097..de89f3b 100644 --- a/kernel/include/kernel/memory/block_list_allocator.hpp +++ b/kernel/include/kernel/memory/block_list_allocator.hpp @@ -43,10 +43,10 @@ namespace kernel::memory private: struct block_header final { - kstd::units::bytes usable_size; - bool free; - block_header * next; - block_header * prev; + kstd::units::bytes usable_size{}; + bool free{}; + block_header * next{}; + block_header * prev{}; }; //! The size of the metadata required for each allocated block. @@ -87,4 +87,4 @@ namespace kernel::memory } // namespace kernel::memory -#endif \ No newline at end of file +#endif diff --git a/libs/kstd/include/kstd/units b/libs/kstd/include/kstd/units index f6dcdb1..bc7e1b9 100644 --- a/libs/kstd/include/kstd/units +++ b/libs/kstd/include/kstd/units @@ -14,6 +14,11 @@ namespace kstd { using value_type = ValueType; + constexpr basic_unit() noexcept + : value{} + { + } + explicit constexpr basic_unit(value_type value) noexcept : value{value} {} @@ -142,4 +147,4 @@ namespace kstd } // namespace kstd -#endif \ No newline at end of file +#endif diff --git a/libs/multiboot2/include/multiboot2/constants/information_id.hpp b/libs/multiboot2/include/multiboot2/constants/information_id.hpp index be492eb..27c5300 100644 --- a/libs/multiboot2/include/multiboot2/constants/information_id.hpp +++ b/libs/multiboot2/include/multiboot2/constants/information_id.hpp @@ -57,10 +57,10 @@ namespace multiboot2 smbios_tables, //! A copy of RSDP as defined per ACPI 1.0 specification. - acpi_old_rsdp, + acpi_rsdp, - //! A copy of RSDP as defined per ACPI 2.0 or later specification. - acpi_new_rsdp, + //! A copy of XSDP as defined per ACPI 2.0 or later specification. + acpi_xsdp, //! The network information specified specified as per DHCP. networking_information, @@ -83,4 +83,4 @@ namespace multiboot2 } // namespace multiboot2 -#endif \ No newline at end of file +#endif diff --git a/libs/multiboot2/include/multiboot2/information.hpp b/libs/multiboot2/include/multiboot2/information.hpp index d2fac2e..a2ded56 100644 --- a/libs/multiboot2/include/multiboot2/information.hpp +++ b/libs/multiboot2/include/multiboot2/information.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -128,6 +129,26 @@ namespace multiboot2 return kstd::units::bytes{end_address - start_address}; } }; + + struct acpi_rsdp : vla_tag + { + using vla_tag::vla_tag; + + [[nodiscard]] auto pointer() const noexcept -> range_type + { + return {data(), size()}; + } + }; + + struct acpi_xsdp : vla_tag + { + using vla_tag::vla_tag; + + [[nodiscard]] auto pointer() const noexcept -> range_type + { + return {data(), size()}; + } + }; struct information_view { @@ -245,6 +266,26 @@ namespace multiboot2 std::views::transform(transform_module); } + [[nodiscard]] auto maybe_acpi_rsdp() const noexcept -> std::optional + { + return get(); + } + + [[nodiscard]] auto acpi_rsdp() const noexcept -> acpi_rsdp + { + return maybe_acpi_rsdp().value(); + } + + [[nodiscard]] auto maybe_acpi_xsdp() const noexcept -> std::optional + { + return get(); + } + + [[nodiscard]] auto acpi_xsdp() const noexcept -> acpi_xsdp + { + return maybe_acpi_xsdp().value(); + } + private: template [[nodiscard]] constexpr auto get() const noexcept -> std::optional @@ -264,4 +305,4 @@ namespace multiboot2 } // namespace multiboot2 -#endif \ No newline at end of file +#endif diff --git a/libs/multiboot2/include/multiboot2/information/data.hpp b/libs/multiboot2/include/multiboot2/information/data.hpp index 3b07d20..315eb39 100644 --- a/libs/multiboot2/include/multiboot2/information/data.hpp +++ b/libs/multiboot2/include/multiboot2/information/data.hpp @@ -167,8 +167,18 @@ namespace multiboot2 std::uint32_t end_address; }; + //! A copy of the ACPI RSDP + struct acpi_rsdp : tag_data + { + }; + + //! A copy of the ACPI XSDP + struct acpi_xsdp : tag_data + { + }; + } // namespace data } // namespace multiboot2 -#endif \ No newline at end of file +#endif -- cgit v1.2.3 From 6e54333bcc08ddd8dbcb6aa9c3404001c309ec74 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 13:27:22 +0200 Subject: kapi: move independent implementation to kernel --- kapi/CMakeLists.txt | 8 ++---- kapi/include/kapi/acpi.hpp | 2 +- kapi/src/acpi.cpp | 68 ---------------------------------------------- kernel/CMakeLists.txt | 1 + kernel/kapi/acpi.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 74 deletions(-) delete mode 100644 kapi/src/acpi.cpp create mode 100644 kernel/kapi/acpi.cpp diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index eeda158..c9aa23f 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -1,6 +1,4 @@ -add_library("kapi" STATIC - "src/acpi.cpp" -) +add_library("kapi" INTERFACE) add_library("os::kapi" ALIAS "kapi") file(GLOB_RECURSE KAPI_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") @@ -12,11 +10,11 @@ target_sources("kapi" PUBLIC ${KAPI_HEADERS} ) -target_include_directories("kapi" PUBLIC +target_include_directories("kapi" INTERFACE "include" ) -target_link_libraries("kapi" PUBLIC +target_link_libraries("kapi" INTERFACE "libs::kstd" "gcc" diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index 20e5e77..1068921 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -12,7 +12,7 @@ namespace kapi::acpi { - //! @addtogroup kapi-acpi-api-defined + //! @addtogroup kapi-acpi-kernel-defined //! @{ struct [[gnu::packed]] root_system_description_pointer diff --git a/kapi/src/acpi.cpp b/kapi/src/acpi.cpp deleted file mode 100644 index aa0066d..0000000 --- a/kapi/src/acpi.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "kapi/acpi.hpp" - -#include "kapi/memory.hpp" - -#include - -#include -#include -#include -#include -#include -#include - -namespace kapi::acpi -{ - - namespace - { - constexpr auto validate_checksum(std::span data) -> bool - { - auto sum = std::ranges::fold_left( - data, std::uint8_t{}, [](std::uint8_t acc, auto byte) { return static_cast(byte) + acc; }); - return sum == 0; - } - } // namespace - - auto root_system_description_pointer::oem_id() const noexcept -> std::string_view - { - return {m_oem_id.data(), m_oem_id.size()}; - } - - auto root_system_description_pointer::revision() const noexcept -> std::uint8_t - { - return m_revision; - } - - auto root_system_description_pointer::signature() const noexcept -> std::string_view - { - return {m_signature.data(), m_signature.size()}; - } - - auto root_system_description_pointer::table_address() const noexcept -> memory::physical_address - { - auto raw = std::bit_cast(m_rsdt_address); - return memory::physical_address{static_cast(raw)}; - } - - auto root_system_description_pointer::validate() const noexcept -> bool - { - return validate_checksum({reinterpret_cast(this), sizeof(root_system_description_pointer)}); - } - - auto extended_system_description_pointer::length() const noexcept -> kstd::units::bytes - { - return kstd::units::bytes{m_length}; - } - - auto extended_system_description_pointer::table_address() const noexcept -> memory::physical_address - { - return memory::physical_address{std::bit_cast(m_xsdt_address)}; - } - - auto extended_system_description_pointer::validate() const noexcept -> bool - { - return validate_checksum({reinterpret_cast(this), m_length}); - } - -}; // namespace kapi::acpi diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 9868eb9..ab77467 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -1,5 +1,6 @@ add_library("kernel_objs" OBJECT # Platform-independent KAPI implementation + "kapi/acpi.cpp" "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp new file mode 100644 index 0000000..aa0066d --- /dev/null +++ b/kernel/kapi/acpi.cpp @@ -0,0 +1,68 @@ +#include "kapi/acpi.hpp" + +#include "kapi/memory.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +namespace kapi::acpi +{ + + namespace + { + constexpr auto validate_checksum(std::span data) -> bool + { + auto sum = std::ranges::fold_left( + data, std::uint8_t{}, [](std::uint8_t acc, auto byte) { return static_cast(byte) + acc; }); + return sum == 0; + } + } // namespace + + auto root_system_description_pointer::oem_id() const noexcept -> std::string_view + { + return {m_oem_id.data(), m_oem_id.size()}; + } + + auto root_system_description_pointer::revision() const noexcept -> std::uint8_t + { + return m_revision; + } + + auto root_system_description_pointer::signature() const noexcept -> std::string_view + { + return {m_signature.data(), m_signature.size()}; + } + + auto root_system_description_pointer::table_address() const noexcept -> memory::physical_address + { + auto raw = std::bit_cast(m_rsdt_address); + return memory::physical_address{static_cast(raw)}; + } + + auto root_system_description_pointer::validate() const noexcept -> bool + { + return validate_checksum({reinterpret_cast(this), sizeof(root_system_description_pointer)}); + } + + auto extended_system_description_pointer::length() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{m_length}; + } + + auto extended_system_description_pointer::table_address() const noexcept -> memory::physical_address + { + return memory::physical_address{std::bit_cast(m_xsdt_address)}; + } + + auto extended_system_description_pointer::validate() const noexcept -> bool + { + return validate_checksum({reinterpret_cast(this), m_length}); + } + +}; // namespace kapi::acpi -- cgit v1.2.3 From 3dcd14a0570fef3bcc68d7df42fe3ff4cd496f93 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 14:47:37 +0200 Subject: kapi: hook ACPI initialization up to boot process --- .vscode/settings.json | 6 +++- arch/x86_64/kapi/devices.cpp | 32 +++++++++++++++++++- kapi/include/kapi/acpi.hpp | 44 +++++++++++++++++++-------- kapi/include/kapi/memory/layout.hpp | 12 ++++++++ kernel/CMakeLists.txt | 1 + kernel/include/kernel/acpi/manager.hpp | 26 ++++++++++++++++ kernel/kapi/acpi.cpp | 32 ++++++++++++++++---- kernel/src/acpi/manager.cpp | 54 ++++++++++++++++++++++++++++++++++ 8 files changed, 186 insertions(+), 21 deletions(-) create mode 100644 kernel/include/kernel/acpi/manager.hpp create mode 100644 kernel/src/acpi/manager.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 4271526..6ea3c79 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -32,6 +32,7 @@ "crtp", "efer", "functors", + "hhdm", "idtr", "initializable", "interprocedural", @@ -43,6 +44,7 @@ "malloc", "memcmp", "memset", + "mmio", "multiboot", "nodiscard", "nolintnextline", @@ -51,12 +53,14 @@ "raii", "rdmsr", "RSDP", + "rsdt", "rvalues", "stringview", "sysret", "teachos", "undelegated", - "wrmsr" + "wrmsr", + "xsdp" ], "testMate.cpp.debug.configTemplate": { "type": "cppdbg", diff --git a/arch/x86_64/kapi/devices.cpp b/arch/x86_64/kapi/devices.cpp index b15503d..e1773e7 100644 --- a/arch/x86_64/kapi/devices.cpp +++ b/arch/x86_64/kapi/devices.cpp @@ -1,5 +1,9 @@ #include "kapi/devices.hpp" +#include "kapi/acpi.hpp" +#include "kapi/boot.hpp" + +#include "arch/boot/boot.hpp" #include "arch/bus/isa.hpp" #include "arch/devices/legacy_pit.hpp" @@ -19,7 +23,33 @@ namespace kapi::devices auto init_platform_devices() -> void { - kstd::println("[x86_64:devices] Initializing ISA bus..."); + auto const & mbi = 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."); + } + + kstd::println("[x86_64:DEV] Initializing ISA bus..."); auto isa_major_number = kapi::devices::allocate_major_number(); auto isa_bus = kstd::make_unique(isa_major_number); diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index 1068921..d5e3c87 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -2,16 +2,36 @@ #define TEACHOS_KAPI_ACPI_HPP #include "kapi/memory.hpp" + #include #include #include #include +#include #include namespace kapi::acpi { + //! @addtogroup kapi-acpi + //! @{ + + struct [[gnu::packed]] system_description_table_header + { + std::array signature; + std::uint32_t length; + std::uint8_t revision; + std::uint8_t checksum; + std::array oem_id; + std::array oem_table_id; + std::uint32_t oem_revision; + std::uint32_t creator_id; + std::uint32_t create_revision; + }; + + //! @} + //! @addtogroup kapi-acpi-kernel-defined //! @{ @@ -44,20 +64,18 @@ namespace kapi::acpi [[maybe_unused]] std::array m_reserved; }; - //! @} + //! Initialize the ACPI subsystem and discover the available tables. + //! + //! @return true iff. a valid system description tabled was found, false otherwise. + auto init(root_system_description_pointer const & sdp) -> bool; - struct [[gnu::packed]] system_description_table_header - { - std::array signature; - std::uint32_t length; - std::uint8_t revision; - std::uint8_t checksum; - std::array oem_id; - std::array oem_table_id; - std::uint32_t oem_revision; - std::uint32_t creator_id; - std::uint32_t create_revision; - }; + //! Validate and ACPI entity checksum. + //! + //! @param data The data to validate the checksum of. + //! @return true iff. the checksum is valid, false otherwise. + auto validate_checksum(std::span data) -> bool; + + //! @} } // namespace kapi::acpi diff --git a/kapi/include/kapi/memory/layout.hpp b/kapi/include/kapi/memory/layout.hpp index 157f41e..26b48d8 100644 --- a/kapi/include/kapi/memory/layout.hpp +++ b/kapi/include/kapi/memory/layout.hpp @@ -37,6 +37,18 @@ namespace kapi::memory //! The linear base address of the loaded kernel image. constexpr auto kernel_base = linear_address{0xffff'ffff'8000'0000uz}; + //! Convert a physical address to a linear address using the higher-half direct map. + constexpr auto hhdm_to_linear(physical_address address) -> linear_address + { + return linear_address{address.raw() + higher_half_direct_map_base.raw()}; + } + + //! Convert a linear address in the higher-half direct map to a physical address. + constexpr auto hhdm_to_physical(linear_address address) -> physical_address + { + return physical_address{address.raw() - higher_half_direct_map_base.raw()}; + } + } // namespace kapi::memory #endif \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index ab77467..fc01723 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -14,6 +14,7 @@ add_library("kernel_objs" OBJECT "kstd/print.cpp" # Kernel Implementation + "src/acpi/manager.cpp" "src/memory/bitmap_allocator.cpp" "src/memory/block_list_allocator.cpp" "src/memory.cpp" diff --git a/kernel/include/kernel/acpi/manager.hpp b/kernel/include/kernel/acpi/manager.hpp new file mode 100644 index 0000000..fc76685 --- /dev/null +++ b/kernel/include/kernel/acpi/manager.hpp @@ -0,0 +1,26 @@ +#ifndef TEACHOS_KERNEL_ACPI_MANAGER_HPP +#define TEACHOS_KERNEL_ACPI_MANAGER_HPP + +#include "kapi/acpi.hpp" + +#include + +namespace kernel::acpi +{ + + struct manager + { + explicit manager(kapi::acpi::root_system_description_pointer const & sdp); + + auto load_tables() -> bool; + + private: + kapi::acpi::root_system_description_pointer const * m_sdp{}; + kapi::acpi::system_description_table_header const * m_rsdt{}; + kstd::vector m_tables{}; + bool m_extended{}; + }; + +} // namespace kernel::acpi + +#endif diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp index aa0066d..787bcff 100644 --- a/kernel/kapi/acpi.cpp +++ b/kernel/kapi/acpi.cpp @@ -1,13 +1,18 @@ #include "kapi/acpi.hpp" #include "kapi/memory.hpp" +#include "kapi/system.hpp" + +#include "kernel/acpi/manager.hpp" #include #include +#include #include #include #include +#include #include #include @@ -16,12 +21,7 @@ namespace kapi::acpi namespace { - constexpr auto validate_checksum(std::span data) -> bool - { - auto sum = std::ranges::fold_left( - data, std::uint8_t{}, [](std::uint8_t acc, auto byte) { return static_cast(byte) + acc; }); - return sum == 0; - } + auto constinit manager = std::optional{}; } // namespace auto root_system_description_pointer::oem_id() const noexcept -> std::string_view @@ -65,4 +65,24 @@ namespace kapi::acpi return validate_checksum({reinterpret_cast(this), m_length}); } + 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!"); + } + + manager.emplace(sdp); + return manager->load_tables(); + } + + auto validate_checksum(std::span data) -> bool + { + auto sum = std::ranges::fold_left(data, std::uint8_t{}, [](auto acc, auto byte) { + return static_cast(acc + static_cast(byte)); + }); + return sum == 0; + } + }; // namespace kapi::acpi diff --git a/kernel/src/acpi/manager.cpp b/kernel/src/acpi/manager.cpp new file mode 100644 index 0000000..6a17920 --- /dev/null +++ b/kernel/src/acpi/manager.cpp @@ -0,0 +1,54 @@ +#include "kernel/acpi/manager.hpp" + +#include "kapi/acpi.hpp" +#include "kapi/memory.hpp" +#include "kapi/system.hpp" + +#include + +namespace kernel::acpi +{ + + manager::manager(kapi::acpi::root_system_description_pointer const & sdp) + : m_sdp{&sdp} + { + if (m_sdp->signature() != "RSD PTR ") + { + kapi::system::panic("[OS:ACPI] Invalid RSDP signature!"); + } + + if (m_sdp->revision() >= 2) + { + auto const xsdp = static_cast(m_sdp); + if (!xsdp->validate()) + { + kapi::system::panic("[OS:ACPI] Invalid XSDP signature!"); + } + auto physical_extended_table_address = xsdp->table_address(); + auto linear_extended_table_address = kapi::memory::hhdm_to_linear(physical_extended_table_address); + m_rsdt = static_cast(linear_extended_table_address); + m_extended = true; + } + else + { + if (!m_sdp->validate()) + { + kapi::system::panic("[OS:ACPI] Invalid RSDP checksum!"); + } + auto physical_root_table_address = m_sdp->table_address(); + auto linear_root_table_address = kapi::memory::hhdm_to_linear(physical_root_table_address); + m_rsdt = static_cast(linear_root_table_address); + } + } + + auto manager::load_tables() -> bool + { + if (!kapi::acpi::validate_checksum({reinterpret_cast(m_rsdt), m_rsdt->length})) + { + kapi::system::panic("[OS:ACPI] Invalid RSDT checksum!"); + } + + return false; + } + +} // namespace kernel::acpi -- cgit v1.2.3 From 4d938cd31a35cd4322fe914edd568faa5391c9c2 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 15:10:04 +0200 Subject: kernel/acpi: implement basic table discovery --- kapi/include/kapi/acpi.hpp | 29 +++++++++++++++------- kernel/include/kernel/acpi/manager.hpp | 5 +++- kernel/kapi/acpi.cpp | 45 ++++++++++++++++++++++++++++++++++ kernel/src/acpi/manager.cpp | 42 +++++++++++++++++++++++++++++-- libs/kstd/include/kstd/flat_map | 18 ++++++++++++++ 5 files changed, 127 insertions(+), 12 deletions(-) diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index d5e3c87..11068d1 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -19,15 +19,26 @@ namespace kapi::acpi struct [[gnu::packed]] system_description_table_header { - std::array signature; - std::uint32_t length; - std::uint8_t revision; - std::uint8_t checksum; - std::array oem_id; - std::array oem_table_id; - std::uint32_t oem_revision; - std::uint32_t creator_id; - std::uint32_t create_revision; + [[nodiscard]] auto checksum() const noexcept -> std::uint8_t; + [[nodiscard]] auto creator_revision() const noexcept -> std::uint32_t; + [[nodiscard]] auto creator_id() const noexcept -> std::uint32_t; + [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; + [[nodiscard]] auto oem_id() const noexcept -> std::string_view; + [[nodiscard]] auto oem_revision() const noexcept -> std::uint32_t; + [[nodiscard]] auto oem_table_id() const noexcept -> std::string_view; + [[nodiscard]] auto revision() const noexcept -> std::uint8_t; + [[nodiscard]] auto signature() const noexcept -> std::string_view; + + private: + std::array m_signature; + std::uint32_t m_length; + std::uint8_t m_revision; + std::uint8_t m_checksum; + std::array m_oem_id; + std::array m_oem_table_id; + std::uint32_t m_oem_revision; + std::uint32_t m_creator_id; + std::uint32_t m_creator_revision; }; //! @} diff --git a/kernel/include/kernel/acpi/manager.hpp b/kernel/include/kernel/acpi/manager.hpp index fc76685..437d4c8 100644 --- a/kernel/include/kernel/acpi/manager.hpp +++ b/kernel/include/kernel/acpi/manager.hpp @@ -3,8 +3,11 @@ #include "kapi/acpi.hpp" +#include #include +#include + namespace kernel::acpi { @@ -17,7 +20,7 @@ namespace kernel::acpi private: kapi::acpi::root_system_description_pointer const * m_sdp{}; kapi::acpi::system_description_table_header const * m_rsdt{}; - kstd::vector m_tables{}; + kstd::flat_map m_tables{}; bool m_extended{}; }; diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp index 787bcff..998d7a0 100644 --- a/kernel/kapi/acpi.cpp +++ b/kernel/kapi/acpi.cpp @@ -65,6 +65,51 @@ namespace kapi::acpi return validate_checksum({reinterpret_cast(this), m_length}); } + [[nodiscard]] auto system_description_table_header::checksum() const noexcept -> std::uint8_t + { + return m_checksum; + } + + [[nodiscard]] auto system_description_table_header::creator_revision() const noexcept -> std::uint32_t + { + return m_creator_revision; + } + + [[nodiscard]] auto system_description_table_header::creator_id() const noexcept -> std::uint32_t + { + return m_creator_id; + } + + [[nodiscard]] auto system_description_table_header::length() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{m_length}; + } + + [[nodiscard]] auto system_description_table_header::oem_id() const noexcept -> std::string_view + { + return {m_oem_id.data(), m_oem_id.size()}; + } + + [[nodiscard]] auto system_description_table_header::oem_revision() const noexcept -> std::uint32_t + { + return m_oem_revision; + } + + [[nodiscard]] auto system_description_table_header::oem_table_id() const noexcept -> std::string_view + { + return {m_oem_table_id.data(), m_oem_table_id.size()}; + } + + [[nodiscard]] auto system_description_table_header::revision() const noexcept -> std::uint8_t + { + return m_revision; + } + + [[nodiscard]] auto system_description_table_header::signature() const noexcept -> std::string_view + { + return {m_signature.data(), m_signature.size()}; + } + auto init(root_system_description_pointer const & sdp) -> bool { auto static constinit initialized = std::atomic_flag{}; diff --git a/kernel/src/acpi/manager.cpp b/kernel/src/acpi/manager.cpp index 6a17920..180d458 100644 --- a/kernel/src/acpi/manager.cpp +++ b/kernel/src/acpi/manager.cpp @@ -4,7 +4,10 @@ #include "kapi/memory.hpp" #include "kapi/system.hpp" +#include + #include +#include namespace kernel::acpi { @@ -43,12 +46,47 @@ namespace kernel::acpi auto manager::load_tables() -> bool { - if (!kapi::acpi::validate_checksum({reinterpret_cast(m_rsdt), m_rsdt->length})) + if (!kapi::acpi::validate_checksum({reinterpret_cast(m_rsdt), m_rsdt->length().value})) { kapi::system::panic("[OS:ACPI] Invalid RSDT checksum!"); } - return false; + auto entry_size = m_extended ? sizeof(std::uint64_t) : sizeof(std::uint32_t); + auto entry_count = (m_rsdt->length().value - sizeof(kapi::acpi::system_description_table_header)) / entry_size; + auto entries_base = + reinterpret_cast(m_rsdt) + sizeof(kapi::acpi::system_description_table_header); + + for (std::size_t i = 0; i < entry_count; ++i) + { + auto physical_table_address = kapi::memory::physical_address{}; + + if (m_extended) + { + auto entry = reinterpret_cast(entries_base + (i * entry_size)); + physical_table_address = kapi::memory::physical_address{*entry}; + } + else + { + auto entry = reinterpret_cast(entries_base + (i * entry_size)); + physical_table_address = kapi::memory::physical_address{*entry}; + } + + auto linear_table_address = kapi::memory::hhdm_to_linear(physical_table_address); + auto table = static_cast(linear_table_address); + + if (!kapi::acpi::validate_checksum({reinterpret_cast(table), table->length().value})) + { + kstd::println(kstd::print_sink::stderr, "[OS:ACPI] Invalid table checksum!"); + } + 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()); + m_tables.emplace(table->signature(), table); + } + } + + return !m_tables.empty(); } } // namespace kernel::acpi diff --git a/libs/kstd/include/kstd/flat_map b/libs/kstd/include/kstd/flat_map index 6ffbbcf..3b13754 100644 --- a/libs/kstd/include/kstd/flat_map +++ b/libs/kstd/include/kstd/flat_map @@ -205,6 +205,24 @@ namespace kstd return const_reverse_iterator{cbegin()}; } + //! Check whether this flat map is empty or not + [[nodiscard]] auto empty() const noexcept -> bool + { + return m_containers.keys.empty(); + } + + //! Get the number of elements in this flat map. + [[nodiscard]] auto size() const noexcept -> size_type + { + return m_containers.keys.size(); + } + + //! Get the maximum number of elements possible in this flat map + [[nodiscard]] auto max_size() const noexcept -> size_type + { + return std::min(m_containers.keys.max_size(), m_containers.values.max_size()); + } + //! Try to insert a new key-value pair into the map. //! //! @param args Arguments to use for constructing the key-value pair. -- cgit v1.2.3 From c18feddd51d1a1398d1245229c5f889dd40554b3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 15:54:21 +0200 Subject: x86_64/devices: extract initialization code --- arch/x86_64/CMakeLists.txt | 1 + arch/x86_64/include/arch/devices/init.hpp | 12 ++++++ arch/x86_64/kapi/devices.cpp | 57 ++----------------------- arch/x86_64/src/devices/init.cpp | 69 +++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 54 deletions(-) create mode 100644 arch/x86_64/include/arch/devices/init.hpp create mode 100644 arch/x86_64/src/devices/init.cpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 83cae0b..f77eec5 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -38,6 +38,7 @@ target_sources("x86_64" PRIVATE "src/debug/qemu_output.cpp" # Devices + "src/devices/init.cpp" "src/devices/legacy_pit.cpp" # Memory management diff --git a/arch/x86_64/include/arch/devices/init.hpp b/arch/x86_64/include/arch/devices/init.hpp new file mode 100644 index 0000000..c5fbf37 --- /dev/null +++ b/arch/x86_64/include/arch/devices/init.hpp @@ -0,0 +1,12 @@ +#ifndef TEACHOS_ARCH_X86_64_DEVICES_INIT_HPP +#define TEACHOS_ARCH_X86_64_DEVICES_INIT_HPP + +namespace arch::devices +{ + + auto init_acpi_devices() -> void; + auto init_legacy_devices() -> void; + +} // namespace arch::devices + +#endif \ No newline at end of file diff --git a/arch/x86_64/kapi/devices.cpp b/arch/x86_64/kapi/devices.cpp index e1773e7..47c7f8c 100644 --- a/arch/x86_64/kapi/devices.cpp +++ b/arch/x86_64/kapi/devices.cpp @@ -1,65 +1,14 @@ #include "kapi/devices.hpp" -#include "kapi/acpi.hpp" -#include "kapi/boot.hpp" - -#include "arch/boot/boot.hpp" -#include "arch/bus/isa.hpp" -#include "arch/devices/legacy_pit.hpp" - -#include -#include - -#include -#include +#include "arch/devices/init.hpp" namespace kapi::devices { - namespace - { - constexpr auto pit_frequency_in_hz = std::uint32_t{100u}; - } - auto init_platform_devices() -> void { - auto const & mbi = 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."); - } - - kstd::println("[x86_64:DEV] Initializing ISA bus..."); - - auto isa_major_number = kapi::devices::allocate_major_number(); - auto isa_bus = kstd::make_unique(isa_major_number); - - auto pit_major_number = kapi::devices::allocate_major_number(); - auto pit = kstd::make_unique(pit_major_number, pit_frequency_in_hz); - isa_bus->add_child(std::move(pit)); - - auto & root_bus = get_root_bus(); - root_bus.add_child(std::move(isa_bus)); + arch::devices::init_acpi_devices(); + arch::devices::init_legacy_devices(); } } // namespace kapi::devices \ No newline at end of file diff --git a/arch/x86_64/src/devices/init.cpp b/arch/x86_64/src/devices/init.cpp new file mode 100644 index 0000000..1099ad6 --- /dev/null +++ b/arch/x86_64/src/devices/init.cpp @@ -0,0 +1,69 @@ +#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" + +#include +#include + +#include +#include + +namespace arch::devices +{ + + namespace + { + 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_legacy_devices() -> void + { + kstd::println("[x86_64:DEV] Initializing ISA bus..."); + + auto isa_major_number = kapi::devices::allocate_major_number(); + auto isa_bus = kstd::make_unique(isa_major_number); + + auto pit_major_number = kapi::devices::allocate_major_number(); + auto pit = kstd::make_unique(pit_major_number, pit_frequency_in_hz); + isa_bus->add_child(std::move(pit)); + + auto & root_bus = kapi::devices::get_root_bus(); + root_bus.add_child(std::move(isa_bus)); + } + +} // namespace arch::devices -- cgit v1.2.3 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_poi