diff options
| author | Felix Morgner <felix.morgner@ost.ch> | 2026-04-15 08:55:03 +0200 |
|---|---|---|
| committer | Felix Morgner <felix.morgner@ost.ch> | 2026-04-15 08:55:03 +0200 |
| commit | 1fa31688a0e237dec1170dcea9e8f0a0571a25e5 (patch) | |
| tree | 47670a4f05f50d45be078e6429950ff5c0b3ca48 /libs/acpi | |
| parent | eacc1becd1308a01a7ffcddf7c8910c8dc708939 (diff) | |
| download | teachos-1fa31688a0e237dec1170dcea9e8f0a0571a25e5.tar.xz teachos-1fa31688a0e237dec1170dcea9e8f0a0571a25e5.zip | |
acpi: add basic MADT tests
Diffstat (limited to 'libs/acpi')
| -rw-r--r-- | libs/acpi/CMakeLists.txt | 34 | ||||
| -rw-r--r-- | libs/acpi/acpi/madt.cpp | 10 | ||||
| -rw-r--r-- | libs/acpi/acpi/madt.hpp | 38 | ||||
| -rw-r--r-- | libs/acpi/acpi/madt.test.cpp | 55 | ||||
| -rw-r--r-- | libs/acpi/acpi/sdt.cpp | 7 | ||||
| -rw-r--r-- | libs/acpi/acpi/sdt.hpp | 8 | ||||
| -rw-r--r-- | libs/acpi/test_data/basic_madt.asl | 29 | ||||
| -rw-r--r-- | libs/acpi/test_data/basic_rsdt.asl | 26 | ||||
| -rw-r--r-- | libs/acpi/test_data/tables.S | 14 | ||||
| -rw-r--r-- | libs/acpi/test_data/tables.hpp | 25 |
10 files changed, 241 insertions, 5 deletions
diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index b8face4..8ace42d 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -1,3 +1,11 @@ +cmake_minimum_required(VERSION "3.27.0") + +project("acpi" + DESCRIPTION "An ACPI parsing library" + VERSION "0.0.1" + LANGUAGES ASM CXX +) + add_library("acpi" STATIC) add_library("libs::acpi" ALIAS "acpi") @@ -29,8 +37,34 @@ target_link_libraries("acpi" PUBLIC ) if(NOT CMAKE_CROSSCOMPILING) + find_program(IASL_EXE NAMES "iasl" REQUIRED) + + set(TEST_TABLES + "basic_madt" + "basic_rsdt" + ) + + foreach(TABLE IN LISTS TEST_TABLES) + add_custom_command(OUTPUT "test_data/${TABLE}.aml" + COMMAND "${IASL_EXE}" -p "test_data/${TABLE}.aml" "${CMAKE_CURRENT_SOURCE_DIR}/test_data/${TABLE}.asl" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/test_data/${TABLE}.asl" + COMMENT "Compiling test_data/${TABLE}.asl" + VERBATIM + ) + list(APPEND GENERATED_TABLE_BLOBS "${CMAKE_CURRENT_BINARY_DIR}/test_data/${TABLE}.aml") + endforeach() + + set_source_files_properties("test_data/tables.S" PROPERTIES OBJECT_DEPENDS "${GENERATED_TABLE_BLOBS}") + add_executable("acpi_tests" + "acpi/madt.test.cpp" "acpi/pointers.test.cpp" + + "test_data/tables.S" + ) + + target_include_directories("acpi_tests" PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/test_data" ) target_link_libraries("acpi_tests" PRIVATE diff --git a/libs/acpi/acpi/madt.cpp b/libs/acpi/acpi/madt.cpp index 40289cf..6a62f07 100644 --- a/libs/acpi/acpi/madt.cpp +++ b/libs/acpi/acpi/madt.cpp @@ -1,3 +1,4 @@ +#include <acpi/acpi.hpp> #include <acpi/madt.hpp> #include <cstddef> @@ -16,9 +17,14 @@ namespace acpi return m_flags; } - auto madt_entry::type() const noexcept -> types + auto madt::validate() const noexcept -> bool { - return static_cast<types>(m_type); + return signature() == madt_table_signature && sdt::validate(); + } + + auto madt_entry::type() const noexcept -> enum type + { + return static_cast<enum type>(m_type); } auto madt_entry::length() const noexcept -> std::size_t diff --git a/libs/acpi/acpi/madt.hpp b/libs/acpi/acpi/madt.hpp index f680430..8b75f58 100644 --- a/libs/acpi/acpi/madt.hpp +++ b/libs/acpi/acpi/madt.hpp @@ -4,6 +4,7 @@ // IWYU pragma: private, include <acpi/acpi.hpp> #include <kstd/ext/bitfield_enum> +#include <kstd/os/error.hpp> #include <kstd/units> #include <acpi/sdt.hpp> @@ -11,6 +12,7 @@ #include <cstddef> #include <cstdint> +#include <ranges> #include <type_traits> namespace acpi @@ -18,7 +20,8 @@ namespace acpi struct [[gnu::packed]] madt_entry { - enum struct types : std::uint8_t + //! The type of an MADT entry. + enum struct type : std::uint8_t { processor_local_apic, io_apic, @@ -29,28 +32,55 @@ namespace acpi processor_local_x2_apic, }; - [[nodiscard]] auto type() const noexcept -> types; + //! Get the type of this entry. + [[nodiscard]] auto type() const noexcept -> enum type; + + //! Get the length of this entry. [[nodiscard]] auto length() const noexcept -> std::size_t; + //! Cast this entry to the given concrete entry type. + //! + //! @warning This function will terminate execution if the desired target type does not match up with this entry's + //! actual type. + template<typename EntryType> + [[nodiscard]] auto as() const -> EntryType const & + { + if (type() != EntryType::this_type) + { + kstd::os::panic("Invalid cast"); + } + return reinterpret_cast<EntryType const &>(*this); + } + private: std::uint8_t m_type; std::uint8_t m_length; }; - //! The common header for all + //! The Multiple APIC Description Table (MADT) struct [[gnu::packed]] madt : sdt { using iterator = sdt_iterator<madt_entry const>; using const_iterator = iterator; + //! Get the physical address of the local interrupt controllers described by this entry. [[nodiscard]] auto local_interrupt_controller_address() const noexcept -> std::uintptr_t; + [[nodiscard]] auto flags() const noexcept -> std::uint32_t; + [[nodiscard]] auto validate() const noexcept -> bool; [[nodiscard]] auto begin() const noexcept -> iterator; [[nodiscard]] auto cbegin() const noexcept -> iterator; [[nodiscard]] auto end() const noexcept -> iterator; [[nodiscard]] auto cend() const noexcept -> iterator; + template<typename EntryType> + [[nodiscard]] auto only() const noexcept + { + return *this | std::views::filter([](auto const & e) { return e.type() == EntryType::this_type; }) | + std::views::transform([](auto const & e) { return e.template as<EntryType>(); }); + } + private: std::uint32_t m_local_interrupt_controller_address; std::uint32_t m_flags; @@ -58,6 +88,8 @@ namespace acpi struct [[gnu::packed]] processor_local_apic : madt_entry { + constexpr auto static this_type = type::processor_local_apic; + enum struct flags : std::uint32_t { processor_enabled = 1, diff --git a/libs/acpi/acpi/madt.test.cpp b/libs/acpi/acpi/madt.test.cpp new file mode 100644 index 0000000..8926192 --- /dev/null +++ b/libs/acpi/acpi/madt.test.cpp @@ -0,0 +1,55 @@ +#include <kstd/units> + +#include <acpi/madt.hpp> +#include <catch2/catch_test_macros.hpp> +#include <test_data/tables.hpp> + +#include <iterator> + +SCENARIO("MADT parsing", "[madt]") +{ + GIVEN("The basic compiled MADT containing a single LAPIC entry and the default x86 LAPIC address") + { + auto data = acpi::test_data::tables::basic_madt(); + + WHEN("parsing the table") + { + auto madt = reinterpret_cast<acpi::madt const *>(data.data()); + + THEN("the signature is correct") + { + REQUIRE(madt->signature() == "APIC"); + } + + THEN("validate returns true") + { + REQUIRE(madt->validate()); + } + + THEN("there is a single entry in the table") + { + REQUIRE(std::distance(madt->begin(), madt->end()) == 1); + } + + THEN("the LAPIC address is 0xfee00000") + { + REQUIRE(madt->local_interrupt_controller_address() == 0xfee0'0000); + } + + THEN("the length is sizeof(madt) + sizeof(processor_local_apic)") + { + REQUIRE(madt->length() == kstd::type_size<acpi::madt> + kstd::type_size<acpi::processor_local_apic>); + } + + THEN("the first entry has type processor_local_apic") + { + REQUIRE(madt->cbegin()->type() == acpi::madt_entry::type::processor_local_apic); + } + + THEN("`only` can be used to get a view of all processor_local_apic entries") + { + REQUIRE(std::ranges::distance(madt->only<acpi::processor_local_apic>()) == 1); + } + } + } +} diff --git a/libs/acpi/acpi/sdt.cpp b/libs/acpi/acpi/sdt.cpp index c2b9d68..6c6cb47 100644 --- a/libs/acpi/acpi/sdt.cpp +++ b/libs/acpi/acpi/sdt.cpp @@ -1,7 +1,9 @@ #include <kstd/units> +#include <acpi/acpi.hpp> #include <acpi/sdt.hpp> +#include <cstddef> #include <cstdint> #include <string_view> @@ -48,4 +50,9 @@ namespace acpi return {m_signature.data(), m_signature.size()}; } + auto sdt::validate() const noexcept -> bool + { + return acpi::validate_checksum({reinterpret_cast<std::byte const *>(this), length().value}); + } + } // namespace acpi diff --git a/libs/acpi/acpi/sdt.hpp b/libs/acpi/acpi/sdt.hpp index 9e5ec89..72b3896 100644 --- a/libs/acpi/acpi/sdt.hpp +++ b/libs/acpi/acpi/sdt.hpp @@ -52,6 +52,11 @@ namespace acpi return *m_entry; } + constexpr auto operator->() const noexcept -> pointer + { + return m_entry; + } + constexpr auto operator==(sdt_iterator const & other) const noexcept -> bool { return m_entry == other.m_entry || (is_end() && other.is_end()); @@ -94,6 +99,9 @@ namespace acpi //! Get the signature of this table. [[nodiscard]] auto signature() const noexcept -> std::string_view; + //! Valide this table's checksum + [[nodiscard]] auto validate() const noexcept -> bool; + private: std::array<char, 4> m_signature; std::uint32_t m_length; diff --git a/libs/acpi/test_data/basic_madt.asl b/libs/acpi/test_data/basic_madt.asl new file mode 100644 index 0000000..cd6958a --- /dev/null +++ b/libs/acpi/test_data/basic_madt.asl @@ -0,0 +1,29 @@ +/* + * Intel ACPI Component Architecture + * iASL Compiler/Disassembler version 20251212 (64-bit version) + * Copyright (c) 2000 - 2025 Intel Corporation + * + * Template for [APIC] ACPI Table (static data table) + * Format: [ByteLength] FieldName : HexFieldValue + */ +[0004] Signature : "APIC" [Multiple APIC Description Table (MADT)] +[0004] Table Length : 00000000 +[0001] Revision : 07 +[0001] Checksum : 00 +[0006] Oem ID : "INTEL " +[0008] Oem Table ID : "Template" +[0004] Oem Revision : 00000001 +[0004] Asl Compiler ID : "INTL" +[0004] Asl Compiler Revision : 20230628 + +[0004] Local Apic Address : FEE00000 +[0004] Flags (decoded below) : 00000001 + PC-AT Compatibility : 1 + +[0001] Subtable Type : 00 [Processor Local APIC] +[0001] Length : 08 +[0001] Processor ID : 00 +[0001] Local Apic ID : 00 +[0004] Flags (decoded below) : 00000001 + Processor Enabled : 1 + Runtime Online Capable : 0 diff --git a/libs/acpi/test_data/basic_rsdt.asl b/libs/acpi/test_data/basic_rsdt.asl new file mode 100644 index 0000000..6cf4c7a --- /dev/null +++ b/libs/acpi/test_data/basic_rsdt.asl @@ -0,0 +1,26 @@ +/* + * Intel ACPI Component Architecture + * iASL Compiler/Disassembler version 20251212 (64-bit version) + * Copyright (c) 2000 - 2025 Intel Corporation + * + * Template for [RSDT] ACPI Table (static data table) + * Format: [ByteLength] FieldName : HexFieldValue + */ +[0004] Signature : "RSDT" [Root System Description Table] +[0004] Table Length : 00000000 +[0001] Revision : 01 +[0001] Checksum : 00 +[0006] Oem ID : "INTEL " +[0008] Oem Table ID : "TEMPLATE" +[0004] Oem Revision : 00000001 +[0004] Asl Compiler ID : "INTL" +[0004] Asl Compiler Revision : 20100528 + +[0004] ACPI Table Address 0 : 00000010 +[0004] ACPI Table Address 1 : 00000020 +[0004] ACPI Table Address 2 : 00000030 +[0004] ACPI Table Address 3 : 00000040 +[0004] ACPI Table Address 4 : 00000050 +[0004] ACPI Table Address 5 : 00000060 +[0004] ACPI Table Address 6 : 00000070 +[0004] ACPI Table Address 7 : 00000080 diff --git a/libs/acpi/test_data/tables.S b/libs/acpi/test_data/tables.S new file mode 100644 index 0000000..af58109 --- /dev/null +++ b/libs/acpi/test_data/tables.S @@ -0,0 +1,14 @@ +.section .rodata + +.balign 16 + +#define TABLE(name, file) \ + .global name##_start; \ + .global name##_end; \ + name##_start: .incbin file; \ + name##_end: + +TABLE(basic_madt, "basic_madt.aml") +TABLE(basic_rsdt, "basic_rsdt.aml") + +#undef TABLE diff --git a/libs/acpi/test_data/tables.hpp b/libs/acpi/test_data/tables.hpp new file mode 100644 index 0000000..2b3874f --- /dev/null +++ b/libs/acpi/test_data/tables.hpp @@ -0,0 +1,25 @@ +#ifndef ACPI_TEST_DATA_TABLES_HPP +#define ACPI_TEST_DATA_TABLES_HPP + +#include <cstddef> +#include <span> + +#define TABLE(name) \ + extern "C" std::byte const name##_start; \ + extern "C" std::byte const name##_end; \ + auto inline name()->std::span<std::byte const> \ + { \ + return {&name##_start, &name##_end}; \ + } + +namespace acpi::test_data::tables +{ + + TABLE(basic_madt); + TABLE(basic_rsdt); + +} // namespace acpi::test_data::tables + +#undef TABLE + +#endif |
