From cd70186845c90aaaefbdb21b6d0f3c7caaa90a27 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 08:01:28 +0200 Subject: kernel: split off objects into a library --- kernel/CMakeLists.txt | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index cb3d8a5..206923e 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable("kernel" +add_library("kernel-objs" OBJECT # Platform-independent KAPI implementation "kapi/boot_modules.cpp" "kapi/cio.cpp" @@ -40,13 +40,29 @@ add_executable("kernel" "src/filesystem/vfs.cpp" ) -target_include_directories("kernel" PRIVATE +target_include_directories("kernel-objs" PUBLIC "include" ) +target_link_libraries("kernel-objs" PRIVATE + "os::kapi" +) + +file(GLOB_RECURSE KERNEL_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") + +target_sources("kernel-objs" PUBLIC + FILE_SET HEADERS + BASE_DIRS "include" + FILES + ${KERNEL_HEADERS} +) + +add_executable("kernel" + $ +) + target_link_libraries("kernel" PRIVATE "os::arch" - "os::kapi" ) target_link_options("kernel" PRIVATE @@ -61,15 +77,6 @@ set_property(TARGET "kernel" "${KERNEL_LINKER_SCRIPT}" ) -file(GLOB_RECURSE KERNEL_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") - -target_sources("kernel" PUBLIC - FILE_SET HEADERS - BASE_DIRS "include" - FILES - ${KERNEL_HEADERS} -) - target_disassemble("kernel") target_extract_debug_symbols("kernel") target_strip("kernel") -- cgit v1.2.3 From 9b80bd2ca528a376c2bb6831020b3d78e4b252d6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 08:10:10 +0200 Subject: build: fix kernel+arch build --- CMakeLists.txt | 21 +++++++++++++-------- kernel/CMakeLists.txt | 49 +++++++++++++++++++++++++++---------------------- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cc8a2f..002ab0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,15 +89,10 @@ if(Doxygen_FOUND AND TEACHOS_GENERATE_DOCS) endif() #[============================================================================[ -# Global Targets +# Build Host Testing #]============================================================================] -if(CMAKE_CROSSCOMPILING) - add_subdirectory("arch/${CMAKE_SYSTEM_PROCESSOR}") - add_subdirectory("kernel") - add_subdirectory("kapi") - add_subdirectory("libs") -else() +if(NOT CMAKE_CROSSCOMPILING) include("EnableCoverage") enable_testing() @@ -110,6 +105,16 @@ else() CXX_CLANG_TIDY "" ) endif() +endif() - add_subdirectory("libs") +#[============================================================================[ +# Global Targets +#]============================================================================] + +if(CMAKE_CROSSCOMPILING) + add_subdirectory("arch/${CMAKE_SYSTEM_PROCESSOR}") endif() + +add_subdirectory("kapi") +add_subdirectory("kernel") +add_subdirectory("libs") diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 206923e..4aa72e1 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -12,7 +12,6 @@ add_library("kernel-objs" OBJECT "kstd/print.cpp" # Kernel Implementation - "src/main.cpp" "src/memory/bitmap_allocator.cpp" "src/memory/block_list_allocator.cpp" "src/memory/operators.cpp" @@ -44,7 +43,7 @@ target_include_directories("kernel-objs" PUBLIC "include" ) -target_link_libraries("kernel-objs" PRIVATE +target_link_libraries("kernel-objs" PUBLIC "os::kapi" ) @@ -57,28 +56,34 @@ target_sources("kernel-objs" PUBLIC ${KERNEL_HEADERS} ) -add_executable("kernel" - $ -) +add_library("os::kernel" ALIAS "kernel-objs") -target_link_libraries("kernel" PRIVATE - "os::arch" -) +if(CMAKE_CROSSCOMPILING) + add_executable("kernel" + "src/main.cpp" + ) -target_link_options("kernel" PRIVATE - "-T${KERNEL_LINKER_SCRIPT}" - "-no-pie" - "-nostdlib" -) + target_link_libraries("kernel" PRIVATE + "os::arch" + "os::kernel" + ) -set_property(TARGET "kernel" - APPEND - PROPERTY LINK_DEPENDS - "${KERNEL_LINKER_SCRIPT}" -) + target_link_options("kernel" PRIVATE + "-T${KERNEL_LINKER_SCRIPT}" + "-no-pie" + "-nostdlib" + ) + + set_property(TARGET "kernel" + APPEND + PROPERTY LINK_DEPENDS + "${KERNEL_LINKER_SCRIPT}" + ) + + target_disassemble("kernel") + target_extract_debug_symbols("kernel") + target_strip("kernel") -target_disassemble("kernel") -target_extract_debug_symbols("kernel") -target_strip("kernel") + target_generate_bootable_iso("kernel") +endif() -target_generate_bootable_iso("kernel") -- cgit v1.2.3 From 5ae03c52fe33882416aa6044993d8422ccb33ab4 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 08:49:21 +0200 Subject: kernel: begin basic bht implementation --- .clangd | 7 +++-- kernel/CMakeLists.txt | 15 ++++++---- kernel/tests/CMakeLists.txt | 49 +++++++++++++++++++++++++++++++ kernel/tests/include/kernel/tests/cpu.hpp | 18 ++++++++++++ kernel/tests/kapi/cpu.cpp | 13 ++++++++ kernel/tests/src/main.cpp | 6 ++++ kernel/tests/src/test_support.cpp | 19 ++++++++++++ libs/kstd/CMakeLists.txt | 4 +-- 8 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 kernel/tests/CMakeLists.txt create mode 100644 kernel/tests/include/kernel/tests/cpu.hpp create mode 100644 kernel/tests/kapi/cpu.cpp create mode 100644 kernel/tests/src/main.cpp create mode 100644 kernel/tests/src/test_support.cpp diff --git a/.clangd b/.clangd index 996361d..71ee568 100644 --- a/.clangd +++ b/.clangd @@ -9,11 +9,12 @@ Documentation: --- If: - PathMatch: + PathMatch: - "libs/.*/tests/.*\\.cpp" + - "kernel/tests/.*\\.cpp" Diagnostics: ClangTidy: - Remove: '*' + Remove: "*" CompileFlags: Add: - - -Wno-c2y-extensions \ No newline at end of file + - -Wno-c2y-extensions diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 4aa72e1..854fb33 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library("kernel-objs" OBJECT +add_library("kernel_objs" OBJECT # Platform-independent KAPI implementation "kapi/boot_modules.cpp" "kapi/cio.cpp" @@ -14,7 +14,6 @@ add_library("kernel-objs" OBJECT # Kernel Implementation "src/memory/bitmap_allocator.cpp" "src/memory/block_list_allocator.cpp" - "src/memory/operators.cpp" "src/memory.cpp" "src/devices/block_device.cpp" "src/devices/block_device_utils.cpp" @@ -39,28 +38,29 @@ add_library("kernel-objs" OBJECT "src/filesystem/vfs.cpp" ) -target_include_directories("kernel-objs" PUBLIC +target_include_directories("kernel_objs" PUBLIC "include" ) -target_link_libraries("kernel-objs" PUBLIC +target_link_libraries("kernel_objs" PUBLIC "os::kapi" ) file(GLOB_RECURSE KERNEL_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") -target_sources("kernel-objs" PUBLIC +target_sources("kernel_objs" PUBLIC FILE_SET HEADERS BASE_DIRS "include" FILES ${KERNEL_HEADERS} ) -add_library("os::kernel" ALIAS "kernel-objs") +add_library("os::kernel" ALIAS "kernel_objs") if(CMAKE_CROSSCOMPILING) add_executable("kernel" "src/main.cpp" + "src/memory/operators.cpp" ) target_link_libraries("kernel" PRIVATE @@ -85,5 +85,8 @@ if(CMAKE_CROSSCOMPILING) target_strip("kernel") target_generate_bootable_iso("kernel") +else() + enable_coverage("kernel_objs") + add_subdirectory("tests") endif() diff --git a/kernel/tests/CMakeLists.txt b/kernel/tests/CMakeLists.txt new file mode 100644 index 0000000..96053f1 --- /dev/null +++ b/kernel/tests/CMakeLists.txt @@ -0,0 +1,49 @@ +#[============================================================================[ +# Kernel Test Support (Fakes, Stubs, Mocks, etc.) +#]============================================================================] + +add_library("kernel_test_support" + "kapi/cpu.cpp" +) + +target_include_directories("kernel_test_support" PUBLIC + "include" +) + +target_link_libraries("kernel_test_support" PUBLIC + "os::kapi" +) + +add_library("os::kernel_test_support" ALIAS "kernel_test_support") + +#[============================================================================[ +# Kernel Tests +#]============================================================================] + +add_executable("kernel_tests" + "src/main.cpp" + "src/test_support.cpp" +) + +target_include_directories("kernel_tests" PRIVATE + "include" +) + +target_link_libraries("kernel_tests" PRIVATE + "os::kernel_test_support" + "os::kernel" + "libs::kstd" + "Catch2::Catch2" +) + +set_target_properties("kernel_tests" PROPERTIES + C_CLANG_TIDY "" + CXX_CLANG_TIDY "" + EXCLUDE_FROM_ALL NO +) + +enable_coverage("kernel_tests") + +add_executable("os::kernel_tests" ALIAS "kernel_tests") + +catch_discover_tests("os::kernel_tests") \ No newline at end of file diff --git a/kernel/tests/include/kernel/tests/cpu.hpp b/kernel/tests/include/kernel/tests/cpu.hpp new file mode 100644 index 0000000..81c0c6f --- /dev/null +++ b/kernel/tests/include/kernel/tests/cpu.hpp @@ -0,0 +1,18 @@ +#ifndef TEACHOS_KERNEL_TESTS_CPU_HPP +#define TEACHOS_KERNEL_TESTS_CPU_HPP + +#include + +namespace kernel::tests::cpu +{ + + struct halt : std::runtime_error + { + halt() + : std::runtime_error{"CPU halt requested!"} + {} + }; + +} // namespace kernel::tests::cpu + +#endif \ No newline at end of file diff --git a/kernel/tests/kapi/cpu.cpp b/kernel/tests/kapi/cpu.cpp new file mode 100644 index 0000000..f03fca0 --- /dev/null +++ b/kernel/tests/kapi/cpu.cpp @@ -0,0 +1,13 @@ +#include "kernel/tests/cpu.hpp" + +#include + +namespace kapi::cpu +{ + + auto halt() -> void + { + throw kernel::tests::cpu::halt{}; + } + +} // namespace kapi::cpu \ No newline at end of file diff --git a/kernel/tests/src/main.cpp b/kernel/tests/src/main.cpp new file mode 100644 index 0000000..89bb1c7 --- /dev/null +++ b/kernel/tests/src/main.cpp @@ -0,0 +1,6 @@ +#include + +auto main(int argc, char ** argv) -> int +{ + return Catch::Session().run(argc, argv); +} \ No newline at end of file diff --git a/kernel/tests/src/test_support.cpp b/kernel/tests/src/test_support.cpp new file mode 100644 index 0000000..bbf5dc0 --- /dev/null +++ b/kernel/tests/src/test_support.cpp @@ -0,0 +1,19 @@ +#include "kapi/cpu.hpp" + +#include "kernel/tests/cpu.hpp" + +#include + +SCENARIO("Kernel test support infrastructure", "[support]") +{ + GIVEN("the test support infrastructure is initialized") + { + WHEN("when a CPU halt is requested") + { + THEN("the correct exception is thrown") + { + REQUIRE_THROWS_AS(kapi::cpu::halt(), kernel::tests::cpu::halt); + } + } + } +} \ No newline at end of file diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 7169aa8..ec0f441 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -40,9 +40,7 @@ if(CMAKE_CROSSCOMPILING) list(TRANSFORM KSTD_LIBC_SYMBOLS PREPEND "-Wl,--undefined=") target_link_options("kstd" INTERFACE ${KSTD_LIBC_SYMBOLS}) -endif() - -if(NOT CMAKE_CROSSCOMPILING) +else() add_executable("kstd_tests" "tests/src/flat_map.cpp" "tests/src/vector.cpp" -- cgit v1.2.3 From fa1ea53e6f3dd6b9b5f5f8160776b230184a30bf Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 09:36:31 +0200 Subject: kernel/tests: implement platform CIO kapi --- kapi/include/kapi/cio.hpp | 5 +++ kernel/tests/CMakeLists.txt | 5 ++- kernel/tests/include/kernel/tests/log_buffer.hpp | 32 +++++++++++++++ kernel/tests/kapi/cio.cpp | 38 ++++++++++++++++++ kernel/tests/src/log_buffer.cpp | 39 ++++++++++++++++++ kernel/tests/src/main.cpp | 4 ++ kernel/tests/src/test_support.cpp | 19 --------- kernel/tests/src/test_support.tests.cpp | 51 ++++++++++++++++++++++++ 8 files changed, 173 insertions(+), 20 deletions(-) create mode 100644 kernel/tests/include/kernel/tests/log_buffer.hpp create mode 100644 kernel/tests/kapi/cio.cpp create mode 100644 kernel/tests/src/log_buffer.cpp delete mode 100644 kernel/tests/src/test_support.cpp create mode 100644 kernel/tests/src/test_support.tests.cpp diff --git a/kapi/include/kapi/cio.hpp b/kapi/include/kapi/cio.hpp index 48f3000..71b5b02 100644 --- a/kapi/include/kapi/cio.hpp +++ b/kapi/include/kapi/cio.hpp @@ -25,6 +25,11 @@ namespace kapi::cio //! @return The previously active output device. auto set_output_device(output_device & device) -> std::optional; + //! @qualifier kernel-defined + //! Write a string to the given output stream. + //! + //! @param stream The output stream to write to. + //! @param text The text to write to the stream. auto write(output_stream stream, std::string_view text) -> void; } // namespace kapi::cio diff --git a/kernel/tests/CMakeLists.txt b/kernel/tests/CMakeLists.txt index 96053f1..39ab3b7 100644 --- a/kernel/tests/CMakeLists.txt +++ b/kernel/tests/CMakeLists.txt @@ -3,6 +3,7 @@ #]============================================================================] add_library("kernel_test_support" + "kapi/cio.cpp" "kapi/cpu.cpp" ) @@ -21,8 +22,10 @@ add_library("os::kernel_test_support" ALIAS "kernel_test_support") #]============================================================================] add_executable("kernel_tests" + "src/log_buffer.cpp" "src/main.cpp" - "src/test_support.cpp" + + "src/test_support.tests.cpp" ) target_include_directories("kernel_tests" PRIVATE diff --git a/kernel/tests/include/kernel/tests/log_buffer.hpp b/kernel/tests/include/kernel/tests/log_buffer.hpp new file mode 100644 index 0000000..5f2a1a7 --- /dev/null +++ b/kernel/tests/include/kernel/tests/log_buffer.hpp @@ -0,0 +1,32 @@ +#ifndef KERNEL_TESTS_LOG_BUFFER_HPP +#define KERNEL_TESTS_LOG_BUFFER_HPP + +#include +#include + +namespace kernel::tests::log_buffer +{ + + //! Append a message to the testing log buffer. + //! + //! @param message The message to append. + auto append(std::string const & message) -> void; + + //! Clear the testing log buffer. + auto clear() -> void; + + //! Get the testing log buffer as a single string. + //! + //! @return The testing log buffer as a single string. + auto flat_messages() -> std::string; + + //! Get the testing log buffer. + //! + //! @note Messages may be split across multiple entries if they are longer than the internal kernel print buffer size. + //! + //! @return The testing log buffer. + auto messages() -> std::vector const &; + +} // namespace kernel::tests::log_buffer + +#endif \ No newline at end of file diff --git a/kernel/tests/kapi/cio.cpp b/kernel/tests/kapi/cio.cpp new file mode 100644 index 0000000..6359fa8 --- /dev/null +++ b/kernel/tests/kapi/cio.cpp @@ -0,0 +1,38 @@ +#include + +#include "kernel/tests/log_buffer.hpp" + +#include +#include +#include + +namespace kapi::cio +{ + + namespace + { + + class test_output_device : public output_device + { + public: + test_output_device() = default; + + auto write(output_stream stream, std::string_view text) -> void override + { + auto & standard_stream = stream == output_stream::stdout ? std::cout : std::cerr; + standard_stream << text; + if (text != "\n") + { + kernel::tests::log_buffer::append(std::string{text}); + } + } + } device{}; + + } // namespace + + auto init() -> void + { + set_output_device(device); + } + +} // namespace kapi::cio \ No newline at end of file diff --git a/kernel/tests/src/log_buffer.cpp b/kernel/tests/src/log_buffer.cpp new file mode 100644 index 0000000..9e30afb --- /dev/null +++ b/kernel/tests/src/log_buffer.cpp @@ -0,0 +1,39 @@ +#include "kernel/tests/log_buffer.hpp" + +#include +#include +#include + +namespace kernel::tests::log_buffer +{ + + namespace + { + std::vector recorded_messages{}; + } + + auto append(std::string const & message) -> void + { + recorded_messages.push_back(message); + } + + auto clear() -> void + { + recorded_messages.clear(); + } + + auto flat_messages() -> std::string + { + return std::ranges::fold_left(recorded_messages, std::string{}, + [](std::string accumulator, std::string const & message) { + accumulator += message; + return accumulator; + }); + } + + auto messages() -> std::vector const & + { + return recorded_messages; + } + +} // namespace kernel::tests::log_buffer diff --git a/kernel/tests/src/main.cpp b/kernel/tests/src/main.cpp index 89bb1c7..f1f9bb4 100644 --- a/kernel/tests/src/main.cpp +++ b/kernel/tests/src/main.cpp @@ -1,6 +1,10 @@ +#include + #include auto main(int argc, char ** argv) -> int { + kapi::cio::init(); + return Catch::Session().run(argc, argv); } \ No newline at end of file diff --git a/kernel/tests/src/test_support.cpp b/kernel/tests/src/test_support.cpp deleted file mode 100644 index bbf5dc0..0000000 --- a/kernel/tests/src/test_support.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "kapi/cpu.hpp" - -#include "kernel/tests/cpu.hpp" - -#include - -SCENARIO("Kernel test support infrastructure", "[support]") -{ - GIVEN("the test support infrastructure is initialized") - { - WHEN("when a CPU halt is requested") - { - THEN("the correct exception is thrown") - { - REQUIRE_THROWS_AS(kapi::cpu::halt(), kernel::tests::cpu::halt); - } - } - } -} \ No newline at end of file diff --git a/kernel/tests/src/test_support.tests.cpp b/kernel/tests/src/test_support.tests.cpp new file mode 100644 index 0000000..f835e65 --- /dev/null +++ b/kernel/tests/src/test_support.tests.cpp @@ -0,0 +1,51 @@ +#include "kapi/cpu.hpp" +#include "kapi/system.hpp" + +#include "kernel/tests/cpu.hpp" +#include "kernel/tests/log_buffer.hpp" + +#include + +#include + +SCENARIO("Kernel test support infrastructure", "[support]") +{ + GIVEN("the test support infrastructure is initialized") + { + WHEN("a CPU halt is requested") + { + THEN("the correct exception is thrown") + { + REQUIRE_THROWS_AS(kapi::cpu::halt(), kernel::tests::cpu::halt); + } + } + + WHEN("a the system panics") + { + kernel::tests::log_buffer::clear(); + + THEN("the correct exception is thrown") + { + REQUIRE_THROWS_AS(kapi::system::panic("[kernel:tests] Test Panic"), kernel::tests::cpu::halt); + } + + THEN("the message is appended to the log buffer") + { + CHECK_THROWS(kapi::system::panic("[kernel:tests] Test Panic")); + REQUIRE(kernel::tests::log_buffer::flat_messages().contains("[kernel:tests] Test Panic")); + } + } + + WHEN("a regular print is issued") + { + kernel::tests::log_buffer::clear(); + + kstd::println("[kernel:tests] Test Print"); + + THEN("the message is appended to the log buffer") + { + REQUIRE(kernel::tests::log_buffer::flat_messages().contains("[kernel:tests] Test Print")); + } + } + } +} \ No newline at end of file -- cgit v1.2.3 From 0b139b3c66b340bb560dc608ea3b15a07ec95ee3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 09:48:12 +0200 Subject: kernel/tests: add kapi::cpu::init stub --- kernel/tests/kapi/cpu.cpp | 16 ++++++++++++++++ kernel/tests/src/main.cpp | 4 +++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/kernel/tests/kapi/cpu.cpp b/kernel/tests/kapi/cpu.cpp index f03fca0..3ecaadc 100644 --- a/kernel/tests/kapi/cpu.cpp +++ b/kernel/tests/kapi/cpu.cpp @@ -2,9 +2,25 @@ #include +#include +#include + namespace kapi::cpu { + auto init() -> void + { + auto static initialized = std::atomic_flag{}; + if (initialized.test_and_set()) + { + throw std::logic_error("kapi::cpu::init() called more than once"); + } + + // TODO: make sure that simulated interrupt can run. + + return; + } + auto halt() -> void { throw kernel::tests::cpu::halt{}; diff --git a/kernel/tests/src/main.cpp b/kernel/tests/src/main.cpp index f1f9bb4..a21aa9d 100644 --- a/kernel/tests/src/main.cpp +++ b/kernel/tests/src/main.cpp @@ -1,10 +1,12 @@ #include +#include #include auto main(int argc, char ** argv) -> int { kapi::cio::init(); + kapi::cpu::init(); return Catch::Session().run(argc, argv); -} \ No newline at end of file +} -- cgit v1.2.3 From b4049de007f9d15c12db227e4745a559359a99e9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 09:51:42 +0200 Subject: kernel/tests: add kapi::interrupts::init stub --- kernel/tests/CMakeLists.txt | 9 +++++---- kernel/tests/kapi/interrupts.cpp | 11 +++++++++++ kernel/tests/src/main.cpp | 2 ++ 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 kernel/tests/kapi/interrupts.cpp diff --git a/kernel/tests/CMakeLists.txt b/kernel/tests/CMakeLists.txt index 39ab3b7..2fe5ff3 100644 --- a/kernel/tests/CMakeLists.txt +++ b/kernel/tests/CMakeLists.txt @@ -5,6 +5,10 @@ add_library("kernel_test_support" "kapi/cio.cpp" "kapi/cpu.cpp" + "kapi/interrupts.cpp" + + "src/log_buffer.cpp" + "src/main.cpp" ) target_include_directories("kernel_test_support" PUBLIC @@ -13,6 +17,7 @@ target_include_directories("kernel_test_support" PUBLIC target_link_libraries("kernel_test_support" PUBLIC "os::kapi" + "Catch2::Catch2" ) add_library("os::kernel_test_support" ALIAS "kernel_test_support") @@ -22,9 +27,6 @@ add_library("os::kernel_test_support" ALIAS "kernel_test_support") #]============================================================================] add_executable("kernel_tests" - "src/log_buffer.cpp" - "src/main.cpp" - "src/test_support.tests.cpp" ) @@ -36,7 +38,6 @@ target_link_libraries("kernel_tests" PRIVATE "os::kernel_test_support" "os::kernel" "libs::kstd" - "Catch2::Catch2" ) set_target_properties("kernel_tests" PROPERTIES diff --git a/kernel/tests/kapi/interrupts.cpp b/kernel/tests/kapi/interrupts.cpp new file mode 100644 index 0000000..0077266 --- /dev/null +++ b/kernel/tests/kapi/interrupts.cpp @@ -0,0 +1,11 @@ +#include + +namespace kapi::interrupts +{ + + auto enable() -> void + { + // TODO: enable simulated interrupts. + } + +} // namespace kapi::interrupts \ No newline at end of file diff --git a/kernel/tests/src/main.cpp b/kernel/tests/src/main.cpp index a21aa9d..11e88a4 100644 --- a/kernel/tests/src/main.cpp +++ b/kernel/tests/src/main.cpp @@ -1,3 +1,4 @@ +#include "kapi/interrupts.hpp" #include #include @@ -7,6 +8,7 @@ auto main(int argc, char ** argv) -> int { kapi::cio::init(); kapi::cpu::init(); + kapi::interrupts::enable(); return Catch::Session().run(argc, argv); } -- cgit v1.2.3 From c30ba8bc8c1cf80a7e9b46e9f1a66dc1b409fcbd Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 10:04:40 +0200 Subject: kernel/tests: add kapi::memory::init stub --- kernel/tests/CMakeLists.txt | 1 + kernel/tests/kapi/memory.cpp | 11 +++++++++++ kernel/tests/src/main.cpp | 3 +++ 3 files changed, 15 insertions(+) create mode 100644 kernel/tests/kapi/memory.cpp diff --git a/kernel/tests/CMakeLists.txt b/kernel/tests/CMakeLists.txt index 2fe5ff3..0e2ea36 100644 --- a/kernel/tests/CMakeLists.txt +++ b/kernel/tests/CMakeLists.txt @@ -6,6 +6,7 @@ add_library("kernel_test_support" "kapi/cio.cpp" "kapi/cpu.cpp" "kapi/interrupts.cpp" + "kapi/memory.cpp" "src/log_buffer.cpp" "src/main.cpp" diff --git a/kernel/tests/kapi/memory.cpp b/kernel/tests/kapi/memory.cpp new file mode 100644 index 0000000..4482c74 --- /dev/null +++ b/kernel/tests/kapi/memory.cpp @@ -0,0 +1,11 @@ +#include + +namespace kapi::memory +{ + + auto init() -> void + { + // TODO: initialize simulated memory. + } + +} // namespace kapi::memory \ No newline at end of file diff --git a/kernel/tests/src/main.cpp b/kernel/tests/src/main.cpp index 11e88a4..c0ec12f 100644 --- a/kernel/tests/src/main.cpp +++ b/kernel/tests/src/main.cpp @@ -1,4 +1,5 @@ #include "kapi/interrupts.hpp" +#include "kapi/memory.hpp" #include #include @@ -10,5 +11,7 @@ auto main(int argc, char ** argv) -> int kapi::cpu::init(); kapi::interrupts::enable(); + kapi::memory::init(); + return Catch::Session().run(argc, argv); } -- cgit v1.2.3 From 419f4bebff5745b46bf30092dc7a7ca43449ea2e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 11:12:20 +0200 Subject: kernel/tests: implement basic simulated memory --- kernel/kapi/memory.cpp | 8 ++- kernel/tests/CMakeLists.txt | 14 +++- .../include/kernel/tests/simulated_memory.hpp | 19 ++++++ kernel/tests/kapi/memory.cpp | 77 +++++++++++++++++++++- kernel/tests/src/main.cpp | 4 ++ kernel/tests/src/simulated_memory.cpp | 33 ++++++++++ 6 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 kernel/tests/include/kernel/tests/simulated_memory.hpp create mode 100644 kernel/tests/src/simulated_memory.cpp diff --git a/kernel/kapi/memory.cpp b/kernel/kapi/memory.cpp index 2803d76..06a3165 100644 --- a/kernel/kapi/memory.cpp +++ b/kernel/kapi/memory.cpp @@ -125,14 +125,18 @@ namespace kapi::memory } auto const flags = page_mapper::flags::writable | page_mapper::flags::supervisor_only | page_mapper::flags::global; + auto bitmap_ptr = static_cast(nullptr); std::ranges::for_each(std::views::iota(0uz, bitmap_pages), [&](auto index) { auto page = page::containing(pmm_metadata_base + index * page::size); auto frame = memory::frame(bitmap_frames->first.number() + index); - active_page_mapper->map(page, frame, flags); + auto mapped = active_page_mapper->map(page, frame, flags); + if (!bitmap_ptr) + { + bitmap_ptr = reinterpret_cast(mapped); + } }); - auto bitmap_ptr = static_cast(pmm_metadata_base); auto bitmap = std::span{bitmap_ptr, (bitmap_bytes + kstd::type_size - 1_B) / kstd::type_size}; diff --git a/kernel/tests/CMakeLists.txt b/kernel/tests/CMakeLists.txt index 0e2ea36..0855520 100644 --- a/kernel/tests/CMakeLists.txt +++ b/kernel/tests/CMakeLists.txt @@ -7,9 +7,19 @@ add_library("kernel_test_support" "kapi/cpu.cpp" "kapi/interrupts.cpp" "kapi/memory.cpp" - + "src/log_buffer.cpp" "src/main.cpp" + "src/simulated_memory.cpp" +) + +file(GLOB_RECURSE KERNEL_TEST_SUPPORT_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") + +target_sources("kernel_test_support" PUBLIC + FILE_SET HEADERS + BASE_DIRS "include" + FILES + ${KERNEL_TEST_SUPPORT_HEADERS} ) target_include_directories("kernel_test_support" PUBLIC @@ -18,6 +28,7 @@ target_include_directories("kernel_test_support" PUBLIC target_link_libraries("kernel_test_support" PUBLIC "os::kapi" + "os::kernel" "Catch2::Catch2" ) @@ -37,7 +48,6 @@ target_include_directories("kernel_tests" PRIVATE target_link_libraries("kernel_tests" PRIVATE "os::kernel_test_support" - "os::kernel" "libs::kstd" ) diff --git a/kernel/tests/include/kernel/tests/simulated_memory.hpp b/kernel/tests/include/kernel/tests/simulated_memory.hpp new file mode 100644 index 0000000..156b1e1 --- /dev/null +++ b/kernel/tests/include/kernel/tests/simulated_memory.hpp @@ -0,0 +1,19 @@ +#ifndef TEACHOS_KERNEL_TESTS_SIMULATED_MEMORY_HPP +#define TEACHOS_KERNEL_TESTS_SIMULATED_MEMORY_HPP + +#include + +#include + +namespace kernel::tests::simulated_memory +{ + + auto init(kstd::units::bytes size) -> void; + + auto pmm_metadata_base() -> std::byte *; + + auto ram_base() -> std::byte *; + +} // namespace kernel::tests::simulated_memory + +#endif \ No newline at end of file diff --git a/kernel/tests/kapi/memory.cpp b/kernel/tests/kapi/memory.cpp index 4482c74..6de2f60 100644 --- a/kernel/tests/kapi/memory.cpp +++ b/kernel/tests/kapi/memory.cpp @@ -1,11 +1,86 @@ +#include "kapi/memory.hpp" + #include +#include "kernel/tests/simulated_memory.hpp" + +#include +#include + +#include +#include +#include +#include + namespace kapi::memory { + namespace + { + //! The size of the simulated RAM. + constexpr auto simulate_memory_size = kstd::units::MiB(32); + + struct test_boostrap_frame_allocator : frame_allocator + { + auto mark_used(frame) -> void override {} + + auto allocate_many(std::size_t count) noexcept -> std::optional> override + { + auto start = next_free_frame; + next_free_frame += count; + return std::pair{frame{start}, count}; + } + + auto release_many(std::pair) -> void override {} + + std::size_t next_free_frame{}; + } boostrap_allocator; + + struct test_page_mapper : page_mapper + { + auto map(page page, frame frame, flags flags) -> std::byte * override + { + kstd::println("mapping page {} onto frame {} with flags {}", page.number(), frame.number(), + static_cast(flags)); + + if ((page.start_address() & pmm_metadata_base.raw()) == pmm_metadata_base.raw()) + { + auto offset = page.start_address() & ~pmm_metadata_base.raw(); + return kernel::tests::simulated_memory::pmm_metadata_base() + offset; + } + + return nullptr; + } + + auto unmap(page page) -> void override + { + kstd::println("unmapping page {}", page.number()); + } + + auto try_unmap(page page) noexcept -> bool override + { + kstd::println("trying to unmap page {}", page.number()); + return false; + } + } test_mapper; + + auto handoff_to_kernel_pmm(frame_allocator & new_allocator) -> void + { + for (auto i = 0uz; i < boostrap_allocator.next_free_frame; ++i) + { + new_allocator.mark_used(frame{i}); + } + } + + } // namespace + auto init() -> void { - // TODO: initialize simulated memory. + kernel::tests::simulated_memory::init(simulate_memory_size); + set_frame_allocator(boostrap_allocator); + set_page_mapper(test_mapper); + + init_pmm(simulate_memory_size / frame::size, handoff_to_kernel_pmm); } } // namespace kapi::memory \ No newline at end of file diff --git a/kernel/tests/src/main.cpp b/kernel/tests/src/main.cpp index c0ec12f..69fd633 100644 --- a/kernel/tests/src/main.cpp +++ b/kernel/tests/src/main.cpp @@ -12,6 +12,10 @@ auto main(int argc, char ** argv) -> int kapi::interrupts::enable(); kapi::memory::init(); + // note: no kernel heap is created, since we are in the test environment. Nonetheless, we still initialize the memory + // subsystem, so that components that rely on it can be tested. No component must ever rely on the heap allocator + // directly, rather they have to go through the new and delete. However, some components may use the frame allocator + // and page mapper in order to perform their tasks. return Catch::Session().run(argc, argv); } diff --git a/kernel/tests/src/simulated_memory.cpp b/kernel/tests/src/simulated_memory.cpp new file mode 100644 index 0000000..9a9b354 --- /dev/null +++ b/kernel/tests/src/simulated_memory.cpp @@ -0,0 +1,33 @@ +#include "kernel/tests/simulated_memory.hpp" + +#include + +#include +#include + +namespace kernel::tests::simulated_memory +{ + + namespace + { + auto constinit ram_storage = std::vector{}; + auto constinit pmm_storage = std::vector{}; + } // namespace + + auto init(kstd::units::bytes size) -> void + { + ram_storage.resize(size / kstd::units::bytes{1}); + pmm_storage.resize(size / kstd::units::bytes{1}); + } + + auto pmm_metadata_base() -> std::byte * + { + return pmm_storage.data(); + } + + auto ram_base() -> std::byte * + { + return ram_storage.data(); + } + +} // namespace kernel::tests::simulated_memory \ No newline at end of file -- cgit v1.2.3 From 0369fb7c4baa543dfb36ebb39ab53ac7560994ba Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 11:37:05 +0200 Subject: kernel/test: only initialize kernel when running tests --- kernel/tests/src/main.cpp | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/kernel/tests/src/main.cpp b/kernel/tests/src/main.cpp index 69fd633..b3ae142 100644 --- a/kernel/tests/src/main.cpp +++ b/kernel/tests/src/main.cpp @@ -7,15 +7,32 @@ auto main(int argc, char ** argv) -> int { - kapi::cio::init(); - kapi::cpu::init(); - kapi::interrupts::enable(); + auto session = Catch::Session{}; - kapi::memory::init(); - // note: no kernel heap is created, since we are in the test environment. Nonetheless, we still initialize the memory - // subsystem, so that components that rely on it can be tested. No component must ever rely on the heap allocator - // directly, rather they have to go through the new and delete. However, some components may use the frame allocator - // and page mapper in order to perform their tasks. + if (auto result = session.applyCommandLine(argc, argv); result != 0) + { + return result; + } - return Catch::Session().run(argc, argv); + auto const & config = session.configData(); + auto skip_init = config.listTests || // + config.listTags || // + config.listReporters || // + config.listListeners || // + config.showHelp; + + if (!skip_init) + { + kapi::cio::init(); + kapi::cpu::init(); + kapi::interrupts::enable(); + + kapi::memory::init(); + // note: no kernel heap is created, since we are in the test environment. Nonetheless, we still initialize the + // memory subsystem, so that components that rely on it can be tested. No component must ever rely on the heap + // allocator directly, rather they have to go through the new and delete. However, some components may use the frame + // allocator and page mapper in order to perform their tasks. + } + + return session.run(); } -- cgit v1.2.3 From 1f652b8b5ca5dbea588975466801cb1479f3dda8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 12:15:25 +0200 Subject: kernel/tests: dissolve tests into source tree --- .clangd | 2 +- kernel/CMakeLists.txt | 42 ++++++++++- kernel/include/kernel/tests/cpu.hpp | 18 +++++ kernel/include/kernel/tests/log_buffer.hpp | 32 ++++++++ kernel/include/kernel/tests/simulated_memory.hpp | 19 +++++ kernel/kapi/cpu.tests.cpp | 19 +++++ kernel/kapi/system.tests.cpp | 30 ++++++++ kernel/kstd/print.tests.cpp | 23 ++++++ kernel/src/main.tests.cpp | 38 ++++++++++ kernel/src/test_support/kapi/cio.cpp | 38 ++++++++++ kernel/src/test_support/kapi/cpu.cpp | 29 ++++++++ kernel/src/test_support/kapi/interrupts.cpp | 11 +++ kernel/src/test_support/kapi/memory.cpp | 86 ++++++++++++++++++++++ kernel/src/test_support/log_buffer.cpp | 39 ++++++++++ kernel/src/test_support/simulated_memory.cpp | 33 +++++++++ kernel/tests/CMakeLists.txt | 64 ---------------- kernel/tests/include/kernel/tests/cpu.hpp | 18 ----- kernel/tests/include/kernel/tests/log_buffer.hpp | 32 -------- .../include/kernel/tests/simulated_memory.hpp | 19 ----- kernel/tests/kapi/cio.cpp | 38 ---------- kernel/tests/kapi/cpu.cpp | 29 -------- kernel/tests/kapi/interrupts.cpp | 11 --- kernel/tests/kapi/memory.cpp | 86 ---------------------- kernel/tests/src/log_buffer.cpp | 39 ---------- kernel/tests/src/main.cpp | 38 ---------- kernel/tests/src/simulated_memory.cpp | 33 --------- kernel/tests/src/test_support.tests.cpp | 51 ------------- 27 files changed, 457 insertions(+), 460 deletions(-) create mode 100644 kernel/include/kernel/tests/cpu.hpp create mode 100644 kernel/include/kernel/tests/log_buffer.hpp create mode 100644 kernel/include/kernel/tests/simulated_memory.hpp create mode 100644 kernel/kapi/cpu.tests.cpp create mode 100644 kernel/kapi/system.tests.cpp create mode 100644 kernel/kstd/print.tests.cpp create mode 100644 kernel/src/main.tests.cpp create mode 100644 kernel/src/test_support/kapi/cio.cpp create mode 100644 kernel/src/test_support/kapi/cpu.cpp create mode 100644 kernel/src/test_support/kapi/interrupts.cpp create mode 100644 kernel/src/test_support/kapi/memory.cpp create mode 100644 kernel/src/test_support/log_buffer.cpp create mode 100644 kernel/src/test_support/simulated_memory.cpp delete mode 100644 kernel/tests/CMakeLists.txt delete mode 100644 kernel/tests/include/kernel/tests/cpu.hpp delete mode 100644 kernel/tests/include/kernel/tests/log_buffer.hpp delete mode 100644 kernel/tests/include/kernel/tests/simulated_memory.hpp delete mode 100644 kernel/tests/kapi/cio.cpp delete mode 100644 kernel/tests/kapi/cpu.cpp delete mode 100644 kernel/tests/kapi/interrupts.cpp delete mode 100644 kernel/tests/kapi/memory.cpp delete mode 100644 kernel/tests/src/log_buffer.cpp delete mode 100644 kernel/tests/src/main.cpp delete mode 100644 kernel/tests/src/simulated_memory.cpp delete mode 100644 kernel/tests/src/test_support.tests.cpp diff --git a/.clangd b/.clangd index 71ee568..fac5c82 100644 --- a/.clangd +++ b/.clangd @@ -11,7 +11,7 @@ Documentation: If: PathMatch: - "libs/.*/tests/.*\\.cpp" - - "kernel/tests/.*\\.cpp" + - "kernel/.*\\.tests.cpp" Diagnostics: ClangTidy: Remove: "*" diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 854fb33..d6b3a1b 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -87,6 +87,46 @@ if(CMAKE_CROSSCOMPILING) target_generate_bootable_iso("kernel") else() enable_coverage("kernel_objs") - add_subdirectory("tests") + + add_library("kernel_test_support" + "src/test_support/kapi/cpu.cpp" + "src/test_support/kapi/cio.cpp" + "src/test_support/kapi/interrupts.cpp" + "src/test_support/kapi/memory.cpp" + "src/test_support/log_buffer.cpp" + "src/test_support/simulated_memory.cpp" + ) + add_library("os::kernel_test_support" ALIAS "kernel_test_support") + + target_link_libraries("kernel_test_support" PUBLIC + "os::kernel" + "Catch2::Catch2" + ) + + add_executable("kernel_tests" + # KAPI Shim Tests + "kapi/cpu.tests.cpp" + "kapi/system.tests.cpp" + + # KSTD Shim Tests + "kstd/print.tests.cpp" + + # Test Executable Main + "src/main.tests.cpp" + ) + add_executable("os::kernel_tests" ALIAS "kernel_tests") + + target_link_libraries("kernel_tests" PRIVATE + "os::kernel_test_support" + ) + + set_target_properties("kernel_tests" PROPERTIES + C_CLANG_TIDY "" + CXX_CLANG_TIDY "" + EXCLUDE_FROM_ALL NO + ) + + enable_coverage("kernel_tests") + catch_discover_tests("os::kernel_tests") endif() diff --git a/kernel/include/kernel/tests/cpu.hpp b/kernel/include/kernel/tests/cpu.hpp new file mode 100644 index 0000000..81c0c6f --- /dev/null +++ b/kernel/include/kernel/tests/cpu.hpp @@ -0,0 +1,18 @@ +#ifndef TEACHOS_KERNEL_TESTS_CPU_HPP +#define TEACHOS_KERNEL_TESTS_CPU_HPP + +#include + +namespace kernel::tests::cpu +{ + + struct halt : std::runtime_error + { + halt() + : std::runtime_error{"CPU halt requested!"} + {} + }; + +} // namespace kernel::tests::cpu + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/tests/log_buffer.hpp b/kernel/include/kernel/tests/log_buffer.hpp new file mode 100644 index 0000000..5f2a1a7 --- /dev/null +++ b/kernel/include/kernel/tests/log_buffer.hpp @@ -0,0 +1,32 @@ +#ifndef KERNEL_TESTS_LOG_BUFFER_HPP +#define KERNEL_TESTS_LOG_BUFFER_HPP + +#include +#include + +namespace kernel::tests::log_buffer +{ + + //! Append a message to the testing log buffer. + //! + //! @param message The message to append. + auto append(std::string const & message) -> void; + + //! Clear the testing log buffer. + auto clear() -> void; + + //! Get the testing log buffer as a single string. + //! + //! @return The testing log buffer as a single string. + auto flat_messages() -> std::string; + + //! Get the testing log buffer. + //! + //! @note Messages may be split across multiple entries if they are longer than the internal kernel print buffer size. + //! + //! @return The testing log buffer. + auto messages() -> std::vector const &; + +} // namespace kernel::tests::log_buffer + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/tests/simulated_memory.hpp b/kernel/include/kernel/tests/simulated_memory.hpp new file mode 100644 index 0000000..156b1e1 --- /dev/null +++ b/kernel/include/kernel/tests/simulated_memory.hpp @@ -0,0 +1,19 @@ +#ifndef TEACHOS_KERNEL_TESTS_SIMULATED_MEMORY_HPP +#define TEACHOS_KERNEL_TESTS_SIMULATED_MEMORY_HPP + +#include + +#include + +namespace kernel::tests::simulated_memory +{ + + auto init(kstd::units::bytes size) -> void; + + auto pmm_metadata_base() -> std::byte *; + + auto ram_base() -> std::byte *; + +} // namespace kernel::tests::simulated_memory + +#endif \ No newline at end of file diff --git a/kernel/kapi/cpu.tests.cpp b/kernel/kapi/cpu.tests.cpp new file mode 100644 index 0000000..6fe026b --- /dev/null +++ b/kernel/kapi/cpu.tests.cpp @@ -0,0 +1,19 @@ +#include "kapi/cpu.hpp" + +#include "kernel/tests/cpu.hpp" + +#include + +SCENARIO("Kernel testing kapi::cpu shims", "[support]") +{ + GIVEN("the test support infrastructure is initialized") + { + WHEN("a CPU halt is requested") + { + THEN("the correct exception is thrown") + { + REQUIRE_THROWS_AS(kapi::cpu::halt(), kernel::tests::cpu::halt); + } + } + } +} \ No newline at end of file diff --git a/kernel/kapi/system.tests.cpp b/kernel/kapi/system.tests.cpp new file mode 100644 index 0000000..c130b54 --- /dev/null +++ b/kernel/kapi/system.tests.cpp @@ -0,0 +1,30 @@ +#include "kapi/system.hpp" + +#include "kernel/tests/cpu.hpp" +#include "kernel/tests/log_buffer.hpp" + +#include + +#include + +SCENARIO("Kernel testing kapi::system shims", "[support]") +{ + GIVEN("the test support infrastructure is initialized") + { + WHEN("a the system panics") + { + kernel::tests::log_buffer::clear(); + + THEN("the correct exception is thrown") + { + REQUIRE_THROWS_AS(kapi::system::panic("[kernel:tests] Test Panic"), kernel::tests::cpu::halt); + } + + THEN("the message is appended to the log buffer") + { + CHECK_THROWS(kapi::system::panic("[kernel:tests] Test Panic")); + REQUIRE(kernel::tests::log_buffer::flat_messages().contains("[kernel:tests] Test Panic")); + } + } + } +} \ No newline at end of file diff --git a/kernel/kstd/print.tests.cpp b/kernel/kstd/print.tests.cpp new file mode 100644 index 0000000..2ab829c --- /dev/null +++ b/kernel/kstd/print.tests.cpp @@ -0,0 +1,23 @@ +#include "kstd/print" + +#include "kernel/tests/log_buffer.hpp" + +#include + +SCENARIO("Kernel testing kstd shims", "[support]") +{ + GIVEN("the test support infrastructure is initialized") + { + WHEN("a regular print is issued") + { + kernel::tests::log_buffer::clear(); + + kstd::println("[kernel:tests] Test Print"); + + THEN("the message is appended to the log buffer") + { + REQUIRE(kernel::tests::log_buffer::flat_messages().contains("[kernel:tests] Test Print")); + } + } + } +} \ No newline at end of file diff --git a/kernel/src/main.tests.cpp b/kernel/src/main.tests.cpp new file mode 100644 index 0000000..b3ae142 --- /dev/null +++ b/kernel/src/main.tests.cpp @@ -0,0 +1,38 @@ +#include "kapi/interrupts.hpp" +#include "kapi/memory.hpp" +#include +#include + +#include + +auto main(int argc, char ** argv) -> int +{ + auto session = Catch::Session{}; + + if (auto result = session.applyCommandLine(argc, argv); result != 0) + { + return result; + } + + auto const & config = session.configData(); + auto skip_init = config.listTests || // + config.listTags || // + config.listReporters || // + config.listListeners || // + config.showHelp; + + if (!skip_init) + { + kapi::cio::init(); + kapi::cpu::init(); + kapi::interrupts::enable(); + + kapi::memory::init(); + // note: no kernel heap is created, since we are in the test environment. Nonetheless, we still initialize the + // memory subsystem, so that components that rely on it can be tested. No component must ever rely on the heap + // allocator directly, rather they have to go through the new and delete. However, some components may use the frame + // allocator and page mapper in order to perform their tasks. + } + + return session.run(); +} diff --git a/kernel/src/test_support/kapi/cio.cpp b/kernel/src/test_support/kapi/cio.cpp new file mode 100644 index 0000000..6359fa8 --- /dev/null +++ b/kernel/src/test_support/kapi/cio.cpp @@ -0,0 +1,38 @@ +#include + +#include "kernel/tests/log_buffer.hpp" + +#include +#include +#include + +namespace kapi::cio +{ + + namespace + { + + class test_output_device : public output_device + { + public: + test_output_device() = default; + + auto write(output_stream stream, std::string_view text) -> void override + { + auto & standard_stream = stream == output_stream::stdout ? std::cout : std::cerr; + standard_stream << text; + if (text != "\n") + { + kernel::tests::log_buffer::append(std::string{text}); + } + } + } device{}; + + } // namespace + + auto init() -> void + { + set_output_device(device); + } + +} // namespace kapi::cio \ No newline at end of file diff --git a/kernel/src/test_support/kapi/cpu.cpp b/kernel/src/test_support/kapi/cpu.cpp new file mode 100644 index 0000000..3ecaadc --- /dev/null +++ b/kernel/src/test_support/kapi/cpu.cpp @@ -0,0 +1,29 @@ +#include "kernel/tests/cpu.hpp" + +#include + +#include +#include + +namespace kapi::cpu +{ + + auto init() -> void + { + auto static initialized = std::atomic_flag{}; + if (initialized.test_and_set()) + { + throw std::logic_error("kapi::cpu::init() called more than once"); + } + + // TODO: make sure that simulated interrupt can run. + + return; + } + + auto halt() -> void + { + throw kernel::tests::cpu::halt{}; + } + +} // namespace kapi::cpu \ No newline at end of file diff --git a/kernel/src/test_support/kapi/interrupts.cpp b/kernel/src/test_support/kapi/interrupts.cpp new file mode 100644 index 0000000..0077266 --- /dev/null +++ b/kernel/src/test_support/kapi/interrupts.cpp @@ -0,0 +1,11 @@ +#include + +namespace kapi::interrupts +{ + + auto enable() -> void + { + // TODO: enable simulated interrupts. + } + +} // namespace kapi::interrupts \ No newline at end of file diff --git a/kernel/src/test_support/kapi/memory.cpp b/kernel/src/test_support/kapi/memory.cpp new file mode 100644 index 0000000..6de2f60 --- /dev/null +++ b/kernel/src/test_support/kapi/memory.cpp @@ -0,0 +1,86 @@ +#include "kapi/memory.hpp" + +#include + +#include "kernel/tests/simulated_memory.hpp" + +#include +#include + +#include +#include +#include +#include + +namespace kapi::memory +{ + + namespace + { + //! The size of the simulated RAM. + constexpr auto simulate_memory_size = kstd::units::MiB(32); + + struct test_boostrap_frame_allocator : frame_allocator + { + auto mark_used(frame) -> void override {} + + auto allocate_many(std::size_t count) noexcept -> std::optional> override + { + auto start = next_free_frame; + next_free_frame += count; + return std::pair{frame{start}, count}; + } + + auto release_many(std::pair) -> void override {} + + std::size_t next_free_frame{}; + } boostrap_allocator; + + struct test_page_mapper : page_mapper + { + auto map(page page, frame frame, flags flags) -> std::byte * override + { + kstd::println("mapping page {} onto frame {} with flags {}", page.number(), frame.number(), + static_cast(flags)); + + if ((page.start_address() & pmm_metadata_base.raw()) == pmm_metadata_base.raw()) + { + auto offset = page.start_address() & ~pmm_metadata_base.raw(); + return kernel::tests::simulated_memory::pmm_metadata_base() + offset; + } + + return nullptr; + } + + auto unmap(page page) -> void override + { + kstd::println("unmapping page {}", page.number()); + } + + auto try_unmap(page page) noexcept -> bool override + { + kstd::println("trying to unmap page {}", page.number()); + return false; + } + } test_mapper; + + auto handoff_to_kernel_pmm(frame_allocator & new_allocator) -> void + { + for (auto i = 0uz; i < boostrap_allocator.next_free_frame; ++i) + { + new_allocator.mark_used(frame{i}); + } + } + + } // namespace + + auto init() -> void + { + kernel::tests::simulated_memory::init(simulate_memory_size); + set_frame_allocator(boostrap_allocator); + set_page_mapper(test_mapper); + + init_pmm(simulate_memory_size / frame::size, handoff_to_kernel_pmm); + } + +} // namespace kapi::memory \ No newline at end of file diff --git a/kernel/src/test_support/log_buffer.cpp b/kernel/src/test_support/log_buffer.cpp new file mode 100644 index 0000000..9e30afb --- /dev/null +++ b/kernel/src/test_support/log_buffer.cpp @@ -0,0 +1,39 @@ +#include "kernel/tests/log_buffer.hpp" + +#include +#include +#include + +namespace kernel::tests::log_buffer +{ + + namespace + { + std::vector recorded_messages{}; + } + + auto append(std::string const & message) -> void + { + recorded_messages.push_back(message); + } + + auto clear() -> void + { + recorded_messages.clear(); + } + + auto flat_messages() -> std::string + { + return std::ranges::fold_left(recorded_messages, std::string{}, + [](std::string accumulator, std::string const & message) { + accumulator += message; + return accumulator; + }); + } + + auto messages() -> std::vector const & + { + return recorded_messages; + } + +} // namespace kernel::tests::log_buffer diff --git a/kernel/src/test_support/simulated_memory.cpp b/kernel/src/test_support/simulated_memory.cpp new file mode 100644 index 0000000..9a9b354 --- /dev/null +++ b/kernel/src/test_support/simulated_memory.cpp @@ -0,0 +1,33 @@ +#include "kernel/tests/simulated_memory.hpp" + +#include + +#include +#include + +namespace kernel::tests::simulated_memory +{ + + namespace + { + auto constinit ram_storage = std::vector{}; + auto constinit pmm_storage = std::vector{}; + } // namespace + + auto init(kstd::units::bytes size) -> void + { + ram_storage.resize(size / kstd::units::bytes{1}); + pmm_storage.resize(size / kstd::units::bytes{1}); + } + + auto pmm_metadata_base() -> std::byte * + { + return pmm_storage.data(); + } + + auto ram_base() -> std::byte * + { + return ram_storage.data(); + } + +} // namespace kernel::tests::simulated_memory \ No newline at end of file diff --git a/kernel/tests/CMakeLists.txt b/kernel/tests/CMakeLists.txt deleted file mode 100644 index 0855520..0000000 --- a/kernel/tests/CMakeLists.txt +++ /dev/null @@ -1,64 +0,0 @@ -#[============================================================================[ -# Kernel Test Support (Fakes, Stubs, Mocks, etc.) -#]============================================================================] - -add_library("kernel_test_support" - "kapi/cio.cpp" - "kapi/cpu.cpp" - "kapi/interrupts.cpp" - "kapi/memory.cpp" - - "src/log_buffer.cpp" - "src/main.cpp" - "src/simulated_memory.cpp" -) - -file(GLOB_RECURSE KERNEL_TEST_SUPPORT_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") - -target_sources("kernel_test_support" PUBLIC - FILE_SET HEADERS - BASE_DIRS "include" - FILES - ${KERNEL_TEST_SUPPORT_HEADERS} -) - -target_include_directories("kernel_test_support" PUBLIC - "include" -) - -target_link_libraries("kernel_test_support" PUBLIC - "os::kapi" - "os::kernel" - "Catch2::Catch2" -) - -add_library("os::kernel_test_support" ALIAS "kernel_test_support") - -#[============================================================================[ -# Kernel Tests -#]============================================================================] - -add_executable("kernel_tests" - "src/test_support.tests.cpp" -) - -target_include_directories("kernel_tests" PRIVATE - "include" -) - -target_link_libraries("kernel_tests" PRIVATE - "os::kernel_test_support" - "libs::kstd" -) - -set_target_properties("kernel_tests" PROPERTIES - C_CLANG_TIDY "" - CXX_CLANG_TIDY "" - EXCLUDE_FROM_ALL NO -) - -enable_coverage("kernel_tests") - -add_executable("os::kernel_tests" ALIAS "kernel_tests") - -catch_discover_tests("os::kernel_tests") \ No newline at end of file diff --git a/kernel/tests/include/kernel/tests/cpu.hpp b/kernel/tests/include/kernel/tests/cpu.hpp deleted file mode 100644 index 81c0c6f..0000000 --- a/kernel/tests/include/kernel/tests/cpu.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef TEACHOS_KERNEL_TESTS_CPU_HPP -#define TEACHOS_KERNEL_TESTS_CPU_HPP - -#include - -namespace kernel::tests::cpu -{ - - struct halt : std::runtime_error - { - halt() - : std::runtime_error{"CPU halt requested!"} - {} - }; - -} // namespace kernel::tests::cpu - -#endif \ No newline at end of file diff --git a/kernel/tests/include/kernel/tests/log_buffer.hpp b/kernel/tests/include/kernel/tests/log_buffer.hpp deleted file mode 100644 index 5f2a1a7..0000000 --- a/kernel/tests/include/kernel/tests/log_buffer.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef KERNEL_TESTS_LOG_BUFFER_HPP -#define KERNEL_TESTS_LOG_BUFFER_HPP - -#include -#include - -namespace kernel::tests::log_buffer -{ - - //! Append a message to the testing log buffer. - //! - //! @param message The message to append. - auto append(std::string const & message) -> void; - - //! Clear the testing log buffer. - auto clear() -> void; - - //! Get the testing log buffer as a single string. - //! - //! @return The testing log buffer as a single string. - auto flat_messages() -> std::string; - - //! Get the testing log buffer. - //! - //! @note Messages may be split across multiple entries if they are longer than the internal kernel print buffer size. - //! - //! @return The testing log buffer. - auto messages() -> std::vector const &; - -} // namespace kernel::tests::log_buffer - -#endif \ No newline at end of file diff --git a/kernel/tests/include/kernel/tests/simulated_memory.hpp b/kernel/tests/include/kernel/tests/simulated_memory.hpp deleted file mode 100644 index 156b1e1..0000000 --- a/kernel/tests/include/kernel/tests/simulated_memory.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef TEACHOS_KERNEL_TESTS_SIMULATED_MEMORY_HPP -#define TEACHOS_KERNEL_TESTS_SIMULATED_MEMORY_HPP - -#include - -#include - -namespace kernel::tests::simulated_memory -{ - - auto init(kstd::units::bytes size) -> void; - - auto pmm_metadata_base() -> std::byte *; - - auto ram_base() -> std::byte *; - -} // namespace kernel::tests::simulated_memory - -#endif \ No newline at end of file diff --git a/kernel/tests/kapi/cio.cpp b/kernel/tests/kapi/cio.cpp deleted file mode 100644 index 6359fa8..0000000 --- a/kernel/tests/kapi/cio.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include - -#include "kernel/tests/log_buffer.hpp" - -#include -#include -#include - -namespace kapi::cio -{ - - namespace - { - - class test_output_device : public output_device - { - public: - test_output_device() = default; - - auto write(output_stream stream, std::string_view text) -> void override - { - auto & standard_stream = stream == output_stream::stdout ? std::cout : std::cerr; - standard_stream << text; - if (text != "\n") - { - kernel::tests::log_buffer::append(std::string{text}); - } - } - } device{}; - - } // namespace - - auto init() -> void - { - set_output_device(device); - } - -} // namespace kapi::cio \ No newline at end of file diff --git a/kernel/tests/kapi/cpu.cpp b/kernel/tests/kapi/cpu.cpp deleted file mode 100644 index 3ecaadc..0000000 --- a/kernel/tests/kapi/cpu.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "kernel/tests/cpu.hpp" - -#include - -#include -#include - -namespace kapi::cpu -{ - - auto init() -> void - { - auto static initialized = std::atomic_flag{}; - if (initialized.test_and_set()) - { - throw std::logic_error("kapi::cpu::init() called more than once"); - } - - // TODO: make sure that simulated interrupt can run. - - return; - } - - auto halt() -> void - { - throw kernel::tests::cpu::halt{}; - } - -} // namespace kapi::cpu \ No newline at end of file diff --git a/kernel/tests/kapi/interrupts.cpp b/kernel/tests/kapi/interrupts.cpp deleted file mode 100644 index 0077266..0000000 --- a/kernel/tests/kapi/interrupts.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -namespace kapi::interrupts -{ - - auto enable() -> void - { - // TODO: enable simulated interrupts. - } - -} // namespace kapi::interrupts \ No newline at end of file diff --git a/kernel/tests/kapi/memory.cpp b/kernel/tests/kapi/memory.cpp deleted file mode 100644 index 6de2f60..0000000 --- a/kernel/tests/kapi/memory.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "kapi/memory.hpp" - -#include - -#include "kernel/tests/simulated_memory.hpp" - -#include -#include - -#include -#include -#include -#include - -namespace kapi::memory -{ - - namespace - { - //! The size of the simulated RAM. - constexpr auto simulate_memory_size = kstd::units::MiB(32); - - struct test_boostrap_frame_allocator : frame_allocator - { - auto mark_used(frame) -> void override {} - - auto allocate_many(std::size_t count) noexcept -> std::optional> override - { - auto start = next_free_frame; - next_free_frame += count; - return std::pair{frame{start}, count}; - } - - auto release_many(std::pair) -> void override {} - - std::size_t next_free_frame{}; - } boostrap_allocator; - - struct test_page_mapper : page_mapper - { - auto map(page page, frame frame, flags flags) -> std::byte * override - { - kstd::println("mapping page {} onto frame {} with flags {}", page.number(), frame.number(), - static_cast(flags)); - - if ((page.start_address() & pmm_metadata_base.raw()) == pmm_metadata_base.raw()) - { - auto offset = page.start_address() & ~pmm_metadata_base.raw(); - return kernel::tests::simulated_memory::pmm_metadata_base() + offset; - } - - return nullptr; - } - - auto unmap(page page) -> void override - { - kstd::println("unmapping page {}", page.number()); - } - - auto try_unmap(page page) noexcept -> bool override - { - kstd::println("trying to unmap page {}", page.number()); - return false; - } - } test_mapper; - - auto handoff_to_kernel_pmm(frame_allocator & new_allocator) -> void - { - for (auto i = 0uz; i < boostrap_allocator.next_free_frame; ++i) - { - new_allocator.mark_used(frame{i}); - } - } - - } // namespace - - auto init() -> void - { - kernel::tests::simulated_memory::init(simulate_memory_size); - set_frame_allocator(boostrap_allocator); - set_page_mapper(test_mapper); - - init_pmm(simulate_memory_size / frame::size, handoff_to_kernel_pmm); - } - -} // namespace kapi::memory \ No newline at end of file diff --git a/kernel/tests/src/log_buffer.cpp b/kernel/tests/src/log_buffer.cpp deleted file mode 100644 index 9e30afb..0000000 --- a/kernel/tests/src/log_buffer.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "kernel/tests/log_buffer.hpp" - -#include -#include -#include - -namespace kernel::tests::log_buffer -{ - - namespace - { - std::vector recorded_messages{}; - } - - auto append(std::string const & message) -> void - { - recorded_messages.push_back(message); - } - - auto clear() -> void - { - recorded_messages.clear(); - } - - auto flat_messages() -> std::string - { - return std::ranges::fold_left(recorded_messages, std::string{}, - [](std::string accumulator, std::string const & message) { - accumulator += message; - return accumulator; - }); - } - - auto messages() -> std::vector const & - { - return recorded_messages; - } - -} // namespace kernel::tests::log_buffer diff --git a/kernel/tests/src/main.cpp b/kernel/tests/src/main.cpp deleted file mode 100644 index b3ae142..0000000 --- a/kernel/tests/src/main.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "kapi/interrupts.hpp" -#include "kapi/memory.hpp" -#include -#include - -#include - -auto main(int argc, char ** argv) -> int -{ - auto session = Catch::Session{}; - - if (auto result = session.applyCommandLine(argc, argv); result != 0) - { - return result; - } - - auto const & config = session.configData(); - auto skip_init = config.listTests || // - config.listTags || // - config.listReporters || // - config.listListeners || // - config.showHelp; - - if (!skip_init) - { - kapi::cio::init(); - kapi::cpu::init(); - kapi::interrupts::enable(); - - kapi::memory::init(); - // note: no kernel heap is created, since we are in the test environment. Nonetheless, we still initialize the - // memory subsystem, so that components that rely on it can be tested. No component must ever rely on the heap - // allocator directly, rather they have to go through the new and delete. However, some components may use the frame - // allocator and page mapper in order to perform their tasks. - } - - return session.run(); -} diff --git a/kernel/tests/src/simulated_memory.cpp b/kernel/tests/src/simulated_memory.cpp deleted file mode 100644 index 9a9b354..0000000 --- a/kernel/tests/src/simulated_memory.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "kernel/tests/simulated_memory.hpp" - -#include - -#include -#include - -namespace kernel::tests::simulated_memory -{ - - namespace - { - auto constinit ram_storage = std::vector{}; - auto constinit pmm_storage = std::vector{}; - } // namespace - - auto init(kstd::units::bytes size) -> void - { - ram_storage.resize(size / kstd::units::bytes{1}); - pmm_storage.resize(size / kstd::units::bytes{1}); - } - - auto pmm_metadata_base() -> std::byte * - { - return pmm_storage.data(); - } - - auto ram_base() -> std::byte * - { - return ram_storage.data(); - } - -} // namespace kernel::tests::simulated_memory \ No newline at end of file diff --git a/kernel/tests/src/test_support.tests.cpp b/kernel/tests/src/test_support.tests.cpp deleted file mode 100644 index f835e65..0000000 --- a/kernel/tests/src/test_support.tests.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "kapi/cpu.hpp" -#include "kapi/system.hpp" - -#include "kernel/tests/cpu.hpp" -#include "kernel/tests/log_buffer.hpp" - -#include - -#include - -SCENARIO("Kernel test support infrastructure", "[support]") -{ - GIVEN("the test support infrastructure is initialized") - { - WHEN("a CPU halt is requested") - { - THEN("the correct exception is thrown") - { - REQUIRE_THROWS_AS(kapi::cpu::halt(), kernel::tests::cpu::halt); - } - } - - WHEN("a the system panics") - { - kernel::tests::log_buffer::clear(); - - THEN("the correct exception is thrown") - { - REQUIRE_THROWS_AS(kapi::system::panic("[kernel:tests] Test Panic"), kernel::tests::cpu::halt); - } - - THEN("the message is appended to the log buffer") - { - CHECK_THROWS(kapi::system::panic("[kernel:tests] Test Panic")); - REQUIRE(kernel::tests::log_buffer::flat_messages().contains("[kernel:tests] Test Panic")); - } - } - - WHEN("a regular print is issued") - { - kernel::tests::log_buffer::clear(); - - kstd::println("[kernel:tests] Test Print"); - - THEN("the message is appended to the log buffer") - { - REQUIRE(kernel::tests::log_buffer::flat_messages().contains("[kernel:tests] Test Print")); - } - } - } -} \ No newline at end of file -- cgit v1.2.3 From 6c1921d77a6d23bd5850db5b8db20e0f1bc67f40 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 12:23:00 +0200 Subject: kernel/tests: update coverage configuration --- .gitlab-ci.yml | 2 +- .lcovrc | 2 ++ kernel/CMakeLists.txt | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0b933c4..661ce63 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,7 +28,7 @@ bht: - cmake --build --preset bht-dbg - ctest --preset bht-dbg - lcov --config-file .lcovrc --capture --directory $(pwd) --output-file coverage.info - - lcov --config-file .lcovrc --list coverage.info + - lcov --list coverage.info - genhtml --prefix $(pwd) --output-directory coverage coverage.info - gcovr --root . --cobertura-pretty --output coverage/cobertura-coverage.xml after_script: diff --git a/.lcovrc b/.lcovrc index 07da866..d0758f0 100644 --- a/.lcovrc +++ b/.lcovrc @@ -1,5 +1,7 @@ exclude = /usr/include/* exclude = build/bht/_deps/* exclude = tests/* +exclude = **.tests.cpp +exclude = kapi/include/kapi/* ignore_errors = unused,empty,inconsistent \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index d6b3a1b..9db2ab7 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -88,7 +88,7 @@ if(CMAKE_CROSSCOMPILING) else() enable_coverage("kernel_objs") - add_library("kernel_test_support" + add_library("kernel_test_support" OBJECT "src/test_support/kapi/cpu.cpp" "src/test_support/kapi/cio.cpp" "src/test_support/kapi/interrupts.cpp" @@ -117,13 +117,13 @@ else() add_executable("os::kernel_tests" ALIAS "kernel_tests") target_link_libraries("kernel_tests" PRIVATE + "os::kernel" "os::kernel_test_support" ) set_target_properties("kernel_tests" PROPERTIES C_CLANG_TIDY "" CXX_CLANG_TIDY "" - EXCLUDE_FROM_ALL NO ) enable_coverage("kernel_tests") -- cgit v1.2.3 From 790ffa870dee2c14cd45f669c0eb3e95c15fd1b6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 13:31:20 +0200 Subject: kernel: add bitmap_allocator tests --- kernel/CMakeLists.txt | 3 + kernel/src/memory/bitmap_allocator.cpp | 20 +- kernel/src/memory/bitmap_allocator.tests.cpp | 289 +++++++++++++++++++++++++++ kernel/src/test_support/kapi/memory.cpp | 8 +- 4 files changed, 309 insertions(+), 11 deletions(-) create mode 100644 kernel/src/memory/bitmap_allocator.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 9db2ab7..926a682 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -111,6 +111,9 @@ else() # KSTD Shim Tests "kstd/print.tests.cpp" + # Memory Subsystem Tests + "src/memory/bitmap_allocator.tests.cpp" + # Test Executable Main "src/main.tests.cpp" ) diff --git a/kernel/src/memory/bitmap_allocator.cpp b/kernel/src/memory/bitmap_allocator.cpp index c010f77..caaf5a4 100644 --- a/kernel/src/memory/bitmap_allocator.cpp +++ b/kernel/src/memory/bitmap_allocator.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -17,7 +18,9 @@ namespace kernel::memory , m_frame_count{frame_count} , m_last_index{} { - std::ranges::fill(m_bitmap, ~0uz); + constexpr auto bits_per_word = 64uz; + auto to_fill = (frame_count + bits_per_word - 1) / bits_per_word; + std::ranges::fill(std::views::take(m_bitmap, to_fill), ~0uz); } auto bitmap_frame_allocator::allocate_many(std::size_t count) noexcept @@ -78,22 +81,25 @@ namespace kernel::memory auto bitmap_frame_allocator::test(std::size_t index) const noexcept -> bool { - auto entry_entry = index / 64; - auto entry_offset = index % 64; + constexpr auto bits_per_word = 64uz; + auto entry_entry = index / bits_per_word; + auto entry_offset = index % bits_per_word; return (m_bitmap[entry_entry] & (1uz << entry_offset)); } auto bitmap_frame_allocator::set(std::size_t index) noexcept -> void { - auto entry_entry = index / 64; - auto entry_offset = index % 64; + constexpr auto bits_per_word = 64uz; + auto entry_entry = index / bits_per_word; + auto entry_offset = index % bits_per_word; m_bitmap[entry_entry] |= (1uz << entry_offset); } auto bitmap_frame_allocator::clear(std::size_t index) noexcept -> void { - auto entry_entry = index / 64; - auto entry_offset = index % 64; + constexpr auto bits_per_word = 64uz; + auto entry_entry = index / bits_per_word; + auto entry_offset = index % bits_per_word; m_bitmap[entry_entry] &= ~(1uz << entry_offset); } diff --git a/kernel/src/memory/bitmap_allocator.tests.cpp b/kernel/src/memory/bitmap_allocator.tests.cpp new file mode 100644 index 0000000..56ec0a4 --- /dev/null +++ b/kernel/src/memory/bitmap_allocator.tests.cpp @@ -0,0 +1,289 @@ +#include "kernel/memory/bitmap_allocator.hpp" + +#include "kapi/memory.hpp" + +#include "catch2/matchers/catch_matchers.hpp" +#include "catch2/matchers/catch_matchers_range_equals.hpp" + +#include + +#include +#include +#include +#include +#include + +constexpr auto all_bits_set = std::numeric_limits::max(); +constexpr auto available_frames = 1024uz; + +SCENARIO("Bitmap allocator construction and initialization", "[memory][bitmap_allocator]") +{ + GIVEN("A storage region") + { + auto storage = std::vector(available_frames / 64, 0uz); + + WHEN("constructing the allocator with 0 frames") + { + auto allocator = kernel::memory::bitmap_frame_allocator{std::span(storage), 0}; + + THEN("the storage region is not modified") + { + REQUIRE_THAT(storage, Catch::Matchers::RangeEquals(std::vector(16, 0uz))); + } + } + + WHEN("constructing with 1 frame") + { + auto allocator = kernel::memory::bitmap_frame_allocator{std::span(storage), 1}; + + THEN("the first word of the storage region is set to all ones") + { + REQUIRE_THAT(std::views::take(storage, 1), Catch::Matchers::RangeEquals(std::vector(1, all_bits_set))); + } + + THEN("the rest of the storage region is not modified") + { + REQUIRE_THAT(std::views::drop(storage, 1), Catch::Matchers::RangeEquals(std::vector(15, 0uz))); + } + } + + WHEN("constructing with 64 frames") + { + auto allocator = kernel::memory::bitmap_frame_allocator{std::span(storage), 64}; + + THEN("the first word of the storage region is set to all ones") + { + REQUIRE_THAT(std::views::take(storage, 1), Catch::Matchers::RangeEquals(std::vector(1, all_bits_set))); + } + + THEN("the rest of the storage region is not modified") + { + REQUIRE_THAT(std::views::drop(storage, 1), Catch::Matchers::RangeEquals(std::vector(15, 0uz))); + } + } + + WHEN("constructing with all available frames") + { + auto allocator = kernel::memory::bitmap_frame_allocator{std::span(storage), available_frames}; + + THEN("the storage region is filled with all ones") + { + REQUIRE_THAT(storage, Catch::Matchers::RangeEquals(std::vector(16, all_bits_set))); + } + } + + WHEN("constructing with half the available frames") + { + auto allocator = kernel::memory::bitmap_frame_allocator{std::span(storage), available_frames / 2}; + + THEN("the first half of the storage region is filled with all ones") + { + REQUIRE_THAT(std::views::take(storage, (available_frames / 2) / 64), + Catch::Matchers::RangeEquals(std::vector((available_frames / 2) / 64, all_bits_set))); + } + + THEN("the second half of the storage region is filled with all zeros") + { + REQUIRE_THAT(std::views::drop(storage, (available_frames / 2) / 64), + Catch::Matchers::RangeEquals(std::vector((available_frames / 2) / 64, 0uz))); + } + } + } +} + +SCENARIO("Bitmap allocator frame allocation", "[memory][bitmap_allocator]") +{ + GIVEN("A storage region") + { + auto storage = std::vector(available_frames / 64, 0uz); + + AND_GIVEN("an allocator constructed with all available frames but no free ones") + { + auto allocator = kernel::memory::bitmap_frame_allocator{std::span(storage), available_frames}; + + WHEN("allocating 1 frame") + { + auto result = allocator.allocate_many(1); + + THEN("the result is empty") + { + REQUIRE_FALSE(result.has_value()); + } + } + } + + AND_GIVEN("an allocator constructed with all available frames but only one free one") + { + auto allocator = kernel::memory::bitmap_frame_allocator{std::span(storage), available_frames}; + allocator.release_many({kapi::memory::frame{0}, 1}); + + WHEN("allocating 1 frame") + { + auto result = allocator.allocate_many(1); + + THEN("the result is not empty") + { + REQUIRE(result.has_value()); + } + + THEN("the result contains 1 frame") + { + REQUIRE(result->second == 1); + } + } + + WHEN("allocating more frames than are free") + { + auto result = allocator.allocate_many(2); + + THEN("the result is empty") + { + REQUIRE_FALSE(result.has_value()); + } + + AND_WHEN("allocating a single frame") + { + auto result = allocator.allocate_many(1); + + THEN("the result is not empty") + { + REQUIRE(result.has_value()); + } + } + + WHEN("allocating 0 frames") + { + auto result = allocator.allocate_many(0); + + THEN("the result is empty") + { + REQUIRE_FALSE(result.has_value()); + } + } + } + } + + AND_GIVEN("an allocator with many single frame holes") + { + auto allocator = kernel::memory::bitmap_frame_allocator{std::span(storage), available_frames}; + for (auto i = 0uz; i < available_frames; i += 2) + { + allocator.release_many({kapi::memory::frame{i}, 1}); + } + + WHEN("allocating 1 frame") + { + auto result = allocator.allocate_many(1); + + THEN("the result is not empty") + { + REQUIRE(result.has_value()); + } + + THEN("the result contains 1 frame") + { + REQUIRE(result->second == 1); + } + } + + WHEN("allocating 2 frames") + { + auto result = allocator.allocate_many(2); + + THEN("the result is empty") + { + REQUIRE_FALSE(result.has_value()); + } + } + } + + AND_GIVEN("and allocator with all frames marked as free") + { + auto allocator = kernel::memory::bitmap_frame_allocator{std::span(storage), available_frames}; + allocator.release_many({kapi::memory::frame{0}, available_frames}); + + WHEN("allocating 1 frame") + { + auto result = allocator.allocate_many(1); + + THEN("the result is not empty") + { + REQUIRE(result.has_value()); + } + + THEN("the result contains 1 frame") + { + REQUIRE(result->second == 1); + } + } + + WHEN("allocating multiple frames") + { + auto result = allocator.allocate_many(20); + + THEN("the result is not empty") + { + REQUIRE(result.has_value()); + } + + THEN("the result contains 20 frames") + { + REQUIRE(result->second == 20); + } + } + + WHEN("marking all frames as used") + { + for (auto i = 0uz; i < available_frames; i++) + { + allocator.mark_used(kapi::memory::frame{i}); + } + + THEN("the allocator has no free frames") + { + REQUIRE_FALSE(allocator.allocate_many(1).has_value()); + } + } + } + + AND_GIVEN("an allocator with a contiguous block of free frames") + { + auto allocator = kernel::memory::bitmap_frame_allocator{std::span(storage), available_frames}; + allocator.release_many({kapi::memory::frame{0}, available_frames}); + for (auto i = 0uz; i < available_frames / 2; i++) + { + allocator.mark_used(kapi::memory::frame{i}); + } + + WHEN("allocating a single frame") + { + auto result = allocator.allocate_many(1); + + THEN("the result is not empty") + { + REQUIRE(result.has_value()); + } + + THEN("the result contains 1 frame") + { + REQUIRE(result->second == 1); + } + } + + WHEN("allocating multiple frames") + { + auto result = allocator.allocate_many(20); + + THEN("the result is not empty") + { + REQUIRE(result.has_value()); + } + + THEN("the result contains 20 frames") + { + REQUIRE(result->second == 20); + } + } + } + } +} \ No newline at end of file diff --git a/kernel/src/test_support/kapi/memory.cpp b/kernel/src/test_support/kapi/memory.cpp index 6de2f60..556962c 100644 --- a/kernel/src/test_support/kapi/memory.cpp +++ b/kernel/src/test_support/kapi/memory.cpp @@ -19,6 +19,7 @@ namespace kapi::memory { //! The size of the simulated RAM. constexpr auto simulate_memory_size = kstd::units::MiB(32); + constexpr auto number_of_simulated_frames = simulate_memory_size / frame::size; struct test_boostrap_frame_allocator : frame_allocator { @@ -66,10 +67,9 @@ namespace kapi::memory auto handoff_to_kernel_pmm(frame_allocator & new_allocator) -> void { - for (auto i = 0uz; i < boostrap_allocator.next_free_frame; ++i) - { - new_allocator.mark_used(frame{i}); - } + auto first_free_frame = boostrap_allocator.next_free_frame; + auto number_of_free_frames = number_of_simulated_frames - first_free_frame; + new_allocator.release_many({frame{first_free_frame}, number_of_free_frames}); } } // namespace -- cgit v1.2.3 From 7fc60f9350ebf86e2e13d09af159635ee8a1d086 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 14:25:04 +0200 Subject: kernel: add ram disk device tests --- kernel/CMakeLists.txt | 3 + .../src/devices/storage/ram_disk/device.tests.cpp | 117 +++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 kernel/src/devices/storage/ram_disk/device.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 926a682..4fadf4c 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -114,6 +114,9 @@ else() # Memory Subsystem Tests "src/memory/bitmap_allocator.tests.cpp" + # Storage Subsystem Tests + "src/devices/storage/ram_disk/device.tests.cpp" + # Test Executable Main "src/main.tests.cpp" ) diff --git a/kernel/src/devices/storage/ram_disk/device.tests.cpp b/kernel/src/devices/storage/ram_disk/device.tests.cpp new file mode 100644 index 0000000..eacdb04 --- /dev/null +++ b/kernel/src/devices/storage/ram_disk/device.tests.cpp @@ -0,0 +1,117 @@ +#include "kernel/devices/storage/ram_disk/device.hpp" + +#include "kapi/boot_module/boot_module.hpp" +#include "kapi/memory.hpp" + +#include "catch2/matchers/catch_matchers.hpp" +#include "catch2/matchers/catch_matchers_range_equals.hpp" + +#include + +#include +#include +#include +#include + +SCENARIO("RAM Disk Device Construction and Initialization", "[ram_disk_device]") +{ + GIVEN("an empty boot module") + { + kapi::boot_modules::boot_module boot_module{}; + boot_module.start_address = kapi::memory::linear_address{nullptr}; + boot_module.size = 0; + + WHEN("constructing the device") + { + kernel::devices::storage::ram_disk::device device{boot_module, 0, 0}; + + THEN("init return false") + { + REQUIRE_FALSE(device.init()); + } + } + } + + GIVEN("a boot module with a valid start address and size") + { + kapi::boot_modules::boot_module boot_module{}; + boot_module.start_address = kapi::memory::linear_address{reinterpret_cast(0x1000)}; + boot_module.size = 512; + + WHEN("constructing the device") + { + kernel::devices::storage::ram_disk::device device{boot_module, 0, 0}; + + THEN("init return true") + { + REQUIRE(device.init()); + } + } + } +} + +SCENARIO("RAM Disk Device Read and Write", "[ram_disk_device]") +{ + GIVEN("a device initialized with a valid boot module") + { + auto storage = std::vector{4096, std::byte{0xff}}; + auto module = + kapi::boot_modules::boot_module{"test_module", kapi::memory::linear_address{storage.data()}, storage.size()}; + auto device = kernel::devices::storage::ram_disk::device{module, 0, 0}; + REQUIRE(device.init()); + + WHEN("reading a full block from the device") + { + auto buffer = std::vector(device.block_size()); + device.read_block(0, buffer.data()); + + THEN("the buffer is filled with the module data") + { + REQUIRE_THAT(buffer, Catch::Matchers::RangeEquals(std::views::take(storage, device.block_size()))); + } + } + + WHEN("reading from a block index beyond the module size") + { + auto buffer = std::vector(device.block_size()); + device.read_block(10, buffer.data()); + + THEN("the buffer is filled with zeros") + { + REQUIRE_THAT(buffer, Catch::Matchers::RangeEquals(std::vector(device.block_size(), std::byte{0}))); + } + } + + WHEN("reading into a null buffer") + { + REQUIRE_THROWS_AS(device.read_block(0, nullptr), std::runtime_error); + } + + WHEN("writing to a full block") + { + auto buffer = std::vector(device.block_size(), std::byte{0x01}); + device.write_block(0, buffer.data()); + + THEN("the module data is updated") + { + REQUIRE_THAT(std::views::take(storage, device.block_size()), Catch::Matchers::RangeEquals(buffer)); + } + } + + WHEN("writing to a block index beyond the module size") + { + auto buffer = std::vector(device.block_size(), std::byte{0x01}); + device.write_block(10, buffer.data()); + + THEN("the module data is not updated") + { + REQUIRE_THAT(storage, Catch::Matchers::RangeEquals(std::vector{4096, std::byte{0xff}})); + } + } + + WHEN("writing from a null buffer") + { + REQUIRE_THROWS_AS(device.write_block(0, nullptr), std::runtime_error); + } + } +} \ No newline at end of file -- cgit v1.2.3 From 38bdee2ba829999862e37999dc212055ebedc4c6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 15:52:43 +0200 Subject: kstd: fix signatures of libc functions --- libs/kstd/include/kstd/cstring | 10 +++++----- libs/kstd/src/libc/string.cpp | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libs/kstd/include/kstd/cstring b/libs/kstd/include/kstd/cstring index a5c41fa..bd8b28d 100644 --- a/libs/kstd/include/kstd/cstring +++ b/libs/kstd/include/kstd/cstring @@ -8,12 +8,12 @@ namespace kstd::libc extern "C" { - auto memcpy(void * dest, void const * src, std::size_t size) -> void *; - auto memset(void * dest, int value, std::size_t size) -> void *; - auto memmove(void * dest, void const * src, std::size_t size) -> void *; - auto memcmp(void const * lhs, void const * rhs, std::size_t size) -> std::size_t; + auto memcpy(void * dest, void const * src, std::size_t size) noexcept -> void *; + auto memset(void * dest, int value, std::size_t size) noexcept -> void *; + auto memmove(void * dest, void const * src, std::size_t size) noexcept -> void *; + auto memcmp(void const * lhs, void const * rhs, std::size_t size) noexcept -> int; - auto strlen(char const * string) -> std::size_t; + auto strlen(char const * string) noexcept -> std::size_t; } } // namespace kstd::libc diff --git a/libs/kstd/src/libc/string.cpp b/libs/kstd/src/libc/string.cpp index 63f012c..c9fada1 100644 --- a/libs/kstd/src/libc/string.cpp +++ b/libs/kstd/src/libc/string.cpp @@ -9,7 +9,7 @@ namespace kstd::libc { - auto memcpy(void * dest, void const * src, std::size_t size) -> void * + auto memcpy(void * dest, void const * src, std::size_t size) noexcept -> void * { auto dest_span = std::span{static_cast(dest), size}; auto src_span = std::span{static_cast(src), size}; @@ -22,7 +22,7 @@ namespace kstd::libc return dest; } - auto memset(void * dest, int value, std::size_t size) -> void * + auto memset(void * dest, int value, std::size_t size) noexcept -> void * { auto const byte_value = static_cast(static_cast(value)); auto dest_span = std::span{static_cast(dest), size}; @@ -35,7 +35,7 @@ namespace kstd::libc return dest; } - auto memcmp(void const * lhs, void const * rhs, std::size_t size) -> std::size_t + auto memcmp(void const * lhs, void const * rhs, std::size_t size) noexcept -> int { auto left_span = std::span{static_cast(lhs), size}; auto right_span = std::span{static_cast(rhs), size}; @@ -49,7 +49,7 @@ namespace kstd::libc return std::bit_cast(*mismatched.in1) - std::bit_cast(*mismatched.in2); } - auto memmove(void * dest, void const * src, std::size_t size) -> void * + auto memmove(void * dest, void const * src, std::size_t size) noexcept -> void * { auto dest_span = std::span{static_cast(dest), size}; auto src_span = std::span{static_cast(src), size}; @@ -70,7 +70,7 @@ namespace kstd::libc return dest; } - auto strlen(char const * string) -> std::size_t + auto strlen(char const * string) noexcept -> std::size_t { return std::distance(string, std::ranges::find(string, nullptr, '\0')); } -- cgit v1.2.3 From eaec1833e978d2443ffdfc226fff60d0b5571cb6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 16:27:51 +0200 Subject: kernel/tests: move initialization to a listener --- kernel/CMakeLists.txt | 6 +-- kernel/include/kernel/tests/simulated_memory.hpp | 2 + kernel/src/main.tests.cpp | 38 ------------------ kernel/src/test_support/kapi/cio.cpp | 5 +++ kernel/src/test_support/kapi/cpu.cpp | 18 +++++++-- kernel/src/test_support/kapi/memory.cpp | 14 +++++-- kernel/src/test_support/simulated_memory.cpp | 6 +++ kernel/src/test_support/state_reset_listener.cpp | 50 ++++++++++++++++++++++++ 8 files changed, 90 insertions(+), 49 deletions(-) delete mode 100644 kernel/src/main.tests.cpp create mode 100644 kernel/src/test_support/state_reset_listener.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 4fadf4c..ffd5156 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -95,12 +95,13 @@ else() "src/test_support/kapi/memory.cpp" "src/test_support/log_buffer.cpp" "src/test_support/simulated_memory.cpp" + "src/test_support/state_reset_listener.cpp" ) add_library("os::kernel_test_support" ALIAS "kernel_test_support") target_link_libraries("kernel_test_support" PUBLIC "os::kernel" - "Catch2::Catch2" + "Catch2::Catch2WithMain" ) add_executable("kernel_tests" @@ -116,9 +117,6 @@ else() # Storage Subsystem Tests "src/devices/storage/ram_disk/device.tests.cpp" - - # Test Executable Main - "src/main.tests.cpp" ) add_executable("os::kernel_tests" ALIAS "kernel_tests") diff --git a/kernel/include/kernel/tests/simulated_memory.hpp b/kernel/include/kernel/tests/simulated_memory.hpp index 156b1e1..fee4d7a 100644 --- a/kernel/include/kernel/tests/simulated_memory.hpp +++ b/kernel/include/kernel/tests/simulated_memory.hpp @@ -10,6 +10,8 @@ namespace kernel::tests::simulated_memory auto init(kstd::units::bytes size) -> void; + auto reset() -> void; + auto pmm_metadata_base() -> std::byte *; auto ram_base() -> std::byte *; diff --git a/kernel/src/main.tests.cpp b/kernel/src/main.tests.cpp deleted file mode 100644 index b3ae142..0000000 --- a/kernel/src/main.tests.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "kapi/interrupts.hpp" -#include "kapi/memory.hpp" -#include -#include - -#include - -auto main(int argc, char ** argv) -> int -{ - auto session = Catch::Session{}; - - if (auto result = session.applyCommandLine(argc, argv); result != 0) - { - return result; - } - - auto const & config = session.configData(); - auto skip_init = config.listTests || // - config.listTags || // - config.listReporters || // - config.listListeners || // - config.showHelp; - - if (!skip_init) - { - kapi::cio::init(); - kapi::cpu::init(); - kapi::interrupts::enable(); - - kapi::memory::init(); - // note: no kernel heap is created, since we are in the test environment. Nonetheless, we still initialize the - // memory subsystem, so that components that rely on it can be tested. No component must ever rely on the heap - // allocator directly, rather they have to go through the new and delete. However, some components may use the frame - // allocator and page mapper in order to perform their tasks. - } - - return session.run(); -} diff --git a/kernel/src/test_support/kapi/cio.cpp b/kernel/src/test_support/kapi/cio.cpp index 6359fa8..0dd4b8c 100644 --- a/kernel/src/test_support/kapi/cio.cpp +++ b/kernel/src/test_support/kapi/cio.cpp @@ -35,4 +35,9 @@ namespace kapi::cio set_output_device(device); } + auto reset() -> void + { + kernel::tests::log_buffer::clear(); + } + } // namespace kapi::cio \ No newline at end of file diff --git a/kernel/src/test_support/kapi/cpu.cpp b/kernel/src/test_support/kapi/cpu.cpp index 3ecaadc..9b2f0b9 100644 --- a/kernel/src/test_support/kapi/cpu.cpp +++ b/kernel/src/test_support/kapi/cpu.cpp @@ -8,17 +8,29 @@ namespace kapi::cpu { - auto init() -> void + namespace { auto static initialized = std::atomic_flag{}; + } + + auto reset() -> void + { + if (!initialized.test()) + { + throw std::logic_error{"kapi::cpu::reset() called before kapi::cpu::init()"}; + } + + initialized.clear(); + } + + auto init() -> void + { if (initialized.test_and_set()) { throw std::logic_error("kapi::cpu::init() called more than once"); } // TODO: make sure that simulated interrupt can run. - - return; } auto halt() -> void diff --git a/kernel/src/test_support/kapi/memory.cpp b/kernel/src/test_support/kapi/memory.cpp index 556962c..4f261ac 100644 --- a/kernel/src/test_support/kapi/memory.cpp +++ b/kernel/src/test_support/kapi/memory.cpp @@ -21,7 +21,7 @@ namespace kapi::memory constexpr auto simulate_memory_size = kstd::units::MiB(32); constexpr auto number_of_simulated_frames = simulate_memory_size / frame::size; - struct test_boostrap_frame_allocator : frame_allocator + struct test_bootstrap_frame_allocator : frame_allocator { auto mark_used(frame) -> void override {} @@ -35,7 +35,7 @@ namespace kapi::memory auto release_many(std::pair) -> void override {} std::size_t next_free_frame{}; - } boostrap_allocator; + } bootstrap_allocator; struct test_page_mapper : page_mapper { @@ -67,7 +67,7 @@ namespace kapi::memory auto handoff_to_kernel_pmm(frame_allocator & new_allocator) -> void { - auto first_free_frame = boostrap_allocator.next_free_frame; + auto first_free_frame = bootstrap_allocator.next_free_frame; auto number_of_free_frames = number_of_simulated_frames - first_free_frame; new_allocator.release_many({frame{first_free_frame}, number_of_free_frames}); } @@ -77,10 +77,16 @@ namespace kapi::memory auto init() -> void { kernel::tests::simulated_memory::init(simulate_memory_size); - set_frame_allocator(boostrap_allocator); + set_frame_allocator(bootstrap_allocator); set_page_mapper(test_mapper); init_pmm(simulate_memory_size / frame::size, handoff_to_kernel_pmm); } + auto reset() -> void + { + kernel::tests::simulated_memory::reset(); + bootstrap_allocator.next_free_frame = 0; + } + } // namespace kapi::memory \ No newline at end of file diff --git a/kernel/src/test_support/simulated_memory.cpp b/kernel/src/test_support/simulated_memory.cpp index 9a9b354..49e172f 100644 --- a/kernel/src/test_support/simulated_memory.cpp +++ b/kernel/src/test_support/simulated_memory.cpp @@ -20,6 +20,12 @@ namespace kernel::tests::simulated_memory pmm_storage.resize(size / kstd::units::bytes{1}); } + auto reset() -> void + { + ram_storage.clear(); + pmm_storage.clear(); + } + auto pmm_metadata_base() -> std::byte * { return pmm_storage.data(); diff --git a/kernel/src/test_support/state_reset_listener.cpp b/kernel/src/test_support/state_reset_listener.cpp new file mode 100644 index 0000000..e201a10 --- /dev/null +++ b/kernel/src/test_support/state_reset_listener.cpp @@ -0,0 +1,50 @@ +#include "kapi/cio.hpp" +#include "kapi/cpu.hpp" +#include "kapi/memory.hpp" + +#include +#include +#include +#include + +namespace kapi +{ + namespace cio + { + auto reset() -> void; + } + + namespace cpu + { + auto reset() -> void; + } + + namespace memory + { + auto reset() -> void; + } +} // namespace kapi + +struct state_reset_listener : Catch::EventListenerBase +{ + using EventListenerBase::EventListenerBase; + + void testCaseStarting(Catch::TestCaseInfo const &) override + { + kapi::cio::init(); + kapi::cpu::init(); + kapi::memory::init(); + } + + void testCaseEnded(Catch::TestCaseStats const &) override + { + kapi::memory::reset(); + kapi::cpu::reset(); + kapi::cio::reset(); + } +}; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +CATCH_REGISTER_LISTENER(state_reset_listener); +#pragma GCC diagnostic pop \ No newline at end of file -- cgit v1.2.3 From 1a22d810ff2772d6b4dba5b1eb27d21285668c6f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 16:32:43 +0200 Subject: kernel/tests: improve memory cleanup --- kernel/src/test_support/kapi/memory.cpp | 35 +++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/kernel/src/test_support/kapi/memory.cpp b/kernel/src/test_support/kapi/memory.cpp index 4f261ac..652a3d3 100644 --- a/kernel/src/test_support/kapi/memory.cpp +++ b/kernel/src/test_support/kapi/memory.cpp @@ -35,7 +35,9 @@ namespace kapi::memory auto release_many(std::pair) -> void override {} std::size_t next_free_frame{}; - } bootstrap_allocator; + }; + + auto constinit bootstrap_allocator = std::optional{}; struct test_page_mapper : page_mapper { @@ -63,22 +65,31 @@ namespace kapi::memory kstd::println("trying to unmap page {}", page.number()); return false; } - } test_mapper; + }; + + auto constinit test_mapper = std::optional{}; auto handoff_to_kernel_pmm(frame_allocator & new_allocator) -> void { - auto first_free_frame = bootstrap_allocator.next_free_frame; + auto first_free_frame = bootstrap_allocator->next_free_frame; auto number_of_free_frames = number_of_simulated_frames - first_free_frame; new_allocator.release_many({frame{first_free_frame}, number_of_free_frames}); } + auto constinit previous_frame_allocator = std::optional{}; + auto constinit previous_page_mapper = std::optional{}; + } // namespace auto init() -> void { kernel::tests::simulated_memory::init(simulate_memory_size); - set_frame_allocator(bootstrap_allocator); - set_page_mapper(test_mapper); + + bootstrap_allocator.emplace(); + test_mapper.emplace(); + + previous_frame_allocator = set_frame_allocator(*bootstrap_allocator); + previous_page_mapper = set_page_mapper(*test_mapper); init_pmm(simulate_memory_size / frame::size, handoff_to_kernel_pmm); } @@ -86,7 +97,19 @@ namespace kapi::memory auto reset() -> void { kernel::tests::simulated_memory::reset(); - bootstrap_allocator.next_free_frame = 0; + + if (previous_frame_allocator && *previous_frame_allocator) + { + set_frame_allocator(**previous_frame_allocator); + } + + if (previous_page_mapper && *previous_page_mapper) + { + set_page_mapper(**previous_page_mapper); + } + + bootstrap_allocator.reset(); + test_mapper.reset(); } } // namespace kapi::memory \ No newline at end of file -- cgit v1.2.3 From b078f2bf4726b5c62584cebd84107ac1028bb083 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 17:26:13 +0200 Subject: kernel/tests: clean up fake memory implementation --- kernel/CMakeLists.txt | 1 + .../include/kernel/tests/bump_frame_allocator.hpp | 32 ++++++++ kernel/include/kernel/tests/page_mapper.hpp | 33 ++++++++ kernel/include/kernel/tests/simulated_memory.hpp | 18 +++-- kernel/src/test_support/kapi/memory.cpp | 92 +++++----------------- kernel/src/test_support/page_mapper.cpp | 57 ++++++++++++++ kernel/src/test_support/simulated_memory.cpp | 31 +++----- 7 files changed, 166 insertions(+), 98 deletions(-) create mode 100644 kernel/include/kernel/tests/bump_frame_allocator.hpp create mode 100644 kernel/include/kernel/tests/page_mapper.hpp create mode 100644 kernel/src/test_support/page_mapper.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index ffd5156..4264441 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -94,6 +94,7 @@ else() "src/test_support/kapi/interrupts.cpp" "src/test_support/kapi/memory.cpp" "src/test_support/log_buffer.cpp" + "src/test_support/page_mapper.cpp" "src/test_support/simulated_memory.cpp" "src/test_support/state_reset_listener.cpp" ) diff --git a/kernel/include/kernel/tests/bump_frame_allocator.hpp b/kernel/include/kernel/tests/bump_frame_allocator.hpp new file mode 100644 index 0000000..8344423 --- /dev/null +++ b/kernel/include/kernel/tests/bump_frame_allocator.hpp @@ -0,0 +1,32 @@ +#ifndef TEACHOS_KERNEL_TESTS_BUMP_FRAME_ALLOCATOR_HPP +#define TEACHOS_KERNEL_TESTS_BUMP_FRAME_ALLOCATOR_HPP + +#include "kapi/memory.hpp" + +#include +#include +#include + +namespace kernel::tests +{ + + struct bump_frame_allocator : kapi::memory::frame_allocator + { + auto mark_used(kapi::memory::frame) -> void override {} + + auto allocate_many(std::size_t count) noexcept + -> std::optional> override + { + auto start = next_free_frame; + next_free_frame += count; + return std::pair{kapi::memory::frame{start}, count}; + } + + auto release_many(std::pair) -> void override {} + + std::size_t next_free_frame{}; + }; + +} // namespace kernel::tests + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/tests/page_mapper.hpp b/kernel/include/kernel/tests/page_mapper.hpp new file mode 100644 index 0000000..9969976 --- /dev/null +++ b/kernel/include/kernel/tests/page_mapper.hpp @@ -0,0 +1,33 @@ +#ifndef TEACHOS_KERNEL_TESTS_PAGE_MAPPER_HPP +#define TEACHOS_KERNEL_TESTS_PAGE_MAPPER_HPP + +#include "kapi/memory.hpp" + +#include "kernel/tests/simulated_memory.hpp" + +#include + +#include +#include +#include + +namespace kernel::tests +{ + + struct page_mapper : kapi::memory::page_mapper + { + explicit page_mapper(kstd::units::bytes memory_size); + + auto map(kapi::memory::page page, kapi::memory::frame frame, flags) -> std::byte * override; + + auto unmap(kapi::memory::page page) -> void override; + + auto try_unmap(kapi::memory::page page) noexcept -> bool override; + + kernel::tests::simulated_memory memory; + std::unordered_map page_mappings; + }; + +} // namespace kernel::tests + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/tests/simulated_memory.hpp b/kernel/include/kernel/tests/simulated_memory.hpp index fee4d7a..446d558 100644 --- a/kernel/include/kernel/tests/simulated_memory.hpp +++ b/kernel/include/kernel/tests/simulated_memory.hpp @@ -4,18 +4,24 @@ #include #include +#include -namespace kernel::tests::simulated_memory +namespace kernel::tests { - auto init(kstd::units::bytes size) -> void; + struct simulated_memory + { + explicit simulated_memory(kstd::units::bytes size); - auto reset() -> void; + auto clear() -> void; - auto pmm_metadata_base() -> std::byte *; + auto ram_base() noexcept -> std::byte *; + [[nodiscard]] auto ram_base() const noexcept -> std::byte const *; - auto ram_base() -> std::byte *; + private: + std::vector m_memory; + }; -} // namespace kernel::tests::simulated_memory +} // namespace kernel::tests #endif \ No newline at end of file diff --git a/kernel/src/test_support/kapi/memory.cpp b/kernel/src/test_support/kapi/memory.cpp index 652a3d3..7b7a81e 100644 --- a/kernel/src/test_support/kapi/memory.cpp +++ b/kernel/src/test_support/kapi/memory.cpp @@ -2,15 +2,12 @@ #include -#include "kernel/tests/simulated_memory.hpp" +#include "kernel/tests/bump_frame_allocator.hpp" +#include "kernel/tests/page_mapper.hpp" -#include #include -#include -#include #include -#include namespace kapi::memory { @@ -18,97 +15,48 @@ namespace kapi::memory namespace { //! The size of the simulated RAM. - constexpr auto simulate_memory_size = kstd::units::MiB(32); - constexpr auto number_of_simulated_frames = simulate_memory_size / frame::size; + constexpr auto memory_size = kstd::units::MiB(32); + constexpr auto number_of_frames = memory_size / frame::size; - struct test_bootstrap_frame_allocator : frame_allocator - { - auto mark_used(frame) -> void override {} - - auto allocate_many(std::size_t count) noexcept -> std::optional> override - { - auto start = next_free_frame; - next_free_frame += count; - return std::pair{frame{start}, count}; - } - - auto release_many(std::pair) -> void override {} + auto constinit bump_allocator = std::optional{}; + auto constinit test_mapper = std::optional{}; - std::size_t next_free_frame{}; - }; - - auto constinit bootstrap_allocator = std::optional{}; - - struct test_page_mapper : page_mapper - { - auto map(page page, frame frame, flags flags) -> std::byte * override - { - kstd::println("mapping page {} onto frame {} with flags {}", page.number(), frame.number(), - static_cast(flags)); - - if ((page.start_address() & pmm_metadata_base.raw()) == pmm_metadata_base.raw()) - { - auto offset = page.start_address() & ~pmm_metadata_base.raw(); - return kernel::tests::simulated_memory::pmm_metadata_base() + offset; - } - - return nullptr; - } - - auto unmap(page page) -> void override - { - kstd::println("unmapping page {}", page.number()); - } - - auto try_unmap(page page) noexcept -> bool override - { - kstd::println("trying to unmap page {}", page.number()); - return false; - } - }; - - auto constinit test_mapper = std::optional{}; + auto constinit old_allocator = std::optional{}; + auto constinit old_mapper = std::optional{}; auto handoff_to_kernel_pmm(frame_allocator & new_allocator) -> void { - auto first_free_frame = bootstrap_allocator->next_free_frame; - auto number_of_free_frames = number_of_simulated_frames - first_free_frame; + auto first_free_frame = bump_allocator->next_free_frame; + auto number_of_free_frames = number_of_frames - first_free_frame; new_allocator.release_many({frame{first_free_frame}, number_of_free_frames}); } - auto constinit previous_frame_allocator = std::optional{}; - auto constinit previous_page_mapper = std::optional{}; - } // namespace auto init() -> void { - kernel::tests::simulated_memory::init(simulate_memory_size); + bump_allocator.emplace(); + test_mapper.emplace(memory_size); - bootstrap_allocator.emplace(); - test_mapper.emplace(); + old_allocator = set_frame_allocator(*bump_allocator); + old_mapper = set_page_mapper(*test_mapper); - previous_frame_allocator = set_frame_allocator(*bootstrap_allocator); - previous_page_mapper = set_page_mapper(*test_mapper); - - init_pmm(simulate_memory_size / frame::size, handoff_to_kernel_pmm); + init_pmm(memory_size / frame::size, handoff_to_kernel_pmm); } auto reset() -> void { - kernel::tests::simulated_memory::reset(); - - if (previous_frame_allocator && *previous_frame_allocator) + if (old_allocator && *old_allocator) { - set_frame_allocator(**previous_frame_allocator); + set_frame_allocator(**old_allocator); } - if (previous_page_mapper && *previous_page_mapper) + if (old_mapper && *old_mapper) { - set_page_mapper(**previous_page_mapper); + set_page_mapper(**old_mapper); } - bootstrap_allocator.reset(); + bump_allocator.reset(); test_mapper.reset(); } diff --git a/kernel/src/test_support/page_mapper.cpp b/kernel/src/test_support/page_mapper.cpp new file mode 100644 index 0000000..a026ec3 --- /dev/null +++ b/kernel/src/test_support/page_mapper.cpp @@ -0,0 +1,57 @@ +#include "kernel/tests/page_mapper.hpp" + +#include "kapi/memory.hpp" + +#include + +#include +#include +#include + +namespace kernel::tests +{ + + page_mapper::page_mapper(kstd::units::bytes memory_size) + : memory{memory_size} + {} + + auto page_mapper::map(kapi::memory::page page, kapi::memory::frame frame, flags) -> std::byte * + { + page_mappings.insert({page.number(), frame}); + + auto page_address = page.start_address(); + + if (page_address >= kapi::memory::mmio_base) + { + throw std::invalid_argument{"MMIO mapping not yet supported in testing!"}; + } + else if (page_address >= kapi::memory::higher_half_direct_map_base) + { + auto offset = frame.number() * kapi::memory::frame::size; + return memory.ram_base() + offset; + } + + return nullptr; + } + + auto page_mapper::unmap(kapi::memory::page page) -> void + { + if (!try_unmap(page)) + { + auto error = std::format("Page {} was never mapped!", page.number()); + throw std::invalid_argument{error}; + } + } + + auto page_mapper::try_unmap(kapi::memory::page page) noexcept -> bool + { + if (page_mappings.contains(page.number())) + { + page_mappings.erase(page.number()); + return true; + } + + return false; + } + +} // namespace kernel::tests \ No newline at end of file diff --git a/kernel/src/test_support/simulated_memory.cpp b/kernel/src/test_support/simulated_memory.cpp index 49e172f..d23350b 100644 --- a/kernel/src/test_support/simulated_memory.cpp +++ b/kernel/src/test_support/simulated_memory.cpp @@ -5,35 +5,26 @@ #include #include -namespace kernel::tests::simulated_memory +namespace kernel::tests { - namespace - { - auto constinit ram_storage = std::vector{}; - auto constinit pmm_storage = std::vector{}; - } // namespace - - auto init(kstd::units::bytes size) -> void - { - ram_storage.resize(size / kstd::units::bytes{1}); - pmm_storage.resize(size / kstd::units::bytes{1}); - } + simulated_memory::simulated_memory(kstd::units::bytes size) + : m_memory{size / kstd::units::bytes{1}} + {} - auto reset() -> void + auto simulated_memory::ram_base() noexcept -> std::byte * { - ram_storage.clear(); - pmm_storage.clear(); + return m_memory.data(); } - auto pmm_metadata_base() -> std::byte * + auto simulated_memory::ram_base() const noexcept -> std::byte const * { - return pmm_storage.data(); + return m_memory.data(); } - auto ram_base() -> std::byte * + auto simulated_memory::clear() -> void { - return ram_storage.data(); + m_memory.clear(); } -} // namespace kernel::tests::simulated_memory \ No newline at end of file +} // namespace kernel::tests \ No newline at end of file -- cgit v1.2.3 From 825d8bafef152a52cd76851764913fb12cdc685d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 17:29:25 +0200 Subject: kernel/test: rename test include to test_support --- .../kernel/test_support/bump_frame_allocator.hpp | 32 +++++++++++++++++++++ kernel/include/kernel/test_support/cpu.hpp | 18 ++++++++++++ kernel/include/kernel/test_support/log_buffer.hpp | 32 +++++++++++++++++++++ kernel/include/kernel/test_support/page_mapper.hpp | 33 ++++++++++++++++++++++ .../kernel/test_support/simulated_memory.hpp | 27 ++++++++++++++++++ .../include/kernel/tests/bump_frame_allocator.hpp | 32 --------------------- kernel/include/kernel/tests/cpu.hpp | 18 ------------ kernel/include/kernel/tests/log_buffer.hpp | 32 --------------------- kernel/include/kernel/tests/page_mapper.hpp | 33 ---------------------- kernel/include/kernel/tests/simulated_memory.hpp | 27 ------------------ kernel/kapi/cpu.tests.cpp | 2 +- kernel/kapi/system.tests.cpp | 4 +-- kernel/kstd/print.tests.cpp | 2 +- kernel/src/test_support/kapi/cio.cpp | 2 +- kernel/src/test_support/kapi/cpu.cpp | 2 +- kernel/src/test_support/kapi/memory.cpp | 4 +-- kernel/src/test_support/log_buffer.cpp | 2 +- kernel/src/test_support/page_mapper.cpp | 2 +- kernel/src/test_support/simulated_memory.cpp | 2 +- 19 files changed, 153 insertions(+), 153 deletions(-) create mode 100644 kernel/include/kernel/test_support/bump_frame_allocator.hpp create mode 100644 kernel/include/kernel/test_support/cpu.hpp create mode 100644 kernel/include/kernel/test_support/log_buffer.hpp create mode 100644 kernel/include/kernel/test_support/page_mapper.hpp create mode 100644 kernel/include/kernel/test_support/simulated_memory.hpp delete mode 100644 kernel/include/kernel/tests/bump_frame_allocator.hpp delete mode 100644 kernel/include/kernel/tests/cpu.hpp delete mode 100644 kernel/include/kernel/tests/log_buffer.hpp delete mode 100644 kernel/include/kernel/tests/page_mapper.hpp delete mode 100644 kernel/include/kernel/tests/simulated_memory.hpp diff --git a/kernel/include/kernel/test_support/bump_frame_allocator.hpp b/kernel/include/kernel/test_support/bump_frame_allocator.hpp new file mode 100644 index 0000000..5203565 --- /dev/null +++ b/kernel/include/kernel/test_support/bump_frame_allocator.hpp @@ -0,0 +1,32 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_BUMP_FRAME_ALLOCATOR_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_BUMP_FRAME_ALLOCATOR_HPP + +#include "kapi/memory.hpp" + +#include +#include +#include + +namespace kernel::tests +{ + + struct bump_frame_allocator : kapi::memory::frame_allocator + { + auto mark_used(kapi::memory::frame) -> void override {} + + auto allocate_many(std::size_t count) noexcept + -> std::optional> override + { + auto start = next_free_frame; + next_free_frame += count; + return std::pair{kapi::memory::frame{start}, count}; + } + + auto release_many(std::pair) -> void override {} + + std::size_t next_free_frame{}; + }; + +} // namespace kernel::tests + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/test_support/cpu.hpp b/kernel/include/kernel/test_support/cpu.hpp new file mode 100644 index 0000000..5445473 --- /dev/null +++ b/kernel/include/kernel/test_support/cpu.hpp @@ -0,0 +1,18 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_CPU_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_CPU_HPP + +#include + +namespace kernel::tests::cpu +{ + + struct halt : std::runtime_error + { + halt() + : std::runtime_error{"CPU halt requested!"} + {} + }; + +} // namespace kernel::tests::cpu + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/test_support/log_buffer.hpp b/kernel/include/kernel/test_support/log_buffer.hpp new file mode 100644 index 0000000..581b32f --- /dev/null +++ b/kernel/include/kernel/test_support/log_buffer.hpp @@ -0,0 +1,32 @@ +#ifndef KERNEL_TEST_SUPPORT_LOG_BUFFER_HPP +#define KERNEL_TEST_SUPPORT_LOG_BUFFER_HPP + +#include +#include + +namespace kernel::tests::log_buffer +{ + + //! Append a message to the testing log buffer. + //! + //! @param message The message to append. + auto append(std::string const & message) -> void; + + //! Clear the testing log buffer. + auto clear() -> void; + + //! Get the testing log buffer as a single string. + //! + //! @return The testing log buffer as a single string. + auto flat_messages() -> std::string; + + //! Get the testing log buffer. + //! + //! @note Messages may be split across multiple entries if they are longer than the internal kernel print buffer size. + //! + //! @return The testing log buffer. + auto messages() -> std::vector const &; + +} // namespace kernel::tests::log_buffer + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/test_support/page_mapper.hpp b/kernel/include/kernel/test_support/page_mapper.hpp new file mode 100644 index 0000000..a40aa2e --- /dev/null +++ b/kernel/include/kernel/test_support/page_mapper.hpp @@ -0,0 +1,33 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_PAGE_MAPPER_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_PAGE_MAPPER_HPP + +#include "kapi/memory.hpp" + +#include "kernel/test_support/simulated_memory.hpp" + +#include + +#include +#include +#include + +namespace kernel::tests +{ + + struct page_mapper : kapi::memory::page_mapper + { + explicit page_mapper(kstd::units::bytes memory_size); + + auto map(kapi::memory::page page, kapi::memory::frame frame, flags) -> std::byte * override; + + auto unmap(kapi::memory::page page) -> void override; + + auto try_unmap(kapi::memory::page page) noexcept -> bool override; + + kernel::tests::simulated_memory memory; + std::unordered_map page_mappings; + }; + +} // namespace kernel::tests + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/test_support/simulated_memory.hpp b/kernel/include/kernel/test_support/simulated_memory.hpp new file mode 100644 index 0000000..fed9f43 --- /dev/null +++ b/kernel/include/kernel/test_support/simulated_memory.hpp @@ -0,0 +1,27 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_SIMULATED_MEMORY_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_SIMULATED_MEMORY_HPP + +#include + +#include +#include + +namespace kernel::tests +{ + + struct simulated_memory + { + explicit simulated_memory(kstd::units::bytes size); + + auto clear() -> void; + + auto ram_base() noexcept -> std::byte *; + [[nodiscard]] auto ram_base() const noexcept -> std::byte const *; + + private: + std::vector m_memory; + }; + +} // namespace kernel::tests + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/tests/bump_frame_allocator.hpp b/kernel/include/kernel/tests/bump_frame_allocator.hpp deleted file mode 100644 index 8344423..0000000 --- a/kernel/include/kernel/tests/bump_frame_allocator.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef TEACHOS_KERNEL_TESTS_BUMP_FRAME_ALLOCATOR_HPP -#define TEACHOS_KERNEL_TESTS_BUMP_FRAME_ALLOCATOR_HPP - -#include "kapi/memory.hpp" - -#include -#include -#include - -namespace kernel::tests -{ - - struct bump_frame_allocator : kapi::memory::frame_allocator - { - auto mark_used(kapi::memory::frame) -> void override {} - - auto allocate_many(std::size_t count) noexcept - -> std::optional> override - { - auto start = next_free_frame; - next_free_frame += count; - return std::pair{kapi::memory::frame{start}, count}; - } - - auto release_many(std::pair) -> void override {} - - std::size_t next_free_frame{}; - }; - -} // namespace kernel::tests - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/tests/cpu.hpp b/kernel/include/kernel/tests/cpu.hpp deleted file mode 100644 index 81c0c6f..0000000 --- a/kernel/include/kernel/tests/cpu.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef TEACHOS_KERNEL_TESTS_CPU_HPP -#define TEACHOS_KERNEL_TESTS_CPU_HPP - -#include - -namespace kernel::tests::cpu -{ - - struct halt : std::runtime_error - { - halt() - : std::runtime_error{"CPU halt requested!"} - {} - }; - -} // namespace kernel::tests::cpu - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/tests/log_buffer.hpp b/kernel/include/kernel/tests/log_buffer.hpp deleted file mode 100644 index 5f2a1a7..0000000 --- a/kernel/include/kernel/tests/log_buffer.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef KERNEL_TESTS_LOG_BUFFER_HPP -#define KERNEL_TESTS_LOG_BUFFER_HPP - -#include -#include - -namespace kernel::tests::log_buffer -{ - - //! Append a message to the testing log buffer. - //! - //! @param message The message to append. - auto append(std::string const & message) -> void; - - //! Clear the testing log buffer. - auto clear() -> void; - - //! Get the testing log buffer as a single string. - //! - //! @return The testing log buffer as a single string. - auto flat_messages() -> std::string; - - //! Get the testing log buffer. - //! - //! @note Messages may be split across multiple entries if they are longer than the internal kernel print buffer size. - //! - //! @return The testing log buffer. - auto messages() -> std::vector const &; - -} // namespace kernel::tests::log_buffer - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/tests/page_mapper.hpp b/kernel/include/kernel/tests/page_mapper.hpp deleted file mode 100644 index 9969976..0000000 --- a/kernel/include/kernel/tests/page_mapper.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef TEACHOS_KERNEL_TESTS_PAGE_MAPPER_HPP -#define TEACHOS_KERNEL_TESTS_PAGE_MAPPER_HPP - -#include "kapi/memory.hpp" - -#include "kernel/tests/simulated_memory.hpp" - -#include - -#include -#include -#include - -namespace kernel::tests -{ - - struct page_mapper : kapi::memory::page_mapper - { - explicit page_mapper(kstd::units::bytes memory_size); - - auto map(kapi::memory::page page, kapi::memory::frame frame, flags) -> std::byte * override; - - auto unmap(kapi::memory::page page) -> void override; - - auto try_unmap(kapi::memory::page page) noexcept -> bool override; - - kernel::tests::simulated_memory memory; - std::unordered_map page_mappings; - }; - -} // namespace kernel::tests - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/tests/simulated_memory.hpp b/kernel/include/kernel/tests/simulated_memory.hpp deleted file mode 100644 index 446d558..0000000 --- a/kernel/include/kernel/tests/simulated_memory.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TEACHOS_KERNEL_TESTS_SIMULATED_MEMORY_HPP -#define TEACHOS_KERNEL_TESTS_SIMULATED_MEMORY_HPP - -#include - -#include -#include - -namespace kernel::tests -{ - - struct simulated_memory - { - explicit simulated_memory(kstd::units::bytes size); - - auto clear() -> void; - - auto ram_base() noexcept -> std::byte *; - [[nodiscard]] auto ram_base() const noexcept -> std::byte const *; - - private: - std::vector m_memory; - }; - -} // namespace kernel::tests - -#endif \ No newline at end of file diff --git a/kernel/kapi/cpu.tests.cpp b/kernel/kapi/cpu.tests.cpp index 6fe026b..85b20fd 100644 --- a/kernel/kapi/cpu.tests.cpp +++ b/kernel/kapi/cpu.tests.cpp @@ -1,6 +1,6 @@ #include "kapi/cpu.hpp" -#include "kernel/tests/cpu.hpp" +#include "kernel/test_support/cpu.hpp" #include diff --git a/kernel/kapi/system.tests.cpp b/kernel/kapi/system.tests.cpp index c130b54..ee31c51 100644 --- a/kernel/kapi/system.tests.cpp +++ b/kernel/kapi/system.tests.cpp @@ -1,7 +1,7 @@ #include "kapi/system.hpp" -#include "kernel/tests/cpu.hpp" -#include "kernel/tests/log_buffer.hpp" +#include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/log_buffer.hpp" #include diff --git a/kernel/kstd/print.tests.cpp b/kernel/kstd/print.tests.cpp index 2ab829c..f8e600f 100644 --- a/kernel/kstd/print.tests.cpp +++ b/kernel/kstd/print.tests.cpp @@ -1,6 +1,6 @@ #include "kstd/print" -#include "kernel/tests/log_buffer.hpp" +#include "kernel/test_support/log_buffer.hpp" #include diff --git a/kernel/src/test_support/kapi/cio.cpp b/kernel/src/test_support/kapi/cio.cpp index 0dd4b8c..35452d4 100644 --- a/kernel/src/test_support/kapi/cio.cpp +++ b/kernel/src/test_support/kapi/cio.cpp @@ -1,6 +1,6 @@ #include -#include "kernel/tests/log_buffer.hpp" +#include "kernel/test_support/log_buffer.hpp" #include #include diff --git a/kernel/src/test_support/kapi/cpu.cpp b/kernel/src/test_support/kapi/cpu.cpp index 9b2f0b9..6592d15 100644 --- a/kernel/src/test_support/kapi/cpu.cpp +++ b/kernel/src/test_support/kapi/cpu.cpp @@ -1,4 +1,4 @@ -#include "kernel/tests/cpu.hpp" +#include "kernel/test_support/cpu.hpp" #include diff --git a/kernel/src/test_support/kapi/memory.cpp b/kernel/src/test_support/kapi/memory.cpp index 7b7a81e..3a2c2ba 100644 --- a/kernel/src/test_support/kapi/memory.cpp +++ b/kernel/src/test_support/kapi/memory.cpp @@ -2,8 +2,8 @@ #include -#include "kernel/tests/bump_frame_allocator.hpp" -#include "kernel/tests/page_mapper.hpp" +#include "kernel/test_support/bump_frame_allocator.hpp" +#include "kernel/test_support/page_mapper.hpp" #include diff --git a/kernel/src/test_support/log_buffer.cpp b/kernel/src/test_support/log_buffer.cpp index 9e30afb..36ed15e 100644 --- a/kernel/src/test_support/log_buffer.cpp +++ b/kernel/src/test_support/log_buffer.cpp @@ -1,4 +1,4 @@ -#include "kernel/tests/log_buffer.hpp" +#include "kernel/test_support/log_buffer.hpp" #include #include diff --git a/kernel/src/test_support/page_mapper.cpp b/kernel/src/test_support/page_mapper.cpp index a026ec3..5a1b747 100644 --- a/kernel/src/test_support/page_mapper.cpp +++ b/kernel/src/test_support/page_mapper.cpp @@ -1,4 +1,4 @@ -#include "kernel/tests/page_mapper.hpp" +#include "kernel/test_support/page_mapper.hpp" #include "kapi/memory.hpp" diff --git a/kernel/src/test_support/simulated_memory.cpp b/kernel/src/test_support/simulated_memory.cpp index d23350b..7264a35 100644 --- a/kernel/src/test_support/simulated_memory.cpp +++ b/kernel/src/test_support/simulated_memory.cpp @@ -1,4 +1,4 @@ -#include "kernel/tests/simulated_memory.hpp" +#include "kernel/test_support/simulated_memory.hpp" #include -- cgit v1.2.3 From dc64b1cba4677b40c9dda31ecd5109507837b817 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 20:59:55 +0200 Subject: kernel/tests: don't rely on vector for fake memory --- .../kernel/test_support/simulated_memory.hpp | 15 ++++- kernel/src/test_support/page_mapper.cpp | 20 +++++- kernel/src/test_support/simulated_memory.cpp | 77 ++++++++++++++++++++-- 3 files changed, 100 insertions(+), 12 deletions(-) diff --git a/kernel/include/kernel/test_support/simulated_memory.hpp b/kernel/include/kernel/test_support/simulated_memory.hpp index fed9f43..9a391d8 100644 --- a/kernel/include/kernel/test_support/simulated_memory.hpp +++ b/kernel/include/kernel/test_support/simulated_memory.hpp @@ -1,10 +1,11 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_SIMULATED_MEMORY_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_SIMULATED_MEMORY_HPP +#include "kapi/memory.hpp" + #include #include -#include namespace kernel::tests { @@ -13,13 +14,21 @@ namespace kernel::tests { explicit simulated_memory(kstd::units::bytes size); + ~simulated_memory(); + auto clear() -> void; - auto ram_base() noexcept -> std::byte *; + [[nodiscard]] auto ram_base() noexcept -> std::byte *; [[nodiscard]] auto ram_base() const noexcept -> std::byte const *; + [[nodiscard]] auto heap_base() const noexcept -> kapi::memory::linear_address; + [[nodiscard]] auto heap_size() const noexcept -> kstd::units::bytes; + [[nodiscard]] auto memory_descriptor() const noexcept -> int; private: - std::vector m_memory; + int m_memory_descriptor{}; + kstd::units::bytes m_size{0}; + std::byte * m_physical_base{nullptr}; + std::byte * m_virtual_base{nullptr}; }; } // namespace kernel::tests diff --git a/kernel/src/test_support/page_mapper.cpp b/kernel/src/test_support/page_mapper.cpp index 5a1b747..805998b 100644 --- a/kernel/src/test_support/page_mapper.cpp +++ b/kernel/src/test_support/page_mapper.cpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace kernel::tests { @@ -20,10 +21,25 @@ namespace kernel::tests page_mappings.insert({page.number(), frame}); auto page_address = page.start_address(); + auto sandbox_start = memory.heap_base(); + auto sandbox_end = sandbox_start + memory.heap_size(); - if (page_address >= kapi::memory::mmio_base) + if (page_address >= sandbox_start && page_address < sandbox_end) { - throw std::invalid_argument{"MMIO mapping not yet supported in testing!"}; + auto virtual_target = static_cast(page_address); + auto physical_offset = frame.number() * kapi::memory::frame::size; + auto mapped_ptr = mmap(virtual_target, kapi::memory::page::size.value, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED, memory.memory_descriptor(), physical_offset.value); + if (mapped_ptr == MAP_FAILED) + { + throw std::runtime_error("Failed to map page"); + } + + return static_cast(mapped_ptr); + } + else if (page_address >= kapi::memory::mmio_base) + { + throw std::runtime_error("MMIO mapping not yet supported in testing!"); } else if (page_address >= kapi::memory::higher_half_direct_map_base) { diff --git a/kernel/src/test_support/simulated_memory.cpp b/kernel/src/test_support/simulated_memory.cpp index 7264a35..fa3d36c 100644 --- a/kernel/src/test_support/simulated_memory.cpp +++ b/kernel/src/test_support/simulated_memory.cpp @@ -1,30 +1,93 @@ #include "kernel/test_support/simulated_memory.hpp" +#include "kapi/memory.hpp" + #include #include -#include +#include +#include +#include +#include +#include + +using namespace kstd::units_literals; namespace kernel::tests { + namespace + { + constexpr auto virtual_size = 1_GiB; + } simulated_memory::simulated_memory(kstd::units::bytes size) - : m_memory{size / kstd::units::bytes{1}} - {} + : m_memory_descriptor{memfd_create("teachos_simulated_memory", 0)} + , m_size{size} + { + if (m_memory_descriptor < 0) + { + throw std::runtime_error("Failed to create simulated memory"); + } + + if (ftruncate(m_memory_descriptor, static_cast(m_size.value)) < 0) + { + throw std::runtime_error("Failed to resize simulated memory"); + } + + auto mapped_pointer = mmap(nullptr, m_size.value, PROT_READ | PROT_WRITE, MAP_SHARED, m_memory_descriptor, 0); + if (mapped_pointer == MAP_FAILED) + { + throw std::runtime_error("Failed to map simulated memory"); + } + + m_physical_base = static_cast(mapped_pointer); + + auto virtual_pointer = mmap(nullptr, virtual_size.value, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (virtual_pointer == MAP_FAILED) + { + throw std::runtime_error("Failed to map simulated memory"); + } + + m_virtual_base = static_cast(virtual_pointer); + + clear(); + } + + simulated_memory::~simulated_memory() + { + munmap(m_physical_base, m_size.value); + munmap(m_virtual_base, virtual_size.value); + close(m_memory_descriptor); + } + + auto simulated_memory::clear() -> void + { + std::memset(m_physical_base, 0, m_size.value); + } auto simulated_memory::ram_base() noexcept -> std::byte * { - return m_memory.data(); + return m_physical_base; } auto simulated_memory::ram_base() const noexcept -> std::byte const * { - return m_memory.data(); + return m_physical_base; } - auto simulated_memory::clear() -> void + auto simulated_memory::heap_base() const noexcept -> kapi::memory::linear_address + { + return kapi::memory::linear_address{m_virtual_base}; + } + + auto simulated_memory::heap_size() const noexcept -> kstd::units::bytes + { + return virtual_size; + } + + auto simulated_memory::memory_descriptor() const noexcept -> int { - m_memory.clear(); + return m_memory_descriptor; } } // namespace kernel::tests \ No newline at end of file -- cgit v1.2.3 From f8456a8709b6894166865eb4ca067604f480eb7f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 21:16:10 +0200 Subject: kernel/tests: add basic heap allocator tests --- kernel/CMakeLists.txt | 1 + kernel/include/kernel/test_support/memory.hpp | 11 ++++ kernel/src/memory/block_list_allocator.tests.cpp | 80 ++++++++++++++++++++++++ kernel/src/test_support/kapi/memory.cpp | 44 +++++++------ 4 files changed, 118 insertions(+), 18 deletions(-) create mode 100644 kernel/include/kernel/test_support/memory.hpp create mode 100644 kernel/src/memory/block_list_allocator.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 4264441..1b71a5f 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -115,6 +115,7 @@ else() # Memory Subsystem Tests "src/memory/bitmap_allocator.tests.cpp" + "src/memory/block_list_allocator.tests.cpp" # Storage Subsystem Tests "src/devices/storage/ram_disk/device.tests.cpp" diff --git a/kernel/include/kernel/test_support/memory.hpp b/kernel/include/kernel/test_support/memory.hpp new file mode 100644 index 0000000..c6b7449 --- /dev/null +++ b/kernel/include/kernel/test_support/memory.hpp @@ -0,0 +1,11 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_MEMORY_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_MEMORY_HPP + +#include "kapi/memory.hpp" + +namespace kernel::tests::memory +{ + auto heap_base() -> kapi::memory::linear_address; +} + +#endif \ No newline at end of file diff --git a/kernel/src/memory/block_list_allocator.tests.cpp b/kernel/src/memory/block_list_allocator.tests.cpp new file mode 100644 index 0000000..5f6f382 --- /dev/null +++ b/kernel/src/memory/block_list_allocator.tests.cpp @@ -0,0 +1,80 @@ +#include "kernel/memory/block_list_allocator.hpp" + +#include "kernel/test_support/memory.hpp" + +#include + +#include + +#include + +using namespace kstd::units_literals; + +SCENARIO("Block List Allocator Operations", "[memory][allocator]") +{ + GIVEN("A newly initialized block list allocator mapped via the test sandbox") + { + auto sandbox_base = kernel::tests::memory::heap_base(); + kernel::memory::block_list_allocator allocator{sandbox_base}; + + WHEN("a basic allocation request is made") + { + void * ptr = allocator.allocate(128_B, 8_B); + + THEN("a valid, non-null pointer is returned") + { + REQUIRE(ptr != nullptr); + } + + AND_THEN("the returned memory is writeable without causing segmentation faults") + { + auto byte_ptr = static_cast(ptr); + byte_ptr[0] = std::byte{0xDE}; + byte_ptr[127] = std::byte{0xAD}; + REQUIRE(byte_ptr[0] == std::byte{0xDE}); + REQUIRE(byte_ptr[127] == std::byte{0xAD}); + } + + allocator.deallocate(ptr); + } + + WHEN("multiple allocations are made sequentially") + { + void * ptr1 = allocator.allocate(64_B, 8_B); + void * ptr2 = allocator.allocate(64_B, 8_B); + void * ptr3 = allocator.allocate(1_KiB, 16_B); + + THEN("they return distinct, non-overlapping memory blocks") + { + REQUIRE(ptr1 != nullptr); + REQUIRE(ptr2 != nullptr); + REQUIRE(ptr3 != nullptr); + REQUIRE(ptr1 != ptr2); + REQUIRE(ptr2 != ptr3); + REQUIRE(ptr1 != ptr3); + } + + allocator.deallocate(ptr1); + allocator.deallocate(ptr2); + allocator.deallocate(ptr3); + } + + WHEN("a block is allocated and then completely freed") + { + void * original_ptr = allocator.allocate(512_B, 16_B); + allocator.deallocate(original_ptr); + + AND_WHEN("a new allocation of equal or smaller size is requested") + { + void * new_ptr = allocator.allocate(128_B, 16_B); + + THEN("the allocator actively reuses the coalesced space") + { + REQUIRE(new_ptr == original_ptr); + } + + allocator.deallocate(new_ptr); + } + } + } +} diff --git a/kernel/src/test_support/kapi/memory.cpp b/kernel/src/test_support/kapi/memory.cpp index 3a2c2ba..f244b7f 100644 --- a/kernel/src/test_support/kapi/memory.cpp +++ b/kernel/src/test_support/kapi/memory.cpp @@ -9,29 +9,29 @@ #include -namespace kapi::memory +namespace { + //! The size of the simulated RAM. + constexpr auto memory_size = kstd::units::MiB(32); + constexpr auto number_of_frames = memory_size / kapi::memory::frame::size; - namespace - { - //! The size of the simulated RAM. - constexpr auto memory_size = kstd::units::MiB(32); - constexpr auto number_of_frames = memory_size / frame::size; + auto constinit bump_allocator = std::optional{}; + auto constinit test_mapper = std::optional{}; - auto constinit bump_allocator = std::optional{}; - auto constinit test_mapper = std::optional{}; + auto constinit old_allocator = std::optional{}; + auto constinit old_mapper = std::optional{}; - auto constinit old_allocator = std::optional{}; - auto constinit old_mapper = std::optional{}; + auto handoff_to_kernel_pmm(kapi::memory::frame_allocator & new_allocator) -> void + { + auto first_free_frame = bump_allocator->next_free_frame; + auto number_of_free_frames = number_of_frames - first_free_frame; + new_allocator.release_many({kapi::memory::frame{first_free_frame}, number_of_free_frames}); + } - auto handoff_to_kernel_pmm(frame_allocator & new_allocator) -> void - { - auto first_free_frame = bump_allocator->next_free_frame; - auto number_of_free_frames = number_of_frames - first_free_frame; - new_allocator.release_many({frame{first_free_frame}, number_of_free_frames}); - } +} // namespace - } // namespace +namespace kapi::memory +{ auto init() -> void { @@ -60,4 +60,12 @@ namespace kapi::memory test_mapper.reset(); } -} // namespace kapi::memory \ No newline at end of file +} // namespace kapi::memory + +namespace kernel::tests::memory +{ + auto heap_base() -> kapi::memory::linear_address + { + return test_mapper->memory.heap_base(); + } +} // namespace kernel::tests::memory \ No newline at end of file -- cgit v1.2.3 From 3eb680cf5bcef626505cac82820996d8db4170d7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 22:52:22 +0200 Subject: kernel/tests: prevent double mapping of pages --- kernel/src/memory/block_list_allocator.tests.cpp | 13 +++++++++++++ kernel/src/test_support/page_mapper.cpp | 7 ++++++- vgcore.1625255 | Bin 0 -> 13705216 bytes 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 vgcore.1625255 diff --git a/kernel/src/memory/block_list_allocator.tests.cpp b/kernel/src/memory/block_list_allocator.tests.cpp index 5f6f382..8ca426d 100644 --- a/kernel/src/memory/block_list_allocator.tests.cpp +++ b/kernel/src/memory/block_list_allocator.tests.cpp @@ -1,5 +1,7 @@ #include "kernel/memory/block_list_allocator.hpp" +#include "kapi/memory.hpp" + #include "kernel/test_support/memory.hpp" #include @@ -10,10 +12,21 @@ using namespace kstd::units_literals; +namespace kapi +{ + namespace memory + { + auto reset() -> void; + } +} // namespace kapi + SCENARIO("Block List Allocator Operations", "[memory][allocator]") { GIVEN("A newly initialized block list allocator mapped via the test sandbox") { + kapi::memory::reset(); + kapi::memory::init(); + auto sandbox_base = kernel::tests::memory::heap_base(); kernel::memory::block_list_allocator allocator{sandbox_base}; diff --git a/kernel/src/test_support/page_mapper.cpp b/kernel/src/test_support/page_mapper.cpp index 805998b..abdcae5 100644 --- a/kernel/src/test_support/page_mapper.cpp +++ b/kernel/src/test_support/page_mapper.cpp @@ -18,7 +18,12 @@ namespace kernel::tests auto page_mapper::map(kapi::memory::page page, kapi::memory::frame frame, flags) -> std::byte * { - page_mappings.insert({page.number(), frame}); + auto result = page_mappings.insert({page.number(), frame}); + if (!result.second) + { + auto error = std::format("Page {} was already mapped!", page.number()); + throw std::invalid_argument{error}; + } auto page_address = page.start_address(); auto sandbox_start = memory.heap_base(); diff --git a/vgcore.1625255 b/vgcore.1625255 new file mode 100644 index 0000000..de22445 Binary files /dev/null and b/vgcore.1625255 differ -- cgit v1.2.3