aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Braun <marcel.braun@ost.ch>2026-04-02 08:48:00 +0200
committerMarcel Braun <marcel.braun@ost.ch>2026-04-02 08:48:00 +0200
commit0c01a95325b26151ff3c9a70142f5dc83ff7d53f (patch)
tree9bf034f544ae773b653554a54edfce232f835754
parent022d3e872de9c5a6a52c67f74af13706552330c0 (diff)
parent3eb680cf5bcef626505cac82820996d8db4170d7 (diff)
downloadteachos-0c01a95325b26151ff3c9a70142f5dc83ff7d53f.tar.xz
teachos-0c01a95325b26151ff3c9a70142f5dc83ff7d53f.zip
Merge branch 'fmorgner/develop-SA-FS26/kernel-bht' into 'develop-BA-FS26'
Add experimental support for kernel tests See merge request teachos/kernel!20
-rw-r--r--.clangd7
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--.lcovrc2
-rw-r--r--CMakeLists.txt21
-rw-r--r--kapi/include/kapi/cio.hpp5
-rw-r--r--kernel/CMakeLists.txt107
-rw-r--r--kernel/include/kernel/test_support/bump_frame_allocator.hpp32
-rw-r--r--kernel/include/kernel/test_support/cpu.hpp18
-rw-r--r--kernel/include/kernel/test_support/log_buffer.hpp32
-rw-r--r--kernel/include/kernel/test_support/memory.hpp11
-rw-r--r--kernel/include/kernel/test_support/page_mapper.hpp33
-rw-r--r--kernel/include/kernel/test_support/simulated_memory.hpp36
-rw-r--r--kernel/kapi/cpu.tests.cpp19
-rw-r--r--kernel/kapi/memory.cpp8
-rw-r--r--kernel/kapi/system.tests.cpp30
-rw-r--r--kernel/kstd/print.tests.cpp23
-rw-r--r--kernel/src/devices/storage/ram_disk/device.tests.cpp117
-rw-r--r--kernel/src/memory/bitmap_allocator.cpp20
-rw-r--r--kernel/src/memory/bitmap_allocator.tests.cpp289
-rw-r--r--kernel/src/memory/block_list_allocator.tests.cpp93
-rw-r--r--kernel/src/test_support/kapi/cio.cpp43
-rw-r--r--kernel/src/test_support/kapi/cpu.cpp41
-rw-r--r--kernel/src/test_support/kapi/interrupts.cpp11
-rw-r--r--kernel/src/test_support/kapi/memory.cpp71
-rw-r--r--kernel/src/test_support/log_buffer.cpp39
-rw-r--r--kernel/src/test_support/page_mapper.cpp78
-rw-r--r--kernel/src/test_support/simulated_memory.cpp93
-rw-r--r--kernel/src/test_support/state_reset_listener.cpp50
-rw-r--r--libs/kstd/CMakeLists.txt4
-rw-r--r--libs/kstd/include/kstd/cstring10
-rw-r--r--libs/kstd/src/libc/string.cpp10
-rw-r--r--vgcore.1625255bin0 -> 13705216 bytes
32 files changed, 1298 insertions, 57 deletions
diff --git a/.clangd b/.clangd
index 996361d..fac5c82 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/.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/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/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<output_device *>;
+ //! @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/CMakeLists.txt b/kernel/CMakeLists.txt
index cb3d8a5..1b71a5f 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"
@@ -12,10 +12,8 @@ add_executable("kernel"
"kstd/print.cpp"
# Kernel Implementation
- "src/main.cpp"
"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"
@@ -40,38 +38,101 @@ add_executable("kernel"
"src/filesystem/vfs.cpp"
)
-target_include_directories("kernel" PRIVATE
+target_include_directories("kernel_objs" PUBLIC
"include"
)
-target_link_libraries("kernel" PRIVATE
- "os::arch"
+target_link_libraries("kernel_objs" PUBLIC
"os::kapi"
)
-target_link_options("kernel" PRIVATE
- "-T${KERNEL_LINKER_SCRIPT}"
- "-no-pie"
- "-nostdlib"
-)
-
-set_property(TARGET "kernel"
- APPEND
- PROPERTY LINK_DEPENDS
- "${KERNEL_LINKER_SCRIPT}"
-)
-
file(GLOB_RECURSE KERNEL_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp")
-target_sources("kernel" PUBLIC
+target_sources("kernel_objs" PUBLIC
FILE_SET HEADERS
BASE_DIRS "include"
FILES
${KERNEL_HEADERS}
)
-target_disassemble("kernel")
-target_extract_debug_symbols("kernel")
-target_strip("kernel")
+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
+ "os::arch"
+ "os::kernel"
+ )
+
+ 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_generate_bootable_iso("kernel")
+else()
+ enable_coverage("kernel_objs")
+
+ add_library("kernel_test_support" OBJECT
+ "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/page_mapper.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::Catch2WithMain"
+ )
+
+ add_executable("kernel_tests"
+ # KAPI Shim Tests
+ "kapi/cpu.tests.cpp"
+ "kapi/system.tests.cpp"
+
+ # KSTD Shim Tests
+ "kstd/print.tests.cpp"
+
+ # 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"
+ )
+ 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 ""
+ )
+
+ enable_coverage("kernel_tests")
+ catch_discover_tests("os::kernel_tests")
+endif()
-target_generate_bootable_iso("kernel")
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 <cstddef>
+#include <optional>
+#include <utility>
+
+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<std::pair<kapi::memory::frame, std::size_t>> override
+ {
+ auto start = next_free_frame;
+ next_free_frame += count;
+ return std::pair{kapi::memory::frame{start}, count};
+ }
+
+ auto release_many(std::pair<kapi::memory::frame, std::size_t>) -> 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 <stdexcept>
+
+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 <string>
+#include <vector>
+
+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<std::string> const &;
+
+} // namespace kernel::tests::log_buffer
+
+#endif \ No newline at end of file
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/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 <kstd/units>
+
+#include <cstddef>
+#include <cstdint>
+#include <unordered_map>
+
+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<std::uint64_t, kapi::memory::frame> 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..9a391d8
--- /dev/null
+++ b/kernel/include/kernel/test_support/simulated_memory.hpp
@@ -0,0 +1,36 @@
+#ifndef TEACHOS_KERNEL_TEST_SUPPORT_SIMULATED_MEMORY_HPP
+#define TEACHOS_KERNEL_TEST_SUPPORT_SIMULATED_MEMORY_HPP
+
+#include "kapi/memory.hpp"
+
+#include <kstd/units>
+
+#include <cstddef>
+
+namespace kernel::tests
+{
+
+ struct simulated_memory
+ {
+ explicit simulated_memory(kstd::units::bytes size);
+
+ ~simulated_memory();
+
+ auto clear() -> void;
+
+ [[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:
+ 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
+
+#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..85b20fd
--- /dev/null
+++ b/kernel/kapi/cpu.tests.cpp
@@ -0,0 +1,19 @@
+#include "kapi/cpu.hpp"
+
+#include "kernel/test_support/cpu.hpp"
+
+#include <catch2/catch_test_macros.hpp>
+
+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/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<std::uint64_t *>(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<std::uint64_t *>(mapped);
+ }
});
- auto bitmap_ptr = static_cast<std::uint64_t *>(pmm_metadata_base);
auto bitmap =
std::span{bitmap_ptr, (bitmap_bytes + kstd::type_size<std::uint64_t> - 1_B) / kstd::type_size<std::uint64_t>};
diff --git a/kernel/kapi/system.tests.cpp b/kernel/kapi/system.tests.cpp
new file mode 100644
index 0000000..ee31c51
--- /dev/null
+++ b/kernel/kapi/system.tests.cpp
@@ -0,0 +1,30 @@
+#include "kapi/system.hpp"
+
+#include "kernel/test_support/cpu.hpp"
+#include "kernel/test_support/log_buffer.hpp"
+
+#include <kstd/print>
+
+#include <catch2/catch_test_macros.hpp>
+
+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..f8e600f
--- /dev/null
+++ b/kernel/kstd/print.tests.cpp
@@ -0,0 +1,23 @@
+#include "kstd/print"
+
+#include "kernel/test_support/log_buffer.hpp"
+
+#include <catch2/catch_test_macros.hpp>
+
+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/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 <catch2/catch_test_macros.hpp>
+
+#include <cstddef>
+#include <ranges>
+#include <stdexcept>
+#include <vector>
+
+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<std::byte *>(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<std::byte>(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<std::byte>(device.block_size());
+ device.read_block(10, buffer.data());
+
+ THEN("the buffer is filled with zeros")
+ {
+ REQUIRE_THAT(buffer, Catch::Matchers::RangeEquals(std::vector<std::byte>(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<std::byte>(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<std::byte>(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
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 <cstddef>
#include <cstdint>
#include <optional>
+#include <ranges>
#include <span>
#include <utility>
@@ -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 <catch2/catch_test_macros.hpp>
+
+#include <cstdint>
+#include <limits>
+#include <ranges>
+#include <span>
+#include <vector>
+
+constexpr auto all_bits_set = std::numeric_limits<std::uint64_t>::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")
+ {