aboutsummaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-04-15 08:55:03 +0200
committerFelix Morgner <felix.morgner@ost.ch>2026-04-15 08:55:03 +0200
commit1fa31688a0e237dec1170dcea9e8f0a0571a25e5 (patch)
tree47670a4f05f50d45be078e6429950ff5c0b3ca48 /libs
parenteacc1becd1308a01a7ffcddf7c8910c8dc708939 (diff)
downloadteachos-1fa31688a0e237dec1170dcea9e8f0a0571a25e5.tar.xz
teachos-1fa31688a0e237dec1170dcea9e8f0a0571a25e5.zip
acpi: add basic MADT tests
Diffstat (limited to 'libs')
-rw-r--r--libs/acpi/CMakeLists.txt34
-rw-r--r--libs/acpi/acpi/madt.cpp10
-rw-r--r--libs/acpi/acpi/madt.hpp38
-rw-r--r--libs/acpi/acpi/madt.test.cpp55
-rw-r--r--libs/acpi/acpi/sdt.cpp7
-rw-r--r--libs/acpi/acpi/sdt.hpp8
-rw-r--r--libs/acpi/test_data/basic_madt.asl29
-rw-r--r--libs/acpi/test_data/basic_rsdt.asl26
-rw-r--r--libs/acpi/test_data/tables.S14
-rw-r--r--libs/acpi/test_data/tables.hpp25
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