From fefc9a138f4d4f26e6b676c4cdca77b3140e4206 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Tue, 17 Feb 2026 17:41:10 +0100 Subject: Save WIP --- arch/x86_64/support/grub.cfg.in | 1 + arch/x86_64/support/modules/test.img | Bin 0 -> 2097152 bytes cmake/Modules/GenerateBootableIso.cmake | 12 ++++++++++++ 3 files changed, 13 insertions(+) create mode 100644 arch/x86_64/support/modules/test.img diff --git a/arch/x86_64/support/grub.cfg.in b/arch/x86_64/support/grub.cfg.in index 49f19ce..b29fc36 100644 --- a/arch/x86_64/support/grub.cfg.in +++ b/arch/x86_64/support/grub.cfg.in @@ -3,5 +3,6 @@ default=0 menuentry "TeachOS" { multiboot2 /$ + module2 isofs/modules/test.img boot } \ No newline at end of file diff --git a/arch/x86_64/support/modules/test.img b/arch/x86_64/support/modules/test.img new file mode 100644 index 0000000..914fa7f Binary files /dev/null and b/arch/x86_64/support/modules/test.img differ diff --git a/cmake/Modules/GenerateBootableIso.cmake b/cmake/Modules/GenerateBootableIso.cmake index 3d1ee30..abdc907 100644 --- a/cmake/Modules/GenerateBootableIso.cmake +++ b/cmake/Modules/GenerateBootableIso.cmake @@ -3,6 +3,13 @@ include_guard(GLOBAL) function(target_generate_bootable_iso TARGET) find_package("grub-mkrescue") + # set(MODULE_SRC + # "${PROJECT_SOURCE_DIR}/arch/${CMAKE_SYSTEM_PROCESSOR}/support/test.img" + # ) + # set(MODULE_DST + # "$/isofs/test.img" + # ) + file(GENERATE OUTPUT "$/isofs/boot/grub/grub.cfg" INPUT "${PROJECT_SOURCE_DIR}/arch/${CMAKE_SYSTEM_PROCESSOR}/support/grub.cfg.in" @@ -10,6 +17,10 @@ function(target_generate_bootable_iso TARGET) add_custom_command(TARGET "${TARGET}" POST_BUILD + # COMMAND "${CMAKE_COMMAND}" -E make_directory "$/isofs" + # COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${MODULE_SRC}" "${MODULE_DST}" + COMMAND "${CMAKE_COMMAND}" -E make_directory "$/isofs" + COMMAND "${CMAKE_COMMAND}" -E copy_directory "${PROJECT_SOURCE_DIR}/arch/${CMAKE_SYSTEM_PROCESSOR}/support/modules" "$/isofs/modules" COMMAND "${GRUB_MKRESCUE_EXE}" "-o" "$/${TARGET}.iso" @@ -19,4 +30,5 @@ function(target_generate_bootable_iso TARGET) BYPRODUCTS "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$/${TARGET}.iso" COMMENT "Creating bootable ISO image" ) + endfunction() -- cgit v1.2.3 From bbbf8c9032a54da4115d57d2897f8bb0a698895b Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Tue, 17 Feb 2026 18:41:25 +0100 Subject: Copy modules to isofs folder --- arch/x86_64/support/grub.cfg.in | 2 +- cmake/Modules/GenerateBootableIso.cmake | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/arch/x86_64/support/grub.cfg.in b/arch/x86_64/support/grub.cfg.in index b29fc36..09d2ace 100644 --- a/arch/x86_64/support/grub.cfg.in +++ b/arch/x86_64/support/grub.cfg.in @@ -3,6 +3,6 @@ default=0 menuentry "TeachOS" { multiboot2 /$ - module2 isofs/modules/test.img + module2 /modules/test.img boot } \ No newline at end of file diff --git a/cmake/Modules/GenerateBootableIso.cmake b/cmake/Modules/GenerateBootableIso.cmake index abdc907..b798787 100644 --- a/cmake/Modules/GenerateBootableIso.cmake +++ b/cmake/Modules/GenerateBootableIso.cmake @@ -3,13 +3,6 @@ include_guard(GLOBAL) function(target_generate_bootable_iso TARGET) find_package("grub-mkrescue") - # set(MODULE_SRC - # "${PROJECT_SOURCE_DIR}/arch/${CMAKE_SYSTEM_PROCESSOR}/support/test.img" - # ) - # set(MODULE_DST - # "$/isofs/test.img" - # ) - file(GENERATE OUTPUT "$/isofs/boot/grub/grub.cfg" INPUT "${PROJECT_SOURCE_DIR}/arch/${CMAKE_SYSTEM_PROCESSOR}/support/grub.cfg.in" @@ -17,8 +10,6 @@ function(target_generate_bootable_iso TARGET) add_custom_command(TARGET "${TARGET}" POST_BUILD - # COMMAND "${CMAKE_COMMAND}" -E make_directory "$/isofs" - # COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${MODULE_SRC}" "${MODULE_DST}" COMMAND "${CMAKE_COMMAND}" -E make_directory "$/isofs" COMMAND "${CMAKE_COMMAND}" -E copy_directory "${PROJECT_SOURCE_DIR}/arch/${CMAKE_SYSTEM_PROCESSOR}/support/modules" "$/isofs/modules" COMMAND "${GRUB_MKRESCUE_EXE}" -- cgit v1.2.3 From e32c889764b56aa0ed5373c07d0225c95ed502bb Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Tue, 17 Feb 2026 19:20:03 +0100 Subject: Add information request to multiboot.s --- arch/x86_64/src/boot/multiboot.s | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/x86_64/src/boot/multiboot.s b/arch/x86_64/src/boot/multiboot.s index 7ccca56..37d8afe 100644 --- a/arch/x86_64/src/boot/multiboot.s +++ b/arch/x86_64/src/boot/multiboot.s @@ -18,6 +18,13 @@ multiboot_header_start: .long 3 .Lflags_end: .align 8 +.Linformation_request_start: + .word 1 + .word 0 + .long .Linformation_request_end - .Linformation_request_start + .long 3 +.Linformation_request_end: +.align 8 .Lend_start: .word 0 .word 0 -- cgit v1.2.3 From 01549be5c53f74df3df1d7f9de3e702ffb906088 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 18 Feb 2026 10:35:30 +0100 Subject: add multiboot2 module tag, all modules can be iterated --- arch/x86_64/support/grub.cfg.in | 3 ++- libs/multiboot2/include/multiboot2/information.hpp | 29 ++++++++++++++++++++++ .../include/multiboot2/information/data.hpp | 10 ++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/arch/x86_64/support/grub.cfg.in b/arch/x86_64/support/grub.cfg.in index 09d2ace..834345a 100644 --- a/arch/x86_64/support/grub.cfg.in +++ b/arch/x86_64/support/grub.cfg.in @@ -3,6 +3,7 @@ default=0 menuentry "TeachOS" { multiboot2 /$ - module2 /modules/test.img + module2 /modules/test.img bbbbbbb + module2 /modules/test.img aaaa boot } \ No newline at end of file diff --git a/libs/multiboot2/include/multiboot2/information.hpp b/libs/multiboot2/include/multiboot2/information.hpp index 0f48835..fbd534c 100644 --- a/libs/multiboot2/include/multiboot2/information.hpp +++ b/libs/multiboot2/include/multiboot2/information.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -106,6 +107,22 @@ namespace multiboot2 } }; + /** + * @copydoc multiboot2::data::module + */ + struct module : vla_tag + { + using vla_tag::vla_tag; + + /** + * @brief The module command line or name. + */ + [[nodiscard]] auto string() const noexcept -> std::string_view + { + return {data(), size()}; + } + }; + struct information_view { using iterator = iterator; @@ -210,6 +227,18 @@ namespace multiboot2 return maybe_memory_map().value(); } + [[nodiscard]] auto modules() const noexcept + { + auto filter_modules = [](auto const & tag) { + return tag.information_id() == module::id; + }; + auto transform_module = [](auto const & tag) { + return module{&tag}; + }; + return std::ranges::subrange(begin(), end()) | std::views::filter(filter_modules) | + std::views::transform(transform_module); + } + private: template [[nodiscard]] constexpr auto get() const noexcept -> std::optional diff --git a/libs/multiboot2/include/multiboot2/information/data.hpp b/libs/multiboot2/include/multiboot2/information/data.hpp index ccd8fbb..8d53448 100644 --- a/libs/multiboot2/include/multiboot2/information/data.hpp +++ b/libs/multiboot2/include/multiboot2/information/data.hpp @@ -127,6 +127,16 @@ namespace multiboot2 std::uint32_t entry_version; }; + //! A module loaded by the bootloader. + struct module : tag_data + { + //! The physical start address of this module. + std::uint32_t start_address; + + //! The physical end address of this module. + std::uint32_t end_address; + }; + } // namespace data } // namespace multiboot2 -- cgit v1.2.3 From 9d568ef3e785ce3d6028fa60bd59eaac2e85900a Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 18 Feb 2026 10:40:52 +0100 Subject: add comment where the command line information is --- libs/multiboot2/include/multiboot2/information/data.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/multiboot2/include/multiboot2/information/data.hpp b/libs/multiboot2/include/multiboot2/information/data.hpp index 8d53448..9fa6d5b 100644 --- a/libs/multiboot2/include/multiboot2/information/data.hpp +++ b/libs/multiboot2/include/multiboot2/information/data.hpp @@ -128,6 +128,9 @@ namespace multiboot2 }; //! A module loaded by the bootloader. + //! + //! @note the command line associated with this module is not part of this structure, since it is of variable size + //! and the contained information starts at the end of this structure. struct module : tag_data { //! The physical start address of this module. -- cgit v1.2.3 From ea8172296fa5b56137f97c01650b8d392bdb897f Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 26 Feb 2026 11:21:18 +0100 Subject: implemented remapping of bootloader modules --- arch/x86_64/kapi/memory.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp index a9e1216..3a2d66b 100644 --- a/arch/x86_64/kapi/memory.cpp +++ b/arch/x86_64/kapi/memory.cpp @@ -118,6 +118,27 @@ namespace kapi::memory mapper.map(page, frame, page_mapper::flags::supervisor_only); } } + + [[maybe_unused]] auto remap_bootloader_modules(page_mapper & mapper) -> void + { + auto modules = boot::bootstrap_information.mbi->modules(); + + for (auto module : modules) + { + auto module_physical_start = physical_address{module.start_address}; + auto module_virtual_start = + linear_address{module.start_address + std::bit_cast(&arch::boot::TEACHOS_VMA)}; + auto module_size = module.end_address - module.start_address; + auto module_block_count = (module_size + PLATFORM_FRAME_SIZE - 1) / PLATFORM_FRAME_SIZE; + + for (auto i = 0uz; i < module_block_count; ++i) + { + auto page = page::containing(module_virtual_start) + i; + auto frame = frame::containing(module_physical_start) + i; + mapper.map(page, frame, page_mapper::flags::empty); // TODO BA-FS26 make writable? + } + } + } [[maybe_unused]] auto handoff_to_kernel_pmm(frame_allocator & new_allocator) -> void { @@ -196,6 +217,7 @@ namespace kapi::memory remap_kernel(*higher_half_mapper); remap_vga_text_mode_buffer(*higher_half_mapper); remap_multiboot_information(*higher_half_mapper); + remap_bootloader_modules(*recursive_page_mapper); auto current_cr3 = arch::cpu::cr3::read(); auto old_pml4 = static_cast(current_cr3.address()); -- cgit v1.2.3 From 035c8d6e38fd901e6769a81f67b8d9e1e3fcea20 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 26 Feb 2026 11:24:27 +0100 Subject: implemented boot_modules and boot_module_registry, init boot_modules in kernel main --- arch/x86_64/CMakeLists.txt | 1 + arch/x86_64/kapi/boot_modules.cpp | 51 ++++++++++ kapi/CMakeLists.txt | 3 + kapi/include/kapi/boot_module/boot_module.hpp | 21 ++++ .../kapi/boot_module/boot_module_registry.hpp | 108 +++++++++++++++++++++ kapi/include/kapi/boot_modules.hpp | 25 +++++ kernel/CMakeLists.txt | 1 + kernel/kapi/boot_modules.cpp | 21 ++++ kernel/src/main.cpp | 4 + 9 files changed, 235 insertions(+) create mode 100644 arch/x86_64/kapi/boot_modules.cpp create mode 100644 kapi/include/kapi/boot_module/boot_module.hpp create mode 100644 kapi/include/kapi/boot_module/boot_module_registry.hpp create mode 100644 kapi/include/kapi/boot_modules.hpp create mode 100644 kernel/kapi/boot_modules.cpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 41932c9..4ae09ff 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -12,6 +12,7 @@ target_link_libraries("x86_64" PUBLIC target_sources("x86_64" PRIVATE # Platform-dependent KAPI implementation + "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" "kapi/memory.cpp" diff --git a/arch/x86_64/kapi/boot_modules.cpp b/arch/x86_64/kapi/boot_modules.cpp new file mode 100644 index 0000000..5d06eb5 --- /dev/null +++ b/arch/x86_64/kapi/boot_modules.cpp @@ -0,0 +1,51 @@ +#include "kapi/boot_modules.hpp" + +#include "kapi/boot.hpp" +#include "kapi/boot_module/boot_module.hpp" +#include "kapi/boot_module/boot_module_registry.hpp" +#include "kapi/system.hpp" + +#include "arch/boot/boot.hpp" +#include "arch/boot/ld.hpp" + +#include + +#include + +#include +#include +#include +#include +#include + +namespace kapi::boot_modules +{ + namespace + { + auto constinit registry = std::optional{}; + } // namespace + + auto init() -> void + { + auto static constinit is_initialized = std::atomic_flag{}; + if (is_initialized.test_and_set()) + { + system::panic("[x86_64] Boot module registry has already been initialized."); + } + + kstd::println("[x86_64:BOOT_MODULES] Initializing boot module registry."); + + registry.emplace(kapi::boot_modules::boot_module_registry{}); + + auto modules = boot::bootstrap_information.mbi->modules(); + std::ranges::for_each(modules, [](auto const & module) { + registry->add_boot_module(kapi::boot_modules::boot_module{ + .name = module.string(), + .start_address = module.start_address + std::bit_cast(&arch::boot::TEACHOS_VMA), + .size = module.end_address - module.start_address, + }); + }); + + set_boot_module_registry(*registry); + } +} // namespace kapi::boot_modules \ No newline at end of file diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index 5e914bb..028cdbb 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -5,8 +5,11 @@ target_sources("kapi" PUBLIC FILE_SET HEADERS BASE_DIRS "include" FILES + "include/kapi/boot_modules.hpp" "include/kapi/boot.hpp" "include/kapi/cio.hpp" + "include/kapi/boot_module/boot_module.hpp" + "include/kapi/boot_module/boot_module_registry.hpp" "include/kapi/memory.hpp" "include/kapi/memory/address.hpp" "include/kapi/memory/frame_allocator.hpp" diff --git a/kapi/include/kapi/boot_module/boot_module.hpp b/kapi/include/kapi/boot_module/boot_module.hpp new file mode 100644 index 0000000..f2d97ae --- /dev/null +++ b/kapi/include/kapi/boot_module/boot_module.hpp @@ -0,0 +1,21 @@ +#ifndef TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_HPP +#define TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_HPP + +#include +#include + +namespace kapi::boot_modules +{ + // ! The boot module struct + // ! + // ! The boot module struct represents a module loaded by the bootloader, and contains information about it, such as + // ! its name, virtual start address, and size. + struct boot_module + { + std::string_view name; + size_t start_address; + size_t size; + }; +} // namespace kapi::boot_modules + +#endif \ No newline at end of file diff --git a/kapi/include/kapi/boot_module/boot_module_registry.hpp b/kapi/include/kapi/boot_module/boot_module_registry.hpp new file mode 100644 index 0000000..3732a5f --- /dev/null +++ b/kapi/include/kapi/boot_module/boot_module_registry.hpp @@ -0,0 +1,108 @@ +#ifndef TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_REGISTRY_HPP +#define TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_REGISTRY_HPP + +#include "kapi/boot_module/boot_module.hpp" + +#include +#include + +namespace kapi::boot_modules +{ + + // ! The interface of the boot module registry + // ! + // ! The boot module registry is responsible for keeping track of the modules loaded by the bootloader, and + // ! providing access to them for the rest of the kernel. + struct boot_module_registry + { + using range_type = std::array; // TODO BA-FS26 use kstd::vector when available + + using value_type = range_type::value_type; + using const_reference = range_type::const_reference; + + using const_iterator = range_type::const_iterator; + using const_reverse_iterator = range_type::const_reverse_iterator; + using size_type = range_type::size_type; + using difference_type = range_type::difference_type; + + [[nodiscard]] auto begin() const noexcept -> const_iterator + { + return m_modules.begin(); + } + + [[nodiscard]] auto end() const noexcept -> const_iterator + { + return m_modules.end(); + } + + [[nodiscard]] auto cbegin() const noexcept -> const_iterator + { + return begin(); + } + + [[nodiscard]] auto cend() const noexcept -> const_iterator + { + return end(); + } + + [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator + { + return m_modules.rbegin(); + } + + [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator + { + return m_modules.rend(); + } + + [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator + { + return rbegin(); + } + + [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator + { + return rend(); + } + + [[nodiscard]] auto front() const noexcept -> const_reference + { + return m_modules.front(); + } + + [[nodiscard]] auto back() const noexcept -> const_reference + { + return m_modules.back(); + } + + [[nodiscard]] auto size() const noexcept -> std::size_t + { + return m_modules.size(); + } + + [[nodiscard]] auto empty() const noexcept -> bool + { + return m_modules.empty(); + } + + [[nodiscard]] auto at(std::size_t index) const -> const_reference + { + return m_modules.at(index); + } + + [[nodiscard]] auto operator[](std::size_t index) const noexcept -> const_reference + { + return m_modules[index]; + } + + auto add_boot_module(boot_module module) -> void + { + m_modules.at(0) = module; // TODO BA-FS26 push back when kstd::vector is available + } + + private: + range_type m_modules{}; + }; +} // namespace kapi::boot_modules + +#endif \ No newline at end of file diff --git a/kapi/include/kapi/boot_modules.hpp b/kapi/include/kapi/boot_modules.hpp new file mode 100644 index 0000000..752b070 --- /dev/null +++ b/kapi/include/kapi/boot_modules.hpp @@ -0,0 +1,25 @@ +#ifndef TEACHOS_KAPI_BOOT_MODULES_HPP +#define TEACHOS_KAPI_BOOT_MODULES_HPP + +#include "kapi/boot_module/boot_module_registry.hpp" // IWYU pragma: export + +namespace kapi::boot_modules +{ + + //! @qualifier platform-defined + //! Initialize the boot module registry. + //! + //! @note This function must be implemented by the target platform. + //! + //! This function initializes the boot module registry, which is responsible for keeping track of the modules loaded + //! by the bootloader, and providing access to them for the rest of the kernel. + auto init() -> void; + + //! @qualifier kernel-defined + //! Set the boot module registry + //! + //! @param registry A new boot module registry. + auto set_boot_module_registry(boot_module_registry & registry) -> void; + +} // namespace kapi::boot_modules +#endif \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 02e517c..b7b7c71 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -1,5 +1,6 @@ add_executable("kernel" # Platform-independent KAPI implementation + "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/memory.cpp" "kapi/system.cpp" diff --git a/kernel/kapi/boot_modules.cpp b/kernel/kapi/boot_modules.cpp new file mode 100644 index 0000000..3f0f044 --- /dev/null +++ b/kernel/kapi/boot_modules.cpp @@ -0,0 +1,21 @@ +#include "kapi/boot_modules.hpp" + +#include "kapi/system.hpp" + +#include + +namespace kapi::boot_modules +{ + + constinit auto static registry = std::optional{}; + + auto set_boot_module_registry(boot_module_registry & registry) -> void + { + if (kapi::boot_modules::registry) + { + system::panic("[x86_64] Boot module registry has already been set."); + } + + kapi::boot_modules::registry = registry; + } +} // namespace kapi::boot_modules diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 01bcbb0..117a1d8 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,3 +1,4 @@ +#include "kapi/boot_modules.hpp" #include "kapi/cio.hpp" #include "kapi/memory.hpp" #include "kapi/system.hpp" @@ -16,5 +17,8 @@ auto main() -> int kstd::println("[OS] Memory subsystem initialized."); kapi::system::memory_initialized(); + kapi::boot_modules::init(); + kstd::println("[OS] Boot module registry initialized."); + kapi::system::panic("Returning from kernel main!"); } -- cgit v1.2.3 From 1486620355dc139603cb6be0105f6e742e6fa8dd Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 26 Feb 2026 11:25:23 +0100 Subject: fix multiboot2 information vla_tag implementation --- libs/multiboot2/include/multiboot2/information/tag.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/multiboot2/include/multiboot2/information/tag.hpp b/libs/multiboot2/include/multiboot2/information/tag.hpp index cd1fc0e..8d90790 100644 --- a/libs/multiboot2/include/multiboot2/information/tag.hpp +++ b/libs/multiboot2/include/multiboot2/information/tag.hpp @@ -172,9 +172,9 @@ namespace multiboot2 return m_vla.data(); } - [[nodiscard]] auto at() const -> const_reference + [[nodiscard]] auto at(std::size_t index) const -> const_reference { - return m_vla.at(); + return m_vla.at(index); } [[nodiscard]] auto operator[](std::size_t index) const noexcept -> const_reference -- cgit v1.2.3 From 144c3fb3845eb9deb1093c3348af5e02cb2cbbb8 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 26 Feb 2026 11:27:48 +0100 Subject: setup basic devices and RAMDisk structure --- kernel/CMakeLists.txt | 3 +++ kernel/devices/CMakeLists.txt | 27 ++++++++++++++++++++++ kernel/devices/include/devices/BlockDevice.hpp | 11 +++++++++ kernel/devices/include/devices/Device.hpp | 11 +++++++++ .../devices/storage/RAMDisk/RAMDiskController.hpp | 11 +++++++++ .../devices/storage/RAMDisk/RAMDiskDevice.hpp | 11 +++++++++ .../include/devices/storage/StorageController.hpp | 11 +++++++++ .../include/devices/storage/StorageManagement.hpp | 16 +++++++++++++ kernel/devices/src/BlockDevice.cpp | 6 +++++ kernel/devices/src/Device.cpp | 6 +++++ .../src/storage/RAMDisk/RAMDiskController.cpp | 6 +++++ .../devices/src/storage/RAMDisk/RAMDiskDevice.cpp | 6 +++++ kernel/devices/src/storage/StorageController.cpp | 6 +++++ kernel/devices/src/storage/StorageManagement.cpp | 9 ++++++++ 14 files changed, 140 insertions(+) create mode 100644 kernel/devices/CMakeLists.txt create mode 100644 kernel/devices/include/devices/BlockDevice.hpp create mode 100644 kernel/devices/include/devices/Device.hpp create mode 100644 kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp create mode 100644 kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp create mode 100644 kernel/devices/include/devices/storage/StorageController.hpp create mode 100644 kernel/devices/include/devices/storage/StorageManagement.hpp create mode 100644 kernel/devices/src/BlockDevice.cpp create mode 100644 kernel/devices/src/Device.cpp create mode 100644 kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp create mode 100644 kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp create mode 100644 kernel/devices/src/storage/StorageController.cpp create mode 100644 kernel/devices/src/storage/StorageManagement.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index b7b7c71..c3c9698 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory("devices") + add_executable("kernel" # Platform-independent KAPI implementation "kapi/boot_modules.cpp" @@ -24,6 +26,7 @@ target_include_directories("kernel" PRIVATE target_link_libraries("kernel" PRIVATE "os::arch" "os::kapi" + "kernel::devices" ) target_link_options("kernel" PRIVATE diff --git a/kernel/devices/CMakeLists.txt b/kernel/devices/CMakeLists.txt new file mode 100644 index 0000000..80b89bf --- /dev/null +++ b/kernel/devices/CMakeLists.txt @@ -0,0 +1,27 @@ +add_library("kernel_devices" STATIC) +add_library("kernel::devices" ALIAS "kernel_devices") + +target_sources("kernel_devices" PRIVATE + "src/Device.cpp" + "src/BlockDevice.cpp" + "src/storage/StorageController.cpp" + "src/storage/StorageManagement.cpp" + "src/storage/RAMDisk/RAMDiskController.cpp" + "src/storage/RAMDisk/RAMDiskDevice.cpp" +) + +target_sources("kernel_devices" PUBLIC + FILE_SET HEADERS + BASE_DIRS "include" + FILES + "include/devices/Device.hpp" + "include/devices/BlockDevice.hpp" + "include/devices/storage/StorageController.hpp" + "include/devices/storage/StorageManagement.hpp" + "include/devices/storage/RAMDisk/RAMDiskController.hpp" + "include/devices/storage/RAMDisk/RAMDiskDevice.hpp" +) + +target_include_directories("kernel_devices" PUBLIC + "include" +) \ No newline at end of file diff --git a/kernel/devices/include/devices/BlockDevice.hpp b/kernel/devices/include/devices/BlockDevice.hpp new file mode 100644 index 0000000..db66683 --- /dev/null +++ b/kernel/devices/include/devices/BlockDevice.hpp @@ -0,0 +1,11 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP +#define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP + +namespace devices +{ + struct block_device + { + }; +} // namespace devices + +#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/Device.hpp b/kernel/devices/include/devices/Device.hpp new file mode 100644 index 0000000..a3cac42 --- /dev/null +++ b/kernel/devices/include/devices/Device.hpp @@ -0,0 +1,11 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_DEVICE_HPP +#define TEACH_OS_KERNEL_DEVICES_DEVICE_HPP + +namespace devices +{ + struct device + { + }; +} // namespace devices + +#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp new file mode 100644 index 0000000..3fcb645 --- /dev/null +++ b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp @@ -0,0 +1,11 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP + +namespace devices::storage::ram_disk +{ + struct ram_disk_controller + { + }; +} // namespace devices::storage::ram_disk + +#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp new file mode 100644 index 0000000..98471cf --- /dev/null +++ b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp @@ -0,0 +1,11 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP + +namespace devices::storage::ram_disk +{ + struct ram_disk_device + { + }; +} // namespace devices::storage::ram_disk + +#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/StorageController.hpp b/kernel/devices/include/devices/storage/StorageController.hpp new file mode 100644 index 0000000..3167be0 --- /dev/null +++ b/kernel/devices/include/devices/storage/StorageController.hpp @@ -0,0 +1,11 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP + +namespace devices::storage +{ + struct storage_controller + { + }; +} // namespace devices::storage + +#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/StorageManagement.hpp b/kernel/devices/include/devices/storage/StorageManagement.hpp new file mode 100644 index 0000000..2b2eb22 --- /dev/null +++ b/kernel/devices/include/devices/storage/StorageManagement.hpp @@ -0,0 +1,16 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP + +namespace devices::storage +{ + struct storage_management + { + //! @qualifier kernel-defined + //! Initialize the storage management subsystem. + auto init() -> void; + + private: + }; +} // namespace devices::storage + +#endif \ No newline at end of file diff --git a/kernel/devices/src/BlockDevice.cpp b/kernel/devices/src/BlockDevice.cpp new file mode 100644 index 0000000..970f7b2 --- /dev/null +++ b/kernel/devices/src/BlockDevice.cpp @@ -0,0 +1,6 @@ +#include "devices/BlockDevice.hpp" + +namespace devices +{ + // TODO BA-FS26 implement block device functionality +} // namespace devices \ No newline at end of file diff --git a/kernel/devices/src/Device.cpp b/kernel/devices/src/Device.cpp new file mode 100644 index 0000000..d755fb9 --- /dev/null +++ b/kernel/devices/src/Device.cpp @@ -0,0 +1,6 @@ +#include "devices/Device.hpp" + +namespace devices +{ + // TODO BA-FS26 implement device functionality +} // namespace devices \ No newline at end of file diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp new file mode 100644 index 0000000..9c6427d --- /dev/null +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp @@ -0,0 +1,6 @@ +#include "devices/storage/RAMDisk/RAMDiskController.hpp" + +namespace devices::storage::ram_disk +{ + // TODO BA-FS26 implement ram disk controller functionality +} // namespace devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp new file mode 100644 index 0000000..8d8f51e --- /dev/null +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp @@ -0,0 +1,6 @@ +#include "devices/storage/RAMDisk/RAMDiskDevice.hpp" + +namespace devices::storage::ram_disk +{ + // TODO BA-FS26 implement ram disk device functionality +} // namespace devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/devices/src/storage/StorageController.cpp b/kernel/devices/src/storage/StorageController.cpp new file mode 100644 index 0000000..fe386cf --- /dev/null +++ b/kernel/devices/src/storage/StorageController.cpp @@ -0,0 +1,6 @@ +#include "devices/storage/StorageController.hpp" + +namespace devices::storage +{ + // TODO BA-FS26 implement storage controller functionality +} // namespace devices::storage \ No newline at end of file diff --git a/kernel/devices/src/storage/StorageManagement.cpp b/kernel/devices/src/storage/StorageManagement.cpp new file mode 100644 index 0000000..f34fa15 --- /dev/null +++ b/kernel/devices/src/storage/StorageManagement.cpp @@ -0,0 +1,9 @@ +#include "devices/storage/StorageManagement.hpp" + +namespace devices::storage +{ + auto storage_management::init() -> void + { + // TODO BA-FS26 implement storage management initialization + } +} // namespace devices::storage \ No newline at end of file -- cgit v1.2.3 From e84c7fbf336847d3ff62aac10ed8f6d04a06cbe8 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 27 Feb 2026 19:27:41 +0100 Subject: use linear_address instead of size_t --- arch/x86_64/kapi/boot_modules.cpp | 4 +++- kapi/include/kapi/boot_module/boot_module.hpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/x86_64/kapi/boot_modules.cpp b/arch/x86_64/kapi/boot_modules.cpp index 5d06eb5..ba01285 100644 --- a/arch/x86_64/kapi/boot_modules.cpp +++ b/arch/x86_64/kapi/boot_modules.cpp @@ -3,6 +3,7 @@ #include "kapi/boot.hpp" #include "kapi/boot_module/boot_module.hpp" #include "kapi/boot_module/boot_module_registry.hpp" +#include "kapi/memory.hpp" #include "kapi/system.hpp" #include "arch/boot/boot.hpp" @@ -41,7 +42,8 @@ namespace kapi::boot_modules std::ranges::for_each(modules, [](auto const & module) { registry->add_boot_module(kapi::boot_modules::boot_module{ .name = module.string(), - .start_address = module.start_address + std::bit_cast(&arch::boot::TEACHOS_VMA), + .start_address = + memory::linear_address{module.start_address + std::bit_cast(&arch::boot::TEACHOS_VMA)}, .size = module.end_address - module.start_address, }); }); diff --git a/kapi/include/kapi/boot_module/boot_module.hpp b/kapi/include/kapi/boot_module/boot_module.hpp index f2d97ae..729efc9 100644 --- a/kapi/include/kapi/boot_module/boot_module.hpp +++ b/kapi/include/kapi/boot_module/boot_module.hpp @@ -1,6 +1,8 @@ #ifndef TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_HPP #define TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_HPP +#include "kapi/memory.hpp" + #include #include @@ -13,7 +15,7 @@ namespace kapi::boot_modules struct boot_module { std::string_view name; - size_t start_address; + memory::linear_address start_address{}; size_t size; }; } // namespace kapi::boot_modules -- cgit v1.2.3 From 296d58550e8e1202d83e66034c24e9454a1b67dc Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 27 Feb 2026 23:23:18 +0100 Subject: - add boot_module_registry getter --- .../kapi/boot_module/boot_module_registry.hpp | 2 +- kapi/include/kapi/boot_modules.hpp | 6 ++++++ kernel/kapi/boot_modules.cpp | 21 +++++++++++++++++---- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/kapi/include/kapi/boot_module/boot_module_registry.hpp b/kapi/include/kapi/boot_module/boot_module_registry.hpp index 3732a5f..eeb01ff 100644 --- a/kapi/include/kapi/boot_module/boot_module_registry.hpp +++ b/kapi/include/kapi/boot_module/boot_module_registry.hpp @@ -15,7 +15,7 @@ namespace kapi::boot_modules // ! providing access to them for the rest of the kernel. struct boot_module_registry { - using range_type = std::array; // TODO BA-FS26 use kstd::vector when available + using range_type = std::array; // TODO BA-FS26 use kstd::vector when available using value_type = range_type::value_type; using const_reference = range_type::const_reference; diff --git a/kapi/include/kapi/boot_modules.hpp b/kapi/include/kapi/boot_modules.hpp index 752b070..6eee169 100644 --- a/kapi/include/kapi/boot_modules.hpp +++ b/kapi/include/kapi/boot_modules.hpp @@ -21,5 +21,11 @@ namespace kapi::boot_modules //! @param registry A new boot module registry. auto set_boot_module_registry(boot_module_registry & registry) -> void; + //! @qualifier kernel-defined + //! Get the boot module registry. + //! + //! @returns The boot module registry. + auto get_boot_module_registry() -> boot_module_registry &; + } // namespace kapi::boot_modules #endif \ No newline at end of file diff --git a/kernel/kapi/boot_modules.cpp b/kernel/kapi/boot_modules.cpp index 3f0f044..5a0ef7f 100644 --- a/kernel/kapi/boot_modules.cpp +++ b/kernel/kapi/boot_modules.cpp @@ -7,15 +7,28 @@ namespace kapi::boot_modules { - constinit auto static registry = std::optional{}; + namespace + { + constinit auto static registry = std::optional{}; + } // namespace - auto set_boot_module_registry(boot_module_registry & registry) -> void + auto set_boot_module_registry(boot_module_registry & new_registry) -> void { - if (kapi::boot_modules::registry) + if (registry) { system::panic("[x86_64] Boot module registry has already been set."); } - kapi::boot_modules::registry = registry; + registry = new_registry; + } + + auto get_boot_module_registry() -> boot_module_registry & + { + if (!registry) + { + system::panic("[x86_64] Boot module registry has not been initialized."); + } + + return *registry; } } // namespace kapi::boot_modules -- cgit v1.2.3 From 7a9bdbc58361ff22491785d778474571035ad697 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sat, 28 Feb 2026 15:18:56 +0100 Subject: Implement basic structure of storage management --- kernel/devices/CMakeLists.txt | 4 +++ .../include/devices/storage/StorageManagement.hpp | 20 +++++++++-- kernel/devices/src/storage/StorageManagement.cpp | 39 +++++++++++++++++++++- 3 files changed, 59 insertions(+), 4 deletions(-) diff --git a/kernel/devices/CMakeLists.txt b/kernel/devices/CMakeLists.txt index 80b89bf..8022dee 100644 --- a/kernel/devices/CMakeLists.txt +++ b/kernel/devices/CMakeLists.txt @@ -24,4 +24,8 @@ target_sources("kernel_devices" PUBLIC target_include_directories("kernel_devices" PUBLIC "include" +) + +target_link_libraries("kernel_devices" PRIVATE + "os::kapi" ) \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/StorageManagement.hpp b/kernel/devices/include/devices/storage/StorageManagement.hpp index 2b2eb22..9098312 100644 --- a/kernel/devices/include/devices/storage/StorageManagement.hpp +++ b/kernel/devices/include/devices/storage/StorageManagement.hpp @@ -1,15 +1,29 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP +#include "devices/BlockDevice.hpp" +#include "devices/storage/StorageController.hpp" + +#include + namespace devices::storage { struct storage_management { - //! @qualifier kernel-defined - //! Initialize the storage management subsystem. - auto init() -> void; + // TODO BA-FS26 add documentation + + auto static init() -> void; + auto static get() -> storage_management &; + + auto add_controller(storage_controller * controller) -> void; + + auto add_device(block_device * device) -> void; private: + storage_management() = default; + + std::array m_controllers{}; // TODO BA-FS26 use kstd::vector + std::array m_devices{}; // TODO BA-FS26 use kstd::vector }; } // namespace devices::storage diff --git a/kernel/devices/src/storage/StorageManagement.cpp b/kernel/devices/src/storage/StorageManagement.cpp index f34fa15..14c59ac 100644 --- a/kernel/devices/src/storage/StorageManagement.cpp +++ b/kernel/devices/src/storage/StorageManagement.cpp @@ -1,9 +1,46 @@ #include "devices/storage/StorageManagement.hpp" +#include "kapi/system.hpp" + +#include "devices/BlockDevice.hpp" +#include "devices/storage/StorageController.hpp" + +#include + namespace devices::storage { + namespace + { + constinit auto static active_storage_management = std::optional{}; + } // namespace + auto storage_management::init() -> void { - // TODO BA-FS26 implement storage management initialization + if (active_storage_management) + { + kapi::system::panic("[DEVICES] Storage management has already been initialized."); + } + active_storage_management.emplace(storage_management{}); + } + + auto storage_management::get() -> storage_management & + { + if (!active_storage_management) + { + kapi::system::panic("[DEVICES] Storage management has not been initialized."); + } + + return *active_storage_management; + } + + auto storage_management::add_controller(storage_controller * controller) -> void + { + m_controllers.at(0) = controller; // TODO BA-FS26 use push_back from kstd:vector } + + auto storage_management::add_device(block_device * device) -> void + { + m_devices.at(0) = device; // TODO BA-FS26 use push_back from kstd:vector + } + } // namespace devices::storage \ No newline at end of file -- cgit v1.2.3 From 2d52fae22143ec94c3741e60fd74374f9675e742 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sat, 28 Feb 2026 17:03:03 +0100 Subject: Implement RAMDiskController and instantiate it in StorageManagement --- kernel/devices/CMakeLists.txt | 1 - .../devices/storage/RAMDisk/RAMDiskController.hpp | 21 +++++++++++++++- .../include/devices/storage/StorageController.hpp | 5 ++++ .../src/storage/RAMDisk/RAMDiskController.cpp | 29 +++++++++++++++++++++- kernel/devices/src/storage/StorageController.cpp | 6 ----- kernel/devices/src/storage/StorageManagement.cpp | 11 ++++++++ 6 files changed, 64 insertions(+), 9 deletions(-) delete mode 100644 kernel/devices/src/storage/StorageController.cpp diff --git a/kernel/devices/CMakeLists.txt b/kernel/devices/CMakeLists.txt index 8022dee..0707a03 100644 --- a/kernel/devices/CMakeLists.txt +++ b/kernel/devices/CMakeLists.txt @@ -4,7 +4,6 @@ add_library("kernel::devices" ALIAS "kernel_devices") target_sources("kernel_devices" PRIVATE "src/Device.cpp" "src/BlockDevice.cpp" - "src/storage/StorageController.cpp" "src/storage/StorageManagement.cpp" "src/storage/RAMDisk/RAMDiskController.cpp" "src/storage/RAMDisk/RAMDiskDevice.cpp" diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp index 3fcb645..6213ed4 100644 --- a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp +++ b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp @@ -1,10 +1,29 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP +#include "kapi/boot_module/boot_module.hpp" +#include "kapi/boot_module/boot_module_registry.hpp" + +#include "devices/storage/RAMDisk/RAMDiskDevice.hpp" +#include "devices/storage/StorageController.hpp" + +#include +#include + namespace devices::storage::ram_disk { - struct ram_disk_controller + struct ram_disk_controller : storage_controller { + explicit ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry); + + auto probe() -> void override; + auto devices_count() -> size_t override; + + private: + auto create_device_from_boot_module(kapi::boot_modules::boot_module const & module) -> void; + + kapi::boot_modules::boot_module_registry const * m_boot_module_registry; + std::array m_devices{}; // TODO BA-FS26 use kstd::vector }; } // namespace devices::storage::ram_disk diff --git a/kernel/devices/include/devices/storage/StorageController.hpp b/kernel/devices/include/devices/storage/StorageController.hpp index 3167be0..7904d07 100644 --- a/kernel/devices/include/devices/storage/StorageController.hpp +++ b/kernel/devices/include/devices/storage/StorageController.hpp @@ -1,10 +1,15 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP +#include namespace devices::storage { struct storage_controller { + virtual ~storage_controller() = default; + + virtual auto probe() -> void = 0; + virtual auto devices_count() -> size_t = 0; }; } // namespace devices::storage diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp index 9c6427d..c4807b0 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp @@ -1,6 +1,33 @@ #include "devices/storage/RAMDisk/RAMDiskController.hpp" +#include "kapi/boot_module/boot_module.hpp" +#include "kapi/boot_module/boot_module_registry.hpp" + +#include + +#include +#include + namespace devices::storage::ram_disk { - // TODO BA-FS26 implement ram disk controller functionality + ram_disk_controller::ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry) + : m_boot_module_registry(registry) + {} + + auto ram_disk_controller::probe() -> void + { + std::ranges::for_each(*m_boot_module_registry, + [this](auto const & module) { create_device_from_boot_module(module); }); + } + + auto ram_disk_controller::devices_count() -> size_t + { + return m_devices.size(); + } + + auto create_device_from_boot_module(kapi::boot_modules::boot_module const & module) -> void + { + kstd::println("[RAM DISK CONTROLLER] Found boot module: {} at address {} with size {} bytes", module.name, + module.start_address, module.size); + } } // namespace devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/devices/src/storage/StorageController.cpp b/kernel/devices/src/storage/StorageController.cpp deleted file mode 100644 index fe386cf..0000000 --- a/kernel/devices/src/storage/StorageController.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "devices/storage/StorageController.hpp" - -namespace devices::storage -{ - // TODO BA-FS26 implement storage controller functionality -} // namespace devices::storage \ No newline at end of file diff --git a/kernel/devices/src/storage/StorageManagement.cpp b/kernel/devices/src/storage/StorageManagement.cpp index 14c59ac..9cc9a06 100644 --- a/kernel/devices/src/storage/StorageManagement.cpp +++ b/kernel/devices/src/storage/StorageManagement.cpp @@ -1,8 +1,10 @@ #include "devices/storage/StorageManagement.hpp" +#include "kapi/boot_modules.hpp" #include "kapi/system.hpp" #include "devices/BlockDevice.hpp" +#include "devices/storage/RAMDisk/RAMDiskController.hpp" #include "devices/storage/StorageController.hpp" #include @@ -12,6 +14,7 @@ namespace devices::storage namespace { constinit auto static active_storage_management = std::optional{}; + constinit auto static active_ram_disk_controller = std::optional{}; } // namespace auto storage_management::init() -> void @@ -21,6 +24,14 @@ namespace devices::storage kapi::system::panic("[DEVICES] Storage management has already been initialized."); } active_storage_management.emplace(storage_management{}); + + active_ram_disk_controller.emplace(&kapi::boot_modules::get_boot_module_registry()); + active_storage_management->add_controller(&active_ram_disk_controller.value()); + + for (auto controller : active_storage_management->m_controllers) + { + controller->probe(); + } } auto storage_management::get() -> storage_management & -- cgit v1.2.3 From 22015882dfb4f4d94df88693f5c0f3832c441066 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sat, 28 Feb 2026 17:33:22 +0100 Subject: Fix build --- kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp index c4807b0..3e12e0d 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp @@ -25,7 +25,7 @@ namespace devices::storage::ram_disk return m_devices.size(); } - auto create_device_from_boot_module(kapi::boot_modules::boot_module const & module) -> void + auto ram_disk_controller::create_device_from_boot_module(kapi::boot_modules::boot_module const & module) -> void { kstd::println("[RAM DISK CONTROLLER] Found boot module: {} at address {} with size {} bytes", module.name, module.start_address, module.size); -- cgit v1.2.3 From dd60285848be17083d7ef77fa0ed8896b6e087b7 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sat, 28 Feb 2026 17:33:42 +0100 Subject: Initialize storage management from kernel main --- kernel/src/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 117a1d8..0336fef 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -3,6 +3,8 @@ #include "kapi/memory.hpp" #include "kapi/system.hpp" +#include "devices/storage/StorageManagement.hpp" + #include "kernel/memory.hpp" #include @@ -20,5 +22,8 @@ auto main() -> int kapi::boot_modules::init(); kstd::println("[OS] Boot module registry initialized."); + devices::storage::storage_management::init(); + kstd::println("[OS] Storage management initialized."); + kapi::system::panic("Returning from kernel main!"); } -- cgit v1.2.3 From d22f98cb70587dc451db5cfc0abd4b7fd89ee602 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 28 Feb 2026 19:09:54 +0100 Subject: small refactoring, add some todos --- .../include/devices/storage/RAMDisk/RAMDiskController.hpp | 3 ++- kernel/devices/include/devices/storage/StorageController.hpp | 4 ++++ kernel/devices/include/devices/storage/StorageManagement.hpp | 9 ++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp index 6213ed4..bb61bbc 100644 --- a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp +++ b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp @@ -17,13 +17,14 @@ namespace devices::storage::ram_disk explicit ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry); auto probe() -> void override; + auto devices_count() -> size_t override; private: auto create_device_from_boot_module(kapi::boot_modules::boot_module const & module) -> void; kapi::boot_modules::boot_module_registry const * m_boot_module_registry; - std::array m_devices{}; // TODO BA-FS26 use kstd::vector + std::array m_devices{}; // TODO BA-FS26 use kstd::vector when available }; } // namespace devices::storage::ram_disk diff --git a/kernel/devices/include/devices/storage/StorageController.hpp b/kernel/devices/include/devices/storage/StorageController.hpp index 7904d07..68c6907 100644 --- a/kernel/devices/include/devices/storage/StorageController.hpp +++ b/kernel/devices/include/devices/storage/StorageController.hpp @@ -9,7 +9,11 @@ namespace devices::storage virtual ~storage_controller() = default; virtual auto probe() -> void = 0; + virtual auto devices_count() -> size_t = 0; + + // TODO BA-FS26 + // virtual auto get_device(size_t index) -> std::optional = 0; }; } // namespace devices::storage diff --git a/kernel/devices/include/devices/storage/StorageManagement.hpp b/kernel/devices/include/devices/storage/StorageManagement.hpp index 9098312..c237245 100644 --- a/kernel/devices/include/devices/storage/StorageManagement.hpp +++ b/kernel/devices/include/devices/storage/StorageManagement.hpp @@ -8,22 +8,21 @@ namespace devices::storage { + // TODO BA-FS26 add documentation struct storage_management { - // TODO BA-FS26 add documentation - auto static init() -> void; + auto static get() -> storage_management &; auto add_controller(storage_controller * controller) -> void; - auto add_device(block_device * device) -> void; private: storage_management() = default; - std::array m_controllers{}; // TODO BA-FS26 use kstd::vector - std::array m_devices{}; // TODO BA-FS26 use kstd::vector + std::array m_controllers{}; // TODO BA-FS26 use kstd::vector when available + std::array m_devices{}; // TODO BA-FS26 use kstd::vector when available }; } // namespace devices::storage -- cgit v1.2.3 From 47d94c6e1c0c46a9c5cdce528c8dca588a531595 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 28 Feb 2026 19:15:38 +0100 Subject: implement first draft of RAMDiskDevice --- kernel/devices/include/devices/BlockDevice.hpp | 5 ++++ .../devices/storage/RAMDisk/RAMDiskDevice.hpp | 18 +++++++++++- .../src/storage/RAMDisk/RAMDiskController.cpp | 15 +++++++--- .../devices/src/storage/RAMDisk/RAMDiskDevice.cpp | 34 +++++++++++++++++++++- 4 files changed, 66 insertions(+), 6 deletions(-) diff --git a/kernel/devices/include/devices/BlockDevice.hpp b/kernel/devices/include/devices/BlockDevice.hpp index db66683..69d7f81 100644 --- a/kernel/devices/include/devices/BlockDevice.hpp +++ b/kernel/devices/include/devices/BlockDevice.hpp @@ -1,10 +1,15 @@ #ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP #define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP +#include + namespace devices { struct block_device { + virtual ~block_device() = default; + + virtual auto read_block(size_t block_index, void * buffer) const -> void = 0; }; } // namespace devices diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp index 98471cf..aa736a4 100644 --- a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp +++ b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp @@ -1,10 +1,26 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP +#include "kapi/memory.hpp" + +#include "devices/BlockDevice.hpp" + +#include + namespace devices::storage::ram_disk { - struct ram_disk_device + struct ram_disk_device : block_device { + constexpr size_t static block_size = 512uz; // TODO BA-FS26 really correct / good?? + + ram_disk_device() = default; + ram_disk_device(kapi::memory::linear_address data_start, size_t data_size); + + auto read_block(size_t block_index, void * buffer) const -> void override; + + private: + kapi::memory::linear_address m_data_start{}; + size_t m_data_size{}; }; } // namespace devices::storage::ram_disk diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp index 3e12e0d..48b9116 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp @@ -3,6 +3,8 @@ #include "kapi/boot_module/boot_module.hpp" #include "kapi/boot_module/boot_module_registry.hpp" +#include "devices/storage/RAMDisk/RAMDiskDevice.hpp" + #include #include @@ -18,16 +20,21 @@ namespace devices::storage::ram_disk { std::ranges::for_each(*m_boot_module_registry, [this](auto const & module) { create_device_from_boot_module(module); }); - } - auto ram_disk_controller::devices_count() -> size_t - { - return m_devices.size(); + // TODO BA-FS26 just for testing, remove again + std::ranges::for_each(m_devices, [](auto const & device) { device.read_block(0, nullptr); }); } auto ram_disk_controller::create_device_from_boot_module(kapi::boot_modules::boot_module const & module) -> void { + m_devices.at(0) = ram_disk_device{module.start_address, module.size}; + kstd::println("[RAM DISK CONTROLLER] Found boot module: {} at address {} with size {} bytes", module.name, module.start_address, module.size); } + + auto ram_disk_controller::devices_count() -> size_t + { + return m_devices.size(); + } } // namespace devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp index 8d8f51e..1e650d8 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp @@ -1,6 +1,38 @@ #include "devices/storage/RAMDisk/RAMDiskDevice.hpp" +#include "kapi/memory.hpp" +#include "kapi/system.hpp" + +#include + +#include + namespace devices::storage::ram_disk { - // TODO BA-FS26 implement ram disk device functionality + ram_disk_device::ram_disk_device(kapi::memory::linear_address data_start, size_t data_size) + : m_data_start(data_start) + , m_data_size(data_size) + {} + + auto ram_disk_device::read_block(size_t block_index, void * /*buffer*/) const -> void + { + // if (buffer == nullptr) + // { + // kapi::system::panic("[RAM DISK DEVICE] read_block called with null buffer."); + // } + + auto const offset = block_index * block_size; + if (offset + block_size > + m_data_size) // TODO BA-FS26 really correct, what if block_size doesn't divide m_data_size? + { + kapi::system::panic("[RAM DISK DEVICE] read_block out of bounds."); + } + + auto const source = static_cast(m_data_start) + offset; + for (size_t i = 0; i < block_size; ++i) + { + kstd::println("address: {}, value: {}", source + i, std::to_integer(*(source + i))); + } + // std::memcpy(buffer, source, block_size); + } } // namespace devices::storage::ram_disk \ No newline at end of file -- cgit v1.2.3 From 9c24c688f945173b5265f6142df2ae377e5dbc18 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 1 Mar 2026 17:42:36 +0100 Subject: refactoring, remove test code --- .../devices/storage/RAMDisk/RAMDiskDevice.hpp | 6 +++--- .../src/storage/RAMDisk/RAMDiskController.cpp | 8 +------- .../devices/src/storage/RAMDisk/RAMDiskDevice.cpp | 23 +++++++++++----------- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp index aa736a4..eb88f86 100644 --- a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp +++ b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP -#include "kapi/memory.hpp" +#include "kapi/boot_module/boot_module.hpp" #include "devices/BlockDevice.hpp" @@ -14,12 +14,12 @@ namespace devices::storage::ram_disk constexpr size_t static block_size = 512uz; // TODO BA-FS26 really correct / good?? ram_disk_device() = default; - ram_disk_device(kapi::memory::linear_address data_start, size_t data_size); + ram_disk_device(kapi::boot_modules::boot_module const & module); auto read_block(size_t block_index, void * buffer) const -> void override; private: - kapi::memory::linear_address m_data_start{}; + kapi::boot_modules::boot_module m_boot_module{}; size_t m_data_size{}; }; } // namespace devices::storage::ram_disk diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp index 48b9116..90a7c0f 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp @@ -20,17 +20,11 @@ namespace devices::storage::ram_disk { std::ranges::for_each(*m_boot_module_registry, [this](auto const & module) { create_device_from_boot_module(module); }); - - // TODO BA-FS26 just for testing, remove again - std::ranges::for_each(m_devices, [](auto const & device) { device.read_block(0, nullptr); }); } auto ram_disk_controller::create_device_from_boot_module(kapi::boot_modules::boot_module const & module) -> void { - m_devices.at(0) = ram_disk_device{module.start_address, module.size}; - - kstd::println("[RAM DISK CONTROLLER] Found boot module: {} at address {} with size {} bytes", module.name, - module.start_address, module.size); + m_devices.at(0) = ram_disk_device{module}; } auto ram_disk_controller::devices_count() -> size_t diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp index 1e650d8..82a3cdf 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp @@ -1,6 +1,6 @@ #include "devices/storage/RAMDisk/RAMDiskDevice.hpp" -#include "kapi/memory.hpp" +#include "kapi/boot_module/boot_module.hpp" #include "kapi/system.hpp" #include @@ -9,26 +9,25 @@ namespace devices::storage::ram_disk { - ram_disk_device::ram_disk_device(kapi::memory::linear_address data_start, size_t data_size) - : m_data_start(data_start) - , m_data_size(data_size) + ram_disk_device::ram_disk_device(kapi::boot_modules::boot_module const & module) + : m_boot_module(module) {} - auto ram_disk_device::read_block(size_t block_index, void * /*buffer*/) const -> void + auto ram_disk_device::read_block(size_t block_index, void * buffer) const -> void { - // if (buffer == nullptr) - // { - // kapi::system::panic("[RAM DISK DEVICE] read_block called with null buffer."); - // } + if (buffer == nullptr) + { + kapi::system::panic("[RAM DISK DEVICE] read_block called with null buffer."); + } auto const offset = block_index * block_size; - if (offset + block_size > - m_data_size) // TODO BA-FS26 really correct, what if block_size doesn't divide m_data_size? + // TODO BA-FS26 really correct, what if block_size doesn't divide m_boot_module.size? + if (offset + block_size > m_boot_module.size) { kapi::system::panic("[RAM DISK DEVICE] read_block out of bounds."); } - auto const source = static_cast(m_data_start) + offset; + auto const source = static_cast(m_boot_module.start_address) + offset; for (size_t i = 0; i < block_size; ++i) { kstd::println("address: {}, value: {}", source + i, std::to_integer(*(source + i))); -- cgit v1.2.3 From 9a1b7e18ad2d9c81e176802d9e922baa882a565d Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 1 Mar 2026 17:45:33 +0100 Subject: use stl algorithm --- kernel/devices/src/storage/StorageManagement.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kernel/devices/src/storage/StorageManagement.cpp b/kernel/devices/src/storage/StorageManagement.cpp index 9cc9a06..2daae0a 100644 --- a/kernel/devices/src/storage/StorageManagement.cpp +++ b/kernel/devices/src/storage/StorageManagement.cpp @@ -7,6 +7,7 @@ #include "devices/storage/RAMDisk/RAMDiskController.hpp" #include "devices/storage/StorageController.hpp" +#include #include namespace devices::storage @@ -28,10 +29,7 @@ namespace devices::storage active_ram_disk_controller.emplace(&kapi::boot_modules::get_boot_module_registry()); active_storage_management->add_controller(&active_ram_disk_controller.value()); - for (auto controller : active_storage_management->m_controllers) - { - controller->probe(); - } + std::ranges::for_each(active_storage_management->m_controllers, [](auto controller) { controller->probe(); }); } auto storage_management::get() -> storage_management & -- cgit v1.2.3 From 760f6ca4d59f6bf90f70944df4f418c236f8eab1 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 2 Mar 2026 23:41:56 +0100 Subject: remove not needed m_data_size member --- kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp index eb88f86..fdcef3f 100644 --- a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp +++ b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp @@ -20,7 +20,6 @@ namespace devices::storage::ram_disk private: kapi::boot_modules::boot_module m_boot_module{}; - size_t m_data_size{}; }; } // namespace devices::storage::ram_disk -- cgit v1.2.3 From 62bf2eef72854750c7325d2e2c6e92562a522e16 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 2 Mar 2026 23:43:09 +0100 Subject: implement memcpy --- libs/kstd/CMakeLists.txt | 2 + libs/kstd/include/kstd/cstring | 19 +++++++ libs/kstd/src/libc/stdlib.cpp | 36 ++++++------- libs/kstd/src/libc/string.cpp | 116 +++++++++++++++++++++++------------------ 4 files changed, 103 insertions(+), 70 deletions(-) create mode 100644 libs/kstd/include/kstd/cstring diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 1f140f6..77b12a9 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -5,6 +5,7 @@ set(KSTD_LIBC_SYMBOLS "abort" "strlen" "memcmp" + "memcpy" ) target_sources("kstd" PRIVATE @@ -33,6 +34,7 @@ target_sources("kstd" PUBLIC "include/kstd/os/print.hpp" "include/kstd/asm_ptr" + "include/kstd/cstring" "include/kstd/format" "include/kstd/memory" "include/kstd/mutex" diff --git a/libs/kstd/include/kstd/cstring b/libs/kstd/include/kstd/cstring new file mode 100644 index 0000000..e97ecac --- /dev/null +++ b/libs/kstd/include/kstd/cstring @@ -0,0 +1,19 @@ +#ifndef KSTD_CSTRING +#define KSTD_CSTRING + +#include + +namespace kstd::libc +{ + + extern "C" + { + auto memcpy(void * dest, void const * src, 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 strlen(char const * string) -> std::size_t; + } + +} // namespace kstd::libc + +#endif \ No newline at end of file diff --git a/libs/kstd/src/libc/stdlib.cpp b/libs/kstd/src/libc/stdlib.cpp index 4a5c91f..bb40605 100644 --- a/libs/kstd/src/libc/stdlib.cpp +++ b/libs/kstd/src/libc/stdlib.cpp @@ -1,19 +1,19 @@ -#include "kstd/os/error.hpp" - -namespace kstd::libc -{ - - extern "C" - { - [[noreturn]] auto abort() -> void - { - kstd::os::abort(); - } - - [[noreturn, gnu::weak]] auto free(void *) -> void - { - kstd::os::panic("Tried to call free."); - } - } - +#include "kstd/os/error.hpp" + +namespace kstd::libc +{ + + extern "C" + { + [[noreturn]] auto abort() -> void + { + kstd::os::abort(); + } + + [[noreturn, gnu::weak]] auto free(void *) -> void + { + kstd::os::panic("Tried to call free."); + } + } + } // namespace kstd::libc \ No newline at end of file diff --git a/libs/kstd/src/libc/string.cpp b/libs/kstd/src/libc/string.cpp index b7d4c6b..4e264f9 100644 --- a/libs/kstd/src/libc/string.cpp +++ b/libs/kstd/src/libc/string.cpp @@ -1,53 +1,65 @@ -#include -#include -#include -#include -#include - -namespace kstd::libc -{ - - extern "C" - { - auto strlen(char const * string) -> std::size_t - { - return std::distance(string, std::ranges::find(string, nullptr, '\0')); - } - - auto memcmp(void const * lhs, void const * rhs, std::size_t size) -> std::size_t - { - auto left_span = std::span{static_cast(lhs), size}; - auto right_span = std::span{static_cast(rhs), size}; - auto mismatched = std::ranges::mismatch(left_span, right_span); - - if (mismatched.in1 == left_span.end()) - { - return 0; - } - - return std::bit_cast(*mismatched.in1) - std::bit_cast(*mismatched.in2); - } - - auto memmove(void * dest, void const * src, std::size_t size) -> void * - { - auto dest_span = std::span{static_cast(dest), size}; - auto src_span = std::span{static_cast(src), size}; - if (dest < src) - { - for (std::size_t i = 0; i < size; ++i) - { - dest_span[i] = src_span[i]; - } - } - else - { - for (std::size_t i = size; i > 0; --i) - { - dest_span[i - 1] = src_span[i - 1]; - } - } - return dest; - } - } - +#include + +#include +#include +#include +#include +#include + +namespace kstd::libc +{ + + auto memcpy(void * dest, void const * src, std::size_t size) -> void * + { + auto dest_span = std::span{static_cast(dest), size}; + auto src_span = std::span{static_cast(src), size}; + + for (std::size_t i = 0; i < size; ++i) + { + dest_span[i] = src_span[i]; + } + + return dest; + } + + auto strlen(char const * string) -> std::size_t + { + return std::distance(string, std::ranges::find(string, nullptr, '\0')); + } + + auto memcmp(void const * lhs, void const * rhs, std::size_t size) -> std::size_t + { + auto left_span = std::span{static_cast(lhs), size}; + auto right_span = std::span{static_cast(rhs), size}; + auto mismatched = std::ranges::mismatch(left_span, right_span); + + if (mismatched.in1 == left_span.end()) + { + return 0; + } + + return std::bit_cast(*mismatched.in1) - std::bit_cast(*mismatched.in2); + } + + auto memmove(void * dest, void const * src, std::size_t size) -> void * + { + auto dest_span = std::span{static_cast(dest), size}; + auto src_span = std::span{static_cast(src), size}; + if (dest < src) + { + for (std::size_t i = 0; i < size; ++i) + { + dest_span[i] = src_span[i]; + } + } + else + { + for (std::size_t i = size; i > 0; --i) + { + dest_span[i - 1] = src_span[i - 1]; + } + } + return dest; + } + } // namespace kstd::libc \ No newline at end of file -- cgit v1.2.3 From 9eeaf95fcc6b2d6302d8447940678e1597d26f0a Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 2 Mar 2026 23:44:53 +0100 Subject: copy data into the buffer in ram_disk_device::read_block --- kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp index 82a3cdf..339e7fa 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp @@ -3,7 +3,7 @@ #include "kapi/boot_module/boot_module.hpp" #include "kapi/system.hpp" -#include +#include #include @@ -20,18 +20,12 @@ namespace devices::storage::ram_disk kapi::system::panic("[RAM DISK DEVICE] read_block called with null buffer."); } - auto const offset = block_index * block_size; - // TODO BA-FS26 really correct, what if block_size doesn't divide m_boot_module.size? - if (offset + block_size > m_boot_module.size) - { - kapi::system::panic("[RAM DISK DEVICE] read_block out of bounds."); - } + // TODO BA-FS26 add bounds checking based on module size? + // TODO BA-FS26 fill with 0 after the end of the module, if the block extends beyond it? + auto const offset = block_index * block_size; auto const source = static_cast(m_boot_module.start_address) + offset; - for (size_t i = 0; i < block_size; ++i) - { - kstd::println("address: {}, value: {}", source + i, std::to_integer(*(source + i))); - } - // std::memcpy(buffer, source, block_size); + + kstd::libc::memcpy(buffer, source, block_size); } } // namespace devices::storage::ram_disk \ No newline at end of file -- cgit v1.2.3 From d2e9e3ee57918ddd4a1f81e70304dc15964555ff Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 3 Mar 2026 08:37:56 +0100 Subject: implement function to get a device by major and minor number --- kernel/devices/include/devices/BlockDevice.hpp | 4 +++- kernel/devices/include/devices/Device.hpp | 9 +++++++++ .../include/devices/storage/RAMDisk/RAMDiskController.hpp | 2 ++ .../devices/include/devices/storage/StorageController.hpp | 7 ++++--- .../devices/include/devices/storage/StorageManagement.hpp | 4 ++-- kernel/devices/src/Device.cpp | 13 ++++++++++++- kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp | 15 +++++++++++++++ kernel/devices/src/storage/StorageManagement.cpp | 12 ++++++++++-- 8 files changed, 57 insertions(+), 9 deletions(-) diff --git a/kernel/devices/include/devices/BlockDevice.hpp b/kernel/devices/include/devices/BlockDevice.hpp index 69d7f81..9e9ef08 100644 --- a/kernel/devices/include/devices/BlockDevice.hpp +++ b/kernel/devices/include/devices/BlockDevice.hpp @@ -1,11 +1,13 @@ #ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP #define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP +#include "devices/Device.hpp" + #include namespace devices { - struct block_device + struct block_device : device { virtual ~block_device() = default; diff --git a/kernel/devices/include/devices/Device.hpp b/kernel/devices/include/devices/Device.hpp index a3cac42..815049f 100644 --- a/kernel/devices/include/devices/Device.hpp +++ b/kernel/devices/include/devices/Device.hpp @@ -1,10 +1,19 @@ #ifndef TEACH_OS_KERNEL_DEVICES_DEVICE_HPP #define TEACH_OS_KERNEL_DEVICES_DEVICE_HPP +#include namespace devices { struct device { + virtual ~device() = default; + + auto major() const -> size_t; + auto minor() const -> size_t; + + private: + size_t m_major = 0; // TODO BA-FS26 initialize correctly + size_t m_minor = 0; // TODO BA-FS26 initialize correctly }; } // namespace devices diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp index bb61bbc..7d656b1 100644 --- a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp +++ b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp @@ -4,6 +4,7 @@ #include "kapi/boot_module/boot_module.hpp" #include "kapi/boot_module/boot_module_registry.hpp" +#include "devices/BlockDevice.hpp" #include "devices/storage/RAMDisk/RAMDiskDevice.hpp" #include "devices/storage/StorageController.hpp" @@ -19,6 +20,7 @@ namespace devices::storage::ram_disk auto probe() -> void override; auto devices_count() -> size_t override; + auto device_by_major_minor(size_t major, size_t minor) -> block_device * override; private: auto create_device_from_boot_module(kapi::boot_modules::boot_module const & module) -> void; diff --git a/kernel/devices/include/devices/storage/StorageController.hpp b/kernel/devices/include/devices/storage/StorageController.hpp index 68c6907..a51683d 100644 --- a/kernel/devices/include/devices/storage/StorageController.hpp +++ b/kernel/devices/include/devices/storage/StorageController.hpp @@ -1,7 +1,10 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP +#include "devices/BlockDevice.hpp" + #include + namespace devices::storage { struct storage_controller @@ -11,9 +14,7 @@ namespace devices::storage virtual auto probe() -> void = 0; virtual auto devices_count() -> size_t = 0; - - // TODO BA-FS26 - // virtual auto get_device(size_t index) -> std::optional = 0; + virtual auto device_by_major_minor(size_t major, size_t minor) -> block_device * = 0; }; } // namespace devices::storage diff --git a/kernel/devices/include/devices/storage/StorageManagement.hpp b/kernel/devices/include/devices/storage/StorageManagement.hpp index c237245..632ff19 100644 --- a/kernel/devices/include/devices/storage/StorageManagement.hpp +++ b/kernel/devices/include/devices/storage/StorageManagement.hpp @@ -5,6 +5,7 @@ #include "devices/storage/StorageController.hpp" #include +#include namespace devices::storage { @@ -16,13 +17,12 @@ namespace devices::storage auto static get() -> storage_management &; auto add_controller(storage_controller * controller) -> void; - auto add_device(block_device * device) -> void; + auto device_by_major_minor(size_t major, size_t minor) -> block_device *; private: storage_management() = default; std::array m_controllers{}; // TODO BA-FS26 use kstd::vector when available - std::array m_devices{}; // TODO BA-FS26 use kstd::vector when available }; } // namespace devices::storage diff --git a/kernel/devices/src/Device.cpp b/kernel/devices/src/Device.cpp index d755fb9..48ba29b 100644 --- a/kernel/devices/src/Device.cpp +++ b/kernel/devices/src/Device.cpp @@ -1,6 +1,17 @@ #include "devices/Device.hpp" +#include + namespace devices { - // TODO BA-FS26 implement device functionality + + auto device::major() const -> size_t + { + return m_major; + } + + auto device::minor() const -> size_t + { + return m_minor; + } } // namespace devices \ No newline at end of file diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp index 90a7c0f..ec2cb97 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp @@ -3,12 +3,15 @@ #include "kapi/boot_module/boot_module.hpp" #include "kapi/boot_module/boot_module_registry.hpp" +#include "devices/BlockDevice.hpp" #include "devices/storage/RAMDisk/RAMDiskDevice.hpp" #include #include +#include #include +#include namespace devices::storage::ram_disk { @@ -27,6 +30,18 @@ namespace devices::storage::ram_disk m_devices.at(0) = ram_disk_device{module}; } + auto ram_disk_controller::device_by_major_minor(size_t major, size_t minor) -> block_device * + { + auto it = std::ranges::find_if( + m_devices, [major, minor](auto const & device) { return device.major() == major && device.minor() == minor; }); + + if (it != m_devices.end()) + { + return &(*it); + } + return nullptr; + } + auto ram_disk_controller::devices_count() -> size_t { return m_devices.size(); diff --git a/kernel/devices/src/storage/StorageManagement.cpp b/kernel/devices/src/storage/StorageManagement.cpp index 2daae0a..cfc5239 100644 --- a/kernel/devices/src/storage/StorageManagement.cpp +++ b/kernel/devices/src/storage/StorageManagement.cpp @@ -8,6 +8,7 @@ #include "devices/storage/StorageController.hpp" #include +#include #include namespace devices::storage @@ -47,9 +48,16 @@ namespace devices::storage m_controllers.at(0) = controller; // TODO BA-FS26 use push_back from kstd:vector } - auto storage_management::add_device(block_device * device) -> void + auto storage_management::device_by_major_minor(size_t major, size_t minor) -> block_device * { - m_devices.at(0) = device; // TODO BA-FS26 use push_back from kstd:vector + block_device * found = nullptr; + + std::ranges::find_if(m_controllers, [&](auto const controller) { + found = controller->device_by_major_minor(major, minor); + return found != nullptr; + }); + + return found; } } // namespace devices::storage \ No newline at end of file -- cgit v1.2.3 From 52a007ebee4e8ca284ec5767de01c311e9f1860e Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 3 Mar 2026 10:15:20 +0100 Subject: implement major and minor analog to the linux kernel --- kernel/devices/include/devices/BlockDevice.hpp | 1 + kernel/devices/include/devices/Device.hpp | 6 ++++-- .../include/devices/storage/RAMDisk/RAMDiskDevice.hpp | 4 ++-- .../include/devices/storage/StorageController.hpp | 7 +++++++ kernel/devices/src/BlockDevice.cpp | 8 +++++++- kernel/devices/src/Device.cpp | 4 ++++ .../devices/src/storage/RAMDisk/RAMDiskController.cpp | 11 +++++++++++ kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp | 11 +++++++++-- kernel/devices/src/storage/StorageController.cpp | 17 +++++++++++++++++ kernel/devices/src/storage/StorageManagement.cpp | 12 ++++++++++-- 10 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 kernel/devices/src/storage/StorageController.cpp diff --git a/kernel/devices/include/devices/BlockDevice.hpp b/kernel/devices/include/devices/BlockDevice.hpp index 9e9ef08..2f9d0aa 100644 --- a/kernel/devices/include/devices/BlockDevice.hpp +++ b/kernel/devices/include/devices/BlockDevice.hpp @@ -9,6 +9,7 @@ namespace devices { struct block_device : device { + block_device(size_t major, size_t minor); virtual ~block_device() = default; virtual auto read_block(size_t block_index, void * buffer) const -> void = 0; diff --git a/kernel/devices/include/devices/Device.hpp b/kernel/devices/include/devices/Device.hpp index 815049f..0f93bff 100644 --- a/kernel/devices/include/devices/Device.hpp +++ b/kernel/devices/include/devices/Device.hpp @@ -2,18 +2,20 @@ #define TEACH_OS_KERNEL_DEVICES_DEVICE_HPP #include + namespace devices { struct device { + device(size_t major, size_t minor); virtual ~device() = default; auto major() const -> size_t; auto minor() const -> size_t; private: - size_t m_major = 0; // TODO BA-FS26 initialize correctly - size_t m_minor = 0; // TODO BA-FS26 initialize correctly + size_t m_major; + size_t m_minor; }; } // namespace devices diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp index fdcef3f..de71663 100644 --- a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp +++ b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp @@ -13,8 +13,8 @@ namespace devices::storage::ram_disk { constexpr size_t static block_size = 512uz; // TODO BA-FS26 really correct / good?? - ram_disk_device() = default; - ram_disk_device(kapi::boot_modules::boot_module const & module); + ram_disk_device(); // TODO BA-FS26 remove when kstd::vector is available + ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor); auto read_block(size_t block_index, void * buffer) const -> void override; diff --git a/kernel/devices/include/devices/storage/StorageController.hpp b/kernel/devices/include/devices/storage/StorageController.hpp index a51683d..50fdb34 100644 --- a/kernel/devices/include/devices/storage/StorageController.hpp +++ b/kernel/devices/include/devices/storage/StorageController.hpp @@ -13,8 +13,15 @@ namespace devices::storage virtual auto probe() -> void = 0; + auto set_ids(size_t major, size_t minors_per_dev) -> void; + auto major() const -> size_t; + virtual auto devices_count() -> size_t = 0; virtual auto device_by_major_minor(size_t major, size_t minor) -> block_device * = 0; + + protected: + size_t m_major{}; + size_t m_minors_per_device{}; }; } // namespace devices::storage diff --git a/kernel/devices/src/BlockDevice.cpp b/kernel/devices/src/BlockDevice.cpp index 970f7b2..6d43073 100644 --- a/kernel/devices/src/BlockDevice.cpp +++ b/kernel/devices/src/BlockDevice.cpp @@ -1,6 +1,12 @@ #include "devices/BlockDevice.hpp" +#include "devices/Device.hpp" + +#include + namespace devices { - // TODO BA-FS26 implement block device functionality + block_device::block_device(size_t major, size_t minor) + : device(major, minor) + {} } // namespace devices \ No newline at end of file diff --git a/kernel/devices/src/Device.cpp b/kernel/devices/src/Device.cpp index 48ba29b..38fdd48 100644 --- a/kernel/devices/src/Device.cpp +++ b/kernel/devices/src/Device.cpp @@ -4,6 +4,10 @@ namespace devices { + device::device(size_t major, size_t minor) + : m_major(major) + , m_minor(minor) + {} auto device::major() const -> size_t { diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp index ec2cb97..1f70e2d 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp @@ -21,6 +21,17 @@ namespace devices::storage::ram_disk auto ram_disk_controller::probe() -> void { + size_t current_device_index = 0; // Starte bei 0 + + std::ranges::for_each(*m_boot_module_registry, [this, ¤t_device_index](auto const & module) { + auto const minor = current_device_index * m_minors_per_device; + + // TODO BA-FS26 use push_back from kstd::vector when available + m_devices.at(0) = ram_disk_device{module, m_major, minor}; + + current_device_index++; + }); + std::ranges::for_each(*m_boot_module_registry, [this](auto const & module) { create_device_from_boot_module(module); }); } diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp index 339e7fa..f33cf94 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp @@ -3,14 +3,21 @@ #include "kapi/boot_module/boot_module.hpp" #include "kapi/system.hpp" +#include "devices/BlockDevice.hpp" + #include #include namespace devices::storage::ram_disk { - ram_disk_device::ram_disk_device(kapi::boot_modules::boot_module const & module) - : m_boot_module(module) + ram_disk_device::ram_disk_device() // TODO BA-FS26 remove when kstd::vector is available + : block_device(0, 0) + {} + + ram_disk_device::ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor) + : block_device(major, minor) + , m_boot_module(module) {} auto ram_disk_device::read_block(size_t block_index, void * buffer) const -> void diff --git a/kernel/devices/src/storage/StorageController.cpp b/kernel/devices/src/storage/StorageController.cpp new file mode 100644 index 0000000..d11a175 --- /dev/null +++ b/kernel/devices/src/storage/StorageController.cpp @@ -0,0 +1,17 @@ +#include "devices/storage/StorageController.hpp" + +#include + +namespace devices::storage +{ + auto storage_controller::set_ids(size_t major, size_t minors_per_dev) -> void + { + m_major = major; + m_minors_per_device = minors_per_dev; + } + + auto storage_controller::major() const -> size_t + { + return m_major; + } +} // namespace devices::storage \ No newline at end of file diff --git a/kernel/devices/src/storage/StorageManagement.cpp b/kernel/devices/src/storage/StorageManagement.cpp index cfc5239..58d8510 100644 --- a/kernel/devices/src/storage/StorageManagement.cpp +++ b/kernel/devices/src/storage/StorageManagement.cpp @@ -15,6 +15,9 @@ namespace devices::storage { namespace { + constexpr size_t static MINORS_PER_DEVICE = 16; + constinit size_t static next_free_major = 1; + constinit auto static active_storage_management = std::optional{}; constinit auto static active_ram_disk_controller = std::optional{}; } // namespace @@ -45,6 +48,7 @@ namespace devices::storage auto storage_management::add_controller(storage_controller * controller) -> void { + controller->set_ids(next_free_major++, MINORS_PER_DEVICE); m_controllers.at(0) = controller; // TODO BA-FS26 use push_back from kstd:vector } @@ -53,8 +57,12 @@ namespace devices::storage block_device * found = nullptr; std::ranges::find_if(m_controllers, [&](auto const controller) { - found = controller->device_by_major_minor(major, minor); - return found != nullptr; + if (controller != nullptr && controller->major() == major) + { + found = controller->device_by_major_minor(major, minor); + return found != nullptr; + } + return false; }); return found; -- cgit v1.2.3 From a8ebcdb0d16fe224d8f05577ea367864cb2fa9c2 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 3 Mar 2026 10:24:23 +0100 Subject: fix build add test code to read a block from device --- .../devices/storage/RAMDisk/RAMDiskController.hpp | 2 -- .../src/storage/RAMDisk/RAMDiskController.cpp | 26 ++++++++++++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp index 7d656b1..699c547 100644 --- a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp +++ b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp @@ -23,8 +23,6 @@ namespace devices::storage::ram_disk auto device_by_major_minor(size_t major, size_t minor) -> block_device * override; private: - auto create_device_from_boot_module(kapi::boot_modules::boot_module const & module) -> void; - kapi::boot_modules::boot_module_registry const * m_boot_module_registry; std::array m_devices{}; // TODO BA-FS26 use kstd::vector when available }; diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp index 1f70e2d..1235c46 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp @@ -24,21 +24,29 @@ namespace devices::storage::ram_disk size_t current_device_index = 0; // Starte bei 0 std::ranges::for_each(*m_boot_module_registry, [this, ¤t_device_index](auto const & module) { - auto const minor = current_device_index * m_minors_per_device; + auto const minor = current_device_index++ * m_minors_per_device; // TODO BA-FS26 use push_back from kstd::vector when available m_devices.at(0) = ram_disk_device{module, m_major, minor}; - - current_device_index++; }); - std::ranges::for_each(*m_boot_module_registry, - [this](auto const & module) { create_device_from_boot_module(module); }); - } + std::ranges::for_each(m_devices, [](auto device) { + std::array buffer{}; + device.read_block(0, buffer.data()); - auto ram_disk_controller::create_device_from_boot_module(kapi::boot_modules::boot_module const & module) -> void - { - m_devices.at(0) = ram_disk_device{module}; + kstd::println("Contents of Block 0 for device:"); + + for (size_t i = 0; i < buffer.size(); ++i) + { + kstd::print("{:02x} ", static_cast(buffer[i])); + + if ((i + 1) % 16 == 0) + { + kstd::println(""); + } + } + kstd::println("--- End of Block ---"); + }); } auto ram_disk_controller::device_by_major_minor(size_t major, size_t minor) -> block_device * -- cgit v1.2.3 From 14d06ffce75625c8cd0cfa705d33c410fc934937 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 3 Mar 2026 10:37:53 +0100 Subject: fix build, refactoring --- kernel/devices/CMakeLists.txt | 1 + kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp | 1 - kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp | 3 +-- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/kernel/devices/CMakeLists.txt b/kernel/devices/CMakeLists.txt index 0707a03..8022dee 100644 --- a/kernel/devices/CMakeLists.txt +++ b/kernel/devices/CMakeLists.txt @@ -4,6 +4,7 @@ add_library("kernel::devices" ALIAS "kernel_devices") target_sources("kernel_devices" PRIVATE "src/Device.cpp" "src/BlockDevice.cpp" + "src/storage/StorageController.cpp" "src/storage/StorageManagement.cpp" "src/storage/RAMDisk/RAMDiskController.cpp" "src/storage/RAMDisk/RAMDiskDevice.cpp" diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp index 699c547..3e6e2bd 100644 --- a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp +++ b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp @@ -1,7 +1,6 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP -#include "kapi/boot_module/boot_module.hpp" #include "kapi/boot_module/boot_module_registry.hpp" #include "devices/BlockDevice.hpp" diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp index 1235c46..d76f2cd 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp @@ -1,6 +1,5 @@ #include "devices/storage/RAMDisk/RAMDiskController.hpp" -#include "kapi/boot_module/boot_module.hpp" #include "kapi/boot_module/boot_module_registry.hpp" #include "devices/BlockDevice.hpp" @@ -21,7 +20,7 @@ namespace devices::storage::ram_disk auto ram_disk_controller::probe() -> void { - size_t current_device_index = 0; // Starte bei 0 + size_t current_device_index = 0; std::ranges::for_each(*m_boot_module_registry, [this, ¤t_device_index](auto const & module) { auto const minor = current_device_index++ * m_minors_per_device; -- cgit v1.2.3 From 09e16896dfcd87c289be18b13867e64441efcaf5 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 3 Mar 2026 11:18:16 +0100 Subject: make module pages writable --- arch/x86_64/kapi/memory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp index 3a2d66b..d6c0ad5 100644 --- a/arch/x86_64/kapi/memory.cpp +++ b/arch/x86_64/kapi/memory.cpp @@ -135,7 +135,7 @@ namespace kapi::memory { auto page = page::containing(module_virtual_start) + i; auto frame = frame::containing(module_physical_start) + i; - mapper.map(page, frame, page_mapper::flags::empty); // TODO BA-FS26 make writable? + mapper.map(page, frame, page_mapper::flags::writable); } } } -- cgit v1.2.3 From 43875979efea5dd73f63ac66c4fcc697c752f6ef Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 3 Mar 2026 11:20:22 +0100 Subject: implement write_block in RAMDiskDevice --- kernel/devices/include/devices/BlockDevice.hpp | 1 + .../include/devices/storage/RAMDisk/RAMDiskDevice.hpp | 1 + kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp | 16 ++++++++++++++++ 3 files changed, 18 insertions(+) diff --git a/kernel/devices/include/devices/BlockDevice.hpp b/kernel/devices/include/devices/BlockDevice.hpp index 2f9d0aa..65d765b 100644 --- a/kernel/devices/include/devices/BlockDevice.hpp +++ b/kernel/devices/include/devices/BlockDevice.hpp @@ -13,6 +13,7 @@ namespace devices virtual ~block_device() = default; virtual auto read_block(size_t block_index, void * buffer) const -> void = 0; + virtual auto write_block(size_t block_index, void const * buffer) -> void = 0; }; } // namespace devices diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp index de71663..b3838e4 100644 --- a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp +++ b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp @@ -17,6 +17,7 @@ namespace devices::storage::ram_disk ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor); auto read_block(size_t block_index, void * buffer) const -> void override; + auto write_block(size_t block_index, void const * buffer) -> void override; private: kapi::boot_modules::boot_module m_boot_module{}; diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp index f33cf94..1bc475d 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp @@ -35,4 +35,20 @@ namespace devices::storage::ram_disk kstd::libc::memcpy(buffer, source, block_size); } + + auto ram_disk_device::write_block(size_t block_index, void const * buffer) -> void + { + if (buffer == nullptr) + { + kapi::system::panic("[RAM DISK DEVICE] write_block called with null buffer."); + } + + // TODO BA-FS26 add bounds checking based on module size? + // TODO BA-FS26 ignore writes beyond the end of the module? + + auto const offset = block_index * block_size; + auto const destination = static_cast(m_boot_module.start_address) + offset; + + kstd::libc::memcpy(destination, buffer, block_size); + } } // namespace devices::storage::ram_disk \ No newline at end of file -- cgit v1.2.3 From e9839b25356c420193cf8b90ed5043eb07aabff0 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 3 Mar 2026 12:42:40 +0100 Subject: remove test code --- .../devices/src/storage/RAMDisk/RAMDiskController.cpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp index d76f2cd..5c18dac 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp @@ -10,7 +10,6 @@ #include #include #include -#include namespace devices::storage::ram_disk { @@ -28,24 +27,6 @@ namespace devices::storage::ram_disk // TODO BA-FS26 use push_back from kstd::vector when available m_devices.at(0) = ram_disk_device{module, m_major, minor}; }); - - std::ranges::for_each(m_devices, [](auto device) { - std::array buffer{}; - device.read_block(0, buffer.data()); - - kstd::println("Contents of Block 0 for device:"); - - for (size_t i = 0; i < buffer.size(); ++i) - { - kstd::print("{:02x} ", static_cast(buffer[i])); - - if ((i + 1) % 16 == 0) - { - kstd::println(""); - } - } - kstd::println("--- End of Block ---"); - }); } auto ram_disk_controller::device_by_major_minor(size_t major, size_t minor) -> block_device * -- cgit v1.2.3 From 880cd2194507c95449949f799d48a4b8ff3e0cd5 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 3 Mar 2026 13:52:30 +0100 Subject: implement determine_boot_device --- kernel/devices/include/devices/storage/StorageManagement.hpp | 2 ++ kernel/devices/src/storage/StorageManagement.cpp | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/kernel/devices/include/devices/storage/StorageManagement.hpp b/kernel/devices/include/devices/storage/StorageManagement.hpp index 632ff19..226fb28 100644 --- a/kernel/devices/include/devices/storage/StorageManagement.hpp +++ b/kernel/devices/include/devices/storage/StorageManagement.hpp @@ -17,7 +17,9 @@ namespace devices::storage auto static get() -> storage_management &; auto add_controller(storage_controller * controller) -> void; + auto device_by_major_minor(size_t major, size_t minor) -> block_device *; + auto determine_boot_device() -> block_device *; private: storage_management() = default; diff --git a/kernel/devices/src/storage/StorageManagement.cpp b/kernel/devices/src/storage/StorageManagement.cpp index 58d8510..1b0d2cc 100644 --- a/kernel/devices/src/storage/StorageManagement.cpp +++ b/kernel/devices/src/storage/StorageManagement.cpp @@ -16,7 +16,8 @@ namespace devices::storage namespace { constexpr size_t static MINORS_PER_DEVICE = 16; - constinit size_t static next_free_major = 1; + constexpr size_t static START_MAJOR = 1; + constinit size_t static next_free_major = START_MAJOR; constinit auto static active_storage_management = std::optional{}; constinit auto static active_ram_disk_controller = std::optional{}; @@ -68,4 +69,10 @@ namespace devices::storage return found; } + auto storage_management::determine_boot_device() -> block_device * + { + // TODO BA-FS26 better way? + return device_by_major_minor(START_MAJOR, 0); + } + } // namespace devices::storage \ No newline at end of file -- cgit v1.2.3 From f25b3c3cb5fd1e00521b91d575da0177dbb44ddc Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 3 Mar 2026 13:53:47 +0100 Subject: implement memset --- libs/kstd/include/kstd/cstring | 2 ++ libs/kstd/src/libc/string.cpp | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/libs/kstd/include/kstd/cstring b/libs/kstd/include/kstd/cstring index e97ecac..06932a4 100644 --- a/libs/kstd/include/kstd/cstring +++ b/libs/kstd/include/kstd/cstring @@ -9,8 +9,10 @@ namespace kstd::libc extern "C" { auto memcpy(void * dest, void const * src, std::size_t size) -> void *; + auto memset(void * dest, std::byte 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 strlen(char const * string) -> std::size_t; } diff --git a/libs/kstd/src/libc/string.cpp b/libs/kstd/src/libc/string.cpp index 4e264f9..302046d 100644 --- a/libs/kstd/src/libc/string.cpp +++ b/libs/kstd/src/libc/string.cpp @@ -22,9 +22,16 @@ namespace kstd::libc return dest; } - auto strlen(char const * string) -> std::size_t + auto memset(void * dest, std::byte value, std::size_t size) -> void * { - return std::distance(string, std::ranges::find(string, nullptr, '\0')); + auto dest_span = std::span{static_cast(dest), size}; + + for (std::size_t i = 0; i < size; ++i) + { + dest_span[i] = value; + } + + return dest; } auto memcmp(void const * lhs, void const * rhs, std::size_t size) -> std::size_t @@ -62,4 +69,9 @@ namespace kstd::libc return dest; } + auto strlen(char const * string) -> std::size_t + { + return std::distance(string, std::ranges::find(string, nullptr, '\0')); + } + } // namespace kstd::libc \ No newline at end of file -- cgit v1.2.3 From 1883c8bbcbd2aeff57ce8f2dac1289f68418bbc7 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 3 Mar 2026 13:54:48 +0100 Subject: handle read / write behind module boundaries --- .../devices/src/storage/RAMDisk/RAMDiskDevice.cpp | 33 +++++++++++++++------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp index 1bc475d..627641c 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp @@ -27,13 +27,22 @@ namespace devices::storage::ram_disk kapi::system::panic("[RAM DISK DEVICE] read_block called with null buffer."); } - // TODO BA-FS26 add bounds checking based on module size? - // TODO BA-FS26 fill with 0 after the end of the module, if the block extends beyond it? + size_t const offset = block_index * block_size; + size_t const limit = m_boot_module.size; - auto const offset = block_index * block_size; - auto const source = static_cast(m_boot_module.start_address) + offset; + size_t const available = (offset < limit) ? (limit - offset) : 0; + size_t const to_copy = (available < block_size) ? available : block_size; - kstd::libc::memcpy(buffer, source, block_size); + if (to_copy > 0) + { + auto const source = static_cast(m_boot_module.start_address) + offset; + kstd::libc::memcpy(buffer, source, to_copy); + } + + if (to_copy < block_size) + { + kstd::libc::memset(static_cast(buffer) + to_copy, std::byte{0}, block_size - to_copy); + } } auto ram_disk_device::write_block(size_t block_index, void const * buffer) -> void @@ -43,12 +52,16 @@ namespace devices::storage::ram_disk kapi::system::panic("[RAM DISK DEVICE] write_block called with null buffer."); } - // TODO BA-FS26 add bounds checking based on module size? - // TODO BA-FS26 ignore writes beyond the end of the module? + size_t const offset = block_index * block_size; + size_t const limit = m_boot_module.size; - auto const offset = block_index * block_size; - auto const destination = static_cast(m_boot_module.start_address) + offset; + if (offset >= limit) + return; - kstd::libc::memcpy(destination, buffer, block_size); + size_t const available = limit - offset; + size_t const to_write = (available < block_size) ? available : block_size; + + auto const destination = static_cast(m_boot_module.start_address) + offset; + kstd::libc::memcpy(destination, buffer, to_write); } } // namespace devices::storage::ram_disk \ No newline at end of file -- cgit v1.2.3 From fe0aadec94834b72f4511ce5e300b9fb22e66e60 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 3 Mar 2026 13:57:33 +0100 Subject: small refactoring --- libs/kstd/include/kstd/cstring | 2 +- libs/kstd/src/libc/string.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libs/kstd/include/kstd/cstring b/libs/kstd/include/kstd/cstring index 06932a4..a5c41fa 100644 --- a/libs/kstd/include/kstd/cstring +++ b/libs/kstd/include/kstd/cstring @@ -9,7 +9,7 @@ namespace kstd::libc extern "C" { auto memcpy(void * dest, void const * src, std::size_t size) -> void *; - auto memset(void * dest, std::byte value, 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; diff --git a/libs/kstd/src/libc/string.cpp b/libs/kstd/src/libc/string.cpp index 302046d..63f012c 100644 --- a/libs/kstd/src/libc/string.cpp +++ b/libs/kstd/src/libc/string.cpp @@ -22,13 +22,14 @@ namespace kstd::libc return dest; } - auto memset(void * dest, std::byte value, std::size_t size) -> void * + auto memset(void * dest, int value, std::size_t size) -> void * { + auto const byte_value = static_cast(static_cast(value)); auto dest_span = std::span{static_cast(dest), size}; for (std::size_t i = 0; i < size; ++i) { - dest_span[i] = value; + dest_span[i] = byte_value; } return dest; -- cgit v1.2.3 From 2ae7a868b867289b9591d662972f559d412315c3 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 3 Mar 2026 14:11:49 +0100 Subject: refactoring read and write block calculations --- kernel/devices/include/devices/BlockDevice.hpp | 15 +++++++- .../devices/storage/RAMDisk/RAMDiskDevice.hpp | 4 +- kernel/devices/src/BlockDevice.cpp | 14 ++++++- .../devices/src/storage/RAMDisk/RAMDiskDevice.cpp | 44 +++++++++++----------- 4 files changed, 52 insertions(+), 25 deletions(-) diff --git a/kernel/devices/include/devices/BlockDevice.hpp b/kernel/devices/include/devices/BlockDevice.hpp index 65d765b..56e26d0 100644 --- a/kernel/devices/include/devices/BlockDevice.hpp +++ b/kernel/devices/include/devices/BlockDevice.hpp @@ -9,11 +9,24 @@ namespace devices { struct block_device : device { - block_device(size_t major, size_t minor); + block_device(size_t major, size_t minor, size_t block_size); virtual ~block_device() = default; virtual auto read_block(size_t block_index, void * buffer) const -> void = 0; virtual auto write_block(size_t block_index, void const * buffer) -> void = 0; + + protected: + struct transfer_info + { + size_t offset; + size_t to_transfer; + size_t remainder; + }; + + virtual auto size() const -> size_t = 0; + auto calculate_transfer(size_t block_index) const -> transfer_info; + + size_t m_block_size; }; } // namespace devices diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp index b3838e4..5971970 100644 --- a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp +++ b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp @@ -11,8 +11,6 @@ namespace devices::storage::ram_disk { struct ram_disk_device : block_device { - constexpr size_t static block_size = 512uz; // TODO BA-FS26 really correct / good?? - ram_disk_device(); // TODO BA-FS26 remove when kstd::vector is available ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor); @@ -20,6 +18,8 @@ namespace devices::storage::ram_disk auto write_block(size_t block_index, void const * buffer) -> void override; private: + auto size() const -> size_t override; + kapi::boot_modules::boot_module m_boot_module{}; }; } // namespace devices::storage::ram_disk diff --git a/kernel/devices/src/BlockDevice.cpp b/kernel/devices/src/BlockDevice.cpp index 6d43073..edc0109 100644 --- a/kernel/devices/src/BlockDevice.cpp +++ b/kernel/devices/src/BlockDevice.cpp @@ -6,7 +6,19 @@ namespace devices { - block_device::block_device(size_t major, size_t minor) + block_device::block_device(size_t major, size_t minor, size_t block_size) : device(major, minor) + , m_block_size(block_size) {} + + auto block_device::calculate_transfer(size_t block_index) const -> transfer_info + { + size_t const offset = block_index * m_block_size; + size_t const limit = size(); + + size_t const available = (offset < limit) ? (limit - offset) : 0; + size_t const to_transfer = (available < m_block_size) ? available : m_block_size; + + return {offset, to_transfer, m_block_size - to_transfer}; + } } // namespace devices \ No newline at end of file diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp index 627641c..f3b8799 100644 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp +++ b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp @@ -11,12 +11,17 @@ namespace devices::storage::ram_disk { + namespace + { + constexpr size_t RAM_DISK_BLOCK_SIZE = 512uz; // TODO BA-FS26 really correct / good?? + } // namespace + ram_disk_device::ram_disk_device() // TODO BA-FS26 remove when kstd::vector is available - : block_device(0, 0) + : block_device(0, 0, RAM_DISK_BLOCK_SIZE) {} ram_disk_device::ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor) - : block_device(major, minor) + : block_device(major, minor, RAM_DISK_BLOCK_SIZE) , m_boot_module(module) {} @@ -27,21 +32,17 @@ namespace devices::storage::ram_disk kapi::system::panic("[RAM DISK DEVICE] read_block called with null buffer."); } - size_t const offset = block_index * block_size; - size_t const limit = m_boot_module.size; + auto const info = calculate_transfer(block_index); - size_t const available = (offset < limit) ? (limit - offset) : 0; - size_t const to_copy = (available < block_size) ? available : block_size; - - if (to_copy > 0) + if (info.to_transfer > 0) { - auto const source = static_cast(m_boot_module.start_address) + offset; - kstd::libc::memcpy(buffer, source, to_copy); + auto const src = static_cast(m_boot_module.start_address) + info.offset; + kstd::libc::memcpy(buffer, src, info.to_transfer); } - if (to_copy < block_size) + if (info.remainder > 0) { - kstd::libc::memset(static_cast(buffer) + to_copy, std::byte{0}, block_size - to_copy); + kstd::libc::memset(static_cast(buffer) + info.to_transfer, 0, info.remainder); } } @@ -52,16 +53,17 @@ namespace devices::storage::ram_disk kapi::system::panic("[RAM DISK DEVICE] write_block called with null buffer."); } - size_t const offset = block_index * block_size; - size_t const limit = m_boot_module.size; - - if (offset >= limit) - return; + auto const info = calculate_transfer(block_index); - size_t const available = limit - offset; - size_t const to_write = (available < block_size) ? available : block_size; + if (info.to_transfer > 0) + { + auto const dest = static_cast(m_boot_module.start_address) + info.offset; + kstd::libc::memcpy(dest, buffer, info.to_transfer); + } + } - auto const destination = static_cast(m_boot_module.start_address) + offset; - kstd::libc::memcpy(destination, buffer, to_write); + auto ram_disk_device::size() const -> size_t + { + return m_boot_module.size; } } // namespace devices::storage::ram_disk \ No newline at end of file -- cgit v1.2.3 From 5268bf6622463c3d233683fe56ae9977baf8eeaa Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 3 Mar 2026 16:57:02 +0100 Subject: rename files to snake_case (temp_device part1 -> renamed to device later, due to capital-/lower-letter problem) --- kernel/devices/CMakeLists.txt | 24 +++---- kernel/devices/include/devices/BlockDevice.hpp | 33 --------- kernel/devices/include/devices/Device.hpp | 22 ------ kernel/devices/include/devices/block_device.hpp | 33 +++++++++ .../devices/storage/RAMDisk/RAMDiskController.hpp | 30 --------- .../devices/storage/RAMDisk/RAMDiskDevice.hpp | 27 -------- .../include/devices/storage/StorageController.hpp | 28 -------- .../include/devices/storage/StorageManagement.hpp | 31 --------- .../storage/ram_disk/ram_disk_controller.hpp | 30 +++++++++ .../devices/storage/ram_disk/ram_disk_device.hpp | 27 ++++++++ .../include/devices/storage/storage_controller.hpp | 28 ++++++++ .../include/devices/storage/storage_management.hpp | 31 +++++++++ kernel/devices/include/devices/temp_device.hpp | 22 ++++++ kernel/devices/src/BlockDevice.cpp | 24 ------- kernel/devices/src/Device.cpp | 21 ------ kernel/devices/src/block_device.cpp | 24 +++++++ .../src/storage/RAMDisk/RAMDiskController.cpp | 48 ------------- .../devices/src/storage/RAMDisk/RAMDiskDevice.cpp | 69 ------------------- kernel/devices/src/storage/StorageController.cpp | 17 ----- kernel/devices/src/storage/StorageManagement.cpp | 78 ---------------------- .../src/storage/ram_disk/ram_disk_controller.cpp | 48 +++++++++++++ .../src/storage/ram_disk/ram_disk_device.cpp | 69 +++++++++++++++++++ kernel/devices/src/storage/storage_controller.cpp | 17 +++++ kernel/devices/src/storage/storage_management.cpp | 78 ++++++++++++++++++++++ kernel/devices/src/temp_device.cpp | 21 ++++++ kernel/src/main.cpp | 2 +- 26 files changed, 441 insertions(+), 441 deletions(-) delete mode 100644 kernel/devices/include/devices/BlockDevice.hpp delete mode 100644 kernel/devices/include/devices/Device.hpp create mode 100644 kernel/devices/include/devices/block_device.hpp delete mode 100644 kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp delete mode 100644 kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp delete mode 100644 kernel/devices/include/devices/storage/StorageController.hpp delete mode 100644 kernel/devices/include/devices/storage/StorageManagement.hpp create mode 100644 kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp create mode 100644 kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp create mode 100644 kernel/devices/include/devices/storage/storage_controller.hpp create mode 100644 kernel/devices/include/devices/storage/storage_management.hpp create mode 100644 kernel/devices/include/devices/temp_device.hpp delete mode 100644 kernel/devices/src/BlockDevice.cpp delete mode 100644 kernel/devices/src/Device.cpp create mode 100644 kernel/devices/src/block_device.cpp delete mode 100644 kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp delete mode 100644 kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp delete mode 100644 kernel/devices/src/storage/StorageController.cpp delete mode 100644 kernel/devices/src/storage/StorageManagement.cpp create mode 100644 kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp create mode 100644 kernel/devices/src/storage/ram_disk/ram_disk_device.cpp create mode 100644 kernel/devices/src/storage/storage_controller.cpp create mode 100644 kernel/devices/src/storage/storage_management.cpp create mode 100644 kernel/devices/src/temp_device.cpp diff --git a/kernel/devices/CMakeLists.txt b/kernel/devices/CMakeLists.txt index 8022dee..691bb11 100644 --- a/kernel/devices/CMakeLists.txt +++ b/kernel/devices/CMakeLists.txt @@ -2,24 +2,24 @@ add_library("kernel_devices" STATIC) add_library("kernel::devices" ALIAS "kernel_devices") target_sources("kernel_devices" PRIVATE - "src/Device.cpp" - "src/BlockDevice.cpp" - "src/storage/StorageController.cpp" - "src/storage/StorageManagement.cpp" - "src/storage/RAMDisk/RAMDiskController.cpp" - "src/storage/RAMDisk/RAMDiskDevice.cpp" + "src/temp_device.cpp" + "src/block_device.cpp" + "src/storage/storage_controller.cpp" + "src/storage/storage_management.cpp" + "src/storage/ram_disk/ram_disk_controller.cpp" + "src/storage/ram_disk/ram_disk_device.cpp" ) target_sources("kernel_devices" PUBLIC FILE_SET HEADERS BASE_DIRS "include" FILES - "include/devices/Device.hpp" - "include/devices/BlockDevice.hpp" - "include/devices/storage/StorageController.hpp" - "include/devices/storage/StorageManagement.hpp" - "include/devices/storage/RAMDisk/RAMDiskController.hpp" - "include/devices/storage/RAMDisk/RAMDiskDevice.hpp" + "include/devices/temp_device.hpp" + "include/devices/block_device.hpp" + "include/devices/storage/storage_controller.hpp" + "include/devices/storage/storage_management.hpp" + "include/devices/storage/ram_disk/ram_disk_controller.hpp" + "include/devices/storage/ram_disk/ram_disk_device.hpp" ) target_include_directories("kernel_devices" PUBLIC diff --git a/kernel/devices/include/devices/BlockDevice.hpp b/kernel/devices/include/devices/BlockDevice.hpp deleted file mode 100644 index 56e26d0..0000000 --- a/kernel/devices/include/devices/BlockDevice.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP -#define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP - -#include "devices/Device.hpp" - -#include - -namespace devices -{ - struct block_device : device - { - block_device(size_t major, size_t minor, size_t block_size); - virtual ~block_device() = default; - - virtual auto read_block(size_t block_index, void * buffer) const -> void = 0; - virtual auto write_block(size_t block_index, void const * buffer) -> void = 0; - - protected: - struct transfer_info - { - size_t offset; - size_t to_transfer; - size_t remainder; - }; - - virtual auto size() const -> size_t = 0; - auto calculate_transfer(size_t block_index) const -> transfer_info; - - size_t m_block_size; - }; -} // namespace devices - -#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/Device.hpp b/kernel/devices/include/devices/Device.hpp deleted file mode 100644 index 0f93bff..0000000 --- a/kernel/devices/include/devices/Device.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_DEVICE_HPP -#define TEACH_OS_KERNEL_DEVICES_DEVICE_HPP - -#include - -namespace devices -{ - struct device - { - device(size_t major, size_t minor); - virtual ~device() = default; - - auto major() const -> size_t; - auto minor() const -> size_t; - - private: - size_t m_major; - size_t m_minor; - }; -} // namespace devices - -#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/block_device.hpp b/kernel/devices/include/devices/block_device.hpp new file mode 100644 index 0000000..d1e4249 --- /dev/null +++ b/kernel/devices/include/devices/block_device.hpp @@ -0,0 +1,33 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP +#define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP + +#include "devices/temp_device.hpp" + +#include + +namespace devices +{ + struct block_device : device + { + block_device(size_t major, size_t minor, size_t block_size); + virtual ~block_device() = default; + + virtual auto read_block(size_t block_index, void * buffer) const -> void = 0; + virtual auto write_block(size_t block_index, void const * buffer) -> void = 0; + + protected: + struct transfer_info + { + size_t offset; + size_t to_transfer; + size_t remainder; + }; + + virtual auto size() const -> size_t = 0; + auto calculate_transfer(size_t block_index) const -> transfer_info; + + size_t m_block_size; + }; +} // namespace devices + +#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp deleted file mode 100644 index 3e6e2bd..0000000 --- a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskController.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP -#define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP - -#include "kapi/boot_module/boot_module_registry.hpp" - -#include "devices/BlockDevice.hpp" -#include "devices/storage/RAMDisk/RAMDiskDevice.hpp" -#include "devices/storage/StorageController.hpp" - -#include -#include - -namespace devices::storage::ram_disk -{ - struct ram_disk_controller : storage_controller - { - explicit ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry); - - auto probe() -> void override; - - auto devices_count() -> size_t override; - auto device_by_major_minor(size_t major, size_t minor) -> block_device * override; - - private: - kapi::boot_modules::boot_module_registry const * m_boot_module_registry; - std::array m_devices{}; // TODO BA-FS26 use kstd::vector when available - }; -} // namespace devices::storage::ram_disk - -#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp b/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp deleted file mode 100644 index 5971970..0000000 --- a/kernel/devices/include/devices/storage/RAMDisk/RAMDiskDevice.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP -#define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP - -#include "kapi/boot_module/boot_module.hpp" - -#include "devices/BlockDevice.hpp" - -#include - -namespace devices::storage::ram_disk -{ - struct ram_disk_device : block_device - { - ram_disk_device(); // TODO BA-FS26 remove when kstd::vector is available - ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor); - - auto read_block(size_t block_index, void * buffer) const -> void override; - auto write_block(size_t block_index, void const * buffer) -> void override; - - private: - auto size() const -> size_t override; - - kapi::boot_modules::boot_module m_boot_module{}; - }; -} // namespace devices::storage::ram_disk - -#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/StorageController.hpp b/kernel/devices/include/devices/storage/StorageController.hpp deleted file mode 100644 index 50fdb34..0000000 --- a/kernel/devices/include/devices/storage/StorageController.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP -#define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP - -#include "devices/BlockDevice.hpp" - -#include - -namespace devices::storage -{ - struct storage_controller - { - virtual ~storage_controller() = default; - - virtual auto probe() -> void = 0; - - auto set_ids(size_t major, size_t minors_per_dev) -> void; - auto major() const -> size_t; - - virtual auto devices_count() -> size_t = 0; - virtual auto device_by_major_minor(size_t major, size_t minor) -> block_device * = 0; - - protected: - size_t m_major{}; - size_t m_minors_per_device{}; - }; -} // namespace devices::storage - -#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/StorageManagement.hpp b/kernel/devices/include/devices/storage/StorageManagement.hpp deleted file mode 100644 index 226fb28..0000000 --- a/kernel/devices/include/devices/storage/StorageManagement.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP -#define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP - -#include "devices/BlockDevice.hpp" -#include "devices/storage/StorageController.hpp" - -#include -#include - -namespace devices::storage -{ - // TODO BA-FS26 add documentation - struct storage_management - { - auto static init() -> void; - - auto static get() -> storage_management &; - - auto add_controller(storage_controller * controller) -> void; - - auto device_by_major_minor(size_t major, size_t minor) -> block_device *; - auto determine_boot_device() -> block_device *; - - private: - storage_management() = default; - - std::array m_controllers{}; // TODO BA-FS26 use kstd::vector when available - }; -} // namespace devices::storage - -#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp b/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp new file mode 100644 index 0000000..f050ba0 --- /dev/null +++ b/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp @@ -0,0 +1,30 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP + +#include "kapi/boot_module/boot_module_registry.hpp" + +#include "devices/block_device.hpp" +#include "devices/storage/ram_disk/ram_disk_device.hpp" +#include "devices/storage/storage_controller.hpp" + +#include +#include + +namespace devices::storage::ram_disk +{ + struct ram_disk_controller : storage_controller + { + explicit ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry); + + auto probe() -> void override; + + auto devices_count() -> size_t override; + auto device_by_major_minor(size_t major, size_t minor) -> block_device * override; + + private: + kapi::boot_modules::boot_module_registry const * m_boot_module_registry; + std::array m_devices{}; // TODO BA-FS26 use kstd::vector when available + }; +} // namespace devices::storage::ram_disk + +#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp b/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp new file mode 100644 index 0000000..1f0292f --- /dev/null +++ b/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp @@ -0,0 +1,27 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP + +#include "kapi/boot_module/boot_module.hpp" + +#include "devices/block_device.hpp" + +#include + +namespace devices::storage::ram_disk +{ + struct ram_disk_device : block_device + { + ram_disk_device(); // TODO BA-FS26 remove when kstd::vector is available + ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor); + + auto read_block(size_t block_index, void * buffer) const -> void override; + auto write_block(size_t block_index, void const * buffer) -> void override; + + private: + auto size() const -> size_t override; + + kapi::boot_modules::boot_module m_boot_module{}; + }; +} // namespace devices::storage::ram_disk + +#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/storage_controller.hpp b/kernel/devices/include/devices/storage/storage_controller.hpp new file mode 100644 index 0000000..0c09815 --- /dev/null +++ b/kernel/devices/include/devices/storage/storage_controller.hpp @@ -0,0 +1,28 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP + +#include "devices/block_device.hpp" + +#include + +namespace devices::storage +{ + struct storage_controller + { + virtual ~storage_controller() = default; + + virtual auto probe() -> void = 0; + + auto set_ids(size_t major, size_t minors_per_dev) -> void; + auto major() const -> size_t; + + virtual auto devices_count() -> size_t = 0; + virtual auto device_by_major_minor(size_t major, size_t minor) -> block_device * = 0; + + protected: + size_t m_major{}; + size_t m_minors_per_device{}; + }; +} // namespace devices::storage + +#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/storage_management.hpp b/kernel/devices/include/devices/storage/storage_management.hpp new file mode 100644 index 0000000..c4f5679 --- /dev/null +++ b/kernel/devices/include/devices/storage/storage_management.hpp @@ -0,0 +1,31 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP + +#include "devices/block_device.hpp" +#include "devices/storage/storage_controller.hpp" + +#include +#include + +namespace devices::storage +{ + // TODO BA-FS26 add documentation + struct storage_management + { + auto static init() -> void; + + auto static get() -> storage_management &; + + auto add_controller(storage_controller * controller) -> void; + + auto device_by_major_minor(size_t major, size_t minor) -> block_device *; + auto determine_boot_device() -> block_device *; + + private: + storage_management() = default; + + std::array m_controllers{}; // TODO BA-FS26 use kstd::vector when available + }; +} // namespace devices::storage + +#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/temp_device.hpp b/kernel/devices/include/devices/temp_device.hpp new file mode 100644 index 0000000..0f93bff --- /dev/null +++ b/kernel/devices/include/devices/temp_device.hpp @@ -0,0 +1,22 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_DEVICE_HPP +#define TEACH_OS_KERNEL_DEVICES_DEVICE_HPP + +#include + +namespace devices +{ + struct device + { + device(size_t major, size_t minor); + virtual ~device() = default; + + auto major() const -> size_t; + auto minor() const -> size_t; + + private: + size_t m_major; + size_t m_minor; + }; +} // namespace devices + +#endif \ No newline at end of file diff --git a/kernel/devices/src/BlockDevice.cpp b/kernel/devices/src/BlockDevice.cpp deleted file mode 100644 index edc0109..0000000 --- a/kernel/devices/src/BlockDevice.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "devices/BlockDevice.hpp" - -#include "devices/Device.hpp" - -#include - -namespace devices -{ - block_device::block_device(size_t major, size_t minor, size_t block_size) - : device(major, minor) - , m_block_size(block_size) - {} - - auto block_device::calculate_transfer(size_t block_index) const -> transfer_info - { - size_t const offset = block_index * m_block_size; - size_t const limit = size(); - - size_t const available = (offset < limit) ? (limit - offset) : 0; - size_t const to_transfer = (available < m_block_size) ? available : m_block_size; - - return {offset, to_transfer, m_block_size - to_transfer}; - } -} // namespace devices \ No newline at end of file diff --git a/kernel/devices/src/Device.cpp b/kernel/devices/src/Device.cpp deleted file mode 100644 index 38fdd48..0000000 --- a/kernel/devices/src/Device.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "devices/Device.hpp" - -#include - -namespace devices -{ - device::device(size_t major, size_t minor) - : m_major(major) - , m_minor(minor) - {} - - auto device::major() const -> size_t - { - return m_major; - } - - auto device::minor() const -> size_t - { - return m_minor; - } -} // namespace devices \ No newline at end of file diff --git a/kernel/devices/src/block_device.cpp b/kernel/devices/src/block_device.cpp new file mode 100644 index 0000000..dd0fd44 --- /dev/null +++ b/kernel/devices/src/block_device.cpp @@ -0,0 +1,24 @@ +#include "devices/block_device.hpp" + +#include "devices/temp_device.hpp" + +#include + +namespace devices +{ + block_device::block_device(size_t major, size_t minor, size_t block_size) + : device(major, minor) + , m_block_size(block_size) + {} + + auto block_device::calculate_transfer(size_t block_index) const -> transfer_info + { + size_t const offset = block_index * m_block_size; + size_t const limit = size(); + + size_t const available = (offset < limit) ? (limit - offset) : 0; + size_t const to_transfer = (available < m_block_size) ? available : m_block_size; + + return {offset, to_transfer, m_block_size - to_transfer}; + } +} // namespace devices \ No newline at end of file diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp deleted file mode 100644 index 5c18dac..0000000 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskController.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "devices/storage/RAMDisk/RAMDiskController.hpp" - -#include "kapi/boot_module/boot_module_registry.hpp" - -#include "devices/BlockDevice.hpp" -#include "devices/storage/RAMDisk/RAMDiskDevice.hpp" - -#include - -#include -#include -#include - -namespace devices::storage::ram_disk -{ - ram_disk_controller::ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry) - : m_boot_module_registry(registry) - {} - - auto ram_disk_controller::probe() -> void - { - size_t current_device_index = 0; - - std::ranges::for_each(*m_boot_module_registry, [this, ¤t_device_index](auto const & module) { - auto const minor = current_device_index++ * m_minors_per_device; - - // TODO BA-FS26 use push_back from kstd::vector when available - m_devices.at(0) = ram_disk_device{module, m_major, minor}; - }); - } - - auto ram_disk_controller::device_by_major_minor(size_t major, size_t minor) -> block_device * - { - auto it = std::ranges::find_if( - m_devices, [major, minor](auto const & device) { return device.major() == major && device.minor() == minor; }); - - if (it != m_devices.end()) - { - return &(*it); - } - return nullptr; - } - - auto ram_disk_controller::devices_count() -> size_t - { - return m_devices.size(); - } -} // namespace devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp b/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp deleted file mode 100644 index f3b8799..0000000 --- a/kernel/devices/src/storage/RAMDisk/RAMDiskDevice.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "devices/storage/RAMDisk/RAMDiskDevice.hpp" - -#include "kapi/boot_module/boot_module.hpp" -#include "kapi/system.hpp" - -#include "devices/BlockDevice.hpp" - -#include - -#include - -namespace devices::storage::ram_disk -{ - namespace - { - constexpr size_t RAM_DISK_BLOCK_SIZE = 512uz; // TODO BA-FS26 really correct / good?? - } // namespace - - ram_disk_device::ram_disk_device() // TODO BA-FS26 remove when kstd::vector is available - : block_device(0, 0, RAM_DISK_BLOCK_SIZE) - {} - - ram_disk_device::ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor) - : block_device(major, minor, RAM_DISK_BLOCK_SIZE) - , m_boot_module(module) - {} - - auto ram_disk_device::read_block(size_t block_index, void * buffer) const -> void - { - if (buffer == nullptr) - { - kapi::system::panic("[RAM DISK DEVICE] read_block called with null buffer."); - } - - auto const info = calculate_transfer(block_index); - - if (info.to_transfer > 0) - { - auto const src = static_cast(m_boot_module.start_address) + info.offset; - kstd::libc::memcpy(buffer, src, info.to_transfer); - } - - if (info.remainder > 0) - { - kstd::libc::memset(static_cast(buffer) + info.to_transfer, 0, info.remainder); - } - } - - auto ram_disk_device::write_block(size_t block_index, void const * buffer) -> void - { - if (buffer == nullptr) - { - kapi::system::panic("[RAM DISK DEVICE] write_block called with null buffer."); - } - - auto const info = calculate_transfer(block_index); - - if (info.to_transfer > 0) - { - auto const dest = static_cast(m_boot_module.start_address) + info.offset; - kstd::libc::memcpy(dest, buffer, info.to_transfer); - } - } - - auto ram_disk_device::size() const -> size_t - { - return m_boot_module.size; - } -} // namespace devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/devices/src/storage/StorageController.cpp b/kernel/devices/src/storage/StorageController.cpp deleted file mode 100644 index d11a175..0000000 --- a/kernel/devices/src/storage/StorageController.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "devices/storage/StorageController.hpp" - -#include - -namespace devices::storage -{ - auto storage_controller::set_ids(size_t major, size_t minors_per_dev) -> void - { - m_major = major; - m_minors_per_device = minors_per_dev; - } - - auto storage_controller::major() const -> size_t - { - return m_major; - } -} // namespace devices::storage \ No newline at end of file diff --git a/kernel/devices/src/storage/StorageManagement.cpp b/kernel/devices/src/storage/StorageManagement.cpp deleted file mode 100644 index 1b0d2cc..0000000 --- a/kernel/devices/src/storage/StorageManagement.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "devices/storage/StorageManagement.hpp" - -#include "kapi/boot_modules.hpp" -#include "kapi/system.hpp" - -#include "devices/BlockDevice.hpp" -#include "devices/storage/RAMDisk/RAMDiskController.hpp" -#include "devices/storage/StorageController.hpp" - -#include -#include -#include - -namespace devices::storage -{ - namespace - { - constexpr size_t static MINORS_PER_DEVICE = 16; - constexpr size_t static START_MAJOR = 1; - constinit size_t static next_free_major = START_MAJOR; - - constinit auto static active_storage_management = std::optional{}; - constinit auto static active_ram_disk_controller = std::optional{}; - } // namespace - - auto storage_management::init() -> void - { - if (active_storage_management) - { - kapi::system::panic("[DEVICES] Storage management has already been initialized."); - } - active_storage_management.emplace(storage_management{}); - - active_ram_disk_controller.emplace(&kapi::boot_modules::get_boot_module_registry()); - active_storage_management->add_controller(&active_ram_disk_controller.value()); - - std::ranges::for_each(active_storage_management->m_controllers, [](auto controller) { controller->probe(); }); - } - - auto storage_management::get() -> storage_management & - { - if (!active_storage_management) - { - kapi::system::panic("[DEVICES] Storage management has not been initialized."); - } - - return *active_storage_management; - } - - auto storage_management::add_controller(storage_controller * controller) -> void - { - controller->set_ids(next_free_major++, MINORS_PER_DEVICE); - m_controllers.at(0) = controller; // TODO BA-FS26 use push_back from kstd:vector - } - - auto storage_management::device_by_major_minor(size_t major, size_t minor) -> block_device * - { - block_device * found = nullptr; - - std::ranges::find_if(m_controllers, [&](auto const controller) { - if (controller != nullptr && controller->major() == major) - { - found = controller->device_by_major_minor(major, minor); - return found != nullptr; - } - return false; - }); - - return found; - } - - auto storage_management::determine_boot_device() -> block_device * - { - // TODO BA-FS26 better way? - return device_by_major_minor(START_MAJOR, 0); - } - -} // namespace devices::storage \ No newline at end of file diff --git a/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp b/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp new file mode 100644 index 0000000..09f1c85 --- /dev/null +++ b/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp @@ -0,0 +1,48 @@ +#include "devices/storage/ram_disk/ram_disk_controller.hpp" + +#include "kapi/boot_module/boot_module_registry.hpp" + +#include "devices/block_device.hpp" +#include "devices/storage/ram_disk/ram_disk_device.hpp" + +#include + +#include +#include +#include + +namespace devices::storage::ram_disk +{ + ram_disk_controller::ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry) + : m_boot_module_registry(registry) + {} + + auto ram_disk_controller::probe() -> void + { + size_t current_device_index = 0; + + std::ranges::for_each(*m_boot_module_registry, [this, ¤t_device_index](auto const & module) { + auto const minor = current_device_index++ * m_minors_per_device; + + // TODO BA-FS26 use push_back from kstd::vector when available + m_devices.at(0) = ram_disk_device{module, m_major, minor}; + }); + } + + auto ram_disk_controller::device_by_major_minor(size_t major, size_t minor) -> block_device * + { + auto it = std::ranges::find_if( + m_devices, [major, minor](auto const & device) { return device.major() == major && device.minor() == minor; }); + + if (it != m_devices.end()) + { + return &(*it); + } + return nullptr; + } + + auto ram_disk_controller::devices_count() -> size_t + { + return m_devices.size(); + } +} // namespace devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp b/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp new file mode 100644 index 0000000..3bd3967 --- /dev/null +++ b/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp @@ -0,0 +1,69 @@ +#include "devices/storage/ram_disk/ram_disk_device.hpp" + +#include "kapi/boot_module/boot_module.hpp" +#include "kapi/system.hpp" + +#include "devices/block_device.hpp" + +#include + +#include + +namespace devices::storage::ram_disk +{ + namespace + { + constexpr size_t RAM_DISK_BLOCK_SIZE = 512uz; // TODO BA-FS26 really correct / good?? + } // namespace + + ram_disk_device::ram_disk_device() // TODO BA-FS26 remove when kstd::vector is available + : block_device(0, 0, RAM_DISK_BLOCK_SIZE) + {} + + ram_disk_device::ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor) + : block_device(major, minor, RAM_DISK_BLOCK_SIZE) + , m_boot_module(module) + {} + + auto ram_disk_device::read_block(size_t block_index, void * buffer) const -> void + { + if (buffer == nullptr) + { + kapi::system::panic("[RAM DISK DEVICE] read_block called with null buffer."); + } + + auto const info = calculate_transfer(block_index); + + if (info.to_transfer > 0) + { + auto const src = static_cast(m_boot_module.start_address) + info.offset; + kstd::libc::memcpy(buffer, src, info.to_transfer); + } + + if (info.remainder > 0) + { + kstd::libc::memset(static_cast(buffer) + info.to_transfer, 0, info.remainder); + } + } + + auto ram_disk_device::write_block(size_t block_index, void const * buffer) -> void + { + if (buffer == nullptr) + { + kapi::system::panic("[RAM DISK DEVICE] write_block called with null buffer."); + } + + auto const info = calculate_transfer(block_index); + + if (info.to_transfer > 0) + { + auto const dest = static_cast(m_boot_module.start_address) + info.offset; + kstd::libc::memcpy(dest, buffer, info.to_transfer); + } + } + + auto ram_disk_device::size() const -> size_t + { + return m_boot_module.size; + } +} // namespace devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/devices/src/storage/storage_controller.cpp b/kernel/devices/src/storage/storage_controller.cpp new file mode 100644 index 0000000..f95f533 --- /dev/null +++ b/kernel/devices/src/storage/storage_controller.cpp @@ -0,0 +1,17 @@ +#include "devices/storage/storage_controller.hpp" + +#include + +namespace devices::storage +{ + auto storage_controller::set_ids(size_t major, size_t minors_per_dev) -> void + { + m_major = major; + m_minors_per_device = minors_per_dev; + } + + auto storage_controller::major() const -> size_t + { + return m_major; + } +} // namespace devices::storage \ No newline at end of file diff --git a/kernel/devices/src/storage/storage_management.cpp b/kernel/devices/src/storage/storage_management.cpp new file mode 100644 index 0000000..1530d7a --- /dev/null +++ b/kernel/devices/src/storage/storage_management.cpp @@ -0,0 +1,78 @@ +#include "devices/storage/storage_management.hpp" + +#include "kapi/boot_modules.hpp" +#include "kapi/system.hpp" + +#include "devices/block_device.hpp" +#include "devices/storage/ram_disk/ram_disk_controller.hpp" +#include "devices/storage/storage_controller.hpp" + +#include +#include +#include + +namespace devices::storage +{ + namespace + { + constexpr size_t static MINORS_PER_DEVICE = 16; + constexpr size_t static START_MAJOR = 1; + constinit size_t static next_free_major = START_MAJOR; + + constinit auto static active_storage_management = std::optional{}; + constinit auto static active_ram_disk_controller = std::optional{}; + } // namespace + + auto storage_management::init() -> void + { + if (active_storage_management) + { + kapi::system::panic("[DEVICES] Storage management has already been initialized."); + } + active_storage_management.emplace(storage_management{}); + + active_ram_disk_controller.emplace(&kapi::boot_modules::get_boot_module_registry()); + active_storage_management->add_controller(&active_ram_disk_controller.value()); + + std::ranges::for_each(active_storage_management->m_controllers, [](auto controller) { controller->probe(); }); + } + + auto storage_management::get() -> storage_management & + { + if (!active_storage_management) + { + kapi::system::panic("[DEVICES] Storage management has not been initialized."); + } + + return *active_storage_management; + } + + auto storage_management::add_controller(storage_controller * controller) -> void + { + controller->set_ids(next_free_major++, MINORS_PER_DEVICE); + m_controllers.at(0) = controller; // TODO BA-FS26 use push_back from kstd:vector + } + + auto storage_management::device_by_major_minor(size_t major, size_t minor) -> block_device * + { + block_device * found = nullptr; + + std::ranges::find_if(m_controllers, [&](auto const controller) { + if (controller != nullptr && controller->major() == major) + { + found = controller->device_by_major_minor(major, minor); + return found != nullptr; + } + return false; + }); + + return found; + } + + auto storage_management::determine_boot_device() -> block_device * + { + // TODO BA-FS26 better way? + return device_by_major_minor(START_MAJOR, 0); + } + +} // namespace devices::storage \ No newline at end of file diff --git a/kernel/devices/src/temp_device.cpp b/kernel/devices/src/temp_device.cpp new file mode 100644 index 0000000..dae20b5 --- /dev/null +++ b/kernel/devices/src/temp_device.cpp @@ -0,0 +1,21 @@ +#include "devices/temp_device.hpp" + +#include + +namespace devices +{ + device::device(size_t major, size_t minor) + : m_major(major) + , m_minor(minor) + {} + + auto device::major() const -> size_t + { + return m_major; + } + + auto device::minor() const -> size_t + { + return m_minor; + } +} // namespace devices \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 0336fef..07aa8ae 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -3,7 +3,7 @@ #include "kapi/memory.hpp" #include "kapi/system.hpp" -#include "devices/storage/StorageManagement.hpp" +#include "devices/storage/storage_management.hpp" #include "kernel/memory.hpp" -- cgit v1.2.3 From 332903fcfc2d1b79b9b201cc98dd65125d2018e6 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 3 Mar 2026 17:01:46 +0100 Subject: rename temp_device back to device --- kernel/devices/CMakeLists.txt | 4 ++-- kernel/devices/include/devices/block_device.hpp | 2 +- kernel/devices/include/devices/device.hpp | 22 ++++++++++++++++++++++ kernel/devices/include/devices/temp_device.hpp | 22 ---------------------- kernel/devices/src/block_device.cpp | 2 +- kernel/devices/src/device.cpp | 21 +++++++++++++++++++++ kernel/devices/src/temp_device.cpp | 21 --------------------- 7 files changed, 47 insertions(+), 47 deletions(-) create mode 100644 kernel/devices/include/devices/device.hpp delete mode 100644 kernel/devices/include/devices/temp_device.hpp create mode 100644 kernel/devices/src/device.cpp delete mode 100644 kernel/devices/src/temp_device.cpp diff --git a/kernel/devices/CMakeLists.txt b/kernel/devices/CMakeLists.txt index 691bb11..15d03fa 100644 --- a/kernel/devices/CMakeLists.txt +++ b/kernel/devices/CMakeLists.txt @@ -2,7 +2,7 @@ add_library("kernel_devices" STATIC) add_library("kernel::devices" ALIAS "kernel_devices") target_sources("kernel_devices" PRIVATE - "src/temp_device.cpp" + "src/device.cpp" "src/block_device.cpp" "src/storage/storage_controller.cpp" "src/storage/storage_management.cpp" @@ -14,7 +14,7 @@ target_sources("kernel_devices" PUBLIC FILE_SET HEADERS BASE_DIRS "include" FILES - "include/devices/temp_device.hpp" + "include/devices/device.hpp" "include/devices/block_device.hpp" "include/devices/storage/storage_controller.hpp" "include/devices/storage/storage_management.hpp" diff --git a/kernel/devices/include/devices/block_device.hpp b/kernel/devices/include/devices/block_device.hpp index d1e4249..240cb3b 100644 --- a/kernel/devices/include/devices/block_device.hpp +++ b/kernel/devices/include/devices/block_device.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP #define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP -#include "devices/temp_device.hpp" +#include "devices/device.hpp" #include diff --git a/kernel/devices/include/devices/device.hpp b/kernel/devices/include/devices/device.hpp new file mode 100644 index 0000000..0f93bff --- /dev/null +++ b/kernel/devices/include/devices/device.hpp @@ -0,0 +1,22 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_DEVICE_HPP +#define TEACH_OS_KERNEL_DEVICES_DEVICE_HPP + +#include + +namespace devices +{ + struct device + { + device(size_t major, size_t minor); + virtual ~device() = default; + + auto major() const -> size_t; + auto minor() const -> size_t; + + private: + size_t m_major; + size_t m_minor; + }; +} // namespace devices + +#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/temp_device.hpp b/kernel/devices/include/devices/temp_device.hpp deleted file mode 100644 index 0f93bff..0000000 --- a/kernel/devices/include/devices/temp_device.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_DEVICE_HPP -#define TEACH_OS_KERNEL_DEVICES_DEVICE_HPP - -#include - -namespace devices -{ - struct device - { - device(size_t major, size_t minor); - virtual ~device() = default; - - auto major() const -> size_t; - auto minor() const -> size_t; - - private: - size_t m_major; - size_t m_minor; - }; -} // namespace devices - -#endif \ No newline at end of file diff --git a/kernel/devices/src/block_device.cpp b/kernel/devices/src/block_device.cpp index dd0fd44..dd63638 100644 --- a/kernel/devices/src/block_device.cpp +++ b/kernel/devices/src/block_device.cpp @@ -1,6 +1,6 @@ #include "devices/block_device.hpp" -#include "devices/temp_device.hpp" +#include "devices/device.hpp" #include diff --git a/kernel/devices/src/device.cpp b/kernel/devices/src/device.cpp new file mode 100644 index 0000000..aa5c1bb --- /dev/null +++ b/kernel/devices/src/device.cpp @@ -0,0 +1,21 @@ +#include "devices/device.hpp" + +#include + +namespace devices +{ + device::device(size_t major, size_t minor) + : m_major(major) + , m_minor(minor) + {} + + auto device::major() const -> size_t + { + return m_major; + } + + auto device::minor() const -> size_t + { + return m_minor; + } +} // namespace devices \ No newline at end of file diff --git a/kernel/devices/src/temp_device.cpp b/kernel/devices/src/temp_device.cpp deleted file mode 100644 index dae20b5..0000000 --- a/kernel/devices/src/temp_device.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "devices/temp_device.hpp" - -#include - -namespace devices -{ - device::device(size_t major, size_t minor) - : m_major(major) - , m_minor(minor) - {} - - auto device::major() const -> size_t - { - return m_major; - } - - auto device::minor() const -> size_t - { - return m_minor; - } -} // namespace devices \ No newline at end of file -- cgit v1.2.3 From 6afdc8ecb3d0d533597621d640910f5faf50331d Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 4 Mar 2026 10:16:23 +0100 Subject: add comments --- kernel/devices/include/devices/block_device.hpp | 44 ++++++++++++++++++++++ kernel/devices/include/devices/device.hpp | 22 +++++++++++ .../storage/ram_disk/ram_disk_controller.hpp | 22 +++++++++++ .../devices/storage/ram_disk/ram_disk_device.hpp | 27 +++++++++++++ .../include/devices/storage/storage_controller.hpp | 33 ++++++++++++++++ .../include/devices/storage/storage_management.hpp | 40 +++++++++++++++++++- 6 files changed, 187 insertions(+), 1 deletion(-) diff --git a/kernel/devices/include/devices/block_device.hpp b/kernel/devices/include/devices/block_device.hpp index 240cb3b..78a2606 100644 --- a/kernel/devices/include/devices/block_device.hpp +++ b/kernel/devices/include/devices/block_device.hpp @@ -7,15 +7,49 @@ namespace devices { + /** + * @brief Base interface for block-addressable devices. + */ struct block_device : device { + /** + * @brief Create a block device descriptor. + * @param major Device major number. + * @param minor Device minor number. + * @param block_size Size of one logical block in bytes. + */ block_device(size_t major, size_t minor, size_t block_size); + + /** + * @brief Virtual destructor for block device. + */ virtual ~block_device() = default; + /** + * @brief Read data from the block at @p block_index into @p buffer. + * @param block_index Zero-based block index. + * @param buffer Destination buffer. + * @warning Panics if @p buffer is null. + * @note Reads up to one logical block (see constructor @p block_size). Implementations may perform a partial + * transfer for the final block when fewer than @p block_size bytes remain. + */ virtual auto read_block(size_t block_index, void * buffer) const -> void = 0; + + /** + * @brief Write data to the block at @p block_index. + * @param block_index Zero-based block index. + * @param buffer Source buffer, must not be null. + * @warning Panics if @p buffer is null. + * @note Writes up to one logical block (see constructor @p block_size). + * Implementations may perform a partial transfer for the final block when + * fewer than @p block_size bytes remain. + */ virtual auto write_block(size_t block_index, void const * buffer) -> void = 0; protected: + /** + * @brief Information describing the transfer window for one block index. + */ struct transfer_info { size_t offset; @@ -23,7 +57,17 @@ namespace devices size_t remainder; }; + /** + * @brief Return total device size in bytes. + * @return Total number of addressable bytes. + */ virtual auto size() const -> size_t = 0; + + /** + * @brief Compute transfer information for @p block_index. + * @param block_index Zero-based block index. + * @return Computed transfer information for one logical block access. + */ auto calculate_transfer(size_t block_index) const -> transfer_info; size_t m_block_size; diff --git a/kernel/devices/include/devices/device.hpp b/kernel/devices/include/devices/device.hpp index 0f93bff..cbf77c2 100644 --- a/kernel/devices/include/devices/device.hpp +++ b/kernel/devices/include/devices/device.hpp @@ -5,17 +5,39 @@ namespace devices { + /** + * @brief Base device identified by a major and minor number. + */ struct device { + /** + * @brief Create a device identifier from @p major and @p minor. + * @param major Device major number. + * @param minor Device minor number. + */ device(size_t major, size_t minor); + + /** + * @brief Virtual destructor for device. + */ virtual ~device() = default; + /** + * @brief Returns the major number of the device. + * @return Device major number. + */ auto major() const -> size_t; + + /** + * @brief Returns the minor number of the device. + * @return Device minor number. + */ auto minor() const -> size_t; private: size_t m_major; size_t m_minor; + // TODO BA-FS26 add name }; } // namespace devices diff --git a/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp b/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp index f050ba0..05cac88 100644 --- a/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp +++ b/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp @@ -12,17 +12,39 @@ namespace devices::storage::ram_disk { + /** + * @brief Storage controller that exposes boot modules as RAM-disk devices. + */ struct ram_disk_controller : storage_controller { + /** + * @brief Create a RAM-disk controller. + * @param registry Boot module registry as device source. + */ explicit ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry); + /** + * @brief Probe boot modules and create RAM-disk devices. + */ auto probe() -> void override; + /** + * @brief Return the number of managed RAM-disk devices. + * @return Number of managed devices. + */ auto devices_count() -> size_t override; + + /** + * @brief Look up a RAM-disk device by major/minor numbers. + * @param major Device major number. + * @param minor Device minor number. + * @return Matching block device, or nullptr if no device matches. + */ auto device_by_major_minor(size_t major, size_t minor) -> block_device * override; private: kapi::boot_modules::boot_module_registry const * m_boot_module_registry; + std::array m_devices{}; // TODO BA-FS26 use kstd::vector when available }; } // namespace devices::storage::ram_disk diff --git a/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp b/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp index 1f0292f..eebbcd7 100644 --- a/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp +++ b/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp @@ -9,15 +9,42 @@ namespace devices::storage::ram_disk { + /** + * @brief Block device for a boot module. + */ struct ram_disk_device : block_device { ram_disk_device(); // TODO BA-FS26 remove when kstd::vector is available + + /** + * @brief Create a RAM disk for the @p module. + * @param module Boot module providing the memory region. + * @param major Device major number. + * @param minor Device minor number. + */ ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor); + /** + * @brief Read one logical block into @p buffer. + * @param block_index Zero-based block index. + * @param buffer Destination buffer, must not be null. + * @note If the request reaches the module end, only available bytes are copied and the rest of the + * logical block is filled with zeros. + */ auto read_block(size_t block_index, void * buffer) const -> void override; + + /** + * @brief Write one logical block from @p buffer. + * @param block_index Zero-based block index. + * @param buffer Source buffer, must not be null. + * @note If the request reaches the module end, only the bytes in the module range are written. + */ auto write_block(size_t block_index, void const * buffer) -> void override; private: + /** + * @brief Return module size in bytes. + */ auto size() const -> size_t override; kapi::boot_modules::boot_module m_boot_module{}; diff --git a/kernel/devices/include/devices/storage/storage_controller.hpp b/kernel/devices/include/devices/storage/storage_controller.hpp index 0c09815..18c7232 100644 --- a/kernel/devices/include/devices/storage/storage_controller.hpp +++ b/kernel/devices/include/devices/storage/storage_controller.hpp @@ -7,16 +7,49 @@ namespace devices::storage { + /** + * @brief Base interface for storage controllers. + * + * A storage controller probes for devices and resolves devices by major/minor + * numbers. + */ struct storage_controller { + /** + * @brief Virtual destructor. + */ virtual ~storage_controller() = default; + /** + * @brief Probe the controller and register discovered devices. + */ virtual auto probe() -> void = 0; + /** + * @brief Assign the major number and minor stride for this controller. + * @param major Major number assigned to this controller. + * @param minors_per_dev Minor number stride between devices. + */ auto set_ids(size_t major, size_t minors_per_dev) -> void; + + /** + * @brief Return the assigned major number. + * @return Assigned major number. + */ auto major() const -> size_t; + /** + * @brief Return the number of devices managed by this controller. + * @return Number of managed devices. + */ virtual auto devices_count() -> size_t = 0; + + /** + * @brief Find a managed device by major/minor numbers. + * @param major Device major number. + * @param minor Device minor number. + * @return Matching block device, or nullptr if no device matches. + */ virtual auto device_by_major_minor(size_t major, size_t minor) -> block_device * = 0; protected: diff --git a/kernel/devices/include/devices/storage/storage_management.hpp b/kernel/devices/include/devices/storage/storage_management.hpp index c4f5679..2bed459 100644 --- a/kernel/devices/include/devices/storage/storage_management.hpp +++ b/kernel/devices/include/devices/storage/storage_management.hpp @@ -9,19 +9,57 @@ namespace devices::storage { - // TODO BA-FS26 add documentation + /** + * @brief Global storage subsystem manager. + * + * Owns registered storage controllers and provides device lookup by + * major/minor numbers. + */ struct storage_management { + /** + * @brief Initialize global storage management. + * + * Creates the singleton instance, registers controllers and probes + * them for devices. + * + * @warning Panics if called more than once. + */ auto static init() -> void; + /** + * @brief Return the active storage manager singleton. + * @return Reference to the active storage manager. + * @warning Panics if storage management has not been initialized. + */ auto static get() -> storage_management &; + /** + * @brief Register a storage controller. + * @param controller Controller to register. + * + * Assigns controller IDs (major number range and minors per device). + */ auto add_controller(storage_controller * controller) -> void; + /** + * @brief Find a block device by major/minor numbers. + * @param major Device major number. + * @param minor Device minor number. + * @return Matching block device, or nullptr if no device matches. + */ auto device_by_major_minor(size_t major, size_t minor) -> block_device *; + + /** + * @brief Determine the boot device. + * @return Boot device, or nullptr if it cannot be determined. + */ auto determine_boot_device() -> block_device *; private: + /** + * @brief Private default constructor for storage management singleton. + */ storage_management() = default; std::array m_controllers{}; // TODO BA-FS26 use kstd::vector when available -- cgit v1.2.3 From 52be8fd3b73b23a6be3662f072c36bcb8df1b87f Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Thu, 5 Mar 2026 14:23:21 +0100 Subject: Ignore windows style file --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index d575651..21c6b0a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ /docs/_build qemu-*-*.log + +/desktop.ini -- cgit v1.2.3 From 79aaf6113a9eca042ccc6f656fac6c7c243c608b Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sat, 7 Mar 2026 13:46:24 +0100 Subject: Simplify device lookup in storage controllers by removing redundant major number --- .../devices/include/devices/storage/ram_disk/ram_disk_controller.hpp | 2 +- kernel/devices/include/devices/storage/storage_controller.hpp | 2 +- kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp | 5 ++--- kernel/devices/src/storage/storage_management.cpp | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp b/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp index 05cac88..424082d 100644 --- a/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp +++ b/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp @@ -40,7 +40,7 @@ namespace devices::storage::ram_disk * @param minor Device minor number. * @return Matching block device, or nullptr if no device matches. */ - auto device_by_major_minor(size_t major, size_t minor) -> block_device * override; + auto device_by_minor(size_t minor) -> block_device * override; private: kapi::boot_modules::boot_module_registry const * m_boot_module_registry; diff --git a/kernel/devices/include/devices/storage/storage_controller.hpp b/kernel/devices/include/devices/storage/storage_controller.hpp index 18c7232..0c7cf83 100644 --- a/kernel/devices/include/devices/storage/storage_controller.hpp +++ b/kernel/devices/include/devices/storage/storage_controller.hpp @@ -50,7 +50,7 @@ namespace devices::storage * @param minor Device minor number. * @return Matching block device, or nullptr if no device matches. */ - virtual auto device_by_major_minor(size_t major, size_t minor) -> block_device * = 0; + virtual auto device_by_minor(size_t minor) -> block_device * = 0; protected: size_t m_major{}; diff --git a/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp b/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp index 09f1c85..9f9537f 100644 --- a/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp +++ b/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp @@ -29,10 +29,9 @@ namespace devices::storage::ram_disk }); } - auto ram_disk_controller::device_by_major_minor(size_t major, size_t minor) -> block_device * + auto ram_disk_controller::device_by_minor(size_t minor) -> block_device * { - auto it = std::ranges::find_if( - m_devices, [major, minor](auto const & device) { return device.major() == major && device.minor() == minor; }); + auto it = std::ranges::find_if(m_devices, [minor](auto const & device) { return device.minor() == minor; }); if (it != m_devices.end()) { diff --git a/kernel/devices/src/storage/storage_management.cpp b/kernel/devices/src/storage/storage_management.cpp index 1530d7a..a5b6503 100644 --- a/kernel/devices/src/storage/storage_management.cpp +++ b/kernel/devices/src/storage/storage_management.cpp @@ -60,7 +60,7 @@ namespace devices::storage std::ranges::find_if(m_controllers, [&](auto const controller) { if (controller != nullptr && controller->major() == major) { - found = controller->device_by_major_minor(major, minor); + found = controller->device_by_minor(minor); return found != nullptr; } return false; -- cgit v1.2.3 From 2f4001cdb1f528d8a0d255d81ac3a8b9aa522fac Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 7 Mar 2026 13:55:40 +0100 Subject: implement first draft of a file_descriptor_table and open_file_description --- kernel/CMakeLists.txt | 2 + kernel/filesystem/CMakeLists.txt | 25 +++++++ kernel/filesystem/include/filesystem/file.hpp | 19 +++++ .../include/filesystem/file_descriptor_table.hpp | 28 ++++++++ .../include/filesystem/open_file_description.hpp | 24 +++++++ kernel/filesystem/src/file.cpp | 6 ++ kernel/filesystem/src/file_descriptor_table.cpp | 81 ++++++++++++++++++++++ kernel/filesystem/src/open_file_description.cpp | 24 +++++++ kernel/src/main.cpp | 4 ++ 9 files changed, 213 insertions(+) create mode 100644 kernel/filesystem/CMakeLists.txt create mode 100644 kernel/filesystem/include/filesystem/file.hpp create mode 100644 kernel/filesystem/include/filesystem/file_descriptor_table.hpp create mode 100644 kernel/filesystem/include/filesystem/open_file_description.hpp create mode 100644 kernel/filesystem/src/file.cpp create mode 100644 kernel/filesystem/src/file_descriptor_table.cpp create mode 100644 kernel/filesystem/src/open_file_description.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index c3c9698..830c527 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory("devices") +add_subdirectory("filesystem") add_executable("kernel" # Platform-independent KAPI implementation @@ -27,6 +28,7 @@ target_link_libraries("kernel" PRIVATE "os::arch" "os::kapi" "kernel::devices" + "kernel::filesystem" ) target_link_options("kernel" PRIVATE diff --git a/kernel/filesystem/CMakeLists.txt b/kernel/filesystem/CMakeLists.txt new file mode 100644 index 0000000..2a93c62 --- /dev/null +++ b/kernel/filesystem/CMakeLists.txt @@ -0,0 +1,25 @@ +add_library("kernel_filesystem" STATIC) +add_library("kernel::filesystem" ALIAS "kernel_filesystem") + +target_sources("kernel_filesystem" PRIVATE + "src/file.cpp" + "src/open_file_description.cpp" + "src/file_descriptor_table.cpp" +) + +target_sources("kernel_filesystem" PUBLIC + FILE_SET HEADERS + BASE_DIRS "include" + FILES + "include/filesystem/file.hpp" + "include/filesystem/open_file_description.hpp" + "include/filesystem/file_descriptor_table.hpp" +) + +target_include_directories("kernel_filesystem" PUBLIC + "include" +) + +target_link_libraries("kernel_filesystem" PRIVATE + "os::kapi" +) diff --git a/kernel/filesystem/include/filesystem/file.hpp b/kernel/filesystem/include/filesystem/file.hpp new file mode 100644 index 0000000..e7e1b12 --- /dev/null +++ b/kernel/filesystem/include/filesystem/file.hpp @@ -0,0 +1,19 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_FILE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_FILE_HPP + +#include + +namespace filesystem +{ + struct file + { + virtual ~file() = default; + + virtual auto open() -> void = 0; + + virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0; + virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0; + }; +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/filesystem/include/filesystem/file_descriptor_table.hpp b/kernel/filesystem/include/filesystem/file_descriptor_table.hpp new file mode 100644 index 0000000..a865d32 --- /dev/null +++ b/kernel/filesystem/include/filesystem/file_descriptor_table.hpp @@ -0,0 +1,28 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP + +#include "open_file_description.hpp" + +#include + +namespace filesystem +{ + struct file_descriptor_table + { + auto static init() -> void; + auto static get() -> file_descriptor_table &; + + ~file_descriptor_table() = default; + + auto add_file(open_file_description & f) -> int; + auto get_file(int fd) -> open_file_description &; + auto remove_file(int fd) -> void; + + private: + file_descriptor_table() = default; + + std::array m_open_files{}; // TODO BA-FS26 use kstd::vector when available + }; +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/filesystem/include/filesystem/open_file_description.hpp b/kernel/filesystem/include/filesystem/open_file_description.hpp new file mode 100644 index 0000000..e4febbd --- /dev/null +++ b/kernel/filesystem/include/filesystem/open_file_description.hpp @@ -0,0 +1,24 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP + +#include "file.hpp" + +#include + +namespace filesystem +{ + struct open_file_description + { + ~open_file_description() = default; + + auto read(void * buffer, size_t size) -> size_t; + auto write(void const * buffer, size_t size) -> size_t; + + private: + file * m_file{}; + size_t m_offset{0}; + }; + +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/filesystem/src/file.cpp b/kernel/filesystem/src/file.cpp new file mode 100644 index 0000000..c67ad1a --- /dev/null +++ b/kernel/filesystem/src/file.cpp @@ -0,0 +1,6 @@ +#include "filesystem/file.hpp" + +namespace filesystem +{ + // TODO BA-FS26 +} \ No newline at end of file diff --git a/kernel/filesystem/src/file_descriptor_table.cpp b/kernel/filesystem/src/file_descriptor_table.cpp new file mode 100644 index 0000000..0c79431 --- /dev/null +++ b/kernel/filesystem/src/file_descriptor_table.cpp @@ -0,0 +1,81 @@ +#include "filesystem/file_descriptor_table.hpp" + +#include "kapi/system.hpp" + +#include "filesystem/open_file_description.hpp" + +#include +#include +#include + +namespace filesystem +{ + namespace + { + constinit auto static global_file_descriptor_table = std::optional{}; + } // namespace + + auto file_descriptor_table::init() -> void + { + if (global_file_descriptor_table) + { + kapi::system::panic("[FILESYSTEM] File descriptor table has already been initialized."); + } + + global_file_descriptor_table.emplace(file_descriptor_table{}); + } + + auto file_descriptor_table::get() -> file_descriptor_table & + { + if (!global_file_descriptor_table) + { + kapi::system::panic("[FILESYSTEM] File descriptor table has not been initialized."); + } + + return *global_file_descriptor_table; + } + + auto file_descriptor_table::add_file(open_file_description & f) -> int + { + auto it = std::ranges::find(m_open_files, nullptr); + if (it != m_open_files.end()) + { + *it = &f; + return static_cast(it - m_open_files.begin()); + } + + return -1; + } + + auto file_descriptor_table::get_file(int fd) -> open_file_description & + { + if (fd < 0) + { + kapi::system::panic("[FILESYSTEM] get_file called with negative descriptor."); + } + + auto const index = static_cast(fd); + if (index >= m_open_files.size() || m_open_files[index] == nullptr) + { + kapi::system::panic("[FILESYSTEM] get_file called with invalid descriptor."); + } + + return *m_open_files[index]; + } + + auto file_descriptor_table::remove_file(int fd) -> void + { + if (fd < 0) + { + kapi::system::panic("[FILESYSTEM] remove_file called with negative descriptor."); + } + + auto const index = static_cast(fd); + if (index >= m_open_files.size()) + { + kapi::system::panic("[FILESYSTEM] remove_file called with out-of-range descriptor."); + } + + m_open_files[index] = nullptr; + } +} // namespace filesystem \ No newline at end of file diff --git a/kernel/filesystem/src/open_file_description.cpp b/kernel/filesystem/src/open_file_description.cpp new file mode 100644 index 0000000..8c20397 --- /dev/null +++ b/kernel/filesystem/src/open_file_description.cpp @@ -0,0 +1,24 @@ +#include "filesystem/open_file_description.hpp" + +#include "filesystem/file.hpp" + +#include + +namespace filesystem +{ + auto open_file_description::read(void * buffer, size_t size) -> size_t + { + // TODO BA-FS26 nullptr check + auto read_bytes = m_file->read(buffer, m_offset, size); + m_offset += read_bytes; + return read_bytes; + } + + auto open_file_description::write(void const * buffer, size_t size) -> size_t + { + // TODO BA-FS26 nullptr check + auto written_bytes = m_file->write(buffer, m_offset, size); + m_offset += written_bytes; + return written_bytes; + } +} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 07aa8ae..4b74bd4 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -4,6 +4,7 @@ #include "kapi/system.hpp" #include "devices/storage/storage_management.hpp" +#include "filesystem/file_descriptor_table.hpp" #include "kernel/memory.hpp" @@ -25,5 +26,8 @@ auto main() -> int devices::storage::storage_management::init(); kstd::println("[OS] Storage management initialized."); + filesystem::file_descriptor_table::init(); + kstd::println("[OS] Global file descriptor table initialized."); + kapi::system::panic("Returning from kernel main!"); } -- cgit v1.2.3 From 9ae5a230b1cd1f842c1a7c64392a0bccdba01a2d Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 7 Mar 2026 17:04:24 +0100 Subject: implement read and write bytes from device --- kernel/devices/include/devices/block_device.hpp | 22 ++++ kernel/devices/include/devices/device.hpp | 9 ++ kernel/devices/src/block_device.cpp | 19 +++- kernel/filesystem/CMakeLists.txt | 6 +- .../filesystem/include/filesystem/device_file.hpp | 30 ++++++ .../include/filesystem/open_file_description.hpp | 2 + kernel/filesystem/src/device_file.cpp | 119 +++++++++++++++++++++ kernel/filesystem/src/open_file_description.cpp | 4 + 8 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 kernel/filesystem/include/filesystem/device_file.hpp create mode 100644 kernel/filesystem/src/device_file.cpp diff --git a/kernel/devices/include/devices/block_device.hpp b/kernel/devices/include/devices/block_device.hpp index 78a2606..9bca53f 100644 --- a/kernel/devices/include/devices/block_device.hpp +++ b/kernel/devices/include/devices/block_device.hpp @@ -46,6 +46,28 @@ namespace devices */ virtual auto write_block(size_t block_index, void const * buffer) -> void = 0; + /** + * @brief Return logical block size in bytes. + * @return One logical block size in bytes. + */ + auto block_size() const -> size_t; + + /** + * @brief Return device capacity in bytes. + * @return Total number of addressable bytes. + */ + auto capacity() const -> size_t; + + /** + * @brief Override to identify block devices. + * @return true if this device is a block device, false otherwise. + */ + + auto is_block_device() const -> bool override + { + return true; + } + protected: /** * @brief Information describing the transfer window for one block index. diff --git a/kernel/devices/include/devices/device.hpp b/kernel/devices/include/devices/device.hpp index cbf77c2..7f9f9e1 100644 --- a/kernel/devices/include/devices/device.hpp +++ b/kernel/devices/include/devices/device.hpp @@ -34,6 +34,15 @@ namespace devices */ auto minor() const -> size_t; + /** + * @brief Check if the device is a block device. + * @return true if this device is a block device, false otherwise. + */ + virtual auto is_block_device() const -> bool + { + return false; + } + private: size_t m_major; size_t m_minor; diff --git a/kernel/devices/src/block_device.cpp b/kernel/devices/src/block_device.cpp index dd63638..2f0c2ca 100644 --- a/kernel/devices/src/block_device.cpp +++ b/kernel/devices/src/block_device.cpp @@ -1,5 +1,7 @@ #include "devices/block_device.hpp" +#include "kapi/system.hpp" + #include "devices/device.hpp" #include @@ -9,7 +11,12 @@ namespace devices block_device::block_device(size_t major, size_t minor, size_t block_size) : device(major, minor) , m_block_size(block_size) - {} + { + if (m_block_size == 0) + { + kapi::system::panic("[DEVICES] block_device constructed with zero block size."); + } + } auto block_device::calculate_transfer(size_t block_index) const -> transfer_info { @@ -21,4 +28,14 @@ namespace devices return {offset, to_transfer, m_block_size - to_transfer}; } + + auto block_device::block_size() const -> size_t + { + return m_block_size; + } + + auto block_device::capacity() const -> size_t + { + return size(); + } } // namespace devices \ No newline at end of file diff --git a/kernel/filesystem/CMakeLists.txt b/kernel/filesystem/CMakeLists.txt index 2a93c62..1d74585 100644 --- a/kernel/filesystem/CMakeLists.txt +++ b/kernel/filesystem/CMakeLists.txt @@ -3,6 +3,7 @@ add_library("kernel::filesystem" ALIAS "kernel_filesystem") target_sources("kernel_filesystem" PRIVATE "src/file.cpp" + "src/device_file.cpp" "src/open_file_description.cpp" "src/file_descriptor_table.cpp" ) @@ -12,6 +13,7 @@ target_sources("kernel_filesystem" PUBLIC BASE_DIRS "include" FILES "include/filesystem/file.hpp" + "include/filesystem/device_file.hpp" "include/filesystem/open_file_description.hpp" "include/filesystem/file_descriptor_table.hpp" ) @@ -20,6 +22,8 @@ target_include_directories("kernel_filesystem" PUBLIC "include" ) -target_link_libraries("kernel_filesystem" PRIVATE +target_link_libraries("kernel_filesystem" PUBLIC + "kernel::devices" +PRIVATE "os::kapi" ) diff --git a/kernel/filesystem/include/filesystem/device_file.hpp b/kernel/filesystem/include/filesystem/device_file.hpp new file mode 100644 index 0000000..08d81f6 --- /dev/null +++ b/kernel/filesystem/include/filesystem/device_file.hpp @@ -0,0 +1,30 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVICE_FILE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_FILE_HPP + +#include "devices/block_device.hpp" +#include "devices/device.hpp" +#include "filesystem/file.hpp" + +#include + +namespace filesystem +{ + struct device_file : file + { + device_file(devices::device * device); + + auto open() -> void override; + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + private: + using block_op = void (*)(size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, + std::byte * scratch, void * buffer); + auto process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t; + + devices::device * m_device; + }; +} // namespace filesystem + +#endif diff --git a/kernel/filesystem/include/filesystem/open_file_description.hpp b/kernel/filesystem/include/filesystem/open_file_description.hpp index e4febbd..3241ea4 100644 --- a/kernel/filesystem/include/filesystem/open_file_description.hpp +++ b/kernel/filesystem/include/filesystem/open_file_description.hpp @@ -9,6 +9,8 @@ namespace filesystem { struct open_file_description { + open_file_description(file * file); + ~open_file_description() = default; auto read(void * buffer, size_t size) -> size_t; diff --git a/kernel/filesystem/src/device_file.cpp b/kernel/filesystem/src/device_file.cpp new file mode 100644 index 0000000..a6c234c --- /dev/null +++ b/kernel/filesystem/src/device_file.cpp @@ -0,0 +1,119 @@ +#include "filesystem/device_file.hpp" + +#include "kapi/system.hpp" + +#include "devices/block_device.hpp" +#include "devices/device.hpp" + +#include + +#include +#include +#include + +namespace filesystem +{ + device_file::device_file(devices::device * device) + : m_device(device) + { + if (m_device == nullptr) + { + kapi::system::panic("[FILESYSTEM] device_file constructed with null device."); + } + } + + auto device_file::open() -> void + { + // Hook point for permission checks or lazy metadata loading. + } + + auto device_file::read(void * buffer, size_t offset, size_t size) const -> size_t + { + if (m_device->is_block_device()) + { + return process_blocks(offset, size, buffer, + [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, + std::byte * scratch, void * buffer) { + auto * out = static_cast(buffer); + if (off == 0 && len == device->block_size()) + { + device->read_block(idx, out + done); + } + else + { + device->read_block(idx, scratch); + kstd::libc::memcpy(out + done, scratch + off, len); + } + }); + } + else + { + kapi::system::panic("[FILESYSTEM] device_file::read called on non-block device."); + } + } + + auto device_file::write(void const * buffer, size_t offset, size_t size) -> size_t + { + if (m_device->is_block_device()) + { + return process_blocks(offset, size, const_cast(buffer), + [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, + std::byte * scratch, void * buffer) { + auto const * in = static_cast(buffer); + if (off == 0 && len == device->block_size()) + { + device->write_block(idx, in + done); + } + else + { + device->read_block(idx, scratch); + kstd::libc::memcpy(scratch + off, in + done, len); + device->write_block(idx, scratch); + } + }); + } + else + { + kapi::system::panic("[FILESYSTEM] device_file::write called on non-block device."); + } + } + + auto device_file::process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t + { + if (buffer == nullptr) + { + kapi::system::panic("[FILESYSTEM] device_file::write called with null buffer."); + } + + if (size == 0) + { + return 0; + } + + auto * block_dev = static_cast(m_device); + + size_t const block_size = block_dev->block_size(); + size_t const capacity = block_dev->capacity(); + + if (offset >= capacity) + return 0; + size_t const total_to_process = std::min(size, capacity - offset); + + std::array scratch_buffer{}; // TODO BA-FS26 better solution than fixed scratch_buffer ?? + auto processed = 0uz; + + while (processed < total_to_process) + { + size_t const absolute_offset = offset + processed; + size_t const block_index = absolute_offset / block_size; + size_t const in_block_offset = absolute_offset % block_size; + size_t const chunk_size = std::min(total_to_process - processed, block_size - in_block_offset); + + op(block_index, in_block_offset, chunk_size, processed, block_dev, scratch_buffer.data(), buffer); + + processed += chunk_size; + } + + return processed; + } +} // namespace filesystem diff --git a/kernel/filesystem/src/open_file_description.cpp b/kernel/filesystem/src/open_file_description.cpp index 8c20397..1f0410c 100644 --- a/kernel/filesystem/src/open_file_description.cpp +++ b/kernel/filesystem/src/open_file_description.cpp @@ -6,6 +6,10 @@ namespace filesystem { + open_file_description::open_file_description(file * file) + : m_file(file) + {} + auto open_file_description::read(void * buffer, size_t size) -> size_t { // TODO BA-FS26 nullptr check -- cgit v1.2.3 From 58aea4f1bbb4f24d4fc4cda5501d753886968234 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 9 Mar 2026 23:54:28 +0100 Subject: small refactoring --- kernel/filesystem/include/filesystem/open_file_description.hpp | 4 ++-- kernel/filesystem/src/open_file_description.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/kernel/filesystem/include/filesystem/open_file_description.hpp b/kernel/filesystem/include/filesystem/open_file_description.hpp index 3241ea4..1589196 100644 --- a/kernel/filesystem/include/filesystem/open_file_description.hpp +++ b/kernel/filesystem/include/filesystem/open_file_description.hpp @@ -17,8 +17,8 @@ namespace filesystem auto write(void const * buffer, size_t size) -> size_t; private: - file * m_file{}; - size_t m_offset{0}; + file * m_file; + size_t m_offset; }; } // namespace filesystem diff --git a/kernel/filesystem/src/open_file_description.cpp b/kernel/filesystem/src/open_file_description.cpp index 1f0410c..d483746 100644 --- a/kernel/filesystem/src/open_file_description.cpp +++ b/kernel/filesystem/src/open_file_description.cpp @@ -8,6 +8,7 @@ namespace filesystem { open_file_description::open_file_description(file * file) : m_file(file) + , m_offset(0) {} auto open_file_description::read(void * buffer, size_t size) -> size_t -- cgit v1.2.3 From 7430778a773cfdb97bed3c77c17f7c737706ba6a Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 9 Mar 2026 23:54:54 +0100 Subject: add todos --- kernel/filesystem/src/device_file.cpp | 1 + kernel/filesystem/src/file_descriptor_table.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/kernel/filesystem/src/device_file.cpp b/kernel/filesystem/src/device_file.cpp index a6c234c..34195db 100644 --- a/kernel/filesystem/src/device_file.cpp +++ b/kernel/filesystem/src/device_file.cpp @@ -99,6 +99,7 @@ namespace filesystem return 0; size_t const total_to_process = std::min(size, capacity - offset); + // TODO BA-FS26 use kstd::vector when available std::array scratch_buffer{}; // TODO BA-FS26 better solution than fixed scratch_buffer ?? auto processed = 0uz; diff --git a/kernel/filesystem/src/file_descriptor_table.cpp b/kernel/filesystem/src/file_descriptor_table.cpp index 0c79431..645da9f 100644 --- a/kernel/filesystem/src/file_descriptor_table.cpp +++ b/kernel/filesystem/src/file_descriptor_table.cpp @@ -49,11 +49,13 @@ namespace filesystem auto file_descriptor_table::get_file(int fd) -> open_file_description & { + // TODO BA-FS26 do not panic, its an user error if (fd < 0) { kapi::system::panic("[FILESYSTEM] get_file called with negative descriptor."); } + // TODO BA-FS26 do not panic, its an user error auto const index = static_cast(fd); if (index >= m_open_files.size() || m_open_files[index] == nullptr) { @@ -65,17 +67,21 @@ namespace filesystem auto file_descriptor_table::remove_file(int fd) -> void { + // TODO BA-FS26 do not panic, its an user error if (fd < 0) { kapi::system::panic("[FILESYSTEM] remove_file called with negative descriptor."); } + // TODO BA-FS26 do not panic, its an user error auto const index = static_cast(fd); if (index >= m_open_files.size()) { kapi::system::panic("[FILESYSTEM] remove_file called with out-of-range descriptor."); } + // TODO BA-FS26 + // delete m_open_files[index]; m_open_files[index] = nullptr; } } // namespace filesystem \ No newline at end of file -- cgit v1.2.3 From 22a32e2ab9fcb779a7359cec9b0244f262661288 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 11 Mar 2026 18:15:54 +0100 Subject: use optional instead of pointer, improve error handling (do not just panic, return std::nullopt) --- .../include/filesystem/file_descriptor_table.hpp | 7 +++++-- kernel/filesystem/src/file_descriptor_table.cpp | 24 ++++++++-------------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/kernel/filesystem/include/filesystem/file_descriptor_table.hpp b/kernel/filesystem/include/filesystem/file_descriptor_table.hpp index a865d32..44fd428 100644 --- a/kernel/filesystem/include/filesystem/file_descriptor_table.hpp +++ b/kernel/filesystem/include/filesystem/file_descriptor_table.hpp @@ -4,6 +4,7 @@ #include "open_file_description.hpp" #include +#include namespace filesystem { @@ -15,13 +16,15 @@ namespace filesystem ~file_descriptor_table() = default; auto add_file(open_file_description & f) -> int; - auto get_file(int fd) -> open_file_description &; + auto get_file(int fd) -> std::optional; auto remove_file(int fd) -> void; private: file_descriptor_table() = default; - std::array m_open_files{}; // TODO BA-FS26 use kstd::vector when available + // TODO BA-FS26 use kstd::shared_ptr when available + // TODO BA-FS26 use kstd::vector when available + std::array, 32> m_open_files{}; }; } // namespace filesystem diff --git a/kernel/filesystem/src/file_descriptor_table.cpp b/kernel/filesystem/src/file_descriptor_table.cpp index 645da9f..286678c 100644 --- a/kernel/filesystem/src/file_descriptor_table.cpp +++ b/kernel/filesystem/src/file_descriptor_table.cpp @@ -37,29 +37,27 @@ namespace filesystem auto file_descriptor_table::add_file(open_file_description & f) -> int { - auto it = std::ranges::find(m_open_files, nullptr); + auto it = std::ranges::find_if(m_open_files, [](auto & open_file) { return !open_file.has_value(); }); if (it != m_open_files.end()) { - *it = &f; + *it = f; return static_cast(it - m_open_files.begin()); } return -1; } - auto file_descriptor_table::get_file(int fd) -> open_file_description & + auto file_descriptor_table::get_file(int fd) -> std::optional { - // TODO BA-FS26 do not panic, its an user error if (fd < 0) { - kapi::system::panic("[FILESYSTEM] get_file called with negative descriptor."); + return std::nullopt; } - // TODO BA-FS26 do not panic, its an user error auto const index = static_cast(fd); - if (index >= m_open_files.size() || m_open_files[index] == nullptr) + if (index >= m_open_files.size() || !m_open_files[index].has_value()) { - kapi::system::panic("[FILESYSTEM] get_file called with invalid descriptor."); + return std::nullopt; } return *m_open_files[index]; @@ -67,21 +65,17 @@ namespace filesystem auto file_descriptor_table::remove_file(int fd) -> void { - // TODO BA-FS26 do not panic, its an user error if (fd < 0) { - kapi::system::panic("[FILESYSTEM] remove_file called with negative descriptor."); + return; } - // TODO BA-FS26 do not panic, its an user error auto const index = static_cast(fd); if (index >= m_open_files.size()) { - kapi::system::panic("[FILESYSTEM] remove_file called with out-of-range descriptor."); + return; } - // TODO BA-FS26 - // delete m_open_files[index]; - m_open_files[index] = nullptr; + m_open_files[index].reset(); } } // namespace filesystem \ No newline at end of file -- cgit v1.2.3 From 9f27b540c160d1990791b62017e68cce622dcee5 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 11 Mar 2026 18:19:50 +0100 Subject: remove not needed cpp file --- kernel/filesystem/src/file.cpp | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 kernel/filesystem/src/file.cpp diff --git a/kernel/filesystem/src/file.cpp b/kernel/filesystem/src/file.cpp deleted file mode 100644 index c67ad1a..0000000 --- a/kernel/filesystem/src/file.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "filesystem/file.hpp" - -namespace filesystem -{ - // TODO BA-FS26 -} \ No newline at end of file -- cgit v1.2.3 From 85cde8c30c522659364bd0cb39c179e8b9dbbe07 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 11 Mar 2026 18:20:08 +0100 Subject: renaming --- kernel/filesystem/src/file_descriptor_table.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/filesystem/src/file_descriptor_table.cpp b/kernel/filesystem/src/file_descriptor_table.cpp index 286678c..49c08cd 100644 --- a/kernel/filesystem/src/file_descriptor_table.cpp +++ b/kernel/filesystem/src/file_descriptor_table.cpp @@ -35,12 +35,12 @@ namespace filesystem return *global_file_descriptor_table; } - auto file_descriptor_table::add_file(open_file_description & f) -> int + auto file_descriptor_table::add_file(open_file_description & file_description) -> int { auto it = std::ranges::find_if(m_open_files, [](auto & open_file) { return !open_file.has_value(); }); if (it != m_open_files.end()) { - *it = f; + *it = file_description; return static_cast(it - m_open_files.begin()); } -- cgit v1.2.3 From 0e6a3205dccbc44b61cf9eb2d9ea906149f8295f Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 11 Mar 2026 20:50:51 +0100 Subject: fix build --- kernel/filesystem/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/filesystem/CMakeLists.txt b/kernel/filesystem/CMakeLists.txt index 1d74585..a5a964f 100644 --- a/kernel/filesystem/CMakeLists.txt +++ b/kernel/filesystem/CMakeLists.txt @@ -2,7 +2,6 @@ add_library("kernel_filesystem" STATIC) add_library("kernel::filesystem" ALIAS "kernel_filesystem") target_sources("kernel_filesystem" PRIVATE - "src/file.cpp" "src/device_file.cpp" "src/open_file_description.cpp" "src/file_descriptor_table.cpp" -- cgit v1.2.3 From 6234691efe02fa095c6efa476606e8c5ff579710 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sun, 15 Mar 2026 14:18:16 +0100 Subject: Add inode_file and inode structure --- kernel/filesystem/CMakeLists.txt | 10 +++++-- kernel/filesystem/include/filesystem/inode.hpp | 14 +++++++++ .../filesystem/include/filesystem/inode_file.hpp | 25 ++++++++++++++++ kernel/filesystem/src/inode.cpp | 20 +++++++++++++ kernel/filesystem/src/inode_file.cpp | 33 ++++++++++++++++++++++ 5 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 kernel/filesystem/include/filesystem/inode.hpp create mode 100644 kernel/filesystem/include/filesystem/inode_file.hpp create mode 100644 kernel/filesystem/src/inode.cpp create mode 100644 kernel/filesystem/src/inode_file.cpp diff --git a/kernel/filesystem/CMakeLists.txt b/kernel/filesystem/CMakeLists.txt index a5a964f..126b54e 100644 --- a/kernel/filesystem/CMakeLists.txt +++ b/kernel/filesystem/CMakeLists.txt @@ -3,18 +3,22 @@ add_library("kernel::filesystem" ALIAS "kernel_filesystem") target_sources("kernel_filesystem" PRIVATE "src/device_file.cpp" - "src/open_file_description.cpp" "src/file_descriptor_table.cpp" + "src/inode_file.cpp" + "src/inode.cpp" + "src/open_file_description.cpp" ) target_sources("kernel_filesystem" PUBLIC FILE_SET HEADERS BASE_DIRS "include" FILES - "include/filesystem/file.hpp" "include/filesystem/device_file.hpp" - "include/filesystem/open_file_description.hpp" "include/filesystem/file_descriptor_table.hpp" + "include/filesystem/file.hpp" + "include/filesystem/inode_file.hpp" + "include/filesystem/inode.hpp" + "include/filesystem/open_file_description.hpp" ) target_include_directories("kernel_filesystem" PUBLIC diff --git a/kernel/filesystem/include/filesystem/inode.hpp b/kernel/filesystem/include/filesystem/inode.hpp new file mode 100644 index 0000000..44ec09a --- /dev/null +++ b/kernel/filesystem/include/filesystem/inode.hpp @@ -0,0 +1,14 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP + +#include +namespace filesystem +{ + struct inode + { + auto read(void * buffer, size_t offset, size_t size) const -> size_t; + auto write(void const * buffer, size_t offset, size_t size) -> size_t; + }; +} // namespace filesystem + +#endif diff --git a/kernel/filesystem/include/filesystem/inode_file.hpp b/kernel/filesystem/include/filesystem/inode_file.hpp new file mode 100644 index 0000000..c091280 --- /dev/null +++ b/kernel/filesystem/include/filesystem/inode_file.hpp @@ -0,0 +1,25 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP + +#include "filesystem/file.hpp" +#include "filesystem/inode.hpp" + +#include + +namespace filesystem +{ + struct inode_file : file + { + explicit inode_file(inode * inode); + + auto open() -> void override; + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + private: + inode * m_inode; + }; +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/filesystem/src/inode.cpp b/kernel/filesystem/src/inode.cpp new file mode 100644 index 0000000..7bca507 --- /dev/null +++ b/kernel/filesystem/src/inode.cpp @@ -0,0 +1,20 @@ +#include "filesystem/inode.hpp" + +#include "kapi/system.hpp" + +#include + +namespace filesystem +{ + auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t + { + // TODO BA-FS26 + return 0; + } + + auto inode::write(void const *, size_t, size_t) -> size_t + { + kapi::system::panic("[FILESYSTEM] inode::write is not implemented yet"); + return 0; + } +} // namespace filesystem \ No newline at end of file diff --git a/kernel/filesystem/src/inode_file.cpp b/kernel/filesystem/src/inode_file.cpp new file mode 100644 index 0000000..071661f --- /dev/null +++ b/kernel/filesystem/src/inode_file.cpp @@ -0,0 +1,33 @@ +#include "filesystem/inode_file.hpp" + +#include "kapi/system.hpp" + +#include "filesystem/inode.hpp" + +#include + +namespace filesystem +{ + inode_file::inode_file(inode * inode) + : m_inode(inode) + { + if (!m_inode) + { + kapi::system::panic("[FILESYSTEM] inode_file constructed with null inode"); + } + } + + auto inode_file::open() -> void + { + // Hook point for permission checks or lazy metadata loading. + } + + auto inode_file::read(void * buffer, size_t offset, size_t size) const -> size_t + { + return m_inode->read(buffer, offset, size); + } + auto inode_file::write(void const * buffer, size_t offset, size_t size) -> size_t + { + return m_inode->write(buffer, offset, size); + } +} // namespace filesystem \ No newline at end of file -- cgit v1.2.3 From afda1c1473919e5d9337614075283a50e865be90 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sun, 15 Mar 2026 14:18:30 +0100 Subject: Refactor device_file --- kernel/filesystem/include/filesystem/device_file.hpp | 2 +- kernel/filesystem/src/device_file.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/filesystem/include/filesystem/device_file.hpp b/kernel/filesystem/include/filesystem/device_file.hpp index 08d81f6..033317b 100644 --- a/kernel/filesystem/include/filesystem/device_file.hpp +++ b/kernel/filesystem/include/filesystem/device_file.hpp @@ -11,7 +11,7 @@ namespace filesystem { struct device_file : file { - device_file(devices::device * device); + explicit device_file(devices::device * device); auto open() -> void override; diff --git a/kernel/filesystem/src/device_file.cpp b/kernel/filesystem/src/device_file.cpp index 34195db..f11638e 100644 --- a/kernel/filesystem/src/device_file.cpp +++ b/kernel/filesystem/src/device_file.cpp @@ -16,7 +16,7 @@ namespace filesystem device_file::device_file(devices::device * device) : m_device(device) { - if (m_device == nullptr) + if (!m_device) { kapi::system::panic("[FILESYSTEM] device_file constructed with null device."); } -- cgit v1.2.3 From 1453fbedde547d9e69cdad659df667f43a8dc750 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sun, 15 Mar 2026 14:31:41 +0100 Subject: Create basic VFS structure --- kernel/filesystem/CMakeLists.txt | 2 ++ kernel/filesystem/include/filesystem/vfs.hpp | 21 +++++++++++++++++ kernel/filesystem/src/vfs.cpp | 34 ++++++++++++++++++++++++++++ kernel/src/main.cpp | 4 ++++ 4 files changed, 61 insertions(+) create mode 100644 kernel/filesystem/include/filesystem/vfs.hpp create mode 100644 kernel/filesystem/src/vfs.cpp diff --git a/kernel/filesystem/CMakeLists.txt b/kernel/filesystem/CMakeLists.txt index 126b54e..a1c625e 100644 --- a/kernel/filesystem/CMakeLists.txt +++ b/kernel/filesystem/CMakeLists.txt @@ -7,6 +7,7 @@ target_sources("kernel_filesystem" PRIVATE "src/inode_file.cpp" "src/inode.cpp" "src/open_file_description.cpp" + "src/vfs.cpp" ) target_sources("kernel_filesystem" PUBLIC @@ -19,6 +20,7 @@ target_sources("kernel_filesystem" PUBLIC "include/filesystem/inode_file.hpp" "include/filesystem/inode.hpp" "include/filesystem/open_file_description.hpp" + "include/filesystem/vfs.hpp" ) target_include_directories("kernel_filesystem" PUBLIC diff --git a/kernel/filesystem/include/filesystem/vfs.hpp b/kernel/filesystem/include/filesystem/vfs.hpp new file mode 100644 index 0000000..671128e --- /dev/null +++ b/kernel/filesystem/include/filesystem/vfs.hpp @@ -0,0 +1,21 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP + +#include +namespace filesystem +{ + struct vfs + { + auto static init() -> void; + auto static get() -> vfs &; + + ~vfs() = default; + + // auto do_mount(std::string_view const & path) -> + + private: + vfs() = default; + }; +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/filesystem/src/vfs.cpp b/kernel/filesystem/src/vfs.cpp new file mode 100644 index 0000000..27f1db9 --- /dev/null +++ b/kernel/filesystem/src/vfs.cpp @@ -0,0 +1,34 @@ +#include "filesystem/vfs.hpp" + +#include "kapi/system.hpp" + +#include + +namespace filesystem +{ + namespace + { + constinit auto static active_vfs = std::optional{}; + } + + auto vfs::init() -> void + { + if (active_vfs) + { + kapi::system::panic("[FILESYSTEM] vfs has already been initialized."); + } + + active_vfs.emplace(vfs{}); + } + + auto vfs::get() -> vfs & + { + if (!active_vfs) + { + kapi::system::panic("[FILESYSTEM] vfs has not been initialized."); + } + + return *active_vfs; + } + +} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 4b74bd4..07fb81b 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -5,6 +5,7 @@ #include "devices/storage/storage_management.hpp" #include "filesystem/file_descriptor_table.hpp" +#include "filesystem/vfs.hpp" #include "kernel/memory.hpp" @@ -29,5 +30,8 @@ auto main() -> int filesystem::file_descriptor_table::init(); kstd::println("[OS] Global file descriptor table initialized."); + filesystem::vfs::init(); + kstd::println("[OS] Virtual filesystem initialized."); + kapi::system::panic("Returning from kernel main!"); } -- cgit v1.2.3 From 76219d593867f6e0c86c9f65ec90c3da18877e2a Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sun, 15 Mar 2026 15:10:49 +0100 Subject: Add basic structure for ext2_filesystem, filesystem and mount --- kernel/filesystem/CMakeLists.txt | 6 +++++ .../include/filesystem/ext2/ext2_filesystem.hpp | 18 +++++++++++++ .../filesystem/include/filesystem/filesystem.hpp | 23 +++++++++++++++++ kernel/filesystem/include/filesystem/mount.hpp | 24 +++++++++++++++++ kernel/filesystem/src/ext2/ext2_filesystem.cpp | 16 ++++++++++++ kernel/filesystem/src/filesystem.cpp | 11 ++++++++ kernel/filesystem/src/mount.cpp | 30 ++++++++++++++++++++++ 7 files changed, 128 insertions(+) create mode 100644 kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp create mode 100644 kernel/filesystem/include/filesystem/filesystem.hpp create mode 100644 kernel/filesystem/include/filesystem/mount.hpp create mode 100644 kernel/filesystem/src/ext2/ext2_filesystem.cpp create mode 100644 kernel/filesystem/src/filesystem.cpp create mode 100644 kernel/filesystem/src/mount.cpp diff --git a/kernel/filesystem/CMakeLists.txt b/kernel/filesystem/CMakeLists.txt index a1c625e..a6b4e49 100644 --- a/kernel/filesystem/CMakeLists.txt +++ b/kernel/filesystem/CMakeLists.txt @@ -3,9 +3,12 @@ add_library("kernel::filesystem" ALIAS "kernel_filesystem") target_sources("kernel_filesystem" PRIVATE "src/device_file.cpp" + "src/ext2/ext2_filesystem.cpp" "src/file_descriptor_table.cpp" + "src/filesystem.cpp" "src/inode_file.cpp" "src/inode.cpp" + "src/mount.cpp" "src/open_file_description.cpp" "src/vfs.cpp" ) @@ -15,10 +18,13 @@ target_sources("kernel_filesystem" PUBLIC BASE_DIRS "include" FILES "include/filesystem/device_file.hpp" + "include/filesystem/ext2/ext2_filesystem.hpp" "include/filesystem/file_descriptor_table.hpp" "include/filesystem/file.hpp" + "include/filesystem/filesystem.hpp" "include/filesystem/inode_file.hpp" "include/filesystem/inode.hpp" + "include/filesystem/mount.hpp" "include/filesystem/open_file_description.hpp" "include/filesystem/vfs.hpp" ) diff --git a/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp b/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp new file mode 100644 index 0000000..0027475 --- /dev/null +++ b/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp @@ -0,0 +1,18 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP + +#include "devices/block_device.hpp" +#include "filesystem/filesystem.hpp" + +namespace filesystem::ext2 +{ + struct ext2_filesystem : filesystem + { + auto mount(devices::block_device * block_device) -> int override; + + private: + devices::block_device * m_block_device{}; + }; +} // namespace filesystem::ext2 + +#endif diff --git a/kernel/filesystem/include/filesystem/filesystem.hpp b/kernel/filesystem/include/filesystem/filesystem.hpp new file mode 100644 index 0000000..d5704c1 --- /dev/null +++ b/kernel/filesystem/include/filesystem/filesystem.hpp @@ -0,0 +1,23 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP + +#include "devices/block_device.hpp" +#include "filesystem/inode.hpp" + +namespace filesystem +{ + struct filesystem + { + virtual ~filesystem() = default; + + virtual auto mount(devices::block_device * block_device) -> int = 0; + + auto root_inode() -> inode *; + + protected: + inode * m_root_inode{}; // TODO BA-FS26 set during mount? + }; + +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/filesystem/include/filesystem/mount.hpp b/kernel/filesystem/include/filesystem/mount.hpp new file mode 100644 index 0000000..793c042 --- /dev/null +++ b/kernel/filesystem/include/filesystem/mount.hpp @@ -0,0 +1,24 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_MOUNT_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_MOUNT_HPP + +#include "filesystem/filesystem.hpp" + +#include + +namespace filesystem +{ + struct mount + { + mount() = default; // TODO BA-FS26 remove again when kstd::vector is available and used in vfs + mount(std::string_view const & path, filesystem * fs); + + auto path() const -> std::string_view; + auto get_filesystem() const -> filesystem *; + + private: + std::string_view m_path; + filesystem * m_filesystem{}; + }; +} // namespace filesystem + +#endif diff --git a/kernel/filesystem/src/ext2/ext2_filesystem.cpp b/kernel/filesystem/src/ext2/ext2_filesystem.cpp new file mode 100644 index 0000000..bdd430d --- /dev/null +++ b/kernel/filesystem/src/ext2/ext2_filesystem.cpp @@ -0,0 +1,16 @@ +#include "filesystem/ext2/ext2_filesystem.hpp" + +#include "devices/block_device.hpp" + +namespace filesystem::ext2 +{ + auto ext2_filesystem::mount(devices::block_device * block_device) -> int + { + if (!block_device) + { + return -1; // TODO BA-FS26 panic or errorcode? + } + // TODO BA-FS26 implement + return 0; + } +} // namespace filesystem::ext2 \ No newline at end of file diff --git a/kernel/filesystem/src/filesystem.cpp b/kernel/filesystem/src/filesystem.cpp new file mode 100644 index 0000000..d6a2f25 --- /dev/null +++ b/kernel/filesystem/src/filesystem.cpp @@ -0,0 +1,11 @@ +#include "filesystem/filesystem.hpp" + +#include "filesystem/inode.hpp" + +namespace filesystem +{ + auto filesystem::root_inode() -> inode * + { + return m_root_inode; + } +} // namespace filesystem \ No newline at end of file diff --git a/kernel/filesystem/src/mount.cpp b/kernel/filesystem/src/mount.cpp new file mode 100644 index 0000000..6594598 --- /dev/null +++ b/kernel/filesystem/src/mount.cpp @@ -0,0 +1,30 @@ +#include "filesystem/mount.hpp" + +#include "kapi/system.hpp" + +#include "filesystem/filesystem.hpp" + +#include + +namespace filesystem +{ + mount::mount(std::string_view const & path, filesystem * fs) + : m_path(path) + , m_filesystem(fs) + { + if (!m_filesystem) + { + kapi::system::panic("[FILESYSTEM] mount initialized with null filesystem."); + } + } + + auto mount::path() const -> std::string_view + { + return m_path; + } + + auto mount::get_filesystem() const -> filesystem * + { + return m_filesystem; + } +} // namespace filesystem \ No newline at end of file -- cgit v1.2.3 From e87963115bcdc0f0534bc2194bf3f7e3d3f3e2b6 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 15 Mar 2026 15:35:04 +0100 Subject: move test code into separate function --- kernel/src/main.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 07fb81b..011821a 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -4,21 +4,92 @@ #include "kapi/system.hpp" #include "devices/storage/storage_management.hpp" +#include "filesystem/device_file.hpp" #include "filesystem/file_descriptor_table.hpp" +#include "filesystem/open_file_description.hpp" #include "filesystem/vfs.hpp" #include "kernel/memory.hpp" +#include #include +#include +#include +#include + +auto run_test_code() -> void +{ + // setup + auto fd_table = filesystem::file_descriptor_table::get(); + auto storage_mgmt = devices::storage::storage_management::get(); + auto device = storage_mgmt.device_by_major_minor(1, 0); + + filesystem::device_file dev_file(device); + filesystem::open_file_description ofd(&dev_file); + auto fd_index = fd_table.add_file(ofd); + + // use: read two bytes and write two again + auto fd = fd_table.get_file(fd_index); + if (!fd) + { + kstd::os::panic("test code failed"); + } + + std::array buffer{}; + auto number_of_read_bytes = fd->read(buffer.data(), buffer.size()); + + for (size_t i = 0; i < number_of_read_bytes; ++i) + { + kstd::print("{:02x} ", static_cast(buffer[i])); + + if ((i + 1) % 16 == 0) + { + kstd::println(""); + } + } + kstd::println("---"); + + // write half of the file new + std::array write_buffer{std::byte{0xBB}, std::byte{0xAA}}; + auto written_bytes = fd->write(write_buffer.data(), write_buffer.size()); + + kstd::println("written bytes: {}", written_bytes); + + fd_table.remove_file(fd_index); + + // use: read four bytes again -> two old bytes two new bytes + filesystem::open_file_description ofd1(&dev_file); + fd_index = fd_table.add_file(ofd1); + auto fd1 = fd_table.get_file(fd_index); + + if (!fd1) + { + kstd::os::panic("test code failed"); + } + + std::array buffer1{}; + number_of_read_bytes = fd1->read(buffer1.data(), buffer1.size()); + + for (size_t i = 0; i < number_of_read_bytes; ++i) + { + kstd::print("{:02x} ", static_cast(buffer1[i])); + + if ((i + 1) % 16 == 0) + { + kstd::println(""); + } + } + kstd::println("---"); +} + auto main() -> int { kapi::cio::init(); kstd::println("[OS] IO subsystem initialized."); kapi::memory::init(); - kernel::memory::init_heap(kapi::memory::heap_base); - kstd::println("[OS] Memory subsystem initialized."); + kernel::memory::init_heap(kapi::memory::heap_base); kstd::println("[OS] Memory subsystem initialized."); kapi::system::memory_initialized(); kapi::boot_modules::init(); @@ -33,5 +104,7 @@ auto main() -> int filesystem::vfs::init(); kstd::println("[OS] Virtual filesystem initialized."); + run_test_code(); + kapi::system::panic("Returning from kernel main!"); } -- cgit v1.2.3 From ace2d2178315d4b4ff1d969feed562a53d7a66c1 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 15 Mar 2026 15:53:29 +0100 Subject: mount root filesystem --- kernel/filesystem/include/filesystem/vfs.hpp | 8 ++++++++ kernel/filesystem/src/vfs.cpp | 28 +++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/kernel/filesystem/include/filesystem/vfs.hpp b/kernel/filesystem/include/filesystem/vfs.hpp index 671128e..5998137 100644 --- a/kernel/filesystem/include/filesystem/vfs.hpp +++ b/kernel/filesystem/include/filesystem/vfs.hpp @@ -1,7 +1,12 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP #define TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP +#include "filesystem/mount.hpp" + +#include +#include #include + namespace filesystem { struct vfs @@ -15,6 +20,9 @@ namespace filesystem private: vfs() = default; + + std::optional m_root_mount; + std::array m_mounts; // TODO BA-FS26 remove when kstd::vector is available and used }; } // namespace filesystem diff --git a/kernel/filesystem/src/vfs.cpp b/kernel/filesystem/src/vfs.cpp index 27f1db9..3f2576f 100644 --- a/kernel/filesystem/src/vfs.cpp +++ b/kernel/filesystem/src/vfs.cpp @@ -2,6 +2,10 @@ #include "kapi/system.hpp" +#include "devices/storage/storage_management.hpp" +#include "filesystem/ext2/ext2_filesystem.hpp" +#include "filesystem/mount.hpp" + #include namespace filesystem @@ -9,7 +13,11 @@ namespace filesystem namespace { constinit auto static active_vfs = std::optional{}; - } + + // TODO BA-FS26 @Felix better solution? while dynamic memory not available? + // TODO BA-FS26 remove when dynamic memory available; + constinit auto static root_fs = std::optional{}; + } // namespace auto vfs::init() -> void { @@ -19,6 +27,24 @@ namespace filesystem } active_vfs.emplace(vfs{}); + + auto storage_mgmt = devices::storage::storage_management::get(); + if (auto boot_device = storage_mgmt.determine_boot_device()) + { + root_fs.emplace(ext2::ext2_filesystem{}); + if (root_fs->mount(boot_device) != 0) + { + kapi::system::panic("[FILESYSTEM] Failed to mount root filesystem."); + } + + active_vfs->m_root_mount = mount{"/", &*root_fs}; + + // TODO BA-FS26 mount all the other devices to "/dev/ramxy" + } + else + { + // TODO BA-FS26 ?? what when no boot_device == no modules loaded?? + } } auto vfs::get() -> vfs & -- cgit v1.2.3 From 8eed4d31cf1d07d43e99d53da0fe3a401ce9e85e Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 15 Mar 2026 16:24:42 +0100 Subject: every device has a name, generate ram disk device names --- kernel/devices/include/devices/block_device.hpp | 4 +++- kernel/devices/include/devices/device.hpp | 12 ++++++++++-- kernel/devices/src/block_device.cpp | 5 +++-- kernel/devices/src/device.cpp | 9 ++++++++- kernel/devices/src/storage/ram_disk/ram_disk_device.cpp | 16 ++++++++++++++-- 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/kernel/devices/include/devices/block_device.hpp b/kernel/devices/include/devices/block_device.hpp index 9bca53f..a2c849f 100644 --- a/kernel/devices/include/devices/block_device.hpp +++ b/kernel/devices/include/devices/block_device.hpp @@ -4,6 +4,7 @@ #include "devices/device.hpp" #include +#include namespace devices { @@ -16,9 +17,10 @@ namespace devices * @brief Create a block device descriptor. * @param major Device major number. * @param minor Device minor number. + * @param name Device name. * @param block_size Size of one logical block in bytes. */ - block_device(size_t major, size_t minor, size_t block_size); + block_device(size_t major, size_t minor, std::string_view name, size_t block_size); /** * @brief Virtual destructor for block device. diff --git a/kernel/devices/include/devices/device.hpp b/kernel/devices/include/devices/device.hpp index 7f9f9e1..d1c202d 100644 --- a/kernel/devices/include/devices/device.hpp +++ b/kernel/devices/include/devices/device.hpp @@ -2,6 +2,7 @@ #define TEACH_OS_KERNEL_DEVICES_DEVICE_HPP #include +#include namespace devices { @@ -14,8 +15,9 @@ namespace devices * @brief Create a device identifier from @p major and @p minor. * @param major Device major number. * @param minor Device minor number. + * @param name Device name. */ - device(size_t major, size_t minor); + device(size_t major, size_t minor, std::string_view name); /** * @brief Virtual destructor for device. @@ -34,6 +36,12 @@ namespace devices */ auto minor() const -> size_t; + /** + * @brief Returns the name of the device. + * @return Device name. + */ + auto name() const -> std::string_view; + /** * @brief Check if the device is a block device. * @return true if this device is a block device, false otherwise. @@ -46,7 +54,7 @@ namespace devices private: size_t m_major; size_t m_minor; - // TODO BA-FS26 add name + std::string_view m_name; }; } // namespace devices diff --git a/kernel/devices/src/block_device.cpp b/kernel/devices/src/block_device.cpp index 2f0c2ca..40f842a 100644 --- a/kernel/devices/src/block_device.cpp +++ b/kernel/devices/src/block_device.cpp @@ -5,11 +5,12 @@ #include "devices/device.hpp" #include +#include namespace devices { - block_device::block_device(size_t major, size_t minor, size_t block_size) - : device(major, minor) + block_device::block_device(size_t major, size_t minor, std::string_view name, size_t block_size) + : device(major, minor, name) , m_block_size(block_size) { if (m_block_size == 0) diff --git a/kernel/devices/src/device.cpp b/kernel/devices/src/device.cpp index aa5c1bb..8184321 100644 --- a/kernel/devices/src/device.cpp +++ b/kernel/devices/src/device.cpp @@ -1,12 +1,14 @@ #include "devices/device.hpp" #include +#include namespace devices { - device::device(size_t major, size_t minor) + device::device(size_t major, size_t minor, std::string_view name) : m_major(major) , m_minor(minor) + , m_name(name) {} auto device::major() const -> size_t @@ -18,4 +20,9 @@ namespace devices { return m_minor; } + + auto device::name() const -> std::string_view + { + return m_name; + } } // namespace devices \ No newline at end of file diff --git a/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp b/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp index 3bd3967..8e9988d 100644 --- a/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp +++ b/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp @@ -7,21 +7,33 @@ #include +#include #include +#include namespace devices::storage::ram_disk { namespace { constexpr size_t RAM_DISK_BLOCK_SIZE = 512uz; // TODO BA-FS26 really correct / good?? + + // TODO BA-FS26 @Felix + // TODO BA-FS26 currently only names for 9 minor devices + constinit std::array NAME = {'r', 'a', 'm', '0', '\0'}; + + auto determine_device_name(size_t minor) -> std::string_view + { + NAME[3] = '0' + minor; + return std::string_view{NAME}; + } } // namespace ram_disk_device::ram_disk_device() // TODO BA-FS26 remove when kstd::vector is available - : block_device(0, 0, RAM_DISK_BLOCK_SIZE) + : block_device(0, 0, determine_device_name(0), RAM_DISK_BLOCK_SIZE) {} ram_disk_device::ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor) - : block_device(major, minor, RAM_DISK_BLOCK_SIZE) + : block_device(major, minor, determine_device_name(minor), RAM_DISK_BLOCK_SIZE) , m_boot_module(module) {} -- cgit v1.2.3 From 2af1bbc99e2a8fc4b86bb31023dbbb077b1cbc97 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 15 Mar 2026 16:49:07 +0100 Subject: move m_devices from ram_disk_controller to storage_controller, store point to devices --- .../storage/ram_disk/ram_disk_controller.hpp | 21 ---------------- .../include/devices/storage/storage_controller.hpp | 7 ++++-- .../include/devices/storage/storage_management.hpp | 10 ++++---- .../src/storage/ram_disk/ram_disk_controller.cpp | 28 ++++++++-------------- kernel/devices/src/storage/storage_controller.cpp | 19 +++++++++++++++ kernel/devices/src/storage/storage_management.cpp | 10 ++++---- .../include/filesystem/ext2/ext2_filesystem.hpp | 6 ++--- .../filesystem/include/filesystem/filesystem.hpp | 4 ++-- kernel/filesystem/src/ext2/ext2_filesystem.cpp | 6 ++--- 9 files changed, 52 insertions(+), 59 deletions(-) diff --git a/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp b/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp index 424082d..f69f8d8 100644 --- a/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp +++ b/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp @@ -3,13 +3,8 @@ #include "kapi/boot_module/boot_module_registry.hpp" -#include "devices/block_device.hpp" -#include "devices/storage/ram_disk/ram_disk_device.hpp" #include "devices/storage/storage_controller.hpp" -#include -#include - namespace devices::storage::ram_disk { /** @@ -28,24 +23,8 @@ namespace devices::storage::ram_disk */ auto probe() -> void override; - /** - * @brief Return the number of managed RAM-disk devices. - * @return Number of managed devices. - */ - auto devices_count() -> size_t override; - - /** - * @brief Look up a RAM-disk device by major/minor numbers. - * @param major Device major number. - * @param minor Device minor number. - * @return Matching block device, or nullptr if no device matches. - */ - auto device_by_minor(size_t minor) -> block_device * override; - private: kapi::boot_modules::boot_module_registry const * m_boot_module_registry; - - std::array m_devices{}; // TODO BA-FS26 use kstd::vector when available }; } // namespace devices::storage::ram_disk diff --git a/kernel/devices/include/devices/storage/storage_controller.hpp b/kernel/devices/include/devices/storage/storage_controller.hpp index 0c7cf83..697d3c3 100644 --- a/kernel/devices/include/devices/storage/storage_controller.hpp +++ b/kernel/devices/include/devices/storage/storage_controller.hpp @@ -2,7 +2,9 @@ #define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP #include "devices/block_device.hpp" +#include "devices/device.hpp" +#include #include namespace devices::storage @@ -42,7 +44,7 @@ namespace devices::storage * @brief Return the number of devices managed by this controller. * @return Number of managed devices. */ - virtual auto devices_count() -> size_t = 0; + auto devices_count() -> size_t; /** * @brief Find a managed device by major/minor numbers. @@ -50,11 +52,12 @@ namespace devices::storage * @param minor Device minor number. * @return Matching block device, or nullptr if no device matches. */ - virtual auto device_by_minor(size_t minor) -> block_device * = 0; + auto device_by_minor(size_t minor) -> device *; protected: size_t m_major{}; size_t m_minors_per_device{}; + std::array m_devices{}; // TODO BA-FS26 use kstd::vector when available }; } // namespace devices::storage diff --git a/kernel/devices/include/devices/storage/storage_management.hpp b/kernel/devices/include/devices/storage/storage_management.hpp index 2bed459..6ca8db7 100644 --- a/kernel/devices/include/devices/storage/storage_management.hpp +++ b/kernel/devices/include/devices/storage/storage_management.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP -#include "devices/block_device.hpp" +#include "devices/device.hpp" #include "devices/storage/storage_controller.hpp" #include @@ -43,18 +43,18 @@ namespace devices::storage auto add_controller(storage_controller * controller) -> void; /** - * @brief Find a block device by major/minor numbers. + * @brief Find a device by major/minor numbers. * @param major Device major number. * @param minor Device minor number. - * @return Matching block device, or nullptr if no device matches. + * @return Matching device, or nullptr if no device matches. */ - auto device_by_major_minor(size_t major, size_t minor) -> block_device *; + auto device_by_major_minor(size_t major, size_t minor) -> device *; /** * @brief Determine the boot device. * @return Boot device, or nullptr if it cannot be determined. */ - auto determine_boot_device() -> block_device *; + auto determine_boot_device() -> device *; private: /** diff --git a/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp b/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp index 9f9537f..b57dcfb 100644 --- a/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp +++ b/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp @@ -2,7 +2,6 @@ #include "kapi/boot_module/boot_module_registry.hpp" -#include "devices/block_device.hpp" #include "devices/storage/ram_disk/ram_disk_device.hpp" #include @@ -10,9 +9,17 @@ #include #include #include +#include namespace devices::storage::ram_disk { + namespace + { + // TODO BA-FS26 @Felix gibts besseren weg (ausser dynamic Memory) + // TODO BA-FS26 remove again, when dynamic memory available + constinit auto static active_ram_disk_device = std::optional{}; + } // namespace + ram_disk_controller::ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry) : m_boot_module_registry(registry) {} @@ -25,23 +32,8 @@ namespace devices::storage::ram_disk auto const minor = current_device_index++ * m_minors_per_device; // TODO BA-FS26 use push_back from kstd::vector when available - m_devices.at(0) = ram_disk_device{module, m_major, minor}; + active_ram_disk_device.emplace(module, m_major, minor); + m_devices.at(0) = &*active_ram_disk_device; }); } - - auto ram_disk_controller::device_by_minor(size_t minor) -> block_device * - { - auto it = std::ranges::find_if(m_devices, [minor](auto const & device) { return device.minor() == minor; }); - - if (it != m_devices.end()) - { - return &(*it); - } - return nullptr; - } - - auto ram_disk_controller::devices_count() -> size_t - { - return m_devices.size(); - } } // namespace devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/devices/src/storage/storage_controller.cpp b/kernel/devices/src/storage/storage_controller.cpp index f95f533..d127a8c 100644 --- a/kernel/devices/src/storage/storage_controller.cpp +++ b/kernel/devices/src/storage/storage_controller.cpp @@ -1,5 +1,8 @@ #include "devices/storage/storage_controller.hpp" +#include "devices/device.hpp" + +#include #include namespace devices::storage @@ -14,4 +17,20 @@ namespace devices::storage { return m_major; } + + auto storage_controller::device_by_minor(size_t minor) -> device * + { + auto it = std::ranges::find_if(m_devices, [minor](auto const & device) { return device->minor() == minor; }); + + if (it != m_devices.end()) + { + return *it; + } + return nullptr; + } + + auto storage_controller::devices_count() -> size_t + { + return m_devices.size(); + } } // namespace devices::storage \ No newline at end of file diff --git a/kernel/devices/src/storage/storage_management.cpp b/kernel/devices/src/storage/storage_management.cpp index a5b6503..a981359 100644 --- a/kernel/devices/src/storage/storage_management.cpp +++ b/kernel/devices/src/storage/storage_management.cpp @@ -3,7 +3,7 @@ #include "kapi/boot_modules.hpp" #include "kapi/system.hpp" -#include "devices/block_device.hpp" +#include "devices/device.hpp" #include "devices/storage/ram_disk/ram_disk_controller.hpp" #include "devices/storage/storage_controller.hpp" @@ -20,6 +20,7 @@ namespace devices::storage constinit size_t static next_free_major = START_MAJOR; constinit auto static active_storage_management = std::optional{}; + // TODO BA-FS26 remove again, when dynamic memory available constinit auto static active_ram_disk_controller = std::optional{}; } // namespace @@ -53,9 +54,9 @@ namespace devices::storage m_controllers.at(0) = controller; // TODO BA-FS26 use push_back from kstd:vector } - auto storage_management::device_by_major_minor(size_t major, size_t minor) -> block_device * + auto storage_management::device_by_major_minor(size_t major, size_t minor) -> device * { - block_device * found = nullptr; + device * found = nullptr; std::ranges::find_if(m_controllers, [&](auto const controller) { if (controller != nullptr && controller->major() == major) @@ -69,9 +70,8 @@ namespace devices::storage return found; } - auto storage_management::determine_boot_device() -> block_device * + auto storage_management::determine_boot_device() -> device * { - // TODO BA-FS26 better way? return device_by_major_minor(START_MAJOR, 0); } diff --git a/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp b/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp index 0027475..91ba14a 100644 --- a/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp +++ b/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp @@ -1,17 +1,17 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP -#include "devices/block_device.hpp" +#include "devices/device.hpp" #include "filesystem/filesystem.hpp" namespace filesystem::ext2 { struct ext2_filesystem : filesystem { - auto mount(devices::block_device * block_device) -> int override; + auto mount(devices::device * device) -> int override; private: - devices::block_device * m_block_device{}; + devices::device * m_device{}; }; } // namespace filesystem::ext2 diff --git a/kernel/filesystem/include/filesystem/filesystem.hpp b/kernel/filesystem/include/filesystem/filesystem.hpp index d5704c1..113b239 100644 --- a/kernel/filesystem/include/filesystem/filesystem.hpp +++ b/kernel/filesystem/include/filesystem/filesystem.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP -#include "devices/block_device.hpp" +#include "devices/device.hpp" #include "filesystem/inode.hpp" namespace filesystem @@ -10,7 +10,7 @@ namespace filesystem { virtual ~filesystem() = default; - virtual auto mount(devices::block_device * block_device) -> int = 0; + virtual auto mount(devices::device * device) -> int = 0; auto root_inode() -> inode *; diff --git a/kernel/filesystem/src/ext2/ext2_filesystem.cpp b/kernel/filesystem/src/ext2/ext2_filesystem.cpp index bdd430d..7111d13 100644 --- a/kernel/filesystem/src/ext2/ext2_filesystem.cpp +++ b/kernel/filesystem/src/ext2/ext2_filesystem.cpp @@ -1,12 +1,12 @@ #include "filesystem/ext2/ext2_filesystem.hpp" -#include "devices/block_device.hpp" +#include "devices/device.hpp" namespace filesystem::ext2 { - auto ext2_filesystem::mount(devices::block_device * block_device) -> int + auto ext2_filesystem::mount(devices::device * device) -> int { - if (!block_device) + if (!device) { return -1; // TODO BA-FS26 panic or errorcode? } -- cgit v1.2.3 From 94a92c50cc209ebfa53a9734aff403945a3c9a77 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 15 Mar 2026 18:30:09 +0100 Subject: improve constness, expose controllers and devices --- kernel/devices/include/devices/storage/storage_controller.hpp | 9 ++++++--- kernel/devices/include/devices/storage/storage_management.hpp | 4 ++++ kernel/devices/src/storage/storage_controller.cpp | 10 ++++++++-- kernel/devices/src/storage/storage_management.cpp | 6 ++++++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/kernel/devices/include/devices/storage/storage_controller.hpp b/kernel/devices/include/devices/storage/storage_controller.hpp index 697d3c3..0518e32 100644 --- a/kernel/devices/include/devices/storage/storage_controller.hpp +++ b/kernel/devices/include/devices/storage/storage_controller.hpp @@ -1,7 +1,6 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP -#include "devices/block_device.hpp" #include "devices/device.hpp" #include @@ -44,7 +43,11 @@ namespace devices::storage * @brief Return the number of devices managed by this controller. * @return Number of managed devices. */ - auto devices_count() -> size_t; + auto devices_count() const -> size_t; + + // TODO BA-FS26 add comment + // TODO BA-FS26 use kstd::vector when available + auto all_devices() const -> std::array const &; /** * @brief Find a managed device by major/minor numbers. @@ -52,7 +55,7 @@ namespace devices::storage * @param minor Device minor number. * @return Matching block device, or nullptr if no device matches. */ - auto device_by_minor(size_t minor) -> device *; + auto device_by_minor(size_t minor) const -> device *; protected: size_t m_major{}; diff --git a/kernel/devices/include/devices/storage/storage_management.hpp b/kernel/devices/include/devices/storage/storage_management.hpp index 6ca8db7..4bc7cbd 100644 --- a/kernel/devices/include/devices/storage/storage_management.hpp +++ b/kernel/devices/include/devices/storage/storage_management.hpp @@ -42,6 +42,10 @@ namespace devices::storage */ auto add_controller(storage_controller * controller) -> void; + // TODO BA-FS26 add comment + // TODO BA-FS26 use kstd::vector when available + auto all_controllers() const -> std::array const &; + /** * @brief Find a device by major/minor numbers. * @param major Device major number. diff --git a/kernel/devices/src/storage/storage_controller.cpp b/kernel/devices/src/storage/storage_controller.cpp index d127a8c..d9bc806 100644 --- a/kernel/devices/src/storage/storage_controller.cpp +++ b/kernel/devices/src/storage/storage_controller.cpp @@ -3,6 +3,7 @@ #include "devices/device.hpp" #include +#include #include namespace devices::storage @@ -18,7 +19,7 @@ namespace devices::storage return m_major; } - auto storage_controller::device_by_minor(size_t minor) -> device * + auto storage_controller::device_by_minor(size_t minor) const -> device * { auto it = std::ranges::find_if(m_devices, [minor](auto const & device) { return device->minor() == minor; }); @@ -29,8 +30,13 @@ namespace devices::storage return nullptr; } - auto storage_controller::devices_count() -> size_t + auto storage_controller::devices_count() const -> size_t { return m_devices.size(); } + + auto storage_controller::all_devices() const -> std::array const & + { + return m_devices; + } } // namespace devices::storage \ No newline at end of file diff --git a/kernel/devices/src/storage/storage_management.cpp b/kernel/devices/src/storage/storage_management.cpp index a981359..e1f1bcc 100644 --- a/kernel/devices/src/storage/storage_management.cpp +++ b/kernel/devices/src/storage/storage_management.cpp @@ -8,6 +8,7 @@ #include "devices/storage/storage_controller.hpp" #include +#include #include #include @@ -54,6 +55,11 @@ namespace devices::storage m_controllers.at(0) = controller; // TODO BA-FS26 use push_back from kstd:vector } + auto storage_management::all_controllers() const -> std::array const & + { + return m_controllers; + } + auto storage_management::device_by_major_minor(size_t major, size_t minor) -> device * { device * found = nullptr; -- cgit v1.2.3 From 5c274e8676006201eb2536f4a09d63fab109e538 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 15 Mar 2026 18:30:32 +0100 Subject: small refactoring --- kernel/devices/src/storage/ram_disk/ram_disk_device.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp b/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp index 8e9988d..d05ae7a 100644 --- a/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp +++ b/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp @@ -19,12 +19,13 @@ namespace devices::storage::ram_disk // TODO BA-FS26 @Felix // TODO BA-FS26 currently only names for 9 minor devices - constinit std::array NAME = {'r', 'a', 'm', '0', '\0'}; + // TODO BA-FS26 when this length is changed, also change the one in vfs.cpp + constinit std::array name = {'r', 'a', 'm', '0', '\0'}; auto determine_device_name(size_t minor) -> std::string_view { - NAME[3] = '0' + minor; - return std::string_view{NAME}; + name[3] = '0' + minor; + return std::string_view{name}; } } // namespace -- cgit v1.2.3 From e355d5e6aa84b1ea4de70820159b5f1af13bc6f8 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 15 Mar 2026 18:32:16 +0100 Subject: implement first draft of make_device_node --- kernel/filesystem/include/filesystem/vfs.hpp | 2 ++ kernel/filesystem/src/vfs.cpp | 31 +++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/kernel/filesystem/include/filesystem/vfs.hpp b/kernel/filesystem/include/filesystem/vfs.hpp index 5998137..67fd5df 100644 --- a/kernel/filesystem/include/filesystem/vfs.hpp +++ b/kernel/filesystem/include/filesystem/vfs.hpp @@ -1,6 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP #define TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP +#include "devices/device.hpp" #include "filesystem/mount.hpp" #include @@ -17,6 +18,7 @@ namespace filesystem ~vfs() = default; // auto do_mount(std::string_view const & path) -> + auto make_device_node(devices::device * device) -> void; private: vfs() = default; diff --git a/kernel/filesystem/src/vfs.cpp b/kernel/filesystem/src/vfs.cpp index 3f2576f..573994a 100644 --- a/kernel/filesystem/src/vfs.cpp +++ b/kernel/filesystem/src/vfs.cpp @@ -2,11 +2,17 @@ #include "kapi/system.hpp" +#include "devices/device.hpp" #include "devices/storage/storage_management.hpp" #include "filesystem/ext2/ext2_filesystem.hpp" #include "filesystem/mount.hpp" +#include + +#include +#include #include +#include namespace filesystem { @@ -17,6 +23,12 @@ namespace filesystem // TODO BA-FS26 @Felix better solution? while dynamic memory not available? // TODO BA-FS26 remove when dynamic memory available; constinit auto static root_fs = std::optional{}; + // TODO BA-FS26 use kstd::vector when available + constinit auto static filesystems = std::array, 1>{}; + + // TODO BA-FS26 @Felix + // TODO BA-FS26 depends on the length of ram_disk_device name + constinit std::array device_mount_path = {'/', 'd', 'e', 'v', '/', 'x', 'x', 'x', 'x', '\0'}; } // namespace auto vfs::init() -> void @@ -39,7 +51,9 @@ namespace filesystem active_vfs->m_root_mount = mount{"/", &*root_fs}; - // TODO BA-FS26 mount all the other devices to "/dev/ramxy" + std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto controller) { + std::ranges::for_each(controller->all_devices(), [&](auto device) { active_vfs->make_device_node(device); }); + }); } else { @@ -57,4 +71,19 @@ namespace filesystem return *active_vfs; } + auto vfs::make_device_node(devices::device * device) -> void + { + auto device_name = device->name(); + kstd::libc::memcpy(&device_mount_path[5], device_name.data(), device_name.size()); + + // TODO BA-FS26 use kstd::vector and push_back when available + filesystems[0].emplace(ext2::ext2_filesystem{}); + if (filesystems[0]->mount(device) != 0) + { + kapi::system::panic("[FILESYSTEM] Failed to mount device filesystem."); + } + + m_mounts[0] = mount{std::string_view{device_mount_path.data()}, &*filesystems[0]}; + } + } // namespace filesystem \ No newline at end of file -- cgit v1.2.3 From f52f4eb4aff3f8346c9ba73bcc57db4ca6fc6cb2 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 15 Mar 2026 18:49:34 +0100 Subject: implement first draft of custody --- kernel/filesystem/CMakeLists.txt | 6 +++-- kernel/filesystem/include/filesystem/custody.hpp | 21 ++++++++++++++++++ kernel/filesystem/src/custody.cpp | 28 ++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 kernel/filesystem/include/filesystem/custody.hpp create mode 100644 kernel/filesystem/src/custody.cpp diff --git a/kernel/filesystem/CMakeLists.txt b/kernel/filesystem/CMakeLists.txt index a6b4e49..19f1be1 100644 --- a/kernel/filesystem/CMakeLists.txt +++ b/kernel/filesystem/CMakeLists.txt @@ -2,8 +2,9 @@ add_library("kernel_filesystem" STATIC) add_library("kernel::filesystem" ALIAS "kernel_filesystem") target_sources("kernel_filesystem" PRIVATE - "src/device_file.cpp" "src/ext2/ext2_filesystem.cpp" + "src/custody.cpp" + "src/device_file.cpp" "src/file_descriptor_table.cpp" "src/filesystem.cpp" "src/inode_file.cpp" @@ -17,8 +18,9 @@ target_sources("kernel_filesystem" PUBLIC FILE_SET HEADERS BASE_DIRS "include" FILES - "include/filesystem/device_file.hpp" "include/filesystem/ext2/ext2_filesystem.hpp" + "include/filesystem/custody.hpp" + "include/filesystem/device_file.hpp" "include/filesystem/file_descriptor_table.hpp" "include/filesystem/file.hpp" "include/filesystem/filesystem.hpp" diff --git a/kernel/filesystem/include/filesystem/custody.hpp b/kernel/filesystem/include/filesystem/custody.hpp new file mode 100644 index 0000000..9ee984d --- /dev/null +++ b/kernel/filesystem/include/filesystem/custody.hpp @@ -0,0 +1,21 @@ +#ifndef TEACH_OS_KERNEL_CUSTODY_HPP +#define TEACH_OS_KERNEL_CUSTODY_HPP + +#include "filesystem/inode.hpp" + +namespace filesystem +{ + struct custody + { + custody(custody * parent, inode * node); + + [[nodiscard]] auto get_inode() const -> inode *; + [[nodiscard]] auto get_parent() const -> custody *; + + private: + custody * m_parent; + inode * m_inode; + }; +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/filesystem/src/custody.cpp b/kernel/filesystem/src/custody.cpp new file mode 100644 index 0000000..614e63b --- /dev/null +++ b/kernel/filesystem/src/custody.cpp @@ -0,0 +1,28 @@ +#include "filesystem/custody.hpp" + +#include "kapi/system.hpp" + +#include "filesystem/inode.hpp" + +namespace filesystem +{ + custody::custody(custody * parent, inode * node) + : m_parent(parent) + , m_inode(node) + { + if (!m_inode) + { + kapi::system::panic("[FILESYSTEM] custody constructed with null inode."); + } + } + + auto custody::get_inode() const -> inode * + { + return m_inode; + } + + auto custody::get_parent() const -> custody * + { + return m_parent; + } +} // namespace filesystem \ No newline at end of file -- cgit v1.2.3 From 6d8ae9c708d43ab3d98d6a1f2fbb4e5f74a4a2aa Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 15 Mar 2026 18:51:26 +0100 Subject: improve constness --- kernel/filesystem/include/filesystem/file_descriptor_table.hpp | 2 +- kernel/filesystem/include/filesystem/filesystem.hpp | 2 +- kernel/filesystem/include/filesystem/mount.hpp | 4 ++-- kernel/filesystem/include/filesystem/vfs.hpp | 2 -- kernel/filesystem/src/file_descriptor_table.cpp | 2 +- kernel/filesystem/src/filesystem.cpp | 2 +- 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/kernel/filesystem/include/filesystem/file_descriptor_table.hpp b/kernel/filesystem/include/filesystem/file_descriptor_table.hpp index 44fd428..3ac03d1 100644 --- a/kernel/filesystem/include/filesystem/file_descriptor_table.hpp +++ b/kernel/filesystem/include/filesystem/file_descriptor_table.hpp @@ -16,7 +16,7 @@ namespace filesystem ~file_descriptor_table() = default; auto add_file(open_file_description & f) -> int; - auto get_file(int fd) -> std::optional; + [[nodiscard]] auto get_file(int fd) const -> std::optional; auto remove_file(int fd) -> void; private: diff --git a/kernel/filesystem/include/filesystem/filesystem.hpp b/kernel/filesystem/include/filesystem/filesystem.hpp index 113b239..a5a1047 100644 --- a/kernel/filesystem/include/filesystem/filesystem.hpp +++ b/kernel/filesystem/include/filesystem/filesystem.hpp @@ -12,7 +12,7 @@ namespace filesystem virtual auto mount(devices::device * device) -> int = 0; - auto root_inode() -> inode *; + [[nodiscard]] auto root_inode() const -> inode *; protected: inode * m_root_inode{}; // TODO BA-FS26 set during mount? diff --git a/kernel/filesystem/include/filesystem/mount.hpp b/kernel/filesystem/include/filesystem/mount.hpp index 793c042..fe5d9cc 100644 --- a/kernel/filesystem/include/filesystem/mount.hpp +++ b/kernel/filesystem/include/filesystem/mount.hpp @@ -12,8 +12,8 @@ namespace filesystem mount() = default; // TODO BA-FS26 remove again when kstd::vector is available and used in vfs mount(std::string_view const & path, filesystem * fs); - auto path() const -> std::string_view; - auto get_filesystem() const -> filesystem *; + [[nodiscard]] auto path() const -> std::string_view; + [[nodiscard]] auto get_filesystem() const -> filesystem *; private: std::string_view m_path; diff --git a/kernel/filesystem/include/filesystem/vfs.hpp b/kernel/filesystem/include/filesystem/vfs.hpp index 67fd5df..182666d 100644 --- a/kernel/filesystem/include/filesystem/vfs.hpp +++ b/kernel/filesystem/include/filesystem/vfs.hpp @@ -6,7 +6,6 @@ #include #include -#include namespace filesystem { @@ -17,7 +16,6 @@ namespace filesystem ~vfs() = default; - // auto do_mount(std::string_view const & path) -> auto make_device_node(devices::device * device) -> void; private: diff --git a/kernel/filesystem/src/file_descriptor_table.cpp b/kernel/filesystem/src/file_descriptor_table.cpp index 49c08cd..eb9a281 100644 --- a/kernel/filesystem/src/file_descriptor_table.cpp +++ b/kernel/filesystem/src/file_descriptor_table.cpp @@ -47,7 +47,7 @@ namespace filesystem return -1; } - auto file_descriptor_table::get_file(int fd) -> std::optional + auto file_descriptor_table::get_file(int fd) const -> std::optional { if (fd < 0) { diff --git a/kernel/filesystem/src/filesystem.cpp b/kernel/filesystem/src/filesystem.cpp index d6a2f25..2c63766 100644 --- a/kernel/filesystem/src/filesystem.cpp +++ b/kernel/filesystem/src/filesystem.cpp @@ -4,7 +4,7 @@ namespace filesystem { - auto filesystem::root_inode() -> inode * + auto filesystem::root_inode() const -> inode * { return m_root_inode; } -- cgit v1.2.3 From 760752ef2045aaceb0393911a0919f9bc0104282 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 15 Mar 2026 20:37:14 +0100 Subject: implement first inode draft, fix make_device_node, implement first draft of resolve_path (currently with temp m_device_nodes in vfs -> has later to be deleted again, just for tests) --- .../filesystem/include/filesystem/filesystem.hpp | 4 +- kernel/filesystem/include/filesystem/inode.hpp | 15 +++++ kernel/filesystem/include/filesystem/vfs.hpp | 15 ++++- kernel/filesystem/src/ext2/ext2_filesystem.cpp | 4 ++ kernel/filesystem/src/filesystem.cpp | 2 +- kernel/filesystem/src/inode.cpp | 56 ++++++++++++++++ kernel/filesystem/src/vfs.cpp | 77 ++++++++++++++++++---- 7 files changed, 156 insertions(+), 17 deletions(-) diff --git a/kernel/filesystem/include/filesystem/filesystem.hpp b/kernel/filesystem/include/filesystem/filesystem.hpp index a5a1047..2077ed6 100644 --- a/kernel/filesystem/include/filesystem/filesystem.hpp +++ b/kernel/filesystem/include/filesystem/filesystem.hpp @@ -12,10 +12,10 @@ namespace filesystem virtual auto mount(devices::device * device) -> int = 0; - [[nodiscard]] auto root_inode() const -> inode *; + [[nodiscard]] auto root_inode() const -> inode const &; protected: - inode * m_root_inode{}; // TODO BA-FS26 set during mount? + inode m_root_inode{}; // TODO BA-FS26 set during mount? }; } // namespace filesystem diff --git a/kernel/filesystem/include/filesystem/inode.hpp b/kernel/filesystem/include/filesystem/inode.hpp index 44ec09a..eb8440f 100644 --- a/kernel/filesystem/include/filesystem/inode.hpp +++ b/kernel/filesystem/include/filesystem/inode.hpp @@ -1,13 +1,28 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP +#include "devices/device.hpp" + #include + namespace filesystem { struct inode { + inode() = default; + explicit inode(devices::device * device); + + [[nodiscard]] auto is_device() const -> bool; + [[nodiscard]] auto is_block_device() const -> bool; + [[nodiscard]] auto major_device() const -> size_t; + [[nodiscard]] auto minor_device() const -> size_t; + [[nodiscard]] auto backing_device() const -> devices::device *; + auto read(void * buffer, size_t offset, size_t size) const -> size_t; auto write(void const * buffer, size_t offset, size_t size) -> size_t; + + private: + devices::device * m_device{}; }; } // namespace filesystem diff --git a/kernel/filesystem/include/filesystem/vfs.hpp b/kernel/filesystem/include/filesystem/vfs.hpp index 182666d..a881241 100644 --- a/kernel/filesystem/include/filesystem/vfs.hpp +++ b/kernel/filesystem/include/filesystem/vfs.hpp @@ -2,10 +2,14 @@ #define TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP #include "devices/device.hpp" +#include "filesystem/custody.hpp" +#include "filesystem/inode.hpp" #include "filesystem/mount.hpp" +#include "filesystem/open_file_description.hpp" #include #include +#include namespace filesystem { @@ -16,13 +20,22 @@ namespace filesystem ~vfs() = default; - auto make_device_node(devices::device * device) -> void; + auto open(std::string_view path) -> std::optional; private: + struct device_node_entry + { + std::string_view name; + inode node; + }; + vfs() = default; + auto make_device_node(devices::device * device) -> void; + [[nodiscard]] auto resolve_path(std::string_view path) -> std::optional; std::optional m_root_mount; std::array m_mounts; // TODO BA-FS26 remove when kstd::vector is available and used + std::array, 10> m_device_nodes; // TODO BA-FS26 use kstd::vector }; } // namespace filesystem diff --git a/kernel/filesystem/src/ext2/ext2_filesystem.cpp b/kernel/filesystem/src/ext2/ext2_filesystem.cpp index 7111d13..97257a2 100644 --- a/kernel/filesystem/src/ext2/ext2_filesystem.cpp +++ b/kernel/filesystem/src/ext2/ext2_filesystem.cpp @@ -1,6 +1,7 @@ #include "filesystem/ext2/ext2_filesystem.hpp" #include "devices/device.hpp" +#include "filesystem/inode.hpp" namespace filesystem::ext2 { @@ -10,6 +11,9 @@ namespace filesystem::ext2 { return -1; // TODO BA-FS26 panic or errorcode? } + + m_root_inode = inode{}; // TODO BA-FS26 set properly during mount? + // TODO BA-FS26 implement return 0; } diff --git a/kernel/filesystem/src/filesystem.cpp b/kernel/filesystem/src/filesystem.cpp index 2c63766..cdfe7f8 100644 --- a/kernel/filesystem/src/filesystem.cpp +++ b/kernel/filesystem/src/filesystem.cpp @@ -4,7 +4,7 @@ namespace filesystem { - auto filesystem::root_inode() const -> inode * + auto filesystem::root_inode() const -> inode const & { return m_root_inode; } diff --git a/kernel/filesystem/src/inode.cpp b/kernel/filesystem/src/inode.cpp index 7bca507..a1427e2 100644 --- a/kernel/filesystem/src/inode.cpp +++ b/kernel/filesystem/src/inode.cpp @@ -2,18 +2,74 @@ #include "kapi/system.hpp" +#include "devices/device.hpp" + #include namespace filesystem { + inode::inode(devices::device * device) + : m_device(device) + { + if (!m_device) + { + kapi::system::panic("[FILESYSTEM] inode constructed with null device."); + } + } + + auto inode::is_device() const -> bool + { + return m_device != nullptr; + } + + auto inode::is_block_device() const -> bool + { + return is_device() && m_device->is_block_device(); + } + + auto inode::major_device() const -> size_t + { + if (!is_device()) + { + kapi::system::panic("[FILESYSTEM] inode::major_device called on non-device inode."); + } + + return m_device->major(); + } + + auto inode::minor_device() const -> size_t + { + if (!is_device()) + { + kapi::system::panic("[FILESYSTEM] inode::minor_device called on non-device inode."); + } + + return m_device->minor(); + } + + auto inode::backing_device() const -> devices::device * + { + return m_device; + } + auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t { + if (is_device()) + { + kapi::system::panic("[FILESYSTEM] inode::read called on device inode. Open it as a device file first."); + } + // TODO BA-FS26 return 0; } auto inode::write(void const *, size_t, size_t) -> size_t { + if (is_device()) + { + kapi::system::panic("[FILESYSTEM] inode::write called on device inode. Open it as a device file first."); + } + kapi::system::panic("[FILESYSTEM] inode::write is not implemented yet"); return 0; } diff --git a/kernel/filesystem/src/vfs.cpp b/kernel/filesystem/src/vfs.cpp index 573994a..2ecf847 100644 --- a/kernel/filesystem/src/vfs.cpp +++ b/kernel/filesystem/src/vfs.cpp @@ -4,13 +4,17 @@ #include "devices/device.hpp" #include "devices/storage/storage_management.hpp" +#include "filesystem/custody.hpp" +#include "filesystem/device_file.hpp" #include "filesystem/ext2/ext2_filesystem.hpp" +#include "filesystem/inode.hpp" +#include "filesystem/inode_file.hpp" #include "filesystem/mount.hpp" +#include "filesystem/open_file_description.hpp" #include #include -#include #include #include @@ -23,12 +27,11 @@ namespace filesystem // TODO BA-FS26 @Felix better solution? while dynamic memory not available? // TODO BA-FS26 remove when dynamic memory available; constinit auto static root_fs = std::optional{}; - // TODO BA-FS26 use kstd::vector when available - constinit auto static filesystems = std::array, 1>{}; - // TODO BA-FS26 @Felix - // TODO BA-FS26 depends on the length of ram_disk_device name - constinit std::array device_mount_path = {'/', 'd', 'e', 'v', '/', 'x', 'x', 'x', 'x', '\0'}; + // TODO BA-FS26 remove when dynamic memory available; + constinit auto static temp_device_file = std::optional{}; + // TODO BA-FS26 remove when dynamic memory available; + constinit auto static temp_inode_file = std::optional{}; } // namespace auto vfs::init() -> void @@ -71,19 +74,67 @@ namespace filesystem return *active_vfs; } + auto vfs::open(std::string_view path) -> std::optional + { + if (auto custody = resolve_path(path)) + { + auto * node = custody->get_inode(); + if (node->is_device()) + { + temp_device_file.emplace(node->backing_device()); + temp_device_file->open(); + return open_file_description{&*temp_device_file}; + } + + temp_inode_file.emplace(node); + temp_inode_file->open(); + return open_file_description{&*temp_inode_file}; + } + + return std::nullopt; + } + auto vfs::make_device_node(devices::device * device) -> void { - auto device_name = device->name(); - kstd::libc::memcpy(&device_mount_path[5], device_name.data(), device_name.size()); + if (!device) + { + kapi::system::panic("[FILESYSTEM] make_device_node called with null device."); + } + + auto const device_name = device->name(); + + // TODO BA-FS26 this logic isn't needed anymore when kstd::vector available, just use push_back + auto const slot = std::ranges::find_if(m_device_nodes, [](auto const & entry) { return !entry.has_value(); }); + if (slot == m_device_nodes.end()) + { + kapi::system::panic("[FILESYSTEM] No free slot available for device nodes."); + } + + slot->emplace(device_node_entry{device_name, inode{device}}); + } + + auto vfs::resolve_path(std::string_view path) -> std::optional + { + // TODO BA-FS26 implement real path resolution with mounts and directories etc. + // For now, just support device nodes at /dev/. - // TODO BA-FS26 use kstd::vector and push_back when available - filesystems[0].emplace(ext2::ext2_filesystem{}); - if (filesystems[0]->mount(device) != 0) + constexpr auto device_prefix = std::string_view{"/dev/"}; + if (path.starts_with(device_prefix)) { - kapi::system::panic("[FILESYSTEM] Failed to mount device filesystem."); + auto const device_name = path.substr(device_prefix.size()); + auto entry = std::ranges::find_if(m_device_nodes, [&](auto const & device_entry) { + return device_entry.has_value() && device_entry->name == device_name; + }); + + if (entry != m_device_nodes.end()) + { + return custody{nullptr, &entry->value().node}; + } + + return std::nullopt; } - m_mounts[0] = mount{std::string_view{device_mount_path.data()}, &*filesystems[0]}; + return std::nullopt; } } // namespace filesystem \ No newline at end of file -- cgit v1.2.3 From 62005f563e3c29b079c69380905a82fd0d91c975 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 16 Mar 2026 18:27:59 +0100 Subject: add first draft of inode metadata --- kernel/filesystem/CMakeLists.txt | 1 + .../include/filesystem/ext2/ext2_filesystem.hpp | 4 +++ .../filesystem/include/filesystem/filesystem.hpp | 6 +++- kernel/filesystem/include/filesystem/inode.hpp | 7 +++++ .../include/filesystem/inode_metadata.hpp | 24 +++++++++++++++ kernel/filesystem/src/ext2/ext2_filesystem.cpp | 14 +++++++-- kernel/filesystem/src/filesystem.cpp | 9 ++++-- kernel/filesystem/src/inode.cpp | 34 ++++++++++++++++++++-- 8 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 kernel/filesystem/include/filesystem/inode_metadata.hpp diff --git a/kernel/filesystem/CMakeLists.txt b/kernel/filesystem/CMakeLists.txt index 19f1be1..bb2090a 100644 --- a/kernel/filesystem/CMakeLists.txt +++ b/kernel/filesystem/CMakeLists.txt @@ -25,6 +25,7 @@ target_sources("kernel_filesystem" PUBLIC "include/filesystem/file.hpp" "include/filesystem/filesystem.hpp" "include/filesystem/inode_file.hpp" + "include/filesystem/inode_metadata.hpp" "include/filesystem/inode.hpp" "include/filesystem/mount.hpp" "include/filesystem/open_file_description.hpp" diff --git a/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp b/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp index 91ba14a..cba2192 100644 --- a/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp +++ b/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp @@ -3,12 +3,16 @@ #include "devices/device.hpp" #include "filesystem/filesystem.hpp" +#include "filesystem/inode.hpp" + +#include namespace filesystem::ext2 { struct ext2_filesystem : filesystem { auto mount(devices::device * device) -> int override; + auto lookup(inode const & parent, std::string_view name) -> inode * override; private: devices::device * m_device{}; diff --git a/kernel/filesystem/include/filesystem/filesystem.hpp b/kernel/filesystem/include/filesystem/filesystem.hpp index 2077ed6..8deb336 100644 --- a/kernel/filesystem/include/filesystem/filesystem.hpp +++ b/kernel/filesystem/include/filesystem/filesystem.hpp @@ -4,6 +4,8 @@ #include "devices/device.hpp" #include "filesystem/inode.hpp" +#include + namespace filesystem { struct filesystem @@ -11,8 +13,10 @@ namespace filesystem virtual ~filesystem() = default; virtual auto mount(devices::device * device) -> int = 0; + virtual auto lookup(inode const & parent, std::string_view name) -> inode * = 0; - [[nodiscard]] auto root_inode() const -> inode const &; + [[nodiscard]] auto root_inode() -> inode *; + [[nodiscard]] auto root_inode() const -> inode const *; protected: inode m_root_inode{}; // TODO BA-FS26 set during mount? diff --git a/kernel/filesystem/include/filesystem/inode.hpp b/kernel/filesystem/include/filesystem/inode.hpp index eb8440f..bceabb6 100644 --- a/kernel/filesystem/include/filesystem/inode.hpp +++ b/kernel/filesystem/include/filesystem/inode.hpp @@ -2,6 +2,7 @@ #define TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP #include "devices/device.hpp" +#include "filesystem/inode_metadata.hpp" #include @@ -10,8 +11,13 @@ namespace filesystem struct inode { inode() = default; + explicit inode(inode_kind kind); explicit inode(devices::device * device); + [[nodiscard]] auto metadata() const -> inode_metadata; + + [[nodiscard]] auto is_directory() const -> bool; + [[nodiscard]] auto is_regular() const -> bool; [[nodiscard]] auto is_device() const -> bool; [[nodiscard]] auto is_block_device() const -> bool; [[nodiscard]] auto major_device() const -> size_t; @@ -22,6 +28,7 @@ namespace filesystem auto write(void const * buffer, size_t offset, size_t size) -> size_t; private: + inode_kind m_kind{inode_kind::regular}; devices::device * m_device{}; }; } // namespace filesystem diff --git a/kernel/filesystem/include/filesystem/inode_metadata.hpp b/kernel/filesystem/include/filesystem/inode_metadata.hpp new file mode 100644 index 0000000..ed5a09d --- /dev/null +++ b/kernel/filesystem/include/filesystem/inode_metadata.hpp @@ -0,0 +1,24 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP + +#include +#include + +namespace filesystem +{ + enum class inode_kind + { + regular, + directory, + device + }; + + struct inode_metadata + { + inode_kind kind{inode_kind::regular}; + std::optional major{}; + std::optional minor{}; + }; +} // namespace filesystem + +#endif // TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP \ No newline at end of file diff --git a/kernel/filesystem/src/ext2/ext2_filesystem.cpp b/kernel/filesystem/src/ext2/ext2_filesystem.cpp index 97257a2..f092ddf 100644 --- a/kernel/filesystem/src/ext2/ext2_filesystem.cpp +++ b/kernel/filesystem/src/ext2/ext2_filesystem.cpp @@ -3,6 +3,8 @@ #include "devices/device.hpp" #include "filesystem/inode.hpp" +#include + namespace filesystem::ext2 { auto ext2_filesystem::mount(devices::device * device) -> int @@ -12,9 +14,17 @@ namespace filesystem::ext2 return -1; // TODO BA-FS26 panic or errorcode? } - m_root_inode = inode{}; // TODO BA-FS26 set properly during mount? + m_device = device; + // TODO BA-FS26 load proper root inode from ext2 metadata + m_root_inode = inode{inode_kind::directory}; // TODO BA-FS26 implement return 0; } -} // namespace filesystem::ext2 \ No newline at end of file + + auto ext2_filesystem::lookup(inode const & /*parent*/, std::string_view /*name*/) -> inode * + { + // TODO BA-FS26 implement ext2 directory traversal and inode loading + return nullptr; + } +} // namespace filesystem::ext2 diff --git a/kernel/filesystem/src/filesystem.cpp b/kernel/filesystem/src/filesystem.cpp index cdfe7f8..a06bccc 100644 --- a/kernel/filesystem/src/filesystem.cpp +++ b/kernel/filesystem/src/filesystem.cpp @@ -4,8 +4,13 @@ namespace filesystem { - auto filesystem::root_inode() const -> inode const & + auto filesystem::root_inode() -> inode * { - return m_root_inode; + return &m_root_inode; + } + + auto filesystem::root_inode() const -> inode const * + { + return &m_root_inode; } } // namespace filesystem \ No newline at end of file diff --git a/kernel/filesystem/src/inode.cpp b/kernel/filesystem/src/inode.cpp index a1427e2..17aa52a 100644 --- a/kernel/filesystem/src/inode.cpp +++ b/kernel/filesystem/src/inode.cpp @@ -3,13 +3,19 @@ #include "kapi/system.hpp" #include "devices/device.hpp" +#include "filesystem/inode_metadata.hpp" #include namespace filesystem { + inode::inode(inode_kind kind) + : m_kind(kind) + {} + inode::inode(devices::device * device) - : m_device(device) + : m_kind(inode_kind::device) + , m_device(device) { if (!m_device) { @@ -17,9 +23,33 @@ namespace filesystem } } + auto inode::metadata() const -> inode_metadata + { + auto meta = inode_metadata{}; + meta.kind = m_kind; + + if (is_device()) + { + meta.major = m_device->major(); + meta.minor = m_device->minor(); + } + + return meta; + } + + auto inode::is_directory() const -> bool + { + return m_kind == inode_kind::directory; + } + + auto inode::is_regular() const -> bool + { + return m_kind == inode_kind::regular; + } + auto inode::is_device() const -> bool { - return m_device != nullptr; + return m_kind == inode_kind::device; } auto inode::is_block_device() const -> bool -- cgit v1.2.3 From b0e2fec58c93c8e993f0ecba0dc4862bde760b1b Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 16 Mar 2026 18:53:50 +0100 Subject: remove comment --- kernel/devices/src/storage/ram_disk/ram_disk_device.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp b/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp index d05ae7a..774e949 100644 --- a/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp +++ b/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp @@ -19,7 +19,6 @@ namespace devices::storage::ram_disk // TODO BA-FS26 @Felix // TODO BA-FS26 currently only names for 9 minor devices - // TODO BA-FS26 when this length is changed, also change the one in vfs.cpp constinit std::array name = {'r', 'a', 'm', '0', '\0'}; auto determine_device_name(size_t minor) -> std::string_view -- cgit v1.2.3 From bcedba978671d4947c63bdcd044e10fae9a7be66 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 16 Mar 2026 19:00:00 +0100 Subject: add todo --- kernel/filesystem/include/filesystem/vfs.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/filesystem/include/filesystem/vfs.hpp b/kernel/filesystem/include/filesystem/vfs.hpp index a881241..1d95d4a 100644 --- a/kernel/filesystem/include/filesystem/vfs.hpp +++ b/kernel/filesystem/include/filesystem/vfs.hpp @@ -35,7 +35,8 @@ namespace filesystem std::optional m_root_mount; std::array m_mounts; // TODO BA-FS26 remove when kstd::vector is available and used - std::array, 10> m_device_nodes; // TODO BA-FS26 use kstd::vector + std::array, 10> + m_device_nodes; // TODO BA-FS26 use kstd::vector // TODO BA-FS26 remove again, use }; } // namespace filesystem -- cgit v1.2.3 From 3733baef9603581d6de2d35fda4535d37b6826b0 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 16 Mar 2026 19:51:53 +0100 Subject: protect multiboot2 boot modules --- arch/x86_64/kapi/memory.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp index d6c0ad5..d9fbe28 100644 --- a/arch/x86_64/kapi/memory.cpp +++ b/arch/x86_64/kapi/memory.cpp @@ -174,9 +174,19 @@ namespace kapi::memory auto mbi_start = frame::containing(mbi_address); auto mbi_end = frame::containing(mbi_address + mbi_size) + 1; - // TODO BA-FS26: Protect MB2 boot modules - std::ranges::for_each(std::views::iota(mbi_start, mbi_end), [&](auto frame) { new_allocator.mark_used(frame); }); + + auto modules = boot::bootstrap_information.mbi->modules(); + for (auto module : modules) + { + auto module_physical_start = physical_address{module.start_address}; + auto module_size = module.end_address - module.start_address; + auto module_start = frame::containing(module_physical_start); + auto module_end = frame::containing(module_physical_start + module_size) + 1; + + std::ranges::for_each(std::views::iota(module_start, module_end), + [&](auto frame) { new_allocator.mark_used(frame); }); + } } } // namespace -- cgit v1.2.3 From 292c5ff7c0c0ae89cfed7124c3ad931b9f555d19 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 16 Mar 2026 19:58:15 +0100 Subject: refactoring --- arch/x86_64/kapi/memory.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp index d9fbe28..f29afe8 100644 --- a/arch/x86_64/kapi/memory.cpp +++ b/arch/x86_64/kapi/memory.cpp @@ -121,10 +121,7 @@ namespace kapi::memory [[maybe_unused]] auto remap_bootloader_modules(page_mapper & mapper) -> void { - auto modules = boot::bootstrap_information.mbi->modules(); - - for (auto module : modules) - { + std::ranges::for_each(boot::bootstrap_information.mbi->modules(), [&mapper](auto const & module) { auto module_physical_start = physical_address{module.start_address}; auto module_virtual_start = linear_address{module.start_address + std::bit_cast(&arch::boot::TEACHOS_VMA)}; @@ -135,9 +132,9 @@ namespace kapi::memory { auto page = page::containing(module_virtual_start) + i; auto frame = frame::containing(module_physical_start) + i; - mapper.map(page, frame, page_mapper::flags::writable); + mapper.map(page, frame, page_mapper::flags::writable | page_mapper::flags::supervisor_only); } - } + }); } [[maybe_unused]] auto handoff_to_kernel_pmm(frame_allocator & new_allocator) -> void @@ -176,9 +173,7 @@ namespace kapi::memory std::ranges::for_each(std::views::iota(mbi_start, mbi_end), [&](auto frame) { new_allocator.mark_used(frame); }); - auto modules = boot::bootstrap_information.mbi->modules(); - for (auto module : modules) - { + std::ranges::for_each(boot::bootstrap_information.mbi->modules(), [&](auto const & module) { auto module_physical_start = physical_address{module.start_address}; auto module_size = module.end_address - module.start_address; auto module_start = frame::containing(module_physical_start); @@ -186,7 +181,7 @@ namespace kapi::memory std::ranges::for_each(std::views::iota(module_start, module_end), [&](auto frame) { new_allocator.mark_used(frame); }); - } + }); } } // namespace -- cgit v1.2.3 From a20045fb209edc1e338039c28634c942e3113ea4 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 16 Mar 2026 21:06:12 +0100 Subject: Protect boot modules in region_allocator --- .../include/arch/memory/region_allocator.hpp | 6 +++ arch/x86_64/kapi/memory.cpp | 1 + arch/x86_64/src/memory/region_allocator.cpp | 43 ++++++++++++++++------ 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/arch/x86_64/include/arch/memory/region_allocator.hpp b/arch/x86_64/include/arch/memory/region_allocator.hpp index 1c5885e..c7a836f 100644 --- a/arch/x86_64/include/arch/memory/region_allocator.hpp +++ b/arch/x86_64/include/arch/memory/region_allocator.hpp @@ -42,6 +42,11 @@ namespace arch::memory //! These include available, unavailable, and reclaimable regions. In general, only frames that are located in //! non-reserved (as in available) regions should be allocated for page storage. multiboot2::memory_map memory_map; + + //! The loader supplied Multiboot2 information structure. + //! + //! This is used to query boot module ranges so these frames can be excluded from early allocations. + multiboot2::information_view const * mbi; }; using region = multiboot2::memory_map::region; @@ -80,6 +85,7 @@ namespace arch::memory kapi::memory::frame m_kernel_end; //!< The end of the kernel image in physical memory. kapi::memory::frame m_multiboot_start; //!< The start of the Multiboot2 information in physical memory. kapi::memory::frame m_multiboot_end; //!< The end of the Multiboot2 information in physical memory. + multiboot2::information_view const * m_multiboot_information; //!< Source of Multiboot2 module ranges. }; } // namespace arch::memory diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp index f29afe8..a354576 100644 --- a/arch/x86_64/kapi/memory.cpp +++ b/arch/x86_64/kapi/memory.cpp @@ -53,6 +53,7 @@ namespace kapi::memory .image_range = std::make_pair(physical_address{&image_span.front()}, physical_address{&image_span.back()}), .mbi_range = std::make_pair(physical_address{&mbi_span.front()}, physical_address{&mbi_span.back()}), .memory_map = *memory_map, + .mbi = mbi, }; } diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp index facb1f2..2690a7c 100644 --- a/arch/x86_64/src/memory/region_allocator.cpp +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -35,7 +35,7 @@ namespace arch::memory , m_kernel_end{kapi::memory::frame::containing(mem_info.image_range.second)} , m_multiboot_start{kapi::memory::frame::containing(mem_info.mbi_range.first)} , m_multiboot_end{kapi::memory::frame::containing(mem_info.mbi_range.second)} - // TODO BA-FS26: Protect MB2 boot modules + , m_multiboot_information{mem_info.mbi} { choose_next_region(); } @@ -76,19 +76,40 @@ namespace arch::memory } } - if (falls_within(m_next_frame, m_kernel_start, m_kernel_end)) + auto advanced = true; + while (advanced) { - m_next_frame = m_kernel_end + 1; - } + advanced = false; - if (falls_within(m_next_frame, m_multiboot_start, m_multiboot_end)) - { - m_next_frame = m_multiboot_end + 1; - } + if (falls_within(m_next_frame, m_kernel_start, m_kernel_end)) + { + m_next_frame = m_kernel_end + 1; + advanced = true; + break; + } - if (falls_within(m_next_frame, m_kernel_start, m_kernel_end)) - { - m_next_frame = m_kernel_end + 1; + if (falls_within(m_next_frame, m_multiboot_start, m_multiboot_end)) + { + m_next_frame = m_multiboot_end + 1; + advanced = true; + break; + } + + if (m_multiboot_information) + { + for (auto const & module : m_multiboot_information->modules()) + { + auto module_start = kapi::memory::frame::containing(kapi::memory::physical_address{module.start_address}); + auto module_end = kapi::memory::frame::containing(kapi::memory::physical_address{module.end_address}); + + if (falls_within(m_next_frame, module_start, module_end)) + { + m_next_frame = module_end + 1; + advanced = true; + break; + } + } + } } if (m_next_frame > last_frame(*m_current_region)) -- cgit v1.2.3 From 9391c5881a8a635677312df80888e972d7175cfa Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 17 Mar 2026 09:13:43 +0100 Subject: fix lint issues --- kernel/devices/include/devices/block_device.hpp | 10 +++++----- kernel/devices/include/devices/device.hpp | 12 ++++++------ .../include/devices/storage/ram_disk/ram_disk_device.hpp | 2 +- .../devices/include/devices/storage/storage_controller.hpp | 8 ++++---- .../devices/include/devices/storage/storage_management.hpp | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/kernel/devices/include/devices/block_device.hpp b/kernel/devices/include/devices/block_device.hpp index a2c849f..4831be5 100644 --- a/kernel/devices/include/devices/block_device.hpp +++ b/kernel/devices/include/devices/block_device.hpp @@ -52,20 +52,20 @@ namespace devices * @brief Return logical block size in bytes. * @return One logical block size in bytes. */ - auto block_size() const -> size_t; + [[nodiscard]] auto block_size() const -> size_t; /** * @brief Return device capacity in bytes. * @return Total number of addressable bytes. */ - auto capacity() const -> size_t; + [[nodiscard]] auto capacity() const -> size_t; /** * @brief Override to identify block devices. * @return true if this device is a block device, false otherwise. */ - auto is_block_device() const -> bool override + [[nodiscard]] auto is_block_device() const -> bool override { return true; } @@ -85,14 +85,14 @@ namespace devices * @brief Return total device size in bytes. * @return Total number of addressable bytes. */ - virtual auto size() const -> size_t = 0; + [[nodiscard]] virtual auto size() const -> size_t = 0; /** * @brief Compute transfer information for @p block_index. * @param block_index Zero-based block index. * @return Computed transfer information for one logical block access. */ - auto calculate_transfer(size_t block_index) const -> transfer_info; + [[nodiscard]] auto calculate_transfer(size_t block_index) const -> transfer_info; size_t m_block_size; }; diff --git a/kernel/devices/include/devices/device.hpp b/kernel/devices/include/devices/device.hpp index d1c202d..d6f520f 100644 --- a/kernel/devices/include/devices/device.hpp +++ b/kernel/devices/include/devices/device.hpp @@ -7,12 +7,12 @@ namespace devices { /** - * @brief Base device identified by a major and minor number. + * @brief Base device identified by a major, minor number and name. */ struct device { /** - * @brief Create a device identifier from @p major and @p minor. + * @brief Create a device identifier from @p major, @p minor and @p name. * @param major Device major number. * @param minor Device minor number. * @param name Device name. @@ -28,25 +28,25 @@ namespace devices * @brief Returns the major number of the device. * @return Device major number. */ - auto major() const -> size_t; + [[nodiscard]] auto major() const -> size_t; /** * @brief Returns the minor number of the device. * @return Device minor number. */ - auto minor() const -> size_t; + [[nodiscard]] auto minor() const -> size_t; /** * @brief Returns the name of the device. * @return Device name. */ - auto name() const -> std::string_view; + [[nodiscard]] auto name() const -> std::string_view; /** * @brief Check if the device is a block device. * @return true if this device is a block device, false otherwise. */ - virtual auto is_block_device() const -> bool + [[nodiscard]] virtual auto is_block_device() const -> bool { return false; } diff --git a/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp b/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp index eebbcd7..678ee99 100644 --- a/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp +++ b/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp @@ -45,7 +45,7 @@ namespace devices::storage::ram_disk /** * @brief Return module size in bytes. */ - auto size() const -> size_t override; + [[nodiscard]] auto size() const -> size_t override; kapi::boot_modules::boot_module m_boot_module{}; }; diff --git a/kernel/devices/include/devices/storage/storage_controller.hpp b/kernel/devices/include/devices/storage/storage_controller.hpp index 0518e32..d10d27d 100644 --- a/kernel/devices/include/devices/storage/storage_controller.hpp +++ b/kernel/devices/include/devices/storage/storage_controller.hpp @@ -37,17 +37,17 @@ namespace devices::storage * @brief Return the assigned major number. * @return Assigned major number. */ - auto major() const -> size_t; + [[nodiscard]] auto major() const -> size_t; /** * @brief Return the number of devices managed by this controller. * @return Number of managed devices. */ - auto devices_count() const -> size_t; + [[nodiscard]] auto devices_count() const -> size_t; // TODO BA-FS26 add comment // TODO BA-FS26 use kstd::vector when available - auto all_devices() const -> std::array const &; + [[nodiscard]] auto all_devices() const -> std::array const &; /** * @brief Find a managed device by major/minor numbers. @@ -55,7 +55,7 @@ namespace devices::storage * @param minor Device minor number. * @return Matching block device, or nullptr if no device matches. */ - auto device_by_minor(size_t minor) const -> device *; + [[nodiscard]] auto device_by_minor(size_t minor) const -> device *; protected: size_t m_major{}; diff --git a/kernel/devices/include/devices/storage/storage_management.hpp b/kernel/devices/include/devices/storage/storage_management.hpp index 4bc7cbd..550db65 100644 --- a/kernel/devices/include/devices/storage/storage_management.hpp +++ b/kernel/devices/include/devices/storage/storage_management.hpp @@ -44,7 +44,7 @@ namespace devices::storage // TODO BA-FS26 add comment // TODO BA-FS26 use kstd::vector when available - auto all_controllers() const -> std::array const &; + [[nodiscard]] auto all_controllers() const -> std::array const &; /** * @brief Find a device by major/minor numbers. -- cgit v1.2.3 From 471888c64ed490b1f1dbaa2c2f67a1e8d315905a Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 17 Mar 2026 11:48:40 +0100 Subject: extend shared_ptr to support nullptr and cross-type conversions --- libs/kstd/include/kstd/bits/shared_ptr.hpp | 131 +++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 6 deletions(-) diff --git a/libs/kstd/include/kstd/bits/shared_ptr.hpp b/libs/kstd/include/kstd/bits/shared_ptr.hpp index cfe5d18..ed23d29 100644 --- a/libs/kstd/include/kstd/bits/shared_ptr.hpp +++ b/libs/kstd/include/kstd/bits/shared_ptr.hpp @@ -3,12 +3,16 @@ #include #include +#include #include // IWYU pragma: private, include namespace kstd { + template + struct shared_ptr; + /** * @brief Shared_pointer is a smart pointer that retains shared ownership of an object through a pointer. Several * shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of @@ -24,6 +28,17 @@ namespace kstd template struct shared_ptr { + template + friend struct shared_ptr; + + /** + * @brief Construct an empty shared_ptr from nullptr. + */ + shared_ptr(std::nullptr_t) noexcept + : pointer(nullptr) + , ref_count(nullptr) + {} + /** * @brief Constructor. * @@ -31,7 +46,7 @@ namespace kstd */ explicit shared_ptr(T * pointer = nullptr) : pointer(pointer) - , ref_count(new std::atomic(pointer != nullptr ? 1 : 0)) + , ref_count(pointer != nullptr ? new std::atomic(1) : nullptr) { // Nothing to do. } @@ -45,7 +60,25 @@ namespace kstd : pointer(other.pointer) , ref_count(other.ref_count) { - if (pointer != nullptr) + if (ref_count != nullptr) + { + ++(*ref_count); + } + } + + /** + * @brief Converting copy constructor for compatible shared_ptr types. + * + * @tparam U Source pointer element type. + * @param other The shared_ptr to copy from. + */ + template + requires(std::is_convertible_v) + shared_ptr(shared_ptr const & other) + : pointer(other.pointer) + , ref_count(other.ref_count) + { + if (ref_count != nullptr) { ++(*ref_count); } @@ -64,6 +97,22 @@ namespace kstd other.ref_count = nullptr; } + /** + * @brief Converting move constructor for compatible shared_ptr types. + * + * @tparam U Source pointer element type. + * @param other The shared_ptr to move from. + */ + template + requires(std::is_convertible_v) + shared_ptr(shared_ptr && other) noexcept + : pointer(other.pointer) + , ref_count(other.ref_count) + { + other.pointer = nullptr; + other.ref_count = nullptr; + } + /** * @brief Copy assignment operator. Replaces the managed object with the one managed by r. Shares ownership of the * object managed by r. If r manages no object, *this manages no object too. Equivalent to @@ -80,7 +129,7 @@ namespace kstd pointer = other.pointer; ref_count = other.ref_count; - if (pointer != nullptr) + if (ref_count != nullptr) { ++(*ref_count); } @@ -89,6 +138,29 @@ namespace kstd return *this; } + /** + * @brief Converting copy assignment for compatible shared_ptr types. + * + * @tparam U Source pointer element type. + * @param other Another smart pointer to share ownership with. + * @return Reference to this shared pointer. + */ + template + requires(std::is_convertible_v) + auto operator=(shared_ptr const & other) -> shared_ptr & + { + cleanup(); + pointer = other.pointer; + ref_count = other.ref_count; + + if (ref_count != nullptr) + { + ++(*ref_count); + } + + return *this; + } + /** * @brief Move assignment operator. Move-assigns a shared_ptr from r. After the assignment, *this contains a copy of * the previous state of r, and r is empty. Equivalent to shared_ptr(std::move(r)).swap(*this). @@ -110,6 +182,37 @@ namespace kstd return *this; } + /** + * @brief Converting move assignment for compatible shared_ptr types. + * + * @tparam U Source pointer element type. + * @param other Another smart pointer to acquire ownership from. + * @return Reference to this shared pointer. + */ + template + requires(std::is_convertible_v) + auto operator=(shared_ptr && other) noexcept -> shared_ptr & + { + cleanup(); + pointer = other.pointer; + ref_count = other.ref_count; + other.pointer = nullptr; + other.ref_count = nullptr; + + return *this; + } + + /** + * @brief Reset this shared_ptr to empty via nullptr assignment. + */ + auto operator=(std::nullptr_t) noexcept -> shared_ptr & + { + cleanup(); + pointer = nullptr; + ref_count = nullptr; + return *this; + } + /** * @brief Destructor. Cleans up resources if necessary. */ @@ -127,7 +230,7 @@ namespace kstd { cleanup(); pointer = ptr; - ref_count = new std::atomic(ptr != nullptr ? 1 : 0); + ref_count = ptr != nullptr ? new std::atomic(1) : nullptr; } /** @@ -185,7 +288,7 @@ namespace kstd */ [[nodiscard]] auto use_count() const -> std::size_t { - if (pointer != nullptr) + if (ref_count != nullptr) { return *ref_count; } @@ -203,6 +306,22 @@ namespace kstd return pointer != nullptr; } + /** + * @brief Compare shared_ptr with nullptr. + */ + [[nodiscard]] auto operator==(std::nullptr_t) const -> bool + { + return pointer == nullptr; + } + + /** + * @brief Compare nullptr with shared_ptr. + */ + friend auto operator==(std::nullptr_t, shared_ptr const & ptr) -> bool + { + return ptr.pointer == nullptr; + } + /** * @brief Defaulted three-way comparator operator. */ @@ -214,7 +333,7 @@ namespace kstd */ auto cleanup() -> void { - if (pointer != nullptr && ref_count != nullptr && --(*ref_count) == 0) + if (ref_count != nullptr && --(*ref_count) == 0) { delete pointer; delete ref_count; -- cgit v1.2.3 From 5801be615a50bf465a9663b7f75cafbcf0870f5c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 17 Mar 2026 11:49:13 +0100 Subject: use kstd::vector instead of std::array and replace plain-pointers with kstd::shared_ptr --- .../kapi/boot_module/boot_module_registry.hpp | 13 +++---- .../devices/storage/ram_disk/ram_disk_device.hpp | 2 - .../include/devices/storage/storage_controller.hpp | 11 +++--- .../include/devices/storage/storage_management.hpp | 15 ++++---- .../src/storage/ram_disk/ram_disk_controller.cpp | 15 +------- .../src/storage/ram_disk/ram_disk_device.cpp | 4 -- kernel/devices/src/storage/storage_controller.cpp | 8 ++-- kernel/devices/src/storage/storage_management.cpp | 25 ++++++------ kernel/filesystem/include/filesystem/custody.hpp | 12 +++--- .../filesystem/include/filesystem/device_file.hpp | 6 ++- .../include/filesystem/ext2/ext2_filesystem.hpp | 6 ++- .../include/filesystem/file_descriptor_table.hpp | 6 +-- .../filesystem/include/filesystem/filesystem.hpp | 7 ++-- kernel/filesystem/include/filesystem/inode.hpp | 8 ++-- .../filesystem/include/filesystem/inode_file.hpp | 6 ++- kernel/filesystem/include/filesystem/mount.hpp | 9 +++-- .../include/filesystem/open_file_description.hpp | 5 ++- kernel/filesystem/include/filesystem/vfs.hpp | 15 +++++--- kernel/filesystem/src/custody.cpp | 8 ++-- kernel/filesystem/src/device_file.cpp | 16 +++++--- kernel/filesystem/src/ext2/ext2_filesystem.cpp | 5 ++- kernel/filesystem/src/file_descriptor_table.cpp | 9 +++-- kernel/filesystem/src/filesystem.cpp | 9 +---- kernel/filesystem/src/inode.cpp | 6 ++- kernel/filesystem/src/inode_file.cpp | 4 +- kernel/filesystem/src/mount.cpp | 6 ++- kernel/filesystem/src/open_file_description.cpp | 14 +++++-- kernel/filesystem/src/vfs.cpp | 45 +++++++--------------- kernel/src/main.cpp | 19 ++++----- 29 files changed, 160 insertions(+), 154 deletions(-) diff --git a/kapi/include/kapi/boot_module/boot_module_registry.hpp b/kapi/include/kapi/boot_module/boot_module_registry.hpp index eeb01ff..70b5592 100644 --- a/kapi/include/kapi/boot_module/boot_module_registry.hpp +++ b/kapi/include/kapi/boot_module/boot_module_registry.hpp @@ -3,7 +3,8 @@ #include "kapi/boot_module/boot_module.hpp" -#include +#include + #include namespace kapi::boot_modules @@ -15,15 +16,13 @@ namespace kapi::boot_modules // ! providing access to them for the rest of the kernel. struct boot_module_registry { - using range_type = std::array; // TODO BA-FS26 use kstd::vector when available - + using range_type = kstd::vector; using value_type = range_type::value_type; using const_reference = range_type::const_reference; - using const_iterator = range_type::const_iterator; - using const_reverse_iterator = range_type::const_reverse_iterator; + using const_iterator = range_type::const_pointer; + using const_reverse_iterator = range_type::const_pointer; using size_type = range_type::size_type; - using difference_type = range_type::difference_type; [[nodiscard]] auto begin() const noexcept -> const_iterator { @@ -97,7 +96,7 @@ namespace kapi::boot_modules auto add_boot_module(boot_module module) -> void { - m_modules.at(0) = module; // TODO BA-FS26 push back when kstd::vector is available + m_modules.push_back(module); } private: diff --git a/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp b/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp index 678ee99..1edf48c 100644 --- a/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp +++ b/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp @@ -14,8 +14,6 @@ namespace devices::storage::ram_disk */ struct ram_disk_device : block_device { - ram_disk_device(); // TODO BA-FS26 remove when kstd::vector is available - /** * @brief Create a RAM disk for the @p module. * @param module Boot module providing the memory region. diff --git a/kernel/devices/include/devices/storage/storage_controller.hpp b/kernel/devices/include/devices/storage/storage_controller.hpp index d10d27d..e90b01c 100644 --- a/kernel/devices/include/devices/storage/storage_controller.hpp +++ b/kernel/devices/include/devices/storage/storage_controller.hpp @@ -3,7 +3,9 @@ #include "devices/device.hpp" -#include +#include +#include + #include namespace devices::storage @@ -46,8 +48,7 @@ namespace devices::storage [[nodiscard]] auto devices_count() const -> size_t; // TODO BA-FS26 add comment - // TODO BA-FS26 use kstd::vector when available - [[nodiscard]] auto all_devices() const -> std::array const &; + [[nodiscard]] auto all_devices() const -> kstd::vector> const &; /** * @brief Find a managed device by major/minor numbers. @@ -55,12 +56,12 @@ namespace devices::storage * @param minor Device minor number. * @return Matching block device, or nullptr if no device matches. */ - [[nodiscard]] auto device_by_minor(size_t minor) const -> device *; + [[nodiscard]] auto device_by_minor(size_t minor) const -> kstd::shared_ptr; protected: size_t m_major{}; size_t m_minors_per_device{}; - std::array m_devices{}; // TODO BA-FS26 use kstd::vector when available + kstd::vector> m_devices{}; }; } // namespace devices::storage diff --git a/kernel/devices/include/devices/storage/storage_management.hpp b/kernel/devices/include/devices/storage/storage_management.hpp index 550db65..dc2f6c9 100644 --- a/kernel/devices/include/devices/storage/storage_management.hpp +++ b/kernel/devices/include/devices/storage/storage_management.hpp @@ -4,7 +4,9 @@ #include "devices/device.hpp" #include "devices/storage/storage_controller.hpp" -#include +#include +#include + #include namespace devices::storage @@ -40,11 +42,10 @@ namespace devices::storage * * Assigns controller IDs (major number range and minors per device). */ - auto add_controller(storage_controller * controller) -> void; + auto add_controller(kstd::shared_ptr controller) -> void; // TODO BA-FS26 add comment - // TODO BA-FS26 use kstd::vector when available - [[nodiscard]] auto all_controllers() const -> std::array const &; + [[nodiscard]] auto all_controllers() const -> kstd::vector> const &; /** * @brief Find a device by major/minor numbers. @@ -52,13 +53,13 @@ namespace devices::storage * @param minor Device minor number. * @return Matching device, or nullptr if no device matches. */ - auto device_by_major_minor(size_t major, size_t minor) -> device *; + auto device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr; /** * @brief Determine the boot device. * @return Boot device, or nullptr if it cannot be determined. */ - auto determine_boot_device() -> device *; + auto determine_boot_device() -> kstd::shared_ptr; private: /** @@ -66,7 +67,7 @@ namespace devices::storage */ storage_management() = default; - std::array m_controllers{}; // TODO BA-FS26 use kstd::vector when available + kstd::vector> m_controllers{}; }; } // namespace devices::storage diff --git a/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp b/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp index b57dcfb..3d14faf 100644 --- a/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp +++ b/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp @@ -4,22 +4,14 @@ #include "devices/storage/ram_disk/ram_disk_device.hpp" +#include #include #include -#include #include -#include namespace devices::storage::ram_disk { - namespace - { - // TODO BA-FS26 @Felix gibts besseren weg (ausser dynamic Memory) - // TODO BA-FS26 remove again, when dynamic memory available - constinit auto static active_ram_disk_device = std::optional{}; - } // namespace - ram_disk_controller::ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry) : m_boot_module_registry(registry) {} @@ -30,10 +22,7 @@ namespace devices::storage::ram_disk std::ranges::for_each(*m_boot_module_registry, [this, ¤t_device_index](auto const & module) { auto const minor = current_device_index++ * m_minors_per_device; - - // TODO BA-FS26 use push_back from kstd::vector when available - active_ram_disk_device.emplace(module, m_major, minor); - m_devices.at(0) = &*active_ram_disk_device; + m_devices.push_back(kstd::make_shared(module, m_major, minor)); }); } } // namespace devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp b/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp index 774e949..6ff2a83 100644 --- a/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp +++ b/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp @@ -28,10 +28,6 @@ namespace devices::storage::ram_disk } } // namespace - ram_disk_device::ram_disk_device() // TODO BA-FS26 remove when kstd::vector is available - : block_device(0, 0, determine_device_name(0), RAM_DISK_BLOCK_SIZE) - {} - ram_disk_device::ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor) : block_device(major, minor, determine_device_name(minor), RAM_DISK_BLOCK_SIZE) , m_boot_module(module) diff --git a/kernel/devices/src/storage/storage_controller.cpp b/kernel/devices/src/storage/storage_controller.cpp index d9bc806..16d40f4 100644 --- a/kernel/devices/src/storage/storage_controller.cpp +++ b/kernel/devices/src/storage/storage_controller.cpp @@ -2,8 +2,10 @@ #include "devices/device.hpp" +#include +#include + #include -#include #include namespace devices::storage @@ -19,7 +21,7 @@ namespace devices::storage return m_major; } - auto storage_controller::device_by_minor(size_t minor) const -> device * + auto storage_controller::device_by_minor(size_t minor) const -> kstd::shared_ptr { auto it = std::ranges::find_if(m_devices, [minor](auto const & device) { return device->minor() == minor; }); @@ -35,7 +37,7 @@ namespace devices::storage return m_devices.size(); } - auto storage_controller::all_devices() const -> std::array const & + auto storage_controller::all_devices() const -> kstd::vector> const & { return m_devices; } diff --git a/kernel/devices/src/storage/storage_management.cpp b/kernel/devices/src/storage/storage_management.cpp index e1f1bcc..00449fb 100644 --- a/kernel/devices/src/storage/storage_management.cpp +++ b/kernel/devices/src/storage/storage_management.cpp @@ -7,8 +7,10 @@ #include "devices/storage/ram_disk/ram_disk_controller.hpp" #include "devices/storage/storage_controller.hpp" +#include +#include + #include -#include #include #include @@ -21,8 +23,6 @@ namespace devices::storage constinit size_t static next_free_major = START_MAJOR; constinit auto static active_storage_management = std::optional{}; - // TODO BA-FS26 remove again, when dynamic memory available - constinit auto static active_ram_disk_controller = std::optional{}; } // namespace auto storage_management::init() -> void @@ -33,8 +33,9 @@ namespace devices::storage } active_storage_management.emplace(storage_management{}); - active_ram_disk_controller.emplace(&kapi::boot_modules::get_boot_module_registry()); - active_storage_management->add_controller(&active_ram_disk_controller.value()); + auto current_ram_disk_controller = + kstd::make_shared(&kapi::boot_modules::get_boot_module_registry()); + active_storage_management->add_controller(current_ram_disk_controller); std::ranges::for_each(active_storage_management->m_controllers, [](auto controller) { controller->probe(); }); } @@ -49,22 +50,22 @@ namespace devices::storage return *active_storage_management; } - auto storage_management::add_controller(storage_controller * controller) -> void + auto storage_management::add_controller(kstd::shared_ptr controller) -> void { controller->set_ids(next_free_major++, MINORS_PER_DEVICE); - m_controllers.at(0) = controller; // TODO BA-FS26 use push_back from kstd:vector + m_controllers.push_back(controller); } - auto storage_management::all_controllers() const -> std::array const & + auto storage_management::all_controllers() const -> kstd::vector> const & { return m_controllers; } - auto storage_management::device_by_major_minor(size_t major, size_t minor) -> device * + auto storage_management::device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr { - device * found = nullptr; + kstd::shared_ptr found = nullptr; - std::ranges::find_if(m_controllers, [&](auto const controller) { + std::ranges::find_if(m_controllers, [&](auto const & controller) { if (controller != nullptr && controller->major() == major) { found = controller->device_by_minor(minor); @@ -76,7 +77,7 @@ namespace devices::storage return found; } - auto storage_management::determine_boot_device() -> device * + auto storage_management::determine_boot_device() -> kstd::shared_ptr { return device_by_major_minor(START_MAJOR, 0); } diff --git a/kernel/filesystem/include/filesystem/custody.hpp b/kernel/filesystem/include/filesystem/custody.hpp index 9ee984d..b6eae22 100644 --- a/kernel/filesystem/include/filesystem/custody.hpp +++ b/kernel/filesystem/include/filesystem/custody.hpp @@ -3,18 +3,20 @@ #include "filesystem/inode.hpp" +#include + namespace filesystem { struct custody { - custody(custody * parent, inode * node); + custody(kstd::shared_ptr parent, kstd::shared_ptr node); - [[nodiscard]] auto get_inode() const -> inode *; - [[nodiscard]] auto get_parent() const -> custody *; + [[nodiscard]] auto get_inode() const -> kstd::shared_ptr; + [[nodiscard]] auto get_parent() const -> kstd::shared_ptr; private: - custody * m_parent; - inode * m_inode; + kstd::shared_ptr m_parent; + kstd::shared_ptr m_inode; }; } // namespace filesystem diff --git a/kernel/filesystem/include/filesystem/device_file.hpp b/kernel/filesystem/include/filesystem/device_file.hpp index 033317b..4dce37f 100644 --- a/kernel/filesystem/include/filesystem/device_file.hpp +++ b/kernel/filesystem/include/filesystem/device_file.hpp @@ -5,13 +5,15 @@ #include "devices/device.hpp" #include "filesystem/file.hpp" +#include + #include namespace filesystem { struct device_file : file { - explicit device_file(devices::device * device); + explicit device_file(kstd::shared_ptr device); auto open() -> void override; @@ -23,7 +25,7 @@ namespace filesystem std::byte * scratch, void * buffer); auto process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t; - devices::device * m_device; + kstd::shared_ptr m_device; }; } // namespace filesystem diff --git a/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp b/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp index cba2192..dd52e72 100644 --- a/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp +++ b/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp @@ -5,17 +5,19 @@ #include "filesystem/filesystem.hpp" #include "filesystem/inode.hpp" +#include + #include namespace filesystem::ext2 { struct ext2_filesystem : filesystem { - auto mount(devices::device * device) -> int override; + auto mount(kstd::shared_ptr device) -> int override; auto lookup(inode const & parent, std::string_view name) -> inode * override; private: - devices::device * m_device{}; + kstd::shared_ptr m_device{}; }; } // namespace filesystem::ext2 diff --git a/kernel/filesystem/include/filesystem/file_descriptor_table.hpp b/kernel/filesystem/include/filesystem/file_descriptor_table.hpp index 3ac03d1..6d78532 100644 --- a/kernel/filesystem/include/filesystem/file_descriptor_table.hpp +++ b/kernel/filesystem/include/filesystem/file_descriptor_table.hpp @@ -3,7 +3,8 @@ #include "open_file_description.hpp" -#include +#include + #include namespace filesystem @@ -23,8 +24,7 @@ namespace filesystem file_descriptor_table() = default; // TODO BA-FS26 use kstd::shared_ptr when available - // TODO BA-FS26 use kstd::vector when available - std::array, 32> m_open_files{}; + kstd::vector> m_open_files{}; }; } // namespace filesystem diff --git a/kernel/filesystem/include/filesystem/filesystem.hpp b/kernel/filesystem/include/filesystem/filesystem.hpp index 8deb336..eabefc0 100644 --- a/kernel/filesystem/include/filesystem/filesystem.hpp +++ b/kernel/filesystem/include/filesystem/filesystem.hpp @@ -4,6 +4,8 @@ #include "devices/device.hpp" #include "filesystem/inode.hpp" +#include + #include namespace filesystem @@ -12,11 +14,10 @@ namespace filesystem { virtual ~filesystem() = default; - virtual auto mount(devices::device * device) -> int = 0; + virtual auto mount(kstd::shared_ptr device) -> int = 0; virtual auto lookup(inode const & parent, std::string_view name) -> inode * = 0; - [[nodiscard]] auto root_inode() -> inode *; - [[nodiscard]] auto root_inode() const -> inode const *; + [[nodiscard]] auto root_inode() const -> inode const &; protected: inode m_root_inode{}; // TODO BA-FS26 set during mount? diff --git a/kernel/filesystem/include/filesystem/inode.hpp b/kernel/filesystem/include/filesystem/inode.hpp index bceabb6..2b5ee6c 100644 --- a/kernel/filesystem/include/filesystem/inode.hpp +++ b/kernel/filesystem/include/filesystem/inode.hpp @@ -4,6 +4,8 @@ #include "devices/device.hpp" #include "filesystem/inode_metadata.hpp" +#include + #include namespace filesystem @@ -12,7 +14,7 @@ namespace filesystem { inode() = default; explicit inode(inode_kind kind); - explicit inode(devices::device * device); + explicit inode(kstd::shared_ptr device); [[nodiscard]] auto metadata() const -> inode_metadata; @@ -22,14 +24,14 @@ namespace filesystem [[nodiscard]] auto is_block_device() const -> bool; [[nodiscard]] auto major_device() const -> size_t; [[nodiscard]] auto minor_device() const -> size_t; - [[nodiscard]] auto backing_device() const -> devices::device *; + [[nodiscard]] auto backing_device() const -> kstd::shared_ptr; auto read(void * buffer, size_t offset, size_t size) const -> size_t; auto write(void const * buffer, size_t offset, size_t size) -> size_t; private: inode_kind m_kind{inode_kind::regular}; - devices::device * m_device{}; + kstd::shared_ptr m_device{}; }; } // namespace filesystem diff --git a/kernel/filesystem/include/filesystem/inode_file.hpp b/kernel/filesystem/include/filesystem/inode_file.hpp index c091280..512f51d 100644 --- a/kernel/filesystem/include/filesystem/inode_file.hpp +++ b/kernel/filesystem/include/filesystem/inode_file.hpp @@ -4,13 +4,15 @@ #include "filesystem/file.hpp" #include "filesystem/inode.hpp" +#include + #include namespace filesystem { struct inode_file : file { - explicit inode_file(inode * inode); + explicit inode_file(kstd::shared_ptr inode); auto open() -> void override; @@ -18,7 +20,7 @@ namespace filesystem auto write(void const * buffer, size_t offset, size_t size) -> size_t override; private: - inode * m_inode; + kstd::shared_ptr m_inode; }; } // namespace filesystem diff --git a/kernel/filesystem/include/filesystem/mount.hpp b/kernel/filesystem/include/filesystem/mount.hpp index fe5d9cc..f28de74 100644 --- a/kernel/filesystem/include/filesystem/mount.hpp +++ b/kernel/filesystem/include/filesystem/mount.hpp @@ -3,21 +3,22 @@ #include "filesystem/filesystem.hpp" +#include + #include namespace filesystem { struct mount { - mount() = default; // TODO BA-FS26 remove again when kstd::vector is available and used in vfs - mount(std::string_view const & path, filesystem * fs); + mount(std::string_view const & path, kstd::shared_ptr fs); [[nodiscard]] auto path() const -> std::string_view; - [[nodiscard]] auto get_filesystem() const -> filesystem *; + [[nodiscard]] auto get_filesystem() const -> kstd::shared_ptr; private: std::string_view m_path; - filesystem * m_filesystem{}; + kstd::shared_ptr m_filesystem{}; }; } // namespace filesystem diff --git a/kernel/filesystem/include/filesystem/open_file_description.hpp b/kernel/filesystem/include/filesystem/open_file_description.hpp index 1589196..035b0ee 100644 --- a/kernel/filesystem/include/filesystem/open_file_description.hpp +++ b/kernel/filesystem/include/filesystem/open_file_description.hpp @@ -2,6 +2,7 @@ #define TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP #include "file.hpp" +#include #include @@ -9,7 +10,7 @@ namespace filesystem { struct open_file_description { - open_file_description(file * file); + open_file_description(kstd::shared_ptr file); ~open_file_description() = default; @@ -17,7 +18,7 @@ namespace filesystem auto write(void const * buffer, size_t size) -> size_t; private: - file * m_file; + kstd::shared_ptr m_file; size_t m_offset; }; diff --git a/kernel/filesystem/include/filesystem/vfs.hpp b/kernel/filesystem/include/filesystem/vfs.hpp index 1d95d4a..9c89044 100644 --- a/kernel/filesystem/include/filesystem/vfs.hpp +++ b/kernel/filesystem/include/filesystem/vfs.hpp @@ -3,11 +3,14 @@ #include "devices/device.hpp" #include "filesystem/custody.hpp" +#include "filesystem/ext2/ext2_filesystem.hpp" #include "filesystem/inode.hpp" #include "filesystem/mount.hpp" #include "filesystem/open_file_description.hpp" -#include +#include +#include + #include #include @@ -26,17 +29,17 @@ namespace filesystem struct device_node_entry { std::string_view name; - inode node; + kstd::shared_ptr node; }; vfs() = default; - auto make_device_node(devices::device * device) -> void; + auto make_device_node(kstd::shared_ptr device) -> void; [[nodiscard]] auto resolve_path(std::string_view path) -> std::optional; + kstd::shared_ptr m_root_fs; std::optional m_root_mount; - std::array m_mounts; // TODO BA-FS26 remove when kstd::vector is available and used - std::array, 10> - m_device_nodes; // TODO BA-FS26 use kstd::vector // TODO BA-FS26 remove again, use + // kstd::vector m_mounts; // TODO BA-FS26 really needed? + kstd::vector> m_device_nodes; // TODO BA-FS26 remove again, use devtempfs }; } // namespace filesystem diff --git a/kernel/filesystem/src/custody.cpp b/kernel/filesystem/src/custody.cpp index 614e63b..7a58229 100644 --- a/kernel/filesystem/src/custody.cpp +++ b/kernel/filesystem/src/custody.cpp @@ -4,9 +4,11 @@ #include "filesystem/inode.hpp" +#include + namespace filesystem { - custody::custody(custody * parent, inode * node) + custody::custody(kstd::shared_ptr parent, kstd::shared_ptr node) : m_parent(parent) , m_inode(node) { @@ -16,12 +18,12 @@ namespace filesystem } } - auto custody::get_inode() const -> inode * + auto custody::get_inode() const -> kstd::shared_ptr { return m_inode; } - auto custody::get_parent() const -> custody * + auto custody::get_parent() const -> kstd::shared_ptr { return m_parent; } diff --git a/kernel/filesystem/src/device_file.cpp b/kernel/filesystem/src/device_file.cpp index f11638e..882c9b1 100644 --- a/kernel/filesystem/src/device_file.cpp +++ b/kernel/filesystem/src/device_file.cpp @@ -6,14 +6,15 @@ #include "devices/device.hpp" #include +#include +#include #include -#include #include namespace filesystem { - device_file::device_file(devices::device * device) + device_file::device_file(kstd::shared_ptr device) : m_device(device) { if (!m_device) @@ -90,7 +91,13 @@ namespace filesystem return 0; } - auto * block_dev = static_cast(m_device); + // @Felix rtti not activated why? e.g. dynamic_cast does not work, whats with std::dynamic_pointer_cast + // online: rtti overhead, bad design, ... ? + auto * block_dev = static_cast(m_device.get()); + if (block_dev == nullptr) + { + kapi::system::panic("[FILESYSTEM] device_file: expected block_device."); + } size_t const block_size = block_dev->block_size(); size_t const capacity = block_dev->capacity(); @@ -99,8 +106,7 @@ namespace filesystem return 0; size_t const total_to_process = std::min(size, capacity - offset); - // TODO BA-FS26 use kstd::vector when available - std::array scratch_buffer{}; // TODO BA-FS26 better solution than fixed scratch_buffer ?? + kstd::vector scratch_buffer{block_size}; auto processed = 0uz; while (processed < total_to_process) diff --git a/kernel/filesystem/src/ext2/ext2_filesystem.cpp b/kernel/filesystem/src/ext2/ext2_filesystem.cpp index f092ddf..2ae5ab7 100644 --- a/kernel/filesystem/src/ext2/ext2_filesystem.cpp +++ b/kernel/filesystem/src/ext2/ext2_filesystem.cpp @@ -2,12 +2,15 @@ #include "devices/device.hpp" #include "filesystem/inode.hpp" +#include "filesystem/inode_metadata.hpp" + +#include #include namespace filesystem::ext2 { - auto ext2_filesystem::mount(devices::device * device) -> int + auto ext2_filesystem::mount(kstd::shared_ptr device) -> int { if (!device) { diff --git a/kernel/filesystem/src/file_descriptor_table.cpp b/kernel/filesystem/src/file_descriptor_table.cpp index eb9a281..64fad0c 100644 --- a/kernel/filesystem/src/file_descriptor_table.cpp +++ b/kernel/filesystem/src/file_descriptor_table.cpp @@ -44,7 +44,8 @@ namespace filesystem return static_cast(it - m_open_files.begin()); } - return -1; + m_open_files.push_back(file_description); + return static_cast(m_open_files.size() - 1); } auto file_descriptor_table::get_file(int fd) const -> std::optional @@ -55,12 +56,12 @@ namespace filesystem } auto const index = static_cast(fd); - if (index >= m_open_files.size() || !m_open_files[index].has_value()) + if (index >= m_open_files.size() || !m_open_files.at(fd).has_value()) { return std::nullopt; } - return *m_open_files[index]; + return m_open_files.at(fd); } auto file_descriptor_table::remove_file(int fd) -> void @@ -76,6 +77,6 @@ namespace filesystem return; } - m_open_files[index].reset(); + m_open_files.at(fd).reset(); } } // namespace filesystem \ No newline at end of file diff --git a/kernel/filesystem/src/filesystem.cpp b/kernel/filesystem/src/filesystem.cpp index a06bccc..cdfe7f8 100644 --- a/kernel/filesystem/src/filesystem.cpp +++ b/kernel/filesystem/src/filesystem.cpp @@ -4,13 +4,8 @@ namespace filesystem { - auto filesystem::root_inode() -> inode * + auto filesystem::root_inode() const -> inode const & { - return &m_root_inode; - } - - auto filesystem::root_inode() const -> inode const * - { - return &m_root_inode; + return m_root_inode; } } // namespace filesystem \ No newline at end of file diff --git a/kernel/filesystem/src/inode.cpp b/kernel/filesystem/src/inode.cpp index 17aa52a..10a9a30 100644 --- a/kernel/filesystem/src/inode.cpp +++ b/kernel/filesystem/src/inode.cpp @@ -5,6 +5,8 @@ #include "devices/device.hpp" #include "filesystem/inode_metadata.hpp" +#include + #include namespace filesystem @@ -13,7 +15,7 @@ namespace filesystem : m_kind(kind) {} - inode::inode(devices::device * device) + inode::inode(kstd::shared_ptr device) : m_kind(inode_kind::device) , m_device(device) { @@ -77,7 +79,7 @@ namespace filesystem return m_device->minor(); } - auto inode::backing_device() const -> devices::device * + auto inode::backing_device() const -> kstd::shared_ptr { return m_device; } diff --git a/kernel/filesystem/src/inode_file.cpp b/kernel/filesystem/src/inode_file.cpp index 071661f..b68b3ee 100644 --- a/kernel/filesystem/src/inode_file.cpp +++ b/kernel/filesystem/src/inode_file.cpp @@ -4,11 +4,13 @@ #include "filesystem/inode.hpp" +#include + #include namespace filesystem { - inode_file::inode_file(inode * inode) + inode_file::inode_file(kstd::shared_ptr inode) : m_inode(inode) { if (!m_inode) diff --git a/kernel/filesystem/src/mount.cpp b/kernel/filesystem/src/mount.cpp index 6594598..53a8143 100644 --- a/kernel/filesystem/src/mount.cpp +++ b/kernel/filesystem/src/mount.cpp @@ -4,11 +4,13 @@ #include "filesystem/filesystem.hpp" +#include + #include namespace filesystem { - mount::mount(std::string_view const & path, filesystem * fs) + mount::mount(std::string_view const & path, kstd::shared_ptr fs) : m_path(path) , m_filesystem(fs) { @@ -23,7 +25,7 @@ namespace filesystem return m_path; } - auto mount::get_filesystem() const -> filesystem * + auto mount::get_filesystem() const -> kstd::shared_ptr { return m_filesystem; } diff --git a/kernel/filesystem/src/open_file_description.cpp b/kernel/filesystem/src/open_file_description.cpp index d483746..9ebf67d 100644 --- a/kernel/filesystem/src/open_file_description.cpp +++ b/kernel/filesystem/src/open_file_description.cpp @@ -2,18 +2,25 @@ #include "filesystem/file.hpp" +#include +#include + #include namespace filesystem { - open_file_description::open_file_description(file * file) + open_file_description::open_file_description(kstd::shared_ptr file) : m_file(file) , m_offset(0) - {} + { + if (!file) + { + kstd::os::panic("[FILESYSTEM] open_file_description constructed with null file."); + } + } auto open_file_description::read(void * buffer, size_t size) -> size_t { - // TODO BA-FS26 nullptr check auto read_bytes = m_file->read(buffer, m_offset, size); m_offset += read_bytes; return read_bytes; @@ -21,7 +28,6 @@ namespace filesystem auto open_file_description::write(void const * buffer, size_t size) -> size_t { - // TODO BA-FS26 nullptr check auto written_bytes = m_file->write(buffer, m_offset, size); m_offset += written_bytes; return written_bytes; diff --git a/kernel/filesystem/src/vfs.cpp b/kernel/filesystem/src/vfs.cpp index 2ecf847..578fde4 100644 --- a/kernel/filesystem/src/vfs.cpp +++ b/kernel/filesystem/src/vfs.cpp @@ -13,6 +13,7 @@ #include "filesystem/open_file_description.hpp" #include +#include #include #include @@ -23,15 +24,6 @@ namespace filesystem namespace { constinit auto static active_vfs = std::optional{}; - - // TODO BA-FS26 @Felix better solution? while dynamic memory not available? - // TODO BA-FS26 remove when dynamic memory available; - constinit auto static root_fs = std::optional{}; - - // TODO BA-FS26 remove when dynamic memory available; - constinit auto static temp_device_file = std::optional{}; - // TODO BA-FS26 remove when dynamic memory available; - constinit auto static temp_inode_file = std::optional{}; } // namespace auto vfs::init() -> void @@ -46,13 +38,13 @@ namespace filesystem auto storage_mgmt = devices::storage::storage_management::get(); if (auto boot_device = storage_mgmt.determine_boot_device()) { - root_fs.emplace(ext2::ext2_filesystem{}); - if (root_fs->mount(boot_device) != 0) + active_vfs->m_root_fs = kstd::make_shared(); + if (active_vfs->m_root_fs->mount(boot_device) != 0) { kapi::system::panic("[FILESYSTEM] Failed to mount root filesystem."); } - active_vfs->m_root_mount = mount{"/", &*root_fs}; + active_vfs->m_root_mount = mount{"/", active_vfs->m_root_fs}; std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto controller) { std::ranges::for_each(controller->all_devices(), [&](auto device) { active_vfs->make_device_node(device); }); @@ -78,39 +70,30 @@ namespace filesystem { if (auto custody = resolve_path(path)) { - auto * node = custody->get_inode(); + auto node = custody->get_inode(); if (node->is_device()) { - temp_device_file.emplace(node->backing_device()); - temp_device_file->open(); - return open_file_description{&*temp_device_file}; + auto current_device_file = kstd::make_shared(node->backing_device()); + current_device_file->open(); + return open_file_description{current_device_file}; } - temp_inode_file.emplace(node); - temp_inode_file->open(); - return open_file_description{&*temp_inode_file}; + auto current_inode_file = kstd::make_shared(node); + current_inode_file->open(); + return open_file_description{current_inode_file}; } return std::nullopt; } - auto vfs::make_device_node(devices::device * device) -> void + auto vfs::make_device_node(kstd::shared_ptr device) -> void { if (!device) { kapi::system::panic("[FILESYSTEM] make_device_node called with null device."); } - auto const device_name = device->name(); - - // TODO BA-FS26 this logic isn't needed anymore when kstd::vector available, just use push_back - auto const slot = std::ranges::find_if(m_device_nodes, [](auto const & entry) { return !entry.has_value(); }); - if (slot == m_device_nodes.end()) - { - kapi::system::panic("[FILESYSTEM] No free slot available for device nodes."); - } - - slot->emplace(device_node_entry{device_name, inode{device}}); + m_device_nodes.push_back(device_node_entry{device->name(), kstd::make_shared(device)}); } auto vfs::resolve_path(std::string_view path) -> std::optional @@ -128,7 +111,7 @@ namespace filesystem if (entry != m_device_nodes.end()) { - return custody{nullptr, &entry->value().node}; + return custody{static_cast>(nullptr), entry->value().node}; } return std::nullopt; diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 011821a..cf3afdf 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -8,13 +8,13 @@ #include "filesystem/file_descriptor_table.hpp" #include "filesystem/open_file_description.hpp" #include "filesystem/vfs.hpp" - #include "kernel/memory.hpp" +#include #include #include +#include -#include #include #include @@ -25,8 +25,8 @@ auto run_test_code() -> void auto storage_mgmt = devices::storage::storage_management::get(); auto device = storage_mgmt.device_by_major_minor(1, 0); - filesystem::device_file dev_file(device); - filesystem::open_file_description ofd(&dev_file); + auto dev_file = kstd::make_shared(device); + filesystem::open_file_description ofd(dev_file); auto fd_index = fd_table.add_file(ofd); // use: read two bytes and write two again @@ -36,7 +36,7 @@ auto run_test_code() -> void kstd::os::panic("test code failed"); } - std::array buffer{}; + kstd::vector buffer{2}; auto number_of_read_bytes = fd->read(buffer.data(), buffer.size()); for (size_t i = 0; i < number_of_read_bytes; ++i) @@ -51,7 +51,7 @@ auto run_test_code() -> void kstd::println("---"); // write half of the file new - std::array write_buffer{std::byte{0xBB}, std::byte{0xAA}}; + kstd::vector write_buffer{std::byte{0xBB}, std::byte{0xAA}}; auto written_bytes = fd->write(write_buffer.data(), write_buffer.size()); kstd::println("written bytes: {}", written_bytes); @@ -59,7 +59,7 @@ auto run_test_code() -> void fd_table.remove_file(fd_index); // use: read four bytes again -> two old bytes two new bytes - filesystem::open_file_description ofd1(&dev_file); + filesystem::open_file_description ofd1(dev_file); fd_index = fd_table.add_file(ofd1); auto fd1 = fd_table.get_file(fd_index); @@ -68,7 +68,7 @@ auto run_test_code() -> void kstd::os::panic("test code failed"); } - std::array buffer1{}; + kstd::vector buffer1{4}; number_of_read_bytes = fd1->read(buffer1.data(), buffer1.size()); for (size_t i = 0; i < number_of_read_bytes; ++i) @@ -89,7 +89,8 @@ auto main() -> int kstd::println("[OS] IO subsystem initialized."); kapi::memory::init(); - kernel::memory::init_heap(kapi::memory::heap_base); kstd::println("[OS] Memory subsystem initialized."); + kernel::memory::init_heap(kapi::memory::heap_base); + kstd::println("[OS] Memory subsystem initialized."); kapi::system::memory_initialized(); kapi::boot_modules::init(); -- cgit v1.2.3 From 3f8af998aaf87d83af3af6c5708d8a84b775b50e Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 17 Mar 2026 12:24:07 +0100 Subject: write comments --- kernel/devices/include/devices/storage/storage_controller.hpp | 5 ++++- kernel/devices/include/devices/storage/storage_management.hpp | 5 ++++- kernel/devices/src/storage/ram_disk/ram_disk_device.cpp | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/kernel/devices/include/devices/storage/storage_controller.hpp b/kernel/devices/include/devices/storage/storage_controller.hpp index e90b01c..7760fc9 100644 --- a/kernel/devices/include/devices/storage/storage_controller.hpp +++ b/kernel/devices/include/devices/storage/storage_controller.hpp @@ -47,7 +47,10 @@ namespace devices::storage */ [[nodiscard]] auto devices_count() const -> size_t; - // TODO BA-FS26 add comment + /** + * @brief Return all devices managed by this controller. + * @return Vector of all managed devices. + */ [[nodiscard]] auto all_devices() const -> kstd::vector> const &; /** diff --git a/kernel/devices/include/devices/storage/storage_management.hpp b/kernel/devices/include/devices/storage/storage_management.hpp index dc2f6c9..65211c8 100644 --- a/kernel/devices/include/devices/storage/storage_management.hpp +++ b/kernel/devices/include/devices/storage/storage_management.hpp @@ -44,7 +44,10 @@ namespace devices::storage */ auto add_controller(kstd::shared_ptr controller) -> void; - // TODO BA-FS26 add comment + /** + * @brief Return all registered storage controllers. + * @return Vector of all registered storage controllers. + */ [[nodiscard]] auto all_controllers() const -> kstd::vector> const &; /** diff --git a/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp b/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp index 6ff2a83..91a278e 100644 --- a/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp +++ b/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp @@ -15,7 +15,7 @@ namespace devices::storage::ram_disk { namespace { - constexpr size_t RAM_DISK_BLOCK_SIZE = 512uz; // TODO BA-FS26 really correct / good?? + constexpr size_t RAM_DISK_BLOCK_SIZE = 512uz; // TODO BA-FS26 @Felix // TODO BA-FS26 currently only names for 9 minor devices -- cgit v1.2.3 From f1885d0d09d5d94ad326f2257c9502b9545f0e79 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 17 Mar 2026 12:39:17 +0100 Subject: use const & wherever applicable --- kernel/devices/include/devices/storage/storage_management.hpp | 2 +- kernel/devices/src/storage/storage_management.cpp | 2 +- kernel/filesystem/include/filesystem/custody.hpp | 6 +++--- kernel/filesystem/include/filesystem/device_file.hpp | 2 +- kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp | 2 +- kernel/filesystem/include/filesystem/filesystem.hpp | 2 +- kernel/filesystem/include/filesystem/inode.hpp | 4 ++-- kernel/filesystem/include/filesystem/inode_file.hpp | 2 +- kernel/filesystem/include/filesystem/mount.hpp | 4 ++-- kernel/filesystem/include/filesystem/open_file_description.hpp | 3 ++- kernel/filesystem/include/filesystem/vfs.hpp | 2 +- kernel/filesystem/src/custody.cpp | 6 +++--- kernel/filesystem/src/device_file.cpp | 2 +- kernel/filesystem/src/ext2/ext2_filesystem.cpp | 2 +- kernel/filesystem/src/inode.cpp | 4 ++-- kernel/filesystem/src/inode_file.cpp | 2 +- kernel/filesystem/src/mount.cpp | 4 ++-- kernel/filesystem/src/open_file_description.cpp | 2 +- kernel/filesystem/src/vfs.cpp | 4 ++-- 19 files changed, 29 insertions(+), 28 deletions(-) diff --git a/kernel/devices/include/devices/storage/storage_management.hpp b/kernel/devices/include/devices/storage/storage_management.hpp index 65211c8..f8ffda8 100644 --- a/kernel/devices/include/devices/storage/storage_management.hpp +++ b/kernel/devices/include/devices/storage/storage_management.hpp @@ -42,7 +42,7 @@ namespace devices::storage * * Assigns controller IDs (major number range and minors per device). */ - auto add_controller(kstd::shared_ptr controller) -> void; + auto add_controller(kstd::shared_ptr const & controller) -> void; /** * @brief Return all registered storage controllers. diff --git a/kernel/devices/src/storage/storage_management.cpp b/kernel/devices/src/storage/storage_management.cpp index 00449fb..25885ad 100644 --- a/kernel/devices/src/storage/storage_management.cpp +++ b/kernel/devices/src/storage/storage_management.cpp @@ -50,7 +50,7 @@ namespace devices::storage return *active_storage_management; } - auto storage_management::add_controller(kstd::shared_ptr controller) -> void + auto storage_management::add_controller(kstd::shared_ptr const & controller) -> void { controller->set_ids(next_free_major++, MINORS_PER_DEVICE); m_controllers.push_back(controller); diff --git a/kernel/filesystem/include/filesystem/custody.hpp b/kernel/filesystem/include/filesystem/custody.hpp index b6eae22..0124eb6 100644 --- a/kernel/filesystem/include/filesystem/custody.hpp +++ b/kernel/filesystem/include/filesystem/custody.hpp @@ -9,10 +9,10 @@ namespace filesystem { struct custody { - custody(kstd::shared_ptr parent, kstd::shared_ptr node); + custody(kstd::shared_ptr const & parent, kstd::shared_ptr const & node); - [[nodiscard]] auto get_inode() const -> kstd::shared_ptr; - [[nodiscard]] auto get_parent() const -> kstd::shared_ptr; + [[nodiscard]] auto get_inode() const -> kstd::shared_ptr const &; + [[nodiscard]] auto get_parent() const -> kstd::shared_ptr const &; private: kstd::shared_ptr m_parent; diff --git a/kernel/filesystem/include/filesystem/device_file.hpp b/kernel/filesystem/include/filesystem/device_file.hpp index 4dce37f..dad4ea5 100644 --- a/kernel/filesystem/include/filesystem/device_file.hpp +++ b/kernel/filesystem/include/filesystem/device_file.hpp @@ -13,7 +13,7 @@ namespace filesystem { struct device_file : file { - explicit device_file(kstd::shared_ptr device); + explicit device_file(kstd::shared_ptr const & device); auto open() -> void override; diff --git a/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp b/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp index dd52e72..37448a1 100644 --- a/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp +++ b/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp @@ -13,7 +13,7 @@ namespace filesystem::ext2 { struct ext2_filesystem : filesystem { - auto mount(kstd::shared_ptr device) -> int override; + auto mount(kstd::shared_ptr const & device) -> int override; auto lookup(inode const & parent, std::string_view name) -> inode * override; private: diff --git a/kernel/filesystem/include/filesystem/filesystem.hpp b/kernel/filesystem/include/filesystem/filesystem.hpp index eabefc0..934f883 100644 --- a/kernel/filesystem/include/filesystem/filesystem.hpp +++ b/kernel/filesystem/include/filesystem/filesystem.hpp @@ -14,7 +14,7 @@ namespace filesystem { virtual ~filesystem() = default; - virtual auto mount(kstd::shared_ptr device) -> int = 0; + virtual auto mount(kstd::shared_ptr const & device) -> int = 0; virtual auto lookup(inode const & parent, std::string_view name) -> inode * = 0; [[nodiscard]] auto root_inode() const -> inode const &; diff --git a/kernel/filesystem/include/filesystem/inode.hpp b/kernel/filesystem/include/filesystem/inode.hpp index 2b5ee6c..abfe2cf 100644 --- a/kernel/filesystem/include/filesystem/inode.hpp +++ b/kernel/filesystem/include/filesystem/inode.hpp @@ -14,7 +14,7 @@ namespace filesystem { inode() = default; explicit inode(inode_kind kind); - explicit inode(kstd::shared_ptr device); + explicit inode(kstd::shared_ptr const & device); [[nodiscard]] auto metadata() const -> inode_metadata; @@ -24,7 +24,7 @@ namespace filesystem [[nodiscard]] auto is_block_device() const -> bool; [[nodiscard]] auto major_device() const -> size_t; [[nodiscard]] auto minor_device() const -> size_t; - [[nodiscard]] auto backing_device() const -> kstd::shared_ptr; + [[nodiscard]] auto backing_device() const -> kstd::shared_ptr const &; auto read(void * buffer, size_t offset, size_t size) const -> size_t; auto write(void const * buffer, size_t offset, size_t size) -> size_t; diff --git a/kernel/filesystem/include/filesystem/inode_file.hpp b/kernel/filesystem/include/filesystem/inode_file.hpp index 512f51d..5f8a95e 100644 --- a/kernel/filesystem/include/filesystem/inode_file.hpp +++ b/kernel/filesystem/include/filesystem/inode_file.hpp @@ -12,7 +12,7 @@ namespace filesystem { struct inode_file : file { - explicit inode_file(kstd::shared_ptr inode); + explicit inode_file(kstd::shared_ptr const & inode); auto open() -> void override; diff --git a/kernel/filesystem/include/filesystem/mount.hpp b/kernel/filesystem/include/filesystem/mount.hpp index f28de74..46a68e2 100644 --- a/kernel/filesystem/include/filesystem/mount.hpp +++ b/kernel/filesystem/include/filesystem/mount.hpp @@ -11,10 +11,10 @@ namespace filesystem { struct mount { - mount(std::string_view const & path, kstd::shared_ptr fs); + mount(std::string_view const & path, kstd::shared_ptr const & fs); [[nodiscard]] auto path() const -> std::string_view; - [[nodiscard]] auto get_filesystem() const -> kstd::shared_ptr; + [[nodiscard]] auto get_filesystem() const -> kstd::shared_ptr const &; private: std::string_view m_path; diff --git a/kernel/filesystem/include/filesystem/open_file_description.hpp b/kernel/filesystem/include/filesystem/open_file_description.hpp index 035b0ee..5ff094d 100644 --- a/kernel/filesystem/include/filesystem/open_file_description.hpp +++ b/kernel/filesystem/include/filesystem/open_file_description.hpp @@ -2,6 +2,7 @@ #define TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP #include "file.hpp" + #include #include @@ -10,7 +11,7 @@ namespace filesystem { struct open_file_description { - open_file_description(kstd::shared_ptr file); + open_file_description(kstd::shared_ptr const & file); ~open_file_description() = default; diff --git a/kernel/filesystem/include/filesystem/vfs.hpp b/kernel/filesystem/include/filesystem/vfs.hpp index 9c89044..e16a961 100644 --- a/kernel/filesystem/include/filesystem/vfs.hpp +++ b/kernel/filesystem/include/filesystem/vfs.hpp @@ -33,7 +33,7 @@ namespace filesystem }; vfs() = default; - auto make_device_node(kstd::shared_ptr device) -> void; + auto make_device_node(kstd::shared_ptr const & device) -> void; [[nodiscard]] auto resolve_path(std::string_view path) -> std::optional; kstd::shared_ptr m_root_fs; diff --git a/kernel/filesystem/src/custody.cpp b/kernel/filesystem/src/custody.cpp index 7a58229..3a87345 100644 --- a/kernel/filesystem/src/custody.cpp +++ b/kernel/filesystem/src/custody.cpp @@ -8,7 +8,7 @@ namespace filesystem { - custody::custody(kstd::shared_ptr parent, kstd::shared_ptr node) + custody::custody(kstd::shared_ptr const & parent, kstd::shared_ptr const & node) : m_parent(parent) , m_inode(node) { @@ -18,12 +18,12 @@ namespace filesystem } } - auto custody::get_inode() const -> kstd::shared_ptr + auto custody::get_inode() const -> kstd::shared_ptr const & { return m_inode; } - auto custody::get_parent() const -> kstd::shared_ptr + auto custody::get_parent() const -> kstd::shared_ptr const & { return m_parent; } diff --git a/kernel/filesystem/src/device_file.cpp b/kernel/filesystem/src/device_file.cpp index 882c9b1..699c161 100644 --- a/kernel/filesystem/src/device_file.cpp +++ b/kernel/filesystem/src/device_file.cpp @@ -14,7 +14,7 @@ namespace filesystem { - device_file::device_file(kstd::shared_ptr device) + device_file::device_file(kstd::shared_ptr const & device) : m_device(device) { if (!m_device) diff --git a/kernel/filesystem/src/ext2/ext2_filesystem.cpp b/kernel/filesystem/src/ext2/ext2_filesystem.cpp index 2ae5ab7..a82a098 100644 --- a/kernel/filesystem/src/ext2/ext2_filesystem.cpp +++ b/kernel/filesystem/src/ext2/ext2_filesystem.cpp @@ -10,7 +10,7 @@ namespace filesystem::ext2 { - auto ext2_filesystem::mount(kstd::shared_ptr device) -> int + auto ext2_filesystem::mount(kstd::shared_ptr const & device) -> int { if (!device) { diff --git a/kernel/filesystem/src/inode.cpp b/kernel/filesystem/src/inode.cpp index 10a9a30..231ee33 100644 --- a/kernel/filesystem/src/inode.cpp +++ b/kernel/filesystem/src/inode.cpp @@ -15,7 +15,7 @@ namespace filesystem : m_kind(kind) {} - inode::inode(kstd::shared_ptr device) + inode::inode(kstd::shared_ptr const & device) : m_kind(inode_kind::device) , m_device(device) { @@ -79,7 +79,7 @@ namespace filesystem return m_device->minor(); } - auto inode::backing_device() const -> kstd::shared_ptr + auto inode::backing_device() const -> kstd::shared_ptr const & { return m_device; } diff --git a/kernel/filesystem/src/inode_file.cpp b/kernel/filesystem/src/inode_file.cpp index b68b3ee..0b8bdf9 100644 --- a/kernel/filesystem/src/inode_file.cpp +++ b/kernel/filesystem/src/inode_file.cpp @@ -10,7 +10,7 @@ namespace filesystem { - inode_file::inode_file(kstd::shared_ptr inode) + inode_file::inode_file(kstd::shared_ptr const & inode) : m_inode(inode) { if (!m_inode) diff --git a/kernel/filesystem/src/mount.cpp b/kernel/filesystem/src/mount.cpp index 53a8143..021b074 100644 --- a/kernel/filesystem/src/mount.cpp +++ b/kernel/filesystem/src/mount.cpp @@ -10,7 +10,7 @@ namespace filesystem { - mount::mount(std::string_view const & path, kstd::shared_ptr fs) + mount::mount(std::string_view const & path, kstd::shared_ptr const & fs) : m_path(path) , m_filesystem(fs) { @@ -25,7 +25,7 @@ namespace filesystem return m_path; } - auto mount::get_filesystem() const -> kstd::shared_ptr + auto mount::get_filesystem() const -> kstd::shared_ptr const & { return m_filesystem; } diff --git a/kernel/filesystem/src/open_file_description.cpp b/kernel/filesystem/src/open_file_description.cpp index 9ebf67d..cddee76 100644 --- a/kernel/filesystem/src/open_file_description.cpp +++ b/kernel/filesystem/src/open_file_description.cpp @@ -9,7 +9,7 @@ namespace filesystem { - open_file_description::open_file_description(kstd::shared_ptr file) + open_file_description::open_file_description(kstd::shared_ptr const & file) : m_file(file) , m_offset(0) { diff --git a/kernel/filesystem/src/vfs.cpp b/kernel/filesystem/src/vfs.cpp index 578fde4..c0f1c3a 100644 --- a/kernel/filesystem/src/vfs.cpp +++ b/kernel/filesystem/src/vfs.cpp @@ -86,7 +86,7 @@ namespace filesystem return std::nullopt; } - auto vfs::make_device_node(kstd::shared_ptr device) -> void + auto vfs::make_device_node(kstd::shared_ptr const & device) -> void { if (!device) { @@ -111,7 +111,7 @@ namespace filesystem if (entry != m_device_nodes.end()) { - return custody{static_cast>(nullptr), entry->value().node}; + return custody{nullptr, entry->value().node}; } return std::nullopt; -- cgit v1.2.3 From 01d59096b1ade0b3d4e1ce77e11065b31bbd1003 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 17 Mar 2026 16:04:15 +0100 Subject: remove comment --- kernel/filesystem/src/device_file.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/filesystem/src/device_file.cpp b/kernel/filesystem/src/device_file.cpp index 699c161..8fc159a 100644 --- a/kernel/filesystem/src/device_file.cpp +++ b/kernel/filesystem/src/device_file.cpp @@ -91,8 +91,6 @@ namespace filesystem return 0; } - // @Felix rtti not activated why? e.g. dynamic_cast does not work, whats with std::dynamic_pointer_cast - // online: rtti overhead, bad design, ... ? auto * block_dev = static_cast(m_device.get()); if (block_dev == nullptr) { -- cgit v1.2.3 From 59504cfd677dd3e9d9ddb0deea4df7614efedb84 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 17 Mar 2026 16:48:26 +0100 Subject: fix rebase (use higher_half_mapper not recursive_page_mapper) --- arch/x86_64/kapi/memory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp index a354576..547d359 100644 --- a/arch/x86_64/kapi/memory.cpp +++ b/arch/x86_64/kapi/memory.cpp @@ -119,7 +119,7 @@ namespace kapi::memory mapper.map(page, frame, page_mapper::flags::supervisor_only); } } - + [[maybe_unused]] auto remap_bootloader_modules(page_mapper & mapper) -> void { std::ranges::for_each(boot::bootstrap_information.mbi->modules(), [&mapper](auto const & module) { @@ -223,7 +223,7 @@ namespace kapi::memory remap_kernel(*higher_half_mapper); remap_vga_text_mode_buffer(*higher_half_mapper); remap_multiboot_information(*higher_half_mapper); - remap_bootloader_modules(*recursive_page_mapper); + remap_bootloader_modules(*higher_half_mapper); auto current_cr3 = arch::cpu::cr3::read(); auto old_pml4 = static_cast(current_cr3.address()); -- cgit v1.2.3 From 3ace886a9e9f044cd48de51f0a15aceb02bfa9b2 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Tue, 17 Mar 2026 19:36:20 +0100 Subject: Clean up project folder structure --- kernel/CMakeLists.txt | 21 +++- kernel/devices/CMakeLists.txt | 31 ------ kernel/devices/include/devices/block_device.hpp | 101 ----------------- kernel/devices/include/devices/device.hpp | 61 ---------- .../storage/ram_disk/ram_disk_controller.hpp | 31 ------ .../devices/storage/ram_disk/ram_disk_device.hpp | 52 --------- .../include/devices/storage/storage_controller.hpp | 71 ------------ .../include/devices/storage/storage_management.hpp | 77 ------------- kernel/devices/src/block_device.cpp | 42 ------- kernel/devices/src/device.cpp | 28 ----- .../src/storage/ram_disk/ram_disk_controller.cpp | 28 ----- .../src/storage/ram_disk/ram_disk_device.cpp | 77 ------------- kernel/devices/src/storage/storage_controller.cpp | 44 -------- kernel/devices/src/storage/storage_management.cpp | 85 -------------- kernel/filesystem/CMakeLists.txt | 43 ------- kernel/filesystem/include/filesystem/custody.hpp | 23 ---- .../filesystem/include/filesystem/device_file.hpp | 32 ------ .../include/filesystem/ext2/ext2_filesystem.hpp | 24 ---- kernel/filesystem/include/filesystem/file.hpp | 19 ---- .../include/filesystem/file_descriptor_table.hpp | 31 ------ .../filesystem/include/filesystem/filesystem.hpp | 28 ----- kernel/filesystem/include/filesystem/inode.hpp | 38 ------- .../filesystem/include/filesystem/inode_file.hpp | 27 ----- .../include/filesystem/inode_metadata.hpp | 24 ---- kernel/filesystem/include/filesystem/mount.hpp | 25 ----- .../include/filesystem/open_file_description.hpp | 28 ----- kernel/filesystem/include/filesystem/vfs.hpp | 46 -------- kernel/filesystem/src/custody.cpp | 30 ----- kernel/filesystem/src/device_file.cpp | 124 --------------------- kernel/filesystem/src/ext2/ext2_filesystem.cpp | 33 ------ kernel/filesystem/src/file_descriptor_table.cpp | 82 -------------- kernel/filesystem/src/filesystem.cpp | 11 -- kernel/filesystem/src/inode.cpp | 108 ------------------ kernel/filesystem/src/inode_file.cpp | 35 ------ kernel/filesystem/src/mount.cpp | 32 ------ kernel/filesystem/src/open_file_description.cpp | 35 ------ kernel/filesystem/src/vfs.cpp | 123 -------------------- kernel/include/kernel/devices/block_device.hpp | 101 +++++++++++++++++ kernel/include/kernel/devices/device.hpp | 61 ++++++++++ .../storage/ram_disk/ram_disk_controller.hpp | 31 ++++++ .../devices/storage/ram_disk/ram_disk_device.hpp | 52 +++++++++ .../kernel/devices/storage/storage_controller.hpp | 71 ++++++++++++ .../kernel/devices/storage/storage_management.hpp | 77 +++++++++++++ kernel/include/kernel/filesystem/custody.hpp | 23 ++++ kernel/include/kernel/filesystem/device_file.hpp | 32 ++++++ .../kernel/filesystem/ext2/ext2_filesystem.hpp | 24 ++++ kernel/include/kernel/filesystem/file.hpp | 19 ++++ .../kernel/filesystem/file_descriptor_table.hpp | 31 ++++++ kernel/include/kernel/filesystem/filesystem.hpp | 28 +++++ kernel/include/kernel/filesystem/inode.hpp | 38 +++++++ kernel/include/kernel/filesystem/inode_file.hpp | 27 +++++ .../include/kernel/filesystem/inode_metadata.hpp | 24 ++++ kernel/include/kernel/filesystem/mount.hpp | 25 +++++ .../kernel/filesystem/open_file_description.hpp | 28 +++++ kernel/include/kernel/filesystem/vfs.hpp | 46 ++++++++ kernel/src/devices/block_device.cpp | 42 +++++++ kernel/src/devices/device.cpp | 28 +++++ .../storage/ram_disk/ram_disk_controller.cpp | 28 +++++ .../devices/storage/ram_disk/ram_disk_device.cpp | 77 +++++++++++++ kernel/src/devices/storage/storage_controller.cpp | 44 ++++++++ kernel/src/devices/storage/storage_management.cpp | 85 ++++++++++++++ kernel/src/filesystem/custody.cpp | 30 +++++ kernel/src/filesystem/device_file.cpp | 124 +++++++++++++++++++++ kernel/src/filesystem/ext2/ext2_filesystem.cpp | 33 ++++++ kernel/src/filesystem/file_descriptor_table.cpp | 82 ++++++++++++++ kernel/src/filesystem/filesystem.cpp | 11 ++ kernel/src/filesystem/inode.cpp | 108 ++++++++++++++++++ kernel/src/filesystem/inode_file.cpp | 35 ++++++ kernel/src/filesystem/mount.cpp | 32 ++++++ kernel/src/filesystem/open_file_description.cpp | 35 ++++++ kernel/src/filesystem/vfs.cpp | 123 ++++++++++++++++++++ kernel/src/main.cpp | 10 +- 72 files changed, 1676 insertions(+), 1739 deletions(-) delete mode 100644 kernel/devices/CMakeLists.txt delete mode 100644 kernel/devices/include/devices/block_device.hpp delete mode 100644 kernel/devices/include/devices/device.hpp delete mode 100644 kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp delete mode 100644 kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp delete mode 100644 kernel/devices/include/devices/storage/storage_controller.hpp delete mode 100644 kernel/devices/include/devices/storage/storage_management.hpp delete mode 100644 kernel/devices/src/block_device.cpp delete mode 100644 kernel/devices/src/device.cpp delete mode 100644 kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp delete mode 100644 kernel/devices/src/storage/ram_disk/ram_disk_device.cpp delete mode 100644 kernel/devices/src/storage/storage_controller.cpp delete mode 100644 kernel/devices/src/storage/storage_management.cpp delete mode 100644 kernel/filesystem/CMakeLists.txt delete mode 100644 kernel/filesystem/include/filesystem/custody.hpp delete mode 100644 kernel/filesystem/include/filesystem/device_file.hpp delete mode 100644 kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp delete mode 100644 kernel/filesystem/include/filesystem/file.hpp delete mode 100644 kernel/filesystem/include/filesystem/file_descriptor_table.hpp delete mode 100644 kernel/filesystem/include/filesystem/filesystem.hpp delete mode 100644 kernel/filesystem/include/filesystem/inode.hpp delete mode 100644 kernel/filesystem/include/filesystem/inode_file.hpp delete mode 100644 kernel/filesystem/include/filesystem/inode_metadata.hpp delete mode 100644 kernel/filesystem/include/filesystem/mount.hpp delete mode 100644 kernel/filesystem/include/filesystem/open_file_description.hpp delete mode 100644 kernel/filesystem/include/filesystem/vfs.hpp delete mode 100644 kernel/filesystem/src/custody.cpp delete mode 100644 kernel/filesystem/src/device_file.cpp delete mode 100644 kernel/filesystem/src/ext2/ext2_filesystem.cpp delete mode 100644 kernel/filesystem/src/file_descriptor_table.cpp delete mode 100644 kernel/filesystem/src/filesystem.cpp delete mode 100644 kernel/filesystem/src/inode.cpp delete mode 100644 kernel/filesystem/src/inode_file.cpp delete mode 100644 kernel/filesystem/src/mount.cpp delete mode 100644 kernel/filesystem/src/open_file_description.cpp delete mode 100644 kernel/filesystem/src/vfs.cpp create mode 100644 kernel/include/kernel/devices/block_device.hpp create mode 100644 kernel/include/kernel/devices/device.hpp create mode 100644 kernel/include/kernel/devices/storage/ram_disk/ram_disk_controller.hpp create mode 100644 kernel/include/kernel/devices/storage/ram_disk/ram_disk_device.hpp create mode 100644 kernel/include/kernel/devices/storage/storage_controller.hpp create mode 100644 kernel/include/kernel/devices/storage/storage_management.hpp create mode 100644 kernel/include/kernel/filesystem/custody.hpp create mode 100644 kernel/include/kernel/filesystem/device_file.hpp create mode 100644 kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp create mode 100644 kernel/include/kernel/filesystem/file.hpp create mode 100644 kernel/include/kernel/filesystem/file_descriptor_table.hpp create mode 100644 kernel/include/kernel/filesystem/filesystem.hpp create mode 100644 kernel/include/kernel/filesystem/inode.hpp create mode 100644 kernel/include/kernel/filesystem/inode_file.hpp create mode 100644 kernel/include/kernel/filesystem/inode_metadata.hpp create mode 100644 kernel/include/kernel/filesystem/mount.hpp create mode 100644 kernel/include/kernel/filesystem/open_file_description.hpp create mode 100644 kernel/include/kernel/filesystem/vfs.hpp create mode 100644 kernel/src/devices/block_device.cpp create mode 100644 kernel/src/devices/device.cpp create mode 100644 kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp create mode 100644 kernel/src/devices/storage/ram_disk/ram_disk_device.cpp create mode 100644 kernel/src/devices/storage/storage_controller.cpp create mode 100644 kernel/src/devices/storage/storage_management.cpp create mode 100644 kernel/src/filesystem/custody.cpp create mode 100644 kernel/src/filesystem/device_file.cpp create mode 100644 kernel/src/filesystem/ext2/ext2_filesystem.cpp create mode 100644 kernel/src/filesystem/file_descriptor_table.cpp create mode 100644 kernel/src/filesystem/filesystem.cpp create mode 100644 kernel/src/filesystem/inode.cpp create mode 100644 kernel/src/filesystem/inode_file.cpp create mode 100644 kernel/src/filesystem/mount.cpp create mode 100644 kernel/src/filesystem/open_file_description.cpp create mode 100644 kernel/src/filesystem/vfs.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 830c527..398022c 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -1,6 +1,3 @@ -add_subdirectory("devices") -add_subdirectory("filesystem") - add_executable("kernel" # Platform-independent KAPI implementation "kapi/boot_modules.cpp" @@ -18,6 +15,22 @@ add_executable("kernel" "src/memory/block_list_allocator.cpp" "src/memory/operators.cpp" "src/memory.cpp" + "src/devices/device.cpp" + "src/devices/block_device.cpp" + "src/devices/storage/storage_controller.cpp" + "src/devices/storage/storage_management.cpp" + "src/devices/storage/ram_disk/ram_disk_controller.cpp" + "src/devices/storage/ram_disk/ram_disk_device.cpp" + "src/filesystem/ext2/ext2_filesystem.cpp" + "src/filesystem/custody.cpp" + "src/filesystem/device_file.cpp" + "src/filesystem/file_descriptor_table.cpp" + "src/filesystem/filesystem.cpp" + "src/filesystem/inode_file.cpp" + "src/filesystem/inode.cpp" + "src/filesystem/mount.cpp" + "src/filesystem/open_file_description.cpp" + "src/filesystem/vfs.cpp" ) target_include_directories("kernel" PRIVATE @@ -27,8 +40,6 @@ target_include_directories("kernel" PRIVATE target_link_libraries("kernel" PRIVATE "os::arch" "os::kapi" - "kernel::devices" - "kernel::filesystem" ) target_link_options("kernel" PRIVATE diff --git a/kernel/devices/CMakeLists.txt b/kernel/devices/CMakeLists.txt deleted file mode 100644 index 15d03fa..0000000 --- a/kernel/devices/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -add_library("kernel_devices" STATIC) -add_library("kernel::devices" ALIAS "kernel_devices") - -target_sources("kernel_devices" PRIVATE - "src/device.cpp" - "src/block_device.cpp" - "src/storage/storage_controller.cpp" - "src/storage/storage_management.cpp" - "src/storage/ram_disk/ram_disk_controller.cpp" - "src/storage/ram_disk/ram_disk_device.cpp" -) - -target_sources("kernel_devices" PUBLIC - FILE_SET HEADERS - BASE_DIRS "include" - FILES - "include/devices/device.hpp" - "include/devices/block_device.hpp" - "include/devices/storage/storage_controller.hpp" - "include/devices/storage/storage_management.hpp" - "include/devices/storage/ram_disk/ram_disk_controller.hpp" - "include/devices/storage/ram_disk/ram_disk_device.hpp" -) - -target_include_directories("kernel_devices" PUBLIC - "include" -) - -target_link_libraries("kernel_devices" PRIVATE - "os::kapi" -) \ No newline at end of file diff --git a/kernel/devices/include/devices/block_device.hpp b/kernel/devices/include/devices/block_device.hpp deleted file mode 100644 index 4831be5..0000000 --- a/kernel/devices/include/devices/block_device.hpp +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP -#define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP - -#include "devices/device.hpp" - -#include -#include - -namespace devices -{ - /** - * @brief Base interface for block-addressable devices. - */ - struct block_device : device - { - /** - * @brief Create a block device descriptor. - * @param major Device major number. - * @param minor Device minor number. - * @param name Device name. - * @param block_size Size of one logical block in bytes. - */ - block_device(size_t major, size_t minor, std::string_view name, size_t block_size); - - /** - * @brief Virtual destructor for block device. - */ - virtual ~block_device() = default; - - /** - * @brief Read data from the block at @p block_index into @p buffer. - * @param block_index Zero-based block index. - * @param buffer Destination buffer. - * @warning Panics if @p buffer is null. - * @note Reads up to one logical block (see constructor @p block_size). Implementations may perform a partial - * transfer for the final block when fewer than @p block_size bytes remain. - */ - virtual auto read_block(size_t block_index, void * buffer) const -> void = 0; - - /** - * @brief Write data to the block at @p block_index. - * @param block_index Zero-based block index. - * @param buffer Source buffer, must not be null. - * @warning Panics if @p buffer is null. - * @note Writes up to one logical block (see constructor @p block_size). - * Implementations may perform a partial transfer for the final block when - * fewer than @p block_size bytes remain. - */ - virtual auto write_block(size_t block_index, void const * buffer) -> void = 0; - - /** - * @brief Return logical block size in bytes. - * @return One logical block size in bytes. - */ - [[nodiscard]] auto block_size() const -> size_t; - - /** - * @brief Return device capacity in bytes. - * @return Total number of addressable bytes. - */ - [[nodiscard]] auto capacity() const -> size_t; - - /** - * @brief Override to identify block devices. - * @return true if this device is a block device, false otherwise. - */ - - [[nodiscard]] auto is_block_device() const -> bool override - { - return true; - } - - protected: - /** - * @brief Information describing the transfer window for one block index. - */ - struct transfer_info - { - size_t offset; - size_t to_transfer; - size_t remainder; - }; - - /** - * @brief Return total device size in bytes. - * @return Total number of addressable bytes. - */ - [[nodiscard]] virtual auto size() const -> size_t = 0; - - /** - * @brief Compute transfer information for @p block_index. - * @param block_index Zero-based block index. - * @return Computed transfer information for one logical block access. - */ - [[nodiscard]] auto calculate_transfer(size_t block_index) const -> transfer_info; - - size_t m_block_size; - }; -} // namespace devices - -#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/device.hpp b/kernel/devices/include/devices/device.hpp deleted file mode 100644 index d6f520f..0000000 --- a/kernel/devices/include/devices/device.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_DEVICE_HPP -#define TEACH_OS_KERNEL_DEVICES_DEVICE_HPP - -#include -#include - -namespace devices -{ - /** - * @brief Base device identified by a major, minor number and name. - */ - struct device - { - /** - * @brief Create a device identifier from @p major, @p minor and @p name. - * @param major Device major number. - * @param minor Device minor number. - * @param name Device name. - */ - device(size_t major, size_t minor, std::string_view name); - - /** - * @brief Virtual destructor for device. - */ - virtual ~device() = default; - - /** - * @brief Returns the major number of the device. - * @return Device major number. - */ - [[nodiscard]] auto major() const -> size_t; - - /** - * @brief Returns the minor number of the device. - * @return Device minor number. - */ - [[nodiscard]] auto minor() const -> size_t; - - /** - * @brief Returns the name of the device. - * @return Device name. - */ - [[nodiscard]] auto name() const -> std::string_view; - - /** - * @brief Check if the device is a block device. - * @return true if this device is a block device, false otherwise. - */ - [[nodiscard]] virtual auto is_block_device() const -> bool - { - return false; - } - - private: - size_t m_major; - size_t m_minor; - std::string_view m_name; - }; -} // namespace devices - -#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp b/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp deleted file mode 100644 index f69f8d8..0000000 --- a/kernel/devices/include/devices/storage/ram_disk/ram_disk_controller.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP -#define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP - -#include "kapi/boot_module/boot_module_registry.hpp" - -#include "devices/storage/storage_controller.hpp" - -namespace devices::storage::ram_disk -{ - /** - * @brief Storage controller that exposes boot modules as RAM-disk devices. - */ - struct ram_disk_controller : storage_controller - { - /** - * @brief Create a RAM-disk controller. - * @param registry Boot module registry as device source. - */ - explicit ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry); - - /** - * @brief Probe boot modules and create RAM-disk devices. - */ - auto probe() -> void override; - - private: - kapi::boot_modules::boot_module_registry const * m_boot_module_registry; - }; -} // namespace devices::storage::ram_disk - -#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp b/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp deleted file mode 100644 index 1edf48c..0000000 --- a/kernel/devices/include/devices/storage/ram_disk/ram_disk_device.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP -#define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP - -#include "kapi/boot_module/boot_module.hpp" - -#include "devices/block_device.hpp" - -#include - -namespace devices::storage::ram_disk -{ - /** - * @brief Block device for a boot module. - */ - struct ram_disk_device : block_device - { - /** - * @brief Create a RAM disk for the @p module. - * @param module Boot module providing the memory region. - * @param major Device major number. - * @param minor Device minor number. - */ - ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor); - - /** - * @brief Read one logical block into @p buffer. - * @param block_index Zero-based block index. - * @param buffer Destination buffer, must not be null. - * @note If the request reaches the module end, only available bytes are copied and the rest of the - * logical block is filled with zeros. - */ - auto read_block(size_t block_index, void * buffer) const -> void override; - - /** - * @brief Write one logical block from @p buffer. - * @param block_index Zero-based block index. - * @param buffer Source buffer, must not be null. - * @note If the request reaches the module end, only the bytes in the module range are written. - */ - auto write_block(size_t block_index, void const * buffer) -> void override; - - private: - /** - * @brief Return module size in bytes. - */ - [[nodiscard]] auto size() const -> size_t override; - - kapi::boot_modules::boot_module m_boot_module{}; - }; -} // namespace devices::storage::ram_disk - -#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/storage_controller.hpp b/kernel/devices/include/devices/storage/storage_controller.hpp deleted file mode 100644 index 7760fc9..0000000 --- a/kernel/devices/include/devices/storage/storage_controller.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP -#define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP - -#include "devices/device.hpp" - -#include -#include - -#include - -namespace devices::storage -{ - /** - * @brief Base interface for storage controllers. - * - * A storage controller probes for devices and resolves devices by major/minor - * numbers. - */ - struct storage_controller - { - /** - * @brief Virtual destructor. - */ - virtual ~storage_controller() = default; - - /** - * @brief Probe the controller and register discovered devices. - */ - virtual auto probe() -> void = 0; - - /** - * @brief Assign the major number and minor stride for this controller. - * @param major Major number assigned to this controller. - * @param minors_per_dev Minor number stride between devices. - */ - auto set_ids(size_t major, size_t minors_per_dev) -> void; - - /** - * @brief Return the assigned major number. - * @return Assigned major number. - */ - [[nodiscard]] auto major() const -> size_t; - - /** - * @brief Return the number of devices managed by this controller. - * @return Number of managed devices. - */ - [[nodiscard]] auto devices_count() const -> size_t; - - /** - * @brief Return all devices managed by this controller. - * @return Vector of all managed devices. - */ - [[nodiscard]] auto all_devices() const -> kstd::vector> const &; - - /** - * @brief Find a managed device by major/minor numbers. - * @param major Device major number. - * @param minor Device minor number. - * @return Matching block device, or nullptr if no device matches. - */ - [[nodiscard]] auto device_by_minor(size_t minor) const -> kstd::shared_ptr; - - protected: - size_t m_major{}; - size_t m_minors_per_device{}; - kstd::vector> m_devices{}; - }; -} // namespace devices::storage - -#endif \ No newline at end of file diff --git a/kernel/devices/include/devices/storage/storage_management.hpp b/kernel/devices/include/devices/storage/storage_management.hpp deleted file mode 100644 index f8ffda8..0000000 --- a/kernel/devices/include/devices/storage/storage_management.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP -#define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP - -#include "devices/device.hpp" -#include "devices/storage/storage_controller.hpp" - -#include -#include - -#include - -namespace devices::storage -{ - /** - * @brief Global storage subsystem manager. - * - * Owns registered storage controllers and provides device lookup by - * major/minor numbers. - */ - struct storage_management - { - /** - * @brief Initialize global storage management. - * - * Creates the singleton instance, registers controllers and probes - * them for devices. - * - * @warning Panics if called more than once. - */ - auto static init() -> void; - - /** - * @brief Return the active storage manager singleton. - * @return Reference to the active storage manager. - * @warning Panics if storage management has not been initialized. - */ - auto static get() -> storage_management &; - - /** - * @brief Register a storage controller. - * @param controller Controller to register. - * - * Assigns controller IDs (major number range and minors per device). - */ - auto add_controller(kstd::shared_ptr const & controller) -> void; - - /** - * @brief Return all registered storage controllers. - * @return Vector of all registered storage controllers. - */ - [[nodiscard]] auto all_controllers() const -> kstd::vector> const &; - - /** - * @brief Find a device by major/minor numbers. - * @param major Device major number. - * @param minor Device minor number. - * @return Matching device, or nullptr if no device matches. - */ - auto device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr; - - /** - * @brief Determine the boot device. - * @return Boot device, or nullptr if it cannot be determined. - */ - auto determine_boot_device() -> kstd::shared_ptr; - - private: - /** - * @brief Private default constructor for storage management singleton. - */ - storage_management() = default; - - kstd::vector> m_controllers{}; - }; -} // namespace devices::storage - -#endif \ No newline at end of file diff --git a/kernel/devices/src/block_device.cpp b/kernel/devices/src/block_device.cpp deleted file mode 100644 index 40f842a..0000000 --- a/kernel/devices/src/block_device.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "devices/block_device.hpp" - -#include "kapi/system.hpp" - -#include "devices/device.hpp" - -#include -#include - -namespace devices -{ - block_device::block_device(size_t major, size_t minor, std::string_view name, size_t block_size) - : device(major, minor, name) - , m_block_size(block_size) - { - if (m_block_size == 0) - { - kapi::system::panic("[DEVICES] block_device constructed with zero block size."); - } - } - - auto block_device::calculate_transfer(size_t block_index) const -> transfer_info - { - size_t const offset = block_index * m_block_size; - size_t const limit = size(); - - size_t const available = (offset < limit) ? (limit - offset) : 0; - size_t const to_transfer = (available < m_block_size) ? available : m_block_size; - - return {offset, to_transfer, m_block_size - to_transfer}; - } - - auto block_device::block_size() const -> size_t - { - return m_block_size; - } - - auto block_device::capacity() const -> size_t - { - return size(); - } -} // namespace devices \ No newline at end of file diff --git a/kernel/devices/src/device.cpp b/kernel/devices/src/device.cpp deleted file mode 100644 index 8184321..0000000 --- a/kernel/devices/src/device.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "devices/device.hpp" - -#include -#include - -namespace devices -{ - device::device(size_t major, size_t minor, std::string_view name) - : m_major(major) - , m_minor(minor) - , m_name(name) - {} - - auto device::major() const -> size_t - { - return m_major; - } - - auto device::minor() const -> size_t - { - return m_minor; - } - - auto device::name() const -> std::string_view - { - return m_name; - } -} // namespace devices \ No newline at end of file diff --git a/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp b/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp deleted file mode 100644 index 3d14faf..0000000 --- a/kernel/devices/src/storage/ram_disk/ram_disk_controller.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "devices/storage/ram_disk/ram_disk_controller.hpp" - -#include "kapi/boot_module/boot_module_registry.hpp" - -#include "devices/storage/ram_disk/ram_disk_device.hpp" - -#include -#include - -#include -#include - -namespace devices::storage::ram_disk -{ - ram_disk_controller::ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry) - : m_boot_module_registry(registry) - {} - - auto ram_disk_controller::probe() -> void - { - size_t current_device_index = 0; - - std::ranges::for_each(*m_boot_module_registry, [this, ¤t_device_index](auto const & module) { - auto const minor = current_device_index++ * m_minors_per_device; - m_devices.push_back(kstd::make_shared(module, m_major, minor)); - }); - } -} // namespace devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp b/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp deleted file mode 100644 index 91a278e..0000000 --- a/kernel/devices/src/storage/ram_disk/ram_disk_device.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "devices/storage/ram_disk/ram_disk_device.hpp" - -#include "kapi/boot_module/boot_module.hpp" -#include "kapi/system.hpp" - -#include "devices/block_device.hpp" - -#include - -#include -#include -#include - -namespace devices::storage::ram_disk -{ - namespace - { - constexpr size_t RAM_DISK_BLOCK_SIZE = 512uz; - - // TODO BA-FS26 @Felix - // TODO BA-FS26 currently only names for 9 minor devices - constinit std::array name = {'r', 'a', 'm', '0', '\0'}; - - auto determine_device_name(size_t minor) -> std::string_view - { - name[3] = '0' + minor; - return std::string_view{name}; - } - } // namespace - - ram_disk_device::ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor) - : block_device(major, minor, determine_device_name(minor), RAM_DISK_BLOCK_SIZE) - , m_boot_module(module) - {} - - auto ram_disk_device::read_block(size_t block_index, void * buffer) const -> void - { - if (buffer == nullptr) - { - kapi::system::panic("[RAM DISK DEVICE] read_block called with null buffer."); - } - - auto const info = calculate_transfer(block_index); - - if (info.to_transfer > 0) - { - auto const src = static_cast(m_boot_module.start_address) + info.offset; - kstd::libc::memcpy(buffer, src, info.to_transfer); - } - - if (info.remainder > 0) - { - kstd::libc::memset(static_cast(buffer) + info.to_transfer, 0, info.remainder); - } - } - - auto ram_disk_device::write_block(size_t block_index, void const * buffer) -> void - { - if (buffer == nullptr) - { - kapi::system::panic("[RAM DISK DEVICE] write_block called with null buffer."); - } - - auto const info = calculate_transfer(block_index); - - if (info.to_transfer > 0) - { - auto const dest = static_cast(m_boot_module.start_address) + info.offset; - kstd::libc::memcpy(dest, buffer, info.to_transfer); - } - } - - auto ram_disk_device::size() const -> size_t - { - return m_boot_module.size; - } -} // namespace devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/devices/src/storage/storage_controller.cpp b/kernel/devices/src/storage/storage_controller.cpp deleted file mode 100644 index 16d40f4..0000000 --- a/kernel/devices/src/storage/storage_controller.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "devices/storage/storage_controller.hpp" - -#include "devices/device.hpp" - -#include -#include - -#include -#include - -namespace devices::storage -{ - auto storage_controller::set_ids(size_t major, size_t minors_per_dev) -> void - { - m_major = major; - m_minors_per_device = minors_per_dev; - } - - auto storage_controller::major() const -> size_t - { - return m_major; - } - - auto storage_controller::device_by_minor(size_t minor) const -> kstd::shared_ptr - { - auto it = std::ranges::find_if(m_devices, [minor](auto const & device) { return device->minor() == minor; }); - - if (it != m_devices.end()) - { - return *it; - } - return nullptr; - } - - auto storage_controller::devices_count() const -> size_t - { - return m_devices.size(); - } - - auto storage_controller::all_devices() const -> kstd::vector> const & - { - return m_devices; - } -} // namespace devices::storage \ No newline at end of file diff --git a/kernel/devices/src/storage/storage_management.cpp b/kernel/devices/src/storage/storage_management.cpp deleted file mode 100644 index 25885ad..0000000 --- a/kernel/devices/src/storage/storage_management.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "devices/storage/storage_management.hpp" - -#include "kapi/boot_modules.hpp" -#include "kapi/system.hpp" - -#include "devices/device.hpp" -#include "devices/storage/ram_disk/ram_disk_controller.hpp" -#include "devices/storage/storage_controller.hpp" - -#include -#include - -#include -#include -#include - -namespace devices::storage -{ - namespace - { - constexpr size_t static MINORS_PER_DEVICE = 16; - constexpr size_t static START_MAJOR = 1; - constinit size_t static next_free_major = START_MAJOR; - - constinit auto static active_storage_management = std::optional{}; - } // namespace - - auto storage_management::init() -> void - { - if (active_storage_management) - { - kapi::system::panic("[DEVICES] Storage management has already been initialized."); - } - active_storage_management.emplace(storage_management{}); - - auto current_ram_disk_controller = - kstd::make_shared(&kapi::boot_modules::get_boot_module_registry()); - active_storage_management->add_controller(current_ram_disk_controller); - - std::ranges::for_each(active_storage_management->m_controllers, [](auto controller) { controller->probe(); }); - } - - auto storage_management::get() -> storage_management & - { - if (!active_storage_management) - { - kapi::system::panic("[DEVICES] Storage management has not been initialized."); - } - - return *active_storage_management; - } - - auto storage_management::add_controller(kstd::shared_ptr const & controller) -> void - { - controller->set_ids(next_free_major++, MINORS_PER_DEVICE); - m_controllers.push_back(controller); - } - - auto storage_management::all_controllers() const -> kstd::vector> const & - { - return m_controllers; - } - - auto storage_management::device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr - { - kstd::shared_ptr found = nullptr; - - std::ranges::find_if(m_controllers, [&](auto const & controller) { - if (controller != nullptr && controller->major() == major) - { - found = controller->device_by_minor(minor); - return found != nullptr; - } - return false; - }); - - return found; - } - - auto storage_management::determine_boot_device() -> kstd::shared_ptr - { - return device_by_major_minor(START_MAJOR, 0); - } - -} // namespace devices::storage \ No newline at end of file diff --git a/kernel/filesystem/CMakeLists.txt b/kernel/filesystem/CMakeLists.txt deleted file mode 100644 index bb2090a..0000000 --- a/kernel/filesystem/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -add_library("kernel_filesystem" STATIC) -add_library("kernel::filesystem" ALIAS "kernel_filesystem") - -target_sources("kernel_filesystem" PRIVATE - "src/ext2/ext2_filesystem.cpp" - "src/custody.cpp" - "src/device_file.cpp" - "src/file_descriptor_table.cpp" - "src/filesystem.cpp" - "src/inode_file.cpp" - "src/inode.cpp" - "src/mount.cpp" - "src/open_file_description.cpp" - "src/vfs.cpp" -) - -target_sources("kernel_filesystem" PUBLIC - FILE_SET HEADERS - BASE_DIRS "include" - FILES - "include/filesystem/ext2/ext2_filesystem.hpp" - "include/filesystem/custody.hpp" - "include/filesystem/device_file.hpp" - "include/filesystem/file_descriptor_table.hpp" - "include/filesystem/file.hpp" - "include/filesystem/filesystem.hpp" - "include/filesystem/inode_file.hpp" - "include/filesystem/inode_metadata.hpp" - "include/filesystem/inode.hpp" - "include/filesystem/mount.hpp" - "include/filesystem/open_file_description.hpp" - "include/filesystem/vfs.hpp" -) - -target_include_directories("kernel_filesystem" PUBLIC - "include" -) - -target_link_libraries("kernel_filesystem" PUBLIC - "kernel::devices" -PRIVATE - "os::kapi" -) diff --git a/kernel/filesystem/include/filesystem/custody.hpp b/kernel/filesystem/include/filesystem/custody.hpp deleted file mode 100644 index 0124eb6..0000000 --- a/kernel/filesystem/include/filesystem/custody.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef TEACH_OS_KERNEL_CUSTODY_HPP -#define TEACH_OS_KERNEL_CUSTODY_HPP - -#include "filesystem/inode.hpp" - -#include - -namespace filesystem -{ - struct custody - { - custody(kstd::shared_ptr const & parent, kstd::shared_ptr const & node); - - [[nodiscard]] auto get_inode() const -> kstd::shared_ptr const &; - [[nodiscard]] auto get_parent() const -> kstd::shared_ptr const &; - - private: - kstd::shared_ptr m_parent; - kstd::shared_ptr m_inode; - }; -} // namespace filesystem - -#endif \ No newline at end of file diff --git a/kernel/filesystem/include/filesystem/device_file.hpp b/kernel/filesystem/include/filesystem/device_file.hpp deleted file mode 100644 index dad4ea5..0000000 --- a/kernel/filesystem/include/filesystem/device_file.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVICE_FILE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_FILE_HPP - -#include "devices/block_device.hpp" -#include "devices/device.hpp" -#include "filesystem/file.hpp" - -#include - -#include - -namespace filesystem -{ - struct device_file : file - { - explicit device_file(kstd::shared_ptr const & device); - - auto open() -> void override; - - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; - - private: - using block_op = void (*)(size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, - std::byte * scratch, void * buffer); - auto process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t; - - kstd::shared_ptr m_device; - }; -} // namespace filesystem - -#endif diff --git a/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp b/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp deleted file mode 100644 index 37448a1..0000000 --- a/kernel/filesystem/include/filesystem/ext2/ext2_filesystem.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP - -#include "devices/device.hpp" -#include "filesystem/filesystem.hpp" -#include "filesystem/inode.hpp" - -#include - -#include - -namespace filesystem::ext2 -{ - struct ext2_filesystem : filesystem - { - auto mount(kstd::shared_ptr const & device) -> int override; - auto lookup(inode const & parent, std::string_view name) -> inode * override; - - private: - kstd::shared_ptr m_device{}; - }; -} // namespace filesystem::ext2 - -#endif diff --git a/kernel/filesystem/include/filesystem/file.hpp b/kernel/filesystem/include/filesystem/file.hpp deleted file mode 100644 index e7e1b12..0000000 --- a/kernel/filesystem/include/filesystem/file.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_FILE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_FILE_HPP - -#include - -namespace filesystem -{ - struct file - { - virtual ~file() = default; - - virtual auto open() -> void = 0; - - virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0; - virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0; - }; -} // namespace filesystem - -#endif \ No newline at end of file diff --git a/kernel/filesystem/include/filesystem/file_descriptor_table.hpp b/kernel/filesystem/include/filesystem/file_descriptor_table.hpp deleted file mode 100644 index 6d78532..0000000 --- a/kernel/filesystem/include/filesystem/file_descriptor_table.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP - -#include "open_file_description.hpp" - -#include - -#include - -namespace filesystem -{ - struct file_descriptor_table - { - auto static init() -> void; - auto static get() -> file_descriptor_table &; - - ~file_descriptor_table() = default; - - auto add_file(open_file_description & f) -> int; - [[nodiscard]] auto get_file(int fd) const -> std::optional; - auto remove_file(int fd) -> void; - - private: - file_descriptor_table() = default; - - // TODO BA-FS26 use kstd::shared_ptr when available - kstd::vector> m_open_files{}; - }; -} // namespace filesystem - -#endif \ No newline at end of file diff --git a/kernel/filesystem/include/filesystem/filesystem.hpp b/kernel/filesystem/include/filesystem/filesystem.hpp deleted file mode 100644 index 934f883..0000000 --- a/kernel/filesystem/include/filesystem/filesystem.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP - -#include "devices/device.hpp" -#include "filesystem/inode.hpp" - -#include - -#include - -namespace filesystem -{ - struct filesystem - { - virtual ~filesystem() = default; - - virtual auto mount(kstd::shared_ptr const & device) -> int = 0; - virtual auto lookup(inode const & parent, std::string_view name) -> inode * = 0; - - [[nodiscard]] auto root_inode() const -> inode const &; - - protected: - inode m_root_inode{}; // TODO BA-FS26 set during mount? - }; - -} // namespace filesystem - -#endif \ No newline at end of file diff --git a/kernel/filesystem/include/filesystem/inode.hpp b/kernel/filesystem/include/filesystem/inode.hpp deleted file mode 100644 index abfe2cf..0000000 --- a/kernel/filesystem/include/filesystem/inode.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP - -#include "devices/device.hpp" -#include "filesystem/inode_metadata.hpp" - -#include - -#include - -namespace filesystem -{ - struct inode - { - inode() = default; - explicit inode(inode_kind kind); - explicit inode(kstd::shared_ptr const & device); - - [[nodiscard]] auto metadata() const -> inode_metadata; - - [[nodiscard]] auto is_directory() const -> bool; - [[nodiscard]] auto is_regular() const -> bool; - [[nodiscard]] auto is_device() const -> bool; - [[nodiscard]] auto is_block_device() const -> bool; - [[nodiscard]] auto major_device() const -> size_t; - [[nodiscard]] auto minor_device() const -> size_t; - [[nodiscard]] auto backing_device() const -> kstd::shared_ptr const &; - - auto read(void * buffer, size_t offset, size_t size) const -> size_t; - auto write(void const * buffer, size_t offset, size_t size) -> size_t; - - private: - inode_kind m_kind{inode_kind::regular}; - kstd::shared_ptr m_device{}; - }; -} // namespace filesystem - -#endif diff --git a/kernel/filesystem/include/filesystem/inode_file.hpp b/kernel/filesystem/include/filesystem/inode_file.hpp deleted file mode 100644 index 5f8a95e..0000000 --- a/kernel/filesystem/include/filesystem/inode_file.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP - -#include "filesystem/file.hpp" -#include "filesystem/inode.hpp" - -#include - -#include - -namespace filesystem -{ - struct inode_file : file - { - explicit inode_file(kstd::shared_ptr const & inode); - - auto open() -> void override; - - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; - - private: - kstd::shared_ptr m_inode; - }; -} // namespace filesystem - -#endif \ No newline at end of file diff --git a/kernel/filesystem/include/filesystem/inode_metadata.hpp b/kernel/filesystem/include/filesystem/inode_metadata.hpp deleted file mode 100644 index ed5a09d..0000000 --- a/kernel/filesystem/include/filesystem/inode_metadata.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP - -#include -#include - -namespace filesystem -{ - enum class inode_kind - { - regular, - directory, - device - }; - - struct inode_metadata - { - inode_kind kind{inode_kind::regular}; - std::optional major{}; - std::optional minor{}; - }; -} // namespace filesystem - -#endif // TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP \ No newline at end of file diff --git a/kernel/filesystem/include/filesystem/mount.hpp b/kernel/filesystem/include/filesystem/mount.hpp deleted file mode 100644 index 46a68e2..0000000 --- a/kernel/filesystem/include/filesystem/mount.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_MOUNT_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_MOUNT_HPP - -#include "filesystem/filesystem.hpp" - -#include - -#include - -namespace filesystem -{ - struct mount - { - mount(std::string_view const & path, kstd::shared_ptr const & fs); - - [[nodiscard]] auto path() const -> std::string_view; - [[nodiscard]] auto get_filesystem() const -> kstd::shared_ptr const &; - - private: - std::string_view m_path; - kstd::shared_ptr m_filesystem{}; - }; -} // namespace filesystem - -#endif diff --git a/kernel/filesystem/include/filesystem/open_file_description.hpp b/kernel/filesystem/include/filesystem/open_file_description.hpp deleted file mode 100644 index 5ff094d..0000000 --- a/kernel/filesystem/include/filesystem/open_file_description.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP - -#include "file.hpp" - -#include - -#include - -namespace filesystem -{ - struct open_file_description - { - open_file_description(kstd::shared_ptr const & file); - - ~open_file_description() = default; - - auto read(void * buffer, size_t size) -> size_t; - auto write(void const * buffer, size_t size) -> size_t; - - private: - kstd::shared_ptr m_file; - size_t m_offset; - }; - -} // namespace filesystem - -#endif \ No newline at end of file diff --git a/kernel/filesystem/include/filesystem/vfs.hpp b/kernel/filesystem/include/filesystem/vfs.hpp deleted file mode 100644 index e16a961..0000000 --- a/kernel/filesystem/include/filesystem/vfs.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP - -#include "devices/device.hpp" -#include "filesystem/custody.hpp" -#include "filesystem/ext2/ext2_filesystem.hpp" -#include "filesystem/inode.hpp" -#include "filesystem/mount.hpp" -#include "filesystem/open_file_description.hpp" - -#include -#include - -#include -#include - -namespace filesystem -{ - struct vfs - { - auto static init() -> void; - auto static get() -> vfs &; - - ~vfs() = default; - - auto open(std::string_view path) -> std::optional; - - private: - struct device_node_entry - { - std::string_view name; - kstd::shared_ptr node; - }; - - vfs() = default; - auto make_device_node(kstd::shared_ptr const & device) -> void; - [[nodiscard]] auto resolve_path(std::string_view path) -> std::optional; - - kstd::shared_ptr m_root_fs; - std::optional m_root_mount; - // kstd::vector m_mounts; // TODO BA-FS26 really needed? - kstd::vector> m_device_nodes; // TODO BA-FS26 remove again, use devtempfs - }; -} // namespace filesystem - -#endif \ No newline at end of file diff --git a/kernel/filesystem/src/custody.cpp b/kernel/filesystem/src/custody.cpp deleted file mode 100644 index 3a87345..0000000 --- a/kernel/filesystem/src/custody.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "filesystem/custody.hpp" - -#include "kapi/system.hpp" - -#include "filesystem/inode.hpp" - -#include - -namespace filesystem -{ - custody::custody(kstd::shared_ptr const & parent, kstd::shared_ptr const & node) - : m_parent(parent) - , m_inode(node) - { - if (!m_inode) - { - kapi::system::panic("[FILESYSTEM] custody constructed with null inode."); - } - } - - auto custody::get_inode() const -> kstd::shared_ptr const & - { - return m_inode; - } - - auto custody::get_parent() const -> kstd::shared_ptr const & - { - return m_parent; - } -} // namespace filesystem \ No newline at end of file diff --git a/kernel/filesystem/src/device_file.cpp b/kernel/filesystem/src/device_file.cpp deleted file mode 100644 index 8fc159a..0000000 --- a/kernel/filesystem/src/device_file.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include "filesystem/device_file.hpp" - -#include "kapi/system.hpp" - -#include "devices/block_device.hpp" -#include "devices/device.hpp" - -#include -#include -#include - -#include -#include - -namespace filesystem -{ - device_file::device_file(kstd::shared_ptr const & device) - : m_device(device) - { - if (!m_device) - { - kapi::system::panic("[FILESYSTEM] device_file constructed with null device."); - } - } - - auto device_file::open() -> void - { - // Hook point for permission checks or lazy metadata loading. - } - - auto device_file::read(void * buffer, size_t offset, size_t size) const -> size_t - { - if (m_device->is_block_device()) - { - return process_blocks(offset, size, buffer, - [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, - std::byte * scratch, void * buffer) { - auto * out = static_cast(buffer); - if (off == 0 && len == device->block_size()) - { - device->read_block(idx, out + done); - } - else - { - device->read_block(idx, scratch); - kstd::libc::memcpy(out + done, scratch + off, len); - } - }); - } - else - { - kapi::system::panic("[FILESYSTEM] device_file::read called on non-block device."); - } - } - - auto device_file::write(void const * buffer, size_t offset, size_t size) -> size_t - { - if (m_device->is_block_device()) - { - return process_blocks(offset, size, const_cast(buffer), - [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, - std::byte * scratch, void * buffer) { - auto const * in = static_cast(buffer); - if (off == 0 && len == device->block_size()) - { - device->write_block(idx, in + done); - } - else - { - device->read_block(idx, scratch); - kstd::libc::memcpy(scratch + off, in + done, len); - device->write_block(idx, scratch); - } - }); - } - else - { - kapi::system::panic("[FILESYSTEM] device_file::write called on non-block device."); - } - } - - auto device_file::process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t - { - if (buffer == nullptr) - { - kapi::system::panic("[FILESYSTEM] device_file::write called with null buffer."); - } - - if (size == 0) - { - return 0; - } - - auto * block_dev = static_cast(m_device.get()); - if (block_dev == nullptr) - { - kapi::system::panic("[FILESYSTEM] device_file: expected block_device."); - } - - size_t const block_size = block_dev->block_size(); - size_t const capacity = block_dev->capacity(); - - if (offset >= capacity) - return 0; - size_t const total_to_process = std::min(size, capacity - offset); - - kstd::vector scratch_buffer{block_size}; - auto processed = 0uz; - - while (processed < total_to_process) - { - size_t const absolute_offset = offset + processed; - size_t const block_index = absolute_offset / block_size; - size_t const in_block_offset = absolute_offset % block_size; - size_t const chunk_size = std::min(total_to_process - processed, block_size - in_block_offset); - - op(block_index, in_block_offset, chunk_size, processed, block_dev, scratch_buffer.data(), buffer); - - processed += chunk_size; - } - - return processed; - } -} // namespace filesystem diff --git a/kernel/filesystem/src/ext2/ext2_filesystem.cpp b/kernel/filesystem/src/ext2/ext2_filesystem.cpp deleted file mode 100644 index a82a098..0000000 --- a/kernel/filesystem/src/ext2/ext2_filesystem.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "filesystem/ext2/ext2_filesystem.hpp" - -#include "devices/device.hpp" -#include "filesystem/inode.hpp" -#include "filesystem/inode_metadata.hpp" - -#include - -#include - -namespace filesystem::ext2 -{ - auto ext2_filesystem::mount(kstd::shared_ptr const & device) -> int - { - if (!device) - { - return -1; // TODO BA-FS26 panic or errorcode? - } - - m_device = device; - // TODO BA-FS26 load proper root inode from ext2 metadata - m_root_inode = inode{inode_kind::directory}; - - // TODO BA-FS26 implement - return 0; - } - - auto ext2_filesystem::lookup(inode const & /*parent*/, std::string_view /*name*/) -> inode * - { - // TODO BA-FS26 implement ext2 directory traversal and inode loading - return nullptr; - } -} // namespace filesystem::ext2 diff --git a/kernel/filesystem/src/file_descriptor_table.cpp b/kernel/filesystem/src/file_descriptor_table.cpp deleted file mode 100644 index 64fad0c..0000000 --- a/kernel/filesystem/src/file_descriptor_table.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "filesystem/file_descriptor_table.hpp" - -#include "kapi/system.hpp" - -#include "filesystem/open_file_description.hpp" - -#include -#include -#include - -namespace filesystem -{ - namespace - { - constinit auto static global_file_descriptor_table = std::optional{}; - } // namespace - - auto file_descriptor_table::init() -> void - { - if (global_file_descriptor_table) - { - kapi::system::panic("[FILESYSTEM] File descriptor table has already been initialized."); - } - - global_file_descriptor_table.emplace(file_descriptor_table{}); - } - - auto file_descriptor_table::get() -> file_descriptor_table & - { - if (!global_file_descriptor_table) - { - kapi::system::panic("[FILESYSTEM] File descriptor table has not been initialized."); - } - - return *global_file_descriptor_table; - } - - auto file_descriptor_table::add_file(open_file_description & file_description) -> int - { - auto it = std::ranges::find_if(m_open_files, [](auto & open_file) { return !open_file.has_value(); }); - if (it != m_open_files.end()) - { - *it = file_description; - return static_cast(it - m_open_files.begin()); - } - - m_open_files.push_back(file_description); - return static_cast(m_open_files.size() - 1); - } - - auto file_descriptor_table::get_file(int fd) const -> std::optional - { - if (fd < 0) - { - return std::nullopt; - } - - auto const index = static_cast(fd); - if (index >= m_open_files.size() || !m_open_files.at(fd).has_value()) - { - return std::nullopt; - } - - return m_open_files.at(fd); - } - - auto file_descriptor_table::remove_file(int fd) -> void - { - if (fd < 0) - { - return; - } - - auto const index = static_cast(fd); - if (index >= m_open_files.size()) - { - return; - } - - m_open_files.at(fd).reset(); - } -} // namespace filesystem \ No newline at end of file diff --git a/kernel/filesystem/src/filesystem.cpp b/kernel/filesystem/src/filesystem.cpp deleted file mode 100644 index cdfe7f8..0000000 --- a/kernel/filesystem/src/filesystem.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "filesystem/filesystem.hpp" - -#include "filesystem/inode.hpp" - -namespace filesystem -{ - auto filesystem::root_inode() const -> inode const & - { - return m_root_inode; - } -} // namespace filesystem \ No newline at end of file diff --git a/kernel/filesystem/src/inode.cpp b/kernel/filesystem/src/inode.cpp deleted file mode 100644 index 231ee33..0000000 --- a/kernel/filesystem/src/inode.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "filesystem/inode.hpp" - -#include "kapi/system.hpp" - -#include "devices/device.hpp" -#include "filesystem/inode_metadata.hpp" - -#include - -#include - -namespace filesystem -{ - inode::inode(inode_kind kind) - : m_kind(kind) - {} - - inode::inode(kstd::shared_ptr const & device) - : m_kind(inode_kind::device) - , m_device(device) - { - if (!m_device) - { - kapi::system::panic("[FILESYSTEM] inode constructed with null device."); - } - } - - auto inode::metadata() const -> inode_metadata - { - auto meta = inode_metadata{}; - meta.kind = m_kind; - - if (is_device()) - { - meta.major = m_device->major(); - meta.minor = m_device->minor(); - } - - return meta; - } - - auto inode::is_directory() const -> bool - { - return m_kind == inode_kind::directory; - } - - auto inode::is_regular() const -> bool - { - return m_kind == inode_kind::regular; - } - - auto inode::is_device() const -> bool - { - return m_kind == inode_kind::device; - } - - auto inode::is_block_device() const -> bool - { - return is_device() && m_device->is_block_device(); - } - - auto inode::major_device() const -> size_t - { - if (!is_device()) - { - kapi::system::panic("[FILESYSTEM] inode::major_device called on non-device inode."); - } - - return m_device->major(); - } - - auto inode::minor_device() const -> size_t - { - if (!is_device()) - { - kapi::system::panic("[FILESYSTEM] inode::minor_device called on non-device inode."); - } - - return m_device->minor(); - } - - auto inode::backing_device() const -> kstd::shared_ptr const & - { - return m_device; - } - - auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t - { - if (is_device()) - { - kapi::system::panic("[FILESYSTEM] inode::read called on device inode. Open it as a device file first."); - } - - // TODO BA-FS26 - return 0; - } - - auto inode::write(void const *, size_t, size_t) -> size_t - { - if (is_device()) - { - kapi::system::panic("[FILESYSTEM] inode::write called on device inode. Open it as a device file first."); - } - - kapi::system::panic("[FILESYSTEM] inode::write is not implemented yet"); - return 0; - } -} // namespace filesystem \ No newline at end of file diff --git a/kernel/filesystem/src/inode_file.cpp b/kernel/filesystem/src/inode_file.cpp deleted file mode 100644 index 0b8bdf9..0000000 --- a/kernel/filesystem/src/inode_file.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "filesystem/inode_file.hpp" - -#include "kapi/system.hpp" - -#include "filesystem/inode.hpp" - -#include - -#include - -namespace filesystem -{ - inode_file::inode_file(kstd::shared_ptr const & inode) - : m_inode(inode) - { - if (!m_inode) - { - kapi::system::panic("[FILESYSTEM] inode_file constructed with null inode"); - } - } - - auto inode_file::open() -> void - { - // Hook point for permission checks or lazy metadata loading. - } - - auto inode_file::read(void * buffer, size_t offset, size_t size) const -> size_t - { - return m_inode->read(buffer, offset, size); - } - auto inode_file::write(void const * buffer, size_t offset, size_t size) -> size_t - { - return m_inode->write(buffer, offset, size); - } -} // namespace filesystem \ No newline at end of file diff --git a/kernel/filesystem/src/mount.cpp b/kernel/filesystem/src/mount.cpp deleted file mode 100644 index 021b074..0000000 --- a/kernel/filesystem/src/mount.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "filesystem/mount.hpp" - -#include "kapi/system.hpp" - -#include "filesystem/filesystem.hpp" - -#include - -#include - -namespace filesystem -{ - mount::mount(std::string_view const & path, kstd::shared_ptr const & fs) - : m_path(path) - , m_filesystem(fs) - { - if (!m_filesystem) - { - kapi::system::panic("[FILESYSTEM] mount initialized with null filesystem."); - } - } - - auto mount::path() const -> std::string_view - { - return m_path; - } - - auto mount::get_filesystem() const -> kstd::shared_ptr const & - { - return m_filesystem; - } -} // namespace filesystem \ No newline at end of file diff --git a/kernel/filesystem/src/open_file_description.cpp b/kernel/filesystem/src/open_file_description.cpp deleted file mode 100644 index cddee76..0000000 --- a/kernel/filesystem/src/open_file_description.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "filesystem/open_file_description.hpp" - -#include "filesystem/file.hpp" - -#include -#include - -#include - -namespace filesystem -{ - open_file_description::open_file_description(kstd::shared_ptr const & file) - : m_file(file) - , m_offset(0) - { - if (!file) - { - kstd::os::panic("[FILESYSTEM] open_file_description constructed with null file."); - } - } - - auto open_file_description::read(void * buffer, size_t size) -> size_t - { - auto read_bytes = m_file->read(buffer, m_offset, size); - m_offset += read_bytes; - return read_bytes; - } - - auto open_file_description::write(void const * buffer, size_t size) -> size_t - { - auto written_bytes = m_file->write(buffer, m_offset, size); - m_offset += written_bytes; - return written_bytes; - } -} // namespace filesystem \ No newline at end of file diff --git a/kernel/filesystem/src/vfs.cpp b/kernel/filesystem/src/vfs.cpp deleted file mode 100644 index c0f1c3a..0000000 --- a/kernel/filesystem/src/vfs.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "filesystem/vfs.hpp" - -#include "kapi/system.hpp" - -#include "devices/device.hpp" -#include "devices/storage/storage_management.hpp" -#include "filesystem/custody.hpp" -#include "filesystem/device_file.hpp" -#include "filesystem/ext2/ext2_filesystem.hpp" -#include "filesystem/inode.hpp" -#include "filesystem/inode_file.hpp" -#include "filesystem/mount.hpp" -#include "filesystem/open_file_description.hpp" - -#include -#include - -#include -#include -#include - -namespace filesystem -{ - namespace - { - constinit auto static active_vfs = std::optional{}; - } // namespace - - auto vfs::init() -> void - { - if (active_vfs) - { - kapi::system::panic("[FILESYSTEM] vfs has already been initialized."); - } - - active_vfs.emplace(vfs{}); - - auto storage_mgmt = devices::storage::storage_management::get(); - if (auto boot_device = storage_mgmt.determine_boot_device()) - { - active_vfs->m_root_fs = kstd::make_shared(); - if (active_vfs->m_root_fs->mount(boot_device) != 0) - { - kapi::system::panic("[FILESYSTEM] Failed to mount root filesystem."); - } - - active_vfs->m_root_mount = mount{"/", active_vfs->m_root_fs}; - - std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto controller) { - std::ranges::for_each(controller->all_devices(), [&](auto device) { active_vfs->make_device_node(device); }); - }); - } - else - { - // TODO BA-FS26 ?? what when no boot_device == no modules loaded?? - } - } - - auto vfs::get() -> vfs & - { - if (!active_vfs) - { - kapi::system::panic("[FILESYSTEM] vfs has not been initialized."); - } - - return *active_vfs; - } - - auto vfs::open(std::string_view path) -> std::optional - { - if (auto custody = resolve_path(path)) - { - auto node = custody->get_inode(); - if (node->is_device()) - { - auto current_device_file = kstd::make_shared(node->backing_device()); - current_device_file->open(); - return open_file_description{current_device_file}; - } - - auto current_inode_file = kstd::make_shared(node); - current_inode_file->open(); - return open_file_description{current_inode_file}; - } - - return std::nullopt; - } - - auto vfs::make_device_node(kstd::shared_ptr const & device) -> void - { - if (!device) - { - kapi::system::panic("[FILESYSTEM] make_device_node called with null device."); - } - - m_device_nodes.push_back(device_node_entry{device->name(), kstd::make_shared(device)}); - } - - auto vfs::resolve_path(std::string_view path) -> std::optional - { - // TODO BA-FS26 implement real path resolution with mounts and directories etc. - // For now, just support device nodes at /dev/. - - constexpr auto device_prefix = std::string_view{"/dev/"}; - if (path.starts_with(device_prefix)) - { - auto const device_name = path.substr(device_prefix.size()); - auto entry = std::ranges::find_if(m_device_nodes, [&](auto const & device_entry) { - return device_entry.has_value() && device_entry->name == device_name; - }); - - if (entry != m_device_nodes.end()) - { - return custody{nullptr, entry->value().node}; - } - - return std::nullopt; - } - - return std::nullopt; - } - -} // namespace filesystem \ No newline at end of file diff --git a/kernel/include/kernel/devices/block_device.hpp b/kernel/include/kernel/devices/block_device.hpp new file mode 100644 index 0000000..2a6f061 --- /dev/null +++ b/kernel/include/kernel/devices/block_device.hpp @@ -0,0 +1,101 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP +#define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP + +#include "kernel/devices/device.hpp" + +#include +#include + +namespace devices +{ + /** + * @brief Base interface for block-addressable devices. + */ + struct block_device : device + { + /** + * @brief Create a block device descriptor. + * @param major Device major number. + * @param minor Device minor number. + * @param name Device name. + * @param block_size Size of one logical block in bytes. + */ + block_device(size_t major, size_t minor, std::string_view name, size_t block_size); + + /** + * @brief Virtual destructor for block device. + */ + virtual ~block_device() = default; + + /** + * @brief Read data from the block at @p block_index into @p buffer. + * @param block_index Zero-based block index. + * @param buffer Destination buffer. + * @warning Panics if @p buffer is null. + * @note Reads up to one logical block (see constructor @p block_size). Implementations may perform a partial + * transfer for the final block when fewer than @p block_size bytes remain. + */ + virtual auto read_block(size_t block_index, void * buffer) const -> void = 0; + + /** + * @brief Write data to the block at @p block_index. + * @param block_index Zero-based block index. + * @param buffer Source buffer, must not be null. + * @warning Panics if @p buffer is null. + * @note Writes up to one logical block (see constructor @p block_size). + * Implementations may perform a partial transfer for the final block when + * fewer than @p block_size bytes remain. + */ + virtual auto write_block(size_t block_index, void const * buffer) -> void = 0; + + /** + * @brief Return logical block size in bytes. + * @return One logical block size in bytes. + */ + [[nodiscard]] auto block_size() const -> size_t; + + /** + * @brief Return device capacity in bytes. + * @return Total number of addressable bytes. + */ + [[nodiscard]] auto capacity() const -> size_t; + + /** + * @brief Override to identify block devices. + * @return true if this device is a block device, false otherwise. + */ + + [[nodiscard]] auto is_block_device() const -> bool override + { + return true; + } + + protected: + /** + * @brief Information describing the transfer window for one block index. + */ + struct transfer_info + { + size_t offset; + size_t to_transfer; + size_t remainder; + }; + + /** + * @brief Return total device size in bytes. + * @return Total number of addressable bytes. + */ + [[nodiscard]] virtual auto size() const -> size_t = 0; + + /** + * @brief Compute transfer information for @p block_index. + * @param block_index Zero-based block index. + * @return Computed transfer information for one logical block access. + */ + [[nodiscard]] auto calculate_transfer(size_t block_index) const -> transfer_info; + + size_t m_block_size; + }; +} // namespace devices + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/device.hpp b/kernel/include/kernel/devices/device.hpp new file mode 100644 index 0000000..d6f520f --- /dev/null +++ b/kernel/include/kernel/devices/device.hpp @@ -0,0 +1,61 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_DEVICE_HPP +#define TEACH_OS_KERNEL_DEVICES_DEVICE_HPP + +#include +#include + +namespace devices +{ + /** + * @brief Base device identified by a major, minor number and name. + */ + struct device + { + /** + * @brief Create a device identifier from @p major, @p minor and @p name. + * @param major Device major number. + * @param minor Device minor number. + * @param name Device name. + */ + device(size_t major, size_t minor, std::string_view name); + + /** + * @brief Virtual destructor for device. + */ + virtual ~device() = default; + + /** + * @brief Returns the major number of the device. + * @return Device major number. + */ + [[nodiscard]] auto major() const -> size_t; + + /** + * @brief Returns the minor number of the device. + * @return Device minor number. + */ + [[nodiscard]] auto minor() const -> size_t; + + /** + * @brief Returns the name of the device. + * @return Device name. + */ + [[nodiscard]] auto name() const -> std::string_view; + + /** + * @brief Check if the device is a block device. + * @return true if this device is a block device, false otherwise. + */ + [[nodiscard]] virtual auto is_block_device() const -> bool + { + return false; + } + + private: + size_t m_major; + size_t m_minor; + std::string_view m_name; + }; +} // namespace devices + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/ram_disk/ram_disk_controller.hpp b/kernel/include/kernel/devices/storage/ram_disk/ram_disk_controller.hpp new file mode 100644 index 0000000..6f022e3 --- /dev/null +++ b/kernel/include/kernel/devices/storage/ram_disk/ram_disk_controller.hpp @@ -0,0 +1,31 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP + +#include "kapi/boot_module/boot_module_registry.hpp" + +#include "kernel/devices/storage/storage_controller.hpp" + +namespace devices::storage::ram_disk +{ + /** + * @brief Storage controller that exposes boot modules as RAM-disk devices. + */ + struct ram_disk_controller : storage_controller + { + /** + * @brief Create a RAM-disk controller. + * @param registry Boot module registry as device source. + */ + explicit ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry); + + /** + * @brief Probe boot modules and create RAM-disk devices. + */ + auto probe() -> void override; + + private: + kapi::boot_modules::boot_module_registry const * m_boot_module_registry; + }; +} // namespace devices::storage::ram_disk + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/ram_disk/ram_disk_device.hpp b/kernel/include/kernel/devices/storage/ram_disk/ram_disk_device.hpp new file mode 100644 index 0000000..c323f4b --- /dev/null +++ b/kernel/include/kernel/devices/storage/ram_disk/ram_disk_device.hpp @@ -0,0 +1,52 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP + +#include "kapi/boot_module/boot_module.hpp" + +#include "kernel/devices/block_device.hpp" + +#include + +namespace devices::storage::ram_disk +{ + /** + * @brief Block device for a boot module. + */ + struct ram_disk_device : block_device + { + /** + * @brief Create a RAM disk for the @p module. + * @param module Boot module providing the memory region. + * @param major Device major number. + * @param minor Device minor number. + */ + ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor); + + /** + * @brief Read one logical block into @p buffer. + * @param block_index Zero-based block index. + * @param buffer Destination buffer, must not be null. + * @note If the request reaches the module end, only available bytes are copied and the rest of the + * logical block is filled with zeros. + */ + auto read_block(size_t block_index, void * buffer) const -> void override; + + /** + * @brief Write one logical block from @p buffer. + * @param block_index Zero-based block index. + * @param buffer Source buffer, must not be null. + * @note If the request reaches the module end, only the bytes in the module range are written. + */ + auto write_block(size_t block_index, void const * buffer) -> void override; + + private: + /** + * @brief Return module size in bytes. + */ + [[nodiscard]] auto size() const -> size_t override; + + kapi::boot_modules::boot_module m_boot_module{}; + }; +} // namespace devices::storage::ram_disk + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/storage_controller.hpp b/kernel/include/kernel/devices/storage/storage_controller.hpp new file mode 100644 index 0000000..58585fa --- /dev/null +++ b/kernel/include/kernel/devices/storage/storage_controller.hpp @@ -0,0 +1,71 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP + +#include "kernel/devices/device.hpp" + +#include +#include + +#include + +namespace devices::storage +{ + /** + * @brief Base interface for storage controllers. + * + * A storage controller probes for devices and resolves devices by major/minor + * numbers. + */ + struct storage_controller + { + /** + * @brief Virtual destructor. + */ + virtual ~storage_controller() = default; + + /** + * @brief Probe the controller and register discovered devices. + */ + virtual auto probe() -> void = 0; + + /** + * @brief Assign the major number and minor stride for this controller. + * @param major Major number assigned to this controller. + * @param minors_per_dev Minor number stride between devices. + */ + auto set_ids(size_t major, size_t minors_per_dev) -> void; + + /** + * @brief Return the assigned major number. + * @return Assigned major number. + */ + [[nodiscard]] auto major() const -> size_t; + + /** + * @brief Return the number of devices managed by this controller. + * @return Number of managed devices. + */ + [[nodiscard]] auto devices_count() const -> size_t; + + /** + * @brief Return all devices managed by this controller. + * @return Vector of all managed devices. + */ + [[nodiscard]] auto all_devices() const -> kstd::vector> const &; + + /** + * @brief Find a managed device by major/minor numbers. + * @param major Device major number. + * @param minor Device minor number. + * @return Matching block device, or nullptr if no device matches. + */ + [[nodiscard]] auto device_by_minor(size_t minor) const -> kstd::shared_ptr; + + protected: + size_t m_major{}; + size_t m_minors_per_device{}; + kstd::vector> m_devices{}; + }; +} // namespace devices::storage + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/storage_management.hpp b/kernel/include/kernel/devices/storage/storage_management.hpp new file mode 100644 index 0000000..413820e --- /dev/null +++ b/kernel/include/kernel/devices/storage/storage_management.hpp @@ -0,0 +1,77 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP + +#include "kernel/devices/device.hpp" +#include "kernel/devices/storage/storage_controller.hpp" + +#include +#include + +#include + +namespace devices::storage +{ + /** + * @brief Global storage subsystem manager. + * + * Owns registered storage controllers and provides device lookup by + * major/minor numbers. + */ + struct storage_management + { + /** + * @brief Initialize global storage management. + * + * Creates the singleton instance, registers controllers and probes + * them for devices. + * + * @warning Panics if called more than once. + */ + auto static init() -> void; + + /** + * @brief Return the active storage manager singleton. + * @return Reference to the active storage manager. + * @warning Panics if storage management has not been initialized. + */ + auto static get() -> storage_management &; + + /** + * @brief Register a storage controller. + * @param controller Controller to register. + * + * Assigns controller IDs (major number range and minors per device). + */ + auto add_controller(kstd::shared_ptr const & controller) -> void; + + /** + * @brief Return all registered storage controllers. + * @return Vector of all registered storage controllers. + */ + [[nodiscard]] auto all_controllers() const -> kstd::vector> const &; + + /** + * @brief Find a device by major/minor numbers. + * @param major Device major number. + * @param minor Device minor number. + * @return Matching device, or nullptr if no device matches. + */ + auto device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr; + + /** + * @brief Determine the boot device. + * @return Boot device, or nullptr if it cannot be determined. + */ + auto determine_boot_device() -> kstd::shared_ptr; + + private: + /** + * @brief Private default constructor for storage management singleton. + */ + storage_management() = default; + + kstd::vector> m_controllers{}; + }; +} // namespace devices::storage + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/custody.hpp b/kernel/include/kernel/filesystem/custody.hpp new file mode 100644 index 0000000..8a0e09a --- /dev/null +++ b/kernel/include/kernel/filesystem/custody.hpp @@ -0,0 +1,23 @@ +#ifndef TEACH_OS_KERNEL_CUSTODY_HPP +#define TEACH_OS_KERNEL_CUSTODY_HPP + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace filesystem +{ + struct custody + { + custody(kstd::shared_ptr const & parent, kstd::shared_ptr const & node); + + [[nodiscard]] auto get_inode() const -> kstd::shared_ptr const &; + [[nodiscard]] auto get_parent() const -> kstd::shared_ptr const &; + + private: + kstd::shared_ptr m_parent; + kstd::shared_ptr m_inode; + }; +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/device_file.hpp b/kernel/include/kernel/filesystem/device_file.hpp new file mode 100644 index 0000000..06aa9b2 --- /dev/null +++ b/kernel/include/kernel/filesystem/device_file.hpp @@ -0,0 +1,32 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVICE_FILE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_FILE_HPP + +#include "kernel/devices/block_device.hpp" +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/file.hpp" + +#include + +#include + +namespace filesystem +{ + struct device_file : file + { + explicit device_file(kstd::shared_ptr const & device); + + auto open() -> void override; + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + private: + using block_op = void (*)(size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, + std::byte * scratch, void * buffer); + auto process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t; + + kstd::shared_ptr m_device; + }; +} // namespace filesystem + +#endif diff --git a/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp b/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp new file mode 100644 index 0000000..d6f69c8 --- /dev/null +++ b/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp @@ -0,0 +1,24 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP + +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/filesystem/inode.hpp" + +#include + +#include + +namespace filesystem::ext2 +{ + struct ext2_filesystem : filesystem + { + auto mount(kstd::shared_ptr const & device) -> int override; + auto lookup(inode const & parent, std::string_view name) -> inode * override; + + private: + kstd::shared_ptr m_device{}; + }; +} // namespace filesystem::ext2 + +#endif diff --git a/kernel/include/kernel/filesystem/file.hpp b/kernel/include/kernel/filesystem/file.hpp new file mode 100644 index 0000000..e7e1b12 --- /dev/null +++ b/kernel/include/kernel/filesystem/file.hpp @@ -0,0 +1,19 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_FILE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_FILE_HPP + +#include + +namespace filesystem +{ + struct file + { + virtual ~file() = default; + + virtual auto open() -> void = 0; + + virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0; + virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0; + }; +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/filesystem/file_descriptor_table.hpp new file mode 100644 index 0000000..6d78532 --- /dev/null +++ b/kernel/include/kernel/filesystem/file_descriptor_table.hpp @@ -0,0 +1,31 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP + +#include "open_file_description.hpp" + +#include + +#include + +namespace filesystem +{ + struct file_descriptor_table + { + auto static init() -> void; + auto static get() -> file_descriptor_table &; + + ~file_descriptor_table() = default; + + auto add_file(open_file_description & f) -> int; + [[nodiscard]] auto get_file(int fd) const -> std::optional; + auto remove_file(int fd) -> void; + + private: + file_descriptor_table() = default; + + // TODO BA-FS26 use kstd::shared_ptr when available + kstd::vector> m_open_files{}; + }; +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp new file mode 100644 index 0000000..23924c4 --- /dev/null +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -0,0 +1,28 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP + +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/inode.hpp" + +#include + +#include + +namespace filesystem +{ + struct filesystem + { + virtual ~filesystem() = default; + + virtual auto mount(kstd::shared_ptr const & device) -> int = 0; + virtual auto lookup(inode const & parent, std::string_view name) -> inode * = 0; + + [[nodiscard]] auto root_inode() const -> inode const &; + + protected: + inode m_root_inode{}; // TODO BA-FS26 set during mount? + }; + +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp new file mode 100644 index 0000000..a2955f9 --- /dev/null +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -0,0 +1,38 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP + +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/inode_metadata.hpp" + +#include + +#include + +namespace filesystem +{ + struct inode + { + inode() = default; + explicit inode(inode_kind kind); + explicit inode(kstd::shared_ptr const & device); + + [[nodiscard]] auto metadata() const -> inode_metadata; + + [[nodiscard]] auto is_directory() const -> bool; + [[nodiscard]] auto is_regular() const -> bool; + [[nodiscard]] auto is_device() const -> bool; + [[nodiscard]] auto is_block_device() const -> bool; + [[nodiscard]] auto major_device() const -> size_t; + [[nodiscard]] auto minor_device() const -> size_t; + [[nodiscard]] auto backing_device() const -> kstd::shared_ptr const &; + + auto read(void * buffer, size_t offset, size_t size) const -> size_t; + auto write(void const * buffer, size_t offset, size_t size) -> size_t; + + private: + inode_kind m_kind{inode_kind::regular}; + kstd::shared_ptr m_device{}; + }; +} // namespace filesystem + +#endif diff --git a/kernel/include/kernel/filesystem/inode_file.hpp b/kernel/include/kernel/filesystem/inode_file.hpp new file mode 100644 index 0000000..ee84eee --- /dev/null +++ b/kernel/include/kernel/filesystem/inode_file.hpp @@ -0,0 +1,27 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP + +#include "kernel/filesystem/file.hpp" +#include "kernel/filesystem/inode.hpp" + +#include + +#include + +namespace filesystem +{ + struct inode_file : file + { + explicit inode_file(kstd::shared_ptr const & inode); + + auto open() -> void override; + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + private: + kstd::shared_ptr m_inode; + }; +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/inode_metadata.hpp b/kernel/include/kernel/filesystem/inode_metadata.hpp new file mode 100644 index 0000000..ed5a09d --- /dev/null +++ b/kernel/include/kernel/filesystem/inode_metadata.hpp @@ -0,0 +1,24 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP + +#include +#include + +namespace filesystem +{ + enum class inode_kind + { + regular, + directory, + device + }; + + struct inode_metadata + { + inode_kind kind{inode_kind::regular}; + std::optional major{}; + std::optional minor{}; + }; +} // namespace filesystem + +#endif // TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp new file mode 100644 index 0000000..232a9be --- /dev/null +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -0,0 +1,25 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_MOUNT_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_MOUNT_HPP + +#include "kernel/filesystem/filesystem.hpp" + +#include + +#include + +namespace filesystem +{ + struct mount + { + mount(std::string_view const & path, kstd::shared_ptr const & fs); + + [[nodiscard]] auto path() const -> std::string_view; + [[nodiscard]] auto get_filesystem() const -> kstd::shared_ptr const &; + + private: + std::string_view m_path; + kstd::shared_ptr m_filesystem{}; + }; +} // namespace filesystem + +#endif diff --git a/kernel/include/kernel/filesystem/open_file_description.hpp b/kernel/include/kernel/filesystem/open_file_description.hpp new file mode 100644 index 0000000..5ff094d --- /dev/null +++ b/kernel/include/kernel/filesystem/open_file_description.hpp @@ -0,0 +1,28 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP + +#include "file.hpp" + +#include + +#include + +namespace filesystem +{ + struct open_file_description + { + open_file_description(kstd::shared_ptr const & file); + + ~open_file_description() = default; + + auto read(void * buffer, size_t size) -> size_t; + auto write(void const * buffer, size_t size) -> size_t; + + private: + kstd::shared_ptr m_file; + size_t m_offset; + }; + +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp new file mode 100644 index 0000000..a2894a6 --- /dev/null +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -0,0 +1,46 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP + +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/custody.hpp" +#include "kernel/filesystem/ext2/ext2_filesystem.hpp" +#include "kernel/filesystem/inode.hpp" +#include "kernel/filesystem/mount.hpp" +#include "kernel/filesystem/open_file_description.hpp" + +#include +#include + +#include +#include + +namespace filesystem +{ + struct vfs + { + auto static init() -> void; + auto static get() -> vfs &; + + ~vfs() = default; + + auto open(std::string_view path) -> std::optional; + + private: + struct device_node_entry + { + std::string_view name; + kstd::shared_ptr node; + }; + + vfs() = default; + auto make_device_node(kstd::shared_ptr const & device) -> void; + [[nodiscard]] auto resolve_path(std::string_view path) -> std::optional; + + kstd::shared_ptr m_root_fs; + std::optional m_root_mount; + // kstd::vector m_mounts; // TODO BA-FS26 really needed? + kstd::vector> m_device_nodes; // TODO BA-FS26 remove again, use devtempfs + }; +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/src/devices/block_device.cpp b/kernel/src/devices/block_device.cpp new file mode 100644 index 0000000..d12251b --- /dev/null +++ b/kernel/src/devices/block_device.cpp @@ -0,0 +1,42 @@ +#include "kernel/devices/block_device.hpp" + +#include "kapi/system.hpp" + +#include "kernel/devices/device.hpp" + +#include +#include + +namespace devices +{ + block_device::block_device(size_t major, size_t minor, std::string_view name, size_t block_size) + : device(major, minor, name) + , m_block_size(block_size) + { + if (m_block_size == 0) + { + kapi::system::panic("[DEVICES] block_device constructed with zero block size."); + } + } + + auto block_device::calculate_transfer(size_t block_index) const -> transfer_info + { + size_t const offset = block_index * m_block_size; + size_t const limit = size(); + + size_t const available = (offset < limit) ? (limit - offset) : 0; + size_t const to_transfer = (available < m_block_size) ? available : m_block_size; + + return {offset, to_transfer, m_block_size - to_transfer}; + } + + auto block_device::block_size() const -> size_t + { + return m_block_size; + } + + auto block_device::capacity() const -> size_t + { + return size(); + } +} // namespace devices \ No newline at end of file diff --git a/kernel/src/devices/device.cpp b/kernel/src/devices/device.cpp new file mode 100644 index 0000000..29498fa --- /dev/null +++ b/kernel/src/devices/device.cpp @@ -0,0 +1,28 @@ +#include "kernel/devices/device.hpp" + +#include +#include + +namespace devices +{ + device::device(size_t major, size_t minor, std::string_view name) + : m_major(major) + , m_minor(minor) + , m_name(name) + {} + + auto device::major() const -> size_t + { + return m_major; + } + + auto device::minor() const -> size_t + { + return m_minor; + } + + auto device::name() const -> std::string_view + { + return m_name; + } +} // namespace devices \ No newline at end of file diff --git a/kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp b/kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp new file mode 100644 index 0000000..efb6256 --- /dev/null +++ b/kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp @@ -0,0 +1,28 @@ +#include "kernel/devices/storage/ram_disk/ram_disk_controller.hpp" + +#include "kapi/boot_module/boot_module_registry.hpp" + +#include "kernel/devices/storage/ram_disk/ram_disk_device.hpp" + +#include +#include + +#include +#include + +namespace devices::storage::ram_disk +{ + ram_disk_controller::ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry) + : m_boot_module_registry(registry) + {} + + auto ram_disk_controller::probe() -> void + { + size_t current_device_index = 0; + + std::ranges::for_each(*m_boot_module_registry, [this, ¤t_device_index](auto const & module) { + auto const minor = current_device_index++ * m_minors_per_device; + m_devices.push_back(kstd::make_shared(module, m_major, minor)); + }); + } +} // namespace devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp b/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp new file mode 100644 index 0000000..650a151 --- /dev/null +++ b/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp @@ -0,0 +1,77 @@ +#include "kernel/devices/storage/ram_disk/ram_disk_device.hpp" + +#include "kapi/boot_module/boot_module.hpp" +#include "kapi/system.hpp" + +#include "kernel/devices/block_device.hpp" + +#include + +#include +#include +#include + +namespace devices::storage::ram_disk +{ + namespace + { + constexpr size_t RAM_DISK_BLOCK_SIZE = 512uz; + + // TODO BA-FS26 @Felix + // TODO BA-FS26 currently only names for 9 minor devices + constinit std::array name = {'r', 'a', 'm', '0', '\0'}; + + auto determine_device_name(size_t minor) -> std::string_view + { + name[3] = '0' + minor; + return std::string_view{name}; + } + } // namespace + + ram_disk_device::ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor) + : block_device(major, minor, determine_device_name(minor), RAM_DISK_BLOCK_SIZE) + , m_boot_module(module) + {} + + auto ram_disk_device::read_block(size_t block_index, void * buffer) const -> void + { + if (buffer == nullptr) + { + kapi::system::panic("[RAM DISK DEVICE] read_block called with null buffer."); + } + + auto const info = calculate_transfer(block_index); + + if (info.to_transfer > 0) + { + auto const src = static_cast(m_boot_module.start_address) + info.offset; + kstd::libc::memcpy(buffer, src, info.to_transfer); + } + + if (info.remainder > 0) + { + kstd::libc::memset(static_cast(buffer) + info.to_transfer, 0, info.remainder); + } + } + + auto ram_disk_device::write_block(size_t block_index, void const * buffer) -> void + { + if (buffer == nullptr) + { + kapi::system::panic("[RAM DISK DEVICE] write_block called with null buffer."); + } + + auto const info = calculate_transfer(block_index); + + if (info.to_transfer > 0) + { + auto const dest = static_cast(m_boot_module.start_address) + info.offset; + kstd::libc::memcpy(dest, buffer, info.to_transfer); + } + } + + auto ram_disk_device::size() const -> size_t + { + return m_boot_module.size; + } +} // namespace devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/src/devices/storage/storage_controller.cpp b/kernel/src/devices/storage/storage_controller.cpp new file mode 100644 index 0000000..e415436 --- /dev/null +++ b/kernel/src/devices/storage/storage_controller.cpp @@ -0,0 +1,44 @@ +#include "kernel/devices/storage/storage_controller.hpp" + +#include "kernel/devices/device.hpp" + +#include +#include + +#include +#include + +namespace devices::storage +{ + auto storage_controller::set_ids(size_t major, size_t minors_per_dev) -> void + { + m_major = major; + m_minors_per_device = minors_per_dev; + } + + auto storage_controller::major() const -> size_t + { + return m_major; + } + + auto storage_controller::device_by_minor(size_t minor) const -> kstd::shared_ptr + { + auto it = std::ranges::find_if(m_devices, [minor](auto const & device) { return device->minor() == minor; }); + + if (it != m_devices.end()) + { + return *it; + } + return nullptr; + } + + auto storage_controller::devices_count() const -> size_t + { + return m_devices.size(); + } + + auto storage_controller::all_devices() const -> kstd::vector> const & + { + return m_devices; + } +} // namespace devices::storage \ No newline at end of file diff --git a/kernel/src/devices/storage/storage_management.cpp b/kernel/src/devices/storage/storage_management.cpp new file mode 100644 index 0000000..56216b0 --- /dev/null +++ b/kernel/src/devices/storage/storage_management.cpp @@ -0,0 +1,85 @@ +#include "kernel/devices/storage/storage_management.hpp" + +#include "kapi/boot_modules.hpp" +#include "kapi/system.hpp" + +#include "kernel/devices/device.hpp" +#include "kernel/devices/storage/ram_disk/ram_disk_controller.hpp" +#include "kernel/devices/storage/storage_controller.hpp" + +#include +#include + +#include +#include +#include + +namespace devices::storage +{ + namespace + { + constexpr size_t static MINORS_PER_DEVICE = 16; + constexpr size_t static START_MAJOR = 1; + constinit size_t static next_free_major = START_MAJOR; + + constinit auto static active_storage_management = std::optional{}; + } // namespace + + auto storage_management::init() -> void + { + if (active_storage_management) + { + kapi::system::panic("[DEVICES] Storage management has already been initialized."); + } + active_storage_management.emplace(storage_management{}); + + auto current_ram_disk_controller = + kstd::make_shared(&kapi::boot_modules::get_boot_module_registry()); + active_storage_management->add_controller(current_ram_disk_controller); + + std::ranges::for_each(active_storage_management->m_controllers, [](auto controller) { controller->probe(); }); + } + + auto storage_management::get() -> storage_management & + { + if (!active_storage_management) + { + kapi::system::panic("[DEVICES] Storage management has not been initialized."); + } + + return *active_storage_management; + } + + auto storage_management::add_controller(kstd::shared_ptr const & controller) -> void + { + controller->set_ids(next_free_major++, MINORS_PER_DEVICE); + m_controllers.push_back(controller); + } + + auto storage_management::all_controllers() const -> kstd::vector> const & + { + return m_controllers; + } + + auto storage_management::device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr + { + kstd::shared_ptr found = nullptr; + + std::ranges::find_if(m_controllers, [&](auto const & controller) { + if (controller != nullptr && controller->major() == major) + { + found = controller->device_by_minor(minor); + return found != nullptr; + } + return false; + }); + + return found; + } + + auto storage_management::determine_boot_device() -> kstd::shared_ptr + { + return device_by_major_minor(START_MAJOR, 0); + } + +} // namespace devices::storage \ No newline at end of file diff --git a/kernel/src/filesystem/custody.cpp b/kernel/src/filesystem/custody.cpp new file mode 100644 index 0000000..a4dd12c --- /dev/null +++ b/kernel/src/filesystem/custody.cpp @@ -0,0 +1,30 @@ +#include "kernel/filesystem/custody.hpp" + +#include "kapi/system.hpp" + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace filesystem +{ + custody::custody(kstd::shared_ptr const & parent, kstd::shared_ptr const & node) + : m_parent(parent) + , m_inode(node) + { + if (!m_inode) + { + kapi::system::panic("[FILESYSTEM] custody constructed with null inode."); + } + } + + auto custody::get_inode() const -> kstd::shared_ptr const & + { + return m_inode; + } + + auto custody::get_parent() const -> kstd::shared_ptr const & + { + return m_parent; + } +} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/device_file.cpp b/kernel/src/filesystem/device_file.cpp new file mode 100644 index 0000000..c6db5af --- /dev/null +++ b/kernel/src/filesystem/device_file.cpp @@ -0,0 +1,124 @@ +#include "kernel/filesystem/device_file.hpp" + +#include "kapi/system.hpp" + +#include "kernel/devices/block_device.hpp" +#include "kernel/devices/device.hpp" + +#include +#include +#include + +#include +#include + +namespace filesystem +{ + device_file::device_file(kstd::shared_ptr const & device) + : m_device(device) + { + if (!m_device) + { + kapi::system::panic("[FILESYSTEM] device_file constructed with null device."); + } + } + + auto device_file::open() -> void + { + // Hook point for permission checks or lazy metadata loading. + } + + auto device_file::read(void * buffer, size_t offset, size_t size) const -> size_t + { + if (m_device->is_block_device()) + { + return process_blocks(offset, size, buffer, + [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, + std::byte * scratch, void * buffer) { + auto * out = static_cast(buffer); + if (off == 0 && len == device->block_size()) + { + device->read_block(idx, out + done); + } + else + { + device->read_block(idx, scratch); + kstd::libc::memcpy(out + done, scratch + off, len); + } + }); + } + else + { + kapi::system::panic("[FILESYSTEM] device_file::read called on non-block device."); + } + } + + auto device_file::write(void const * buffer, size_t offset, size_t size) -> size_t + { + if (m_device->is_block_device()) + { + return process_blocks(offset, size, const_cast(buffer), + [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, + std::byte * scratch, void * buffer) { + auto const * in = static_cast(buffer); + if (off == 0 && len == device->block_size()) + { + device->write_block(idx, in + done); + } + else + { + device->read_block(idx, scratch); + kstd::libc::memcpy(scratch + off, in + done, len); + device->write_block(idx, scratch); + } + }); + } + else + { + kapi::system::panic("[FILESYSTEM] device_file::write called on non-block device."); + } + } + + auto device_file::process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t + { + if (buffer == nullptr) + { + kapi::system::panic("[FILESYSTEM] device_file::write called with null buffer."); + } + + if (size == 0) + { + return 0; + } + + auto * block_dev = static_cast(m_device.get()); + if (block_dev == nullptr) + { + kapi::system::panic("[FILESYSTEM] device_file: expected block_device."); + } + + size_t const block_size = block_dev->block_size(); + size_t const capacity = block_dev->capacity(); + + if (offset >= capacity) + return 0; + size_t const total_to_process = std::min(size, capacity - offset); + + kstd::vector scratch_buffer{block_size}; + auto processed = 0uz; + + while (processed < total_to_process) + { + size_t const absolute_offset = offset + processed; + size_t const block_index = absolute_offset / block_size; + size_t const in_block_offset = absolute_offset % block_size; + size_t const chunk_size = std::min(total_to_process - processed, block_size - in_block_offset); + + op(block_index, in_block_offset, chunk_size, processed, block_dev, scratch_buffer.data(), buffer); + + processed += chunk_size; + } + + return processed; + } +} // namespace filesystem diff --git a/kernel/src/filesystem/ext2/ext2_filesystem.cpp b/kernel/src/filesystem/ext2/ext2_filesystem.cpp new file mode 100644 index 0000000..408b292 --- /dev/null +++ b/kernel/src/filesystem/ext2/ext2_filesystem.cpp @@ -0,0 +1,33 @@ +#include "kernel/filesystem/ext2/ext2_filesystem.hpp" + +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/inode.hpp" +#include "kernel/filesystem/inode_metadata.hpp" + +#include + +#include + +namespace filesystem::ext2 +{ + auto ext2_filesystem::mount(kstd::shared_ptr const & device) -> int + { + if (!device) + { + return -1; // TODO BA-FS26 panic or errorcode? + } + + m_device = device; + // TODO BA-FS26 load proper root inode from ext2 metadata + m_root_inode = inode{inode_kind::directory}; + + // TODO BA-FS26 implement + return 0; + } + + auto ext2_filesystem::lookup(inode const & /*parent*/, std::string_view /*name*/) -> inode * + { + // TODO BA-FS26 implement ext2 directory traversal and inode loading + return nullptr; + } +} // namespace filesystem::ext2 diff --git a/kernel/src/filesystem/file_descriptor_table.cpp b/kernel/src/filesystem/file_descriptor_table.cpp new file mode 100644 index 0000000..814322e --- /dev/null +++ b/kernel/src/filesystem/file_descriptor_table.cpp @@ -0,0 +1,82 @@ +#include "kernel/filesystem/file_descriptor_table.hpp" + +#include "kapi/system.hpp" + +#include "kernel/filesystem/open_file_description.hpp" + +#include +#include +#include + +namespace filesystem +{ + namespace + { + constinit auto static global_file_descriptor_table = std::optional{}; + } // namespace + + auto file_descriptor_table::init() -> void + { + if (global_file_descriptor_table) + { + kapi::system::panic("[FILESYSTEM] File descriptor table has already been initialized."); + } + + global_file_descriptor_table.emplace(file_descriptor_table{}); + } + + auto file_descriptor_table::get() -> file_descriptor_table & + { + if (!global_file_descriptor_table) + { + kapi::system::panic("[FILESYSTEM] File descriptor table has not been initialized."); + } + + return *global_file_descriptor_table; + } + + auto file_descriptor_table::add_file(open_file_description & file_description) -> int + { + auto it = std::ranges::find_if(m_open_files, [](auto & open_file) { return !open_file.has_value(); }); + if (it != m_open_files.end()) + { + *it = file_description; + return static_cast(it - m_open_files.begin()); + } + + m_open_files.push_back(file_description); + return static_cast(m_open_files.size() - 1); + } + + auto file_descriptor_table::get_file(int fd) const -> std::optional + { + if (fd < 0) + { + return std::nullopt; + } + + auto const index = static_cast(fd); + if (index >= m_open_files.size() || !m_open_files.at(fd).has_value()) + { + return std::nullopt; + } + + return m_open_files.at(fd); + } + + auto file_descriptor_table::remove_file(int fd) -> void + { + if (fd < 0) + { + return; + } + + auto const index = static_cast(fd); + if (index >= m_open_files.size()) + { + return; + } + + m_open_files.at(fd).reset(); + } +} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp new file mode 100644 index 0000000..86b7940 --- /dev/null +++ b/kernel/src/filesystem/filesystem.cpp @@ -0,0 +1,11 @@ +#include "kernel/filesystem/filesystem.hpp" + +#include "kernel/filesystem/inode.hpp" + +namespace filesystem +{ + auto filesystem::root_inode() const -> inode const & + { + return m_root_inode; + } +} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/inode.cpp b/kernel/src/filesystem/inode.cpp new file mode 100644 index 0000000..af73662 --- /dev/null +++ b/kernel/src/filesystem/inode.cpp @@ -0,0 +1,108 @@ +#include "kernel/filesystem/inode.hpp" + +#include "kapi/system.hpp" + +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/inode_metadata.hpp" + +#include + +#include + +namespace filesystem +{ + inode::inode(inode_kind kind) + : m_kind(kind) + {} + + inode::inode(kstd::shared_ptr const & device) + : m_kind(inode_kind::device) + , m_device(device) + { + if (!m_device) + { + kapi::system::panic("[FILESYSTEM] inode constructed with null device."); + } + } + + auto inode::metadata() const -> inode_metadata + { + auto meta = inode_metadata{}; + meta.kind = m_kind; + + if (is_device()) + { + meta.major = m_device->major(); + meta.minor = m_device->minor(); + } + + return meta; + } + + auto inode::is_directory() const -> bool + { + return m_kind == inode_kind::directory; + } + + auto inode::is_regular() const -> bool + { + return m_kind == inode_kind::regular; + } + + auto inode::is_device() const -> bool + { + return m_kind == inode_kind::device; + } + + auto inode::is_block_device() const -> bool + { + return is_device() && m_device->is_block_device(); + } + + auto inode::major_device() const -> size_t + { + if (!is_device()) + { + kapi::system::panic("[FILESYSTEM] inode::major_device called on non-device inode."); + } + + return m_device->major(); + } + + auto inode::minor_device() const -> size_t + { + if (!is_device()) + { + kapi::system::panic("[FILESYSTEM] inode::minor_device called on non-device inode."); + } + + return m_device->minor(); + } + + auto inode::backing_device() const -> kstd::shared_ptr const & + { + return m_device; + } + + auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t + { + if (is_device()) + { + kapi::system::panic("[FILESYSTEM] inode::read called on device inode. Open it as a device file first."); + } + + // TODO BA-FS26 + return 0; + } + + auto inode::write(void const *, size_t, size_t) -> size_t + { + if (is_device()) + { + kapi::system::panic("[FILESYSTEM] inode::write called on device inode. Open it as a device file first."); + } + + kapi::system::panic("[FILESYSTEM] inode::write is not implemented yet"); + return 0; + } +} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/inode_file.cpp b/kernel/src/filesystem/inode_file.cpp new file mode 100644 index 0000000..7abac7b --- /dev/null +++ b/kernel/src/filesystem/inode_file.cpp @@ -0,0 +1,35 @@ +#include "kernel/filesystem/inode_file.hpp" + +#include "kapi/system.hpp" + +#include "kernel/filesystem/inode.hpp" + +#include + +#include + +namespace filesystem +{ + inode_file::inode_file(kstd::shared_ptr const & inode) + : m_inode(inode) + { + if (!m_inode) + { + kapi::system::panic("[FILESYSTEM] inode_file constructed with null inode"); + } + } + + auto inode_file::open() -> void + { + // Hook point for permission checks or lazy metadata loading. + } + + auto inode_file::read(void * buffer, size_t offset, size_t size) const -> size_t + { + return m_inode->read(buffer, offset, size); + } + auto inode_file::write(void const * buffer, size_t offset, size_t size) -> size_t + { + return m_inode->write(buffer, offset, size); + } +} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp new file mode 100644 index 0000000..a2c501f --- /dev/null +++ b/kernel/src/filesystem/mount.cpp @@ -0,0 +1,32 @@ +#include "kernel/filesystem/mount.hpp" + +#include "kapi/system.hpp" + +#include "kernel/filesystem/filesystem.hpp" + +#include + +#include + +namespace filesystem +{ + mount::mount(std::string_view const & path, kstd::shared_ptr const & fs) + : m_path(path) + , m_filesystem(fs) + { + if (!m_filesystem) + { + kapi::system::panic("[FILESYSTEM] mount initialized with null filesystem."); + } + } + + auto mount::path() const -> std::string_view + { + return m_path; + } + + auto mount::get_filesystem() const -> kstd::shared_ptr const & + { + return m_filesystem; + } +} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/open_file_description.cpp b/kernel/src/filesystem/open_file_description.cpp new file mode 100644 index 0000000..ff4d678 --- /dev/null +++ b/kernel/src/filesystem/open_file_description.cpp @@ -0,0 +1,35 @@ +#include "kernel/filesystem/open_file_description.hpp" + +#include "kernel/filesystem/file.hpp" + +#include +#include + +#include + +namespace filesystem +{ + open_file_description::open_file_description(kstd::shared_ptr const & file) + : m_file(file) + , m_offset(0) + { + if (!file) + { + kstd::os::panic("[FILESYSTEM] open_file_description constructed with null file."); + } + } + + auto open_file_description::read(void * buffer, size_t size) -> size_t + { + auto read_bytes = m_file->read(buffer, m_offset, size); + m_offset += read_bytes; + return read_bytes; + } + + auto open_file_description::write(void const * buffer, size_t size) -> size_t + { + auto written_bytes = m_file->write(buffer, m_offset, size); + m_offset += written_bytes; + return written_bytes; + } +} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp new file mode 100644 index 0000000..8d2ed0a --- /dev/null +++ b/kernel/src/filesystem/vfs.cpp @@ -0,0 +1,123 @@ +#include "kernel/filesystem/vfs.hpp" + +#include "kapi/system.hpp" + +#include "kernel/devices/storage/storage_management.hpp" +#include "kernel/filesystem/custody.hpp" +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/device_file.hpp" +#include "kernel/filesystem/ext2/ext2_filesystem.hpp" +#include "kernel/filesystem/inode.hpp" +#include "kernel/filesystem/inode_file.hpp" +#include "kernel/filesystem/mount.hpp" +#include "kernel/filesystem/open_file_description.hpp" + +#include +#include + +#include +#include +#include + +namespace filesystem +{ + namespace + { + constinit auto static active_vfs = std::optional{}; + } // namespace + + auto vfs::init() -> void + { + if (active_vfs) + { + kapi::system::panic("[FILESYSTEM] vfs has already been initialized."); + } + + active_vfs.emplace(vfs{}); + + auto storage_mgmt = devices::storage::storage_management::get(); + if (auto boot_device = storage_mgmt.determine_boot_device()) + { + active_vfs->m_root_fs = kstd::make_shared(); + if (active_vfs->m_root_fs->mount(boot_device) != 0) + { + kapi::system::panic("[FILESYSTEM] Failed to mount root filesystem."); + } + + active_vfs->m_root_mount = mount{"/", active_vfs->m_root_fs}; + + std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto controller) { + std::ranges::for_each(controller->all_devices(), [&](auto device) { active_vfs->make_device_node(device); }); + }); + } + else + { + // TODO BA-FS26 ?? what when no boot_device == no modules loaded?? + } + } + + auto vfs::get() -> vfs & + { + if (!active_vfs) + { + kapi::system::panic("[FILESYSTEM] vfs has not been initialized."); + } + + return *active_vfs; + } + + auto vfs::open(std::string_view path) -> std::optional + { + if (auto custody = resolve_path(path)) + { + auto node = custody->get_inode(); + if (node->is_device()) + { + auto current_device_file = kstd::make_shared(node->backing_device()); + current_device_file->open(); + return open_file_description{current_device_file}; + } + + auto current_inode_file = kstd::make_shared(node); + current_inode_file->open(); + return open_file_description{current_inode_file}; + } + + return std::nullopt; + } + + auto vfs::make_device_node(kstd::shared_ptr const & device) -> void + { + if (!device) + { + kapi::system::panic("[FILESYSTEM] make_device_node called with null device."); + } + + m_device_nodes.push_back(device_node_entry{device->name(), kstd::make_shared(device)}); + } + + auto vfs::resolve_path(std::string_view path) -> std::optional + { + // TODO BA-FS26 implement real path resolution with mounts and directories etc. + // For now, just support device nodes at /dev/. + + constexpr auto device_prefix = std::string_view{"/dev/"}; + if (path.starts_with(device_prefix)) + { + auto const device_name = path.substr(device_prefix.size()); + auto entry = std::ranges::find_if(m_device_nodes, [&](auto const & device_entry) { + return device_entry.has_value() && device_entry->name == device_name; + }); + + if (entry != m_device_nodes.end()) + { + return custody{nullptr, entry->value().node}; + } + + return std::nullopt; + } + + return std::nullopt; + } + +} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index cf3afdf..6ced18c 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -3,11 +3,11 @@ #include "kapi/memory.hpp" #include "kapi/system.hpp" -#include "devices/storage/storage_management.hpp" -#include "filesystem/device_file.hpp" -#include "filesystem/file_descriptor_table.hpp" -#include "filesystem/open_file_description.hpp" -#include "filesystem/vfs.hpp" +#include "kernel/devices/storage/storage_management.hpp" +#include "kernel/filesystem/device_file.hpp" +#include "kernel/filesystem/file_descriptor_table.hpp" +#include "kernel/filesystem/open_file_description.hpp" +#include "kernel/filesystem/vfs.hpp" #include "kernel/memory.hpp" #include -- cgit v1.2.3 From fde097681f96e2c6f23ecd71a5c0037acb3ac79e Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Tue, 17 Mar 2026 19:47:53 +0100 Subject: Fix include order --- kernel/src/filesystem/vfs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 8d2ed0a..2123fc7 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -2,9 +2,9 @@ #include "kapi/system.hpp" +#include "kernel/devices/device.hpp" #include "kernel/devices/storage/storage_management.hpp" #include "kernel/filesystem/custody.hpp" -#include "kernel/devices/device.hpp" #include "kernel/filesystem/device_file.hpp" #include "kernel/filesystem/ext2/ext2_filesystem.hpp" #include "kernel/filesystem/inode.hpp" -- cgit v1.2.3 From 1f8cc6683f4e2ef2e311078f48a1b4477dadaaec Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 17 Mar 2026 22:21:33 +0100 Subject: x86_64/memory: fix region allocator logic --- arch/x86_64/src/memory/region_allocator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp index 2690a7c..4ee3ca4 100644 --- a/arch/x86_64/src/memory/region_allocator.cpp +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -85,14 +85,14 @@ namespace arch::memory { m_next_frame = m_kernel_end + 1; advanced = true; - break; + continue; } if (falls_within(m_next_frame, m_multiboot_start, m_multiboot_end)) { m_next_frame = m_multiboot_end + 1; advanced = true; - break; + continue; } if (m_multiboot_information) -- cgit v1.2.3 From 9ad3eafdfa46dc01d7a8737a59fe59caffd91d67 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 17 Mar 2026 22:22:29 +0100 Subject: kstd: fix constructor selection in vector The old version would lead to potential issues, since an explicit ctor may get selected. Ideally vector should be adapted to not allocated an array of it's value type but simply suitably aligned raw storage. --- libs/kstd/include/kstd/vector | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 7568cb6..41b380e 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -85,7 +85,7 @@ namespace kstd vector(vector const & other) : _size(other._size) , _capacity(other._capacity) - , _data(new value_type[_capacity]{}) + , _data(new value_type[_capacity]()) { std::ranges::copy(other, _data); } @@ -104,7 +104,7 @@ namespace kstd delete[] _data; _size = other._size; _capacity = other._capacity; - _data = new value_type[_capacity]{}; + _data = new value_type[_capacity](); std::ranges::copy(other, _data); return *this; } @@ -490,7 +490,7 @@ namespace kstd } _capacity = new_capacity; - auto temp = new value_type[_capacity]{}; + auto temp = new value_type[_capacity](); std::ranges::copy(begin(), end(), temp); delete[] _data; _data = temp; -- cgit v1.2.3 From 044a64ef019ce2ff241e72f2b7b3a444b922b798 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 17 Mar 2026 22:31:40 +0100 Subject: kstd: add more nodiscard to shared_ptr --- libs/kstd/include/kstd/bits/shared_ptr.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libs/kstd/include/kstd/bits/shared_ptr.hpp b/libs/kstd/include/kstd/bits/shared_ptr.hpp index ed23d29..6bce83f 100644 --- a/libs/kstd/include/kstd/bits/shared_ptr.hpp +++ b/libs/kstd/include/kstd/bits/shared_ptr.hpp @@ -250,7 +250,7 @@ namespace kstd * * @return Returns the object owned by *this, equivalent to *get(). */ - auto operator*() const -> T & + [[nodiscard]] auto operator*() const -> T & { return *pointer; } @@ -260,7 +260,7 @@ namespace kstd * * @return Returns a pointer to the object owned by *this, i.e. get(). */ - auto operator->() const -> T * + [[nodiscard]] auto operator->() const -> T * { return pointer; } @@ -270,7 +270,7 @@ namespace kstd * * @return Pointer to the managed object or nullptr if no object is owned. */ - auto get() const -> T * + [[nodiscard]] auto get() const -> T * { return pointer; } @@ -301,7 +301,7 @@ namespace kstd * * @return true if *this owns an object, false otherwise. */ - explicit operator bool() const + [[nodiscard]] explicit operator bool() const { return pointer != nullptr; } @@ -317,7 +317,7 @@ namespace kstd /** * @brief Compare nullptr with shared_ptr. */ - friend auto operator==(std::nullptr_t, shared_ptr const & ptr) -> bool + [[nodiscard]] friend auto operator==(std::nullptr_t, shared_ptr const & ptr) -> bool { return ptr.pointer == nullptr; } @@ -325,7 +325,7 @@ namespace kstd /** * @brief Defaulted three-way comparator operator. */ - auto operator<=>(shared_ptr const & other) const = default; + [[nodiscard]] auto operator<=>(shared_ptr const & other) const = default; private: /** -- cgit v1.2.3 From 483b7789c38e42b26fbdbf10601fe47567078a50 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 17 Mar 2026 22:32:06 +0100 Subject: kapi/memory: remove penalizing explicit --- kapi/include/kapi/memory/address.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kapi/include/kapi/memory/address.hpp b/kapi/include/kapi/memory/address.hpp index 3bef358..69fc7b9 100644 --- a/kapi/include/kapi/memory/address.hpp +++ b/kapi/include/kapi/memory/address.hpp @@ -33,7 +33,7 @@ namespace kapi::memory struct address { //! Construct a null-address. - constexpr explicit address() noexcept = default; + constexpr address() noexcept = default; //! Construct an address representing the given value. //! -- cgit v1.2.3 From d2e7a4e2fd5a2973b6c9071951eaf8b2d24d84a3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 17 Mar 2026 22:32:52 +0100 Subject: kapi/bootm: initialize all members --- kapi/include/kapi/boot_module/boot_module.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kapi/include/kapi/boot_module/boot_module.hpp b/kapi/include/kapi/boot_module/boot_module.hpp index 729efc9..85a1ac5 100644 --- a/kapi/include/kapi/boot_module/boot_module.hpp +++ b/kapi/include/kapi/boot_module/boot_module.hpp @@ -14,9 +14,9 @@ namespace kapi::boot_modules // ! its name, virtual start address, and size. struct boot_module { - std::string_view name; + std::string_view name{}; memory::linear_address start_address{}; - size_t size; + std::size_t size{}; }; } // namespace kapi::boot_modules -- cgit v1.2.3 From a4af9851433481575798e7bbb505c9d22bdf699a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 18 Mar 2026 06:41:16 +0000 Subject: build: install clang tidy in devcontainer --- .devcontainer/x86-64/devcontainer.json | 4 ++-- CMakeLists.txt | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.devcontainer/x86-64/devcontainer.json b/.devcontainer/x86-64/devcontainer.json index ec50ecb..41e9a95 100644 --- a/.devcontainer/x86-64/devcontainer.json +++ b/.devcontainer/x86-64/devcontainer.json @@ -1,11 +1,11 @@ { "name": "TeachOS on x86-64", - "image": "registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-2", + "image": "registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-3", "features": { "ghcr.io/devcontainers/features/git:1": {}, "ghcr.io/devcontainers/features/git-lfs:1": {}, "ghcr.io/devcontainers-extra/features/apt-packages:1": { - "packages": "cmake,grub2-common,grub-pc,mtools,ninja-build,qemu-system-x86,ssh,xorriso" + "packages": "clang-tidy-21,cmake,grub2-common,grub-pc,mtools,ninja-build,qemu-system-x86,ssh,xorriso" } }, "customizations": { diff --git a/CMakeLists.txt b/CMakeLists.txt index 2343c77..7654ed0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,10 @@ add_compile_options( # Global Linting Configuration #]============================================================================] -find_program(CLANG_TIDY_EXE "clang-tidy") +find_program(CLANG_TIDY_EXE NAMES + "clang-tidy-21" + "clang-tidy" +) if(CLANG_TIDY_EXE AND TEACHOS_ENABLE_LINTING) set(CMAKE_C_CLANG_TIDY "${CLANG_TIDY_EXE}") -- cgit v1.2.3 From 3439e75d1571ff1ef0179a358e522050121ab1da Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 18 Mar 2026 06:49:30 +0000 Subject: ci: install clang-tidy --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1c1548b..f2cfd1d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,10 +6,10 @@ build: stage: build - image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-1 + image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-3 before_script: - apt update - - apt install -y cmake grub2-common grub-pc mtools ninja-build xorriso + - apt install -y clang-tidy-21 cmake grub2-common grub-pc mtools ninja-build xorriso script: - cmake --preset $PLATFORM - cmake --build --preset $PLATFORM-$TYPE -- cgit v1.2.3 From 6cbd857d605a7b340f6cbbc157377736c3e67196 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 18 Mar 2026 07:24:17 +0000 Subject: ide: enable additional clangd flags --- .vscode/settings.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 08f9457..321f765 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,7 +4,11 @@ "clangd.arguments": [ "--compile-commands-dir=${workspaceFolder}/build", - "--query-driver=**/x86_64-pc-elf-g++" + "--query-driver=**/x86_64-pc-elf-g++", + "--completion-style=detailed", + "--background-index", + "--clang-tidy", + "--header-insertion=iwyu" ], "files.associations": { -- cgit v1.2.3 From 8c502bc3423a6b3597ffbebb06a3fa3e17a6e4b0 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 18 Mar 2026 09:21:11 +0100 Subject: fix clang-tidy warnings --- kernel/include/kernel/devices/block_device.hpp | 5 ----- kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp | 1 - kernel/src/filesystem/vfs.cpp | 1 - kernel/src/main.cpp | 4 +++- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/kernel/include/kernel/devices/block_device.hpp b/kernel/include/kernel/devices/block_device.hpp index 2a6f061..fb7d104 100644 --- a/kernel/include/kernel/devices/block_device.hpp +++ b/kernel/include/kernel/devices/block_device.hpp @@ -22,11 +22,6 @@ namespace devices */ block_device(size_t major, size_t minor, std::string_view name, size_t block_size); - /** - * @brief Virtual destructor for block device. - */ - virtual ~block_device() = default; - /** * @brief Read data from the block at @p block_index into @p buffer. * @param block_index Zero-based block index. diff --git a/kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp b/kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp index efb6256..f3e9f70 100644 --- a/kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp +++ b/kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp @@ -5,7 +5,6 @@ #include "kernel/devices/storage/ram_disk/ram_disk_device.hpp" #include -#include #include #include diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 2123fc7..4e0b6bf 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -12,7 +12,6 @@ #include "kernel/filesystem/mount.hpp" #include "kernel/filesystem/open_file_description.hpp" -#include #include #include diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 6ced18c..eb59402 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -51,7 +51,9 @@ auto run_test_code() -> void kstd::println("---"); // write half of the file new - kstd::vector write_buffer{std::byte{0xBB}, std::byte{0xAA}}; + auto const value1 = std::byte{0xAA}; + auto const value2 = std::byte{0xBB}; + kstd::vector write_buffer{value1, value2}; auto written_bytes = fd->write(write_buffer.data(), write_buffer.size()); kstd::println("written bytes: {}", written_bytes); -- cgit v1.2.3 From 12c0586ee15cadfa178e6982dc0f76b047cb2df9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 18 Mar 2026 15:24:26 +0100 Subject: kapi/memory: remove page/frame size macros --- CMakePresets.json | 7 +------ arch/x86_64/kapi/memory.cpp | 4 ++-- arch/x86_64/src/memory/kernel_mapper.cpp | 2 +- kapi/CMakeLists.txt | 6 ------ kapi/include/kapi/memory/layout.hpp | 20 +++++++++++++++++--- 5 files changed, 21 insertions(+), 18 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index fd88d3c..15bf4bc 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -15,12 +15,7 @@ { "name": "x86_64", "inherits": "base", - "toolchainFile": "cmake/Platforms/x86_64.cmake", - "cacheVariables": { - "TEACHOS_PLATFORM_FRAME_SIZE": "4096", - "TEACHOS_PLATFORM_PAGE_SIZE": "4096", - "TEACHOS_PLATFORM_PAGING_LEVELS": "4" - } + "toolchainFile": "cmake/Platforms/x86_64.cmake" } ], "buildPresets": [ diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp index 547d359..07e7465 100644 --- a/arch/x86_64/kapi/memory.cpp +++ b/arch/x86_64/kapi/memory.cpp @@ -110,7 +110,7 @@ namespace kapi::memory auto mbi_size = boot::bootstrap_information.mbi->size_bytes(); auto mbi_physical_start = physical_address{mbi_base & ~std::bit_cast(&arch::boot::TEACHOS_VMA)}; auto mbi_virtual_start = linear_address{mbi_base}; - auto mbi_block_count = (mbi_size + PLATFORM_FRAME_SIZE - 1) / PLATFORM_FRAME_SIZE; + auto mbi_block_count = (mbi_size + frame::size - 1) / frame::size; for (auto i = 0uz; i < mbi_block_count; ++i) { @@ -127,7 +127,7 @@ namespace kapi::memory auto module_virtual_start = linear_address{module.start_address + std::bit_cast(&arch::boot::TEACHOS_VMA)}; auto module_size = module.end_address - module.start_address; - auto module_block_count = (module_size + PLATFORM_FRAME_SIZE - 1) / PLATFORM_FRAME_SIZE; + auto module_block_count = (module_size + frame::size - 1) / frame::size; for (auto i = 0uz; i < module_block_count; ++i) { diff --git a/arch/x86_64/src/memory/kernel_mapper.cpp b/arch/x86_64/src/memory/kernel_mapper.cpp index 08c32c5..e5bb7f8 100644 --- a/arch/x86_64/src/memory/kernel_mapper.cpp +++ b/arch/x86_64/src/memory/kernel_mapper.cpp @@ -71,7 +71,7 @@ namespace arch::memory auto kernel_mapper::map_section(section_header_type const & section, std::string_view name, kapi::memory::page_mapper & mapper) -> void { - auto number_of_pages = (section.size + (PLATFORM_PAGE_SIZE - 1)) / PLATFORM_PAGE_SIZE; + auto number_of_pages = (section.size + (kapi::memory::page::size - 1)) / kapi::memory::page::size; auto linear_start_address = kapi::memory::linear_address{section.virtual_load_address}; auto physical_start_address = kapi::memory::physical_address{section.virtual_load_address & ~m_kernel_load_base}; diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index 028cdbb..b239adb 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -29,9 +29,3 @@ target_link_libraries("kapi" INTERFACE "gcc" "stdc++" ) - -target_compile_definitions("kapi" INTERFACE - "PLATFORM_PAGE_SIZE=${TEACHOS_PLATFORM_PAGE_SIZE}uz" - "PLATFORM_PAGING_LEVELS=${TEACHOS_PLATFORM_PAGING_LEVELS}uz" - "PLATFORM_FRAME_SIZE=${TEACHOS_PLATFORM_FRAME_SIZE}uz" -) diff --git a/kapi/include/kapi/memory/layout.hpp b/kapi/include/kapi/memory/layout.hpp index f5ba0f9..d4153d8 100644 --- a/kapi/include/kapi/memory/layout.hpp +++ b/kapi/include/kapi/memory/layout.hpp @@ -8,17 +8,31 @@ namespace kapi::memory { - constexpr auto page_size = PLATFORM_PAGE_SIZE; - constexpr auto frame_size = PLATFORM_FRAME_SIZE; - + //! The size of a single page of virtual memory. + //! + //! Platforms that use different sizes of pages are expected to emulate 4 KiB pages towards the kernel. + constexpr auto page_size = 4096uz; + + //! The size of a single frame of physical memory. + //! + //! Platforms that use different sizes of frames are expected to emulate 4 KiB pages towards the kernel. + constexpr auto frame_size = 4096uz; + + //! The linear base address of the higher-half direct map. + //! + //! Platforms are expected to provide a mapping of at least the first 512 GiB of available memory at this address. constexpr auto higher_half_direct_map_base = linear_address{0xffff'8000'0000'0000uz}; + //! The linear base address of the kernel heap. constexpr auto heap_base = linear_address{0xffff'c000'0000'0000uz}; + //! The linear base address of the memory region reserved for the metadata required by the PMM. constexpr auto pmm_metadata_base = linear_address{0xffff'd000'0000'0000uz}; + //! The linear base address of all Memory Mapped I/O mappings. constexpr auto mmio_base = linear_address{0xffff'e000'0000'0000uz}; + //! The linear base address of the loaded kernel image. constexpr auto kernel_base = linear_address{0xffff'ffff'8000'0000uz}; } // namespace kapi::memory -- cgit v1.2.3 From e7ccb96aecae7b231fb05818d7e45a767aebc31d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 18 Mar 2026 17:18:37 +0100 Subject: kstd: introduce strong type for memory amounts --- arch/x86_64/include/arch/memory/page_table.hpp | 3 +- arch/x86_64/kapi/memory.cpp | 17 ++- arch/x86_64/src/memory/kernel_mapper.cpp | 9 +- kapi/include/kapi/memory/address.hpp | 11 ++ kapi/include/kapi/memory/chunk.hpp | 8 +- kapi/include/kapi/memory/layout.hpp | 6 +- .../include/kernel/memory/block_list_allocator.hpp | 13 +- kernel/include/kernel/memory/heap_allocator.hpp | 5 +- kernel/kapi/memory.cpp | 10 +- kernel/src/memory.cpp | 5 +- kernel/src/memory/block_list_allocator.cpp | 28 ++-- kernel/src/memory/operators.cpp | 13 +- libs/kstd/include/kstd/units | 145 +++++++++++++++++++++ libs/multiboot2/CMakeLists.txt | 1 + libs/multiboot2/include/multiboot2/information.hpp | 16 ++- .../include/multiboot2/information/data.hpp | 31 ++++- 16 files changed, 266 insertions(+), 55 deletions(-) create mode 100644 libs/kstd/include/kstd/units diff --git a/arch/x86_64/include/arch/memory/page_table.hpp b/arch/x86_64/include/arch/memory/page_table.hpp index 2889d38..3cbb0af 100644 --- a/arch/x86_64/include/arch/memory/page_table.hpp +++ b/arch/x86_64/include/arch/memory/page_table.hpp @@ -4,6 +4,7 @@ #include "kapi/memory.hpp" #include +#include #include #include @@ -116,7 +117,7 @@ namespace arch::memory }; //! The maximum number of entries in this table. - constexpr auto static entry_count{kapi::memory::page::size / sizeof(entry)}; + constexpr auto static entry_count{kapi::memory::page::size / kstd::units::bytes{sizeof(entry)}}; //! Get the entry at the given index. //! diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp index 07e7465..5f12e5c 100644 --- a/arch/x86_64/kapi/memory.cpp +++ b/arch/x86_64/kapi/memory.cpp @@ -13,6 +13,7 @@ #include "arch/memory/region_allocator.hpp" #include +#include #include #include @@ -28,6 +29,8 @@ #include #include +using namespace kstd::units_literals; + namespace kapi::memory { @@ -46,7 +49,7 @@ namespace kapi::memory } auto const & mbi = boot::bootstrap_information.mbi; - auto mbi_span = std::span{std::bit_cast(mbi), mbi->size_bytes()}; + auto mbi_span = std::span{std::bit_cast(mbi), static_cast(mbi->size())}; auto image_span = std::span{&arch::boot::_start_physical, &arch::boot::_end_physical}; return arch::memory::region_allocator::memory_information{ @@ -107,10 +110,10 @@ namespace kapi::memory [[maybe_unused]] auto remap_multiboot_information(page_mapper & mapper) -> void { auto mbi_base = std::bit_cast(boot::bootstrap_information.mbi); - auto mbi_size = boot::bootstrap_information.mbi->size_bytes(); + auto mbi_size = boot::bootstrap_information.mbi->size(); auto mbi_physical_start = physical_address{mbi_base & ~std::bit_cast(&arch::boot::TEACHOS_VMA)}; auto mbi_virtual_start = linear_address{mbi_base}; - auto mbi_block_count = (mbi_size + frame::size - 1) / frame::size; + auto mbi_block_count = (mbi_size + frame::size - 1_B) / frame::size; for (auto i = 0uz; i < mbi_block_count; ++i) { @@ -126,8 +129,8 @@ namespace kapi::memory auto module_physical_start = physical_address{module.start_address}; auto module_virtual_start = linear_address{module.start_address + std::bit_cast(&arch::boot::TEACHOS_VMA)}; - auto module_size = module.end_address - module.start_address; - auto module_block_count = (module_size + frame::size - 1) / frame::size; + auto module_size = static_cast(module.end_address - module.start_address); + auto module_block_count = (module_size + frame::size - 1_B) / frame::size; for (auto i = 0uz; i < module_block_count; ++i) { @@ -147,7 +150,7 @@ namespace kapi::memory })) { auto start = frame::containing(physical_address{region.base}); - auto count = region.size_in_B / page::size; + auto count = kstd::units::bytes{region.size_in_B} / page::size; new_allocator.release_many({start, count}); } @@ -167,7 +170,7 @@ namespace kapi::memory [&](auto frame) { new_allocator.mark_used(frame); }); auto mbi_base = std::bit_cast(boot::bootstrap_information.mbi); - auto mbi_size = boot::bootstrap_information.mbi->size_bytes(); + auto mbi_size = boot::bootstrap_information.mbi->size(); auto mbi_address = physical_address{mbi_base & ~std::bit_cast(&arch::boot::TEACHOS_VMA)}; auto mbi_start = frame::containing(mbi_address); auto mbi_end = frame::containing(mbi_address + mbi_size) + 1; diff --git a/arch/x86_64/src/memory/kernel_mapper.cpp b/arch/x86_64/src/memory/kernel_mapper.cpp index e5bb7f8..ced8a14 100644 --- a/arch/x86_64/src/memory/kernel_mapper.cpp +++ b/arch/x86_64/src/memory/kernel_mapper.cpp @@ -6,6 +6,7 @@ #include "arch/boot/ld.hpp" #include +#include #include #include @@ -19,13 +20,14 @@ #include #include +using namespace std::string_view_literals; +using namespace kstd::units_literals; + namespace arch::memory { namespace { - using namespace std::string_view_literals; - constexpr auto static ignored_section_prefixes = std::array{ ".boot_"sv, }; @@ -71,7 +73,8 @@ namespace arch::memory auto kernel_mapper::map_section(section_header_type const & section, std::string_view name, kapi::memory::page_mapper & mapper) -> void { - auto number_of_pages = (section.size + (kapi::memory::page::size - 1)) / kapi::memory::page::size; + auto number_of_pages = + (kstd::units::bytes{section.size} + (kapi::memory::page::size - 1_B)) / kapi::memory::page::size; auto linear_start_address = kapi::memory::linear_address{section.virtual_load_address}; auto physical_start_address = kapi::memory::physical_address{section.virtual_load_address & ~m_kernel_load_base}; diff --git a/kapi/include/kapi/memory/address.hpp b/kapi/include/kapi/memory/address.hpp index 69fc7b9..587aeac 100644 --- a/kapi/include/kapi/memory/address.hpp +++ b/kapi/include/kapi/memory/address.hpp @@ -4,6 +4,7 @@ // IWYU pragma: private, include "kapi/memory.hpp" #include +#include #include #include @@ -183,6 +184,16 @@ namespace kapi::memory return static_cast(m_value & mask); } + constexpr auto operator+(kstd::units::bytes n) const noexcept -> address + { + return address{m_value + n.value}; + } + + constexpr auto operator+=(kstd::units::bytes n) noexcept -> address & + { + return *this = *this + n; + } + //! Check if this address is equal to another one. constexpr auto operator==(address const &) const noexcept -> bool = default; diff --git a/kapi/include/kapi/memory/chunk.hpp b/kapi/include/kapi/memory/chunk.hpp index 4529535..a046221 100644 --- a/kapi/include/kapi/memory/chunk.hpp +++ b/kapi/include/kapi/memory/chunk.hpp @@ -3,6 +3,8 @@ // IWYU pragma: private, include "kapi/memory.hpp" +#include + #include #include @@ -15,7 +17,7 @@ namespace kapi::memory //! @tparam ChunkType The CRTP type of the deriving class //! @tparam AddressType The type of addresses used to index this chunk //! @tparam Size The size of this chunk. - template + template struct chunk { //! The type of addresses used to index this chunk @@ -30,7 +32,7 @@ namespace kapi::memory //! @return A handle to a chunk containing the given address. constexpr auto static containing(address_type address) noexcept -> ChunkType { - return ChunkType{address / size}; + return ChunkType{address / size.value}; } //! Get the start address of the chunk referenced by this handle. @@ -38,7 +40,7 @@ namespace kapi::memory //! @return The address of the first byte contained by the chunk referenced by this handle. [[nodiscard]] constexpr auto start_address() const noexcept -> address_type { - return address_type{m_number * size}; + return address_type{(m_number * size).value}; } //! Get the number of the chunk referenced by this handle. diff --git a/kapi/include/kapi/memory/layout.hpp b/kapi/include/kapi/memory/layout.hpp index d4153d8..157f41e 100644 --- a/kapi/include/kapi/memory/layout.hpp +++ b/kapi/include/kapi/memory/layout.hpp @@ -5,18 +5,20 @@ #include "kapi/memory/address.hpp" +#include + namespace kapi::memory { //! The size of a single page of virtual memory. //! //! Platforms that use different sizes of pages are expected to emulate 4 KiB pages towards the kernel. - constexpr auto page_size = 4096uz; + constexpr auto page_size = kstd::units::KiB(4); //! The size of a single frame of physical memory. //! //! Platforms that use different sizes of frames are expected to emulate 4 KiB pages towards the kernel. - constexpr auto frame_size = 4096uz; + constexpr auto frame_size = kstd::units::KiB(4); //! The linear base address of the higher-half direct map. //! diff --git a/kernel/include/kernel/memory/block_list_allocator.hpp b/kernel/include/kernel/memory/block_list_allocator.hpp index 5e81c44..f319097 100644 --- a/kernel/include/kernel/memory/block_list_allocator.hpp +++ b/kernel/include/kernel/memory/block_list_allocator.hpp @@ -6,6 +6,7 @@ #include "kernel/memory/heap_allocator.hpp" #include +#include #include #include @@ -32,7 +33,7 @@ namespace kernel::memory //! @param size The size of the block to allocate //! @param alignment The desired alignment of the allocated block //! @return A pointer to the beginning of the block on success, @p nullptr otherwise. - [[nodiscard]] auto allocate(std::size_t size, std::align_val_t alignment) noexcept -> void * override; + [[nodiscard]] auto allocate(kstd::units::bytes size, kstd::units::bytes alignment) noexcept -> void * override; //! Deallocate a block of memory previously allocated. //! @@ -42,7 +43,7 @@ namespace kernel::memory private: struct block_header final { - std::size_t usable_size; + kstd::units::bytes usable_size; bool free; block_header * next; block_header * prev; @@ -52,16 +53,16 @@ namespace kernel::memory //! //! Each allocated block carries a block header, like any unallocated one, but in addition also has a back-pointer //! to the block header to support padding due to alignment. - constexpr auto static allocated_metadata_size = sizeof(block_header) + sizeof(block_header *); + constexpr auto static allocated_metadata_size = kstd::units::bytes{sizeof(block_header) + sizeof(block_header *)}; //! The minimum number of bytes for an allocation. - constexpr auto static minimum_allocation_size = 16uz; + constexpr auto static minimum_allocation_size = kstd::units::bytes{16uz}; //! Try to expand the heap to accommodate the given size. //! //! @param delta The size to expand the heap by. //! @return @p true if the heap was expanded, @p false otherwise. - auto expand(std::size_t delta) noexcept -> bool; + auto expand(kstd::units::bytes delta) noexcept -> bool; //! Split a given free block to accommodate and allocation. //! @@ -70,7 +71,7 @@ namespace kernel::memory //! @param block The block to split. //! @param size The size of the allocation. //! @param padding The amount of padding to apply. - auto split(block_header * block, std::size_t size, std::size_t padding) noexcept -> void; + auto split(block_header * block, kstd::units::bytes size, kstd::units::bytes padding) noexcept -> void; //! Try to coalesce a given block with it's preceding and/or following block. //! diff --git a/kernel/include/kernel/memory/heap_allocator.hpp b/kernel/include/kernel/memory/heap_allocator.hpp index bc771e3..55de7e4 100644 --- a/kernel/include/kernel/memory/heap_allocator.hpp +++ b/kernel/include/kernel/memory/heap_allocator.hpp @@ -1,9 +1,8 @@ #ifndef TEACHOS_KERNEL_MEMORY_HEAP_ALLOCATOR_HPP #define TEACHOS_KERNEL_MEMORY_HEAP_ALLOCATOR_HPP -#include +#include -#include #include namespace kernel::memory @@ -19,7 +18,7 @@ namespace kernel::memory //! @param size The size of the block to allocate //! @param alignment The desired alignment of the allocated block //! @return A pointer to the beginning of the block on success, @p nullptr otherwise. - [[nodiscard]] virtual auto allocate(std::size_t size, std::align_val_t alignment) noexcept -> void * = 0; + [[nodiscard]] virtual auto allocate(kstd::units::bytes size, kstd::units::bytes alignment) noexcept -> void * = 0; //! Deallocate a block of memory previously allocated. //! diff --git a/kernel/kapi/memory.cpp b/kernel/kapi/memory.cpp index d8065d4..2803d76 100644 --- a/kernel/kapi/memory.cpp +++ b/kernel/kapi/memory.cpp @@ -5,6 +5,7 @@ #include "kernel/memory/bitmap_allocator.hpp" #include +#include #include #include @@ -112,8 +113,10 @@ namespace kapi::memory auto init_pmm(std::size_t frame_count, void (&handoff_handler)(frame_allocator &)) -> void { - auto const bitmap_bytes = (frame_count + 7uz) / 8uz; - auto const bitmap_pages = (bitmap_bytes + page::size - 1uz) / page::size; + using namespace kstd::units_literals; + + auto const bitmap_bytes = kstd::units::bytes{(frame_count + 7uz) / 8uz}; + auto const bitmap_pages = (bitmap_bytes + page::size - 1_B) / page::size; auto const bitmap_frames = allocate_many_frames(bitmap_pages); if (!bitmap_frames) @@ -130,7 +133,8 @@ namespace kapi::memory }); auto bitmap_ptr = static_cast(pmm_metadata_base); - auto bitmap = std::span{bitmap_ptr, (bitmap_bytes + sizeof(std::uint64_t) - 1uz) / sizeof(std::uint64_t)}; + auto bitmap = + std::span{bitmap_ptr, (bitmap_bytes + kstd::type_size - 1_B) / kstd::type_size}; allocator.emplace(bitmap, frame_count); diff --git a/kernel/src/memory.cpp b/kernel/src/memory.cpp index 0f614f0..4a8e203 100644 --- a/kernel/src/memory.cpp +++ b/kernel/src/memory.cpp @@ -7,10 +7,9 @@ #include "kernel/memory/heap_allocator.hpp" #include +#include #include -#include -#include #include namespace kernel::memory @@ -22,7 +21,7 @@ namespace kernel::memory { null_allocator static instance; - [[nodiscard]] auto allocate(std::size_t, std::align_val_t) noexcept -> void * override + [[nodiscard]] auto allocate(kstd::units::bytes, kstd::units::bytes) noexcept -> void * override { kstd::print(kstd::print_sink::stderr, "Tried to allocate memory without an active heap!"); return nullptr; diff --git a/kernel/src/memory/block_list_allocator.cpp b/kernel/src/memory/block_list_allocator.cpp index 5d870d8..fbc5945 100644 --- a/kernel/src/memory/block_list_allocator.cpp +++ b/kernel/src/memory/block_list_allocator.cpp @@ -6,19 +6,21 @@ #include "kernel/memory/heap_allocator.hpp" #include +#include #include #include #include #include -#include + +using namespace kstd::units_literals; namespace kernel::memory { namespace { - [[nodiscard]] constexpr auto align_up(std::byte * pointer, std::align_val_t alignment) noexcept -> std::byte * + [[nodiscard]] constexpr auto align_up(std::byte * pointer, kstd::units::bytes alignment) noexcept -> std::byte * { auto const remainder = std::bit_cast(pointer) % static_cast(alignment); return remainder == 0 ? pointer : pointer + static_cast(alignment) - remainder; @@ -33,7 +35,7 @@ namespace kernel::memory , m_lock{} {} - auto block_list_allocator::allocate(std::size_t size, std::align_val_t alignment) noexcept -> void * + auto block_list_allocator::allocate(kstd::units::bytes size, kstd::units::bytes alignment) noexcept -> void * { kstd::lock_guard guard{m_lock}; @@ -47,13 +49,13 @@ namespace kernel::memory auto const raw_block = reinterpret_cast(current); auto const unaligned_payload = raw_block + allocated_metadata_size; auto const aligned_payload = align_up(unaligned_payload, alignment); - auto const required_padding = static_cast(aligned_payload - unaligned_payload); + auto const required_padding = static_cast(aligned_payload - unaligned_payload); auto const total_required_size = required_padding + allocated_metadata_size + size; if (current->usable_size >= total_required_size) { auto const payload_header = aligned_payload - sizeof(block_header *) - sizeof(block_header); - auto const front_padding = static_cast(payload_header - raw_block); + auto const front_padding = static_cast(payload_header - raw_block); auto payload_block = current; @@ -72,9 +74,10 @@ namespace kernel::memory payload_block->prev = current; } - auto const payload_size = - aligned_payload - reinterpret_cast(payload_block) - allocated_metadata_size + size; - split(payload_block, payload_size, 0uz); + auto const header_size = + static_cast(aligned_payload - reinterpret_cast(payload_block)); + auto const payload_size = header_size - allocated_metadata_size + size; + split(payload_block, payload_size, 0_B); payload_block->free = false; @@ -87,7 +90,7 @@ namespace kernel::memory current = current->next; } - auto const search_size = size + static_cast(alignment); + auto const search_size = size + alignment; if (attempt == 0uz && !expand(search_size)) { return nullptr; @@ -114,10 +117,10 @@ namespace kernel::memory coalesce(block); } - auto block_list_allocator::expand(std::size_t size) noexcept -> bool + auto block_list_allocator::expand(kstd::units::bytes size) noexcept -> bool { auto const total_required_size = size + allocated_metadata_size; - auto const frames_needed = (total_required_size + kapi::memory::frame::size - 1) / kapi::memory::frame::size; + auto const frames_needed = (total_required_size + kapi::memory::frame::size - 1_B) / kapi::memory::frame::size; auto const flags = kapi::memory::page_mapper::flags::writable | kapi::memory::page_mapper::flags::supervisor_only | kapi::memory::page_mapper::flags::global; @@ -187,7 +190,8 @@ namespace kernel::memory } } - auto block_list_allocator::split(block_header * block, std::size_t size, std::size_t padding) noexcept -> void + auto block_list_allocator::split(block_header * block, kstd::units::bytes size, kstd::units::bytes padding) noexcept + -> void { auto const new_block_size = size + padding; diff --git a/kernel/src/memory/operators.cpp b/kernel/src/memory/operators.cpp index 57e31e6..c786b53 100644 --- a/kernel/src/memory/operators.cpp +++ b/kernel/src/memory/operators.cpp @@ -2,13 +2,16 @@ #include "kernel/memory.hpp" +#include + #include #include [[nodiscard]] auto operator new(std::size_t size, std::align_val_t alignment, std::nothrow_t const &) noexcept -> void * { auto & allocator = kernel::memory::get_heap_allocator(); - return allocator.allocate(size, alignment); + return allocator.allocate(static_cast(size), + static_cast(static_cast(alignment))); } [[nodiscard]] auto operator new(std::size_t size, std::align_val_t alignment) -> void * @@ -83,9 +86,9 @@ auto operator delete[](void * pointer) noexcept -> void ::operator delete(pointer); } -auto operator delete[](void * pointer, std::size_t) noexcept -> void +auto operator delete[](void * pointer, std::size_t size) noexcept -> void { - ::operator delete(pointer); + ::operator delete(pointer, size); } auto operator delete[](void * pointer, std::align_val_t) noexcept -> void @@ -93,7 +96,7 @@ auto operator delete[](void * pointer, std::align_val_t) noexcept -> void ::operator delete(pointer); } -auto operator delete[](void * pointer, std::size_t, std::align_val_t) noexcept -> void +auto operator delete[](void * pointer, std::size_t size, std::align_val_t) noexcept -> void { - ::operator delete(pointer); + ::operator delete(pointer, size); } diff --git a/libs/kstd/include/kstd/units b/libs/kstd/include/kstd/units new file mode 100644 index 0000000..f6dcdb1 --- /dev/null +++ b/libs/kstd/include/kstd/units @@ -0,0 +1,145 @@ +#ifndef KSTD_UNITS_HPP +#define KSTD_UNITS_HPP + +#include +#include +#include + +namespace kstd +{ + + //! A basic template for strongly typed units. + template + struct basic_unit + { + using value_type = ValueType; + + explicit constexpr basic_unit(value_type value) noexcept + : value{value} + {} + + explicit constexpr operator value_type() const noexcept + { + return value; + } + + constexpr auto operator+(basic_unit const & other) const noexcept -> basic_unit + { + return basic_unit{value + other.value}; + } + + constexpr auto operator+=(basic_unit const & other) noexcept -> basic_unit & + { + return *this = *this + other; + } + + constexpr auto operator-(basic_unit const & other) const noexcept -> basic_unit + { + return basic_unit{value - other.value}; + } + + constexpr auto operator-=(basic_unit const & other) noexcept -> basic_unit & + { + return *this = *this - other; + } + + constexpr auto operator*(std::integral auto factor) noexcept -> basic_unit + { + return basic_unit{value * factor}; + } + + constexpr auto operator*=(std::integral auto factor) noexcept -> basic_unit + { + return *this = *this * factor; + } + + constexpr auto operator/(std::integral auto divisor) noexcept -> basic_unit + { + return basic_unit{value / divisor}; + } + + constexpr auto operator/=(std::integral auto divisor) noexcept -> basic_unit + { + return *this = *this / divisor; + } + + constexpr auto operator/(basic_unit const & other) const noexcept + { + return value / other.value; + } + + constexpr auto operator<=>(basic_unit const & other) const noexcept -> std::strong_ordering = default; + + value_type value; + }; + + template + constexpr auto operator*(Factor factor, basic_unit const & unit) noexcept + -> basic_unit + { + return basic_unit{unit.value * factor}; + } + + namespace units + { + using bytes = basic_unit; + + constexpr auto KiB(std::size_t value) noexcept -> bytes + { + return bytes{value * 1024}; + } + + constexpr auto MiB(std::size_t value) noexcept -> bytes + { + return bytes{value * 1024 * 1024}; + } + + constexpr auto GiB(std::size_t value) noexcept -> bytes + { + return bytes{value * 1024 * 1024 * 1024}; + } + + template + constexpr auto operator+(ValueType * pointer, bytes offset) -> ValueType * + { + return pointer + offset.value; + } + + } // namespace units + + namespace units_literals + { + constexpr auto operator""_B(unsigned long long value) noexcept -> units::bytes + { + return units::bytes{value}; + } + + constexpr auto operator""_KiB(unsigned long long value) noexcept -> units::bytes + { + return units::KiB(value); + } + + constexpr auto operator""_MiB(unsigned long long value) noexcept -> units::bytes + { + return units::MiB(value); + } + + constexpr auto operator""_GiB(unsigned long long value) noexcept -> units::bytes + { + return units::GiB(value); + } + + } // namespace units_literals + + template + constexpr auto object_size(ValueType const &) -> units::bytes + { + return units::bytes{sizeof(ValueType)}; + } + + template + constexpr auto type_size = units::bytes{sizeof(T)}; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/multiboot2/CMakeLists.txt b/libs/multiboot2/CMakeLists.txt index 350a996..b306b66 100644 --- a/libs/multiboot2/CMakeLists.txt +++ b/libs/multiboot2/CMakeLists.txt @@ -20,6 +20,7 @@ target_include_directories("multiboot2" INTERFACE target_link_libraries("multiboot2" INTERFACE "libs::elf" + "libs::kstd" ) set_target_properties("multiboot2" PROPERTIES diff --git a/libs/multiboot2/include/multiboot2/information.hpp b/libs/multiboot2/include/multiboot2/information.hpp index fbd534c..d2fac2e 100644 --- a/libs/multiboot2/include/multiboot2/information.hpp +++ b/libs/multiboot2/include/multiboot2/information.hpp @@ -5,11 +5,12 @@ #include "information/iterator.hpp" // IWYU pragma: export #include "information/tag.hpp" // IWYU pragma: export +#include + #include #include #include -#include #include #include #include @@ -119,7 +120,12 @@ namespace multiboot2 */ [[nodiscard]] auto string() const noexcept -> std::string_view { - return {data(), size()}; + return {data(), vla_tag::size()}; + } + + [[nodiscard]] constexpr auto size() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{end_address - start_address}; } }; @@ -130,9 +136,9 @@ namespace multiboot2 using pointer = iterator::pointer; using reference = iterator::reference; - [[nodiscard]] auto size_bytes() const noexcept -> std::size_t + [[nodiscard]] auto size() const noexcept -> kstd::units::bytes { - return m_size; + return kstd::units::bytes{m_size}; } // Range access @@ -190,7 +196,7 @@ namespace multiboot2 { return get>().and_then( [](auto x) -> std::optional> { - if (x.entry_size == elf::section_header_size) + if (x.entry_size_in_B == elf::section_header_size) { return std::optional{x}; } diff --git a/libs/multiboot2/include/multiboot2/information/data.hpp b/libs/multiboot2/include/multiboot2/information/data.hpp index 9fa6d5b..3b07d20 100644 --- a/libs/multiboot2/include/multiboot2/information/data.hpp +++ b/libs/multiboot2/include/multiboot2/information/data.hpp @@ -6,6 +6,8 @@ #include "multiboot2/constants/information_id.hpp" #include "multiboot2/constants/memory_type.hpp" +#include + #include namespace multiboot2 @@ -27,6 +29,16 @@ namespace multiboot2 //! loader. struct basic_memory : tag_data { + [[nodiscard]] constexpr auto lower() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{lower_KiB * 1024}; + } + + [[nodiscard]] constexpr auto upper() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{upper_KiB * 1024}; + } + //! The amount of lower memory available to the system. //! //! Any memory below the 1 MiB address boundary is considered to be lower memory. The maximum possible value for @@ -72,11 +84,16 @@ namespace multiboot2 //! time. The array begins after the last member of this structure. struct elf_symbols : tag_data { + [[nodiscard]] constexpr auto entry_size() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{entry_size_in_B}; + } + //! The number of section header table entries. std::uint32_t count; //! The size of each section header table entry. - std::uint32_t entry_size; + std::uint32_t entry_size_in_B; //! The section number of the string table containing the section names. std::uint32_t string_table_index; @@ -102,6 +119,11 @@ namespace multiboot2 return type == memory_type::available; } + [[nodiscard]] constexpr auto size() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{size_in_B}; + } + //! The physical start address of this region std::uint64_t base; @@ -117,11 +139,16 @@ namespace multiboot2 std::uint32_t : 0; }; + [[nodiscard]] constexpr auto entry_size() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{entry_size_in_B}; + } + //! The size of each entry present in the map. //! //! This field is present to allow for future extension of the entry format. Each entry's size is guaranteed to //! always be an integer multiple of 8. - std::uint32_t entry_size; + std::uint32_t entry_size_in_B; //! The version of each entry present in the map std::uint32_t entry_version; -- cgit v1.2.3 From c86c898836b1564de2733330540f5d0cd56e8b10 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 18 Mar 2026 17:18:51 +0100 Subject: kstd: don't allocate 0-sized memory regions --- libs/kstd/include/kstd/vector | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 41b380e..6af7c12 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -40,7 +40,7 @@ namespace kstd explicit vector(size_type n, value_type initial = value_type{}) : _size(n) , _capacity(n) - , _data(new value_type[_capacity]{}) + , _data(_capacity ? new value_type[_capacity]{} : nullptr) { std::ranges::fill(*this, initial); } @@ -56,7 +56,7 @@ namespace kstd explicit vector(InputIterator first, InputIterator last) : _size(std::distance(first, last)) , _capacity(std::distance(first, last)) - , _data(new value_type[_capacity]{}) + , _data(_capacity ? new value_type[_capacity]() : nullptr) { std::ranges::copy(first, last, _data); } @@ -69,7 +69,7 @@ namespace kstd explicit vector(std::initializer_list initializer_list) : _size(initializer_list.size()) , _capacity(initializer_list.size()) - , _data(new value_type[_capacity]{}) + , _data(_capacity ? new value_type[_capacity]() : nullptr) { std::ranges::copy(initializer_list, _data); } @@ -85,7 +85,7 @@ namespace kstd vector(vector const & other) : _size(other._size) , _capacity(other._capacity) - , _data(new value_type[_capacity]()) + , _data(_capacity ? new value_type[_capacity]() : nullptr) { std::ranges::copy(other, _data); } @@ -104,7 +104,7 @@ namespace kstd delete[] _data; _size = other._size; _capacity = other._capacity; - _data = new value_type[_capacity](); + _data = _capacity ? new value_type[_capacity]() : nullptr; std::ranges::copy(other, _data); return *this; } -- cgit v1.2.3 From c049d767c7d41d04226f2c6b01ee2e07aae7ea1f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 19 Mar 2026 07:16:58 +0100 Subject: kstd: prepare vector to be allocator aware --- libs/kstd/include/kstd/vector | 426 ++++++++++++++++++++++++++++-------------- 1 file changed, 289 insertions(+), 137 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 6af7c12..b87756a 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -6,6 +6,10 @@ #include #include #include +#include +#include +#include +#include namespace kstd { @@ -14,129 +18,254 @@ namespace kstd * custom memory management. * * @tparam T Element the vector instance should contain. + * @tparam Allocator The allocator to use when allocating new elements. */ - template + template> struct vector { using value_type = T; ///< Type of the elements contained in the container. + using allocator_type = Allocator; ///< Type of the allocator used by the container. using size_type = std::size_t; ///< Type of the size in the container. + using difference_type = std::ptrdiff_t; ///< Type of the difference between two iterators. using reference = value_type &; ///< Type of reference to the elements. using const_reference = value_type const &; ///< Type of constant reference to the elements. using pointer = value_type *; ///< Type of pointer to the elements. using const_pointer = value_type const *; ///< Type of constant pointer to the elements. + using iterator = pointer; ///< Type of iterator to the elements. + using const_iterator = const_pointer; ///< Type of constant iterator to the elements. + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; - /** - * @brief Default Constructor. - */ + //! Construct a new, empty vector. vector() = default; - /** - * @brief Constructs data with the given amount of elements containing the given value or alternatively the default - * constructed value. - * - * @param n Amount of elements we want to create and set the given value for. - * @param initial Inital value of all elements in the underlying data array. - */ + //! Construct a new vector, with the given number of element and initialize them to the given value. + //! + //! @param n The number of elements to construct the container with. + //! @param initial The value to initialize each element to. explicit vector(size_type n, value_type initial = value_type{}) - : _size(n) - , _capacity(n) - , _data(_capacity ? new value_type[_capacity]{} : nullptr) + : m_size{n} + , m_capacity{n} + , m_data{allocate_n(m_capacity)} { - std::ranges::fill(*this, initial); + for (auto i = 0uz; i < n; ++i) + { + std::allocator_traits::construct(m_allocator, m_data + i, initial); + } } - /** - * @brief Constructs data by copying all element from the given exclusive range. - * - * @tparam InputIterator Template that should have atleast input iterator characteristics. - * @param first Input iterator to the first element in the range we want to copy from. - * @param last Input iterator to one past the last element in the range we want to copy from. - */ + //! Construct a new vector and initialize it's content by copying all elements in the given range. + //! + //! @tparam InputIterator An iterator type used to describe the source range. + //! @param first The start of the source range. + //! @param last The end of the source range. template explicit vector(InputIterator first, InputIterator last) - : _size(std::distance(first, last)) - , _capacity(std::distance(first, last)) - , _data(_capacity ? new value_type[_capacity]() : nullptr) + : m_allocator{} + , m_size{std::ranges::distance(first, last)} + , m_capacity{std::ranges::distance(first, last)} + , m_data{allocate_n(m_capacity)} { - std::ranges::copy(first, last, _data); + for (auto destination = begin(); first != last; ++first, ++destination) + { + std::allocator_traits::construct(m_allocator, destination, *first); + } } - /** - * @brief Construct data by copying all elements from the initializer list. - * - * @param initializer_list List we want to copy all elements from. - */ - explicit vector(std::initializer_list initializer_list) - : _size(initializer_list.size()) - , _capacity(initializer_list.size()) - , _data(_capacity ? new value_type[_capacity]() : nullptr) - { - std::ranges::copy(initializer_list, _data); + //! Construct a new vector and initialize it's content by copying all elements in the given initializer list. + //! + //! @param list The initializer list containing the source objects. + explicit vector(std::initializer_list list) + : vector{std::ranges::begin(list), std::ranges::end(list)} + {} + + //! Construct a new vector and initialize it's content by copying all elements from a given vector. + //! + //! @param other The source vector. + vector(vector const & other) + : m_allocator{other.m_allocator} + , m_size{other.m_size} + , m_capacity{other.m_capacity} + , m_data(allocate_n(m_capacity)) + { + auto first = std::ranges::begin(other); + auto last = std::ranges::end(other); + for (auto destination = begin(); first != last; ++first, ++destination) + { + std::allocator_traits::construct(m_allocator, destination, *first); + } } - /** - * @brief Copy constructor. - * - * @note Allocates underlying data container with the same capacity as vector we are copying from and copies all - * elements from it. - * - * @param other Other instance of vector we want to copy the data from. - */ - vector(vector const & other) - : _size(other._size) - , _capacity(other._capacity) - , _data(_capacity ? new value_type[_capacity]() : nullptr) + //! Construct a new vector and initialize it's content by moving from a given vector. + //! + //! @param other The source vector. + vector(vector && other) noexcept + : m_allocator{std::exchange(other.m_allocator, allocator_type{})} + , m_size{std::exchange(other.m_size, size_type{})} + , m_capacity(std::exchange(other.m_capacity, size_type{})) + , m_data(std::exchange(other.m_data, nullptr)) + {} + + //! Destroy this vector. + ~vector() { - std::ranges::copy(other, _data); + std::ranges::for_each(std::views::reverse(*this), [&](auto & element) { + std::allocator_traits::destroy(m_allocator, std::addressof(element)); + }); + std::allocator_traits::deallocate(m_allocator, m_data, m_capacity); } - /** - * @brief Copy assignment operator. - * - * @note Allocates underlying data container with the same capacity as vector we are copying from and copies all - * elements from it. - * - * @param other Other instance of vector we want to copy the data from. - * @return Newly created copy. - */ + //! Replace the contents of this vector by the copying from the given source vector. + //! + //! @param other The source vector. auto operator=(vector const & other) -> vector & { - delete[] _data; - _size = other._size; - _capacity = other._capacity; - _data = _capacity ? new value_type[_capacity]() : nullptr; - std::ranges::copy(other, _data); + if (this == std::addressof(other)) + { + return *this; + } + + if constexpr (std::allocator_traits::propagate_on_container_move_assignment::value) + { + if (m_allocator != other.m_allocator) + { + clear_and_deallocate(); + } + m_allocator = other.m_allocator; + } + + if (m_capacity >= other.m_size) + { + auto const overlap = std::min(m_size, other.m_size); + auto first = std::ranges::begin(other); + for (auto i = 0uz; i < overlap; ++first, ++i) + { + m_data[i] = *first; + } + + if (m_size < other.m_size) + { + for (auto i = m_size; i < other.m_size; ++first, ++i) + { + std::allocator_traits::construct(m_allocator, m_data + i, *first); + } + } + else if (m_size > other.m_size) + { + for (auto i = other.m_size; i < m_size; ++i) + { + std::allocator_traits::destroy(m_allocator, m_data + i); + } + } + } + else + { + auto new_data = std::allocator_traits::allocate(m_allocator, other.m_size); + for (auto i = 0uz; i < other.m_size; ++i) + { + std::allocator_traits::construct(m_allocator, new_data + i, other.m_data[i]); + } + clear_and_deallocate(); + std::exchange(m_data, new_data); + m_capacity = other.m_size; + } + + m_size = other.m_size; return *this; } - /** - * @brief Destructor. - */ - ~vector() + //! Replace the contents fo this vector by moving from the given source. + //! + //! @param other The source vector. + auto operator=(vector && other) noexcept -> vector & { - delete[] _data; + if (this == std::addressof(other)) + { + return *this; + } + + if constexpr (std::allocator_traits::propagate_on_container_move_assignment::value) + { + clear_and_deallocate(); + m_allocator = std::move(other.m_allocator); + m_size = std::exchange(other.m_size, size_type{}); + m_capacity = std::exchange(other.m_capacity, size_type{}); + m_data = std::exchange(other.m_data, nullptr); + } + else if (m_allocator != other.m_allocator) + { + clear_and_deallocate(); + m_size = std::exchange(other.m_size, size_type{}); + m_capacity = std::exchange(other.m_capacity, size_type{}); + m_data = std::exchange(other.m_data, nullptr); + } + else + { + if (m_capacity >= other.m_size) + { + auto const overlap = std::min(m_size, other.m_size); + auto first = std::ranges::begin(other); + for (auto i = 0uz; i < overlap; ++first, ++i) + { + m_data[i] = std::move(*first); + } + + if (m_size < other.m_size) + { + for (auto i = m_size; i < other.m_size; ++first, ++i) + { + std::allocator_traits::construct(m_allocator, m_data + i, std::move(*first)); + } + } + else if (m_size > other.m_size) + { + for (auto i = other.m_size; i < m_size; ++i) + { + std::allocator_traits::destroy(m_allocator, m_data + i); + } + } + } + else + { + auto new_data = std::allocator_traits::allocate(m_allocator, other.m_size); + for (auto i = 0uz; i < other.m_size; ++i) + { + std::allocator_traits::destroy(m_allocator, m_data + i); + } + clear_and_deallocate(); + std::exchange(m_data, new_data); + m_capacity = other.m_size; + } + for (auto i = 0uz; i < other.m_size; ++i) + { + std::allocator_traits::destroy(other.m_allocator, other.m_data + i); + } + m_size = std::exchange(other.m_size, size_type{}); + } + + return *this; } /** - * @brief Amount of elements currently contained in this vector, will fill up until we have reached the capacity. If - * that is the case the capacity is increased automatically. + * @brief Amount of elements currently contained in this vector, will fill up until we have reached the capacity. + * If that is the case the capacity is increased automatically. * * @return Current amount of elements. */ [[nodiscard]] auto size() const -> size_type { - return _size; + return m_size; } /** - * @brief Amount of space the vector currently has, can be different than the size, because we allocate more than we - * exactly require to decrease the amount of allocations and deallocation to improve speed. + * @brief Amount of space the vector currently has, can be different than the size, because we allocate more than + * we exactly require to decrease the amount of allocations and deallocation to improve speed. * * @return Current amount of space the vector has for elements. */ [[nodiscard]] auto capacity() const -> size_type { - return _capacity; + return m_capacity; } /** @@ -149,7 +278,7 @@ namespace kstd */ auto operator[](size_type index) -> reference { - return _data[index]; + return m_data[index]; } /** @@ -162,13 +291,14 @@ namespace kstd */ auto operator[](size_type index) const -> const_reference { - return _data[index]; + return m_data[index]; } /** * @brief Array indexing operator. Allowing to access element at the given index. * - * @note Ensures we do not access element outside of the bounds of the array, if we do further execution is halted. + * @note Ensures we do not access element outside of the bounds of the array, if we do further execution is + * halted. * * @param index Index we want to access elements at. * @return Reference to the underlying element. @@ -176,13 +306,14 @@ namespace kstd auto at(size_type index) -> reference { throw_if_out_of_range(index); - return this->operator[](index); + return (*this)[index]; } /** * @brief Array indexing operator. Allowing to access element at the given index. * - * @note Ensures we do not access element outside of the bounds of the array, if we do further execution is halted. + * @note Ensures we do not access element outside of the bounds of the array, if we do further execution is + * halted. * * @param index Index we want to access elements at. * @return Reference to the underlying element. @@ -190,7 +321,7 @@ namespace kstd [[nodiscard]] auto at(size_type index) const -> const_reference { throw_if_out_of_range(index); - return this->operator[](index); + return (*this)[index]; } /** @@ -209,13 +340,13 @@ namespace kstd auto push_back(U && value) -> void { increase_capacity_if_full(); - _data[_size] = std::forward(value); - (void)_size++; + std::allocator_traits::construct(m_allocator, &(*this)[m_size], std::forward(value)); + ++m_size; } /** - * @brief Appends a new element to the end of the container. The element is constructed through a constructor of the - * template type. The arguments args... are forwarded to the constructor as std::forward(args).... + * @brief Appends a new element to the end of the container. The element is constructed through a constructor of + * the template type. The arguments args... are forwarded to the constructor as std::forward(args).... * * If after the operation the new size() is greater than old capacity() a reallocation takes place, in which case * all iterators (including the end() iterator) and all references to the elements are invalidated. Otherwise only @@ -230,9 +361,9 @@ namespace kstd auto emplace_back(Args &&... args) -> value_type & { increase_capacity_if_full(); - _data[_size] = value_type{std::forward(args)...}; - auto const index = _size++; - return _data[index]; + std::allocator_traits::construct(m_allocator, &(*this)[m_size], std::forward(args)...); + ++m_size; + return this->back(); } /** @@ -243,7 +374,7 @@ namespace kstd auto pop_back() -> void { throw_if_empty(); - (void)_size--; + (void)m_size--; } /** @@ -254,7 +385,7 @@ namespace kstd */ auto begin() noexcept -> pointer { - return _data; + return m_data; } /** @@ -265,7 +396,7 @@ namespace kstd */ [[nodiscard]] auto begin() const noexcept -> const_pointer { - return _data; + return m_data; } /** @@ -280,30 +411,30 @@ namespace kstd } /** - * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last element - * of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend(). + * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last + * element of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend(). * * @return Reverse iterator to the first element. */ auto rbegin() noexcept -> pointer { - return _data + _size - 1; + return m_data + m_size - 1; } /** - * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last element - * of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend(). + * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last + * element of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend(). * * @return Reverse iterator to the first element. */ [[nodiscard]] auto rbegin() const noexcept -> const_pointer { - return _data + _size - 1; + return m_data + m_size - 1; } /** - * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last element - * of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend(). + * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last + * element of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend(). * * @return Reverse iterator to the first element. */ @@ -320,7 +451,7 @@ namespace kstd */ auto end() noexcept -> pointer { - return _data + _size; + return m_data + m_size; } /** @@ -331,7 +462,7 @@ namespace kstd */ [[nodiscard]] auto end() const noexcept -> const_pointer { - return _data + _size; + return m_data + m_size; } /** @@ -354,7 +485,7 @@ namespace kstd */ auto rend() noexcept -> pointer { - return _data + size() - 1; + return m_data + size() - 1; } /** @@ -366,7 +497,7 @@ namespace kstd */ [[nodiscard]] auto rend() const noexcept -> const_pointer { - return _data + size() - 1; + return m_data + size() - 1; } /** @@ -383,28 +514,28 @@ namespace kstd /** * @brief Returns a pointer to the underlying array serving as element storage. The pointer is such that range - * [data(), data() + size()) is always a valid range, even if the container is empty (data() is not dereferenceable - * in that case). + * [data(), data() + size()) is always a valid range, even if the container is empty (data() is not + * dereferenceable in that case). * - * @return Pointer to the underlying element storage. For non-empty containers, the returned pointer compares equal - * to the address of the first element. + * @return Pointer to the underlying element storage. For non-empty containers, the returned pointer compares + * equal to the address of the first element. */ auto data() -> pointer { - return _data; + return m_data; } /** * @brief Returns a pointer to the underlying array serving as element storage. The pointer is such that range - * [data(), data() + size()) is always a valid range, even if the container is empty (data() is not dereferenceable - * in that case). + * [data(), data() + size()) is always a valid range, even if the container is empty (data() is not + * dereferenceable in that case). * - * @return Pointer to the underlying element storage. For non-empty containers, the returned pointer compares equal - * to the address of the first element. + * @return Pointer to the underlying element storage. For non-empty containers, the returned pointer compares + * equal to the address of the first element. */ [[nodiscard]] auto data() const -> const_pointer { - return _data; + return m_data; } /** @@ -469,11 +600,11 @@ namespace kstd * the vector greater than the value of capacity(). * * @note Correctly using reserve() can prevent unnecessary reallocations, but inappropriate uses of reserve() (for - * instance, calling it before every push_back() call) may actually increase the number of reallocations (by causing - * the capacity to grow linearly rather than exponentially) and result in increased computational complexity and - * decreased performance. For example, a function that receives an arbitrary vector by reference and appends - * elements to it should usually not call reserve() on the vector, since it does not know of the vector's usage - * characteristics. + * instance, calling it before every push_back() call) may actually increase the number of reallocations (by + * causing the capacity to grow linearly rather than exponentially) and result in increased computational + * complexity and decreased performance. For example, a function that receives an arbitrary vector by reference + * and appends elements to it should usually not call reserve() on the vector, since it does not know of the + * vector's usage characteristics. * * When inserting a range, the range version of insert() is generally preferable as it preserves the correct * capacity growth behavior, unlike reserve() followed by a series of push_back()s. @@ -484,16 +615,16 @@ namespace kstd */ auto reserve(size_type new_capacity) -> void { - if (new_capacity <= _capacity) + if (new_capacity <= m_capacity) { return; } - _capacity = new_capacity; - auto temp = new value_type[_capacity](); + m_capacity = new_capacity; + auto temp = new value_type[m_capacity](); std::ranges::copy(begin(), end(), temp); - delete[] _data; - _data = temp; + delete[] m_data; + m_data = temp; } /** @@ -504,16 +635,16 @@ namespace kstd */ auto shrink_to_fit() -> void { - if (_size == _capacity) + if (m_size == m_capacity) { return; } - _capacity = _size; - auto temp = new value_type[_capacity]{}; + m_capacity = m_size; + auto temp = new value_type[m_capacity]{}; std::ranges::copy(begin(), end(), temp); - delete[] _data; - _data = temp; + delete[] m_data; + m_data = temp; } /** @@ -523,10 +654,30 @@ namespace kstd */ [[nodiscard]] auto empty() const -> bool { - return _size <= 0; + return m_size <= 0; } private: + auto allocate_n(std::size_t count) -> std::allocator_traits::pointer + { + if (count) + { + return std::allocator_traits::allocate(m_allocator, count); + } + return nullptr; + } + + auto clear_and_deallocate() -> void + { + std::ranges::for_each(std::views::reverse(*this), [&](auto & element) { + std::allocator_traits::destroy(m_allocator, std::addressof(element)); + }); + std::allocator_traits::deallocate(m_allocator, m_data, m_capacity); + m_capacity = 0; + m_size = 0; + m_data = nullptr; + } + /** * @brief Halts the execution of the application if the data container is currently empty. */ @@ -540,7 +691,7 @@ namespace kstd auto throw_if_out_of_range(size_type index) const -> void { - if (index >= _size) + if (index >= m_size) { os::panic("[Vector] Attempted to read element at invalid index"); } @@ -548,20 +699,21 @@ namespace kstd /** * @brief Increases the internal capacity to 1 if it was previously 0 and to * 2 after that, meaning exponential - * growth. This is done to decrease the amount of single allocations done and because a power of 2 in memory size is - * normally perferable for the cache. + * growth. This is done to decrease the amount of single allocations done and because a power of 2 in memory size + * is normally perferable for the cache. */ auto increase_capacity_if_full() -> void { - if (_size == _capacity) + if (m_size == m_capacity) { - reserve(_capacity == 0U ? 1U : _capacity * 2U); + reserve(m_capacity == 0U ? 1U : m_capacity * 2U); } } - size_type _size = {}; ///< Amount of elements in the underlying data container - size_type _capacity = {}; ///< Amount of space for elements in the underlying data container - value_type * _data = {}; ///< Pointer to the first element in the underlying data container + [[no_unique_address]] allocator_type m_allocator{}; + size_type m_size{}; ///< Amount of elements in the underlying data container + size_type m_capacity{}; ///< Amount of space for elements in the underlying data container + value_type * m_data{}; ///< Pointer to the first element in the underlying data container }; } // namespace kstd -- cgit v1.2.3 From cc55324b0f3a988befaecff15e0ff87e3c6a4dee Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 19 Mar 2026 08:08:32 +0100 Subject: kstd: implement default allocator --- libs/kstd/include/kstd/allocator | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 libs/kstd/include/kstd/allocator diff --git a/libs/kstd/include/kstd/allocator b/libs/kstd/include/kstd/allocator new file mode 100644 index 0000000..0de0e10 --- /dev/null +++ b/libs/kstd/include/kstd/allocator @@ -0,0 +1,64 @@ +#ifndef KSTD_ALLOCATOR_HPP +#define KSTD_ALLOCATOR_HPP + +#include +#include +#include + +#if __has_builtin(__builtin_operator_new) >= 201'802L +#define KSTD_OPERATOR_NEW __builtin_operator_new +#define KSTD_OPERATOR_DELETE __builtin_operator_delete +#else +#define KSTD_OPERATOR_NEW ::operator new +#define KSTD_OPERATOR_DELETE ::operator delete +#endif + +namespace kstd +{ + + template + struct allocator + { + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using propagate_on_container_move_assignment = std::true_type; + using is_always_equal = std::true_type; + + constexpr allocator() noexcept = default; + + template + constexpr allocator(allocator const &) noexcept + {} + + constexpr auto allocate(std::size_t n) -> T * + { + if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + { + return static_cast(KSTD_OPERATOR_NEW(n * sizeof(T), std::align_val_t{alignof(T)})); + } + return static_cast(KSTD_OPERATOR_NEW(n * sizeof(T))); + } + + constexpr void deallocate(T * p, std::size_t n) noexcept + { + if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + { + KSTD_OPERATOR_DELETE(p, n * sizeof(T), std::align_val_t{alignof(T)}); + } + KSTD_OPERATOR_DELETE(p, n * sizeof(T)); + } + }; + + template + constexpr auto operator==(allocator const &, allocator const &) noexcept -> bool + { + return true; + } + +} // namespace kstd + +#undef KSTD_OPERATOR_NEW +#undef KSTD_OPERATOR_DELETE + +#endif \ No newline at end of file -- cgit v1.2.3 From ae2a264b117ecf556f742d8e9c357f906cb3fd83 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 19 Mar 2026 13:00:08 +0100 Subject: kstd: finish preliminary vector implementation --- .../kapi/boot_module/boot_module_registry.hpp | 4 +- libs/kstd/include/kstd/ranges | 21 + libs/kstd/include/kstd/vector | 810 ++++++++++----------- 3 files changed, 403 insertions(+), 432 deletions(-) create mode 100644 libs/kstd/include/kstd/ranges diff --git a/kapi/include/kapi/boot_module/boot_module_registry.hpp b/kapi/include/kapi/boot_module/boot_module_registry.hpp index 70b5592..0692d37 100644 --- a/kapi/include/kapi/boot_module/boot_module_registry.hpp +++ b/kapi/include/kapi/boot_module/boot_module_registry.hpp @@ -20,8 +20,8 @@ namespace kapi::boot_modules using value_type = range_type::value_type; using const_reference = range_type::const_reference; - using const_iterator = range_type::const_pointer; - using const_reverse_iterator = range_type::const_pointer; + using const_iterator = range_type::const_iterator; + using const_reverse_iterator = range_type::const_reverse_iterator; using size_type = range_type::size_type; [[nodiscard]] auto begin() const noexcept -> const_iterator diff --git a/libs/kstd/include/kstd/ranges b/libs/kstd/include/kstd/ranges new file mode 100644 index 0000000..78c3adb --- /dev/null +++ b/libs/kstd/include/kstd/ranges @@ -0,0 +1,21 @@ +#ifndef KSTD_RANGES +#define KSTD_RANGES + +#include // IWYU pragma: export + +namespace kstd +{ +#if __glibcxx_ranges_to_container + using std::from_range; + using std::from_range_t; +#else + struct from_range_t + { + explicit from_range_t() = default; + }; + constexpr auto inline from_range = from_range_t{}; +#endif + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index b87756a..9709c2a 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -1,14 +1,19 @@ #ifndef KSTD_VECTOR_HPP #define KSTD_VECTOR_HPP +#include #include +#include #include +#include +#include #include #include #include #include #include +#include #include namespace kstd @@ -20,7 +25,7 @@ namespace kstd * @tparam T Element the vector instance should contain. * @tparam Allocator The allocator to use when allocating new elements. */ - template> + template> struct vector { using value_type = T; ///< Type of the elements contained in the container. @@ -37,20 +42,51 @@ namespace kstd using const_reverse_iterator = std::reverse_iterator; //! Construct a new, empty vector. - vector() = default; + vector() noexcept(std::is_nothrow_default_constructible_v) + : vector(allocator_type{}) + {} + + //! Construct a new, empty vector with a given allocator. + //! + //! @param allocator The allocator to use in the vector. + explicit constexpr vector(allocator_type const & allocator) noexcept( + std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} + {} + + //! Construct a new vector and fill it with the given number of default constructed elements. + //! + //! @param count The number of element to create the vector with. + //! @param allocator The allocator to use in the vector. + explicit constexpr vector(size_type count, allocator_type const & allocator = allocator_type{}) noexcept( + std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} + , m_size{count} + , m_capacity{count} + , m_data{allocate_n(m_capacity)} + { + for (auto i = 0uz; i < count; ++i) + { + std::allocator_traits::construct(m_allocator, m_data + i); + } + } - //! Construct a new vector, with the given number of element and initialize them to the given value. + //! Construct a new vector and fill it with the given number of copy constructed elements. //! - //! @param n The number of elements to construct the container with. - //! @param initial The value to initialize each element to. - explicit vector(size_type n, value_type initial = value_type{}) - : m_size{n} - , m_capacity{n} + //! @param count The number of element to create the vector with. + //! @param value The value to copy for each element + //! @param allocator The allocator to use in the vector. + constexpr vector(size_type count, const_reference value, + allocator_type const & allocator = + allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} + , m_size{count} + , m_capacity{m_size} , m_data{allocate_n(m_capacity)} { - for (auto i = 0uz; i < n; ++i) + for (auto i = 0uz; i < count; ++i) { - std::allocator_traits::construct(m_allocator, m_data + i, initial); + std::allocator_traits::construct(m_allocator, m_data + i, value); } } @@ -59,66 +95,103 @@ namespace kstd //! @tparam InputIterator An iterator type used to describe the source range. //! @param first The start of the source range. //! @param last The end of the source range. - template - explicit vector(InputIterator first, InputIterator last) - : m_allocator{} + template + constexpr vector(InputIterator first, InputIterator last, + allocator_type const & allocator = + allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} , m_size{std::ranges::distance(first, last)} - , m_capacity{std::ranges::distance(first, last)} + , m_capacity{m_size} , m_data{allocate_n(m_capacity)} { - for (auto destination = begin(); first != last; ++first, ++destination) + for (auto destination = m_data; first != last; ++first, ++destination) { std::allocator_traits::construct(m_allocator, destination, *first); } } - //! Construct a new vector and initialize it's content by copying all elements in the given initializer list. + //! Construct a new vector and initialize it's content by copying all elements in the given range. //! - //! @param list The initializer list containing the source objects. - explicit vector(std::initializer_list list) - : vector{std::ranges::begin(list), std::ranges::end(list)} - {} + //! + template + requires(std::ranges::input_range && std::convertible_to, T>) + constexpr vector(kstd::from_range_t, Range && range, allocator_type const & allocator = allocator_type{}) + : m_allocator{allocator} + , m_size{std::ranges::size(range)} + , m_capacity{m_size} + , m_data{allocate_n(m_capacity)} + { + auto destination = m_data; + for (auto && element : std::forward(range)) + { + std::allocator_traits::construct(m_allocator, destination++, + std::forward>(element)); + } + } //! Construct a new vector and initialize it's content by copying all elements from a given vector. //! //! @param other The source vector. - vector(vector const & other) + constexpr vector(vector const & other) : m_allocator{other.m_allocator} , m_size{other.m_size} , m_capacity{other.m_capacity} , m_data(allocate_n(m_capacity)) { - auto first = std::ranges::begin(other); - auto last = std::ranges::end(other); - for (auto destination = begin(); first != last; ++first, ++destination) - { - std::allocator_traits::construct(m_allocator, destination, *first); - } + uninitialized_copy_with_allocator(other.begin(), begin(), other.size()); } //! Construct a new vector and initialize it's content by moving from a given vector. //! //! @param other The source vector. - vector(vector && other) noexcept - : m_allocator{std::exchange(other.m_allocator, allocator_type{})} + constexpr vector(vector && other) noexcept + : m_allocator{std::move(std::exchange(other.m_allocator, allocator_type{}))} , m_size{std::exchange(other.m_size, size_type{})} , m_capacity(std::exchange(other.m_capacity, size_type{})) , m_data(std::exchange(other.m_data, nullptr)) {} + //! Construct a new vector and initialize it's content by copying from a given vector. + //! + //! @param other The source vector. + //! @param allocator The allocator to use in the vector. + constexpr vector(vector const & other, std::type_identity_t const & allocator) + : m_allocator{allocator} + , m_size{other.m_size} + , m_capacity{other.m_capacity} + , m_data{allocate_n(m_capacity)} + { + uninitialized_copy_with_allocator(other.begin(), begin(), other.size()); + } + + //! Construct a new vector and initialize it's content by copying from a given vector. + //! + //! @param other The source vector. + //! @param allocator The allocator to use in the vector. + constexpr vector(vector && other, std::type_identity_t const & allocator) + : m_allocator{allocator} + , m_size{std::exchange(other.m_size, size_type{})} + , m_capacity(std::exchange(other.m_capacity, size_type{})) + , m_data(std::exchange(other.m_data, nullptr)) + {} + + //! Construct a new vector and initialize it's content by copying all elements in the given initializer list. + //! + //! @param list The initializer list containing the source objects. + explicit vector(std::initializer_list list, allocator_type const & allocator = allocator_type{}) + : vector{std::ranges::begin(list), std::ranges::end(list), allocator} + {} + //! Destroy this vector. - ~vector() + constexpr ~vector() { - std::ranges::for_each(std::views::reverse(*this), [&](auto & element) { - std::allocator_traits::destroy(m_allocator, std::addressof(element)); - }); - std::allocator_traits::deallocate(m_allocator, m_data, m_capacity); + clear_and_deallocate(); } //! Replace the contents of this vector by the copying from the given source vector. //! //! @param other The source vector. - auto operator=(vector const & other) -> vector & + constexpr auto operator=(vector const & other) -> vector & { if (this == std::addressof(other)) { @@ -127,58 +200,49 @@ namespace kstd if constexpr (std::allocator_traits::propagate_on_container_move_assignment::value) { - if (m_allocator != other.m_allocator) + if (get_allocator() != other.get_allocator()) { clear_and_deallocate(); } - m_allocator = other.m_allocator; + m_allocator = other.get_allocator(); } - if (m_capacity >= other.m_size) + if (capacity() >= other.size()) { auto const overlap = std::min(m_size, other.m_size); - auto first = std::ranges::begin(other); - for (auto i = 0uz; i < overlap; ++first, ++i) - { - m_data[i] = *first; - } + copy_elements(other.begin(), begin(), overlap); if (m_size < other.m_size) { - for (auto i = m_size; i < other.m_size; ++first, ++i) - { - std::allocator_traits::construct(m_allocator, m_data + i, *first); - } + uninitialized_copy_with_allocator(other.begin() + size(), begin() + size(), other.m_size - size()); } else if (m_size > other.m_size) { - for (auto i = other.m_size; i < m_size; ++i) - { - std::allocator_traits::destroy(m_allocator, m_data + i); - } + destroy_n(begin() + other.size(), size() - other.size()); } } else { - auto new_data = std::allocator_traits::allocate(m_allocator, other.m_size); - for (auto i = 0uz; i < other.m_size; ++i) - { - std::allocator_traits::construct(m_allocator, new_data + i, other.m_data[i]); - } + auto new_data = std::allocator_traits::allocate(m_allocator, other.size()); + uninitialized_copy_with_allocator(other.begin(), new_data, other.size()); clear_and_deallocate(); - std::exchange(m_data, new_data); - m_capacity = other.m_size; + m_data = new_data; + m_capacity = other.size(); } - m_size = other.m_size; + m_size = other.size(); return *this; } //! Replace the contents fo this vector by moving from the given source. //! //! @param other The source vector. - auto operator=(vector && other) noexcept -> vector & + constexpr auto operator=(vector && other) noexcept( + std::allocator_traits::propagate_on_container_move_assignment::value || + std::allocator_traits::is_always_equal::value) -> vector & { + using std::swap; + if (this == std::addressof(other)) { return *this; @@ -187,478 +251,338 @@ namespace kstd if constexpr (std::allocator_traits::propagate_on_container_move_assignment::value) { clear_and_deallocate(); - m_allocator = std::move(other.m_allocator); - m_size = std::exchange(other.m_size, size_type{}); - m_capacity = std::exchange(other.m_capacity, size_type{}); - m_data = std::exchange(other.m_data, nullptr); + swap(m_allocator, other.m_allocator); + swap(m_size, other.m_size); + swap(m_capacity, other.m_capacity); + swap(m_data, other.m_data); } - else if (m_allocator != other.m_allocator) + else if (m_allocator == other.m_allocator) { clear_and_deallocate(); - m_size = std::exchange(other.m_size, size_type{}); - m_capacity = std::exchange(other.m_capacity, size_type{}); - m_data = std::exchange(other.m_data, nullptr); + swap(m_size, other.m_size); + swap(m_capacity, other.m_capacity); + swap(m_data, other.m_data); } else { - if (m_capacity >= other.m_size) + if (capacity() >= other.size()) { - auto const overlap = std::min(m_size, other.m_size); - auto first = std::ranges::begin(other); - for (auto i = 0uz; i < overlap; ++first, ++i) - { - m_data[i] = std::move(*first); - } + auto const overlap = std::min(size(), other.size()); + move_elements(other.begin(), begin(), overlap); - if (m_size < other.m_size) + if (size() < other.size()) { - for (auto i = m_size; i < other.m_size; ++first, ++i) - { - std::allocator_traits::construct(m_allocator, m_data + i, std::move(*first)); - } + uninitialized_move_with_allocator(other.begin() + size(), begin() + size(), other.size() - size()); } - else if (m_size > other.m_size) + else if (size() > other.size()) { - for (auto i = other.m_size; i < m_size; ++i) - { - std::allocator_traits::destroy(m_allocator, m_data + i); - } + destroy_n(begin() + other.size(), size() - other.size()); } } else { - auto new_data = std::allocator_traits::allocate(m_allocator, other.m_size); - for (auto i = 0uz; i < other.m_size; ++i) - { - std::allocator_traits::destroy(m_allocator, m_data + i); - } + auto new_data = std::allocator_traits::allocate(get_allocator(), other.size()); + uninitialized_move_with_allocator(other.begin(), new_data, other.size()); clear_and_deallocate(); - std::exchange(m_data, new_data); + m_data = new_data; m_capacity = other.m_size; } - for (auto i = 0uz; i < other.m_size; ++i) - { - std::allocator_traits::destroy(other.m_allocator, other.m_data + i); - } + + other.destroy_n(other.begin(), other.size()); m_size = std::exchange(other.m_size, size_type{}); } return *this; } - /** - * @brief Amount of elements currently contained in this vector, will fill up until we have reached the capacity. - * If that is the case the capacity is increased automatically. - * - * @return Current amount of elements. - */ - [[nodiscard]] auto size() const -> size_type + //! Get a copy of the allocator associated with this vector. + [[nodiscard]] constexpr auto get_allocator() const noexcept(std::is_nothrow_copy_constructible_v) + -> allocator_type { - return m_size; + return m_allocator; } - /** - * @brief Amount of space the vector currently has, can be different than the size, because we allocate more than - * we exactly require to decrease the amount of allocations and deallocation to improve speed. - * - * @return Current amount of space the vector has for elements. - */ - [[nodiscard]] auto capacity() const -> size_type + //! Get a reference to the element at the given index. + //! + //! This function will panic if the index is out of bounds for this vector. + //! + //! @param index The index of the element to retrieve. + //! @return A reference to the element at the specified index. + [[nodiscard]] constexpr auto at(size_type index) -> reference { - return m_capacity; + panic_if_out_of_bounds(index); + return (*this)[index]; } - /** - * @brief Array indexing operator. Allowing to access element at the given index. - * - * @note Does not do any bounds checks use at() for that. - * - * @param index Index we want to access elements at. - * @return Reference to the underlying element. - */ - auto operator[](size_type index) -> reference - { - return m_data[index]; - } - - /** - * @brief Array indexing operator. Allowing to access element at the given index. - * - * @note Does not do any bounds checks use at() for that. - * - * @param index Index we want to access elements at. - * @return Reference to the underlying element. - */ - auto operator[](size_type index) const -> const_reference - { - return m_data[index]; - } - - /** - * @brief Array indexing operator. Allowing to access element at the given index. - * - * @note Ensures we do not access element outside of the bounds of the array, if we do further execution is - * halted. - * - * @param index Index we want to access elements at. - * @return Reference to the underlying element. - */ - auto at(size_type index) -> reference - { - throw_if_out_of_range(index); + //! Get a reference to the element at the given index. + //! + //! This function will panic if the index is out of bounds for this vector. + //! + //! @param index The index of the element to retrieve. + //! @return A reference to the element at the specified index. + [[nodiscard]] constexpr auto at(size_type index) const -> const_reference + { + panic_if_out_of_bounds(index); return (*this)[index]; } - /** - * @brief Array indexing operator. Allowing to access element at the given index. - * - * @note Ensures we do not access element outside of the bounds of the array, if we do further execution is - * halted. - * - * @param index Index we want to access elements at. - * @return Reference to the underlying element. - */ - [[nodiscard]] auto at(size_type index) const -> const_reference + //! Get a reference to the element at the given index. + //! + //! The behavior is undefined if the index is out of bounds for this vector. + //! + //! @param index The index of the element to retrieve. + //! @return A reference to the element at the specified index. + [[nodiscard]] constexpr auto operator[](size_type index) -> reference { - throw_if_out_of_range(index); - return (*this)[index]; + return data()[index]; } - /** - * @brief Appends the given element value to the end of the container. The element is assigned through the - * assignment operator of the template type. The value is forwarded to the constructor as - * std::forward(value), meaning it is either moved (rvalue) or copied (lvalue). - * - * @note If after the operation the new size() is greater than old capacity() a reallocation takes place, - * in which case all iterators (including the end() iterator) and all references to the elements are invalidated. - * Otherwise only the end() iterator is invalidated. Uses a forward reference for the actual value passed, which - * allows the template method to be used by both lvalue and rvalues and compile a different implementation. - * - * @param value The value of the element to append. - */ - template - auto push_back(U && value) -> void + //! Get a reference to the element at the given index. + //! + //! The behavior is undefined if the index is out of bounds for this vector. + //! + //! @param index The index of the element to retrieve. + //! @return A reference to the element at the specified index. + [[nodiscard]] constexpr auto operator[](size_type index) const -> const_reference { - increase_capacity_if_full(); - std::allocator_traits::construct(m_allocator, &(*this)[m_size], std::forward(value)); - ++m_size; + return data()[index]; } - /** - * @brief Appends a new element to the end of the container. The element is constructed through a constructor of - * the template type. The arguments args... are forwarded to the constructor as std::forward(args).... - * - * If after the operation the new size() is greater than old capacity() a reallocation takes place, in which case - * all iterators (including the end() iterator) and all references to the elements are invalidated. Otherwise only - * the end() iterator is invalidated. Uses a forward reference for the actual value passed, which - * allows the template method to be used by both lvalue and rvalues and compile a different implementation. - * - * @tparam Args - * @param args Arguments to forward to the constructor of the element - * @return value_type& - */ - template - auto emplace_back(Args &&... args) -> value_type & + //! Get a reference to the first element of this vector. + //! + //! The behavior is undefined if this vector is empty. + [[nodiscard]] constexpr auto front() -> reference { - increase_capacity_if_full(); - std::allocator_traits::construct(m_allocator, &(*this)[m_size], std::forward(args)...); - ++m_size; - return this->back(); + return *begin(); } - /** - * @brief Removes the last element of the container. Calling pop_back on an empty container results in halting the - * further execution. Iterators and references to the last element are invalidated. The end() - * iterator is also invalidated. - */ - auto pop_back() -> void + //! Get a reference to the first element of this vector. + //! + //! The behavior is undefined if this vector is empty. + [[nodiscard]] auto front() const -> const_reference { - throw_if_empty(); - (void)m_size--; + return *begin(); } - /** - * @brief Returns an iterator to the first element of the vector. - * If the vector is empty, the returned iterator will be equal to end(). - * - * @return Iterator to the first element. - */ - auto begin() noexcept -> pointer + //! Get a reference to the last element of this vector. + //! + //! The behavior is undefined if this vector is empty. + [[nodiscard]] constexpr auto back() -> reference { - return m_data; + return *rbegin(); + } + + //! Get a reference to the last element of this vector. + //! + //! The behavior is undefined if this vector is empty. + [[nodiscard]] constexpr auto back() const -> const_reference + { + return *rbegin(); } - /** - * @brief Returns an iterator to the first element of the vector. - * If the vector is empty, the returned iterator will be equal to end(). - * - * @return Iterator to the first element. - */ - [[nodiscard]] auto begin() const noexcept -> const_pointer + //! Get a pointer to the beginning of the underlying contiguous storage. + [[nodiscard]] constexpr auto data() noexcept -> pointer { return m_data; } - /** - * @brief Returns an iterator to the first element of the vector. - * If the vector is empty, the returned iterator will be equal to end(). - * - * @return Iterator to the first element. - */ - [[nodiscard]] auto cbegin() const noexcept -> const_pointer + //! Get a pointer to the beginning of the underlying contiguous storage. + [[nodiscard]] constexpr auto data() const noexcept -> const_pointer { - return begin(); + return m_data; } - /** - * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last - * element of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend(). - * - * @return Reverse iterator to the first element. - */ - auto rbegin() noexcept -> pointer + //! Get an iterator to the first element of this vector. + //! + //! @return An iterator to the first element of this container, or end() if the container is empty. + [[nodiscard]] constexpr auto begin() noexcept -> iterator { - return m_data + m_size - 1; + return empty() ? end() : data(); } - /** - * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last - * element of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend(). - * - * @return Reverse iterator to the first element. - */ - [[nodiscard]] auto rbegin() const noexcept -> const_pointer + //! Get an iterator to the first element of this vector. + //! + //! @return An iterator to the first element of this container, or end() if the container is empty. + [[nodiscard]] constexpr auto begin() const noexcept -> const_iterator { - return m_data + m_size - 1; + return empty() ? end() : data(); } - /** - * @brief Returns a reverse iterator to the first element of the reversed vector. It corresponds to the last - * element of the non-reversed vector. If the vector is empty, the returned iterator will be equal to rend(). - * - * @return Reverse iterator to the first element. - */ - [[nodiscard]] auto crbegin() const noexcept -> const_pointer + //! Get an iterator to the first element of this vector. + //! + //! @return An iterator to the first element of this container, or end() if the container is empty. + [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator { - return rbegin(); + return begin(); } - /** - * @brief Returns an iterator to the element following the last element of the vector. This element acts as a - * placeholder, attempting to access it results in undefined behavior. - * - * @return Iterator to the element following the last element. - */ - auto end() noexcept -> pointer + //! Get an iterator past the last element of this vector. + [[nodiscard]] constexpr auto end() noexcept -> pointer { - return m_data + m_size; + return capacity() ? data() + size() : nullptr; } - /** - * @brief Returns an iterator to the element following the last element of the vector. This element acts as a - * placeholder, attempting to access it results in undefined behavior. - * - * @return Iterator to the element following the last element. - */ - [[nodiscard]] auto end() const noexcept -> const_pointer + //! Get an iterator past the last element of this vector. + [[nodiscard]] constexpr auto end() const noexcept -> const_pointer { - return m_data + m_size; + return capacity() ? data() + size() : nullptr; } - /** - * @brief Returns an iterator to the element following the last element of the vector. This element acts as a - * placeholder, attempting to access it results in undefined behavior. - * - * @return Iterator to the element following the last element. - */ - [[nodiscard]] auto cend() const noexcept -> const_pointer + //! Get an iterator past the last element of this vector. + [[nodiscard]] constexpr auto cend() const noexcept -> const_pointer { return end(); } - /** - * @brief Returns a reverse iterator to the element following the last element of the reversed vector. It - * corresponds to the element preceding the first element of the non-reversed vector. This element acts as a - * placeholder, attempting to access it results in undefined behavior. - * - * @return Reverse iterator to the element following the last element. - */ - auto rend() noexcept -> pointer + //! Get a reverse iterator to the reverse beginning. + [[nodiscard]] constexpr auto rbegin() noexcept -> reverse_iterator { - return m_data + size() - 1; + return empty() ? rend() : reverse_iterator{begin() + (m_size - 1)}; } - /** - * @brief Returns a reverse iterator to the element following the last element of the reversed vector. It - * corresponds to the element preceding the first element of the non-reversed vector. This element acts as a - * placeholder, attempting to access it results in undefined behavior. - * - * @return Reverse iterator to the element following the last element. - */ - [[nodiscard]] auto rend() const noexcept -> const_pointer + //! Get a reverse iterator to the reverse beginning. + [[nodiscard]] constexpr auto rbegin() const noexcept -> const_reverse_iterator { - return m_data + size() - 1; + return empty() ? rend() : const_reverse_iterator{begin() + (m_size - 1)}; } - /** - * @brief Returns a reverse iterator to the element following the last element of the reversed vector. It - * corresponds to the element preceding the first element of the non-reversed vector. This element acts as a - * placeholder, attempting to access it results in undefined behavior. - * - * @return Reverse iterator to the element following the last element. - */ - [[nodiscard]] auto crend() const noexcept -> const_pointer + //! Get a reverse iterator to the reverse beginning. + [[nodiscard]] constexpr auto crbegin() const noexcept -> const_reverse_iterator { return rbegin(); } - /** - * @brief Returns a pointer to the underlying array serving as element storage. The pointer is such that range - * [data(), data() + size()) is always a valid range, even if the container is empty (data() is not - * dereferenceable in that case). - * - * @return Pointer to the underlying element storage. For non-empty containers, the returned pointer compares - * equal to the address of the first element. - */ - auto data() -> pointer + //! Get a reverse iterator to the reverse end. + [[nodiscard]] constexpr auto rend() noexcept -> reverse_iterator { - return m_data; + return reverse_iterator{capacity() ? data() - 1 : nullptr}; } - /** - * @brief Returns a pointer to the underlying array serving as element storage. The pointer is such that range - * [data(), data() + size()) is always a valid range, even if the container is empty (data() is not - * dereferenceable in that case). - * - * @return Pointer to the underlying element storage. For non-empty containers, the returned pointer compares - * equal to the address of the first element. - */ - [[nodiscard]] auto data() const -> const_pointer + //! Get a reverse iterator to the reverse end. + [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator { - return m_data; + return const_reverse_iterator{capacity() ? data() - 1 : nullptr}; } - /** - * @brief Returns a reference to the first element in the container. Calling front on an empty container causes - * undefined behavior. - * - * @return Reference to the first element. - */ - auto front() -> reference + //! Get a reverse iterator to the reverse end. + [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator { - throw_if_empty(); - return *begin(); + return rend(); } - /** - * @brief Returns a reference to the first element in the container. Calling front on an empty container causes - * undefined behavior. - * - * @return Reference to the first element. - */ - [[nodiscard]] auto front() const -> const_reference + //! Check whether this vector is empty. + [[nodiscard]] constexpr auto empty() const noexcept -> bool { - throw_if_empty(); - return *begin(); + return !size(); } - /** - * @brief Returns a reference to the last element in the container. Calling back on an empty container causes - * undefined behavior. - * - * @return Reference to the last element. - */ - auto back() -> reference + //! Get the number of elements present in this vector. + [[nodiscard]] constexpr auto size() const noexcept -> size_type { - throw_if_empty(); - return *rbegin(); + return m_size; } - /** - * @brief Returns a reference to the last element in the container. Calling back on an empty container causes - * undefined behavior. - * - * @return Reference to the last element. - */ - [[nodiscard]] auto back() const -> const_reference + //! Get the maximum possible number of element for this vector. + [[nodiscard]] constexpr auto max_size() const noexcept -> size_type { - throw_if_empty(); - return *rbegin(); + return std::allocator_traits::max_size(m_allocator); } - /** - * @brief Increase the capacity of the vector (the total number of elements that the vector can hold without - * requiring reallocation) to a value that's greater or equal to new_cap. If new_cap is greater than the current - * capacity(), new storage is allocated, otherwise the function does nothing. - * - * reserve() does not change the size of the vector. - * - * If new_cap is greater than capacity(), all iterators (including the end() iterator) and all references to the - * elements are invalidated. Otherwise, no iterators or references are invalidated. - * - * After a call to reserve(), insertions will not trigger reallocation unless the insertion would make the size of - * the vector greater than the value of capacity(). - * - * @note Correctly using reserve() can prevent unnecessary reallocations, but inappropriate uses of reserve() (for - * instance, calling it before every push_back() call) may actually increase the number of reallocations (by - * causing the capacity to grow linearly rather than exponentially) and result in increased computational - * complexity and decreased performance. For example, a function that receives an arbitrary vector by reference - * and appends elements to it should usually not call reserve() on the vector, since it does not know of the - * vector's usage characteristics. - * - * When inserting a range, the range version of insert() is generally preferable as it preserves the correct - * capacity growth behavior, unlike reserve() followed by a series of push_back()s. - * - * reserve() cannot be used to reduce the capacity of the container; to that end shrink_to_fit() is provided. - * - * @param new_capacity New capacity of the vector, in number of elements - */ - auto reserve(size_type new_capacity) -> void - { - if (new_capacity <= m_capacity) + //! Reserve storage for at list the given number of elements. + constexpr auto reserve(size_type new_capacity) -> void + { + if (new_capacity <= capacity()) + { + return; + } + + if (new_capacity > max_size()) { + kstd::os::panic("[kstd:vector] Tried to reserve more space than theoretically possible."); return; } + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + for (auto i = 0uz; i < old_size; ++i) + { + std::allocator_traits::construct(m_allocator, new_data + i, std::move((*this)[i])); + } + clear_and_deallocate(); + std::exchange(m_data, new_data); m_capacity = new_capacity; - auto temp = new value_type[m_capacity](); - std::ranges::copy(begin(), end(), temp); - delete[] m_data; - m_data = temp; + m_size = old_size; } - /** - * @brief Requests the removal of unused capacity. Meaning it requests to reduce capacity() to size(). - * - * If reallocation occurs, all iterators (including the end() iterator) and all references to the elements are - * invalidated. If no reallocation occurs, no iterators or references are invalidated. - */ - auto shrink_to_fit() -> void + //! Get the number of element this vector has currently space for, including elements currently in this vector. + [[nodiscard]] constexpr auto capacity() const noexcept -> size_type + { + return m_capacity; + } + + //! Try to release unused storage space. + constexpr auto shrink_to_fit() -> void { if (m_size == m_capacity) { return; } + auto new_data = allocate_n(m_size); + for (auto & element : *this) + { + std::allocator_traits::construct(m_allocator, new_data++, std::move(element)); + } + clear_and_deallocate(); + std::exchange(m_data, new_data); m_capacity = m_size; - auto temp = new value_type[m_capacity]{}; - std::ranges::copy(begin(), end(), temp); - delete[] m_data; - m_data = temp; } - /** - * @brief Wheter there are currently any items this container or not. - * - * @return True if there are no elements, false if there are. - */ - [[nodiscard]] auto empty() const -> bool + //! Clear the contents of this vector. + constexpr auto clear() noexcept -> void { - return m_size <= 0; + for (auto i = m_size; i > 0; --i) + { + pop_back(); + } + } + + //! Append a given element to this vector via copy construction. + constexpr auto push_back(value_type const & value) -> void + { + increase_capacity_if_full(); + std::allocator_traits::construct(m_allocator, &(*this)[m_size], value); + ++m_size; + } + + //! Append a given element to this vector via move construction. + constexpr auto push_back(value_type && value) -> void + { + increase_capacity_if_full(); + std::allocator_traits::construct(m_allocator, &(*this)[m_size], std::move(value)); + ++m_size; + } + + //! Append a given element to this vector via direct construction. + template + constexpr auto emplace_back(Args &&... args) -> reference + { + increase_capacity_if_full(); + std::allocator_traits::construct(m_allocator, &(*this)[m_size], std::forward(args)...); + ++m_size; + return this->back(); + } + + //! Remove the last element of this vector. + //! + //! If this vector is empty, the behavior is undefined. + auto pop_back() -> void + { + std::allocator_traits::destroy(m_allocator, &(*this)[--m_size]); } private: - auto allocate_n(std::size_t count) -> std::allocator_traits::pointer + [[nodiscard]] constexpr auto allocate_n(std::size_t count) -> std::allocator_traits::pointer { if (count) { @@ -667,46 +591,72 @@ namespace kstd return nullptr; } - auto clear_and_deallocate() -> void + constexpr auto clear_and_deallocate() -> void + { + clear(); + deallocate(); + } + + constexpr auto copy_elements(const_iterator from, iterator to, size_type count) -> void + { + for (auto i = 0uz; i < count; ++i) + { + *to++ = std::move(*from++); + } + } + + constexpr auto deallocate() { - std::ranges::for_each(std::views::reverse(*this), [&](auto & element) { - std::allocator_traits::destroy(m_allocator, std::addressof(element)); - }); std::allocator_traits::deallocate(m_allocator, m_data, m_capacity); m_capacity = 0; m_size = 0; m_data = nullptr; } - /** - * @brief Halts the execution of the application if the data container is currently empty. - */ - auto throw_if_empty() const -> void + constexpr auto destroy_n(iterator begin, std::size_t count) -> void + { + std::ranges::for_each(begin, begin + count, [&](auto & element) { + std::allocator_traits::destroy(m_allocator, std::addressof(element)); + }); + } + + auto increase_capacity_if_full() -> void + { + if (m_size == m_capacity) + { + reserve(m_capacity == 0U ? 1U : m_capacity * 2U); + } + } + + constexpr auto move_elements(iterator from, iterator to, size_t count) { - if (empty()) + for (auto i = 0uz; i < count; ++i) { - os::panic("[Vector] Attempted to access element of currently empty vector"); + *to++ = std::move(*from++); } } - auto throw_if_out_of_range(size_type index) const -> void + constexpr auto panic_if_out_of_bounds(size_type index) const -> void { if (index >= m_size) { - os::panic("[Vector] Attempted to read element at invalid index"); + os::panic("[kstd:vector] Attempted to read element at invalid index"); } } - /** - * @brief Increases the internal capacity to 1 if it was previously 0 and to * 2 after that, meaning exponential - * growth. This is done to decrease the amount of single allocations done and because a power of 2 in memory size - * is normally perferable for the cache. - */ - auto increase_capacity_if_full() -> void + constexpr auto uninitialized_copy_with_allocator(const_iterator from, iterator to, difference_type count) { - if (m_size == m_capacity) + for (auto i = 0z; i < count; ++i) { - reserve(m_capacity == 0U ? 1U : m_capacity * 2U); + std::allocator_traits::construct(m_allocator, to++, *from++); + } + } + + constexpr auto uninitialized_move_with_allocator(iterator from, iterator to, difference_type count) + { + for (auto i = 0z; i < count; ++i) + { + std::allocator_traits::construct(m_allocator, to++, std::move(*from++)); } } -- cgit v1.2.3 From 584304d99b6bb43cef8aab9c6bfe22d24a5bf5a0 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 19 Mar 2026 13:31:54 +0100 Subject: kernel/memory: fix sized operator delete --- kernel/src/memory/operators.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/src/memory/operators.cpp b/kernel/src/memory/operators.cpp index c786b53..25b5f24 100644 --- a/kernel/src/memory/operators.cpp +++ b/kernel/src/memory/operators.cpp @@ -76,9 +76,9 @@ auto operator delete(void * pointer, std::align_val_t) noexcept -> void ::operator delete(pointer); } -auto operator delete(void * pointer, std::size_t, std::align_val_t) noexcept -> void +auto operator delete(void * pointer, std::size_t size, std::align_val_t) noexcept -> void { - ::operator delete(pointer); + ::operator delete(pointer, size); } auto operator delete[](void * pointer) noexcept -> void -- cgit v1.2.3 From b038db886fd1e2179f543217f5d3ed7b8d964c4a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 19 Mar 2026 13:54:37 +0100 Subject: kstd: fix vector bugs --- libs/kstd/include/kstd/vector | 53 ++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 9709c2a..75f699b 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -95,8 +95,8 @@ namespace kstd //! @tparam InputIterator An iterator type used to describe the source range. //! @param first The start of the source range. //! @param last The end of the source range. - template - constexpr vector(InputIterator first, InputIterator last, + template + constexpr vector(ForwardIterator first, ForwardIterator last, allocator_type const & allocator = allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) : m_allocator{allocator} @@ -114,7 +114,8 @@ namespace kstd //! //! template - requires(std::ranges::input_range && std::convertible_to, T>) + requires(std::ranges::input_range && std::ranges::sized_range && + std::convertible_to, T>) constexpr vector(kstd::from_range_t, Range && range, allocator_type const & allocator = allocator_type{}) : m_allocator{allocator} , m_size{std::ranges::size(range)} @@ -170,10 +171,26 @@ namespace kstd //! @param allocator The allocator to use in the vector. constexpr vector(vector && other, std::type_identity_t const & allocator) : m_allocator{allocator} - , m_size{std::exchange(other.m_size, size_type{})} - , m_capacity(std::exchange(other.m_capacity, size_type{})) - , m_data(std::exchange(other.m_data, nullptr)) - {} + , m_size{} + , m_capacity{} + , m_data{} + { + if constexpr (!std::allocator_traits::is_always_equal::value) + { + if (m_allocator != other.m_allocator) + { + m_capacity = other.size(); + m_data = allocate_n(capacity()); + m_size = other.size(); + uninitialized_move_with_allocator(other.begin(), begin(), other.size()); + other.clear(); + return; + } + } + m_size = std::exchange(other.m_size, size_type{}); + m_capacity = std::exchange(other.m_capacity, size_type{}); + m_data = std::exchange(other.m_data, nullptr); + } //! Construct a new vector and initialize it's content by copying all elements in the given initializer list. //! @@ -198,7 +215,7 @@ namespace kstd return *this; } - if constexpr (std::allocator_traits::propagate_on_container_move_assignment::value) + if constexpr (std::allocator_traits::propagate_on_container_copy_assignment::value) { if (get_allocator() != other.get_allocator()) { @@ -529,13 +546,12 @@ namespace kstd } auto new_data = allocate_n(m_size); - for (auto & element : *this) - { - std::allocator_traits::construct(m_allocator, new_data++, std::move(element)); - } + auto old_size = size(); + uninitialized_move_with_allocator(begin(), new_data, old_size); clear_and_deallocate(); std::exchange(m_data, new_data); - m_capacity = m_size; + m_capacity = old_size; + m_size = old_size; } //! Clear the contents of this vector. @@ -551,7 +567,7 @@ namespace kstd constexpr auto push_back(value_type const & value) -> void { increase_capacity_if_full(); - std::allocator_traits::construct(m_allocator, &(*this)[m_size], value); + std::allocator_traits::construct(m_allocator, data() + size(), value); ++m_size; } @@ -559,7 +575,7 @@ namespace kstd constexpr auto push_back(value_type && value) -> void { increase_capacity_if_full(); - std::allocator_traits::construct(m_allocator, &(*this)[m_size], std::move(value)); + std::allocator_traits::construct(m_allocator, data() + size(), std::move(value)); ++m_size; } @@ -568,7 +584,7 @@ namespace kstd constexpr auto emplace_back(Args &&... args) -> reference { increase_capacity_if_full(); - std::allocator_traits::construct(m_allocator, &(*this)[m_size], std::forward(args)...); + std::allocator_traits::construct(m_allocator, data() + size(), std::forward(args)...); ++m_size; return this->back(); } @@ -578,7 +594,8 @@ namespace kstd //! If this vector is empty, the behavior is undefined. auto pop_back() -> void { - std::allocator_traits::destroy(m_allocator, &(*this)[--m_size]); + --m_size; + std::allocator_traits::destroy(m_allocator, data() + size()); } private: @@ -601,7 +618,7 @@ namespace kstd { for (auto i = 0uz; i < count; ++i) { - *to++ = std::move(*from++); + *to++ = *from++; } } -- cgit v1.2.3 From 0decd023337694872e2d3a530b4a768049e59f5d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 19 Mar 2026 13:56:03 +0100 Subject: kstd: remove illegal include --- libs/kstd/include/kstd/vector | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 75f699b..8e0377e 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -6,7 +6,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From cffc61472430e3c59630201f8f2698e9ce8c0733 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 19 Mar 2026 14:22:12 +0100 Subject: kstd: apply minor cleanup to vector --- libs/kstd/include/kstd/vector | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 8e0377e..a897b47 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -194,7 +194,7 @@ namespace kstd //! Construct a new vector and initialize it's content by copying all elements in the given initializer list. //! //! @param list The initializer list containing the source objects. - explicit vector(std::initializer_list list, allocator_type const & allocator = allocator_type{}) + vector(std::initializer_list list, allocator_type const & allocator = allocator_type{}) : vector{std::ranges::begin(list), std::ranges::end(list), allocator} {} @@ -239,7 +239,7 @@ namespace kstd } else { - auto new_data = std::allocator_traits::allocate(m_allocator, other.size()); + auto new_data = allocate_n(other.size()); uninitialized_copy_with_allocator(other.begin(), new_data, other.size()); clear_and_deallocate(); m_data = new_data; @@ -297,7 +297,7 @@ namespace kstd } else { - auto new_data = std::allocator_traits::allocate(get_allocator(), other.size()); + auto new_data = allocate_n(other.size()); uninitialized_move_with_allocator(other.begin(), new_data, other.size()); clear_and_deallocate(); m_data = new_data; @@ -520,12 +520,9 @@ namespace kstd auto new_data = allocate_n(new_capacity); auto old_size = size(); - for (auto i = 0uz; i < old_size; ++i) - { - std::allocator_traits::construct(m_allocator, new_data + i, std::move((*this)[i])); - } + uninitialized_move_with_allocator(begin(), new_data, size()); clear_and_deallocate(); - std::exchange(m_data, new_data); + m_data = new_data; m_capacity = new_capacity; m_size = old_size; } @@ -660,17 +657,17 @@ namespace kstd } } - constexpr auto uninitialized_copy_with_allocator(const_iterator from, iterator to, difference_type count) + constexpr auto uninitialized_copy_with_allocator(const_iterator from, iterator to, size_type count) { - for (auto i = 0z; i < count; ++i) + for (auto i = 0uz; i < count; ++i) { std::allocator_traits::construct(m_allocator, to++, *from++); } } - constexpr auto uninitialized_move_with_allocator(iterator from, iterator to, difference_type count) + constexpr auto uninitialized_move_with_allocator(iterator from, iterator to, size_type count) { - for (auto i = 0z; i < count; ++i) + for (auto i = 0uz; i < count; ++i) { std::allocator_traits::construct(m_allocator, to++, std::move(*from++)); } -- cgit v1.2.3 From cabcb0a8365e4d9dc8245b618f00d105e757e8d9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 19 Mar 2026 14:42:48 +0100 Subject: kstd: improve vector documentation --- libs/kstd/include/kstd/vector | 103 +++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 27 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index a897b47..de5c059 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -17,27 +17,36 @@ namespace kstd { - /** - * @brief Custom vector implementation mirroring the std::vector to allow for the usage of STL functionality with our - * custom memory management. - * - * @tparam T Element the vector instance should contain. - * @tparam Allocator The allocator to use when allocating new elements. - */ - template> + //! A resizable, contiguous container. + //! + //! @tparam ValueType The type of values contained in this vector. + //! @tparam Allocator The type of allocator used for memory management by this container. + template> struct vector { - using value_type = T; ///< Type of the elements contained in the container. - using allocator_type = Allocator; ///< Type of the allocator used by the container. - using size_type = std::size_t; ///< Type of the size in the container. - using difference_type = std::ptrdiff_t; ///< Type of the difference between two iterators. - using reference = value_type &; ///< Type of reference to the elements. - using const_reference = value_type const &; ///< Type of constant reference to the elements. - using pointer = value_type *; ///< Type of pointer to the elements. - using const_pointer = value_type const *; ///< Type of constant pointer to the elements. - using iterator = pointer; ///< Type of iterator to the elements. - using const_iterator = const_pointer; ///< Type of constant iterator to the elements. + //! The type of the elements contained in this vector. + using value_type = ValueType; + //! The allocator used by this vector for memory management. + using allocator_type = Allocator; + //! The type of all sizes used in and with this vector. + using size_type = std::size_t; + //! The type of the difference between two iterators. + using difference_type = std::ptrdiff_t; + //! The type of references to elements in this vector. + using reference = value_type &; + //! The type of references to constant elements in this vector. + using const_reference = value_type const &; + //! The type of pointers to elements in this vector. + using pointer = value_type *; + //! The type of pointers to constant elements in this vector. + using const_pointer = value_type const *; + //! The type of iterators into this container. + using iterator = pointer; + //! The type of constant iterators into this container. + using const_iterator = const_pointer; + //! The type of reverse iterators into this container. using reverse_iterator = std::reverse_iterator; + //! The type of constant reverse iterators into this container. using const_reverse_iterator = std::reverse_iterator; //! Construct a new, empty vector. @@ -114,7 +123,7 @@ namespace kstd //! template requires(std::ranges::input_range && std::ranges::sized_range && - std::convertible_to, T>) + std::convertible_to, ValueType>) constexpr vector(kstd::from_range_t, Range && range, allocator_type const & allocator = allocator_type{}) : m_allocator{allocator} , m_size{std::ranges::size(range)} @@ -595,6 +604,9 @@ namespace kstd } private: + //! Use the allocator of this vector to allocate enough space for the given number of elements. + //! + //! @param count The number of elements to allocate space for. [[nodiscard]] constexpr auto allocate_n(std::size_t count) -> std::allocator_traits::pointer { if (count) @@ -604,13 +616,19 @@ namespace kstd return nullptr; } + //! Clear this vector and release it's memory. constexpr auto clear_and_deallocate() -> void { clear(); deallocate(); } - constexpr auto copy_elements(const_iterator from, iterator to, size_type count) -> void + //! Copy a number of elements from one storage location to another. + //! + //! @param from The start of the source range. + //! @param to The start of the target range. + //! @param count The number of element to copy. + constexpr auto static copy_elements(const_iterator from, iterator to, size_type count) -> void { for (auto i = 0uz; i < count; ++i) { @@ -618,6 +636,7 @@ namespace kstd } } + //! Release the memory of this vector. constexpr auto deallocate() { std::allocator_traits::deallocate(m_allocator, m_data, m_capacity); @@ -626,14 +645,19 @@ namespace kstd m_data = nullptr; } - constexpr auto destroy_n(iterator begin, std::size_t count) -> void + //! Destroy a number of elements in this vector. + //! + //! @param first The start of the range of the elements to be destroyed. + //! @param count The number of elements to destroy. + constexpr auto destroy_n(iterator first, std::size_t count) -> void { - std::ranges::for_each(begin, begin + count, [&](auto & element) { + std::ranges::for_each(first, first + count, [&](auto & element) { std::allocator_traits::destroy(m_allocator, std::addressof(element)); }); } - auto increase_capacity_if_full() -> void + //! Check if there is still room in this vector, and if not allocate more space. + constexpr auto increase_capacity_if_full() -> void { if (m_size == m_capacity) { @@ -641,7 +665,12 @@ namespace kstd } } - constexpr auto move_elements(iterator from, iterator to, size_t count) + //! Move a number of elements from one storage location to another. + //! + //! @param from The start of the source range. + //! @param to The start of the target range. + //! @param count The number of element to copy. + constexpr auto static move_elements(iterator from, iterator to, size_t count) { for (auto i = 0uz; i < count; ++i) { @@ -649,6 +678,9 @@ namespace kstd } } + //! Panic the kernel if the given index is out of bounds. + //! + //! @param index The index to check. constexpr auto panic_if_out_of_bounds(size_type index) const -> void { if (index >= m_size) @@ -657,6 +689,11 @@ namespace kstd } } + //! Copy a number of elements from a source range into the uninitialized destination range inside this vector. + //! + //! @param from The start of the source range. + //! @param to The start of the target range inside this vector. + //! @param count The number of elements to copy constexpr auto uninitialized_copy_with_allocator(const_iterator from, iterator to, size_type count) { for (auto i = 0uz; i < count; ++i) @@ -665,6 +702,11 @@ namespace kstd } } + //! Move a number of elements from a source range into the uninitialized destination range inside this vector. + //! + //! @param from The start of the source range. + //! @param to The start of the target range inside this vector. + //! @param count The number of elements to copy constexpr auto uninitialized_move_with_allocator(iterator from, iterator to, size_type count) { for (auto i = 0uz; i < count; ++i) @@ -673,10 +715,17 @@ namespace kstd } } + //! The allocator used by this vector. [[no_unique_address]] allocator_type m_allocator{}; - size_type m_size{}; ///< Amount of elements in the underlying data container - size_type m_capacity{}; ///< Amount of space for elements in the underlying data container - value_type * m_data{}; ///< Pointer to the first element in the underlying data container + + //! The number of elements in this vector. + size_type m_size{}; + + //! The number of elements this vector has room for. + size_type m_capacity{}; + + //! The pointer to the start of the memory managed by this vector. + value_type * m_data{}; }; } // namespace kstd -- cgit v1.2.3 From c2ba7be0e1e84752d21abdcb4ec4f9df444bc367 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 19 Mar 2026 15:08:18 +0100 Subject: kstd: add vector comparison operators --- libs/kstd/include/kstd/vector | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index de5c059..f8e9ce2 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -728,6 +728,21 @@ namespace kstd value_type * m_data{}; }; + //! Check if the content of two vectors is equal. + template + constexpr auto operator==(vector const & lhs, vector const & rhs) -> bool + { + return std::ranges::equal(lhs, rhs); + } + + //! Perform a lexicographical comparison of the content of two vectors. + template + constexpr auto operator<=>(vector const & lhs, vector const & rhs) + -> decltype(std::declval() <=> std::declval()) + { + return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + } + } // namespace kstd #endif -- cgit v1.2.3 From 6c9e50bee8362bd87c66ee10c253d94a78e1459c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 19 Mar 2026 15:08:39 +0100 Subject: kstd/vector: add deduction guides --- libs/kstd/include/kstd/vector | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index f8e9ce2..79530d2 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -743,6 +743,16 @@ namespace kstd return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } + //! Deduction guide for vector construction from an interator pair. + template::value_type>> + vector(ForwardIterator, ForwardIterator, Allocator = Allocator()) + -> vector::value_type, Allocator>; + + //! Deduction guide for vector construction from a range. + template>> + vector(kstd::from_range_t, Range &&, Allocator = Allocator()) -> vector, Allocator>; + } // namespace kstd #endif -- cgit v1.2.3 From 63395fecd439989734f80e6420e4961557465ff9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 19 Mar 2026 17:05:26 +0100 Subject: kstd/format: enable formatting of bool values --- libs/kstd/include/kstd/bits/formatter.hpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/libs/kstd/include/kstd/bits/formatter.hpp b/libs/kstd/include/kstd/bits/formatter.hpp index eadc0ef..a467edd 100644 --- a/libs/kstd/include/kstd/bits/formatter.hpp +++ b/libs/kstd/include/kstd/bits/formatter.hpp @@ -95,7 +95,7 @@ namespace kstd auto prefix = std::array{'0', '\0'}; auto prefix_length = 0uz; - if (specs.alternative_form && value != 0) + if (specs.alternative_form) { switch (base) { @@ -259,6 +259,29 @@ namespace kstd { }; + template<> + struct formatter + { + bits::format_specs specs{}; + + constexpr auto parse(std::string_view context) -> std::string_view + { + return bits::parse_specs(context, specs); + } + + auto format(bool value, format_context & context) const -> void + { + if (specs.type == 's' || specs.type == '\0') + { + context.push(value ? "true" : "false"); + } + else + { + formatter{specs}.format(static_cast(value), context); + } + } + }; + struct format_arg { using formatting_function = std::string_view(void const *, std::string_view, format_context &); -- cgit v1.2.3 From 1865f7a162a496592e236ffcff171e7e7bc47ee2 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 19 Mar 2026 17:21:21 +0100 Subject: kstd/format: add support for formatting of orderings --- libs/kstd/include/kstd/bits/formatter.hpp | 94 +++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/libs/kstd/include/kstd/bits/formatter.hpp b/libs/kstd/include/kstd/bits/formatter.hpp index a467edd..7c9c31d 100644 --- a/libs/kstd/include/kstd/bits/formatter.hpp +++ b/libs/kstd/include/kstd/bits/formatter.hpp @@ -5,9 +5,11 @@ #include #include +#include #include #include +#include #include #include #include @@ -282,6 +284,98 @@ namespace kstd } }; + template<> + struct formatter + { + bits::format_specs specs{}; + + constexpr auto parse(std::string_view context) -> std::string_view + { + return bits::parse_specs(context, specs); + } + + auto format(std::strong_ordering value, format_context & context) const -> void + { + if (value == std::strong_ordering::equal) + { + return context.push(specs.alternative_form ? "==" : "equal"); + } + else if (value == std::strong_ordering::equivalent) + { + return context.push(specs.alternative_form ? "==" : "equivalent"); + } + else if (value == std::strong_ordering::greater) + { + return context.push(specs.alternative_form ? ">" : "greater"); + } + else if (value == std::strong_ordering::less) + { + return context.push(specs.alternative_form ? "<" : "less"); + } + kstd::os::panic("[kstd:format] Invalid strong ordering value!"); + } + }; + + template<> + struct formatter + { + bits::format_specs specs{}; + + constexpr auto parse(std::string_view context) -> std::string_view + { + return bits::parse_specs(context, specs); + } + + auto format(std::weak_ordering value, format_context & context) const -> void + { + if (value == std::weak_ordering::equivalent) + { + return context.push(specs.alternative_form ? "==" : "equivalent"); + } + else if (value == std::weak_ordering::greater) + { + return context.push(specs.alternative_form ? ">" : "greater"); + } + else if (value == std::weak_ordering::less) + { + return context.push(specs.alternative_form ? "<" : "less"); + } + kstd::os::panic("[kstd:format] Invalid weak ordering value!"); + } + }; + + template<> + struct formatter + { + bits::format_specs specs{}; + + constexpr auto parse(std::string_view context) -> std::string_view + { + return bits::parse_specs(context, specs); + } + + auto format(std::partial_ordering value, format_context & context) const -> void + { + if (value == std::partial_ordering::equivalent) + { + return context.push(specs.alternative_form ? "==" : "equivalent"); + } + else if (value == std::partial_ordering::greater) + { + return context.push(specs.alternative_form ? ">" : "greater"); + } + else if (value == std::partial_ordering::less) + { + return context.push(specs.alternative_form ? "<" : "less"); + } + else if (value == std::partial_ordering::unordered) + { + return context.push(specs.alternative_form ? "<=>" : "unordered"); + } + kstd::os::panic("[kstd:format] Invalid partial ordering value!"); + } + }; + struct format_arg { using formatting_function = std::string_view(void const *, std::string_view, format_context &); -- cgit v1.2.3 From 4f942e014dab44ccb8850c5921b81d4bd777d831 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 11:19:05 +0100 Subject: kstd: rework formatting to be closer to std --- kapi/include/kapi/memory/address.hpp | 8 +- kernel/kstd/print.cpp | 137 +++++---- libs/kstd/include/kstd/bits/format_args.hpp | 62 +++++ libs/kstd/include/kstd/bits/format_context.hpp | 89 ++++++ libs/kstd/include/kstd/bits/format_specifiers.hpp | 204 ++++++++++++++ libs/kstd/include/kstd/bits/format_specs.hpp | 104 ------- libs/kstd/include/kstd/bits/format_string.hpp | 151 +++++----- libs/kstd/include/kstd/bits/formatter.hpp | 325 +++++++++++----------- libs/kstd/include/kstd/bits/print_sink.hpp | 2 - libs/kstd/include/kstd/os/print.hpp | 2 +- libs/kstd/include/kstd/print | 20 +- 11 files changed, 692 insertions(+), 412 deletions(-) create mode 100644 libs/kstd/include/kstd/bits/format_args.hpp create mode 100644 libs/kstd/include/kstd/bits/format_specifiers.hpp delete mode 100644 libs/kstd/include/kstd/bits/format_specs.hpp diff --git a/kapi/include/kapi/memory/address.hpp b/kapi/include/kapi/memory/address.hpp index 587aeac..13bdf4c 100644 --- a/kapi/include/kapi/memory/address.hpp +++ b/kapi/include/kapi/memory/address.hpp @@ -229,13 +229,13 @@ namespace kstd { constexpr auto static suffix = Type == kapi::memory::address_type::linear ? "%lin" : "%phy"; - constexpr auto parse(std::string_view context) -> std::string_view + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator { auto result = formatter::parse(context); - if (!this->specs.type) + if (!this->specifiers.type) { - this->specs.type = 'p'; - this->specs.alternative_form = true; + this->specifiers.type = 'p'; + this->specifiers.alternative_form = true; } return result; } diff --git a/kernel/kstd/print.cpp b/kernel/kstd/print.cpp index c7d26ba..44f80a7 100644 --- a/kernel/kstd/print.cpp +++ b/kernel/kstd/print.cpp @@ -70,74 +70,113 @@ namespace kstd::os auto writer = write_buffer{(sink == print_sink::stderr) ? kapi::cio::output_stream::stderr : kapi::cio::output_stream::stdout}; auto context = kstd::format_context{.writer = write_buffer::callback, .user_data = &writer}; + auto parse_context = kstd::format_parse_context{format, args.size()}; - auto current = format.begin(); - auto end = format.end(); - auto next_automatic_index = 0uz; + auto it = parse_context.begin(); + auto end = parse_context.end(); - while (current != end) + while (it != end) { - if (*current != '{') + if (*it != '{' && *it != '}') { - auto start = current; - while (current != end && *current != '{') + auto start = it; + while (it != end && *it != '{' && *it != '}') { - std::advance(current, 1); + std::advance(it, 1); } - context.push(std::string_view(start, current - start)); + parse_context.advance_to(it); + context.push(std::string_view(start, it - start)); continue; } - if (std::next(current) != end && *(std::next(current)) == '{') + if (*it == '{') { - context.push('{'); - std::advance(current, 2); - continue; - } + std::advance(it, 1); + if (it != end && *it == '{') + { + context.push('{'); + std::advance(it, 1); + parse_context.advance_to(it); + continue; + } - std::advance(current, 1); + parse_context.advance_to(it); + auto index = 0uz; - auto index = 0uz; - if (current != end && *current >= '0' && *current <= '9') - { - while (current != end && *current >= '0' && *current <= '9') + if (it != end && *it >= '0' && *it <= '9') { - index = index * 10 + static_cast(*current - '0'); - std::advance(current, 1); + while (it != end && *it >= '0' && *it <= '9') + { + index = index * 10 + static_cast(*it - '0'); + std::advance(it, 1); + } + parse_context.check_arg_id(index); + } + else + { + index = parse_context.next_arg_id(); } - } - else - { - index = next_automatic_index++; - } - auto remaining_fmt = std::string_view{current, static_cast(std::distance(current, end))}; + if (it != end && *it == ':') + { + std::advance(it, 1); + } - auto const arg = args.get(index); - if (arg.format) - { - auto const after_specs = arg.format(arg.value, remaining_fmt, context); - auto const consumed = remaining_fmt.size() - after_specs.size(); - std::advance(current, consumed); - } - else - { - context.push("{?}"); - while (current != end && *current != '}') - std::advance(current, 1); - } + parse_context.advance_to(it); - if (current != end && *current == '}') - { - std::advance(current, 1); + if (index < args.size()) + { + auto const & arg = args[index]; + if (arg.format_function) + { + arg.format_function(arg.value_pointer, parse_context, context); + } + else + { + context.push("{?}"); + } + } + else + { + context.push("{fmt-err: bound}"); + } + + it = parse_context.begin(); + + if (it != end && *it == '}') + { + std::advance(it, 1); + parse_context.advance_to(it); + } + else + { + context.push("{fmt-err: unconsumed}"); + while (it != end && *it != '}') + { + std::advance(it, 1); + } + + if (it != end) + { + std::advance(it, 1); + parse_context.advance_to(it); + } + } } - else + else if (*it == '}') { - context.push("{fmt-err}"); - while (current != end && *current != '}') - std::advance(current, 1); - if (current != end) - std::advance(current, 1); + std::advance(it, 1); + if (it != end && *it == '}') + { + context.push('}'); + std::advance(it, 1); + parse_context.advance_to(it); + } + else + { + context.push("{fmt-err: unescaped}"); + parse_context.advance_to(it); + } } } } diff --git a/libs/kstd/include/kstd/bits/format_args.hpp b/libs/kstd/include/kstd/bits/format_args.hpp new file mode 100644 index 0000000..d236a23 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format_args.hpp @@ -0,0 +1,62 @@ +#ifndef KSTD_BITS_FORMAT_ARGS_HPP +#define KSTD_BITS_FORMAT_ARGS_HPP + +// IWYU pragma: private, include + +#include "kstd/bits/format_context.hpp" + +#include +#include +#include +#include + +namespace kstd +{ + template + struct formatter; + + struct format_arg + { + using format_function_type = auto(void const * value, format_parse_context & parse_context, + format_context & context) -> void; + + void const * value_pointer; + format_function_type * format_function; + }; + + using format_args = std::span; + + template + struct format_arg_store + { + std::array args{}; + }; + + template + auto format_trampoline(void const * value_pointer, format_parse_context & parse_context, format_context & context) + -> void + { + auto typed_value_pointer = static_cast(value_pointer); + auto fmt = formatter>{}; + auto const it = fmt.parse(parse_context); + parse_context.advance_to(it); + fmt.format(*typed_value_pointer, context); + } + + template + [[nodiscard]] auto make_format_args(Arguments const &... args) noexcept -> format_arg_store + { + if constexpr (sizeof...(Arguments) == 0) + { + return format_arg_store<0>{}; + } + else + { + return format_arg_store{std::array{ + format_arg{static_cast(&args), format_trampoline}...}}; + } + } + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format_context.hpp b/libs/kstd/include/kstd/bits/format_context.hpp index b5c7d21..863047d 100644 --- a/libs/kstd/include/kstd/bits/format_context.hpp +++ b/libs/kstd/include/kstd/bits/format_context.hpp @@ -3,11 +3,100 @@ // IWYU pragma: private, include +#include "kstd/os/error.hpp" + +#include #include namespace kstd { + constexpr auto report_format_error(char const * message) -> void + { + if consteval + { + extern void compile_time_format_error_triggered(char const *); + compile_time_format_error_triggered(message); + } + else + { + kstd::os::panic("Error while formatting a string."); + } + } + + struct format_parse_context + { + using iterator = std::string_view::const_iterator; + + constexpr format_parse_context(std::string_view format, std::size_t argument_count) + : m_current(format.begin()) + , m_end(format.end()) + , m_argument_count(argument_count) + {} + + [[nodiscard]] constexpr auto begin() const -> iterator + { + return m_current; + } + + [[nodiscard]] constexpr auto end() const -> iterator + { + return m_end; + } + + constexpr auto advance_to(iterator position) -> void + { + m_current = position; + } + + constexpr auto next_arg_id() -> std::size_t + { + if (m_mode == index_mode::manual) + { + report_format_error("Cannot mix automatic and manual indexing."); + } + + m_mode = index_mode::automatic; + + if (m_next_argument_id >= m_argument_count) + { + report_format_error("Argument index out of bounds."); + } + return m_next_argument_id++; + } + + constexpr auto check_arg_id(std::size_t index) -> void + { + if (m_mode == index_mode::automatic) + { + report_format_error("Cannot mix automatic and manual indexing."); + } + + m_mode = index_mode::manual; + + if (index >= m_argument_count) + { + report_format_error("Argument index out of bounds."); + } + } + + private: + enum class index_mode + { + unknown, + automatic, + manual, + }; + + iterator m_current{}; + iterator m_end{}; + index_mode m_mode{}; + std::size_t m_next_argument_id{}; + std::size_t m_argument_count{}; + + public: + }; + struct format_context { using writer_function = void(void *, std::string_view); diff --git a/libs/kstd/include/kstd/bits/format_specifiers.hpp b/libs/kstd/include/kstd/bits/format_specifiers.hpp new file mode 100644 index 0000000..8fb50ef --- /dev/null +++ b/libs/kstd/include/kstd/bits/format_specifiers.hpp @@ -0,0 +1,204 @@ +#ifndef KSTD_BITS_FORMAT_SPECS_HPP +#define KSTD_BITS_FORMAT_SPECS_HPP + +// IWYU pragma: private + +#include "kstd/bits/format_context.hpp" + +#include +#include +#include + +namespace kstd::bits +{ + enum struct alignment : std::uint8_t + { + none, + left, + right, + center, + }; + + enum struct sign_mode : std::uint8_t + { + none, + plus, + minus, + space, + }; + + enum struct width_mode : std::uint8_t + { + none, + static_value, + dynamic_argument_id + }; + + struct format_specifiers + { + char fill{' '}; + alignment align{}; + sign_mode sign{}; + bool alternative_form{}; + bool zero_pad{}; + + width_mode width_mode{}; + std::size_t width_value{}; + char type{}; + }; + + struct format_padding + { + std::size_t left{}; + std::size_t right{}; + }; + + constexpr auto parse_format_specifiers(format_parse_context & context) -> format_specifiers + { + auto specifiers = format_specifiers{}; + auto it = context.begin(); + auto const end = context.end(); + + if (it != end && *it == '}') + { + return specifiers; + } + + if (std::next(it) != end && ((*std::next(it)) == '<' || (*std::next(it)) == '>' || (*std::next(it)) == '^')) + { + specifiers.fill = *it; + switch (*std::next(it)) + { + case '<': + specifiers.align = alignment::left; + break; + case '>': + specifiers.align = alignment::right; + break; + case '^': + default: + specifiers.align = alignment::center; + break; + } + std::advance(it, 2); + } + else if (*it == '<' || *it == '>' || *it == '^') + { + switch (*it) + { + case '<': + specifiers.align = alignment::left; + break; + case '>': + specifiers.align = alignment::right; + break; + case '^': + default: + specifiers.align = alignment::center; + break; + } + std::advance(it, 1); + } + + if (it != end && (*it == '+' || *it == '-' || *it == ' ')) + { + switch (*it) + { + case '+': + specifiers.sign = sign_mode::plus; + break; + case '-': + specifiers.sign = sign_mode::minus; + break; + case ' ': + default: + specifiers.sign = sign_mode::space; + break; + } + std::advance(it, 1); + } + + if (it != end && *it == '#') + { + specifiers.alternative_form = true; + std::advance(it, 1); + } + + if (it != end && *it == '0') + { + specifiers.zero_pad = true; + std::advance(it, 1); + } + + if (it != end && *it == '{') + { + specifiers.width_mode = width_mode::dynamic_argument_id; + std::advance(it, 1); + auto argument_id = 0uz; + + if (it != end && *it >= '0' && *it <= '9') + { + while (it != end && *it >= '0' && *it <= '9') + { + argument_id = argument_id * 10 + static_cast(*it - '0'); + std::advance(it, 1); + } + context.check_arg_id(argument_id); + } + else + { + argument_id = context.next_arg_id(); + } + + if (it == end || *it != '}') + { + report_format_error("Expected '}' for dynamic width."); + } + std::advance(it, 1); + specifiers.width_value = argument_id; + } + else if (it != end && *it >= '0' && *it <= '9') + { + specifiers.width_mode = width_mode::static_value; + while (it != end && *it >= '0' && *it <= '9') + { + specifiers.width_value = specifiers.width_value * 10 + static_cast(*it - '0'); + std::advance(it, 1); + } + } + + context.advance_to(it); + return specifiers; + } + + constexpr auto calculate_format_padding(std::size_t target_width, std::size_t content_length, + alignment requested_alignment, alignment default_alignment) -> format_padding + { + if (target_width <= content_length) + { + return {}; + } + + auto total_padding = target_width - content_length; + auto effective_alignment = (requested_alignment == alignment::none) ? default_alignment : requested_alignment; + + switch (effective_alignment) + { + case alignment::center: + { + auto left = total_padding / 2; + auto right = total_padding - left; + return {left, right}; + } + case alignment::left: + return {0, total_padding}; + case alignment::right: + default: + return {total_padding, 0}; + break; + } + } + +} // namespace kstd::bits + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format_specs.hpp b/libs/kstd/include/kstd/bits/format_specs.hpp deleted file mode 100644 index 092a875..0000000 --- a/libs/kstd/include/kstd/bits/format_specs.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_SPECS_HPP -#define KSTD_BITS_FORMAT_SPECS_HPP - -// IWYU pragma: private - -#include -#include -#include - -namespace kstd::bits -{ - - struct format_specs - { - std::size_t width{}; - char fill{' '}; - char type{}; - bool align_left{}; - bool sign_plus{}; - bool sign_space{}; - bool alternative_form{}; - bool zero_pad{}; - }; - - constexpr auto parse_specs(std::string_view string, format_specs & specs) -> std::string_view - { - auto current = string.begin(); - auto end = string.end(); - - if (current == end || *current != ':') - { - return {current, end}; - } - - std::advance(current, 1); - - if (current != end && std::next(current) != end && (*std::next(current) == '<' || *std::next(current) == '>')) - { - specs.fill = *current; - specs.align_left = *std::next(current) == '<'; - std::advance(current, 2); - } - else if (current != end) - { - if (*current == '<') - { - specs.align_left = true; - std::advance(current, 1); - } - else if (*current == '>') - { - specs.align_left = false; - std::advance(current, 1); - } - } - - if (current != end) - { - if (*current == '+') - { - specs.sign_plus = true; - std::advance(current, 1); - } - else if (*current == ' ') - { - specs.sign_space = true; - std::advance(current, 1); - } - else if (*current == '-') - { - std::advance(current, 1); - } - } - - if (current != end && *current == '#') - { - specs.alternative_form = true; - std::advance(current, 1); - } - - if (current != end && *current == '0') - { - specs.zero_pad = true; - std::advance(current, 1); - } - - while (current != end && *current >= '0' && *current <= '9') - { - specs.width = specs.width * 10 + (*current - '0'); - std::advance(current, 1); - } - - if (current != end && *current != '}') - { - specs.type = *current; - std::advance(current, 1); - } - - return {current, end}; - } - -} // namespace kstd::bits - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format_string.hpp b/libs/kstd/include/kstd/bits/format_string.hpp index 3a15bf0..cbdfb7d 100644 --- a/libs/kstd/include/kstd/bits/format_string.hpp +++ b/libs/kstd/include/kstd/bits/format_string.hpp @@ -3,127 +3,114 @@ // IWYU pragma: private, include -#include +#include "kstd/bits/format_context.hpp" + #include -#include #include +#include namespace kstd { + template + struct formatter; + namespace bits { - auto invalid_format_string(char const *) -> void; - - consteval auto validate_format_string(std::string_view string, std::size_t argument_count) -> void + template + constexpr auto validate_argument(std::size_t target_index, format_parse_context & context) -> void { - auto next_automatic_index = 0uz; - auto placeholder_count = 0uz; - auto has_manual_index = false; - auto has_automatic_index = false; + auto found = false; + auto current_index = 0uz; + + (..., [&] { + if (current_index == target_index && !found) + { + using decay_type = std::remove_cvref_t; + auto fmt = formatter{}; + + auto it = fmt.parse(context); + context.advance_to(it); + found = true; + } + ++current_index; + }()); + + if (!found) + { + report_format_error("Argument index out of bounds."); + } + } + } // namespace bits - auto current = string.begin(); - auto end = string.end(); + template + struct format_string + { + template + consteval format_string(char const (&str)[Size]) noexcept(false) // NOLINT + : str_view{str} + { + auto context = format_parse_context{str_view, sizeof...(Args)}; + auto it = context.begin(); - while (current != end) + while (it != context.end()) { - if (*current == '{') + if (*it == '{') { - if (std::next(current) != end && *std::next(current) == '{') + ++it; + if (it != context.end() && *it == '{') { - std::advance(current, 2); + ++it; + context.advance_to(it); continue; } - std::advance(current, 1); - - auto index = 0uz; - auto is_manual_index = false; + context.advance_to(it); + auto argument_id = 0uz; - if (current != end && *current >= '0' && *current <= '9') + if (it != context.end() && *it >= '0' && *it <= '9') { - is_manual_index = true; - while (current != end && *current >= '0' && *current <= '9') + while (it != context.end() && *it >= '0' && *it <= '9') { - index = index * 10 + (*current - '0'); - std::advance(current, 1); - } - } - - if (is_manual_index) - { - placeholder_count = std::max(placeholder_count, index + 1); - if (has_automatic_index) - { - invalid_format_string("Cannot mix automatic and manual indexing."); - } - has_manual_index = true; - if (index >= argument_count) - { - invalid_format_string("Argument index out of range"); + argument_id = argument_id * 10 + static_cast(*it - '0'); + ++it; } + context.check_arg_id(argument_id); + context.advance_to(it); } else { - if (has_manual_index) - { - invalid_format_string("Cannot mix automatic and manual indexing."); - } - has_automatic_index = true; - ++placeholder_count; - if (next_automatic_index >= argument_count) - { - invalid_format_string("Not enough arguments provided for format string."); - } - index = next_automatic_index++; + argument_id = context.next_arg_id(); } - while (current != end && *current != '}') + if (it != context.end() && *it == ':') { - std::advance(current, 1); + ++it; + context.advance_to(it); } - if (current == end) + bits::validate_argument(argument_id, context); + + it = context.begin(); + if (it == context.end() || *it != '}') { - invalid_format_string("Unexpected end of format string."); + report_format_error("Missing closing '}' in format string."); } - std::advance(current, 1); } - else if (*current == '}') + else if (*it == '}') { - if (std::next(current) != end && *std::next(current) == '}') + ++it; + if (it != context.end() && *it == '}') { - std::advance(current, 2); - continue; + report_format_error("Unescaped '}' in format string."); } - invalid_format_string("Unexpected '}' in format string."); - } - else - { - std::advance(current, 1); } - } - if (argument_count < placeholder_count) - { - invalid_format_string("Not enough arguments provided for format string."); - } - else if (argument_count > placeholder_count) - { - invalid_format_string("Too many arguments provided for format string."); + ++it; + context.advance_to(it); } } - } // namespace bits - - template - struct format_string - { - std::string_view str; - consteval format_string(char const * format) - : str{format} - { - bits::validate_format_string(str, sizeof...(Args)); - } + std::string_view str_view; }; } // namespace kstd diff --git a/libs/kstd/include/kstd/bits/formatter.hpp b/libs/kstd/include/kstd/bits/formatter.hpp index 7c9c31d..e9a45e0 100644 --- a/libs/kstd/include/kstd/bits/formatter.hpp +++ b/libs/kstd/include/kstd/bits/formatter.hpp @@ -3,8 +3,9 @@ // IWYU pragma: private, include -#include -#include +#include "kstd/bits/format_context.hpp" +#include "kstd/bits/format_specifiers.hpp" + #include #include @@ -21,30 +22,98 @@ namespace kstd { - template + template struct formatter; + template<> + struct formatter + { + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + auto it = context.begin(); + + if (it != context.end() && *it == 's') + { + ++it; + } + + if (it != context.end() && *it != '}') + { + report_format_error("Invalid specifier for string_view."); + } + return it; + } + + auto format(std::string_view const & string, format_context & context) const -> void + { + context.push(string); + } + }; + + template<> + struct formatter : formatter + { + auto format(char const * string, format_context & context) const -> void + { + formatter::format(string ? std::string_view{string} : "(null)", context); + } + }; + template struct formatter { - bits::format_specs specs{}; + bits::format_specifiers specifiers{}; + + constexpr auto static maximum_digits = 80; - constexpr auto parse(std::string_view context) -> std::string_view + enum struct base { - return bits::parse_specs(context, specs); + bin = 2, + oct = 8, + dec = 10, + hex = 16, + }; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::parse_format_specifiers(context); + + auto it = context.begin(); + auto const end = context.end(); + + if (it != end && *it != '}') + { + if (*it == 'b' || *it == 'B' || *it == 'd' || *it == 'o' || *it == 'x' || *it == 'X' || *it == 'p') + { + specifiers.type = *it; + std::advance(it, 1); + } + else + { + report_format_error("Invalid type specifier for integral type."); + } + } + + if (it != end && *it != '}') + { + report_format_error("Missing terminating '}' in format string."); + } + + return it; } auto format(T value, format_context & context) const -> void { - enum struct base + auto final_width = 0uz; + if (specifiers.width_mode == bits::width_mode::static_value) { - bin = 2, - oct = 8, - dec = 10, - hex = 16, - }; - - constexpr auto static maximum_digits = 80; + final_width = specifiers.width_value; + } + else if (specifiers.width_mode == bits::width_mode::dynamic_argument_id) + { + // TODO: implement argument references so we can read the dynamic width argument. + final_width = 0; + } using unsigned_T = std::make_unsigned_t; auto absolute_value = static_cast(value); @@ -55,11 +124,11 @@ namespace kstd if (value < 0) { is_negative = true; - absolute_value = 0 - value; + absolute_value = 0 - static_cast(value); } } - auto const base = [type = specs.type] -> auto { + auto const base = [type = specifiers.type] -> auto { switch (type) { case 'x': @@ -77,7 +146,7 @@ namespace kstd }(); auto buffer = std::array{}; - auto digits = (specs.type == 'X') ? "0123456789ABCDEF" : "0123456789abcdef"; + auto digits = (specifiers.type == 'X') ? "0123456789ABCDEF" : "0123456789abcdef"; auto current = buffer.rbegin(); if (absolute_value == 0) @@ -95,21 +164,22 @@ namespace kstd } } + auto content_length = static_cast(std::distance(buffer.rbegin(), current)); auto prefix = std::array{'0', '\0'}; auto prefix_length = 0uz; - if (specs.alternative_form) + if (specifiers.alternative_form) { switch (base) { case base::bin: - prefix[1] = specs.type == 'B' ? 'B' : 'b'; + prefix[1] = specifiers.type == 'B' ? 'B' : 'b'; prefix_length = 2; break; case base::oct: prefix_length = 1; break; case base::hex: - prefix[1] = specs.type == 'X' ? 'X' : 'x'; + prefix[1] = specifiers.type == 'X' ? 'X' : 'x'; prefix_length = 2; break; default: @@ -122,53 +192,50 @@ namespace kstd { sign_character = '-'; } - else if (specs.sign_plus) + else if (specifiers.sign == bits::sign_mode::plus) { sign_character = '+'; } - else if (specs.sign_space) + else if (specifiers.sign == bits::sign_mode::space) { sign_character = ' '; } - auto const content_length = static_cast(std::distance(buffer.rbegin(), current)); auto const total_length = content_length + prefix_length + (sign_character != '\0'); - auto const padding_length = (specs.width > total_length) ? (specs.width - total_length) : 0; + auto const padding = + bits::calculate_format_padding(final_width, total_length, specifiers.align, bits::alignment::right); + auto const effective_zero_pad = specifiers.zero_pad && (specifiers.align == bits::alignment::none); - if (!specs.align_left && !specs.zero_pad) + if (!effective_zero_pad) { - for (auto i = 0uz; i < padding_length; ++i) + for (auto i = 0uz; i < padding.left; ++i) { - context.push(specs.fill); + context.push(specifiers.fill); } } - if (sign_character) + if (sign_character != '\0') { context.push(sign_character); } - - if (prefix_length) + if (prefix_length > 0) { - context.push({prefix.data(), prefix_length}); + context.push(std::string_view{prefix.data(), prefix_length}); } - if (!specs.align_left && specs.zero_pad) + if (effective_zero_pad) { - for (auto i = 0uz; i < padding_length; ++i) + for (auto i = 0uz; i < padding.left; ++i) { context.push('0'); } } - context.push({current.base(), content_length}); + context.push(std::string_view{current.base(), content_length}); - if (specs.align_left) + for (auto i = 0uz; i < padding.right; ++i) { - for (auto i = 0uz; i < padding_length; ++i) - { - context.push(specs.fill); - } + context.push(specifiers.fill); } } }; @@ -176,13 +243,13 @@ namespace kstd template struct formatter : formatter { - constexpr auto parse(std::string_view context) -> std::string_view + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator { auto result = formatter::parse(context); - if (!this->specs.type) + if (!this->specifiers.type) { - this->specs.type = 'p'; - this->specs.alternative_form = true; + this->specifiers.type = 'p'; + this->specifiers.alternative_form = true; } return result; } @@ -199,87 +266,64 @@ namespace kstd }; template<> - struct formatter + struct formatter : formatter { - bits::format_specs specs{}; + }; - constexpr auto parse(std::string_view context) -> std::string_view - { - return bits::parse_specs(context, specs); - } + template<> + struct formatter + { + bits::format_specifiers specifiers{}; - auto format(std::string_view string, format_context & context) const -> void + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator { - auto const content_length = string.size(); - auto const padding_length = (specs.width > content_length) ? (specs.width - content_length) : 0; + specifiers = bits::parse_format_specifiers(context); + + auto it = context.begin(); + auto const end = context.end(); - if (!specs.align_left) + if (it != end && *it != '}') { - for (auto i = 0uz; i < padding_length; ++i) + if (*it == 's') { - context.push(specs.fill); + specifiers.type = *it; + std::advance(it, 1); } - } - - context.push(string); - - if (specs.align_left) - { - for (auto i = 0uz; i < padding_length; ++i) + else { - context.push(specs.fill); + report_format_error("Invalid type specifier for bool."); } } - } - }; - template<> - struct formatter - { - bits::format_specs specs{}; + if (it != end && *it != '}') + { + report_format_error("Missing terminating '}' in format string."); + } - constexpr auto parse(std::string_view context) -> std::string_view - { - return bits::parse_specs(context, specs); + return it; } - auto format(char const * string, format_context & context) const -> void + auto format(bool value, format_context & context) const -> void { - if (string) - { - formatter{specs}.format(string, context); - } - else + auto const text = value ? std::string_view{"true"} : std::string_view{"false"}; + auto final_width = 0uz; + if (specifiers.width_mode == bits::width_mode::static_value) { - formatter{specs}.format("(null)", context); + final_width = specifiers.width_value; } - } - }; - template<> - struct formatter : formatter - { - }; + auto padding = bits::calculate_format_padding(final_width, text.size(), specifiers.align, bits::alignment::left); - template<> - struct formatter - { - bits::format_specs specs{}; - - constexpr auto parse(std::string_view context) -> std::string_view - { - return bits::parse_specs(context, specs); - } - - auto format(bool value, format_context & context) const -> void - { - if (specs.type == 's' || specs.type == '\0') + for (auto i = 0uz; i < padding.left; ++i) { - context.push(value ? "true" : "false"); + context.push(specifiers.fill); } - else + + context.push(text); + + for (auto i = 0uz; i < padding.right; ++i) { - formatter{specs}.format(static_cast(value), context); + context.push(specifiers.fill); } } }; @@ -287,30 +331,31 @@ namespace kstd template<> struct formatter { - bits::format_specs specs{}; + bits::format_specifiers specifiers{}; - constexpr auto parse(std::string_view context) -> std::string_view + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator { - return bits::parse_specs(context, specs); + specifiers = bits::parse_format_specifiers(context); + return context.begin(); } auto format(std::strong_ordering value, format_context & context) const -> void { if (value == std::strong_ordering::equal) { - return context.push(specs.alternative_form ? "==" : "equal"); + return context.push(specifiers.alternative_form ? "==" : "equal"); } else if (value == std::strong_ordering::equivalent) { - return context.push(specs.alternative_form ? "==" : "equivalent"); + return context.push(specifiers.alternative_form ? "==" : "equivalent"); } else if (value == std::strong_ordering::greater) { - return context.push(specs.alternative_form ? ">" : "greater"); + return context.push(specifiers.alternative_form ? ">" : "greater"); } else if (value == std::strong_ordering::less) { - return context.push(specs.alternative_form ? "<" : "less"); + return context.push(specifiers.alternative_form ? "<" : "less"); } kstd::os::panic("[kstd:format] Invalid strong ordering value!"); } @@ -319,26 +364,27 @@ namespace kstd template<> struct formatter { - bits::format_specs specs{}; + bits::format_specifiers specifiers{}; - constexpr auto parse(std::string_view context) -> std::string_view + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator { - return bits::parse_specs(context, specs); + specifiers = bits::parse_format_specifiers(context); + return context.begin(); } auto format(std::weak_ordering value, format_context & context) const -> void { if (value == std::weak_ordering::equivalent) { - return context.push(specs.alternative_form ? "==" : "equivalent"); + return context.push(specifiers.alternative_form ? "==" : "equivalent"); } else if (value == std::weak_ordering::greater) { - return context.push(specs.alternative_form ? ">" : "greater"); + return context.push(specifiers.alternative_form ? ">" : "greater"); } else if (value == std::weak_ordering::less) { - return context.push(specs.alternative_form ? "<" : "less"); + return context.push(specifiers.alternative_form ? "<" : "less"); } kstd::os::panic("[kstd:format] Invalid weak ordering value!"); } @@ -347,71 +393,36 @@ namespace kstd template<> struct formatter { - bits::format_specs specs{}; + bits::format_specifiers specifiers{}; - constexpr auto parse(std::string_view context) -> std::string_view + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator { - return bits::parse_specs(context, specs); + specifiers = bits::parse_format_specifiers(context); + return context.begin(); } auto format(std::partial_ordering value, format_context & context) const -> void { if (value == std::partial_ordering::equivalent) { - return context.push(specs.alternative_form ? "==" : "equivalent"); + return context.push(specifiers.alternative_form ? "==" : "equivalent"); } else if (value == std::partial_ordering::greater) { - return context.push(specs.alternative_form ? ">" : "greater"); + return context.push(specifiers.alternative_form ? ">" : "greater"); } else if (value == std::partial_ordering::less) { - return context.push(specs.alternative_form ? "<" : "less"); + return context.push(specifiers.alternative_form ? "<" : "less"); } else if (value == std::partial_ordering::unordered) { - return context.push(specs.alternative_form ? "<=>" : "unordered"); + return context.push(specifiers.alternative_form ? "<=>" : "unordered"); } kstd::os::panic("[kstd:format] Invalid partial ordering value!"); } }; - struct format_arg - { - using formatting_function = std::string_view(void const *, std::string_view, format_context &); - - void const * value; - formatting_function * format; - }; - - struct format_args - { - constexpr format_args(format_arg const * args, std::size_t number_of_args) - : m_args(args) - , m_number_of_args(number_of_args) - {} - - [[nodiscard]] constexpr auto get(std::size_t index) const -> format_arg - { - if (index >= m_number_of_args) - return {.value = nullptr, .format = nullptr}; - return m_args[index]; - } - - private: - format_arg const * m_args; - std::size_t m_number_of_args; - }; - - template - auto format_dispatcher(void const * value, std::string_view format_spec, format_context & context) -> std::string_view - { - auto formatter_for_T = formatter{}; - auto const remainder = formatter_for_T.parse(format_spec); - formatter_for_T.format(*static_cast(value), context); - return remainder; - } - } // namespace kstd #endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/print_sink.hpp b/libs/kstd/include/kstd/bits/print_sink.hpp index 0e0955c..af765e0 100644 --- a/libs/kstd/include/kstd/bits/print_sink.hpp +++ b/libs/kstd/include/kstd/bits/print_sink.hpp @@ -3,8 +3,6 @@ // IWYU pragma: private, include -#include - namespace kstd { diff --git a/libs/kstd/include/kstd/os/print.hpp b/libs/kstd/include/kstd/os/print.hpp index f189042..0dde6e7 100644 --- a/libs/kstd/include/kstd/os/print.hpp +++ b/libs/kstd/include/kstd/os/print.hpp @@ -1,7 +1,7 @@ #ifndef KSTD_OS_PRINT_HPP #define KSTD_OS_PRINT_HPP -#include "kstd/bits/formatter.hpp" +#include "kstd/bits/format_args.hpp" #include "kstd/bits/print_sink.hpp" #include diff --git a/libs/kstd/include/kstd/print b/libs/kstd/include/kstd/print index ffafda9..1ab24bd 100644 --- a/libs/kstd/include/kstd/print +++ b/libs/kstd/include/kstd/print @@ -1,12 +1,12 @@ #ifndef KSTD_PRINT #define KSTD_PRINT +#include "bits/format_args.hpp" #include "bits/print_sink.hpp" // IWYU pragma: export #include "os/print.hpp" #include -#include #include namespace kstd @@ -19,13 +19,10 @@ namespace kstd //! @param args The arguments to use to place in the format string's placeholders. template // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) - auto print(kstd::format_string...> format, Args &&... args) -> void + auto print(kstd::format_string...> format, Args const &... args) -> void { - auto arguments = std::array{ - kstd::format_arg{&args, kstd::format_dispatcher>} - ... - }; - os::vprint(print_sink::stdout, format.str, kstd::format_args{arguments.data(), sizeof...(Args)}); + auto const arg_store = kstd::make_format_args(args...); + os::vprint(print_sink::stdout, format.str_view, arg_store.args); } //! @qualifier kernel-defined @@ -37,11 +34,8 @@ namespace kstd // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) auto print(print_sink sink, kstd::format_string...> format, Args &&... args) -> void { - auto arguments = std::array{ - kstd::format_arg{&args, kstd::format_dispatcher>} - ... - }; - os::vprint(sink, format.str, kstd::format_args{arguments.data(), sizeof...(Args)}); + auto const arg_store = kstd::make_format_args(args...); + os::vprint(sink, format.str_view, arg_store.args); } //! @qualifier kernel-defined @@ -54,7 +48,7 @@ namespace kstd // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) auto println(kstd::format_string...> format, Args &&... args) -> void { - print(format, std::forward(args)...); + print(print_sink::stdout, format, std::forward(args)...); print(print_sink::stdout, "\n"); } -- cgit v1.2.3 From d7147ddd7416a6cce874d290d1615527f4d7cdb9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 12:01:22 +0100 Subject: kstd/format: implement dynamic width support --- kernel/kstd/print.cpp | 2 +- libs/kstd/include/kstd/bits/format_args.hpp | 32 +++--- libs/kstd/include/kstd/bits/format_context.hpp | 114 ++++++------------- .../include/kstd/bits/format_parse_context.hpp | 122 +++++++++++++++++++++ libs/kstd/include/kstd/bits/format_specifiers.hpp | 5 +- libs/kstd/include/kstd/bits/format_string.hpp | 30 ++++- libs/kstd/include/kstd/bits/formatter.hpp | 11 +- 7 files changed, 217 insertions(+), 99 deletions(-) create mode 100644 libs/kstd/include/kstd/bits/format_parse_context.hpp diff --git a/kernel/kstd/print.cpp b/kernel/kstd/print.cpp index 44f80a7..db03816 100644 --- a/kernel/kstd/print.cpp +++ b/kernel/kstd/print.cpp @@ -69,7 +69,7 @@ namespace kstd::os { auto writer = write_buffer{(sink == print_sink::stderr) ? kapi::cio::output_stream::stderr : kapi::cio::output_stream::stdout}; - auto context = kstd::format_context{.writer = write_buffer::callback, .user_data = &writer}; + auto context = kstd::format_context{.writer = write_buffer::callback, .user_data = &writer, .args = args}; auto parse_context = kstd::format_parse_context{format, args.size()}; auto it = parse_context.begin(); diff --git a/libs/kstd/include/kstd/bits/format_args.hpp b/libs/kstd/include/kstd/bits/format_args.hpp index d236a23..71ee5ae 100644 --- a/libs/kstd/include/kstd/bits/format_args.hpp +++ b/libs/kstd/include/kstd/bits/format_args.hpp @@ -4,28 +4,20 @@ // IWYU pragma: private, include #include "kstd/bits/format_context.hpp" +#include "kstd/bits/format_parse_context.hpp" #include #include -#include #include namespace kstd { + struct format_context; + struct format_parse_context; + template struct formatter; - struct format_arg - { - using format_function_type = auto(void const * value, format_parse_context & parse_context, - format_context & context) -> void; - - void const * value_pointer; - format_function_type * format_function; - }; - - using format_args = std::span; - template struct format_arg_store { @@ -43,6 +35,20 @@ namespace kstd fmt.format(*typed_value_pointer, context); } + template + auto get_size_trampoline(void const * value_pointer) -> std::size_t + { + if constexpr (bits::is_format_width_compatible_v) + { + return static_cast(*static_cast(value_pointer)); + } + else + { + report_format_error("Dynamic width argument is not an integral value."); + return 0; + } + } + template [[nodiscard]] auto make_format_args(Arguments const &... args) noexcept -> format_arg_store { @@ -53,7 +59,7 @@ namespace kstd else { return format_arg_store{std::array{ - format_arg{static_cast(&args), format_trampoline}...}}; + format_arg{static_cast(&args), format_trampoline, get_size_trampoline}...}}; } } diff --git a/libs/kstd/include/kstd/bits/format_context.hpp b/libs/kstd/include/kstd/bits/format_context.hpp index 863047d..05e37ca 100644 --- a/libs/kstd/include/kstd/bits/format_context.hpp +++ b/libs/kstd/include/kstd/bits/format_context.hpp @@ -3,106 +3,60 @@ // IWYU pragma: private, include -#include "kstd/os/error.hpp" +#include +#include #include +#include #include namespace kstd { - constexpr auto report_format_error(char const * message) -> void + namespace bits { - if consteval - { - extern void compile_time_format_error_triggered(char const *); - compile_time_format_error_triggered(message); - } - else - { - kstd::os::panic("Error while formatting a string."); - } + template + constexpr auto inline is_format_width_compatible_v = std::integral && // + !std::same_as && // + !std::same_as && // + !std::same_as && // + !std::same_as && // + !std::same_as && // + !std::same_as; } - struct format_parse_context - { - using iterator = std::string_view::const_iterator; - - constexpr format_parse_context(std::string_view format, std::size_t argument_count) - : m_current(format.begin()) - , m_end(format.end()) - , m_argument_count(argument_count) - {} - - [[nodiscard]] constexpr auto begin() const -> iterator - { - return m_current; - } - - [[nodiscard]] constexpr auto end() const -> iterator - { - return m_end; - } - - constexpr auto advance_to(iterator position) -> void - { - m_current = position; - } - - constexpr auto next_arg_id() -> std::size_t - { - if (m_mode == index_mode::manual) - { - report_format_error("Cannot mix automatic and manual indexing."); - } - - m_mode = index_mode::automatic; + struct format_parse_context; + struct format_context; - if (m_next_argument_id >= m_argument_count) - { - report_format_error("Argument index out of bounds."); - } - return m_next_argument_id++; - } - - constexpr auto check_arg_id(std::size_t index) -> void - { - if (m_mode == index_mode::automatic) - { - report_format_error("Cannot mix automatic and manual indexing."); - } - - m_mode = index_mode::manual; - - if (index >= m_argument_count) - { - report_format_error("Argument index out of bounds."); - } - } + struct format_arg + { + using format_function_type = auto(void const * value, format_parse_context & parse_context, + format_context & context) -> void; + using get_size_function_type = auto(void const * value) -> std::size_t; - private: - enum class index_mode - { - unknown, - automatic, - manual, - }; - - iterator m_current{}; - iterator m_end{}; - index_mode m_mode{}; - std::size_t m_next_argument_id{}; - std::size_t m_argument_count{}; - - public: + void const * value_pointer; + format_function_type * format_function; + get_size_function_type * get_size_function; }; + using format_args = std::span; + struct format_context { using writer_function = void(void *, std::string_view); writer_function * writer; void * user_data; + format_args args; + + [[nodiscard]] auto arg(std::size_t const id) const -> format_arg const & + { + if (id >= args.size()) + { + kstd::os::panic("[kstd:format] argument index out of range!"); + } + return args[id]; + } constexpr auto push(std::string_view string) -> void { diff --git a/libs/kstd/include/kstd/bits/format_parse_context.hpp b/libs/kstd/include/kstd/bits/format_parse_context.hpp new file mode 100644 index 0000000..76c3f03 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format_parse_context.hpp @@ -0,0 +1,122 @@ +#ifndef KSTD_BITS_FORMAT_PARSE_CONTEXT_HPP +#define KSTD_BITS_FORMAT_PARSE_CONTEXT_HPP + +// IWYU pragma: private, include + +#include + +#include +#include + +namespace kstd +{ + + constexpr auto report_format_error(char const * message) -> void + { + if consteval + { + extern void compile_time_format_error_triggered(char const *); + compile_time_format_error_triggered(message); + } + else + { + kstd::os::panic("Error while formatting a string."); + } + } + + struct format_parse_context + { + using iterator = std::string_view::const_iterator; + + constexpr format_parse_context(std::string_view format, std::size_t argument_count, + bool const * is_integral = nullptr) + : m_current{format.begin()} + , m_end{format.end()} + , m_argument_count{argument_count} + , m_is_integral{is_integral} + {} + + [[nodiscard]] constexpr auto begin() const -> iterator + { + return m_current; + } + + [[nodiscard]] constexpr auto end() const -> iterator + { + return m_end; + } + + constexpr auto advance_to(iterator position) -> void + { + m_current = position; + } + + constexpr auto next_arg_id() -> std::size_t + { + if (m_mode == index_mode::manual) + { + report_format_error("Cannot mix automatic and manual indexing."); + } + + m_mode = index_mode::automatic; + + if (m_next_argument_id >= m_argument_count) + { + report_format_error("Argument index out of bounds."); + } + return m_next_argument_id++; + } + + constexpr auto check_arg_id(std::size_t index) -> void + { + if (m_mode == index_mode::automatic) + { + report_format_error("Cannot mix automatic and manual indexing."); + } + + m_mode = index_mode::manual; + + if (index >= m_argument_count) + { + report_format_error("Argument index out of bounds."); + } + } + + constexpr auto check_dynamic_width_id(std::size_t id) -> void + { + check_arg_id(id); + if (m_is_integral && !m_is_integral[id]) + { + report_format_error("Dynamic width argument must be an integral object."); + } + } + + constexpr auto next_dynamic_width_id() -> std::size_t + { + auto const id = next_arg_id(); + if (m_is_integral && !m_is_integral[id]) + { + report_format_error("Dynamic width argument must be an integral object."); + } + return id; + } + + private: + enum class index_mode + { + unknown, + automatic, + manual, + }; + + iterator m_current{}; + iterator m_end{}; + index_mode m_mode{}; + std::size_t m_next_argument_id{}; + std::size_t m_argument_count{}; + bool const * m_is_integral{}; + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format_specifiers.hpp b/libs/kstd/include/kstd/bits/format_specifiers.hpp index 8fb50ef..00cca40 100644 --- a/libs/kstd/include/kstd/bits/format_specifiers.hpp +++ b/libs/kstd/include/kstd/bits/format_specifiers.hpp @@ -4,6 +4,7 @@ // IWYU pragma: private #include "kstd/bits/format_context.hpp" +#include "kstd/bits/format_parse_context.hpp" #include #include @@ -143,11 +144,11 @@ namespace kstd::bits argument_id = argument_id * 10 + static_cast(*it - '0'); std::advance(it, 1); } - context.check_arg_id(argument_id); + context.check_dynamic_width_id(argument_id); } else { - argument_id = context.next_arg_id(); + argument_id = context.next_dynamic_width_id(); } if (it == end || *it != '}') diff --git a/libs/kstd/include/kstd/bits/format_string.hpp b/libs/kstd/include/kstd/bits/format_string.hpp index cbdfb7d..f16f1ee 100644 --- a/libs/kstd/include/kstd/bits/format_string.hpp +++ b/libs/kstd/include/kstd/bits/format_string.hpp @@ -4,7 +4,9 @@ // IWYU pragma: private, include #include "kstd/bits/format_context.hpp" +#include "kstd/bits/format_parse_context.hpp" +#include #include #include #include @@ -41,6 +43,30 @@ namespace kstd report_format_error("Argument index out of bounds."); } } + + template + constexpr auto validate_dynamic_width(std::size_t target_index) -> void + { + auto is_valid_integer = false; + auto current_index = 0uz; + + (..., [&] { + if (current_index == target_index) + { + using decay_type = std::remove_cvref_t; + if constexpr (std::is_integral_v) + { + is_valid_integer = true; + } + } + ++current_index; + }()); + + if (!is_valid_integer) + { + report_format_error("Dynamic width argument must be an integral object."); + } + } } // namespace bits template @@ -50,7 +76,9 @@ namespace kstd consteval format_string(char const (&str)[Size]) noexcept(false) // NOLINT : str_view{str} { - auto context = format_parse_context{str_view, sizeof...(Args)}; + auto const is_width_compatible = std::array 0 ? sizeof...(Args) : 1)>{ + bits::is_format_width_compatible_v>...}; + auto context = format_parse_context{str_view, sizeof...(Args), is_width_compatible.data()}; auto it = context.begin(); while (it != context.end()) diff --git a/libs/kstd/include/kstd/bits/formatter.hpp b/libs/kstd/include/kstd/bits/formatter.hpp index e9a45e0..e46f6ad 100644 --- a/libs/kstd/include/kstd/bits/formatter.hpp +++ b/libs/kstd/include/kstd/bits/formatter.hpp @@ -4,6 +4,7 @@ // IWYU pragma: private, include #include "kstd/bits/format_context.hpp" +#include "kstd/bits/format_parse_context.hpp" #include "kstd/bits/format_specifiers.hpp" #include @@ -111,8 +112,8 @@ namespace kstd } else if (specifiers.width_mode == bits::width_mode::dynamic_argument_id) { - // TODO: implement argument references so we can read the dynamic width argument. - final_width = 0; + auto const & arg = context.arg(specifiers.width_value); + final_width = arg.get_size_function(arg.value_pointer); } using unsigned_T = std::make_unsigned_t; @@ -307,10 +308,16 @@ namespace kstd { auto const text = value ? std::string_view{"true"} : std::string_view{"false"}; auto final_width = 0uz; + if (specifiers.width_mode == bits::width_mode::static_value) { final_width = specifiers.width_value; } + else if (specifiers.width_mode == bits::width_mode::dynamic_argument_id) + { + auto const & arg = context.arg(specifiers.width_value); + final_width = arg.get_size_function(arg.value_pointer); + } auto padding = bits::calculate_format_padding(final_width, text.size(), specifiers.align, bits::alignment::left); -- cgit v1.2.3 From 8365a09e18ac043d2e0aa6a9d3e394a02fe7c6cb Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 12:06:17 +0100 Subject: kstd: fix build system errors --- libs/kstd/CMakeLists.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 77b12a9..9b4976b 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -21,10 +21,13 @@ target_sources("kstd" PUBLIC FILE_SET HEADERS BASE_DIRS "include" FILES + "include/kstd/bits/format_args.hpp" "include/kstd/bits/format_context.hpp" - "include/kstd/bits/format_specs.hpp" + "include/kstd/bits/format_parse_context.hpp" + "include/kstd/bits/format_specifiers.hpp" "include/kstd/bits/format_string.hpp" "include/kstd/bits/formatter.hpp" + "include/kstd/bits/print_sink.hpp" "include/kstd/bits/shared_ptr.hpp" "include/kstd/bits/unique_ptr.hpp" -- cgit v1.2.3 From 07cb15c42c16497b0b09b75886ce3baddeaaafb3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 14:03:45 +0100 Subject: kstd/format: split implementation --- libs/kstd/include/kstd/bits/format/arg.hpp | 26 ++ libs/kstd/include/kstd/bits/format/args.hpp | 75 ++++ libs/kstd/include/kstd/bits/format/context.hpp | 61 +++ libs/kstd/include/kstd/bits/format/error.hpp | 24 ++ libs/kstd/include/kstd/bits/format/formatter.hpp | 14 + .../include/kstd/bits/format/formatter/bool.hpp | 83 ++++ .../include/kstd/bits/format/formatter/cstring.hpp | 29 ++ .../kstd/bits/format/formatter/integral.hpp | 204 ++++++++++ .../kstd/bits/format/formatter/ordering.hpp | 111 ++++++ .../include/kstd/bits/format/formatter/pointer.hpp | 42 ++ .../kstd/bits/format/formatter/string_view.hpp | 41 ++ libs/kstd/include/kstd/bits/format/fwd.hpp | 23 ++ .../include/kstd/bits/format/parse_context.hpp | 109 ++++++ libs/kstd/include/kstd/bits/format/specifiers.hpp | 205 ++++++++++ libs/kstd/include/kstd/bits/format/string.hpp | 146 +++++++ libs/kstd/include/kstd/bits/format_args.hpp | 68 ---- libs/kstd/include/kstd/bits/format_context.hpp | 74 ---- .../include/kstd/bits/format_parse_context.hpp | 122 ------ libs/kstd/include/kstd/bits/format_specifiers.hpp | 205 ---------- libs/kstd/include/kstd/bits/format_string.hpp | 146 ------- libs/kstd/include/kstd/bits/formatter.hpp | 435 --------------------- libs/kstd/include/kstd/format | 15 +- libs/kstd/include/kstd/os/print.hpp | 2 +- libs/kstd/include/kstd/print | 1 - 24 files changed, 1206 insertions(+), 1055 deletions(-) create mode 100644 libs/kstd/include/kstd/bits/format/arg.hpp create mode 100644 libs/kstd/include/kstd/bits/format/args.hpp create mode 100644 libs/kstd/include/kstd/bits/format/context.hpp create mode 100644 libs/kstd/include/kstd/bits/format/error.hpp create mode 100644 libs/kstd/include/kstd/bits/format/formatter.hpp create mode 100644 libs/kstd/include/kstd/bits/format/formatter/bool.hpp create mode 100644 libs/kstd/include/kstd/bits/format/formatter/cstring.hpp create mode 100644 libs/kstd/include/kstd/bits/format/formatter/integral.hpp create mode 100644 libs/kstd/include/kstd/bits/format/formatter/ordering.hpp create mode 100644 libs/kstd/include/kstd/bits/format/formatter/pointer.hpp create mode 100644 libs/kstd/include/kstd/bits/format/formatter/string_view.hpp create mode 100644 libs/kstd/include/kstd/bits/format/fwd.hpp create mode 100644 libs/kstd/include/kstd/bits/format/parse_context.hpp create mode 100644 libs/kstd/include/kstd/bits/format/specifiers.hpp create mode 100644 libs/kstd/include/kstd/bits/format/string.hpp delete mode 100644 libs/kstd/include/kstd/bits/format_args.hpp delete mode 100644 libs/kstd/include/kstd/bits/format_context.hpp delete mode 100644 libs/kstd/include/kstd/bits/format_parse_context.hpp delete mode 100644 libs/kstd/include/kstd/bits/format_specifiers.hpp delete mode 100644 libs/kstd/include/kstd/bits/format_string.hpp delete mode 100644 libs/kstd/include/kstd/bits/formatter.hpp diff --git a/libs/kstd/include/kstd/bits/format/arg.hpp b/libs/kstd/include/kstd/bits/format/arg.hpp new file mode 100644 index 0000000..92e6431 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/arg.hpp @@ -0,0 +1,26 @@ +#ifndef KSTD_BITS_FORMAT_ARG_HPP +#define KSTD_BITS_FORMAT_ARG_HPP + +// IWYU pragma: private, include + +#include "fwd.hpp" + +#include + +namespace kstd +{ + + struct format_arg + { + using format_function_type = auto(void const * value, format_parse_context & parse_context, + format_context & context) -> void; + using get_size_function_type = auto(void const * value) -> std::size_t; + + void const * value_pointer; + format_function_type * format_function; + get_size_function_type * get_size_function; + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/args.hpp b/libs/kstd/include/kstd/bits/format/args.hpp new file mode 100644 index 0000000..5cca3ff --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/args.hpp @@ -0,0 +1,75 @@ +#ifndef KSTD_BITS_FORMAT_ARGS_HPP +#define KSTD_BITS_FORMAT_ARGS_HPP + +// IWYU pragma: private, include + +#include "context.hpp" +#include "error.hpp" +#include "fwd.hpp" +#include "parse_context.hpp" + +#include +#include +#include +#include + +namespace kstd +{ + + namespace bits::format + { + + template + auto format_trampoline(void const * value_pointer, format_parse_context & parse_context, format_context & context) + -> void + { + auto typed_value_pointer = static_cast(value_pointer); + auto fmt = formatter>{}; + auto const it = fmt.parse(parse_context); + parse_context.advance_to(it); + fmt.format(*typed_value_pointer, context); + } + + template + auto get_size_trampoline(void const * value_pointer) -> std::size_t + { + if constexpr (is_width_v) + { + return static_cast(*static_cast(value_pointer)); + } + else + { + error("Dynamic width argument is not an integral value."); + return 0; + } + } + + } // namespace bits::format + + using format_args = std::span; + + template + struct format_arg_store + { + std::array args{}; + }; + + template + [[nodiscard]] auto make_format_args(Arguments const &... args) noexcept -> format_arg_store + { + using namespace bits::format; + + if constexpr (sizeof...(Arguments) == 0) + { + return format_arg_store<0>{}; + } + else + { + return format_arg_store{std::array{ + format_arg{static_cast(&args), format_trampoline, get_size_trampoline}...}}; + } + } + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/context.hpp b/libs/kstd/include/kstd/bits/format/context.hpp new file mode 100644 index 0000000..478a48f --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/context.hpp @@ -0,0 +1,61 @@ +#ifndef KSTD_BITS_FORMAT_CONTEXT_HPP +#define KSTD_BITS_FORMAT_CONTEXT_HPP + +// IWYU pragma: private, include + +#include "arg.hpp" + +#include + +#include +#include +#include +#include + +namespace kstd +{ + + namespace bits::format + { + template + constexpr auto inline is_width_v = std::integral && // + !std::same_as && // + !std::same_as && // + !std::same_as && // + !std::same_as && // + !std::same_as && // + !std::same_as; + } + + struct format_context + { + using writer_function = void(void *, std::string_view); + using format_args = std::span; + + writer_function * writer{}; + void * user_data{}; + format_args args{}; + + [[nodiscard]] auto arg(std::size_t const id) const -> format_arg const & + { + if (id >= args.size()) + { + kstd::os::panic("[kstd:format] argument index out of range!"); + } + return args[id]; + } + + constexpr auto push(std::string_view string) -> void + { + writer(user_data, string); + } + + constexpr auto push(char character) -> void + { + writer(user_data, std::string_view(&character, 1)); + } + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/error.hpp b/libs/kstd/include/kstd/bits/format/error.hpp new file mode 100644 index 0000000..f0863eb --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/error.hpp @@ -0,0 +1,24 @@ +#ifndef KSTD_BITS_FORMAT_ERROR_HPP +#define KSTD_BITS_FORMAT_ERROR_HPP + +#include "kstd/os/error.hpp" + +namespace kstd::bits::format +{ + + constexpr auto error(char const * message) -> void + { + if consteval + { + extern void compile_time_format_error_triggered(char const *); + compile_time_format_error_triggered(message); + } + else + { + kstd::os::panic("Error while formatting a string."); + } + } + +} // namespace kstd::bits::format + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter.hpp b/libs/kstd/include/kstd/bits/format/formatter.hpp new file mode 100644 index 0000000..bff5f55 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter.hpp @@ -0,0 +1,14 @@ +#ifndef KSTD_BITS_FORMATTER_HPP +#define KSTD_BITS_FORMATTER_HPP + +// IWYU pragma: private, include + +namespace kstd +{ + + template + struct formatter; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp new file mode 100644 index 0000000..bb6cacf --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp @@ -0,0 +1,83 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_BOOL_HPP +#define KSTD_BITS_FORMAT_FORMATTER_BOOL_HPP + +#include "../context.hpp" +#include "../error.hpp" +#include "../formatter.hpp" +#include "../parse_context.hpp" +#include "../specifiers.hpp" + +#include +#include + +namespace kstd +{ + + template<> + struct formatter + { + bits::format::format_specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + + auto it = context.begin(); + auto const end = context.end(); + + if (it != end && *it != '}') + { + if (*it == 's') + { + specifiers.type = *it; + std::advance(it, 1); + } + else + { + bits::format::error("Invalid type specifier for bool."); + } + } + + if (it != end && *it != '}') + { + bits::format::error("Missing terminating '}' in format string."); + } + + return it; + } + + auto format(bool value, format_context & context) const -> void + { + auto const text = value ? std::string_view{"true"} : std::string_view{"false"}; + auto final_width = 0uz; + + if (specifiers.width_mode == bits::format::width_mode::static_value) + { + final_width = specifiers.width_value; + } + else if (specifiers.width_mode == bits::format::width_mode::dynamic_argument_id) + { + auto const & arg = context.arg(specifiers.width_value); + final_width = arg.get_size_function(arg.value_pointer); + } + + auto padding = bits::format::calculate_format_padding(final_width, text.size(), specifiers.align, + bits::format::alignment::left); + + for (auto i = 0uz; i < padding.left; ++i) + { + context.push(specifiers.fill); + } + + context.push(text); + + for (auto i = 0uz; i < padding.right; ++i) + { + context.push(specifiers.fill); + } + } + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp new file mode 100644 index 0000000..9afb974 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp @@ -0,0 +1,29 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_CSTRING_HPP +#define KSTD_BITS_FORMAT_FORMATTER_CSTRING_HPP + +#include "../context.hpp" +#include "../formatter.hpp" +#include "string_view.hpp" + +#include + +namespace kstd +{ + + template<> + struct formatter : formatter + { + auto format(char const * string, format_context & context) const -> void + { + formatter::format(string ? std::string_view{string} : "(null)", context); + } + }; + + template<> + struct formatter : formatter + { + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp new file mode 100644 index 0000000..b0caed1 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp @@ -0,0 +1,204 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_INTEGRAL_HPP +#define KSTD_BITS_FORMAT_FORMATTER_INTEGRAL_HPP + +#include "../context.hpp" +#include "../error.hpp" +#include "../formatter.hpp" +#include "../parse_context.hpp" +#include "../specifiers.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace kstd +{ + + template + struct formatter + { + bits::format::format_specifiers specifiers{}; + + constexpr auto static maximum_digits = 80; + + enum struct base + { + bin = 2, + oct = 8, + dec = 10, + hex = 16, + }; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + + auto it = context.begin(); + auto const end = context.end(); + + if (it != end && *it != '}') + { + if (*it == 'b' || *it == 'B' || *it == 'd' || *it == 'o' || *it == 'x' || *it == 'X' || *it == 'p') + { + specifiers.type = *it; + std::advance(it, 1); + } + else + { + bits::format::error("Invalid type specifier for integral type."); + } + } + + if (it != end && *it != '}') + { + bits::format::error("Missing terminating '}' in format string."); + } + + return it; + } + + auto format(T value, format_context & context) const -> void + { + auto final_width = 0uz; + if (specifiers.width_mode == bits::format::width_mode::static_value) + { + final_width = specifiers.width_value; + } + else if (specifiers.width_mode == bits::format::width_mode::dynamic_argument_id) + { + auto const & arg = context.arg(specifiers.width_value); + final_width = arg.get_size_function(arg.value_pointer); + } + + using unsigned_T = std::make_unsigned_t; + auto absolute_value = static_cast(value); + auto is_negative = false; + + if constexpr (std::is_signed_v) + { + if (value < 0) + { + is_negative = true; + absolute_value = 0 - static_cast(value); + } + } + + auto const base = [type = specifiers.type] -> auto { + switch (type) + { + case 'x': + case 'X': + case 'p': + return base::hex; + case 'b': + case 'B': + return base::bin; + case 'o': + return base::oct; + default: + return base::dec; + } + }(); + + auto buffer = std::array{}; + auto digits = (specifiers.type == 'X') ? "0123456789ABCDEF" : "0123456789abcdef"; + auto current = buffer.rbegin(); + + if (absolute_value == 0) + { + *current = '0'; + std::advance(current, 1); + } + else + { + while (absolute_value != 0) + { + *current = digits[absolute_value % std::to_underlying(base)]; + std::advance(current, 1); + absolute_value /= std::to_underlying(base); + } + } + + auto content_length = static_cast(std::distance(buffer.rbegin(), current)); + auto prefix = std::array{'0', '\0'}; + auto prefix_length = 0uz; + if (specifiers.alternative_form) + { + switch (base) + { + case base::bin: + prefix[1] = specifiers.type == 'B' ? 'B' : 'b'; + prefix_length = 2; + break; + case base::oct: + prefix_length = 1; + break; + case base::hex: + prefix[1] = specifiers.type == 'X' ? 'X' : 'x'; + prefix_length = 2; + break; + default: + break; + } + } + + auto sign_character = '\0'; + if (is_negative) + { + sign_character = '-'; + } + else if (specifiers.sign == bits::format::sign_mode::plus) + { + sign_character = '+'; + } + else if (specifiers.sign == bits::format::sign_mode::space) + { + sign_character = ' '; + } + + auto const total_length = content_length + prefix_length + (sign_character != '\0'); + auto const padding = bits::format::calculate_format_padding(final_width, total_length, specifiers.align, + bits::format::alignment::right); + auto const effective_zero_pad = specifiers.zero_pad && (specifiers.align == bits::format::alignment::none); + + if (!effective_zero_pad) + { + for (auto i = 0uz; i < padding.left; ++i) + { + context.push(specifiers.fill); + } + } + + if (sign_character != '\0') + { + context.push(sign_character); + } + if (prefix_length > 0) + { + context.push(std::string_view{prefix.data(), prefix_length}); + } + + if (effective_zero_pad) + { + for (auto i = 0uz; i < padding.left; ++i) + { + context.push('0'); + } + } + + context.push(std::string_view{current.base(), content_length}); + + for (auto i = 0uz; i < padding.right; ++i) + { + context.push(specifiers.fill); + } + } + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp b/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp new file mode 100644 index 0000000..78e7f7b --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp @@ -0,0 +1,111 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_ORDERING_HPP +#define KSTD_BITS_FORMAT_FORMATTER_ORDERING_HPP + +#include "../context.hpp" +#include "../formatter.hpp" +#include "../parse_context.hpp" +#include "../specifiers.hpp" + +#include + +namespace kstd +{ + + template<> + struct formatter + { + bits::format::format_specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + return context.begin(); + } + + auto format(std::strong_ordering value, format_context & context) const -> void + { + if (value == std::strong_ordering::equal) + { + return context.push(specifiers.alternative_form ? "==" : "equal"); + } + else if (value == std::strong_ordering::equivalent) + { + return context.push(specifiers.alternative_form ? "==" : "equivalent"); + } + else if (value == std::strong_ordering::greater) + { + return context.push(specifiers.alternative_form ? ">" : "greater"); + } + else if (value == std::strong_ordering::less) + { + return context.push(specifiers.alternative_form ? "<" : "less"); + } + kstd::os::panic("[kstd:format] Invalid strong ordering value!"); + } + }; + + template<> + struct formatter + { + bits::format::format_specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + return context.begin(); + } + + auto format(std::weak_ordering value, format_context & context) const -> void + { + if (value == std::weak_ordering::equivalent) + { + return context.push(specifiers.alternative_form ? "==" : "equivalent"); + } + else if (value == std::weak_ordering::greater) + { + return context.push(specifiers.alternative_form ? ">" : "greater"); + } + else if (value == std::weak_ordering::less) + { + return context.push(specifiers.alternative_form ? "<" : "less"); + } + kstd::os::panic("[kstd:format] Invalid weak ordering value!"); + } + }; + + template<> + struct formatter + { + bits::format::format_specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + return context.begin(); + } + + auto format(std::partial_ordering value, format_context & context) const -> void + { + if (value == std::partial_ordering::equivalent) + { + return context.push(specifiers.alternative_form ? "==" : "equivalent"); + } + else if (value == std::partial_ordering::greater) + { + return context.push(specifiers.alternative_form ? ">" : "greater"); + } + else if (value == std::partial_ordering::less) + { + return context.push(specifiers.alternative_form ? "<" : "less"); + } + else if (value == std::partial_ordering::unordered) + { + return context.push(specifiers.alternative_form ? "<=>" : "unordered"); + } + kstd::os::panic("[kstd:format] Invalid partial ordering value!"); + } + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp b/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp new file mode 100644 index 0000000..fe75a2f --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp @@ -0,0 +1,42 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_POINTER_HPP +#define KSTD_BITS_FORMAT_FORMATTER_POINTER_HPP + +#include "../context.hpp" +#include "../formatter.hpp" +#include "../parse_context.hpp" +#include "../specifiers.hpp" +#include "integral.hpp" + +#include +#include + +namespace kstd +{ + + template + struct formatter : formatter + { + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + auto result = formatter::parse(context); + if (!this->specifiers.type) + { + this->specifiers.type = 'p'; + this->specifiers.alternative_form = true; + } + return result; + } + + auto format(T const * pointer, format_context & context) const -> void + { + formatter::format(std::bit_cast(pointer), context); + } + }; + + template + struct formatter : formatter + { + }; + +} // namespace kstd +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp b/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp new file mode 100644 index 0000000..f5b698e --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp @@ -0,0 +1,41 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_STRING_VIEW_HPP +#define KSTD_BITS_FORMAT_FORMATTER_STRING_VIEW_HPP + +#include "../context.hpp" +#include "../error.hpp" +#include "../formatter.hpp" +#include "../parse_context.hpp" + +#include + +namespace kstd +{ + + template<> + struct formatter + { + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + auto it = context.begin(); + + if (it != context.end() && *it == 's') + { + ++it; + } + + if (it != context.end() && *it != '}') + { + bits::format::error("Invalid specifier for string_view."); + } + return it; + } + + auto format(std::string_view const & string, format_context & context) const -> void + { + context.push(string); + } + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/fwd.hpp b/libs/kstd/include/kstd/bits/format/fwd.hpp new file mode 100644 index 0000000..6caedae --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/fwd.hpp @@ -0,0 +1,23 @@ +#ifndef KSTD_BITS_FORMAT_FWD_HPP +#define KSTD_BITS_FORMAT_FWD_HPP + +// IWYU pragma: private + +#include + +namespace kstd +{ + + struct format_parse_context; + struct format_context; + struct format_arg; + + template + struct formatter; + + template + struct format_arg_store; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/parse_context.hpp b/libs/kstd/include/kstd/bits/format/parse_context.hpp new file mode 100644 index 0000000..063263b --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/parse_context.hpp @@ -0,0 +1,109 @@ +#ifndef KSTD_BITS_FORMAT_PARSE_CONTEXT_HPP +#define KSTD_BITS_FORMAT_PARSE_CONTEXT_HPP + +// IWYU pragma: private, include + +#include "error.hpp" + +#include +#include + +namespace kstd +{ + + struct format_parse_context + { + using iterator = std::string_view::const_iterator; + + constexpr format_parse_context(std::string_view format, std::size_t argument_count, + bool const * is_integral = nullptr) + : m_current{format.begin()} + , m_end{format.end()} + , m_argument_count{argument_count} + , m_is_integral{is_integral} + {} + + [[nodiscard]] constexpr auto begin() const -> iterator + { + return m_current; + } + + [[nodiscard]] constexpr auto end() const -> iterator + { + return m_end; + } + + constexpr auto advance_to(iterator position) -> void + { + m_current = position; + } + + constexpr auto next_arg_id() -> std::size_t + { + if (m_mode == index_mode::manual) + { + bits::format::error("Cannot mix automatic and manual indexing."); + } + + m_mode = index_mode::automatic; + + if (m_next_argument_id >= m_argument_count) + { + bits::format::error("Argument index out of bounds."); + } + return m_next_argument_id++; + } + + constexpr auto check_arg_id(std::size_t index) -> void + { + if (m_mode == index_mode::automatic) + { + bits::format::error("Cannot mix automatic and manual indexing."); + } + + m_mode = index_mode::manual; + + if (index >= m_argument_count) + { + bits::format::error("Argument index out of bounds."); + } + } + + constexpr auto check_dynamic_width_id(std::size_t id) -> void + { + check_arg_id(id); + if (m_is_integral && !m_is_integral[id]) + { + bits::format::error("Dynamic width argument must be an integral object."); + } + } + + constexpr auto next_dynamic_width_id() -> std::size_t + { + auto const id = next_arg_id(); + if (m_is_integral && !m_is_integral[id]) + { + bits::format::error("Dynamic width argument must be an integral object."); + } + return id; + } + + private: + enum class index_mode + { + unknown, + automatic, + manual, + }; + + iterator m_current{}; + iterator m_end{}; + index_mode m_mode{}; + std::size_t m_next_argument_id{}; + std::size_t m_argument_count{}; + bool const * m_is_integral{}; + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/specifiers.hpp b/libs/kstd/include/kstd/bits/format/specifiers.hpp new file mode 100644 index 0000000..85581e6 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/specifiers.hpp @@ -0,0 +1,205 @@ +#ifndef KSTD_BITS_FORMAT_SPECS_HPP +#define KSTD_BITS_FORMAT_SPECS_HPP + +// IWYU pragma: private + +#include "error.hpp" +#include "parse_context.hpp" + +#include +#include +#include + +namespace kstd::bits::format +{ + enum struct alignment : std::uint8_t + { + none, + left, + right, + center, + }; + + enum struct sign_mode : std::uint8_t + { + none, + plus, + minus, + space, + }; + + enum struct width_mode : std::uint8_t + { + none, + static_value, + dynamic_argument_id + }; + + struct format_specifiers + { + char fill{' '}; + alignment align{}; + sign_mode sign{}; + bool alternative_form{}; + bool zero_pad{}; + + width_mode width_mode{}; + std::size_t width_value{}; + char type{}; + }; + + struct format_padding + { + std::size_t left{}; + std::size_t right{}; + }; + + constexpr auto parse_format_specifiers(format_parse_context & context) -> format_specifiers + { + auto specifiers = format_specifiers{}; + auto it = context.begin(); + auto const end = context.end(); + + if (it != end && *it == '}') + { + return specifiers; + } + + if (std::next(it) != end && ((*std::next(it)) == '<' || (*std::next(it)) == '>' || (*std::next(it)) == '^')) + { + specifiers.fill = *it; + switch (*std::next(it)) + { + case '<': + specifiers.align = alignment::left; + break; + case '>': + specifiers.align = alignment::right; + break; + case '^': + default: + specifiers.align = alignment::center; + break; + } + std::advance(it, 2); + } + else if (*it == '<' || *it == '>' || *it == '^') + { + switch (*it) + { + case '<': + specifiers.align = alignment::left; + break; + case '>': + specifiers.align = alignment::right; + break; + case '^': + default: + specifiers.align = alignment::center; + break; + } + std::advance(it, 1); + } + + if (it != end && (*it == '+' || *it == '-' || *it == ' ')) + { + switch (*it) + { + case '+': + specifiers.sign = sign_mode::plus; + break; + case '-': + specifiers.sign = sign_mode::minus; + break; + case ' ': + default: + specifiers.sign = sign_mode::space; + break; + } + std::advance(it, 1); + } + + if (it != end && *it == '#') + { + specifiers.alternative_form = true; + std::advance(it, 1); + } + + if (it != end && *it == '0') + { + specifiers.zero_pad = true; + std::advance(it, 1); + } + + if (it != end && *it == '{') + { + specifiers.width_mode = width_mode::dynamic_argument_id; + std::advance(it, 1); + auto argument_id = 0uz; + + if (it != end && *it >= '0' && *it <= '9') + { + while (it != end && *it >= '0' && *it <= '9') + { + argument_id = argument_id * 10 + static_cast(*it - '0'); + std::advance(it, 1); + } + context.check_dynamic_width_id(argument_id); + } + else + { + argument_id = context.next_dynamic_width_id(); + } + + if (it == end || *it != '}') + { + error("Expected '}' for dynamic width."); + } + std::advance(it, 1); + specifiers.width_value = argument_id; + } + else if (it != end && *it >= '0' && *it <= '9') + { + specifiers.width_mode = width_mode::static_value; + while (it != end && *it >= '0' && *it <= '9') + { + specifiers.width_value = specifiers.width_value * 10 + static_cast(*it - '0'); + std::advance(it, 1); + } + } + + context.advance_to(it); + return specifiers; + } + + constexpr auto calculate_format_padding(std::size_t target_width, std::size_t content_length, + alignment requested_alignment, alignment default_alignment) -> format_padding + { + if (target_width <= content_length) + { + return {}; + } + + auto total_padding = target_width - content_length; + auto effective_alignment = (requested_alignment == alignment::none) ? default_alignment : requested_alignment; + + switch (effective_alignment) + { + case alignment::center: + { + auto left = total_padding / 2; + auto right = total_padding - left; + return {left, right}; + } + case alignment::left: + return {0, total_padding}; + case alignment::right: + default: + return {total_padding, 0}; + break; + } + } + +} // namespace kstd::bits::format + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/string.hpp b/libs/kstd/include/kstd/bits/format/string.hpp new file mode 100644 index 0000000..2e7d60a --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/string.hpp @@ -0,0 +1,146 @@ +#ifndef KSTD_BITS_FORMAT_STRING_HPP +#define KSTD_BITS_FORMAT_STRING_HPP + +// IWYU pragma: private, include + +#include "context.hpp" +#include "error.hpp" +#include "parse_context.hpp" + +#include +#include +#include +#include + +namespace kstd +{ + + namespace bits::format + { + template + constexpr auto validate_argument(std::size_t target_index, format_parse_context & context) -> void + { + auto found = false; + auto current_index = 0uz; + + (..., [&] { + if (current_index == target_index && !found) + { + using decay_type = std::remove_cvref_t; + auto fmt = formatter{}; + + auto it = fmt.parse(context); + context.advance_to(it); + found = true; + } + ++current_index; + }()); + + if (!found) + { + error("Argument index out of bounds."); + } + } + + template + constexpr auto validate_dynamic_width(std::size_t target_index) -> void + { + auto is_valid_integer = false; + auto current_index = 0uz; + + (..., [&] { + if (current_index == target_index) + { + using decay_type = std::remove_cvref_t; + if constexpr (std::is_integral_v) + { + is_valid_integer = true; + } + } + ++current_index; + }()); + + if (!is_valid_integer) + { + error("Dynamic width argument must be an integral object."); + } + } + } // namespace bits::format + + template + struct format_string + { + template + consteval format_string(char const (&str)[Size]) noexcept(false) // NOLINT + : str_view{str} + { + using namespace bits::format; + + auto const is_width_compatible = + std::array 0 ? sizeof...(Args) : 1)>{is_width_v>...}; + auto context = format_parse_context{str_view, sizeof...(Args), is_width_compatible.data()}; + auto it = context.begin(); + + while (it != context.end()) + { + if (*it == '{') + { + ++it; + if (it != context.end() && *it == '{') + { + ++it; + context.advance_to(it); + continue; + } + + context.advance_to(it); + auto argument_id = 0uz; + + if (it != context.end() && *it >= '0' && *it <= '9') + { + while (it != context.end() && *it >= '0' && *it <= '9') + { + argument_id = argument_id * 10 + static_cast(*it - '0'); + ++it; + } + context.check_arg_id(argument_id); + context.advance_to(it); + } + else + { + argument_id = context.next_arg_id(); + } + + if (it != context.end() && *it == ':') + { + ++it; + context.advance_to(it); + } + + validate_argument(argument_id, context); + + it = context.begin(); + if (it == context.end() || *it != '}') + { + bits::format::error("Missing closing '}' in format string."); + } + } + else if (*it == '}') + { + ++it; + if (it != context.end() && *it == '}') + { + bits::format::error("Unescaped '}' in format string."); + } + } + ++it; + context.advance_to(it); + } + } + + std::string_view str_view; + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format_args.hpp b/libs/kstd/include/kstd/bits/format_args.hpp deleted file mode 100644 index 71ee5ae..0000000 --- a/libs/kstd/include/kstd/bits/format_args.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_ARGS_HPP -#define KSTD_BITS_FORMAT_ARGS_HPP - -// IWYU pragma: private, include - -#include "kstd/bits/format_context.hpp" -#include "kstd/bits/format_parse_context.hpp" - -#include -#include -#include - -namespace kstd -{ - struct format_context; - struct format_parse_context; - - template - struct formatter; - - template - struct format_arg_store - { - std::array args{}; - }; - - template - auto format_trampoline(void const * value_pointer, format_parse_context & parse_context, format_context & context) - -> void - { - auto typed_value_pointer = static_cast(value_pointer); - auto fmt = formatter>{}; - auto const it = fmt.parse(parse_context); - parse_context.advance_to(it); - fmt.format(*typed_value_pointer, context); - } - - template - auto get_size_trampoline(void const * value_pointer) -> std::size_t - { - if constexpr (bits::is_format_width_compatible_v) - { - return static_cast(*static_cast(value_pointer)); - } - else - { - report_format_error("Dynamic width argument is not an integral value."); - return 0; - } - } - - template - [[nodiscard]] auto make_format_args(Arguments const &... args) noexcept -> format_arg_store - { - if constexpr (sizeof...(Arguments) == 0) - { - return format_arg_store<0>{}; - } - else - { - return format_arg_store{std::array{ - format_arg{static_cast(&args), format_trampoline, get_size_trampoline}...}}; - } - } - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format_context.hpp b/libs/kstd/include/kstd/bits/format_context.hpp deleted file mode 100644 index 05e37ca..0000000 --- a/libs/kstd/include/kstd/bits/format_context.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_CONTEXT_HPP -#define KSTD_BITS_FORMAT_CONTEXT_HPP - -// IWYU pragma: private, include - -#include - -#include -#include -#include -#include - -namespace kstd -{ - - namespace bits - { - template - constexpr auto inline is_format_width_compatible_v = std::integral && // - !std::same_as && // - !std::same_as && // - !std::same_as && // - !std::same_as && // - !std::same_as && // - !std::same_as; - } - - struct format_parse_context; - struct format_context; - - struct format_arg - { - using format_function_type = auto(void const * value, format_parse_context & parse_context, - format_context & context) -> void; - using get_size_function_type = auto(void const * value) -> std::size_t; - - void const * value_pointer; - format_function_type * format_function; - get_size_function_type * get_size_function; - }; - - using format_args = std::span; - - struct format_context - { - using writer_function = void(void *, std::string_view); - - writer_function * writer; - void * user_data; - format_args args; - - [[nodiscard]] auto arg(std::size_t const id) const -> format_arg const & - { - if (id >= args.size()) - { - kstd::os::panic("[kstd:format] argument index out of range!"); - } - return args[id]; - } - - constexpr auto push(std::string_view string) -> void - { - writer(user_data, string); - } - - constexpr auto push(char character) -> void - { - writer(user_data, std::string_view(&character, 1)); - } - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format_parse_context.hpp b/libs/kstd/include/kstd/bits/format_parse_context.hpp deleted file mode 100644 index 76c3f03..0000000 --- a/libs/kstd/include/kstd/bits/format_parse_context.hpp +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_PARSE_CONTEXT_HPP -#define KSTD_BITS_FORMAT_PARSE_CONTEXT_HPP - -// IWYU pragma: private, include - -#include - -#include -#include - -namespace kstd -{ - - constexpr auto report_format_error(char const * message) -> void - { - if consteval - { - extern void compile_time_format_error_triggered(char const *); - compile_time_format_error_triggered(message); - } - else - { - kstd::os::panic("Error while formatting a string."); - } - } - - struct format_parse_context - { - using iterator = std::string_view::const_iterator; - - constexpr format_parse_context(std::string_view format, std::size_t argument_count, - bool const * is_integral = nullptr) - : m_current{format.begin()} - , m_end{format.end()} - , m_argument_count{argument_count} - , m_is_integral{is_integral} - {} - - [[nodiscard]] constexpr auto begin() const -> iterator - { - return m_current; - } - - [[nodiscard]] constexpr auto end() const -> iterator - { - return m_end; - } - - constexpr auto advance_to(iterator position) -> void - { - m_current = position; - } - - constexpr auto next_arg_id() -> std::size_t - { - if (m_mode == index_mode::manual) - { - report_format_error("Cannot mix automatic and manual indexing."); - } - - m_mode = index_mode::automatic; - - if (m_next_argument_id >= m_argument_count) - { - report_format_error("Argument index out of bounds."); - } - return m_next_argument_id++; - } - - constexpr auto check_arg_id(std::size_t index) -> void - { - if (m_mode == index_mode::automatic) - { - report_format_error("Cannot mix automatic and manual indexing."); - } - - m_mode = index_mode::manual; - - if (index >= m_argument_count) - { - report_format_error("Argument index out of bounds."); - } - } - - constexpr auto check_dynamic_width_id(std::size_t id) -> void - { - check_arg_id(id); - if (m_is_integral && !m_is_integral[id]) - { - report_format_error("Dynamic width argument must be an integral object."); - } - } - - constexpr auto next_dynamic_width_id() -> std::size_t - { - auto const id = next_arg_id(); - if (m_is_integral && !m_is_integral[id]) - { - report_format_error("Dynamic width argument must be an integral object."); - } - return id; - } - - private: - enum class index_mode - { - unknown, - automatic, - manual, - }; - - iterator m_current{}; - iterator m_end{}; - index_mode m_mode{}; - std::size_t m_next_argument_id{}; - std::size_t m_argument_count{}; - bool const * m_is_integral{}; - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format_specifiers.hpp b/libs/kstd/include/kstd/bits/format_specifiers.hpp deleted file mode 100644 index 00cca40..0000000 --- a/libs/kstd/include/kstd/bits/format_specifiers.hpp +++ /dev/null @@ -1,205 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_SPECS_HPP -#define KSTD_BITS_FORMAT_SPECS_HPP - -// IWYU pragma: private - -#include "kstd/bits/format_context.hpp" -#include "kstd/bits/format_parse_context.hpp" - -#include -#include -#include - -namespace kstd::bits -{ - enum struct alignment : std::uint8_t - { - none, - left, - right, - center, - }; - - enum struct sign_mode : std::uint8_t - { - none, - plus, - minus, - space, - }; - - enum struct width_mode : std::uint8_t - { - none, - static_value, - dynamic_argument_id - }; - - struct format_specifiers - { - char fill{' '}; - alignment align{}; - sign_mode sign{}; - bool alternative_form{}; - bool zero_pad{}; - - width_mode width_mode{}; - std::size_t width_value{}; - char type{}; - }; - - struct format_padding - { - std::size_t left{}; - std::size_t right{}; - }; - - constexpr auto parse_format_specifiers(format_parse_context & context) -> format_specifiers - { - auto specifiers = format_specifiers{}; - auto it = context.begin(); - auto const end = context.end(); - - if (it != end && *it == '}') - { - return specifiers; - } - - if (std::next(it) != end && ((*std::next(it)) == '<' || (*std::next(it)) == '>' || (*std::next(it)) == '^')) - { - specifiers.fill = *it; - switch (*std::next(it)) - { - case '<': - specifiers.align = alignment::left; - break; - case '>': - specifiers.align = alignment::right; - break; - case '^': - default: - specifiers.align = alignment::center; - break; - } - std::advance(it, 2); - } - else if (*it == '<' || *it == '>' || *it == '^') - { - switch (*it) - { - case '<': - specifiers.align = alignment::left; - break; - case '>': - specifiers.align = alignment::right; - break; - case '^': - default: - specifiers.align = alignment::center; - break; - } - std::advance(it, 1); - } - - if (it != end && (*it == '+' || *it == '-' || *it == ' ')) - { - switch (*it) - { - case '+': - specifiers.sign = sign_mode::plus; - break; - case '-': - specifiers.sign = sign_mode::minus; - break; - case ' ': - default: - specifiers.sign = sign_mode::space; - break; - } - std::advance(it, 1); - } - - if (it != end && *it == '#') - { - specifiers.alternative_form = true; - std::advance(it, 1); - } - - if (it != end && *it == '0') - { - specifiers.zero_pad = true; - std::advance(it, 1); - } - - if (it != end && *it == '{') - { - specifiers.width_mode = width_mode::dynamic_argument_id; - std::advance(it, 1); - auto argument_id = 0uz; - - if (it != end && *it >= '0' && *it <= '9') - { - while (it != end && *it >= '0' && *it <= '9') - { - argument_id = argument_id * 10 + static_cast(*it - '0'); - std::advance(it, 1); - } - context.check_dynamic_width_id(argument_id); - } - else - { - argument_id = context.next_dynamic_width_id(); - } - - if (it == end || *it != '}') - { - report_format_error("Expected '}' for dynamic width."); - } - std::advance(it, 1); - specifiers.width_value = argument_id; - } - else if (it != end && *it >= '0' && *it <= '9') - { - specifiers.width_mode = width_mode::static_value; - while (it != end && *it >= '0' && *it <= '9') - { - specifiers.width_value = specifiers.width_value * 10 + static_cast(*it - '0'); - std::advance(it, 1); - } - } - - context.advance_to(it); - return specifiers; - } - - constexpr auto calculate_format_padding(std::size_t target_width, std::size_t content_length, - alignment requested_alignment, alignment default_alignment) -> format_padding - { - if (target_width <= content_length) - { - return {}; - } - - auto total_padding = target_width - content_length; - auto effective_alignment = (requested_alignment == alignment::none) ? default_alignment : requested_alignment; - - switch (effective_alignment) - { - case alignment::center: - { - auto left = total_padding / 2; - auto right = total_padding - left; - return {left, right}; - } - case alignment::left: - return {0, total_padding}; - case alignment::right: - default: - return {total_padding, 0}; - break; - } - } - -} // namespace kstd::bits - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format_string.hpp b/libs/kstd/include/kstd/bits/format_string.hpp deleted file mode 100644 index f16f1ee..0000000 --- a/libs/kstd/include/kstd/bits/format_string.hpp +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_STRING_HPP -#define KSTD_BITS_FORMAT_STRING_HPP - -// IWYU pragma: private, include - -#include "kstd/bits/format_context.hpp" -#include "kstd/bits/format_parse_context.hpp" - -#include -#include -#include -#include - -namespace kstd -{ - - template - struct formatter; - - namespace bits - { - template - constexpr auto validate_argument(std::size_t target_index, format_parse_context & context) -> void - { - auto found = false; - auto current_index = 0uz; - - (..., [&] { - if (current_index == target_index && !found) - { - using decay_type = std::remove_cvref_t; - auto fmt = formatter{}; - - auto it = fmt.parse(context); - context.advance_to(it); - found = true; - } - ++current_index; - }()); - - if (!found) - { - report_format_error("Argument index out of bounds."); - } - } - - template - constexpr auto validate_dynamic_width(std::size_t target_index) -> void - { - auto is_valid_integer = false; - auto current_index = 0uz; - - (..., [&] { - if (current_index == target_index) - { - using decay_type = std::remove_cvref_t; - if constexpr (std::is_integral_v) - { - is_valid_integer = true; - } - } - ++current_index; - }()); - - if (!is_valid_integer) - { - report_format_error("Dynamic width argument must be an integral object."); - } - } - } // namespace bits - - template - struct format_string - { - template - consteval format_string(char const (&str)[Size]) noexcept(false) // NOLINT - : str_view{str} - { - auto const is_width_compatible = std::array 0 ? sizeof...(Args) : 1)>{ - bits::is_format_width_compatible_v>...}; - auto context = format_parse_context{str_view, sizeof...(Args), is_width_compatible.data()}; - auto it = context.begin(); - - while (it != context.end()) - { - if (*it == '{') - { - ++it; - if (it != context.end() && *it == '{') - { - ++it; - context.advance_to(it); - continue; - } - - context.advance_to(it); - auto argument_id = 0uz; - - if (it != context.end() && *it >= '0' && *it <= '9') - { - while (it != context.end() && *it >= '0' && *it <= '9') - { - argument_id = argument_id * 10 + static_cast(*it - '0'); - ++it; - } - context.check_arg_id(argument_id); - context.advance_to(it); - } - else - { - argument_id = context.next_arg_id(); - } - - if (it != context.end() && *it == ':') - { - ++it; - context.advance_to(it); - } - - bits::validate_argument(argument_id, context); - - it = context.begin(); - if (it == context.end() || *it != '}') - { - report_format_error("Missing closing '}' in format string."); - } - } - else if (*it == '}') - { - ++it; - if (it != context.end() && *it == '}') - { - report_format_error("Unescaped '}' in format string."); - } - } - ++it; - context.advance_to(it); - } - } - - std::string_view str_view; - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/formatter.hpp b/libs/kstd/include/kstd/bits/formatter.hpp deleted file mode 100644 index e46f6ad..0000000 --- a/libs/kstd/include/kstd/bits/formatter.hpp +++ /dev/null @@ -1,435 +0,0 @@ -#ifndef KSTD_BITS_FORMATTER_HPP -#define KSTD_BITS_FORMATTER_HPP - -// IWYU pragma: private, include - -#include "kstd/bits/format_context.hpp" -#include "kstd/bits/format_parse_context.hpp" -#include "kstd/bits/format_specifiers.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace kstd -{ - - template - struct formatter; - - template<> - struct formatter - { - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - auto it = context.begin(); - - if (it != context.end() && *it == 's') - { - ++it; - } - - if (it != context.end() && *it != '}') - { - report_format_error("Invalid specifier for string_view."); - } - return it; - } - - auto format(std::string_view const & string, format_context & context) const -> void - { - context.push(string); - } - }; - - template<> - struct formatter : formatter - { - auto format(char const * string, format_context & context) const -> void - { - formatter::format(string ? std::string_view{string} : "(null)", context); - } - }; - - template - struct formatter - { - bits::format_specifiers specifiers{}; - - constexpr auto static maximum_digits = 80; - - enum struct base - { - bin = 2, - oct = 8, - dec = 10, - hex = 16, - }; - - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - specifiers = bits::parse_format_specifiers(context); - - auto it = context.begin(); - auto const end = context.end(); - - if (it != end && *it != '}') - { - if (*it == 'b' || *it == 'B' || *it == 'd' || *it == 'o' || *it == 'x' || *it == 'X' || *it == 'p') - { - specifiers.type = *it; - std::advance(it, 1); - } - else - { - report_format_error("Invalid type specifier for integral type."); - } - } - - if (it != end && *it != '}') - { - report_format_error("Missing terminating '}' in format string."); - } - - return it; - } - - auto format(T value, format_context & context) const -> void - { - auto final_width = 0uz; - if (specifiers.width_mode == bits::width_mode::static_value) - { - final_width = specifiers.width_value; - } - else if (specifiers.width_mode == bits::width_mode::dynamic_argument_id) - { - auto const & arg = context.arg(specifiers.width_value); - final_width = arg.get_size_function(arg.value_pointer); - } - - using unsigned_T = std::make_unsigned_t; - auto absolute_value = static_cast(value); - auto is_negative = false; - - if constexpr (std::is_signed_v) - { - if (value < 0) - { - is_negative = true; - absolute_value = 0 - static_cast(value); - } - } - - auto const base = [type = specifiers.type] -> auto { - switch (type) - { - case 'x': - case 'X': - case 'p': - return base::hex; - case 'b': - case 'B': - return base::bin; - case 'o': - return base::oct; - default: - return base::dec; - } - }(); - - auto buffer = std::array{}; - auto digits = (specifiers.type == 'X') ? "0123456789ABCDEF" : "0123456789abcdef"; - auto current = buffer.rbegin(); - - if (absolute_value == 0) - { - *current = '0'; - std::advance(current, 1); - } - else - { - while (absolute_value != 0) - { - *current = digits[absolute_value % std::to_underlying(base)]; - std::advance(current, 1); - absolute_value /= std::to_underlying(base); - } - } - - auto content_length = static_cast(std::distance(buffer.rbegin(), current)); - auto prefix = std::array{'0', '\0'}; - auto prefix_length = 0uz; - if (specifiers.alternative_form) - { - switch (base) - { - case base::bin: - prefix[1] = specifiers.type == 'B' ? 'B' : 'b'; - prefix_length = 2; - break; - case base::oct: - prefix_length = 1; - break; - case base::hex: - prefix[1] = specifiers.type == 'X' ? 'X' : 'x'; - prefix_length = 2; - break; - default: - break; - } - } - - auto sign_character = '\0'; - if (is_negative) - { - sign_character = '-'; - } - else if (specifiers.sign == bits::sign_mode::plus) - { - sign_character = '+'; - } - else if (specifiers.sign == bits::sign_mode::space) - { - sign_character = ' '; - } - - auto const total_length = content_length + prefix_length + (sign_character != '\0'); - auto const padding = - bits::calculate_format_padding(final_width, total_length, specifiers.align, bits::alignment::right); - auto const effective_zero_pad = specifiers.zero_pad && (specifiers.align == bits::alignment::none); - - if (!effective_zero_pad) - { - for (auto i = 0uz; i < padding.left; ++i) - { - context.push(specifiers.fill); - } - } - - if (sign_character != '\0') - { - context.push(sign_character); - } - if (prefix_length > 0) - { - context.push(std::string_view{prefix.data(), prefix_length}); - } - - if (effective_zero_pad) - { - for (auto i = 0uz; i < padding.left; ++i) - { - context.push('0'); - } - } - - context.push(std::string_view{current.base(), content_length}); - - for (auto i = 0uz; i < padding.right; ++i) - { - context.push(specifiers.fill); - } - } - }; - - template - struct formatter : formatter - { - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - auto result = formatter::parse(context); - if (!this->specifiers.type) - { - this->specifiers.type = 'p'; - this->specifiers.alternative_form = true; - } - return result; - } - - auto format(T const * pointer, format_context & context) const -> void - { - formatter::format(std::bit_cast(pointer), context); - } - }; - - template - struct formatter : formatter - { - }; - - template<> - struct formatter : formatter - { - }; - - template<> - struct formatter - { - bits::format_specifiers specifiers{}; - - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - specifiers = bits::parse_format_specifiers(context); - - auto it = context.begin(); - auto const end = context.end(); - - if (it != end && *it != '}') - { - if (*it == 's') - { - specifiers.type = *it; - std::advance(it, 1); - } - else - { - report_format_error("Invalid type specifier for bool."); - } - } - - if (it != end && *it != '}') - { - report_format_error("Missing terminating '}' in format string."); - } - - return it; - } - - auto format(bool value, format_context & context) const -> void - { - auto const text = value ? std::string_view{"true"} : std::string_view{"false"}; - auto final_width = 0uz; - - if (specifiers.width_mode == bits::width_mode::static_value) - { - final_width = specifiers.width_value; - } - else if (specifiers.width_mode == bits::width_mode::dynamic_argument_id) - { - auto const & arg = context.arg(specifiers.width_value); - final_width = arg.get_size_function(arg.value_pointer); - } - - auto padding = bits::calculate_format_padding(final_width, text.size(), specifiers.align, bits::alignment::left); - - for (auto i = 0uz; i < padding.left; ++i) - { - context.push(specifiers.fill); - } - - context.push(text); - - for (auto i = 0uz; i < padding.right; ++i) - { - context.push(specifiers.fill); - } - } - }; - - template<> - struct formatter - { - bits::format_specifiers specifiers{}; - - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - specifiers = bits::parse_format_specifiers(context); - return context.begin(); - } - - auto format(std::strong_ordering value, format_context & context) const -> void - { - if (value == std::strong_ordering::equal) - { - return context.push(specifiers.alternative_form ? "==" : "equal"); - } - else if (value == std::strong_ordering::equivalent) - { - return context.push(specifiers.alternative_form ? "==" : "equivalent"); - } - else if (value == std::strong_ordering::greater) - { - return context.push(specifiers.alternative_form ? ">" : "greater"); - } - else if (value == std::strong_ordering::less) - { - return context.push(specifiers.alternative_form ? "<" : "less"); - } - kstd::os::panic("[kstd:format] Invalid strong ordering value!"); - } - }; - - template<> - struct formatter - { - bits::format_specifiers specifiers{}; - - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - specifiers = bits::parse_format_specifiers(context); - return context.begin(); - } - - auto format(std::weak_ordering value, format_context & context) const -> void - { - if (value == std::weak_ordering::equivalent) - { - return context.push(specifiers.alternative_form ? "==" : "equivalent"); - } - else if (value == std::weak_ordering::greater) - { - return context.push(specifiers.alternative_form ? ">" : "greater"); - } - else if (value == std::weak_ordering::less) - { - return context.push(specifiers.alternative_form ? "<" : "less"); - } - kstd::os::panic("[kstd:format] Invalid weak ordering value!"); - } - }; - - template<> - struct formatter - { - bits::format_specifiers specifiers{}; - - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - specifiers = bits::parse_format_specifiers(context); - return context.begin(); - } - - auto format(std::partial_ordering value, format_context & context) const -> void - { - if (value == std::partial_ordering::equivalent) - { - return context.push(specifiers.alternative_form ? "==" : "equivalent"); - } - else if (value == std::partial_ordering::greater) - { - return context.push(specifiers.alternative_form ? ">" : "greater"); - } - else if (value == std::partial_ordering::less) - { - return context.push(specifiers.alternative_form ? "<" : "less"); - } - else if (value == std::partial_ordering::unordered) - { - return context.push(specifiers.alternative_form ? "<=>" : "unordered"); - } - kstd::os::panic("[kstd:format] Invalid partial ordering value!"); - } - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/format b/libs/kstd/include/kstd/format index 0a1bcd1..76aaed8 100644 --- a/libs/kstd/include/kstd/format +++ b/libs/kstd/include/kstd/format @@ -1,8 +1,17 @@ #ifndef KSTD_FORMAT_HPP #define KSTD_FORMAT_HPP -#include "bits/format_context.hpp" // IWYU pragma: export -#include "bits/format_string.hpp" // IWYU pragma: export -#include "bits/formatter.hpp" // IWYU pragma: export +#include "bits/format/arg.hpp" // IWYU pragma: export +#include "bits/format/args.hpp" // IWYU pragma: export +#include "bits/format/context.hpp" // IWYU pragma: export +#include "bits/format/formatter.hpp" // IWYU pragma: export +#include "bits/format/formatter/bool.hpp" // IWYU pragma: export +#include "bits/format/formatter/cstring.hpp" // IWYU pragma: export +#include "bits/format/formatter/integral.hpp" // IWYU pragma: export +#include "bits/format/formatter/ordering.hpp" // IWYU pragma: export +#include "bits/format/formatter/pointer.hpp" // IWYU pragma: export +#include "bits/format/formatter/string_view.hpp" // IWYU pragma: export +#include "bits/format/parse_context.hpp" // IWYU pragma: export +#include "bits/format/string.hpp" // IWYU pragma: export #endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/os/print.hpp b/libs/kstd/include/kstd/os/print.hpp index 0dde6e7..b8e0732 100644 --- a/libs/kstd/include/kstd/os/print.hpp +++ b/libs/kstd/include/kstd/os/print.hpp @@ -1,7 +1,7 @@ #ifndef KSTD_OS_PRINT_HPP #define KSTD_OS_PRINT_HPP -#include "kstd/bits/format_args.hpp" +#include "kstd/bits/format/args.hpp" #include "kstd/bits/print_sink.hpp" #include diff --git a/libs/kstd/include/kstd/print b/libs/kstd/include/kstd/print index 1ab24bd..f91cb04 100644 --- a/libs/kstd/include/kstd/print +++ b/libs/kstd/include/kstd/print @@ -1,7 +1,6 @@ #ifndef KSTD_PRINT #define KSTD_PRINT -#include "bits/format_args.hpp" #include "bits/print_sink.hpp" // IWYU pragma: export #include "os/print.hpp" -- cgit v1.2.3 From 5f7d5c6dafd97b0fe59fdaadc3d87dc02f69c218 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 14:11:07 +0100 Subject: kstd/fmt: clean up naming --- .../include/kstd/bits/format/formatter/bool.hpp | 2 +- .../kstd/bits/format/formatter/integral.hpp | 2 +- .../kstd/bits/format/formatter/ordering.hpp | 6 +-- libs/kstd/include/kstd/bits/format/specifiers.hpp | 46 +++++++++++----------- libs/kstd/include/kstd/bits/format/string.hpp | 1 + 5 files changed, 29 insertions(+), 28 deletions(-) diff --git a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp index bb6cacf..b409e06 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp @@ -16,7 +16,7 @@ namespace kstd template<> struct formatter { - bits::format::format_specifiers specifiers{}; + bits::format::specifiers specifiers{}; constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator { diff --git a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp index b0caed1..d5cd6c5 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp @@ -21,7 +21,7 @@ namespace kstd template struct formatter { - bits::format::format_specifiers specifiers{}; + bits::format::specifiers specifiers{}; constexpr auto static maximum_digits = 80; diff --git a/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp b/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp index 78e7f7b..758285d 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp @@ -14,7 +14,7 @@ namespace kstd template<> struct formatter { - bits::format::format_specifiers specifiers{}; + bits::format::specifiers specifiers{}; constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator { @@ -47,7 +47,7 @@ namespace kstd template<> struct formatter { - bits::format::format_specifiers specifiers{}; + bits::format::specifiers specifiers{}; constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator { @@ -76,7 +76,7 @@ namespace kstd template<> struct formatter { - bits::format::format_specifiers specifiers{}; + bits::format::specifiers specifiers{}; constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator { diff --git a/libs/kstd/include/kstd/bits/format/specifiers.hpp b/libs/kstd/include/kstd/bits/format/specifiers.hpp index 85581e6..9bc66c7 100644 --- a/libs/kstd/include/kstd/bits/format/specifiers.hpp +++ b/libs/kstd/include/kstd/bits/format/specifiers.hpp @@ -35,7 +35,7 @@ namespace kstd::bits::format dynamic_argument_id }; - struct format_specifiers + struct specifiers { char fill{' '}; alignment align{}; @@ -48,37 +48,37 @@ namespace kstd::bits::format char type{}; }; - struct format_padding + struct padding { std::size_t left{}; std::size_t right{}; }; - constexpr auto parse_format_specifiers(format_parse_context & context) -> format_specifiers + constexpr auto parse_format_specifiers(format_parse_context & context) -> specifiers { - auto specifiers = format_specifiers{}; + auto specs = specifiers{}; auto it = context.begin(); auto const end = context.end(); if (it != end && *it == '}') { - return specifiers; + return specs; } if (std::next(it) != end && ((*std::next(it)) == '<' || (*std::next(it)) == '>' || (*std::next(it)) == '^')) { - specifiers.fill = *it; + specs.fill = *it; switch (*std::next(it)) { case '<': - specifiers.align = alignment::left; + specs.align = alignment::left; break; case '>': - specifiers.align = alignment::right; + specs.align = alignment::right; break; case '^': default: - specifiers.align = alignment::center; + specs.align = alignment::center; break; } std::advance(it, 2); @@ -88,14 +88,14 @@ namespace kstd::bits::format switch (*it) { case '<': - specifiers.align = alignment::left; + specs.align = alignment::left; break; case '>': - specifiers.align = alignment::right; + specs.align = alignment::right; break; case '^': default: - specifiers.align = alignment::center; + specs.align = alignment::center; break; } std::advance(it, 1); @@ -106,14 +106,14 @@ namespace kstd::bits::format switch (*it) { case '+': - specifiers.sign = sign_mode::plus; + specs.sign = sign_mode::plus; break; case '-': - specifiers.sign = sign_mode::minus; + specs.sign = sign_mode::minus; break; case ' ': default: - specifiers.sign = sign_mode::space; + specs.sign = sign_mode::space; break; } std::advance(it, 1); @@ -121,19 +121,19 @@ namespace kstd::bits::format if (it != end && *it == '#') { - specifiers.alternative_form = true; + specs.alternative_form = true; std::advance(it, 1); } if (it != end && *it == '0') { - specifiers.zero_pad = true; + specs.zero_pad = true; std::advance(it, 1); } if (it != end && *it == '{') { - specifiers.width_mode = width_mode::dynamic_argument_id; + specs.width_mode = width_mode::dynamic_argument_id; std::advance(it, 1); auto argument_id = 0uz; @@ -156,24 +156,24 @@ namespace kstd::bits::format error("Expected '}' for dynamic width."); } std::advance(it, 1); - specifiers.width_value = argument_id; + specs.width_value = argument_id; } else if (it != end && *it >= '0' && *it <= '9') { - specifiers.width_mode = width_mode::static_value; + specs.width_mode = width_mode::static_value; while (it != end && *it >= '0' && *it <= '9') { - specifiers.width_value = specifiers.width_value * 10 + static_cast(*it - '0'); + specs.width_value = specs.width_value * 10 + static_cast(*it - '0'); std::advance(it, 1); } } context.advance_to(it); - return specifiers; + return specs; } constexpr auto calculate_format_padding(std::size_t target_width, std::size_t content_length, - alignment requested_alignment, alignment default_alignment) -> format_padding + alignment requested_alignment, alignment default_alignment) -> padding { if (target_width <= content_length) { diff --git a/libs/kstd/include/kstd/bits/format/string.hpp b/libs/kstd/include/kstd/bits/format/string.hpp index 2e7d60a..edeaed1 100644 --- a/libs/kstd/include/kstd/bits/format/string.hpp +++ b/libs/kstd/include/kstd/bits/format/string.hpp @@ -5,6 +5,7 @@ #include "context.hpp" #include "error.hpp" +#include "formatter.hpp" #include "parse_context.hpp" #include -- cgit v1.2.3 From e92343922bc8dce0c5653b83789498d4c97ade62 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 14:13:43 +0100 Subject: kstd: simplify header packaging --- libs/kstd/CMakeLists.txt | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 9b4976b..877a04e 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -17,32 +17,13 @@ target_sources("kstd" PRIVATE "src/mutex.cpp" ) +file(GLOB_RECURSE KSTD_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") + target_sources("kstd" PUBLIC FILE_SET HEADERS BASE_DIRS "include" FILES - "include/kstd/bits/format_args.hpp" - "include/kstd/bits/format_context.hpp" - "include/kstd/bits/format_parse_context.hpp" - "include/kstd/bits/format_specifiers.hpp" - "include/kstd/bits/format_string.hpp" - "include/kstd/bits/formatter.hpp" - "include/kstd/bits/print_sink.hpp" - "include/kstd/bits/shared_ptr.hpp" - "include/kstd/bits/unique_ptr.hpp" - - "include/kstd/ext/bitfield_enum" - - "include/kstd/os/error.hpp" - "include/kstd/os/print.hpp" - - "include/kstd/asm_ptr" - "include/kstd/cstring" - "include/kstd/format" - "include/kstd/memory" - "include/kstd/mutex" - "include/kstd/stack" - "include/kstd/vector" + ${KSTD_HEADERS} ) target_include_directories("kstd" PUBLIC -- cgit v1.2.3 From 8ae0f5a9a83aa58f2bd9eacfb51369b0bf966809 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 15:22:37 +0100 Subject: kstd/format: use tagged union to reduce template bloat --- kernel/kstd/print.cpp | 80 +++++++++++++++-- libs/kstd/include/kstd/bits/format/arg.hpp | 63 +++++++++++-- libs/kstd/include/kstd/bits/format/args.hpp | 100 +++++++++++++++++++-- .../include/kstd/bits/format/formatter/bool.hpp | 2 +- .../include/kstd/bits/format/formatter/cstring.hpp | 6 ++ .../kstd/bits/format/formatter/integral.hpp | 2 +- 6 files changed, 230 insertions(+), 23 deletions(-) diff --git a/kernel/kstd/print.cpp b/kernel/kstd/print.cpp index db03816..beee2e5 100644 --- a/kernel/kstd/print.cpp +++ b/kernel/kstd/print.cpp @@ -127,13 +127,81 @@ namespace kstd::os if (index < args.size()) { auto const & arg = args[index]; - if (arg.format_function) + switch (arg.type) { - arg.format_function(arg.value_pointer, parse_context, context); - } - else - { - context.push("{?}"); + case kstd::bits::format::arg_type::boolean: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.boolean, context); + break; + } + case kstd::bits::format::arg_type::character: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.character, context); + break; + } + case kstd::bits::format::arg_type::integer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.integer, context); + break; + } + case kstd::bits::format::arg_type::unsigned_integer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.unsigned_integer, context); + break; + } + case kstd::bits::format::arg_type::string_view: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.string_view, context); + break; + } + case kstd::bits::format::arg_type::c_string: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.c_string, context); + break; + } + case kstd::bits::format::arg_type::pointer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.pointer, context); + break; + } + case kstd::bits::format::arg_type::user_defined: + { + if (arg.value.user_defined.format) + { + arg.value.user_defined.format(arg.value.user_defined.pointer, parse_context, context); + } + else + { + context.push("{?}"); + } + break; + } + default: + { + context.push("{fmt-err: unknown-type}"); + break; + } } } else diff --git a/libs/kstd/include/kstd/bits/format/arg.hpp b/libs/kstd/include/kstd/bits/format/arg.hpp index 92e6431..a9a6ab5 100644 --- a/libs/kstd/include/kstd/bits/format/arg.hpp +++ b/libs/kstd/include/kstd/bits/format/arg.hpp @@ -3,24 +3,73 @@ // IWYU pragma: private, include +#include "error.hpp" #include "fwd.hpp" #include +#include +#include namespace kstd { - struct format_arg + namespace bits::format { - using format_function_type = auto(void const * value, format_parse_context & parse_context, - format_context & context) -> void; - using get_size_function_type = auto(void const * value) -> std::size_t; + enum struct arg_type : std::uint8_t + { + none, + boolean, + character, + integer, + unsigned_integer, + string_view, + c_string, + pointer, + user_defined, + }; + } // namespace bits::format - void const * value_pointer; - format_function_type * format_function; - get_size_function_type * get_size_function; + struct format_arg + { + bits::format::arg_type type{}; + union + { + bool boolean; + char character; + std::int64_t integer; + std::uint64_t unsigned_integer; + std::string_view string_view; + char const * c_string; + void const * pointer; + struct + { + void const * pointer; + auto (*format)(void const * value, format_parse_context & parse_context, format_context & context) -> void; + } user_defined; + } value{}; }; + namespace bits::format + { + constexpr auto extrat_dynamic_width(format_arg const & arg) -> std::size_t + { + if (arg.type == arg_type::unsigned_integer) + { + return static_cast(arg.value.unsigned_integer); + } + else if (arg.type == arg_type::integer) + { + if (arg.value.integer < 0) + { + error("Dynamic width cannont be negative."); + } + return static_cast(arg.value.integer); + } + error("Dynamic width argument is not an integral value."); + return 0; + } + } // namespace bits::format + } // namespace kstd #endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/args.hpp b/libs/kstd/include/kstd/bits/format/args.hpp index 5cca3ff..008cc03 100644 --- a/libs/kstd/include/kstd/bits/format/args.hpp +++ b/libs/kstd/include/kstd/bits/format/args.hpp @@ -3,14 +3,17 @@ // IWYU pragma: private, include +#include "arg.hpp" #include "context.hpp" -#include "error.hpp" #include "fwd.hpp" #include "parse_context.hpp" #include +#include #include +#include #include +#include #include namespace kstd @@ -31,17 +34,98 @@ namespace kstd } template - auto get_size_trampoline(void const * value_pointer) -> std::size_t + constexpr auto determine_arg_type() -> arg_type { - if constexpr (is_width_v) + using decay_type = std::remove_cvref_t>; + if constexpr (std::same_as) { - return static_cast(*static_cast(value_pointer)); + return arg_type::boolean; + } + else if constexpr (std::same_as) + { + return arg_type::character; + } + else if constexpr (std::integral && std::is_signed_v) + { + return arg_type::integer; + } + else if constexpr (std::integral && std::is_unsigned_v) + { + return arg_type::unsigned_integer; + } + else if constexpr (std::same_as) + { + return arg_type::string_view; + } + else if constexpr (std::same_as || std::same_as) + { + return arg_type::c_string; + } + else if constexpr (std::is_pointer_v || std::same_as) + { + if constexpr (std::same_as || std::same_as) + { + return arg_type::user_defined; + } + else + { + return arg_type::pointer; + } + } + else + { + return arg_type::user_defined; + } + } + + template + constexpr auto make_single_arg(ValueType const & value) -> format_arg + { + auto result = format_arg{}; + constexpr auto type = determine_arg_type(); + result.type = type; + + if constexpr (type == arg_type::boolean) + { + result.value.boolean = value; + } + else if constexpr (type == arg_type::character) + { + result.value.character = value; + } + else if constexpr (type == arg_type::integer) + { + result.value.integer = static_cast(value); + } + else if constexpr (type == arg_type::unsigned_integer) + { + result.value.unsigned_integer = static_cast(value); + } + else if constexpr (type == arg_type::string_view) + { + result.value.string_view = value; + } + else if constexpr (type == arg_type::c_string) + { + result.value.c_string = value; + } + else if constexpr (type == arg_type::pointer) + { + if constexpr (std::same_as, std::nullptr_t>) + { + result.value.pointer = nullptr; + } + else + { + result.value.pointer = static_cast(value); + } } else { - error("Dynamic width argument is not an integral value."); - return 0; + result.value.user_defined.pointer = &value; + result.value.user_defined.format = format_trampoline; } + return result; } } // namespace bits::format @@ -65,8 +149,8 @@ namespace kstd } else { - return format_arg_store{std::array{ - format_arg{static_cast(&args), format_trampoline, get_size_trampoline}...}}; + return format_arg_store{ + std::array{make_single_arg(args)...}}; } } diff --git a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp index b409e06..336e1b0 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp @@ -58,7 +58,7 @@ namespace kstd else if (specifiers.width_mode == bits::format::width_mode::dynamic_argument_id) { auto const & arg = context.arg(specifiers.width_value); - final_width = arg.get_size_function(arg.value_pointer); + final_width = bits::format::extrat_dynamic_width(arg); } auto padding = bits::format::calculate_format_padding(final_width, text.size(), specifiers.align, diff --git a/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp index 9afb974..bf52f2e 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp @@ -5,6 +5,7 @@ #include "../formatter.hpp" #include "string_view.hpp" +#include #include namespace kstd @@ -24,6 +25,11 @@ namespace kstd { }; + template + struct formatter : formatter // NOLINT + { + }; + } // namespace kstd #endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp index d5cd6c5..4912a44 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp @@ -71,7 +71,7 @@ namespace kstd else if (specifiers.width_mode == bits::format::width_mode::dynamic_argument_id) { auto const & arg = context.arg(specifiers.width_value); - final_width = arg.get_size_function(arg.value_pointer); + final_width = bits::format::extrat_dynamic_width(arg); } using unsigned_T = std::make_unsigned_t; -- cgit v1.2.3 From aa2b6bc208785c1d8ced8451478b14433c762896 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 16:00:14 +0100 Subject: kstd/format: fix type decay for c strings --- libs/kstd/include/kstd/bits/format/args.hpp | 5 +++-- libs/kstd/include/kstd/bits/format/formatter.hpp | 7 ++++++- libs/kstd/include/kstd/bits/format/formatter/cstring.hpp | 6 ------ libs/kstd/include/kstd/bits/format/string.hpp | 1 + 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/libs/kstd/include/kstd/bits/format/args.hpp b/libs/kstd/include/kstd/bits/format/args.hpp index 008cc03..d1586ac 100644 --- a/libs/kstd/include/kstd/bits/format/args.hpp +++ b/libs/kstd/include/kstd/bits/format/args.hpp @@ -36,7 +36,7 @@ namespace kstd template constexpr auto determine_arg_type() -> arg_type { - using decay_type = std::remove_cvref_t>; + using decay_type = std::remove_cvref_t; if constexpr (std::same_as) { return arg_type::boolean; @@ -57,7 +57,8 @@ namespace kstd { return arg_type::string_view; } - else if constexpr (std::same_as || std::same_as) + else if constexpr (std::same_as, char *> || + std::same_as, char const *>) { return arg_type::c_string; } diff --git a/libs/kstd/include/kstd/bits/format/formatter.hpp b/libs/kstd/include/kstd/bits/format/formatter.hpp index bff5f55..f391c8e 100644 --- a/libs/kstd/include/kstd/bits/format/formatter.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter.hpp @@ -7,7 +7,12 @@ namespace kstd { template - struct formatter; + struct formatter + { + formatter() = delete; + formatter(formatter const &) = delete; + auto operator=(formatter const &) -> formatter & = delete; + }; } // namespace kstd diff --git a/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp index bf52f2e..9afb974 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp @@ -5,7 +5,6 @@ #include "../formatter.hpp" #include "string_view.hpp" -#include #include namespace kstd @@ -25,11 +24,6 @@ namespace kstd { }; - template - struct formatter : formatter // NOLINT - { - }; - } // namespace kstd #endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/string.hpp b/libs/kstd/include/kstd/bits/format/string.hpp index edeaed1..40282e4 100644 --- a/libs/kstd/include/kstd/bits/format/string.hpp +++ b/libs/kstd/include/kstd/bits/format/string.hpp @@ -28,6 +28,7 @@ namespace kstd if (current_index == target_index && !found) { using decay_type = std::remove_cvref_t; + static_assert(std::is_default_constructible_v>, "Missing formatter specialization."); auto fmt = formatter{}; auto it = fmt.parse(context); -- cgit v1.2.3 From f59a839ad60e941697797a9e3a81a5098b2b945f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 16:28:22 +0100 Subject: kstd/format: support range formatting --- libs/kstd/include/kstd/bits/format/formatter.hpp | 73 ++++++++++++++++++++++ .../include/kstd/bits/format/formatter/range.hpp | 32 ++++++++++ libs/kstd/include/kstd/format | 1 + 3 files changed, 106 insertions(+) create mode 100644 libs/kstd/include/kstd/bits/format/formatter/range.hpp diff --git a/libs/kstd/include/kstd/bits/format/formatter.hpp b/libs/kstd/include/kstd/bits/format/formatter.hpp index f391c8e..096168d 100644 --- a/libs/kstd/include/kstd/bits/format/formatter.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter.hpp @@ -3,6 +3,14 @@ // IWYU pragma: private, include +#include "context.hpp" +#include "error.hpp" +#include "parse_context.hpp" + +#include +#include +#include + namespace kstd { @@ -14,6 +22,71 @@ namespace kstd auto operator=(formatter const &) -> formatter & = delete; }; + template + struct range_formatter + { + constexpr auto set_separator(std::string_view sep) -> void + { + m_separator = sep; + } + + constexpr auto set_brackets(std::string_view opening, std::string_view closing) + { + m_prefix = opening; + m_suffix = closing; + } + + constexpr auto parse(format_parse_context & context) + { + auto it = context.begin(); + auto const end = context.end(); + + if (it != end && *it == 'n') + { + set_brackets("", ""); + std::advance(it, 1); + } + + if (it != end && *it == ':') + { + std::advance(it, 1); + context.advance_to(it); + it = m_inner_formatter.parse(context); + } + + if (it != end && *it != '}') + { + bits::format::error("Invalid formate specifier for range"); + } + + return it; + } + + template + auto format(Range const & range, format_context & context) + { + context.push(m_prefix); + + auto is_first = true; + std::ranges::for_each(range, [&](auto const & element) { + if (!is_first) + { + context.push(m_separator); + } + m_inner_formatter.format(element, context); + is_first = false; + }); + + context.push(m_suffix); + } + + private: + kstd::formatter m_inner_formatter{}; + std::string_view m_separator{", "}; + std::string_view m_prefix{"["}; + std::string_view m_suffix{"]"}; + }; + } // namespace kstd #endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/range.hpp b/libs/kstd/include/kstd/bits/format/formatter/range.hpp new file mode 100644 index 0000000..54ee7fb --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/range.hpp @@ -0,0 +1,32 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_RANGE_HPP +#define KSTD_BITS_FORMAT_FORMATTER_RANGE_HPP + +#include "../formatter.hpp" + +#include +#include +#include +#include + +namespace kstd +{ + namespace bits::format + { + template + concept iterable = requires(T const & t) { + t.begin(); + t.end(); + }; + + template + concept formattable_range = iterable && !std::same_as, std::string_view> && + !std::same_as, char *> && !std::same_as, char const *>; + } // namespace bits::format + + template + struct formatter : range_formatter> + { + }; +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/format b/libs/kstd/include/kstd/format index 76aaed8..946ed69 100644 --- a/libs/kstd/include/kstd/format +++ b/libs/kstd/include/kstd/format @@ -10,6 +10,7 @@ #include "bits/format/formatter/integral.hpp" // IWYU pragma: export #include "bits/format/formatter/ordering.hpp" // IWYU pragma: export #include "bits/format/formatter/pointer.hpp" // IWYU pragma: export +#include "bits/format/formatter/range.hpp" // IWYU pragma: export #include "bits/format/formatter/string_view.hpp" // IWYU pragma: export #include "bits/format/parse_context.hpp" // IWYU pragma: export #include "bits/format/string.hpp" // IWYU pragma: export -- cgit v1.2.3 From 3e8efb0d65c32556d4a9cb603966beacfd61b29d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 16:35:46 +0100 Subject: kstd/format: add support for std::byte --- .../include/kstd/bits/format/formatter/byte.hpp | 23 ++++++++++++++++++++++ libs/kstd/include/kstd/format | 1 + 2 files changed, 24 insertions(+) create mode 100644 libs/kstd/include/kstd/bits/format/formatter/byte.hpp diff --git a/libs/kstd/include/kstd/bits/format/formatter/byte.hpp b/libs/kstd/include/kstd/bits/format/formatter/byte.hpp new file mode 100644 index 0000000..70d98f4 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/byte.hpp @@ -0,0 +1,23 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_BYTE_HPP +#define KSTD_BITS_FORMAT_FORMATTER_BYTE_HPP + +#include "../context.hpp" +#include "../formatter.hpp" +#include "integral.hpp" + +#include + +namespace kstd +{ + + template<> + struct formatter : formatter + { + auto format(std::byte value, format_context & context) const -> void + { + formatter::format(static_cast(value), context); + } + }; + +} // namespace kstd +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/format b/libs/kstd/include/kstd/format index 946ed69..adee5f8 100644 --- a/libs/kstd/include/kstd/format +++ b/libs/kstd/include/kstd/format @@ -6,6 +6,7 @@ #include "bits/format/context.hpp" // IWYU pragma: export #include "bits/format/formatter.hpp" // IWYU pragma: export #include "bits/format/formatter/bool.hpp" // IWYU pragma: export +#include "bits/format/formatter/byte.hpp" // IWYU pragma: export #include "bits/format/formatter/cstring.hpp" // IWYU pragma: export #include "bits/format/formatter/integral.hpp" // IWYU pragma: export #include "bits/format/formatter/ordering.hpp" // IWYU pragma: export -- cgit v1.2.3 From cd6bff48ab828f0a1c5b6a1a36f8eec81f0eb81f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 16:56:35 +0100 Subject: kstd/vector: fix rbegin and rend --- libs/kstd/include/kstd/vector | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 79530d2..d66c63b 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -462,13 +462,13 @@ namespace kstd //! Get a reverse iterator to the reverse beginning. [[nodiscard]] constexpr auto rbegin() noexcept -> reverse_iterator { - return empty() ? rend() : reverse_iterator{begin() + (m_size - 1)}; + return empty() ? rend() : reverse_iterator{end()}; } //! Get a reverse iterator to the reverse beginning. [[nodiscard]] constexpr auto rbegin() const noexcept -> const_reverse_iterator { - return empty() ? rend() : const_reverse_iterator{begin() + (m_size - 1)}; + return empty() ? rend() : const_reverse_iterator{end()}; } //! Get a reverse iterator to the reverse beginning. @@ -480,13 +480,13 @@ namespace kstd //! Get a reverse iterator to the reverse end. [[nodiscard]] constexpr auto rend() noexcept -> reverse_iterator { - return reverse_iterator{capacity() ? data() - 1 : nullptr}; + return reverse_iterator{begin()}; } //! Get a reverse iterator to the reverse end. [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator { - return const_reverse_iterator{capacity() ? data() - 1 : nullptr}; + return const_reverse_iterator{begin()}; } //! Get a reverse iterator to the reverse end. -- cgit v1.2.3 From 9433102e33dae5697ca71c1d9116791fa933c974 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 16:58:03 +0100 Subject: kstd/vector: add missing constexpr --- libs/kstd/include/kstd/vector | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index d66c63b..2d148e4 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -384,7 +384,7 @@ namespace kstd //! Get a reference to the first element of this vector. //! //! The behavior is undefined if this vector is empty. - [[nodiscard]] auto front() const -> const_reference + [[nodiscard]] constexpr auto front() const -> const_reference { return *begin(); } @@ -484,13 +484,13 @@ namespace kstd } //! Get a reverse iterator to the reverse end. - [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator + [[nodiscard]] constexpr auto rend() const noexcept -> const_reverse_iterator { return const_reverse_iterator{begin()}; } //! Get a reverse iterator to the reverse end. - [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator + [[nodiscard]] constexpr auto crend() const noexcept -> const_reverse_iterator { return rend(); } @@ -597,7 +597,7 @@ namespace kstd //! Remove the last element of this vector. //! //! If this vector is empty, the behavior is undefined. - auto pop_back() -> void + constexpr auto pop_back() -> void { --m_size; std::allocator_traits::destroy(m_allocator, data() + size()); -- cgit v1.2.3 From cb01bffde672cf7f4bec3a1c5b6383241181c960 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 16:59:46 +0100 Subject: kstd/vector: relax move constructor --- libs/kstd/include/kstd/vector | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 2d148e4..0a3d6b7 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -154,7 +154,7 @@ namespace kstd //! //! @param other The source vector. constexpr vector(vector && other) noexcept - : m_allocator{std::move(std::exchange(other.m_allocator, allocator_type{}))} + : m_allocator{std::move(other.m_allocator)} , m_size{std::exchange(other.m_size, size_type{})} , m_capacity(std::exchange(other.m_capacity, size_type{})) , m_data(std::exchange(other.m_data, nullptr)) -- cgit v1.2.3 From b430f23711071872ff054a1e1b30f8a028584fe4 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 17:01:04 +0100 Subject: kstd/vector: optimize clear --- libs/kstd/include/kstd/vector | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 0a3d6b7..e4979e5 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -562,10 +562,8 @@ namespace kstd //! Clear the contents of this vector. constexpr auto clear() noexcept -> void { - for (auto i = m_size; i > 0; --i) - { - pop_back(); - } + destroy_n(begin(), size()); + m_size = 0; } //! Append a given element to this vector via copy construction. -- cgit v1.2.3 From fb366b3c59941ea4c8e0a8d6e29ba0263f89bb02 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 17:09:39 +0100 Subject: kstd/vector: allow input iterators for construction --- libs/kstd/include/kstd/vector | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index e4979e5..7042ff1 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -100,7 +100,7 @@ namespace kstd //! Construct a new vector and initialize it's content by copying all elements in the given range. //! - //! @tparam InputIterator An iterator type used to describe the source range. + //! @tparam ForwardIterator An iterator type used to describe the source range. //! @param first The start of the source range. //! @param last The end of the source range. template @@ -118,6 +118,27 @@ namespace kstd } } + //! Construct a new vector and initialize it's content by copying all elements in the given range. + //! + //! @tparam InputIterator An iterator type used to describe the source range. + //! @param first The start of the source range. + //! @param last The end of the source range. + template + constexpr vector(InputIterator first, InputIterator last, + allocator_type const & allocator = + allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} + , m_size{0} + , m_capacity{0} + , m_data{} + { + while (first != last) + { + emplace_back(*first); + ++first; + } + } + //! Construct a new vector and initialize it's content by copying all elements in the given range. //! //! @@ -747,6 +768,12 @@ namespace kstd vector(ForwardIterator, ForwardIterator, Allocator = Allocator()) -> vector::value_type, Allocator>; + //! Deduction guide for vector construction from an interator pair. + template::value_type>> + vector(InputIterator, InputIterator, Allocator = Allocator()) + -> vector::value_type, Allocator>; + //! Deduction guide for vector construction from a range. template>> vector(kstd::from_range_t, Range &&, Allocator = Allocator()) -> vector, Allocator>; -- cgit v1.2.3 From 2f9e3917ef86ac0b00a517394df8a903c97770e1 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 17:16:16 +0100 Subject: kstd/vector: allow self-referential pushes Previously, calling `v.push_back(v.front())` might have resulted in undefined behavior if reallocation needed to occur. This patch provides for this and allows self-referential pushes. --- libs/kstd/include/kstd/vector | 60 ++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 7042ff1..a49572b 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -590,16 +590,42 @@ namespace kstd //! Append a given element to this vector via copy construction. constexpr auto push_back(value_type const & value) -> void { - increase_capacity_if_full(); - std::allocator_traits::construct(m_allocator, data() + size(), value); + if (m_capacity == m_size) + { + auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; + auto new_data = allocate_n(new_capacity); + std::allocator_traits::construct(m_allocator, new_data + m_size, value); + uninitialized_move_with_allocator(begin(), new_data, size()); + destroy_n(begin(), size()); + deallocate(); + m_data = new_data; + m_capacity = new_capacity; + } + else + { + std::allocator_traits::construct(m_allocator, data() + size(), value); + } ++m_size; } //! Append a given element to this vector via move construction. constexpr auto push_back(value_type && value) -> void { - increase_capacity_if_full(); - std::allocator_traits::construct(m_allocator, data() + size(), std::move(value)); + if (m_capacity == m_size) + { + auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; + auto new_data = allocate_n(new_capacity); + std::allocator_traits::construct(m_allocator, new_data + m_size, std::move(value)); + uninitialized_move_with_allocator(begin(), new_data, size()); + destroy_n(begin(), size()); + deallocate(); + m_data = new_data; + m_capacity = new_capacity; + } + else + { + std::allocator_traits::construct(m_allocator, data() + size(), std::move(value)); + } ++m_size; } @@ -607,8 +633,21 @@ namespace kstd template constexpr auto emplace_back(Args &&... args) -> reference { - increase_capacity_if_full(); - std::allocator_traits::construct(m_allocator, data() + size(), std::forward(args)...); + if (m_capacity == m_size) + { + auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; + auto new_data = allocate_n(new_capacity); + std::allocator_traits::construct(m_allocator, new_data + m_size, std::forward(args)...); + uninitialized_move_with_allocator(begin(), new_data, size()); + destroy_n(begin(), size()); + deallocate(); + m_data = new_data; + m_capacity = new_capacity; + } + else + { + std::allocator_traits::construct(m_allocator, data() + size(), std::forward(args)...); + } ++m_size; return this->back(); } @@ -675,15 +714,6 @@ namespace kstd }); } - //! Check if there is still room in this vector, and if not allocate more space. - constexpr auto increase_capacity_if_full() -> void - { - if (m_size == m_capacity) - { - reserve(m_capacity == 0U ? 1U : m_capacity * 2U); - } - } - //! Move a number of elements from one storage location to another. //! //! @param from The start of the source range. -- cgit v1.2.3 From 69b8b89542530eb7360dddd0875610f4cca9268b Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 17:34:56 +0100 Subject: x86_64/cpu: move gdt initialization code --- arch/x86_64/CMakeLists.txt | 3 + arch/x86_64/include/arch/cpu/initialization.hpp | 6 ++ arch/x86_64/kapi/system.cpp | 113 +--------------------- arch/x86_64/src/cpu/initialization.cpp | 122 ++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 111 deletions(-) create mode 100644 arch/x86_64/include/arch/cpu/initialization.hpp create mode 100644 arch/x86_64/src/cpu/initialization.cpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 4ae09ff..f053982 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -18,6 +18,9 @@ target_sources("x86_64" PRIVATE "kapi/memory.cpp" "kapi/system.cpp" + # CPU Initialization + "src/cpu/initialization.cpp" + # Low-level bootstrap "src/boot/boot32.S" "src/boot/entry64.s" diff --git a/arch/x86_64/include/arch/cpu/initialization.hpp b/arch/x86_64/include/arch/cpu/initialization.hpp new file mode 100644 index 0000000..b2ce864 --- /dev/null +++ b/arch/x86_64/include/arch/cpu/initialization.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace arch::cpu +{ + auto initialize_descriptors() -> void; +} // namespace arch::cpu diff --git a/arch/x86_64/kapi/system.cpp b/arch/x86_64/kapi/system.cpp index ca4418e..ffb6a46 100644 --- a/arch/x86_64/kapi/system.cpp +++ b/arch/x86_64/kapi/system.cpp @@ -1,122 +1,13 @@ #include "kapi/system.hpp" -#include "arch/cpu/global_descriptor_table.hpp" -#include "arch/cpu/segment_descriptor.hpp" -#include "arch/cpu/task_state_segment.hpp" - -#include - -#include -#include +#include "arch/cpu/initialization.hpp" namespace kapi::system { - namespace - { - constexpr auto gdt_null_descriptor = arch::cpu::segment_descriptor{}; - - constexpr auto gdt_kernel_code_descriptor = arch::cpu::segment_descriptor{ - .limit_low = 0xffff, - .base_low = 0, - .accessed = false, - .read_write = false, - .direction_or_conforming = false, - .executable = true, - .type = arch::cpu::segment_type::code_or_data, - .privilege_level = 0, - .present = true, - .limit_high = 0xf, - .long_mode = true, - .protected_mode = false, - .granularity = arch::cpu::segment_granularity::page, - .base_high = 0, - }; - - constexpr auto gdt_kernel_data_descriptor = arch::cpu::segment_descriptor{ - .limit_low = 0xffff, - .base_low = 0, - .accessed = false, - .read_write = true, - .direction_or_conforming = false, - .executable = false, - .type = arch::cpu::segment_type::code_or_data, - .privilege_level = 0, - .present = true, - .limit_high = 0xf, - .long_mode = false, - .protected_mode = true, - .granularity = arch::cpu::segment_granularity::page, - .base_high = 0, - }; - - constexpr auto gdt_user_code_descriptor = arch::cpu::segment_descriptor{ - .limit_low = 0xffff, - .base_low = 0, - .accessed = false, - .read_write = false, - .direction_or_conforming = false, - .executable = true, - .type = arch::cpu::segment_type::code_or_data, - .privilege_level = 3, - .present = true, - .limit_high = 0xf, - .long_mode = true, - .protected_mode = false, - .granularity = arch::cpu::segment_granularity::page, - .base_high = 0, - }; - - constexpr auto gdt_user_data_descriptor = arch::cpu::segment_descriptor{ - .limit_low = 0xffff, - .base_low = 0, - .accessed = false, - .read_write = true, - .direction_or_conforming = false, - .executable = false, - .type = arch::cpu::segment_type::code_or_data, - .privilege_level = 3, - .present = true, - .limit_high = 0xf, - .long_mode = false, - .protected_mode = false, - .granularity = arch::cpu::segment_granularity::page, - .base_high = 0, - }; - } // namespace - auto memory_initialized() -> void { - auto static tss = arch::cpu::task_state_segment{}; - auto static tss_descriptor = arch::cpu::system_segment_descriptor{ - { - .limit_low = (sizeof(tss) - 1) & 0xffff, // NOLINT(readability-magic-numbers) - .base_low = std::bit_cast(&tss) & 0xffffff, // NOLINT(readability-magic-numbers) - .accessed = false, - .read_write = false, - .direction_or_conforming = false, - .executable = false, - .type = arch::cpu::segment_type::system, - .privilege_level = 0, - .present = true, - .limit_high = ((sizeof(tss) - 1) >> 16) & 0xf, // NOLINT(readability-magic-numbers) - .long_mode = false, - .protected_mode = false, - .granularity = arch::cpu::segment_granularity::byte, - .base_high = (std::bit_cast(&tss) >> 24) & 0xff, // NOLINT(readability-magic-numbers) - }, - (std::bit_cast(&tss) >> 32) & 0xffff'ffff, // NOLINT(readability-magic-numbers) - }; - - auto static gdt = arch::cpu::global_descriptor_table{ - gdt_null_descriptor, gdt_kernel_code_descriptor, gdt_kernel_data_descriptor, - gdt_user_code_descriptor, gdt_user_data_descriptor, tss_descriptor, - }; - - kstd::println("[x86_64:SYS] Reloading Global Descriptor Table."); - gdt.load(1, 2); - - kstd::println("[x86_64:SYS] TODO: initialize Interrupt Descriptor Table."); + arch::cpu::initialize_descriptors(); } } // namespace kapi::system \ No newline at end of file diff --git a/arch/x86_64/src/cpu/initialization.cpp b/arch/x86_64/src/cpu/initialization.cpp new file mode 100644 index 0000000..aae4f1f --- /dev/null +++ b/arch/x86_64/src/cpu/initialization.cpp @@ -0,0 +1,122 @@ +#include "arch/cpu/initialization.hpp" + +#include "arch/cpu/global_descriptor_table.hpp" +#include "arch/cpu/segment_descriptor.hpp" +#include "arch/cpu/task_state_segment.hpp" + +#include + +#include +#include + +namespace arch::cpu +{ + + namespace + { + constexpr auto gdt_null_descriptor = segment_descriptor{}; + + constexpr auto gdt_kernel_code_descriptor = segment_descriptor{ + .limit_low = 0xffff, + .base_low = 0, + .accessed = false, + .read_write = false, + .direction_or_conforming = false, + .executable = true, + .type = segment_type::code_or_data, + .privilege_level = 0, + .present = true, + .limit_high = 0xf, + .long_mode = true, + .protected_mode = false, + .granularity = segment_granularity::page, + .base_high = 0, + }; + + constexpr auto gdt_kernel_data_descriptor = segment_descriptor{ + .limit_low = 0xffff, + .base_low = 0, + .accessed = false, + .read_write = true, + .direction_or_conforming = false, + .executable = false, + .type = segment_type::code_or_data, + .privilege_level = 0, + .present = true, + .limit_high = 0xf, + .long_mode = false, + .protected_mode = true, + .granularity = segment_granularity::page, + .base_high = 0, + }; + + constexpr auto gdt_user_code_descriptor = segment_descriptor{ + .limit_low = 0xffff, + .base_low = 0, + .accessed = false, + .read_write = false, + .direction_or_conforming = false, + .executable = true, + .type = segment_type::code_or_data, + .privilege_level = 3, + .present = true, + .limit_high = 0xf, + .long_mode = true, + .protected_mode = false, + .granularity = segment_granularity::page, + .base_high = 0, + }; + + constexpr auto gdt_user_data_descriptor = segment_descriptor{ + .limit_low = 0xffff, + .base_low = 0, + .accessed = false, + .read_write = true, + .direction_or_conforming = false, + .executable = false, + .type = segment_type::code_or_data, + .privilege_level = 3, + .present = true, + .limit_high = 0xf, + .long_mode = false, + .protected_mode = false, + .granularity = segment_granularity::page, + .base_high = 0, + }; + } // namespace + + auto initialize_descriptors() -> void + { + auto static tss = task_state_segment{}; + auto static tss_descriptor = system_segment_descriptor{ + { + .limit_low = (sizeof(tss) - 1) & 0xffff, // NOLINT(readability-magic-numbers) + .base_low = std::bit_cast(&tss) & 0xffffff, // NOLINT(readability-magic-numbers) + .accessed = false, + .read_write = false, + .direction_or_conforming = false, + .executable = false, + .type = segment_type::system, + .privilege_level = 0, + .present = true, + .limit_high = ((sizeof(tss) - 1) >> 16) & 0xf, // NOLINT(readability-magic-numbers) + .long_mode = false, + .protected_mode = false, + .granularity = segment_granularity::byte, + .base_high = (std::bit_cast(&tss) >> 24) & 0xff, // NOLINT(readability-magic-numbers) + }, + (std::bit_cast(&tss) >> 32) & 0xffff'ffff, // NOLINT(readability-magic-numbers) + }; + + auto static gdt = global_descriptor_table{ + gdt_null_descriptor, gdt_kernel_code_descriptor, gdt_kernel_data_descriptor, + gdt_user_code_descriptor, gdt_user_data_descriptor, tss_descriptor, + }; + + kstd::println("[x86_64:SYS] Reloading Global Descriptor Table."); + gdt.load(1, 2); + + kstd::println("[x86_64:SYS] TODO: initialize Interrupt Descriptor Table."); + } + +} // namespace arch::cpu -- cgit v1.2.3 From 52bc2d4105ac0d10d10831d470e9d0212dbb2b4d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 17:39:00 +0100 Subject: libs: fix header globs --- libs/elf/CMakeLists.txt | 6 +++--- libs/kstd/CMakeLists.txt | 2 +- libs/multiboot2/CMakeLists.txt | 10 +++------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/libs/elf/CMakeLists.txt b/libs/elf/CMakeLists.txt index 66e59ee..f254094 100644 --- a/libs/elf/CMakeLists.txt +++ b/libs/elf/CMakeLists.txt @@ -1,13 +1,13 @@ add_library("elf" INTERFACE) add_library("libs::elf" ALIAS "elf") +file(GLOB_RECURSE ELF_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") + target_sources("elf" INTERFACE FILE_SET HEADERS BASE_DIRS "include" FILES - "include/elf/format.hpp" - "include/elf/section_header.hpp" - + ${ELF_HEADERS} ) target_include_directories("elf" INTERFACE diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 877a04e..b0c9c63 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -17,7 +17,7 @@ target_sources("kstd" PRIVATE "src/mutex.cpp" ) -file(GLOB_RECURSE KSTD_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") +file(GLOB_RECURSE KSTD_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/kstd/*") target_sources("kstd" PUBLIC FILE_SET HEADERS diff --git a/libs/multiboot2/CMakeLists.txt b/libs/multiboot2/CMakeLists.txt index b306b66..7384a3d 100644 --- a/libs/multiboot2/CMakeLists.txt +++ b/libs/multiboot2/CMakeLists.txt @@ -1,17 +1,13 @@ add_library("multiboot2" INTERFACE) add_library("libs::multiboot2" ALIAS "multiboot2") +file(GLOB_RECURSE MULTIBOOT2_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") + target_sources("multiboot2" INTERFACE FILE_SET HEADERS BASE_DIRS "include" FILES - "include/multiboot2/constants.hpp" - "include/multiboot2/information.hpp" - - "include/multiboot2/impl/data.hpp" - "include/multiboot2/impl/ids.hpp" - "include/multiboot2/impl/iterator.hpp" - "include/multiboot2/impl/tag.hpp" + ${MULTIBOOT2_HEADERS} ) target_include_directories("multiboot2" INTERFACE -- cgit v1.2.3 From 96f1511dbe2e80223732bcbef8068c3d5a330cee Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 18:08:41 +0100 Subject: kstd/vector: add missing constexpr clang-tidy is not happy about constexpr memory allocation except through the blessed std::allocator::allocate though. So for now we can't use it since it will break the build when linting is enabled. --- libs/kstd/include/kstd/vector | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index a49572b..b2cce0b 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -50,7 +50,7 @@ namespace kstd using const_reverse_iterator = std::reverse_iterator; //! Construct a new, empty vector. - vector() noexcept(std::is_nothrow_default_constructible_v) + constexpr vector() noexcept(std::is_nothrow_default_constructible_v) : vector(allocator_type{}) {} @@ -697,10 +697,13 @@ namespace kstd //! Release the memory of this vector. constexpr auto deallocate() { - std::allocator_traits::deallocate(m_allocator, m_data, m_capacity); - m_capacity = 0; - m_size = 0; - m_data = nullptr; + if (m_data) + { + std::allocator_traits::deallocate(m_allocator, m_data, m_capacity); + m_capacity = 0; + m_size = 0; + m_data = nullptr; + } } //! Destroy a number of elements in this vector. -- cgit v1.2.3 From dd2dc3ef9a5318a0f7c7c35be59759ab08adc3dc Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 20 Mar 2026 21:48:30 +0100 Subject: x86_64/cpu: implement basic interrupt handling --- .vscode/settings.json | 1 + arch/x86_64/CMakeLists.txt | 2 + arch/x86_64/include/arch/cpu/interrupts.hpp | 57 +++++++++++++- arch/x86_64/src/cpu/initialization.cpp | 51 ++++++++----- arch/x86_64/src/cpu/interrupt_stubs.S | 112 ++++++++++++++++++++++++++++ arch/x86_64/src/cpu/interrupts.cpp | 94 +++++++++++++++++++++++ 6 files changed, 295 insertions(+), 22 deletions(-) create mode 100644 arch/x86_64/src/cpu/interrupt_stubs.S create mode 100644 arch/x86_64/src/cpu/interrupts.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 321f765..1ab19f2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -33,6 +33,7 @@ "crtp", "efer", "functors", + "idtr", "initializable", "interprocedural", "invlpg", diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index f053982..89d9bc0 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -20,6 +20,8 @@ target_sources("x86_64" PRIVATE # CPU Initialization "src/cpu/initialization.cpp" + "src/cpu/interrupts.cpp" + "src/cpu/interrupt_stubs.S" # Low-level bootstrap "src/boot/boot32.S" diff --git a/arch/x86_64/include/arch/cpu/interrupts.hpp b/arch/x86_64/include/arch/cpu/interrupts.hpp index 92c5824..19358ac 100644 --- a/arch/x86_64/include/arch/cpu/interrupts.hpp +++ b/arch/x86_64/include/arch/cpu/interrupts.hpp @@ -30,7 +30,7 @@ namespace arch::cpu //! Reserved std::uint8_t : 5; //! The type of this gate. - gate_type gate_type : 4; + enum gate_type gate_type : 4; //! Reserved std::uint8_t : 1; //! The privilege level required to enter through this gate. @@ -48,14 +48,67 @@ namespace arch::cpu static_assert(sizeof(gate_descriptor) == 2 * sizeof(std::uint64_t)); static_assert(std::is_aggregate_v); + //! The stack frame as established by the low-level assembly interrupt stubs. + //! + //! @note The layout of this struct is reverse than what would reasonably be expected. The reason for this reversal is + //! that fact that it represents a stack frame. Stack frames on x86_64 grow towards lower addresses, meaning the first + //! item on the stack is the last item in C++ memory layout order. + struct interrupt_frame + { + struct + { + std::uint64_t r15; + std::uint64_t r14; + std::uint64_t r13; + std::uint64_t r12; + std::uint64_t r11; + std::uint64_t r10; + std::uint64_t r9; + std::uint64_t r8; + std::uint64_t rdi; + std::uint64_t rsi; + std::uint64_t rbp; + std::uint64_t rdx; + std::uint64_t rcx; + std::uint64_t rbx; + std::uint64_t rax; + } handler_saved; + + struct + { + std::uint64_t number; + std::uint64_t error_code; + } interrupt; + + struct + { + std::uint64_t rip; + std::uint64_t cs; + std::uint64_t rflags; + std::uint64_t rsp; + std::uint64_t ss; + } cpu_saved; + }; + struct interrupt_descriptor_table { - interrupt_descriptor_table(); + interrupt_descriptor_table() noexcept; + + auto load() const -> void; private: std::array m_descriptors{}; }; + struct [[gnu::packed]] interrupt_descriptor_table_register + { + std::uint16_t limit; + gate_descriptor const * base; + + auto load() const -> void; + auto static read() -> interrupt_descriptor_table_register; + }; + } // namespace arch::cpu #endif \ No newline at end of file diff --git a/arch/x86_64/src/cpu/initialization.cpp b/arch/x86_64/src/cpu/initialization.cpp index aae4f1f..214687c 100644 --- a/arch/x86_64/src/cpu/initialization.cpp +++ b/arch/x86_64/src/cpu/initialization.cpp @@ -1,6 +1,7 @@ #include "arch/cpu/initialization.hpp" #include "arch/cpu/global_descriptor_table.hpp" +#include "arch/cpu/interrupts.hpp" #include "arch/cpu/segment_descriptor.hpp" #include "arch/cpu/task_state_segment.hpp" @@ -83,30 +84,38 @@ namespace arch::cpu .granularity = segment_granularity::page, .base_high = 0, }; + + constexpr auto make_tss_descriptor(task_state_segment const * tss_ptr) -> system_segment_descriptor + { + auto const address = std::bit_cast(tss_ptr); + auto const limit = sizeof(task_state_segment) - 1; + + return system_segment_descriptor{ + { + .limit_low = limit & 0xffff, // NOLINT(readability-magic-numbers) + .base_low = address & 0xffffff, // NOLINT(readability-magic-numbers) + .accessed = false, + .read_write = false, + .direction_or_conforming = false, + .executable = false, + .type = segment_type::system, + .privilege_level = 0, + .present = true, + .limit_high = (limit >> 16) & 0xf, // NOLINT(readability-magic-numbers) + .long_mode = false, + .protected_mode = false, + .granularity = segment_granularity::byte, + .base_high = (address >> 24) & 0xff, // NOLINT(readability-magic-numbers) + }, + (address >> 32) & 0xffff'ffff, // NOLINT(readability-magic-numbers) + }; + } } // namespace auto initialize_descriptors() -> void { auto static tss = task_state_segment{}; - auto static tss_descriptor = system_segment_descriptor{ - { - .limit_low = (sizeof(tss) - 1) & 0xffff, // NOLINT(readability-magic-numbers) - .base_low = std::bit_cast(&tss) & 0xffffff, // NOLINT(readability-magic-numbers) - .accessed = false, - .read_write = false, - .direction_or_conforming = false, - .executable = false, - .type = segment_type::system, - .privilege_level = 0, - .present = true, - .limit_high = ((sizeof(tss) - 1) >> 16) & 0xf, // NOLINT(readability-magic-numbers) - .long_mode = false, - .protected_mode = false, - .granularity = segment_granularity::byte, - .base_high = (std::bit_cast(&tss) >> 24) & 0xff, // NOLINT(readability-magic-numbers) - }, - (std::bit_cast(&tss) >> 32) & 0xffff'ffff, // NOLINT(readability-magic-numbers) - }; + auto static tss_descriptor = make_tss_descriptor(&tss); auto static gdt = global_descriptor_table{ gdt_null_descriptor, gdt_kernel_code_descriptor, gdt_kernel_data_descriptor, @@ -116,7 +125,9 @@ namespace arch::cpu kstd::println("[x86_64:SYS] Reloading Global Descriptor Table."); gdt.load(1, 2); - kstd::println("[x86_64:SYS] TODO: initialize Interrupt Descriptor Table."); + kstd::println("[x86_64:SYS] Initializing Interrupt Descriptor Table."); + auto static idt = interrupt_descriptor_table{}; + idt.load(); } } // namespace arch::cpu diff --git a/arch/x86_64/src/cpu/interrupt_stubs.S b/arch/x86_64/src/cpu/interrupt_stubs.S new file mode 100644 index 0000000..e59bdd2 --- /dev/null +++ b/arch/x86_64/src/cpu/interrupt_stubs.S @@ -0,0 +1,112 @@ +.altmacro + +.macro ISR_WITHOUT_ERROR_CODE vector + .global isr\vector + isr\vector: + pushq $0 + pushq $\vector + jmp common_interrupt_handler +.endm + +.macro ISR_WITH_ERROR_CODE vector + .global isr\vector + isr\vector: + pushq $\vector + jmp common_interrupt_handler +.endm + +.macro ISR_TABLE_ENTRY vector + .quad isr\vector +.endm + +.section .rodata +.global isr_stub_table +.align 16 + +isr_stub_table: +.set i, 0 +.rept 256 + ISR_TABLE_ENTRY %i + .set i, i + 1 +.endr + + +.section .text + +common_interrupt_handler: + push %rax + push %rbx + push %rcx + push %rdx + push %rbp + push %rsi + push %rdi + push %r8 + push %r9 + push %r10 + push %r11 + push %r12 + push %r13 + push %r14 + push %r15 + + mov %rsp, %rdi + call interrupt_dispatch + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %r11 + pop %r10 + pop %r9 + pop %r8 + pop %rdi + pop %rsi + pop %rbp + pop %rdx + pop %rcx + pop %rbx + pop %rax + + add $16, %rsp + iretq + +ISR_WITHOUT_ERROR_CODE 0 +ISR_WITHOUT_ERROR_CODE 1 +ISR_WITHOUT_ERROR_CODE 2 +ISR_WITHOUT_ERROR_CODE 3 +ISR_WITHOUT_ERROR_CODE 4 +ISR_WITHOUT_ERROR_CODE 5 +ISR_WITHOUT_ERROR_CODE 6 +ISR_WITHOUT_ERROR_CODE 7 +ISR_WITH_ERROR_CODE 8 +ISR_WITHOUT_ERROR_CODE 9 +ISR_WITH_ERROR_CODE 10 +ISR_WITH_ERROR_CODE 11 +ISR_WITH_ERROR_CODE 12 +ISR_WITH_ERROR_CODE 13 +ISR_WITH_ERROR_CODE 14 +ISR_WITHOUT_ERROR_CODE 15 +ISR_WITHOUT_ERROR_CODE 16 +ISR_WITH_ERROR_CODE 17 +ISR_WITHOUT_ERROR_CODE 18 +ISR_WITHOUT_ERROR_CODE 19 +ISR_WITHOUT_ERROR_CODE 20 +ISR_WITH_ERROR_CODE 21 +ISR_WITHOUT_ERROR_CODE 22 +ISR_WITHOUT_ERROR_CODE 23 +ISR_WITHOUT_ERROR_CODE 24 +ISR_WITHOUT_ERROR_CODE 25 +ISR_WITHOUT_ERROR_CODE 26 +ISR_WITHOUT_ERROR_CODE 27 +ISR_WITHOUT_ERROR_CODE 28 +ISR_WITH_ERROR_CODE 29 +ISR_WITH_ERROR_CODE 30 +ISR_WITHOUT_ERROR_CODE 31 + +.set i, 32 +.rept 256 - 32 + ISR_WITHOUT_ERROR_CODE %i + .set i, i + 1 +.endr diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp new file mode 100644 index 0000000..08469c0 --- /dev/null +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -0,0 +1,94 @@ +#include "arch/cpu/interrupts.hpp" + +#include "kapi/cpu.hpp" + +#include "arch/cpu/segment_selector.hpp" + +#include + +#include + +namespace arch::cpu +{ + + namespace + { + constexpr auto isr_code_PF = 14; + + auto handle_page_fault(interrupt_frame * frame) -> void + { + auto fault_address = std::uintptr_t{}; + asm volatile("mov %%cr2, %0" : "=r"(fault_address)); + + auto const present = (frame->interrupt.error_code & 0x1) != 0; + auto const write = (frame->interrupt.error_code & 0x2) != 0; + auto const user = (frame->interrupt.error_code & 0x4) != 0; + + kstd::println(kstd::print_sink::stderr, "[x86_64:MMU] PAGE FAULT!"); + kstd::println(kstd::print_sink::stderr, "\tFault address: 0x{:x}", fault_address); + kstd::println(kstd::print_sink::stderr, "\tPresent: {}", present); + kstd::println(kstd::print_sink::stderr, "\tWrite: {}", write); + kstd::println(kstd::print_sink::stderr, "\tUser: {}", user); + kstd::println(kstd::print_sink::stderr, "\tRIP: {:#018x}", frame->cpu_saved.rip); + kstd::println(kstd::print_sink::stderr, "\tHalting the system now!"); + + kapi::cpu::halt(); + } + } // namespace + + extern "C" + { + extern std::uintptr_t const isr_stub_table[256]; + + auto interrupt_dispatch(interrupt_frame * frame) -> void + { + switch (frame->interrupt.number) + { + case isr_code_PF: + handle_page_fault(frame); + break; + default: + kstd::println(kstd::print_sink::stderr, "[x86_64:SYS] Unhandled interrupt {} received with code {}", + frame->interrupt.number, frame->interrupt.error_code); + kapi::cpu::halt(); + } + } + } + + interrupt_descriptor_table::interrupt_descriptor_table() noexcept + { + for (auto i = 0uz; i < 256; ++i) + { + m_descriptors[i] = gate_descriptor{ + .offset_low = static_cast(isr_stub_table[i] & 0xffff), // NOLINT(readability-magic-numbers) + .m_code_segment = segment_selector{0, false, 1}, + .interrupt_stack_table_selector = 0, + .gate_type = gate_type::interrupt_gate, + .descriptor_privilege_level = 0, + .present = true, + .offset_middle = + static_cast((isr_stub_table[i] >> 16) & 0xffff), // NOLINT(readability-magic-numbers) + .offset_high = + static_cast((isr_stub_table[i] >> 32) & 0xffff'ffff), // NOLINT(readability-magic-numbers) + }; + } + } + + auto interrupt_descriptor_table::load() const -> void + { + interrupt_descriptor_table_register{.limit = sizeof(m_descriptors) - 1, .base = m_descriptors.data()}.load(); + } + + auto interrupt_descriptor_table_register::load() const -> void + { + asm volatile("lidt %0" : : "m"(*this)); + } + + auto interrupt_descriptor_table_register::read() -> interrupt_descriptor_table_register + { + interrupt_descriptor_table_register idtr{}; + asm volatile("sidt %0" : : "m"(idtr)); + return idtr; + } + +} // namespace arch::cpu \ No newline at end of file -- cgit v1.2.3 From eb8074e9003034ef2186b62fc66b1073455be5de Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 21 Mar 2026 08:48:26 +0100 Subject: x86_64/cpu: fixup 8259 interrupts --- arch/x86_64/include/arch/cpu/initialization.hpp | 3 ++ arch/x86_64/include/arch/cpu/interrupts.hpp | 3 ++ arch/x86_64/kapi/system.cpp | 3 ++ arch/x86_64/src/cpu/initialization.cpp | 37 +++++++++++++++++++++++++ arch/x86_64/src/cpu/interrupts.cpp | 10 +++++++ 5 files changed, 56 insertions(+) diff --git a/arch/x86_64/include/arch/cpu/initialization.hpp b/arch/x86_64/include/arch/cpu/initialization.hpp index b2ce864..71186d4 100644 --- a/arch/x86_64/include/arch/cpu/initialization.hpp +++ b/arch/x86_64/include/arch/cpu/initialization.hpp @@ -3,4 +3,7 @@ namespace arch::cpu { auto initialize_descriptors() -> void; + + auto initialize_legacy_interrupts() -> void; + } // namespace arch::cpu diff --git a/arch/x86_64/include/arch/cpu/interrupts.hpp b/arch/x86_64/include/arch/cpu/interrupts.hpp index 19358ac..8f156a4 100644 --- a/arch/x86_64/include/arch/cpu/interrupts.hpp +++ b/arch/x86_64/include/arch/cpu/interrupts.hpp @@ -109,6 +109,9 @@ namespace arch::cpu auto static read() -> interrupt_descriptor_table_register; }; + auto enable_interrupts() -> void; + auto disable_interrupts() -> void; + } // namespace arch::cpu #endif \ No newline at end of file diff --git a/arch/x86_64/kapi/system.cpp b/arch/x86_64/kapi/system.cpp index ffb6a46..301169f 100644 --- a/arch/x86_64/kapi/system.cpp +++ b/arch/x86_64/kapi/system.cpp @@ -1,6 +1,7 @@ #include "kapi/system.hpp" #include "arch/cpu/initialization.hpp" +#include "arch/cpu/interrupts.hpp" namespace kapi::system { @@ -8,6 +9,8 @@ namespace kapi::system auto memory_initialized() -> void { arch::cpu::initialize_descriptors(); + arch::cpu::initialize_legacy_interrupts(); + arch::cpu::enable_interrupts(); } } // namespace kapi::system \ No newline at end of file diff --git a/arch/x86_64/src/cpu/initialization.cpp b/arch/x86_64/src/cpu/initialization.cpp index 214687c..85da38d 100644 --- a/arch/x86_64/src/cpu/initialization.cpp +++ b/arch/x86_64/src/cpu/initialization.cpp @@ -4,6 +4,7 @@ #include "arch/cpu/interrupts.hpp" #include "arch/cpu/segment_descriptor.hpp" #include "arch/cpu/task_state_segment.hpp" +#include "arch/device_io/port_io.hpp" #include @@ -130,4 +131,40 @@ namespace arch::cpu idt.load(); } + auto initialize_legacy_interrupts() -> void + { + using pic_master_control_port = io::port<0x20, std::uint8_t, io::port_read, io::port_write>; + using pic_master_data_port = io::port<0x21, std::uint8_t, io::port_read, io::port_write>; + using pic_slave_control_port = io::port<0xa0, std::uint8_t, io::port_read, io::port_write>; + using pic_slave_data_port = io::port<0xa1, std::uint8_t, io::port_read, io::port_write>; + + constexpr auto pic_init_command = std::uint8_t{0x11}; + constexpr auto pic_master_offset = std::uint8_t{0x20}; + constexpr auto pic_slave_offset = std::uint8_t{0x28}; + constexpr auto pic_cascade_address = std::uint8_t{0x04}; + constexpr auto pic_cascade_slave_identity = std::uint8_t{0x02}; + constexpr auto pic_use_8086_mode = std::uint8_t{0x01}; + constexpr auto pic_master_mask = std::uint8_t{0xfb}; + constexpr auto pic_slave_mask = std::uint8_t{0xff}; + constexpr auto pic_timer_mask = std::uint8_t{0x01}; + + pic_master_control_port::write(pic_init_command); + pic_slave_control_port::write(pic_init_command); + + pic_master_data_port::write(pic_master_offset); + pic_slave_data_port::write(pic_slave_offset); + + pic_master_data_port::write(pic_cascade_address); + pic_slave_data_port::write(pic_cascade_slave_identity); + + pic_master_data_port::write(pic_use_8086_mode); + pic_slave_data_port::write(pic_use_8086_mode); + + pic_master_data_port::write(pic_master_mask); + pic_slave_data_port::write(pic_slave_mask); + + auto const current_master_mask = pic_master_data_port::read(); + pic_master_data_port::write(current_master_mask | pic_timer_mask); + } + } // namespace arch::cpu diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 08469c0..19cf6f4 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -91,4 +91,14 @@ namespace arch::cpu return idtr; } + auto enable_interrupts() -> void + { + asm volatile("sti"); + } + + auto disable_interrupts() -> void + { + asm volatile("cli"); + } + } // namespace arch::cpu \ No newline at end of file -- cgit v1.2.3 From 6a392e8e40f163470d7fb12e0846f2ec7bdee61a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 21 Mar 2026 09:02:12 +0100 Subject: x86_64/cpu: ignore 8259 interrupts --- arch/x86_64/src/cpu/initialization.cpp | 8 ++------ arch/x86_64/src/cpu/interrupts.cpp | 20 ++++++++++++++++++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/arch/x86_64/src/cpu/initialization.cpp b/arch/x86_64/src/cpu/initialization.cpp index 85da38d..5f4703d 100644 --- a/arch/x86_64/src/cpu/initialization.cpp +++ b/arch/x86_64/src/cpu/initialization.cpp @@ -144,9 +144,8 @@ namespace arch::cpu constexpr auto pic_cascade_address = std::uint8_t{0x04}; constexpr auto pic_cascade_slave_identity = std::uint8_t{0x02}; constexpr auto pic_use_8086_mode = std::uint8_t{0x01}; - constexpr auto pic_master_mask = std::uint8_t{0xfb}; - constexpr auto pic_slave_mask = std::uint8_t{0xff}; - constexpr auto pic_timer_mask = std::uint8_t{0x01}; + constexpr auto pic_master_mask = std::uint8_t{0x00}; + constexpr auto pic_slave_mask = std::uint8_t{0x00}; pic_master_control_port::write(pic_init_command); pic_slave_control_port::write(pic_init_command); @@ -162,9 +161,6 @@ namespace arch::cpu pic_master_data_port::write(pic_master_mask); pic_slave_data_port::write(pic_slave_mask); - - auto const current_master_mask = pic_master_data_port::read(); - pic_master_data_port::write(current_master_mask | pic_timer_mask); } } // namespace arch::cpu diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 19cf6f4..2eec026 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -13,7 +13,11 @@ namespace arch::cpu namespace { - constexpr auto isr_code_PF = 14; + constexpr auto isr_number_page_fault = 0x0e; + constexpr auto isr_number_legacy_start = 0x20; + constexpr auto isr_number_legacy_end = 0x29; + constexpr auto isr_number_cascade_start = 0x2c; + constexpr auto isr_number_cascade_end = 0x2f; auto handle_page_fault(interrupt_frame * frame) -> void { @@ -34,6 +38,11 @@ namespace arch::cpu kapi::cpu::halt(); } + + auto handle_legacy_interrupt(interrupt_frame * frame) -> void + { + kstd::println("[x86_64:SYS] Ignoring 8259 legacy interrupt {}", frame->interrupt.number); + } } // namespace extern "C" @@ -42,9 +51,16 @@ namespace arch::cpu auto interrupt_dispatch(interrupt_frame * frame) -> void { + if ((frame->interrupt.number >= isr_number_legacy_start && frame->interrupt.number <= isr_number_legacy_end) || + (frame->interrupt.number >= isr_number_cascade_start && frame->interrupt.number <= isr_number_cascade_end)) + { + handle_legacy_interrupt(frame); + return; + } + switch (frame->interrupt.number) { - case isr_code_PF: + case isr_number_page_fault: handle_page_fault(frame); break; default: -- cgit v1.2.3 From cceaf717405059e8b02132d7c33f9fe3b2645b56 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 21 Mar 2026 09:06:05 +0100 Subject: x86_64/cpu: log interrupt data in hex --- arch/x86_64/src/cpu/interrupts.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 2eec026..466389d 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -29,7 +29,7 @@ namespace arch::cpu auto const user = (frame->interrupt.error_code & 0x4) != 0; kstd::println(kstd::print_sink::stderr, "[x86_64:MMU] PAGE FAULT!"); - kstd::println(kstd::print_sink::stderr, "\tFault address: 0x{:x}", fault_address); + kstd::println(kstd::print_sink::stderr, "\tFault address: {:#018x}", fault_address); kstd::println(kstd::print_sink::stderr, "\tPresent: {}", present); kstd::println(kstd::print_sink::stderr, "\tWrite: {}", write); kstd::println(kstd::print_sink::stderr, "\tUser: {}", user); @@ -41,7 +41,7 @@ namespace arch::cpu auto handle_legacy_interrupt(interrupt_frame * frame) -> void { - kstd::println("[x86_64:SYS] Ignoring 8259 legacy interrupt {}", frame->interrupt.number); + kstd::println("[x86_64:SYS] Ignoring 8259 legacy interrupt {:#04x}", frame->interrupt.number); } } // namespace @@ -64,7 +64,7 @@ namespace arch::cpu handle_page_fault(frame); break; default: - kstd::println(kstd::print_sink::stderr, "[x86_64:SYS] Unhandled interrupt {} received with code {}", + kstd::println(kstd::print_sink::stderr, "[x86_64:SYS] Unhandled interrupt {:#04x} received with code {:#04x}", frame->interrupt.number, frame->interrupt.error_code); kapi::cpu::halt(); } -- cgit v1.2.3 From c9ce8625dd80f701e280f90cb30c30f8663473e9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 21 Mar 2026 10:13:17 +0100 Subject: x86_64/cpu: fixup 8259 interrupt handling We now mask the timer interrupt and ensure we are informing the PIC about us having handled the interrupt. --- arch/x86_64/include/arch/cpu/legacy_pic.hpp | 19 +++++++++++++++++++ arch/x86_64/src/cpu/initialization.cpp | 12 +++++------- arch/x86_64/src/cpu/interrupts.cpp | 4 ++++ 3 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 arch/x86_64/include/arch/cpu/legacy_pic.hpp diff --git a/arch/x86_64/include/arch/cpu/legacy_pic.hpp b/arch/x86_64/include/arch/cpu/legacy_pic.hpp new file mode 100644 index 0000000..9f53d86 --- /dev/null +++ b/arch/x86_64/include/arch/cpu/legacy_pic.hpp @@ -0,0 +1,19 @@ +#ifndef TEACHOS_X86_64_CPU_LEGACY_PIC_HPP +#define TEACHOS_X86_64_CPU_LEGACY_PIC_HPP + +#include "arch/device_io/port_io.hpp" + +#include + +namespace arch::cpu +{ + using pic_master_control_port = io::port<0x20, std::uint8_t, io::port_read, io::port_write>; + using pic_master_data_port = io::port<0x21, std::uint8_t, io::port_read, io::port_write>; + using pic_slave_control_port = io::port<0xa0, std::uint8_t, io::port_read, io::port_write>; + using pic_slave_data_port = io::port<0xa1, std::uint8_t, io::port_read, io::port_write>; + + constexpr auto pic_end_of_interrupt = std::uint8_t{0x20}; + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/src/cpu/initialization.cpp b/arch/x86_64/src/cpu/initialization.cpp index 5f4703d..878fa07 100644 --- a/arch/x86_64/src/cpu/initialization.cpp +++ b/arch/x86_64/src/cpu/initialization.cpp @@ -2,9 +2,9 @@ #include "arch/cpu/global_descriptor_table.hpp" #include "arch/cpu/interrupts.hpp" +#include "arch/cpu/legacy_pic.hpp" #include "arch/cpu/segment_descriptor.hpp" #include "arch/cpu/task_state_segment.hpp" -#include "arch/device_io/port_io.hpp" #include @@ -133,18 +133,13 @@ namespace arch::cpu auto initialize_legacy_interrupts() -> void { - using pic_master_control_port = io::port<0x20, std::uint8_t, io::port_read, io::port_write>; - using pic_master_data_port = io::port<0x21, std::uint8_t, io::port_read, io::port_write>; - using pic_slave_control_port = io::port<0xa0, std::uint8_t, io::port_read, io::port_write>; - using pic_slave_data_port = io::port<0xa1, std::uint8_t, io::port_read, io::port_write>; - constexpr auto pic_init_command = std::uint8_t{0x11}; constexpr auto pic_master_offset = std::uint8_t{0x20}; constexpr auto pic_slave_offset = std::uint8_t{0x28}; constexpr auto pic_cascade_address = std::uint8_t{0x04}; constexpr auto pic_cascade_slave_identity = std::uint8_t{0x02}; constexpr auto pic_use_8086_mode = std::uint8_t{0x01}; - constexpr auto pic_master_mask = std::uint8_t{0x00}; + constexpr auto pic_master_mask = std::uint8_t{0x01}; constexpr auto pic_slave_mask = std::uint8_t{0x00}; pic_master_control_port::write(pic_init_command); @@ -161,6 +156,9 @@ namespace arch::cpu pic_master_data_port::write(pic_master_mask); pic_slave_data_port::write(pic_slave_mask); + + pic_master_data_port::write(pic_master_mask); + pic_slave_data_port::write(pic_slave_mask); } } // namespace arch::cpu diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 466389d..048c461 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -2,6 +2,7 @@ #include "kapi/cpu.hpp" +#include "arch/cpu/legacy_pic.hpp" #include "arch/cpu/segment_selector.hpp" #include @@ -42,6 +43,9 @@ namespace arch::cpu auto handle_legacy_interrupt(interrupt_frame * frame) -> void { kstd::println("[x86_64:SYS] Ignoring 8259 legacy interrupt {:#04x}", frame->interrupt.number); + + pic_master_control_port::write(pic_end_of_interrupt); + pic_slave_control_port::write(pic_end_of_interrupt); } } // namespace -- cgit v1.2.3 From 754012dd458985a6a4953c99204c6651318892b2 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 23 Mar 2026 08:10:49 +0100 Subject: testing: enable build-host testing --- .clang-tidy | 2 +- .clangd | 13 ++++++++- .devcontainer/x86-64/devcontainer.json | 1 + .vscode/extensions.json | 1 + .vscode/settings.json | 1 + CMakeLists.txt | 36 ++++++++++++++++++++++--- CMakePresets.json | 10 +++++++ libs/kstd/CMakeLists.txt | 48 +++++++++++++++++++++++++++------- libs/kstd/tests/os_mock.cpp | 15 +++++++++++ libs/kstd/tests/vector.cpp | 11 ++++++++ 10 files changed, 122 insertions(+), 16 deletions(-) create mode 100644 libs/kstd/tests/os_mock.cpp create mode 100644 libs/kstd/tests/vector.cpp diff --git a/.clang-tidy b/.clang-tidy index e802dbd..71f1be9 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -66,5 +66,5 @@ CheckOptions: readability-magic-numbers.IgnoreTypeAliases: true FormatStyle: file -HeaderFilterRegex: '(.*/kstd/.*)|.*\.hpp' +HeaderFilterRegex: '(.*/kstd/include/.*)|(arch|kernel|kapi)/.*\.hpp' SystemHeaders: true \ No newline at end of file diff --git a/.clangd b/.clangd index e3c98ed..cb9a1f6 100644 --- a/.clangd +++ b/.clangd @@ -1,3 +1,14 @@ Diagnostics: UnusedIncludes: Strict - MissingIncludes: Strict \ No newline at end of file + MissingIncludes: Strict + +--- +If: + PathMatch: + - "libs/.*/tests/.*\\.cpp" +Diagnostics: + ClangTidy: + Remove: '*' +CompileFlags: + Add: + - -Wno-c2y-extensions \ No newline at end of file diff --git a/.devcontainer/x86-64/devcontainer.json b/.devcontainer/x86-64/devcontainer.json index 41e9a95..f0059ac 100644 --- a/.devcontainer/x86-64/devcontainer.json +++ b/.devcontainer/x86-64/devcontainer.json @@ -14,6 +14,7 @@ "basdp.language-gas-x86", "gruntfuggly.todo-tree", "llvm-vs-code-extensions.vscode-clangd", + "matepek.vscode-catch2-test-adapter", "ms-vscode.cmake-tools", "KylinIdeTeam.cppdebug", "zixuanwang.linkerscript" diff --git a/.vscode/extensions.json b/.vscode/extensions.json index ea07f1b..fd40a4c 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,6 +3,7 @@ "basdp.language-gas-x86", "gruntfuggly.todo-tree", "llvm-vs-code-extensions.vscode-clangd", + "matepek.vscode-catch2-test-adapter", "ms-vscode.cmake-tools", "KylinIdeTeam.cppdebug", "zixuanwang.linkerscript" diff --git a/.vscode/settings.json b/.vscode/settings.json index 1ab19f2..f9b075d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "cmake.useCMakePresets": "always", "cmake.options.statusBarVisibility": "visible", + "cmake.ctest.testExplorerIntegrationEnabled": false, "clangd.arguments": [ "--compile-commands-dir=${workspaceFolder}/build", diff --git a/CMakeLists.txt b/CMakeLists.txt index 7654ed0..f4c3762 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,21 @@ set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/Modules") include("ElfTransformations") include("GenerateBootableIso") +#[============================================================================[ +# External Dependencies +#]============================================================================] + +include("FetchContent") + +FetchContent_Declare( + "Catch2" + GIT_REPOSITORY "https://github.com/catchorg/Catch2.git" + GIT_TAG "v3.13.0" + FIND_PACKAGE_ARGS +) + +FetchContent_MakeAvailable("Catch2") + #[============================================================================[ # Global Build System Options #]============================================================================] @@ -70,7 +85,20 @@ endif() # Global Targets #]============================================================================] -add_subdirectory("arch/${CMAKE_SYSTEM_PROCESSOR}") -add_subdirectory("kapi") -add_subdirectory("libs") -add_subdirectory("kernel") \ No newline at end of file +if(CMAKE_CROSSCOMPILING) + add_subdirectory("arch/${CMAKE_SYSTEM_PROCESSOR}") + add_subdirectory("kernel") + add_subdirectory("kapi") + add_subdirectory("libs") +else() + enable_testing() + find_package("Catch2") + include("Catch") + + set_target_properties("Catch2" "Catch2WithMain" PROPERTIES + C_CLANG_TIDY "" + CXX_CLANG_TIDY "" + ) + + add_subdirectory("libs") +endif() diff --git a/CMakePresets.json b/CMakePresets.json index 15bf4bc..8910b5f 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -16,6 +16,11 @@ "name": "x86_64", "inherits": "base", "toolchainFile": "cmake/Platforms/x86_64.cmake" + }, + { + "name": "bht", + "inherits": "base", + "description": "Build-host Testing" } ], "buildPresets": [ @@ -28,6 +33,11 @@ "name": "x86_64-rel", "configurePreset": "x86_64", "configuration": "MinSizeRel" + }, + { + "name": "bht-dbg", + "configurePreset": "bht", + "configuration": "Debug" } ] } diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index b0c9c63..2f360cd 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -1,16 +1,22 @@ add_library("kstd" STATIC) add_library("libs::kstd" ALIAS "kstd") -set(KSTD_LIBC_SYMBOLS - "abort" - "strlen" - "memcmp" - "memcpy" -) +if(CMAKE_CROSSCOMPILING) + set(KSTD_LIBC_SYMBOLS + "abort" + "strlen" + "memcmp" + "memcpy" + ) + + set(KSTD_LIBC_SOURCES + "src/libc/stdlib.cpp" + "src/libc/string.cpp" + ) +endif() target_sources("kstd" PRIVATE - "src/libc/stdlib.cpp" - "src/libc/string.cpp" + ${KSTD_LIBC_SOURCES} "src/os/error.cpp" @@ -30,6 +36,28 @@ target_include_directories("kstd" PUBLIC "include" ) -list(TRANSFORM KSTD_LIBC_SYMBOLS PREPEND "-Wl,--undefined=") +if(CMAKE_CROSSCOMPILING) + list(TRANSFORM KSTD_LIBC_SYMBOLS PREPEND "-Wl,--undefined=") + + target_link_options("kstd" INTERFACE ${KSTD_LIBC_SYMBOLS}) +endif() + +if(NOT CMAKE_CROSSCOMPILING) + add_executable("kstd_tests" + "tests/vector.cpp" + "tests/os_mock.cpp" + ) + + target_link_libraries("kstd_tests" PRIVATE + "Catch2::Catch2WithMain" + "libs::kstd" + ) + + set_target_properties("kstd_tests" PROPERTIES + C_CLANG_TIDY "" + CXX_CLANG_TIDY "" + EXCLUDE_FROM_ALL NO + ) -target_link_options("kstd" INTERFACE ${KSTD_LIBC_SYMBOLS}) \ No newline at end of file + catch_discover_tests("kstd_tests") +endif() \ No newline at end of file diff --git a/libs/kstd/tests/os_mock.cpp b/libs/kstd/tests/os_mock.cpp new file mode 100644 index 0000000..39b7f0d --- /dev/null +++ b/libs/kstd/tests/os_mock.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include + +namespace kstd::os +{ + auto panic(std::string_view message, std::source_location location) + { + auto full_message = + std::format("OS Panic Handler called '{}' at {}:{}", message, location.file_name(), location.line()); + throw std::runtime_error{full_message}; + } +} // namespace kstd::os \ No newline at end of file diff --git a/libs/kstd/tests/vector.cpp b/libs/kstd/tests/vector.cpp new file mode 100644 index 0000000..3a45008 --- /dev/null +++ b/libs/kstd/tests/vector.cpp @@ -0,0 +1,11 @@ +#include + +#include + +TEST_CASE("Creating an empty vector") +{ + kstd::vector v; + REQUIRE(v.empty()); + REQUIRE(v.size() == 0); + REQUIRE(v.capacity() == 0); +} -- cgit v1.2.3 From 48a2c33d205397adeaad385aebc1d1e008915b3e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 23 Mar 2026 10:32:15 +0100 Subject: ci: enable test builds --- .clang-tidy | 4 +- .clangd | 3 + .gitignore | 6 +- .gitlab-ci.yml | 18 ++ .lcovrc | 5 + .vscode/settings.json | 16 +- CMakeLists.txt | 7 +- CMakePresets.json | 9 +- cmake/Modules/EnableCoverage.cmake | 9 + libs/kstd/CMakeLists.txt | 11 +- libs/kstd/tests/include/kstd/tests/os_panic.hpp | 23 ++ libs/kstd/tests/os_mock.cpp | 15 - libs/kstd/tests/src/os_panic.cpp | 15 + libs/kstd/tests/src/vector.cpp | 395 ++++++++++++++++++++++++ libs/kstd/tests/vector.cpp | 11 - 15 files changed, 508 insertions(+), 39 deletions(-) create mode 100644 .lcovrc create mode 100644 cmake/Modules/EnableCoverage.cmake create mode 100644 libs/kstd/tests/include/kstd/tests/os_panic.hpp delete mode 100644 libs/kstd/tests/os_mock.cpp create mode 100644 libs/kstd/tests/src/os_panic.cpp create mode 100644 libs/kstd/tests/src/vector.cpp delete mode 100644 libs/kstd/tests/vector.cpp diff --git a/.clang-tidy b/.clang-tidy index 71f1be9..8fa3943 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -67,4 +67,6 @@ CheckOptions: FormatStyle: file HeaderFilterRegex: '(.*/kstd/include/.*)|(arch|kernel|kapi)/.*\.hpp' -SystemHeaders: true \ No newline at end of file +SystemHeaders: true +RemovedArgs: + - -fcondition-coverage \ No newline at end of file diff --git a/.clangd b/.clangd index cb9a1f6..556b73c 100644 --- a/.clangd +++ b/.clangd @@ -1,6 +1,9 @@ Diagnostics: UnusedIncludes: Strict MissingIncludes: Strict +CompileFlags: + Remove: + - -fcondition-coverage --- If: diff --git a/.gitignore b/.gitignore index 21c6b0a..e96f481 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,7 @@ /docs/_build qemu-*-*.log - -/desktop.ini + +/desktop.ini + +coverage.info diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f2cfd1d..b37da93 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,6 +22,24 @@ build: - kernel.iso <<: *build_matrix +bht: + stage: build + image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-3 + before_script: + - apt update + - apt install -y build-essential libcatch2-dev + script: + - cmake --preset 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 + coverage: '/Total:\|(\d+\.?\d+)\%/' + artifacts: + paths: + coverage.info + expire_in: 24 hours + license_check: stage: .pre image: diff --git a/.lcovrc b/.lcovrc new file mode 100644 index 0000000..d19e80d --- /dev/null +++ b/.lcovrc @@ -0,0 +1,5 @@ +exclude = /usr/include/* +exclude = build/_deps/* +exclude = tests/* + +ignore_errors = unused,empty,inconsistent \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index f9b075d..21f4885 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,6 @@ "cmake.useCMakePresets": "always", "cmake.options.statusBarVisibility": "visible", "cmake.ctest.testExplorerIntegrationEnabled": false, - "clangd.arguments": [ "--compile-commands-dir=${workspaceFolder}/build", "--query-driver=**/x86_64-pc-elf-g++", @@ -11,16 +10,13 @@ "--clang-tidy", "--header-insertion=iwyu" ], - "files.associations": { "**/kstd/include/kstd/**": "cpp", }, - "[cpp]": { "editor.formatOnSave": true, "editor.tabSize": 2, }, - "[gas]": { "editor.rulers": [ 80 @@ -59,5 +55,15 @@ "teachos", "undelegated", "wrmsr" - ] + ], + "testMate.cpp.debug.configTemplate": { + "type": "cppdbg", + "MIMode": "gdb", + "program": "${exec}", + "args": "${argsArray}", + "cwd": "${cwd}", + "env": "${envObject}", + "environment": "${envObjArray}", + "sourceFileMap": "${sourceFileMapObj}" + } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index f4c3762..71e4fef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,8 +20,9 @@ include("FetchContent") FetchContent_Declare( "Catch2" - GIT_REPOSITORY "https://github.com/catchorg/Catch2.git" - GIT_TAG "v3.13.0" + URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" + URL_HASH "SHA256=c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c" + EXCLUDE_FROM_ALL FIND_PACKAGE_ARGS ) @@ -91,6 +92,8 @@ if(CMAKE_CROSSCOMPILING) add_subdirectory("kapi") add_subdirectory("libs") else() + include("EnableCoverage") + enable_testing() find_package("Catch2") include("Catch") diff --git a/CMakePresets.json b/CMakePresets.json index 8910b5f..3445119 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -14,6 +14,7 @@ }, { "name": "x86_64", + "description": "Target x86-64", "inherits": "base", "toolchainFile": "cmake/Platforms/x86_64.cmake" }, @@ -39,5 +40,11 @@ "configurePreset": "bht", "configuration": "Debug" } + ], + "testPresets": [ + { + "name": "bht-dbg", + "configurePreset": "bht" + } ] -} +} \ No newline at end of file diff --git a/cmake/Modules/EnableCoverage.cmake b/cmake/Modules/EnableCoverage.cmake new file mode 100644 index 0000000..9602869 --- /dev/null +++ b/cmake/Modules/EnableCoverage.cmake @@ -0,0 +1,9 @@ +function (enable_coverage TARGET) + target_compile_options("${TARGET}" PRIVATE + "$<$,$>:-fcondition-coverage>" + "$<$,$>:--coverage>" + ) + target_link_libraries("${TARGET}" PRIVATE + "$<$,$>:gcov>" + ) +endfunction () \ No newline at end of file diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 2f360cd..06543ab 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -44,8 +44,12 @@ endif() if(NOT CMAKE_CROSSCOMPILING) add_executable("kstd_tests" - "tests/vector.cpp" - "tests/os_mock.cpp" + "tests/src/vector.cpp" + "tests/src/os_panic.cpp" + ) + + target_include_directories("kstd_tests" PRIVATE + "tests/include" ) target_link_libraries("kstd_tests" PRIVATE @@ -59,5 +63,8 @@ if(NOT CMAKE_CROSSCOMPILING) EXCLUDE_FROM_ALL NO ) + enable_coverage("kstd") + enable_coverage("kstd_tests") + catch_discover_tests("kstd_tests") endif() \ No newline at end of file diff --git a/libs/kstd/tests/include/kstd/tests/os_panic.hpp b/libs/kstd/tests/include/kstd/tests/os_panic.hpp new file mode 100644 index 0000000..4396a9f --- /dev/null +++ b/libs/kstd/tests/include/kstd/tests/os_panic.hpp @@ -0,0 +1,23 @@ +#ifndef KSTD_TESTS_OS_PANIC_HPP +#define KSTD_TESTS_OS_PANIC_HPP + +#include +#include +#include + +namespace kstd::tests +{ + + struct os_panic : std::runtime_error + { + os_panic(std::string message, std::source_location location) + : std::runtime_error{message} + , location(location) + {} + + std::source_location location; + }; + +} // namespace kstd::tests + +#endif \ No newline at end of file diff --git a/libs/kstd/tests/os_mock.cpp b/libs/kstd/tests/os_mock.cpp deleted file mode 100644 index 39b7f0d..0000000 --- a/libs/kstd/tests/os_mock.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include -#include -#include -#include -#include - -namespace kstd::os -{ - auto panic(std::string_view message, std::source_location location) - { - auto full_message = - std::format("OS Panic Handler called '{}' at {}:{}", message, location.file_name(), location.line()); - throw std::runtime_error{full_message}; - } -} // namespace kstd::os \ No newline at end of file diff --git a/libs/kstd/tests/src/os_panic.cpp b/libs/kstd/tests/src/os_panic.cpp new file mode 100644 index 0000000..3eae6ff --- /dev/null +++ b/libs/kstd/tests/src/os_panic.cpp @@ -0,0 +1,15 @@ +#include "kstd/tests/os_panic.hpp" + +#include +#include +#include + +namespace kstd::os +{ + + auto panic(std::string_view message, std::source_location location) + { + throw kstd::tests::os_panic{std::string{message}, location}; + } + +} // namespace kstd::os \ No newline at end of file diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp new file mode 100644 index 0000000..cb1b182 --- /dev/null +++ b/libs/kstd/tests/src/vector.cpp @@ -0,0 +1,395 @@ +#include "kstd/tests/os_panic.hpp" + +#include +#include + +#include + +#include +#include +#include + +SCENARIO("Vector initialization and construction", "[vector]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + kstd::vector v; + + THEN("the vector is empty") + { + REQUIRE(v.empty()); + } + + THEN("the size and capacity are zero") + { + REQUIRE(v.size() == 0); + REQUIRE(v.capacity() == 0); + } + } + + WHEN("constructing with a specific size") + { + kstd::vector v(10); + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size is and capacity match the specified value") + { + REQUIRE(v.size() == 10); + REQUIRE(v.capacity() == 10); + } + } + + WHEN("constructing from an initializer list") + { + kstd::vector v = {1, 2, 3, 4, 5}; + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size is and capacity match the specified value") + { + REQUIRE(v.size() == 5); + REQUIRE(v.capacity() == 5); + } + + THEN("the elements are correctly initialized") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + REQUIRE(v[3] == 4); + REQUIRE(v[4] == 5); + } + } + } + + GIVEN("A non-empty range") + { + auto range = std::array{1, 2, 3}; + + WHEN("constructing from a random-access iterator range") + { + auto v = kstd::vector{std::begin(range), std::end(range)}; + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size and capacity match the range size") + { + REQUIRE(v.size() == std::size(range)); + REQUIRE(v.capacity() == std::size(range)); + } + + THEN("the elements are correctly initialized") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + + WHEN("constructing from a range") + { + auto v = kstd::vector{kstd::from_range, range}; + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size and capacity match the range size") + { + REQUIRE(v.size() == std::ranges::size(range)); + REQUIRE(v.capacity() == std::ranges::size(range)); + } + + THEN("the elements are correctly initialized") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + } + + GIVEN("A populated vector") + { + kstd::vector source = {1, 2, 3, 4, 5}; + + WHEN("copy constructing a new vector") + { + kstd::vector copy(source); + + THEN("the copy matches the original") + { + REQUIRE(copy.size() == source.size()); + REQUIRE(copy.capacity() == source.capacity()); + REQUIRE(copy[0] == 1); + REQUIRE(copy[1] == 2); + REQUIRE(copy[2] == 3); + REQUIRE(copy[3] == 4); + REQUIRE(copy[4] == 5); + } + + THEN("the original is left unchanged") + { + REQUIRE(source.size() == 5); + REQUIRE(source.capacity() == 5); + REQUIRE(source[0] == 1); + REQUIRE(source[1] == 2); + REQUIRE(source[2] == 3); + REQUIRE(source[3] == 4); + REQUIRE(source[4] == 5); + } + } + + WHEN("move constructing a new vector") + { + kstd::vector moved(std::move(source)); + + THEN("The new vector has the original elements") + { + REQUIRE(moved.size() == 5); + REQUIRE(moved.capacity() == 5); + REQUIRE(moved[0] == 1); + REQUIRE(moved[1] == 2); + REQUIRE(moved[2] == 3); + REQUIRE(moved[3] == 4); + REQUIRE(moved[4] == 5); + } + + THEN("The original vector is left in a valid but unspecified state") + { + REQUIRE(source.empty()); + REQUIRE(source.size() == 0); + REQUIRE(source.capacity() == 0); + } + } + } +} + +SCENARIO("Vector element access", "[vector]") +{ + GIVEN("A populated vector") + { + kstd::vector v = {10, 20, 30}; + + WHEN("accessing elements for reading") + { + THEN("operator[] and at() return the correct elements") + { + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + + REQUIRE(v.at(0) == 10); + REQUIRE(v.at(1) == 20); + REQUIRE(v.at(2) == 30); + } + + THEN("front() and back() return the first and last elements") + { + REQUIRE(v.front() == 10); + REQUIRE(v.back() == 30); + } + + THEN("data() return a pointer to the contiguous storage") + { + auto ptr = v.data(); + REQUIRE(ptr); + REQUIRE(ptr[0] == 10); + REQUIRE(ptr[1] == 20); + REQUIRE(ptr[2] == 30); + } + + THEN("accessing out of bounds elements panics") + { + REQUIRE_THROWS_AS(v.at(3), kstd::tests::os_panic); + } + } + + WHEN("accessing elements for writing") + { + v[0] = 100; + v.at(1) = 200; + v.back() = 300; + + THEN("the elements are correctly modified") + { + REQUIRE(v[0] == 100); + REQUIRE(v[1] == 200); + REQUIRE(v[2] == 300); + } + } + } +} + +SCENARIO("Vector iterators", "[vector]") +{ + GIVEN("A populated vector") + { + kstd::vector v = {1, 2, 3}; + + WHEN("using forward iterators") + { + THEN("they navigate the elements in the correct forward order") + { + auto it = v.begin(); + REQUIRE(it != v.end()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it != v.end()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.end()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it == v.end()); + } + + THEN("const forward iterators provide correct access") + { + auto it = v.cbegin(); + REQUIRE(it != v.cend()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it != v.cend()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.cend()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it == v.cend()); + } + } + + WHEN("using reverse iterators") + { + THEN("they navigate the elements in the correct reverse order") + { + auto it = v.rbegin(); + REQUIRE(it != v.rend()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it != v.rend()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.rend()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it == v.rend()); + } + + THEN("const reverse iterators provide correct access") + { + auto it = v.crbegin(); + REQUIRE(it != v.crend()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it != v.crend()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.crend()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it == v.crend()); + } + } + } + + GIVEN("an empty vector") + { + kstd::vector v; + + WHEN("getting iterators") + { + THEN("begin() equals end() and cbegin() equals cend()") + { + REQUIRE(v.begin() == v.end()); + REQUIRE(v.cbegin() == v.cend()); + } + + THEN("rbegin() equals rend() and crbegin() equals crend()") + { + REQUIRE(v.rbegin() == v.rend()); + REQUIRE(v.crbegin() == v.crend()); + } + } + } +} + +SCENARIO("Vector capacity management", "[vector]") +{ + GIVEN("An empty vector") + { + kstd::vector v; + + WHEN("reserving space") + { + v.reserve(10); + + THEN("the capacity is at least the reserved amount") + { + REQUIRE(v.capacity() >= 10); + } + + THEN("the size is still zero") + { + REQUIRE(v.size() == 0); + } + + THEN("the vector is still empty") + { + REQUIRE(v.empty()); + } + } + } + + GIVEN("A populated vector with excess capacity") + { + kstd::vector v{1, 2, 3}; + v.reserve(10); + + REQUIRE(v.capacity() == 10); + + WHEN("calling shrink_to_fit") + { + v.shrink_to_fit(); + + THEN("the capacity is reduced to match the size") + { + REQUIRE(v.capacity() == 3); + REQUIRE(v.size() == 3); + } + + THEN("the elements remain unchanged") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + } +} \ No newline at end of file diff --git a/libs/kstd/tests/vector.cpp b/libs/kstd/tests/vector.cpp deleted file mode 100644 index 3a45008..0000000 --- a/libs/kstd/tests/vector.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include - -#include - -TEST_CASE("Creating an empty vector") -{ - kstd::vector v; - REQUIRE(v.empty()); - REQUIRE(v.size() == 0); - REQUIRE(v.capacity() == 0); -} -- cgit v1.2.3 From 33708afec8717b091e2f93eda34f9e73e5b4c93e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 23 Mar 2026 10:33:07 +0100 Subject: ci: fix artifact configuration --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b37da93..b9385ce 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,7 +37,7 @@ bht: coverage: '/Total:\|(\d+\.?\d+)\%/' artifacts: paths: - coverage.info + - coverage.info expire_in: 24 hours license_check: -- cgit v1.2.3 From c95c6392174f1bde1ccbedf8396f83cf6efadd5f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 23 Mar 2026 10:34:36 +0100 Subject: ci: add missing test build dependencies --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b9385ce..cf61007 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -27,7 +27,7 @@ bht: image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-3 before_script: - apt update - - apt install -y build-essential libcatch2-dev + - apt install -y build-essential cmake ninja-build lcov libcatch2-dev script: - cmake --preset bht - cmake --build --preset bht-dbg -- cgit v1.2.3 From 730a1b83fe02e6249c20cbdd24b0c097c7c57b49 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 23 Mar 2026 10:37:41 +0100 Subject: deps: disable clang-tidy when not preinstalled --- CMakeLists.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 71e4fef..febcf0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,10 +98,12 @@ else() find_package("Catch2") include("Catch") - set_target_properties("Catch2" "Catch2WithMain" PROPERTIES - C_CLANG_TIDY "" - CXX_CLANG_TIDY "" - ) + if(TARGET "Catch2" AND TARGET "Catch2WithMain") + set_target_properties("Catch2" "Catch2WithMain" PROPERTIES + C_CLANG_TIDY "" + CXX_CLANG_TIDY "" + ) + endif() add_subdirectory("libs") endif() -- cgit v1.2.3 From bb7a6bd989b748a29c1a1e68807da2dd3176186a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 23 Mar 2026 11:19:30 +0100 Subject: kstd: fix push/emplace_back bug in vector --- libs/kstd/include/kstd/vector | 6 ++ libs/kstd/tests/src/vector.cpp | 168 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index b2cce0b..1242489 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -594,12 +594,14 @@ namespace kstd { auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; auto new_data = allocate_n(new_capacity); + auto old_size = size(); std::allocator_traits::construct(m_allocator, new_data + m_size, value); uninitialized_move_with_allocator(begin(), new_data, size()); destroy_n(begin(), size()); deallocate(); m_data = new_data; m_capacity = new_capacity; + m_size = old_size; } else { @@ -615,12 +617,14 @@ namespace kstd { auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; auto new_data = allocate_n(new_capacity); + auto old_size = size(); std::allocator_traits::construct(m_allocator, new_data + m_size, std::move(value)); uninitialized_move_with_allocator(begin(), new_data, size()); destroy_n(begin(), size()); deallocate(); m_data = new_data; m_capacity = new_capacity; + m_size = old_size; } else { @@ -637,12 +641,14 @@ namespace kstd { auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; auto new_data = allocate_n(new_capacity); + auto old_size = size(); std::allocator_traits::construct(m_allocator, new_data + m_size, std::forward(args)...); uninitialized_move_with_allocator(begin(), new_data, size()); destroy_n(begin(), size()); deallocate(); m_data = new_data; m_capacity = new_capacity; + m_size = old_size; } else { diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index cb1b182..2440247 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -392,4 +392,172 @@ SCENARIO("Vector capacity management", "[vector]") } } } +} + +SCENARIO("Vector modifiers", "[vector]") +{ + GIVEN("An empty vector") + { + kstd::vector v; + + WHEN("push_back is called with a value") + { + v.push_back(10); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v.back() == 10); + } + } + + WHEN("emplace_back is called with constructor arguments") + { + v.emplace_back(20); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v.back() == 20); + } + } + } + + GIVEN("A populated vector") + { + kstd::vector v = {10, 20, 30}; + auto initial_capacity = v.capacity(); + + WHEN("push_back is called") + { + v.push_back(40); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() >= initial_capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 40); + } + } + + WHEN("emplace_back is called with constructor arguments") + { + v.emplace_back(40); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() >= initial_capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 40); + } + } + + WHEN("pop_back is called") + { + v.pop_back(); + + THEN("the last element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v.capacity() == initial_capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + } + } + + WHEN("clear is called") + { + v.clear(); + + THEN("the vector is empty") + { + REQUIRE(v.empty()); + REQUIRE(v.size() == 0); + REQUIRE(v.capacity() == initial_capacity); + } + } + } +} + +SCENARIO("Vector comparison", "[vector]") +{ + GIVEN("Two identical vectors") + { + kstd::vector v1 = {1, 2, 3}; + kstd::vector v2 = {1, 2, 3}; + + WHEN("comparing for equality") + { + THEN("the vectors are equal") + { + REQUIRE(v1 == v2); + } + + THEN("the vectors and not not-equal") + { + REQUIRE_FALSE(v1 != v2); + } + } + + WHEN("comparing using the spaceship operator") + { + THEN("the vectors are equivalent") + { + REQUIRE((v1 <=> v2) == 0); + REQUIRE(v1 <= v2); + REQUIRE(v1 >= v2); + } + } + } + + GIVEN("Two vectors of different sizes") + { + kstd::vector v1 = {1, 2, 3}; + kstd::vector v2 = {1, 2, 3, 4}; + + WHEN("comparing for equality") + { + THEN("the vectors are not equal") + { + REQUIRE_FALSE(v1 == v2); + } + + THEN("the vectors are not-equal") + { + REQUIRE(v1 != v2); + } + } + + WHEN("comparing for ordering") + { + THEN("the shorter vector evaluates as less than the longer vector") + { + REQUIRE(v1 < v2); + REQUIRE(v2 > v1); + } + } + } + + GIVEN("Two vectors of the same size but different elements") + { + kstd::vector v1 = {1, 2, 3}; + kstd::vector v2 = {1, 2, 4}; + + WHEN("comparing for ordering") + { + THEN("they are ordered lexicographically") + { + REQUIRE(v1 < v2); + REQUIRE(v2 > v1); + } + } + } } \ No newline at end of file -- cgit v1.2.3 From a2a407efd453635af9ff6d3514114bfee95bcbb9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 23 Mar 2026 11:40:44 +0100 Subject: kstd/vector: add more tests for different types --- libs/kstd/tests/src/vector.cpp | 186 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 2440247..1427ce8 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -560,4 +561,189 @@ SCENARIO("Vector comparison", "[vector]") } } } +} + +struct non_default_constructible +{ + int value; + non_default_constructible() = delete; + explicit non_default_constructible(int v) + : value{v} + {} + + bool operator==(non_default_constructible const & other) const noexcept = default; +}; + +SCENARIO("Vector with non-default-constructible types", "[vector]") +{ + GIVEN("A type without a default constructor") + { + WHEN("constructing an empty vector") + { + kstd::vector v; + + THEN("the vector is empty") + { + REQUIRE(v.empty()); + } + } + + WHEN("using emplace_back") + { + kstd::vector v; + v.emplace_back(42); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.back() == non_default_constructible{42}); + } + } + } +} + +template +struct tracking_allocator +{ + using value_type = T; + + int * allocation_count; + + tracking_allocator(int * counter) + : allocation_count{counter} + {} + + template + tracking_allocator(tracking_allocator const & other) noexcept + : allocation_count{other.allocation_count} + {} + + T * allocate(std::size_t n) + { + if (allocation_count) + { + (*allocation_count)++; + } + return static_cast(::operator new(n * sizeof(T))); + } + + void deallocate(T * p, std::size_t n) noexcept + { + ::operator delete(p, n * sizeof(T)); + } + + bool operator==(tracking_allocator const & other) + { + return allocation_count == other.allocation_count; + } +}; + +SCENARIO("Vector with custom allocator", "[vector]") +{ + GIVEN("a tracking allocator acting as the vector's memory manager") + { + auto allocations = 0; + tracking_allocator allocator{&allocations}; + + WHEN("a vector uses this allocator to allocate memory") + { + kstd::vector> v{allocator}; + REQUIRE(allocations == 0); + + v.reserve(10); + + THEN("the allocator was used to allocate memory") + { + REQUIRE(allocations > 0); + } + } + } +} + +struct move_tracker +{ + int value; + bool was_copied{}; + bool was_moved{}; + + move_tracker() = default; + explicit move_tracker(int v) + : value{v} + {} + + move_tracker(move_tracker const & other) + : value{other.value} + , was_copied{true} + , was_moved{false} + {} + + move_tracker(move_tracker && other) noexcept + : value{other.value} + , was_copied{false} + , was_moved{true} + { + other.value = -1; + } + + move_tracker & operator=(move_tracker const & other) + { + value = other.value; + was_copied = true; + was_moved = false; + return *this; + } + + move_tracker & operator=(move_tracker && other) noexcept + { + value = other.value; + was_moved = true; + was_copied = false; + other.value = -1; + return *this; + } +}; + +SCENARIO("Vector modifier move semantics", "[vector]") +{ + GIVEN("An empty vector and a move tracker element") + { + kstd::vector v; + move_tracker tracker{42}; + + WHEN("push_back is called with the move tracker") + { + v.push_back(std::move(tracker)); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.back().was_moved); + REQUIRE_FALSE(v.back().was_copied); + REQUIRE(v.back().value == 42); + } + + THEN("the original tracker is left in a valid but unspecified state") + { + REQUIRE(tracker.value == -1); + } + } + } + + GIVEN("An empty vector") + { + kstd::vector v; + + WHEN("emplace_back is called with constructor arguments") + { + v.emplace_back(42); + + THEN("the element is constructed directly, without moves or copies") + { + REQUIRE(v.size() == 1); + REQUIRE_FALSE(v.back().was_moved); + REQUIRE_FALSE(v.back().was_copied); + REQUIRE(v.back().value == 42); + } + } + } } \ No newline at end of file -- cgit v1.2.3 From d31d12a612e9bbb1eb32ba7bcb17d70ff917ac9e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 23 Mar 2026 12:19:23 +0100 Subject: kstd/vector: expand tests --- libs/kstd/include/kstd/vector | 7 +- libs/kstd/tests/src/vector.cpp | 511 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 488 insertions(+), 30 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 1242489..5655854 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -60,6 +60,9 @@ namespace kstd explicit constexpr vector(allocator_type const & allocator) noexcept( std::is_nothrow_copy_constructible_v) : m_allocator{allocator} + , m_size{0} + , m_capacity{0} + , m_data{allocate_n(m_capacity)} {} //! Construct a new vector and fill it with the given number of default constructed elements. @@ -237,7 +240,7 @@ namespace kstd //! Replace the contents of this vector by the copying from the given source vector. //! //! @param other The source vector. - constexpr auto operator=(vector const & other) -> vector & + constexpr auto operator=(vector const & other) -> vector & { if (this == std::addressof(other)) { @@ -285,7 +288,7 @@ namespace kstd //! @param other The source vector. constexpr auto operator=(vector && other) noexcept( std::allocator_traits::propagate_on_container_move_assignment::value || - std::allocator_traits::is_always_equal::value) -> vector & + std::allocator_traits::is_always_equal::value) -> vector & { using std::swap; diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 1427ce8..713f6ab 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -16,7 +17,7 @@ SCENARIO("Vector initialization and construction", "[vector]") { WHEN("constructing by default") { - kstd::vector v; + auto v = kstd::vector{}; THEN("the vector is empty") { @@ -32,7 +33,7 @@ SCENARIO("Vector initialization and construction", "[vector]") WHEN("constructing with a specific size") { - kstd::vector v(10); + auto v = kstd::vector(10); THEN("the vector is not empty") { @@ -48,7 +49,7 @@ SCENARIO("Vector initialization and construction", "[vector]") WHEN("constructing from an initializer list") { - kstd::vector v = {1, 2, 3, 4, 5}; + auto v = kstd::vector{1, 2, 3, 4, 5}; THEN("the vector is not empty") { @@ -125,11 +126,11 @@ SCENARIO("Vector initialization and construction", "[vector]") GIVEN("A populated vector") { - kstd::vector source = {1, 2, 3, 4, 5}; + auto source = kstd::vector{1, 2, 3, 4, 5}; WHEN("copy constructing a new vector") { - kstd::vector copy(source); + auto copy = kstd::vector(source); THEN("the copy matches the original") { @@ -156,7 +157,7 @@ SCENARIO("Vector initialization and construction", "[vector]") WHEN("move constructing a new vector") { - kstd::vector moved(std::move(source)); + auto moved = kstd::vector(std::move(source)); THEN("The new vector has the original elements") { @@ -183,7 +184,7 @@ SCENARIO("Vector element access", "[vector]") { GIVEN("A populated vector") { - kstd::vector v = {10, 20, 30}; + auto v = kstd::vector{10, 20, 30}; WHEN("accessing elements for reading") { @@ -239,7 +240,7 @@ SCENARIO("Vector iterators", "[vector]") { GIVEN("A populated vector") { - kstd::vector v = {1, 2, 3}; + auto v = kstd::vector{1, 2, 3}; WHEN("using forward iterators") { @@ -322,7 +323,7 @@ SCENARIO("Vector iterators", "[vector]") GIVEN("an empty vector") { - kstd::vector v; + auto v = kstd::vector{}; WHEN("getting iterators") { @@ -345,7 +346,7 @@ SCENARIO("Vector capacity management", "[vector]") { GIVEN("An empty vector") { - kstd::vector v; + auto v = kstd::vector{}; WHEN("reserving space") { @@ -366,11 +367,31 @@ SCENARIO("Vector capacity management", "[vector]") REQUIRE(v.empty()); } } + + WHEN("reserving space less than or equal to current capacity") + { + v.reserve(10); + auto const current_capacity = v.capacity(); + v.reserve(5); + + THEN("the capacity remains unchanged") + { + REQUIRE(v.capacity() == current_capacity); + } + } + + WHEN("reserving space greater than max_size") + { + THEN("a panic is triggered") + { + REQUIRE_THROWS_AS(v.reserve(v.max_size() + 1), kstd::tests::os_panic); + } + } } GIVEN("A populated vector with excess capacity") { - kstd::vector v{1, 2, 3}; + auto v = kstd::vector{1, 2, 3}; v.reserve(10); REQUIRE(v.capacity() == 10); @@ -399,7 +420,7 @@ SCENARIO("Vector modifiers", "[vector]") { GIVEN("An empty vector") { - kstd::vector v; + auto v = kstd::vector{}; WHEN("push_back is called with a value") { @@ -424,11 +445,28 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE(v.back() == 20); } } + + WHEN("elements are added while capacity is sufficient") + { + v.reserve(10); + auto const capacity = v.capacity(); + + v.push_back(10); + v.emplace_back(20); + + THEN("the elements are added without reallocation") + { + REQUIRE(v.size() == 2); + REQUIRE(v.capacity() == capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + } + } } GIVEN("A populated vector") { - kstd::vector v = {10, 20, 30}; + auto v = kstd::vector{10, 20, 30}; auto initial_capacity = v.capacity(); WHEN("push_back is called") @@ -461,6 +499,21 @@ SCENARIO("Vector modifiers", "[vector]") } } + WHEN("push_back is called with a reference to an internal element") + { + v.shrink_to_fit(); + auto const original_value = v[0]; + + v.push_back(v[0]); + + THEN("reallocation handles the internal reference safely without dangling") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == original_value); + REQUIRE(v.back() == original_value); + } + } + WHEN("pop_back is called") { v.pop_back(); @@ -492,8 +545,8 @@ SCENARIO("Vector comparison", "[vector]") { GIVEN("Two identical vectors") { - kstd::vector v1 = {1, 2, 3}; - kstd::vector v2 = {1, 2, 3}; + auto v1 = kstd::vector{1, 2, 3}; + auto v2 = kstd::vector{1, 2, 3}; WHEN("comparing for equality") { @@ -521,8 +574,8 @@ SCENARIO("Vector comparison", "[vector]") GIVEN("Two vectors of different sizes") { - kstd::vector v1 = {1, 2, 3}; - kstd::vector v2 = {1, 2, 3, 4}; + auto v1 = kstd::vector{1, 2, 3}; + auto v2 = kstd::vector{1, 2, 3, 4}; WHEN("comparing for equality") { @@ -549,8 +602,8 @@ SCENARIO("Vector comparison", "[vector]") GIVEN("Two vectors of the same size but different elements") { - kstd::vector v1 = {1, 2, 3}; - kstd::vector v2 = {1, 2, 4}; + auto v1 = kstd::vector{1, 2, 3}; + auto v2 = kstd::vector{1, 2, 4}; WHEN("comparing for ordering") { @@ -580,7 +633,7 @@ SCENARIO("Vector with non-default-constructible types", "[vector]") { WHEN("constructing an empty vector") { - kstd::vector v; + auto v = kstd::vector{}; THEN("the vector is empty") { @@ -590,7 +643,7 @@ SCENARIO("Vector with non-default-constructible types", "[vector]") WHEN("using emplace_back") { - kstd::vector v; + auto v = kstd::vector{}; v.emplace_back(42); THEN("the element is added and the size and capacity increase") @@ -632,10 +685,29 @@ struct tracking_allocator ::operator delete(p, n * sizeof(T)); } - bool operator==(tracking_allocator const & other) + bool operator==(tracking_allocator const & other) const { return allocation_count == other.allocation_count; } + + bool operator!=(tracking_allocator const & other) const + { + return allocation_count != other.allocation_count; + } +}; + +template +struct propagating_allocator : tracking_allocator +{ + using propagate_on_container_copy_assignment = std::true_type; + propagating_allocator(int * counter) + : tracking_allocator{counter} + {} + + template + propagating_allocator(propagating_allocator const & other) noexcept + : tracking_allocator{other.allocation_count} + {} }; SCENARIO("Vector with custom allocator", "[vector]") @@ -643,11 +715,11 @@ SCENARIO("Vector with custom allocator", "[vector]") GIVEN("a tracking allocator acting as the vector's memory manager") { auto allocations = 0; - tracking_allocator allocator{&allocations}; + auto allocator = tracking_allocator{&allocations}; WHEN("a vector uses this allocator to allocate memory") { - kstd::vector> v{allocator}; + auto v = kstd::vector>(allocator); REQUIRE(allocations == 0); v.reserve(10); @@ -707,8 +779,8 @@ SCENARIO("Vector modifier move semantics", "[vector]") { GIVEN("An empty vector and a move tracker element") { - kstd::vector v; - move_tracker tracker{42}; + auto v = kstd::vector{}; + auto tracker = move_tracker{42}; WHEN("push_back is called with the move tracker") { @@ -731,7 +803,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") GIVEN("An empty vector") { - kstd::vector v; + auto v = kstd::vector{}; WHEN("emplace_back is called with constructor arguments") { @@ -746,4 +818,387 @@ SCENARIO("Vector modifier move semantics", "[vector]") } } } -} \ No newline at end of file +} + +struct test_input_iterator +{ + using iterator_concept = std::input_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = int; + + int const * current; + int operator*() const + { + return *current; + } + test_input_iterator & operator++() + { + ++current; + return *this; + } + void operator++(int) + { + ++current; + } + bool operator==(test_input_iterator const & other) const + { + return current == other.current; + } +}; + +SCENARIO("Vector advanced construction", "[vector]") +{ + GIVEN("A count and a default value") + { + WHEN("constructing with count and default argument") + { + auto const count = 5uz; + auto const value = 42; + auto v = kstd::vector(count, value); + + THEN("the vector is initialized with count copies of value") + { + REQUIRE(v.size() == 5); + REQUIRE(v.capacity() == 5); + REQUIRE(v.front() == 42); + REQUIRE(v.back() == 42); + } + } + } + + GIVEN("A pure input iterator range") + { + WHEN("constructing from input iterators") + { + auto const arr = std::array{1, 2, 3}; + auto const first = test_input_iterator{arr.data()}; + auto const last = test_input_iterator{arr.data() + arr.size()}; + + auto v = kstd::vector(first, last); + + THEN("the vector is generated dynamically and initialized correctly") + { + REQUIRE(v.size() == 3); + REQUIRE(v[0] == 1); + REQUIRE(v[2] == 3); + } + } + } + + GIVEN("A tracking allocator and a source vector") + { + auto allocs = 0; + auto allocator = tracking_allocator{&allocs}; + auto source = kstd::vector>{allocator}; + source.push_back(1); + source.push_back(2); + source.push_back(3); + + allocs = 0; + + WHEN("copy constructing with an allocator") + { + auto copy = kstd::vector>(source, allocator); + + THEN("the copy succeeds and the allocator is used") + { + REQUIRE(copy.size() == 3); + REQUIRE(allocs > 0); + REQUIRE(copy[0] == 1); + REQUIRE(copy[2] == 3); + } + } + + WHEN("move constructing with an identically comparing allocator") + { + auto moved = kstd::vector>(std::move(source), allocator); + + THEN("the move succeeds and no new allocations are made (memory is stolen)") + { + REQUIRE(moved.size() == 3); + REQUIRE(allocs == 0); + REQUIRE(moved[0] == 1); + REQUIRE(moved[2] == 3); + } + } + + WHEN("move constructing with a non-equal allocator") + { + auto allocs2 = 0; + auto allocator2 = tracking_allocator{&allocs2}; + + auto moved = kstd::vector>(std::move(source), allocator2); + + THEN("the move allocates new memory and moves elements") + { + REQUIRE(allocs2 > 0); + REQUIRE(moved.size() == 3); + REQUIRE(source.empty()); + } + } + } +} + +SCENARIO("Vector assignment operators", "[vector]") +{ + GIVEN("A source vector and an empty target vector") + { + auto source = kstd::vector{1, 2, 3}; + auto target = kstd::vector{}; + + WHEN("copy assigning") + { + target = source; + + THEN("the target matches the source") + { + REQUIRE(target.size() == 3); + REQUIRE(target[0] == 1); + REQUIRE(target[2] == 3); + REQUIRE(source.size() == 3); + } + } + + WHEN("move assigning") + { + target = std::move(source); + + THEN("the target assumes the source's data") + { + REQUIRE(target.size() == 3); + REQUIRE(target[0] == 1); + REQUIRE(target[2] == 3); + REQUIRE(source.empty()); + } + } + } + + GIVEN("Vectors with propagating copy allocator") + { + auto allocs1 = 0; + auto allocs2 = 0; + auto alloc1 = propagating_allocator{&allocs1}; + auto alloc2 = propagating_allocator{&allocs2}; + + auto v1 = kstd::vector>{alloc1}; + v1.push_back(1); + v1.push_back(2); + auto v2 = kstd::vector>{alloc2}; + + WHEN("copy assigning") + { + v2 = v1; + THEN("the allocator propagates") + { + REQUIRE(v2.get_allocator() == v1.get_allocator()); + } + } + } + + GIVEN("Vectors for copy assignment overlap") + { + auto v1 = kstd::vector{1, 2, 3}; + auto v2 = kstd::vector{4, 5}; + v2.reserve(10); + + WHEN("copy assigning a larger vector to a smaller one with enough capacity") + { + v2 = v1; + THEN("elements are copied and size is updated") + { + REQUIRE(v2.size() == 3); + REQUIRE(v2.capacity() >= 10); + REQUIRE(v2[2] == 3); + } + } + + auto v3 = kstd::vector{1, 2, 3, 4}; + v3.reserve(10); + WHEN("copy assigning a smaller vector to a larger one") + { + v3 = v1; + THEN("excess elements are destroyed") + { + REQUIRE(v3.size() == 3); + REQUIRE(v3[0] == 1); + } + } + } + + GIVEN("Vectors with the same tracking allocator") + { + auto allocs = 0; + auto alloc = tracking_allocator{&allocs}; + auto v1 = kstd::vector>{alloc}; + v1.push_back(1); + auto v2 = kstd::vector>{alloc}; + + WHEN("move assigning") + { + v2 = std::move(v1); + THEN("memory is stolen without allocation") + { + REQUIRE(v2.size() == 1); + REQUIRE(allocs == 1); + } + } + } + + GIVEN("Vectors with different non-propagating tracking allocators") + { + auto allocs1 = 0; + auto allocs2 = 0; + auto alloc1 = tracking_allocator{&allocs1}; + auto alloc2 = tracking_allocator{&allocs2}; + + auto v1 = kstd::vector>{alloc1}; + v1.push_back(1); + v1.push_back(2); + v1.push_back(3); + + auto v2 = kstd::vector>{alloc2}; + v2.push_back(4); + v2.push_back(5); + + WHEN("move assigning a larger vector to a smaller one without enough capacity") + { + v2.shrink_to_fit(); + v2 = std::move(v1); + THEN("memory is reallocated and elements are moved") + { + REQUIRE(v2.size() == 3); + REQUIRE(allocs2 > 2); + } + } + + auto v3 = kstd::vector>{alloc2}; + v3.reserve(10); + v3.push_back(4); + v3.push_back(5); + WHEN("move assigning a larger vector to a smaller one with enough capacity") + { + v3 = std::move(v1); + THEN("elements are move-assigned over overlap and move-constructed over remainder") + { + REQUIRE(v3.size() == 3); + } + } + + auto v4 = kstd::vector>{alloc2}; + v4.reserve(10); + v4.push_back(4); + v4.push_back(5); + v4.push_back(6); + v4.push_back(7); + WHEN("move assigning a smaller vector to a larger one with enough capacity") + { + v4 = std::move(v1); + THEN("overlap is moved and excess is destroyed") + { + REQUIRE(v4.size() == 3); + } + } + } +} + +SCENARIO("Vector self-assignment operators", "[vector]") +{ + GIVEN("A populated vector") + { + auto v = kstd::vector{1, 2, 3}; + auto const initial_capacity = v.capacity(); + auto const * initial_data = v.data(); + + WHEN("copy assigning to itself") + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-warning-option" +#pragma GCC diagnostic ignored "-Wself-assign-overloaded" + v = v; +#pragma GCC diagnostic pop + + THEN("the vector remains unchanged") + { + REQUIRE(v.size() == 3); + REQUIRE(v.capacity() == initial_capacity); + REQUIRE(v.data() == initial_data); + REQUIRE(v[0] == 1); + REQUIRE(v[2] == 3); + } + } + + WHEN("move assigning to itself") + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-warning-option" +#pragma GCC diagnostic ignored "-Wself-move" + v = std::move(v); +#pragma GCC diagnostic pop + + THEN("the vector remains unchanged") + { + REQUIRE(v.size() == 3); + REQUIRE(v.capacity() == initial_capacity); + REQUIRE(v.data() == initial_data); + REQUIRE(v[0] == 1); + REQUIRE(v[2] == 3); + } + } + } +} + +SCENARIO("Vector const accessors and copy insertion", "[vector]") +{ + GIVEN("A const populated vector") + { + auto const v = kstd::vector{10, 20, 30}; + + WHEN("calling const accessors") + { + THEN("elements are read correctly as const references") + { + REQUIRE(v.front() == 10); + REQUIRE(v.back() == 30); + REQUIRE(v[1] == 20); + REQUIRE(v.at(1) == 20); + } + } + } + + GIVEN("An empty vector and a const lvalue tracker") + { + auto v = kstd::vector{}; + auto const tracker = move_tracker{42}; + + WHEN("push_back is called with the const lvalue") + { + v.push_back(tracker); + + THEN("the element is gracefully copy-constructed") + { + REQUIRE(v.size() == 1); + REQUIRE(v.back().value == 42); + REQUIRE(v.back().was_copied); + REQUIRE_FALSE(v.back().was_moved); + } + } + + WHEN("push_back is called with a const lvalue when capacity is sufficient") + { + v.reserve(10); + auto const current_capacity = v.capacity(); + v.push_back(tracker); + + THEN("the element is copy-constructed without reallocation") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() == current_capacity); + REQUIRE(v.back().value == 42); + REQUIRE(v.back().was_copied); + REQUIRE_FALSE(v.back().was_moved); + } + } + } +} -- cgit v1.2.3 From 42abbf23256c69398d660dd52ce88f16242f6cf3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 23 Mar 2026 12:24:11 +0100 Subject: ci: handle 100% coverage --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cf61007..5abf210 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,7 +34,7 @@ bht: - ctest --preset bht-dbg - lcov --config-file .lcovrc --capture --directory $(pwd) --output-file coverage.info - lcov --config-file .lcovrc --list coverage.info - coverage: '/Total:\|(\d+\.?\d+)\%/' + coverage: '/Total:\|(\d+(\.\d+)?)\%/' artifacts: paths: - coverage.info -- cgit v1.2.3 From 3b5f694af4c366e6e699800d9b2fbcb977f1612d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 23 Mar 2026 12:27:16 +0100 Subject: ci: allow for whitespace in coverage regex --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5abf210..094cf91 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -34,7 +34,7 @@ bht: - ctest --preset bht-dbg - lcov --config-file .lcovrc --capture --directory $(pwd) --output-file coverage.info - lcov --config-file .lcovrc --list coverage.info - coverage: '/Total:\|(\d+(\.\d+)?)\%/' + coverage: '/Total:\|\s*(\d+(\.\d+)?)\%/' artifacts: paths: - coverage.info -- cgit v1.2.3 From f7b065d72684526aedd580cb564f6d010653a22e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 23 Mar 2026 12:48:52 +0100 Subject: kstd/tests: extract test helper types --- libs/kstd/tests/include/kstd/tests/test_types.hpp | 251 ++++++++++++++++++++++ libs/kstd/tests/src/vector.cpp | 199 +++-------------- 2 files changed, 283 insertions(+), 167 deletions(-) create mode 100644 libs/kstd/tests/include/kstd/tests/test_types.hpp diff --git a/libs/kstd/tests/include/kstd/tests/test_types.hpp b/libs/kstd/tests/include/kstd/tests/test_types.hpp new file mode 100644 index 0000000..6a06311 --- /dev/null +++ b/libs/kstd/tests/include/kstd/tests/test_types.hpp @@ -0,0 +1,251 @@ +#ifndef KSTD_TESTS_TEST_TYPES_HPP +#define KSTD_TESTS_TEST_TYPES_HPP + +#include +#include +#include + +namespace kstd::tests +{ + + //! A type tracking copy and move operations + //! + //! This type is designed to test move and copy semantics of standard library containers implemented in kstd. + struct move_tracker + { + //! A value indicating that the object was moved from. + constexpr auto static moved_from_v = -1; + + //! A simple value to be able to track the move-from state. + int value{}; + //! A flag to track if an instance of this type was either copy constructed or copy assigned. + bool was_copied{false}; + //! A flag to track if an instance of this type was either move constructed or move assigned. + bool was_moved{false}; + + //! Construct a new move tracker with the given value, if any. + constexpr move_tracker(int v = 0) + : value{v} + {} + + //! Construct a new move tracker by copying an existing one. + constexpr move_tracker(move_tracker const & other) + : value{other.value} + , was_copied{true} + {} + + //! Construct a new move tracker by moving from an existing one. + constexpr move_tracker(move_tracker && other) noexcept + : value{other.value} + , was_moved{true} + { + other.value = moved_from_v; + } + + //! Copy assign a new move tracker from an existing one. + constexpr auto operator=(move_tracker const & other) -> move_tracker & + { + if (this != &other) + { + value = other.value; + was_copied = true; + } + return *this; + } + + //! Move assign a new move tracker from an existing one. + //! + //! This function ensures that the moved-from state is marked. + constexpr auto operator=(move_tracker && other) noexcept -> move_tracker & + { + if (this != &other) + { + value = other.value; + was_moved = true; + other.value = moved_from_v; + } + return *this; + } + }; + + //! A type that is not default constructible. + //! + //! This type is designed to test default construction semantics of standard library containers implemented in kstd. + struct non_default_constructible + { + //! A simple placeholder value. + int value{}; + + //! Construct a new non-default-constructible object with the given value. + constexpr explicit non_default_constructible(int v) + : value{v} + {} + + //! Compare two non-default-constructible objects for equality. + [[nodiscard]] constexpr auto operator==(non_default_constructible const & other) const -> bool + { + return value == other.value; + } + }; + + //! An allocator that tracks the number of allocations. + //! + //! This allocator is designed to test allocation semantics of standard library containers implemented in kstd. + //! + //! @tparam T The type of the elements to be allocated. + template + struct tracking_allocator + { + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + //! A pointer to a counter that is incremented on allocation. + //! + //! A pointer is used so that multiple allocators can share the same counter. + int * allocation_count{}; + + //! Construct a new tracking allocator referencing the given counter. + tracking_allocator(int * counter) + : allocation_count{counter} + {} + + //! Construct a new tracking allocator by copying from another tracking allocator. + //! + //! This constructor is templated to allow for conversion from allocators of different types. + //! + //! @tparam U The type of the elements to be allocated by the other allocator. + template + tracking_allocator(tracking_allocator const & other) noexcept + : allocation_count{other.allocation_count} + {} + + //! Allocate memory for n elements of type T. + //! + //! @param n The number of elements to allocate. + //! @return A pointer to the allocated memory. + [[nodiscard]] auto allocate(std::size_t n) -> T * + { + if (allocation_count != nullptr) + { + ++(*allocation_count); + } + return static_cast(::operator new(n * sizeof(T))); + } + + //! Deallocate memory for n elements of type T. + //! + //! @param p A pointer to the memory to deallocate. + //! @param n The number of elements to deallocate. + auto deallocate(T * p, std::size_t n) noexcept -> void + { + ::operator delete(p, n * sizeof(T)); + } + + //! Compare two tracking allocators for equality. + //! + //! Two allocators are considered equal if they reference the same allocation counter. + //! + //! @param other The other tracking allocator to compare to. + //! @return True if the two tracking allocators are equal, false otherwise. + [[nodiscard]] auto operator==(tracking_allocator const & other) const -> bool + { + return allocation_count == other.allocation_count; + } + + //! Compare two tracking_allocators for inequality. + //! + //! @param other The other tracking_allocator to compare to. + //! @return True if the two tracking_allocators are not equal, false otherwise. + [[nodiscard]] auto operator!=(tracking_allocator const & other) const -> bool + { + return allocation_count != other.allocation_count; + } + }; + + //! An allocator that propagates copy assignment. + //! + //! This allocator is designed to test copy assignment semantics of standard library containers implemented in kstd. + //! + //! @tparam T The type of the elements to be allocated. + template + struct propagating_allocator : tracking_allocator + { + //! A flag to indicate that the allocator propagates copy assignment. + using propagate_on_container_copy_assignment = std::true_type; + + //! Construct a new propagating allocator referencing the given counter. + //! + //! @param counter A pointer to a counter that is incremented on allocation. + //! @see tracking_allocator::tracking_allocator(int*) + propagating_allocator(int * counter) + : tracking_allocator{counter} + {} + + //! Construct a new propagating allocator by copying from another propagating allocator. + //! + //! This constructor is templated to allow for conversion from allocators of different types. + //! + //! @tparam U The type of the elements to be allocated by the other allocator. + //! @see tracking_allocator::tracking_allocator(tracking_allocator const&) + template + propagating_allocator(propagating_allocator const & other) noexcept + : tracking_allocator{other.allocation_count} + {} + }; + + //! A test input iterator. + //! + //! This iterator is designed to test input iterator semantics of standard library containers implemented in kstd. + struct test_input_iterator + { + using iterator_concept = std::input_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = int; + + //! The current element pointed to by the iterator. + int const * current; + + //! Construct a new test input iterator. + //! + //! @param current The current element pointed to by the iterator. + constexpr explicit test_input_iterator(int const * current) + : current{current} + {} + + //! Dereference the iterator to get the current element. + //! + //! @return The current element pointed to by the iterator. + [[nodiscard]] auto operator*() const -> int + { + return *current; + } + + //! Increment the iterator to point to the next element. + //! + //! @return A reference to the incremented iterator. + auto operator++() -> test_input_iterator & + { + ++current; + return *this; + } + + //! Increment the iterator to point to the next element. + auto operator++(int) -> void + { + ++*this; + } + + //! Compare two test input iterators for equality. + //! + //! @param other The other test input iterator to compare to. + //! @return True if the two test input iterators are equal, false otherwise. + [[nodiscard]] auto operator==(test_input_iterator const & other) const -> bool + { + return current == other.current; + } + }; + +} // namespace kstd::tests + +#endif diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 713f6ab..d4c5e4f 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -1,12 +1,12 @@ #include "kstd/tests/os_panic.hpp" #include +#include #include #include #include -#include #include #include #include @@ -616,24 +616,13 @@ SCENARIO("Vector comparison", "[vector]") } } -struct non_default_constructible -{ - int value; - non_default_constructible() = delete; - explicit non_default_constructible(int v) - : value{v} - {} - - bool operator==(non_default_constructible const & other) const noexcept = default; -}; - SCENARIO("Vector with non-default-constructible types", "[vector]") { GIVEN("A type without a default constructor") { WHEN("constructing an empty vector") { - auto v = kstd::vector{}; + auto v = kstd::vector{}; THEN("the vector is empty") { @@ -643,83 +632,28 @@ SCENARIO("Vector with non-default-constructible types", "[vector]") WHEN("using emplace_back") { - auto v = kstd::vector{}; + auto v = kstd::vector{}; v.emplace_back(42); THEN("the element is added and the size and capacity increase") { REQUIRE(v.size() == 1); - REQUIRE(v.back() == non_default_constructible{42}); + REQUIRE(v.back() == kstd::tests::non_default_constructible{42}); } } } } -template -struct tracking_allocator -{ - using value_type = T; - - int * allocation_count; - - tracking_allocator(int * counter) - : allocation_count{counter} - {} - - template - tracking_allocator(tracking_allocator const & other) noexcept - : allocation_count{other.allocation_count} - {} - - T * allocate(std::size_t n) - { - if (allocation_count) - { - (*allocation_count)++; - } - return static_cast(::operator new(n * sizeof(T))); - } - - void deallocate(T * p, std::size_t n) noexcept - { - ::operator delete(p, n * sizeof(T)); - } - - bool operator==(tracking_allocator const & other) const - { - return allocation_count == other.allocation_count; - } - - bool operator!=(tracking_allocator const & other) const - { - return allocation_count != other.allocation_count; - } -}; - -template -struct propagating_allocator : tracking_allocator -{ - using propagate_on_container_copy_assignment = std::true_type; - propagating_allocator(int * counter) - : tracking_allocator{counter} - {} - - template - propagating_allocator(propagating_allocator const & other) noexcept - : tracking_allocator{other.allocation_count} - {} -}; - SCENARIO("Vector with custom allocator", "[vector]") { GIVEN("a tracking allocator acting as the vector's memory manager") { auto allocations = 0; - auto allocator = tracking_allocator{&allocations}; + auto allocator = kstd::tests::tracking_allocator{&allocations}; WHEN("a vector uses this allocator to allocate memory") { - auto v = kstd::vector>(allocator); + auto v = kstd::vector>(allocator); REQUIRE(allocations == 0); v.reserve(10); @@ -732,55 +666,12 @@ SCENARIO("Vector with custom allocator", "[vector]") } } -struct move_tracker -{ - int value; - bool was_copied{}; - bool was_moved{}; - - move_tracker() = default; - explicit move_tracker(int v) - : value{v} - {} - - move_tracker(move_tracker const & other) - : value{other.value} - , was_copied{true} - , was_moved{false} - {} - - move_tracker(move_tracker && other) noexcept - : value{other.value} - , was_copied{false} - , was_moved{true} - { - other.value = -1; - } - - move_tracker & operator=(move_tracker const & other) - { - value = other.value; - was_copied = true; - was_moved = false; - return *this; - } - - move_tracker & operator=(move_tracker && other) noexcept - { - value = other.value; - was_moved = true; - was_copied = false; - other.value = -1; - return *this; - } -}; - SCENARIO("Vector modifier move semantics", "[vector]") { GIVEN("An empty vector and a move tracker element") { - auto v = kstd::vector{}; - auto tracker = move_tracker{42}; + auto v = kstd::vector{}; + auto tracker = kstd::tests::move_tracker{42}; WHEN("push_back is called with the move tracker") { @@ -803,7 +694,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") GIVEN("An empty vector") { - auto v = kstd::vector{}; + auto v = kstd::vector{}; WHEN("emplace_back is called with constructor arguments") { @@ -820,32 +711,6 @@ SCENARIO("Vector modifier move semantics", "[vector]") } } -struct test_input_iterator -{ - using iterator_concept = std::input_iterator_tag; - using difference_type = std::ptrdiff_t; - using value_type = int; - - int const * current; - int operator*() const - { - return *current; - } - test_input_iterator & operator++() - { - ++current; - return *this; - } - void operator++(int) - { - ++current; - } - bool operator==(test_input_iterator const & other) const - { - return current == other.current; - } -}; - SCENARIO("Vector advanced construction", "[vector]") { GIVEN("A count and a default value") @@ -871,8 +736,8 @@ SCENARIO("Vector advanced construction", "[vector]") WHEN("constructing from input iterators") { auto const arr = std::array{1, 2, 3}; - auto const first = test_input_iterator{arr.data()}; - auto const last = test_input_iterator{arr.data() + arr.size()}; + auto const first = kstd::tests::test_input_iterator{arr.data()}; + auto const last = kstd::tests::test_input_iterator{arr.data() + arr.size()}; auto v = kstd::vector(first, last); @@ -888,8 +753,8 @@ SCENARIO("Vector advanced construction", "[vector]") GIVEN("A tracking allocator and a source vector") { auto allocs = 0; - auto allocator = tracking_allocator{&allocs}; - auto source = kstd::vector>{allocator}; + auto allocator = kstd::tests::tracking_allocator{&allocs}; + auto source = kstd::vector>{allocator}; source.push_back(1); source.push_back(2); source.push_back(3); @@ -898,7 +763,7 @@ SCENARIO("Vector advanced construction", "[vector]") WHEN("copy constructing with an allocator") { - auto copy = kstd::vector>(source, allocator); + auto copy = kstd::vector>(source, allocator); THEN("the copy succeeds and the allocator is used") { @@ -911,7 +776,7 @@ SCENARIO("Vector advanced construction", "[vector]") WHEN("move constructing with an identically comparing allocator") { - auto moved = kstd::vector>(std::move(source), allocator); + auto moved = kstd::vector>(std::move(source), allocator); THEN("the move succeeds and no new allocations are made (memory is stolen)") { @@ -925,9 +790,9 @@ SCENARIO("Vector advanced construction", "[vector]") WHEN("move constructing with a non-equal allocator") { auto allocs2 = 0; - auto allocator2 = tracking_allocator{&allocs2}; + auto allocator2 = kstd::tests::tracking_allocator{&allocs2}; - auto moved = kstd::vector>(std::move(source), allocator2); + auto moved = kstd::vector>(std::move(source), allocator2); THEN("the move allocates new memory and moves elements") { @@ -977,13 +842,13 @@ SCENARIO("Vector assignment operators", "[vector]") { auto allocs1 = 0; auto allocs2 = 0; - auto alloc1 = propagating_allocator{&allocs1}; - auto alloc2 = propagating_allocator{&allocs2}; + auto alloc1 = kstd::tests::propagating_allocator{&allocs1}; + auto alloc2 = kstd::tests::propagating_allocator{&allocs2}; - auto v1 = kstd::vector>{alloc1}; + auto v1 = kstd::vector>{alloc1}; v1.push_back(1); v1.push_back(2); - auto v2 = kstd::vector>{alloc2}; + auto v2 = kstd::vector>{alloc2}; WHEN("copy assigning") { @@ -1028,10 +893,10 @@ SCENARIO("Vector assignment operators", "[vector]") GIVEN("Vectors with the same tracking allocator") { auto allocs = 0; - auto alloc = tracking_allocator{&allocs}; - auto v1 = kstd::vector>{alloc}; + auto alloc = kstd::tests::tracking_allocator{&allocs}; + auto v1 = kstd::vector>{alloc}; v1.push_back(1); - auto v2 = kstd::vector>{alloc}; + auto v2 = kstd::vector>{alloc}; WHEN("move assigning") { @@ -1048,15 +913,15 @@ SCENARIO("Vector assignment operators", "[vector]") { auto allocs1 = 0; auto allocs2 = 0; - auto alloc1 = tracking_allocator{&allocs1}; - auto alloc2 = tracking_allocator{&allocs2}; + auto alloc1 = kstd::tests::tracking_allocator{&allocs1}; + auto alloc2 = kstd::tests::tracking_allocator{&allocs2}; - auto v1 = kstd::vector>{alloc1}; + auto v1 = kstd::vector>{alloc1}; v1.push_back(1); v1.push_back(2); v1.push_back(3); - auto v2 = kstd::vector>{alloc2}; + auto v2 = kstd::vector>{alloc2}; v2.push_back(4); v2.push_back(5); @@ -1071,7 +936,7 @@ SCENARIO("Vector assignment operators", "[vector]") } } - auto v3 = kstd::vector>{alloc2}; + auto v3 = kstd::vector>{alloc2}; v3.reserve(10); v3.push_back(4); v3.push_back(5); @@ -1084,7 +949,7 @@ SCENARIO("Vector assignment operators", "[vector]") } } - auto v4 = kstd::vector>{alloc2}; + auto v4 = kstd::vector>{alloc2}; v4.reserve(10); v4.push_back(4); v4.push_back(5); @@ -1169,8 +1034,8 @@ SCENARIO("Vector const accessors and copy insertion", "[vector]") GIVEN("An empty vector and a const lvalue tracker") { - auto v = kstd::vector{}; - auto const tracker = move_tracker{42}; + auto v = kstd::vector{}; + auto const tracker = kstd::tests::move_tracker{42}; WHEN("push_back is called with the const lvalue") { -- cgit v1.2.3 From 40ab04b8f16d36bb0858d6ad8f4aa7a12d970cb5 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 23 Mar 2026 12:52:02 +0100 Subject: build: simplify switching between overall targets --- CMakePresets.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakePresets.json b/CMakePresets.json index 3445119..2ea281f 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -4,7 +4,7 @@ { "name": "base", "hidden": true, - "binaryDir": "${sourceDir}/build", + "binaryDir": "${sourceDir}/build/${presetName}", "generator": "Ninja Multi-Config", "cacheVariables": { "CMAKE_CONFIGURATION_TYPES": "Debug;MinSizeRel", -- cgit v1.2.3 From efbbe84719b85794ea2795ccd120c081a0575490 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 23 Mar 2026 13:01:04 +0100 Subject: ci: fix main build artifact extraction --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 094cf91..7b5d12f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ build: script: - cmake --preset $PLATFORM - cmake --build --preset $PLATFORM-$TYPE - - cp build/bin/**/kernel.{dis,elf,sym,iso} . + - cp build/${PLATFORM}/bin/**/kernel.{dis,elf,sym,iso} . artifacts: paths: - kernel.dis -- cgit v1.2.3 From e6733ef652d408ca907a866ad8d6ba87b95fe0e7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 23 Mar 2026 13:07:30 +0100 Subject: ide: ensure clangd sees active compile commands --- .vscode/settings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 21f4885..a4f7aef 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,7 @@ "cmake.useCMakePresets": "always", "cmake.options.statusBarVisibility": "visible", "cmake.ctest.testExplorerIntegrationEnabled": false, + "cmake.copyCompileCommands": "${workspaceFolder}/build/compile_commands.json", "clangd.arguments": [ "--compile-commands-dir=${workspaceFolder}/build", "--query-driver=**/x86_64-pc-elf-g++", -- cgit v1.2.3 From f08512d1cddcf43de64f0b3f8b0e6c9fe31bcaea Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 24 Mar 2026 13:03:15 +0100 Subject: kstd/vector: add basic insert overloads --- libs/kstd/include/kstd/vector | 130 +++++++++++++++++++++++++-------- libs/kstd/tests/src/vector.cpp | 160 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 262 insertions(+), 28 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 5655854..0d4aac8 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -259,7 +259,7 @@ namespace kstd if (capacity() >= other.size()) { auto const overlap = std::min(m_size, other.m_size); - copy_elements(other.begin(), begin(), overlap); + std::ranges::copy(other.begin(), other.begin() + overlap, begin()); if (m_size < other.m_size) { @@ -317,7 +317,7 @@ namespace kstd if (capacity() >= other.size()) { auto const overlap = std::min(size(), other.size()); - move_elements(other.begin(), begin(), overlap); + std::ranges::move(other.begin(), other.begin() + overlap, begin()); if (size() < other.size()) { @@ -590,6 +590,106 @@ namespace kstd m_size = 0; } + //! Insert an element at a given position. + //! + //! @param position The position to insert the element at. + //! @param value The value to insert. + //! @return An iterator to the inserted element. + constexpr auto insert(const_iterator position, value_type const & value) -> iterator + { + auto prefix_size = std::ranges::distance(begin(), position); + auto suffix_size = std::ranges::distance(position, end()); + + if (position == end()) + { + push_back(value); + return begin() + prefix_size; + } + + if (m_capacity == m_size) + { + auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + std::allocator_traits::construct(m_allocator, new_data + prefix_size, value); + uninitialized_move_with_allocator(begin(), new_data, prefix_size); + uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); + destroy_n(begin(), old_size); + deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size; + } + else if (&value >= begin() && &value < end()) + { + auto value_copy = value; + auto insert_position = begin() + prefix_size; + std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); + std::ranges::move_backward(insert_position, end() - 1, end()); + *insert_position = std::move(value_copy); + } + else + { + auto insert_position = begin() + prefix_size; + std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); + std::ranges::move_backward(insert_position, end() - 1, end()); + *insert_position = value; + } + + ++m_size; + return begin() + prefix_size; + } + + //! Insert an element at a given position. + //! + //! @param position The position to insert the element at. + //! @param value The value to insert. + //! @return An iterator to the inserted element. + constexpr auto insert(const_iterator position, value_type && value) -> iterator + { + auto prefix_size = std::ranges::distance(begin(), position); + auto suffix_size = std::ranges::distance(position, end()); + + if (position == end()) + { + push_back(std::move(value)); + return begin() + prefix_size; + } + + if (m_capacity == m_size) + { + auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + std::allocator_traits::construct(m_allocator, new_data + prefix_size, std::move(value)); + uninitialized_move_with_allocator(begin(), new_data, prefix_size); + uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); + destroy_n(begin(), old_size); + deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size; + } + else if (&value >= begin() && &value < end()) + { + auto value_copy = std::move(value); + auto insert_position = begin() + prefix_size; + std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); + std::ranges::move_backward(insert_position, end() - 1, end()); + *insert_position = std::move(value_copy); + } + else + { + auto insert_position = begin() + prefix_size; + std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); + std::ranges::move_backward(insert_position, end() - 1, end()); + *insert_position = std::move(value); + } + + ++m_size; + return begin() + prefix_size; + } + //! Append a given element to this vector via copy construction. constexpr auto push_back(value_type const & value) -> void { @@ -690,19 +790,6 @@ namespace kstd deallocate(); } - //! Copy a number of elements from one storage location to another. - //! - //! @param from The start of the source range. - //! @param to The start of the target range. - //! @param count The number of element to copy. - constexpr auto static copy_elements(const_iterator from, iterator to, size_type count) -> void - { - for (auto i = 0uz; i < count; ++i) - { - *to++ = *from++; - } - } - //! Release the memory of this vector. constexpr auto deallocate() { @@ -726,19 +813,6 @@ namespace kstd }); } - //! Move a number of elements from one storage location to another. - //! - //! @param from The start of the source range. - //! @param to The start of the target range. - //! @param count The number of element to copy. - constexpr auto static move_elements(iterator from, iterator to, size_t count) - { - for (auto i = 0uz; i < count; ++i) - { - *to++ = std::move(*from++); - } - } - //! Panic the kernel if the given index is out of bounds. //! //! @param index The index to check. diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index d4c5e4f..0735c9a 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -462,6 +462,19 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE(v[1] == 20); } } + + WHEN("inserting an element") + { + auto it = v.insert(v.cbegin(), 42); + + THEN("the size and capacity increase and the element is inserted") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v[0] == 42); + REQUIRE(it == v.begin()); + } + } } GIVEN("A populated vector") @@ -538,6 +551,104 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE(v.capacity() == initial_capacity); } } + + WHEN("inserting at the beginning") + { + auto it = v.insert(v.cbegin(), 5); + + THEN("the element is inserted at the front") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 5); + REQUIRE(v[1] == 10); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting in the middle") + { + auto it = v.insert(v.cbegin() + 1, 15); + + THEN("the element is inserted in the middle") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 15); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting at the end") + { + auto it = v.insert(v.cend(), 40); + + THEN("the element is inserted at the back") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 40); + REQUIRE(it == v.begin() + 3); + } + } + + WHEN("inserting when capacity is sufficient") + { + v.reserve(10); + auto const capacity = v.capacity(); + + auto it = v.insert(v.cbegin() + 1, 15); + + THEN("the element is added without reallocation") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() == capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 15); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting a reference to an existing element with reallocation") + { + v.shrink_to_fit(); + REQUIRE(v.capacity() == v.size()); + auto it = v.insert(v.cbegin() + 1, v[2]); + + THEN("the element is correctly copied and inserted") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting a reference to an existing element without reallocation") + { + v.reserve(10); + REQUIRE(v.capacity() > v.size()); + auto it = v.insert(v.cbegin() + 1, v[2]); + + THEN("the element is correctly copied and inserted") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } } } @@ -709,6 +820,55 @@ SCENARIO("Vector modifier move semantics", "[vector]") } } } + + GIVEN("A populated vector of move trackers") + { + auto v = kstd::vector{}; + v.reserve(10); + v.emplace_back(10); + v.emplace_back(20); + v.emplace_back(30); + + WHEN("inserting an element in the middle with sufficient capacity") + { + auto const tracker = kstd::tests::move_tracker{15}; + v.insert(v.cbegin() + 1, tracker); + + THEN("the shifted elements are move-assigned and the new element is copy-assigned") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0].value == 10); + REQUIRE_FALSE(v[0].was_moved); + REQUIRE(v[1].value == 15); + REQUIRE(v[1].was_copied); + REQUIRE_FALSE(v[1].was_moved); + REQUIRE(v[2].value == 20); + REQUIRE(v[2].was_moved); + REQUIRE(v[3].value == 30); + REQUIRE(v[3].was_moved); + } + } + + WHEN("inserting an rvalue element in the middle with sufficient capacity") + { + auto tracker = kstd::tests::move_tracker{15}; + v.insert(v.cbegin() + 1, std::move(tracker)); + + THEN("the shifted elements are move-assigned and the new element is move-assigned") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0].value == 10); + REQUIRE_FALSE(v[0].was_moved); + REQUIRE(v[1].value == 15); + REQUIRE_FALSE(v[1].was_copied); + REQUIRE(v[1].was_moved); + REQUIRE(v[2].value == 20); + REQUIRE(v[2].was_moved); + REQUIRE(v[3].value == 30); + REQUIRE(v[3].was_moved); + } + } + } } SCENARIO("Vector advanced construction", "[vector]") -- cgit v1.2.3 From b51fd5e5087b3a6f34d53068c76a67eeda30ff21 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 13:20:50 +0000 Subject: build: update llvm components in container --- .devcontainer/x86-64/devcontainer.json | 4 ++-- .gitlab-ci.yml | 6 +++--- .vscode/settings.json | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.devcontainer/x86-64/devcontainer.json b/.devcontainer/x86-64/devcontainer.json index f0059ac..00e33ad 100644 --- a/.devcontainer/x86-64/devcontainer.json +++ b/.devcontainer/x86-64/devcontainer.json @@ -1,11 +1,11 @@ { "name": "TeachOS on x86-64", - "image": "registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-3", + "image": "registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-4", "features": { "ghcr.io/devcontainers/features/git:1": {}, "ghcr.io/devcontainers/features/git-lfs:1": {}, "ghcr.io/devcontainers-extra/features/apt-packages:1": { - "packages": "clang-tidy-21,cmake,grub2-common,grub-pc,mtools,ninja-build,qemu-system-x86,ssh,xorriso" + "packages": "build-essential,clang-tidy,clangd,cmake,grub2-common,grub-pc,mtools,ninja-build,qemu-system-x86,ssh,xorriso" } }, "customizations": { diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7b5d12f..05bba19 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,10 +6,10 @@ build: stage: build - image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-3 + image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-4 before_script: - apt update - - apt install -y clang-tidy-21 cmake grub2-common grub-pc mtools ninja-build xorriso + - apt install -y clang-tidy cmake grub2-common grub-pc mtools ninja-build xorriso script: - cmake --preset $PLATFORM - cmake --build --preset $PLATFORM-$TYPE @@ -24,7 +24,7 @@ build: bht: stage: build - image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-3 + image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-4 before_script: - apt update - apt install -y build-essential cmake ninja-build lcov libcatch2-dev diff --git a/.vscode/settings.json b/.vscode/settings.json index a4f7aef..4271526 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,7 @@ "cmake.options.statusBarVisibility": "visible", "cmake.ctest.testExplorerIntegrationEnabled": false, "cmake.copyCompileCommands": "${workspaceFolder}/build/compile_commands.json", + "clangd.path": "clangd", "clangd.arguments": [ "--compile-commands-dir=${workspaceFolder}/build", "--query-driver=**/x86_64-pc-elf-g++", -- cgit v1.2.3 From 4d3b5d9115aa896c0ed12758ff0fc9a16ebc8df2 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 14:27:03 +0100 Subject: ide: enable doxygen-style comment parsing --- .clangd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.clangd b/.clangd index 556b73c..996361d 100644 --- a/.clangd +++ b/.clangd @@ -4,6 +4,8 @@ Diagnostics: CompileFlags: Remove: - -fcondition-coverage +Documentation: + CommentFormat: Doxygen --- If: -- cgit v1.2.3 From 98b6633ea8e961f8668259dbd4970330494408d5 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 19 Mar 2026 21:32:38 +0100 Subject: prepare files for new inode structure --- kernel/CMakeLists.txt | 2 ++ kernel/include/kernel/filesystem/device_inode.hpp | 14 ++++++++++++++ kernel/include/kernel/filesystem/ext2/ext2_inode.hpp | 14 ++++++++++++++ kernel/src/filesystem/device_inode.cpp | 6 ++++++ kernel/src/filesystem/ext2/ext2_inode.cpp | 6 ++++++ 5 files changed, 42 insertions(+) create mode 100644 kernel/include/kernel/filesystem/device_inode.hpp create mode 100644 kernel/include/kernel/filesystem/ext2/ext2_inode.hpp create mode 100644 kernel/src/filesystem/device_inode.cpp create mode 100644 kernel/src/filesystem/ext2/ext2_inode.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 398022c..a36eac0 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -22,8 +22,10 @@ add_executable("kernel" "src/devices/storage/ram_disk/ram_disk_controller.cpp" "src/devices/storage/ram_disk/ram_disk_device.cpp" "src/filesystem/ext2/ext2_filesystem.cpp" + "src/filesystem/ext2/ext2_inode.cpp" "src/filesystem/custody.cpp" "src/filesystem/device_file.cpp" + "src/filesystem/device_inode.cpp" "src/filesystem/file_descriptor_table.cpp" "src/filesystem/filesystem.cpp" "src/filesystem/inode_file.cpp" diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp new file mode 100644 index 0000000..a99e5d1 --- /dev/null +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -0,0 +1,14 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP + +#include "kernel/filesystem/inode.hpp" + +namespace filesystem +{ + struct device_inode : inode + { + // TODO BA-FS26 + }; +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp new file mode 100644 index 0000000..7cd6f8f --- /dev/null +++ b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp @@ -0,0 +1,14 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP + +#include "kernel/filesystem/inode.hpp" + +namespace filesystem::ext2 +{ + struct ext2_inode : inode + { + // TODO BA-FS26 + }; +} // namespace filesystem::ext2 + +#endif \ No newline at end of file diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp new file mode 100644 index 0000000..b9018b1 --- /dev/null +++ b/kernel/src/filesystem/device_inode.cpp @@ -0,0 +1,6 @@ +#include "kernel/filesystem/device_inode.hpp" + +namespace filesystem +{ + // TODO BA-FS26: Implement device inode +} \ No newline at end of file diff --git a/kernel/src/filesystem/ext2/ext2_inode.cpp b/kernel/src/filesystem/ext2/ext2_inode.cpp new file mode 100644 index 0000000..ec68ee9 --- /dev/null +++ b/kernel/src/filesystem/ext2/ext2_inode.cpp @@ -0,0 +1,6 @@ +#include "kernel/filesystem/ext2/ext2_inode.hpp" + +namespace filesystem::ext2 +{ + // TODO BA-FS26: Implement ext2 inode +} \ No newline at end of file -- cgit v1.2.3 From 8d3471f1d160d301f9d990455bd8c63450df1cf3 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 19 Mar 2026 21:59:31 +0100 Subject: remove inode metadata --- kernel/include/kernel/filesystem/device_inode.hpp | 15 +++- kernel/include/kernel/filesystem/filesystem.hpp | 4 +- kernel/include/kernel/filesystem/inode.hpp | 23 +++--- .../include/kernel/filesystem/inode_metadata.hpp | 24 ------ kernel/src/filesystem/device_inode.cpp | 50 ++++++++++++- kernel/src/filesystem/ext2/ext2_filesystem.cpp | 3 +- kernel/src/filesystem/filesystem.cpp | 4 +- kernel/src/filesystem/inode.cpp | 85 ---------------------- kernel/src/filesystem/vfs.cpp | 6 +- 9 files changed, 82 insertions(+), 132 deletions(-) delete mode 100644 kernel/include/kernel/filesystem/inode_metadata.hpp diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index a99e5d1..0287875 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -1,13 +1,26 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP +#include "kernel/devices/device.hpp" #include "kernel/filesystem/inode.hpp" +#include + +#include + namespace filesystem { struct device_inode : inode { - // TODO BA-FS26 + explicit device_inode(kstd::shared_ptr const & device); + + [[nodiscard]] auto backing_device() const -> kstd::shared_ptr const &; + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + private: + kstd::shared_ptr m_device{}; }; } // namespace filesystem diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 23924c4..035f49d 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -17,10 +17,10 @@ namespace filesystem virtual auto mount(kstd::shared_ptr const & device) -> int = 0; virtual auto lookup(inode const & parent, std::string_view name) -> inode * = 0; - [[nodiscard]] auto root_inode() const -> inode const &; + [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; protected: - inode m_root_inode{}; // TODO BA-FS26 set during mount? + kstd::shared_ptr m_root_inode{}; }; } // namespace filesystem diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index a2955f9..dcf83b9 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -1,9 +1,6 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP -#include "kernel/devices/device.hpp" -#include "kernel/filesystem/inode_metadata.hpp" - #include #include @@ -12,26 +9,26 @@ namespace filesystem { struct inode { - inode() = default; + enum class inode_kind + { + regular, + directory, + device + }; + explicit inode(inode_kind kind); - explicit inode(kstd::shared_ptr const & device); - [[nodiscard]] auto metadata() const -> inode_metadata; + virtual ~inode() = default; [[nodiscard]] auto is_directory() const -> bool; [[nodiscard]] auto is_regular() const -> bool; [[nodiscard]] auto is_device() const -> bool; - [[nodiscard]] auto is_block_device() const -> bool; - [[nodiscard]] auto major_device() const -> size_t; - [[nodiscard]] auto minor_device() const -> size_t; - [[nodiscard]] auto backing_device() const -> kstd::shared_ptr const &; - auto read(void * buffer, size_t offset, size_t size) const -> size_t; - auto write(void const * buffer, size_t offset, size_t size) -> size_t; + virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0; + virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0; private: inode_kind m_kind{inode_kind::regular}; - kstd::shared_ptr m_device{}; }; } // namespace filesystem diff --git a/kernel/include/kernel/filesystem/inode_metadata.hpp b/kernel/include/kernel/filesystem/inode_metadata.hpp deleted file mode 100644 index ed5a09d..0000000 --- a/kernel/include/kernel/filesystem/inode_metadata.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP - -#include -#include - -namespace filesystem -{ - enum class inode_kind - { - regular, - directory, - device - }; - - struct inode_metadata - { - inode_kind kind{inode_kind::regular}; - std::optional major{}; - std::optional minor{}; - }; -} // namespace filesystem - -#endif // TEACH_OS_KERNEL_FILESYSTEM_INODE_METADATA_HPP \ No newline at end of file diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index b9018b1..18e3b24 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -1,6 +1,52 @@ #include "kernel/filesystem/device_inode.hpp" +#include "kapi/system.hpp" + +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/inode.hpp" + +#include + +#include + namespace filesystem { - // TODO BA-FS26: Implement device inode -} \ No newline at end of file + device_inode::device_inode(kstd::shared_ptr const & device) + : inode(inode_kind::device) + , m_device(device) + { + if (!device) + { + kapi::system::panic("[FILESYSTEM] device_inode constructed with null device."); + } + } + + auto device_inode::backing_device() const -> kstd::shared_ptr const & + { + return m_device; + } + + auto device_inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t + { + if (!m_device) + { + kapi::system::panic("[FILESYSTEM] device_inode has null device."); + } + + // TODO BA-FS26 use device file? + // return m_device->read(buffer, offset, size); + return 0; + } + + auto device_inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t + { + if (!m_device) + { + kapi::system::panic("[FILESYSTEM] device_inode has null device."); + } + + // TODO BA-FS26 use device file? + // return m_device->write(buffer, offset, size); + return 0; + } +} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/ext2/ext2_filesystem.cpp b/kernel/src/filesystem/ext2/ext2_filesystem.cpp index 408b292..87845d5 100644 --- a/kernel/src/filesystem/ext2/ext2_filesystem.cpp +++ b/kernel/src/filesystem/ext2/ext2_filesystem.cpp @@ -2,7 +2,6 @@ #include "kernel/devices/device.hpp" #include "kernel/filesystem/inode.hpp" -#include "kernel/filesystem/inode_metadata.hpp" #include @@ -19,7 +18,7 @@ namespace filesystem::ext2 m_device = device; // TODO BA-FS26 load proper root inode from ext2 metadata - m_root_inode = inode{inode_kind::directory}; + // m_root_inode = inode{inode_kind::directory}; // TODO BA-FS26 implement return 0; diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index 86b7940..50b5587 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -2,9 +2,11 @@ #include "kernel/filesystem/inode.hpp" +#include + namespace filesystem { - auto filesystem::root_inode() const -> inode const & + auto filesystem::root_inode() const -> kstd::shared_ptr const & { return m_root_inode; } diff --git a/kernel/src/filesystem/inode.cpp b/kernel/src/filesystem/inode.cpp index af73662..de3282f 100644 --- a/kernel/src/filesystem/inode.cpp +++ b/kernel/src/filesystem/inode.cpp @@ -1,44 +1,11 @@ #include "kernel/filesystem/inode.hpp" -#include "kapi/system.hpp" - -#include "kernel/devices/device.hpp" -#include "kernel/filesystem/inode_metadata.hpp" - -#include - -#include - namespace filesystem { inode::inode(inode_kind kind) : m_kind(kind) {} - inode::inode(kstd::shared_ptr const & device) - : m_kind(inode_kind::device) - , m_device(device) - { - if (!m_device) - { - kapi::system::panic("[FILESYSTEM] inode constructed with null device."); - } - } - - auto inode::metadata() const -> inode_metadata - { - auto meta = inode_metadata{}; - meta.kind = m_kind; - - if (is_device()) - { - meta.major = m_device->major(); - meta.minor = m_device->minor(); - } - - return meta; - } - auto inode::is_directory() const -> bool { return m_kind == inode_kind::directory; @@ -53,56 +20,4 @@ namespace filesystem { return m_kind == inode_kind::device; } - - auto inode::is_block_device() const -> bool - { - return is_device() && m_device->is_block_device(); - } - - auto inode::major_device() const -> size_t - { - if (!is_device()) - { - kapi::system::panic("[FILESYSTEM] inode::major_device called on non-device inode."); - } - - return m_device->major(); - } - - auto inode::minor_device() const -> size_t - { - if (!is_device()) - { - kapi::system::panic("[FILESYSTEM] inode::minor_device called on non-device inode."); - } - - return m_device->minor(); - } - - auto inode::backing_device() const -> kstd::shared_ptr const & - { - return m_device; - } - - auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t - { - if (is_device()) - { - kapi::system::panic("[FILESYSTEM] inode::read called on device inode. Open it as a device file first."); - } - - // TODO BA-FS26 - return 0; - } - - auto inode::write(void const *, size_t, size_t) -> size_t - { - if (is_device()) - { - kapi::system::panic("[FILESYSTEM] inode::write called on device inode. Open it as a device file first."); - } - - kapi::system::panic("[FILESYSTEM] inode::write is not implemented yet"); - return 0; - } } // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 4e0b6bf..188da6d 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -6,6 +6,7 @@ #include "kernel/devices/storage/storage_management.hpp" #include "kernel/filesystem/custody.hpp" #include "kernel/filesystem/device_file.hpp" +#include "kernel/filesystem/device_inode.hpp" #include "kernel/filesystem/ext2/ext2_filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include "kernel/filesystem/inode_file.hpp" @@ -72,7 +73,8 @@ namespace filesystem auto node = custody->get_inode(); if (node->is_device()) { - auto current_device_file = kstd::make_shared(node->backing_device()); + auto device_node = static_cast(node.get()); + auto current_device_file = kstd::make_shared(device_node->backing_device()); current_device_file->open(); return open_file_description{current_device_file}; } @@ -92,7 +94,7 @@ namespace filesystem kapi::system::panic("[FILESYSTEM] make_device_node called with null device."); } - m_device_nodes.push_back(device_node_entry{device->name(), kstd::make_shared(device)}); + m_device_nodes.push_back(device_node_entry{device->name(), kstd::make_shared(device)}); } auto vfs::resolve_path(std::string_view path) -> std::optional -- cgit v1.2.3 From 09e3d0cb2272e7eabd79a320c17c58124515d427 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 19 Mar 2026 22:58:31 +0100 Subject: first try to simplify the architecture (remove redundant inode_file and open() methods), add ext2_file placeholder struct --- kernel/CMakeLists.txt | 2 +- kernel/include/kernel/filesystem/device_file.hpp | 2 -- kernel/include/kernel/filesystem/device_inode.hpp | 8 ++--- .../include/kernel/filesystem/ext2/ext2_file.hpp | 17 +++++++++++ .../include/kernel/filesystem/ext2/ext2_inode.hpp | 10 ++++++- kernel/include/kernel/filesystem/file.hpp | 2 -- kernel/include/kernel/filesystem/inode.hpp | 7 ++--- kernel/include/kernel/filesystem/inode_file.hpp | 27 ----------------- kernel/src/filesystem/device_file.cpp | 5 ---- kernel/src/filesystem/device_inode.cpp | 32 +++----------------- kernel/src/filesystem/ext2/ext2_file.cpp | 20 +++++++++++++ kernel/src/filesystem/ext2/ext2_inode.cpp | 17 +++++++++-- kernel/src/filesystem/inode_file.cpp | 35 ---------------------- kernel/src/filesystem/vfs.cpp | 14 +++------ 14 files changed, 75 insertions(+), 123 deletions(-) create mode 100644 kernel/include/kernel/filesystem/ext2/ext2_file.hpp delete mode 100644 kernel/include/kernel/filesystem/inode_file.hpp create mode 100644 kernel/src/filesystem/ext2/ext2_file.cpp delete mode 100644 kernel/src/filesystem/inode_file.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index a36eac0..a8287ba 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -21,6 +21,7 @@ add_executable("kernel" "src/devices/storage/storage_management.cpp" "src/devices/storage/ram_disk/ram_disk_controller.cpp" "src/devices/storage/ram_disk/ram_disk_device.cpp" + "src/filesystem/ext2/ext2_file.cpp" "src/filesystem/ext2/ext2_filesystem.cpp" "src/filesystem/ext2/ext2_inode.cpp" "src/filesystem/custody.cpp" @@ -28,7 +29,6 @@ add_executable("kernel" "src/filesystem/device_inode.cpp" "src/filesystem/file_descriptor_table.cpp" "src/filesystem/filesystem.cpp" - "src/filesystem/inode_file.cpp" "src/filesystem/inode.cpp" "src/filesystem/mount.cpp" "src/filesystem/open_file_description.cpp" diff --git a/kernel/include/kernel/filesystem/device_file.hpp b/kernel/include/kernel/filesystem/device_file.hpp index 06aa9b2..ff803f8 100644 --- a/kernel/include/kernel/filesystem/device_file.hpp +++ b/kernel/include/kernel/filesystem/device_file.hpp @@ -15,8 +15,6 @@ namespace filesystem { explicit device_file(kstd::shared_ptr const & device); - auto open() -> void override; - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; auto write(void const * buffer, size_t offset, size_t size) -> size_t override; diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index 0287875..c38c75b 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -2,22 +2,18 @@ #define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP #include "kernel/devices/device.hpp" +#include "kernel/filesystem/file.hpp" #include "kernel/filesystem/inode.hpp" #include -#include - namespace filesystem { struct device_inode : inode { explicit device_inode(kstd::shared_ptr const & device); - [[nodiscard]] auto backing_device() const -> kstd::shared_ptr const &; - - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + [[nodiscard]] auto open_file() const -> kstd::shared_ptr override; private: kstd::shared_ptr m_device{}; diff --git a/kernel/include/kernel/filesystem/ext2/ext2_file.hpp b/kernel/include/kernel/filesystem/ext2/ext2_file.hpp new file mode 100644 index 0000000..b5708d8 --- /dev/null +++ b/kernel/include/kernel/filesystem/ext2/ext2_file.hpp @@ -0,0 +1,17 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILE_HPP + +#include "kernel/filesystem/file.hpp" + +#include + +namespace filesystem::ext2 +{ + struct ext2_file : file + { + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + }; +} // namespace filesystem::ext2 + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp index 7cd6f8f..f103877 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp @@ -1,13 +1,21 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP +#include "kernel/filesystem/file.hpp" #include "kernel/filesystem/inode.hpp" +#include + namespace filesystem::ext2 { struct ext2_inode : inode { - // TODO BA-FS26 + ext2_inode(); + + [[nodiscard]] auto open_file() const -> kstd::shared_ptr override; + + private: + // TODO BA-FS26 add ext2-specific inode metadata here (e.g. block pointers, size, permissions, etc.) }; } // namespace filesystem::ext2 diff --git a/kernel/include/kernel/filesystem/file.hpp b/kernel/include/kernel/filesystem/file.hpp index e7e1b12..5a41fab 100644 --- a/kernel/include/kernel/filesystem/file.hpp +++ b/kernel/include/kernel/filesystem/file.hpp @@ -9,8 +9,6 @@ namespace filesystem { virtual ~file() = default; - virtual auto open() -> void = 0; - virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0; virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0; }; diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index dcf83b9..79cbcf9 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -1,9 +1,9 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP -#include +#include "kernel/filesystem/file.hpp" -#include +#include namespace filesystem { @@ -24,8 +24,7 @@ namespace filesystem [[nodiscard]] auto is_regular() const -> bool; [[nodiscard]] auto is_device() const -> bool; - virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0; - virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0; + [[nodiscard]] virtual auto open_file() const -> kstd::shared_ptr = 0; private: inode_kind m_kind{inode_kind::regular}; diff --git a/kernel/include/kernel/filesystem/inode_file.hpp b/kernel/include/kernel/filesystem/inode_file.hpp deleted file mode 100644 index ee84eee..0000000 --- a/kernel/include/kernel/filesystem/inode_file.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP - -#include "kernel/filesystem/file.hpp" -#include "kernel/filesystem/inode.hpp" - -#include - -#include - -namespace filesystem -{ - struct inode_file : file - { - explicit inode_file(kstd::shared_ptr const & inode); - - auto open() -> void override; - - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; - - private: - kstd::shared_ptr m_inode; - }; -} // namespace filesystem - -#endif \ No newline at end of file diff --git a/kernel/src/filesystem/device_file.cpp b/kernel/src/filesystem/device_file.cpp index c6db5af..48ed20d 100644 --- a/kernel/src/filesystem/device_file.cpp +++ b/kernel/src/filesystem/device_file.cpp @@ -23,11 +23,6 @@ namespace filesystem } } - auto device_file::open() -> void - { - // Hook point for permission checks or lazy metadata loading. - } - auto device_file::read(void * buffer, size_t offset, size_t size) const -> size_t { if (m_device->is_block_device()) diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index 18e3b24..592637d 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -3,12 +3,12 @@ #include "kapi/system.hpp" #include "kernel/devices/device.hpp" +#include "kernel/filesystem/device_file.hpp" +#include "kernel/filesystem/file.hpp" #include "kernel/filesystem/inode.hpp" #include -#include - namespace filesystem { device_inode::device_inode(kstd::shared_ptr const & device) @@ -21,32 +21,8 @@ namespace filesystem } } - auto device_inode::backing_device() const -> kstd::shared_ptr const & - { - return m_device; - } - - auto device_inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t + auto device_inode::open_file() const -> kstd::shared_ptr { - if (!m_device) - { - kapi::system::panic("[FILESYSTEM] device_inode has null device."); - } - - // TODO BA-FS26 use device file? - // return m_device->read(buffer, offset, size); - return 0; - } - - auto device_inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t - { - if (!m_device) - { - kapi::system::panic("[FILESYSTEM] device_inode has null device."); - } - - // TODO BA-FS26 use device file? - // return m_device->write(buffer, offset, size); - return 0; + return kstd::make_shared(m_device); } } // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/ext2/ext2_file.cpp b/kernel/src/filesystem/ext2/ext2_file.cpp new file mode 100644 index 0000000..7217c77 --- /dev/null +++ b/kernel/src/filesystem/ext2/ext2_file.cpp @@ -0,0 +1,20 @@ +#include "kernel/filesystem/ext2/ext2_file.hpp" + +#include "kapi/system.hpp" + +#include + +namespace filesystem::ext2 +{ + auto ext2_file::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t + { + kapi::system::panic("[FILESYSTEM] ext2_file::read is not implemented yet."); + return 0; + } + + auto ext2_file::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t + { + kapi::system::panic("[FILESYSTEM] ext2_file::write is not implemented yet."); + return 0; + } +} // namespace filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/ext2_inode.cpp b/kernel/src/filesystem/ext2/ext2_inode.cpp index ec68ee9..1522387 100644 --- a/kernel/src/filesystem/ext2/ext2_inode.cpp +++ b/kernel/src/filesystem/ext2/ext2_inode.cpp @@ -1,6 +1,19 @@ #include "kernel/filesystem/ext2/ext2_inode.hpp" +#include "kernel/filesystem/ext2/ext2_file.hpp" +#include "kernel/filesystem/file.hpp" +#include "kernel/filesystem/inode.hpp" + +#include + namespace filesystem::ext2 { - // TODO BA-FS26: Implement ext2 inode -} \ No newline at end of file + ext2_inode::ext2_inode() + : inode(inode_kind::regular) + {} + + auto ext2_inode::open_file() const -> kstd::shared_ptr + { + return kstd::make_shared(); + } +} // namespace filesystem::ext2 \ No newline at end of file diff --git a/kernel/src/filesystem/inode_file.cpp b/kernel/src/filesystem/inode_file.cpp deleted file mode 100644 index 7abac7b..0000000 --- a/kernel/src/filesystem/inode_file.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "kernel/filesystem/inode_file.hpp" - -#include "kapi/system.hpp" - -#include "kernel/filesystem/inode.hpp" - -#include - -#include - -namespace filesystem -{ - inode_file::inode_file(kstd::shared_ptr const & inode) - : m_inode(inode) - { - if (!m_inode) - { - kapi::system::panic("[FILESYSTEM] inode_file constructed with null inode"); - } - } - - auto inode_file::open() -> void - { - // Hook point for permission checks or lazy metadata loading. - } - - auto inode_file::read(void * buffer, size_t offset, size_t size) const -> size_t - { - return m_inode->read(buffer, offset, size); - } - auto inode_file::write(void const * buffer, size_t offset, size_t size) -> size_t - { - return m_inode->write(buffer, offset, size); - } -} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 188da6d..4b0c7d7 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -5,11 +5,8 @@ #include "kernel/devices/device.hpp" #include "kernel/devices/storage/storage_management.hpp" #include "kernel/filesystem/custody.hpp" -#include "kernel/filesystem/device_file.hpp" #include "kernel/filesystem/device_inode.hpp" #include "kernel/filesystem/ext2/ext2_filesystem.hpp" -#include "kernel/filesystem/inode.hpp" -#include "kernel/filesystem/inode_file.hpp" #include "kernel/filesystem/mount.hpp" #include "kernel/filesystem/open_file_description.hpp" @@ -71,16 +68,13 @@ namespace filesystem if (auto custody = resolve_path(path)) { auto node = custody->get_inode(); - if (node->is_device()) + + auto current_inode_file = node->open_file(); + if (!current_inode_file) { - auto device_node = static_cast(node.get()); - auto current_device_file = kstd::make_shared(device_node->backing_device()); - current_device_file->open(); - return open_file_description{current_device_file}; + kapi::system::panic("[FILESYSTEM] inode::open_file returned null file."); } - auto current_inode_file = kstd::make_shared(node); - current_inode_file->open(); return open_file_description{current_inode_file}; } -- cgit v1.2.3 From f669454966c9fa8cbdbbefb1d9cfdd61026849f9 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 20 Mar 2026 21:47:58 +0100 Subject: improve architecture again -> use same architecture for devices and ext2_files --- kernel/CMakeLists.txt | 1 + kernel/include/kernel/filesystem/disk_file.hpp | 19 +++++++++++++ .../include/kernel/filesystem/ext2/ext2_file.hpp | 4 +-- .../include/kernel/filesystem/ext2/ext2_inode.hpp | 5 ++-- kernel/include/kernel/filesystem/inode_file.hpp | 25 +++++++++++++++++ kernel/src/filesystem/ext2/ext2_inode.cpp | 8 +++--- kernel/src/filesystem/inode_file.cpp | 31 ++++++++++++++++++++++ 7 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 kernel/include/kernel/filesystem/disk_file.hpp create mode 100644 kernel/include/kernel/filesystem/inode_file.hpp create mode 100644 kernel/src/filesystem/inode_file.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index a8287ba..e0b40be 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable("kernel" "src/filesystem/device_inode.cpp" "src/filesystem/file_descriptor_table.cpp" "src/filesystem/filesystem.cpp" + "src/filesystem/inode_file.cpp" "src/filesystem/inode.cpp" "src/filesystem/mount.cpp" "src/filesystem/open_file_description.cpp" diff --git a/kernel/include/kernel/filesystem/disk_file.hpp b/kernel/include/kernel/filesystem/disk_file.hpp new file mode 100644 index 0000000..c6a7655 --- /dev/null +++ b/kernel/include/kernel/filesystem/disk_file.hpp @@ -0,0 +1,19 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_DISK_FILE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_DISK_FILE_HPP + +#include + +#include + +namespace filesystem +{ + struct disk_file + { + virtual ~disk_file() = default; + + virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0; + virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0; + }; +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/ext2_file.hpp b/kernel/include/kernel/filesystem/ext2/ext2_file.hpp index b5708d8..371507d 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_file.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_file.hpp @@ -1,13 +1,13 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILE_HPP -#include "kernel/filesystem/file.hpp" +#include "kernel/filesystem/disk_file.hpp" #include namespace filesystem::ext2 { - struct ext2_file : file + struct ext2_file : disk_file { auto read(void * buffer, size_t offset, size_t size) const -> size_t override; auto write(void const * buffer, size_t offset, size_t size) -> size_t override; diff --git a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp index f103877..7b5ff3f 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp @@ -1,6 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP +#include "kernel/filesystem/disk_file.hpp" #include "kernel/filesystem/file.hpp" #include "kernel/filesystem/inode.hpp" @@ -10,12 +11,12 @@ namespace filesystem::ext2 { struct ext2_inode : inode { - ext2_inode(); + explicit ext2_inode(kstd::shared_ptr const & disk_file); [[nodiscard]] auto open_file() const -> kstd::shared_ptr override; private: - // TODO BA-FS26 add ext2-specific inode metadata here (e.g. block pointers, size, permissions, etc.) + kstd::shared_ptr m_disk_file{}; }; } // namespace filesystem::ext2 diff --git a/kernel/include/kernel/filesystem/inode_file.hpp b/kernel/include/kernel/filesystem/inode_file.hpp new file mode 100644 index 0000000..3e2b954 --- /dev/null +++ b/kernel/include/kernel/filesystem/inode_file.hpp @@ -0,0 +1,25 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP + +#include "kernel/filesystem/disk_file.hpp" +#include "kernel/filesystem/file.hpp" + +#include + +#include + +namespace filesystem +{ + struct inode_file : file + { + explicit inode_file(kstd::shared_ptr const & disk_file); + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + private: + kstd::shared_ptr m_disk_file; + }; +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/src/filesystem/ext2/ext2_inode.cpp b/kernel/src/filesystem/ext2/ext2_inode.cpp index 1522387..8131f43 100644 --- a/kernel/src/filesystem/ext2/ext2_inode.cpp +++ b/kernel/src/filesystem/ext2/ext2_inode.cpp @@ -1,19 +1,21 @@ #include "kernel/filesystem/ext2/ext2_inode.hpp" -#include "kernel/filesystem/ext2/ext2_file.hpp" +#include "kernel/filesystem/disk_file.hpp" #include "kernel/filesystem/file.hpp" #include "kernel/filesystem/inode.hpp" +#include "kernel/filesystem/inode_file.hpp" #include namespace filesystem::ext2 { - ext2_inode::ext2_inode() + ext2_inode::ext2_inode(kstd::shared_ptr const & disk_file) : inode(inode_kind::regular) + , m_disk_file(disk_file) {} auto ext2_inode::open_file() const -> kstd::shared_ptr { - return kstd::make_shared(); + return kstd::make_shared(m_disk_file); } } // namespace filesystem::ext2 \ No newline at end of file diff --git a/kernel/src/filesystem/inode_file.cpp b/kernel/src/filesystem/inode_file.cpp new file mode 100644 index 0000000..9c351da --- /dev/null +++ b/kernel/src/filesystem/inode_file.cpp @@ -0,0 +1,31 @@ +#include "kernel/filesystem/inode_file.hpp" + +#include "kapi/system.hpp" + +#include "kernel/filesystem/disk_file.hpp" + +#include + +#include + +namespace filesystem +{ + inode_file::inode_file(kstd::shared_ptr const & disk_file) + : m_disk_file(disk_file) + { + if (!m_disk_file) + { + kapi::system::panic("[FILESYSTEM] inode_file constructed with null disk_file."); + } + } + + auto inode_file::read(void * buffer, size_t offset, size_t size) const -> size_t + { + return m_disk_file->read(buffer, offset, size); + } + + auto inode_file::write(void const * buffer, size_t offset, size_t size) -> size_t + { + return m_disk_file->write(buffer, offset, size); + } +} // namespace filesystem \ No newline at end of file -- cgit v1.2.3 From 91feb8a2a70af1915c8cfa4ee7d95b6e276f5c02 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 20 Mar 2026 21:51:51 +0100 Subject: small refactoring --- kernel/src/filesystem/vfs.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 4b0c7d7..1bc9ae6 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -69,13 +69,12 @@ namespace filesystem { auto node = custody->get_inode(); - auto current_inode_file = node->open_file(); - if (!current_inode_file) + if (auto current_inode_file = node->open_file();) { - kapi::system::panic("[FILESYSTEM] inode::open_file returned null file."); + return open_file_description{current_inode_file}; } - return open_file_description{current_inode_file}; + kapi::system::panic("[FILESYSTEM] inode::open_file returned null file."); } return std::nullopt; -- cgit v1.2.3 From b02b90f21de5954aef34eb37a17775f194b8de39 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 20 Mar 2026 22:11:33 +0100 Subject: prepare test code for a further test function --- kernel/src/main.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index eb59402..e719164 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -18,7 +18,7 @@ #include #include -auto run_test_code() -> void +auto test_file_description_manually() -> void { // setup auto fd_table = filesystem::file_descriptor_table::get(); @@ -85,6 +85,17 @@ auto run_test_code() -> void kstd::println("---"); } +auto test_device_with_vfs() -> void +{ + // TODO BA-FS26 +} + +auto run_test_code() -> void +{ + test_file_description_manually(); + test_device_with_vfs(); +} + auto main() -> int { kapi::cio::init(); -- cgit v1.2.3 From a396b71827a24f9d6c8010fd85b9afd9d86b6e2a Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 20 Mar 2026 22:13:36 +0100 Subject: implement first draft of a do_mount function --- kernel/include/kernel/filesystem/vfs.hpp | 10 ++++--- kernel/src/filesystem/vfs.cpp | 49 +++++++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index a2894a6..2ad570b 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -3,7 +3,7 @@ #include "kernel/devices/device.hpp" #include "kernel/filesystem/custody.hpp" -#include "kernel/filesystem/ext2/ext2_filesystem.hpp" +#include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include "kernel/filesystem/mount.hpp" #include "kernel/filesystem/open_file_description.hpp" @@ -24,22 +24,24 @@ namespace filesystem ~vfs() = default; auto open(std::string_view path) -> std::optional; + auto do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int; private: + // TODO BA-FS26 remove again and use devtempfs struct device_node_entry { std::string_view name; kstd::shared_ptr node; }; + kstd::vector> m_device_nodes; // TODO BA-FS26 remove again, use devtempfs vfs() = default; auto make_device_node(kstd::shared_ptr const & device) -> void; [[nodiscard]] auto resolve_path(std::string_view path) -> std::optional; - kstd::shared_ptr m_root_fs; + kstd::shared_ptr m_root_fs; std::optional m_root_mount; - // kstd::vector m_mounts; // TODO BA-FS26 really needed? - kstd::vector> m_device_nodes; // TODO BA-FS26 remove again, use devtempfs + kstd::vector m_mounts; }; } // namespace filesystem diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 1bc9ae6..febb844 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -36,13 +36,12 @@ namespace filesystem if (auto boot_device = storage_mgmt.determine_boot_device()) { active_vfs->m_root_fs = kstd::make_shared(); - if (active_vfs->m_root_fs->mount(boot_device) != 0) + if (active_vfs->do_mount("/", active_vfs->m_root_fs) != 0) { kapi::system::panic("[FILESYSTEM] Failed to mount root filesystem."); } - active_vfs->m_root_mount = mount{"/", active_vfs->m_root_fs}; - + // TODO BA-FS26 use do_mount when tempdevfs is implemented -> just call /dev/ with all devices in devtempfs std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto controller) { std::ranges::for_each(controller->all_devices(), [&](auto device) { active_vfs->make_device_node(device); }); }); @@ -69,8 +68,9 @@ namespace filesystem { auto node = custody->get_inode(); - if (auto current_inode_file = node->open_file();) + if (auto current_inode_file = node->open_file()) { + // TODO BA-FS26 return shared_ptr? return open_file_description{current_inode_file}; } @@ -80,6 +80,45 @@ namespace filesystem return std::nullopt; } + auto vfs::do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int + { + if (!filesystem) + { + return -1; // TODO BA-FS26 panic or errorcode? + } + + if (path.empty() || path.front() != '/') + { + return -1; // TODO BA-FS26 panic or errorcode? + } + + // TODO BA-FS26 better path validation + if ((path.size() > 1 && path.back() == '/') || path.find("//") != std::string_view::npos) + { + return -1; // TODO BA-FS26 panic or errorcode? + } + + if (path == "/") + { + m_root_fs = filesystem; + m_root_mount = mount{"/", filesystem}; + return 0; + } + + auto existing_mount = + std::ranges::find_if(m_mounts, [&](auto const & existing) { return existing.path() == path; }); + if (existing_mount != m_mounts.end()) + { + *existing_mount = mount{path, filesystem}; + } + else + { + m_mounts.push_back(mount{path, filesystem}); + } + + return 0; + } + auto vfs::make_device_node(kstd::shared_ptr const & device) -> void { if (!device) @@ -94,6 +133,8 @@ namespace filesystem { // TODO BA-FS26 implement real path resolution with mounts and directories etc. // For now, just support device nodes at /dev/. + // TODO BA-FS26 better path validation + // TODO BA-FS26 implement a path parser (maybe in libs?) and use it here and in do_mount constexpr auto device_prefix = std::string_view{"/dev/"}; if (path.starts_with(device_prefix)) -- cgit v1.2.3 From be44d4b778bb7c3a947af4cae610ecc3b8851672 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 20 Mar 2026 22:27:26 +0100 Subject: use kstd::shared_ptr instead of std::optional for open_file_descriptions --- .../kernel/filesystem/file_descriptor_table.hpp | 10 ++++----- kernel/include/kernel/filesystem/vfs.hpp | 2 +- kernel/src/filesystem/file_descriptor_table.cpp | 24 ++++++++++++++-------- kernel/src/filesystem/vfs.cpp | 8 ++++---- kernel/src/main.cpp | 4 ++-- 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/kernel/include/kernel/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/filesystem/file_descriptor_table.hpp index 6d78532..bc6fb24 100644 --- a/kernel/include/kernel/filesystem/file_descriptor_table.hpp +++ b/kernel/include/kernel/filesystem/file_descriptor_table.hpp @@ -3,10 +3,9 @@ #include "open_file_description.hpp" +#include #include -#include - namespace filesystem { struct file_descriptor_table @@ -16,15 +15,14 @@ namespace filesystem ~file_descriptor_table() = default; - auto add_file(open_file_description & f) -> int; - [[nodiscard]] auto get_file(int fd) const -> std::optional; + auto add_file(kstd::shared_ptr const & f) -> int; + [[nodiscard]] auto get_file(int fd) const -> kstd::shared_ptr; auto remove_file(int fd) -> void; private: file_descriptor_table() = default; - // TODO BA-FS26 use kstd::shared_ptr when available - kstd::vector> m_open_files{}; + kstd::vector> m_open_files{}; }; } // namespace filesystem diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 2ad570b..ca51d90 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -23,7 +23,7 @@ namespace filesystem ~vfs() = default; - auto open(std::string_view path) -> std::optional; + auto open(std::string_view path) -> kstd::shared_ptr; auto do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int; private: diff --git a/kernel/src/filesystem/file_descriptor_table.cpp b/kernel/src/filesystem/file_descriptor_table.cpp index 814322e..6eb3845 100644 --- a/kernel/src/filesystem/file_descriptor_table.cpp +++ b/kernel/src/filesystem/file_descriptor_table.cpp @@ -4,6 +4,8 @@ #include "kernel/filesystem/open_file_description.hpp" +#include + #include #include #include @@ -35,9 +37,15 @@ namespace filesystem return *global_file_descriptor_table; } - auto file_descriptor_table::add_file(open_file_description & file_description) -> int + auto file_descriptor_table::add_file(kstd::shared_ptr const & file_description) -> int { - auto it = std::ranges::find_if(m_open_files, [](auto & open_file) { return !open_file.has_value(); }); + if (!file_description) + { + // TODO BA-FS26 panic or errorcode? + return -1; + } + + auto it = std::ranges::find_if(m_open_files, [](auto const & open_file) { return open_file == nullptr; }); if (it != m_open_files.end()) { *it = file_description; @@ -48,20 +56,20 @@ namespace filesystem return static_cast(m_open_files.size() - 1); } - auto file_descriptor_table::get_file(int fd) const -> std::optional + auto file_descriptor_table::get_file(int fd) const -> kstd::shared_ptr { if (fd < 0) { - return std::nullopt; + return nullptr; } auto const index = static_cast(fd); - if (index >= m_open_files.size() || !m_open_files.at(fd).has_value()) + if (index >= m_open_files.size()) { - return std::nullopt; + return nullptr; } - return m_open_files.at(fd); + return m_open_files.at(index); } auto file_descriptor_table::remove_file(int fd) -> void @@ -77,6 +85,6 @@ namespace filesystem return; } - m_open_files.at(fd).reset(); + m_open_files.at(index) = nullptr; } } // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index febb844..a3e554e 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -7,6 +7,7 @@ #include "kernel/filesystem/custody.hpp" #include "kernel/filesystem/device_inode.hpp" #include "kernel/filesystem/ext2/ext2_filesystem.hpp" +#include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/mount.hpp" #include "kernel/filesystem/open_file_description.hpp" @@ -62,7 +63,7 @@ namespace filesystem return *active_vfs; } - auto vfs::open(std::string_view path) -> std::optional + auto vfs::open(std::string_view path) -> kstd::shared_ptr { if (auto custody = resolve_path(path)) { @@ -70,14 +71,13 @@ namespace filesystem if (auto current_inode_file = node->open_file()) { - // TODO BA-FS26 return shared_ptr? - return open_file_description{current_inode_file}; + return kstd::make_shared(current_inode_file); } kapi::system::panic("[FILESYSTEM] inode::open_file returned null file."); } - return std::nullopt; + return nullptr; } auto vfs::do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index e719164..31133a5 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -26,7 +26,7 @@ auto test_file_description_manually() -> void auto device = storage_mgmt.device_by_major_minor(1, 0); auto dev_file = kstd::make_shared(device); - filesystem::open_file_description ofd(dev_file); + auto ofd = kstd::make_shared(dev_file); auto fd_index = fd_table.add_file(ofd); // use: read two bytes and write two again @@ -61,7 +61,7 @@ auto test_file_description_manually() -> void fd_table.remove_file(fd_index); // use: read four bytes again -> two old bytes two new bytes - filesystem::open_file_description ofd1(dev_file); + auto ofd1 = kstd::make_shared(dev_file); fd_index = fd_table.add_file(ofd1); auto fd1 = fd_table.get_file(fd_index); -- cgit v1.2.3 From 90452b752688110db2edc8d5dccdf0d6e0face5f Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 20 Mar 2026 23:17:11 +0100 Subject: fix build --- kernel/src/filesystem/vfs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index a3e554e..ee5fd9b 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -93,7 +93,7 @@ namespace filesystem } // TODO BA-FS26 better path validation - if ((path.size() > 1 && path.back() == '/') || path.find("//") != std::string_view::npos) + if ((path.size() > 1 && path.back() == '/')) { return -1; // TODO BA-FS26 panic or errorcode? } -- cgit v1.2.3 From ba63fbfc2bb43a6f0f05b6b49fd51fd6c89a0861 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 21 Mar 2026 00:28:17 +0100 Subject: refactor file and inode architecture again --- kernel/CMakeLists.txt | 1 + kernel/include/kernel/filesystem/device_file.hpp | 6 ++++-- kernel/include/kernel/filesystem/device_inode.hpp | 7 ++++-- kernel/include/kernel/filesystem/disk_file.hpp | 19 ---------------- .../include/kernel/filesystem/ext2/ext2_file.hpp | 8 +++---- .../include/kernel/filesystem/ext2/ext2_inode.hpp | 9 ++++---- kernel/include/kernel/filesystem/file.hpp | 8 +++++++ kernel/include/kernel/filesystem/inode.hpp | 8 +++---- kernel/include/kernel/filesystem/inode_file.hpp | 9 ++++---- kernel/src/filesystem/device_file.cpp | 25 +++++++++++++++------- kernel/src/filesystem/device_inode.cpp | 14 ++++++++++-- kernel/src/filesystem/ext2/ext2_inode.cpp | 15 ++++++++----- kernel/src/filesystem/file.cpp | 20 +++++++++++++++++ kernel/src/filesystem/inode_file.cpp | 21 ++++++++++-------- kernel/src/filesystem/vfs.cpp | 5 +++-- kernel/src/main.cpp | 5 ++++- 16 files changed, 112 insertions(+), 68 deletions(-) delete mode 100644 kernel/include/kernel/filesystem/disk_file.hpp create mode 100644 kernel/src/filesystem/file.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index e0b40be..ec0d4e2 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -27,6 +27,7 @@ add_executable("kernel" "src/filesystem/custody.cpp" "src/filesystem/device_file.cpp" "src/filesystem/device_inode.cpp" + "src/filesystem/file.cpp" "src/filesystem/file_descriptor_table.cpp" "src/filesystem/filesystem.cpp" "src/filesystem/inode_file.cpp" diff --git a/kernel/include/kernel/filesystem/device_file.hpp b/kernel/include/kernel/filesystem/device_file.hpp index ff803f8..22ddb49 100644 --- a/kernel/include/kernel/filesystem/device_file.hpp +++ b/kernel/include/kernel/filesystem/device_file.hpp @@ -11,9 +11,11 @@ namespace filesystem { + struct inode; + struct device_file : file { - explicit device_file(kstd::shared_ptr const & device); + explicit device_file(kstd::shared_ptr const & inode); auto read(void * buffer, size_t offset, size_t size) const -> size_t override; auto write(void const * buffer, size_t offset, size_t size) -> size_t override; @@ -23,7 +25,7 @@ namespace filesystem std::byte * scratch, void * buffer); auto process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t; - kstd::shared_ptr m_device; + [[nodiscard]] auto device() const -> kstd::shared_ptr const &; }; } // namespace filesystem diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index c38c75b..af2a51b 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -2,18 +2,21 @@ #define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP #include "kernel/devices/device.hpp" -#include "kernel/filesystem/file.hpp" #include "kernel/filesystem/inode.hpp" #include namespace filesystem { + struct file; + struct device_inode : inode { explicit device_inode(kstd::shared_ptr const & device); - [[nodiscard]] auto open_file() const -> kstd::shared_ptr override; + [[nodiscard]] auto open_file(kstd::shared_ptr const & self) const -> kstd::shared_ptr override; + + [[nodiscard]] auto device() const -> kstd::shared_ptr const &; private: kstd::shared_ptr m_device{}; diff --git a/kernel/include/kernel/filesystem/disk_file.hpp b/kernel/include/kernel/filesystem/disk_file.hpp deleted file mode 100644 index c6a7655..0000000 --- a/kernel/include/kernel/filesystem/disk_file.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_DISK_FILE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_DISK_FILE_HPP - -#include - -#include - -namespace filesystem -{ - struct disk_file - { - virtual ~disk_file() = default; - - virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0; - virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0; - }; -} // namespace filesystem - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/ext2_file.hpp b/kernel/include/kernel/filesystem/ext2/ext2_file.hpp index 371507d..e5357e3 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_file.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_file.hpp @@ -1,16 +1,14 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILE_HPP -#include "kernel/filesystem/disk_file.hpp" - #include namespace filesystem::ext2 { - struct ext2_file : disk_file + struct ext2_file { - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + auto read(void * buffer, size_t offset, size_t size) const -> size_t; + auto write(void const * buffer, size_t offset, size_t size) -> size_t; }; } // namespace filesystem::ext2 diff --git a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp index 7b5ff3f..b57d267 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP -#include "kernel/filesystem/disk_file.hpp" +#include "kernel/filesystem/ext2/ext2_file.hpp" #include "kernel/filesystem/file.hpp" #include "kernel/filesystem/inode.hpp" @@ -11,12 +11,13 @@ namespace filesystem::ext2 { struct ext2_inode : inode { - explicit ext2_inode(kstd::shared_ptr const & disk_file); + explicit ext2_inode(); - [[nodiscard]] auto open_file() const -> kstd::shared_ptr override; + [[nodiscard]] auto open_file(kstd::shared_ptr const & self) const + -> kstd::shared_ptr override; private: - kstd::shared_ptr m_disk_file{}; + kstd::shared_ptr m_file{}; }; } // namespace filesystem::ext2 diff --git a/kernel/include/kernel/filesystem/file.hpp b/kernel/include/kernel/filesystem/file.hpp index 5a41fab..522f078 100644 --- a/kernel/include/kernel/filesystem/file.hpp +++ b/kernel/include/kernel/filesystem/file.hpp @@ -1,16 +1,24 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_FILE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_FILE_HPP +#include + #include namespace filesystem { + struct inode; + struct file { + explicit file(kstd::shared_ptr const & inode); virtual ~file() = default; virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0; virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0; + + protected: + kstd::shared_ptr m_inode; }; } // namespace filesystem diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index 79cbcf9..8c9f75e 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -1,12 +1,12 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP -#include "kernel/filesystem/file.hpp" - #include namespace filesystem { + struct file; + struct inode { enum class inode_kind @@ -20,12 +20,12 @@ namespace filesystem virtual ~inode() = default; + [[nodiscard]] virtual auto open_file(kstd::shared_ptr const & self) const -> kstd::shared_ptr = 0; + [[nodiscard]] auto is_directory() const -> bool; [[nodiscard]] auto is_regular() const -> bool; [[nodiscard]] auto is_device() const -> bool; - [[nodiscard]] virtual auto open_file() const -> kstd::shared_ptr = 0; - private: inode_kind m_kind{inode_kind::regular}; }; diff --git a/kernel/include/kernel/filesystem/inode_file.hpp b/kernel/include/kernel/filesystem/inode_file.hpp index 3e2b954..f87b77c 100644 --- a/kernel/include/kernel/filesystem/inode_file.hpp +++ b/kernel/include/kernel/filesystem/inode_file.hpp @@ -1,8 +1,8 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP -#include "kernel/filesystem/disk_file.hpp" #include "kernel/filesystem/file.hpp" +#include "kernel/filesystem/inode.hpp" #include @@ -10,15 +10,14 @@ namespace filesystem { + struct inode; + struct inode_file : file { - explicit inode_file(kstd::shared_ptr const & disk_file); + explicit inode_file(kstd::shared_ptr const & inode); auto read(void * buffer, size_t offset, size_t size) const -> size_t override; auto write(void const * buffer, size_t offset, size_t size) -> size_t override; - - private: - kstd::shared_ptr m_disk_file; }; } // namespace filesystem diff --git a/kernel/src/filesystem/device_file.cpp b/kernel/src/filesystem/device_file.cpp index 48ed20d..26c7511 100644 --- a/kernel/src/filesystem/device_file.cpp +++ b/kernel/src/filesystem/device_file.cpp @@ -4,6 +4,9 @@ #include "kernel/devices/block_device.hpp" #include "kernel/devices/device.hpp" +#include "kernel/filesystem/device_inode.hpp" +#include "kernel/filesystem/file.hpp" +#include "kernel/filesystem/inode.hpp" #include #include @@ -14,18 +17,18 @@ namespace filesystem { - device_file::device_file(kstd::shared_ptr const & device) - : m_device(device) + device_file::device_file(kstd::shared_ptr const & inode) + : file(inode) { - if (!m_device) + if (!m_inode->is_device()) { - kapi::system::panic("[FILESYSTEM] device_file constructed with null device."); + kapi::system::panic("[FILESYSTEM] device_file constructed with non-device inode."); } } auto device_file::read(void * buffer, size_t offset, size_t size) const -> size_t { - if (m_device->is_block_device()) + if (device()->is_block_device()) { return process_blocks(offset, size, buffer, [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, @@ -50,7 +53,7 @@ namespace filesystem auto device_file::write(void const * buffer, size_t offset, size_t size) -> size_t { - if (m_device->is_block_device()) + if (device()->is_block_device()) { return process_blocks(offset, size, const_cast(buffer), [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, @@ -78,7 +81,7 @@ namespace filesystem { if (buffer == nullptr) { - kapi::system::panic("[FILESYSTEM] device_file::write called with null buffer."); + kapi::system::panic("[FILESYSTEM] device_file::process_blocks called with null buffer."); } if (size == 0) @@ -86,7 +89,7 @@ namespace filesystem return 0; } - auto * block_dev = static_cast(m_device.get()); + auto * block_dev = static_cast(device().get()); if (block_dev == nullptr) { kapi::system::panic("[FILESYSTEM] device_file: expected block_device."); @@ -116,4 +119,10 @@ namespace filesystem return processed; } + + auto device_file::device() const -> kstd::shared_ptr const & + { + auto inode = static_cast(m_inode.get()); + return inode->device(); + } } // namespace filesystem diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index 592637d..65dd9a3 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -21,8 +21,18 @@ namespace filesystem } } - auto device_inode::open_file() const -> kstd::shared_ptr + auto device_inode::open_file(kstd::shared_ptr const & self) const -> kstd::shared_ptr { - return kstd::make_shared(m_device); + if (!self) + { + kapi::system::panic("[FILESYSTEM] device_inode::open_file called with null inode."); + } + + return kstd::make_shared(self); + } + + auto device_inode::device() const -> kstd::shared_ptr const & + { + return m_device; } } // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/ext2/ext2_inode.cpp b/kernel/src/filesystem/ext2/ext2_inode.cpp index 8131f43..0a1994b 100644 --- a/kernel/src/filesystem/ext2/ext2_inode.cpp +++ b/kernel/src/filesystem/ext2/ext2_inode.cpp @@ -1,6 +1,7 @@ #include "kernel/filesystem/ext2/ext2_inode.hpp" -#include "kernel/filesystem/disk_file.hpp" +#include "kapi/system.hpp" + #include "kernel/filesystem/file.hpp" #include "kernel/filesystem/inode.hpp" #include "kernel/filesystem/inode_file.hpp" @@ -9,13 +10,17 @@ namespace filesystem::ext2 { - ext2_inode::ext2_inode(kstd::shared_ptr const & disk_file) + ext2_inode::ext2_inode() : inode(inode_kind::regular) - , m_disk_file(disk_file) {} - auto ext2_inode::open_file() const -> kstd::shared_ptr + auto ext2_inode::open_file(kstd::shared_ptr const & self) const -> kstd::shared_ptr { - return kstd::make_shared(m_disk_file); + if (!self) + { + kapi::system::panic("[FILESYSTEM] ext2_inode::open_file called with null inode."); + } + + return kstd::make_shared(self); } } // namespace filesystem::ext2 \ No newline at end of file diff --git a/kernel/src/filesystem/file.cpp b/kernel/src/filesystem/file.cpp new file mode 100644 index 0000000..a147863 --- /dev/null +++ b/kernel/src/filesystem/file.cpp @@ -0,0 +1,20 @@ +#include "kernel/filesystem/file.hpp" + +#include "kapi/system.hpp" + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace filesystem +{ + + file::file(kstd::shared_ptr const & inode) + : m_inode(inode) + { + if (!m_inode) + { + kapi::system::panic("[FILESYSTEM] file constructed with null inode."); + } + } +} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/inode_file.cpp b/kernel/src/filesystem/inode_file.cpp index 9c351da..8b2fcba 100644 --- a/kernel/src/filesystem/inode_file.cpp +++ b/kernel/src/filesystem/inode_file.cpp @@ -2,7 +2,8 @@ #include "kapi/system.hpp" -#include "kernel/filesystem/disk_file.hpp" +#include "kernel/filesystem/file.hpp" +#include "kernel/filesystem/inode.hpp" #include @@ -10,22 +11,24 @@ namespace filesystem { - inode_file::inode_file(kstd::shared_ptr const & disk_file) - : m_disk_file(disk_file) + inode_file::inode_file(kstd::shared_ptr const & inode) + : file(inode) { - if (!m_disk_file) + if (m_inode->is_device()) { - kapi::system::panic("[FILESYSTEM] inode_file constructed with null disk_file."); + kapi::system::panic("[FILESYSTEM] inode_file constructed with device inode."); } } - auto inode_file::read(void * buffer, size_t offset, size_t size) const -> size_t + auto inode_file::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t { - return m_disk_file->read(buffer, offset, size); + // TODO BA-FS26 + return 0; } - auto inode_file::write(void const * buffer, size_t offset, size_t size) -> size_t + auto inode_file::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t { - return m_disk_file->write(buffer, offset, size); + // TODO BA-FS26 + return 0; } } // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index ee5fd9b..5330b82 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -34,6 +34,7 @@ namespace filesystem active_vfs.emplace(vfs{}); auto storage_mgmt = devices::storage::storage_management::get(); + // TODO BA-FS26 fix mounting boot_device if (auto boot_device = storage_mgmt.determine_boot_device()) { active_vfs->m_root_fs = kstd::make_shared(); @@ -69,9 +70,9 @@ namespace filesystem { auto node = custody->get_inode(); - if (auto current_inode_file = node->open_file()) + if (auto opened_file = node->open_file(node)) { - return kstd::make_shared(current_inode_file); + return kstd::make_shared(opened_file); } kapi::system::panic("[FILESYSTEM] inode::open_file returned null file."); diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 31133a5..8d7308b 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -5,6 +5,7 @@ #include "kernel/devices/storage/storage_management.hpp" #include "kernel/filesystem/device_file.hpp" +#include "kernel/filesystem/device_inode.hpp" #include "kernel/filesystem/file_descriptor_table.hpp" #include "kernel/filesystem/open_file_description.hpp" #include "kernel/filesystem/vfs.hpp" @@ -25,7 +26,9 @@ auto test_file_description_manually() -> void auto storage_mgmt = devices::storage::storage_management::get(); auto device = storage_mgmt.device_by_major_minor(1, 0); - auto dev_file = kstd::make_shared(device); + auto dev_node = kstd::make_shared(device); + + auto dev_file = kstd::make_shared(dev_node); auto ofd = kstd::make_shared(dev_file); auto fd_index = fd_table.add_file(ofd); -- cgit v1.2.3 From 09bf8eba8dbc76dc9c46ec1486cbf2f9530233a8 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 21 Mar 2026 10:44:05 +0100 Subject: implement simple weak_ptr and enable_shared_from_this --- libs/kstd/include/kstd/bits/shared_ptr.hpp | 320 ++++++++++++++++++++++++++--- 1 file changed, 286 insertions(+), 34 deletions(-) diff --git a/libs/kstd/include/kstd/bits/shared_ptr.hpp b/libs/kstd/include/kstd/bits/shared_ptr.hpp index 6bce83f..674807d 100644 --- a/libs/kstd/include/kstd/bits/shared_ptr.hpp +++ b/libs/kstd/include/kstd/bits/shared_ptr.hpp @@ -10,9 +10,199 @@ namespace kstd { + /** + * @brief Control block for shared_ptr and weak_ptr. This control block contains the reference counts for shared + * ownership and weak ownership. The shared_count tracks the number of shared_ptr instances that own the managed + * object, while the weak_count tracks the number of weak_ptr instances that reference the managed object. + * The weak_count is needed to determine when it is safe to delete the shared_control_block itself + */ + struct shared_control_block + { + std::atomic shared_count; + std::atomic weak_count; + + explicit shared_control_block(std::size_t shared = 1, std::size_t weak = 0) + : shared_count(shared) + , weak_count(weak) + {} + }; + template struct shared_ptr; + /** + * @brief weak_ptr is a smart pointer that holds a non-owning weak reference to an object that is managed by + * shared_ptr. It must be converted to shared_ptr in to be able to access the referenced object. A weak_ptr is created + * as a copy of a shared_ptr, or as a copy of another weak_ptr. A weak_ptr is typically used to break circular + * references between shared_ptr to avoid memory leaks. A weak_ptr does not contribute to the reference count of the + * object, and it does not manage the lifetime of the object. If the object managed by shared_ptr is destroyed, the + * weak_ptr becomes expired and cannot be used to access the object anymore. + */ + template + struct weak_ptr + { + template + friend struct shared_ptr; + + /** + * @brief Constructs a null weak_ptr. + */ + weak_ptr() noexcept + : pointer(nullptr) + , control(nullptr) + {} + + template + requires(std::is_convertible_v) + weak_ptr(shared_ptr const & other) + : pointer(other.pointer) + , control(other.control) + { + if (control != nullptr) + { + ++(control->weak_count); + } + } + + /** + * @brief Copy constructor. Constructs a weak_ptr which shares ownership of the object managed by other. + */ + weak_ptr(weak_ptr const & other) + : pointer(other.pointer) + , control(other.control) + { + if (control != nullptr) + { + ++(control->weak_count); + } + } + + /** + * @brief Move constructor. Constructs a weak_ptr which takes ownership of the object managed by other. + */ + weak_ptr(weak_ptr && other) noexcept + : pointer(other.pointer) + , control(other.control) + { + other.pointer = nullptr; + other.control = nullptr; + } + + /** + * @brief Assignment operator. Assigns the weak_ptr to another weak_ptr. + */ + auto operator=(weak_ptr const & other) -> weak_ptr & + { + if (this != &other) + { + cleanup(); + pointer = other.pointer; + control = other.control; + if (control != nullptr) + { + ++(control->weak_count); + } + } + + return *this; + } + + /** + * @brief Move assignment operator. Move-assigns a weak_ptr from other. + */ + auto operator=(weak_ptr && other) noexcept -> weak_ptr & + { + if (this != &other) + { + cleanup(); + pointer = other.pointer; + control = other.control; + other.pointer = nullptr; + other.control = nullptr; + } + + return *this; + } + + /** + * @brief Destructor. Cleans up resources if necessary. + */ + ~weak_ptr() + { + cleanup(); + } + + /** + * @brief Returns a shared_ptr that shares ownership of the managed object if the managed object still exists, or an + * empty shared_ptr otherwise. + */ + [[nodiscard]] auto lock() const -> shared_ptr + { + return shared_ptr(*this); + } + + private: + auto cleanup() -> void + { + if (control != nullptr) + { + if (--(control->weak_count) == 0 && control->shared_count == 0) + { + delete control; + } + } + } + + T * pointer; + shared_control_block * control; + }; + + /** + * @brief enable_shared_from_this is a base class that allows an object that is currently managed by a shared_ptr to + * create additional shared_ptr instances that share ownership of the same object. This is usefl when you want to + * create shared_ptr instances in a member function of the object. + * + * @tparam T The type of the managed object. + */ + template + struct enable_shared_from_this + { + template + friend struct shared_ptr; + + friend T; + + public: + /** + * @brief Returns a shared_ptr that shares ownership of *this. + */ + auto shared_from_this() -> shared_ptr + { + return shared_ptr(weak_this); + } + + /** + * @brief Returns a shared_ptr that shares ownership of *this. + */ + auto shared_from_this() const -> shared_ptr + { + return shared_ptr(weak_this); + } + + private: + enable_shared_from_this() = default; + enable_shared_from_this(enable_shared_from_this const &) = default; + auto operator=(enable_shared_from_this const &) -> enable_shared_from_this & = default; + ~enable_shared_from_this() = default; + + void internal_assign_ptr(shared_ptr const & ptr) const + { + weak_this = ptr; + } + + mutable weak_ptr weak_this{}; ///< Weak pointer to the object, used for shared_from_this functionality. + }; + /** * @brief Shared_pointer is a smart pointer that retains shared ownership of an object through a pointer. Several * shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of @@ -31,12 +221,23 @@ namespace kstd template friend struct shared_ptr; + template + friend struct weak_ptr; + + /** + * @brief Construct an empty shared_ptr. + */ + shared_ptr() noexcept + : pointer(nullptr) + , control(nullptr) + {} + /** * @brief Construct an empty shared_ptr from nullptr. */ shared_ptr(std::nullptr_t) noexcept : pointer(nullptr) - , ref_count(nullptr) + , control(nullptr) {} /** @@ -44,11 +245,33 @@ namespace kstd * * @param pointer A pointer to an object to manage (default is nullptr). */ - explicit shared_ptr(T * pointer = nullptr) + template + requires(std::is_convertible_v) + explicit shared_ptr(U * pointer = nullptr) : pointer(pointer) - , ref_count(pointer != nullptr ? new std::atomic(1) : nullptr) + , control(pointer != nullptr ? new shared_control_block() : nullptr) + { + assign_enable_shared_from_this(pointer); + } + + /** + * @brief Constructor a shared_ptr from a weak_ptr. If other is not expired, constructs a shared_ptr which shares + * ownership of the object managed by other. Otherwise, constructs an empty shared_ptr. + * + * @param other The weak_ptr to construct from. + */ + template + requires(std::is_convertible_v) + explicit shared_ptr(weak_ptr const & other) + : pointer(nullptr) + , control(nullptr) { - // Nothing to do. + if (other.control != nullptr && other.control->shared_count != 0) + { + pointer = other.pointer; + control = other.control; + ++(control->shared_count); + } } /** @@ -58,11 +281,11 @@ namespace kstd */ shared_ptr(shared_ptr const & other) : pointer(other.pointer) - , ref_count(other.ref_count) + , control(other.control) { - if (ref_count != nullptr) + if (control != nullptr) { - ++(*ref_count); + ++(control->shared_count); } } @@ -76,11 +299,11 @@ namespace kstd requires(std::is_convertible_v) shared_ptr(shared_ptr const & other) : pointer(other.pointer) - , ref_count(other.ref_count) + , control(other.control) { - if (ref_count != nullptr) + if (control != nullptr) { - ++(*ref_count); + ++(control->shared_count); } } @@ -91,10 +314,10 @@ namespace kstd */ shared_ptr(shared_ptr && other) noexcept : pointer(other.pointer) - , ref_count(other.ref_count) + , control(other.control) { other.pointer = nullptr; - other.ref_count = nullptr; + other.control = nullptr; } /** @@ -107,10 +330,10 @@ namespace kstd requires(std::is_convertible_v) shared_ptr(shared_ptr && other) noexcept : pointer(other.pointer) - , ref_count(other.ref_count) + , control(other.control) { other.pointer = nullptr; - other.ref_count = nullptr; + other.control = nullptr; } /** @@ -127,11 +350,11 @@ namespace kstd { cleanup(); pointer = other.pointer; - ref_count = other.ref_count; + control = other.control; - if (ref_count != nullptr) + if (control != nullptr) { - ++(*ref_count); + ++(control->shared_count); } } @@ -151,11 +374,11 @@ namespace kstd { cleanup(); pointer = other.pointer; - ref_count = other.ref_count; + control = other.control; - if (ref_count != nullptr) + if (control != nullptr) { - ++(*ref_count); + ++(control->shared_count); } return *this; @@ -174,9 +397,9 @@ namespace kstd { cleanup(); pointer = other.pointer; - ref_count = other.ref_count; + control = other.control; other.pointer = nullptr; - other.ref_count = nullptr; + other.control = nullptr; } return *this; @@ -195,9 +418,9 @@ namespace kstd { cleanup(); pointer = other.pointer; - ref_count = other.ref_count; + control = other.control; other.pointer = nullptr; - other.ref_count = nullptr; + other.control = nullptr; return *this; } @@ -209,7 +432,7 @@ namespace kstd { cleanup(); pointer = nullptr; - ref_count = nullptr; + control = nullptr; return *this; } @@ -230,7 +453,8 @@ namespace kstd { cleanup(); pointer = ptr; - ref_count = ptr != nullptr ? new std::atomic(1) : nullptr; + control = ptr != nullptr ? new shared_control_block() : nullptr; + assign_enable_shared_from_this(ptr); } /** @@ -242,7 +466,7 @@ namespace kstd void swap(shared_ptr & other) { std::swap(pointer, other.pointer); - std::swap(ref_count, other.ref_count); + std::swap(control, other.control); } /** @@ -288,9 +512,9 @@ namespace kstd */ [[nodiscard]] auto use_count() const -> std::size_t { - if (ref_count != nullptr) + if (control != nullptr) { - return *ref_count; + return control->shared_count; } return 0; @@ -328,20 +552,48 @@ namespace kstd [[nodiscard]] auto operator<=>(shared_ptr const & other) const = default; private: + /** + * @brief If the candidate type inherits from enable_shared_from_this, assigns the internal weak pointer to this + * shared_ptr. This weak_ptr is used to implement shared_from_this functionality for the candidate type. If the + * candidate type does not inherit from enable_shared_from_this, this function does nothing. + * + * @tparam U The candidate type to check for enable_shared_from_this inheritance. + * @param candidate The candidate object to assign the internal weak pointer for. + */ + template + auto assign_enable_shared_from_this(U * candidate) -> void + { + if constexpr (requires(U * p, shared_ptr const & sp) { p->internal_assign_ptr(sp); }) + { + if (candidate != nullptr) + { + candidate->internal_assign_ptr(*this); + } + } + } + /** * @brief Releases ownership and deletes the object if this was the last reference to the owned managed object. */ auto cleanup() -> void { - if (ref_count != nullptr && --(*ref_count) == 0) + if (control != nullptr) { - delete pointer; - delete ref_count; + if (--(control->shared_count) == 0) + { + delete pointer; + pointer = nullptr; + + if (control->weak_count == 0) + { + delete control; + } + } } } - T * pointer; ///< The managed object. - std::atomic * ref_count; ///< Reference count. + T * pointer; ///< The managed object. + shared_control_block * control; ///< Shared control block. }; /** -- cgit v1.2.3 From ffb2accb09a013d8da16acd824c846bc1acfd8e4 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 21 Mar 2026 10:52:46 +0100 Subject: use enable_shared_from_this instead of self invocation method --- kernel/include/kernel/filesystem/device_inode.hpp | 4 ++-- kernel/include/kernel/filesystem/ext2/ext2_inode.hpp | 5 ++--- kernel/include/kernel/filesystem/inode.hpp | 4 ++-- kernel/src/filesystem/device_inode.cpp | 9 ++------- kernel/src/filesystem/ext2/ext2_inode.cpp | 11 ++--------- kernel/src/filesystem/vfs.cpp | 2 +- 6 files changed, 11 insertions(+), 24 deletions(-) diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index af2a51b..10e40b3 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -14,12 +14,12 @@ namespace filesystem { explicit device_inode(kstd::shared_ptr const & device); - [[nodiscard]] auto open_file(kstd::shared_ptr const & self) const -> kstd::shared_ptr override; + [[nodiscard]] auto open_file() -> kstd::shared_ptr override; [[nodiscard]] auto device() const -> kstd::shared_ptr const &; private: - kstd::shared_ptr m_device{}; + kstd::shared_ptr m_device; }; } // namespace filesystem diff --git a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp index b57d267..e6e8e99 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp @@ -13,11 +13,10 @@ namespace filesystem::ext2 { explicit ext2_inode(); - [[nodiscard]] auto open_file(kstd::shared_ptr const & self) const - -> kstd::shared_ptr override; + [[nodiscard]] auto open_file() -> kstd::shared_ptr override; private: - kstd::shared_ptr m_file{}; + kstd::shared_ptr m_file; }; } // namespace filesystem::ext2 diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index 8c9f75e..1083d13 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -7,7 +7,7 @@ namespace filesystem { struct file; - struct inode + struct inode : kstd::enable_shared_from_this { enum class inode_kind { @@ -20,7 +20,7 @@ namespace filesystem virtual ~inode() = default; - [[nodiscard]] virtual auto open_file(kstd::shared_ptr const & self) const -> kstd::shared_ptr = 0; + [[nodiscard]] virtual auto open_file() -> kstd::shared_ptr = 0; [[nodiscard]] auto is_directory() const -> bool; [[nodiscard]] auto is_regular() const -> bool; diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index 65dd9a3..b9ccd6d 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -21,14 +21,9 @@ namespace filesystem } } - auto device_inode::open_file(kstd::shared_ptr const & self) const -> kstd::shared_ptr + auto device_inode::open_file() -> kstd::shared_ptr { - if (!self) - { - kapi::system::panic("[FILESYSTEM] device_inode::open_file called with null inode."); - } - - return kstd::make_shared(self); + return kstd::make_shared(shared_from_this()); } auto device_inode::device() const -> kstd::shared_ptr const & diff --git a/kernel/src/filesystem/ext2/ext2_inode.cpp b/kernel/src/filesystem/ext2/ext2_inode.cpp index 0a1994b..0760cb1 100644 --- a/kernel/src/filesystem/ext2/ext2_inode.cpp +++ b/kernel/src/filesystem/ext2/ext2_inode.cpp @@ -1,7 +1,5 @@ #include "kernel/filesystem/ext2/ext2_inode.hpp" -#include "kapi/system.hpp" - #include "kernel/filesystem/file.hpp" #include "kernel/filesystem/inode.hpp" #include "kernel/filesystem/inode_file.hpp" @@ -14,13 +12,8 @@ namespace filesystem::ext2 : inode(inode_kind::regular) {} - auto ext2_inode::open_file(kstd::shared_ptr const & self) const -> kstd::shared_ptr + auto ext2_inode::open_file() -> kstd::shared_ptr { - if (!self) - { - kapi::system::panic("[FILESYSTEM] ext2_inode::open_file called with null inode."); - } - - return kstd::make_shared(self); + return kstd::make_shared(shared_from_this()); } } // namespace filesystem::ext2 \ No newline at end of file diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 5330b82..2316de0 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -70,7 +70,7 @@ namespace filesystem { auto node = custody->get_inode(); - if (auto opened_file = node->open_file(node)) + if (auto opened_file = node->open_file()) { return kstd::make_shared(opened_file); } -- cgit v1.2.3 From 6c172389b562a08a6540574d6fbdf6a5bdce37b8 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 21 Mar 2026 16:22:23 +0100 Subject: simplify architecture again --- kernel/CMakeLists.txt | 3 - kernel/include/kernel/filesystem/device_file.hpp | 32 ------ kernel/include/kernel/filesystem/device_inode.hpp | 14 ++- .../include/kernel/filesystem/ext2/ext2_inode.hpp | 6 +- kernel/include/kernel/filesystem/file.hpp | 25 ---- kernel/include/kernel/filesystem/inode.hpp | 7 +- kernel/include/kernel/filesystem/inode_file.hpp | 24 ---- .../kernel/filesystem/open_file_description.hpp | 6 +- kernel/src/filesystem/device_file.cpp | 128 --------------------- kernel/src/filesystem/device_inode.cpp | 100 +++++++++++++++- kernel/src/filesystem/ext2/ext2_inode.cpp | 15 ++- kernel/src/filesystem/file.cpp | 20 ---- kernel/src/filesystem/inode_file.cpp | 34 ------ kernel/src/filesystem/open_file_description.cpp | 14 +-- kernel/src/filesystem/vfs.cpp | 6 +- kernel/src/main.cpp | 9 +- 16 files changed, 138 insertions(+), 305 deletions(-) delete mode 100644 kernel/include/kernel/filesystem/device_file.hpp delete mode 100644 kernel/include/kernel/filesystem/file.hpp delete mode 100644 kernel/include/kernel/filesystem/inode_file.hpp delete mode 100644 kernel/src/filesystem/device_file.cpp delete mode 100644 kernel/src/filesystem/file.cpp delete mode 100644 kernel/src/filesystem/inode_file.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index ec0d4e2..16ebb8b 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -25,12 +25,9 @@ add_executable("kernel" "src/filesystem/ext2/ext2_filesystem.cpp" "src/filesystem/ext2/ext2_inode.cpp" "src/filesystem/custody.cpp" - "src/filesystem/device_file.cpp" "src/filesystem/device_inode.cpp" - "src/filesystem/file.cpp" "src/filesystem/file_descriptor_table.cpp" "src/filesystem/filesystem.cpp" - "src/filesystem/inode_file.cpp" "src/filesystem/inode.cpp" "src/filesystem/mount.cpp" "src/filesystem/open_file_description.cpp" diff --git a/kernel/include/kernel/filesystem/device_file.hpp b/kernel/include/kernel/filesystem/device_file.hpp deleted file mode 100644 index 22ddb49..0000000 --- a/kernel/include/kernel/filesystem/device_file.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVICE_FILE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_FILE_HPP - -#include "kernel/devices/block_device.hpp" -#include "kernel/devices/device.hpp" -#include "kernel/filesystem/file.hpp" - -#include - -#include - -namespace filesystem -{ - struct inode; - - struct device_file : file - { - explicit device_file(kstd::shared_ptr const & inode); - - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; - - private: - using block_op = void (*)(size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, - std::byte * scratch, void * buffer); - auto process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t; - - [[nodiscard]] auto device() const -> kstd::shared_ptr const &; - }; -} // namespace filesystem - -#endif diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index 10e40b3..ce0f91c 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -1,24 +1,28 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP +#include "kernel/devices/block_device.hpp" #include "kernel/devices/device.hpp" #include "kernel/filesystem/inode.hpp" #include +#include + namespace filesystem { - struct file; - struct device_inode : inode { explicit device_inode(kstd::shared_ptr const & device); - [[nodiscard]] auto open_file() -> kstd::shared_ptr override; - - [[nodiscard]] auto device() const -> kstd::shared_ptr const &; + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; private: + using block_op = void (*)(size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, + std::byte * scratch, void * buffer); + auto process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t; + kstd::shared_ptr m_device; }; } // namespace filesystem diff --git a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp index e6e8e99..5f4d16a 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp @@ -2,18 +2,20 @@ #define TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP #include "kernel/filesystem/ext2/ext2_file.hpp" -#include "kernel/filesystem/file.hpp" #include "kernel/filesystem/inode.hpp" #include +#include + namespace filesystem::ext2 { struct ext2_inode : inode { explicit ext2_inode(); - [[nodiscard]] auto open_file() -> kstd::shared_ptr override; + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; private: kstd::shared_ptr m_file; diff --git a/kernel/include/kernel/filesystem/file.hpp b/kernel/include/kernel/filesystem/file.hpp deleted file mode 100644 index 522f078..0000000 --- a/kernel/include/kernel/filesystem/file.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_FILE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_FILE_HPP - -#include - -#include - -namespace filesystem -{ - struct inode; - - struct file - { - explicit file(kstd::shared_ptr const & inode); - virtual ~file() = default; - - virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0; - virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0; - - protected: - kstd::shared_ptr m_inode; - }; -} // namespace filesystem - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index 1083d13..94ccd89 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -3,10 +3,10 @@ #include +#include + namespace filesystem { - struct file; - struct inode : kstd::enable_shared_from_this { enum class inode_kind @@ -20,7 +20,8 @@ namespace filesystem virtual ~inode() = default; - [[nodiscard]] virtual auto open_file() -> kstd::shared_ptr = 0; + virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0; + virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0; [[nodiscard]] auto is_directory() const -> bool; [[nodiscard]] auto is_regular() const -> bool; diff --git a/kernel/include/kernel/filesystem/inode_file.hpp b/kernel/include/kernel/filesystem/inode_file.hpp deleted file mode 100644 index f87b77c..0000000 --- a/kernel/include/kernel/filesystem/inode_file.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_INODE_FILE_HPP - -#include "kernel/filesystem/file.hpp" -#include "kernel/filesystem/inode.hpp" - -#include - -#include - -namespace filesystem -{ - struct inode; - - struct inode_file : file - { - explicit inode_file(kstd::shared_ptr const & inode); - - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; - }; -} // namespace filesystem - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/open_file_description.hpp b/kernel/include/kernel/filesystem/open_file_description.hpp index 5ff094d..3c97d50 100644 --- a/kernel/include/kernel/filesystem/open_file_description.hpp +++ b/kernel/include/kernel/filesystem/open_file_description.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP #define TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP -#include "file.hpp" +#include "kernel/filesystem/inode.hpp" #include @@ -11,7 +11,7 @@ namespace filesystem { struct open_file_description { - open_file_description(kstd::shared_ptr const & file); + open_file_description(kstd::shared_ptr const & inode); ~open_file_description() = default; @@ -19,7 +19,7 @@ namespace filesystem auto write(void const * buffer, size_t size) -> size_t; private: - kstd::shared_ptr m_file; + kstd::shared_ptr m_inode; size_t m_offset; }; diff --git a/kernel/src/filesystem/device_file.cpp b/kernel/src/filesystem/device_file.cpp deleted file mode 100644 index 26c7511..0000000 --- a/kernel/src/filesystem/device_file.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "kernel/filesystem/device_file.hpp" - -#include "kapi/system.hpp" - -#include "kernel/devices/block_device.hpp" -#include "kernel/devices/device.hpp" -#include "kernel/filesystem/device_inode.hpp" -#include "kernel/filesystem/file.hpp" -#include "kernel/filesystem/inode.hpp" - -#include -#include -#include - -#include -#include - -namespace filesystem -{ - device_file::device_file(kstd::shared_ptr const & inode) - : file(inode) - { - if (!m_inode->is_device()) - { - kapi::system::panic("[FILESYSTEM] device_file constructed with non-device inode."); - } - } - - auto device_file::read(void * buffer, size_t offset, size_t size) const -> size_t - { - if (device()->is_block_device()) - { - return process_blocks(offset, size, buffer, - [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, - std::byte * scratch, void * buffer) { - auto * out = static_cast(buffer); - if (off == 0 && len == device->block_size()) - { - device->read_block(idx, out + done); - } - else - { - device->read_block(idx, scratch); - kstd::libc::memcpy(out + done, scratch + off, len); - } - }); - } - else - { - kapi::system::panic("[FILESYSTEM] device_file::read called on non-block device."); - } - } - - auto device_file::write(void const * buffer, size_t offset, size_t size) -> size_t - { - if (device()->is_block_device()) - { - return process_blocks(offset, size, const_cast(buffer), - [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, - std::byte * scratch, void * buffer) { - auto const * in = static_cast(buffer); - if (off == 0 && len == device->block_size()) - { - device->write_block(idx, in + done); - } - else - { - device->read_block(idx, scratch); - kstd::libc::memcpy(scratch + off, in + done, len); - device->write_block(idx, scratch); - } - }); - } - else - { - kapi::system::panic("[FILESYSTEM] device_file::write called on non-block device."); - } - } - - auto device_file::process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t - { - if (buffer == nullptr) - { - kapi::system::panic("[FILESYSTEM] device_file::process_blocks called with null buffer."); - } - - if (size == 0) - { - return 0; - } - - auto * block_dev = static_cast(device().get()); - if (block_dev == nullptr) - { - kapi::system::panic("[FILESYSTEM] device_file: expected block_device."); - } - - size_t const block_size = block_dev->block_size(); - size_t const capacity = block_dev->capacity(); - - if (offset >= capacity) - return 0; - size_t const total_to_process = std::min(size, capacity - offset); - - kstd::vector scratch_buffer{block_size}; - auto processed = 0uz; - - while (processed < total_to_process) - { - size_t const absolute_offset = offset + processed; - size_t const block_index = absolute_offset / block_size; - size_t const in_block_offset = absolute_offset % block_size; - size_t const chunk_size = std::min(total_to_process - processed, block_size - in_block_offset); - - op(block_index, in_block_offset, chunk_size, processed, block_dev, scratch_buffer.data(), buffer); - - processed += chunk_size; - } - - return processed; - } - - auto device_file::device() const -> kstd::shared_ptr const & - { - auto inode = static_cast(m_inode.get()); - return inode->device(); - } -} // namespace filesystem diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index b9ccd6d..812b43a 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -2,12 +2,16 @@ #include "kapi/system.hpp" +#include "kernel/devices/block_device.hpp" #include "kernel/devices/device.hpp" -#include "kernel/filesystem/device_file.hpp" -#include "kernel/filesystem/file.hpp" #include "kernel/filesystem/inode.hpp" +#include #include +#include + +#include +#include namespace filesystem { @@ -21,13 +25,97 @@ namespace filesystem } } - auto device_inode::open_file() -> kstd::shared_ptr + auto device_inode::read(void * buffer, size_t offset, size_t size) const -> size_t { - return kstd::make_shared(shared_from_this()); + if (m_device->is_block_device()) + { + return process_blocks(offset, size, buffer, + [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, + std::byte * scratch, void * buffer) { + auto * out = static_cast(buffer); + if (off == 0 && len == device->block_size()) + { + device->read_block(idx, out + done); + } + else + { + device->read_block(idx, scratch); + kstd::libc::memcpy(out + done, scratch + off, len); + } + }); + } + else + { + kapi::system::panic("[FILESYSTEM] device_file::read called on non-block device."); + } } - auto device_inode::device() const -> kstd::shared_ptr const & + auto device_inode::write(void const * buffer, size_t offset, size_t size) -> size_t { - return m_device; + if (m_device->is_block_device()) + { + return process_blocks(offset, size, const_cast(buffer), + [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, + std::byte * scratch, void * buffer) { + auto const * in = static_cast(buffer); + if (off == 0 && len == device->block_size()) + { + device->write_block(idx, in + done); + } + else + { + device->read_block(idx, scratch); + kstd::libc::memcpy(scratch + off, in + done, len); + device->write_block(idx, scratch); + } + }); + } + else + { + kapi::system::panic("[FILESYSTEM] device_file::write called on non-block device."); + } + } + + auto device_inode::process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t + { + if (buffer == nullptr) + { + kapi::system::panic("[FILESYSTEM] device_file::process_blocks called with null buffer."); + } + + if (size == 0) + { + return 0; + } + + auto * block_dev = static_cast(m_device.get()); + if (block_dev == nullptr) + { + kapi::system::panic("[FILESYSTEM] device_file: expected block_device."); + } + + size_t const block_size = block_dev->block_size(); + size_t const capacity = block_dev->capacity(); + + if (offset >= capacity) + return 0; + size_t const total_to_process = std::min(size, capacity - offset); + + kstd::vector scratch_buffer{block_size}; + auto processed = 0uz; + + while (processed < total_to_process) + { + size_t const absolute_offset = offset + processed; + size_t const block_index = absolute_offset / block_size; + size_t const in_block_offset = absolute_offset % block_size; + size_t const chunk_size = std::min(total_to_process - processed, block_size - in_block_offset); + + op(block_index, in_block_offset, chunk_size, processed, block_dev, scratch_buffer.data(), buffer); + + processed += chunk_size; + } + + return processed; } } // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/ext2/ext2_inode.cpp b/kernel/src/filesystem/ext2/ext2_inode.cpp index 0760cb1..3cc0fb2 100644 --- a/kernel/src/filesystem/ext2/ext2_inode.cpp +++ b/kernel/src/filesystem/ext2/ext2_inode.cpp @@ -1,10 +1,8 @@ #include "kernel/filesystem/ext2/ext2_inode.hpp" -#include "kernel/filesystem/file.hpp" #include "kernel/filesystem/inode.hpp" -#include "kernel/filesystem/inode_file.hpp" -#include +#include namespace filesystem::ext2 { @@ -12,8 +10,15 @@ namespace filesystem::ext2 : inode(inode_kind::regular) {} - auto ext2_inode::open_file() -> kstd::shared_ptr + auto ext2_inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t { - return kstd::make_shared(shared_from_this()); + // TODO BA-FS26 implement + return 0; + } + + auto ext2_inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t + { + // TODO BA-FS26 implement + return 0; } } // namespace filesystem::ext2 \ No newline at end of file diff --git a/kernel/src/filesystem/file.cpp b/kernel/src/filesystem/file.cpp deleted file mode 100644 index a147863..0000000 --- a/kernel/src/filesystem/file.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "kernel/filesystem/file.hpp" - -#include "kapi/system.hpp" - -#include "kernel/filesystem/inode.hpp" - -#include - -namespace filesystem -{ - - file::file(kstd::shared_ptr const & inode) - : m_inode(inode) - { - if (!m_inode) - { - kapi::system::panic("[FILESYSTEM] file constructed with null inode."); - } - } -} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/inode_file.cpp b/kernel/src/filesystem/inode_file.cpp deleted file mode 100644 index 8b2fcba..0000000 --- a/kernel/src/filesystem/inode_file.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "kernel/filesystem/inode_file.hpp" - -#include "kapi/system.hpp" - -#include "kernel/filesystem/file.hpp" -#include "kernel/filesystem/inode.hpp" - -#include - -#include - -namespace filesystem -{ - inode_file::inode_file(kstd::shared_ptr const & inode) - : file(inode) - { - if (m_inode->is_device()) - { - kapi::system::panic("[FILESYSTEM] inode_file constructed with device inode."); - } - } - - auto inode_file::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t - { - // TODO BA-FS26 - return 0; - } - - auto inode_file::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t - { - // TODO BA-FS26 - return 0; - } -} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/open_file_description.cpp b/kernel/src/filesystem/open_file_description.cpp index ff4d678..93c38ac 100644 --- a/kernel/src/filesystem/open_file_description.cpp +++ b/kernel/src/filesystem/open_file_description.cpp @@ -1,6 +1,6 @@ #include "kernel/filesystem/open_file_description.hpp" -#include "kernel/filesystem/file.hpp" +#include "kernel/filesystem/inode.hpp" #include #include @@ -9,26 +9,26 @@ namespace filesystem { - open_file_description::open_file_description(kstd::shared_ptr const & file) - : m_file(file) + open_file_description::open_file_description(kstd::shared_ptr const & inode) + : m_inode(inode) , m_offset(0) { - if (!file) + if (!inode) { - kstd::os::panic("[FILESYSTEM] open_file_description constructed with null file."); + kstd::os::panic("[FILESYSTEM] open_file_description constructed with null inode."); } } auto open_file_description::read(void * buffer, size_t size) -> size_t { - auto read_bytes = m_file->read(buffer, m_offset, size); + auto read_bytes = m_inode->read(buffer, m_offset, size); m_offset += read_bytes; return read_bytes; } auto open_file_description::write(void const * buffer, size_t size) -> size_t { - auto written_bytes = m_file->write(buffer, m_offset, size); + auto written_bytes = m_inode->write(buffer, m_offset, size); m_offset += written_bytes; return written_bytes; } diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 2316de0..86991ea 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -68,11 +68,9 @@ namespace filesystem { if (auto custody = resolve_path(path)) { - auto node = custody->get_inode(); - - if (auto opened_file = node->open_file()) + if (auto node = custody->get_inode()) { - return kstd::make_shared(opened_file); + return kstd::make_shared(node); } kapi::system::panic("[FILESYSTEM] inode::open_file returned null file."); diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 8d7308b..eb699bd 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -4,7 +4,6 @@ #include "kapi/system.hpp" #include "kernel/devices/storage/storage_management.hpp" -#include "kernel/filesystem/device_file.hpp" #include "kernel/filesystem/device_inode.hpp" #include "kernel/filesystem/file_descriptor_table.hpp" #include "kernel/filesystem/open_file_description.hpp" @@ -28,8 +27,7 @@ auto test_file_description_manually() -> void auto dev_node = kstd::make_shared(device); - auto dev_file = kstd::make_shared(dev_node); - auto ofd = kstd::make_shared(dev_file); + auto ofd = kstd::make_shared(dev_node); auto fd_index = fd_table.add_file(ofd); // use: read two bytes and write two again @@ -64,7 +62,7 @@ auto test_file_description_manually() -> void fd_table.remove_file(fd_index); // use: read four bytes again -> two old bytes two new bytes - auto ofd1 = kstd::make_shared(dev_file); + auto ofd1 = kstd::make_shared(dev_node); fd_index = fd_table.add_file(ofd1); auto fd1 = fd_table.get_file(fd_index); @@ -91,6 +89,9 @@ auto test_file_description_manually() -> void auto test_device_with_vfs() -> void { // TODO BA-FS26 + + auto vfs = filesystem::vfs::get(); + vfs.open("/"); } auto run_test_code() -> void -- cgit v1.2.3 From 9d23a9fb6ae774bf3b1345d0b7adcb88f9627dc0 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 21 Mar 2026 16:35:01 +0100 Subject: refactoring, node cannot be null --- kernel/src/filesystem/vfs.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 86991ea..c448618 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -68,12 +68,7 @@ namespace filesystem { if (auto custody = resolve_path(path)) { - if (auto node = custody->get_inode()) - { - return kstd::make_shared(node); - } - - kapi::system::panic("[FILESYSTEM] inode::open_file returned null file."); + return kstd::make_shared(custody->get_inode()); } return nullptr; -- cgit v1.2.3 From 10b77e5c9741211f99cefecd50bcec76dc046d84 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 21 Mar 2026 19:07:02 +0100 Subject: implement simple kstd::string --- libs/kstd/include/kstd/string | 264 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 libs/kstd/include/kstd/string diff --git a/libs/kstd/include/kstd/string b/libs/kstd/include/kstd/string new file mode 100644 index 0000000..62126a9 --- /dev/null +++ b/libs/kstd/include/kstd/string @@ -0,0 +1,264 @@ +#ifndef KSTD_STRING_HPP +#define KSTD_STRING_HPP + +#include +#include +#include + +#include +#include +#include + +namespace kstd +{ + /** + * @brief A simple string implementation that owns its data and provides basic operations. + */ + struct string + { + //! The type of the characters contained in this string. + using value_type = char; + //! The type of the underlying storage used by this string. + using storage_type = kstd::vector; + //! The type of all sizes used in and with this string. + using size_type = std::size_t; + //! The type of the difference between two iterators. + using difference_type = std::ptrdiff_t; + //! The type of references to single values in this string. + using reference = value_type &; + //! The type of references to constant single values in this string. + using const_reference = value_type const &; + //! The type of pointers to single values in this string. + using pointer = value_type *; + //! The type of pointers to constant single values in this string. + using const_pointer = value_type const *; + //! The type of iterators into this string. + using iterator = pointer; + //! The type of constant iterators into this string. + using const_iterator = const_pointer; + + /** + * @brief Constructs an empty null-terminated string. + */ + string() + : m_storage{value_type{'\0'}} + {} + + /** + * @brief Constructs a string from a string view by copying the characters into owned storage. + * @param view The string view to copy the characters from. + */ + string(std::string_view view) + : string() + { + append(view); + } + + /** + * @brief Constructs a string by copying another string. + * @param other The string to copy. + */ + constexpr string(string const & other) + : m_storage{other.m_storage} + {} + + /** + * @brief Destructs the string. + */ + constexpr ~string() = default; + + /** + * @brief Assigns the value of another string to this string. + * @param other The string to assign from. + * @return A reference to this string. + */ + constexpr auto operator=(string const & other) -> string & = default; + + /** + * @brief Returns the number of characters in this string, not including the null terminator. + */ + [[nodiscard]] constexpr auto size() const noexcept -> size_type + { + return m_storage.empty() ? 0 : m_storage.size() - 1; + } + + /** + * @brief Checks if this string is empty, not including the null terminator. + */ + [[nodiscard]] constexpr auto empty() const noexcept -> bool + { + return size() == 0; + } + + /** + * @brief Clears the content of the string, resulting in an empty string. + * The string remains null-terminated after this operation. + */ + constexpr auto clear() -> void + { + m_storage.clear(); + m_storage.push_back(value_type{'\0'}); + } + + //! Get a pointer to the underlying storage of the string + [[nodiscard]] constexpr auto data() noexcept -> pointer + { + return m_storage.data(); + } + + //! Get a const pointer to the underlying storage of the string + [[nodiscard]] constexpr auto data() const noexcept -> const_pointer + { + return m_storage.data(); + } + + //! Get a const pointer to the underlying storage of the string + [[nodiscard]] constexpr auto c_str() const noexcept -> const_pointer + { + return data(); + } + + //! Get an iterator to the beginning of the string + [[nodiscard]] constexpr auto begin() noexcept -> iterator + { + return data(); + } + + //! Get an const iterator to the beginning of the string + [[nodiscard]] constexpr auto begin() const noexcept -> const_iterator + { + return data(); + } + + //! Get an const iterator to the beginning of the string + [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator + { + return begin(); + } + + //! Get an iterator to the end of the string + [[nodiscard]] constexpr auto end() noexcept -> iterator + { + return data() + size(); + } + + //! Get an const iterator to the end of the string + [[nodiscard]] constexpr auto end() const noexcept -> const_iterator + { + return data() + size(); + } + + //! Get an const iterator to the end of the string + [[nodiscard]] constexpr auto cend() const noexcept -> const_iterator + { + return end(); + } + + //! Get a reference to the first character of the string + [[nodiscard]] constexpr auto front() -> reference + { + return m_storage.front(); + } + + //! Get a const reference to the first character of the string + [[nodiscard]] constexpr auto front() const -> const_reference + { + return m_storage.front(); + } + + //! Get a reference to the last character of the string + [[nodiscard]] constexpr auto back() -> reference + { + return m_storage[size() - 1]; + } + + //! Get a const reference to the last character of the string + [[nodiscard]] constexpr auto back() const -> const_reference + { + return m_storage[size() - 1]; + } + + /** + * @brief Appends a character to the end of the string. + * @param ch The character to append. + */ + constexpr auto push_back(value_type ch) -> void + { + m_storage.back() = ch; + m_storage.push_back(value_type{'\0'}); + } + + /** + * @brief Appends a string view to the end of the string by copying the characters into owned storage. + * @param view The string view to append. + * @return A reference to this string. + */ + constexpr auto append(std::string_view view) -> string & + { + if (!view.empty()) + { + std::ranges::for_each(view, [this](auto const ch) { push_back(ch); }); + } + + return *this; + } + + /** + * @brief Appends another string to the end of this string by copying the characters into owned storage. + * @param other The string to append. + * @return A reference to this string. + */ + constexpr auto append(string const & other) -> string & + { + return append(other.view()); + } + + /** + * @brief Appends another string to the end of this string by copying the characters into owned storage. + * @param other The string to append. + * @return A reference to this string. + */ + constexpr auto operator+=(string const & other) -> string & + { + return append(other); + } + + /** + * @brief Appends a character to the end of the string. + * @param ch The character to append. + * @return A reference to this string. + */ + constexpr auto operator+=(value_type ch) -> string & + { + push_back(ch); + return *this; + } + + /** + * @brief Returns a string view of this string, which is a non-owning view into the characters of this string. + */ + [[nodiscard]] constexpr auto view() const noexcept -> std::string_view + { + return std::string_view{data(), size()}; + } + + private: + //! The underlying storage of the string, which owns the characters and ensures null-termination. + storage_type m_storage{}; + }; + + /** + * @brief Concatenates a strings and a character and returns the result as a new string. + * @param lhs The string to concatenate. + * @param c The character to concatenate. + * @return A new string that is the result of concatenating @p lhs and @p c. + */ + [[nodiscard]] constexpr auto inline operator+(string const & lhs, char const c) -> string + { + string result{lhs}; + result += c; + return result; + } +} // namespace kstd + +#endif \ No newline at end of file -- cgit v1.2.3 From c470ca76ce7801a2a4efb03c9ed606b34b368ded Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 21 Mar 2026 23:57:21 +0100 Subject: implement simple conversion function from unsigned integral values to kstd::string --- libs/kstd/include/kstd/string | 61 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/libs/kstd/include/kstd/string b/libs/kstd/include/kstd/string index 62126a9..f9583d5 100644 --- a/libs/kstd/include/kstd/string +++ b/libs/kstd/include/kstd/string @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -54,6 +55,29 @@ namespace kstd append(view); } + /** + * @brief Constructs a string from a null-terminated C-style string by copying the characters into owned storage. + * @param c_str The null-terminated C-style string to copy. + */ + string(char const * c_str) + : string() + { + if (c_str != nullptr) + { + append(std::string_view{c_str}); + } + } + + /** + * @brief Constructs a string containing a single character. + * @param c The character to copy. + */ + string(value_type c) + : string() + { + push_back(c); + } + /** * @brief Constructs a string by copying another string. * @param other The string to copy. @@ -250,13 +274,42 @@ namespace kstd /** * @brief Concatenates a strings and a character and returns the result as a new string. * @param lhs The string to concatenate. - * @param c The character to concatenate. - * @return A new string that is the result of concatenating @p lhs and @p c. + * @param rhs The string to concatenate. + * @return A new string that is the result of concatenating @p lhs and @p rhs. */ - [[nodiscard]] constexpr auto inline operator+(string const & lhs, char const c) -> string + [[nodiscard]] constexpr auto inline operator+(string const & lhs, string const & rhs) -> string { string result{lhs}; - result += c; + result += rhs; + return result; + } + + /** + * @brief Converts an unsigned integer to a string by converting each digit to the corresponding character and + * concatenating them. + * @tparam N The type of the unsigned integer to convert. + * @param value The unsigned integer to convert. + * @return A string representation of the given unsigned integer. + */ + template + requires std::unsigned_integral + [[nodiscard]] constexpr auto inline to_string(N value) -> string + { + if (value == 0) + { + return "0"; + } + + string result; + + while (value > 0) + { + char const digit = '0' + (value % 10); + result.push_back(digit); + value /= 10; + } + + std::reverse(result.begin(), result.end()); return result; } } // namespace kstd -- cgit v1.2.3 From ac3510bb9f696869f059ecd4ece2c6970fa63b6c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 21 Mar 2026 23:57:49 +0100 Subject: implement device names with kstd::string --- kernel/include/kernel/devices/block_device.hpp | 5 +++-- kernel/include/kernel/devices/device.hpp | 9 +++++---- kernel/src/devices/block_device.cpp | 5 +++-- kernel/src/devices/device.cpp | 7 ++++--- kernel/src/devices/storage/ram_disk/ram_disk_device.cpp | 15 ++------------- kernel/src/filesystem/vfs.cpp | 2 +- kernel/src/main.cpp | 13 +++++++++++++ 7 files changed, 31 insertions(+), 25 deletions(-) diff --git a/kernel/include/kernel/devices/block_device.hpp b/kernel/include/kernel/devices/block_device.hpp index fb7d104..e2026dd 100644 --- a/kernel/include/kernel/devices/block_device.hpp +++ b/kernel/include/kernel/devices/block_device.hpp @@ -3,8 +3,9 @@ #include "kernel/devices/device.hpp" +#include + #include -#include namespace devices { @@ -20,7 +21,7 @@ namespace devices * @param name Device name. * @param block_size Size of one logical block in bytes. */ - block_device(size_t major, size_t minor, std::string_view name, size_t block_size); + block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size); /** * @brief Read data from the block at @p block_index into @p buffer. diff --git a/kernel/include/kernel/devices/device.hpp b/kernel/include/kernel/devices/device.hpp index d6f520f..66cb7f8 100644 --- a/kernel/include/kernel/devices/device.hpp +++ b/kernel/include/kernel/devices/device.hpp @@ -1,8 +1,9 @@ #ifndef TEACH_OS_KERNEL_DEVICES_DEVICE_HPP #define TEACH_OS_KERNEL_DEVICES_DEVICE_HPP +#include + #include -#include namespace devices { @@ -17,7 +18,7 @@ namespace devices * @param minor Device minor number. * @param name Device name. */ - device(size_t major, size_t minor, std::string_view name); + device(size_t major, size_t minor, kstd::string const & name); /** * @brief Virtual destructor for device. @@ -40,7 +41,7 @@ namespace devices * @brief Returns the name of the device. * @return Device name. */ - [[nodiscard]] auto name() const -> std::string_view; + [[nodiscard]] auto name() const -> kstd::string const &; /** * @brief Check if the device is a block device. @@ -54,7 +55,7 @@ namespace devices private: size_t m_major; size_t m_minor; - std::string_view m_name; + kstd::string m_name; }; } // namespace devices diff --git a/kernel/src/devices/block_device.cpp b/kernel/src/devices/block_device.cpp index d12251b..3402814 100644 --- a/kernel/src/devices/block_device.cpp +++ b/kernel/src/devices/block_device.cpp @@ -4,12 +4,13 @@ #include "kernel/devices/device.hpp" +#include + #include -#include namespace devices { - block_device::block_device(size_t major, size_t minor, std::string_view name, size_t block_size) + block_device::block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size) : device(major, minor, name) , m_block_size(block_size) { diff --git a/kernel/src/devices/device.cpp b/kernel/src/devices/device.cpp index 29498fa..287f14b 100644 --- a/kernel/src/devices/device.cpp +++ b/kernel/src/devices/device.cpp @@ -1,11 +1,12 @@ #include "kernel/devices/device.hpp" +#include + #include -#include namespace devices { - device::device(size_t major, size_t minor, std::string_view name) + device::device(size_t major, size_t minor, kstd::string const & name) : m_major(major) , m_minor(minor) , m_name(name) @@ -21,7 +22,7 @@ namespace devices return m_minor; } - auto device::name() const -> std::string_view + auto device::name() const -> kstd::string const & { return m_name; } diff --git a/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp b/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp index 650a151..bf329cb 100644 --- a/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp +++ b/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp @@ -6,30 +6,19 @@ #include "kernel/devices/block_device.hpp" #include +#include -#include #include -#include namespace devices::storage::ram_disk { namespace { constexpr size_t RAM_DISK_BLOCK_SIZE = 512uz; - - // TODO BA-FS26 @Felix - // TODO BA-FS26 currently only names for 9 minor devices - constinit std::array name = {'r', 'a', 'm', '0', '\0'}; - - auto determine_device_name(size_t minor) -> std::string_view - { - name[3] = '0' + minor; - return std::string_view{name}; - } } // namespace ram_disk_device::ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor) - : block_device(major, minor, determine_device_name(minor), RAM_DISK_BLOCK_SIZE) + : block_device(major, minor, "ram" + kstd::to_string(minor), RAM_DISK_BLOCK_SIZE) , m_boot_module(module) {} diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index c448618..1bd6fb5 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -120,7 +120,7 @@ namespace filesystem kapi::system::panic("[FILESYSTEM] make_device_node called with null device."); } - m_device_nodes.push_back(device_node_entry{device->name(), kstd::make_shared(device)}); + m_device_nodes.push_back(device_node_entry{device->name().view(), kstd::make_shared(device)}); } auto vfs::resolve_path(std::string_view path) -> std::optional diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index eb699bd..1043c81 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -10,14 +10,26 @@ #include "kernel/filesystem/vfs.hpp" #include "kernel/memory.hpp" +#include #include #include #include +#include #include +#include #include #include +auto test_device_names() -> void +{ + auto storage_mgmt = devices::storage::storage_management::get(); + std::ranges::for_each(storage_mgmt.all_controllers(), [](auto const & controller) { + std::ranges::for_each(controller->all_devices(), + [](auto const & device) { kstd::println("{}", device->name().view()); }); + }); +} + auto test_file_description_manually() -> void { // setup @@ -96,6 +108,7 @@ auto test_device_with_vfs() -> void auto run_test_code() -> void { + test_device_names(); test_file_description_manually(); test_device_with_vfs(); } -- cgit v1.2.3 From fde90dcc551b35ea03fef1231fafac9faea2c5b5 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 22 Mar 2026 09:31:03 +0100 Subject: add tests, use better println for vectors --- kernel/src/main.cpp | 57 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 1043c81..3ba240d 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -19,7 +19,6 @@ #include #include -#include auto test_device_names() -> void { @@ -51,17 +50,8 @@ auto test_file_description_manually() -> void kstd::vector buffer{2}; auto number_of_read_bytes = fd->read(buffer.data(), buffer.size()); - - for (size_t i = 0; i < number_of_read_bytes; ++i) - { - kstd::print("{:02x} ", static_cast(buffer[i])); - - if ((i + 1) % 16 == 0) - { - kstd::println(""); - } - } - kstd::println("---"); + kstd::println("read bytes: {}", number_of_read_bytes); + kstd::println("buffer: {::#04x}", buffer); // write half of the file new auto const value1 = std::byte{0xAA}; @@ -85,17 +75,8 @@ auto test_file_description_manually() -> void kstd::vector buffer1{4}; number_of_read_bytes = fd1->read(buffer1.data(), buffer1.size()); - - for (size_t i = 0; i < number_of_read_bytes; ++i) - { - kstd::print("{:02x} ", static_cast(buffer1[i])); - - if ((i + 1) % 16 == 0) - { - kstd::println(""); - } - } - kstd::println("---"); + kstd::println("read bytes: {}", number_of_read_bytes); + kstd::println("buffer: {::#04x}", buffer1); } auto test_device_with_vfs() -> void @@ -103,14 +84,41 @@ auto test_device_with_vfs() -> void // TODO BA-FS26 auto vfs = filesystem::vfs::get(); - vfs.open("/"); + auto ofd = vfs.open("/dev/ram0"); + if (!ofd) + { + kstd::os::panic("test code failed"); + } + + auto fd_table = filesystem::file_descriptor_table::get(); + auto fd = fd_table.add_file(ofd); + kstd::vector buffer{2}; + auto file = fd_table.get_file(fd); + if (!file) + { + kstd::os::panic("test code failed"); + } + + auto number_of_read_bytes = file->read(buffer.data(), buffer.size()); + kstd::println("read bytes: {}", number_of_read_bytes); + kstd::println("buffer: {::#04x}", buffer); } auto run_test_code() -> void { + kstd::println("[TEST] Running test code..."); + + kstd::println("[TEST] device names"); test_device_names(); + kstd::println("---------------------------------"); + + kstd::println("[TEST] file description manually"); test_file_description_manually(); + kstd::println("---------------------------------"); + + kstd::println("[TEST] device with VFS"); test_device_with_vfs(); + kstd::println("---------------------------------"); } auto main() -> int @@ -136,6 +144,7 @@ auto main() -> int kstd::println("[OS] Virtual filesystem initialized."); run_test_code(); + kstd::println("[TEST] All tests completed."); kapi::system::panic("Returning from kernel main!"); } -- cgit v1.2.3 From 840c7fec926e7fa8a9d9b64b23167f269096b59b Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sun, 22 Mar 2026 15:08:42 +0100 Subject: Remove unneeded enable_shared_from_this in inode --- kernel/include/kernel/filesystem/inode.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index 94ccd89..6d8f0d4 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -1,13 +1,11 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP -#include - #include namespace filesystem { - struct inode : kstd::enable_shared_from_this + struct inode { enum class inode_kind { -- cgit v1.2.3 From f90cbdaa91b1b7a4752db3f159ce2524696cff9f Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Mon, 23 Mar 2026 09:03:27 +0100 Subject: Rename custody to dentry and add children --- kernel/CMakeLists.txt | 2 +- kernel/include/kernel/filesystem/custody.hpp | 23 --------------------- kernel/include/kernel/filesystem/dentry.hpp | 25 +++++++++++++++++++++++ kernel/include/kernel/filesystem/vfs.hpp | 6 +++--- kernel/src/filesystem/custody.cpp | 30 ---------------------------- kernel/src/filesystem/dentry.cpp | 30 ++++++++++++++++++++++++++++ kernel/src/filesystem/vfs.cpp | 16 +++++++-------- 7 files changed, 67 insertions(+), 65 deletions(-) delete mode 100644 kernel/include/kernel/filesystem/custody.hpp create mode 100644 kernel/include/kernel/filesystem/dentry.hpp delete mode 100644 kernel/src/filesystem/custody.cpp create mode 100644 kernel/src/filesystem/dentry.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 16ebb8b..01fee74 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -24,7 +24,7 @@ add_executable("kernel" "src/filesystem/ext2/ext2_file.cpp" "src/filesystem/ext2/ext2_filesystem.cpp" "src/filesystem/ext2/ext2_inode.cpp" - "src/filesystem/custody.cpp" + "src/filesystem/dentry.cpp" "src/filesystem/device_inode.cpp" "src/filesystem/file_descriptor_table.cpp" "src/filesystem/filesystem.cpp" diff --git a/kernel/include/kernel/filesystem/custody.hpp b/kernel/include/kernel/filesystem/custody.hpp deleted file mode 100644 index 8a0e09a..0000000 --- a/kernel/include/kernel/filesystem/custody.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef TEACH_OS_KERNEL_CUSTODY_HPP -#define TEACH_OS_KERNEL_CUSTODY_HPP - -#include "kernel/filesystem/inode.hpp" - -#include - -namespace filesystem -{ - struct custody - { - custody(kstd::shared_ptr const & parent, kstd::shared_ptr const & node); - - [[nodiscard]] auto get_inode() const -> kstd::shared_ptr const &; - [[nodiscard]] auto get_parent() const -> kstd::shared_ptr const &; - - private: - kstd::shared_ptr m_parent; - kstd::shared_ptr m_inode; - }; -} // namespace filesystem - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp new file mode 100644 index 0000000..1532880 --- /dev/null +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -0,0 +1,25 @@ +#ifndef TEACH_OS_KERNEL_DENTRY_HPP +#define TEACH_OS_KERNEL_DENTRY_HPP + +#include "kernel/filesystem/inode.hpp" + +#include +#include + +namespace filesystem +{ + struct dentry + { + dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node); + + [[nodiscard]] auto get_inode() const -> kstd::shared_ptr const &; + [[nodiscard]] auto get_parent() const -> kstd::shared_ptr const &; + + private: + kstd::shared_ptr m_parent; + kstd::vector> m_children; + kstd::shared_ptr m_inode; + }; +} // namespace filesystem + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index ca51d90..bcf06f7 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -2,7 +2,7 @@ #define TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP #include "kernel/devices/device.hpp" -#include "kernel/filesystem/custody.hpp" +#include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include "kernel/filesystem/mount.hpp" @@ -37,11 +37,11 @@ namespace filesystem vfs() = default; auto make_device_node(kstd::shared_ptr const & device) -> void; - [[nodiscard]] auto resolve_path(std::string_view path) -> std::optional; + [[nodiscard]] auto resolve_path(std::string_view path) -> std::optional; kstd::shared_ptr m_root_fs; std::optional m_root_mount; - kstd::vector m_mounts; + kstd::vector m_mount_table; }; } // namespace filesystem diff --git a/kernel/src/filesystem/custody.cpp b/kernel/src/filesystem/custody.cpp deleted file mode 100644 index a4dd12c..0000000 --- a/kernel/src/filesystem/custody.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "kernel/filesystem/custody.hpp" - -#include "kapi/system.hpp" - -#include "kernel/filesystem/inode.hpp" - -#include - -namespace filesystem -{ - custody::custody(kstd::shared_ptr const & parent, kstd::shared_ptr const & node) - : m_parent(parent) - , m_inode(node) - { - if (!m_inode) - { - kapi::system::panic("[FILESYSTEM] custody constructed with null inode."); - } - } - - auto custody::get_inode() const -> kstd::shared_ptr const & - { - return m_inode; - } - - auto custody::get_parent() const -> kstd::shared_ptr const & - { - return m_parent; - } -} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp new file mode 100644 index 0000000..08f0b25 --- /dev/null +++ b/kernel/src/filesystem/dentry.cpp @@ -0,0 +1,30 @@ +#include "kernel/filesystem/dentry.hpp" + +#include "kapi/system.hpp" + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace filesystem +{ + dentry::dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node) + : m_parent(parent) + , m_inode(node) + { + if (!m_inode) + { + kapi::system::panic("[FILESYSTEM] dentry constructed with null inode."); + } + } + + auto dentry::get_inode() const -> kstd::shared_ptr const & + { + return m_inode; + } + + auto dentry::get_parent() const -> kstd::shared_ptr const & + { + return m_parent; + } +} // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 1bd6fb5..a111f23 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -4,7 +4,7 @@ #include "kernel/devices/device.hpp" #include "kernel/devices/storage/storage_management.hpp" -#include "kernel/filesystem/custody.hpp" +#include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/device_inode.hpp" #include "kernel/filesystem/ext2/ext2_filesystem.hpp" #include "kernel/filesystem/filesystem.hpp" @@ -66,9 +66,9 @@ namespace filesystem auto vfs::open(std::string_view path) -> kstd::shared_ptr { - if (auto custody = resolve_path(path)) + if (auto dentry = resolve_path(path)) { - return kstd::make_shared(custody->get_inode()); + return kstd::make_shared(dentry->get_inode()); } return nullptr; @@ -100,14 +100,14 @@ namespace filesystem } auto existing_mount = - std::ranges::find_if(m_mounts, [&](auto const & existing) { return existing.path() == path; }); - if (existing_mount != m_mounts.end()) + std::ranges::find_if(m_mount_table, [&](auto const & existing) { return existing.path() == path; }); + if (existing_mount != m_mount_table.end()) { *existing_mount = mount{path, filesystem}; } else { - m_mounts.push_back(mount{path, filesystem}); + m_mount_table.push_back(mount{path, filesystem}); } return 0; @@ -123,7 +123,7 @@ namespace filesystem m_device_nodes.push_back(device_node_entry{device->name().view(), kstd::make_shared(device)}); } - auto vfs::resolve_path(std::string_view path) -> std::optional + auto vfs::resolve_path(std::string_view path) -> std::optional { // TODO BA-FS26 implement real path resolution with mounts and directories etc. // For now, just support device nodes at /dev/. @@ -140,7 +140,7 @@ namespace filesystem if (entry != m_device_nodes.end()) { - return custody{nullptr, entry->value().node}; + return dentry{nullptr, entry->value().node}; } return std::nullopt; -- cgit v1.2.3 From d70e2df0885a844d47b6498bf2c710fb9730b364 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Mon, 23 Mar 2026 09:49:10 +0100 Subject: Add dentry structure --- kernel/include/kernel/filesystem/dentry.hpp | 14 ++++++++++++++ kernel/include/kernel/filesystem/vfs.hpp | 2 +- kernel/src/filesystem/dentry.cpp | 17 +++++++++++++++++ kernel/src/filesystem/vfs.cpp | 21 +++++++++++---------- 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index 1532880..db15b48 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -4,21 +4,35 @@ #include "kernel/filesystem/inode.hpp" #include +#include #include +#include + namespace filesystem { struct dentry { + enum class dentry_flags : uint32_t + { + dcache_mounted = 1 << 15 + }; + dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node); [[nodiscard]] auto get_inode() const -> kstd::shared_ptr const &; [[nodiscard]] auto get_parent() const -> kstd::shared_ptr const &; + auto set_flag(dentry_flags flag) -> void; + auto unset_flag(dentry_flags flag) -> void; + [[nodiscard]] auto has_flag(dentry_flags flag) const -> bool; + private: + kstd::string m_name; kstd::shared_ptr m_parent; kstd::vector> m_children; kstd::shared_ptr m_inode; + uint32_t m_flags{0}; }; } // namespace filesystem diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index bcf06f7..01cd2ea 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -40,7 +40,7 @@ namespace filesystem [[nodiscard]] auto resolve_path(std::string_view path) -> std::optional; kstd::shared_ptr m_root_fs; - std::optional m_root_mount; + kstd::shared_ptr m_root_dentry; kstd::vector m_mount_table; }; } // namespace filesystem diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index 08f0b25..e498b52 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -6,6 +6,8 @@ #include +#include + namespace filesystem { dentry::dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node) @@ -27,4 +29,19 @@ namespace filesystem { return m_parent; } + + auto dentry::set_flag(dentry_flags flag) -> void + { + m_flags |= static_cast(flag); + } + + auto dentry::unset_flag(dentry_flags flag) -> void + { + m_flags &= ~static_cast(flag); + } + + auto dentry::has_flag(dentry_flags flag) const -> bool + { + return (m_flags & static_cast(flag)) != 0; + } } // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index a111f23..2c4a4d5 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -38,10 +38,11 @@ namespace filesystem if (auto boot_device = storage_mgmt.determine_boot_device()) { active_vfs->m_root_fs = kstd::make_shared(); - if (active_vfs->do_mount("/", active_vfs->m_root_fs) != 0) - { - kapi::system::panic("[FILESYSTEM] Failed to mount root filesystem."); - } + active_vfs->m_root_dentry = kstd::make_shared(nullptr, active_vfs->m_root_fs->root_inode()); + // if (active_vfs->do_mount("/", active_vfs->m_root_fs) != 0) + // { + // kapi::system::panic("[FILESYSTEM] Failed to mount root filesystem."); + // } // TODO BA-FS26 use do_mount when tempdevfs is implemented -> just call /dev/ with all devices in devtempfs std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto controller) { @@ -92,12 +93,12 @@ namespace filesystem return -1; // TODO BA-FS26 panic or errorcode? } - if (path == "/") - { - m_root_fs = filesystem; - m_root_mount = mount{"/", filesystem}; - return 0; - } + // if (path == "/") + // { + // m_root_fs = filesystem; + // m_root_mount = mount{"/", filesystem}; + // return 0; + // } auto existing_mount = std::ranges::find_if(m_mount_table, [&](auto const & existing) { return existing.path() == path; }); -- cgit v1.2.3 From bcb4f8a76dea2443c1597716e27b7c2d268bfc44 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Mon, 23 Mar 2026 09:49:32 +0100 Subject: Refactor filesystem --- kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp | 1 - kernel/include/kernel/filesystem/filesystem.hpp | 5 ++++- kernel/src/filesystem/ext2/ext2_filesystem.cpp | 8 ++------ kernel/src/filesystem/filesystem.cpp | 11 +++++++++++ 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp b/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp index d6f69c8..92e05c8 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp @@ -17,7 +17,6 @@ namespace filesystem::ext2 auto lookup(inode const & parent, std::string_view name) -> inode * override; private: - kstd::shared_ptr m_device{}; }; } // namespace filesystem::ext2 diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 035f49d..733c51a 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -5,6 +5,7 @@ #include "kernel/filesystem/inode.hpp" #include +#include #include @@ -14,13 +15,15 @@ namespace filesystem { virtual ~filesystem() = default; - virtual auto mount(kstd::shared_ptr const & device) -> int = 0; + virtual auto mount(kstd::shared_ptr const & device) -> int; virtual auto lookup(inode const & parent, std::string_view name) -> inode * = 0; [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; protected: kstd::shared_ptr m_root_inode{}; + kstd::shared_ptr m_device{}; + kstd::vector> m_inodes{}; }; } // namespace filesystem diff --git a/kernel/src/filesystem/ext2/ext2_filesystem.cpp b/kernel/src/filesystem/ext2/ext2_filesystem.cpp index 87845d5..ea692ee 100644 --- a/kernel/src/filesystem/ext2/ext2_filesystem.cpp +++ b/kernel/src/filesystem/ext2/ext2_filesystem.cpp @@ -1,6 +1,7 @@ #include "kernel/filesystem/ext2/ext2_filesystem.hpp" #include "kernel/devices/device.hpp" +#include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include @@ -11,12 +12,7 @@ namespace filesystem::ext2 { auto ext2_filesystem::mount(kstd::shared_ptr const & device) -> int { - if (!device) - { - return -1; // TODO BA-FS26 panic or errorcode? - } - - m_device = device; + filesystem::mount(device); // TODO BA-FS26 error handling? // TODO BA-FS26 load proper root inode from ext2 metadata // m_root_inode = inode{inode_kind::directory}; diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index 50b5587..0e33d95 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -1,11 +1,22 @@ #include "kernel/filesystem/filesystem.hpp" +#include "kernel/devices/device.hpp" #include "kernel/filesystem/inode.hpp" #include namespace filesystem { + auto filesystem::mount(kstd::shared_ptr const & device) -> int + { + if (!device) + { + return -1; // TODO BA-FS26 panic or errorcode? + } + m_device = device; + return 0; + } + auto filesystem::root_inode() const -> kstd::shared_ptr const & { return m_root_inode; -- cgit v1.2.3 From fdcf1c7d2b47d418916e311cea8b87affaf63f90 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Mon, 23 Mar 2026 20:12:29 +0100 Subject: Small refactoring use shared pointer --- kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp | 2 +- kernel/include/kernel/filesystem/filesystem.hpp | 2 +- kernel/src/filesystem/ext2/ext2_filesystem.cpp | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp b/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp index 92e05c8..1445e5a 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp @@ -14,7 +14,7 @@ namespace filesystem::ext2 struct ext2_filesystem : filesystem { auto mount(kstd::shared_ptr const & device) -> int override; - auto lookup(inode const & parent, std::string_view name) -> inode * override; + auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; private: }; diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 733c51a..e069ced 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -16,7 +16,7 @@ namespace filesystem virtual ~filesystem() = default; virtual auto mount(kstd::shared_ptr const & device) -> int; - virtual auto lookup(inode const & parent, std::string_view name) -> inode * = 0; + virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr = 0; [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; diff --git a/kernel/src/filesystem/ext2/ext2_filesystem.cpp b/kernel/src/filesystem/ext2/ext2_filesystem.cpp index ea692ee..536e328 100644 --- a/kernel/src/filesystem/ext2/ext2_filesystem.cpp +++ b/kernel/src/filesystem/ext2/ext2_filesystem.cpp @@ -20,7 +20,8 @@ namespace filesystem::ext2 return 0; } - auto ext2_filesystem::lookup(inode const & /*parent*/, std::string_view /*name*/) -> inode * + auto ext2_filesystem::lookup(kstd::shared_ptr const & /*parent*/, std::string_view /*name*/) + -> kstd::shared_ptr { // TODO BA-FS26 implement ext2 directory traversal and inode loading return nullptr; -- cgit v1.2.3 From 7173e5ba354dccc4b5d5fea119b946f28bc5b08f Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Mon, 23 Mar 2026 20:22:43 +0100 Subject: Refactor move init logic into member function --- kernel/include/kernel/filesystem/vfs.hpp | 2 ++ kernel/src/filesystem/vfs.cpp | 15 +++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 01cd2ea..b6d4c4c 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -36,6 +36,8 @@ namespace filesystem kstd::vector> m_device_nodes; // TODO BA-FS26 remove again, use devtempfs vfs() = default; + auto init_internal() -> void; + auto make_device_node(kstd::shared_ptr const & device) -> void; [[nodiscard]] auto resolve_path(std::string_view path) -> std::optional; diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 2c4a4d5..ece3080 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -32,21 +32,28 @@ namespace filesystem } active_vfs.emplace(vfs{}); + active_vfs->init_internal(); + } + auto vfs::init_internal() -> void + { auto storage_mgmt = devices::storage::storage_management::get(); // TODO BA-FS26 fix mounting boot_device if (auto boot_device = storage_mgmt.determine_boot_device()) { - active_vfs->m_root_fs = kstd::make_shared(); - active_vfs->m_root_dentry = kstd::make_shared(nullptr, active_vfs->m_root_fs->root_inode()); - // if (active_vfs->do_mount("/", active_vfs->m_root_fs) != 0) + m_root_fs = kstd::make_shared(); + + m_root_fs->mount(boot_device); + + m_root_dentry = kstd::make_shared(nullptr, m_root_fs->root_inode()); + // if (do_mount("/", m_root_fs) != 0) // { // kapi::system::panic("[FILESYSTEM] Failed to mount root filesystem."); // } // TODO BA-FS26 use do_mount when tempdevfs is implemented -> just call /dev/ with all devices in devtempfs std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto controller) { - std::ranges::for_each(controller->all_devices(), [&](auto device) { active_vfs->make_device_node(device); }); + std::ranges::for_each(controller->all_devices(), [&](auto device) { make_device_node(device); }); }); } else -- cgit v1.2.3 From 336b25458b75e28c93c0bab23ccd359042f9df41 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Mon, 23 Mar 2026 21:40:34 +0100 Subject: Implement == and != operators for string and string_view --- libs/kstd/include/kstd/string | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/libs/kstd/include/kstd/string b/libs/kstd/include/kstd/string index f9583d5..075422e 100644 --- a/libs/kstd/include/kstd/string +++ b/libs/kstd/include/kstd/string @@ -312,6 +312,37 @@ namespace kstd std::reverse(result.begin(), result.end()); return result; } + + [[nodiscard]] constexpr auto inline operator==(string const & lhs, string const & rhs) -> bool + { + return lhs.view() == rhs.view(); + } + + [[nodiscard]] constexpr auto inline operator!=(string const & lhs, string const & rhs) -> bool + { + return !(lhs == rhs); + } + + [[nodiscard]] constexpr auto inline operator==(string const & lhs, std::string_view rhs) -> bool + { + return lhs.view() == rhs; + } + + [[nodiscard]] constexpr auto inline operator!=(string const & lhs, std::string_view rhs) -> bool + { + return !(lhs == rhs); + } + + [[nodiscard]] constexpr auto inline operator==(std::string_view lhs, string const & rhs) -> bool + { + return lhs == rhs.view(); + } + + [[nodiscard]] constexpr auto inline operator!=(std::string_view lhs, string const & rhs) -> bool + { + return !(lhs == rhs); + } + } // namespace kstd #endif \ No newline at end of file -- cgit v1.2.3 From fbb4eefce7bf825b0406f6fa63de318153a3b95a Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Mon, 23 Mar 2026 21:41:37 +0100 Subject: Implement resolve_path --- kernel/include/kernel/filesystem/dentry.hpp | 6 ++- .../kernel/filesystem/open_file_description.hpp | 2 +- kernel/include/kernel/filesystem/vfs.hpp | 2 +- kernel/src/filesystem/dentry.cpp | 18 ++++++++- kernel/src/filesystem/vfs.cpp | 46 +++++++++++++++++++--- 5 files changed, 64 insertions(+), 10 deletions(-) diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index db15b48..ca7ca95 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -8,6 +8,7 @@ #include #include +#include namespace filesystem { @@ -18,11 +19,14 @@ namespace filesystem dcache_mounted = 1 << 15 }; - dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node); + dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node, std::string_view name = {}); [[nodiscard]] auto get_inode() const -> kstd::shared_ptr const &; [[nodiscard]] auto get_parent() const -> kstd::shared_ptr const &; + auto add_child(kstd::shared_ptr const & child) -> void; + [[nodiscard]] auto find_child(std::string_view name) const -> kstd::shared_ptr; + auto set_flag(dentry_flags flag) -> void; auto unset_flag(dentry_flags flag) -> void; [[nodiscard]] auto has_flag(dentry_flags flag) const -> bool; diff --git a/kernel/include/kernel/filesystem/open_file_description.hpp b/kernel/include/kernel/filesystem/open_file_description.hpp index 3c97d50..e17f9fe 100644 --- a/kernel/include/kernel/filesystem/open_file_description.hpp +++ b/kernel/include/kernel/filesystem/open_file_description.hpp @@ -11,7 +11,7 @@ namespace filesystem { struct open_file_description { - open_file_description(kstd::shared_ptr const & inode); + explicit open_file_description(kstd::shared_ptr const & inode); ~open_file_description() = default; diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index b6d4c4c..317f2d1 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -39,7 +39,7 @@ namespace filesystem auto init_internal() -> void; auto make_device_node(kstd::shared_ptr const & device) -> void; - [[nodiscard]] auto resolve_path(std::string_view path) -> std::optional; + [[nodiscard]] auto resolve_path(std::string_view path) -> kstd::shared_ptr; kstd::shared_ptr m_root_fs; kstd::shared_ptr m_root_dentry; diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index e498b52..76949f2 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -6,12 +6,15 @@ #include +#include #include +#include namespace filesystem { - dentry::dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node) - : m_parent(parent) + dentry::dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node, std::string_view name) + : m_name(name) + , m_parent(parent) , m_inode(node) { if (!m_inode) @@ -30,6 +33,17 @@ namespace filesystem return m_parent; } + auto dentry::add_child(kstd::shared_ptr const & child) -> void + { + m_children.push_back(child); + } + + auto dentry::find_child(std::string_view name) const -> kstd::shared_ptr + { + auto it = std::ranges::find_if(m_children, [&](auto const & child) { return child->m_name == name; }); + return (it != m_children.end()) ? *it : nullptr; + } + auto dentry::set_flag(dentry_flags flag) -> void { m_flags |= static_cast(flag); diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index ece3080..0e8a76d 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -15,6 +15,7 @@ #include #include +#include #include namespace filesystem @@ -42,7 +43,6 @@ namespace filesystem if (auto boot_device = storage_mgmt.determine_boot_device()) { m_root_fs = kstd::make_shared(); - m_root_fs->mount(boot_device); m_root_dentry = kstd::make_shared(nullptr, m_root_fs->root_inode()); @@ -131,7 +131,7 @@ namespace filesystem m_device_nodes.push_back(device_node_entry{device->name().view(), kstd::make_shared(device)}); } - auto vfs::resolve_path(std::string_view path) -> std::optional + auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr { // TODO BA-FS26 implement real path resolution with mounts and directories etc. // For now, just support device nodes at /dev/. @@ -148,13 +148,49 @@ namespace filesystem if (entry != m_device_nodes.end()) { - return dentry{nullptr, entry->value().node}; + return kstd::make_shared(nullptr, entry->value().node); } - return std::nullopt; + return nullptr; } + else if (!path.empty() && path.front() == '/') + { + auto path_parts = + std::views::split(path, '/') | std::views::filter([](auto const & part) { return !part.empty(); }); + auto parent = m_root_dentry; - return std::nullopt; + for (auto const & part : path_parts) + { + if (auto child = parent->find_child(std::string_view{part})) + { + parent = child; + } + else if (auto found_inode = m_root_fs->lookup(parent->get_inode(), std::string_view{part})) + { + auto next_dentry = kstd::make_shared(parent, found_inode, std::string_view{part}); + parent->add_child(next_dentry); + parent = next_dentry; + } + else + { + return nullptr; + } + } + // | std::views::transform([this](auto const & part) { + // // TODO BA-FS26 implement real path resolution with mounts and directories etc. + // // For now, just return null for any non-device-node path. + // return std::optional>{}; + // }) + // | std::views::filter([](auto const & opt) { return opt.has_value(); }) + // | std::views::transform([](auto const & opt) { return opt.value(); }) + // | std::ranges::find_if(m_mount_table, [&](auto const & mount) { + // // TODO BA-FS26 implement real path resolution with mounts and directories etc. + // // For now, just check if the first path component matches a mount point. + // return part == mount.path(); + // }); + } + + return nullptr; } } // namespace filesystem \ No newline at end of file -- cgit v1.2.3 From b3cb1d0f8864bf54362f1da2b7a65ca693778cff Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Mon, 23 Mar 2026 22:18:54 +0100 Subject: Add test for resolve_path --- kernel/src/filesystem/ext2/ext2_filesystem.cpp | 5 ++++- kernel/src/main.cpp | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/kernel/src/filesystem/ext2/ext2_filesystem.cpp b/kernel/src/filesystem/ext2/ext2_filesystem.cpp index 536e328..3f5774c 100644 --- a/kernel/src/filesystem/ext2/ext2_filesystem.cpp +++ b/kernel/src/filesystem/ext2/ext2_filesystem.cpp @@ -1,6 +1,7 @@ #include "kernel/filesystem/ext2/ext2_filesystem.hpp" #include "kernel/devices/device.hpp" +#include "kernel/filesystem/ext2/ext2_inode.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" @@ -17,6 +18,7 @@ namespace filesystem::ext2 // m_root_inode = inode{inode_kind::directory}; // TODO BA-FS26 implement + m_root_inode = kstd::make_shared(); return 0; } @@ -24,6 +26,7 @@ namespace filesystem::ext2 -> kstd::shared_ptr { // TODO BA-FS26 implement ext2 directory traversal and inode loading - return nullptr; + // return nullptr; + return kstd::make_shared(); } } // namespace filesystem::ext2 diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 3ba240d..a575be2 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -104,6 +104,16 @@ auto test_device_with_vfs() -> void kstd::println("buffer: {::#04x}", buffer); } +auto test_file_lookup() -> void +{ + // TODO BA-FS26 implement a more complete test with multiple files and directories and mounts etc. + + auto vfs = filesystem::vfs::get(); + vfs.open("/a/b/c"); + vfs.open("/a/d/e"); + vfs.open("x/y/z"); +} + auto run_test_code() -> void { kstd::println("[TEST] Running test code..."); @@ -119,6 +129,10 @@ auto run_test_code() -> void kstd::println("[TEST] device with VFS"); test_device_with_vfs(); kstd::println("---------------------------------"); + + kstd::println("[TEST] file lookup"); + test_file_lookup(); + kstd::println("---------------------------------"); } auto main() -> int -- cgit v1.2.3 From 444810121f9c89285da1ec118828640f0445b2a7 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Tue, 24 Mar 2026 16:50:27 +0100 Subject: Add mount_table --- kernel/CMakeLists.txt | 1 + kernel/include/kernel/filesystem/mount_table.hpp | 23 +++++++++++++++++++ kernel/src/filesystem/mount_table.cpp | 29 ++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 kernel/include/kernel/filesystem/mount_table.hpp create mode 100644 kernel/src/filesystem/mount_table.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 01fee74..ee1f585 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable("kernel" "src/filesystem/file_descriptor_table.cpp" "src/filesystem/filesystem.cpp" "src/filesystem/inode.cpp" + "src/filesystem/mount_table.cpp" "src/filesystem/mount.cpp" "src/filesystem/open_file_description.cpp" "src/filesystem/vfs.cpp" diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp new file mode 100644 index 0000000..14d6d08 --- /dev/null +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -0,0 +1,23 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_MOUNT_TABLE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_MOUNT_TABLE_HPP + +#include "kernel/filesystem/dentry.hpp" +#include "kernel/filesystem/mount.hpp" + +#include +#include + +namespace filesystem +{ + struct mount_table + { + public: + void add_mount(kstd::shared_ptr); + auto find_mount_by_dentry(kstd::shared_ptr const & dentry) -> kstd::shared_ptr; + + private: + kstd::vector> m_mounts; + }; +} // namespace filesystem + +#endif diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp new file mode 100644 index 0000000..0e9a53d --- /dev/null +++ b/kernel/src/filesystem/mount_table.cpp @@ -0,0 +1,29 @@ +#include "kernel/filesystem/mount_table.hpp" + +#include "kapi/system.hpp" + +#include "kernel/filesystem/dentry.hpp" +#include "kernel/filesystem/mount.hpp" + +#include + +#include + +namespace filesystem +{ + void mount_table::add_mount(kstd::shared_ptr mount) + { + m_mounts.push_back(mount); + } + + auto mount_table::find_mount_by_dentry(kstd::shared_ptr const & dentry) -> kstd::shared_ptr + { + auto found = + std::ranges::find_if(m_mounts, [&](auto const & mount) { return mount->get_dentry().get() == dentry.get(); }); + if (found != m_mounts.end()) + { + return *found; + } + kapi::system::panic("[FILESYSTEM] dentry has mount flag set but no corresponding mount found."); + } +} // namespace filesystem \ No newline at end of file -- cgit v1.2.3 From 6110774495a10d1dd8766f8fa8597fb3bdc4aabd Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Tue, 24 Mar 2026 16:51:28 +0100 Subject: Fix dentry header guards --- kernel/include/kernel/filesystem/dentry.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index ca7ca95..c28246f 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -1,5 +1,5 @@ -#ifndef TEACH_OS_KERNEL_DENTRY_HPP -#define TEACH_OS_KERNEL_DENTRY_HPP +#ifndef TEACH_OS_KERNEL_FILESYSTEM_DENTRY_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_DENTRY_HPP #include "kernel/filesystem/inode.hpp" -- cgit v1.2.3 From b742349ba039d1a864462332bb7ae5a071afdec1 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Tue, 24 Mar 2026 16:53:21 +0100 Subject: Refactor mount_table entry (mount) to use dentry as key instead of path --- kernel/include/kernel/filesystem/mount.hpp | 9 ++++----- kernel/src/filesystem/mount.cpp | 9 +++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index 232a9be..dd47ce7 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -1,23 +1,22 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_MOUNT_HPP #define TEACH_OS_KERNEL_FILESYSTEM_MOUNT_HPP +#include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/filesystem.hpp" #include -#include - namespace filesystem { struct mount { - mount(std::string_view const & path, kstd::shared_ptr const & fs); + mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & fs); - [[nodiscard]] auto path() const -> std::string_view; + [[nodiscard]] auto get_dentry() const -> kstd::shared_ptr; [[nodiscard]] auto get_filesystem() const -> kstd::shared_ptr const &; private: - std::string_view m_path; + kstd::shared_ptr m_dentry; kstd::shared_ptr m_filesystem{}; }; } // namespace filesystem diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index a2c501f..b65f331 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -2,6 +2,7 @@ #include "kapi/system.hpp" +#include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/filesystem.hpp" #include @@ -10,8 +11,8 @@ namespace filesystem { - mount::mount(std::string_view const & path, kstd::shared_ptr const & fs) - : m_path(path) + mount::mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & fs) + : m_dentry(mount_dentry) , m_filesystem(fs) { if (!m_filesystem) @@ -20,9 +21,9 @@ namespace filesystem } } - auto mount::path() const -> std::string_view + auto mount::get_dentry() const -> kstd::shared_ptr { - return m_path; + return m_dentry; } auto mount::get_filesystem() const -> kstd::shared_ptr const & -- cgit v1.2.3 From 70229b88ad195a6e945b4cc75e1248685cda0951 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Tue, 24 Mar 2026 17:05:57 +0100 Subject: Add root_dentry to filesystem --- kernel/include/kernel/filesystem/filesystem.hpp | 5 +++++ kernel/src/filesystem/filesystem.cpp | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index e069ced..362242f 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -2,6 +2,7 @@ #define TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP #include "kernel/devices/device.hpp" +#include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/inode.hpp" #include @@ -19,11 +20,15 @@ namespace filesystem virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr = 0; [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; + [[nodiscard]] auto root_dentry() const -> kstd::shared_ptr const &; + + auto set_root_dentry(kstd::shared_ptr dentry) -> void; protected: kstd::shared_ptr m_root_inode{}; kstd::shared_ptr m_device{}; kstd::vector> m_inodes{}; + kstd::shared_ptr m_root_dentry; }; } // namespace filesystem diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index 0e33d95..8ebe58f 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -1,6 +1,7 @@ #include "kernel/filesystem/filesystem.hpp" #include "kernel/devices/device.hpp" +#include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/inode.hpp" #include @@ -21,4 +22,14 @@ namespace filesystem { return m_root_inode; } + + auto filesystem::root_dentry() const -> kstd::shared_ptr const & + { + return m_root_dentry; + } + + auto filesystem::set_root_dentry(kstd::shared_ptr dentry) -> void + { + m_root_dentry = dentry; + } } // namespace filesystem \ No newline at end of file -- cgit v1.2.3 From a2ac8770937a28d0d4c674e25c9b3c77802a5c9e Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Tue, 24 Mar 2026 17:22:55 +0100 Subject: Refactor mounting of new filesystems and path resolving in vfs --- kernel/include/kernel/filesystem/vfs.hpp | 3 +- kernel/src/filesystem/vfs.cpp | 53 ++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 317f2d1..55bf6a4 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -6,6 +6,7 @@ #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include "kernel/filesystem/mount.hpp" +#include "kernel/filesystem/mount_table.hpp" #include "kernel/filesystem/open_file_description.hpp" #include @@ -43,7 +44,7 @@ namespace filesystem kstd::shared_ptr m_root_fs; kstd::shared_ptr m_root_dentry; - kstd::vector m_mount_table; + mount_table m_mount_table; }; } // namespace filesystem diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 0e8a76d..c77736a 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -82,9 +82,9 @@ namespace filesystem return nullptr; } - auto vfs::do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int + auto vfs::do_mount(std::string_view path, kstd::shared_ptr const & new_filesystem) -> int { - if (!filesystem) + if (!new_filesystem) { return -1; // TODO BA-FS26 panic or errorcode? } @@ -100,24 +100,21 @@ namespace filesystem return -1; // TODO BA-FS26 panic or errorcode? } - // if (path == "/") - // { - // m_root_fs = filesystem; - // m_root_mount = mount{"/", filesystem}; - // return 0; - // } + auto mount_dentry = resolve_path(path); - auto existing_mount = - std::ranges::find_if(m_mount_table, [&](auto const & existing) { return existing.path() == path; }); - if (existing_mount != m_mount_table.end()) + if (!mount_dentry) { - *existing_mount = mount{path, filesystem}; - } - else - { - m_mount_table.push_back(mount{path, filesystem}); + return -1; // mount point path doesn't exist } + // TODO BA-FS26 check if mount point is already mounted and handle it (unmount old fs, fail, etc.) + + mount_dentry->set_flag(dentry::dentry_flags::dcache_mounted); + m_mount_table.add_mount(kstd::make_shared(mount_dentry, new_filesystem)); + + new_filesystem->set_root_dentry( + kstd::make_shared(mount_dentry->get_parent(), new_filesystem->root_inode())); + return 0; } @@ -157,19 +154,29 @@ namespace filesystem { auto path_parts = std::views::split(path, '/') | std::views::filter([](auto const & part) { return !part.empty(); }); - auto parent = m_root_dentry; + auto current_parent = m_root_dentry; + auto current_fs = m_root_fs; for (auto const & part : path_parts) { - if (auto child = parent->find_child(std::string_view{part})) + if (auto child = current_parent->find_child(std::string_view{part})) { - parent = child; + if (child->has_flag(dentry::dentry_flags::dcache_mounted)) // found dentry is a mounted fs + { + auto found_mount = m_mount_table.find_mount_by_dentry(child); + current_fs = found_mount->get_filesystem(); + current_parent = current_fs->root_dentry(); + } + else + { // found dentry is NOT a mounted fs -> continue path resolution in current fs + current_parent = child; + } } - else if (auto found_inode = m_root_fs->lookup(parent->get_inode(), std::string_view{part})) + else if (auto found_inode = m_root_fs->lookup(current_parent->get_inode(), std::string_view{part})) { - auto next_dentry = kstd::make_shared(parent, found_inode, std::string_view{part}); - parent->add_child(next_dentry); - parent = next_dentry; + auto next_dentry = kstd::make_shared(current_parent, found_inode, std::string_view{part}); + current_parent->add_child(next_dentry); + current_parent = next_dentry; } else { -- cgit v1.2.3 From 7351349f296f100f10db62b5a834a971fbe51473 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 24 Mar 2026 18:52:41 +0100 Subject: small refactoring --- kernel/src/filesystem/mount_table.cpp | 6 +++--- kernel/src/filesystem/vfs.cpp | 36 ++++++++++------------------------- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 0e9a53d..b4a906b 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -18,11 +18,11 @@ namespace filesystem auto mount_table::find_mount_by_dentry(kstd::shared_ptr const & dentry) -> kstd::shared_ptr { - auto found = + auto it = std::ranges::find_if(m_mounts, [&](auto const & mount) { return mount->get_dentry().get() == dentry.get(); }); - if (found != m_mounts.end()) + if (it != m_mounts.end()) { - return *found; + return *it; } kapi::system::panic("[FILESYSTEM] dentry has mount flag set but no corresponding mount found."); } diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index c77736a..78dec77 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -39,7 +39,6 @@ namespace filesystem auto vfs::init_internal() -> void { auto storage_mgmt = devices::storage::storage_management::get(); - // TODO BA-FS26 fix mounting boot_device if (auto boot_device = storage_mgmt.determine_boot_device()) { m_root_fs = kstd::make_shared(); @@ -82,9 +81,9 @@ namespace filesystem return nullptr; } - auto vfs::do_mount(std::string_view path, kstd::shared_ptr const & new_filesystem) -> int + auto vfs::do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int { - if (!new_filesystem) + if (!filesystem) { return -1; // TODO BA-FS26 panic or errorcode? } @@ -100,22 +99,19 @@ namespace filesystem return -1; // TODO BA-FS26 panic or errorcode? } - auto mount_dentry = resolve_path(path); - - if (!mount_dentry) + if (auto mount_dentry = resolve_path(path)) { - return -1; // mount point path doesn't exist - } + // TODO BA-FS26 check if mount point is already mounted and handle it (unmount old fs, fail, etc.) - // TODO BA-FS26 check if mount point is already mounted and handle it (unmount old fs, fail, etc.) + mount_dentry->set_flag(dentry::dentry_flags::dcache_mounted); + m_mount_table.add_mount(kstd::make_shared(mount_dentry, filesystem)); - mount_dentry->set_flag(dentry::dentry_flags::dcache_mounted); - m_mount_table.add_mount(kstd::make_shared(mount_dentry, new_filesystem)); + filesystem->set_root_dentry(kstd::make_shared(mount_dentry->get_parent(), filesystem->root_inode())); - new_filesystem->set_root_dentry( - kstd::make_shared(mount_dentry->get_parent(), new_filesystem->root_inode())); + return 0; + } - return 0; + return -1; } auto vfs::make_device_node(kstd::shared_ptr const & device) -> void @@ -183,18 +179,6 @@ namespace filesystem return nullptr; } } - // | std::views::transform([this](auto const & part) { - // // TODO BA-FS26 implement real path resolution with mounts and directories etc. - // // For now, just return null for any non-device-node path. - // return std::optional>{}; - // }) - // | std::views::filter([](auto const & opt) { return opt.has_value(); }) - // | std::views::transform([](auto const & opt) { return opt.value(); }) - // | std::ranges::find_if(m_mount_table, [&](auto const & mount) { - // // TODO BA-FS26 implement real path resolution with mounts and directories etc. - // // For now, just check if the first path component matches a mount point. - // return part == mount.path(); - // }); } return nullptr; -- cgit v1.2.3 From 2eb086d516f20a0b5cef9881a3459adb389c6ee8 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 24 Mar 2026 19:06:50 +0100 Subject: implement == and <=> operator in shared_ptr --- kernel/src/filesystem/mount_table.cpp | 3 +-- libs/kstd/include/kstd/bits/shared_ptr.hpp | 29 ++++++++++++++++++++++++----- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index b4a906b..3176d6d 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -18,8 +18,7 @@ namespace filesystem auto mount_table::find_mount_by_dentry(kstd::shared_ptr const & dentry) -> kstd::shared_ptr { - auto it = - std::ranges::find_if(m_mounts, [&](auto const & mount) { return mount->get_dentry().get() == dentry.get(); }); + auto it = std::ranges::find_if(m_mounts, [&](auto const & mount) { return mount->get_dentry() == dentry; }); if (it != m_mounts.end()) { return *it; diff --git a/libs/kstd/include/kstd/bits/shared_ptr.hpp b/libs/kstd/include/kstd/bits/shared_ptr.hpp index 674807d..8930095 100644 --- a/libs/kstd/include/kstd/bits/shared_ptr.hpp +++ b/libs/kstd/include/kstd/bits/shared_ptr.hpp @@ -546,11 +546,6 @@ namespace kstd return ptr.pointer == nullptr; } - /** - * @brief Defaulted three-way comparator operator. - */ - [[nodiscard]] auto operator<=>(shared_ptr const & other) const = default; - private: /** * @brief If the candidate type inherits from enable_shared_from_this, assigns the internal weak pointer to this @@ -624,6 +619,30 @@ namespace kstd { return shared_ptr(new T(std::forward(args)...)); } + + /** + * @brief Equality operator for shared_ptr. Two shared_ptr instances are equal if they point to the same object + * @tparam T, U Types of the managed objects of the shared_ptr instances being compared. + * @param lhs, rhs The shared_ptr instances to compare. + * @return true if lhs and rhs point to the same object, false otherwise. + */ + template + [[nodiscard]] auto inline operator==(shared_ptr const & lhs, shared_ptr const & rhs) -> bool + { + return lhs.get() == rhs.get(); + } + + /** + * @brief Three-way comparison operator for shared_ptr. Compares the stored pointers of lhs and rhs using operator<=>. + * @tparam T, U Types of the managed objects of the shared_ptr instances being compared. + * @param lhs, rhs The shared_ptr instances to compare. + * @return The result of comparing the stored pointers of lhs and rhs using operator<=> + */ + template + [[nodiscard]] auto inline operator<=>(shared_ptr const & lhs, shared_ptr const & rhs) + { + return lhs.get() <=> rhs.get(); + } } // namespace kstd #endif \ No newline at end of file -- cgit v1.2.3 From 76de81de1e12694bf6bec1edd3e3409092a92d09 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 24 Mar 2026 20:41:32 +0100 Subject: refactoring, add root_mount into the root_table --- kernel/include/kernel/filesystem/mount_table.hpp | 2 + kernel/include/kernel/filesystem/vfs.hpp | 3 -- kernel/src/filesystem/mount_table.cpp | 7 ++++ kernel/src/filesystem/vfs.cpp | 53 ++++++++++++++---------- kernel/src/main.cpp | 1 - 5 files changed, 40 insertions(+), 26 deletions(-) diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp index 14d6d08..cf523c1 100644 --- a/kernel/include/kernel/filesystem/mount_table.hpp +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -13,6 +13,8 @@ namespace filesystem { public: void add_mount(kstd::shared_ptr); + + [[nodiscard]] auto get_root_mount() const -> kstd::shared_ptr; auto find_mount_by_dentry(kstd::shared_ptr const & dentry) -> kstd::shared_ptr; private: diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 55bf6a4..cf268a3 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -5,7 +5,6 @@ #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" -#include "kernel/filesystem/mount.hpp" #include "kernel/filesystem/mount_table.hpp" #include "kernel/filesystem/open_file_description.hpp" @@ -42,8 +41,6 @@ namespace filesystem auto make_device_node(kstd::shared_ptr const & device) -> void; [[nodiscard]] auto resolve_path(std::string_view path) -> kstd::shared_ptr; - kstd::shared_ptr m_root_fs; - kstd::shared_ptr m_root_dentry; mount_table m_mount_table; }; } // namespace filesystem diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 3176d6d..debb9ab 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -16,6 +16,13 @@ namespace filesystem m_mounts.push_back(mount); } + auto mount_table::get_root_mount() const -> kstd::shared_ptr + { + auto it = + std::ranges::find_if(m_mounts, [](auto const & mount) { return mount->get_dentry()->get_parent() == nullptr; }); + return it != m_mounts.end() ? *it : nullptr; + } + auto mount_table::find_mount_by_dentry(kstd::shared_ptr const & dentry) -> kstd::shared_ptr { auto it = std::ranges::find_if(m_mounts, [&](auto const & mount) { return mount->get_dentry() == dentry; }); diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 78dec77..c8e5bc5 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -41,14 +41,13 @@ namespace filesystem auto storage_mgmt = devices::storage::storage_management::get(); if (auto boot_device = storage_mgmt.determine_boot_device()) { - m_root_fs = kstd::make_shared(); - m_root_fs->mount(boot_device); + auto root_fs = kstd::make_shared(); + root_fs->mount(boot_device); - m_root_dentry = kstd::make_shared(nullptr, m_root_fs->root_inode()); - // if (do_mount("/", m_root_fs) != 0) - // { - // kapi::system::panic("[FILESYSTEM] Failed to mount root filesystem."); - // } + auto root_dentry = kstd::make_shared(nullptr, root_fs->root_inode(), "/"); + root_fs->set_root_dentry(root_dentry); + + m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs)); // TODO BA-FS26 use do_mount when tempdevfs is implemented -> just call /dev/ with all devices in devtempfs std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto controller) { @@ -148,37 +147,47 @@ namespace filesystem } else if (!path.empty() && path.front() == '/') { + auto root_mount = m_mount_table.get_root_mount(); + if (!root_mount) + { + kapi::system::panic("[FILESYSTEM] no root mount found."); + } + + auto current_dentry = root_mount->get_dentry(); + auto current_fs = root_mount->get_filesystem(); + auto path_parts = std::views::split(path, '/') | std::views::filter([](auto const & part) { return !part.empty(); }); - auto current_parent = m_root_dentry; - auto current_fs = m_root_fs; - for (auto const & part : path_parts) { - if (auto child = current_parent->find_child(std::string_view{part})) + auto next_dentry = current_dentry->find_child(std::string_view{part}); + + if (!next_dentry) { - if (child->has_flag(dentry::dentry_flags::dcache_mounted)) // found dentry is a mounted fs + if (auto found_inode = current_fs->lookup(current_dentry->get_inode(), std::string_view{part})) { - auto found_mount = m_mount_table.find_mount_by_dentry(child); - current_fs = found_mount->get_filesystem(); - current_parent = current_fs->root_dentry(); + next_dentry = kstd::make_shared(current_dentry, found_inode, std::string_view{part}); + current_dentry->add_child(next_dentry); } else - { // found dentry is NOT a mounted fs -> continue path resolution in current fs - current_parent = child; + { + return nullptr; } } - else if (auto found_inode = m_root_fs->lookup(current_parent->get_inode(), std::string_view{part})) + + if (next_dentry->has_flag(dentry::dentry_flags::dcache_mounted)) { - auto next_dentry = kstd::make_shared(current_parent, found_inode, std::string_view{part}); - current_parent->add_child(next_dentry); - current_parent = next_dentry; + auto found_mount = m_mount_table.find_mount_by_dentry(next_dentry); + current_fs = found_mount->get_filesystem(); + current_dentry = current_fs->root_dentry(); } else { - return nullptr; + current_dentry = next_dentry; } } + + return current_dentry; } return nullptr; diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index a575be2..9571a0d 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3 From 84d4476650b31dbfc52becf2ff65ddce9d31c9ec Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 24 Mar 2026 23:54:28 +0100 Subject: implement a rootfs to handle / mounts correctly --- kernel/CMakeLists.txt | 2 + kernel/include/kernel/filesystem/filesystem.hpp | 5 --- kernel/include/kernel/filesystem/mount.hpp | 10 +++-- .../kernel/filesystem/rootfs/rootfs_filesystem.hpp | 21 +++++++++++ .../kernel/filesystem/rootfs/rootfs_inode.hpp | 19 ++++++++++ kernel/src/filesystem/filesystem.cpp | 11 ------ kernel/src/filesystem/mount.cpp | 15 ++++++-- kernel/src/filesystem/mount_table.cpp | 14 ++----- kernel/src/filesystem/rootfs/rootfs_filesystem.cpp | 24 ++++++++++++ kernel/src/filesystem/rootfs/rootfs_inode.cpp | 22 +++++++++++ kernel/src/filesystem/vfs.cpp | 43 +++++++++++++++------- kernel/src/main.cpp | 10 +++++ 12 files changed, 149 insertions(+), 47 deletions(-) create mode 100644 kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp create mode 100644 kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp create mode 100644 kernel/src/filesystem/rootfs/rootfs_filesystem.cpp create mode 100644 kernel/src/filesystem/rootfs/rootfs_inode.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index ee1f585..406ac54 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -32,6 +32,8 @@ add_executable("kernel" "src/filesystem/mount_table.cpp" "src/filesystem/mount.cpp" "src/filesystem/open_file_description.cpp" + "src/filesystem/rootfs/rootfs_filesystem.cpp" + "src/filesystem/rootfs/rootfs_inode.cpp" "src/filesystem/vfs.cpp" ) diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 362242f..e069ced 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -2,7 +2,6 @@ #define TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP #include "kernel/devices/device.hpp" -#include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/inode.hpp" #include @@ -20,15 +19,11 @@ namespace filesystem virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr = 0; [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; - [[nodiscard]] auto root_dentry() const -> kstd::shared_ptr const &; - - auto set_root_dentry(kstd::shared_ptr dentry) -> void; protected: kstd::shared_ptr m_root_inode{}; kstd::shared_ptr m_device{}; kstd::vector> m_inodes{}; - kstd::shared_ptr m_root_dentry; }; } // namespace filesystem diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index dd47ce7..0cd30b4 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -10,13 +10,17 @@ namespace filesystem { struct mount { - mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & fs); + mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, + kstd::shared_ptr const & fs); + + [[nodiscard]] auto get_mount_dentry() const -> kstd::shared_ptr; + [[nodiscard]] auto root_dentry() const -> kstd::shared_ptr const &; - [[nodiscard]] auto get_dentry() const -> kstd::shared_ptr; [[nodiscard]] auto get_filesystem() const -> kstd::shared_ptr const &; private: - kstd::shared_ptr m_dentry; + kstd::shared_ptr m_mount_dentry; + kstd::shared_ptr m_root_dentry; kstd::shared_ptr m_filesystem{}; }; } // namespace filesystem diff --git a/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp new file mode 100644 index 0000000..949ac83 --- /dev/null +++ b/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp @@ -0,0 +1,21 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_ROOTFS_FILESYSTEM_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_ROOTFS_FILESYSTEM_HPP + +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/filesystem/inode.hpp" + +#include + +#include + +namespace filesystem::rootfs +{ + struct rootfs_filesystem : filesystem + { + auto mount(kstd::shared_ptr const & device) -> int override; + auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; + }; +} // namespace filesystem::rootfs + +#endif diff --git a/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp b/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp new file mode 100644 index 0000000..d533e8e --- /dev/null +++ b/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp @@ -0,0 +1,19 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_ROOTFS_INODE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_ROOTFS_INODE_HPP + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace filesystem::rootfs +{ + struct rootfs_inode : inode + { + rootfs_inode(); + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + }; +} // namespace filesystem::rootfs + +#endif diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index 8ebe58f..0e33d95 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -1,7 +1,6 @@ #include "kernel/filesystem/filesystem.hpp" #include "kernel/devices/device.hpp" -#include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/inode.hpp" #include @@ -22,14 +21,4 @@ namespace filesystem { return m_root_inode; } - - auto filesystem::root_dentry() const -> kstd::shared_ptr const & - { - return m_root_dentry; - } - - auto filesystem::set_root_dentry(kstd::shared_ptr dentry) -> void - { - m_root_dentry = dentry; - } } // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index b65f331..d9937dc 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -11,8 +11,10 @@ namespace filesystem { - mount::mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & fs) - : m_dentry(mount_dentry) + mount::mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, + kstd::shared_ptr const & fs) + : m_mount_dentry(mount_dentry) + , m_root_dentry(root_dentry) , m_filesystem(fs) { if (!m_filesystem) @@ -21,13 +23,18 @@ namespace filesystem } } - auto mount::get_dentry() const -> kstd::shared_ptr + auto mount::get_mount_dentry() const -> kstd::shared_ptr { - return m_dentry; + return m_mount_dentry; } auto mount::get_filesystem() const -> kstd::shared_ptr const & { return m_filesystem; } + + auto mount::root_dentry() const -> kstd::shared_ptr const & + { + return m_root_dentry; + } } // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index debb9ab..681c2b9 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -1,7 +1,5 @@ #include "kernel/filesystem/mount_table.hpp" -#include "kapi/system.hpp" - #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/mount.hpp" @@ -18,18 +16,14 @@ namespace filesystem auto mount_table::get_root_mount() const -> kstd::shared_ptr { - auto it = - std::ranges::find_if(m_mounts, [](auto const & mount) { return mount->get_dentry()->get_parent() == nullptr; }); + auto it = std::ranges::find_if(m_mounts, [](auto const & mount) { return mount->get_mount_dentry() == nullptr; }); return it != m_mounts.end() ? *it : nullptr; } auto mount_table::find_mount_by_dentry(kstd::shared_ptr const & dentry) -> kstd::shared_ptr { - auto it = std::ranges::find_if(m_mounts, [&](auto const & mount) { return mount->get_dentry() == dentry; }); - if (it != m_mounts.end()) - { - return *it; - } - kapi::system::panic("[FILESYSTEM] dentry has mount flag set but no corresponding mount found."); + auto it = std::ranges::find_if(m_mounts, [&](auto const & mnt) { return mnt->get_mount_dentry() == dentry; }); + + return it != m_mounts.end() ? *it : nullptr; } } // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp b/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp new file mode 100644 index 0000000..22b1962 --- /dev/null +++ b/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp @@ -0,0 +1,24 @@ +#include "kernel/filesystem/rootfs/rootfs_filesystem.hpp" + +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/inode.hpp" +#include "kernel/filesystem/rootfs/rootfs_inode.hpp" + +#include + +#include + +namespace filesystem::rootfs +{ + auto rootfs_filesystem::mount(kstd::shared_ptr const & /*device*/) -> int + { + m_root_inode = kstd::make_shared(); + return 0; + } + + auto rootfs_filesystem::lookup(kstd::shared_ptr const & /*parent*/, std::string_view /*name*/) + -> kstd::shared_ptr + { + return nullptr; + } +} // namespace filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/rootfs_inode.cpp b/kernel/src/filesystem/rootfs/rootfs_inode.cpp new file mode 100644 index 0000000..ed057f7 --- /dev/null +++ b/kernel/src/filesystem/rootfs/rootfs_inode.cpp @@ -0,0 +1,22 @@ +#include "kernel/filesystem/rootfs/rootfs_inode.hpp" + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace filesystem::rootfs +{ + rootfs_inode::rootfs_inode() + : inode(inode_kind::directory) + {} + + auto rootfs_inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t + { + return 0; + } + + auto rootfs_inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t + { + return 0; + } +} // namespace filesystem::rootfs diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index c8e5bc5..1c4dd8b 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -10,6 +10,7 @@ #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/mount.hpp" #include "kernel/filesystem/open_file_description.hpp" +#include "kernel/filesystem/rootfs/rootfs_filesystem.hpp" #include @@ -38,16 +39,20 @@ namespace filesystem auto vfs::init_internal() -> void { + auto virtual_fs = kstd::make_shared(); + virtual_fs->mount(nullptr); + + auto virtual_root_dentry = kstd::make_shared(nullptr, virtual_fs->root_inode(), "/"); + m_mount_table.add_mount(kstd::make_shared(nullptr, virtual_root_dentry, virtual_fs)); + auto storage_mgmt = devices::storage::storage_management::get(); if (auto boot_device = storage_mgmt.determine_boot_device()) { - auto root_fs = kstd::make_shared(); - root_fs->mount(boot_device); + // TODO BA-FS26 detect fs type from boot device and load corresponding fs, for now just assume ext2 + auto boot_root_fs = kstd::make_shared(); + boot_root_fs->mount(boot_device); - auto root_dentry = kstd::make_shared(nullptr, root_fs->root_inode(), "/"); - root_fs->set_root_dentry(root_dentry); - - m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs)); + do_mount("/", boot_root_fs); // TODO BA-FS26 use do_mount when tempdevfs is implemented -> just call /dev/ with all devices in devtempfs std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto controller) { @@ -102,10 +107,10 @@ namespace filesystem { // TODO BA-FS26 check if mount point is already mounted and handle it (unmount old fs, fail, etc.) + auto new_fs_root = kstd::make_shared(mount_dentry, filesystem->root_inode()); + auto new_mount = kstd::make_shared(mount_dentry, new_fs_root, filesystem); + m_mount_table.add_mount(new_mount); mount_dentry->set_flag(dentry::dentry_flags::dcache_mounted); - m_mount_table.add_mount(kstd::make_shared(mount_dentry, filesystem)); - - filesystem->set_root_dentry(kstd::make_shared(mount_dentry->get_parent(), filesystem->root_inode())); return 0; } @@ -153,20 +158,30 @@ namespace filesystem kapi::system::panic("[FILESYSTEM] no root mount found."); } - auto current_dentry = root_mount->get_dentry(); + auto current_dentry = root_mount->root_dentry(); auto current_fs = root_mount->get_filesystem(); + while (current_dentry->has_flag(dentry::dentry_flags::dcache_mounted)) + { + auto mnt = m_mount_table.find_mount_by_dentry(current_dentry); + if (!mnt) + kapi::system::panic("[FILESYSTEM] dcache_mounted set but no covering mount found."); + current_dentry = mnt->root_dentry(); + current_fs = mnt->get_filesystem(); + } + auto path_parts = std::views::split(path, '/') | std::views::filter([](auto const & part) { return !part.empty(); }); for (auto const & part : path_parts) { - auto next_dentry = current_dentry->find_child(std::string_view{part}); + std::string_view part_view{part}; + auto next_dentry = current_dentry->find_child(part_view); if (!next_dentry) { - if (auto found_inode = current_fs->lookup(current_dentry->get_inode(), std::string_view{part})) + if (auto found_inode = current_fs->lookup(current_dentry->get_inode(), part_view)) { - next_dentry = kstd::make_shared(current_dentry, found_inode, std::string_view{part}); + next_dentry = kstd::make_shared(current_dentry, found_inode, part_view); current_dentry->add_child(next_dentry); } else @@ -179,7 +194,7 @@ namespace filesystem { auto found_mount = m_mount_table.find_mount_by_dentry(next_dentry); current_fs = found_mount->get_filesystem(); - current_dentry = current_fs->root_dentry(); + current_dentry = found_mount->root_dentry(); } else { diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 9571a0d..19a0992 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -5,6 +5,7 @@ #include "kernel/devices/storage/storage_management.hpp" #include "kernel/filesystem/device_inode.hpp" +#include "kernel/filesystem/ext2/ext2_filesystem.hpp" #include "kernel/filesystem/file_descriptor_table.hpp" #include "kernel/filesystem/open_file_description.hpp" #include "kernel/filesystem/vfs.hpp" @@ -108,8 +109,17 @@ auto test_file_lookup() -> void // TODO BA-FS26 implement a more complete test with multiple files and directories and mounts etc. auto vfs = filesystem::vfs::get(); + auto storage_mgmt = devices::storage::storage_management::get(); + vfs.open("/a/b/c"); vfs.open("/a/d/e"); + + auto new_filesystem = kstd::make_shared(); + auto device = storage_mgmt.device_by_major_minor(1, 16); + new_filesystem->mount(device); + vfs.do_mount("/a/b", new_filesystem); + vfs.open("/a/b/c"); + vfs.open("x/y/z"); } -- cgit v1.2.3 From 74351bbcd440189bafa79c806dd3ae8255f42c11 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 25 Mar 2026 07:47:24 +0100 Subject: currently do not support stacked mounts --- kernel/src/filesystem/vfs.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 1c4dd8b..483bae5 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -161,7 +161,8 @@ namespace filesystem auto current_dentry = root_mount->root_dentry(); auto current_fs = root_mount->get_filesystem(); - while (current_dentry->has_flag(dentry::dentry_flags::dcache_mounted)) + // TODO BA-FS26 use while to allow stacked mounts? + if (current_dentry->has_flag(dentry::dentry_flags::dcache_mounted)) { auto mnt = m_mount_table.find_mount_by_dentry(current_dentry); if (!mnt) @@ -190,6 +191,7 @@ namespace filesystem } } + // TODO BA-FS26 use while to allow stacked mounts? if (next_dentry->has_flag(dentry::dentry_flags::dcache_mounted)) { auto found_mount = m_mount_table.find_mount_by_dentry(next_dentry); -- cgit v1.2.3 From 31ac3e6ffff00b7ac3b3dbb3db38c44409251b34 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 26 Mar 2026 20:09:37 +0100 Subject: first draft of an devfs implementation --- kernel/CMakeLists.txt | 2 + .../kernel/filesystem/devfs/devfs_filesystem.hpp | 25 ++++++++++ .../kernel/filesystem/devfs/devfs_root_inode.hpp | 19 ++++++++ kernel/include/kernel/filesystem/device_inode.hpp | 2 + kernel/include/kernel/filesystem/vfs.hpp | 13 ----- kernel/src/filesystem/devfs/devfs_filesystem.cpp | 57 ++++++++++++++++++++++ kernel/src/filesystem/devfs/devfs_root_inode.cpp | 22 +++++++++ kernel/src/filesystem/device_inode.cpp | 5 ++ kernel/src/filesystem/vfs.cpp | 50 ++++++------------- 9 files changed, 146 insertions(+), 49 deletions(-) create mode 100644 kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp create mode 100644 kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp create mode 100644 kernel/src/filesystem/devfs/devfs_filesystem.cpp create mode 100644 kernel/src/filesystem/devfs/devfs_root_inode.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 406ac54..eb762ac 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -21,6 +21,8 @@ add_executable("kernel" "src/devices/storage/storage_management.cpp" "src/devices/storage/ram_disk/ram_disk_controller.cpp" "src/devices/storage/ram_disk/ram_disk_device.cpp" + "src/filesystem/devfs/devfs_filesystem.cpp" + "src/filesystem/devfs/devfs_root_inode.cpp" "src/filesystem/ext2/ext2_file.cpp" "src/filesystem/ext2/ext2_filesystem.cpp" "src/filesystem/ext2/ext2_inode.cpp" diff --git a/kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp b/kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp new file mode 100644 index 0000000..5559c2a --- /dev/null +++ b/kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp @@ -0,0 +1,25 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVFS_DEVFS_FILESYSTEM_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_DEVFS_FILESYSTEM_HPP + +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/filesystem/inode.hpp" + +#include +#include + +#include + +namespace filesystem::devfs +{ + struct devfs_filesystem : filesystem + { + auto mount(kstd::shared_ptr const & device) -> int override; + auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; + + private: + auto build_device_inode_table() -> void; + }; +} // namespace filesystem::devfs + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp b/kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp new file mode 100644 index 0000000..b1d37ab --- /dev/null +++ b/kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp @@ -0,0 +1,19 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVFS_DEVFS_ROOT_INODE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_DEVFS_ROOT_INODE_HPP + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace filesystem::devfs +{ + struct devfs_root_inode : inode + { + devfs_root_inode(); + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + }; +} // namespace filesystem::devfs + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index ce0f91c..1cf08d4 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -18,6 +18,8 @@ namespace filesystem auto read(void * buffer, size_t offset, size_t size) const -> size_t override; auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + [[nodiscard]] auto device() const -> kstd::shared_ptr const &; + private: using block_op = void (*)(size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, std::byte * scratch, void * buffer); diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index cf268a3..f301b7a 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -1,17 +1,13 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP #define TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP -#include "kernel/devices/device.hpp" #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/filesystem.hpp" -#include "kernel/filesystem/inode.hpp" #include "kernel/filesystem/mount_table.hpp" #include "kernel/filesystem/open_file_description.hpp" #include -#include -#include #include namespace filesystem @@ -27,18 +23,9 @@ namespace filesystem auto do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int; private: - // TODO BA-FS26 remove again and use devtempfs - struct device_node_entry - { - std::string_view name; - kstd::shared_ptr node; - }; - kstd::vector> m_device_nodes; // TODO BA-FS26 remove again, use devtempfs - vfs() = default; auto init_internal() -> void; - auto make_device_node(kstd::shared_ptr const & device) -> void; [[nodiscard]] auto resolve_path(std::string_view path) -> kstd::shared_ptr; mount_table m_mount_table; diff --git a/kernel/src/filesystem/devfs/devfs_filesystem.cpp b/kernel/src/filesystem/devfs/devfs_filesystem.cpp new file mode 100644 index 0000000..cfd2d88 --- /dev/null +++ b/kernel/src/filesystem/devfs/devfs_filesystem.cpp @@ -0,0 +1,57 @@ +#include "kernel/filesystem/devfs/devfs_filesystem.hpp" + +#include "kernel/devices/device.hpp" +#include "kernel/devices/storage/storage_management.hpp" +#include "kernel/filesystem/devfs/devfs_root_inode.hpp" +#include "kernel/filesystem/device_inode.hpp" +#include "kernel/filesystem/inode.hpp" + +#include + +#include +#include + +namespace filesystem::devfs +{ + auto devfs_filesystem::mount(kstd::shared_ptr const & /*device*/) -> int + { + m_root_inode = kstd::make_shared(); + build_device_inode_table(); + return 0; + } + + auto devfs_filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) + -> kstd::shared_ptr + { + if (!parent || !parent->is_directory()) + { + return nullptr; + } + + if (parent.get() != m_root_inode.get()) + { + return nullptr; + } + + auto it = std::ranges::find_if(m_inodes, [&](auto const & dev_node) { + auto device_inode_ptr = static_cast(dev_node.get()); + if (!device_inode_ptr) + { + return false; + } + return device_inode_ptr->device()->name() == name; + }); + return (it != m_inodes.end()) ? *it : nullptr; + } + + auto devfs_filesystem::build_device_inode_table() -> void + { + m_inodes.clear(); + + auto storage_mgmt = devices::storage::storage_management::get(); + std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto const & controller) { + std::ranges::for_each(controller->all_devices(), + [&](auto const & device) { m_inodes.push_back(kstd::make_shared(device)); }); + }); + } +} // namespace filesystem::devfs \ No newline at end of file diff --git a/kernel/src/filesystem/devfs/devfs_root_inode.cpp b/kernel/src/filesystem/devfs/devfs_root_inode.cpp new file mode 100644 index 0000000..53441b0 --- /dev/null +++ b/kernel/src/filesystem/devfs/devfs_root_inode.cpp @@ -0,0 +1,22 @@ +#include "kernel/filesystem/devfs/devfs_root_inode.hpp" + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace filesystem::devfs +{ + devfs_root_inode::devfs_root_inode() + : inode(inode_kind::directory) + {} + + auto devfs_root_inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t + { + return 0; + } + + auto devfs_root_inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t + { + return 0; + } +} // namespace filesystem::devfs \ No newline at end of file diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index 812b43a..64cd6e9 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -76,6 +76,11 @@ namespace filesystem } } + auto device_inode::device() const -> kstd::shared_ptr const & + { + return m_device; + } + auto device_inode::process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t { if (buffer == nullptr) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 483bae5..368d994 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -2,10 +2,9 @@ #include "kapi/system.hpp" -#include "kernel/devices/device.hpp" #include "kernel/devices/storage/storage_management.hpp" #include "kernel/filesystem/dentry.hpp" -#include "kernel/filesystem/device_inode.hpp" +#include "kernel/filesystem/devfs/devfs_filesystem.hpp" #include "kernel/filesystem/ext2/ext2_filesystem.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/mount.hpp" @@ -14,7 +13,6 @@ #include -#include #include #include #include @@ -52,12 +50,18 @@ namespace filesystem auto boot_root_fs = kstd::make_shared(); boot_root_fs->mount(boot_device); - do_mount("/", boot_root_fs); + if (do_mount("/", boot_root_fs) != 0) + { + kapi::system::panic("[FILESYSTEM] failed to mount root filesystem."); + } + + auto device_fs = kstd::make_shared(); + device_fs->mount(nullptr); - // TODO BA-FS26 use do_mount when tempdevfs is implemented -> just call /dev/ with all devices in devtempfs - std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto controller) { - std::ranges::for_each(controller->all_devices(), [&](auto device) { make_device_node(device); }); - }); + if (do_mount("/dev", device_fs) != 0) + { + kapi::system::panic("[FILESYSTEM] failed to mount devfs at /dev."); + } } else { @@ -118,39 +122,13 @@ namespace filesystem return -1; } - auto vfs::make_device_node(kstd::shared_ptr const & device) -> void - { - if (!device) - { - kapi::system::panic("[FILESYSTEM] make_device_node called with null device."); - } - - m_device_nodes.push_back(device_node_entry{device->name().view(), kstd::make_shared(device)}); - } - auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr { - // TODO BA-FS26 implement real path resolution with mounts and directories etc. - // For now, just support device nodes at /dev/. + // TODO BA-FS26 implement full path resolution semantics. // TODO BA-FS26 better path validation // TODO BA-FS26 implement a path parser (maybe in libs?) and use it here and in do_mount - constexpr auto device_prefix = std::string_view{"/dev/"}; - if (path.starts_with(device_prefix)) - { - auto const device_name = path.substr(device_prefix.size()); - auto entry = std::ranges::find_if(m_device_nodes, [&](auto const & device_entry) { - return device_entry.has_value() && device_entry->name == device_name; - }); - - if (entry != m_device_nodes.end()) - { - return kstd::make_shared(nullptr, entry->value().node); - } - - return nullptr; - } - else if (!path.empty() && path.front() == '/') + if (!path.empty() && path.front() == '/') { auto root_mount = m_mount_table.get_root_mount(); if (!root_mount) -- cgit v1.2.3 From e97b668f82ad1a51e1e9352073a3ae51cedfdd34 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 26 Mar 2026 20:27:23 +0100 Subject: mount /dev in the rootfs --- .../kernel/filesystem/rootfs/rootfs_filesystem.hpp | 4 ++++ .../include/kernel/filesystem/rootfs/rootfs_inode.hpp | 12 ++++++++++++ kernel/src/filesystem/rootfs/rootfs_filesystem.cpp | 9 +++++++-- kernel/src/filesystem/rootfs/rootfs_inode.cpp | 17 +++++++++++++++++ 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp index 949ac83..cb07111 100644 --- a/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp +++ b/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp @@ -4,10 +4,14 @@ #include "kernel/devices/device.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" +#include "kernel/filesystem/rootfs/rootfs_inode.hpp" #include +#include +#include #include +#include namespace filesystem::rootfs { diff --git a/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp b/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp index d533e8e..de4fb7c 100644 --- a/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp +++ b/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp @@ -3,7 +3,13 @@ #include "kernel/filesystem/inode.hpp" +#include +#include +#include + #include +#include +#include namespace filesystem::rootfs { @@ -13,6 +19,12 @@ namespace filesystem::rootfs auto read(void * buffer, size_t offset, size_t size) const -> size_t override; auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + auto add_child(std::string_view name) -> void; + auto lookup_child(std::string_view name) -> kstd::shared_ptr; + + private: + kstd::vector>> m_children; }; } // namespace filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp b/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp index 22b1962..e819fc7 100644 --- a/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp +++ b/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp @@ -12,13 +12,18 @@ namespace filesystem::rootfs { auto rootfs_filesystem::mount(kstd::shared_ptr const & /*device*/) -> int { - m_root_inode = kstd::make_shared(); + auto rfs_inode = kstd::make_shared(); + rfs_inode->add_child("dev"); + m_root_inode = rfs_inode; + return 0; } - auto rootfs_filesystem::lookup(kstd::shared_ptr const & /*parent*/, std::string_view /*name*/) + auto rootfs_filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr { + if (auto * rfs_inode = static_cast(parent.get())) + return rfs_inode->lookup_child(name); return nullptr; } } // namespace filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/rootfs_inode.cpp b/kernel/src/filesystem/rootfs/rootfs_inode.cpp index ed057f7..9bbfbce 100644 --- a/kernel/src/filesystem/rootfs/rootfs_inode.cpp +++ b/kernel/src/filesystem/rootfs/rootfs_inode.cpp @@ -2,7 +2,13 @@ #include "kernel/filesystem/inode.hpp" +#include +#include + +#include #include +#include +#include namespace filesystem::rootfs { @@ -19,4 +25,15 @@ namespace filesystem::rootfs { return 0; } + + auto rootfs_inode::add_child(std::string_view name) -> void + { + m_children.push_back(std::make_pair(kstd::string{name}, kstd::make_shared())); + } + + auto rootfs_inode::lookup_child(std::string_view name) -> kstd::shared_ptr + { + auto it = std::ranges::find_if(m_children, [&](auto const & pair) { return pair.first == name; }); + return (it != m_children.end()) ? it->second : nullptr; + } } // namespace filesystem::rootfs -- cgit v1.2.3 From 372148a1379d7f14cbe641272f8f28766925f3aa Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 26 Mar 2026 21:13:59 +0100 Subject: mock filesystem correctly for tests with /dev --- kernel/src/filesystem/ext2/ext2_filesystem.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/kernel/src/filesystem/ext2/ext2_filesystem.cpp b/kernel/src/filesystem/ext2/ext2_filesystem.cpp index 3f5774c..373c6a2 100644 --- a/kernel/src/filesystem/ext2/ext2_filesystem.cpp +++ b/kernel/src/filesystem/ext2/ext2_filesystem.cpp @@ -22,11 +22,16 @@ namespace filesystem::ext2 return 0; } - auto ext2_filesystem::lookup(kstd::shared_ptr const & /*parent*/, std::string_view /*name*/) + auto ext2_filesystem::lookup(kstd::shared_ptr const & /*parent*/, std::string_view name) -> kstd::shared_ptr { // TODO BA-FS26 implement ext2 directory traversal and inode loading - // return nullptr; + if (name == "dev") + { + // TODO BA-FS26 just for testing + return nullptr; + } + return kstd::make_shared(); } } // namespace filesystem::ext2 -- cgit v1.2.3 From c3e80174faa2db9e6674af995aaa95b59ec53fec Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 26 Mar 2026 21:15:40 +0100 Subject: fix resolve_path -> first traverse rootfs before jumping into the mounted root filesystem --- kernel/src/filesystem/vfs.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 368d994..40c97f8 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -139,16 +139,6 @@ namespace filesystem auto current_dentry = root_mount->root_dentry(); auto current_fs = root_mount->get_filesystem(); - // TODO BA-FS26 use while to allow stacked mounts? - if (current_dentry->has_flag(dentry::dentry_flags::dcache_mounted)) - { - auto mnt = m_mount_table.find_mount_by_dentry(current_dentry); - if (!mnt) - kapi::system::panic("[FILESYSTEM] dcache_mounted set but no covering mount found."); - current_dentry = mnt->root_dentry(); - current_fs = mnt->get_filesystem(); - } - auto path_parts = std::views::split(path, '/') | std::views::filter([](auto const & part) { return !part.empty(); }); for (auto const & part : path_parts) @@ -173,6 +163,8 @@ namespace filesystem if (next_dentry->has_flag(dentry::dentry_flags::dcache_mounted)) { auto found_mount = m_mount_table.find_mount_by_dentry(next_dentry); + if (!found_mount) + kapi::system::panic("[FILESYSTEM] dcache_mounted set but no covering mount found."); current_fs = found_mount->get_filesystem(); current_dentry = found_mount->root_dentry(); } -- cgit v1.2.3 From 05269b10e50a80f557c2be475904ff15dc1bbec4 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 27 Mar 2026 06:58:18 +0100 Subject: x86_64/port_io: fix assembly templates --- arch/x86_64/include/arch/device_io/port_io.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86_64/include/arch/device_io/port_io.hpp b/arch/x86_64/include/arch/device_io/port_io.hpp index 65e58e3..70773dd 100644 --- a/arch/x86_64/include/arch/device_io/port_io.hpp +++ b/arch/x86_64/include/arch/device_io/port_io.hpp @@ -69,9 +69,9 @@ namespace arch::io //! The assembly templates used for writing to an I/O port. constexpr auto static code = std::array{ - std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%al, %%dx"}, - std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%ax, %%dx"}, - std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%eax, %%dx"}, + std::string_view{"mov %[port], %%dx\nmov %[data], %%al\nout %%al, %%dx"}, + std::string_view{"mov %[port], %%dx\nmov %[data], %%ax\nout %%ax, %%dx"}, + std::string_view{"mov %[port], %%dx\nmov %[data], %%eax\nout %%eax, %%dx"}, }; }; -- cgit v1.2.3 From cb60cdebdc36dd2358fe1ce06eec197e213af491 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 24 Mar 2026 17:35:54 +0100 Subject: kapi/cpu: introduce CPU API --- arch/x86_64/include/arch/cpu/interrupts.hpp | 46 +++++----- arch/x86_64/kapi/cpu.cpp | 21 +++++ arch/x86_64/kapi/system.cpp | 10 +-- arch/x86_64/src/cpu/interrupts.cpp | 125 ++++++++++++++++++++-------- kapi/include/kapi/cpu.hpp | 7 ++ kapi/include/kapi/cpu/exception.hpp | 60 +++++++++++++ kernel/CMakeLists.txt | 2 + kernel/include/kernel/cpu.hpp | 14 ++++ kernel/kapi/cpu.cpp | 30 +++++++ kernel/src/cpu.cpp | 46 ++++++++++ kernel/src/main.cpp | 5 ++ 11 files changed, 301 insertions(+), 65 deletions(-) create mode 100644 kapi/include/kapi/cpu/exception.hpp create mode 100644 kernel/include/kernel/cpu.hpp create mode 100644 kernel/kapi/cpu.cpp create mode 100644 kernel/src/cpu.cpp diff --git a/arch/x86_64/include/arch/cpu/interrupts.hpp b/arch/x86_64/include/arch/cpu/interrupts.hpp index 8f156a4..08ecd9c 100644 --- a/arch/x86_64/include/arch/cpu/interrupts.hpp +++ b/arch/x86_64/include/arch/cpu/interrupts.hpp @@ -1,6 +1,8 @@ #ifndef TEACHOS_X86_64_CPU_INTERRUPTS_HPP #define TEACHOS_X86_64_CPU_INTERRUPTS_HPP +#include "kapi/memory.hpp" + #include "arch/cpu/segment_selector.hpp" #include @@ -57,36 +59,36 @@ namespace arch::cpu { struct { - std::uint64_t r15; - std::uint64_t r14; - std::uint64_t r13; - std::uint64_t r12; - std::uint64_t r11; - std::uint64_t r10; - std::uint64_t r9; - std::uint64_t r8; - std::uint64_t rdi; - std::uint64_t rsi; - std::uint64_t rbp; - std::uint64_t rdx; - std::uint64_t rcx; - std::uint64_t rbx; - std::uint64_t rax; + std::uint64_t r15{}; + std::uint64_t r14{}; + std::uint64_t r13{}; + std::uint64_t r12{}; + std::uint64_t r11{}; + std::uint64_t r10{}; + std::uint64_t r9{}; + std::uint64_t r8{}; + std::uint64_t rdi{}; + std::uint64_t rsi{}; + std::uint64_t rbp{}; + std::uint64_t rdx{}; + std::uint64_t rcx{}; + std::uint64_t rbx{}; + std::uint64_t rax{}; } handler_saved; struct { - std::uint64_t number; - std::uint64_t error_code; + std::uint64_t number{}; + std::uint64_t error_code{}; } interrupt; struct { - std::uint64_t rip; - std::uint64_t cs; - std::uint64_t rflags; - std::uint64_t rsp; - std::uint64_t ss; + kapi::memory::linear_address rip{}; + std::uint64_t cs{}; + std::uint64_t rflags{}; + kapi::memory::linear_address rsp{}; + std::uint64_t ss{}; } cpu_saved; }; diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index 2a0f8f7..693d328 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -1,8 +1,29 @@ #include "kapi/cpu.hpp" +#include "kapi/system.hpp" + +#include "arch/cpu/initialization.hpp" +#include "arch/cpu/interrupts.hpp" + +#include + namespace kapi::cpu { + auto init() -> void + { + auto static constinit is_initialized = std::atomic_flag{}; + + if (is_initialized.test_and_set()) + { + system::panic("[x86_64] CPU has already been initialized."); + } + + arch::cpu::initialize_descriptors(); + arch::cpu::initialize_legacy_interrupts(); + arch::cpu::enable_interrupts(); + } + auto halt() -> void { asm volatile("1: hlt\njmp 1b"); diff --git a/arch/x86_64/kapi/system.cpp b/arch/x86_64/kapi/system.cpp index 301169f..09c7152 100644 --- a/arch/x86_64/kapi/system.cpp +++ b/arch/x86_64/kapi/system.cpp @@ -1,16 +1,8 @@ #include "kapi/system.hpp" -#include "arch/cpu/initialization.hpp" -#include "arch/cpu/interrupts.hpp" - namespace kapi::system { - auto memory_initialized() -> void - { - arch::cpu::initialize_descriptors(); - arch::cpu::initialize_legacy_interrupts(); - arch::cpu::enable_interrupts(); - } + auto memory_initialized() -> void {} } // namespace kapi::system \ No newline at end of file diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 048c461..6f66bbd 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -1,6 +1,7 @@ #include "arch/cpu/interrupts.hpp" #include "kapi/cpu.hpp" +#include "kapi/memory.hpp" #include "arch/cpu/legacy_pic.hpp" #include "arch/cpu/segment_selector.hpp" @@ -14,38 +15,92 @@ namespace arch::cpu namespace { - constexpr auto isr_number_page_fault = 0x0e; - constexpr auto isr_number_legacy_start = 0x20; - constexpr auto isr_number_legacy_end = 0x29; - constexpr auto isr_number_cascade_start = 0x2c; - constexpr auto isr_number_cascade_end = 0x2f; - - auto handle_page_fault(interrupt_frame * frame) -> void + enum struct exception { - auto fault_address = std::uintptr_t{}; - asm volatile("mov %%cr2, %0" : "=r"(fault_address)); - - auto const present = (frame->interrupt.error_code & 0x1) != 0; - auto const write = (frame->interrupt.error_code & 0x2) != 0; - auto const user = (frame->interrupt.error_code & 0x4) != 0; + divide_error, + debug_exception, + non_maskable_interrupt, + breakpoint, + overflow, + bound_range_exceeded, + invalid_opcode, + device_not_available, + double_fault, + coprocessor_segment_overrun, + invalid_tss, + stack_segment_fault, + general_protection_fault, + page_fault, + x87_fpu_floating_point_error, + alignment_check, + machine_check, + simd_floating_point_error, + virtualization_exception, + control_protection_exception, + vmm_communication_exception, + security_exception, + }; + + constexpr auto pic_master_irq_start = 0x20; + constexpr auto pic_master_irq_end = pic_master_irq_start + 8; + constexpr auto pic_slave_irq_start = pic_master_irq_end; + constexpr auto pic_slave_irq_end = pic_slave_irq_start + 8; + + constexpr auto to_exception_type(exception e) + { + switch (e) + { + case exception::divide_error: + case exception::x87_fpu_floating_point_error: + case exception::simd_floating_point_error: + return kapi::cpu::exception::type::arithmetic_error; + case exception::breakpoint: + return kapi::cpu::exception::type::breakpoint; + case exception::invalid_opcode: + return kapi::cpu::exception::type::illegal_instruction; + case exception::stack_segment_fault: + return kapi::cpu::exception::type::memory_access_fault; + case exception::general_protection_fault: + return kapi::cpu::exception::type::privilege_violation; + case exception::alignment_check: + return kapi::cpu::exception::type::alignment_fault; + default: + return kapi::cpu::exception::type::unknown; + } + } - kstd::println(kstd::print_sink::stderr, "[x86_64:MMU] PAGE FAULT!"); - kstd::println(kstd::print_sink::stderr, "\tFault address: {:#018x}", fault_address); - kstd::println(kstd::print_sink::stderr, "\tPresent: {}", present); - kstd::println(kstd::print_sink::stderr, "\tWrite: {}", write); - kstd::println(kstd::print_sink::stderr, "\tUser: {}", user); - kstd::println(kstd::print_sink::stderr, "\tRIP: {:#018x}", frame->cpu_saved.rip); - kstd::println(kstd::print_sink::stderr, "\tHalting the system now!"); + auto dispatch_exception(interrupt_frame * frame) -> bool + { + auto type = to_exception_type(static_cast(frame->interrupt.number)); + auto fault_address = kapi::memory::linear_address{}; + auto instruction_pointer = frame->cpu_saved.rip; - kapi::cpu::halt(); + switch (type) + { + case kapi::cpu::exception::type::page_fault: + { + asm volatile("mov %%cr2, %0" : "=r"(fault_address)); + auto present = (frame->interrupt.error_code & 0x1) != 0; + auto write = (frame->interrupt.error_code & 0x2) != 0; + auto user = (frame->interrupt.error_code & 0x4) != 0; + + return kapi::cpu::get_exception_handler().handle( + {type, instruction_pointer, fault_address, present, write, user}); + } + default: + return kapi::cpu::get_exception_handler().handle({type, instruction_pointer}); + } } auto handle_legacy_interrupt(interrupt_frame * frame) -> void { kstd::println("[x86_64:SYS] Ignoring 8259 legacy interrupt {:#04x}", frame->interrupt.number); + if (frame->interrupt.number >= pic_slave_irq_start) + { + pic_slave_control_port::write(pic_end_of_interrupt); + } pic_master_control_port::write(pic_end_of_interrupt); - pic_slave_control_port::write(pic_end_of_interrupt); } } // namespace @@ -55,23 +110,25 @@ namespace arch::cpu auto interrupt_dispatch(interrupt_frame * frame) -> void { - if ((frame->interrupt.number >= isr_number_legacy_start && frame->interrupt.number <= isr_number_legacy_end) || - (frame->interrupt.number >= isr_number_cascade_start && frame->interrupt.number <= isr_number_cascade_end)) + auto [number, code] = frame->interrupt; + + if (number < pic_master_irq_start && !dispatch_exception(frame)) { - handle_legacy_interrupt(frame); - return; + kstd::println(kstd::print_sink::stderr, + "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); + kapi::cpu::halt(); } - switch (frame->interrupt.number) + if ((number >= pic_master_irq_start && number < pic_master_irq_end) || + (number >= pic_slave_irq_start && number < pic_slave_irq_end)) { - case isr_number_page_fault: - handle_page_fault(frame); - break; - default: - kstd::println(kstd::print_sink::stderr, "[x86_64:SYS] Unhandled interrupt {:#04x} received with code {:#04x}", - frame->interrupt.number, frame->interrupt.error_code); - kapi::cpu::halt(); + handle_legacy_interrupt(frame); + return; } + + kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x} received with code {:#04x}", + frame->interrupt.number, frame->interrupt.error_code); + kapi::cpu::halt(); } } diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index 05b84b7..1dd7cfe 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -1,6 +1,8 @@ #ifndef TEACHOS_KAPI_CPU_HPP #define TEACHOS_KAPI_CPU_HPP +#include "kapi/cpu/exception.hpp" + namespace kapi::cpu { //! @qualifier platform-defined @@ -8,6 +10,11 @@ namespace kapi::cpu //! //! This function terminates execution of the kernel. [[noreturn]] auto halt() -> void; + + //! @qualifier platform-defined + //! Perform early CPU initialization. + auto init() -> void; + } // namespace kapi::cpu #endif diff --git a/kapi/include/kapi/cpu/exception.hpp b/kapi/include/kapi/cpu/exception.hpp new file mode 100644 index 0000000..09e15a7 --- /dev/null +++ b/kapi/include/kapi/cpu/exception.hpp @@ -0,0 +1,60 @@ +#ifndef TEACHOS_KAPI_CPU_EXCEPTION_HPP +#define TEACHOS_KAPI_CPU_EXCEPTION_HPP + +// IWYU pragma: private, include "kapi/cpu.hpp" + +#include "kapi/memory.hpp" + +#include + +namespace kapi::cpu +{ + + struct exception + { + enum class type : std::uint8_t + { + unknown, + page_fault, + alignment_fault, + memory_access_fault, + illegal_instruction, + privilege_violation, + arithmetic_error, + breakpoint, + single_step, + }; + + //! The type of exception. + type type{}; + + //! The instruction pointer at the time of the exception. + kapi::memory::linear_address instruction_pointer{}; + + //! The memory address that caused the exception. + kapi::memory::linear_address access_address{}; + + //! Whether the page was present at the time of the exception. + bool is_present{}; + + //! Whether the exception was caused by a write access. + bool is_write_access{}; + + //! Whether the exception was caused by a user mode access. + bool is_user_mode{}; + }; + + struct exception_handler + { + virtual ~exception_handler() = default; + + virtual auto handle(exception const & context) -> bool = 0; + }; + + auto get_exception_handler() -> exception_handler &; + + auto set_exception_handler(exception_handler & handler) -> void; + +} // namespace kapi::cpu + +#endif \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 398022c..d9a5c75 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -2,6 +2,7 @@ add_executable("kernel" # Platform-independent KAPI implementation "kapi/boot_modules.cpp" "kapi/cio.cpp" + "kapi/cpu.cpp" "kapi/memory.cpp" "kapi/system.cpp" @@ -10,6 +11,7 @@ add_executable("kernel" "kstd/print.cpp" # Kernel Implementation + "src/cpu.cpp" "src/main.cpp" "src/memory/bitmap_allocator.cpp" "src/memory/block_list_allocator.cpp" diff --git a/kernel/include/kernel/cpu.hpp b/kernel/include/kernel/cpu.hpp new file mode 100644 index 0000000..d4e1ced --- /dev/null +++ b/kernel/include/kernel/cpu.hpp @@ -0,0 +1,14 @@ +#ifndef TEACHOS_KERNEL_CPU_HPP +#define TEACHOS_KERNEL_CPU_HPP + +namespace kernel::cpu +{ + + //! Initialize the kernel heap. + auto init() -> void; + + //! + +} // namespace kernel::cpu + +#endif \ No newline at end of file diff --git a/kernel/kapi/cpu.cpp b/kernel/kapi/cpu.cpp new file mode 100644 index 0000000..2089098 --- /dev/null +++ b/kernel/kapi/cpu.cpp @@ -0,0 +1,30 @@ +#include "kapi/cpu.hpp" + +namespace kapi::cpu +{ + + namespace + { + struct null_exception_handler : exception_handler + { + auto handle(exception const &) -> bool override + { + return false; + } + } static constinit default_exception_handler; + + exception_handler * current_handler = &default_exception_handler; + + } // namespace + + auto get_exception_handler() -> exception_handler & + { + return *current_handler; + } + + auto set_exception_handler(exception_handler & handler) -> void + { + current_handler = &handler; + } + +} // namespace kapi::cpu \ No newline at end of file diff --git a/kernel/src/cpu.cpp b/kernel/src/cpu.cpp new file mode 100644 index 0000000..fc460c9 --- /dev/null +++ b/kernel/src/cpu.cpp @@ -0,0 +1,46 @@ +#include "kernel/cpu.hpp" + +#include "kapi/cpu.hpp" +#include "kapi/system.hpp" + +#include + +namespace kernel::cpu +{ + + namespace + { + struct exception_handler : kapi::cpu::exception_handler + { + auto handle(kapi::cpu::exception const & context) -> bool override + { + switch (context.type) + { + case kapi::cpu::exception::type::page_fault: + return handle_page_fault(context); + default: + return false; + } + } + + private: + auto handle_page_fault(kapi::cpu::exception const & context) -> bool + { + kstd::println(kstd::print_sink::stderr, "[OS:CPU] PAGE FAULT!"); + kstd::println(kstd::print_sink::stderr, "\tFault address: {:#018x}", context.access_address); + kstd::println(kstd::print_sink::stderr, "\tPresent: {}", context.is_present); + kstd::println(kstd::print_sink::stderr, "\tWrite: {}", context.is_write_access); + kstd::println(kstd::print_sink::stderr, "\tUser: {}", context.is_user_mode); + kstd::println(kstd::print_sink::stderr, "\tRIP: {:#018x}", context.instruction_pointer); + + kapi::system::panic("Halting the system due to an unrecoverable page fault."); + } + } static constinit handler; + } // namespace + + auto init() -> void + { + kapi::cpu::set_exception_handler(handler); + } + +} // namespace kernel::cpu \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index eb59402..0416ee9 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,8 +1,10 @@ #include "kapi/boot_modules.hpp" #include "kapi/cio.hpp" +#include "kapi/cpu.hpp" #include "kapi/memory.hpp" #include "kapi/system.hpp" +#include "kernel/cpu.hpp" #include "kernel/devices/storage/storage_management.hpp" #include "kernel/filesystem/device_file.hpp" #include "kernel/filesystem/file_descriptor_table.hpp" @@ -90,6 +92,9 @@ auto main() -> int kapi::cio::init(); kstd::println("[OS] IO subsystem initialized."); + kapi::cpu::init(); + kernel::cpu::init(); + kapi::memory::init(); kernel::memory::init_heap(kapi::memory::heap_base); kstd::println("[OS] Memory subsystem initialized."); -- cgit v1.2.3 From 3888a111be2e6b2a53b10752de36d58f18fc8874 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 24 Mar 2026 17:39:33 +0100 Subject: x86_64: remove stale source code --- .../interrupt_descriptor_table/gate_descriptor.hpp | 69 -------------- .../interrupt_descriptor_table/idt_flags.hpp | 81 ---------------- .../interrupt_descriptor_table.hpp | 24 ----- .../interrupt_descriptor_table_pointer.hpp | 40 -------- .../interrupt_descriptor_table/ist_offset.hpp | 45 --------- .../segment_selector.hpp | 105 --------------------- .../generic_interrupt_handler.hpp | 34 ------- arch/x86_64/pre/include/arch/kernel/cpu/call.hpp | 30 ------ arch/x86_64/pre/include/arch/kernel/cpu/idtr.hpp | 27 ------ arch/x86_64/pre/include/arch/kernel/cpu/if.hpp | 21 ----- arch/x86_64/pre/include/arch/kernel/cpu/msr.hpp | 64 ------------- arch/x86_64/pre/include/arch/kernel/cpu/tr.hpp | 24 ----- .../interrupt_descriptor_table/gate_descriptor.cpp | 24 ----- .../interrupt_descriptor_table/idt_flags.cpp | 20 ---- .../interrupt_descriptor_table.cpp | 53 ----------- .../interrupt_descriptor_table_pointer.cpp | 13 --- .../interrupt_descriptor_table/ist_offset.cpp | 10 -- .../segment_selector.cpp | 24 ----- .../generic_interrupt_handler.cpp | 13 --- arch/x86_64/pre/src/kernel/cpu/call.cpp | 9 -- arch/x86_64/pre/src/kernel/cpu/idtr.cpp | 18 ---- arch/x86_64/pre/src/kernel/cpu/if.cpp | 13 --- arch/x86_64/pre/src/kernel/cpu/tr.cpp | 16 ---- 23 files changed, 777 deletions(-) delete mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp delete mode 100644 arch/x86_64/pre/include/arch/interrupt_handling/generic_interrupt_handler.hpp delete mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/call.hpp delete mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/idtr.hpp delete mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/if.hpp delete mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/msr.hpp delete mode 100644 arch/x86_64/pre/include/arch/kernel/cpu/tr.hpp delete mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp delete mode 100644 arch/x86_64/pre/src/interrupt_handling/generic_interrupt_handler.cpp delete mode 100644 arch/x86_64/pre/src/kernel/cpu/call.cpp delete mode 100644 arch/x86_64/pre/src/kernel/cpu/idtr.cpp delete mode 100644 arch/x86_64/pre/src/kernel/cpu/if.cpp delete mode 100644 arch/x86_64/pre/src/kernel/cpu/tr.cpp diff --git a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp deleted file mode 100644 index 07110c8..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_GATE_DESCRIPTOR_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_GATE_DESCRIPTOR_HPP - -#include "arch/context_switching/interrupt_descriptor_table/idt_flags.hpp" -#include "arch/context_switching/interrupt_descriptor_table/ist_offset.hpp" -#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp" - -#include -#include - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - __extension__ typedef __int128 int128_t; - __extension__ typedef unsigned __int128 uint128_t; - - /** - * @brief Defines helper function for all states and the actual data the gate descriptor can have. - */ - struct [[gnu::packed]] gate_descriptor - { - /** - * @brief Default Constructor. - */ - gate_descriptor() = default; - - /** - * @brief Constructor. - * - * @note Created gate descriptor copies the given bytes into these components ending with a 32 bit reserved - * field that has to be used, because the 64-bit gate descriptor needs to be big enough for two 32-bit gate - * descriptor. - * - 16 bit Segment Selector - * - 3 bit Interrupt Stack Table Offset - * - 8 bit Type and Flags - * - 64 bit Offset - * - * @param flags Copies the bits set from the given data into the individual components of a gate - * descriptor. - */ - explicit gate_descriptor(uint128_t flags); - - /** - * @brief Constructor. - * - * @param selector, ist, flags, offset Copies the bits set from the given data into the individual components of - * a gate descriptor. - */ - gate_descriptor(segment_selector selector, ist_offset ist, idt_flags flags, uint64_t offset); - - /** - * @brief Allows to compare the underlying bits of two instances. - * - * @param other Other instance that we want to compare with. - * @return Whether the underlying set bits of both types are the same. - */ - auto operator==(gate_descriptor const & other) const -> bool = default; - - private: - // The order in private variables starts for the first variable being the rightmost bit. - uint16_t _offset_1 = {}; ///< Lower 16 bits of handler function address (0 - 15) - segment_selector _selector = {}; ///< Segment selector (16 - 31) - ist_offset _ist = {}; ///< Interrupt Stack Table offset (32 - 39) - idt_flags _flags = {}; ///< Gate Type and Flags (40 - 47) - uint64_t _offset_2 : 48 = {}; ///< Upper 48 bits of handler function address (48 - 95) - uint32_t : 32; ///< Reserved field used to ensure this struct is 128 bits big (96 - 127) - }; -} // namespace teachos::arch::context_switching::interrupt_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_GATE_DESCRIPTOR_HPP diff --git a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp deleted file mode 100644 index 5104c36..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp +++ /dev/null @@ -1,81 +0,0 @@ - -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IDT_FLAGS_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IDT_FLAGS_HPP - -#include -#include - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - /** - * @brief Defines helper function for all states that the access byte field of a segment descriptor can - * have. - */ - struct [[gnu::packed]] idt_flags - { - /** - * @brief Possible set bits in our underlying bits and the meaning when they are set. - */ - enum bitset : uint8_t - { - INTERRUPT_GATE = 0b01110, ///< The actual type of gate segment is a interrupt gate. - TRAP_GATE = 0b01111, ///< The actual type of gate segment is a trap gate. - DESCRIPTOR_LEVEL_KERNEL = - 0U << 5U, ///< Highest privileged level used by the kernel to allow for full access of resources. - DESCRIPTOR_LEVEL_ADMIN = - 1U << 5U, ///< Restricts access to own application and thoose of lower privilege. Should only be used if more - ///< than two privilege levels are required, otherwise using Level 3 and Level 0 is recommended. - DESCRIPTOR_LEVEL_PRIVILEGED_USER = - 2U << 5U, ///< Restricts access to own application and thoose of lower privilege. Should only be used if more - ///< than two privilege levels are required, otherwise using Level 3 and Level 0 is recommended. - DESCRIPTOR_LEVEL_USER = 3U << 5U, ///< Restricts access to only application and their specific memory. - PRESENT = 1U << 7U, ///< Present bit; Allows an entry to refer to a valid segment. - ///< Must be set (1) for any valid segment. - }; - - /** - * @brief Default Constructor. - */ - idt_flags() = default; - - /** - * @brief Constructor. - * - * @param flags Allows to set flags for the access byte field using the unscoped enum contained in this class, used - * to allow for direct integer conversion. This value is saved and can later be used to check whether certain flags - * are enabled or not using contains_flags method. - */ - idt_flags(uint8_t flags); - - /** - * @brief Checks if the given std::bitset is a subset or equivalent to the underlying data. - * - * @note Meaning that all bits that are set in the given std::bitset also have to be set in the underlyng - * data. Any additional bits that are set are not relevant. - * - * @param other Flags that we want to compare against and check if the underlying data has the same bits set. - * @return Whether the given flags are a subset or equivalent with the underlying data. - */ - auto contains_flags(std::bitset<8U> other) const -> bool; - - /** - * @brief Allows to compare the underlying bits of two instances. - * - * @param other Other instance that we want to compare with. - * @return Whether the underlying set bits of both types are the same. - */ - auto operator==(idt_flags const & other) const -> bool = default; - - /** - * @brief Combines all bits that are set in the std::bitset flags with the bits already set in the underlying data. - * - * @param other Additional bits that should be set. - */ - auto operator|=(std::bitset<8U> other) -> void; - - private: - uint8_t _flags = {}; ///< Underlying bits used to read the flags from. - }; -} // namespace teachos::arch::context_switching::interrupt_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IDT_FLAGS_HPP \ No newline at end of file diff --git a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp deleted file mode 100644 index b388e0e..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_HPP - -#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - /** - * @brief Updates the IDTR with the created interrupt descriptor table. If it has not been created yet this - * method will create it. - */ - auto update_interrupt_descriptor_table_register() -> void; - - /** - * @brief Creates the interrupt descriptor table, with the minimum required configuration. If this method is called - * more than once, the previously created instance is returned instead. - * - * @return Reference to the created interrupt_descriptor_table. - */ - auto get_or_create_interrupt_descriptor_table() -> interrupt_descriptor_table &; - -} // namespace teachos::arch::context_switching::interrupt_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_HPP diff --git a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp deleted file mode 100644 index 7fe933b..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_POINTER_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_POINTER_HPP - -#include "arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp" -#include "arch/stl/vector.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - using interrupt_descriptor_table = stl::vector; - - /** - * @brief Represents a pointer to the Interrupt Descriptor Table (IDT). - * - * This structure is used to store the base address and length of the IDT. - */ - struct [[gnu::packed]] interrupt_descriptor_table_pointer - { - /** - * @brief Default constructor. - */ - interrupt_descriptor_table_pointer() = default; - - /** - * @brief Constructor. - */ - interrupt_descriptor_table_pointer(uint16_t table_length, gate_descriptor * address); - - /** - * @brief Defaulted three-way comparsion operator. - */ - auto operator<=>(interrupt_descriptor_table_pointer const & other) const -> std::strong_ordering = default; - - private: - uint16_t table_length = {}; ///< The amount of segment descriptor entries in the global descriptor table - 1. - gate_descriptor * address = {}; ///< Non-owning pointer to the IDT base address. - }; - -} // namespace teachos::arch::context_switching::interrupt_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_INTERRUPT_DESCRIPTOR_TABLE_POINTER_HPP diff --git a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp deleted file mode 100644 index e45bcf4..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IST_OFFSET_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IST_OFFSET_HPP - -#include -#include - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - /** - * @brief Defines helper function for all states that the interrupt stack table offset field of a gate descriptor can - * have. Is automatically increased to one byte in size, to include the following 5 reserved bits in the gate - * descriptor. - */ - struct [[gnu::packed]] ist_offset - { - /** - * @brief Default Constructor. - */ - ist_offset() = default; - - /** - * @brief Constructor. - * - * @param offset Offset into the interrupt stack table. A value of of 0 means we do not switch stacks, whereas 1 - 7 - * mean we switch to the n-th stack in the Interrupt Stack Table, contained in the TSS if the gate descriptor that - * contains this field is called. - */ - ist_offset(uint8_t offset); - - /** - * @brief Allows to compare the underlying set bits of two instances. - * - * @param other Other instance that we want to compare with. - * @return Whether the underlying set bits of both types are the same. - */ - auto operator==(ist_offset const & other) const -> bool = default; - - private: - uint8_t _ist : 3 = {}; ///< Offset into the interrupt stack table. A value of of 0 means we do not switch stacks, - ///< whereas 1 - 7 mean we switch to the n-th stack in the Interrupt Stack Table, contained - ///< in the TSS if the gate descriptor that contains this field is called. - }; -} // namespace teachos::arch::context_switching::interrupt_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_IST_OFFSET_HPP diff --git a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp b/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp deleted file mode 100644 index ea8c145..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_SEGMENT_SELECTOR_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_SEGMENT_SELECTOR_HPP - -#include -#include - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - /** - * @brief Represents a segment selector in the x86_64 architecture, which points to a valid code segment in the global - * descriptor table. - * - * A segment selector is a 16-bit identifier used to select a segment descriptor - * from the Global Descriptor Table (GDT) or the Local Descriptor Table (LDT). - * It contains an index, a table indicator (TI), and a requested privilege level (RPL). - */ - struct [[gnu::packed]] segment_selector - { - /** - * @brief Possible set bits in our underlying bits and the meaning when they are set. - */ - enum bitset : uint8_t - { - REQUEST_LEVEL_KERNEL = - 0U << 0U, ///< Highest privileged level used by the kernel to allow for full access of resources. - REQUEST_LEVEL_ADMIN = - 1U << 0U, ///< Restricts access to own application and thoose of lower privilege. Should only be used if more - ///< than two privilege levels are required, otherwise using Level 3 and Level 0 is recommended. - REQUEST_LEVEL_PRIVILEGED_USER = - 2U << 0U, ///< Restricts access to own application and thoose of lower privilege. Should only be used if more - ///< than two privilege levels are required, otherwise using Level 3 and Level 0 is recommended. - REQUEST_LEVEL_USER = 3U << 0U, ///< Restricts access to only application and their specific memory. - LOCAL_DESCRIPTOR_TABLE = 1U << 2U, ///< Wheter the index referes to an entry in the local or global descriptor - ///< table. If enabled the index points to a local descriptor table, if it is - ///< cleared it referes to a global descriptor table instead. - }; - - /** - * @brief Default constructor. - */ - segment_selector() = default; - - /** - * @brief Constructor. - * - * @param index Index into the local or global descriptor table. Processor multiplies the index value by 8 (number - * of bytes in 32-bit segment descriptor) and adds the result to the base GDT or LDT address. - * @param flags Allows to set flags for the flags field using the unscoped enum contained in this class, used to - * allow for direct integer conversion. - */ - constexpr segment_selector(uint16_t index, uint8_t flags) - : _flags(flags) - , _index(index) - { - // Nothing to do. - } - - /** - * @brief Checks if the given std::bitset is a subset or equivalent to the underlying data. - * - * @note Meaning that all bits that are set in the given std::bitset also have to be set in the underlyng - * data. Any additional bits that are set are not relevant. - * - * @param other Flags that we want to compare against and check if the underlying data has the same bits set. - * @return Whether the given flags are a subset or equivalent with the underlying data. - */ - auto contains_flags(std::bitset<3U> other) const -> bool; - - /** - * @brief Gets the index into the global descriptor table or the local descriptor table this segment selector is - * pointing too. - * - * @return Underlying value of the index field, bit 3 - 16. - */ - [[gnu::section(".user_text")]] - auto get_index() const -> uint16_t; - - /** - * @brief Defaulted three-way comparsion operator. - */ - auto operator<=>(segment_selector const & other) const -> std::strong_ordering = default; - - /** - * @brief Combines all bits that are set in the std::bitset flags with the bits already set in the underlying data. - * - * @param other Additional bits that should be set. - */ - auto operator|=(std::bitset<3U> other) -> void; - - /** - * @brief Cast the underlying data into a combined 16-bit form, that contains all data. - * - * @return Underlying value combined into it's full size. - */ - operator uint16_t() const; - - private: - uint8_t _flags : 3 = {}; ///< Underlying bits used to read the flags from. - uint16_t _index - : 13 = {}; ///< Index into the local or global descriptor table. Processor multiplies the index value by 16 - ///< (number of bytes in segment descriptor) and adds the result to the base address. - }; -} // namespace teachos::arch::context_switching::interrupt_descriptor_table - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_INTERRUPT_DESCRIPTOR_TABLE_SEGMENT_SELECTOR_HPP diff --git a/arch/x86_64/pre/include/arch/interrupt_handling/generic_interrupt_handler.hpp b/arch/x86_64/pre/include/arch/interrupt_handling/generic_interrupt_handler.hpp deleted file mode 100644 index 15b35c1..0000000 --- a/arch/x86_64/pre/include/arch/interrupt_handling/generic_interrupt_handler.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_INTERRUPT_HANDLING_GENERIC_INTERRUPT_HANDLER_HPP -#define TEACHOS_ARCH_X86_64_INTERRUPT_HANDLING_GENERIC_INTERRUPT_HANDLER_HPP - -#include - -namespace teachos::arch::interrupt_handling -{ - /** - * @brief Represents the CPU state during an interrupt. - * - * Some interrupts push an error code, while others do not. The full list - * of which vector number contains the error code can be found here: https://wiki.osdev.org/Exceptions - */ - struct [[gnu::packed]] interrupt_frame - { - // uint64_t error_code; ///< Error code only pushed by some exceptions, therefore it is commented out. - uint64_t ip; ///< Instruction pointer at the time of the interrupt. - uint64_t cs; ///< Code segment selector indicating privilege level. - uint64_t flags; ///< CPU flags (RFLAGS) storing processor state. - uint64_t sp; ///< Stack pointer at the time of the interrupt. - uint64_t ss; ///< Stack segment selector, usually unused in 64-bit mode. - }; - - /** - * @brief Generic interrupt handler function. - * - * @param frame Pointer to the interrupt frame containing CPU state. - */ - [[gnu::interrupt]] - auto generic_interrupt_handler(interrupt_frame * frame) -> void; - -} // namespace teachos::arch::interrupt_handling - -#endif // TEACHOS_ARCH_X86_64_INTERRUPT_HANDLING_GENERIC_INTERRUPT_HANDLER_HPP diff --git a/arch/x86_64/pre/include/arch/kernel/cpu/call.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/call.hpp deleted file mode 100644 index 3c43304..0000000 --- a/arch/x86_64/pre/include/arch/kernel/cpu/call.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_JMP_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_JMP_HPP - -#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp" - -#include - -namespace teachos::arch::kernel::cpu -{ - /** - * @brief Far Pointer. Address to function located in another code segment. - */ - struct far_pointer - { - void (*function)(); ///< Address of the function we want to call. (0-63) - context_switching::interrupt_descriptor_table::segment_selector - selector; ///< Segment selector pointing to the GDT entry we want to load into register CS. (64-79) - }; - - /** - * @brief Far call - A call to an instruction located in a different segment than the current code segment but at the - * same privilege level. - * - * @param pointer 64-bit operand size far pointer that we want to call. - */ - auto call(far_pointer pointer) -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_JMP_HPP diff --git a/arch/x86_64/pre/include/arch/kernel/cpu/idtr.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/idtr.hpp deleted file mode 100644 index cb800d0..0000000 --- a/arch/x86_64/pre/include/arch/kernel/cpu/idtr.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_IDTR_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_IDTR_HPP - -#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp" - -#include -#include - -namespace teachos::arch::kernel::cpu -{ - /** - * @brief Returns the value in the IDTR register. - * - * @return Value of IDTR register. - */ - auto store_interrupt_descriptor_table() - -> context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer; - - /** - * @brief Loads the interrupt_descriptor_table_pointer into the interrupt descriptor table register (IDTR). - */ - auto load_interrupt_descriptor_table( - context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer const & idt_pointer) -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_IDTR_HPP diff --git a/arch/x86_64/pre/include/arch/kernel/cpu/if.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/if.hpp deleted file mode 100644 index 48707dc..0000000 --- a/arch/x86_64/pre/include/arch/kernel/cpu/if.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_IF_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_IF_HPP - -namespace teachos::arch::kernel::cpu -{ - /** - * @brief Sets the interrupt flag (IF) in the EFLAGS register. - * This allows the processor to respond to maskable hardware interrupts. - */ - auto set_interrupt_flag() -> void; - - /** - * @brief Clears the interrupt flag (IF) in the EFLAGS register. - * This will stop the processor to respond to maskable hardware interrupts and needs to be done before changing the - * Interrupt Descriptor Table with lidt. - */ - auto clear_interrupt_flag() -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_IF_HPP diff --git a/arch/x86_64/pre/include/arch/kernel/cpu/msr.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/msr.hpp deleted file mode 100644 index 99d6378..0000000 --- a/arch/x86_64/pre/include/arch/kernel/cpu/msr.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_NXE_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_NXE_HPP - -#include -#include - -namespace teachos::arch::kernel::cpu -{ - /** - * @brief Important flags that can be writen into the Extended Feature Enable Register (EFER). - * - * @note EFER is a model-specific register allowing to configure CPU extensions. Only the most important extensions - * are listed below, the rest are excluded for brevity. See https://en.wikipedia.org/wiki/Control_register#EFER for - * more information. - */ - enum class efer_flags : uint64_t - { - SCE = 1UL << 0UL, ///< System Call Extensions. - LME = 1UL << 8UL, ///< Long Mode Enabled. - LMA = 1UL << 10UL, ///< Long Mode Active. - NXE = 1UL << 11UL, ///< No-Execute Enable. - SVME = 1UL << 12UL, ///< Secure Virtual Machine Enable. - LMSLE = 1UL << 13UL, ///< Long Mode Segment Limit Enable. - FFXSR = 1UL << 14UL, ///< Fast FXSAVE/FXSTOR. - TCE = 1UL << 15UL, ///< Translation Cache Extension. - }; - - /** - * @brief Reads a 64-bit from the Model-Specific Register (MSR). - * - * @note This function reads the value of an MSR specified by the given address. It combines the lower and upper - * 32-bits of the MSR value read using the 'rdmsr' instruction and returns it as a 64-bit unsigned integer. - * - * @param msr The address of the MSR to read. - * @return The 64-bit value read from the MSR. - */ - auto read_msr(uint32_t msr) -> uint64_t; - - /** - * @brief Writes a 64-bit value to a Model-Specific Register (MSR). - * - * @note This function writes a 64-bit value to the MSR specified by the given address. - * It splits the 64-bit value into two 32-bit parts and writes them using the - * `wrmsr` instruction. - * - * @param msr The address of the MSR to write to. - * @param new_value The 64-bit value to write to the MSR. - */ - auto write_msr(uint32_t msr, uint64_t new_value) -> void; - - /** - * @brief Sets a specific bit in the Extended Feature Enable Register (EFER), which is a Model-Specific Register - * (MSR). - * - * @note This function reads the current value of the EFER register, ORs the specified - * bit with the current value, and writes the updated value back to the EFER register. - * - * @param flag The flag to set in the EFER register. - */ - auto set_efer_bit(efer_flags flag) -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_NXE_HPP \ No newline at end of file diff --git a/arch/x86_64/pre/include/arch/kernel/cpu/tr.hpp b/arch/x86_64/pre/include/arch/kernel/cpu/tr.hpp deleted file mode 100644 index 7c856f1..0000000 --- a/arch/x86_64/pre/include/arch/kernel/cpu/tr.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_CPU_TR_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_CPU_TR_HPP - -#include -#include - -namespace teachos::arch::kernel::cpu -{ - - /** - * @brief Returns the value in the LTR register. - * - * @return Value of LTR register. - */ - auto store_task_register() -> uint16_t; - - /** - * @brief Loads the gdt offset to the tss segment descriptor into the task register (TR). - */ - auto load_task_register(uint16_t gdt_offset) -> void; - -} // namespace teachos::arch::kernel::cpu - -#endif // TEACHOS_ARCH_X86_64_KERNEL_CPU_TR_HPP diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp deleted file mode 100644 index 28f289c..0000000 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/gate_descriptor.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - gate_descriptor::gate_descriptor(uint128_t flags) - : _offset_1(flags) - , _selector(flags >> 19U, flags >> 16U) - , _ist(flags >> 32U) - , _flags(flags >> 40U) - , _offset_2(flags >> 48U) - { - // Nothing to do. - } - - gate_descriptor::gate_descriptor(segment_selector selector, ist_offset ist, idt_flags flags, uint64_t offset) - : _offset_1(offset) - , _selector(selector) - , _ist(ist) - , _flags(flags) - , _offset_2(offset >> 16U) - { - // Nothing to do. - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp deleted file mode 100644 index f3b9d5e..0000000 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/idt_flags.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/idt_flags.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - idt_flags::idt_flags(uint8_t flags) - : _flags(flags) - { - // Nothing to do. - } - - auto idt_flags::contains_flags(std::bitset<8U> other) const -> bool - { - return (std::bitset<8U>{_flags} & other) == other; - } - - auto idt_flags::operator|=(std::bitset<8U> other) -> void - { - _flags |= other.to_ulong(); - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp deleted file mode 100644 index 8640385..0000000 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp" - -#include "arch/exception_handling/assert.hpp" -#include "arch/interrupt_handling/generic_interrupt_handler.hpp" -#include "arch/kernel/cpu/idtr.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - namespace - { - /// @brief Amount of currently reserved interrupt indicies. - /// See https://wiki.osdev.org/Interrupt_Descriptor_Table#IDT_items for more information. - constexpr uint16_t RESERVED_INTERRUPT_COUNT = 256U; - - auto create_interrupt_descriptor_table() -> interrupt_descriptor_table - { - interrupt_descriptor_table interrupt_descriptor_table{RESERVED_INTERRUPT_COUNT}; - - uint64_t offset = reinterpret_cast(interrupt_handling::generic_interrupt_handler); - segment_selector selector{1U, segment_selector::REQUEST_LEVEL_KERNEL}; - ist_offset ist{0U}; - idt_flags flags{idt_flags::DESCRIPTOR_LEVEL_KERNEL | idt_flags::INTERRUPT_GATE | idt_flags::PRESENT}; - - for (std::size_t i = 0; i < interrupt_descriptor_table.size(); i++) - { - interrupt_descriptor_table.at(i) = {selector, ist, flags, offset}; - } - - return interrupt_descriptor_table; - } - } // namespace - - auto get_or_create_interrupt_descriptor_table() -> interrupt_descriptor_table & - { - // Interrupt Descriptor Table needs to be kept alive - interrupt_descriptor_table static idt = create_interrupt_descriptor_table(); - return idt; - } - - auto update_interrupt_descriptor_table_register() -> void - { - decltype(auto) idt = get_or_create_interrupt_descriptor_table(); - - interrupt_descriptor_table_pointer idt_pointer{static_cast((idt.size() * sizeof(gate_descriptor)) - 1), - idt.data()}; - kernel::cpu::load_interrupt_descriptor_table(idt_pointer); - - auto const stored_gdt_pointer = kernel::cpu::store_interrupt_descriptor_table(); - arch::exception_handling::assert( - idt_pointer == stored_gdt_pointer, - "[Interrupt Descriptor Table] Loaded IDTR value is not the same as the stored value."); - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp deleted file mode 100644 index 7bcbae6..0000000 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - interrupt_descriptor_table_pointer::interrupt_descriptor_table_pointer(uint16_t table_length, - gate_descriptor * address) - : table_length(table_length) - , address(address) - { - // Nothing to do. - } - -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp deleted file mode 100644 index a70e75d..0000000 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/ist_offset.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/ist_offset.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - ist_offset::ist_offset(uint8_t index) - : _ist(index) - { - // Nothing to do. - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp b/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp deleted file mode 100644 index 25ba859..0000000 --- a/arch/x86_64/pre/src/context_switching/interrupt_descriptor_table/segment_selector.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp" - -namespace teachos::arch::context_switching::interrupt_descriptor_table -{ - auto segment_selector::contains_flags(std::bitset<3U> other) const -> bool - { - return (std::bitset<3U>{_flags} & other) == other; - } - - auto segment_selector::get_index() const -> uint16_t - { - return _index; - } - - auto segment_selector::operator|=(std::bitset<3U> other) -> void - { - _flags |= other.to_ulong(); - } - - segment_selector::operator uint16_t() const - { - return *reinterpret_cast(this); - } -} // namespace teachos::arch::context_switching::interrupt_descriptor_table diff --git a/arch/x86_64/pre/src/interrupt_handling/generic_interrupt_handler.cpp b/arch/x86_64/pre/src/interrupt_handling/generic_interrupt_handler.cpp deleted file mode 100644 index 9d061a8..0000000 --- a/arch/x86_64/pre/src/interrupt_handling/generic_interrupt_handler.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "arch/interrupt_handling/generic_interrupt_handler.hpp" - -#include "arch/video/vga/text.hpp" - -namespace teachos::arch::interrupt_handling -{ - auto generic_interrupt_handler(interrupt_frame * frame) -> void - { - (void)frame; - video::vga::text::write("An Interrupt occurred.", video::vga::text::common_attributes::green_on_black); - video::vga::text::newline(); - } -} // namespace teachos::arch::interrupt_handling diff --git a/arch/x86_64/pre/src/kernel/cpu/call.cpp b/arch/x86_64/pre/src/kernel/cpu/call.cpp deleted file mode 100644 index 98fa248..0000000 --- a/arch/x86_64/pre/src/kernel/cpu/call.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "arch/kernel/cpu/call.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto call(far_pointer pointer) -> void - { - asm volatile("rex64 lcall *%[input]" : /* no output from call */ : [input] "m"(pointer)); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/pre/src/kernel/cpu/idtr.cpp b/arch/x86_64/pre/src/kernel/cpu/idtr.cpp deleted file mode 100644 index 7aa20c1..0000000 --- a/arch/x86_64/pre/src/kernel/cpu/idtr.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "arch/kernel/cpu/idtr.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto store_interrupt_descriptor_table() - -> context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer - { - context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer current_value{}; - asm("sidt %[output]" : [output] "=m"(current_value)); - return current_value; - } - - auto load_interrupt_descriptor_table( - context_switching::interrupt_descriptor_table::interrupt_descriptor_table_pointer const & idt_pointer) -> void - { - asm volatile("lidt %[input]" : /* no output from call */ : [input] "m"(idt_pointer)); - } -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/pre/src/kernel/cpu/if.cpp b/arch/x86_64/pre/src/kernel/cpu/if.cpp deleted file mode 100644 index 5d056fc..0000000 --- a/arch/x86_64/pre/src/kernel/cpu/if.cpp +++ /dev/null @@ -1,13 +0,0 @@ -namespace teachos::arch::kernel::cpu -{ - auto set_interrupt_flag() -> void - { - asm volatile("sti"); - } - - auto clear_interrupt_flag() -> void - { - asm volatile("cli"); - } - -} // namespace teachos::arch::kernel::cpu diff --git a/arch/x86_64/pre/src/kernel/cpu/tr.cpp b/arch/x86_64/pre/src/kernel/cpu/tr.cpp deleted file mode 100644 index a435540..0000000 --- a/arch/x86_64/pre/src/kernel/cpu/tr.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "arch/kernel/cpu/tr.hpp" - -namespace teachos::arch::kernel::cpu -{ - auto store_task_register() -> uint16_t - { - uint16_t current_value{}; - asm("str %[output]" : [output] "=r"(current_value)); - return current_value; - } - - auto load_task_register(uint16_t gdt_offset) -> void - { - asm volatile("ltr %[input]" : /* no output from call */ : [input] "m"(gdt_offset)); - } -} // namespace teachos::arch::kernel::cpu -- cgit v1.2.3 From 42895684b631380c8aca94f82209297ac0c0e5f2 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 24 Mar 2026 17:44:21 +0100 Subject: kapi: extract interrupt enablement --- arch/x86_64/include/arch/cpu/interrupts.hpp | 3 --- arch/x86_64/kapi/cpu.cpp | 12 ++++++++++-- arch/x86_64/src/cpu/interrupts.cpp | 10 ---------- kapi/include/kapi/cpu.hpp | 3 ++- kapi/include/kapi/cpu/interrupts.hpp | 19 +++++++++++++++++++ kernel/src/main.cpp | 1 + 6 files changed, 32 insertions(+), 16 deletions(-) create mode 100644 kapi/include/kapi/cpu/interrupts.hpp diff --git a/arch/x86_64/include/arch/cpu/interrupts.hpp b/arch/x86_64/include/arch/cpu/interrupts.hpp index 08ecd9c..b9adb6e 100644 --- a/arch/x86_64/include/arch/cpu/interrupts.hpp +++ b/arch/x86_64/include/arch/cpu/interrupts.hpp @@ -111,9 +111,6 @@ namespace arch::cpu auto static read() -> interrupt_descriptor_table_register; }; - auto enable_interrupts() -> void; - auto disable_interrupts() -> void; - } // namespace arch::cpu #endif \ No newline at end of file diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index 693d328..8ca3847 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -3,7 +3,6 @@ #include "kapi/system.hpp" #include "arch/cpu/initialization.hpp" -#include "arch/cpu/interrupts.hpp" #include @@ -21,7 +20,6 @@ namespace kapi::cpu arch::cpu::initialize_descriptors(); arch::cpu::initialize_legacy_interrupts(); - arch::cpu::enable_interrupts(); } auto halt() -> void @@ -30,4 +28,14 @@ namespace kapi::cpu __builtin_unreachable(); } + auto enable_interrupts() -> void + { + asm volatile("sti"); + } + + auto disable_interrupts() -> void + { + asm volatile("cli"); + } + } // namespace kapi::cpu diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 6f66bbd..dc236e6 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -168,14 +168,4 @@ namespace arch::cpu return idtr; } - auto enable_interrupts() -> void - { - asm volatile("sti"); - } - - auto disable_interrupts() -> void - { - asm volatile("cli"); - } - } // namespace arch::cpu \ No newline at end of file diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index 1dd7cfe..ade954c 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -1,7 +1,8 @@ #ifndef TEACHOS_KAPI_CPU_HPP #define TEACHOS_KAPI_CPU_HPP -#include "kapi/cpu/exception.hpp" +#include "kapi/cpu/exception.hpp" // IWYU pragma: export +#include "kapi/cpu/interrupts.hpp" // IWYU pragma: export namespace kapi::cpu { diff --git a/kapi/include/kapi/cpu/interrupts.hpp b/kapi/include/kapi/cpu/interrupts.hpp new file mode 100644 index 0000000..26a215e --- /dev/null +++ b/kapi/include/kapi/cpu/interrupts.hpp @@ -0,0 +1,19 @@ +#ifndef TEACHOS_KAPI_CPU_INTERRUPTS_HPP +#define TEACHOS_KAPI_CPU_INTERRUPTS_HPP + +// IWYU pragma: private, include "kapi/cpu.hpp" + +namespace kapi::cpu +{ + + //! @qualifier platform-defined + //! Enable external interrupts. + auto enable_interrupts() -> void; + + //! @qualifier platform-defined + //! Disable external interrupts. + auto disable_interrupts() -> void; + +} // namespace kapi::cpu + +#endif \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 0416ee9..6bd168c 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -94,6 +94,7 @@ auto main() -> int kapi::cpu::init(); kernel::cpu::init(); + kapi::cpu::enable_interrupts(); kapi::memory::init(); kernel::memory::init_heap(kapi::memory::heap_base); -- cgit v1.2.3 From 9b879b06e202a41cdecc25e08ed5e69c57814141 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 07:44:57 +0100 Subject: kapi: add missing header to build --- kapi/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index b239adb..4c94829 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -8,6 +8,9 @@ target_sources("kapi" PUBLIC "include/kapi/boot_modules.hpp" "include/kapi/boot.hpp" "include/kapi/cio.hpp" + "include/kapi/cpu.hpp" + "include/kapi/cpu/interrupts.hpp" + "include/kapi/cpu/exception.hpp" "include/kapi/boot_module/boot_module.hpp" "include/kapi/boot_module/boot_module_registry.hpp" "include/kapi/memory.hpp" -- cgit v1.2.3 From fd6ac1cbfbe90fa807dca60657bb80ed43c78aee Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 07:45:08 +0100 Subject: kapi/cpu: improve documentation --- kapi/include/kapi/cpu/exception.hpp | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/kapi/include/kapi/cpu/exception.hpp b/kapi/include/kapi/cpu/exception.hpp index 09e15a7..6d39175 100644 --- a/kapi/include/kapi/cpu/exception.hpp +++ b/kapi/include/kapi/cpu/exception.hpp @@ -10,28 +10,40 @@ namespace kapi::cpu { + //! An exception originating from the CPU directly. + //! + //! Exception generally model interrupts that are synchronous to the instruction stream. This means that they do not + //! originate from external hardware, but rather are a product of program execution. struct exception { + //! The type of the exception, which identifies the reason for it being raised. enum class type : std::uint8_t { + //! The reason for the exception is unknown or platform-specific unknown, + //! A page fault occurred page_fault, + //! A memory access (either in the data or instruction stream) violated it's alignment constraints. alignment_fault, + //! A memory access (either in the data or instruction stream) violated it's permissions. memory_access_fault, - illegal_instruction, + //! The precoditions for the execution of an instruction were not met. privilege_violation, + //! An arithmetic error occurred. arithmetic_error, + //! A breakpoint was hit in the instruction stream. breakpoint, + //! The CPU is single-stepping through the instruction stream. single_step, }; - //! The type of exception. + //! The type of this exception. type type{}; - //! The instruction pointer at the time of the exception. + //! The value of the instruction pointer at the time this exception was raised. kapi::memory::linear_address instruction_pointer{}; - //! The memory address that caused the exception. + //! The memory address that caused this exception. kapi::memory::linear_address access_address{}; //! Whether the page was present at the time of the exception. @@ -44,15 +56,26 @@ namespace kapi::cpu bool is_user_mode{}; }; + //! The abstract interface for exception handlers. + //! + //! The kernel must define an exception handler to be used during execution. struct exception_handler { virtual ~exception_handler() = default; + //! Handle an exception. + //! + //! @param context The exception context. + //! @return Whether the exception was handled. virtual auto handle(exception const & context) -> bool = 0; }; + //! @qualifier kernel-defined + //! Get the currently active exception handler. auto get_exception_handler() -> exception_handler &; + //! @qualifier kernel-defined + //! Set the exception handler. auto set_exception_handler(exception_handler & handler) -> void; } // namespace kapi::cpu -- cgit v1.2.3 From 2f8c5ca6d5ab6131a148502e1d1be4ce2a65b339 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 07:47:04 +0100 Subject: kapi/cpu: add missing exception type --- kapi/include/kapi/cpu/exception.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kapi/include/kapi/cpu/exception.hpp b/kapi/include/kapi/cpu/exception.hpp index 6d39175..9fc697a 100644 --- a/kapi/include/kapi/cpu/exception.hpp +++ b/kapi/include/kapi/cpu/exception.hpp @@ -27,6 +27,8 @@ namespace kapi::cpu alignment_fault, //! A memory access (either in the data or instruction stream) violated it's permissions. memory_access_fault, + //! An invalid instruction was present in the instruction stream. + illegal_instruction, //! The precoditions for the execution of an instruction were not met. privilege_violation, //! An arithmetic error occurred. -- cgit v1.2.3 From 363a6d701d4998137fcc123059f9749098ac7d75 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 08:12:49 +0100 Subject: x86_64/cpu: fix exception enum --- arch/x86_64/src/cpu/interrupts.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index dc236e6..e578aa2 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -28,15 +28,17 @@ namespace arch::cpu double_fault, coprocessor_segment_overrun, invalid_tss, + segment_not_present, stack_segment_fault, general_protection_fault, page_fault, - x87_fpu_floating_point_error, + x87_fpu_floating_point_error = 16, alignment_check, machine_check, simd_floating_point_error, virtualization_exception, control_protection_exception, + hypervisor_injection_exception = 28, vmm_communication_exception, security_exception, }; @@ -62,6 +64,8 @@ namespace arch::cpu return kapi::cpu::exception::type::memory_access_fault; case exception::general_protection_fault: return kapi::cpu::exception::type::privilege_violation; + case exception::page_fault: + return kapi::cpu::exception::type::page_fault; case exception::alignment_check: return kapi::cpu::exception::type::alignment_fault; default: -- cgit v1.2.3 From fd1c5a50bb35f772b8e37125188640447d4b3b2a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 13:24:03 +0100 Subject: kapi/cpu: enable formatting of exception types --- kapi/include/kapi/cpu/exception.hpp | 36 ++++++++++++++++++++++++++++++++++++ kernel/src/cpu.cpp | 3 +-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/kapi/include/kapi/cpu/exception.hpp b/kapi/include/kapi/cpu/exception.hpp index 9fc697a..00b02e7 100644 --- a/kapi/include/kapi/cpu/exception.hpp +++ b/kapi/include/kapi/cpu/exception.hpp @@ -5,6 +5,8 @@ #include "kapi/memory.hpp" +#include + #include namespace kapi::cpu @@ -82,4 +84,38 @@ namespace kapi::cpu } // namespace kapi::cpu +template<> +struct kstd::formatter +{ + constexpr auto parse(kstd::format_parse_context & ctx) -> decltype(ctx.begin()) + { + return ctx.begin(); + } + + constexpr auto format(enum kapi::cpu::exception::type type, kstd::format_context & ctx) const -> void + { + switch (type) + { + case kapi::cpu::exception::type::unknown: + return ctx.push("unknown"); + case kapi::cpu::exception::type::page_fault: + return ctx.push("page fault"); + case kapi::cpu::exception::type::alignment_fault: + return ctx.push("alignment fault"); + case kapi::cpu::exception::type::memory_access_fault: + return ctx.push("memory access fault"); + case kapi::cpu::exception::type::illegal_instruction: + return ctx.push("illegal instruction"); + case kapi::cpu::exception::type::privilege_violation: + return ctx.push("privilege violation"); + case kapi::cpu::exception::type::arithmetic_error: + return ctx.push("arithmetic error"); + case kapi::cpu::exception::type::breakpoint: + return ctx.push("breakpoint"); + case kapi::cpu::exception::type::single_step: + return ctx.push("single step"); + } + } +}; + #endif \ No newline at end of file diff --git a/kernel/src/cpu.cpp b/kernel/src/cpu.cpp index fc460c9..11b6551 100644 --- a/kernel/src/cpu.cpp +++ b/kernel/src/cpu.cpp @@ -14,6 +14,7 @@ namespace kernel::cpu { auto handle(kapi::cpu::exception const & context) -> bool override { + kstd::println(kstd::print_sink::stderr, "[OS:CPU] {} @ {:#018x}", context.type, context.instruction_pointer); switch (context.type) { case kapi::cpu::exception::type::page_fault: @@ -26,12 +27,10 @@ namespace kernel::cpu private: auto handle_page_fault(kapi::cpu::exception const & context) -> bool { - kstd::println(kstd::print_sink::stderr, "[OS:CPU] PAGE FAULT!"); kstd::println(kstd::print_sink::stderr, "\tFault address: {:#018x}", context.access_address); kstd::println(kstd::print_sink::stderr, "\tPresent: {}", context.is_present); kstd::println(kstd::print_sink::stderr, "\tWrite: {}", context.is_write_access); kstd::println(kstd::print_sink::stderr, "\tUser: {}", context.is_user_mode); - kstd::println(kstd::print_sink::stderr, "\tRIP: {:#018x}", context.instruction_pointer); kapi::system::panic("Halting the system due to an unrecoverable page fault."); } -- cgit v1.2.3 From a82416648d148152338dc612c25bf8dff428e773 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 16:39:13 +0100 Subject: kapi: introduce cpu::interrupt_handler --- arch/x86_64/kapi/cpu.cpp | 49 ++++++++++++++++++++++++++++++++++++ arch/x86_64/src/cpu/interrupts.cpp | 17 +++---------- kapi/include/kapi/cpu/interrupts.hpp | 44 ++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 13 deletions(-) diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index 8ca3847..b19ba21 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -4,11 +4,22 @@ #include "arch/cpu/initialization.hpp" +#include +#include + +#include #include +#include namespace kapi::cpu { + namespace + { + constexpr auto irq_offset = 32uz; + auto constinit interrupt_handlers = std::array, 256 - irq_offset>{}; + } // namespace + auto init() -> void { auto static constinit is_initialized = std::atomic_flag{}; @@ -38,4 +49,42 @@ namespace kapi::cpu asm volatile("cli"); } + auto register_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void + { + if (irq_number < irq_offset) + { + system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); + } + + interrupt_handlers[irq_number - irq_offset].push_back(&handler); + } + + auto unregister_interrupt_handler(std::uint32_t irq_number, [[maybe_unused]] interrupt_handler & handler) -> void + { + if (irq_number < irq_offset) + { + system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); + } + + kstd::println("[x86_64:CPU] TODO: support erasure from vector."); + } + + auto dispatch_interrupt(std::uint32_t irq_number) -> status + { + if (irq_number < irq_offset) + { + return status::unhandled; + } + + for (auto handler : interrupt_handlers[irq_number - irq_offset]) + { + if (handler && handler->handle_interrupt(irq_number) == status::handled) + { + return status::handled; + } + } + + return status::unhandled; + } + } // namespace kapi::cpu diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index e578aa2..1f65336 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -46,7 +46,6 @@ namespace arch::cpu constexpr auto pic_master_irq_start = 0x20; constexpr auto pic_master_irq_end = pic_master_irq_start + 8; constexpr auto pic_slave_irq_start = pic_master_irq_end; - constexpr auto pic_slave_irq_end = pic_slave_irq_start + 8; constexpr auto to_exception_type(exception e) { @@ -96,10 +95,8 @@ namespace arch::cpu } } - auto handle_legacy_interrupt(interrupt_frame * frame) -> void + auto acknowledge_pic_interrupt(interrupt_frame * frame) -> void { - kstd::println("[x86_64:SYS] Ignoring 8259 legacy interrupt {:#04x}", frame->interrupt.number); - if (frame->interrupt.number >= pic_slave_irq_start) { pic_slave_control_port::write(pic_end_of_interrupt); @@ -120,19 +117,13 @@ namespace arch::cpu { kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); - kapi::cpu::halt(); } - - if ((number >= pic_master_irq_start && number < pic_master_irq_end) || - (number >= pic_slave_irq_start && number < pic_slave_irq_end)) + else if (number >= pic_master_irq_start && kapi::cpu::dispatch_interrupt(number) == kapi::cpu::status::unhandled) { - handle_legacy_interrupt(frame); - return; + kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x}", number); } - kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x} received with code {:#04x}", - frame->interrupt.number, frame->interrupt.error_code); - kapi::cpu::halt(); + acknowledge_pic_interrupt(frame); } } diff --git a/kapi/include/kapi/cpu/interrupts.hpp b/kapi/include/kapi/cpu/interrupts.hpp index 26a215e..328e4a7 100644 --- a/kapi/include/kapi/cpu/interrupts.hpp +++ b/kapi/include/kapi/cpu/interrupts.hpp @@ -3,9 +3,32 @@ // IWYU pragma: private, include "kapi/cpu.hpp" +#include + namespace kapi::cpu { + enum class status : bool + { + unhandled, + handled, + }; + + //! The interface for all interrupt handlers. + struct interrupt_handler + { + virtual ~interrupt_handler() = default; + + //! Handle an interrupt with the given IRQ number. + // + //! This function will be called by the kernel in an interrupt context. As such, the function should complete its + //! task quickly and must take care when acquiring globally shared locks. + //! + //! @param irq_number The identifier of the interrupt request that triggered the handler. + //! @return status::handled if the handler successfully handled the interrupt, status::unhandled otherwise. + virtual auto handle_interrupt(std::uint32_t irq_number) -> status = 0; + }; + //! @qualifier platform-defined //! Enable external interrupts. auto enable_interrupts() -> void; @@ -14,6 +37,27 @@ namespace kapi::cpu //! Disable external interrupts. auto disable_interrupts() -> void; + //! @qualifier platform-defined + //! Register an interrupt handler for the given IRQ number. + //! + //! @param irq_number The IRQ number to register the handler for. + //! @param handler The interrupt handler to register. + auto register_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void; + + //! @qualifier platform-defined + //! Unregister a previously registered interrupt handler. + //! + //! @param irq_number The IRQ number to unregister the handler for. + //! @param handler The interrupt handler to unregister. + auto unregister_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void; + + //! @qualifier platform-defined + //! Dispatch an interrupt to all registered handlers. + //! + //! @param irq_number The IRQ number to dispatch. + //! @return status::handled if the interrupt was handled by at least one handler, status::unhandled otherwise. + auto dispatch_interrupt(std::uint32_t irq_number) -> status; + } // namespace kapi::cpu #endif \ No newline at end of file -- cgit v1.2.3 From 953768ed7af8692818f742566864bfd264a824a2 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 18:37:55 +0100 Subject: x86_64/cpu: fix interrupt gate types --- arch/x86_64/src/cpu/interrupts.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 1f65336..1f12898 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -135,7 +135,7 @@ namespace arch::cpu .offset_low = static_cast(isr_stub_table[i] & 0xffff), // NOLINT(readability-magic-numbers) .m_code_segment = segment_selector{0, false, 1}, .interrupt_stack_table_selector = 0, - .gate_type = gate_type::interrupt_gate, + .gate_type = (i < 32 && i != 2) ? gate_type::trap_gate : gate_type::interrupt_gate, .descriptor_privilege_level = 0, .present = true, .offset_middle = -- cgit v1.2.3 From 6a1addc7663bfae3306abb8800d3e387b3f66e82 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 25 Mar 2026 18:46:20 +0100 Subject: x86_64/cpu: improve exception logging --- arch/x86_64/src/cpu/interrupts.cpp | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 1f12898..4e52d71 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -43,6 +43,8 @@ namespace arch::cpu security_exception, }; + constexpr auto number_of_exception_vectors = 32u; + constexpr auto pic_master_irq_start = 0x20; constexpr auto pic_master_irq_end = pic_master_irq_start + 8; constexpr auto pic_slave_irq_start = pic_master_irq_end; @@ -72,6 +74,25 @@ namespace arch::cpu } } + constexpr auto has_error_code(exception e) + { + switch (e) + { + case exception::double_fault: + case exception::invalid_tss: + case exception::segment_not_present: + case exception::stack_segment_fault: + case exception::general_protection_fault: + case exception::page_fault: + case exception::alignment_check: + case exception::control_protection_exception: + case exception::security_exception: + return true; + default: + return false; + } + } + auto dispatch_exception(interrupt_frame * frame) -> bool { auto type = to_exception_type(static_cast(frame->interrupt.number)); @@ -113,12 +134,20 @@ namespace arch::cpu { auto [number, code] = frame->interrupt; - if (number < pic_master_irq_start && !dispatch_exception(frame)) + if (number < number_of_exception_vectors && !dispatch_exception(frame)) { - kstd::println(kstd::print_sink::stderr, - "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); + if (has_error_code(static_cast(number))) + { + kstd::println(kstd::print_sink::stderr, + "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); + } + else + { + kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled exception number {:#04x} received", number); + } } - else if (number >= pic_master_irq_start && kapi::cpu::dispatch_interrupt(number) == kapi::cpu::status::unhandled) + else if (number >= number_of_exception_vectors && + kapi::cpu::dispatch_interrupt(number) == kapi::cpu::status::unhandled) { kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x}", number); } -- cgit v1.2.3 From 9e5788cc6bbc6291de6a6f542e4f2754e29dd75a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 14:30:05 +0100 Subject: ci: use preconfigured image --- .gitlab-ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 05bba19..de9303d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,10 +6,7 @@ build: stage: build - image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-4 - before_script: - - apt update - - apt install -y clang-tidy cmake grub2-common grub-pc mtools ninja-build xorriso + image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64.ci:latest script: - cmake --preset $PLATFORM - cmake --build --preset $PLATFORM-$TYPE -- cgit v1.2.3 From a8cc3a2ad00c7dbb756bf6bc4f02399c551bab07 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 14:35:37 +0100 Subject: ci: set artifact expiry time --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index de9303d..7abc89e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,6 +17,7 @@ build: - kernel.elf - kernel.sym - kernel.iso + expire_in: 1 week <<: *build_matrix bht: -- cgit v1.2.3 From 997f462b540cca32fd55aa7f40c6d4bfef79e367 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 14:39:14 +0100 Subject: ci: use preconfigured image for bht builds --- .gitlab-ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7abc89e..35af269 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,10 +22,7 @@ build: bht: stage: build - image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-4 - before_script: - - apt update - - apt install -y build-essential cmake ninja-build lcov libcatch2-dev + image: registry.gitlab.ost.ch:45023/teachos/devcontainers/bht.ci:latest script: - cmake --preset bht - cmake --build --preset bht-dbg -- cgit v1.2.3 From 465d323cc89dc67d793148728d222621eea335c8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 15:03:52 +0100 Subject: ci: fix coverage regex --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 35af269..b4296b6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,7 +29,7 @@ bht: - ctest --preset bht-dbg - lcov --config-file .lcovrc --capture --directory $(pwd) --output-file coverage.info - lcov --config-file .lcovrc --list coverage.info - coverage: '/Total:\|\s*(\d+(\.\d+)?)\%/' + coverage: '/Total:\|\s*(\d+(?:\.\d+)?)\%/' artifacts: paths: - coverage.info -- cgit v1.2.3 From 1cb599798a0b29302ab71d1ee0fb9febff8f6a75 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 15:24:10 +0100 Subject: kapi/cpu: update documentation of init() --- kapi/include/kapi/cpu.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index ade954c..712de16 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -14,6 +14,9 @@ namespace kapi::cpu //! @qualifier platform-defined //! Perform early CPU initialization. + //! + //! When this function returns, the CPU is in a state where interrupt could be enabled. This function must not enable + //! interrupts itself. auto init() -> void; } // namespace kapi::cpu -- cgit v1.2.3 From d56700342ea0266a6e49f9515eb83279f66b4fcf Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 15:28:41 +0100 Subject: x86_64: split kapi::cpu implementation --- arch/x86_64/CMakeLists.txt | 1 + arch/x86_64/kapi/cpu.cpp | 59 -------------------------------- arch/x86_64/kapi/cpu/interrupts.cpp | 67 +++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 59 deletions(-) create mode 100644 arch/x86_64/kapi/cpu/interrupts.cpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 89d9bc0..8ff81d8 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -15,6 +15,7 @@ target_sources("x86_64" PRIVATE "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" + "kapi/cpu/interrupts.cpp" "kapi/memory.cpp" "kapi/system.cpp" diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index b19ba21..12edb0f 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -4,22 +4,11 @@ #include "arch/cpu/initialization.hpp" -#include -#include - -#include #include -#include namespace kapi::cpu { - namespace - { - constexpr auto irq_offset = 32uz; - auto constinit interrupt_handlers = std::array, 256 - irq_offset>{}; - } // namespace - auto init() -> void { auto static constinit is_initialized = std::atomic_flag{}; @@ -39,52 +28,4 @@ namespace kapi::cpu __builtin_unreachable(); } - auto enable_interrupts() -> void - { - asm volatile("sti"); - } - - auto disable_interrupts() -> void - { - asm volatile("cli"); - } - - auto register_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void - { - if (irq_number < irq_offset) - { - system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); - } - - interrupt_handlers[irq_number - irq_offset].push_back(&handler); - } - - auto unregister_interrupt_handler(std::uint32_t irq_number, [[maybe_unused]] interrupt_handler & handler) -> void - { - if (irq_number < irq_offset) - { - system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); - } - - kstd::println("[x86_64:CPU] TODO: support erasure from vector."); - } - - auto dispatch_interrupt(std::uint32_t irq_number) -> status - { - if (irq_number < irq_offset) - { - return status::unhandled; - } - - for (auto handler : interrupt_handlers[irq_number - irq_offset]) - { - if (handler && handler->handle_interrupt(irq_number) == status::handled) - { - return status::handled; - } - } - - return status::unhandled; - } - } // namespace kapi::cpu diff --git a/arch/x86_64/kapi/cpu/interrupts.cpp b/arch/x86_64/kapi/cpu/interrupts.cpp new file mode 100644 index 0000000..b98595c --- /dev/null +++ b/arch/x86_64/kapi/cpu/interrupts.cpp @@ -0,0 +1,67 @@ +#include "kapi/cpu.hpp" +#include "kapi/system.hpp" + +#include +#include + +#include +#include + +namespace kapi::cpu +{ + + namespace + { + constexpr auto irq_offset = 32uz; + auto constinit interrupt_handlers = std::array, 256 - irq_offset>{}; + } // namespace + + auto enable_interrupts() -> void + { + asm volatile("sti"); + } + + auto disable_interrupts() -> void + { + asm volatile("cli"); + } + + auto register_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void + { + if (irq_number < irq_offset) + { + system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); + } + + interrupt_handlers[irq_number - irq_offset].push_back(&handler); + } + + auto unregister_interrupt_handler(std::uint32_t irq_number, [[maybe_unused]] interrupt_handler & handler) -> void + { + if (irq_number < irq_offset) + { + system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); + } + + kstd::println("[x86_64:CPU] TODO: support erasure from vector."); + } + + auto dispatch_interrupt(std::uint32_t irq_number) -> status + { + if (irq_number < irq_offset) + { + return status::unhandled; + } + + for (auto handler : interrupt_handlers[irq_number - irq_offset]) + { + if (handler && handler->handle_interrupt(irq_number) == status::handled) + { + return status::handled; + } + } + + return status::unhandled; + } + +} // namespace kapi::cpu \ No newline at end of file -- cgit v1.2.3 From 8d06763f47e7b7c93af2a55f6bd2dbc4aa9abfa2 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 16:10:50 +0100 Subject: kapi/cpu: simplify exception handling --- arch/x86_64/src/cpu/interrupts.cpp | 5 ++--- kapi/include/kapi/cpu/exception.hpp | 27 +++++----------------- kernel/CMakeLists.txt | 1 - kernel/include/kernel/cpu.hpp | 14 ------------ kernel/kapi/cpu.cpp | 35 ++++++++++++++++------------- kernel/src/cpu.cpp | 45 ------------------------------------- kernel/src/main.cpp | 2 -- 7 files changed, 28 insertions(+), 101 deletions(-) delete mode 100644 kernel/include/kernel/cpu.hpp delete mode 100644 kernel/src/cpu.cpp diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 4e52d71..9ee3ce8 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -108,11 +108,10 @@ namespace arch::cpu auto write = (frame->interrupt.error_code & 0x2) != 0; auto user = (frame->interrupt.error_code & 0x4) != 0; - return kapi::cpu::get_exception_handler().handle( - {type, instruction_pointer, fault_address, present, write, user}); + return kapi::cpu::dispatch({type, instruction_pointer, fault_address, present, write, user}); } default: - return kapi::cpu::get_exception_handler().handle({type, instruction_pointer}); + return kapi::cpu::dispatch({type, instruction_pointer}); } } diff --git a/kapi/include/kapi/cpu/exception.hpp b/kapi/include/kapi/cpu/exception.hpp index 00b02e7..d6e8511 100644 --- a/kapi/include/kapi/cpu/exception.hpp +++ b/kapi/include/kapi/cpu/exception.hpp @@ -31,7 +31,7 @@ namespace kapi::cpu memory_access_fault, //! An invalid instruction was present in the instruction stream. illegal_instruction, - //! The precoditions for the execution of an instruction were not met. + //! The preconditions for the execution of an instruction were not met. privilege_violation, //! An arithmetic error occurred. arithmetic_error, @@ -60,27 +60,12 @@ namespace kapi::cpu bool is_user_mode{}; }; - //! The abstract interface for exception handlers. - //! - //! The kernel must define an exception handler to be used during execution. - struct exception_handler - { - virtual ~exception_handler() = default; - - //! Handle an exception. - //! - //! @param context The exception context. - //! @return Whether the exception was handled. - virtual auto handle(exception const & context) -> bool = 0; - }; - - //! @qualifier kernel-defined - //! Get the currently active exception handler. - auto get_exception_handler() -> exception_handler &; - //! @qualifier kernel-defined - //! Set the exception handler. - auto set_exception_handler(exception_handler & handler) -> void; + //! Dispatch an exception to the appropriate handler. + //! + //! @param context The exception context. + //! @return Whether the exception was handled. + auto dispatch(exception const & context) -> bool; } // namespace kapi::cpu diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index d9a5c75..9b1e2ad 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -11,7 +11,6 @@ add_executable("kernel" "kstd/print.cpp" # Kernel Implementation - "src/cpu.cpp" "src/main.cpp" "src/memory/bitmap_allocator.cpp" "src/memory/block_list_allocator.cpp" diff --git a/kernel/include/kernel/cpu.hpp b/kernel/include/kernel/cpu.hpp deleted file mode 100644 index d4e1ced..0000000 --- a/kernel/include/kernel/cpu.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef TEACHOS_KERNEL_CPU_HPP -#define TEACHOS_KERNEL_CPU_HPP - -namespace kernel::cpu -{ - - //! Initialize the kernel heap. - auto init() -> void; - - //! - -} // namespace kernel::cpu - -#endif \ No newline at end of file diff --git a/kernel/kapi/cpu.cpp b/kernel/kapi/cpu.cpp index 2089098..13de584 100644 --- a/kernel/kapi/cpu.cpp +++ b/kernel/kapi/cpu.cpp @@ -1,30 +1,35 @@ #include "kapi/cpu.hpp" +#include "kapi/system.hpp" + +#include + namespace kapi::cpu { namespace { - struct null_exception_handler : exception_handler + auto handle_page_fault(kapi::cpu::exception const & context) -> bool { - auto handle(exception const &) -> bool override - { - return false; - } - } static constinit default_exception_handler; - - exception_handler * current_handler = &default_exception_handler; + kstd::println(kstd::print_sink::stderr, "\tFault address: {:#018x}", context.access_address); + kstd::println(kstd::print_sink::stderr, "\tPresent: {}", context.is_present); + kstd::println(kstd::print_sink::stderr, "\tWrite: {}", context.is_write_access); + kstd::println(kstd::print_sink::stderr, "\tUser: {}", context.is_user_mode); + kapi::system::panic("Halting the system due to an unrecoverable page fault."); + } } // namespace - auto get_exception_handler() -> exception_handler & + auto dispatch(exception const & context) -> bool { - return *current_handler; - } - - auto set_exception_handler(exception_handler & handler) -> void - { - current_handler = &handler; + kstd::println(kstd::print_sink::stderr, "[OS:CPU] {} @ {:#018x}", context.type, context.instruction_pointer); + switch (context.type) + { + case kapi::cpu::exception::type::page_fault: + return handle_page_fault(context); + default: + return false; + } } } // namespace kapi::cpu \ No newline at end of file diff --git a/kernel/src/cpu.cpp b/kernel/src/cpu.cpp deleted file mode 100644 index 11b6551..0000000 --- a/kernel/src/cpu.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "kernel/cpu.hpp" - -#include "kapi/cpu.hpp" -#include "kapi/system.hpp" - -#include - -namespace kernel::cpu -{ - - namespace - { - struct exception_handler : kapi::cpu::exception_handler - { - auto handle(kapi::cpu::exception const & context) -> bool override - { - kstd::println(kstd::print_sink::stderr, "[OS:CPU] {} @ {:#018x}", context.type, context.instruction_pointer); - switch (context.type) - { - case kapi::cpu::exception::type::page_fault: - return handle_page_fault(context); - default: - return false; - } - } - - private: - auto handle_page_fault(kapi::cpu::exception const & context) -> bool - { - kstd::println(kstd::print_sink::stderr, "\tFault address: {:#018x}", context.access_address); - kstd::println(kstd::print_sink::stderr, "\tPresent: {}", context.is_present); - kstd::println(kstd::print_sink::stderr, "\tWrite: {}", context.is_write_access); - kstd::println(kstd::print_sink::stderr, "\tUser: {}", context.is_user_mode); - - kapi::system::panic("Halting the system due to an unrecoverable page fault."); - } - } static constinit handler; - } // namespace - - auto init() -> void - { - kapi::cpu::set_exception_handler(handler); - } - -} // namespace kernel::cpu \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 6bd168c..45a4aae 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -4,7 +4,6 @@ #include "kapi/memory.hpp" #include "kapi/system.hpp" -#include "kernel/cpu.hpp" #include "kernel/devices/storage/storage_management.hpp" #include "kernel/filesystem/device_file.hpp" #include "kernel/filesystem/file_descriptor_table.hpp" @@ -93,7 +92,6 @@ auto main() -> int kstd::println("[OS] IO subsystem initialized."); kapi::cpu::init(); - kernel::cpu::init(); kapi::cpu::enable_interrupts(); kapi::memory::init(); -- cgit v1.2.3 From 2521d58e7a5c16595e401e1af7becb572ad35f53 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 16:25:35 +0100 Subject: kapi: dissolve cpu/exception.hpp into cpu.hpp --- kapi/include/kapi/cpu.hpp | 96 +++++++++++++++++++++++++++++++- kapi/include/kapi/cpu/exception.hpp | 106 ------------------------------------ 2 files changed, 95 insertions(+), 107 deletions(-) delete mode 100644 kapi/include/kapi/cpu/exception.hpp diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index 712de16..041a5db 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -1,11 +1,64 @@ #ifndef TEACHOS_KAPI_CPU_HPP #define TEACHOS_KAPI_CPU_HPP -#include "kapi/cpu/exception.hpp" // IWYU pragma: export #include "kapi/cpu/interrupts.hpp" // IWYU pragma: export +#include "kapi/memory.hpp" + +#include + +#include namespace kapi::cpu { + + //! An exception originating from the CPU directly. + //! + //! Exception generally model interrupts that are synchronous to the instruction stream. This means that they do not + //! originate from external hardware, but rather are a product of program execution. + struct exception + { + //! The type of the exception, which identifies the reason for it being raised. + enum class type : std::uint8_t + { + //! The reason for the exception is unknown or platform-specific + unknown, + //! A page fault occurred + page_fault, + //! A memory access (either in the data or instruction stream) violated it's alignment constraints. + alignment_fault, + //! A memory access (either in the data or instruction stream) violated it's permissions. + memory_access_fault, + //! An invalid instruction was present in the instruction stream. + illegal_instruction, + //! The preconditions for the execution of an instruction were not met. + privilege_violation, + //! An arithmetic error occurred. + arithmetic_error, + //! A breakpoint was hit in the instruction stream. + breakpoint, + //! The CPU is single-stepping through the instruction stream. + single_step, + }; + + //! The type of this exception. + type type{}; + + //! The value of the instruction pointer at the time this exception was raised. + kapi::memory::linear_address instruction_pointer{}; + + //! The memory address that caused this exception. + kapi::memory::linear_address access_address{}; + + //! Whether the page was present at the time of the exception. + bool is_present{}; + + //! Whether the exception was caused by a write access. + bool is_write_access{}; + + //! Whether the exception was caused by a user mode access. + bool is_user_mode{}; + }; + //! @qualifier platform-defined //! Halt the CPU. //! @@ -19,6 +72,47 @@ namespace kapi::cpu //! interrupts itself. auto init() -> void; + //! @qualifier kernel-defined + //! Dispatch an exception to the appropriate handler. + //! + //! @param context The exception context. + //! @return Whether the exception was handled. + [[nodiscard]] auto dispatch(exception const & context) -> bool; + } // namespace kapi::cpu +template<> +struct kstd::formatter +{ + constexpr auto parse(kstd::format_parse_context & ctx) -> decltype(ctx.begin()) + { + return ctx.begin(); + } + + constexpr auto format(enum kapi::cpu::exception::type type, kstd::format_context & ctx) const -> void + { + switch (type) + { + case kapi::cpu::exception::type::unknown: + return ctx.push("unknown"); + case kapi::cpu::exception::type::page_fault: + return ctx.push("page fault"); + case kapi::cpu::exception::type::alignment_fault: + return ctx.push("alignment fault"); + case kapi::cpu::exception::type::memory_access_fault: + return ctx.push("memory access fault"); + case kapi::cpu::exception::type::illegal_instruction: + return ctx.push("illegal instruction"); + case kapi::cpu::exception::type::privilege_violation: + return ctx.push("privilege violation"); + case kapi::cpu::exception::type::arithmetic_error: + return ctx.push("arithmetic error"); + case kapi::cpu::exception::type::breakpoint: + return ctx.push("breakpoint"); + case kapi::cpu::exception::type::single_step: + return ctx.push("single step"); + } + } +}; + #endif diff --git a/kapi/include/kapi/cpu/exception.hpp b/kapi/include/kapi/cpu/exception.hpp deleted file mode 100644 index d6e8511..0000000 --- a/kapi/include/kapi/cpu/exception.hpp +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef TEACHOS_KAPI_CPU_EXCEPTION_HPP -#define TEACHOS_KAPI_CPU_EXCEPTION_HPP - -// IWYU pragma: private, include "kapi/cpu.hpp" - -#include "kapi/memory.hpp" - -#include - -#include - -namespace kapi::cpu -{ - - //! An exception originating from the CPU directly. - //! - //! Exception generally model interrupts that are synchronous to the instruction stream. This means that they do not - //! originate from external hardware, but rather are a product of program execution. - struct exception - { - //! The type of the exception, which identifies the reason for it being raised. - enum class type : std::uint8_t - { - //! The reason for the exception is unknown or platform-specific - unknown, - //! A page fault occurred - page_fault, - //! A memory access (either in the data or instruction stream) violated it's alignment constraints. - alignment_fault, - //! A memory access (either in the data or instruction stream) violated it's permissions. - memory_access_fault, - //! An invalid instruction was present in the instruction stream. - illegal_instruction, - //! The preconditions for the execution of an instruction were not met. - privilege_violation, - //! An arithmetic error occurred. - arithmetic_error, - //! A breakpoint was hit in the instruction stream. - breakpoint, - //! The CPU is single-stepping through the instruction stream. - single_step, - }; - - //! The type of this exception. - type type{}; - - //! The value of the instruction pointer at the time this exception was raised. - kapi::memory::linear_address instruction_pointer{}; - - //! The memory address that caused this exception. - kapi::memory::linear_address access_address{}; - - //! Whether the page was present at the time of the exception. - bool is_present{}; - - //! Whether the exception was caused by a write access. - bool is_write_access{}; - - //! Whether the exception was caused by a user mode access. - bool is_user_mode{}; - }; - - //! @qualifier kernel-defined - //! Dispatch an exception to the appropriate handler. - //! - //! @param context The exception context. - //! @return Whether the exception was handled. - auto dispatch(exception const & context) -> bool; - -} // namespace kapi::cpu - -template<> -struct kstd::formatter -{ - constexpr auto parse(kstd::format_parse_context & ctx) -> decltype(ctx.begin()) - { - return ctx.begin(); - } - - constexpr auto format(enum kapi::cpu::exception::type type, kstd::format_context & ctx) const -> void - { - switch (type) - { - case kapi::cpu::exception::type::unknown: - return ctx.push("unknown"); - case kapi::cpu::exception::type::page_fault: - return ctx.push("page fault"); - case kapi::cpu::exception::type::alignment_fault: - return ctx.push("alignment fault"); - case kapi::cpu::exception::type::memory_access_fault: - return ctx.push("memory access fault"); - case kapi::cpu::exception::type::illegal_instruction: - return ctx.push("illegal instruction"); - case kapi::cpu::exception::type::privilege_violation: - return ctx.push("privilege violation"); - case kapi::cpu::exception::type::arithmetic_error: - return ctx.push("arithmetic error"); - case kapi::cpu::exception::type::breakpoint: - return ctx.push("breakpoint"); - case kapi::cpu::exception::type::single_step: - return ctx.push("single step"); - } - } -}; - -#endif \ No newline at end of file -- cgit v1.2.3 From 00a77644192642e06462c11479a5c0e9bd859e9a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 16:35:32 +0100 Subject: kapi: extract interrupts API --- arch/x86_64/CMakeLists.txt | 2 +- arch/x86_64/kapi/cpu/interrupts.cpp | 67 ----------------------------------- arch/x86_64/kapi/interrupts.cpp | 68 ++++++++++++++++++++++++++++++++++++ arch/x86_64/src/cpu/interrupts.cpp | 3 +- kapi/CMakeLists.txt | 3 +- kapi/include/kapi/cpu.hpp | 1 - kapi/include/kapi/cpu/interrupts.hpp | 63 --------------------------------- kapi/include/kapi/interrupts.hpp | 61 ++++++++++++++++++++++++++++++++ kernel/src/main.cpp | 3 +- 9 files changed, 135 insertions(+), 136 deletions(-) delete mode 100644 arch/x86_64/kapi/cpu/interrupts.cpp create mode 100644 arch/x86_64/kapi/interrupts.cpp delete mode 100644 kapi/include/kapi/cpu/interrupts.hpp create mode 100644 kapi/include/kapi/interrupts.hpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 8ff81d8..4427e4c 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -15,7 +15,7 @@ target_sources("x86_64" PRIVATE "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" - "kapi/cpu/interrupts.cpp" + "kapi/interrupts.cpp" "kapi/memory.cpp" "kapi/system.cpp" diff --git a/arch/x86_64/kapi/cpu/interrupts.cpp b/arch/x86_64/kapi/cpu/interrupts.cpp deleted file mode 100644 index b98595c..0000000 --- a/arch/x86_64/kapi/cpu/interrupts.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "kapi/cpu.hpp" -#include "kapi/system.hpp" - -#include -#include - -#include -#include - -namespace kapi::cpu -{ - - namespace - { - constexpr auto irq_offset = 32uz; - auto constinit interrupt_handlers = std::array, 256 - irq_offset>{}; - } // namespace - - auto enable_interrupts() -> void - { - asm volatile("sti"); - } - - auto disable_interrupts() -> void - { - asm volatile("cli"); - } - - auto register_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void - { - if (irq_number < irq_offset) - { - system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); - } - - interrupt_handlers[irq_number - irq_offset].push_back(&handler); - } - - auto unregister_interrupt_handler(std::uint32_t irq_number, [[maybe_unused]] interrupt_handler & handler) -> void - { - if (irq_number < irq_offset) - { - system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); - } - - kstd::println("[x86_64:CPU] TODO: support erasure from vector."); - } - - auto dispatch_interrupt(std::uint32_t irq_number) -> status - { - if (irq_number < irq_offset) - { - return status::unhandled; - } - - for (auto handler : interrupt_handlers[irq_number - irq_offset]) - { - if (handler && handler->handle_interrupt(irq_number) == status::handled) - { - return status::handled; - } - } - - return status::unhandled; - } - -} // namespace kapi::cpu \ No newline at end of file diff --git a/arch/x86_64/kapi/interrupts.cpp b/arch/x86_64/kapi/interrupts.cpp new file mode 100644 index 0000000..babc926 --- /dev/null +++ b/arch/x86_64/kapi/interrupts.cpp @@ -0,0 +1,68 @@ +#include "kapi/interrupts.hpp" + +#include "kapi/system.hpp" + +#include +#include + +#include +#include + +namespace kapi::interrupts +{ + + namespace + { + constexpr auto irq_offset = 32uz; + auto constinit handlers = std::array, 256 - irq_offset>{}; + } // namespace + + auto enable() -> void + { + asm volatile("sti"); + } + + auto disable() -> void + { + asm volatile("cli"); + } + + auto register_handler(std::uint32_t irq_number, handler & handler) -> void + { + if (irq_number < irq_offset) + { + system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); + } + + handlers[irq_number - irq_offset].push_back(&handler); + } + + auto unregister_handler(std::uint32_t irq_number, [[maybe_unused]] handler & handler) -> void + { + if (irq_number < irq_offset) + { + system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); + } + + kstd::println("[x86_64:CPU] TODO: support erasure from vector."); + } + + auto dispatch(std::uint32_t irq_number) -> status + { + if (irq_number < irq_offset) + { + return status::unhandled; + } + + for (auto handler : handlers[irq_number - irq_offset]) + { + if (handler && handler->handle_interrupt(irq_number) == status::handled) + { + return status::handled; + } + } + + return status::unhandled; + } + +} // namespace kapi::interrupts \ No newline at end of file diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 9ee3ce8..2f23f07 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -1,6 +1,7 @@ #include "arch/cpu/interrupts.hpp" #include "kapi/cpu.hpp" +#include "kapi/interrupts.hpp" #include "kapi/memory.hpp" #include "arch/cpu/legacy_pic.hpp" @@ -146,7 +147,7 @@ namespace arch::cpu } } else if (number >= number_of_exception_vectors && - kapi::cpu::dispatch_interrupt(number) == kapi::cpu::status::unhandled) + kapi::interrupts::dispatch(number) == kapi::interrupts::status::unhandled) { kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x}", number); } diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index 4c94829..99b737c 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -9,8 +9,7 @@ target_sources("kapi" PUBLIC "include/kapi/boot.hpp" "include/kapi/cio.hpp" "include/kapi/cpu.hpp" - "include/kapi/cpu/interrupts.hpp" - "include/kapi/cpu/exception.hpp" + "include/kapi/interrupts.hpp" "include/kapi/boot_module/boot_module.hpp" "include/kapi/boot_module/boot_module_registry.hpp" "include/kapi/memory.hpp" diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index 041a5db..c6aa6ff 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -1,7 +1,6 @@ #ifndef TEACHOS_KAPI_CPU_HPP #define TEACHOS_KAPI_CPU_HPP -#include "kapi/cpu/interrupts.hpp" // IWYU pragma: export #include "kapi/memory.hpp" #include diff --git a/kapi/include/kapi/cpu/interrupts.hpp b/kapi/include/kapi/cpu/interrupts.hpp deleted file mode 100644 index 328e4a7..0000000 --- a/kapi/include/kapi/cpu/interrupts.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef TEACHOS_KAPI_CPU_INTERRUPTS_HPP -#define TEACHOS_KAPI_CPU_INTERRUPTS_HPP - -// IWYU pragma: private, include "kapi/cpu.hpp" - -#include - -namespace kapi::cpu -{ - - enum class status : bool - { - unhandled, - handled, - }; - - //! The interface for all interrupt handlers. - struct interrupt_handler - { - virtual ~interrupt_handler() = default; - - //! Handle an interrupt with the given IRQ number. - // - //! This function will be called by the kernel in an interrupt context. As such, the function should complete its - //! task quickly and must take care when acquiring globally shared locks. - //! - //! @param irq_number The identifier of the interrupt request that triggered the handler. - //! @return status::handled if the handler successfully handled the interrupt, status::unhandled otherwise. - virtual auto handle_interrupt(std::uint32_t irq_number) -> status = 0; - }; - - //! @qualifier platform-defined - //! Enable external interrupts. - auto enable_interrupts() -> void; - - //! @qualifier platform-defined - //! Disable external interrupts. - auto disable_interrupts() -> void; - - //! @qualifier platform-defined - //! Register an interrupt handler for the given IRQ number. - //! - //! @param irq_number The IRQ number to register the handler for. - //! @param handler The interrupt handler to register. - auto register_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void; - - //! @qualifier platform-defined - //! Unregister a previously registered interrupt handler. - //! - //! @param irq_number The IRQ number to unregister the handler for. - //! @param handler The interrupt handler to unregister. - auto unregister_interrupt_handler(std::uint32_t irq_number, interrupt_handler & handler) -> void; - - //! @qualifier platform-defined - //! Dispatch an interrupt to all registered handlers. - //! - //! @param irq_number The IRQ number to dispatch. - //! @return status::handled if the interrupt was handled by at least one handler, status::unhandled otherwise. - auto dispatch_interrupt(std::uint32_t irq_number) -> status; - -} // namespace kapi::cpu - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/interrupts.hpp b/kapi/include/kapi/interrupts.hpp new file mode 100644 index 0000000..fa4bc95 --- /dev/null +++ b/kapi/include/kapi/interrupts.hpp @@ -0,0 +1,61 @@ +#ifndef TEACHOS_KAPI_INTERRUPTS_HPP +#define TEACHOS_KAPI_INTERRUPTS_HPP + +#include + +namespace kapi::interrupts +{ + + enum class status : bool + { + unhandled, + handled, + }; + + //! The interface for all interrupt handlers. + struct handler + { + virtual ~handler() = default; + + //! Handle an interrupt with the given IRQ number. + // + //! This function will be called by the kernel in an interrupt context. As such, the function should complete its + //! task quickly and must take care when acquiring globally shared locks. + //! + //! @param irq_number The identifier of the interrupt request that triggered the handler. + //! @return status::handled if the handler successfully handled the interrupt, status::unhandled otherwise. + virtual auto handle_interrupt(std::uint32_t irq_number) -> status = 0; + }; + + //! @qualifier platform-defined + //! Enable external interrupts. + auto enable() -> void; + + //! @qualifier platform-defined + //! Disable external interrupts. + auto disable() -> void; + + //! @qualifier platform-defined + //! Register an interrupt handler for the given IRQ number. + //! + //! @param irq_number The IRQ number to register the handler for. + //! @param handler The interrupt handler to register. + auto register_handler(std::uint32_t irq_number, handler & handler) -> void; + + //! @qualifier platform-defined + //! Unregister a previously registered interrupt handler. + //! + //! @param irq_number The IRQ number to unregister the handler for. + //! @param handler The interrupt handler to unregister. + auto unregister_handler(std::uint32_t irq_number, handler & handler) -> void; + + //! @qualifier platform-defined + //! Dispatch an interrupt to all registered handlers. + //! + //! @param irq_number The IRQ number to dispatch. + //! @return status::handled if the interrupt was handled by at least one handler, status::unhandled otherwise. + auto dispatch(std::uint32_t irq_number) -> status; + +} // namespace kapi::interrupts + +#endif \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 45a4aae..6434045 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,6 +1,7 @@ #include "kapi/boot_modules.hpp" #include "kapi/cio.hpp" #include "kapi/cpu.hpp" +#include "kapi/interrupts.hpp" #include "kapi/memory.hpp" #include "kapi/system.hpp" @@ -92,7 +93,7 @@ auto main() -> int kstd::println("[OS] IO subsystem initialized."); kapi::cpu::init(); - kapi::cpu::enable_interrupts(); + kapi::interrupts::enable(); kapi::memory::init(); kernel::memory::init_heap(kapi::memory::heap_base); -- cgit v1.2.3 From f4dc64976049761a6f56dd55d9d0b651f1e9475f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 16:47:41 +0100 Subject: kapi: move interrupt handling to kernel --- arch/x86_64/kapi/interrupts.cpp | 52 ---------------------------------------- kapi/include/kapi/interrupts.hpp | 6 ++--- kernel/CMakeLists.txt | 1 + kernel/kapi/interrupts.cpp | 45 ++++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+), 55 deletions(-) create mode 100644 kernel/kapi/interrupts.cpp diff --git a/arch/x86_64/kapi/interrupts.cpp b/arch/x86_64/kapi/interrupts.cpp index babc926..cf1defa 100644 --- a/arch/x86_64/kapi/interrupts.cpp +++ b/arch/x86_64/kapi/interrupts.cpp @@ -1,22 +1,8 @@ #include "kapi/interrupts.hpp" -#include "kapi/system.hpp" - -#include -#include - -#include -#include - namespace kapi::interrupts { - namespace - { - constexpr auto irq_offset = 32uz; - auto constinit handlers = std::array, 256 - irq_offset>{}; - } // namespace - auto enable() -> void { asm volatile("sti"); @@ -27,42 +13,4 @@ namespace kapi::interrupts asm volatile("cli"); } - auto register_handler(std::uint32_t irq_number, handler & handler) -> void - { - if (irq_number < irq_offset) - { - system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); - } - - handlers[irq_number - irq_offset].push_back(&handler); - } - - auto unregister_handler(std::uint32_t irq_number, [[maybe_unused]] handler & handler) -> void - { - if (irq_number < irq_offset) - { - system::panic("[x86_64:CPU] IRQ number must be in range [32, 255]."); - } - - kstd::println("[x86_64:CPU] TODO: support erasure from vector."); - } - - auto dispatch(std::uint32_t irq_number) -> status - { - if (irq_number < irq_offset) - { - return status::unhandled; - } - - for (auto handler : handlers[irq_number - irq_offset]) - { - if (handler && handler->handle_interrupt(irq_number) == status::handled) - { - return status::handled; - } - } - - return status::unhandled; - } - } // namespace kapi::interrupts \ No newline at end of file diff --git a/kapi/include/kapi/interrupts.hpp b/kapi/include/kapi/interrupts.hpp index fa4bc95..f72ef8c 100644 --- a/kapi/include/kapi/interrupts.hpp +++ b/kapi/include/kapi/interrupts.hpp @@ -35,21 +35,21 @@ namespace kapi::interrupts //! Disable external interrupts. auto disable() -> void; - //! @qualifier platform-defined + //! @qualifier kernel-defined //! Register an interrupt handler for the given IRQ number. //! //! @param irq_number The IRQ number to register the handler for. //! @param handler The interrupt handler to register. auto register_handler(std::uint32_t irq_number, handler & handler) -> void; - //! @qualifier platform-defined + //! @qualifier kernel-defined //! Unregister a previously registered interrupt handler. //! //! @param irq_number The IRQ number to unregister the handler for. //! @param handler The interrupt handler to unregister. auto unregister_handler(std::uint32_t irq_number, handler & handler) -> void; - //! @qualifier platform-defined + //! @qualifier kernel-defined //! Dispatch an interrupt to all registered handlers. //! //! @param irq_number The IRQ number to dispatch. diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 9b1e2ad..535c441 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -3,6 +3,7 @@ add_executable("kernel" "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" + "kapi/interrupts.cpp" "kapi/memory.cpp" "kapi/system.cpp" diff --git a/kernel/kapi/interrupts.cpp b/kernel/kapi/interrupts.cpp new file mode 100644 index 0000000..e79453a --- /dev/null +++ b/kernel/kapi/interrupts.cpp @@ -0,0 +1,45 @@ +#include "kapi/interrupts.hpp" + +#include +#include + +#include +#include + +namespace kapi::interrupts +{ + + namespace + { + auto constinit handlers = std::array, 256>{}; + } // namespace + + auto register_handler(std::uint32_t irq_number, handler & handler) -> void + { + auto & handler_list = handlers.at(irq_number); + handler_list.push_back(&handler); + } + + auto unregister_handler(std::uint32_t irq_number, handler & handler) -> void + { + static_cast(irq_number); + static_cast(handler); + kstd::println("[OS:interrupts] TODO: support erasure from vector."); + } + + auto dispatch(std::uint32_t irq_number) -> status + { + auto & handler_list = handlers.at(irq_number); + + for (auto handler : handler_list) + { + if (handler && handler->handle_interrupt(irq_number) == status::handled) + { + return status::handled; + } + } + + return status::unhandled; + } + +} // namespace kapi::interrupts \ No newline at end of file -- cgit v1.2.3 From a2834cc22b096e848448bb681ab7b517ecbe70b9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 16:54:07 +0100 Subject: build: simplify header scanning --- CMakeLists.txt | 4 ++++ kapi/CMakeLists.txt | 17 +++-------------- kernel/CMakeLists.txt | 9 +++++++++ 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index febcf0e..d785d4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,10 @@ option(TEACHOS_GENERATE_DOCS "Generate documentation during build" ON) # Global Build System Configuration #]============================================================================] +if(POLICY CMP0209) + cmake_policy(SET CMP0209 NEW) +endif() + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") set(CMAKE_INTERPROCEDURAL_OPTIMIZATION YES) diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index 99b737c..c9aa23f 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -1,24 +1,13 @@ add_library("kapi" INTERFACE) add_library("os::kapi" ALIAS "kapi") +file(GLOB_RECURSE KAPI_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") + target_sources("kapi" PUBLIC FILE_SET HEADERS BASE_DIRS "include" FILES - "include/kapi/boot_modules.hpp" - "include/kapi/boot.hpp" - "include/kapi/cio.hpp" - "include/kapi/cpu.hpp" - "include/kapi/interrupts.hpp" - "include/kapi/boot_module/boot_module.hpp" - "include/kapi/boot_module/boot_module_registry.hpp" - "include/kapi/memory.hpp" - "include/kapi/memory/address.hpp" - "include/kapi/memory/frame_allocator.hpp" - "include/kapi/memory/frame.hpp" - "include/kapi/memory/page_mapper.hpp" - "include/kapi/memory/page.hpp" - "include/kapi/system.hpp" + ${KAPI_HEADERS} ) target_include_directories("kapi" INTERFACE diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 535c441..3d3c7e2 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -56,6 +56,15 @@ 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 aa68f53d2502e0ea81c8e9c95e37d9847cb6cb16 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 17:15:26 +0100 Subject: arch/cpu: fix interrupt dispatch --- arch/x86_64/src/cpu/interrupts.cpp | 34 +++++++++++++++++++++------------- kernel/kapi/interrupts.cpp | 6 ++++++ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 2f23f07..907f289 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -134,25 +134,33 @@ namespace arch::cpu { auto [number, code] = frame->interrupt; - if (number < number_of_exception_vectors && !dispatch_exception(frame)) + if (number < number_of_exception_vectors) { - if (has_error_code(static_cast(number))) + if (!dispatch_exception(frame)) { - kstd::println(kstd::print_sink::stderr, - "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); - } - else - { - kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled exception number {:#04x} received", number); + if (has_error_code(static_cast(number))) + { + kstd::println(kstd::print_sink::stderr, + "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); + } + else + { + kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled exception number {:#04x} received", number); + } } } - else if (number >= number_of_exception_vectors && - kapi::interrupts::dispatch(number) == kapi::interrupts::status::unhandled) + else { - kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x}", number); - } + auto irq_number = number - number_of_exception_vectors; - acknowledge_pic_interrupt(frame); + if (kapi::interrupts::dispatch(irq_number) == kapi::interrupts::status::unhandled) + { + kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x} (IRQ{})", number, + irq_number); + } + + acknowledge_pic_interrupt(frame); + } } } diff --git a/kernel/kapi/interrupts.cpp b/kernel/kapi/interrupts.cpp index e79453a..27427bb 100644 --- a/kernel/kapi/interrupts.cpp +++ b/kernel/kapi/interrupts.cpp @@ -31,6 +31,12 @@ namespace kapi::interrupts { auto & handler_list = handlers.at(irq_number); + if (handler_list.empty()) + { + kstd::println(kstd::print_sink::stderr, "[OS:interrupts] No handler for IRQ{}", irq_number); + return status::unhandled; + } + for (auto handler : handler_list) { if (handler && handler->handle_interrupt(irq_number) == status::handled) -- cgit v1.2.3 From 4b094e2bd5a8e60ec1018d6aa90aa9da4adf49c9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 18:04:12 +0100 Subject: kstd/vector: implement single-element erase --- libs/kstd/include/kstd/vector | 16 ++++++++ libs/kstd/tests/src/vector.cpp | 91 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 0d4aac8..74aefa9 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -690,6 +690,22 @@ namespace kstd return begin() + prefix_size; } + constexpr auto erase(const_iterator position) -> iterator + { + if (position == end()) + { + os::panic("[kstd:vector] Attempted to erase end()!"); + } + + auto prefix_size = std::ranges::distance(cbegin(), position); + + std::ranges::move(begin() + prefix_size + 1, end(), begin() + prefix_size); + std::allocator_traits::destroy(m_allocator, end() - 1); + --m_size; + + return begin() + prefix_size; + } + //! Append a given element to this vector via copy construction. constexpr auto push_back(value_type const & value) -> void { diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 0735c9a..3ff041f 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -649,6 +649,45 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE(it == v.begin() + 1); } } + + WHEN("erasing the first element") + { + auto it = v.erase(v.cbegin()); + + THEN("the first element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 20); + REQUIRE(v[1] == 30); + REQUIRE(it == v.begin()); + } + } + + WHEN("erasing a middle element") + { + auto it = v.erase(v.cbegin() + 1); + + THEN("the middle element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("erasing the last element") + { + auto it = v.erase(v.cend() - 1); + + THEN("the last element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(it == v.end()); + } + } } } @@ -868,6 +907,58 @@ SCENARIO("Vector modifier move semantics", "[vector]") REQUIRE(v[3].was_moved); } } + + WHEN("erasing an element in the middle") + { + for (auto& elem : v) + { + elem.was_copied = false; + elem.was_moved = false; + } + + auto it = v.erase(v.cbegin() + 1); + + THEN("the subsequent elements are move-assigned leftwards") + { + REQUIRE(v.size() == 2); + + REQUIRE(v[0].value == 10); + REQUIRE_FALSE(v[0].was_moved); + REQUIRE_FALSE(v[0].was_copied); + + REQUIRE(v[1].value == 30); + REQUIRE(v[1].was_moved); + REQUIRE_FALSE(v[1].was_copied); + + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("erasing the last element") + { + for (auto& elem : v) + { + elem.was_copied = false; + elem.was_moved = false; + } + + auto it = v.erase(v.cend() - 1); + + THEN("no elements are moved, just the last element destroyed") + { + REQUIRE(v.size() == 2); + + REQUIRE(v[0].value == 10); + REQUIRE_FALSE(v[0].was_moved); + REQUIRE_FALSE(v[0].was_copied); + + REQUIRE(v[1].value == 20); + REQUIRE_FALSE(v[1].was_moved); + REQUIRE_FALSE(v[1].was_copied); + + REQUIRE(it == v.end()); + } + } } } -- cgit v1.2.3 From f6100699cd93606147ebe94a777b6e4aff7c5f50 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 18:09:15 +0100 Subject: kstd/vector: add missing tests for insert --- libs/kstd/tests/src/vector.cpp | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 3ff041f..02b8786 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -650,6 +650,40 @@ SCENARIO("Vector modifiers", "[vector]") } } + WHEN("inserting an rvalue reference to an existing element with reallocation") + { + v.shrink_to_fit(); + REQUIRE(v.capacity() == v.size()); + auto it = v.insert(v.cbegin() + 1, std::move(v[2])); + + THEN("the element is correctly moved and inserted") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting an rvalue reference to an existing element without reallocation") + { + v.reserve(10); + REQUIRE(v.capacity() > v.size()); + auto it = v.insert(v.cbegin() + 1, std::move(v[2])); + + THEN("the element is correctly moved and inserted") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + WHEN("erasing the first element") { auto it = v.erase(v.cbegin()); -- cgit v1.2.3 From 096d7505cfc2d60e58a6dd4d80fd7f3638c9bb94 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 18:19:52 +0100 Subject: kstd/vector: increase test coverage --- .lcovrc | 2 +- libs/kstd/include/kstd/vector | 7 ++++++- libs/kstd/tests/src/vector.cpp | 44 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/.lcovrc b/.lcovrc index d19e80d..07da866 100644 --- a/.lcovrc +++ b/.lcovrc @@ -1,5 +1,5 @@ exclude = /usr/include/* -exclude = build/_deps/* +exclude = build/bht/_deps/* exclude = tests/* ignore_errors = unused,empty,inconsistent \ No newline at end of file diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 74aefa9..771fc87 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -548,7 +548,6 @@ namespace kstd if (new_capacity > max_size()) { kstd::os::panic("[kstd:vector] Tried to reserve more space than theoretically possible."); - return; } auto new_data = allocate_n(new_capacity); @@ -690,6 +689,12 @@ namespace kstd return begin() + prefix_size; } + //! Erase an element at a given position. + //! + //! @note This function will panic if position == end() + //! + //! @param position An interator pointing to the element to delete + //! @return An iterator pointing to the element after the deleted element constexpr auto erase(const_iterator position) -> iterator { if (position == end()) diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 02b8786..913427c 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -475,6 +475,20 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE(it == v.begin()); } } + + WHEN("inserting an lvalue element") + { + auto const value = 42; + auto it = v.insert(v.cbegin(), value); + + THEN("the size and capacity increase and the element is inserted") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v[0] == 42); + REQUIRE(it == v.begin()); + } + } } GIVEN("A populated vector") @@ -597,6 +611,22 @@ SCENARIO("Vector modifiers", "[vector]") } } + WHEN("inserting an lvalue at the end") + { + auto const value = 40; + auto it = v.insert(v.cend(), value); + + THEN("the element is inserted at the back") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 40); + REQUIRE(it == v.begin() + 3); + } + } + WHEN("inserting when capacity is sufficient") { v.reserve(10); @@ -722,6 +752,14 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE(it == v.end()); } } + + WHEN("erasing the end() iterator") + { + THEN("a panic is triggered") + { + REQUIRE_THROWS_AS(v.erase(v.end()), kstd::tests::os_panic); + } + } } } @@ -944,7 +982,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") WHEN("erasing an element in the middle") { - for (auto& elem : v) + for (auto & elem : v) { elem.was_copied = false; elem.was_moved = false; @@ -955,7 +993,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") THEN("the subsequent elements are move-assigned leftwards") { REQUIRE(v.size() == 2); - + REQUIRE(v[0].value == 10); REQUIRE_FALSE(v[0].was_moved); REQUIRE_FALSE(v[0].was_copied); @@ -970,7 +1008,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") WHEN("erasing the last element") { - for (auto& elem : v) + for (auto & elem : v) { elem.was_copied = false; elem.was_moved = false; -- cgit v1.2.3 From 11c6d57e013832983bcd9bb965d470bf4c282ab6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 26 Mar 2026 18:40:39 +0100 Subject: kstd/vector: implement range erase --- CMakeLists.txt | 18 +++++++------ kernel/kapi/interrupts.cpp | 7 ++--- libs/kstd/include/kstd/vector | 22 ++++++++++++++++ libs/kstd/tests/src/vector.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d785d4e..6cc8a2f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,15 +18,17 @@ include("GenerateBootableIso") include("FetchContent") -FetchContent_Declare( - "Catch2" - URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" - URL_HASH "SHA256=c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c" - EXCLUDE_FROM_ALL - FIND_PACKAGE_ARGS -) +if (NOT CMAKE_CROSSCOMPILING) + FetchContent_Declare( + "Catch2" + URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" + URL_HASH "SHA256=c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c" + EXCLUDE_FROM_ALL + FIND_PACKAGE_ARGS + ) -FetchContent_MakeAvailable("Catch2") + FetchContent_MakeAvailable("Catch2") +endif() #[============================================================================[ # Global Build System Options diff --git a/kernel/kapi/interrupts.cpp b/kernel/kapi/interrupts.cpp index 27427bb..e172e70 100644 --- a/kernel/kapi/interrupts.cpp +++ b/kernel/kapi/interrupts.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -22,9 +23,9 @@ namespace kapi::interrupts auto unregister_handler(std::uint32_t irq_number, handler & handler) -> void { - static_cast(irq_number); - static_cast(handler); - kstd::println("[OS:interrupts] TODO: support erasure from vector."); + auto & handler_list = handlers.at(irq_number); + auto [first, last] = std::ranges::remove(handler_list, &handler); + handler_list.erase(first, last); } auto dispatch(std::uint32_t irq_number) -> status diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 771fc87..9e41cb6 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -711,6 +711,28 @@ namespace kstd return begin() + prefix_size; } + //! Erase a range of elements from this vector. + //! + //! @param first The start of the range to erase. + //! @param last The end of the range to erase. + //! @return An iterator pointing to the element after the last deleted element. + constexpr auto erase(const_iterator first, const_iterator last) -> iterator + { + if (first == last) + { + return begin() + std::ranges::distance(cbegin(), first); + } + + auto prefix_size = std::ranges::distance(cbegin(), first); + auto element_count = std::ranges::distance(first, last); + + std::ranges::move(begin() + prefix_size + element_count, end(), begin() + prefix_size); + destroy_n(end() - element_count, element_count); + m_size -= element_count; + + return begin() + prefix_size; + } + //! Append a given element to this vector via copy construction. constexpr auto push_back(value_type const & value) -> void { diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 913427c..b7971f4 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -760,6 +760,33 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE_THROWS_AS(v.erase(v.end()), kstd::tests::os_panic); } } + + WHEN("erasing a range of elements") + { + auto it = v.erase(v.cbegin() + 1, v.cend() - 1); + + THEN("the specified range is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("erasing an empty range") + { + auto it = v.erase(v.cbegin() + 1, v.cbegin() + 1); + + THEN("the vector is unchanged") + { + REQUIRE(v.size() == 3); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(it == v.begin() + 1); + } + } } } @@ -1031,6 +1058,39 @@ SCENARIO("Vector modifier move semantics", "[vector]") REQUIRE(it == v.end()); } } + + WHEN("erasing a range of elements in the middle") + { + v.emplace_back(40); + v.emplace_back(50); + + for (auto & elem : v) + { + elem.was_copied = false; + elem.was_moved = false; + } + + auto it = v.erase(v.cbegin() + 1, v.cbegin() + 3); + + THEN("the specified elements are destroyed and subsequent elements are move-assigned leftwards") + { + REQUIRE(v.size() == 3); + + REQUIRE(v[0].value == 10); + REQUIRE_FALSE(v[0].was_moved); + REQUIRE_FALSE(v[0].was_copied); + + REQUIRE(v[1].value == 40); + REQUIRE(v[1].was_moved); + REQUIRE_FALSE(v[1].was_copied); + + REQUIRE(v[2].value == 50); + REQUIRE(v[2].was_moved); + REQUIRE_FALSE(v[2].was_copied); + + REQUIRE(it == v.begin() + 1); + } + } } } -- cgit v1.2.3 From 3070bb45b9741165d786b2c5a018ee55c1a82db8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 27 Mar 2026 07:05:05 +0100 Subject: kernel/interrupts: switch to flat_map for dispatch --- kernel/kapi/interrupts.cpp | 21 +- libs/kstd/CMakeLists.txt | 1 + libs/kstd/include/kstd/bits/flat_map.hpp | 82 ++++++++ libs/kstd/include/kstd/flat_map | 320 +++++++++++++++++++++++++++++++ libs/kstd/tests/src/flat_map.cpp | 288 ++++++++++++++++++++++++++++ 5 files changed, 708 insertions(+), 4 deletions(-) create mode 100644 libs/kstd/include/kstd/bits/flat_map.hpp create mode 100644 libs/kstd/include/kstd/flat_map create mode 100644 libs/kstd/tests/src/flat_map.cpp diff --git a/kernel/kapi/interrupts.cpp b/kernel/kapi/interrupts.cpp index e172e70..0e37bc3 100644 --- a/kernel/kapi/interrupts.cpp +++ b/kernel/kapi/interrupts.cpp @@ -1,10 +1,10 @@ #include "kapi/interrupts.hpp" +#include #include #include #include -#include #include namespace kapi::interrupts @@ -12,13 +12,20 @@ namespace kapi::interrupts namespace { - auto constinit handlers = std::array, 256>{}; + auto constinit handlers = kstd::flat_map>{}; } // namespace auto register_handler(std::uint32_t irq_number, handler & handler) -> void { - auto & handler_list = handlers.at(irq_number); - handler_list.push_back(&handler); + if (handlers.contains(irq_number)) + { + auto & handler_list = handlers.at(irq_number); + handler_list.push_back(&handler); + } + else + { + handlers.emplace(irq_number, kstd::vector{&handler}); + } } auto unregister_handler(std::uint32_t irq_number, handler & handler) -> void @@ -30,6 +37,12 @@ namespace kapi::interrupts auto dispatch(std::uint32_t irq_number) -> status { + if (!handlers.contains(irq_number)) + { + kstd::println(kstd::print_sink::stderr, "[OS:interrupts] No handler for IRQ{}", irq_number); + return status::unhandled; + } + auto & handler_list = handlers.at(irq_number); if (handler_list.empty()) diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 06543ab..ff3c8cc 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -44,6 +44,7 @@ endif() if(NOT CMAKE_CROSSCOMPILING) add_executable("kstd_tests" + "tests/src/flat_map.cpp" "tests/src/vector.cpp" "tests/src/os_panic.cpp" ) diff --git a/libs/kstd/include/kstd/bits/flat_map.hpp b/libs/kstd/include/kstd/bits/flat_map.hpp new file mode 100644 index 0000000..903841e --- /dev/null +++ b/libs/kstd/include/kstd/bits/flat_map.hpp @@ -0,0 +1,82 @@ +#ifndef KSTD_BITS_FLAT_MAP_HPP +#define KSTD_BITS_FLAT_MAP_HPP + +#include +#include +#include + +namespace kstd::bits +{ + + template + struct flat_map_iterator + { + using iterator_category = std::random_access_iterator_tag; + using value_type = std::pair; + using difference_type = std::ptrdiff_t; + using reference = std::pair; + using pointer = void; + + constexpr flat_map_iterator(KeyIterator key_iterator, MappedIterator mapped_iterator) + : m_key_iterator{key_iterator} + , m_mapped_iterator{mapped_iterator} + {} + + [[nodiscard]] auto key_iterator() const noexcept -> KeyIterator + { + return m_key_iterator; + } + + [[nodiscard]] constexpr auto operator*() const noexcept -> reference + { + return {*m_key_iterator, *m_mapped_iterator}; + } + + constexpr auto operator++() noexcept -> flat_map_iterator & + { + ++m_key_iterator; + ++m_mapped_iterator; + return *this; + } + + constexpr auto operator++(int) noexcept -> flat_map_iterator + { + auto copy = *this; + ++(*this); + return copy; + } + + constexpr auto operator--() noexcept -> flat_map_iterator & + { + --m_key_iterator; + --m_mapped_iterator; + return *this; + } + + constexpr auto operator--(int) noexcept -> flat_map_iterator + { + auto copy = *this; + --(*this); + return copy; + } + + [[nodiscard]] constexpr auto operator+(difference_type offset) const noexcept -> flat_map_iterator + { + return {m_key_iterator + offset, m_mapped_iterator + offset}; + } + + [[nodiscard]] constexpr auto operator-(flat_map_iterator const & other) const noexcept -> difference_type + { + return m_key_iterator - other.m_key_iterator; + } + + [[nodiscard]] constexpr auto operator<=>(flat_map_iterator const & other) const noexcept = default; + + private: + KeyIterator m_key_iterator{}; + MappedIterator m_mapped_iterator{}; + }; + +} // namespace kstd::bits + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/flat_map b/libs/kstd/include/kstd/flat_map new file mode 100644 index 0000000..6ffbbcf --- /dev/null +++ b/libs/kstd/include/kstd/flat_map @@ -0,0 +1,320 @@ +#ifndef KSTD_FLAT_MAP_HPP +#define KSTD_FLAT_MAP_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace kstd +{ + + template, + typename KeyContainerType = kstd::vector, typename MappedContainerType = kstd::vector> + struct flat_map + { + using key_container_type = KeyContainerType; + using mapped_container_type = MappedContainerType; + using key_type = KeyType; + using mapped_type = MappedType; + using value_type = std::pair; + using key_compare = KeyCompare; + using reference = std::pair; + using const_reference = std::pair; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using iterator = bits::flat_map_iterator; + using const_iterator = + bits::flat_map_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using containers = struct + { + key_container_type keys; + mapped_container_type values; + }; + + //! Compare two object of type value_type. + struct value_compare + { + constexpr auto operator()(const_reference lhs, const_reference rhs) const -> bool + { + return lhs.first < rhs.first; + } + }; + + //! Construct an empty flat map. + constexpr flat_map() + : flat_map{key_compare{}} + {} + + //! Construct an empty flat map using the given custom comparator. + //! + //! @param comparator The comparator to use for comparing keys. + constexpr explicit flat_map(key_compare const & comparator) + : m_containers{} + , m_comparator{comparator} + {} + + //! Get a reference to the mapped value associated with the given key. + //! + //! @warning This function will panic if the key is not found. + //! @param key The key to look up. + //! @return A reference to the mapped value. + [[nodiscard]] constexpr auto at(key_type const & key) -> mapped_type & + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return *(m_containers.values.begin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get a reference to the mapped value associated with the given key. + //! + //! @warning This function will panic if the key is not found. + //! @param key The key to look up. + //! @return A const reference to the mapped value. + [[nodiscard]] constexpr auto at(key_type const & key) const -> mapped_type const & + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.cbegin(), found); + return *(m_containers.values.cbegin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get a reference to the mapped value associated with the given key. + //! + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @warning This function will panic if the key is not found. + //! @param x The key to look up. + //! @return A reference to the mapped value. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] constexpr auto at(K const & x) -> mapped_type & + { + auto found = find(x); + if (found != end()) + { + auto offset = std::distance(m_containers.keys.begin(), found.key_iterator()); + return *(m_containers.values.begin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get a reference to the mapped value associated with the given key. + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @warning This function will panic if the key is not found. + //! @param x The key to look up. + //! @return A const reference to the mapped value. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] auto at(K const & x) const -> mapped_type const & + { + auto found = find(x); + if (found != end()) + { + auto offset = std::distance(m_containers.keys.cbegin(), found.key_iterator()); + return *(m_containers.values.cbegin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get an iterator to the first element. + [[nodiscard]] auto begin() noexcept -> iterator + { + return iterator{m_containers.keys.begin(), m_containers.values.begin()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto begin() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cbegin(), m_containers.values.cbegin()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto cbegin() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cbegin(), m_containers.values.cbegin()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto end() noexcept -> iterator + { + return iterator{m_containers.keys.end(), m_containers.values.end()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto end() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cend(), m_containers.values.cend()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto cend() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cend(), m_containers.values.cend()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto rbegin() noexcept -> reverse_iterator + { + return reverse_iterator{end()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cend()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cend()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto rend() noexcept -> reverse_iterator + { + return reverse_iterator{begin()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cbegin()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cbegin()}; + } + + //! Try to insert a new key-value pair into the map. + //! + //! @param args Arguments to use for constructing the key-value pair. + //! @return A pair of an iterator to the inserted element and a boolean indicating whether the insertion took place. + template + auto emplace(Args &&... args) -> std::pair + requires std::constructible_from + { + auto value = value_type{std::forward(args)...}; + auto found = std::ranges::lower_bound(m_containers.keys, value.first, m_comparator); + + if (found != m_containers.keys.cend() && !m_comparator(value.first, *found) && !m_comparator(*found, value.first)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return { + iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}, + false + }; + } + + auto offset = std::distance(m_containers.keys.begin(), found); + auto key_iterator = m_containers.keys.begin() + offset; + auto mapped_iterator = m_containers.values.begin() + offset; + + auto inserted_key = m_containers.keys.insert(key_iterator, std::move(value.first)); + auto inserted_mapped = m_containers.values.insert(mapped_iterator, std::move(value.second)); + + return { + iterator{inserted_key, inserted_mapped}, + true + }; + } + + //! Find an element with an equivalent key. + //! + //! @param key The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + [[nodiscard]] auto find(key_type const & key) noexcept -> iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}; + } + return end(); + } + + //! Find an element with an equivalent key. + //! + //! @param key The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + [[nodiscard]] auto find(key_type const & key) const noexcept -> const_iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.cbegin(), found); + return const_iterator{m_containers.keys.cbegin() + offset, m_containers.values.cbegin() + offset}; + } + return cend(); + } + + //! Find an element with an equivalent key. + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @param x The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] auto find(K const & x) noexcept -> iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, x, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(x, *found) && !m_comparator(*found, x)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}; + } + return end(); + } + + //! Find an element with an equivalent key. + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @param x The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] auto find(K const & x) const noexcept -> const_iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, x, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(x, *found) && !m_comparator(*found, x)) + { + auto offset = std::distance(m_containers.keys.cbegin(), found); + return const_iterator{m_containers.keys.cbegin() + offset, m_containers.values.cbegin() + offset}; + } + return cend(); + } + + //! Check if the map contains the given key. + //! + //! @param key The key to check. + //! @return true iff. the key is found, false otherwise. + [[nodiscard]] constexpr auto contains(key_type const & key) const noexcept -> bool + { + return find(key) != cend(); + } + + private: + containers m_containers; + key_compare m_comparator; + }; +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/tests/src/flat_map.cpp b/libs/kstd/tests/src/flat_map.cpp new file mode 100644 index 0000000..cde136a --- /dev/null +++ b/libs/kstd/tests/src/flat_map.cpp @@ -0,0 +1,288 @@ +#include +#include + +#include + +#include + +SCENARIO("Flat Map initialization and construction", "[flat_map]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto map = kstd::flat_map{}; + + THEN("the Flat Map does not contain elements") + { + REQUIRE_FALSE(map.contains(1)); + } + } + } +} + +SCENARIO("Flat Map modifiers", "[flat_map]") +{ + GIVEN("An empty Flat Map") + { + auto map = kstd::flat_map{}; + + WHEN("emplacing a new element") + { + auto [it, inserted] = map.emplace(1, 100); + + THEN("the map contains the new element") + { + REQUIRE(inserted); + REQUIRE(map.contains(1)); + } + } + + WHEN("emplacing an existing element") + { + map.emplace(1, 100); + auto [it, inserted] = map.emplace(1, 200); + + THEN("the map does not insert the duplicate") + { + REQUIRE_FALSE(inserted); + REQUIRE(map.contains(1)); + } + } + } +} + +SCENARIO("Flat Map element access", "[flat_map]") +{ + GIVEN("A populated Flat Map") + { + auto map = kstd::flat_map{}; + map.emplace(1, 10); + map.emplace(2, 20); + map.emplace(3, 30); + + WHEN("accessing an existing element with at()") + { + auto & val = map.at(2); + + THEN("it returns a reference to the mapped value") + { + REQUIRE(val == 20); + } + + THEN("the mapped value can be modified") + { + val = 200; + REQUIRE(map.at(2) == 200); + } + } + + WHEN("accessing a non-existent element with at()") + { + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(4), kstd::tests::os_panic); + } + } + } + + GIVEN("A const populated Flat Map") + { + auto map_builder = kstd::flat_map{}; + map_builder.emplace(1, 10); + map_builder.emplace(2, 20); + auto const map = map_builder; + + WHEN("accessing an existing element with const at()") + { + auto const & val = map.at(2); + + THEN("it returns a const reference to the mapped value") + { + REQUIRE(val == 20); + } + } + + WHEN("accessing a non-existent element with const at()") + { + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(4), kstd::tests::os_panic); + } + } + } +} + +SCENARIO("Flat Map iterators", "[flat_map]") +{ + GIVEN("A populated Flat Map") + { + auto map = kstd::flat_map{}; + map.emplace(1, 10); + map.emplace(2, 20); + map.emplace(3, 30); + + WHEN("using forward iterators") + { + THEN("they navigate the elements in the correct forward order") + { + auto it = map.begin(); + REQUIRE(it != map.end()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it != map.end()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.end()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it == map.end()); + } + + THEN("const forward iterators provide correct access") + { + auto it = map.cbegin(); + REQUIRE(it != map.cend()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it != map.cend()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.cend()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it == map.cend()); + } + } + + WHEN("using reverse iterators") + { + THEN("they navigate the elements in the correct reverse order") + { + auto it = map.rbegin(); + REQUIRE(it != map.rend()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it != map.rend()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.rend()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it == map.rend()); + } + + THEN("const reverse iterators provide correct access") + { + auto it = map.crbegin(); + REQUIRE(it != map.crend()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it != map.crend()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.crend()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it == map.crend()); + } + } + } + + GIVEN("an empty Flat Map") + { + auto map = kstd::flat_map{}; + + WHEN("getting iterators") + { + THEN("begin() equals end() and cbegin() equals cend()") + { + REQUIRE(map.begin() == map.end()); + REQUIRE(map.cbegin() == map.cend()); + } + + THEN("rbegin() equals rend() and crbegin() equals crend()") + { + REQUIRE(map.rbegin() == map.rend()); + REQUIRE(map.crbegin() == map.crend()); + } + } + } +} + +SCENARIO("Flat Map heterogeneous element access", "[flat_map]") +{ + GIVEN("A populated Flat Map with a transparent comparator") + { + auto map = kstd::flat_map>{}; + map.emplace(1, 10); + map.emplace(2, 20); + map.emplace(3, 30); + + WHEN("accessing an existing element with a different key type via at()") + { + long const key = 2L; + auto & val = map.at(key); + + THEN("it returns a reference to the mapped value") + { + REQUIRE(val == 20); + } + + THEN("the mapped value can be modified") + { + val = 200; + REQUIRE(map.at(2L) == 200); + } + } + + WHEN("accessing a non-existent element with a different key type via at()") + { + long const key = 4L; + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(key), kstd::tests::os_panic); + } + } + } + + GIVEN("A const populated Flat Map with a transparent comparator") + { + auto map_builder = kstd::flat_map>{}; + map_builder.emplace(1, 10); + map_builder.emplace(2, 20); + auto const map = map_builder; + + WHEN("accessing an existing element with a different key type via const at()") + { + long const key = 2L; + auto const & val = map.at(key); + + THEN("it returns a const reference to the mapped value") + { + REQUIRE(val == 20); + } + } + + WHEN("accessing a non-existent element with a different key type via const at()") + { + long const key = 4L; + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(key), kstd::tests::os_panic); + } + } + } +} -- cgit v1.2.3 From 610707e896504a33fa82db4905e57a4822d3bb9d Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 27 Mar 2026 19:39:55 +0100 Subject: add string tests --- libs/kstd/CMakeLists.txt | 1 + libs/kstd/tests/src/string.cpp | 390 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 391 insertions(+) create mode 100644 libs/kstd/tests/src/string.cpp diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 06543ab..d4f415f 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -46,6 +46,7 @@ if(NOT CMAKE_CROSSCOMPILING) add_executable("kstd_tests" "tests/src/vector.cpp" "tests/src/os_panic.cpp" + "tests/src/string.cpp" ) target_include_directories("kstd_tests" PRIVATE diff --git a/libs/kstd/tests/src/string.cpp b/libs/kstd/tests/src/string.cpp new file mode 100644 index 0000000..f7f629d --- /dev/null +++ b/libs/kstd/tests/src/string.cpp @@ -0,0 +1,390 @@ +#include + +#include + +#include +#include +#include + +SCENARIO("String initialization and construction", "[string]") +{ + GIVEN("Nothing") + { + WHEN("constructing an empty string") + { + auto str = kstd::string{}; + + THEN("the size is zero and therefore the string is empty") + { + REQUIRE(str.empty()); + REQUIRE(str.size() == 0); + } + + THEN("the string is empty") + { + REQUIRE(str.view() == std::string_view{}); + } + } + } + + GIVEN("A string view") + { + auto view = std::string_view{"Blub Blub"}; + + WHEN("constructing a string from string_view") + { + auto str = kstd::string{view}; + + THEN("the string is not empty and has the same size as the view") + { + REQUIRE(!str.empty()); + REQUIRE(str.size() == view.size()); + } + + THEN("the string contains the same characters as the view") + { + REQUIRE(str.view() == view); + } + } + } + + GIVEN("A C-style string") + { + auto c_str = "Blub Blub"; + + WHEN("constructing a string from the C-style string") + { + auto str = kstd::string{c_str}; + + THEN("the string is not empty and has the same size as the C-style string") + { + REQUIRE(!str.empty()); + REQUIRE(str.size() == strlen(c_str)); + } + + THEN("the string contains the same characters as the C-style string") + { + REQUIRE(str.view() == c_str); + } + } + } + + GIVEN("A character") + { + auto ch = 'x'; + + WHEN("constructing a string from the character") + { + auto str = kstd::string{ch}; + + THEN("the string is not empty and has size 1") + { + REQUIRE(!str.empty()); + REQUIRE(str.size() == 1); + } + + THEN("the string contains the same character as the given character") + { + REQUIRE(str.view() == std::string_view{&ch, 1}); + } + } + } + + GIVEN("Another string") + { + auto other = kstd::string{"Blub Blub"}; + + WHEN("copy constructing a new string") + { + auto str = kstd::string{other}; + + THEN("the new string contains the same characters as the original") + { + REQUIRE(str.view() == other.view()); + } + } + + auto str = kstd::string{"Blub"}; + + WHEN("copy assigning another string") + { + auto other = kstd::string{"Blub Blub"}; + str = other; + + THEN("the string contains the same characters as the assigned string") + { + REQUIRE(str.view() == other.view()); + } + } + } + + GIVEN("A string") + { + auto str = kstd::string{"Hello"}; + + WHEN("copy assigning a string view") + { + auto view = std::string_view{"Hello, world!"}; + str = view; + + THEN("the string contains the same characters as the assigned view") + { + REQUIRE(str.view() == view); + } + } + } + + GIVEN("A string") + { + auto str = kstd::string{"Hello"}; + + WHEN("copy assigning a C-style string") + { + auto c_str = "Hello, world!"; + str = c_str; + + THEN("the string contains the same characters as the assigned C-style string") + { + REQUIRE(str.view() == c_str); + } + } + } +} + +SCENARIO("String concatenation", "[string]") +{ + GIVEN("Two strings") + { + auto str1 = kstd::string{"Blub"}; + auto str2 = kstd::string{" Blub"}; + + WHEN("appending the second string to the first string") + { + str1.append(str2); + + THEN("the first string contains the characters of both strings concatenated") + { + REQUIRE(str1.view() == "Blub Blub"); + } + + THEN("the size of the first string is the sum of the sizes of both strings") + { + REQUIRE(str1.size() == str2.size() + 4); + } + } + + WHEN("using operator+= to append the second string to the first string") + { + str1 += str2; + + THEN("the first string contains the characters of both strings concatenated") + { + REQUIRE(str1.view() == "Blub Blub"); + } + + THEN("the size of the first string is the sum of the sizes of both strings") + { + REQUIRE(str1.size() == str2.size() + 4); + } + } + + WHEN("using operator+ to concatenate the two strings into a new string") + { + auto str3 = str1 + str2; + + THEN("the new string contains the characters of both strings concatenated") + { + REQUIRE(str3.view() == "Blub Blub"); + } + + THEN("the size of the new string is the sum of the sizes of both strings") + { + REQUIRE(str3.size() == str1.size() + str2.size()); + } + } + } + + GIVEN("A string and a string view") + { + auto str = kstd::string{"Blub"}; + auto view = std::string_view{" Blub"}; + + WHEN("appending the string view to the string") + { + str.append(view); + + THEN("the string contains the characters of both the original string and the appended view concatenated") + { + REQUIRE(str.view() == "Blub Blub"); + } + + THEN("the size of the string is the sum of the sizes of the original string and the appended view") + { + REQUIRE(str.size() == view.size() + 4); + } + } + } + + GIVEN("A string and a character") + { + auto str = kstd::string{"Blub"}; + auto ch = '!'; + + WHEN("appending the character to the string") + { + str.push_back(ch); + + THEN("the string contains the original characters followed by the appended character") + { + REQUIRE(str.view() == "Blub!"); + } + + THEN("the size of the string is one more than the original size") + { + REQUIRE(str.size() == 5); + } + } + + WHEN("using operator+= to append the character to the string") + { + str += ch; + + THEN("the string contains the original characters followed by the appended character") + { + REQUIRE(str.view() == "Blub!"); + } + + THEN("the size of the string is one more than the original size") + { + REQUIRE(str.size() == 5); + } + } + } +} + +SCENARIO("String conversion and comparison", "[string]") +{ + GIVEN("An unsigned integer") + { + auto value1 = 12345u; + auto value2 = 0u; + + WHEN("converting the unsigned integer to a string") + { + auto str1 = kstd::to_string(value1); + auto str2 = kstd::to_string(value2); + + THEN("the string contains the decimal representation of the unsigned integer") + { + REQUIRE(str1.view() == "12345"); + REQUIRE(str2.view() == "0"); + } + } + } + + GIVEN("Two strings with the same characters") + { + auto str1 = kstd::string{"Blub Blub"}; + auto str2 = kstd::string{"Blub Blub"}; + + THEN("the strings are equal") + { + REQUIRE(str1 == str2); + } + + THEN("the strings are not unequal") + { + REQUIRE(!(str1 != str2)); + } + } + + GIVEN("A string and a string view with the same characters") + { + auto str = kstd::string{"Blub Blub"}; + auto view = std::string_view{"Blub Blub"}; + + THEN("the string and the string view are equal") + { + REQUIRE(str == view); + REQUIRE(view == str); + } + + THEN("the string and the string view are not unequal") + { + REQUIRE(!(str != view)); + REQUIRE(!(view != str)); + } + } +} + +SCENARIO("String clearing", "[string]") +{ + GIVEN("A non-empty string") + { + auto str = kstd::string{"Blub Blub"}; + + WHEN("clearing the string") + { + str.clear(); + + THEN("the string is empty and has size zero") + { + REQUIRE(str.empty()); + REQUIRE(str.size() == 0); + } + + THEN("the string contains no characters") + { + REQUIRE(str.view() == std::string_view{}); + } + } + } +} + +SCENARIO("String iteration", "[string]") +{ + GIVEN("A string") + { + auto str = kstd::string{"Blub"}; + + WHEN("iterating over the characters of the string as string_view using a range-based for loop") + { + kstd::string result; + + for (auto ch : str.view()) + { + result.push_back(ch); + } + + THEN("the iterated characters are the same as the characters in the string") + { + REQUIRE(result == str); + } + } + + WHEN("using std::ranges::for_each to iterate over the characters of the string") + { + kstd::string result; + + std::ranges::for_each(str, [&result](auto ch) { result.push_back(ch); }); + + THEN("the iterated characters are the same as the characters in the string") + { + REQUIRE(result == str); + } + } + + WHEN("using front and back to access the first and last characters of the string") + { + THEN("front returns the first character of the string") + { + REQUIRE(str.front() == 'B'); + } + + THEN("back returns the last character of the string") + { + REQUIRE(str.back() == 'b'); + } + } + } +} -- cgit v1.2.3 From feea1b8101bd148a93ebf6f1622dcf78efbbbd6d Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 27 Mar 2026 19:59:39 +0100 Subject: generate visual coverage report --- .gitlab-ci.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 05bba19..a48e4a7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -27,18 +27,26 @@ bht: image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-4 before_script: - apt update - - apt install -y build-essential cmake ninja-build lcov libcatch2-dev + - apt install -y build-essential cmake ninja-build lcov libcatch2-dev gcovr script: - cmake --preset 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 + - genhtml --prefix $(pwd) --output-directory coverage coverage.info + - gcovr --root . --cobertura-pretty --output coverage/cobertura-coverage.xml coverage: '/Total:\|\s*(\d+(\.\d+)?)\%/' artifacts: paths: - coverage.info + - coverage/ expire_in: 24 hours + when: always + reports: + coverage_report: + coverage_format: cobertura + path: coverage/cobertura-coverage.xml license_check: stage: .pre -- cgit v1.2.3 From 2c913506a0e07d88883aa7624ecba17e9a4fd328 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 27 Mar 2026 20:04:17 +0100 Subject: echo public coverage report url --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a48e4a7..1479cf8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -36,6 +36,8 @@ bht: - lcov --config-file .lcovrc --list coverage.info - genhtml --prefix $(pwd) --output-directory coverage coverage.info - gcovr --root . --cobertura-pretty --output coverage/cobertura-coverage.xml + after_script: + - echo "CoverageReport public URL - https://gitlab.ost.ch/teachos/kernel/-/jobs/$CI_JOB_ID/artifacts/browse/coverage/index.html" coverage: '/Total:\|\s*(\d+(\.\d+)?)\%/' artifacts: paths: -- cgit v1.2.3 From b0b7c83fb3798832a4bec5b314af2d1633da0640 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 27 Mar 2026 20:06:37 +0100 Subject: fix url --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1479cf8..b655e60 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,7 +37,7 @@ bht: - genhtml --prefix $(pwd) --output-directory coverage coverage.info - gcovr --root . --cobertura-pretty --output coverage/cobertura-coverage.xml after_script: - - echo "CoverageReport public URL - https://gitlab.ost.ch/teachos/kernel/-/jobs/$CI_JOB_ID/artifacts/browse/coverage/index.html" + - echo "CoverageReport public URL - https://gitlab.ost.ch/teachos/kernel/-/jobs/$CI_JOB_ID/artifacts/external_file/coverage/index.html" coverage: '/Total:\|\s*(\d+(\.\d+)?)\%/' artifacts: paths: -- cgit v1.2.3 From c3893d6f31b79719035f383ab489c2d18f01b3a3 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 27 Mar 2026 20:10:24 +0100 Subject: fix url --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b655e60..2231956 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,7 +37,7 @@ bht: - genhtml --prefix $(pwd) --output-directory coverage coverage.info - gcovr --root . --cobertura-pretty --output coverage/cobertura-coverage.xml after_script: - - echo "CoverageReport public URL - https://gitlab.ost.ch/teachos/kernel/-/jobs/$CI_JOB_ID/artifacts/external_file/coverage/index.html" + - echo "CoverageReport public URL - https://teachos.pages.ost.ch/-/kernel/-/jobs/$CI_JOB_ID/artifacts/coverage/index.html" coverage: '/Total:\|\s*(\d+(\.\d+)?)\%/' artifacts: paths: -- cgit v1.2.3 From 4f7ae11655807acf68f49637cc9dd01a03af36d5 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 27 Mar 2026 20:31:48 +0100 Subject: add some more tests --- libs/kstd/tests/src/string.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/libs/kstd/tests/src/string.cpp b/libs/kstd/tests/src/string.cpp index f7f629d..43e9a6b 100644 --- a/libs/kstd/tests/src/string.cpp +++ b/libs/kstd/tests/src/string.cpp @@ -387,4 +387,59 @@ SCENARIO("String iteration", "[string]") } } } + + GIVEN("A const string") + { + auto const str = kstd::string{"Blub"}; + + WHEN("iterating over the characters of the string as string_view using a range-based for loop") + { + kstd::string result; + + for (auto ch : str.view()) + { + result.push_back(ch); + } + + THEN("the iterated characters are the same as the characters in the string") + { + REQUIRE(result == str.view()); + } + } + + WHEN("using front and back to access the first and last characters of the string") + { + THEN("front returns the first character of the string") + { + REQUIRE(str.front() == 'B'); + } + + THEN("back returns the last character of the string") + { + REQUIRE(str.back() == 'b'); + } + } + } + + GIVEN("An empty string") + { + auto str = kstd::string{}; + + WHEN("iterating over the characters of an empty string") + { + kstd::string result; + + for (auto ch : str.view()) + { + result.push_back(ch); + } + + THEN("no characters are iterated and the result is an empty string") + { + REQUIRE(result.empty()); + REQUIRE(result.size() == 0); + REQUIRE(result.view() == std::string_view{}); + } + } + } } -- cgit v1.2.3 From a6f93bf8df0dbfa7d19aa1168bfc8b052e41c42f Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 28 Mar 2026 17:28:32 +0100 Subject: fix vfs mount with /dev & /a and rootfs & devfs --- kernel/include/kernel/filesystem/mount.hpp | 7 +- kernel/include/kernel/filesystem/mount_table.hpp | 6 +- .../kernel/filesystem/rootfs/rootfs_filesystem.hpp | 2 - kernel/include/kernel/filesystem/vfs.hpp | 2 + kernel/src/filesystem/devfs/devfs_filesystem.cpp | 3 +- kernel/src/filesystem/mount.cpp | 11 +- kernel/src/filesystem/mount_table.cpp | 29 +++-- kernel/src/filesystem/rootfs/rootfs_filesystem.cpp | 2 +- kernel/src/filesystem/vfs.cpp | 122 +++++++++------------ 9 files changed, 93 insertions(+), 91 deletions(-) diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index 0cd30b4..0f37687 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -5,20 +5,25 @@ #include "kernel/filesystem/filesystem.hpp" #include +#include + +#include namespace filesystem { struct mount { mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, - kstd::shared_ptr const & fs); + kstd::shared_ptr const & fs, std::string_view mount_path); [[nodiscard]] auto get_mount_dentry() const -> kstd::shared_ptr; [[nodiscard]] auto root_dentry() const -> kstd::shared_ptr const &; [[nodiscard]] auto get_filesystem() const -> kstd::shared_ptr const &; + [[nodiscard]] auto get_mount_path() const -> std::string_view; private: + kstd::string m_mount_path; kstd::shared_ptr m_mount_dentry; kstd::shared_ptr m_root_dentry; kstd::shared_ptr m_filesystem{}; diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp index cf523c1..2cd66ea 100644 --- a/kernel/include/kernel/filesystem/mount_table.hpp +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -1,12 +1,13 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_MOUNT_TABLE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_MOUNT_TABLE_HPP -#include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/mount.hpp" #include #include +#include + namespace filesystem { struct mount_table @@ -14,8 +15,7 @@ namespace filesystem public: void add_mount(kstd::shared_ptr); - [[nodiscard]] auto get_root_mount() const -> kstd::shared_ptr; - auto find_mount_by_dentry(kstd::shared_ptr const & dentry) -> kstd::shared_ptr; + [[nodiscard]] auto find_longest_prefix_mount(std::string_view path) const -> kstd::shared_ptr; private: kstd::vector> m_mounts; diff --git a/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp index cb07111..b3e03a9 100644 --- a/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp +++ b/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp @@ -4,14 +4,12 @@ #include "kernel/devices/device.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" -#include "kernel/filesystem/rootfs/rootfs_inode.hpp" #include #include #include #include -#include namespace filesystem::rootfs { diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index f301b7a..9bee104 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -27,6 +27,8 @@ namespace filesystem auto init_internal() -> void; [[nodiscard]] auto resolve_path(std::string_view path) -> kstd::shared_ptr; + auto do_mount_internal(std::string_view path, kstd::shared_ptr const & mount_point_dentry, + kstd::shared_ptr const & fs) -> void; mount_table m_mount_table; }; diff --git a/kernel/src/filesystem/devfs/devfs_filesystem.cpp b/kernel/src/filesystem/devfs/devfs_filesystem.cpp index cfd2d88..c4cd81a 100644 --- a/kernel/src/filesystem/devfs/devfs_filesystem.cpp +++ b/kernel/src/filesystem/devfs/devfs_filesystem.cpp @@ -13,10 +13,11 @@ namespace filesystem::devfs { - auto devfs_filesystem::mount(kstd::shared_ptr const & /*device*/) -> int + auto devfs_filesystem::mount(kstd::shared_ptr const &) -> int { m_root_inode = kstd::make_shared(); build_device_inode_table(); + return 0; } diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index d9937dc..afc07fa 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -6,14 +6,16 @@ #include "kernel/filesystem/filesystem.hpp" #include +#include #include namespace filesystem { mount::mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, - kstd::shared_ptr const & fs) - : m_mount_dentry(mount_dentry) + kstd::shared_ptr const & fs, std::string_view mount_path) + : m_mount_path(mount_path) + , m_mount_dentry(mount_dentry) , m_root_dentry(root_dentry) , m_filesystem(fs) { @@ -37,4 +39,9 @@ namespace filesystem { return m_root_dentry; } + + auto mount::get_mount_path() const -> std::string_view + { + return m_mount_path.view(); + } } // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 681c2b9..b9e57d4 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -1,11 +1,11 @@ #include "kernel/filesystem/mount_table.hpp" -#include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/mount.hpp" #include -#include +#include +#include namespace filesystem { @@ -14,16 +14,25 @@ namespace filesystem m_mounts.push_back(mount); } - auto mount_table::get_root_mount() const -> kstd::shared_ptr + auto mount_table::find_longest_prefix_mount(std::string_view path) const -> kstd::shared_ptr { - auto it = std::ranges::find_if(m_mounts, [](auto const & mount) { return mount->get_mount_dentry() == nullptr; }); - return it != m_mounts.end() ? *it : nullptr; - } + kstd::shared_ptr mount_with_longest_prefix = nullptr; + std::size_t best_len = 0; - auto mount_table::find_mount_by_dentry(kstd::shared_ptr const & dentry) -> kstd::shared_ptr - { - auto it = std::ranges::find_if(m_mounts, [&](auto const & mnt) { return mnt->get_mount_dentry() == dentry; }); + for (auto const & mount : m_mounts) + { + auto mp = mount->get_mount_path(); + + // /a/b/c should match /a/b but not /a/bb or /a/b/c/d, / should match everything + bool is_prefix = path.starts_with(mp) && (mp == "/" || path.size() == mp.size() || path[mp.size()] == '/'); + + if (is_prefix && mp.size() >= best_len) + { + mount_with_longest_prefix = mount; + best_len = mp.size(); + } + } - return it != m_mounts.end() ? *it : nullptr; + return mount_with_longest_prefix; } } // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp b/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp index e819fc7..22502aa 100644 --- a/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp +++ b/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp @@ -10,7 +10,7 @@ namespace filesystem::rootfs { - auto rootfs_filesystem::mount(kstd::shared_ptr const & /*device*/) -> int + auto rootfs_filesystem::mount(kstd::shared_ptr const &) -> int { auto rfs_inode = kstd::make_shared(); rfs_inode->add_child("dev"); diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 40c97f8..da7fa05 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -37,11 +37,11 @@ namespace filesystem auto vfs::init_internal() -> void { - auto virtual_fs = kstd::make_shared(); - virtual_fs->mount(nullptr); + auto root_fs = kstd::make_shared(); + root_fs->mount(nullptr); - auto virtual_root_dentry = kstd::make_shared(nullptr, virtual_fs->root_inode(), "/"); - m_mount_table.add_mount(kstd::make_shared(nullptr, virtual_root_dentry, virtual_fs)); + auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode()); + m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, "")); auto storage_mgmt = devices::storage::storage_management::get(); if (auto boot_device = storage_mgmt.determine_boot_device()) @@ -49,24 +49,12 @@ namespace filesystem // TODO BA-FS26 detect fs type from boot device and load corresponding fs, for now just assume ext2 auto boot_root_fs = kstd::make_shared(); boot_root_fs->mount(boot_device); - - if (do_mount("/", boot_root_fs) != 0) - { - kapi::system::panic("[FILESYSTEM] failed to mount root filesystem."); - } - - auto device_fs = kstd::make_shared(); - device_fs->mount(nullptr); - - if (do_mount("/dev", device_fs) != 0) - { - kapi::system::panic("[FILESYSTEM] failed to mount devfs at /dev."); - } - } - else - { - // TODO BA-FS26 ?? what when no boot_device == no modules loaded?? + do_mount_internal("/", root_fs_root_dentry, boot_root_fs); } + + auto device_fs = kstd::make_shared(); + device_fs->mount(nullptr); + do_mount_internal("/dev", root_fs_root_dentry, device_fs); } auto vfs::get() -> vfs & @@ -107,77 +95,69 @@ namespace filesystem return -1; // TODO BA-FS26 panic or errorcode? } - if (auto mount_dentry = resolve_path(path)) + if (auto mount_point_dentry = resolve_path(path)) { - // TODO BA-FS26 check if mount point is already mounted and handle it (unmount old fs, fail, etc.) - - auto new_fs_root = kstd::make_shared(mount_dentry, filesystem->root_inode()); - auto new_mount = kstd::make_shared(mount_dentry, new_fs_root, filesystem); - m_mount_table.add_mount(new_mount); - mount_dentry->set_flag(dentry::dentry_flags::dcache_mounted); - + do_mount_internal(path, mount_point_dentry, filesystem); return 0; } return -1; } + auto vfs::do_mount_internal(std::string_view path, kstd::shared_ptr const & mount_point_dentry, + kstd::shared_ptr const & fs) -> void + { + // TODO BA-FS26 check if mount point is already mounted and handle it (unmount old fs, fail, etc.) + auto new_fs_root = kstd::make_shared(mount_point_dentry, fs->root_inode()); + auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, path); + m_mount_table.add_mount(new_mount); + } + auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr { // TODO BA-FS26 implement full path resolution semantics. // TODO BA-FS26 better path validation // TODO BA-FS26 implement a path parser (maybe in libs?) and use it here and in do_mount - if (!path.empty() && path.front() == '/') + if (path.empty() || path.front() != '/') + return nullptr; + + // TODO BA-FS26 longest match in mount_table -> jump into final fs directly + // TODO BA-FS26 performance optimization first check mounted_flag O(1) then check mount_table O(n) + + auto best_mount = m_mount_table.find_longest_prefix_mount(path); + if (!best_mount) { - auto root_mount = m_mount_table.get_root_mount(); - if (!root_mount) - { - kapi::system::panic("[FILESYSTEM] no root mount found."); - } + kapi::system::panic("[FILESYSTEM] no root mount found."); + } - auto current_dentry = root_mount->root_dentry(); - auto current_fs = root_mount->get_filesystem(); + auto current_dentry = best_mount->root_dentry(); + auto current_fs = best_mount->get_filesystem(); - auto path_parts = - std::views::split(path, '/') | std::views::filter([](auto const & part) { return !part.empty(); }); - for (auto const & part : path_parts) + std::string_view remaining = path.substr(best_mount->get_mount_path().size()); + + auto path_parts = + std::views::split(remaining, '/') | std::views::filter([](auto const & part) { return !part.empty(); }); + + for (auto const & part : path_parts) + { + std::string_view part_view{part}; + + auto next_dentry = current_dentry->find_child(part_view); + if (!next_dentry) { - std::string_view part_view{part}; - auto next_dentry = current_dentry->find_child(part_view); - - if (!next_dentry) - { - if (auto found_inode = current_fs->lookup(current_dentry->get_inode(), part_view)) - { - next_dentry = kstd::make_shared(current_dentry, found_inode, part_view); - current_dentry->add_child(next_dentry); - } - else - { - return nullptr; - } - } - - // TODO BA-FS26 use while to allow stacked mounts? - if (next_dentry->has_flag(dentry::dentry_flags::dcache_mounted)) - { - auto found_mount = m_mount_table.find_mount_by_dentry(next_dentry); - if (!found_mount) - kapi::system::panic("[FILESYSTEM] dcache_mounted set but no covering mount found."); - current_fs = found_mount->get_filesystem(); - current_dentry = found_mount->root_dentry(); - } - else - { - current_dentry = next_dentry; - } + auto found_inode = current_fs->lookup(current_dentry->get_inode(), part_view); + if (!found_inode) + return nullptr; + + next_dentry = kstd::make_shared(current_dentry, found_inode, part_view); + current_dentry->add_child(next_dentry); } - return current_dentry; + current_dentry = next_dentry; } - return nullptr; + return current_dentry; } } // namespace filesystem \ No newline at end of file -- cgit v1.2.3 From 1f0d290bc303ac8f039963c4eb6421536d36827c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 28 Mar 2026 17:29:31 +0100 Subject: string tests --- libs/kstd/tests/src/string.cpp | 84 ++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/libs/kstd/tests/src/string.cpp b/libs/kstd/tests/src/string.cpp index 43e9a6b..a94f0f6 100644 --- a/libs/kstd/tests/src/string.cpp +++ b/libs/kstd/tests/src/string.cpp @@ -386,60 +386,74 @@ SCENARIO("String iteration", "[string]") REQUIRE(str.back() == 'b'); } } - } - - GIVEN("A const string") - { - auto const str = kstd::string{"Blub"}; - WHEN("iterating over the characters of the string as string_view using a range-based for loop") + GIVEN("A non-empty string") { - kstd::string result; - - for (auto ch : str.view()) - { - result.push_back(ch); - } + auto str = kstd::string{"Hello"}; - THEN("the iterated characters are the same as the characters in the string") + WHEN("using const end()") { - REQUIRE(result == str.view()); + auto it = str.end(); // calls const end() + THEN("it points past the last character") + { + REQUIRE(*std::prev(it) == 'o'); + REQUIRE(std::distance(str.begin(), it) == str.size()); + } } } - WHEN("using front and back to access the first and last characters of the string") + GIVEN("A const string") { - THEN("front returns the first character of the string") + auto const str = kstd::string{"Blub"}; + + WHEN("iterating over the characters of the string as string_view using a range-based for loop") { - REQUIRE(str.front() == 'B'); + kstd::string result; + + for (auto ch : str.view()) + { + result.push_back(ch); + } + + THEN("the iterated characters are the same as the characters in the string") + { + REQUIRE(result == str.view()); + } } - THEN("back returns the last character of the string") + WHEN("using front and back to access the first and last characters of the string") { - REQUIRE(str.back() == 'b'); + THEN("front returns the first character of the string") + { + REQUIRE(str.front() == 'B'); + } + + THEN("back returns the last character of the string") + { + REQUIRE(str.back() == 'b'); + } } } - } - - GIVEN("An empty string") - { - auto str = kstd::string{}; - WHEN("iterating over the characters of an empty string") + GIVEN("An empty string") { - kstd::string result; + auto str = kstd::string{}; - for (auto ch : str.view()) + WHEN("iterating over the characters of an empty string") { - result.push_back(ch); - } + kstd::string result; - THEN("no characters are iterated and the result is an empty string") - { - REQUIRE(result.empty()); - REQUIRE(result.size() == 0); - REQUIRE(result.view() == std::string_view{}); + for (auto ch : str.view()) + { + result.push_back(ch); + } + + THEN("no characters are iterated and the result is an empty string") + { + REQUIRE(result.empty()); + REQUIRE(result.size() == 0); + REQUIRE(result.view() == std::string_view{}); + } } } } -} -- cgit v1.2.3 From 09300635a916b67c027f8b6c1823db944f578669 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 28 Mar 2026 17:34:08 +0100 Subject: improve open and mount tests --- kernel/src/main.cpp | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 19a0992..4510240 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -111,16 +111,32 @@ auto test_file_lookup() -> void auto vfs = filesystem::vfs::get(); auto storage_mgmt = devices::storage::storage_management::get(); - vfs.open("/a/b/c"); - vfs.open("/a/d/e"); + auto ofd1 = vfs.open("/a/b/c"); + auto ofd2 = vfs.open("/dev/ram0"); + auto ofd3 = vfs.open("/a/d/e"); + if (!ofd1 || !ofd2 || !ofd3) + { + kstd::os::panic("test code failed"); + } + + if (auto ofd4 = vfs.open("/dev/xxx")) + { + kstd::os::panic("test code failed"); + } auto new_filesystem = kstd::make_shared(); auto device = storage_mgmt.device_by_major_minor(1, 16); new_filesystem->mount(device); - vfs.do_mount("/a/b", new_filesystem); - vfs.open("/a/b/c"); + if (vfs.do_mount("/a/b", new_filesystem) != 0) + { + kstd::os::panic("test code failed"); + } + auto ofd5 = vfs.open("/a/b/c"); - vfs.open("x/y/z"); + if (auto ofd6 = vfs.open("x/y/z")) + { + kstd::os::panic("test code failed"); + } } auto run_test_code() -> void -- cgit v1.2.3 From ddfa0cd69ec06b2ccaae36cb8dd676a324742b9c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 28 Mar 2026 17:37:17 +0100 Subject: Revert "string tests" This reverts commit 1f0d290bc303ac8f039963c4eb6421536d36827c. --- libs/kstd/tests/src/string.cpp | 84 ++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 49 deletions(-) diff --git a/libs/kstd/tests/src/string.cpp b/libs/kstd/tests/src/string.cpp index a94f0f6..43e9a6b 100644 --- a/libs/kstd/tests/src/string.cpp +++ b/libs/kstd/tests/src/string.cpp @@ -386,74 +386,60 @@ SCENARIO("String iteration", "[string]") REQUIRE(str.back() == 'b'); } } + } + + GIVEN("A const string") + { + auto const str = kstd::string{"Blub"}; - GIVEN("A non-empty string") + WHEN("iterating over the characters of the string as string_view using a range-based for loop") { - auto str = kstd::string{"Hello"}; + kstd::string result; + + for (auto ch : str.view()) + { + result.push_back(ch); + } - WHEN("using const end()") + THEN("the iterated characters are the same as the characters in the string") { - auto it = str.end(); // calls const end() - THEN("it points past the last character") - { - REQUIRE(*std::prev(it) == 'o'); - REQUIRE(std::distance(str.begin(), it) == str.size()); - } + REQUIRE(result == str.view()); } } - GIVEN("A const string") + WHEN("using front and back to access the first and last characters of the string") { - auto const str = kstd::string{"Blub"}; - - WHEN("iterating over the characters of the string as string_view using a range-based for loop") + THEN("front returns the first character of the string") { - kstd::string result; - - for (auto ch : str.view()) - { - result.push_back(ch); - } - - THEN("the iterated characters are the same as the characters in the string") - { - REQUIRE(result == str.view()); - } + REQUIRE(str.front() == 'B'); } - WHEN("using front and back to access the first and last characters of the string") + THEN("back returns the last character of the string") { - THEN("front returns the first character of the string") - { - REQUIRE(str.front() == 'B'); - } - - THEN("back returns the last character of the string") - { - REQUIRE(str.back() == 'b'); - } + REQUIRE(str.back() == 'b'); } } + } + + GIVEN("An empty string") + { + auto str = kstd::string{}; - GIVEN("An empty string") + WHEN("iterating over the characters of an empty string") { - auto str = kstd::string{}; + kstd::string result; - WHEN("iterating over the characters of an empty string") + for (auto ch : str.view()) { - kstd::string result; - - for (auto ch : str.view()) - { - result.push_back(ch); - } + result.push_back(ch); + } - THEN("no characters are iterated and the result is an empty string") - { - REQUIRE(result.empty()); - REQUIRE(result.size() == 0); - REQUIRE(result.view() == std::string_view{}); - } + THEN("no characters are iterated and the result is an empty string") + { + REQUIRE(result.empty()); + REQUIRE(result.size() == 0); + REQUIRE(result.view() == std::string_view{}); } } } +} -- cgit v1.2.3 From fe964c2c9d3fb29223060f99c07ae1ce4e69daae Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 28 Mar 2026 19:14:02 +0100 Subject: set flag (currently not needed) --- kernel/src/filesystem/vfs.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index da7fa05..7a90531 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -111,6 +111,7 @@ namespace filesystem auto new_fs_root = kstd::make_shared(mount_point_dentry, fs->root_inode()); auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, path); m_mount_table.add_mount(new_mount); + mount_point_dentry->set_flag(dentry::dentry_flags::dcache_mounted); } auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr -- cgit v1.2.3 From 8a9bf5a90b7f46d5c615b55a3fc418b419db4926 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 28 Mar 2026 19:14:26 +0100 Subject: improve test --- kernel/src/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 4510240..98c88f2 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -132,6 +132,10 @@ auto test_file_lookup() -> void kstd::os::panic("test code failed"); } auto ofd5 = vfs.open("/a/b/c"); + if (!ofd5) + { + kstd::os::panic("test code failed"); + } if (auto ofd6 = vfs.open("x/y/z")) { -- cgit v1.2.3 From 8c0488fb8df8742740eb8464a7ad51d71a24e416 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 29 Mar 2026 13:29:23 +0200 Subject: remove redundant ext2_file class --- kernel/CMakeLists.txt | 1 - kernel/include/kernel/filesystem/ext2/ext2_file.hpp | 15 --------------- kernel/include/kernel/filesystem/ext2/ext2_inode.hpp | 4 ---- kernel/src/filesystem/ext2/ext2_file.cpp | 20 -------------------- 4 files changed, 40 deletions(-) delete mode 100644 kernel/include/kernel/filesystem/ext2/ext2_file.hpp delete mode 100644 kernel/src/filesystem/ext2/ext2_file.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index eb762ac..1a8eb99 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -23,7 +23,6 @@ add_executable("kernel" "src/devices/storage/ram_disk/ram_disk_device.cpp" "src/filesystem/devfs/devfs_filesystem.cpp" "src/filesystem/devfs/devfs_root_inode.cpp" - "src/filesystem/ext2/ext2_file.cpp" "src/filesystem/ext2/ext2_filesystem.cpp" "src/filesystem/ext2/ext2_inode.cpp" "src/filesystem/dentry.cpp" diff --git a/kernel/include/kernel/filesystem/ext2/ext2_file.hpp b/kernel/include/kernel/filesystem/ext2/ext2_file.hpp deleted file mode 100644 index e5357e3..0000000 --- a/kernel/include/kernel/filesystem/ext2/ext2_file.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILE_HPP - -#include - -namespace filesystem::ext2 -{ - struct ext2_file - { - auto read(void * buffer, size_t offset, size_t size) const -> size_t; - auto write(void const * buffer, size_t offset, size_t size) -> size_t; - }; -} // namespace filesystem::ext2 - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp index 5f4d16a..b697c93 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp @@ -1,7 +1,6 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP -#include "kernel/filesystem/ext2/ext2_file.hpp" #include "kernel/filesystem/inode.hpp" #include @@ -16,9 +15,6 @@ namespace filesystem::ext2 auto read(void * buffer, size_t offset, size_t size) const -> size_t override; auto write(void const * buffer, size_t offset, size_t size) -> size_t override; - - private: - kstd::shared_ptr m_file; }; } // namespace filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/ext2_file.cpp b/kernel/src/filesystem/ext2/ext2_file.cpp deleted file mode 100644 index 7217c77..0000000 --- a/kernel/src/filesystem/ext2/ext2_file.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "kernel/filesystem/ext2/ext2_file.hpp" - -#include "kapi/system.hpp" - -#include - -namespace filesystem::ext2 -{ - auto ext2_file::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t - { - kapi::system::panic("[FILESYSTEM] ext2_file::read is not implemented yet."); - return 0; - } - - auto ext2_file::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t - { - kapi::system::panic("[FILESYSTEM] ext2_file::write is not implemented yet."); - return 0; - } -} // namespace filesystem::ext2 -- cgit v1.2.3 From fd6e5899f6e0dc238a7944772e35d0ec6b738a38 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sun, 29 Mar 2026 16:56:30 +0200 Subject: Refactor --- kernel/include/kernel/filesystem/ext2/ext2_inode.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp index b697c93..3f6a3b0 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp @@ -11,7 +11,7 @@ namespace filesystem::ext2 { struct ext2_inode : inode { - explicit ext2_inode(); + ext2_inode(); auto read(void * buffer, size_t offset, size_t size) const -> size_t override; auto write(void const * buffer, size_t offset, size_t size) -> size_t override; -- cgit v1.2.3 From a64b9daefeee0f91fe74d6df394944b71ecc7d80 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sun, 29 Mar 2026 16:57:19 +0200 Subject: Add ext2_superblock definiton --- .../kernel/filesystem/ext2/ext2_superblock.hpp | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 kernel/include/kernel/filesystem/ext2/ext2_superblock.hpp diff --git a/kernel/include/kernel/filesystem/ext2/ext2_superblock.hpp b/kernel/include/kernel/filesystem/ext2/ext2_superblock.hpp new file mode 100644 index 0000000..cd77cd4 --- /dev/null +++ b/kernel/include/kernel/filesystem/ext2/ext2_superblock.hpp @@ -0,0 +1,76 @@ +#ifndef EXT2_SUPERBLOCK_HPP +#define EXT2_SUPERBLOCK_HPP + +#include +#include + +namespace filesystem::ext2 +{ + struct ext2_superblock + { + uint32_t inodes_count; + uint32_t blocks_count; + uint32_t reserved_blocks_count; + uint32_t free_blocks_count; + uint32_t free_inodes_count; + uint32_t first_data_block; + uint32_t log_block_size; + uint32_t log_frag_size; + uint32_t blocks_per_group; + uint32_t frags_per_group; + uint32_t inodes_per_group; + uint32_t mtime; + uint32_t wtime; + uint16_t mnt_count; + uint16_t max_mnt_count; + uint16_t magic; + uint16_t state; + uint16_t errors; + uint16_t minor_rev_level; + uint32_t lastcheck; + uint32_t checkinterval; + uint32_t creator_os; + uint32_t rev_level; + uint16_t def_resuid; + uint16_t def_resgid; + + // EXT2_DYNAMIC_REV superblock only + uint32_t first_ino; + uint16_t inode_size; + uint16_t block_group_nr; + uint32_t feature_compat; + uint32_t feature_incompat; + uint32_t feature_ro_compat; + // uint8_t uuid[16]; // TODO BA-FS26 really correct? + std::array uuid; + // uint8_t volume_name[16]; // TODO BA-FS26 really correct? + std::array volume_name; + // uint8_t last_mounted[64]; // TODO BA-FS26 really correct? + std::array last_mounted; + uint32_t algorithm_usage_bitmap; + + // Performance Hints + uint8_t prealloc_blocks; + uint8_t prealloc_dir_blocks; + uint16_t padding1; + + // Journaling Support + // uint8_t journal_uuid[16]; // TODO BA-FS26 really correct? + std::array journal_uuid; + uint32_t journal_inum; + uint32_t journal_dev; + uint32_t last_orphan; + + // Directory Indexing Support + // uint32_t hash_seed[4]; // TODO BA-FS26 really correct? + std::array hash_seed; + uint8_t def_hash_version; + std::array padding2; + + // Other options + uint32_t default_mount_options; + uint32_t first_meta_bg; + std::array unused; // NOLINT(readability-magic-numbers) + }; +} // namespace filesystem::ext2 +#endif \ No newline at end of file -- cgit v1.2.3 From b03b6b851082d91ca374fed3e8a070d278eb93ab Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sun, 29 Mar 2026 17:22:55 +0200 Subject: Add ext2 block_group_descriptor, inode and linked_directory_entry definitions --- .../ext2/ext2_block_group_descriptor.hpp | 21 ++++++++++++++++++++ .../include/kernel/filesystem/ext2/ext2_inode.hpp | 23 ++++++++++++++++++++++ .../ext2/ext2_linked_directory_entry.hpp | 20 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 kernel/include/kernel/filesystem/ext2/ext2_block_group_descriptor.hpp create mode 100644 kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp diff --git a/kernel/include/kernel/filesystem/ext2/ext2_block_group_descriptor.hpp b/kernel/include/kernel/filesystem/ext2/ext2_block_group_descriptor.hpp new file mode 100644 index 0000000..b0966da --- /dev/null +++ b/kernel/include/kernel/filesystem/ext2/ext2_block_group_descriptor.hpp @@ -0,0 +1,21 @@ +#ifndef EXT2_BLOCK_GROUP_DESCRIPTOR_HPP +#define EXT2_BLOCK_GROUP_DESCRIPTOR_HPP + +#include +#include + +namespace filesystem::ext2 +{ + struct ext2_block_group_descriptor + { + uint32_t block_bitmap; + uint32_t inode_bitmap; + uint32_t inode_table; + uint16_t free_blocks_count; + uint16_t free_inodes_count; + uint16_t used_dirs_count; + std::array padding; + std::array reserved; // NOLINT(readability-magic-numbers) + }; +} // namespace filesystem::ext2 +#endif // EXT2_BLOCK_GROUP_DESCRIPTOR_HPP \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp index 3f6a3b0..c35f84c 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp @@ -5,7 +5,9 @@ #include +#include #include +#include namespace filesystem::ext2 { @@ -15,6 +17,27 @@ namespace filesystem::ext2 auto read(void * buffer, size_t offset, size_t size) const -> size_t override; auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + uint16_t mode; + uint16_t uid; + uint32_t size; + uint32_t atime; + uint32_t ctime; + uint32_t mtime; + uint32_t dtime; + uint16_t gid; + uint16_t links_count; + uint32_t blocks; + uint32_t flags; + uint32_t osd1; + // uint32_t block[15]; // TODO BA-FS26 really correct? + std::array block; // NOLINT(readability-magic-numbers) + uint32_t generation; + uint32_t file_acl; + uint32_t dir_acl; + uint32_t faddr; + // uint8_t osd2[12]; // TODO BA-FS26 really correct? + std::array osd2; // NOLINT(readability-magic-numbers) }; } // namespace filesystem::ext2 diff --git a/kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp b/kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp new file mode 100644 index 0000000..2a8872b --- /dev/null +++ b/kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp @@ -0,0 +1,20 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_LINKED_DIRECTORY_ENTRY_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_LINKED_DIRECTORY_ENTRY_HPP + +#include +#include + +namespace filesystem::ext2 +{ + struct ext2_linked_directory_entry + { + uint32_t inode; + uint16_t rec_len; + uint8_t name_len; + uint8_t file_type; + uint8_t pad; + std::array name; + }; +} // namespace filesystem::ext2 + +#endif \ No newline at end of file -- cgit v1.2.3 From 3888a9f90301003780fc02ce317fde29e1c20c1f Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sun, 29 Mar 2026 18:36:53 +0200 Subject: Fix name length --- kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp b/kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp index 2a8872b..3128617 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp @@ -13,7 +13,7 @@ namespace filesystem::ext2 uint8_t name_len; uint8_t file_type; uint8_t pad; - std::array name; + std::array name; // NOLINT(readability-magic-numbers) }; } // namespace filesystem::ext2 -- cgit v1.2.3 From 9c602f2cf8fd87f55adc31c085e469e72b7cbbfa Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sun, 29 Mar 2026 20:49:03 +0200 Subject: Move block device offset and size-to-blocks calculation to block_device_utils --- kernel/CMakeLists.txt | 1 + .../include/kernel/devices/block_device_utils.hpp | 17 ++++ kernel/include/kernel/filesystem/device_inode.hpp | 5 - kernel/src/devices/block_device_utils.cpp | 103 +++++++++++++++++++++ kernel/src/filesystem/device_inode.cpp | 76 +-------------- kernel/src/filesystem/ext2/ext2_filesystem.cpp | 2 + 6 files changed, 126 insertions(+), 78 deletions(-) create mode 100644 kernel/include/kernel/devices/block_device_utils.hpp create mode 100644 kernel/src/devices/block_device_utils.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 1a8eb99..5f9a019 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -17,6 +17,7 @@ add_executable("kernel" "src/memory.cpp" "src/devices/device.cpp" "src/devices/block_device.cpp" + "src/devices/block_device_utils.cpp" "src/devices/storage/storage_controller.cpp" "src/devices/storage/storage_management.cpp" "src/devices/storage/ram_disk/ram_disk_controller.cpp" diff --git a/kernel/include/kernel/devices/block_device_utils.hpp b/kernel/include/kernel/devices/block_device_utils.hpp new file mode 100644 index 0000000..5ec69d1 --- /dev/null +++ b/kernel/include/kernel/devices/block_device_utils.hpp @@ -0,0 +1,17 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_UTILS_HPP +#define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_UTILS_HPP + +#include "kernel/devices/device.hpp" + +#include + +#include + +namespace devices::block_device_utils +{ + auto read(kstd::shared_ptr const & device, void * buffer, size_t offset, size_t size) -> size_t; + auto write(kstd::shared_ptr const & device, void const * buffer, size_t offset, size_t size) + -> size_t; +} // namespace devices::block_device_utils + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index 1cf08d4..0477969 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -1,7 +1,6 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP -#include "kernel/devices/block_device.hpp" #include "kernel/devices/device.hpp" #include "kernel/filesystem/inode.hpp" @@ -21,10 +20,6 @@ namespace filesystem [[nodiscard]] auto device() const -> kstd::shared_ptr const &; private: - using block_op = void (*)(size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, - std::byte * scratch, void * buffer); - auto process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t; - kstd::shared_ptr m_device; }; } // namespace filesystem diff --git a/kernel/src/devices/block_device_utils.cpp b/kernel/src/devices/block_device_utils.cpp new file mode 100644 index 0000000..9d3af1b --- /dev/null +++ b/kernel/src/devices/block_device_utils.cpp @@ -0,0 +1,103 @@ +#include "kernel/devices/block_device_utils.hpp" + +#include "kapi/system.hpp" + +#include "kernel/devices/block_device.hpp" +#include "kernel/devices/device.hpp" + +#include +#include +#include + +#include +#include + +namespace devices::block_device_utils +{ + + using block_op = void (*)(size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, + std::byte * scratch, void * buffer); + + auto process_blocks(kstd::shared_ptr const & device, size_t offset, size_t size, void * buffer, + block_op op) -> size_t + { + if (buffer == nullptr) + { + kapi::system::panic("[FILESYSTEM] device_file::process_blocks called with null buffer."); + } + + if (size == 0) + { + return 0; + } + + auto * block_dev = static_cast(device.get()); + if (block_dev == nullptr) + { + kapi::system::panic("[FILESYSTEM] device_file: expected block_device."); + } + + size_t const block_size = block_dev->block_size(); + size_t const capacity = block_dev->capacity(); + + if (offset >= capacity) + return 0; + size_t const total_to_process = std::min(size, capacity - offset); + + kstd::vector scratch_buffer{block_size}; + auto processed = 0uz; + + while (processed < total_to_process) + { + size_t const absolute_offset = offset + processed; + size_t const block_index = absolute_offset / block_size; + size_t const in_block_offset = absolute_offset % block_size; + size_t const chunk_size = std::min(total_to_process - processed, block_size - in_block_offset); + + op(block_index, in_block_offset, chunk_size, processed, block_dev, scratch_buffer.data(), buffer); + + processed += chunk_size; + } + + return processed; + } + + auto read(kstd::shared_ptr const & device, void * buffer, size_t offset, size_t size) -> size_t + { + return process_blocks(device, offset, size, buffer, + [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, + std::byte * scratch, void * buffer) { + auto * out = static_cast(buffer); + if (off == 0 && len == device->block_size()) + { + device->read_block(idx, out + done); + } + else + { + device->read_block(idx, scratch); + kstd::libc::memcpy(out + done, scratch + off, len); + } + }); + } + + auto write(kstd::shared_ptr const & device, void const * buffer, size_t offset, size_t size) + -> size_t + { + return process_blocks(device, offset, size, const_cast(buffer), + [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, + std::byte * scratch, void * buffer) { + auto const * in = static_cast(buffer); + if (off == 0 && len == device->block_size()) + { + device->write_block(idx, in + done); + } + else + { + device->read_block(idx, scratch); + kstd::libc::memcpy(scratch + off, in + done, len); + device->write_block(idx, scratch); + } + }); + } + +} // namespace devices::block_device_utils \ No newline at end of file diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index 64cd6e9..da062fc 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -2,7 +2,7 @@ #include "kapi/system.hpp" -#include "kernel/devices/block_device.hpp" +#include "kernel/devices/block_device_utils.hpp" #include "kernel/devices/device.hpp" #include "kernel/filesystem/inode.hpp" @@ -10,7 +10,6 @@ #include #include -#include #include namespace filesystem @@ -29,20 +28,7 @@ namespace filesystem { if (m_device->is_block_device()) { - return process_blocks(offset, size, buffer, - [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, - std::byte * scratch, void * buffer) { - auto * out = static_cast(buffer); - if (off == 0 && len == device->block_size()) - { - device->read_block(idx, out + done); - } - else - { - device->read_block(idx, scratch); - kstd::libc::memcpy(out + done, scratch + off, len); - } - }); + return devices::block_device_utils::read(m_device, buffer, offset, size); } else { @@ -54,21 +40,7 @@ namespace filesystem { if (m_device->is_block_device()) { - return process_blocks(offset, size, const_cast(buffer), - [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, - std::byte * scratch, void * buffer) { - auto const * in = static_cast(buffer); - if (off == 0 && len == device->block_size()) - { - device->write_block(idx, in + done); - } - else - { - device->read_block(idx, scratch); - kstd::libc::memcpy(scratch + off, in + done, len); - device->write_block(idx, scratch); - } - }); + return devices::block_device_utils::write(m_device, buffer, offset, size); } else { @@ -81,46 +53,4 @@ namespace filesystem return m_device; } - auto device_inode::process_blocks(size_t offset, size_t size, void * buffer, block_op op) const -> size_t - { - if (buffer == nullptr) - { - kapi::system::panic("[FILESYSTEM] device_file::process_blocks called with null buffer."); - } - - if (size == 0) - { - return 0; - } - - auto * block_dev = static_cast(m_device.get()); - if (block_dev == nullptr) - { - kapi::system::panic("[FILESYSTEM] device_file: expected block_device."); - } - - size_t const block_size = block_dev->block_size(); - size_t const capacity = block_dev->capacity(); - - if (offset >= capacity) - return 0; - size_t const total_to_process = std::min(size, capacity - offset); - - kstd::vector scratch_buffer{block_size}; - auto processed = 0uz; - - while (processed < total_to_process) - { - size_t const absolute_offset = offset + processed; - size_t const block_index = absolute_offset / block_size; - size_t const in_block_offset = absolute_offset % block_size; - size_t const chunk_size = std::min(total_to_process - processed, block_size - in_block_offset); - - op(block_index, in_block_offset, chunk_size, processed, block_dev, scratch_buffer.data(), buffer); - - processed += chunk_size; - } - - return processed; - } } // namespace filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/ext2/ext2_filesystem.cpp b/kernel/src/filesystem/ext2/ext2_filesystem.cpp index 373c6a2..19f1014 100644 --- a/kernel/src/filesystem/ext2/ext2_filesystem.cpp +++ b/kernel/src/filesystem/ext2/ext2_filesystem.cpp @@ -1,5 +1,6 @@ #include "kernel/filesystem/ext2/ext2_filesystem.hpp" +#include "kernel/devices/block_device_utils.hpp" #include "kernel/devices/device.hpp" #include "kernel/filesystem/ext2/ext2_inode.hpp" #include "kernel/filesystem/filesystem.hpp" @@ -19,6 +20,7 @@ namespace filesystem::ext2 // TODO BA-FS26 implement m_root_inode = kstd::make_shared(); + // devices::block_device_utils::read(device, nullptr, 0, 0); // TODO BA-FS26 just for testing return 0; } -- cgit v1.2.3 From ea578b348f63939bb07bd669ad86d3fe1b6d5f65 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Sun, 29 Mar 2026 21:30:32 +0200 Subject: Save WIP add some definitions and helper functions --- kernel/src/filesystem/ext2/ext2_filesystem.cpp | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/kernel/src/filesystem/ext2/ext2_filesystem.cpp b/kernel/src/filesystem/ext2/ext2_filesystem.cpp index 19f1014..e176ade 100644 --- a/kernel/src/filesystem/ext2/ext2_filesystem.cpp +++ b/kernel/src/filesystem/ext2/ext2_filesystem.cpp @@ -3,15 +3,48 @@ #include "kernel/devices/block_device_utils.hpp" #include "kernel/devices/device.hpp" #include "kernel/filesystem/ext2/ext2_inode.hpp" +#include "kernel/filesystem/ext2/ext2_superblock.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include +#include +#include #include namespace filesystem::ext2 { + namespace + { + constexpr size_t SUPERBLOCK_OFFSET = 1024; + constexpr uint16_t EXT2_MAGIC = 0xEF53; + + // Mode bits + constexpr uint16_t S_IFMT = 0xF000; + constexpr uint16_t S_IFREG = 0x8000; + constexpr uint16_t S_IFDIR = 0x4000; + + auto S_ISREG(uint16_t mode) -> bool + { + return (mode & S_IFMT) == S_IFREG; + } + auto S_ISDIR(uint16_t mode) -> bool + { + return (mode & S_IFMT) == S_IFDIR; + } + + auto get_block_size(ext2_superblock const & superblock) -> size_t + { + return 1024U << superblock.log_block_size; + } + + auto get_inode_size(ext2_superblock const & superblock) -> size_t + { + return superblock.rev_level == 0 ? 128 : superblock.inode_size; + } + } // namespace + auto ext2_filesystem::mount(kstd::shared_ptr const & device) -> int { filesystem::mount(device); // TODO BA-FS26 error handling? -- cgit v1.2.3 From c946cf6a89bbeae7fb96a67b55d91b7ae0cfa48d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 30 Mar 2026 12:41:14 +0000 Subject: kstd/flat_map: fix iterator reference --- libs/kstd/include/kstd/bits/flat_map.hpp | 108 ++++++++++++++++++++++++++++++- libs/kstd/tests/src/flat_map.cpp | 25 +++++++ 2 files changed, 131 insertions(+), 2 deletions(-) diff --git a/libs/kstd/include/kstd/bits/flat_map.hpp b/libs/kstd/include/kstd/bits/flat_map.hpp index 903841e..9455549 100644 --- a/libs/kstd/include/kstd/bits/flat_map.hpp +++ b/libs/kstd/include/kstd/bits/flat_map.hpp @@ -1,27 +1,107 @@ #ifndef KSTD_BITS_FLAT_MAP_HPP #define KSTD_BITS_FLAT_MAP_HPP +#include #include #include +#include +#include +#include #include namespace kstd::bits { + template + struct flat_map_reference + { + using key_type = KeyType; + using mapped_type = MappedType; + + constexpr flat_map_reference(key_type const & key, mapped_type & mapped) + : first{key} + , second{mapped} + {} + + constexpr auto operator=(flat_map_reference const & other) const -> flat_map_reference const & + { + second = other.second; + return *this; + } + + constexpr auto operator=(flat_map_reference && other) const -> flat_map_reference const & + { + second = std::move(other.second); + return *this; + } + + template + requires(std::tuple_size_v> == 2) + constexpr auto operator=(TupleLikeType && tuple) const -> flat_map_reference const & + { + second = std::forward(tuple).second; + return *this; + } + + template + requires(Index >= 0 && Index <= 1) + constexpr auto get() const noexcept -> decltype(auto) + { + if constexpr (Index == 0) + { + return (first); + } + else + { + return (second); + } + } + + key_type const & first; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) + mapped_type & second; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) + }; + + template + struct flat_map_pointer + { + Reference reference; + + [[nodiscard]] constexpr auto operator->() noexcept -> Reference * + { + return std::addressof(reference); + } + + [[nodiscard]] constexpr auto operator->() const noexcept -> Reference const * + { + return std::addressof(reference); + } + }; + template struct flat_map_iterator { using iterator_category = std::random_access_iterator_tag; using value_type = std::pair; using difference_type = std::ptrdiff_t; - using reference = std::pair; - using pointer = void; + using reference = flat_map_reference; + using pointer = flat_map_pointer; + + constexpr flat_map_iterator() = default; constexpr flat_map_iterator(KeyIterator key_iterator, MappedIterator mapped_iterator) : m_key_iterator{key_iterator} , m_mapped_iterator{mapped_iterator} {} + template + requires(std::convertible_to && + std::convertible_to) + constexpr flat_map_iterator( + flat_map_iterator const & other) noexcept + : m_key_iterator{other.m_key_iterator} + , m_mapped_iterator{other.m_mapped_iterator} + {} + [[nodiscard]] auto key_iterator() const noexcept -> KeyIterator { return m_key_iterator; @@ -32,6 +112,13 @@ namespace kstd::bits return {*m_key_iterator, *m_mapped_iterator}; } + [[nodiscard]] constexpr auto operator->() const noexcept -> pointer + { + return { + {*m_key_iterator, *m_mapped_iterator} + }; + } + constexpr auto operator++() noexcept -> flat_map_iterator & { ++m_key_iterator; @@ -79,4 +166,21 @@ namespace kstd::bits } // namespace kstd::bits +template +struct std::tuple_size> : std::integral_constant +{ +}; + +template +struct std::tuple_element<0, kstd::bits::flat_map_reference> +{ + using type = K const &; +}; + +template +struct std::tuple_element<1, kstd::bits::flat_map_reference> +{ + using type = M &; +}; + #endif \ No newline at end of file diff --git a/libs/kstd/tests/src/flat_map.cpp b/libs/kstd/tests/src/flat_map.cpp index cde136a..eb599af 100644 --- a/libs/kstd/tests/src/flat_map.cpp +++ b/libs/kstd/tests/src/flat_map.cpp @@ -4,6 +4,8 @@ #include #include +#include +#include SCENARIO("Flat Map initialization and construction", "[flat_map]") { @@ -159,6 +161,29 @@ SCENARIO("Flat Map iterators", "[flat_map]") ++it; REQUIRE(it == map.cend()); } + + THEN("assignment through the proxy modifies the mapped value") + { + auto it = map.begin(); + + *it = std::pair{1, 100}; + + REQUIRE(it->second == 100); + REQUIRE(map.at(1) == 100); + } + + THEN("structured bindings evaluate correctly") + { + auto it = map.cbegin(); + + auto [key, value] = *it; + + REQUIRE(key == 1); + REQUIRE(value == 10); + + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + } } WHEN("using reverse iterators") -- cgit v1.2.3 From 7ad07a735759dc93b668ec92896f57c0c0df0025 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Mon, 30 Mar 2026 19:26:48 +0200 Subject: Fix linter warnings --- kernel/src/filesystem/ext2/ext2_filesystem.cpp | 44 +++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/kernel/src/filesystem/ext2/ext2_filesystem.cpp b/kernel/src/filesystem/ext2/ext2_filesystem.cpp index e176ade..ea6fe0d 100644 --- a/kernel/src/filesystem/ext2/ext2_filesystem.cpp +++ b/kernel/src/filesystem/ext2/ext2_filesystem.cpp @@ -17,32 +17,32 @@ namespace filesystem::ext2 { namespace { - constexpr size_t SUPERBLOCK_OFFSET = 1024; - constexpr uint16_t EXT2_MAGIC = 0xEF53; + // constexpr size_t SUPERBLOCK_OFFSET = 1024; + // constexpr uint16_t EXT2_MAGIC = 0xEF53; - // Mode bits - constexpr uint16_t S_IFMT = 0xF000; - constexpr uint16_t S_IFREG = 0x8000; - constexpr uint16_t S_IFDIR = 0x4000; + // // Mode bits + // constexpr uint16_t S_IFMT = 0xF000; + // constexpr uint16_t S_IFREG = 0x8000; + // constexpr uint16_t S_IFDIR = 0x4000; - auto S_ISREG(uint16_t mode) -> bool - { - return (mode & S_IFMT) == S_IFREG; - } - auto S_ISDIR(uint16_t mode) -> bool - { - return (mode & S_IFMT) == S_IFDIR; - } + // auto S_ISREG(uint16_t mode) -> bool + // { + // return (mode & S_IFMT) == S_IFREG; + // } + // auto S_ISDIR(uint16_t mode) -> bool + // { + // return (mode & S_IFMT) == S_IFDIR; + // } - auto get_block_size(ext2_superblock const & superblock) -> size_t - { - return 1024U << superblock.log_block_size; - } + // auto get_block_size(ext2_superblock const & superblock) -> size_t + // { + // return 1024U << superblock.log_block_size; + // } - auto get_inode_size(ext2_superblock const & superblock) -> size_t - { - return superblock.rev_level == 0 ? 128 : superblock.inode_size; - } + // auto get_inode_size(ext2_superblock const & superblock) -> size_t + // { + // return superblock.rev_level == 0 ? 128 : superblock.inode_size; + // } } // namespace auto ext2_filesystem::mount(kstd::shared_ptr const & device) -> int -- cgit v1.2.3 From 5603c7ec2b07dbc772fe2c20a9e9e176c5465c57 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Mon, 30 Mar 2026 21:22:22 +0200 Subject: Move everything in kernel into kernel namespace --- kernel/include/kernel/devices/block_device.hpp | 4 ++-- .../include/kernel/devices/block_device_utils.hpp | 4 ++-- kernel/include/kernel/devices/device.hpp | 4 ++-- .../storage/ram_disk/ram_disk_controller.hpp | 4 ++-- .../devices/storage/ram_disk/ram_disk_device.hpp | 4 ++-- .../kernel/devices/storage/storage_controller.hpp | 4 ++-- .../kernel/devices/storage/storage_management.hpp | 4 ++-- kernel/include/kernel/filesystem/dentry.hpp | 4 ++-- .../kernel/filesystem/devfs/devfs_filesystem.hpp | 4 ++-- .../kernel/filesystem/devfs/devfs_root_inode.hpp | 4 ++-- kernel/include/kernel/filesystem/device_inode.hpp | 4 ++-- .../ext2/ext2_block_group_descriptor.hpp | 4 ++-- .../kernel/filesystem/ext2/ext2_filesystem.hpp | 4 ++-- .../include/kernel/filesystem/ext2/ext2_inode.hpp | 4 ++-- .../ext2/ext2_linked_directory_entry.hpp | 4 ++-- .../kernel/filesystem/ext2/ext2_superblock.hpp | 4 ++-- .../kernel/filesystem/file_descriptor_table.hpp | 4 ++-- kernel/include/kernel/filesystem/filesystem.hpp | 4 ++-- kernel/include/kernel/filesystem/inode.hpp | 4 ++-- kernel/include/kernel/filesystem/mount.hpp | 4 ++-- kernel/include/kernel/filesystem/mount_table.hpp | 4 ++-- .../kernel/filesystem/open_file_description.hpp | 4 ++-- .../kernel/filesystem/rootfs/rootfs_filesystem.hpp | 4 ++-- .../kernel/filesystem/rootfs/rootfs_inode.hpp | 4 ++-- kernel/include/kernel/filesystem/vfs.hpp | 4 ++-- kernel/include/kernel/memory/heap_allocator.hpp | 2 -- kernel/src/devices/block_device.cpp | 4 ++-- kernel/src/devices/block_device_utils.cpp | 4 ++-- kernel/src/devices/device.cpp | 4 ++-- .../storage/ram_disk/ram_disk_controller.cpp | 4 ++-- .../devices/storage/ram_disk/ram_disk_device.cpp | 4 ++-- kernel/src/devices/storage/storage_controller.cpp | 4 ++-- kernel/src/devices/storage/storage_management.cpp | 4 ++-- kernel/src/filesystem/dentry.cpp | 4 ++-- kernel/src/filesystem/devfs/devfs_filesystem.cpp | 4 ++-- kernel/src/filesystem/devfs/devfs_root_inode.cpp | 4 ++-- kernel/src/filesystem/device_inode.cpp | 4 ++-- kernel/src/filesystem/ext2/ext2_filesystem.cpp | 4 ++-- kernel/src/filesystem/ext2/ext2_inode.cpp | 4 ++-- kernel/src/filesystem/file_descriptor_table.cpp | 4 ++-- kernel/src/filesystem/filesystem.cpp | 4 ++-- kernel/src/filesystem/inode.cpp | 4 ++-- kernel/src/filesystem/mount.cpp | 4 ++-- kernel/src/filesystem/mount_table.cpp | 4 ++-- kernel/src/filesystem/open_file_description.cpp | 4 ++-- kernel/src/filesystem/rootfs/rootfs_filesystem.cpp | 4 ++-- kernel/src/filesystem/rootfs/rootfs_inode.cpp | 4 ++-- kernel/src/filesystem/vfs.cpp | 4 ++-- kernel/src/main.cpp | 28 +++++++++++----------- 49 files changed, 108 insertions(+), 110 deletions(-) diff --git a/kernel/include/kernel/devices/block_device.hpp b/kernel/include/kernel/devices/block_device.hpp index e2026dd..619b815 100644 --- a/kernel/include/kernel/devices/block_device.hpp +++ b/kernel/include/kernel/devices/block_device.hpp @@ -7,7 +7,7 @@ #include -namespace devices +namespace kernel::devices { /** * @brief Base interface for block-addressable devices. @@ -92,6 +92,6 @@ namespace devices size_t m_block_size; }; -} // namespace devices +} // namespace kernel::devices #endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/block_device_utils.hpp b/kernel/include/kernel/devices/block_device_utils.hpp index 5ec69d1..bb49d04 100644 --- a/kernel/include/kernel/devices/block_device_utils.hpp +++ b/kernel/include/kernel/devices/block_device_utils.hpp @@ -7,11 +7,11 @@ #include -namespace devices::block_device_utils +namespace kernel::devices::block_device_utils { auto read(kstd::shared_ptr const & device, void * buffer, size_t offset, size_t size) -> size_t; auto write(kstd::shared_ptr const & device, void const * buffer, size_t offset, size_t size) -> size_t; -} // namespace devices::block_device_utils +} // namespace kernel::devices::block_device_utils #endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/device.hpp b/kernel/include/kernel/devices/device.hpp index 66cb7f8..67fa5ad 100644 --- a/kernel/include/kernel/devices/device.hpp +++ b/kernel/include/kernel/devices/device.hpp @@ -5,7 +5,7 @@ #include -namespace devices +namespace kernel::devices { /** * @brief Base device identified by a major, minor number and name. @@ -57,6 +57,6 @@ namespace devices size_t m_minor; kstd::string m_name; }; -} // namespace devices +} // namespace kernel::devices #endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/ram_disk/ram_disk_controller.hpp b/kernel/include/kernel/devices/storage/ram_disk/ram_disk_controller.hpp index 6f022e3..40763bf 100644 --- a/kernel/include/kernel/devices/storage/ram_disk/ram_disk_controller.hpp +++ b/kernel/include/kernel/devices/storage/ram_disk/ram_disk_controller.hpp @@ -5,7 +5,7 @@ #include "kernel/devices/storage/storage_controller.hpp" -namespace devices::storage::ram_disk +namespace kernel::devices::storage::ram_disk { /** * @brief Storage controller that exposes boot modules as RAM-disk devices. @@ -26,6 +26,6 @@ namespace devices::storage::ram_disk private: kapi::boot_modules::boot_module_registry const * m_boot_module_registry; }; -} // namespace devices::storage::ram_disk +} // namespace kernel::devices::storage::ram_disk #endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/ram_disk/ram_disk_device.hpp b/kernel/include/kernel/devices/storage/ram_disk/ram_disk_device.hpp index c323f4b..75ff50b 100644 --- a/kernel/include/kernel/devices/storage/ram_disk/ram_disk_device.hpp +++ b/kernel/include/kernel/devices/storage/ram_disk/ram_disk_device.hpp @@ -7,7 +7,7 @@ #include -namespace devices::storage::ram_disk +namespace kernel::devices::storage::ram_disk { /** * @brief Block device for a boot module. @@ -47,6 +47,6 @@ namespace devices::storage::ram_disk kapi::boot_modules::boot_module m_boot_module{}; }; -} // namespace devices::storage::ram_disk +} // namespace kernel::devices::storage::ram_disk #endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/storage_controller.hpp b/kernel/include/kernel/devices/storage/storage_controller.hpp index 58585fa..5639160 100644 --- a/kernel/include/kernel/devices/storage/storage_controller.hpp +++ b/kernel/include/kernel/devices/storage/storage_controller.hpp @@ -8,7 +8,7 @@ #include -namespace devices::storage +namespace kernel::devices::storage { /** * @brief Base interface for storage controllers. @@ -66,6 +66,6 @@ namespace devices::storage size_t m_minors_per_device{}; kstd::vector> m_devices{}; }; -} // namespace devices::storage +} // namespace kernel::devices::storage #endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/storage_management.hpp b/kernel/include/kernel/devices/storage/storage_management.hpp index 413820e..b6d1318 100644 --- a/kernel/include/kernel/devices/storage/storage_management.hpp +++ b/kernel/include/kernel/devices/storage/storage_management.hpp @@ -9,7 +9,7 @@ #include -namespace devices::storage +namespace kernel::devices::storage { /** * @brief Global storage subsystem manager. @@ -72,6 +72,6 @@ namespace devices::storage kstd::vector> m_controllers{}; }; -} // namespace devices::storage +} // namespace kernel::devices::storage #endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index c28246f..fc85a7d 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -10,7 +10,7 @@ #include #include -namespace filesystem +namespace kernel::filesystem { struct dentry { @@ -38,6 +38,6 @@ namespace filesystem kstd::shared_ptr m_inode; uint32_t m_flags{0}; }; -} // namespace filesystem +} // namespace kernel::filesystem #endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp b/kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp index 5559c2a..2330915 100644 --- a/kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp +++ b/kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp @@ -10,7 +10,7 @@ #include -namespace filesystem::devfs +namespace kernel::filesystem::devfs { struct devfs_filesystem : filesystem { @@ -20,6 +20,6 @@ namespace filesystem::devfs private: auto build_device_inode_table() -> void; }; -} // namespace filesystem::devfs +} // namespace kernel::filesystem::devfs #endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp b/kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp index b1d37ab..206fc13 100644 --- a/kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp +++ b/kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp @@ -5,7 +5,7 @@ #include -namespace filesystem::devfs +namespace kernel::filesystem::devfs { struct devfs_root_inode : inode { @@ -14,6 +14,6 @@ namespace filesystem::devfs auto read(void * buffer, size_t offset, size_t size) const -> size_t override; auto write(void const * buffer, size_t offset, size_t size) -> size_t override; }; -} // namespace filesystem::devfs +} // namespace kernel::filesystem::devfs #endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index 0477969..7f044b0 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -8,7 +8,7 @@ #include -namespace filesystem +namespace kernel::filesystem { struct device_inode : inode { @@ -22,6 +22,6 @@ namespace filesystem private: kstd::shared_ptr m_device; }; -} // namespace filesystem +} // namespace kernel::filesystem #endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/ext2_block_group_descriptor.hpp b/kernel/include/kernel/filesystem/ext2/ext2_block_group_descriptor.hpp index b0966da..0de7428 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_block_group_descriptor.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_block_group_descriptor.hpp @@ -4,7 +4,7 @@ #include #include -namespace filesystem::ext2 +namespace kernel::filesystem::ext2 { struct ext2_block_group_descriptor { @@ -17,5 +17,5 @@ namespace filesystem::ext2 std::array padding; std::array reserved; // NOLINT(readability-magic-numbers) }; -} // namespace filesystem::ext2 +} // namespace kernel::filesystem::ext2 #endif // EXT2_BLOCK_GROUP_DESCRIPTOR_HPP \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp b/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp index 1445e5a..753aea1 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp @@ -9,7 +9,7 @@ #include -namespace filesystem::ext2 +namespace kernel::filesystem::ext2 { struct ext2_filesystem : filesystem { @@ -18,6 +18,6 @@ namespace filesystem::ext2 private: }; -} // namespace filesystem::ext2 +} // namespace kernel::filesystem::ext2 #endif diff --git a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp index c35f84c..2054227 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp @@ -9,7 +9,7 @@ #include #include -namespace filesystem::ext2 +namespace kernel::filesystem::ext2 { struct ext2_inode : inode { @@ -39,6 +39,6 @@ namespace filesystem::ext2 // uint8_t osd2[12]; // TODO BA-FS26 really correct? std::array osd2; // NOLINT(readability-magic-numbers) }; -} // namespace filesystem::ext2 +} // namespace kernel::filesystem::ext2 #endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp b/kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp index 3128617..84e47e8 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp @@ -4,7 +4,7 @@ #include #include -namespace filesystem::ext2 +namespace kernel::filesystem::ext2 { struct ext2_linked_directory_entry { @@ -15,6 +15,6 @@ namespace filesystem::ext2 uint8_t pad; std::array name; // NOLINT(readability-magic-numbers) }; -} // namespace filesystem::ext2 +} // namespace kernel::filesystem::ext2 #endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/ext2_superblock.hpp b/kernel/include/kernel/filesystem/ext2/ext2_superblock.hpp index cd77cd4..aa93e68 100644 --- a/kernel/include/kernel/filesystem/ext2/ext2_superblock.hpp +++ b/kernel/include/kernel/filesystem/ext2/ext2_superblock.hpp @@ -4,7 +4,7 @@ #include #include -namespace filesystem::ext2 +namespace kernel::filesystem::ext2 { struct ext2_superblock { @@ -72,5 +72,5 @@ namespace filesystem::ext2 uint32_t first_meta_bg; std::array unused; // NOLINT(readability-magic-numbers) }; -} // namespace filesystem::ext2 +} // namespace kernel::filesystem::ext2 #endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/filesystem/file_descriptor_table.hpp index bc6fb24..91e2960 100644 --- a/kernel/include/kernel/filesystem/file_descriptor_table.hpp +++ b/kernel/include/kernel/filesystem/file_descriptor_table.hpp @@ -6,7 +6,7 @@ #include #include -namespace filesystem +namespace kernel::filesystem { struct file_descriptor_table { @@ -24,6 +24,6 @@ namespace filesystem kstd::vector> m_open_files{}; }; -} // namespace filesystem +} // namespace kernel::filesystem #endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index e069ced..c50a86c 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -9,7 +9,7 @@ #include -namespace filesystem +namespace kernel::filesystem { struct filesystem { @@ -26,6 +26,6 @@ namespace filesystem kstd::vector> m_inodes{}; }; -} // namespace filesystem +} // namespace kernel::filesystem #endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index 6d8f0d4..d97b5ab 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -3,7 +3,7 @@ #include -namespace filesystem +namespace kernel::filesystem { struct inode { @@ -28,6 +28,6 @@ namespace filesystem private: inode_kind m_kind{inode_kind::regular}; }; -} // namespace filesystem +} // namespace kernel::filesystem #endif diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index 0f37687..a054750 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -9,7 +9,7 @@ #include -namespace filesystem +namespace kernel::filesystem { struct mount { @@ -28,6 +28,6 @@ namespace filesystem kstd::shared_ptr m_root_dentry; kstd::shared_ptr m_filesystem{}; }; -} // namespace filesystem +} // namespace kernel::filesystem #endif diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp index 2cd66ea..6dc2218 100644 --- a/kernel/include/kernel/filesystem/mount_table.hpp +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -8,7 +8,7 @@ #include -namespace filesystem +namespace kernel::filesystem { struct mount_table { @@ -20,6 +20,6 @@ namespace filesystem private: kstd::vector> m_mounts; }; -} // namespace filesystem +} // namespace kernel::filesystem #endif diff --git a/kernel/include/kernel/filesystem/open_file_description.hpp b/kernel/include/kernel/filesystem/open_file_description.hpp index e17f9fe..45719cf 100644 --- a/kernel/include/kernel/filesystem/open_file_description.hpp +++ b/kernel/include/kernel/filesystem/open_file_description.hpp @@ -7,7 +7,7 @@ #include -namespace filesystem +namespace kernel::filesystem { struct open_file_description { @@ -23,6 +23,6 @@ namespace filesystem size_t m_offset; }; -} // namespace filesystem +} // namespace kernel::filesystem #endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp index b3e03a9..b91f728 100644 --- a/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp +++ b/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp @@ -11,13 +11,13 @@ #include -namespace filesystem::rootfs +namespace kernel::filesystem::rootfs { struct rootfs_filesystem : filesystem { auto mount(kstd::shared_ptr const & device) -> int override; auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; }; -} // namespace filesystem::rootfs +} // namespace kernel::filesystem::rootfs #endif diff --git a/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp b/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp index de4fb7c..26e7f88 100644 --- a/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp +++ b/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp @@ -11,7 +11,7 @@ #include #include -namespace filesystem::rootfs +namespace kernel::filesystem::rootfs { struct rootfs_inode : inode { @@ -26,6 +26,6 @@ namespace filesystem::rootfs private: kstd::vector>> m_children; }; -} // namespace filesystem::rootfs +} // namespace kernel::filesystem::rootfs #endif diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 9bee104..5823a83 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -10,7 +10,7 @@ #include -namespace filesystem +namespace kernel::filesystem { struct vfs { @@ -32,6 +32,6 @@ namespace filesystem mount_table m_mount_table; }; -} // namespace filesystem +} // namespace kernel::filesystem #endif \ No newline at end of file diff --git a/kernel/include/kernel/memory/heap_allocator.hpp b/kernel/include/kernel/memory/heap_allocator.hpp index 55de7e4..fd39bef 100644 --- a/kernel/include/kernel/memory/heap_allocator.hpp +++ b/kernel/include/kernel/memory/heap_allocator.hpp @@ -3,8 +3,6 @@ #include -#include - namespace kernel::memory { diff --git a/kernel/src/devices/block_device.cpp b/kernel/src/devices/block_device.cpp index 3402814..cfe2eb2 100644 --- a/kernel/src/devices/block_device.cpp +++ b/kernel/src/devices/block_device.cpp @@ -8,7 +8,7 @@ #include -namespace devices +namespace kernel::devices { block_device::block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size) : device(major, minor, name) @@ -40,4 +40,4 @@ namespace devices { return size(); } -} // namespace devices \ No newline at end of file +} // namespace kernel::devices \ No newline at end of file diff --git a/kernel/src/devices/block_device_utils.cpp b/kernel/src/devices/block_device_utils.cpp index 9d3af1b..5469087 100644 --- a/kernel/src/devices/block_device_utils.cpp +++ b/kernel/src/devices/block_device_utils.cpp @@ -12,7 +12,7 @@ #include #include -namespace devices::block_device_utils +namespace kernel::devices::block_device_utils { using block_op = void (*)(size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, @@ -100,4 +100,4 @@ namespace devices::block_device_utils }); } -} // namespace devices::block_device_utils \ No newline at end of file +} // namespace kernel::devices::block_device_utils \ No newline at end of file diff --git a/kernel/src/devices/device.cpp b/kernel/src/devices/device.cpp index 287f14b..1e7589e 100644 --- a/kernel/src/devices/device.cpp +++ b/kernel/src/devices/device.cpp @@ -4,7 +4,7 @@ #include -namespace devices +namespace kernel::devices { device::device(size_t major, size_t minor, kstd::string const & name) : m_major(major) @@ -26,4 +26,4 @@ namespace devices { return m_name; } -} // namespace devices \ No newline at end of file +} // namespace kernel::devices \ No newline at end of file diff --git a/kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp b/kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp index f3e9f70..26eb10b 100644 --- a/kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp +++ b/kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp @@ -9,7 +9,7 @@ #include #include -namespace devices::storage::ram_disk +namespace kernel::devices::storage::ram_disk { ram_disk_controller::ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry) : m_boot_module_registry(registry) @@ -24,4 +24,4 @@ namespace devices::storage::ram_disk m_devices.push_back(kstd::make_shared(module, m_major, minor)); }); } -} // namespace devices::storage::ram_disk \ No newline at end of file +} // namespace kernel::devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp b/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp index bf329cb..72e8025 100644 --- a/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp +++ b/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp @@ -10,7 +10,7 @@ #include -namespace devices::storage::ram_disk +namespace kernel::devices::storage::ram_disk { namespace { @@ -63,4 +63,4 @@ namespace devices::storage::ram_disk { return m_boot_module.size; } -} // namespace devices::storage::ram_disk \ No newline at end of file +} // namespace kernel::devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/src/devices/storage/storage_controller.cpp b/kernel/src/devices/storage/storage_controller.cpp index e415436..3d13b66 100644 --- a/kernel/src/devices/storage/storage_controller.cpp +++ b/kernel/src/devices/storage/storage_controller.cpp @@ -8,7 +8,7 @@ #include #include -namespace devices::storage +namespace kernel::devices::storage { auto storage_controller::set_ids(size_t major, size_t minors_per_dev) -> void { @@ -41,4 +41,4 @@ namespace devices::storage { return m_devices; } -} // namespace devices::storage \ No newline at end of file +} // namespace kernel::devices::storage \ No newline at end of file diff --git a/kernel/src/devices/storage/storage_management.cpp b/kernel/src/devices/storage/storage_management.cpp index 56216b0..2bc57c4 100644 --- a/kernel/src/devices/storage/storage_management.cpp +++ b/kernel/src/devices/storage/storage_management.cpp @@ -14,7 +14,7 @@ #include #include -namespace devices::storage +namespace kernel::devices::storage { namespace { @@ -82,4 +82,4 @@ namespace devices::storage return device_by_major_minor(START_MAJOR, 0); } -} // namespace devices::storage \ No newline at end of file +} // namespace kernel::devices::storage \ No newline at end of file diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index 76949f2..2f99e91 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -10,7 +10,7 @@ #include #include -namespace filesystem +namespace kernel::filesystem { dentry::dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node, std::string_view name) : m_name(name) @@ -58,4 +58,4 @@ namespace filesystem { return (m_flags & static_cast(flag)) != 0; } -} // namespace filesystem \ No newline at end of file +} // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/devfs/devfs_filesystem.cpp b/kernel/src/filesystem/devfs/devfs_filesystem.cpp index c4cd81a..df977b8 100644 --- a/kernel/src/filesystem/devfs/devfs_filesystem.cpp +++ b/kernel/src/filesystem/devfs/devfs_filesystem.cpp @@ -11,7 +11,7 @@ #include #include -namespace filesystem::devfs +namespace kernel::filesystem::devfs { auto devfs_filesystem::mount(kstd::shared_ptr const &) -> int { @@ -55,4 +55,4 @@ namespace filesystem::devfs [&](auto const & device) { m_inodes.push_back(kstd::make_shared(device)); }); }); } -} // namespace filesystem::devfs \ No newline at end of file +} // namespace kernel::filesystem::devfs \ No newline at end of file diff --git a/kernel/src/filesystem/devfs/devfs_root_inode.cpp b/kernel/src/filesystem/devfs/devfs_root_inode.cpp index 53441b0..a7308dc 100644 --- a/kernel/src/filesystem/devfs/devfs_root_inode.cpp +++ b/kernel/src/filesystem/devfs/devfs_root_inode.cpp @@ -4,7 +4,7 @@ #include -namespace filesystem::devfs +namespace kernel::filesystem::devfs { devfs_root_inode::devfs_root_inode() : inode(inode_kind::directory) @@ -19,4 +19,4 @@ namespace filesystem::devfs { return 0; } -} // namespace filesystem::devfs \ No newline at end of file +} // namespace kernel::filesystem::devfs \ No newline at end of file diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index da062fc..d574d8f 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -12,7 +12,7 @@ #include -namespace filesystem +namespace kernel::filesystem { device_inode::device_inode(kstd::shared_ptr const & device) : inode(inode_kind::device) @@ -53,4 +53,4 @@ namespace filesystem return m_device; } -} // namespace filesystem \ No newline at end of file +} // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/ext2/ext2_filesystem.cpp b/kernel/src/filesystem/ext2/ext2_filesystem.cpp index ea6fe0d..036b80e 100644 --- a/kernel/src/filesystem/ext2/ext2_filesystem.cpp +++ b/kernel/src/filesystem/ext2/ext2_filesystem.cpp @@ -13,7 +13,7 @@ #include #include -namespace filesystem::ext2 +namespace kernel::filesystem::ext2 { namespace { @@ -69,4 +69,4 @@ namespace filesystem::ext2 return kstd::make_shared(); } -} // namespace filesystem::ext2 +} // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/ext2_inode.cpp b/kernel/src/filesystem/ext2/ext2_inode.cpp index 3cc0fb2..142502e 100644 --- a/kernel/src/filesystem/ext2/ext2_inode.cpp +++ b/kernel/src/filesystem/ext2/ext2_inode.cpp @@ -4,7 +4,7 @@ #include -namespace filesystem::ext2 +namespace kernel::filesystem::ext2 { ext2_inode::ext2_inode() : inode(inode_kind::regular) @@ -21,4 +21,4 @@ namespace filesystem::ext2 // TODO BA-FS26 implement return 0; } -} // namespace filesystem::ext2 \ No newline at end of file +} // namespace kernel::filesystem::ext2 \ No newline at end of file diff --git a/kernel/src/filesystem/file_descriptor_table.cpp b/kernel/src/filesystem/file_descriptor_table.cpp index 6eb3845..287aea2 100644 --- a/kernel/src/filesystem/file_descriptor_table.cpp +++ b/kernel/src/filesystem/file_descriptor_table.cpp @@ -10,7 +10,7 @@ #include #include -namespace filesystem +namespace kernel::filesystem { namespace { @@ -87,4 +87,4 @@ namespace filesystem m_open_files.at(index) = nullptr; } -} // namespace filesystem \ No newline at end of file +} // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index 0e33d95..c891d00 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -5,7 +5,7 @@ #include -namespace filesystem +namespace kernel::filesystem { auto filesystem::mount(kstd::shared_ptr const & device) -> int { @@ -21,4 +21,4 @@ namespace filesystem { return m_root_inode; } -} // namespace filesystem \ No newline at end of file +} // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/inode.cpp b/kernel/src/filesystem/inode.cpp index de3282f..1cbead8 100644 --- a/kernel/src/filesystem/inode.cpp +++ b/kernel/src/filesystem/inode.cpp @@ -1,6 +1,6 @@ #include "kernel/filesystem/inode.hpp" -namespace filesystem +namespace kernel::filesystem { inode::inode(inode_kind kind) : m_kind(kind) @@ -20,4 +20,4 @@ namespace filesystem { return m_kind == inode_kind::device; } -} // namespace filesystem \ No newline at end of file +} // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index afc07fa..f9e709c 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -10,7 +10,7 @@ #include -namespace filesystem +namespace kernel::filesystem { mount::mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, kstd::shared_ptr const & fs, std::string_view mount_path) @@ -44,4 +44,4 @@ namespace filesystem { return m_mount_path.view(); } -} // namespace filesystem \ No newline at end of file +} // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index b9e57d4..737434e 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -7,7 +7,7 @@ #include #include -namespace filesystem +namespace kernel::filesystem { void mount_table::add_mount(kstd::shared_ptr mount) { @@ -35,4 +35,4 @@ namespace filesystem return mount_with_longest_prefix; } -} // namespace filesystem \ No newline at end of file +} // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/open_file_description.cpp b/kernel/src/filesystem/open_file_description.cpp index 93c38ac..8c04225 100644 --- a/kernel/src/filesystem/open_file_description.cpp +++ b/kernel/src/filesystem/open_file_description.cpp @@ -7,7 +7,7 @@ #include -namespace filesystem +namespace kernel::filesystem { open_file_description::open_file_description(kstd::shared_ptr const & inode) : m_inode(inode) @@ -32,4 +32,4 @@ namespace filesystem m_offset += written_bytes; return written_bytes; } -} // namespace filesystem \ No newline at end of file +} // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp b/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp index 22502aa..804e211 100644 --- a/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp +++ b/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp @@ -8,7 +8,7 @@ #include -namespace filesystem::rootfs +namespace kernel::filesystem::rootfs { auto rootfs_filesystem::mount(kstd::shared_ptr const &) -> int { @@ -26,4 +26,4 @@ namespace filesystem::rootfs return rfs_inode->lookup_child(name); return nullptr; } -} // namespace filesystem::rootfs +} // namespace kernel::filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/rootfs_inode.cpp b/kernel/src/filesystem/rootfs/rootfs_inode.cpp index 9bbfbce..f44f06c 100644 --- a/kernel/src/filesystem/rootfs/rootfs_inode.cpp +++ b/kernel/src/filesystem/rootfs/rootfs_inode.cpp @@ -10,7 +10,7 @@ #include #include -namespace filesystem::rootfs +namespace kernel::filesystem::rootfs { rootfs_inode::rootfs_inode() : inode(inode_kind::directory) @@ -36,4 +36,4 @@ namespace filesystem::rootfs auto it = std::ranges::find_if(m_children, [&](auto const & pair) { return pair.first == name; }); return (it != m_children.end()) ? it->second : nullptr; } -} // namespace filesystem::rootfs +} // namespace kernel::filesystem::rootfs diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 7a90531..2dbdb12 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -17,7 +17,7 @@ #include #include -namespace filesystem +namespace kernel::filesystem { namespace { @@ -161,4 +161,4 @@ namespace filesystem return current_dentry; } -} // namespace filesystem \ No newline at end of file +} // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index bb6d57d..2dd9fd9 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -24,7 +24,7 @@ auto test_device_names() -> void { - auto storage_mgmt = devices::storage::storage_management::get(); + auto storage_mgmt = kernel::devices::storage::storage_management::get(); std::ranges::for_each(storage_mgmt.all_controllers(), [](auto const & controller) { std::ranges::for_each(controller->all_devices(), [](auto const & device) { kstd::println("{}", device->name().view()); }); @@ -34,13 +34,13 @@ auto test_device_names() -> void auto test_file_description_manually() -> void { // setup - auto fd_table = filesystem::file_descriptor_table::get(); - auto storage_mgmt = devices::storage::storage_management::get(); + auto fd_table = kernel::filesystem::file_descriptor_table::get(); + auto storage_mgmt = kernel::devices::storage::storage_management::get(); auto device = storage_mgmt.device_by_major_minor(1, 0); - auto dev_node = kstd::make_shared(device); + auto dev_node = kstd::make_shared(device); - auto ofd = kstd::make_shared(dev_node); + auto ofd = kstd::make_shared(dev_node); auto fd_index = fd_table.add_file(ofd); // use: read two bytes and write two again @@ -66,7 +66,7 @@ auto test_file_description_manually() -> void fd_table.remove_file(fd_index); // use: read four bytes again -> two old bytes two new bytes - auto ofd1 = kstd::make_shared(dev_node); + auto ofd1 = kstd::make_shared(dev_node); fd_index = fd_table.add_file(ofd1); auto fd1 = fd_table.get_file(fd_index); @@ -85,14 +85,14 @@ auto test_device_with_vfs() -> void { // TODO BA-FS26 - auto vfs = filesystem::vfs::get(); + auto vfs = kernel::filesystem::vfs::get(); auto ofd = vfs.open("/dev/ram0"); if (!ofd) { kstd::os::panic("test code failed"); } - auto fd_table = filesystem::file_descriptor_table::get(); + auto fd_table = kernel::filesystem::file_descriptor_table::get(); auto fd = fd_table.add_file(ofd); kstd::vector buffer{2}; auto file = fd_table.get_file(fd); @@ -110,8 +110,8 @@ auto test_file_lookup() -> void { // TODO BA-FS26 implement a more complete test with multiple files and directories and mounts etc. - auto vfs = filesystem::vfs::get(); - auto storage_mgmt = devices::storage::storage_management::get(); + auto vfs = kernel::filesystem::vfs::get(); + auto storage_mgmt = kernel::devices::storage::storage_management::get(); auto ofd1 = vfs.open("/a/b/c"); auto ofd2 = vfs.open("/dev/ram0"); @@ -126,7 +126,7 @@ auto test_file_lookup() -> void kstd::os::panic("test code failed"); } - auto new_filesystem = kstd::make_shared(); + auto new_filesystem = kstd::make_shared(); auto device = storage_mgmt.device_by_major_minor(1, 16); new_filesystem->mount(device); if (vfs.do_mount("/a/b", new_filesystem) != 0) @@ -182,13 +182,13 @@ auto main() -> int kapi::boot_modules::init(); kstd::println("[OS] Boot module registry initialized."); - devices::storage::storage_management::init(); + kernel::devices::storage::storage_management::init(); kstd::println("[OS] Storage management initialized."); - filesystem::file_descriptor_table::init(); + kernel::filesystem::file_descriptor_table::init(); kstd::println("[OS] Global file descriptor table initialized."); - filesystem::vfs::init(); + kernel::filesystem::vfs::init(); kstd::println("[OS] Virtual filesystem initialized."); run_test_code(); -- cgit v1.2.3 From 55e37a219fc953d1675bc2edb8573c6d47df7647 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Mon, 30 Mar 2026 21:23:02 +0200 Subject: Rename ext2 filesystem files --- kernel/CMakeLists.txt | 4 +- .../filesystem/ext2/block_group_descriptor.hpp | 21 ++++++ .../ext2/ext2_block_group_descriptor.hpp | 21 ------ .../kernel/filesystem/ext2/ext2_filesystem.hpp | 23 ------- .../include/kernel/filesystem/ext2/ext2_inode.hpp | 44 ------------- .../ext2/ext2_linked_directory_entry.hpp | 20 ------ .../kernel/filesystem/ext2/ext2_superblock.hpp | 76 ---------------------- .../include/kernel/filesystem/ext2/filesystem.hpp | 22 +++++++ kernel/include/kernel/filesystem/ext2/inode.hpp | 44 +++++++++++++ .../filesystem/ext2/linked_directory_entry.hpp | 20 ++++++ .../include/kernel/filesystem/ext2/superblock.hpp | 76 ++++++++++++++++++++++ kernel/src/filesystem/ext2/ext2_filesystem.cpp | 72 -------------------- kernel/src/filesystem/ext2/ext2_inode.cpp | 24 ------- kernel/src/filesystem/ext2/filesystem.cpp | 72 ++++++++++++++++++++ kernel/src/filesystem/ext2/inode.cpp | 24 +++++++ kernel/src/filesystem/vfs.cpp | 4 +- kernel/src/main.cpp | 4 +- 17 files changed, 285 insertions(+), 286 deletions(-) create mode 100644 kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp delete mode 100644 kernel/include/kernel/filesystem/ext2/ext2_block_group_descriptor.hpp delete mode 100644 kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp delete mode 100644 kernel/include/kernel/filesystem/ext2/ext2_inode.hpp delete mode 100644 kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp delete mode 100644 kernel/include/kernel/filesystem/ext2/ext2_superblock.hpp create mode 100644 kernel/include/kernel/filesystem/ext2/filesystem.hpp create mode 100644 kernel/include/kernel/filesystem/ext2/inode.hpp create mode 100644 kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp create mode 100644 kernel/include/kernel/filesystem/ext2/superblock.hpp delete mode 100644 kernel/src/filesystem/ext2/ext2_filesystem.cpp delete mode 100644 kernel/src/filesystem/ext2/ext2_inode.cpp create mode 100644 kernel/src/filesystem/ext2/filesystem.cpp create mode 100644 kernel/src/filesystem/ext2/inode.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 5cc6f2d..93ac322 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -26,8 +26,8 @@ add_executable("kernel" "src/devices/storage/ram_disk/ram_disk_device.cpp" "src/filesystem/devfs/devfs_filesystem.cpp" "src/filesystem/devfs/devfs_root_inode.cpp" - "src/filesystem/ext2/ext2_filesystem.cpp" - "src/filesystem/ext2/ext2_inode.cpp" + "src/filesystem/ext2/filesystem.cpp" + "src/filesystem/ext2/inode.cpp" "src/filesystem/dentry.cpp" "src/filesystem/device_inode.cpp" "src/filesystem/file_descriptor_table.cpp" diff --git a/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp b/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp new file mode 100644 index 0000000..a23c045 --- /dev/null +++ b/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp @@ -0,0 +1,21 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_BLOCK_GROUP_DESCRIPTOR_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_BLOCK_GROUP_DESCRIPTOR_HPP + +#include +#include + +namespace kernel::filesystem::ext2 +{ + struct block_group_descriptor + { + uint32_t block_bitmap; + uint32_t inode_bitmap; + uint32_t inode_table; + uint16_t free_blocks_count; + uint16_t free_inodes_count; + uint16_t used_dirs_count; + std::array padding; + std::array reserved; // NOLINT(readability-magic-numbers) + }; +} // namespace kernel::filesystem::ext2 +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/ext2_block_group_descriptor.hpp b/kernel/include/kernel/filesystem/ext2/ext2_block_group_descriptor.hpp deleted file mode 100644 index 0de7428..0000000 --- a/kernel/include/kernel/filesystem/ext2/ext2_block_group_descriptor.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef EXT2_BLOCK_GROUP_DESCRIPTOR_HPP -#define EXT2_BLOCK_GROUP_DESCRIPTOR_HPP - -#include -#include - -namespace kernel::filesystem::ext2 -{ - struct ext2_block_group_descriptor - { - uint32_t block_bitmap; - uint32_t inode_bitmap; - uint32_t inode_table; - uint16_t free_blocks_count; - uint16_t free_inodes_count; - uint16_t used_dirs_count; - std::array padding; - std::array reserved; // NOLINT(readability-magic-numbers) - }; -} // namespace kernel::filesystem::ext2 -#endif // EXT2_BLOCK_GROUP_DESCRIPTOR_HPP \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp b/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp deleted file mode 100644 index 753aea1..0000000 --- a/kernel/include/kernel/filesystem/ext2/ext2_filesystem.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP - -#include "kernel/devices/device.hpp" -#include "kernel/filesystem/filesystem.hpp" -#include "kernel/filesystem/inode.hpp" - -#include - -#include - -namespace kernel::filesystem::ext2 -{ - struct ext2_filesystem : filesystem - { - auto mount(kstd::shared_ptr const & device) -> int override; - auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; - - private: - }; -} // namespace kernel::filesystem::ext2 - -#endif diff --git a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp b/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp deleted file mode 100644 index 2054227..0000000 --- a/kernel/include/kernel/filesystem/ext2/ext2_inode.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP - -#include "kernel/filesystem/inode.hpp" - -#include - -#include -#include -#include - -namespace kernel::filesystem::ext2 -{ - struct ext2_inode : inode - { - ext2_inode(); - - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; - - uint16_t mode; - uint16_t uid; - uint32_t size; - uint32_t atime; - uint32_t ctime; - uint32_t mtime; - uint32_t dtime; - uint16_t gid; - uint16_t links_count; - uint32_t blocks; - uint32_t flags; - uint32_t osd1; - // uint32_t block[15]; // TODO BA-FS26 really correct? - std::array block; // NOLINT(readability-magic-numbers) - uint32_t generation; - uint32_t file_acl; - uint32_t dir_acl; - uint32_t faddr; - // uint8_t osd2[12]; // TODO BA-FS26 really correct? - std::array osd2; // NOLINT(readability-magic-numbers) - }; -} // namespace kernel::filesystem::ext2 - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp b/kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp deleted file mode 100644 index 84e47e8..0000000 --- a/kernel/include/kernel/filesystem/ext2/ext2_linked_directory_entry.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_LINKED_DIRECTORY_ENTRY_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_LINKED_DIRECTORY_ENTRY_HPP - -#include -#include - -namespace kernel::filesystem::ext2 -{ - struct ext2_linked_directory_entry - { - uint32_t inode; - uint16_t rec_len; - uint8_t name_len; - uint8_t file_type; - uint8_t pad; - std::array name; // NOLINT(readability-magic-numbers) - }; -} // namespace kernel::filesystem::ext2 - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/ext2_superblock.hpp b/kernel/include/kernel/filesystem/ext2/ext2_superblock.hpp deleted file mode 100644 index aa93e68..0000000 --- a/kernel/include/kernel/filesystem/ext2/ext2_superblock.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef EXT2_SUPERBLOCK_HPP -#define EXT2_SUPERBLOCK_HPP - -#include -#include - -namespace kernel::filesystem::ext2 -{ - struct ext2_superblock - { - uint32_t inodes_count; - uint32_t blocks_count; - uint32_t reserved_blocks_count; - uint32_t free_blocks_count; - uint32_t free_inodes_count; - uint32_t first_data_block; - uint32_t log_block_size; - uint32_t log_frag_size; - uint32_t blocks_per_group; - uint32_t frags_per_group; - uint32_t inodes_per_group; - uint32_t mtime; - uint32_t wtime; - uint16_t mnt_count; - uint16_t max_mnt_count; - uint16_t magic; - uint16_t state; - uint16_t errors; - uint16_t minor_rev_level; - uint32_t lastcheck; - uint32_t checkinterval; - uint32_t creator_os; - uint32_t rev_level; - uint16_t def_resuid; - uint16_t def_resgid; - - // EXT2_DYNAMIC_REV superblock only - uint32_t first_ino; - uint16_t inode_size; - uint16_t block_group_nr; - uint32_t feature_compat; - uint32_t feature_incompat; - uint32_t feature_ro_compat; - // uint8_t uuid[16]; // TODO BA-FS26 really correct? - std::array uuid; - // uint8_t volume_name[16]; // TODO BA-FS26 really correct? - std::array volume_name; - // uint8_t last_mounted[64]; // TODO BA-FS26 really correct? - std::array last_mounted; - uint32_t algorithm_usage_bitmap; - - // Performance Hints - uint8_t prealloc_blocks; - uint8_t prealloc_dir_blocks; - uint16_t padding1; - - // Journaling Support - // uint8_t journal_uuid[16]; // TODO BA-FS26 really correct? - std::array journal_uuid; - uint32_t journal_inum; - uint32_t journal_dev; - uint32_t last_orphan; - - // Directory Indexing Support - // uint32_t hash_seed[4]; // TODO BA-FS26 really correct? - std::array hash_seed; - uint8_t def_hash_version; - std::array padding2; - - // Other options - uint32_t default_mount_options; - uint32_t first_meta_bg; - std::array unused; // NOLINT(readability-magic-numbers) - }; -} // namespace kernel::filesystem::ext2 -#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp new file mode 100644 index 0000000..763cd1d --- /dev/null +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -0,0 +1,22 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP + +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/filesystem/inode.hpp" + +#include + +#include + +namespace kernel::filesystem::ext2 +{ + struct filesystem : kernel::filesystem::filesystem + { + auto mount(kstd::shared_ptr const & device) -> int override; + auto lookup(kstd::shared_ptr const & parent, std::string_view name) + -> kstd::shared_ptr override; + }; +} // namespace kernel::filesystem::ext2 + +#endif diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp new file mode 100644 index 0000000..2c27c17 --- /dev/null +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -0,0 +1,44 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP + +#include "kernel/filesystem/inode.hpp" + +#include + +#include +#include +#include + +namespace kernel::filesystem::ext2 +{ + struct inode : kernel::filesystem::inode + { + inode(); + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + uint16_t mode; + uint16_t uid; + uint32_t size; + uint32_t atime; + uint32_t ctime; + uint32_t mtime; + uint32_t dtime; + uint16_t gid; + uint16_t links_count; + uint32_t blocks; + uint32_t flags; + uint32_t osd1; + // uint32_t block[15]; // TODO BA-FS26 really correct? + std::array block; // NOLINT(readability-magic-numbers) + uint32_t generation; + uint32_t file_acl; + uint32_t dir_acl; + uint32_t faddr; + // uint8_t osd2[12]; // TODO BA-FS26 really correct? + std::array osd2; // NOLINT(readability-magic-numbers) + }; +} // namespace kernel::filesystem::ext2 + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp new file mode 100644 index 0000000..8dd42a1 --- /dev/null +++ b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp @@ -0,0 +1,20 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_LINKED_DIRECTORY_ENTRY_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_LINKED_DIRECTORY_ENTRY_HPP + +#include +#include + +namespace kernel::filesystem::ext2 +{ + struct linked_directory_entry + { + uint32_t inode; + uint16_t rec_len; + uint8_t name_len; + uint8_t file_type; + uint8_t pad; + std::array name; // NOLINT(readability-magic-numbers) + }; +} // namespace kernel::filesystem::ext2 + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/superblock.hpp b/kernel/include/kernel/filesystem/ext2/superblock.hpp new file mode 100644 index 0000000..8600b4c --- /dev/null +++ b/kernel/include/kernel/filesystem/ext2/superblock.hpp @@ -0,0 +1,76 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_SUPERBLOCK_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_SUPERBLOCK_HPP + +#include +#include + +namespace kernel::filesystem::ext2 +{ + struct superblock + { + uint32_t inodes_count; + uint32_t blocks_count; + uint32_t reserved_blocks_count; + uint32_t free_blocks_count; + uint32_t free_inodes_count; + uint32_t first_data_block; + uint32_t log_block_size; + uint32_t log_frag_size; + uint32_t blocks_per_group; + uint32_t frags_per_group; + uint32_t inodes_per_group; + uint32_t mtime; + uint32_t wtime; + uint16_t mnt_count; + uint16_t max_mnt_count; + uint16_t magic; + uint16_t state; + uint16_t errors; + uint16_t minor_rev_level; + uint32_t lastcheck; + uint32_t checkinterval; + uint32_t creator_os; + uint32_t rev_level; + uint16_t def_resuid; + uint16_t def_resgid; + + // EXT2_DYNAMIC_REV superblock only + uint32_t first_ino; + uint16_t inode_size; + uint16_t block_group_nr; + uint32_t feature_compat; + uint32_t feature_incompat; + uint32_t feature_ro_compat; + // uint8_t uuid[16]; // TODO BA-FS26 really correct? + std::array uuid; + // uint8_t volume_name[16]; // TODO BA-FS26 really correct? + std::array volume_name; + // uint8_t last_mounted[64]; // TODO BA-FS26 really correct? + std::array last_mounted; + uint32_t algorithm_usage_bitmap; + + // Performance Hints + uint8_t prealloc_blocks; + uint8_t prealloc_dir_blocks; + uint16_t padding1; + + // Journaling Support + // uint8_t journal_uuid[16]; // TODO BA-FS26 really correct? + std::array journal_uuid; + uint32_t journal_inum; + uint32_t journal_dev; + uint32_t last_orphan; + + // Directory Indexing Support + // uint32_t hash_seed[4]; // TODO BA-FS26 really correct? + std::array hash_seed; + uint8_t def_hash_version; + std::array padding2; + + // Other options + uint32_t default_mount_options; + uint32_t first_meta_bg; + std::array unused; // NOLINT(readability-magic-numbers) + }; +} // namespace kernel::filesystem::ext2 +#endif \ No newline at end of file diff --git a/kernel/src/filesystem/ext2/ext2_filesystem.cpp b/kernel/src/filesystem/ext2/ext2_filesystem.cpp deleted file mode 100644 index 036b80e..0000000 --- a/kernel/src/filesystem/ext2/ext2_filesystem.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "kernel/filesystem/ext2/ext2_filesystem.hpp" - -#include "kernel/devices/block_device_utils.hpp" -#include "kernel/devices/device.hpp" -#include "kernel/filesystem/ext2/ext2_inode.hpp" -#include "kernel/filesystem/ext2/ext2_superblock.hpp" -#include "kernel/filesystem/filesystem.hpp" -#include "kernel/filesystem/inode.hpp" - -#include - -#include -#include -#include - -namespace kernel::filesystem::ext2 -{ - namespace - { - // constexpr size_t SUPERBLOCK_OFFSET = 1024; - // constexpr uint16_t EXT2_MAGIC = 0xEF53; - - // // Mode bits - // constexpr uint16_t S_IFMT = 0xF000; - // constexpr uint16_t S_IFREG = 0x8000; - // constexpr uint16_t S_IFDIR = 0x4000; - - // auto S_ISREG(uint16_t mode) -> bool - // { - // return (mode & S_IFMT) == S_IFREG; - // } - // auto S_ISDIR(uint16_t mode) -> bool - // { - // return (mode & S_IFMT) == S_IFDIR; - // } - - // auto get_block_size(ext2_superblock const & superblock) -> size_t - // { - // return 1024U << superblock.log_block_size; - // } - - // auto get_inode_size(ext2_superblock const & superblock) -> size_t - // { - // return superblock.rev_level == 0 ? 128 : superblock.inode_size; - // } - } // namespace - - auto ext2_filesystem::mount(kstd::shared_ptr const & device) -> int - { - filesystem::mount(device); // TODO BA-FS26 error handling? - // TODO BA-FS26 load proper root inode from ext2 metadata - // m_root_inode = inode{inode_kind::directory}; - - // TODO BA-FS26 implement - m_root_inode = kstd::make_shared(); - // devices::block_device_utils::read(device, nullptr, 0, 0); // TODO BA-FS26 just for testing - return 0; - } - - auto ext2_filesystem::lookup(kstd::shared_ptr const & /*parent*/, std::string_view name) - -> kstd::shared_ptr - { - // TODO BA-FS26 implement ext2 directory traversal and inode loading - if (name == "dev") - { - // TODO BA-FS26 just for testing - return nullptr; - } - - return kstd::make_shared(); - } -} // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/ext2_inode.cpp b/kernel/src/filesystem/ext2/ext2_inode.cpp deleted file mode 100644 index 142502e..0000000 --- a/kernel/src/filesystem/ext2/ext2_inode.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "kernel/filesystem/ext2/ext2_inode.hpp" - -#include "kernel/filesystem/inode.hpp" - -#include - -namespace kernel::filesystem::ext2 -{ - ext2_inode::ext2_inode() - : inode(inode_kind::regular) - {} - - auto ext2_inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t - { - // TODO BA-FS26 implement - return 0; - } - - auto ext2_inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t - { - // TODO BA-FS26 implement - return 0; - } -} // namespace kernel::filesystem::ext2 \ No newline at end of file diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp new file mode 100644 index 0000000..d951158 --- /dev/null +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -0,0 +1,72 @@ +#include "kernel/filesystem/ext2/filesystem.hpp" + +#include "kernel/devices/block_device_utils.hpp" +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/ext2/inode.hpp" +#include "kernel/filesystem/ext2/superblock.hpp" +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/filesystem/inode.hpp" + +#include + +#include +#include +#include + +namespace kernel::filesystem::ext2 +{ + namespace + { + // constexpr size_t SUPERBLOCK_OFFSET = 1024; + // constexpr uint16_t EXT2_MAGIC = 0xEF53; + + // // Mode bits + // constexpr uint16_t S_IFMT = 0xF000; + // constexpr uint16_t S_IFREG = 0x8000; + // constexpr uint16_t S_IFDIR = 0x4000; + + // auto S_ISREG(uint16_t mode) -> bool + // { + // return (mode & S_IFMT) == S_IFREG; + // } + // auto S_ISDIR(uint16_t mode) -> bool + // { + // return (mode & S_IFMT) == S_IFDIR; + // } + + // auto get_block_size(superblock const & superblock) -> size_t + // { + // return 1024U << superblock.log_block_size; + // } + + // auto get_inode_size(superblock const & superblock) -> size_t + // { + // return superblock.rev_level == 0 ? 128 : superblock.inode_size; + // } + } // namespace + + auto filesystem::mount(kstd::shared_ptr const & device) -> int + { + kernel::filesystem::filesystem::mount(device); // TODO BA-FS26 error handling? + // TODO BA-FS26 load proper root inode from ext2 metadata + // m_root_inode = inode{inode_kind::directory}; + + // TODO BA-FS26 implement + m_root_inode = kstd::make_shared(); + // devices::block_device_utils::read(device, nullptr, 0, 0); // TODO BA-FS26 just for testing + return 0; + } + + auto filesystem::lookup(kstd::shared_ptr const & /*parent*/, std::string_view name) + -> kstd::shared_ptr + { + // TODO BA-FS26 implement ext2 directory traversal and inode loading + if (name == "dev") + { + // TODO BA-FS26 just for testing + return nullptr; + } + + return kstd::make_shared(); + } +} // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp new file mode 100644 index 0000000..b75969a --- /dev/null +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -0,0 +1,24 @@ +#include "kernel/filesystem/ext2/inode.hpp" + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace kernel::filesystem::ext2 +{ + inode::inode() + : kernel::filesystem::inode(inode_kind::regular) + {} + + auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t + { + // TODO BA-FS26 implement + return 0; + } + + auto inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t + { + // TODO BA-FS26 implement + return 0; + } +} // namespace kernel::filesystem::ext2 \ No newline at end of file diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 2dbdb12..69740c3 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -5,7 +5,7 @@ #include "kernel/devices/storage/storage_management.hpp" #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/devfs/devfs_filesystem.hpp" -#include "kernel/filesystem/ext2/ext2_filesystem.hpp" +#include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/mount.hpp" #include "kernel/filesystem/open_file_description.hpp" @@ -47,7 +47,7 @@ namespace kernel::filesystem if (auto boot_device = storage_mgmt.determine_boot_device()) { // TODO BA-FS26 detect fs type from boot device and load corresponding fs, for now just assume ext2 - auto boot_root_fs = kstd::make_shared(); + auto boot_root_fs = kstd::make_shared(); boot_root_fs->mount(boot_device); do_mount_internal("/", root_fs_root_dentry, boot_root_fs); } diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 2dd9fd9..ff73985 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -7,7 +7,7 @@ #include "kernel/devices/storage/storage_management.hpp" #include "kernel/filesystem/device_inode.hpp" -#include "kernel/filesystem/ext2/ext2_filesystem.hpp" +#include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/file_descriptor_table.hpp" #include "kernel/filesystem/open_file_description.hpp" #include "kernel/filesystem/vfs.hpp" @@ -126,7 +126,7 @@ auto test_file_lookup() -> void kstd::os::panic("test code failed"); } - auto new_filesystem = kstd::make_shared(); + auto new_filesystem = kstd::make_shared(); auto device = storage_mgmt.device_by_major_minor(1, 16); new_filesystem->mount(device); if (vfs.do_mount("/a/b", new_filesystem) != 0) -- cgit v1.2.3 From 0b5084780e4a89dcaccbda5823495c9cdd62b006 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Mon, 30 Mar 2026 20:52:25 +0200 Subject: Rename devfs filesystem files --- kernel/CMakeLists.txt | 4 +- .../kernel/filesystem/devfs/devfs_filesystem.hpp | 25 ---------- .../kernel/filesystem/devfs/devfs_root_inode.hpp | 19 ------- .../include/kernel/filesystem/devfs/filesystem.hpp | 26 ++++++++++ kernel/include/kernel/filesystem/devfs/inode.hpp | 19 +++++++ kernel/src/filesystem/devfs/devfs_filesystem.cpp | 58 ---------------------- kernel/src/filesystem/devfs/devfs_root_inode.cpp | 22 -------- kernel/src/filesystem/devfs/filesystem.cpp | 58 ++++++++++++++++++++++ kernel/src/filesystem/devfs/inode.cpp | 22 ++++++++ kernel/src/filesystem/vfs.cpp | 4 +- 10 files changed, 129 insertions(+), 128 deletions(-) delete mode 100644 kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp delete mode 100644 kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp create mode 100644 kernel/include/kernel/filesystem/devfs/filesystem.hpp create mode 100644 kernel/include/kernel/filesystem/devfs/inode.hpp delete mode 100644 kernel/src/filesystem/devfs/devfs_filesystem.cpp delete mode 100644 kernel/src/filesystem/devfs/devfs_root_inode.cpp create mode 100644 kernel/src/filesystem/devfs/filesystem.cpp create mode 100644 kernel/src/filesystem/devfs/inode.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 93ac322..f9e4a70 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -24,8 +24,8 @@ add_executable("kernel" "src/devices/storage/storage_management.cpp" "src/devices/storage/ram_disk/ram_disk_controller.cpp" "src/devices/storage/ram_disk/ram_disk_device.cpp" - "src/filesystem/devfs/devfs_filesystem.cpp" - "src/filesystem/devfs/devfs_root_inode.cpp" + "src/filesystem/devfs/filesystem.cpp" + "src/filesystem/devfs/inode.cpp" "src/filesystem/ext2/filesystem.cpp" "src/filesystem/ext2/inode.cpp" "src/filesystem/dentry.cpp" diff --git a/kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp b/kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp deleted file mode 100644 index 2330915..0000000 --- a/kernel/include/kernel/filesystem/devfs/devfs_filesystem.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVFS_DEVFS_FILESYSTEM_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_DEVFS_FILESYSTEM_HPP - -#include "kernel/devices/device.hpp" -#include "kernel/filesystem/filesystem.hpp" -#include "kernel/filesystem/inode.hpp" - -#include -#include - -#include - -namespace kernel::filesystem::devfs -{ - struct devfs_filesystem : filesystem - { - auto mount(kstd::shared_ptr const & device) -> int override; - auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; - - private: - auto build_device_inode_table() -> void; - }; -} // namespace kernel::filesystem::devfs - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp b/kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp deleted file mode 100644 index 206fc13..0000000 --- a/kernel/include/kernel/filesystem/devfs/devfs_root_inode.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVFS_DEVFS_ROOT_INODE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_DEVFS_ROOT_INODE_HPP - -#include "kernel/filesystem/inode.hpp" - -#include - -namespace kernel::filesystem::devfs -{ - struct devfs_root_inode : inode - { - devfs_root_inode(); - - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; - }; -} // namespace kernel::filesystem::devfs - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/devfs/filesystem.hpp b/kernel/include/kernel/filesystem/devfs/filesystem.hpp new file mode 100644 index 0000000..5ec6221 --- /dev/null +++ b/kernel/include/kernel/filesystem/devfs/filesystem.hpp @@ -0,0 +1,26 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVFS_FILESYSTEM_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_FILESYSTEM_HPP + +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/filesystem/inode.hpp" + +#include +#include + +#include + +namespace kernel::filesystem::devfs +{ + struct filesystem : kernel::filesystem::filesystem + { + auto mount(kstd::shared_ptr const & device) -> int override; + auto lookup(kstd::shared_ptr const & parent, std::string_view name) + -> kstd::shared_ptr override; + + private: + auto build_device_inode_table() -> void; + }; +} // namespace kernel::filesystem::devfs + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/devfs/inode.hpp b/kernel/include/kernel/filesystem/devfs/inode.hpp new file mode 100644 index 0000000..9c11edf --- /dev/null +++ b/kernel/include/kernel/filesystem/devfs/inode.hpp @@ -0,0 +1,19 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVFS_INODE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_INODE_HPP + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace kernel::filesystem::devfs +{ + struct inode : kernel::filesystem::inode + { + inode(); + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + }; +} // namespace kernel::filesystem::devfs + +#endif \ No newline at end of file diff --git a/kernel/src/filesystem/devfs/devfs_filesystem.cpp b/kernel/src/filesystem/devfs/devfs_filesystem.cpp deleted file mode 100644 index df977b8..0000000 --- a/kernel/src/filesystem/devfs/devfs_filesystem.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "kernel/filesystem/devfs/devfs_filesystem.hpp" - -#include "kernel/devices/device.hpp" -#include "kernel/devices/storage/storage_management.hpp" -#include "kernel/filesystem/devfs/devfs_root_inode.hpp" -#include "kernel/filesystem/device_inode.hpp" -#include "kernel/filesystem/inode.hpp" - -#include - -#include -#include - -namespace kernel::filesystem::devfs -{ - auto devfs_filesystem::mount(kstd::shared_ptr const &) -> int - { - m_root_inode = kstd::make_shared(); - build_device_inode_table(); - - return 0; - } - - auto devfs_filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) - -> kstd::shared_ptr - { - if (!parent || !parent->is_directory()) - { - return nullptr; - } - - if (parent.get() != m_root_inode.get()) - { - return nullptr; - } - - auto it = std::ranges::find_if(m_inodes, [&](auto const & dev_node) { - auto device_inode_ptr = static_cast(dev_node.get()); - if (!device_inode_ptr) - { - return false; - } - return device_inode_ptr->device()->name() == name; - }); - return (it != m_inodes.end()) ? *it : nullptr; - } - - auto devfs_filesystem::build_device_inode_table() -> void - { - m_inodes.clear(); - - auto storage_mgmt = devices::storage::storage_management::get(); - std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto const & controller) { - std::ranges::for_each(controller->all_devices(), - [&](auto const & device) { m_inodes.push_back(kstd::make_shared(device)); }); - }); - } -} // namespace kernel::filesystem::devfs \ No newline at end of file diff --git a/kernel/src/filesystem/devfs/devfs_root_inode.cpp b/kernel/src/filesystem/devfs/devfs_root_inode.cpp deleted file mode 100644 index a7308dc..0000000 --- a/kernel/src/filesystem/devfs/devfs_root_inode.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "kernel/filesystem/devfs/devfs_root_inode.hpp" - -#include "kernel/filesystem/inode.hpp" - -#include - -namespace kernel::filesystem::devfs -{ - devfs_root_inode::devfs_root_inode() - : inode(inode_kind::directory) - {} - - auto devfs_root_inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t - { - return 0; - } - - auto devfs_root_inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t - { - return 0; - } -} // namespace kernel::filesystem::devfs \ No newline at end of file diff --git a/kernel/src/filesystem/devfs/filesystem.cpp b/kernel/src/filesystem/devfs/filesystem.cpp new file mode 100644 index 0000000..e7d0e13 --- /dev/null +++ b/kernel/src/filesystem/devfs/filesystem.cpp @@ -0,0 +1,58 @@ +#include "kernel/filesystem/devfs/filesystem.hpp" + +#include "kernel/devices/device.hpp" +#include "kernel/devices/storage/storage_management.hpp" +#include "kernel/filesystem/devfs/inode.hpp" +#include "kernel/filesystem/device_inode.hpp" +#include "kernel/filesystem/inode.hpp" + +#include + +#include +#include + +namespace kernel::filesystem::devfs +{ + auto filesystem::mount(kstd::shared_ptr const &) -> int + { + m_root_inode = kstd::make_shared(); + build_device_inode_table(); + + return 0; + } + + auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) + -> kstd::shared_ptr + { + if (!parent || !parent->is_directory()) + { + return nullptr; + } + + if (parent.get() != m_root_inode.get()) + { + return nullptr; + } + + auto it = std::ranges::find_if(m_inodes, [&](auto const & dev_node) { + auto device_inode_ptr = static_cast(dev_node.get()); + if (!device_inode_ptr) + { + return false; + } + return device_inode_ptr->device()->name() == name; + }); + return (it != m_inodes.end()) ? *it : nullptr; + } + + auto filesystem::build_device_inode_table() -> void + { + m_inodes.clear(); + + auto storage_mgmt = devices::storage::storage_management::get(); + std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto const & controller) { + std::ranges::for_each(controller->all_devices(), + [&](auto const & device) { m_inodes.push_back(kstd::make_shared(device)); }); + }); + } +} // namespace kernel::filesystem::devfs \ No newline at end of file diff --git a/kernel/src/filesystem/devfs/inode.cpp b/kernel/src/filesystem/devfs/inode.cpp new file mode 100644 index 0000000..52cf6fa --- /dev/null +++ b/kernel/src/filesystem/devfs/inode.cpp @@ -0,0 +1,22 @@ +#include "kernel/filesystem/devfs/inode.hpp" + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace kernel::filesystem::devfs +{ + inode::inode() + : kernel::filesystem::inode(inode_kind::directory) + {} + + auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t + { + return 0; + } + + auto inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t + { + return 0; + } +} // namespace kernel::filesystem::devfs \ No newline at end of file diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 69740c3..fdad36c 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -4,7 +4,7 @@ #include "kernel/devices/storage/storage_management.hpp" #include "kernel/filesystem/dentry.hpp" -#include "kernel/filesystem/devfs/devfs_filesystem.hpp" +#include "kernel/filesystem/devfs/filesystem.hpp" #include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/mount.hpp" @@ -52,7 +52,7 @@ namespace kernel::filesystem do_mount_internal("/", root_fs_root_dentry, boot_root_fs); } - auto device_fs = kstd::make_shared(); + auto device_fs = kstd::make_shared(); device_fs->mount(nullptr); do_mount_internal("/dev", root_fs_root_dentry, device_fs); } -- cgit v1.2.3 From 81ab0ba35d724dd465ed870e87047b3bf74cea13 Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Mon, 30 Mar 2026 21:01:18 +0200 Subject: Rename rootfs filesystem files --- kernel/CMakeLists.txt | 4 +-- .../kernel/filesystem/rootfs/filesystem.hpp | 24 +++++++++++++ kernel/include/kernel/filesystem/rootfs/inode.hpp | 31 +++++++++++++++++ .../kernel/filesystem/rootfs/rootfs_filesystem.hpp | 23 ------------- .../kernel/filesystem/rootfs/rootfs_inode.hpp | 31 ----------------- kernel/src/filesystem/rootfs/filesystem.cpp | 29 ++++++++++++++++ kernel/src/filesystem/rootfs/inode.cpp | 39 ++++++++++++++++++++++ kernel/src/filesystem/rootfs/rootfs_filesystem.cpp | 29 ---------------- kernel/src/filesystem/rootfs/rootfs_inode.cpp | 39 ---------------------- kernel/src/filesystem/vfs.cpp | 4 +-- 10 files changed, 127 insertions(+), 126 deletions(-) create mode 100644 kernel/include/kernel/filesystem/rootfs/filesystem.hpp create mode 100644 kernel/include/kernel/filesystem/rootfs/inode.hpp delete mode 100644 kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp delete mode 100644 kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp create mode 100644 kernel/src/filesystem/rootfs/filesystem.cpp create mode 100644 kernel/src/filesystem/rootfs/inode.cpp delete mode 100644 kernel/src/filesystem/rootfs/rootfs_filesystem.cpp delete mode 100644 kernel/src/filesystem/rootfs/rootfs_inode.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index f9e4a70..3dbc0f4 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -36,8 +36,8 @@ add_executable("kernel" "src/filesystem/mount_table.cpp" "src/filesystem/mount.cpp" "src/filesystem/open_file_description.cpp" - "src/filesystem/rootfs/rootfs_filesystem.cpp" - "src/filesystem/rootfs/rootfs_inode.cpp" + "src/filesystem/rootfs/filesystem.cpp" + "src/filesystem/rootfs/inode.cpp" "src/filesystem/vfs.cpp" ) diff --git a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp new file mode 100644 index 0000000..b1f33a9 --- /dev/null +++ b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp @@ -0,0 +1,24 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_FILESYSTEM_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_FILESYSTEM_HPP + +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/filesystem/inode.hpp" + +#include +#include +#include + +#include + +namespace kernel::filesystem::rootfs +{ + struct filesystem : kernel::filesystem::filesystem + { + auto mount(kstd::shared_ptr const & device) -> int override; + auto lookup(kstd::shared_ptr const & parent, std::string_view name) + -> kstd::shared_ptr override; + }; +} // namespace kernel::filesystem::rootfs + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/rootfs/inode.hpp b/kernel/include/kernel/filesystem/rootfs/inode.hpp new file mode 100644 index 0000000..24d3e6b --- /dev/null +++ b/kernel/include/kernel/filesystem/rootfs/inode.hpp @@ -0,0 +1,31 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_INODE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_INODE_HPP + +#include "kernel/filesystem/inode.hpp" + +#include +#include +#include + +#include +#include +#include + +namespace kernel::filesystem::rootfs +{ + struct inode : kernel::filesystem::inode + { + inode(); + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + auto add_child(std::string_view name) -> void; + auto lookup_child(std::string_view name) -> kstd::shared_ptr; + + private: + kstd::vector>> m_children; + }; +} // namespace kernel::filesystem::rootfs + +#endif diff --git a/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp deleted file mode 100644 index b91f728..0000000 --- a/kernel/include/kernel/filesystem/rootfs/rootfs_filesystem.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_ROOTFS_FILESYSTEM_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_ROOTFS_FILESYSTEM_HPP - -#include "kernel/devices/device.hpp" -#include "kernel/filesystem/filesystem.hpp" -#include "kernel/filesystem/inode.hpp" - -#include -#include -#include - -#include - -namespace kernel::filesystem::rootfs -{ - struct rootfs_filesystem : filesystem - { - auto mount(kstd::shared_ptr const & device) -> int override; - auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; - }; -} // namespace kernel::filesystem::rootfs - -#endif diff --git a/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp b/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp deleted file mode 100644 index 26e7f88..0000000 --- a/kernel/include/kernel/filesystem/rootfs/rootfs_inode.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_ROOTFS_INODE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_ROOTFS_INODE_HPP - -#include "kernel/filesystem/inode.hpp" - -#include -#include -#include - -#include -#include -#include - -namespace kernel::filesystem::rootfs -{ - struct rootfs_inode : inode - { - rootfs_inode(); - - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; - - auto add_child(std::string_view name) -> void; - auto lookup_child(std::string_view name) -> kstd::shared_ptr; - - private: - kstd::vector>> m_children; - }; -} // namespace kernel::filesystem::rootfs - -#endif diff --git a/kernel/src/filesystem/rootfs/filesystem.cpp b/kernel/src/filesystem/rootfs/filesystem.cpp new file mode 100644 index 0000000..0133612 --- /dev/null +++ b/kernel/src/filesystem/rootfs/filesystem.cpp @@ -0,0 +1,29 @@ +#include "kernel/filesystem/rootfs/filesystem.hpp" + +#include "kernel/devices/device.hpp" +#include "kernel/filesystem/inode.hpp" +#include "kernel/filesystem/rootfs/inode.hpp" + +#include + +#include + +namespace kernel::filesystem::rootfs +{ + auto filesystem::mount(kstd::shared_ptr const &) -> int + { + auto rfs_inode = kstd::make_shared(); + rfs_inode->add_child("dev"); + m_root_inode = rfs_inode; + + return 0; + } + + auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) + -> kstd::shared_ptr + { + if (auto * rfs_inode = static_cast(parent.get())) + return rfs_inode->lookup_child(name); + return nullptr; + } +} // namespace kernel::filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/inode.cpp b/kernel/src/filesystem/rootfs/inode.cpp new file mode 100644 index 0000000..3ca9c02 --- /dev/null +++ b/kernel/src/filesystem/rootfs/inode.cpp @@ -0,0 +1,39 @@ +#include "kernel/filesystem/inode.hpp" + +#include "kernel/filesystem/rootfs/inode.hpp" + +#include +#include + +#include +#include +#include +#include + +namespace kernel::filesystem::rootfs +{ + inode::inode() + : kernel::filesystem::inode(inode_kind::directory) + {} + + auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t + { + return 0; + } + + auto inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t + { + return 0; + } + + auto inode::add_child(std::string_view name) -> void + { + m_children.push_back(std::make_pair(kstd::string{name}, kstd::make_shared())); + } + + auto inode::lookup_child(std::string_view name) -> kstd::shared_ptr + { + auto it = std::ranges::find_if(m_children, [&](auto const & pair) { return pair.first == name; }); + return (it != m_children.end()) ? it->second : nullptr; + } +} // namespace kernel::filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp b/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp deleted file mode 100644 index 804e211..0000000 --- a/kernel/src/filesystem/rootfs/rootfs_filesystem.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "kernel/filesystem/rootfs/rootfs_filesystem.hpp" - -#include "kernel/devices/device.hpp" -#include "kernel/filesystem/inode.hpp" -#include "kernel/filesystem/rootfs/rootfs_inode.hpp" - -#include - -#include - -namespace kernel::filesystem::rootfs -{ - auto rootfs_filesystem::mount(kstd::shared_ptr const &) -> int - { - auto rfs_inode = kstd::make_shared(); - rfs_inode->add_child("dev"); - m_root_inode = rfs_inode; - - return 0; - } - - auto rootfs_filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) - -> kstd::shared_ptr - { - if (auto * rfs_inode = static_cast(parent.get())) - return rfs_inode->lookup_child(name); - return nullptr; - } -} // namespace kernel::filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/rootfs_inode.cpp b/kernel/src/filesystem/rootfs/rootfs_inode.cpp deleted file mode 100644 index f44f06c..0000000 --- a/kernel/src/filesystem/rootfs/rootfs_inode.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "kernel/filesystem/rootfs/rootfs_inode.hpp" - -#include "kernel/filesystem/inode.hpp" - -#include -#include - -#include -#include -#include -#include - -namespace kernel::filesystem::rootfs -{ - rootfs_inode::rootfs_inode() - : inode(inode_kind::directory) - {} - - auto rootfs_inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t - { - return 0; - } - - auto rootfs_inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t - { - return 0; - } - - auto rootfs_inode::add_child(std::string_view name) -> void - { - m_children.push_back(std::make_pair(kstd::string{name}, kstd::make_shared())); - } - - auto rootfs_inode::lookup_child(std::string_view name) -> kstd::shared_ptr - { - auto it = std::ranges::find_if(m_children, [&](auto const & pair) { return pair.first == name; }); - return (it != m_children.end()) ? it->second : nullptr; - } -} // namespace kernel::filesystem::rootfs diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index fdad36c..bee68e6 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -9,7 +9,7 @@ #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/mount.hpp" #include "kernel/filesystem/open_file_description.hpp" -#include "kernel/filesystem/rootfs/rootfs_filesystem.hpp" +#include "kernel/filesystem/rootfs/filesystem.hpp" #include @@ -37,7 +37,7 @@ namespace kernel::filesystem auto vfs::init_internal() -> void { - auto root_fs = kstd::make_shared(); + auto root_fs = kstd::make_shared(); root_fs->mount(nullptr); auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode()); -- cgit v1.2.3 From 846135ba5cdfa545124b97c74182f5eada9a403a Mon Sep 17 00:00:00 2001 From: "marcel.braun" Date: Mon, 30 Mar 2026 21:20:25 +0200 Subject: Rename ram_disk and storage files --- kernel/CMakeLists.txt | 8 +- .../include/kernel/devices/storage/controller.hpp | 71 ++++++++++++++++++ .../include/kernel/devices/storage/management.hpp | 77 ++++++++++++++++++++ .../kernel/devices/storage/ram_disk/controller.hpp | 31 ++++++++ .../kernel/devices/storage/ram_disk/device.hpp | 52 +++++++++++++ .../storage/ram_disk/ram_disk_controller.hpp | 31 -------- .../devices/storage/ram_disk/ram_disk_device.hpp | 52 ------------- .../kernel/devices/storage/storage_controller.hpp | 71 ------------------ .../kernel/devices/storage/storage_management.hpp | 77 -------------------- kernel/src/devices/storage/controller.cpp | 44 +++++++++++ kernel/src/devices/storage/management.cpp | 85 ++++++++++++++++++++++ kernel/src/devices/storage/ram_disk/controller.cpp | 27 +++++++ kernel/src/devices/storage/ram_disk/device.cpp | 65 +++++++++++++++++ .../storage/ram_disk/ram_disk_controller.cpp | 27 ------- .../devices/storage/ram_disk/ram_disk_device.cpp | 66 ----------------- kernel/src/devices/storage/storage_controller.cpp | 44 ----------- kernel/src/devices/storage/storage_management.cpp | 85 ---------------------- kernel/src/filesystem/devfs/filesystem.cpp | 4 +- kernel/src/filesystem/vfs.cpp | 4 +- kernel/src/main.cpp | 10 +-- 20 files changed, 465 insertions(+), 466 deletions(-) create mode 100644 kernel/include/kernel/devices/storage/controller.hpp create mode 100644 kernel/include/kernel/devices/storage/management.hpp create mode 100644 kernel/include/kernel/devices/storage/ram_disk/controller.hpp create mode 100644 kernel/include/kernel/devices/storage/ram_disk/device.hpp delete mode 100644 kernel/include/kernel/devices/storage/ram_disk/ram_disk_controller.hpp delete mode 100644 kernel/include/kernel/devices/storage/ram_disk/ram_disk_device.hpp delete mode 100644 kernel/include/kernel/devices/storage/storage_controller.hpp delete mode 100644 kernel/include/kernel/devices/storage/storage_management.hpp create mode 100644 kernel/src/devices/storage/controller.cpp create mode 100644 kernel/src/devices/storage/management.cpp create mode 100644 kernel/src/devices/storage/ram_disk/controller.cpp create mode 100644 kernel/src/devices/storage/ram_disk/device.cpp delete mode 100644 kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp delete mode 100644 kernel/src/devices/storage/ram_disk/ram_disk_device.cpp delete mode 100644 kernel/src/devices/storage/storage_controller.cpp delete mode 100644 kernel/src/devices/storage/storage_management.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 3dbc0f4..10bfecb 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -20,10 +20,10 @@ add_executable("kernel" "src/devices/device.cpp" "src/devices/block_device.cpp" "src/devices/block_device_utils.cpp" - "src/devices/storage/storage_controller.cpp" - "src/devices/storage/storage_management.cpp" - "src/devices/storage/ram_disk/ram_disk_controller.cpp" - "src/devices/storage/ram_disk/ram_disk_device.cpp" + "src/devices/storage/controller.cpp" + "src/devices/storage/management.cpp" + "src/devices/storage/ram_disk/controller.cpp" + "src/devices/storage/ram_disk/device.cpp" "src/filesystem/devfs/filesystem.cpp" "src/filesystem/devfs/inode.cpp" "src/filesystem/ext2/filesystem.cpp" diff --git a/kernel/include/kernel/devices/storage/controller.hpp b/kernel/include/kernel/devices/storage/controller.hpp new file mode 100644 index 0000000..e3bfd01 --- /dev/null +++ b/kernel/include/kernel/devices/storage/controller.hpp @@ -0,0 +1,71 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_CONTROLLER_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_CONTROLLER_HPP + +#include "kernel/devices/device.hpp" + +#include +#include + +#include + +namespace kernel::devices::storage +{ + /** + * @brief Base interface for storage controllers. + * + * A storage controller probes for devices and resolves devices by major/minor + * numbers. + */ + struct controller + { + /** + * @brief Virtual destructor. + */ + virtual ~controller() = default; + + /** + * @brief Probe the controller and register discovered devices. + */ + virtual auto probe() -> void = 0; + + /** + * @brief Assign the major number and minor stride for this controller. + * @param major Major number assigned to this controller. + * @param minors_per_dev Minor number stride between devices. + */ + auto set_ids(size_t major, size_t minors_per_dev) -> void; + + /** + * @brief Return the assigned major number. + * @return Assigned major number. + */ + [[nodiscard]] auto major() const -> size_t; + + /** + * @brief Return the number of devices managed by this controller. + * @return Number of managed devices. + */ + [[nodiscard]] auto devices_count() const -> size_t; + + /** + * @brief Return all devices managed by this controller. + * @return Vector of all managed devices. + */ + [[nodiscard]] auto all_devices() const -> kstd::vector> const &; + + /** + * @brief Find a managed device by major/minor numbers. + * @param major Device major number. + * @param minor Device minor number. + * @return Matching block device, or nullptr if no device matches. + */ + [[nodiscard]] auto device_by_minor(size_t minor) const -> kstd::shared_ptr; + + protected: + size_t m_major{}; + size_t m_minors_per_device{}; + kstd::vector> m_devices{}; + }; +} // namespace kernel::devices::storage + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/management.hpp b/kernel/include/kernel/devices/storage/management.hpp new file mode 100644 index 0000000..255d170 --- /dev/null +++ b/kernel/include/kernel/devices/storage/management.hpp @@ -0,0 +1,77 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_MANAGEMENT_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_MANAGEMENT_HPP + +#include "kernel/devices/device.hpp" +#include "kernel/devices/storage/controller.hpp" + +#include +#include + +#include + +namespace kernel::devices::storage +{ + /** + * @brief Global storage subsystem manager. + * + * Owns registered storage controllers and provides device lookup by + * major/minor numbers. + */ + struct management + { + /** + * @brief Initialize global storage management. + * + * Creates the singleton instance, registers controllers and probes + * them for devices. + * + * @warning Panics if called more than once. + */ + auto static init() -> void; + + /** + * @brief Return the active storage manager singleton. + * @return Reference to the active storage manager. + * @warning Panics if storage management has not been initialized. + */ + auto static get() -> management &; + + /** + * @brief Register a storage controller. + * @param controller Controller to register. + * + * Assigns controller IDs (major number range and minors per device). + */ + auto add_controller(kstd::shared_ptr const & controller) -> void; + + /** + * @brief Return all registered storage controllers. + * @return Vector of all registered storage controllers. + */ + [[nodiscard]] auto all_controllers() const -> kstd::vector> const &; + + /** + * @brief Find a device by major/minor numbers. + * @param major Device major number. + * @param minor Device minor number. + * @return Matching device, or nullptr if no device matches. + */ + auto device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr; + + /** + * @brief Determine the boot device. + * @return Boot device, or nullptr if it cannot be determined. + */ + auto determine_boot_device() -> kstd::shared_ptr; + + private: + /** + * @brief Private default constructor for storage management singleton. + */ + management() = default; + + kstd::vector> m_controllers{}; + }; +} // namespace kernel::devices::storage + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/ram_disk/controller.hpp b/kernel/include/kernel/devices/storage/ram_disk/controller.hpp new file mode 100644 index 0000000..ad8b29f --- /dev/null +++ b/kernel/include/kernel/devices/storage/ram_disk/controller.hpp @@ -0,0 +1,31 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP + +#include "kapi/boot_module/boot_module_registry.hpp" + +#include "kernel/devices/storage/controller.hpp" + +namespace kernel::devices::storage::ram_disk +{ + /** + * @brief Storage controller that exposes boot modules as RAM-disk devices. + */ + struct controller : kernel::devices::storage::controller + { + /** + * @brief Create a RAM-disk controller. + * @param registry Boot module registry as device source. + */ + explicit controller(kapi::boot_modules::boot_module_registry const * registry); + + /** + * @brief Probe boot modules and create RAM-disk devices. + */ + auto probe() -> void override; + + private: + kapi::boot_modules::boot_module_registry const * m_boot_module_registry; + }; +} // namespace kernel::devices::storage::ram_disk + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/ram_disk/device.hpp b/kernel/include/kernel/devices/storage/ram_disk/device.hpp new file mode 100644 index 0000000..0777e86 --- /dev/null +++ b/kernel/include/kernel/devices/storage/ram_disk/device.hpp @@ -0,0 +1,52 @@ +#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP +#define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP + +#include "kapi/boot_module/boot_module.hpp" + +#include "kernel/devices/block_device.hpp" + +#include + +namespace kernel::devices::storage::ram_disk +{ + /** + * @brief Block device for a boot module. + */ + struct device : block_device + { + /** + * @brief Create a RAM disk for the @p module. + * @param module Boot module providing the memory region. + * @param major Device major number. + * @param minor Device minor number. + */ + device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor); + + /** + * @brief Read one logical block into @p buffer. + * @param block_index Zero-based block index. + * @param buffer Destination buffer, must not be null. + * @note If the request reaches the module end, only available bytes are copied and the rest of the + * logical block is filled with zeros. + */ + auto read_block(size_t block_index, void * buffer) const -> void override; + + /** + * @brief Write one logical block from @p buffer. + * @param block_index Zero-based block index. + * @param buffer Source buffer, must not be null. + * @note If the request reaches the module end, only the bytes in the module range are written. + */ + auto write_block(size_t block_index, void const * buffer) -> void override; + + private: + /** + * @brief Return module size in bytes. + */ + [[nodiscard]] auto size() const -> size_t override; + + kapi::boot_modules::boot_module m_boot_module{}; + }; +} // namespace kernel::devices::storage::ram_disk + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/ram_disk/ram_disk_controller.hpp b/kernel/include/kernel/devices/storage/ram_disk/ram_disk_controller.hpp deleted file mode 100644 index 40763bf..0000000 --- a/kernel/include/kernel/devices/storage/ram_disk/ram_disk_controller.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP -#define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP - -#include "kapi/boot_module/boot_module_registry.hpp" - -#include "kernel/devices/storage/storage_controller.hpp" - -namespace kernel::devices::storage::ram_disk -{ - /** - * @brief Storage controller that exposes boot modules as RAM-disk devices. - */ - struct ram_disk_controller : storage_controller - { - /** - * @brief Create a RAM-disk controller. - * @param registry Boot module registry as device source. - */ - explicit ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry); - - /** - * @brief Probe boot modules and create RAM-disk devices. - */ - auto probe() -> void override; - - private: - kapi::boot_modules::boot_module_registry const * m_boot_module_registry; - }; -} // namespace kernel::devices::storage::ram_disk - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/ram_disk/ram_disk_device.hpp b/kernel/include/kernel/devices/storage/ram_disk/ram_disk_device.hpp deleted file mode 100644 index 75ff50b..0000000 --- a/kernel/include/kernel/devices/storage/ram_disk/ram_disk_device.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP -#define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP - -#include "kapi/boot_module/boot_module.hpp" - -#include "kernel/devices/block_device.hpp" - -#include - -namespace kernel::devices::storage::ram_disk -{ - /** - * @brief Block device for a boot module. - */ - struct ram_disk_device : block_device - { - /** - * @brief Create a RAM disk for the @p module. - * @param module Boot module providing the memory region. - * @param major Device major number. - * @param minor Device minor number. - */ - ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor); - - /** - * @brief Read one logical block into @p buffer. - * @param block_index Zero-based block index. - * @param buffer Destination buffer, must not be null. - * @note If the request reaches the module end, only available bytes are copied and the rest of the - * logical block is filled with zeros. - */ - auto read_block(size_t block_index, void * buffer) const -> void override; - - /** - * @brief Write one logical block from @p buffer. - * @param block_index Zero-based block index. - * @param buffer Source buffer, must not be null. - * @note If the request reaches the module end, only the bytes in the module range are written. - */ - auto write_block(size_t block_index, void const * buffer) -> void override; - - private: - /** - * @brief Return module size in bytes. - */ - [[nodiscard]] auto size() const -> size_t override; - - kapi::boot_modules::boot_module m_boot_module{}; - }; -} // namespace kernel::devices::storage::ram_disk - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/storage_controller.hpp b/kernel/include/kernel/devices/storage/storage_controller.hpp deleted file mode 100644 index 5639160..0000000 --- a/kernel/include/kernel/devices/storage/storage_controller.hpp +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP -#define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_CONTROLLER_HPP - -#include "kernel/devices/device.hpp" - -#include -#include - -#include - -namespace kernel::devices::storage -{ - /** - * @brief Base interface for storage controllers. - * - * A storage controller probes for devices and resolves devices by major/minor - * numbers. - */ - struct storage_controller - { - /** - * @brief Virtual destructor. - */ - virtual ~storage_controller() = default; - - /** - * @brief Probe the controller and register discovered devices. - */ - virtual auto probe() -> void = 0; - - /** - * @brief Assign the major number and minor stride for this controller. - * @param major Major number assigned to this controller. - * @param minors_per_dev Minor number stride between devices. - */ - auto set_ids(size_t major, size_t minors_per_dev) -> void; - - /** - * @brief Return the assigned major number. - * @return Assigned major number. - */ - [[nodiscard]] auto major() const -> size_t; - - /** - * @brief Return the number of devices managed by this controller. - * @return Number of managed devices. - */ - [[nodiscard]] auto devices_count() const -> size_t; - - /** - * @brief Return all devices managed by this controller. - * @return Vector of all managed devices. - */ - [[nodiscard]] auto all_devices() const -> kstd::vector> const &; - - /** - * @brief Find a managed device by major/minor numbers. - * @param major Device major number. - * @param minor Device minor number. - * @return Matching block device, or nullptr if no device matches. - */ - [[nodiscard]] auto device_by_minor(size_t minor) const -> kstd::shared_ptr; - - protected: - size_t m_major{}; - size_t m_minors_per_device{}; - kstd::vector> m_devices{}; - }; -} // namespace kernel::devices::storage - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/storage_management.hpp b/kernel/include/kernel/devices/storage/storage_management.hpp deleted file mode 100644 index b6d1318..0000000 --- a/kernel/include/kernel/devices/storage/storage_management.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP -#define TEACH_OS_KERNEL_DEVICES_STORAGE_STORAGE_MANAGEMENT_HPP - -#include "kernel/devices/device.hpp" -#include "kernel/devices/storage/storage_controller.hpp" - -#include -#include - -#include - -namespace kernel::devices::storage -{ - /** - * @brief Global storage subsystem manager. - * - * Owns registered storage controllers and provides device lookup by - * major/minor numbers. - */ - struct storage_management - { - /** - * @brief Initialize global storage management. - * - * Creates the singleton instance, registers controllers and probes - * them for devices. - * - * @warning Panics if called more than once. - */ - auto static init() -> void; - - /** - * @brief Return the active storage manager singleton. - * @return Reference to the active storage manager. - * @warning Panics if storage management has not been initialized. - */ - auto static get() -> storage_management &; - - /** - * @brief Register a storage controller. - * @param controller Controller to register. - * - * Assigns controller IDs (major number range and minors per device). - */ - auto add_controller(kstd::shared_ptr const & controller) -> void; - - /** - * @brief Return all registered storage controllers. - * @return Vector of all registered storage controllers. - */ - [[nodiscard]] auto all_controllers() const -> kstd::vector> const &; - - /** - * @brief Find a device by major/minor numbers. - * @param major Device major number. - * @param minor Device minor number. - * @return Matching device, or nullptr if no device matches. - */ - auto device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr; - - /** - * @brief Determine the boot device. - * @return Boot device, or nullptr if it cannot be determined. - */ - auto determine_boot_device() -> kstd::shared_ptr; - - private: - /** - * @brief Private default constructor for storage management singleton. - */ - storage_management() = default; - - kstd::vector> m_controllers{}; - }; -} // namespace kernel::devices::storage - -#endif \ No newline at end of file diff --git a/kernel/src/devices/storage/controller.cpp b/kernel/src/devices/storage/controller.cpp new file mode 100644 index 0000000..46c45e4 --- /dev/null +++ b/kernel/src/devices/storage/controller.cpp @@ -0,0 +1,44 @@ +#include "kernel/devices/storage/controller.hpp" + +#include "kernel/devices/device.hpp" + +#include +#include + +#include +#include + +namespace kernel::devices::storage +{ + auto controller::set_ids(size_t major, size_t minors_per_dev) -> void + { + m_major = major; + m_minors_per_device = minors_per_dev; + } + + auto controller::major() const -> size_t + { + return m_major; + } + + auto controller::device_by_minor(size_t minor) const -> kstd::shared_ptr + { + auto it = std::ranges::find_if(m_devices, [minor](auto const & device) { return device->minor() == minor; }); + + if (it != m_devices.end()) + { + return *it; + } + return nullptr; + } + + auto controller::devices_count() const -> size_t + { + return m_devices.size(); + } + + auto controller::all_devices() const -> kstd::vector> const & + { + return m_devices; + } +} // namespace kernel::devices::storage \ No newline at end of file diff --git a/kernel/src/devices/storage/management.cpp b/kernel/src/devices/storage/management.cpp new file mode 100644 index 0000000..62c0ce4 --- /dev/null +++ b/kernel/src/devices/storage/management.cpp @@ -0,0 +1,85 @@ +#include "kernel/devices/storage/management.hpp" + +#include "kapi/boot_modules.hpp" +#include "kapi/system.hpp" + +#include "kernel/devices/device.hpp" +#include "kernel/devices/storage/controller.hpp" +#include "kernel/devices/storage/ram_disk/controller.hpp" + +#include +#include + +#include +#include +#include + +namespace kernel::devices::storage +{ + namespace + { + constexpr size_t static MINORS_PER_DEVICE = 16; + constexpr size_t static START_MAJOR = 1; + constinit size_t static next_free_major = START_MAJOR; + + constinit auto static active_storage_management = std::optional{}; + } // namespace + + auto management::init() -> void + { + if (active_storage_management) + { + kapi::system::panic("[DEVICES] Storage management has already been initialized."); + } + active_storage_management.emplace(management{}); + + auto current_ram_disk_controller = + kstd::make_shared(&kapi::boot_modules::get_boot_module_registry()); + active_storage_management->add_controller(current_ram_disk_controller); + + std::ranges::for_each(active_storage_management->m_controllers, [](auto controller) { controller->probe(); }); + } + + auto management::get() -> management & + { + if (!active_storage_management) + { + kapi::system::panic("[DEVICES] Storage management has not been initialized."); + } + + return *active_storage_management; + } + + auto management::add_controller(kstd::shared_ptr const & controller) -> void + { + controller->set_ids(next_free_major++, MINORS_PER_DEVICE); + m_controllers.push_back(controller); + } + + auto management::all_controllers() const -> kstd::vector> const & + { + return m_controllers; + } + + auto management::device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr + { + kstd::shared_ptr found = nullptr; + + std::ranges::find_if(m_controllers, [&](auto const & controller) { + if (controller != nullptr && controller->major() == major) + { + found = controller->device_by_minor(minor); + return found != nullptr; + } + return false; + }); + + return found; + } + + auto management::determine_boot_device() -> kstd::shared_ptr + { + return device_by_major_minor(START_MAJOR, 0); + } + +} // namespace kernel::devices::storage \ No newline at end of file diff --git a/kernel/src/devices/storage/ram_disk/controller.cpp b/kernel/src/devices/storage/ram_disk/controller.cpp new file mode 100644 index 0000000..040e61f --- /dev/null +++ b/kernel/src/devices/storage/ram_disk/controller.cpp @@ -0,0 +1,27 @@ +#include "kernel/devices/storage/ram_disk/controller.hpp" + +#include "kapi/boot_module/boot_module_registry.hpp" + +#include "kernel/devices/storage/ram_disk/device.hpp" + +#include + +#include +#include + +namespace kernel::devices::storage::ram_disk +{ + controller::controller(kapi::boot_modules::boot_module_registry const * registry) + : m_boot_module_registry(registry) + {} + + auto controller::probe() -> void + { + size_t current_device_index = 0; + + std::ranges::for_each(*m_boot_module_registry, [this, ¤t_device_index](auto const & module) { + auto const minor = current_device_index++ * m_minors_per_device; + m_devices.push_back(kstd::make_shared(module, m_major, minor)); + }); + } +} // namespace kernel::devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/src/devices/storage/ram_disk/device.cpp b/kernel/src/devices/storage/ram_disk/device.cpp new file mode 100644 index 0000000..5116c93 --- /dev/null +++ b/kernel/src/devices/storage/ram_disk/device.cpp @@ -0,0 +1,65 @@ +#include "kapi/boot_module/boot_module.hpp" +#include "kapi/system.hpp" + +#include "kernel/devices/block_device.hpp" +#include "kernel/devices/storage/ram_disk/device.hpp" + +#include +#include + +#include + +namespace kernel::devices::storage::ram_disk +{ + namespace + { + constexpr size_t RAM_DISK_BLOCK_SIZE = 512uz; + } // namespace + + device::device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor) + : block_device(major, minor, "ram" + kstd::to_string(minor), RAM_DISK_BLOCK_SIZE) + , m_boot_module(module) + {} + + auto device::read_block(size_t block_index, void * buffer) const -> void + { + if (buffer == nullptr) + { + kapi::system::panic("[RAM DISK DEVICE] read_block called with null buffer."); + } + + auto const info = calculate_transfer(block_index); + + if (info.to_transfer > 0) + { + auto const src = static_cast(m_boot_module.start_address) + info.offset; + kstd::libc::memcpy(buffer, src, info.to_transfer); + } + + if (info.remainder > 0) + { + kstd::libc::memset(static_cast(buffer) + info.to_transfer, 0, info.remainder); + } + } + + auto device::write_block(size_t block_index, void const * buffer) -> void + { + if (buffer == nullptr) + { + kapi::system::panic("[RAM DISK DEVICE] write_block called with null buffer."); + } + + auto const info = calculate_transfer(block_index); + + if (info.to_transfer > 0) + { + auto const dest = static_cast(m_boot_module.start_address) + info.offset; + kstd::libc::memcpy(dest, buffer, info.to_transfer); + } + } + + auto device::size() const -> size_t + { + return m_boot_module.size; + } +} // namespace kernel::devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp b/kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp deleted file mode 100644 index 26eb10b..0000000 --- a/kernel/src/devices/storage/ram_disk/ram_disk_controller.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "kernel/devices/storage/ram_disk/ram_disk_controller.hpp" - -#include "kapi/boot_module/boot_module_registry.hpp" - -#include "kernel/devices/storage/ram_disk/ram_disk_device.hpp" - -#include - -#include -#include - -namespace kernel::devices::storage::ram_disk -{ - ram_disk_controller::ram_disk_controller(kapi::boot_modules::boot_module_registry const * registry) - : m_boot_module_registry(registry) - {} - - auto ram_disk_controller::probe() -> void - { - size_t current_device_index = 0; - - std::ranges::for_each(*m_boot_module_registry, [this, ¤t_device_index](auto const & module) { - auto const minor = current_device_index++ * m_minors_per_device; - m_devices.push_back(kstd::make_shared(module, m_major, minor)); - }); - } -} // namespace kernel::devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp b/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp deleted file mode 100644 index 72e8025..0000000 --- a/kernel/src/devices/storage/ram_disk/ram_disk_device.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "kernel/devices/storage/ram_disk/ram_disk_device.hpp" - -#include "kapi/boot_module/boot_module.hpp" -#include "kapi/system.hpp" - -#include "kernel/devices/block_device.hpp" - -#include -#include - -#include - -namespace kernel::devices::storage::ram_disk -{ - namespace - { - constexpr size_t RAM_DISK_BLOCK_SIZE = 512uz; - } // namespace - - ram_disk_device::ram_disk_device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor) - : block_device(major, minor, "ram" + kstd::to_string(minor), RAM_DISK_BLOCK_SIZE) - , m_boot_module(module) - {} - - auto ram_disk_device::read_block(size_t block_index, void * buffer) const -> void - { - if (buffer == nullptr) - { - kapi::system::panic("[RAM DISK DEVICE] read_block called with null buffer."); - } - - auto const info = calculate_transfer(block_index); - - if (info.to_transfer > 0) - { - auto const src = static_cast(m_boot_module.start_address) + info.offset; - kstd::libc::memcpy(buffer, src, info.to_transfer); - } - - if (info.remainder > 0) - { - kstd::libc::memset(static_cast(buffer) + info.to_transfer, 0, info.remainder); - } - } - - auto ram_disk_device::write_block(size_t block_index, void const * buffer) -> void - { - if (buffer == nullptr) - { - kapi::system::panic("[RAM DISK DEVICE] write_block called with null buffer."); - } - - auto const info = calculate_transfer(block_index); - - if (info.to_transfer > 0) - { - auto const dest = static_cast(m_boot_module.start_address) + info.offset; - kstd::libc::memcpy(dest, buffer, info.to_transfer); - } - } - - auto ram_disk_device::size() const -> size_t - { - return m_boot_module.size; - } -} // namespace kernel::devices::storage::ram_disk \ No newline at end of file diff --git a/kernel/src/devices/storage/storage_controller.cpp b/kernel/src/devices/storage/storage_controller.cpp deleted file mode 100644 index 3d13b66..0000000 --- a/kernel/src/devices/storage/storage_controller.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "kernel/devices/storage/storage_controller.hpp" - -#include "kernel/devices/device.hpp" - -#include -#include - -#include -#include - -namespace kernel::devices::storage -{ - auto storage_controller::set_ids(size_t major, size_t minors_per_dev) -> void - { - m_major = major; - m_minors_per_device = minors_per_dev; - } - - auto storage_controller::major() const -> size_t - { - return m_major; - } - - auto storage_controller::device_by_minor(size_t minor) const -> kstd::shared_ptr - { - auto it = std::ranges::find_if(m_devices, [minor](auto const & device) { return device->minor() == minor; }); - - if (it != m_devices.end()) - { - return *it; - } - return nullptr; - } - - auto storage_controller::devices_count() const -> size_t - { - return m_devices.size(); - } - - auto storage_controller::all_devices() const -> kstd::vector> const & - { - return m_devices; - } -} // namespace kernel::devices::storage \ No newline at end of file diff --git a/kernel/src/devices/storage/storage_management.cpp b/kernel/src/devices/storage/storage_management.cpp deleted file mode 100644 index 2bc57c4..0000000 --- a/kernel/src/devices/storage/storage_management.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "kernel/devices/storage/storage_management.hpp" - -#include "kapi/boot_modules.hpp" -#include "kapi/system.hpp" - -#include "kernel/devices/device.hpp" -#include "kernel/devices/storage/ram_disk/ram_disk_controller.hpp" -#include "kernel/devices/storage/storage_controller.hpp" - -#include -#include - -#include -#include -#include - -namespace kernel::devices::storage -{ - namespace - { - constexpr size_t static MINORS_PER_DEVICE = 16; - constexpr size_t static START_MAJOR = 1; - constinit size_t static next_free_major = START_MAJOR; - - constinit auto static active_storage_management = std::optional{}; - } // namespace - - auto storage_management::init() -> void - { - if (active_storage_management) - { - kapi::system::panic("[DEVICES] Storage management has already been initialized."); - } - active_storage_management.emplace(storage_management{}); - - auto current_ram_disk_controller = - kstd::make_shared(&kapi::boot_modules::get_boot_module_registry()); - active_storage_management->add_controller(current_ram_disk_controller); - - std::ranges::for_each(active_storage_management->m_controllers, [](auto controller) { controller->probe(); }); - } - - auto storage_management::get() -> storage_management & - { - if (!active_storage_management) - { - kapi::system::panic("[DEVICES] Storage management has not been initialized."); - } - - return *active_storage_management; - } - - auto storage_management::add_controller(kstd::shared_ptr const & controller) -> void - { - controller->set_ids(next_free_major++, MINORS_PER_DEVICE); - m_controllers.push_back(controller); - } - - auto storage_management::all_controllers() const -> kstd::vector> const & - { - return m_controllers; - } - - auto storage_management::device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr - { - kstd::shared_ptr found = nullptr; - - std::ranges::find_if(m_controllers, [&](auto const & controller) { - if (controller != nullptr && controller->major() == major) - { - found = controller->device_by_minor(minor); - return found != nullptr; - } - return false; - }); - - return found; - } - - auto storage_management::determine_boot_device() -> kstd::shared_ptr - { - return device_by_major_minor(START_MAJOR, 0); - } - -} // namespace kernel::devices::storage \ No newline at end of file diff --git a/kernel/src/filesystem/devfs/filesystem.cpp b/kernel/src/filesystem/devfs/filesystem.cpp index e7d0e13..94c9b05 100644 --- a/kernel/src/filesystem/devfs/filesystem.cpp +++ b/kernel/src/filesystem/devfs/filesystem.cpp @@ -1,7 +1,7 @@ #include "kernel/filesystem/devfs/filesystem.hpp" #include "kernel/devices/device.hpp" -#include "kernel/devices/storage/storage_management.hpp" +#include "kernel/devices/storage/management.hpp" #include "kernel/filesystem/devfs/inode.hpp" #include "kernel/filesystem/device_inode.hpp" #include "kernel/filesystem/inode.hpp" @@ -49,7 +49,7 @@ namespace kernel::filesystem::devfs { m_inodes.clear(); - auto storage_mgmt = devices::storage::storage_management::get(); + auto storage_mgmt = devices::storage::management::get(); std::ranges::for_each(storage_mgmt.all_controllers(), [&](auto const & controller) { std::ranges::for_each(controller->all_devices(), [&](auto const & device) { m_inodes.push_back(kstd::make_shared(device)); }); diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index bee68e6..06214d2 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -2,7 +2,7 @@ #include "kapi/system.hpp" -#include "kernel/devices/storage/storage_management.hpp" +#include "kernel/devices/storage/management.hpp" #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/devfs/filesystem.hpp" #include "kernel/filesystem/ext2/filesystem.hpp" @@ -43,7 +43,7 @@ namespace kernel::filesystem auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode()); m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, "")); - auto storage_mgmt = devices::storage::storage_management::get(); + auto storage_mgmt = devices::storage::management::get(); if (auto boot_device = storage_mgmt.determine_boot_device()) { // TODO BA-FS26 detect fs type from boot device and load corresponding fs, for now just assume ext2 diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index ff73985..37b4c5b 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -5,7 +5,7 @@ #include "kapi/memory.hpp" #include "kapi/system.hpp" -#include "kernel/devices/storage/storage_management.hpp" +#include "kernel/devices/storage/management.hpp" #include "kernel/filesystem/device_inode.hpp" #include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/file_descriptor_table.hpp" @@ -24,7 +24,7 @@ auto test_device_names() -> void { - auto storage_mgmt = kernel::devices::storage::storage_management::get(); + auto storage_mgmt = kernel::devices::storage::management::get(); std::ranges::for_each(storage_mgmt.all_controllers(), [](auto const & controller) { std::ranges::for_each(controller->all_devices(), [](auto const & device) { kstd::println("{}", device->name().view()); }); @@ -35,7 +35,7 @@ auto test_file_description_manually() -> void { // setup auto fd_table = kernel::filesystem::file_descriptor_table::get(); - auto storage_mgmt = kernel::devices::storage::storage_management::get(); + auto storage_mgmt = kernel::devices::storage::management::get(); auto device = storage_mgmt.device_by_major_minor(1, 0); auto dev_node = kstd::make_shared(device); @@ -111,7 +111,7 @@ auto test_file_lookup() -> void // TODO BA-FS26 implement a more complete test with multiple files and directories and mounts etc. auto vfs = kernel::filesystem::vfs::get(); - auto storage_mgmt = kernel::devices::storage::storage_management::get(); + auto storage_mgmt = kernel::devices::storage::management::get(); auto ofd1 = vfs.open("/a/b/c"); auto ofd2 = vfs.open("/dev/ram0"); @@ -182,7 +182,7 @@ auto main() -> int kapi::boot_modules::init(); kstd::println("[OS] Boot module registry initialized."); - kernel::devices::storage::storage_management::init(); + kernel::devices::storage::management::init(); kstd::println("[OS] Storage management initialized."); kernel::filesystem::file_descriptor_table::init(); -- cgit v1.2.3 From 9e85f9d1f34d08213a918d9c1b0845c179e323af Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 31 Mar 2026 08:56:17 +0200 Subject: move device into kapi --- kapi/include/kapi/devices/device.hpp | 81 ++++++++++++++++++++++ kernel/CMakeLists.txt | 1 - kernel/include/kernel/devices/block_device.hpp | 4 +- .../include/kernel/devices/block_device_utils.hpp | 6 +- kernel/include/kernel/devices/device.hpp | 62 ----------------- .../include/kernel/devices/storage/controller.hpp | 8 +-- .../include/kernel/devices/storage/management.hpp | 6 +- .../kernel/devices/storage/ram_disk/device.hpp | 6 ++ .../include/kernel/filesystem/devfs/filesystem.hpp | 4 +- kernel/include/kernel/filesystem/device_inode.hpp | 8 +-- .../include/kernel/filesystem/ext2/filesystem.hpp | 4 +- kernel/include/kernel/filesystem/filesystem.hpp | 6 +- .../kernel/filesystem/rootfs/filesystem.hpp | 4 +- kernel/src/devices/block_device.cpp | 4 +- kernel/src/devices/block_device_utils.cpp | 8 +-- kernel/src/devices/device.cpp | 29 -------- kernel/src/devices/storage/controller.cpp | 6 +- kernel/src/devices/storage/management.cpp | 8 +-- kernel/src/devices/storage/ram_disk/device.cpp | 8 ++- kernel/src/filesystem/devfs/filesystem.cpp | 4 +- kernel/src/filesystem/device_inode.cpp | 6 +- kernel/src/filesystem/ext2/filesystem.cpp | 4 +- kernel/src/filesystem/filesystem.cpp | 4 +- kernel/src/filesystem/rootfs/filesystem.cpp | 4 +- 24 files changed, 143 insertions(+), 142 deletions(-) create mode 100644 kapi/include/kapi/devices/device.hpp delete mode 100644 kernel/include/kernel/devices/device.hpp delete mode 100644 kernel/src/devices/device.cpp diff --git a/kapi/include/kapi/devices/device.hpp b/kapi/include/kapi/devices/device.hpp new file mode 100644 index 0000000..a049cf5 --- /dev/null +++ b/kapi/include/kapi/devices/device.hpp @@ -0,0 +1,81 @@ +#ifndef TEACH_OS_KAPI_DEVICES_DEVICE_HPP +#define TEACH_OS_KAPI_DEVICES_DEVICE_HPP + +#include + +#include + +namespace kapi::devices +{ + /** + * @brief Base device identified by a major, minor number and name. + */ + struct device + { + /** + * @brief Create a device identifier from @p major, @p minor and @p name. + * @param major Device major number. + * @param minor Device minor number. + * @param name Device name. + */ + device(size_t major, size_t minor, kstd::string const & name) + : m_major(major) + , m_minor(minor) + , m_name(name) + {} + + /** + * @brief Virtual destructor for device. + */ + virtual ~device() = default; + + /** + * @brief Initialize the device. + * @return true on success, false otherwise. + */ + virtual auto init() -> bool = 0; + + /** + * @brief Returns the major number of the device. + * @return Device major number. + */ + [[nodiscard]] auto major() const -> size_t + { + return m_major; + } + + /** + * @brief Returns the minor number of the device. + * @return Device minor number. + */ + [[nodiscard]] auto minor() const -> size_t + { + return m_minor; + } + + /** + * @brief Returns the name of the device. + * @return Device name. + */ + [[nodiscard]] auto name() const -> kstd::string const & + { + return m_name; + } + + /** + * @brief Check if the device is a block device. + * @return true if this device is a block device, false otherwise. + */ + [[nodiscard]] virtual auto is_block_device() const -> bool + { + return false; + } + + private: + size_t m_major; + size_t m_minor; + kstd::string m_name; + }; +} // namespace kapi::devices + +#endif \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 10bfecb..cb3d8a5 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -17,7 +17,6 @@ add_executable("kernel" "src/memory/block_list_allocator.cpp" "src/memory/operators.cpp" "src/memory.cpp" - "src/devices/device.cpp" "src/devices/block_device.cpp" "src/devices/block_device_utils.cpp" "src/devices/storage/controller.cpp" diff --git a/kernel/include/kernel/devices/block_device.hpp b/kernel/include/kernel/devices/block_device.hpp index 619b815..43e6511 100644 --- a/kernel/include/kernel/devices/block_device.hpp +++ b/kernel/include/kernel/devices/block_device.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP #define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include @@ -12,7 +12,7 @@ namespace kernel::devices /** * @brief Base interface for block-addressable devices. */ - struct block_device : device + struct block_device : kapi::devices::device { /** * @brief Create a block device descriptor. diff --git a/kernel/include/kernel/devices/block_device_utils.hpp b/kernel/include/kernel/devices/block_device_utils.hpp index bb49d04..5e862ba 100644 --- a/kernel/include/kernel/devices/block_device_utils.hpp +++ b/kernel/include/kernel/devices/block_device_utils.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_UTILS_HPP #define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_UTILS_HPP -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include @@ -9,8 +9,8 @@ namespace kernel::devices::block_device_utils { - auto read(kstd::shared_ptr const & device, void * buffer, size_t offset, size_t size) -> size_t; - auto write(kstd::shared_ptr const & device, void const * buffer, size_t offset, size_t size) + auto read(kstd::shared_ptr const & device, void * buffer, size_t offset, size_t size) -> size_t; + auto write(kstd::shared_ptr const & device, void const * buffer, size_t offset, size_t size) -> size_t; } // namespace kernel::devices::block_device_utils diff --git a/kernel/include/kernel/devices/device.hpp b/kernel/include/kernel/devices/device.hpp deleted file mode 100644 index 67fa5ad..0000000 --- a/kernel/include/kernel/devices/device.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef TEACH_OS_KERNEL_DEVICES_DEVICE_HPP -#define TEACH_OS_KERNEL_DEVICES_DEVICE_HPP - -#include - -#include - -namespace kernel::devices -{ - /** - * @brief Base device identified by a major, minor number and name. - */ - struct device - { - /** - * @brief Create a device identifier from @p major, @p minor and @p name. - * @param major Device major number. - * @param minor Device minor number. - * @param name Device name. - */ - device(size_t major, size_t minor, kstd::string const & name); - - /** - * @brief Virtual destructor for device. - */ - virtual ~device() = default; - - /** - * @brief Returns the major number of the device. - * @return Device major number. - */ - [[nodiscard]] auto major() const -> size_t; - - /** - * @brief Returns the minor number of the device. - * @return Device minor number. - */ - [[nodiscard]] auto minor() const -> size_t; - - /** - * @brief Returns the name of the device. - * @return Device name. - */ - [[nodiscard]] auto name() const -> kstd::string const &; - - /** - * @brief Check if the device is a block device. - * @return true if this device is a block device, false otherwise. - */ - [[nodiscard]] virtual auto is_block_device() const -> bool - { - return false; - } - - private: - size_t m_major; - size_t m_minor; - kstd::string m_name; - }; -} // namespace kernel::devices - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/storage/controller.hpp b/kernel/include/kernel/devices/storage/controller.hpp index e3bfd01..a91e452 100644 --- a/kernel/include/kernel/devices/storage/controller.hpp +++ b/kernel/include/kernel/devices/storage/controller.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_CONTROLLER_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_CONTROLLER_HPP -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include #include @@ -51,7 +51,7 @@ namespace kernel::devices::storage * @brief Return all devices managed by this controller. * @return Vector of all managed devices. */ - [[nodiscard]] auto all_devices() const -> kstd::vector> const &; + [[nodiscard]] auto all_devices() const -> kstd::vector> const &; /** * @brief Find a managed device by major/minor numbers. @@ -59,12 +59,12 @@ namespace kernel::devices::storage * @param minor Device minor number. * @return Matching block device, or nullptr if no device matches. */ - [[nodiscard]] auto device_by_minor(size_t minor) const -> kstd::shared_ptr; + [[nodiscard]] auto device_by_minor(size_t minor) const -> kstd::shared_ptr; protected: size_t m_major{}; size_t m_minors_per_device{}; - kstd::vector> m_devices{}; + kstd::vector> m_devices{}; }; } // namespace kernel::devices::storage diff --git a/kernel/include/kernel/devices/storage/management.hpp b/kernel/include/kernel/devices/storage/management.hpp index 255d170..0176ce1 100644 --- a/kernel/include/kernel/devices/storage/management.hpp +++ b/kernel/include/kernel/devices/storage/management.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_MANAGEMENT_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_MANAGEMENT_HPP -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include "kernel/devices/storage/controller.hpp" #include @@ -56,13 +56,13 @@ namespace kernel::devices::storage * @param minor Device minor number. * @return Matching device, or nullptr if no device matches. */ - auto device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr; + auto device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr; /** * @brief Determine the boot device. * @return Boot device, or nullptr if it cannot be determined. */ - auto determine_boot_device() -> kstd::shared_ptr; + auto determine_boot_device() -> kstd::shared_ptr; private: /** diff --git a/kernel/include/kernel/devices/storage/ram_disk/device.hpp b/kernel/include/kernel/devices/storage/ram_disk/device.hpp index 0777e86..e17416e 100644 --- a/kernel/include/kernel/devices/storage/ram_disk/device.hpp +++ b/kernel/include/kernel/devices/storage/ram_disk/device.hpp @@ -22,6 +22,12 @@ namespace kernel::devices::storage::ram_disk */ device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor); + /** + * @brief Initialize the RAM disk device. + * @return true if module backing memory is valid, false otherwise. + */ + auto init() -> bool override; + /** * @brief Read one logical block into @p buffer. * @param block_index Zero-based block index. diff --git a/kernel/include/kernel/filesystem/devfs/filesystem.hpp b/kernel/include/kernel/filesystem/devfs/filesystem.hpp index 5ec6221..29ae388 100644 --- a/kernel/include/kernel/filesystem/devfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/devfs/filesystem.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVFS_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_FILESYSTEM_HPP -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" @@ -14,7 +14,7 @@ namespace kernel::filesystem::devfs { struct filesystem : kernel::filesystem::filesystem { - auto mount(kstd::shared_ptr const & device) -> int override; + auto mount(kstd::shared_ptr const & device) -> int override; auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index 7f044b0..18a98f5 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include "kernel/filesystem/inode.hpp" #include @@ -12,15 +12,15 @@ namespace kernel::filesystem { struct device_inode : inode { - explicit device_inode(kstd::shared_ptr const & device); + explicit device_inode(kstd::shared_ptr const & device); auto read(void * buffer, size_t offset, size_t size) const -> size_t override; auto write(void const * buffer, size_t offset, size_t size) -> size_t override; - [[nodiscard]] auto device() const -> kstd::shared_ptr const &; + [[nodiscard]] auto device() const -> kstd::shared_ptr const &; private: - kstd::shared_ptr m_device; + kstd::shared_ptr m_device; }; } // namespace kernel::filesystem diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 763cd1d..078da31 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" @@ -13,7 +13,7 @@ namespace kernel::filesystem::ext2 { struct filesystem : kernel::filesystem::filesystem { - auto mount(kstd::shared_ptr const & device) -> int override; + auto mount(kstd::shared_ptr const & device) -> int override; auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; }; diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index c50a86c..1d86178 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include "kernel/filesystem/inode.hpp" #include @@ -15,14 +15,14 @@ namespace kernel::filesystem { virtual ~filesystem() = default; - virtual auto mount(kstd::shared_ptr const & device) -> int; + virtual auto mount(kstd::shared_ptr const & device) -> int; virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr = 0; [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; protected: kstd::shared_ptr m_root_inode{}; - kstd::shared_ptr m_device{}; + kstd::shared_ptr m_device{}; kstd::vector> m_inodes{}; }; diff --git a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp index b1f33a9..5632d86 100644 --- a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_FILESYSTEM_HPP -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" @@ -15,7 +15,7 @@ namespace kernel::filesystem::rootfs { struct filesystem : kernel::filesystem::filesystem { - auto mount(kstd::shared_ptr const & device) -> int override; + auto mount(kstd::shared_ptr const & device) -> int override; auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; }; diff --git a/kernel/src/devices/block_device.cpp b/kernel/src/devices/block_device.cpp index cfe2eb2..c006198 100644 --- a/kernel/src/devices/block_device.cpp +++ b/kernel/src/devices/block_device.cpp @@ -2,7 +2,7 @@ #include "kapi/system.hpp" -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include @@ -11,7 +11,7 @@ namespace kernel::devices { block_device::block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size) - : device(major, minor, name) + : kapi::devices::device(major, minor, name) , m_block_size(block_size) { if (m_block_size == 0) diff --git a/kernel/src/devices/block_device_utils.cpp b/kernel/src/devices/block_device_utils.cpp index 5469087..6fe89fe 100644 --- a/kernel/src/devices/block_device_utils.cpp +++ b/kernel/src/devices/block_device_utils.cpp @@ -3,7 +3,7 @@ #include "kapi/system.hpp" #include "kernel/devices/block_device.hpp" -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include #include @@ -18,7 +18,7 @@ namespace kernel::devices::block_device_utils using block_op = void (*)(size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, std::byte * scratch, void * buffer); - auto process_blocks(kstd::shared_ptr const & device, size_t offset, size_t size, void * buffer, + auto process_blocks(kstd::shared_ptr const & device, size_t offset, size_t size, void * buffer, block_op op) -> size_t { if (buffer == nullptr) @@ -62,7 +62,7 @@ namespace kernel::devices::block_device_utils return processed; } - auto read(kstd::shared_ptr const & device, void * buffer, size_t offset, size_t size) -> size_t + auto read(kstd::shared_ptr const & device, void * buffer, size_t offset, size_t size) -> size_t { return process_blocks(device, offset, size, buffer, [](size_t idx, size_t off, size_t len, size_t done, devices::block_device * device, @@ -80,7 +80,7 @@ namespace kernel::devices::block_device_utils }); } - auto write(kstd::shared_ptr const & device, void const * buffer, size_t offset, size_t size) + auto write(kstd::shared_ptr const & device, void const * buffer, size_t offset, size_t size) -> size_t { return process_blocks(device, offset, size, const_cast(buffer), diff --git a/kernel/src/devices/device.cpp b/kernel/src/devices/device.cpp deleted file mode 100644 index 1e7589e..0000000 --- a/kernel/src/devices/device.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "kernel/devices/device.hpp" - -#include - -#include - -namespace kernel::devices -{ - device::device(size_t major, size_t minor, kstd::string const & name) - : m_major(major) - , m_minor(minor) - , m_name(name) - {} - - auto device::major() const -> size_t - { - return m_major; - } - - auto device::minor() const -> size_t - { - return m_minor; - } - - auto device::name() const -> kstd::string const & - { - return m_name; - } -} // namespace kernel::devices \ No newline at end of file diff --git a/kernel/src/devices/storage/controller.cpp b/kernel/src/devices/storage/controller.cpp index 46c45e4..1bef670 100644 --- a/kernel/src/devices/storage/controller.cpp +++ b/kernel/src/devices/storage/controller.cpp @@ -1,6 +1,6 @@ #include "kernel/devices/storage/controller.hpp" -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include #include @@ -21,7 +21,7 @@ namespace kernel::devices::storage return m_major; } - auto controller::device_by_minor(size_t minor) const -> kstd::shared_ptr + auto controller::device_by_minor(size_t minor) const -> kstd::shared_ptr { auto it = std::ranges::find_if(m_devices, [minor](auto const & device) { return device->minor() == minor; }); @@ -37,7 +37,7 @@ namespace kernel::devices::storage return m_devices.size(); } - auto controller::all_devices() const -> kstd::vector> const & + auto controller::all_devices() const -> kstd::vector> const & { return m_devices; } diff --git a/kernel/src/devices/storage/management.cpp b/kernel/src/devices/storage/management.cpp index 62c0ce4..d440bf0 100644 --- a/kernel/src/devices/storage/management.cpp +++ b/kernel/src/devices/storage/management.cpp @@ -3,7 +3,7 @@ #include "kapi/boot_modules.hpp" #include "kapi/system.hpp" -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include "kernel/devices/storage/controller.hpp" #include "kernel/devices/storage/ram_disk/controller.hpp" @@ -61,9 +61,9 @@ namespace kernel::devices::storage return m_controllers; } - auto management::device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr + auto management::device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr { - kstd::shared_ptr found = nullptr; + kstd::shared_ptr found = nullptr; std::ranges::find_if(m_controllers, [&](auto const & controller) { if (controller != nullptr && controller->major() == major) @@ -77,7 +77,7 @@ namespace kernel::devices::storage return found; } - auto management::determine_boot_device() -> kstd::shared_ptr + auto management::determine_boot_device() -> kstd::shared_ptr { return device_by_major_minor(START_MAJOR, 0); } diff --git a/kernel/src/devices/storage/ram_disk/device.cpp b/kernel/src/devices/storage/ram_disk/device.cpp index 5116c93..8fc3b2a 100644 --- a/kernel/src/devices/storage/ram_disk/device.cpp +++ b/kernel/src/devices/storage/ram_disk/device.cpp @@ -1,8 +1,9 @@ +#include "kernel/devices/storage/ram_disk/device.hpp" + #include "kapi/boot_module/boot_module.hpp" #include "kapi/system.hpp" #include "kernel/devices/block_device.hpp" -#include "kernel/devices/storage/ram_disk/device.hpp" #include #include @@ -21,6 +22,11 @@ namespace kernel::devices::storage::ram_disk , m_boot_module(module) {} + auto device::init() -> bool + { + return m_boot_module.start_address.raw() != 0 && m_boot_module.size > 0; + } + auto device::read_block(size_t block_index, void * buffer) const -> void { if (buffer == nullptr) diff --git a/kernel/src/filesystem/devfs/filesystem.cpp b/kernel/src/filesystem/devfs/filesystem.cpp index 94c9b05..9043ac5 100644 --- a/kernel/src/filesystem/devfs/filesystem.cpp +++ b/kernel/src/filesystem/devfs/filesystem.cpp @@ -1,6 +1,6 @@ #include "kernel/filesystem/devfs/filesystem.hpp" -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include "kernel/devices/storage/management.hpp" #include "kernel/filesystem/devfs/inode.hpp" #include "kernel/filesystem/device_inode.hpp" @@ -13,7 +13,7 @@ namespace kernel::filesystem::devfs { - auto filesystem::mount(kstd::shared_ptr const &) -> int + auto filesystem::mount(kstd::shared_ptr const &) -> int { m_root_inode = kstd::make_shared(); build_device_inode_table(); diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index d574d8f..af8cecc 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -3,7 +3,7 @@ #include "kapi/system.hpp" #include "kernel/devices/block_device_utils.hpp" -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include "kernel/filesystem/inode.hpp" #include @@ -14,7 +14,7 @@ namespace kernel::filesystem { - device_inode::device_inode(kstd::shared_ptr const & device) + device_inode::device_inode(kstd::shared_ptr const & device) : inode(inode_kind::device) , m_device(device) { @@ -48,7 +48,7 @@ namespace kernel::filesystem } } - auto device_inode::device() const -> kstd::shared_ptr const & + auto device_inode::device() const -> kstd::shared_ptr const & { return m_device; } diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index d951158..eb9edc4 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -1,7 +1,7 @@ #include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/devices/block_device_utils.hpp" -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include "kernel/filesystem/ext2/inode.hpp" #include "kernel/filesystem/ext2/superblock.hpp" #include "kernel/filesystem/filesystem.hpp" @@ -45,7 +45,7 @@ namespace kernel::filesystem::ext2 // } } // namespace - auto filesystem::mount(kstd::shared_ptr const & device) -> int + auto filesystem::mount(kstd::shared_ptr const & device) -> int { kernel::filesystem::filesystem::mount(device); // TODO BA-FS26 error handling? // TODO BA-FS26 load proper root inode from ext2 metadata diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index c891d00..0ac9cf8 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -1,13 +1,13 @@ #include "kernel/filesystem/filesystem.hpp" -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include "kernel/filesystem/inode.hpp" #include namespace kernel::filesystem { - auto filesystem::mount(kstd::shared_ptr const & device) -> int + auto filesystem::mount(kstd::shared_ptr const & device) -> int { if (!device) { diff --git a/kernel/src/filesystem/rootfs/filesystem.cpp b/kernel/src/filesystem/rootfs/filesystem.cpp index 0133612..37bf588 100644 --- a/kernel/src/filesystem/rootfs/filesystem.cpp +++ b/kernel/src/filesystem/rootfs/filesystem.cpp @@ -1,6 +1,6 @@ #include "kernel/filesystem/rootfs/filesystem.hpp" -#include "kernel/devices/device.hpp" +#include "kapi/devices/device.hpp" #include "kernel/filesystem/inode.hpp" #include "kernel/filesystem/rootfs/inode.hpp" @@ -10,7 +10,7 @@ namespace kernel::filesystem::rootfs { - auto filesystem::mount(kstd::shared_ptr const &) -> int + auto filesystem::mount(kstd::shared_ptr const &) -> int { auto rfs_inode = kstd::make_shared(); rfs_inode->add_child("dev"); -- cgit v1.2.3 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 From f7ff847498d629c05bb206b41a172f6735e2afe6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 09:51:44 +0200 Subject: kernel/tests: clean up implementation structure --- kernel/CMakeLists.txt | 1 + kernel/include/kernel/test_support/cio.hpp | 29 ++++++++ kernel/include/kernel/test_support/cpu.hpp | 2 + kernel/include/kernel/test_support/log_buffer.hpp | 49 +++++++------- kernel/include/kernel/test_support/memory.hpp | 6 +- kernel/include/kernel/test_support/page_mapper.hpp | 2 +- .../kernel/test_support/simulated_memory.hpp | 23 ++++--- kernel/kapi/system.tests.cpp | 6 +- kernel/kstd/print.tests.cpp | 6 +- kernel/src/memory/block_list_allocator.tests.cpp | 12 +--- kernel/src/test_support/kapi/cio.cpp | 63 ++++++++++-------- kernel/src/test_support/kapi/cpu.cpp | 37 ++++++----- kernel/src/test_support/kapi/memory.cpp | 25 +++---- kernel/src/test_support/log_buffer.cpp | 32 ++++----- kernel/src/test_support/output_device.cpp | 28 ++++++++ kernel/src/test_support/page_mapper.cpp | 22 ++----- kernel/src/test_support/simulated_memory.cpp | 77 +++++++++++++--------- kernel/src/test_support/state_reset_listener.cpp | 28 ++------ 18 files changed, 261 insertions(+), 187 deletions(-) create mode 100644 kernel/include/kernel/test_support/cio.hpp create mode 100644 kernel/src/test_support/output_device.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 1b71a5f..f283588 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/output_device.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/test_support/cio.hpp b/kernel/include/kernel/test_support/cio.hpp new file mode 100644 index 0000000..4b897b5 --- /dev/null +++ b/kernel/include/kernel/test_support/cio.hpp @@ -0,0 +1,29 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_CIO_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_CIO_HPP + +#include "kapi/cio.hpp" + +#include "kernel/test_support/log_buffer.hpp" + +#include + +namespace kernel::tests::cio +{ + struct output_device : kapi::cio::output_device + { + output_device() = default; + + auto write(kapi::cio::output_stream stream, std::string_view text) -> void override; + + [[nodiscard]] auto log_buffer() noexcept -> kernel::tests::log_buffer &; + + private: + kernel::tests::log_buffer m_log_buffer{}; + }; + + auto deinit() -> void; + + [[nodiscard]] auto log_buffer() -> kernel::tests::log_buffer &; +} // namespace kernel::tests::cio + +#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 index 5445473..037b1b2 100644 --- a/kernel/include/kernel/test_support/cpu.hpp +++ b/kernel/include/kernel/test_support/cpu.hpp @@ -13,6 +13,8 @@ namespace kernel::tests::cpu {} }; + auto deinit() -> void; + } // 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 index 581b32f..41d9a76 100644 --- a/kernel/include/kernel/test_support/log_buffer.hpp +++ b/kernel/include/kernel/test_support/log_buffer.hpp @@ -4,29 +4,34 @@ #include #include -namespace kernel::tests::log_buffer +namespace kernel::tests { - //! 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 + struct log_buffer + { + //! Append a message to this buffer. + //! @param message The message to append. + auto append(std::string const & message) -> void; + + //! Clear this buffer. + auto clear() -> void; + + //! Get all messages in this buffer as a single string. + //! + //! @return All messages in this buffer as a single string. + auto flat_messages() -> std::string; + + //! Get all messages in this buffer. + //! + //! @note Messages may be split across multiple entries if they are longer than the internal kernel print buffer + //! size. + //! + //! @return All messages in this buffer. + auto messages() -> std::vector const &; + + private: + std::vector m_messages{}; + }; +} // namespace kernel::tests #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 index c6b7449..b475116 100644 --- a/kernel/include/kernel/test_support/memory.hpp +++ b/kernel/include/kernel/test_support/memory.hpp @@ -5,7 +5,9 @@ namespace kernel::tests::memory { - auto heap_base() -> kapi::memory::linear_address; -} + auto deinit() -> void; + + auto virtual_base() -> kapi::memory::linear_address; +} // namespace kernel::tests::memory #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 index a40aa2e..1658455 100644 --- a/kernel/include/kernel/test_support/page_mapper.hpp +++ b/kernel/include/kernel/test_support/page_mapper.hpp @@ -16,7 +16,7 @@ namespace kernel::tests struct page_mapper : kapi::memory::page_mapper { - explicit page_mapper(kstd::units::bytes memory_size); + page_mapper(kstd::units::bytes physical_size, kstd::units::bytes virtual_size); auto map(kapi::memory::page page, kapi::memory::frame frame, flags) -> std::byte * override; diff --git a/kernel/include/kernel/test_support/simulated_memory.hpp b/kernel/include/kernel/test_support/simulated_memory.hpp index 9a391d8..1619f31 100644 --- a/kernel/include/kernel/test_support/simulated_memory.hpp +++ b/kernel/include/kernel/test_support/simulated_memory.hpp @@ -6,27 +6,34 @@ #include #include +#include namespace kernel::tests { struct simulated_memory { - explicit simulated_memory(kstd::units::bytes size); + simulated_memory(kstd::units::bytes physical_size, kstd::units::bytes virtual_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; + [[nodiscard]] auto physical_base() noexcept -> std::byte *; + [[nodiscard]] auto physical_base() const noexcept -> std::byte const *; + [[nodiscard]] auto physical_size() const noexcept -> kstd::units::bytes; + + [[nodiscard]] auto virtual_base() const noexcept -> kapi::memory::linear_address; + [[nodiscard]] auto virtual_size() const noexcept -> kstd::units::bytes; + + [[nodiscard]] auto descriptor() const noexcept -> int; + + [[nodiscard]] auto map(kstd::units::bytes size, std::byte * to, off_t offset) -> std::byte *; private: - int m_memory_descriptor{}; - kstd::units::bytes m_size{0}; + int m_descriptor{}; + kstd::units::bytes m_physical_size{0}; + kstd::units::bytes m_virtual_size{0}; std::byte * m_physical_base{nullptr}; std::byte * m_virtual_base{nullptr}; }; diff --git a/kernel/kapi/system.tests.cpp b/kernel/kapi/system.tests.cpp index ee31c51..687053e 100644 --- a/kernel/kapi/system.tests.cpp +++ b/kernel/kapi/system.tests.cpp @@ -1,7 +1,7 @@ #include "kapi/system.hpp" +#include "kernel/test_support/cio.hpp" #include "kernel/test_support/cpu.hpp" -#include "kernel/test_support/log_buffer.hpp" #include @@ -13,7 +13,7 @@ SCENARIO("Kernel testing kapi::system shims", "[support]") { WHEN("a the system panics") { - kernel::tests::log_buffer::clear(); + kernel::tests::cio::log_buffer().clear(); THEN("the correct exception is thrown") { @@ -23,7 +23,7 @@ SCENARIO("Kernel testing kapi::system shims", "[support]") 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")); + REQUIRE(kernel::tests::cio::log_buffer().flat_messages().contains("[kernel:tests] Test Panic")); } } } diff --git a/kernel/kstd/print.tests.cpp b/kernel/kstd/print.tests.cpp index f8e600f..8deeb17 100644 --- a/kernel/kstd/print.tests.cpp +++ b/kernel/kstd/print.tests.cpp @@ -1,6 +1,6 @@ #include "kstd/print" -#include "kernel/test_support/log_buffer.hpp" +#include "kernel/test_support/cio.hpp" #include @@ -10,13 +10,13 @@ SCENARIO("Kernel testing kstd shims", "[support]") { WHEN("a regular print is issued") { - kernel::tests::log_buffer::clear(); + kernel::tests::cio::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")); + REQUIRE(kernel::tests::cio::log_buffer().flat_messages().contains("[kernel:tests] Test Print")); } } } diff --git a/kernel/src/memory/block_list_allocator.tests.cpp b/kernel/src/memory/block_list_allocator.tests.cpp index 8ca426d..0571441 100644 --- a/kernel/src/memory/block_list_allocator.tests.cpp +++ b/kernel/src/memory/block_list_allocator.tests.cpp @@ -12,22 +12,14 @@ 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(); + kernel::tests::memory::deinit(); kapi::memory::init(); - auto sandbox_base = kernel::tests::memory::heap_base(); + auto sandbox_base = kernel::tests::memory::virtual_base(); kernel::memory::block_list_allocator allocator{sandbox_base}; WHEN("a basic allocation request is made") diff --git a/kernel/src/test_support/kapi/cio.cpp b/kernel/src/test_support/kapi/cio.cpp index 35452d4..4a1cd1f 100644 --- a/kernel/src/test_support/kapi/cio.cpp +++ b/kernel/src/test_support/kapi/cio.cpp @@ -1,43 +1,54 @@ +#include "kernel/test_support/cio.hpp" + #include #include "kernel/test_support/log_buffer.hpp" -#include -#include -#include +#include +#include +#include + +namespace +{ + + auto constinit is_initialized = std::atomic_flag{}; + auto constinit device = std::optional{}; + +} // namespace namespace kapi::cio { - namespace + auto init() -> void { - - class test_output_device : public output_device + if (is_initialized.test_and_set()) { - 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 + throw std::logic_error("kapi::cio::init() called more than once"); + } - auto init() -> void + device.emplace(); + set_output_device(*device); + } + +} // namespace kapi::cio + +namespace kernel::tests::cio +{ + + auto deinit() -> void { - set_output_device(device); + if (!is_initialized.test()) + { + throw std::logic_error("kapi::cio::deinit() called before kapi::cio::init()"); + } + + device.reset(); + is_initialized.clear(); } - auto reset() -> void + auto log_buffer() -> kernel::tests::log_buffer & { - kernel::tests::log_buffer::clear(); + return device->log_buffer(); } -} // namespace kapi::cio \ No newline at end of file +} // namespace kernel::tests::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 6592d15..96dd10a 100644 --- a/kernel/src/test_support/kapi/cpu.cpp +++ b/kernel/src/test_support/kapi/cpu.cpp @@ -5,23 +5,13 @@ #include #include -namespace kapi::cpu +namespace { + auto static initialized = std::atomic_flag{}; +} - 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(); - } +namespace kapi::cpu +{ auto init() -> void { @@ -38,4 +28,19 @@ namespace kapi::cpu throw kernel::tests::cpu::halt{}; } -} // namespace kapi::cpu \ No newline at end of file +} // namespace kapi::cpu + +namespace kernel::tests::cpu +{ + + auto deinit() -> void + { + if (!initialized.test()) + { + throw std::logic_error{"kapi::cpu::reset() called before kapi::cpu::init()"}; + } + + initialized.clear(); + } + +} // namespace kernel::tests::cpu \ 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 f244b7f..e926ba6 100644 --- a/kernel/src/test_support/kapi/memory.cpp +++ b/kernel/src/test_support/kapi/memory.cpp @@ -12,8 +12,10 @@ 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; + constexpr auto physical_size = kstd::units::MiB(32); + constexpr auto virtual_size = kstd::units::GiB(1); + + constexpr auto number_of_frames = physical_size / kapi::memory::frame::size; auto constinit bump_allocator = std::optional{}; auto constinit test_mapper = std::optional{}; @@ -36,15 +38,19 @@ namespace kapi::memory auto init() -> void { bump_allocator.emplace(); - test_mapper.emplace(memory_size); + test_mapper.emplace(physical_size, virtual_size); old_allocator = set_frame_allocator(*bump_allocator); old_mapper = set_page_mapper(*test_mapper); - init_pmm(memory_size / frame::size, handoff_to_kernel_pmm); + init_pmm(physical_size / frame::size, handoff_to_kernel_pmm); } +} // namespace kapi::memory + +namespace kernel::tests::memory +{ - auto reset() -> void + auto deinit() -> void { if (old_allocator && *old_allocator) { @@ -60,12 +66,9 @@ namespace kapi::memory test_mapper.reset(); } -} // namespace kapi::memory - -namespace kernel::tests::memory -{ - auto heap_base() -> kapi::memory::linear_address + auto virtual_base() -> kapi::memory::linear_address { - return test_mapper->memory.heap_base(); + return test_mapper->memory.virtual_base(); } + } // namespace kernel::tests::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 index 36ed15e..9cff596 100644 --- a/kernel/src/test_support/log_buffer.cpp +++ b/kernel/src/test_support/log_buffer.cpp @@ -4,36 +4,30 @@ #include #include -namespace kernel::tests::log_buffer +namespace kernel::tests { - namespace + auto log_buffer::append(std::string const & message) -> void { - std::vector recorded_messages{}; + m_messages.push_back(message); } - auto append(std::string const & message) -> void + auto log_buffer::clear() -> void { - recorded_messages.push_back(message); + m_messages.clear(); } - auto clear() -> void + auto log_buffer::flat_messages() -> std::string { - recorded_messages.clear(); + return std::ranges::fold_left(m_messages, std::string{}, [](std::string accumulator, std::string const & message) { + accumulator += message; + return accumulator; + }); } - auto flat_messages() -> std::string + auto log_buffer::messages() -> std::vector const & { - return std::ranges::fold_left(recorded_messages, std::string{}, - [](std::string accumulator, std::string const & message) { - accumulator += message; - return accumulator; - }); + return m_messages; } - auto messages() -> std::vector const & - { - return recorded_messages; - } - -} // namespace kernel::tests::log_buffer +} // namespace kernel::tests diff --git a/kernel/src/test_support/output_device.cpp b/kernel/src/test_support/output_device.cpp new file mode 100644 index 0000000..83dcbcc --- /dev/null +++ b/kernel/src/test_support/output_device.cpp @@ -0,0 +1,28 @@ +#include "kapi/cio.hpp" + +#include "kernel/test_support/cio.hpp" +#include "kernel/test_support/log_buffer.hpp" + +#include +#include +#include + +namespace kernel::tests::cio +{ + + auto output_device::write(kapi::cio::output_stream stream, std::string_view text) -> void + { + auto & standard_stream = stream == kapi::cio::output_stream::stdout ? std::cout : std::cerr; + standard_stream << text; + if (text != "\n") + { + m_log_buffer.append(std::string{text}); + } + } + + auto output_device::log_buffer() noexcept -> kernel::tests::log_buffer & + { + return m_log_buffer; + } + +} // namespace kernel::tests::cio \ No newline at end of file diff --git a/kernel/src/test_support/page_mapper.cpp b/kernel/src/test_support/page_mapper.cpp index abdcae5..9236deb 100644 --- a/kernel/src/test_support/page_mapper.cpp +++ b/kernel/src/test_support/page_mapper.cpp @@ -7,13 +7,12 @@ #include #include #include -#include namespace kernel::tests { - page_mapper::page_mapper(kstd::units::bytes memory_size) - : memory{memory_size} + page_mapper::page_mapper(kstd::units::bytes physical_size, kstd::units::bytes virtual_size) + : memory{physical_size, virtual_size} {} auto page_mapper::map(kapi::memory::page page, kapi::memory::frame frame, flags) -> std::byte * @@ -26,21 +25,14 @@ namespace kernel::tests } auto page_address = page.start_address(); - auto sandbox_start = memory.heap_base(); - auto sandbox_end = sandbox_start + memory.heap_size(); + auto virtual_base = memory.virtual_base(); + auto virtual_end = virtual_base + memory.virtual_size(); - if (page_address >= sandbox_start && page_address < sandbox_end) + if (page_address >= virtual_base && page_address < virtual_end) { 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); + return memory.map(kapi::memory::page::size, virtual_target, physical_offset.value); } else if (page_address >= kapi::memory::mmio_base) { @@ -49,7 +41,7 @@ namespace kernel::tests 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 memory.physical_base() + offset; } return nullptr; diff --git a/kernel/src/test_support/simulated_memory.cpp b/kernel/src/test_support/simulated_memory.cpp index fa3d36c..0648df2 100644 --- a/kernel/src/test_support/simulated_memory.cpp +++ b/kernel/src/test_support/simulated_memory.cpp @@ -4,50 +4,50 @@ #include +#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_descriptor{memfd_create("teachos_simulated_memory", 0)} - , m_size{size} + simulated_memory::simulated_memory(kstd::units::bytes physical_size, kstd::units::bytes virtual_size) + : m_descriptor{memfd_create("teachos_simulated_memory", 0)} + , m_physical_size{physical_size} + , m_virtual_size{virtual_size} { - if (m_memory_descriptor < 0) + if (m_descriptor < 0) { - throw std::runtime_error("Failed to create simulated memory"); + auto error = std::format("Failed to allocate backing memory: {}", strerror(errno)); + throw std::runtime_error(error); } - if (ftruncate(m_memory_descriptor, static_cast(m_size.value)) < 0) + if (ftruncate(m_descriptor, static_cast(m_physical_size.value)) < 0) { - throw std::runtime_error("Failed to resize simulated memory"); + auto error = std::format("Failed to reserve backing memory: {}", strerror(errno)); + throw std::runtime_error(error); } - auto mapped_pointer = mmap(nullptr, m_size.value, PROT_READ | PROT_WRITE, MAP_SHARED, m_memory_descriptor, 0); - if (mapped_pointer == MAP_FAILED) + auto physical_storage = mmap(nullptr, m_physical_size.value, PROT_READ | PROT_WRITE, MAP_SHARED, m_descriptor, 0); + if (physical_storage == MAP_FAILED) { - throw std::runtime_error("Failed to map simulated memory"); + auto error = std::format("Failed to map backing memory: {}", strerror(errno)); + throw std::runtime_error(error); } - 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"); + auto error = std::format("Failed to reserve virtual memory: {}", strerror(errno)); + throw std::runtime_error(error); } + m_physical_base = static_cast(physical_storage); m_virtual_base = static_cast(virtual_pointer); clear(); @@ -55,39 +55,56 @@ namespace kernel::tests simulated_memory::~simulated_memory() { - munmap(m_physical_base, m_size.value); - munmap(m_virtual_base, virtual_size.value); - close(m_memory_descriptor); + munmap(m_virtual_base, m_virtual_size.value); + munmap(m_physical_base, m_physical_size.value); + close(m_descriptor); } auto simulated_memory::clear() -> void { - std::memset(m_physical_base, 0, m_size.value); + std::memset(m_physical_base, 0, m_physical_size.value); } - auto simulated_memory::ram_base() noexcept -> std::byte * + auto simulated_memory::physical_base() noexcept -> std::byte * { return m_physical_base; } - auto simulated_memory::ram_base() const noexcept -> std::byte const * + auto simulated_memory::physical_base() const noexcept -> std::byte const * { return m_physical_base; } - auto simulated_memory::heap_base() const noexcept -> kapi::memory::linear_address + auto simulated_memory::physical_size() const noexcept -> kstd::units::bytes + { + return m_physical_size; + } + + auto simulated_memory::virtual_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 + auto simulated_memory::virtual_size() const noexcept -> kstd::units::bytes { - return virtual_size; + return m_virtual_size; } - auto simulated_memory::memory_descriptor() const noexcept -> int + auto simulated_memory::descriptor() const noexcept -> int { - return m_memory_descriptor; + return m_descriptor; + } + + auto simulated_memory::map(kstd::units::bytes size, std::byte * to, off_t offset) -> std::byte * + { + auto mapped_ptr = mmap(to, size.value, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, m_descriptor, offset); + if (mapped_ptr == MAP_FAILED) + { + auto error = std::format("Failed to map page: {}", strerror(errno)); + throw std::runtime_error(error); + } + + return static_cast(mapped_ptr); } } // namespace kernel::tests \ No newline at end of file diff --git a/kernel/src/test_support/state_reset_listener.cpp b/kernel/src/test_support/state_reset_listener.cpp index e201a10..8c95cc8 100644 --- a/kernel/src/test_support/state_reset_listener.cpp +++ b/kernel/src/test_support/state_reset_listener.cpp @@ -2,29 +2,15 @@ #include "kapi/cpu.hpp" #include "kapi/memory.hpp" +#include "kernel/test_support/cio.hpp" +#include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/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; @@ -38,9 +24,9 @@ struct state_reset_listener : Catch::EventListenerBase void testCaseEnded(Catch::TestCaseStats const &) override { - kapi::memory::reset(); - kapi::cpu::reset(); - kapi::cio::reset(); + kernel::tests::memory::deinit(); + kernel::tests::cpu::deinit(); + kernel::tests::cio::deinit(); } }; -- cgit v1.2.3 From 39d2696ee75db377c7e31fda1e29e4a7d3cfb378 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 10:15:18 +0200 Subject: kernel/tests: improve documentation --- .../kernel/test_support/bump_frame_allocator.hpp | 24 ++++++++++++++++++- kernel/include/kernel/test_support/cio.hpp | 14 +++++++++-- kernel/include/kernel/test_support/cpu.hpp | 3 +++ kernel/include/kernel/test_support/memory.hpp | 9 ++++++++ kernel/include/kernel/test_support/page_mapper.hpp | 16 +++++++++++++ .../kernel/test_support/simulated_memory.hpp | 27 ++++++++++++++++++++-- kernel/src/test_support/simulated_memory.cpp | 5 ---- 7 files changed, 88 insertions(+), 10 deletions(-) diff --git a/kernel/include/kernel/test_support/bump_frame_allocator.hpp b/kernel/include/kernel/test_support/bump_frame_allocator.hpp index 5203565..6380294 100644 --- a/kernel/include/kernel/test_support/bump_frame_allocator.hpp +++ b/kernel/include/kernel/test_support/bump_frame_allocator.hpp @@ -10,10 +10,28 @@ namespace kernel::tests { + //! A simple bump-based frame allocator used for initial test bootstrap. + //! + //! We emulate the expected behavior of a platform, in that there is a handover to the kernel PMM that needs to happen + //! during boot. The expectation of the PMM initializer is that there is an active (if simple) frame allocator it can + //! use to reserve space for it's own metadata. + //! + //! @see kapi::memory::init_pmm struct bump_frame_allocator : kapi::memory::frame_allocator { - auto mark_used(kapi::memory::frame) -> void override {} + //! @copydoc kapi::memory::frame_allocator::mark_used + //! + //! @note Due to the simple nature of this allocator, all frames up to the given frame will be marked as used as + //! well. + auto mark_used(kapi::memory::frame frame) -> void override + { + if (frame.number() >= next_free_frame) + { + next_free_frame = frame.number() + 1; + } + } + //! @copydoc kapi::memory::frame_allocator::allocate_many auto allocate_many(std::size_t count) noexcept -> std::optional> override { @@ -22,8 +40,12 @@ namespace kernel::tests return std::pair{kapi::memory::frame{start}, count}; } + //! @copydoc kapi::memory::frame_allocator::release_many + //! + //! @note Due to the simple nature of this allocator, frames are never actually released. auto release_many(std::pair) -> void override {} + //! The next free frame to be allocated. std::size_t next_free_frame{}; }; diff --git a/kernel/include/kernel/test_support/cio.hpp b/kernel/include/kernel/test_support/cio.hpp index 4b897b5..f7bac2d 100644 --- a/kernel/include/kernel/test_support/cio.hpp +++ b/kernel/include/kernel/test_support/cio.hpp @@ -9,20 +9,30 @@ namespace kernel::tests::cio { + + //! A simple output device that writes to one of the standard output streams and a log buffer. struct output_device : kapi::cio::output_device { - output_device() = default; - + //! @copydoc kapi::cio::output_device::write auto write(kapi::cio::output_stream stream, std::string_view text) -> void override; + //! Get the log buffer associated with this device. + //! + //! @return The associated log buffer. [[nodiscard]] auto log_buffer() noexcept -> kernel::tests::log_buffer &; private: + //! The log buffer of this device. kernel::tests::log_buffer m_log_buffer{}; }; + //! Deinitialize the CIO subsystem. auto deinit() -> void; + //! Get the log buffer of the currently active output device. + //! + //! @throws std::runtime_error if no output device has been registered. + //! @return The currently active device's log buffer. [[nodiscard]] auto log_buffer() -> kernel::tests::log_buffer &; } // namespace kernel::tests::cio diff --git a/kernel/include/kernel/test_support/cpu.hpp b/kernel/include/kernel/test_support/cpu.hpp index 037b1b2..c99d1a7 100644 --- a/kernel/include/kernel/test_support/cpu.hpp +++ b/kernel/include/kernel/test_support/cpu.hpp @@ -6,13 +6,16 @@ namespace kernel::tests::cpu { + //! Exception thrown when the CPU is halted. struct halt : std::runtime_error { + //! Construct a new halt exception. halt() : std::runtime_error{"CPU halt requested!"} {} }; + //! Deinitialize the CPU subsystem. auto deinit() -> void; } // namespace kernel::tests::cpu diff --git a/kernel/include/kernel/test_support/memory.hpp b/kernel/include/kernel/test_support/memory.hpp index b475116..6034a1e 100644 --- a/kernel/include/kernel/test_support/memory.hpp +++ b/kernel/include/kernel/test_support/memory.hpp @@ -5,9 +5,18 @@ namespace kernel::tests::memory { + + //! Deinitialize the memory subsystem. auto deinit() -> void; + //! Get the virtual base address of the simulated system. + //! + //! Since we do not have direct access to an actual MMU, we simulate the virtual address space by mapping the + //! physical address space starting at some process local virtual address. + //! + //! @return The virtual base address of the simulated system. auto virtual_base() -> kapi::memory::linear_address; + } // namespace kernel::tests::memory #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 index 1658455..7ae9a4f 100644 --- a/kernel/include/kernel/test_support/page_mapper.hpp +++ b/kernel/include/kernel/test_support/page_mapper.hpp @@ -16,15 +16,31 @@ namespace kernel::tests struct page_mapper : kapi::memory::page_mapper { + //! Construct a new page mapper. + //! + //! @param physical_size The size of the physical memory. + //! @param virtual_size The size of the virtual address space. page_mapper(kstd::units::bytes physical_size, kstd::units::bytes virtual_size); + //! @copydoc kapi::memory::page_mapper::map + //! + //! @throws std::invalid_argument if the page has already been mapped. + //! @throws std::runtime_error if the page cannot be mapped. + //! @throws std::runtime_error if the underlying simulated memory cannot map the page. auto map(kapi::memory::page page, kapi::memory::frame frame, flags) -> std::byte * override; + //! @copydoc kapi::memory::page_mapper::unmap + //! + //! @throws std::invalid_argument if the page has not been mapped. auto unmap(kapi::memory::page page) -> void override; + //! @copydoc kapi::memory::page_mapper::try_unmap auto try_unmap(kapi::memory::page page) noexcept -> bool override; + //! The simulated memory. kernel::tests::simulated_memory memory; + + //! The simplified page table entries. std::unordered_map page_mappings; }; diff --git a/kernel/include/kernel/test_support/simulated_memory.hpp b/kernel/include/kernel/test_support/simulated_memory.hpp index 1619f31..fa78aed 100644 --- a/kernel/include/kernel/test_support/simulated_memory.hpp +++ b/kernel/include/kernel/test_support/simulated_memory.hpp @@ -11,23 +11,46 @@ namespace kernel::tests { + //! A simulated memory device that can be used in tests. + //! + //! The general idea is to provide a means to simulate the physical and virtual address spaces of the system. This + //! implementation provides for a single physical and a single virtual address space. + //! + //! @note This is not a full-featured MMU. It is a simple simulation that can be used for testing only. struct simulated_memory { + //! Construct a new simulated memory device. + //! @param physical_size The size of the physical memory. + //! @param virtual_size The size of the virtual address space. simulated_memory(kstd::units::bytes physical_size, kstd::units::bytes virtual_size); + //! Destroy this device ~simulated_memory(); + //! Clear the contents of the physical memory of this device. auto clear() -> void; + //! Get the base address of the physical memory of this device. [[nodiscard]] auto physical_base() noexcept -> std::byte *; + + //! Get the base address of the physical memory of this device. [[nodiscard]] auto physical_base() const noexcept -> std::byte const *; + + //! Get the size of the physical memory of this device. [[nodiscard]] auto physical_size() const noexcept -> kstd::units::bytes; + //! Get the base address of the virtual address space of this device. [[nodiscard]] auto virtual_base() const noexcept -> kapi::memory::linear_address; - [[nodiscard]] auto virtual_size() const noexcept -> kstd::units::bytes; - [[nodiscard]] auto descriptor() const noexcept -> int; + //! Get the size of the virtual address space of this device. + [[nodiscard]] auto virtual_size() const noexcept -> kstd::units::bytes; + //! Map a region of physical memory to a region of virtual memory. + //! + //! @param size The size of the region to map. + //! @param to The base address of the virtual region. + //! @param offset The offset into the physical memory to map. + //! @return A pointer to the first byte of the mapped region. [[nodiscard]] auto map(kstd::units::bytes size, std::byte * to, off_t offset) -> std::byte *; private: diff --git a/kernel/src/test_support/simulated_memory.cpp b/kernel/src/test_support/simulated_memory.cpp index 0648df2..03dac1d 100644 --- a/kernel/src/test_support/simulated_memory.cpp +++ b/kernel/src/test_support/simulated_memory.cpp @@ -90,11 +90,6 @@ namespace kernel::tests return m_virtual_size; } - auto simulated_memory::descriptor() const noexcept -> int - { - return m_descriptor; - } - auto simulated_memory::map(kstd::units::bytes size, std::byte * to, off_t offset) -> std::byte * { auto mapped_ptr = mmap(to, size.value, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, m_descriptor, offset); -- cgit v1.2.3 From 4a783fa81815dd40764b3e96e34e550b15a08481 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 10:16:47 +0200 Subject: chore: remove valgrind core file --- vgcore.1625255 | Bin 13705216 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 vgcore.1625255 diff --git a/vgcore.1625255 b/vgcore.1625255 deleted file mode 100644 index de22445..0000000 Binary files a/vgcore.1625255 and /dev/null differ -- cgit v1.2.3 From 68b673fe0800067dafa7a47469eb89617bfd1b17 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 11:06:12 +0200 Subject: ci: split build and test --- .gitlab-ci.yml | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 661ce63..139eb0a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,10 +1,15 @@ -.build_matrix: &build_matrix - parallel: - matrix: - - PLATFORM: ["x86_64"] - TYPE: ["dbg", "rel"] +build:bht: + stage: build + image: registry.gitlab.ost.ch:45023/teachos/devcontainers/bht.ci:latest + script: + - cmake --preset bht + - cmake --build --preset bht-dbg + artifacts: + paths: + - build/bht/ + expire_in: 5 min -build: +build:bootable: stage: build image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64.ci:latest script: @@ -18,14 +23,17 @@ build: - kernel.sym - kernel.iso expire_in: 1 week - <<: *build_matrix -bht: - stage: build + parallel: + matrix: + - PLATFORM: ["x86_64"] + TYPE: ["dbg", "rel"] + +test:bht: + stage: test image: registry.gitlab.ost.ch:45023/teachos/devcontainers/bht.ci:latest + needs: ["build:bht"] script: - - cmake --preset bht - - cmake --build --preset bht-dbg - ctest --preset bht-dbg - lcov --config-file .lcovrc --capture --directory $(pwd) --output-file coverage.info - lcov --list coverage.info @@ -36,10 +44,8 @@ bht: coverage: '/Total:\|\s*(\d+(?:\.\d+)?)\%/' artifacts: paths: - - coverage.info - coverage/ expire_in: 24 hours - when: always reports: coverage_report: coverage_format: cobertura -- cgit v1.2.3 From 1f010078983e6ab4e74ee3d1efcfb3284620b002 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 11:17:21 +0200 Subject: ci: reduce coverage verbosity --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 139eb0a..26e474e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,9 +35,9 @@ test:bht: needs: ["build:bht"] script: - ctest --preset bht-dbg - - lcov --config-file .lcovrc --capture --directory $(pwd) --output-file coverage.info - - lcov --list coverage.info - - genhtml --prefix $(pwd) --output-directory coverage coverage.info + - lcov --quiet --config-file .lcovrc --capture --directory $(pwd) --output-file coverage.info + - lcov --quiet --list coverage.info + - genhtml --quiet --prefix $(pwd) --output-directory coverage coverage.info - gcovr --root . --cobertura-pretty --output coverage/cobertura-coverage.xml after_script: - echo "CoverageReport public URL - https://teachos.pages.ost.ch/-/kernel/-/jobs/$CI_JOB_ID/artifacts/coverage/index.html" -- cgit v1.2.3 From e7abfb7bf87ac605b2168891973d7e04a84d627e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 31 Mar 2026 21:56:37 +0200 Subject: kapi/devices: introduce basic bus abstraction --- kapi/include/kapi/devices/bus.hpp | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 kapi/include/kapi/devices/bus.hpp diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp new file mode 100644 index 0000000..cf8d090 --- /dev/null +++ b/kapi/include/kapi/devices/bus.hpp @@ -0,0 +1,40 @@ +#ifndef TEACHOS_KAPI_DEVICES_BUS_HPP +#define TEACHOS_KAPI_DEVICES_BUS_HPP + +#include "kapi/devices/device.hpp" + +#include +#include +#include + +#include + +namespace kapi::devices +{ + //! A bus device that represents a logical/physical tree of devices and busses. + struct bus : device + { + //! Construct a bus with the given major number, minor number, and name. + //! + //! @param major The major number of the bus. + //! @param minor The minor number of the bus. + //! @param name The name of the bus. + bus(std::size_t major, std::size_t minor, kstd::string const & name) + : device(major, minor, name) + {} + + //! Attach a child device to this bus. + //! + //! Whenever a device is attached to a bus, the bus takes sole ownership of the device. + //! + //! @param child The child device to attach. + virtual auto add_child(kstd::unique_ptr child) -> void = 0; + + //! Get a list of all child devices attached to this bus. + //! + //! @return A reference to list of child devices of this bus. + [[nodiscard]] virtual auto children() const -> kstd::vector const & = 0; + }; +} // namespace kapi::devices + +#endif \ No newline at end of file -- cgit v1.2.3 From 77473afe9d5acb9450443b07b56d3dbc2f0639a6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 31 Mar 2026 22:22:25 +0200 Subject: kstd: introduce observer_ptr --- kapi/include/kapi/devices/bus.hpp | 3 +- libs/kstd/include/kstd/observer_ptr | 154 ++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 libs/kstd/include/kstd/observer_ptr diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index cf8d090..0b25ac1 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -4,6 +4,7 @@ #include "kapi/devices/device.hpp" #include +#include #include #include @@ -33,7 +34,7 @@ namespace kapi::devices //! Get a list of all child devices attached to this bus. //! //! @return A reference to list of child devices of this bus. - [[nodiscard]] virtual auto children() const -> kstd::vector const & = 0; + [[nodiscard]] virtual auto children() const -> kstd::vector> const & = 0; }; } // namespace kapi::devices diff --git a/libs/kstd/include/kstd/observer_ptr b/libs/kstd/include/kstd/observer_ptr new file mode 100644 index 0000000..d3d24b4 --- /dev/null +++ b/libs/kstd/include/kstd/observer_ptr @@ -0,0 +1,154 @@ +#ifndef KSTD_OBSERVER_PTR_HPP +#define KSTD_OBSERVER_PTR_HPP + +#include "kstd/os/error.hpp" + +#include +#include +#include +#include + +namespace kstd +{ + + template + struct observer_ptr + { + //! The type of the element being pointed to. + using element_type = ElementType; + + //! Construct an empty observer pointer. + constexpr observer_ptr() noexcept = default; + + //! Construct an empty observer pointer from a null pointer. + constexpr observer_ptr(std::nullptr_t) noexcept {} + + //! Construct an observer pointer from a raw pointer. + constexpr explicit observer_ptr(element_type * pointer) + : m_ptr{pointer} + {} + + //! Construct an observer pointer from another observer pointer. + template + requires std::convertible_to + constexpr observer_ptr(observer_ptr other) noexcept + : m_ptr{other.get()} + {} + + //! Copy construct an observer pointer. + constexpr observer_ptr(observer_ptr const & other) noexcept = default; + + //! Move construct an observer pointer. + constexpr observer_ptr(observer_ptr && other) noexcept = default; + + //! Stop watching the the watched object. + //! + //! @return The currently watched object, or nullptr if no object is being watched. + [[nodiscard]] constexpr auto release() noexcept -> element_type * + { + return std::exchange(m_ptr, nullptr); + } + + //! Reset the observer pointer. + //! + //! @param pointer The new object to watch. + constexpr auto reset(element_type * pointer) noexcept -> void + { + m_ptr = pointer; + } + + //! Swap the observer pointer with another observer pointer. + //! + //! @param other The other observer pointer to swap with. + constexpr auto swap(observer_ptr & other) noexcept -> void + { + std::swap(m_ptr, other.m_ptr); + } + + //! Get the currently watched object. + //! + //! @return The currently watched object, or nullptr if no object is being watched. + [[nodiscard]] constexpr auto get() const noexcept -> element_type * + { + return m_ptr; + } + + //! Check if the observer pointer is watching an object. + //! + //! @return True if the observer pointer is watching an object, false otherwise. + [[nodiscard]] constexpr explicit operator bool() const noexcept + { + return m_ptr != nullptr; + } + + //! Get the currently watched object. + //! + //! @return A reference to the currently watched object. + [[nodiscard]] constexpr auto operator*() const noexcept -> element_type & + { + throw_on_null(); + return *m_ptr; + } + + //! Get the currently watched object. + //! + //! @return A pointer to the currently watched object. + [[nodiscard]] constexpr auto operator->() const noexcept -> element_type * + { + throw_on_null(); + return m_ptr; + } + + //! Convert the observer pointer to a raw pointer. + //! + //! @return A pointer to the currently watched object. + constexpr explicit operator element_type *() const noexcept + { + return m_ptr; + } + + //! Compare the observer pointer with another observer pointer. + //! + //! @param other The other observer pointer to compare with. + //! @return The result of the comparison. + constexpr auto operator<=>(observer_ptr const & other) const noexcept -> std::strong_ordering = default; + + private: + //! Throw an exception if the observer pointer is null. + //! + //! @throws std::runtime_error if the observer pointer is null. + constexpr auto throw_on_null() const noexcept -> void + { + if (m_ptr == nullptr) + { + os::panic("[kstd:observer_ptr] Dereferencing a null observer pointer"); + } + } + + //! The raw pointer to the watched object. + ElementType * m_ptr; + }; + + //! Swap two observer pointers. + //! + //! @param lhs The first observer pointer to swap. + //! @param rhs The second observer pointer to swap. + template + constexpr auto swap(observer_ptr & lhs, observer_ptr & rhs) noexcept -> void + { + lhs.swap(rhs); + } + + //! Create an observer pointer from a raw pointer. + //! + //! @param pointer The raw pointer to create an observer pointer from. + //! @return An observer pointer to the given raw pointer. + template + constexpr auto make_observer(ElementType * pointer) noexcept -> observer_ptr + { + return observer_ptr{pointer}; + } + +} // namespace kstd + +#endif \ No newline at end of file -- cgit v1.2.3 From 15b882d0416bb83a18e5437480c08419b2035e1f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 31 Mar 2026 22:52:52 +0200 Subject: kstd: add some basic observer_ptr tests --- libs/kstd/CMakeLists.txt | 1 + libs/kstd/include/kstd/observer_ptr | 7 +- libs/kstd/tests/src/observer_ptr.cpp | 234 +++++++++++++++++++++++++++++++++++ 3 files changed, 239 insertions(+), 3 deletions(-) create mode 100644 libs/kstd/tests/src/observer_ptr.cpp diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index ec0f441..240118e 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -44,6 +44,7 @@ else() add_executable("kstd_tests" "tests/src/flat_map.cpp" "tests/src/vector.cpp" + "tests/src/observer_ptr.cpp" "tests/src/os_panic.cpp" "tests/src/string.cpp" ) diff --git a/libs/kstd/include/kstd/observer_ptr b/libs/kstd/include/kstd/observer_ptr index d3d24b4..97c9e5e 100644 --- a/libs/kstd/include/kstd/observer_ptr +++ b/libs/kstd/include/kstd/observer_ptr @@ -6,6 +6,7 @@ #include #include #include +#include #include namespace kstd @@ -52,7 +53,7 @@ namespace kstd //! Reset the observer pointer. //! //! @param pointer The new object to watch. - constexpr auto reset(element_type * pointer) noexcept -> void + constexpr auto reset(element_type * pointer = nullptr) noexcept -> void { m_ptr = pointer; } @@ -84,7 +85,7 @@ namespace kstd //! Get the currently watched object. //! //! @return A reference to the currently watched object. - [[nodiscard]] constexpr auto operator*() const noexcept -> element_type & + [[nodiscard]] constexpr auto operator*() const noexcept -> std::add_lvalue_reference_t { throw_on_null(); return *m_ptr; @@ -126,7 +127,7 @@ namespace kstd } //! The raw pointer to the watched object. - ElementType * m_ptr; + ElementType * m_ptr{}; }; //! Swap two observer pointers. diff --git a/libs/kstd/tests/src/observer_ptr.cpp b/libs/kstd/tests/src/observer_ptr.cpp new file mode 100644 index 0000000..2fe2226 --- /dev/null +++ b/libs/kstd/tests/src/observer_ptr.cpp @@ -0,0 +1,234 @@ +#include +#include + +#include + +#include + +SCENARIO("Observer Pointer initialization and construction", "[observer_ptr]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto ptr = kstd::observer_ptr{}; + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + } + + WHEN("constructing from a nullptr") + { + auto ptr = kstd::observer_ptr{nullptr}; + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + } + + WHEN("constructing from a raw pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + + THEN("the observer pointer is not null") + { + REQUIRE(ptr); + } + + THEN("the observer pointer points to the correct object") + { + REQUIRE(&*ptr == &value); + } + } + + WHEN("copy constructing from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = ptr; + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("copy constructing from an existing observer pointer with a compatible type") + { + struct A + { + }; + + struct B : A + { + }; + + auto value = B{}; + auto ptr = kstd::observer_ptr(&value); + auto copy = kstd::observer_ptr(ptr); + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("copy assigning from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = ptr; + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("move constructing from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = std::move(ptr); + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("move assigning from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = std::move(ptr); + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + } +} + +SCENARIO("Observer pointer modifiers", "[observer_ptr]") +{ + GIVEN("A non-null observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + + WHEN("releasing the observer pointer") + { + auto raw_ptr = ptr.release(); + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + + THEN("the returned pointer points to the correct object") + { + REQUIRE(raw_ptr == &value); + } + } + + WHEN("resetting the observer pointer to nullptr") + { + ptr.reset(); + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + } + + WHEN("resetting the observer pointer to a new object") + { + auto other_value = 2; + ptr.reset(&other_value); + + THEN("the observer pointer points to the new object") + { + REQUIRE(&*ptr == &other_value); + } + } + + WHEN("swapping it with another observer pointer") + { + auto other_value = 2; + auto other_ptr = kstd::observer_ptr{&other_value}; + ptr.swap(other_ptr); + + THEN("the observer pointer points to the other object") + { + REQUIRE(&*ptr == &other_value); + } + + THEN("the other observer pointer points to the original object") + { + REQUIRE(&*other_ptr == &value); + } + } + } +} + +SCENARIO("Observer pointer observers", "[observer_ptr]") +{ + GIVEN("A non-null observer pointer") + { + struct A + { + int value{}; + + constexpr auto operator<=>(A const & other) const noexcept = default; + }; + + auto value = A{1}; + auto ptr = kstd::observer_ptr{&value}; + + WHEN("getting the raw pointer") + { + auto raw_ptr = ptr.get(); + + THEN("the raw pointer points to the correct object") + { + REQUIRE(raw_ptr == &value); + } + } + + WHEN("dereferencing the observer pointer") + { + auto dereferenced = *ptr; + + THEN("the dereferenced value is the correct value") + { + REQUIRE(dereferenced == value); + } + } + + WHEN("writing through the observer pointer with the arrow operator") + { + ptr->value = 2; + + THEN("the value is updated") + { + REQUIRE(value.value == 2); + } + } + + WHEN("converting the observer pointer to a raw pointer") + { + auto raw_ptr = static_cast(ptr); + + THEN("the raw pointer points to the correct object") + { + REQUIRE(raw_ptr == &value); + } + } + } +} \ No newline at end of file -- cgit v1.2.3 From 5f084e49a27e73fdf9ca88f46121618d9fae399f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 06:53:41 +0200 Subject: kstd/tests: extend operation tracker --- libs/kstd/tests/include/kstd/tests/test_types.hpp | 71 ++++++++++--- libs/kstd/tests/src/vector.cpp | 119 +++++++++++++--------- 2 files changed, 128 insertions(+), 62 deletions(-) diff --git a/libs/kstd/tests/include/kstd/tests/test_types.hpp b/libs/kstd/tests/include/kstd/tests/test_types.hpp index 6a06311..9207ee9 100644 --- a/libs/kstd/tests/include/kstd/tests/test_types.hpp +++ b/libs/kstd/tests/include/kstd/tests/test_types.hpp @@ -11,44 +11,43 @@ namespace kstd::tests //! A type tracking copy and move operations //! //! This type is designed to test move and copy semantics of standard library containers implemented in kstd. - struct move_tracker + struct special_member_tracker { //! A value indicating that the object was moved from. constexpr auto static moved_from_v = -1; - //! A simple value to be able to track the move-from state. - int value{}; - //! A flag to track if an instance of this type was either copy constructed or copy assigned. - bool was_copied{false}; - //! A flag to track if an instance of this type was either move constructed or move assigned. - bool was_moved{false}; + constexpr special_member_tracker() + : default_constructed_count{1} + {} //! Construct a new move tracker with the given value, if any. - constexpr move_tracker(int v = 0) + constexpr special_member_tracker(int v = 0) : value{v} + , value_constructed_count{1} {} //! Construct a new move tracker by copying an existing one. - constexpr move_tracker(move_tracker const & other) + constexpr special_member_tracker(special_member_tracker const & other) : value{other.value} - , was_copied{true} + , copy_constructed_count{1} {} //! Construct a new move tracker by moving from an existing one. - constexpr move_tracker(move_tracker && other) noexcept + constexpr special_member_tracker(special_member_tracker && other) noexcept : value{other.value} - , was_moved{true} + , move_constructed_count{1} { other.value = moved_from_v; } //! Copy assign a new move tracker from an existing one. - constexpr auto operator=(move_tracker const & other) -> move_tracker & + constexpr auto operator=(special_member_tracker const & other) -> special_member_tracker & { if (this != &other) { value = other.value; - was_copied = true; + ++copy_assigned_count; + ++other.copied_from_count; } return *this; } @@ -56,16 +55,56 @@ namespace kstd::tests //! Move assign a new move tracker from an existing one. //! //! This function ensures that the moved-from state is marked. - constexpr auto operator=(move_tracker && other) noexcept -> move_tracker & + constexpr auto operator=(special_member_tracker && other) noexcept -> special_member_tracker & { if (this != &other) { value = other.value; - was_moved = true; + ++move_assigned_count; other.value = moved_from_v; + ++other.moved_from_count; } return *this; } + + ~special_member_tracker() + { + ++destroyed_count; + } + + auto reset_counts() -> void + { + default_constructed_count = 0; + copy_constructed_count = 0; + move_constructed_count = 0; + value_constructed_count = 0; + copy_assigned_count = 0; + move_assigned_count = 0; + destroyed_count = 0; + copied_from_count = 0; + moved_from_count = 0; + } + + //! A simple value to be able to track the move-from state. + int value{}; + //! A counter to track how many times an instance of this type was default constructed. + std::size_t default_constructed_count{0}; + //! A counter to track how many times an instance of this type was copy constructed. + std::size_t copy_constructed_count{0}; + //! A counter to track how many times an instance of this type was move constructed. + std::size_t move_constructed_count{0}; + //! A counter to track how many times an instance of this type was value constructed. + std::size_t value_constructed_count{0}; + //! A counter to track how many times an instance of this type was copy assigned. + std::size_t copy_assigned_count{0}; + //! A counter to track how many times an instance of this type was move assigned. + std::size_t move_assigned_count{0}; + //! A counter to track how many times an instance of this type was destroyed. + std::size_t destroyed_count{0}; + //! A counter to track how many times an instance of this type was copied from another instance. + mutable std::size_t copied_from_count{0}; + //! A counter to track how many times an instance of this type was moved from another instance. + std::size_t moved_from_count{0}; }; //! A type that is not default constructible. diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index b7971f4..81bf32f 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -919,8 +919,8 @@ SCENARIO("Vector modifier move semantics", "[vector]") { GIVEN("An empty vector and a move tracker element") { - auto v = kstd::vector{}; - auto tracker = kstd::tests::move_tracker{42}; + auto v = kstd::vector{}; + auto tracker = kstd::tests::special_member_tracker{42}; WHEN("push_back is called with the move tracker") { @@ -929,8 +929,8 @@ SCENARIO("Vector modifier move semantics", "[vector]") THEN("the element is added and the size and capacity increase") { REQUIRE(v.size() == 1); - REQUIRE(v.back().was_moved); - REQUIRE_FALSE(v.back().was_copied); + REQUIRE(v.back().move_constructed_count == 1); + REQUIRE(v.back().copy_constructed_count == 0); REQUIRE(v.back().value == 42); } @@ -943,7 +943,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") GIVEN("An empty vector") { - auto v = kstd::vector{}; + auto v = kstd::vector{}; WHEN("emplace_back is called with constructor arguments") { @@ -952,8 +952,8 @@ SCENARIO("Vector modifier move semantics", "[vector]") THEN("the element is constructed directly, without moves or copies") { REQUIRE(v.size() == 1); - REQUIRE_FALSE(v.back().was_moved); - REQUIRE_FALSE(v.back().was_copied); + REQUIRE(v.back().move_constructed_count == 0); + REQUIRE(v.back().copy_constructed_count == 0); REQUIRE(v.back().value == 42); } } @@ -961,7 +961,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") GIVEN("A populated vector of move trackers") { - auto v = kstd::vector{}; + auto v = kstd::vector{}; v.reserve(10); v.emplace_back(10); v.emplace_back(20); @@ -969,41 +969,46 @@ SCENARIO("Vector modifier move semantics", "[vector]") WHEN("inserting an element in the middle with sufficient capacity") { - auto const tracker = kstd::tests::move_tracker{15}; + auto const tracker = kstd::tests::special_member_tracker{15}; v.insert(v.cbegin() + 1, tracker); THEN("the shifted elements are move-assigned and the new element is copy-assigned") { REQUIRE(v.size() == 4); REQUIRE(v[0].value == 10); - REQUIRE_FALSE(v[0].was_moved); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); REQUIRE(v[1].value == 15); - REQUIRE(v[1].was_copied); - REQUIRE_FALSE(v[1].was_moved); + REQUIRE(v[1].move_assigned_count == 0); + REQUIRE(v[1].copy_assigned_count == 1); + REQUIRE(tracker.copied_from_count == 1); REQUIRE(v[2].value == 20); - REQUIRE(v[2].was_moved); + REQUIRE(v[2].move_assigned_count == 1); + REQUIRE(v[2].copy_assigned_count == 0); REQUIRE(v[3].value == 30); - REQUIRE(v[3].was_moved); + REQUIRE(v[3].move_constructed_count == 1); } } WHEN("inserting an rvalue element in the middle with sufficient capacity") { - auto tracker = kstd::tests::move_tracker{15}; + auto tracker = kstd::tests::special_member_tracker{15}; v.insert(v.cbegin() + 1, std::move(tracker)); THEN("the shifted elements are move-assigned and the new element is move-assigned") { REQUIRE(v.size() == 4); REQUIRE(v[0].value == 10); - REQUIRE_FALSE(v[0].was_moved); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); REQUIRE(v[1].value == 15); - REQUIRE_FALSE(v[1].was_copied); - REQUIRE(v[1].was_moved); + REQUIRE(v[1].move_assigned_count == 1); + REQUIRE(v[1].copy_assigned_count == 0); + REQUIRE(tracker.moved_from_count == 1); REQUIRE(v[2].value == 20); - REQUIRE(v[2].was_moved); + REQUIRE(v[2].move_assigned_count == 1); REQUIRE(v[3].value == 30); - REQUIRE(v[3].was_moved); + REQUIRE(v[3].move_constructed_count == 1); } } @@ -1011,8 +1016,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") { for (auto & elem : v) { - elem.was_copied = false; - elem.was_moved = false; + elem.reset_counts(); } auto it = v.erase(v.cbegin() + 1); @@ -1022,12 +1026,15 @@ SCENARIO("Vector modifier move semantics", "[vector]") REQUIRE(v.size() == 2); REQUIRE(v[0].value == 10); - REQUIRE_FALSE(v[0].was_moved); - REQUIRE_FALSE(v[0].was_copied); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); REQUIRE(v[1].value == 30); - REQUIRE(v[1].was_moved); - REQUIRE_FALSE(v[1].was_copied); + REQUIRE(v[1].move_assigned_count == 1); + REQUIRE(v[1].copy_assigned_count == 0); + + REQUIRE(v.data()[2].destroyed_count == 1); + REQUIRE(v.data()[2].moved_from_count == 1); REQUIRE(it == v.begin() + 1); } @@ -1037,8 +1044,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") { for (auto & elem : v) { - elem.was_copied = false; - elem.was_moved = false; + elem.reset_counts(); } auto it = v.erase(v.cend() - 1); @@ -1048,12 +1054,20 @@ SCENARIO("Vector modifier move semantics", "[vector]") REQUIRE(v.size() == 2); REQUIRE(v[0].value == 10); - REQUIRE_FALSE(v[0].was_moved); - REQUIRE_FALSE(v[0].was_copied); + REQUIRE(v[0].move_constructed_count == 0); + REQUIRE(v[0].copy_constructed_count == 0); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); REQUIRE(v[1].value == 20); - REQUIRE_FALSE(v[1].was_moved); - REQUIRE_FALSE(v[1].was_copied); + REQUIRE(v[1].move_constructed_count == 0); + REQUIRE(v[1].copy_constructed_count == 0); + REQUIRE(v[1].move_assigned_count == 0); + REQUIRE(v[1].copy_assigned_count == 0); + + REQUIRE(v.data()[2].destroyed_count == 1); + REQUIRE(v.data()[2].moved_from_count == 0); + REQUIRE(v.data()[2].copied_from_count == 0); REQUIRE(it == v.end()); } @@ -1066,8 +1080,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") for (auto & elem : v) { - elem.was_copied = false; - elem.was_moved = false; + elem.reset_counts(); } auto it = v.erase(v.cbegin() + 1, v.cbegin() + 3); @@ -1077,16 +1090,30 @@ SCENARIO("Vector modifier move semantics", "[vector]") REQUIRE(v.size() == 3); REQUIRE(v[0].value == 10); - REQUIRE_FALSE(v[0].was_moved); - REQUIRE_FALSE(v[0].was_copied); + REQUIRE(v[0].move_constructed_count == 0); + REQUIRE(v[0].copy_constructed_count == 0); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); REQUIRE(v[1].value == 40); - REQUIRE(v[1].was_moved); - REQUIRE_FALSE(v[1].was_copied); + REQUIRE(v[1].move_constructed_count == 0); + REQUIRE(v[1].copy_constructed_count == 0); + REQUIRE(v[1].move_assigned_count == 1); + REQUIRE(v[1].copy_assigned_count == 0); REQUIRE(v[2].value == 50); - REQUIRE(v[2].was_moved); - REQUIRE_FALSE(v[2].was_copied); + REQUIRE(v[2].move_constructed_count == 0); + REQUIRE(v[2].copy_constructed_count == 0); + REQUIRE(v[2].move_assigned_count == 1); + REQUIRE(v[2].copy_assigned_count == 0); + + REQUIRE(v.data()[3].destroyed_count == 1); + REQUIRE(v.data()[3].moved_from_count == 1); + REQUIRE(v.data()[3].copied_from_count == 0); + + REQUIRE(v.data()[4].destroyed_count == 1); + REQUIRE(v.data()[4].moved_from_count == 1); + REQUIRE(v.data()[4].copied_from_count == 0); REQUIRE(it == v.begin() + 1); } @@ -1417,8 +1444,8 @@ SCENARIO("Vector const accessors and copy insertion", "[vector]") GIVEN("An empty vector and a const lvalue tracker") { - auto v = kstd::vector{}; - auto const tracker = kstd::tests::move_tracker{42}; + auto v = kstd::vector{}; + auto const tracker = kstd::tests::special_member_tracker{42}; WHEN("push_back is called with the const lvalue") { @@ -1428,8 +1455,8 @@ SCENARIO("Vector const accessors and copy insertion", "[vector]") { REQUIRE(v.size() == 1); REQUIRE(v.back().value == 42); - REQUIRE(v.back().was_copied); - REQUIRE_FALSE(v.back().was_moved); + REQUIRE(v.back().copy_constructed_count == 1); + REQUIRE(v.back().move_constructed_count == 0); } } @@ -1444,8 +1471,8 @@ SCENARIO("Vector const accessors and copy insertion", "[vector]") REQUIRE(v.size() == 1); REQUIRE(v.capacity() == current_capacity); REQUIRE(v.back().value == 42); - REQUIRE(v.back().was_copied); - REQUIRE_FALSE(v.back().was_moved); + REQUIRE(v.back().copy_constructed_count == 1); + REQUIRE(v.back().move_constructed_count == 0); } } } -- cgit v1.2.3 From 724b9693897642497ca5feee65546dc670bed722 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 1 Apr 2026 07:19:38 +0200 Subject: kstd/observer_ptr: extend test suite --- libs/kstd/include/kstd/observer_ptr | 8 +- libs/kstd/tests/src/observer_ptr.cpp | 165 ++++++++++++++++++++++++++++++----- 2 files changed, 149 insertions(+), 24 deletions(-) diff --git a/libs/kstd/include/kstd/observer_ptr b/libs/kstd/include/kstd/observer_ptr index 97c9e5e..a328331 100644 --- a/libs/kstd/include/kstd/observer_ptr +++ b/libs/kstd/include/kstd/observer_ptr @@ -85,7 +85,7 @@ namespace kstd //! Get the currently watched object. //! //! @return A reference to the currently watched object. - [[nodiscard]] constexpr auto operator*() const noexcept -> std::add_lvalue_reference_t + [[nodiscard]] constexpr auto operator*() const -> std::add_lvalue_reference_t { throw_on_null(); return *m_ptr; @@ -94,7 +94,7 @@ namespace kstd //! Get the currently watched object. //! //! @return A pointer to the currently watched object. - [[nodiscard]] constexpr auto operator->() const noexcept -> element_type * + [[nodiscard]] constexpr auto operator->() const -> element_type * { throw_on_null(); return m_ptr; @@ -109,7 +109,7 @@ namespace kstd } //! Compare the observer pointer with another observer pointer. - //! + //!> //! @param other The other observer pointer to compare with. //! @return The result of the comparison. constexpr auto operator<=>(observer_ptr const & other) const noexcept -> std::strong_ordering = default; @@ -118,7 +118,7 @@ namespace kstd //! Throw an exception if the observer pointer is null. //! //! @throws std::runtime_error if the observer pointer is null. - constexpr auto throw_on_null() const noexcept -> void + constexpr auto throw_on_null() const -> void { if (m_ptr == nullptr) { diff --git a/libs/kstd/tests/src/observer_ptr.cpp b/libs/kstd/tests/src/observer_ptr.cpp index 2fe2226..5d94098 100644 --- a/libs/kstd/tests/src/observer_ptr.cpp +++ b/libs/kstd/tests/src/observer_ptr.cpp @@ -3,8 +3,28 @@ #include +#include +#include #include +namespace +{ + struct Base + { + }; + + struct Derived : Base + { + }; + + struct Element + { + int value{}; + + constexpr auto operator<=>(Element const &) const noexcept = default; + }; +} // namespace + SCENARIO("Observer Pointer initialization and construction", "[observer_ptr]") { GIVEN("An empty context") @@ -59,17 +79,9 @@ SCENARIO("Observer Pointer initialization and construction", "[observer_ptr]") WHEN("copy constructing from an existing observer pointer with a compatible type") { - struct A - { - }; - - struct B : A - { - }; - - auto value = B{}; - auto ptr = kstd::observer_ptr(&value); - auto copy = kstd::observer_ptr(ptr); + auto value = Derived{}; + auto ptr = kstd::observer_ptr(&value); + kstd::observer_ptr copy = ptr; THEN("the new observer pointer points to the same object as the other observer pointer") { @@ -112,6 +124,22 @@ SCENARIO("Observer Pointer initialization and construction", "[observer_ptr]") REQUIRE(&*copy == &value); } } + + WHEN("constructing an observer pointer using make_observer") + { + auto value = 1; + auto ptr = kstd::make_observer(&value); + + THEN("the observer pointer points to the correct object") + { + REQUIRE(&*ptr == &value); + } + + THEN("the observe pointer has the correct element type") + { + STATIC_REQUIRE(std::is_same_v>); + } + } } } @@ -174,6 +202,24 @@ SCENARIO("Observer pointer modifiers", "[observer_ptr]") REQUIRE(&*other_ptr == &value); } } + + WHEN("using namespace-level swap to swap it with another observer pointer") + { + using std::swap; + auto other_value = 2; + auto other_ptr = kstd::observer_ptr{&other_value}; + swap(ptr, other_ptr); + + THEN("the observer pointer points to the other object") + { + REQUIRE(&*ptr == &other_value); + } + + THEN("the other observer pointer points to the original object") + { + REQUIRE(&*other_ptr == &value); + } + } } } @@ -181,14 +227,7 @@ SCENARIO("Observer pointer observers", "[observer_ptr]") { GIVEN("A non-null observer pointer") { - struct A - { - int value{}; - - constexpr auto operator<=>(A const & other) const noexcept = default; - }; - - auto value = A{1}; + auto value = Element{1}; auto ptr = kstd::observer_ptr{&value}; WHEN("getting the raw pointer") @@ -223,12 +262,98 @@ SCENARIO("Observer pointer observers", "[observer_ptr]") WHEN("converting the observer pointer to a raw pointer") { - auto raw_ptr = static_cast(ptr); + auto raw_ptr = static_cast(ptr); THEN("the raw pointer points to the correct object") { REQUIRE(raw_ptr == &value); } } + + WHEN("checking the observer pointer as a boolean") + { + THEN("it returns true") + { + REQUIRE(static_cast(ptr)); + } + } + } + + GIVEN("A null observer pointer") + { + auto ptr = kstd::observer_ptr{}; + + WHEN("checking the observer pointer as a boolean") + { + THEN("it returns false") + { + REQUIRE_FALSE(static_cast(ptr)); + } + } + + WHEN("dereferencing the observer pointer") + { + THEN("the observer pointer panics") + { + REQUIRE_THROWS_AS(*ptr, kstd::tests::os_panic); + } + } + + WHEN("writing through the observer pointer with the arrow operator") + { + THEN("the observer pointer panics") + { + REQUIRE_THROWS_AS(ptr->value = 2, kstd::tests::os_panic); + } + } + } +} + +SCENARIO("Observer pointer comparisons", "[observer_ptr]") +{ + GIVEN("Observer pointers to elements of an array") + { + int arr[] = {1, 2}; + auto ptr1 = kstd::observer_ptr{&arr[0]}; + auto ptr2 = kstd::observer_ptr{&arr[1]}; + + WHEN("comparing the same observer pointer") + { + THEN("they are equal") + { + REQUIRE(ptr1 == ptr1); + REQUIRE((ptr1 <=> ptr1) == std::strong_ordering::equal); + } + } + + WHEN("comparing different observer pointers") + { + THEN("they are ordered correctly") + { + REQUIRE(ptr1 != ptr2); + REQUIRE(ptr1 < ptr2); + REQUIRE(ptr1 <= ptr2); + REQUIRE(ptr2 > ptr1); + REQUIRE(ptr2 >= ptr1); + REQUIRE((ptr1 <=> ptr2) == std::strong_ordering::less); + REQUIRE((ptr2 <=> ptr1) == std::strong_ordering::greater); + } + } + } + + GIVEN("A null observer pointer") + { + auto ptr = kstd::observer_ptr{}; + + WHEN("comparing with another null observer pointer") + { + auto other_ptr = kstd::observer_ptr{}; + + THEN("they are equal") + { + REQUIRE(ptr == other_ptr); + REQUIRE((ptr <=> other_ptr) == std::strong_ordering::equal); + } + } } } \ No newline at end of file -- cgit v1.2.3 From a2ff4ace21699fe2be2e0401af78790c01f78d85 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 11:45:55 +0200 Subject: kstd: move observer_ptr to bits --- kapi/include/kapi/devices/bus.hpp | 1 - libs/kstd/include/kstd/bits/observer_ptr.hpp | 157 +++++++++++++++++++++++++++ libs/kstd/include/kstd/memory | 9 +- libs/kstd/include/kstd/observer_ptr | 155 -------------------------- libs/kstd/tests/src/observer_ptr.cpp | 2 +- 5 files changed, 163 insertions(+), 161 deletions(-) create mode 100644 libs/kstd/include/kstd/bits/observer_ptr.hpp delete mode 100644 libs/kstd/include/kstd/observer_ptr diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index 0b25ac1..a8d7df8 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -4,7 +4,6 @@ #include "kapi/devices/device.hpp" #include -#include #include #include diff --git a/libs/kstd/include/kstd/bits/observer_ptr.hpp b/libs/kstd/include/kstd/bits/observer_ptr.hpp new file mode 100644 index 0000000..43ea409 --- /dev/null +++ b/libs/kstd/include/kstd/bits/observer_ptr.hpp @@ -0,0 +1,157 @@ +#ifndef KSTD_OBSERVER_PTR_HPP +#define KSTD_OBSERVER_PTR_HPP + +// IWYU pragma: private, include + +#include "kstd/os/error.hpp" + +#include +#include +#include +#include +#include + +namespace kstd +{ + + template + struct observer_ptr + { + //! The type of the element being pointed to. + using element_type = ElementType; + + //! Construct an empty observer pointer. + constexpr observer_ptr() noexcept = default; + + //! Construct an empty observer pointer from a null pointer. + constexpr observer_ptr(std::nullptr_t) noexcept {} + + //! Construct an observer pointer from a raw pointer. + constexpr explicit observer_ptr(element_type * pointer) + : m_ptr{pointer} + {} + + //! Construct an observer pointer from another observer pointer. + template + requires std::convertible_to + constexpr observer_ptr(observer_ptr other) noexcept + : m_ptr{other.get()} + {} + + //! Copy construct an observer pointer. + constexpr observer_ptr(observer_ptr const & other) noexcept = default; + + //! Move construct an observer pointer. + constexpr observer_ptr(observer_ptr && other) noexcept = default; + + //! Stop watching the the watched object. + //! + //! @return The currently watched object, or nullptr if no object is being watched. + [[nodiscard]] constexpr auto release() noexcept -> element_type * + { + return std::exchange(m_ptr, nullptr); + } + + //! Reset the observer pointer. + //! + //! @param pointer The new object to watch. + constexpr auto reset(element_type * pointer = nullptr) noexcept -> void + { + m_ptr = pointer; + } + + //! Swap the observer pointer with another observer pointer. + //! + //! @param other The other observer pointer to swap with. + constexpr auto swap(observer_ptr & other) noexcept -> void + { + std::swap(m_ptr, other.m_ptr); + } + + //! Get the currently watched object. + //! + //! @return The currently watched object, or nullptr if no object is being watched. + [[nodiscard]] constexpr auto get() const noexcept -> element_type * + { + return m_ptr; + } + + //! Check if the observer pointer is watching an object. + //! + //! @return True if the observer pointer is watching an object, false otherwise. + [[nodiscard]] constexpr explicit operator bool() const noexcept + { + return m_ptr != nullptr; + } + + //! Get the currently watched object. + //! + //! @return A reference to the currently watched object. + [[nodiscard]] constexpr auto operator*() const -> std::add_lvalue_reference_t + { + throw_on_null(); + return *m_ptr; + } + + //! Get the currently watched object. + //! + //! @return A pointer to the currently watched object. + [[nodiscard]] constexpr auto operator->() const -> element_type * + { + throw_on_null(); + return m_ptr; + } + + //! Convert the observer pointer to a raw pointer. + //! + //! @return A pointer to the currently watched object. + constexpr explicit operator element_type *() const noexcept + { + return m_ptr; + } + + //! Compare the observer pointer with another observer pointer. + //!> + //! @param other The other observer pointer to compare with. + //! @return The result of the comparison. + constexpr auto operator<=>(observer_ptr const & other) const noexcept -> std::strong_ordering = default; + + private: + //! Throw an exception if the observer pointer is null. + //! + //! @throws std::runtime_error if the observer pointer is null. + constexpr auto throw_on_null() const -> void + { + if (m_ptr == nullptr) + { + os::panic("[kstd:observer_ptr] Dereferencing a null observer pointer"); + } + } + + //! The raw pointer to the watched object. + ElementType * m_ptr{}; + }; + + //! Swap two observer pointers. + //! + //! @param lhs The first observer pointer to swap. + //! @param rhs The second observer pointer to swap. + template + constexpr auto swap(observer_ptr & lhs, observer_ptr & rhs) noexcept -> void + { + lhs.swap(rhs); + } + + //! Create an observer pointer from a raw pointer. + //! + //! @param pointer The raw pointer to create an observer pointer from. + //! @return An observer pointer to the given raw pointer. + template + constexpr auto make_observer(ElementType * pointer) noexcept -> observer_ptr + { + return observer_ptr{pointer}; + } + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/memory b/libs/kstd/include/kstd/memory index cab2fba..493f49a 100644 --- a/libs/kstd/include/kstd/memory +++ b/libs/kstd/include/kstd/memory @@ -1,7 +1,8 @@ -#ifndef KSTD_SHARED_POINTER_HPP -#define KSTD_SHARED_POINTER_HPP +#ifndef KSTD_MEMORY_HPP +#define KSTD_MEMORY_HPP -#include "kstd/bits/shared_ptr.hpp" // IWYU pragma: export -#include "kstd/bits/unique_ptr.hpp" // IWYU pragma: export +#include "kstd/bits/observer_ptr.hpp" // IWYU pragma: export +#include "kstd/bits/shared_ptr.hpp" // IWYU pragma: export +#include "kstd/bits/unique_ptr.hpp" // IWYU pragma: export #endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/observer_ptr b/libs/kstd/include/kstd/observer_ptr deleted file mode 100644 index a328331..0000000 --- a/libs/kstd/include/kstd/observer_ptr +++ /dev/null @@ -1,155 +0,0 @@ -#ifndef KSTD_OBSERVER_PTR_HPP -#define KSTD_OBSERVER_PTR_HPP - -#include "kstd/os/error.hpp" - -#include -#include -#include -#include -#include - -namespace kstd -{ - - template - struct observer_ptr - { - //! The type of the element being pointed to. - using element_type = ElementType; - - //! Construct an empty observer pointer. - constexpr observer_ptr() noexcept = default; - - //! Construct an empty observer pointer from a null pointer. - constexpr observer_ptr(std::nullptr_t) noexcept {} - - //! Construct an observer pointer from a raw pointer. - constexpr explicit observer_ptr(element_type * pointer) - : m_ptr{pointer} - {} - - //! Construct an observer pointer from another observer pointer. - template - requires std::convertible_to - constexpr observer_ptr(observer_ptr other) noexcept - : m_ptr{other.get()} - {} - - //! Copy construct an observer pointer. - constexpr observer_ptr(observer_ptr const & other) noexcept = default; - - //! Move construct an observer pointer. - constexpr observer_ptr(observer_ptr && other) noexcept = default; - - //! Stop watching the the watched object. - //! - //! @return The currently watched object, or nullptr if no object is being watched. - [[nodiscard]] constexpr auto release() noexcept -> element_type * - { - return std::exchange(m_ptr, nullptr); - } - - //! Reset the observer pointer. - //! - //! @param pointer The new object to watch. - constexpr auto reset(element_type * pointer = nullptr) noexcept -> void - { - m_ptr = pointer; - } - - //! Swap the observer pointer with another observer pointer. - //! - //! @param other The other observer pointer to swap with. - constexpr auto swap(observer_ptr & other) noexcept -> void - { - std::swap(m_ptr, other.m_ptr); - } - - //! Get the currently watched object. - //! - //! @return The currently watched object, or nullptr if no object is being watched. - [[nodiscard]] constexpr auto get() const noexcept -> element_type * - { - return m_ptr; - } - - //! Check if the observer pointer is watching an object. - //! - //! @return True if the observer pointer is watching an object, false otherwise. - [[nodiscard]] constexpr explicit operator bool() const noexcept - { - return m_ptr != nullptr; - } - - //! Get the currently watched object. - //! - //! @return A reference to the currently watched object. - [[nodiscard]] constexpr auto operator*() const -> std::add_lvalue_reference_t - { - throw_on_null(); - return *m_ptr; - } - - //! Get the currently watched object. - //! - //! @return A pointer to the currently watched object. - [[nodiscard]] constexpr auto operator->() const -> element_type * - { - throw_on_null(); - return m_ptr; - } - - //! Convert the observer pointer to a raw pointer. - //! - //! @return A pointer to the currently watched object. - constexpr explicit operator element_type *() const noexcept - { - return m_ptr; - } - - //! Compare the observer pointer with another observer pointer. - //!> - //! @param other The other observer pointer to compare with. - //! @return The result of the comparison. - constexpr auto operator<=>(observer_ptr const & other) const noexcept -> std::strong_ordering = default; - - private: - //! Throw an exception if the observer pointer is null. - //! - //! @throws std::runtime_error if the observer pointer is null. - constexpr auto throw_on_null() const -> void - { - if (m_ptr == nullptr) - { - os::panic("[kstd:observer_ptr] Dereferencing a null observer pointer"); - } - } - - //! The raw pointer to the watched object. - ElementType * m_ptr{}; - }; - - //! Swap two observer pointers. - //! - //! @param lhs The first observer pointer to swap. - //! @param rhs The second observer pointer to swap. - template - constexpr auto swap(observer_ptr & lhs, observer_ptr & rhs) noexcept -> void - { - lhs.swap(rhs); - } - - //! Create an observer pointer from a raw pointer. - //! - //! @param pointer The raw pointer to create an observer pointer from. - //! @return An observer pointer to the given raw pointer. - template - constexpr auto make_observer(ElementType * pointer) noexcept -> observer_ptr - { - return observer_ptr{pointer}; - } - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/tests/src/observer_ptr.cpp b/libs/kstd/tests/src/observer_ptr.cpp index 5d94098..006ebde 100644 --- a/libs/kstd/tests/src/observer_ptr.cpp +++ b/libs/kstd/tests/src/observer_ptr.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include -- cgit v1.2.3 From e7af7ceea2324dcf7d2222986c09d1c478ee4c7e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 13:22:28 +0200 Subject: kstd: make string formattable --- libs/kstd/include/kstd/string | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libs/kstd/include/kstd/string b/libs/kstd/include/kstd/string index 075422e..4ce19ce 100644 --- a/libs/kstd/include/kstd/string +++ b/libs/kstd/include/kstd/string @@ -1,6 +1,10 @@ #ifndef KSTD_STRING_HPP #define KSTD_STRING_HPP +#include "kstd/bits/format/context.hpp" +#include "kstd/bits/format/formatter.hpp" +#include "kstd/bits/format/formatter/string_view.hpp" + #include #include #include @@ -343,6 +347,15 @@ namespace kstd return !(lhs == rhs); } + template<> + struct formatter : formatter + { + auto format(string const & str, format_context & context) const -> void + { + formatter::format(str.view(), context); + } + }; + } // namespace kstd #endif \ No newline at end of file -- cgit v1.2.3 From d0c532af74d8d486d734904fd330d5dae7f49754 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 13:36:01 +0200 Subject: kapi: add basic device subsystem --- kapi/include/kapi/devices.hpp | 46 ++++++++++++++++++++ kernel/CMakeLists.txt | 2 + kernel/include/kernel/devices/root_bus.hpp | 32 ++++++++++++++ kernel/kapi/devices.cpp | 63 ++++++++++++++++++++++++++++ kernel/src/devices/root_bus.cpp | 55 ++++++++++++++++++++++++ kernel/src/main.cpp | 4 ++ libs/kstd/include/kstd/bits/observer_ptr.hpp | 6 +++ 7 files changed, 208 insertions(+) create mode 100644 kapi/include/kapi/devices.hpp create mode 100644 kernel/include/kernel/devices/root_bus.hpp create mode 100644 kernel/kapi/devices.cpp create mode 100644 kernel/src/devices/root_bus.cpp diff --git a/kapi/include/kapi/devices.hpp b/kapi/include/kapi/devices.hpp new file mode 100644 index 0000000..60a39bd --- /dev/null +++ b/kapi/include/kapi/devices.hpp @@ -0,0 +1,46 @@ +#ifndef TEACHOS_KAPI_DEVICES_HPP +#define TEACHOS_KAPI_DEVICES_HPP + +#include "kapi/devices/bus.hpp" // IWYU pragma: export +#include "kapi/devices/device.hpp" // IWYU pragma: export + +#include + +namespace kapi::devices +{ + + //! @addtogroup kernel-defined + //! @{ + + //! Initialize the kernel's device management subsystem. + auto init() -> void; + + //! Get the virtual system root bus. + //! + //! @warning This function will panic if the root bus has not been initialized. + //! + //! @return a reference to the root bus. + auto get_root_bus() -> bus &; + + //! Ask the kernel to allocate a new major number. + //! + //! @return a new, unused major number. + auto allocate_major_number() -> std::size_t; + + //! Register a new device with the kernel's device manager. + //! + //! @param device The device to register. + //! @return true if the device was registered successfully, false otherwise. + auto register_device(device & device) -> bool; + + //! Unregister a device from the kernel's device manager. + //! + //! @param device The device to unregister. + //! @return true if the device was unregistered successfully, false otherwise. + auto unregister_device(device & device) -> bool; + + //! @} + +} // namespace kapi::devices + +#endif \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index f283588..9868eb9 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -3,6 +3,7 @@ add_library("kernel_objs" OBJECT "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" + "kapi/devices.cpp" "kapi/interrupts.cpp" "kapi/memory.cpp" "kapi/system.cpp" @@ -17,6 +18,7 @@ add_library("kernel_objs" OBJECT "src/memory.cpp" "src/devices/block_device.cpp" "src/devices/block_device_utils.cpp" + "src/devices/root_bus.cpp" "src/devices/storage/controller.cpp" "src/devices/storage/management.cpp" "src/devices/storage/ram_disk/controller.cpp" diff --git a/kernel/include/kernel/devices/root_bus.hpp b/kernel/include/kernel/devices/root_bus.hpp new file mode 100644 index 0000000..f7bfbfb --- /dev/null +++ b/kernel/include/kernel/devices/root_bus.hpp @@ -0,0 +1,32 @@ +#ifndef TEACHOS_KERNEL_DEVICES_ROOT_BUS_HPP +#define TEACHOS_KERNEL_DEVICES_ROOT_BUS_HPP + +#include "kapi/devices/bus.hpp" +#include "kapi/devices/device.hpp" + +#include +#include +#include + +namespace kernel::devices +{ + + struct root_bus final : kapi::devices::bus + { + root_bus(); + + auto add_child(kstd::unique_ptr child) -> void override; + + [[nodiscard]] auto children() const -> kstd::vector> const & override; + + auto init() -> bool override; + + private: + kstd::vector> m_children{}; + kstd::vector> m_observers{}; + bool m_initialized{}; + }; + +} // namespace kernel::devices + +#endif \ No newline at end of file diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp new file mode 100644 index 0000000..dc19ab4 --- /dev/null +++ b/kernel/kapi/devices.cpp @@ -0,0 +1,63 @@ +#include "kapi/devices.hpp" + +#include "kapi/system.hpp" + +#include "kernel/devices/root_bus.hpp" + +#include +#include +#include + +#include +#include +#include +#include + +namespace kapi::devices +{ + + namespace + { + auto constinit next_major_number = std::atomic_size_t{0}; + auto constinit root_bus = std::optional{}; + auto constinit device_tree = kstd::flat_map, kstd::observer_ptr>{}; + } // namespace + + auto init() -> void + { + auto static is_initialized = std::atomic_flag{}; + if (is_initialized.test_and_set()) + { + return; + } + + root_bus.emplace(); + root_bus->init(); + } + + auto get_root_bus() -> bus & + { + if (!root_bus.has_value()) + { + kapi::system::panic("[kernel:devices] Root bus not initialized!"); + } + return *root_bus; + } + + auto allocate_major_number() -> std::size_t + { + return next_major_number++; + } + + auto register_device(device & device) -> bool + { + return device_tree.emplace(std::pair{device.major(), device.minor()}, &device).second; + } + + auto unregister_device(device &) -> bool + { + kstd::println("[kernel:devices] TODO: implement device deregistration"); + return false; + } + +} // namespace kapi::devices \ No newline at end of file diff --git a/kernel/src/devices/root_bus.cpp b/kernel/src/devices/root_bus.cpp new file mode 100644 index 0000000..a7f3c1a --- /dev/null +++ b/kernel/src/devices/root_bus.cpp @@ -0,0 +1,55 @@ +#include "kernel/devices/root_bus.hpp" + +#include "kapi/devices.hpp" +#include "kapi/devices/bus.hpp" +#include "kapi/devices/device.hpp" +#include "kapi/system.hpp" + +#include +#include +#include + +#include +#include + +namespace kernel::devices +{ + + root_bus::root_bus() + : kapi::devices::bus{kapi::devices::allocate_major_number(), 0, "system"} + {} + + auto root_bus::add_child(kstd::unique_ptr child) -> void + { + auto observer = m_observers.emplace_back(child.get()); + m_children.push_back(std::move(child)); + + if (m_initialized) + { + kstd::println("Initializing child device '{}'", observer->name()); + if (!observer->init()) + { + kapi::system::panic("[kernel:devices] Failed to initialize child device"); + } + } + } + + auto root_bus::children() const -> kstd::vector> const & + { + return m_observers; + } + + auto root_bus::init() -> bool + { + if (m_initialized) + { + kapi::system::panic("[kernel:devices] Root bus already initialized!"); + } + + return std::ranges::fold_left(m_children, true, [](bool acc, auto & child) -> bool { + kstd::println("[kernel:devices] Initializing child device '{}'", child->name()); + return acc && child->init(); + }); + } + +} // namespace kernel::devices \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 37b4c5b..9d6028d 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,6 +1,7 @@ #include "kapi/boot_modules.hpp" #include "kapi/cio.hpp" #include "kapi/cpu.hpp" +#include "kapi/devices.hpp" #include "kapi/interrupts.hpp" #include "kapi/memory.hpp" #include "kapi/system.hpp" @@ -179,6 +180,9 @@ auto main() -> int kstd::println("[OS] Memory subsystem initialized."); kapi::system::memory_initialized(); + kapi::devices::init(); + kstd::println("[OS] System root bus initialized."); + kapi::boot_modules::init(); kstd::println("[OS] Boot module registry initialized."); diff --git a/libs/kstd/include/kstd/bits/observer_ptr.hpp b/libs/kstd/include/kstd/bits/observer_ptr.hpp index 43ea409..1c5da15 100644 --- a/libs/kstd/include/kstd/bits/observer_ptr.hpp +++ b/libs/kstd/include/kstd/bits/observer_ptr.hpp @@ -44,6 +44,12 @@ namespace kstd //! Move construct an observer pointer. constexpr observer_ptr(observer_ptr && other) noexcept = default; + //! Copy assign an observer pointer. + constexpr auto operator=(observer_ptr const & other) noexcept -> observer_ptr & = default; + + //! Move assign an observer pointer. + constexpr auto operator=(observer_ptr && other) noexcept -> observer_ptr & = default; + //! Stop watching the the watched object. //! //! @return The currently watched object, or nullptr if no object is being watched. -- cgit v1.2.3 From b84c4c9d8c90f3d3fd5a60de282278912fad2f04 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 13:59:27 +0200 Subject: x86_64/devices: implement ISA bus stub --- arch/x86_64/CMakeLists.txt | 4 +++ arch/x86_64/include/arch/bus/isa.hpp | 31 +++++++++++++++++ arch/x86_64/kapi/devices.cpp | 22 ++++++++++++ arch/x86_64/src/bus/isa.cpp | 56 ++++++++++++++++++++++++++++++ kapi/include/kapi/devices.hpp | 8 +++++ kernel/include/kernel/devices/root_bus.hpp | 4 ++- kernel/src/devices/root_bus.cpp | 4 +-- kernel/src/main.cpp | 3 ++ libs/kstd/include/kstd/bits/unique_ptr.hpp | 11 ++++++ 9 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 arch/x86_64/include/arch/bus/isa.hpp create mode 100644 arch/x86_64/kapi/devices.cpp create mode 100644 arch/x86_64/src/bus/isa.cpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 4427e4c..21dceef 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -15,6 +15,7 @@ target_sources("x86_64" PRIVATE "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" + "kapi/devices.cpp" "kapi/interrupts.cpp" "kapi/memory.cpp" "kapi/system.cpp" @@ -24,6 +25,9 @@ target_sources("x86_64" PRIVATE "src/cpu/interrupts.cpp" "src/cpu/interrupt_stubs.S" + # Bus Initialization + "src/bus/isa.cpp" + # Low-level bootstrap "src/boot/boot32.S" "src/boot/entry64.s" diff --git a/arch/x86_64/include/arch/bus/isa.hpp b/arch/x86_64/include/arch/bus/isa.hpp new file mode 100644 index 0000000..41dda93 --- /dev/null +++ b/arch/x86_64/include/arch/bus/isa.hpp @@ -0,0 +1,31 @@ +#ifndef TEACHOS_X86_64_BUS_ISA_HPP +#define TEACHOS_X86_64_BUS_ISA_HPP + +#include "kapi/devices/bus.hpp" +#include "kapi/devices/device.hpp" + +#include +#include + +namespace arch::bus +{ + + struct isa final : public kapi::devices::bus + { + isa(); + + auto add_child(kstd::unique_ptr child) -> void override; + + [[nodiscard]] auto children() const -> kstd::vector> const & override; + + auto init() -> bool override; + + private: + kstd::vector> m_devices{}; + kstd::vector> m_observers{}; + bool m_initialized{}; + }; + +} // namespace arch::bus + +#endif // TEACHOS_X86_64_BUS_ISA_HPP diff --git a/arch/x86_64/kapi/devices.cpp b/arch/x86_64/kapi/devices.cpp new file mode 100644 index 0000000..25185d6 --- /dev/null +++ b/arch/x86_64/kapi/devices.cpp @@ -0,0 +1,22 @@ +#include "kapi/devices.hpp" + +#include "arch/bus/isa.hpp" + +#include +#include + +#include + +namespace kapi::devices +{ + + auto init_platform_devices() -> void + { + kstd::println("[x86_64:devices] Initializing ISA bus..."); + auto isa_bus = kstd::make_unique(); + + auto & root_bus = get_root_bus(); + root_bus.add_child(std::move(isa_bus)); + } + +} // namespace kapi::devices \ No newline at end of file diff --git a/arch/x86_64/src/bus/isa.cpp b/arch/x86_64/src/bus/isa.cpp new file mode 100644 index 0000000..3fe4f6f --- /dev/null +++ b/arch/x86_64/src/bus/isa.cpp @@ -0,0 +1,56 @@ +#include "arch/bus/isa.hpp" + +#include "kapi/devices.hpp" +#include "kapi/devices/device.hpp" +#include "kapi/system.hpp" + +#include +#include +#include + +#include +#include + +namespace arch::bus +{ + + isa::isa() + : kapi::devices::bus(kapi::devices::allocate_major_number(), 0, "isa") + {} + + auto isa::add_child(kstd::unique_ptr child) -> void + { + auto observer = m_observers.emplace_back(child.get()); + m_devices.push_back(std::move(child)); + + if (m_initialized) + { + kstd::println("[bus:{}} Initializing child device '{}'", name(), observer->name()); + if (!observer->init()) + { + kapi::system::panic("[x86_64:devices] Failed to initialize child device"); + } + } + } + + auto isa::children() const -> kstd::vector> const & + { + return m_observers; + } + + auto isa::init() -> bool + { + if (m_initialized) + { + kapi::system::panic("[x86_64:devices] ISA bus already initialized!"); + } + + m_initialized = std::ranges::fold_left(m_devices, true, [](bool acc, auto & child) -> bool { + kstd::println("[x86_64:devices] Initializing child device '{}'", child->name()); + return acc && child->init(); + }); + + return m_initialized; + } + +} // namespace arch::bus \ No newline at end of file diff --git a/kapi/include/kapi/devices.hpp b/kapi/include/kapi/devices.hpp index 60a39bd..2028a64 100644 --- a/kapi/include/kapi/devices.hpp +++ b/kapi/include/kapi/devices.hpp @@ -41,6 +41,14 @@ namespace kapi::devices //! @} + //! @addtogroup platform-defined + //! @{ + + //! Initialize the platform's device tree. + auto init_platform_devices() -> void; + + //! @} + } // namespace kapi::devices #endif \ No newline at end of file diff --git a/kernel/include/kernel/devices/root_bus.hpp b/kernel/include/kernel/devices/root_bus.hpp index f7bfbfb..d92914d 100644 --- a/kernel/include/kernel/devices/root_bus.hpp +++ b/kernel/include/kernel/devices/root_bus.hpp @@ -8,6 +8,8 @@ #include #include +#include + namespace kernel::devices { @@ -24,7 +26,7 @@ namespace kernel::devices private: kstd::vector> m_children{}; kstd::vector> m_observers{}; - bool m_initialized{}; + std::atomic_flag m_initialized{}; }; } // namespace kernel::devices diff --git a/kernel/src/devices/root_bus.cpp b/kernel/src/devices/root_bus.cpp index a7f3c1a..75b5b80 100644 --- a/kernel/src/devices/root_bus.cpp +++ b/kernel/src/devices/root_bus.cpp @@ -24,7 +24,7 @@ namespace kernel::devices auto observer = m_observers.emplace_back(child.get()); m_children.push_back(std::move(child)); - if (m_initialized) + if (m_initialized.test()) { kstd::println("Initializing child device '{}'", observer->name()); if (!observer->init()) @@ -41,7 +41,7 @@ namespace kernel::devices auto root_bus::init() -> bool { - if (m_initialized) + if (m_initialized.test_and_set()) { kapi::system::panic("[kernel:devices] Root bus already initialized!"); } diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 9d6028d..eaaf87f 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -183,6 +183,9 @@ auto main() -> int kapi::devices::init(); kstd::println("[OS] System root bus initialized."); + kapi::devices::init_platform_devices(); + kstd::println("[OS] Platform devices initialized."); + kapi::boot_modules::init(); kstd::println("[OS] Boot module registry initialized."); diff --git a/libs/kstd/include/kstd/bits/unique_ptr.hpp b/libs/kstd/include/kstd/bits/unique_ptr.hpp index e0870b1..f50335c 100644 --- a/libs/kstd/include/kstd/bits/unique_ptr.hpp +++ b/libs/kstd/include/kstd/bits/unique_ptr.hpp @@ -16,6 +16,9 @@ namespace kstd template struct unique_ptr { + template + friend struct unique_ptr; + /** * @brief Constructor. * @@ -40,6 +43,14 @@ namespace kstd */ unique_ptr(unique_ptr const &) = delete; + template + requires(std::is_convertible_v) + unique_ptr(unique_ptr && other) noexcept + : pointer(other.pointer) + { + other.pointer = nullptr; + } + /** * @brief Deleted copy assignment operator to enforce unique ownership. */ -- cgit v1.2.3 From 66ffd2ad8c793c4eea1527848fe4772e42595718 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 14:24:52 +0200 Subject: kapi: extract common bus code --- arch/x86_64/include/arch/bus/isa.hpp | 15 -------- arch/x86_64/src/bus/isa.cpp | 44 --------------------- kapi/include/kapi/devices/bus.hpp | 61 ++++++++++++++++++++++++++++-- kernel/include/kernel/devices/root_bus.hpp | 18 --------- kernel/kapi/devices.cpp | 4 +- kernel/src/devices/root_bus.cpp | 43 --------------------- libs/kstd/include/kstd/bits/unique_ptr.hpp | 22 ++++------- 7 files changed, 66 insertions(+), 141 deletions(-) diff --git a/arch/x86_64/include/arch/bus/isa.hpp b/arch/x86_64/include/arch/bus/isa.hpp index 41dda93..bd92b2e 100644 --- a/arch/x86_64/include/arch/bus/isa.hpp +++ b/arch/x86_64/include/arch/bus/isa.hpp @@ -2,10 +2,6 @@ #define TEACHOS_X86_64_BUS_ISA_HPP #include "kapi/devices/bus.hpp" -#include "kapi/devices/device.hpp" - -#include -#include namespace arch::bus { @@ -13,17 +9,6 @@ namespace arch::bus struct isa final : public kapi::devices::bus { isa(); - - auto add_child(kstd::unique_ptr child) -> void override; - - [[nodiscard]] auto children() const -> kstd::vector> const & override; - - auto init() -> bool override; - - private: - kstd::vector> m_devices{}; - kstd::vector> m_observers{}; - bool m_initialized{}; }; } // namespace arch::bus diff --git a/arch/x86_64/src/bus/isa.cpp b/arch/x86_64/src/bus/isa.cpp index 3fe4f6f..2ad4d21 100644 --- a/arch/x86_64/src/bus/isa.cpp +++ b/arch/x86_64/src/bus/isa.cpp @@ -1,15 +1,6 @@ #include "arch/bus/isa.hpp" #include "kapi/devices.hpp" -#include "kapi/devices/device.hpp" -#include "kapi/system.hpp" - -#include -#include -#include - -#include -#include namespace arch::bus { @@ -18,39 +9,4 @@ namespace arch::bus : kapi::devices::bus(kapi::devices::allocate_major_number(), 0, "isa") {} - auto isa::add_child(kstd::unique_ptr child) -> void - { - auto observer = m_observers.emplace_back(child.get()); - m_devices.push_back(std::move(child)); - - if (m_initialized) - { - kstd::println("[bus:{}} Initializing child device '{}'", name(), observer->name()); - if (!observer->init()) - { - kapi::system::panic("[x86_64:devices] Failed to initialize child device"); - } - } - } - - auto isa::children() const -> kstd::vector> const & - { - return m_observers; - } - - auto isa::init() -> bool - { - if (m_initialized) - { - kapi::system::panic("[x86_64:devices] ISA bus already initialized!"); - } - - m_initialized = std::ranges::fold_left(m_devices, true, [](bool acc, auto & child) -> bool { - kstd::println("[x86_64:devices] Initializing child device '{}'", child->name()); - return acc && child->init(); - }); - - return m_initialized; - } - } // namespace arch::bus \ No newline at end of file diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index a8d7df8..ccaf0f2 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -2,12 +2,17 @@ #define TEACHOS_KAPI_DEVICES_BUS_HPP #include "kapi/devices/device.hpp" +#include "kapi/system.hpp" #include +#include #include #include +#include +#include #include +#include namespace kapi::devices { @@ -23,17 +28,65 @@ namespace kapi::devices : device(major, minor, name) {} + //! Initialize the bus and all of its children. + //! + //! @return true iff. the bus and all of its children are healthy, false otherwise. + auto init() -> bool final + { + if (m_initialized.test_and_set()) + { + return true; + } + + if (!probe()) + { + return false; + } + + return std::ranges::fold_left(m_devices, true, [&](bool acc, auto & child) -> bool { + kstd::println("[kAPI:BUS] Initializing child device {}@{}", child->name(), name()); + return acc && child->init(); + }); + } + //! Attach a child device to this bus. //! //! Whenever a device is attached to a bus, the bus takes sole ownership of the device. //! //! @param child The child device to attach. - virtual auto add_child(kstd::unique_ptr child) -> void = 0; + auto add_child(kstd::unique_ptr child) -> void + { + auto observer = m_observers.emplace_back(child.get()); + m_devices.push_back(std::move(child)); - //! Get a list of all child devices attached to this bus. + if (m_initialized.test()) + { + kstd::println("[kAPI:BUS] Initializing child device {}@{}", observer->name(), name()); + if (!observer->init()) + { + kapi::system::panic("[kAPI:BUS] Failed to initialize child device"); + } + } + } + + [[nodiscard]] auto children() const -> kstd::vector> const & + { + return m_observers; + } + + protected: + //! Probe the bus hardware state. //! - //! @return A reference to list of child devices of this bus. - [[nodiscard]] virtual auto children() const -> kstd::vector> const & = 0; + //! @return true iff. the bus hardware is healthy, false otherwise. + auto virtual probe() -> bool + { + return true; + } + + private: + kstd::vector> m_devices; + kstd::vector> m_observers; + std::atomic_flag m_initialized{}; }; } // namespace kapi::devices diff --git a/kernel/include/kernel/devices/root_bus.hpp b/kernel/include/kernel/devices/root_bus.hpp index d92914d..660b715 100644 --- a/kernel/include/kernel/devices/root_bus.hpp +++ b/kernel/include/kernel/devices/root_bus.hpp @@ -2,13 +2,6 @@ #define TEACHOS_KERNEL_DEVICES_ROOT_BUS_HPP #include "kapi/devices/bus.hpp" -#include "kapi/devices/device.hpp" - -#include -#include -#include - -#include namespace kernel::devices { @@ -16,17 +9,6 @@ namespace kernel::devices struct root_bus final : kapi::devices::bus { root_bus(); - - auto add_child(kstd::unique_ptr child) -> void override; - - [[nodiscard]] auto children() const -> kstd::vector> const & override; - - auto init() -> bool override; - - private: - kstd::vector> m_children{}; - kstd::vector> m_observers{}; - std::atomic_flag m_initialized{}; }; } // namespace kernel::devices diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index dc19ab4..dbf5e68 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -39,7 +39,7 @@ namespace kapi::devices { if (!root_bus.has_value()) { - kapi::system::panic("[kernel:devices] Root bus not initialized!"); + kapi::system::panic("[OS:DEV] Root bus not initialized!"); } return *root_bus; } @@ -56,7 +56,7 @@ namespace kapi::devices auto unregister_device(device &) -> bool { - kstd::println("[kernel:devices] TODO: implement device deregistration"); + kstd::println("[OS:DEV] TODO: implement device deregistration"); return false; } diff --git a/kernel/src/devices/root_bus.cpp b/kernel/src/devices/root_bus.cpp index 75b5b80..d3ba23f 100644 --- a/kernel/src/devices/root_bus.cpp +++ b/kernel/src/devices/root_bus.cpp @@ -1,16 +1,6 @@ #include "kernel/devices/root_bus.hpp" #include "kapi/devices.hpp" -#include "kapi/devices/bus.hpp" -#include "kapi/devices/device.hpp" -#include "kapi/system.hpp" - -#include -#include -#include - -#include -#include namespace kernel::devices { @@ -19,37 +9,4 @@ namespace kernel::devices : kapi::devices::bus{kapi::devices::allocate_major_number(), 0, "system"} {} - auto root_bus::add_child(kstd::unique_ptr child) -> void - { - auto observer = m_observers.emplace_back(child.get()); - m_children.push_back(std::move(child)); - - if (m_initialized.test()) - { - kstd::println("Initializing child device '{}'", observer->name()); - if (!observer->init()) - { - kapi::system::panic("[kernel:devices] Failed to initialize child device"); - } - } - } - - auto root_bus::children() const -> kstd::vector> const & - { - return m_observers; - } - - auto root_bus::init() -> bool - { - if (m_initialized.test_and_set()) - { - kapi::system::panic("[kernel:devices] Root bus already initialized!"); - } - - return std::ranges::fold_left(m_children, true, [](bool acc, auto & child) -> bool { - kstd::println("[kernel:devices] Initializing child device '{}'", child->name()); - return acc && child->init(); - }); - } - } // namespace kernel::devices \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/unique_ptr.hpp b/libs/kstd/include/kstd/bits/unique_ptr.hpp index f50335c..3d803b4 100644 --- a/libs/kstd/include/kstd/bits/unique_ptr.hpp +++ b/libs/kstd/include/kstd/bits/unique_ptr.hpp @@ -46,10 +46,8 @@ namespace kstd template requires(std::is_convertible_v) unique_ptr(unique_ptr && other) noexcept - : pointer(other.pointer) - { - other.pointer = nullptr; - } + : pointer{std::exchange(other.pointer, nullptr)} + {} /** * @brief Deleted copy assignment operator to enforce unique ownership. @@ -62,10 +60,8 @@ namespace kstd * @param other Unique pointer to move from. */ unique_ptr(unique_ptr && other) noexcept - : pointer(other.pointer) - { - other.pointer = nullptr; - } + : pointer{std::exchange(other.pointer, nullptr)} + {} /** * @brief Move assignment operator. Transfers ownership from other to *this as if by calling reset(r.release()). @@ -78,8 +74,7 @@ namespace kstd if (this != &other) { delete pointer; - pointer = other.pointer; - other.pointer = nullptr; + pointer = std::exchange(other.pointer, nullptr); } return *this; } @@ -134,9 +129,7 @@ namespace kstd */ auto release() -> T * { - T * temp = pointer; - pointer = nullptr; - return temp; + return std::exchange(pointer, nullptr); } /** @@ -150,8 +143,7 @@ namespace kstd */ auto reset(T * ptr = nullptr) -> void { - delete pointer; - pointer = ptr; + delete std::exchange(pointer, ptr); } /** -- cgit v1.2.3 From ab4c59912c526d515e6e72188c08a7f92e5573e8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 15:07:54 +0200 Subject: x86_64: implement legacy PIT driver --- arch/x86_64/CMakeLists.txt | 3 ++ arch/x86_64/include/arch/devices/legacy_pit.hpp | 28 +++++++++++++ arch/x86_64/kapi/devices.cpp | 11 +++++ arch/x86_64/src/cpu/initialization.cpp | 2 +- arch/x86_64/src/devices/legacy_pit.cpp | 54 +++++++++++++++++++++++++ kapi/include/kapi/devices.hpp | 24 ++--------- kapi/include/kapi/devices/bus.hpp | 2 + kapi/include/kapi/devices/manager.hpp | 37 +++++++++++++++++ kernel/kapi/devices.cpp | 6 ++- kernel/src/main.cpp | 4 +- 10 files changed, 146 insertions(+), 25 deletions(-) create mode 100644 arch/x86_64/include/arch/devices/legacy_pit.hpp create mode 100644 arch/x86_64/src/devices/legacy_pit.cpp create mode 100644 kapi/include/kapi/devices/manager.hpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 21dceef..83cae0b 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -37,6 +37,9 @@ target_sources("x86_64" PRIVATE # Debug interfaces "src/debug/qemu_output.cpp" + # Devices + "src/devices/legacy_pit.cpp" + # Memory management "src/memory/kernel_mapper.cpp" "src/memory/higher_half_mapper.cpp" diff --git a/arch/x86_64/include/arch/devices/legacy_pit.hpp b/arch/x86_64/include/arch/devices/legacy_pit.hpp new file mode 100644 index 0000000..d28e4d6 --- /dev/null +++ b/arch/x86_64/include/arch/devices/legacy_pit.hpp @@ -0,0 +1,28 @@ +#ifndef TEACHOS_ARCH_X86_64_DEVICES_LEGACY_PIT_HPP +#define TEACHOS_ARCH_X86_64_DEVICES_LEGACY_PIT_HPP + +#include "kapi/devices/device.hpp" +#include "kapi/interrupts.hpp" + +#include + +namespace arch::devices +{ + + struct legacy_pit : kapi::devices::device, kapi::interrupts::handler + { + explicit legacy_pit(std::uint32_t frequency_in_hz); + + auto init() -> bool override; + + auto handle_interrupt(std::uint32_t irq_number) -> kapi::interrupts::status override; + + private: + std::uint32_t m_irq_number{}; + std::uint32_t m_frequency_in_hz{}; + std::uint64_t m_ticks{}; + }; + +} // namespace arch::devices + +#endif \ No newline at end of file diff --git a/arch/x86_64/kapi/devices.cpp b/arch/x86_64/kapi/devices.cpp index 25185d6..7aa7090 100644 --- a/arch/x86_64/kapi/devices.cpp +++ b/arch/x86_64/kapi/devices.cpp @@ -1,20 +1,31 @@ #include "kapi/devices.hpp" #include "arch/bus/isa.hpp" +#include "arch/devices/legacy_pit.hpp" #include #include +#include #include namespace kapi::devices { + namespace + { + constexpr auto pit_frequency_in_hz = std::uint32_t{100u}; + } + auto init_platform_devices() -> void { kstd::println("[x86_64:devices] Initializing ISA bus..."); + auto isa_bus = kstd::make_unique(); + auto pit = kstd::make_unique(pit_frequency_in_hz); + isa_bus->add_child(std::move(pit)); + auto & root_bus = get_root_bus(); root_bus.add_child(std::move(isa_bus)); } diff --git a/arch/x86_64/src/cpu/initialization.cpp b/arch/x86_64/src/cpu/initialization.cpp index 878fa07..b808c76 100644 --- a/arch/x86_64/src/cpu/initialization.cpp +++ b/arch/x86_64/src/cpu/initialization.cpp @@ -139,7 +139,7 @@ namespace arch::cpu constexpr auto pic_cascade_address = std::uint8_t{0x04}; constexpr auto pic_cascade_slave_identity = std::uint8_t{0x02}; constexpr auto pic_use_8086_mode = std::uint8_t{0x01}; - constexpr auto pic_master_mask = std::uint8_t{0x01}; + constexpr auto pic_master_mask = std::uint8_t{0x00}; constexpr auto pic_slave_mask = std::uint8_t{0x00}; pic_master_control_port::write(pic_init_command); diff --git a/arch/x86_64/src/devices/legacy_pit.cpp b/arch/x86_64/src/devices/legacy_pit.cpp new file mode 100644 index 0000000..f2fb70e --- /dev/null +++ b/arch/x86_64/src/devices/legacy_pit.cpp @@ -0,0 +1,54 @@ +#include "arch/devices/legacy_pit.hpp" + +#include "kapi/devices.hpp" +#include "kapi/devices/device.hpp" +#include "kapi/interrupts.hpp" + +#include "arch/device_io/port_io.hpp" + +#include + +namespace arch::devices +{ + + namespace + { + using command_port = io::port<0x43, std::uint8_t, io::port_write>; + using channel_0_port = io::port<0x40, std::uint8_t, io::port_write>; + using channel_1_port = io::port<0x41, std::uint8_t, io::port_write>; + using channel_2_port = io::port<0x42, std::uint8_t, io::port_write>; + } // namespace + + legacy_pit::legacy_pit(std::uint32_t frequency_in_hz) + : kapi::devices::device{kapi::devices::allocate_major_number(), 0, "legacy_pit"} + , m_irq_number{0} + , m_frequency_in_hz{frequency_in_hz} + {} + + auto legacy_pit::init() -> bool + { + constexpr auto base_frequency = 1'193'182u; + auto divisor = static_cast(base_frequency / m_frequency_in_hz); + + kapi::interrupts::register_handler(m_irq_number, *this); + + command_port::write(0x36); // NOLINT + channel_0_port::write(divisor & 0xff); // NOLINT + channel_0_port::write(divisor >> 8 & 0xff); // NOLINT + + return true; + } + + auto legacy_pit::handle_interrupt(std::uint32_t irq_number) -> kapi::interrupts::status + { + if (irq_number != m_irq_number) + { + return kapi::interrupts::status::unhandled; + } + + ++m_ticks; + + return kapi::interrupts::status::handled; + } + +} // namespace arch::devices \ No newline at end of file diff --git a/kapi/include/kapi/devices.hpp b/kapi/include/kapi/devices.hpp index 2028a64..5c01b2f 100644 --- a/kapi/include/kapi/devices.hpp +++ b/kapi/include/kapi/devices.hpp @@ -1,10 +1,9 @@ #ifndef TEACHOS_KAPI_DEVICES_HPP #define TEACHOS_KAPI_DEVICES_HPP -#include "kapi/devices/bus.hpp" // IWYU pragma: export -#include "kapi/devices/device.hpp" // IWYU pragma: export - -#include +#include "kapi/devices/bus.hpp" // IWYU pragma: export +#include "kapi/devices/device.hpp" // IWYU pragma: export +#include "kapi/devices/manager.hpp" // IWYU pragma: export namespace kapi::devices { @@ -22,23 +21,6 @@ namespace kapi::devices //! @return a reference to the root bus. auto get_root_bus() -> bus &; - //! Ask the kernel to allocate a new major number. - //! - //! @return a new, unused major number. - auto allocate_major_number() -> std::size_t; - - //! Register a new device with the kernel's device manager. - //! - //! @param device The device to register. - //! @return true if the device was registered successfully, false otherwise. - auto register_device(device & device) -> bool; - - //! Unregister a device from the kernel's device manager. - //! - //! @param device The device to unregister. - //! @return true if the device was unregistered successfully, false otherwise. - auto unregister_device(device & device) -> bool; - //! @} //! @addtogroup platform-defined diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index ccaf0f2..a5457e1 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -2,6 +2,7 @@ #define TEACHOS_KAPI_DEVICES_BUS_HPP #include "kapi/devices/device.hpp" +#include "kapi/devices/manager.hpp" #include "kapi/system.hpp" #include @@ -58,6 +59,7 @@ namespace kapi::devices { auto observer = m_observers.emplace_back(child.get()); m_devices.push_back(std::move(child)); + kapi::devices::register_device(*observer); if (m_initialized.test()) { diff --git a/kapi/include/kapi/devices/manager.hpp b/kapi/include/kapi/devices/manager.hpp new file mode 100644 index 0000000..56cd678 --- /dev/null +++ b/kapi/include/kapi/devices/manager.hpp @@ -0,0 +1,37 @@ +#ifndef TEACHOS_KAPI_DEVICES_MANAGER_HPP +#define TEACHOS_KAPI_DEVICES_MANAGER_HPP + +// IWYU pragma: private, include "kapi/devices.hpp" + +#include "kapi/devices/device.hpp" + +#include + +namespace kapi::devices +{ + + //! @addtogroup kernel-defined + //! @{ + + //! Ask the kernel to allocate a new major number. + //! + //! @return a new, unused major number. + auto allocate_major_number() -> std::size_t; + + //! Register a new device with the kernel's device manager. + //! + //! @param device The device to register. + //! @return true if the device was registered successfully, false otherwise. + auto register_device(device & device) -> bool; + + //! Unregister a device from the kernel's device manager. + //! + //! @param device The device to unregister. + //! @return true if the device was unregistered successfully, false otherwise. + auto unregister_device(device & device) -> bool; + + //! @} + +} // namespace kapi::devices + +#endif \ No newline at end of file diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index dbf5e68..c0b738e 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -31,8 +31,9 @@ namespace kapi::devices return; } - root_bus.emplace(); - root_bus->init(); + auto & bus = root_bus.emplace(); + register_device(bus); + bus.init(); } auto get_root_bus() -> bus & @@ -51,6 +52,7 @@ namespace kapi::devices auto register_device(device & device) -> bool { + kstd::println("[OS:DEV] Registering device {}@{}:{}", device.name(), device.major(), device.minor()); return device_tree.emplace(std::pair{device.major(), device.minor()}, &device).second; } diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index eaaf87f..2eaa2d8 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -173,7 +173,6 @@ auto main() -> int kstd::println("[OS] IO subsystem initialized."); kapi::cpu::init(); - kapi::interrupts::enable(); kapi::memory::init(); kernel::memory::init_heap(kapi::memory::heap_base); @@ -186,6 +185,9 @@ auto main() -> int kapi::devices::init_platform_devices(); kstd::println("[OS] Platform devices initialized."); + kapi::interrupts::enable(); + kstd::println("[OS] Interrupts enabled."); + kapi::boot_modules::init(); kstd::println("[OS] Boot module registry initialized."); -- cgit v1.2.3 From 3e80b6baa8f9666a9dd3cd4531bc68a3de4fee92 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 15:18:05 +0200 Subject: kapi: allow for device searches --- kapi/include/kapi/devices/manager.hpp | 16 ++++++++++++++++ kernel/kapi/devices.cpp | 22 ++++++++++++++++++++++ libs/kstd/include/kstd/bits/flat_map.hpp | 2 +- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/kapi/include/kapi/devices/manager.hpp b/kapi/include/kapi/devices/manager.hpp index 56cd678..7817fbc 100644 --- a/kapi/include/kapi/devices/manager.hpp +++ b/kapi/include/kapi/devices/manager.hpp @@ -5,7 +5,10 @@ #include "kapi/devices/device.hpp" +#include + #include +#include namespace kapi::devices { @@ -30,6 +33,19 @@ namespace kapi::devices //! @return true if the device was unregistered successfully, false otherwise. auto unregister_device(device & device) -> bool; + //! Find a device by its major and minor numbers. + //! + //! @param major the major number of the device. + //! @param minor the minor number of the device. + //! @return a pointer to the device iff. the device was found, nullptr otherwise. + auto find_device(std::size_t major, std::size_t minor) -> kstd::observer_ptr; + + //! Find a device by its name. + //! + //! @param name the name of the device. + //! @return a pointer to the device iff. the device was found, nullptr otherwise. + auto find_device(std::string_view name) -> kstd::observer_ptr; + //! @} } // namespace kapi::devices diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index c0b738e..7d35778 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace kapi::devices @@ -62,4 +63,25 @@ namespace kapi::devices return false; } + auto find_device(std::size_t major, std::size_t minor) -> kstd::observer_ptr + { + if (device_tree.contains(std::pair{major, minor})) + { + return device_tree.at(std::pair{major, minor}); + } + return nullptr; + } + + auto find_device(std::string_view name) -> kstd::observer_ptr + { + for (auto const & [key, value] : device_tree) + { + if (value->name() == name) + { + return value; + } + } + return nullptr; + } + } // namespace kapi::devices \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/flat_map.hpp b/libs/kstd/include/kstd/bits/flat_map.hpp index 9455549..fe46203 100644 --- a/libs/kstd/include/kstd/bits/flat_map.hpp +++ b/libs/kstd/include/kstd/bits/flat_map.hpp @@ -45,7 +45,7 @@ namespace kstd::bits template requires(Index >= 0 && Index <= 1) - constexpr auto get() const noexcept -> decltype(auto) + [[nodiscard]] constexpr auto get() const noexcept -> decltype(auto) { if constexpr (Index == 0) { -- cgit v1.2.3 From 33b43603936ed0108d67853727a17d6b3740b445 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 15:43:34 +0200 Subject: kapi/bus: ensure all devices get initialized --- kapi/include/kapi/devices/bus.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index a5457e1..ee774b7 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -46,7 +46,7 @@ namespace kapi::devices return std::ranges::fold_left(m_devices, true, [&](bool acc, auto & child) -> bool { kstd::println("[kAPI:BUS] Initializing child device {}@{}", child->name(), name()); - return acc && child->init(); + return child->init() && acc; }); } -- cgit v1.2.3 From 21489576381d827871e7cdf060929c5d7f3d4e9f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 15:49:14 +0200 Subject: devices: don't automatically allocate major numbers in ctors --- arch/x86_64/include/arch/bus/isa.hpp | 4 +++- arch/x86_64/include/arch/devices/legacy_pit.hpp | 3 ++- arch/x86_64/kapi/devices.cpp | 6 ++++-- arch/x86_64/src/bus/isa.cpp | 6 ++++-- arch/x86_64/src/devices/legacy_pit.cpp | 5 +++-- kernel/kapi/devices.cpp | 2 +- kernel/src/devices/root_bus.cpp | 2 +- 7 files changed, 18 insertions(+), 10 deletions(-) diff --git a/arch/x86_64/include/arch/bus/isa.hpp b/arch/x86_64/include/arch/bus/isa.hpp index bd92b2e..5deed25 100644 --- a/arch/x86_64/include/arch/bus/isa.hpp +++ b/arch/x86_64/include/arch/bus/isa.hpp @@ -3,12 +3,14 @@ #include "kapi/devices/bus.hpp" +#include + namespace arch::bus { struct isa final : public kapi::devices::bus { - isa(); + isa(std::size_t major); }; } // namespace arch::bus diff --git a/arch/x86_64/include/arch/devices/legacy_pit.hpp b/arch/x86_64/include/arch/devices/legacy_pit.hpp index d28e4d6..de742ae 100644 --- a/arch/x86_64/include/arch/devices/legacy_pit.hpp +++ b/arch/x86_64/include/arch/devices/legacy_pit.hpp @@ -4,6 +4,7 @@ #include "kapi/devices/device.hpp" #include "kapi/interrupts.hpp" +#include #include namespace arch::devices @@ -11,7 +12,7 @@ namespace arch::devices struct legacy_pit : kapi::devices::device, kapi::interrupts::handler { - explicit legacy_pit(std::uint32_t frequency_in_hz); + legacy_pit(std::size_t major, std::uint32_t frequency_in_hz); auto init() -> bool override; diff --git a/arch/x86_64/kapi/devices.cpp b/arch/x86_64/kapi/devices.cpp index 7aa7090..b15503d 100644 --- a/arch/x86_64/kapi/devices.cpp +++ b/arch/x86_64/kapi/devices.cpp @@ -21,9 +21,11 @@ namespace kapi::devices { kstd::println("[x86_64:devices] Initializing ISA bus..."); - auto isa_bus = kstd::make_unique(); + auto isa_major_number = kapi::devices::allocate_major_number(); + auto isa_bus = kstd::make_unique(isa_major_number); - auto pit = kstd::make_unique(pit_frequency_in_hz); + auto pit_major_number = kapi::devices::allocate_major_number(); + auto pit = kstd::make_unique(pit_major_number, pit_frequency_in_hz); isa_bus->add_child(std::move(pit)); auto & root_bus = get_root_bus(); diff --git a/arch/x86_64/src/bus/isa.cpp b/arch/x86_64/src/bus/isa.cpp index 2ad4d21..ff4ad71 100644 --- a/arch/x86_64/src/bus/isa.cpp +++ b/arch/x86_64/src/bus/isa.cpp @@ -2,11 +2,13 @@ #include "kapi/devices.hpp" +#include + namespace arch::bus { - isa::isa() - : kapi::devices::bus(kapi::devices::allocate_major_number(), 0, "isa") + isa::isa(std::size_t major) + : kapi::devices::bus{major, 0, "isa"} {} } // namespace arch::bus \ No newline at end of file diff --git a/arch/x86_64/src/devices/legacy_pit.cpp b/arch/x86_64/src/devices/legacy_pit.cpp index f2fb70e..970f538 100644 --- a/arch/x86_64/src/devices/legacy_pit.cpp +++ b/arch/x86_64/src/devices/legacy_pit.cpp @@ -6,6 +6,7 @@ #include "arch/device_io/port_io.hpp" +#include #include namespace arch::devices @@ -19,8 +20,8 @@ namespace arch::devices using channel_2_port = io::port<0x42, std::uint8_t, io::port_write>; } // namespace - legacy_pit::legacy_pit(std::uint32_t frequency_in_hz) - : kapi::devices::device{kapi::devices::allocate_major_number(), 0, "legacy_pit"} + legacy_pit::legacy_pit(std::size_t major, std::uint32_t frequency_in_hz) + : kapi::devices::device{major, 0, "legacy_pit"} , m_irq_number{0} , m_frequency_in_hz{frequency_in_hz} {} diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index 7d35778..031f2c9 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -19,7 +19,7 @@ namespace kapi::devices namespace { - auto constinit next_major_number = std::atomic_size_t{0}; + auto constinit next_major_number = std::atomic_size_t{1}; auto constinit root_bus = std::optional{}; auto constinit device_tree = kstd::flat_map, kstd::observer_ptr>{}; } // namespace diff --git a/kernel/src/devices/root_bus.cpp b/kernel/src/devices/root_bus.cpp index d3ba23f..43a35bf 100644 --- a/kernel/src/devices/root_bus.cpp +++ b/kernel/src/devices/root_bus.cpp @@ -6,7 +6,7 @@ namespace kernel::devices { root_bus::root_bus() - : kapi::devices::bus{kapi::devices::allocate_major_number(), 0, "system"} + : kapi::devices::bus{0, 0, "system"} {} } // namespace kernel::devices \ No newline at end of file -- cgit v1.2.3 From c5afb5c1ce1c084c840dbb58d73af6fe2b235ec7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 15:55:47 +0200 Subject: x86_64: ensure PIT is not overwhelmed on config --- .clang-tidy | 2 +- arch/x86_64/include/arch/device_io/port_io.hpp | 5 +++++ arch/x86_64/src/devices/legacy_pit.cpp | 13 +++++++++---- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 8fa3943..61ae9c9 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -60,7 +60,7 @@ CheckOptions: modernize-use-std-print.ReplacementPrintlnFunction: 'kstd::println' modernize-use-std-print.PrintHeader: 'kstd/print' modernize-use-trailing-return-type.TransformLambdas: none - readability-magic-numbers.IgnoredIntegerValues: '1;2;3;4;5;6;7;10' + readability-magic-numbers.IgnoredIntegerValues: '1;2;3;4;5;6;7;10;255' readability-magic-numbers.IgnorePowersOf2IntegerValues: true readability-magic-numbers.IgnoreBitFieldsWidths: true readability-magic-numbers.IgnoreTypeAliases: true diff --git a/arch/x86_64/include/arch/device_io/port_io.hpp b/arch/x86_64/include/arch/device_io/port_io.hpp index 70773dd..4c8d66a 100644 --- a/arch/x86_64/include/arch/device_io/port_io.hpp +++ b/arch/x86_64/include/arch/device_io/port_io.hpp @@ -102,6 +102,11 @@ namespace arch::io : std::string_view{"eax"}; }; + auto inline wait() -> void + { + port<0x80, std::uint8_t, port_write>::write(0); + } + } // namespace arch::io #endif \ No newline at end of file diff --git a/arch/x86_64/src/devices/legacy_pit.cpp b/arch/x86_64/src/devices/legacy_pit.cpp index 970f538..a8df3c3 100644 --- a/arch/x86_64/src/devices/legacy_pit.cpp +++ b/arch/x86_64/src/devices/legacy_pit.cpp @@ -18,6 +18,9 @@ namespace arch::devices using channel_0_port = io::port<0x40, std::uint8_t, io::port_write>; using channel_1_port = io::port<0x41, std::uint8_t, io::port_write>; using channel_2_port = io::port<0x42, std::uint8_t, io::port_write>; + + constexpr auto base_frequency = 1'193'182u; + constexpr auto square_wave_mode = 0x36; } // namespace legacy_pit::legacy_pit(std::size_t major, std::uint32_t frequency_in_hz) @@ -28,14 +31,16 @@ namespace arch::devices auto legacy_pit::init() -> bool { - constexpr auto base_frequency = 1'193'182u; auto divisor = static_cast(base_frequency / m_frequency_in_hz); kapi::interrupts::register_handler(m_irq_number, *this); - command_port::write(0x36); // NOLINT - channel_0_port::write(divisor & 0xff); // NOLINT - channel_0_port::write(divisor >> 8 & 0xff); // NOLINT + command_port::write(square_wave_mode); + io::wait(); + channel_0_port::write(divisor & 0xff); + io::wait(); + channel_0_port::write(divisor >> 8 & 0xff); + io::wait(); return true; } -- cgit v1.2.3 From 4cce84317474dd14da806d3ddc7f69ef11356b5f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 2 Apr 2026 20:04:07 +0200 Subject: docs: begin breathe documentation --- docs/conf.py | 7 ++++--- docs/index.rst | 2 +- docs/kapi.rst | 10 ++++++++++ docs/kapi/cio.rst | 17 +++++++++++++++++ docs/kapi/cpu.rst | 17 +++++++++++++++++ docs/kapi/devices.rst | 17 +++++++++++++++++ docs/kapi/interrupts.rst | 17 +++++++++++++++++ docs/kapi/memory.rst | 14 ++++++++++++++ docs/kapi/system.rst | 14 ++++++++++++++ docs/requirements.txt | 5 +++-- kapi/include/kapi/cio.hpp | 27 +++++++++++++++++++-------- kapi/include/kapi/cpu.hpp | 28 ++++++++++++++++++++-------- kapi/include/kapi/devices.hpp | 4 ++-- kapi/include/kapi/devices/bus.hpp | 7 +++++++ kapi/include/kapi/devices/device.hpp | 7 +++++++ kapi/include/kapi/devices/manager.hpp | 2 +- kapi/include/kapi/interrupts.hpp | 31 ++++++++++++++++++++++--------- kapi/include/kapi/memory.hpp | 33 +++++++++++++++++---------------- kapi/include/kapi/system.hpp | 12 ++++++++++-- kapi/kapi.dox | 8 ++++++++ 20 files changed, 227 insertions(+), 52 deletions(-) create mode 100644 docs/kapi.rst create mode 100644 docs/kapi/cio.rst create mode 100644 docs/kapi/cpu.rst create mode 100644 docs/kapi/devices.rst create mode 100644 docs/kapi/interrupts.rst create mode 100644 docs/kapi/memory.rst create mode 100644 docs/kapi/system.rst create mode 100644 kapi/kapi.dox diff --git a/docs/conf.py b/docs/conf.py index b8cfe69..99613b6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,7 +13,7 @@ author = "Felix Morgner" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration -#extensions = ["breathe"] +extensions = ["breathe"] templates_path = ["_templates"] exclude_patterns = ["pre/**"] @@ -21,8 +21,9 @@ exclude_patterns = ["pre/**"] # -- Options Breathe --------------------------------------------------------- # https://breathe.readthedocs.io/en/stable/directives.html#config-values -#breathe_projects = {"kernel": "../build/doxygen/xml"} -#breathe_default_project = "kernel" +breathe_projects = {"kernel": "../build/doxygen/xml"} +breathe_default_project = "kernel" +breathe_default_members = ('members', 'undoc-members', 'private-members') # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output diff --git a/docs/index.rst b/docs/index.rst index 649e6de..425da57 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,10 +6,10 @@ Welcome to TeachOS Kernel's documentation! :caption: Contents: briefs + kapi Indices and tables ================== * :ref:`genindex` -* :ref:`modindex` * :ref:`search` diff --git a/docs/kapi.rst b/docs/kapi.rst new file mode 100644 index 0000000..4c60ab3 --- /dev/null +++ b/docs/kapi.rst @@ -0,0 +1,10 @@ +Kernel API +========== + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + :glob: + + kapi/* + diff --git a/docs/kapi/cio.rst b/docs/kapi/cio.rst new file mode 100644 index 0000000..62a3639 --- /dev/null +++ b/docs/kapi/cio.rst @@ -0,0 +1,17 @@ +Character I/O Interface +======================= + +.. doxygengroup:: kapi-cio + :content-only: + +Kernel-defined API +------------------ + +.. doxygengroup:: kapi-cio-kernel-defined + :content-only: + +Platform-defined API +-------------------- + +.. doxygengroup:: kapi-cio-platform-defined + :content-only: diff --git a/docs/kapi/cpu.rst b/docs/kapi/cpu.rst new file mode 100644 index 0000000..0793dd8 --- /dev/null +++ b/docs/kapi/cpu.rst @@ -0,0 +1,17 @@ +CPU Interface +============= + +.. doxygengroup:: kapi-cpu + :content-only: + +Kernel-defined API +------------------ + +.. doxygengroup:: kapi-cpu-kernel-defined + :content-only: + +Platform-defined API +-------------------- + +.. doxygengroup:: kapi-cpu-platform-defined + :content-only: diff --git a/docs/kapi/devices.rst b/docs/kapi/devices.rst new file mode 100644 index 0000000..c9853a1 --- /dev/null +++ b/docs/kapi/devices.rst @@ -0,0 +1,17 @@ +Device Interface +================ + +.. doxygengroup:: kapi-devices + :content-only: + +Kernel-defined API +------------------ + +.. doxygengroup:: kapi-devices-kernel-defined + :content-only: + +Platform-defined API +-------------------- + +.. doxygengroup:: kapi-devices-platform-defined + :content-only: diff --git a/docs/kapi/interrupts.rst b/docs/kapi/interrupts.rst new file mode 100644 index 0000000..7150f07 --- /dev/null +++ b/docs/kapi/interrupts.rst @@ -0,0 +1,17 @@ +Interrupt Interface +=================== + +.. doxygengroup:: kapi-interrupts + :content-only: + +Kernel-defined API +------------------ + +.. doxygengroup:: kapi-interrupts-kernel-defined + :content-only: + +Platform-defined API +-------------------- + +.. doxygengroup:: kapi-interrupts-platform-defined + :content-only: diff --git a/docs/kapi/memory.rst b/docs/kapi/memory.rst new file mode 100644 index 0000000..9ee1584 --- /dev/null +++ b/docs/kapi/memory.rst @@ -0,0 +1,14 @@ +Memory Interface +================ + +Kernel-defined API +------------------ + +.. doxygengroup:: kapi-memory-kernel-defined + :content-only: + +Platform-defined API +-------------------- + +.. doxygengroup:: kapi-memory-platform-defined + :content-only: diff --git a/docs/kapi/system.rst b/docs/kapi/system.rst new file mode 100644 index 0000000..2eaea6d --- /dev/null +++ b/docs/kapi/system.rst @@ -0,0 +1,14 @@ +System Interface +================ + +Kernel-defined API +------------------ + +.. doxygengroup:: kapi-system-kernel-defined + :content-only: + +Platform-defined API +-------------------- + +.. doxygengroup:: kapi-system-platform-defined + :content-only: diff --git a/docs/requirements.txt b/docs/requirements.txt index 733e873..fc08790 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,3 @@ -Sphinx~=8.2.0 -sphinx_book_theme~=1.1.0 +Sphinx~=9.1.0 +sphinx_book_theme~=1.2.0 +breathe~=4.36.0 \ No newline at end of file diff --git a/kapi/include/kapi/cio.hpp b/kapi/include/kapi/cio.hpp index 71b5b02..8941a9f 100644 --- a/kapi/include/kapi/cio.hpp +++ b/kapi/include/kapi/cio.hpp @@ -11,27 +11,38 @@ namespace kapi::cio { - //! @qualifier platform-defined - //! Initialize the character I/O subsystem. - //! - //! @note If a platform support character output, it shall ensure that when this function returns, basic character - //! output can be performed on the system. - auto init() -> void; + //! @addtogroup kapi-cio + //! @{ + //! @} + + //! @addtogroup kapi-cio-kernel-defined + //! @{ - //! @qualifier kernel-defined //! Set the currently active output device. //! //! @param device A new output device. //! @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; + //! @} + + //! @addtogroup kapi-cio-platform-defined + //! @{ + + //! Initialize the character I/O subsystem. + //! + //! If a platform support character output, it shall ensure that when this function returns, basic character + //! output can be performed on the system. + auto init() -> void; + + //! @} + } // namespace kapi::cio #endif diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index c6aa6ff..d90365a 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -10,6 +10,9 @@ namespace kapi::cpu { + //! @addtogroup kapi-cpu + //! @{ + //! An exception originating from the CPU directly. //! //! Exception generally model interrupts that are synchronous to the instruction stream. This means that they do not @@ -58,25 +61,34 @@ namespace kapi::cpu bool is_user_mode{}; }; - //! @qualifier platform-defined + //! @} + + //! @addtogroup kapi-cpu-kernel-defined + //! @{ + + //! Dispatch an exception to the appropriate handler. + //! + //! @param context The exception context. + //! @return Whether the exception was handled. + [[nodiscard]] auto dispatch(exception const & context) -> bool; + + //! @} + + //! @addtogroup kapi-cpu-platform-defined + //! @{ + //! Halt the CPU. //! //! This function terminates execution of the kernel. [[noreturn]] auto halt() -> void; - //! @qualifier platform-defined //! Perform early CPU initialization. //! //! When this function returns, the CPU is in a state where interrupt could be enabled. This function must not enable //! interrupts itself. auto init() -> void; - //! @qualifier kernel-defined - //! Dispatch an exception to the appropriate handler. - //! - //! @param context The exception context. - //! @return Whether the exception was handled. - [[nodiscard]] auto dispatch(exception const & context) -> bool; + //! @} } // namespace kapi::cpu diff --git a/kapi/include/kapi/devices.hpp b/kapi/include/kapi/devices.hpp index 5c01b2f..c26efb9 100644 --- a/kapi/include/kapi/devices.hpp +++ b/kapi/include/kapi/devices.hpp @@ -8,7 +8,7 @@ namespace kapi::devices { - //! @addtogroup kernel-defined + //! @addtogroup kapi-devices-kernel-defined //! @{ //! Initialize the kernel's device management subsystem. @@ -23,7 +23,7 @@ namespace kapi::devices //! @} - //! @addtogroup platform-defined + //! @addtogroup kapi-devices-platform-defined //! @{ //! Initialize the platform's device tree. diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index ee774b7..052c062 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -17,6 +17,10 @@ namespace kapi::devices { + + //! @addtogroup kapi-devices + //! @{ + //! A bus device that represents a logical/physical tree of devices and busses. struct bus : device { @@ -90,6 +94,9 @@ namespace kapi::devices kstd::vector> m_observers; std::atomic_flag m_initialized{}; }; + + //! @} + } // namespace kapi::devices #endif \ No newline at end of file diff --git a/kapi/include/kapi/devices/device.hpp b/kapi/include/kapi/devices/device.hpp index a049cf5..b7b6bba 100644 --- a/kapi/include/kapi/devices/device.hpp +++ b/kapi/include/kapi/devices/device.hpp @@ -7,6 +7,10 @@ namespace kapi::devices { + + //! @addtogroup kapi-devices + //! @{ + /** * @brief Base device identified by a major, minor number and name. */ @@ -76,6 +80,9 @@ namespace kapi::devices size_t m_minor; kstd::string m_name; }; + + //! @} + } // namespace kapi::devices #endif \ No newline at end of file diff --git a/kapi/include/kapi/devices/manager.hpp b/kapi/include/kapi/devices/manager.hpp index 7817fbc..f19366e 100644 --- a/kapi/include/kapi/devices/manager.hpp +++ b/kapi/include/kapi/devices/manager.hpp @@ -13,7 +13,7 @@ namespace kapi::devices { - //! @addtogroup kernel-defined + //! @addtogroup kapi-devices-kernel-defined //! @{ //! Ask the kernel to allocate a new major number. diff --git a/kapi/include/kapi/interrupts.hpp b/kapi/include/kapi/interrupts.hpp index f72ef8c..4ba0684 100644 --- a/kapi/include/kapi/interrupts.hpp +++ b/kapi/include/kapi/interrupts.hpp @@ -6,9 +6,15 @@ namespace kapi::interrupts { + //! @addtogroup kapi-interrupts + //! @{ + + //! A status that indicates whether an interrupt was handled by a handler. enum class status : bool { + //! The interrupt was not handled by any handler. unhandled, + //! The interrupt was handled by at least one handler. handled, }; @@ -27,35 +33,42 @@ namespace kapi::interrupts virtual auto handle_interrupt(std::uint32_t irq_number) -> status = 0; }; - //! @qualifier platform-defined - //! Enable external interrupts. - auto enable() -> void; + //! @} - //! @qualifier platform-defined - //! Disable external interrupts. - auto disable() -> void; + //! @addtogroup kapi-interrupts-kernel-defined + //! @{ - //! @qualifier kernel-defined //! Register an interrupt handler for the given IRQ number. //! //! @param irq_number The IRQ number to register the handler for. //! @param handler The interrupt handler to register. auto register_handler(std::uint32_t irq_number, handler & handler) -> void; - //! @qualifier kernel-defined //! Unregister a previously registered interrupt handler. //! //! @param irq_number The IRQ number to unregister the handler for. //! @param handler The interrupt handler to unregister. auto unregister_handler(std::uint32_t irq_number, handler & handler) -> void; - //! @qualifier kernel-defined //! Dispatch an interrupt to all registered handlers. //! //! @param irq_number The IRQ number to dispatch. //! @return status::handled if the interrupt was handled by at least one handler, status::unhandled otherwise. auto dispatch(std::uint32_t irq_number) -> status; + //! @} + + //! @addtogroup kapi-interrupts-platform-defined + //! @{ + + //! Enable external interrupts. + auto enable() -> void; + + //! Disable external interrupts. + auto disable() -> void; + + //! @} + } // namespace kapi::interrupts #endif \ No newline at end of file diff --git a/kapi/include/kapi/memory.hpp b/kapi/include/kapi/memory.hpp index e31fa34..914ca61 100644 --- a/kapi/include/kapi/memory.hpp +++ b/kapi/include/kapi/memory.hpp @@ -16,16 +16,9 @@ namespace kapi::memory { - //! @qualifier platform-defined - //! Initialize the memory subsystem. - //! - //! @note This function must be implemented by the target platform. - //! - //! This function initializes the memory subsystem and activates the platform-specific frame allocator and page - //! mapper. When this function returns, a valid frame allocator and page mapper are expected to have been registered. - auto init() -> void; + //! @addtogroup kapi-memory-kernel-defined + //! @{ - //! @qualifier kernel-defined //! Initialize the physical memory manager. //! //! This function initializes the kernel-wide physical memory manager. The function will invoke the handoff handler to @@ -39,25 +32,21 @@ namespace kapi::memory //! allocator to hand off to is passed to the handler. auto init_pmm(std::size_t frame_count, void (&handoff_handler)(frame_allocator &)) -> void; - //! @qualifier kernel-defined //! Get the currently active frame allocator. auto get_frame_allocator() -> frame_allocator &; - //! @qualifier kernel-defined //! Set the currently active frame allocator. //! //! @param allocator A new frame allocator. //! @return The previously active frame allocator. auto set_frame_allocator(frame_allocator & allocator) -> std::optional; - //! @qualifier kernel-defined //! Set the currently active page mapper. //! //! @param mapper A new page mapper. //! @return The previously active page mapper. auto set_page_mapper(page_mapper & mapper) -> std::optional; - //! @qualifier kernel-defined //! Allocate a new frame of physical memory //! //! @warning This function will panic if no frame allocator has been registered. @@ -65,7 +54,6 @@ namespace kapi::memory //! @return An engaged std::optional iff. a frame could be allocated, std::nullopt otherwise. auto allocate_frame() -> std::optional; - //! @qualifier kernel-defined //! Allocate multiple new frames of physical memory //! //! @warning This function will panic if no frame allocator has been registered. @@ -73,7 +61,6 @@ namespace kapi::memory //! @return An engaged std::optional iff. @p count frames could be allocated, std::nullopt otherwise. auto allocate_many_frames(std::size_t count) -> std::optional>; - //! @qualifier kernel-defined //! Map a page onto a frame. //! //! @warning This function will panic if no page mapper has been registered, or the page has already been mapped. @@ -85,7 +72,6 @@ namespace kapi::memory //! @return A pointer to the first byte of the mapped page. auto map(page page, frame frame, page_mapper::flags flags = page_mapper::flags::empty) -> std::byte *; - //! @qualifier kernel-defined //! Unmap a page. //! //! @warning This function will panic if no page mapper has been registered, or the page is not mapped. @@ -93,6 +79,21 @@ namespace kapi::memory //! @param page The page to unmap auto unmap(page page) -> void; + //! @} + + //! @addtogroup kapi-memory-platform-defined + //! @{ + + //! Initialize the memory subsystem. + //! + //! @note This function must be implemented by the target platform. + //! + //! This function initializes the memory subsystem and activates the platform-specific frame allocator and page + //! mapper. When this function returns, a valid frame allocator and page mapper are expected to have been registered. + auto init() -> void; + + //! @} + } // namespace kapi::memory #endif diff --git a/kapi/include/kapi/system.hpp b/kapi/include/kapi/system.hpp index e5c43c5..8a20af9 100644 --- a/kapi/include/kapi/system.hpp +++ b/kapi/include/kapi/system.hpp @@ -7,7 +7,9 @@ namespace kapi::system { - //! @qualifier kernel-defined + //! @addtogroup kapi-system-kernel-defined + //! @{ + //! Terminate kernel execution with the given error message. //! //! This function terminates the execution of the kernel and attempts to issue the given error message to the user. @@ -15,10 +17,16 @@ namespace kapi::system //! @param message The message associated with the panic [[noreturn]] auto panic(std::string_view message, std::source_location = std::source_location::current()) -> void; - //! @qualifier platform-defined + //! @} // end group kernel-defined + + //! @addtogroup kapi-system-platform-defined + //! @{ + //! A hook that runs once the memory subsystem has been initialized. auto memory_initialized() -> void; + //! @} // end group platform-defined + } // namespace kapi::system #endif diff --git a/kapi/kapi.dox b/kapi/kapi.dox new file mode 100644 index 0000000..929fc1f --- /dev/null +++ b/kapi/kapi.dox @@ -0,0 +1,8 @@ +//! @namespace kapi +//! The Kernel/Platform API +//! +//! This namespace defines the interface between the platform independent kernel and each supported platform. + +//! @defgroup kapi-kernel-defined Kernel-defined API + +//! @defgroup kapi-platform-defined Platform-defined API -- cgit v1.2.3 From 60162cfa17e94ea4e4cefa9479646266ffb1dee2 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 22:19:28 +0200 Subject: add windows development notes --- README.rst | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 716013d..9be2aea 100644 --- a/README.rst +++ b/README.rst @@ -45,4 +45,16 @@ The default build target generates a bootable image. On x86-64 for example, this image takes the form of a bootable, grub2 based ISO image. These images are designed to be booted in QEMU, and should theoretically also be bootable on real hardware. However, note that not warranty is provided, and the kernel code may irreparably destroy any physical hardware if booted on a real system. -The VSCodium IDE configuration provides a launch task using QEMU, available for debugging (via F5) and direct launch as a task. \ No newline at end of file +The VSCodium IDE configuration provides a launch task using QEMU, available for debugging (via F5) and direct launch as a task. + +Notes for Development under Windows +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While this repository and the devcontainer can be used from Windows, significant performance issues may occur. +To reduce these issues, you can either set up a Linux VM for development, or, if that is not preferred, clone the repository inside WSL and open it from there with Visual Studio Code. + +``git clone `` +``cd `` +``code .`` + +If you use tools such as Git Extensions or GitHub Desktop, access the repository via the WSL network path, for example ``\\wsl.localhost\\``. -- cgit v1.2.3 From ddc4931d326b0bd4046f6731cc543886c557def3 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 22:27:44 +0200 Subject: fix styling --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 9be2aea..c850c7f 100644 --- a/README.rst +++ b/README.rst @@ -48,13 +48,13 @@ However, note that not warranty is provided, and the kernel code may irreparably The VSCodium IDE configuration provides a launch task using QEMU, available for debugging (via F5) and direct launch as a task. Notes for Development under Windows -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ While this repository and the devcontainer can be used from Windows, significant performance issues may occur. To reduce these issues, you can either set up a Linux VM for development, or, if that is not preferred, clone the repository inside WSL and open it from there with Visual Studio Code. -``git clone `` -``cd `` -``code .`` +| ``git clone `` +| ``cd `` +| ``code .`` If you use tools such as Git Extensions or GitHub Desktop, access the repository via the WSL network path, for example ``\\wsl.localhost\\``. -- cgit v1.2.3 From bd585306e31889ee4fce60abb79bc3b3a58e2b84 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 13:11:15 +0200 Subject: kapi: add basic ACPI support --- kapi/CMakeLists.txt | 8 ++- kapi/include/kapi/acpi.hpp | 64 ++++++++++++++++++++ kapi/src/acpi.cpp | 68 ++++++++++++++++++++++ .../include/kernel/memory/block_list_allocator.hpp | 10 ++-- libs/kstd/include/kstd/units | 7 ++- .../multiboot2/constants/information_id.hpp | 8 +-- libs/multiboot2/include/multiboot2/information.hpp | 43 +++++++++++++- .../include/multiboot2/information/data.hpp | 12 +++- 8 files changed, 205 insertions(+), 15 deletions(-) create mode 100644 kapi/include/kapi/acpi.hpp create mode 100644 kapi/src/acpi.cpp diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index c9aa23f..eeda158 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -1,4 +1,6 @@ -add_library("kapi" INTERFACE) +add_library("kapi" STATIC + "src/acpi.cpp" +) add_library("os::kapi" ALIAS "kapi") file(GLOB_RECURSE KAPI_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") @@ -10,11 +12,11 @@ target_sources("kapi" PUBLIC ${KAPI_HEADERS} ) -target_include_directories("kapi" INTERFACE +target_include_directories("kapi" PUBLIC "include" ) -target_link_libraries("kapi" INTERFACE +target_link_libraries("kapi" PUBLIC "libs::kstd" "gcc" diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp new file mode 100644 index 0000000..20e5e77 --- /dev/null +++ b/kapi/include/kapi/acpi.hpp @@ -0,0 +1,64 @@ +#ifndef TEACHOS_KAPI_ACPI_HPP +#define TEACHOS_KAPI_ACPI_HPP + +#include "kapi/memory.hpp" +#include + +#include +#include +#include +#include + +namespace kapi::acpi +{ + + //! @addtogroup kapi-acpi-api-defined + //! @{ + + struct [[gnu::packed]] root_system_description_pointer + { + [[nodiscard]] auto oem_id() const noexcept -> std::string_view; + [[nodiscard]] auto revision() const noexcept -> std::uint8_t; + [[nodiscard]] auto signature() const noexcept -> std::string_view; + [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; + [[nodiscard]] auto validate() const noexcept -> bool; + + private: + std::array m_signature; + [[maybe_unused]] std::uint8_t m_checksum; + std::array m_oem_id; + std::uint8_t m_revision; + std::array m_rsdt_address; + }; + + struct [[gnu::packed]] extended_system_description_pointer : root_system_description_pointer + { + [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; + [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; + [[nodiscard]] auto validate() const noexcept -> bool; + + private: + std::uint32_t m_length; + std::array m_xsdt_address; + [[maybe_unused]] std::uint8_t m_extended_checksum; + [[maybe_unused]] std::array m_reserved; + }; + + //! @} + + struct [[gnu::packed]] system_description_table_header + { + std::array signature; + std::uint32_t length; + std::uint8_t revision; + std::uint8_t checksum; + std::array oem_id; + std::array oem_table_id; + std::uint32_t oem_revision; + std::uint32_t creator_id; + std::uint32_t create_revision; + }; + +} // namespace kapi::acpi + +#endif diff --git a/kapi/src/acpi.cpp b/kapi/src/acpi.cpp new file mode 100644 index 0000000..aa0066d --- /dev/null +++ b/kapi/src/acpi.cpp @@ -0,0 +1,68 @@ +#include "kapi/acpi.hpp" + +#include "kapi/memory.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +namespace kapi::acpi +{ + + namespace + { + constexpr auto validate_checksum(std::span data) -> bool + { + auto sum = std::ranges::fold_left( + data, std::uint8_t{}, [](std::uint8_t acc, auto byte) { return static_cast(byte) + acc; }); + return sum == 0; + } + } // namespace + + auto root_system_description_pointer::oem_id() const noexcept -> std::string_view + { + return {m_oem_id.data(), m_oem_id.size()}; + } + + auto root_system_description_pointer::revision() const noexcept -> std::uint8_t + { + return m_revision; + } + + auto root_system_description_pointer::signature() const noexcept -> std::string_view + { + return {m_signature.data(), m_signature.size()}; + } + + auto root_system_description_pointer::table_address() const noexcept -> memory::physical_address + { + auto raw = std::bit_cast(m_rsdt_address); + return memory::physical_address{static_cast(raw)}; + } + + auto root_system_description_pointer::validate() const noexcept -> bool + { + return validate_checksum({reinterpret_cast(this), sizeof(root_system_description_pointer)}); + } + + auto extended_system_description_pointer::length() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{m_length}; + } + + auto extended_system_description_pointer::table_address() const noexcept -> memory::physical_address + { + return memory::physical_address{std::bit_cast(m_xsdt_address)}; + } + + auto extended_system_description_pointer::validate() const noexcept -> bool + { + return validate_checksum({reinterpret_cast(this), m_length}); + } + +}; // namespace kapi::acpi diff --git a/kernel/include/kernel/memory/block_list_allocator.hpp b/kernel/include/kernel/memory/block_list_allocator.hpp index f319097..de89f3b 100644 --- a/kernel/include/kernel/memory/block_list_allocator.hpp +++ b/kernel/include/kernel/memory/block_list_allocator.hpp @@ -43,10 +43,10 @@ namespace kernel::memory private: struct block_header final { - kstd::units::bytes usable_size; - bool free; - block_header * next; - block_header * prev; + kstd::units::bytes usable_size{}; + bool free{}; + block_header * next{}; + block_header * prev{}; }; //! The size of the metadata required for each allocated block. @@ -87,4 +87,4 @@ namespace kernel::memory } // namespace kernel::memory -#endif \ No newline at end of file +#endif diff --git a/libs/kstd/include/kstd/units b/libs/kstd/include/kstd/units index f6dcdb1..bc7e1b9 100644 --- a/libs/kstd/include/kstd/units +++ b/libs/kstd/include/kstd/units @@ -14,6 +14,11 @@ namespace kstd { using value_type = ValueType; + constexpr basic_unit() noexcept + : value{} + { + } + explicit constexpr basic_unit(value_type value) noexcept : value{value} {} @@ -142,4 +147,4 @@ namespace kstd } // namespace kstd -#endif \ No newline at end of file +#endif diff --git a/libs/multiboot2/include/multiboot2/constants/information_id.hpp b/libs/multiboot2/include/multiboot2/constants/information_id.hpp index be492eb..27c5300 100644 --- a/libs/multiboot2/include/multiboot2/constants/information_id.hpp +++ b/libs/multiboot2/include/multiboot2/constants/information_id.hpp @@ -57,10 +57,10 @@ namespace multiboot2 smbios_tables, //! A copy of RSDP as defined per ACPI 1.0 specification. - acpi_old_rsdp, + acpi_rsdp, - //! A copy of RSDP as defined per ACPI 2.0 or later specification. - acpi_new_rsdp, + //! A copy of XSDP as defined per ACPI 2.0 or later specification. + acpi_xsdp, //! The network information specified specified as per DHCP. networking_information, @@ -83,4 +83,4 @@ namespace multiboot2 } // namespace multiboot2 -#endif \ No newline at end of file +#endif diff --git a/libs/multiboot2/include/multiboot2/information.hpp b/libs/multiboot2/include/multiboot2/information.hpp index d2fac2e..a2ded56 100644 --- a/libs/multiboot2/include/multiboot2/information.hpp +++ b/libs/multiboot2/include/multiboot2/information.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -128,6 +129,26 @@ namespace multiboot2 return kstd::units::bytes{end_address - start_address}; } }; + + struct acpi_rsdp : vla_tag + { + using vla_tag::vla_tag; + + [[nodiscard]] auto pointer() const noexcept -> range_type + { + return {data(), size()}; + } + }; + + struct acpi_xsdp : vla_tag + { + using vla_tag::vla_tag; + + [[nodiscard]] auto pointer() const noexcept -> range_type + { + return {data(), size()}; + } + }; struct information_view { @@ -245,6 +266,26 @@ namespace multiboot2 std::views::transform(transform_module); } + [[nodiscard]] auto maybe_acpi_rsdp() const noexcept -> std::optional + { + return get(); + } + + [[nodiscard]] auto acpi_rsdp() const noexcept -> acpi_rsdp + { + return maybe_acpi_rsdp().value(); + } + + [[nodiscard]] auto maybe_acpi_xsdp() const noexcept -> std::optional + { + return get(); + } + + [[nodiscard]] auto acpi_xsdp() const noexcept -> acpi_xsdp + { + return maybe_acpi_xsdp().value(); + } + private: template [[nodiscard]] constexpr auto get() const noexcept -> std::optional @@ -264,4 +305,4 @@ namespace multiboot2 } // namespace multiboot2 -#endif \ No newline at end of file +#endif diff --git a/libs/multiboot2/include/multiboot2/information/data.hpp b/libs/multiboot2/include/multiboot2/information/data.hpp index 3b07d20..315eb39 100644 --- a/libs/multiboot2/include/multiboot2/information/data.hpp +++ b/libs/multiboot2/include/multiboot2/information/data.hpp @@ -167,8 +167,18 @@ namespace multiboot2 std::uint32_t end_address; }; + //! A copy of the ACPI RSDP + struct acpi_rsdp : tag_data + { + }; + + //! A copy of the ACPI XSDP + struct acpi_xsdp : tag_data + { + }; + } // namespace data } // namespace multiboot2 -#endif \ No newline at end of file +#endif -- cgit v1.2.3 From 6e54333bcc08ddd8dbcb6aa9c3404001c309ec74 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 13:27:22 +0200 Subject: kapi: move independent implementation to kernel --- kapi/CMakeLists.txt | 8 ++---- kapi/include/kapi/acpi.hpp | 2 +- kapi/src/acpi.cpp | 68 ---------------------------------------------- kernel/CMakeLists.txt | 1 + kernel/kapi/acpi.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 74 deletions(-) delete mode 100644 kapi/src/acpi.cpp create mode 100644 kernel/kapi/acpi.cpp diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index eeda158..c9aa23f 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -1,6 +1,4 @@ -add_library("kapi" STATIC - "src/acpi.cpp" -) +add_library("kapi" INTERFACE) add_library("os::kapi" ALIAS "kapi") file(GLOB_RECURSE KAPI_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") @@ -12,11 +10,11 @@ target_sources("kapi" PUBLIC ${KAPI_HEADERS} ) -target_include_directories("kapi" PUBLIC +target_include_directories("kapi" INTERFACE "include" ) -target_link_libraries("kapi" PUBLIC +target_link_libraries("kapi" INTERFACE "libs::kstd" "gcc" diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index 20e5e77..1068921 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -12,7 +12,7 @@ namespace kapi::acpi { - //! @addtogroup kapi-acpi-api-defined + //! @addtogroup kapi-acpi-kernel-defined //! @{ struct [[gnu::packed]] root_system_description_pointer diff --git a/kapi/src/acpi.cpp b/kapi/src/acpi.cpp deleted file mode 100644 index aa0066d..0000000 --- a/kapi/src/acpi.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "kapi/acpi.hpp" - -#include "kapi/memory.hpp" - -#include - -#include -#include -#include -#include -#include -#include - -namespace kapi::acpi -{ - - namespace - { - constexpr auto validate_checksum(std::span data) -> bool - { - auto sum = std::ranges::fold_left( - data, std::uint8_t{}, [](std::uint8_t acc, auto byte) { return static_cast(byte) + acc; }); - return sum == 0; - } - } // namespace - - auto root_system_description_pointer::oem_id() const noexcept -> std::string_view - { - return {m_oem_id.data(), m_oem_id.size()}; - } - - auto root_system_description_pointer::revision() const noexcept -> std::uint8_t - { - return m_revision; - } - - auto root_system_description_pointer::signature() const noexcept -> std::string_view - { - return {m_signature.data(), m_signature.size()}; - } - - auto root_system_description_pointer::table_address() const noexcept -> memory::physical_address - { - auto raw = std::bit_cast(m_rsdt_address); - return memory::physical_address{static_cast(raw)}; - } - - auto root_system_description_pointer::validate() const noexcept -> bool - { - return validate_checksum({reinterpret_cast(this), sizeof(root_system_description_pointer)}); - } - - auto extended_system_description_pointer::length() const noexcept -> kstd::units::bytes - { - return kstd::units::bytes{m_length}; - } - - auto extended_system_description_pointer::table_address() const noexcept -> memory::physical_address - { - return memory::physical_address{std::bit_cast(m_xsdt_address)}; - } - - auto extended_system_description_pointer::validate() const noexcept -> bool - { - return validate_checksum({reinterpret_cast(this), m_length}); - } - -}; // namespace kapi::acpi diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 9868eb9..ab77467 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -1,5 +1,6 @@ add_library("kernel_objs" OBJECT # Platform-independent KAPI implementation + "kapi/acpi.cpp" "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp new file mode 100644 index 0000000..aa0066d --- /dev/null +++ b/kernel/kapi/acpi.cpp @@ -0,0 +1,68 @@ +#include "kapi/acpi.hpp" + +#include "kapi/memory.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +namespace kapi::acpi +{ + + namespace + { + constexpr auto validate_checksum(std::span data) -> bool + { + auto sum = std::ranges::fold_left( + data, std::uint8_t{}, [](std::uint8_t acc, auto byte) { return static_cast(byte) + acc; }); + return sum == 0; + } + } // namespace + + auto root_system_description_pointer::oem_id() const noexcept -> std::string_view + { + return {m_oem_id.data(), m_oem_id.size()}; + } + + auto root_system_description_pointer::revision() const noexcept -> std::uint8_t + { + return m_revision; + } + + auto root_system_description_pointer::signature() const noexcept -> std::string_view + { + return {m_signature.data(), m_signature.size()}; + } + + auto root_system_description_pointer::table_address() const noexcept -> memory::physical_address + { + auto raw = std::bit_cast(m_rsdt_address); + return memory::physical_address{static_cast(raw)}; + } + + auto root_system_description_pointer::validate() const noexcept -> bool + { + return validate_checksum({reinterpret_cast(this), sizeof(root_system_description_pointer)}); + } + + auto extended_system_description_pointer::length() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{m_length}; + } + + auto extended_system_description_pointer::table_address() const noexcept -> memory::physical_address + { + return memory::physical_address{std::bit_cast(m_xsdt_address)}; + } + + auto extended_system_description_pointer::validate() const noexcept -> bool + { + return validate_checksum({reinterpret_cast(this), m_length}); + } + +}; // namespace kapi::acpi -- cgit v1.2.3 From 3dcd14a0570fef3bcc68d7df42fe3ff4cd496f93 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 14:47:37 +0200 Subject: kapi: hook ACPI initialization up to boot process --- .vscode/settings.json | 6 +++- arch/x86_64/kapi/devices.cpp | 32 +++++++++++++++++++- kapi/include/kapi/acpi.hpp | 44 +++++++++++++++++++-------- kapi/include/kapi/memory/layout.hpp | 12 ++++++++ kernel/CMakeLists.txt | 1 + kernel/include/kernel/acpi/manager.hpp | 26 ++++++++++++++++ kernel/kapi/acpi.cpp | 32 ++++++++++++++++---- kernel/src/acpi/manager.cpp | 54 ++++++++++++++++++++++++++++++++++ 8 files changed, 186 insertions(+), 21 deletions(-) create mode 100644 kernel/include/kernel/acpi/manager.hpp create mode 100644 kernel/src/acpi/manager.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 4271526..6ea3c79 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -32,6 +32,7 @@ "crtp", "efer", "functors", + "hhdm", "idtr", "initializable", "interprocedural", @@ -43,6 +44,7 @@ "malloc", "memcmp", "memset", + "mmio", "multiboot", "nodiscard", "nolintnextline", @@ -51,12 +53,14 @@ "raii", "rdmsr", "RSDP", + "rsdt", "rvalues", "stringview", "sysret", "teachos", "undelegated", - "wrmsr" + "wrmsr", + "xsdp" ], "testMate.cpp.debug.configTemplate": { "type": "cppdbg", diff --git a/arch/x86_64/kapi/devices.cpp b/arch/x86_64/kapi/devices.cpp index b15503d..e1773e7 100644 --- a/arch/x86_64/kapi/devices.cpp +++ b/arch/x86_64/kapi/devices.cpp @@ -1,5 +1,9 @@ #include "kapi/devices.hpp" +#include "kapi/acpi.hpp" +#include "kapi/boot.hpp" + +#include "arch/boot/boot.hpp" #include "arch/bus/isa.hpp" #include "arch/devices/legacy_pit.hpp" @@ -19,7 +23,33 @@ namespace kapi::devices auto init_platform_devices() -> void { - kstd::println("[x86_64:devices] Initializing ISA bus..."); + auto const & mbi = boot::bootstrap_information.mbi; + auto system_description_pointer = static_cast(nullptr); + + if (auto const & xsdp = mbi->maybe_acpi_xsdp()) + { + auto data = xsdp->pointer().data(); + system_description_pointer = reinterpret_cast(data); + } + else if (auto const & rsdp = mbi->maybe_acpi_rsdp()) + { + auto data = rsdp->pointer().data(); + system_description_pointer = reinterpret_cast(data); + } + + if (system_description_pointer) + { + if (!kapi::acpi::init(*system_description_pointer)) + { + kstd::println(kstd::print_sink::stderr, "[x86_64:DEV] ACPI initialization failed. No tables loaded."); + } + } + else + { + kstd::println(kstd::print_sink::stderr, "[x86_64:DEV] No ACPI RSDP found. Most devices will not be available."); + } + + kstd::println("[x86_64:DEV] Initializing ISA bus..."); auto isa_major_number = kapi::devices::allocate_major_number(); auto isa_bus = kstd::make_unique(isa_major_number); diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index 1068921..d5e3c87 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -2,16 +2,36 @@ #define TEACHOS_KAPI_ACPI_HPP #include "kapi/memory.hpp" + #include #include #include #include +#include #include namespace kapi::acpi { + //! @addtogroup kapi-acpi + //! @{ + + struct [[gnu::packed]] system_description_table_header + { + std::array signature; + std::uint32_t length; + std::uint8_t revision; + std::uint8_t checksum; + std::array oem_id; + std::array oem_table_id; + std::uint32_t oem_revision; + std::uint32_t creator_id; + std::uint32_t create_revision; + }; + + //! @} + //! @addtogroup kapi-acpi-kernel-defined //! @{ @@ -44,20 +64,18 @@ namespace kapi::acpi [[maybe_unused]] std::array m_reserved; }; - //! @} + //! Initialize the ACPI subsystem and discover the available tables. + //! + //! @return true iff. a valid system description tabled was found, false otherwise. + auto init(root_system_description_pointer const & sdp) -> bool; - struct [[gnu::packed]] system_description_table_header - { - std::array signature; - std::uint32_t length; - std::uint8_t revision; - std::uint8_t checksum; - std::array oem_id; - std::array oem_table_id; - std::uint32_t oem_revision; - std::uint32_t creator_id; - std::uint32_t create_revision; - }; + //! Validate and ACPI entity checksum. + //! + //! @param data The data to validate the checksum of. + //! @return true iff. the checksum is valid, false otherwise. + auto validate_checksum(std::span data) -> bool; + + //! @} } // namespace kapi::acpi diff --git a/kapi/include/kapi/memory/layout.hpp b/kapi/include/kapi/memory/layout.hpp index 157f41e..26b48d8 100644 --- a/kapi/include/kapi/memory/layout.hpp +++ b/kapi/include/kapi/memory/layout.hpp @@ -37,6 +37,18 @@ namespace kapi::memory //! The linear base address of the loaded kernel image. constexpr auto kernel_base = linear_address{0xffff'ffff'8000'0000uz}; + //! Convert a physical address to a linear address using the higher-half direct map. + constexpr auto hhdm_to_linear(physical_address address) -> linear_address + { + return linear_address{address.raw() + higher_half_direct_map_base.raw()}; + } + + //! Convert a linear address in the higher-half direct map to a physical address. + constexpr auto hhdm_to_physical(linear_address address) -> physical_address + { + return physical_address{address.raw() - higher_half_direct_map_base.raw()}; + } + } // namespace kapi::memory #endif \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index ab77467..fc01723 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -14,6 +14,7 @@ add_library("kernel_objs" OBJECT "kstd/print.cpp" # Kernel Implementation + "src/acpi/manager.cpp" "src/memory/bitmap_allocator.cpp" "src/memory/block_list_allocator.cpp" "src/memory.cpp" diff --git a/kernel/include/kernel/acpi/manager.hpp b/kernel/include/kernel/acpi/manager.hpp new file mode 100644 index 0000000..fc76685 --- /dev/null +++ b/kernel/include/kernel/acpi/manager.hpp @@ -0,0 +1,26 @@ +#ifndef TEACHOS_KERNEL_ACPI_MANAGER_HPP +#define TEACHOS_KERNEL_ACPI_MANAGER_HPP + +#include "kapi/acpi.hpp" + +#include + +namespace kernel::acpi +{ + + struct manager + { + explicit manager(kapi::acpi::root_system_description_pointer const & sdp); + + auto load_tables() -> bool; + + private: + kapi::acpi::root_system_description_pointer const * m_sdp{}; + kapi::acpi::system_description_table_header const * m_rsdt{}; + kstd::vector m_tables{}; + bool m_extended{}; + }; + +} // namespace kernel::acpi + +#endif diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp index aa0066d..787bcff 100644 --- a/kernel/kapi/acpi.cpp +++ b/kernel/kapi/acpi.cpp @@ -1,13 +1,18 @@ #include "kapi/acpi.hpp" #include "kapi/memory.hpp" +#include "kapi/system.hpp" + +#include "kernel/acpi/manager.hpp" #include #include +#include #include #include #include +#include #include #include @@ -16,12 +21,7 @@ namespace kapi::acpi namespace { - constexpr auto validate_checksum(std::span data) -> bool - { - auto sum = std::ranges::fold_left( - data, std::uint8_t{}, [](std::uint8_t acc, auto byte) { return static_cast(byte) + acc; }); - return sum == 0; - } + auto constinit manager = std::optional{}; } // namespace auto root_system_description_pointer::oem_id() const noexcept -> std::string_view @@ -65,4 +65,24 @@ namespace kapi::acpi return validate_checksum({reinterpret_cast(this), m_length}); } + auto init(root_system_description_pointer const & sdp) -> bool + { + auto static constinit initialized = std::atomic_flag{}; + if (initialized.test_and_set()) + { + system::panic("[OS::ACPI] The ACPI manager has already been initialized!"); + } + + manager.emplace(sdp); + return manager->load_tables(); + } + + auto validate_checksum(std::span data) -> bool + { + auto sum = std::ranges::fold_left(data, std::uint8_t{}, [](auto acc, auto byte) { + return static_cast(acc + static_cast(byte)); + }); + return sum == 0; + } + }; // namespace kapi::acpi diff --git a/kernel/src/acpi/manager.cpp b/kernel/src/acpi/manager.cpp new file mode 100644 index 0000000..6a17920 --- /dev/null +++ b/kernel/src/acpi/manager.cpp @@ -0,0 +1,54 @@ +#include "kernel/acpi/manager.hpp" + +#include "kapi/acpi.hpp" +#include "kapi/memory.hpp" +#include "kapi/system.hpp" + +#include + +namespace kernel::acpi +{ + + manager::manager(kapi::acpi::root_system_description_pointer const & sdp) + : m_sdp{&sdp} + { + if (m_sdp->signature() != "RSD PTR ") + { + kapi::system::panic("[OS:ACPI] Invalid RSDP signature!"); + } + + if (m_sdp->revision() >= 2) + { + auto const xsdp = static_cast(m_sdp); + if (!xsdp->validate()) + { + kapi::system::panic("[OS:ACPI] Invalid XSDP signature!"); + } + auto physical_extended_table_address = xsdp->table_address(); + auto linear_extended_table_address = kapi::memory::hhdm_to_linear(physical_extended_table_address); + m_rsdt = static_cast(linear_extended_table_address); + m_extended = true; + } + else + { + if (!m_sdp->validate()) + { + kapi::system::panic("[OS:ACPI] Invalid RSDP checksum!"); + } + auto physical_root_table_address = m_sdp->table_address(); + auto linear_root_table_address = kapi::memory::hhdm_to_linear(physical_root_table_address); + m_rsdt = static_cast(linear_root_table_address); + } + } + + auto manager::load_tables() -> bool + { + if (!kapi::acpi::validate_checksum({reinterpret_cast(m_rsdt), m_rsdt->length})) + { + kapi::system::panic("[OS:ACPI] Invalid RSDT checksum!"); + } + + return false; + } + +} // namespace kernel::acpi -- cgit v1.2.3 From 4d938cd31a35cd4322fe914edd568faa5391c9c2 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 15:10:04 +0200 Subject: kernel/acpi: implement basic table discovery --- kapi/include/kapi/acpi.hpp | 29 +++++++++++++++------- kernel/include/kernel/acpi/manager.hpp | 5 +++- kernel/kapi/acpi.cpp | 45 ++++++++++++++++++++++++++++++++++ kernel/src/acpi/manager.cpp | 42 +++++++++++++++++++++++++++++-- libs/kstd/include/kstd/flat_map | 18 ++++++++++++++ 5 files changed, 127 insertions(+), 12 deletions(-) diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index d5e3c87..11068d1 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -19,15 +19,26 @@ namespace kapi::acpi struct [[gnu::packed]] system_description_table_header { - std::array signature; - std::uint32_t length; - std::uint8_t revision; - std::uint8_t checksum; - std::array oem_id; - std::array oem_table_id; - std::uint32_t oem_revision; - std::uint32_t creator_id; - std::uint32_t create_revision; + [[nodiscard]] auto checksum() const noexcept -> std::uint8_t; + [[nodiscard]] auto creator_revision() const noexcept -> std::uint32_t; + [[nodiscard]] auto creator_id() const noexcept -> std::uint32_t; + [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; + [[nodiscard]] auto oem_id() const noexcept -> std::string_view; + [[nodiscard]] auto oem_revision() const noexcept -> std::uint32_t; + [[nodiscard]] auto oem_table_id() const noexcept -> std::string_view; + [[nodiscard]] auto revision() const noexcept -> std::uint8_t; + [[nodiscard]] auto signature() const noexcept -> std::string_view; + + private: + std::array m_signature; + std::uint32_t m_length; + std::uint8_t m_revision; + std::uint8_t m_checksum; + std::array m_oem_id; + std::array m_oem_table_id; + std::uint32_t m_oem_revision; + std::uint32_t m_creator_id; + std::uint32_t m_creator_revision; }; //! @} diff --git a/kernel/include/kernel/acpi/manager.hpp b/kernel/include/kernel/acpi/manager.hpp index fc76685..437d4c8 100644 --- a/kernel/include/kernel/acpi/manager.hpp +++ b/kernel/include/kernel/acpi/manager.hpp @@ -3,8 +3,11 @@ #include "kapi/acpi.hpp" +#include #include +#include + namespace kernel::acpi { @@ -17,7 +20,7 @@ namespace kernel::acpi private: kapi::acpi::root_system_description_pointer const * m_sdp{}; kapi::acpi::system_description_table_header const * m_rsdt{}; - kstd::vector m_tables{}; + kstd::flat_map m_tables{}; bool m_extended{}; }; diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp index 787bcff..998d7a0 100644 --- a/kernel/kapi/acpi.cpp +++ b/kernel/kapi/acpi.cpp @@ -65,6 +65,51 @@ namespace kapi::acpi return validate_checksum({reinterpret_cast(this), m_length}); } + [[nodiscard]] auto system_description_table_header::checksum() const noexcept -> std::uint8_t + { + return m_checksum; + } + + [[nodiscard]] auto system_description_table_header::creator_revision() const noexcept -> std::uint32_t + { + return m_creator_revision; + } + + [[nodiscard]] auto system_description_table_header::creator_id() const noexcept -> std::uint32_t + { + return m_creator_id; + } + + [[nodiscard]] auto system_description_table_header::length() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{m_length}; + } + + [[nodiscard]] auto system_description_table_header::oem_id() const noexcept -> std::string_view + { + return {m_oem_id.data(), m_oem_id.size()}; + } + + [[nodiscard]] auto system_description_table_header::oem_revision() const noexcept -> std::uint32_t + { + return m_oem_revision; + } + + [[nodiscard]] auto system_description_table_header::oem_table_id() const noexcept -> std::string_view + { + return {m_oem_table_id.data(), m_oem_table_id.size()}; + } + + [[nodiscard]] auto system_description_table_header::revision() const noexcept -> std::uint8_t + { + return m_revision; + } + + [[nodiscard]] auto system_description_table_header::signature() const noexcept -> std::string_view + { + return {m_signature.data(), m_signature.size()}; + } + auto init(root_system_description_pointer const & sdp) -> bool { auto static constinit initialized = std::atomic_flag{}; diff --git a/kernel/src/acpi/manager.cpp b/kernel/src/acpi/manager.cpp index 6a17920..180d458 100644 --- a/kernel/src/acpi/manager.cpp +++ b/kernel/src/acpi/manager.cpp @@ -4,7 +4,10 @@ #include "kapi/memory.hpp" #include "kapi/system.hpp" +#include + #include +#include namespace kernel::acpi { @@ -43,12 +46,47 @@ namespace kernel::acpi auto manager::load_tables() -> bool { - if (!kapi::acpi::validate_checksum({reinterpret_cast(m_rsdt), m_rsdt->length})) + if (!kapi::acpi::validate_checksum({reinterpret_cast(m_rsdt), m_rsdt->length().value})) { kapi::system::panic("[OS:ACPI] Invalid RSDT checksum!"); } - return false; + auto entry_size = m_extended ? sizeof(std::uint64_t) : sizeof(std::uint32_t); + auto entry_count = (m_rsdt->length().value - sizeof(kapi::acpi::system_description_table_header)) / entry_size; + auto entries_base = + reinterpret_cast(m_rsdt) + sizeof(kapi::acpi::system_description_table_header); + + for (std::size_t i = 0; i < entry_count; ++i) + { + auto physical_table_address = kapi::memory::physical_address{}; + + if (m_extended) + { + auto entry = reinterpret_cast(entries_base + (i * entry_size)); + physical_table_address = kapi::memory::physical_address{*entry}; + } + else + { + auto entry = reinterpret_cast(entries_base + (i * entry_size)); + physical_table_address = kapi::memory::physical_address{*entry}; + } + + auto linear_table_address = kapi::memory::hhdm_to_linear(physical_table_address); + auto table = static_cast(linear_table_address); + + if (!kapi::acpi::validate_checksum({reinterpret_cast(table), table->length().value})) + { + kstd::println(kstd::print_sink::stderr, "[OS:ACPI] Invalid table checksum!"); + } + else + { + kstd::println("[OS:ACPI] Found table: {}@{:#x}/{:#x} OEM: {} Rev: {}", table->signature(), linear_table_address, + physical_table_address, table->oem_id(), table->creator_revision()); + m_tables.emplace(table->signature(), table); + } + } + + return !m_tables.empty(); } } // namespace kernel::acpi diff --git a/libs/kstd/include/kstd/flat_map b/libs/kstd/include/kstd/flat_map index 6ffbbcf..3b13754 100644 --- a/libs/kstd/include/kstd/flat_map +++ b/libs/kstd/include/kstd/flat_map @@ -205,6 +205,24 @@ namespace kstd return const_reverse_iterator{cbegin()}; } + //! Check whether this flat map is empty or not + [[nodiscard]] auto empty() const noexcept -> bool + { + return m_containers.keys.empty(); + } + + //! Get the number of elements in this flat map. + [[nodiscard]] auto size() const noexcept -> size_type + { + return m_containers.keys.size(); + } + + //! Get the maximum number of elements possible in this flat map + [[nodiscard]] auto max_size() const noexcept -> size_type + { + return std::min(m_containers.keys.max_size(), m_containers.values.max_size()); + } + //! Try to insert a new key-value pair into the map. //! //! @param args Arguments to use for constructing the key-value pair. -- cgit v1.2.3 From c18feddd51d1a1398d1245229c5f889dd40554b3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 15:54:21 +0200 Subject: x86_64/devices: extract initialization code --- arch/x86_64/CMakeLists.txt | 1 + arch/x86_64/include/arch/devices/init.hpp | 12 ++++++ arch/x86_64/kapi/devices.cpp | 57 ++----------------------- arch/x86_64/src/devices/init.cpp | 69 +++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 54 deletions(-) create mode 100644 arch/x86_64/include/arch/devices/init.hpp create mode 100644 arch/x86_64/src/devices/init.cpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 83cae0b..f77eec5 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -38,6 +38,7 @@ target_sources("x86_64" PRIVATE "src/debug/qemu_output.cpp" # Devices + "src/devices/init.cpp" "src/devices/legacy_pit.cpp" # Memory management diff --git a/arch/x86_64/include/arch/devices/init.hpp b/arch/x86_64/include/arch/devices/init.hpp new file mode 100644 index 0000000..c5fbf37 --- /dev/null +++ b/arch/x86_64/include/arch/devices/init.hpp @@ -0,0 +1,12 @@ +#ifndef TEACHOS_ARCH_X86_64_DEVICES_INIT_HPP +#define TEACHOS_ARCH_X86_64_DEVICES_INIT_HPP + +namespace arch::devices +{ + + auto init_acpi_devices() -> void; + auto init_legacy_devices() -> void; + +} // namespace arch::devices + +#endif \ No newline at end of file diff --git a/arch/x86_64/kapi/devices.cpp b/arch/x86_64/kapi/devices.cpp index e1773e7..47c7f8c 100644 --- a/arch/x86_64/kapi/devices.cpp +++ b/arch/x86_64/kapi/devices.cpp @@ -1,65 +1,14 @@ #include "kapi/devices.hpp" -#include "kapi/acpi.hpp" -#include "kapi/boot.hpp" - -#include "arch/boot/boot.hpp" -#include "arch/bus/isa.hpp" -#include "arch/devices/legacy_pit.hpp" - -#include -#include - -#include -#include +#include "arch/devices/init.hpp" namespace kapi::devices { - namespace - { - constexpr auto pit_frequency_in_hz = std::uint32_t{100u}; - } - auto init_platform_devices() -> void { - auto const & mbi = boot::bootstrap_information.mbi; - auto system_description_pointer = static_cast(nullptr); - - if (auto const & xsdp = mbi->maybe_acpi_xsdp()) - { - auto data = xsdp->pointer().data(); - system_description_pointer = reinterpret_cast(data); - } - else if (auto const & rsdp = mbi->maybe_acpi_rsdp()) - { - auto data = rsdp->pointer().data(); - system_description_pointer = reinterpret_cast(data); - } - - if (system_description_pointer) - { - if (!kapi::acpi::init(*system_description_pointer)) - { - kstd::println(kstd::print_sink::stderr, "[x86_64:DEV] ACPI initialization failed. No tables loaded."); - } - } - else - { - kstd::println(kstd::print_sink::stderr, "[x86_64:DEV] No ACPI RSDP found. Most devices will not be available."); - } - - kstd::println("[x86_64:DEV] Initializing ISA bus..."); - - auto isa_major_number = kapi::devices::allocate_major_number(); - auto isa_bus = kstd::make_unique(isa_major_number); - - auto pit_major_number = kapi::devices::allocate_major_number(); - auto pit = kstd::make_unique(pit_major_number, pit_frequency_in_hz); - isa_bus->add_child(std::move(pit)); - - auto & root_bus = get_root_bus(); - root_bus.add_child(std::move(isa_bus)); + arch::devices::init_acpi_devices(); + arch::devices::init_legacy_devices(); } } // namespace kapi::devices \ No newline at end of file diff --git a/arch/x86_64/src/devices/init.cpp b/arch/x86_64/src/devices/init.cpp new file mode 100644 index 0000000..1099ad6 --- /dev/null +++ b/arch/x86_64/src/devices/init.cpp @@ -0,0 +1,69 @@ +#include "arch/devices/init.hpp" + +#include "kapi/acpi.hpp" +#include "kapi/boot.hpp" +#include "kapi/devices.hpp" + +#include "arch/boot/boot.hpp" +#include "arch/bus/isa.hpp" +#include "arch/devices/legacy_pit.hpp" + +#include +#include + +#include +#include + +namespace arch::devices +{ + + namespace + { + constexpr auto pit_frequency_in_hz = std::uint32_t{100u}; + } + + auto init_acpi_devices() -> void + { + auto const & mbi = kapi::boot::bootstrap_information.mbi; + auto system_description_pointer = static_cast(nullptr); + + if (auto const & xsdp = mbi->maybe_acpi_xsdp()) + { + auto data = xsdp->pointer().data(); + system_description_pointer = reinterpret_cast(data); + } + else if (auto const & rsdp = mbi->maybe_acpi_rsdp()) + { + auto data = rsdp->pointer().data(); + system_description_pointer = reinterpret_cast(data); + } + + if (system_description_pointer) + { + if (!kapi::acpi::init(*system_description_pointer)) + { + kstd::println(kstd::print_sink::stderr, "[x86_64:DEV] ACPI initialization failed. No tables loaded."); + } + } + else + { + kstd::println(kstd::print_sink::stderr, "[x86_64:DEV] No ACPI RSDP found. Most devices will not be available."); + } + } + + auto init_legacy_devices() -> void + { + kstd::println("[x86_64:DEV] Initializing ISA bus..."); + + auto isa_major_number = kapi::devices::allocate_major_number(); + auto isa_bus = kstd::make_unique(isa_major_number); + + auto pit_major_number = kapi::devices::allocate_major_number(); + auto pit = kstd::make_unique(pit_major_number, pit_frequency_in_hz); + isa_bus->add_child(std::move(pit)); + + auto & root_bus = kapi::devices::get_root_bus(); + root_bus.add_child(std::move(isa_bus)); + } + +} // namespace arch::devices -- cgit v1.2.3 From f456f1674d48932846eb7b5ec1df630ad67e7e3d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 17:24:36 +0200 Subject: kernel/acpi: discover local interrupt controllers --- .vscode/settings.json | 6 +- arch/x86_64/CMakeLists.txt | 2 + arch/x86_64/include/arch/devices/local_apic.hpp | 26 +++++++ arch/x86_64/kapi/acpi.cpp | 43 +++++++++++ arch/x86_64/src/devices/init.cpp | 32 +------- arch/x86_64/src/devices/local_apic.cpp | 28 +++++++ kapi/include/kapi/acpi.hpp | 91 +++++++++++++++++------ kapi/include/kapi/devices/bus.hpp | 2 + kapi/include/kapi/devices/device.hpp | 2 + kernel/CMakeLists.txt | 1 + kernel/include/kernel/acpi/manager.hpp | 3 + kernel/include/kernel/devices/cpu.hpp | 33 +++++++++ kernel/kapi/acpi.cpp | 43 ++++++++++- kernel/kapi/devices.cpp | 10 +++ kernel/src/acpi/manager.cpp | 15 +++- kernel/src/devices/cpu.cpp | 97 +++++++++++++++++++++++++ kernel/src/main.cpp | 10 +++ 17 files changed, 386 insertions(+), 58 deletions(-) create mode 100644 arch/x86_64/include/arch/devices/local_apic.hpp create mode 100644 arch/x86_64/kapi/acpi.cpp create mode 100644 arch/x86_64/src/devices/local_apic.cpp create mode 100644 kernel/include/kernel/devices/cpu.hpp create mode 100644 kernel/src/devices/cpu.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 6ea3c79..bebda51 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -26,6 +26,7 @@ }, "cSpell.words": [ "acpi", + "APIC", "bugprone", "cppcoreguidelines", "crtc", @@ -41,6 +42,8 @@ "iwyu", "kapi", "kstd", + "lapic", + "madt", "malloc", "memcmp", "memset", @@ -60,7 +63,8 @@ "teachos", "undelegated", "wrmsr", - "xsdp" + "xsdp", + "xsdt" ], "testMate.cpp.debug.configTemplate": { "type": "cppdbg", diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index f77eec5..62a2aad 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -12,6 +12,7 @@ target_link_libraries("x86_64" PUBLIC target_sources("x86_64" PRIVATE # Platform-dependent KAPI implementation + "kapi/acpi.cpp" "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" @@ -40,6 +41,7 @@ target_sources("x86_64" PRIVATE # Devices "src/devices/init.cpp" "src/devices/legacy_pit.cpp" + "src/devices/local_apic.cpp" # Memory management "src/memory/kernel_mapper.cpp" diff --git a/arch/x86_64/include/arch/devices/local_apic.hpp b/arch/x86_64/include/arch/devices/local_apic.hpp new file mode 100644 index 0000000..71e9758 --- /dev/null +++ b/arch/x86_64/include/arch/devices/local_apic.hpp @@ -0,0 +1,26 @@ +#ifndef TEACHOS_ARCH_X86_64_DEVICES_LOCAL_APIC_HPP +#define TEACHOS_ARCH_X86_64_DEVICES_LOCAL_APIC_HPP + +#include "kapi/devices/device.hpp" +#include "kapi/memory.hpp" + +#include +#include + +namespace arch::devices +{ + + struct local_apic : kapi::devices::device + { + local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id, kapi::memory::physical_address base); + + auto init() -> bool override; + + private: + std::uint64_t m_hardware_id{}; + kapi::memory::physical_address m_base{}; + }; + +} // namespace arch::devices + +#endif \ No newline at end of file diff --git a/arch/x86_64/kapi/acpi.cpp b/arch/x86_64/kapi/acpi.cpp new file mode 100644 index 0000000..9766154 --- /dev/null +++ b/arch/x86_64/kapi/acpi.cpp @@ -0,0 +1,43 @@ +#include "kapi/acpi.hpp" + +#include "kapi/devices.hpp" +#include "kapi/memory.hpp" + +#include "arch/boot/boot.hpp" +#include "arch/devices/local_apic.hpp" + +#include + +#include +#include + +namespace kapi::acpi +{ + + auto get_root_pointer() -> kstd::observer_ptr + { + auto const & mbi = kapi::boot::bootstrap_information.mbi; + auto system_description_pointer = static_cast(nullptr); + + if (auto const & xsdp = mbi->maybe_acpi_xsdp()) + { + auto data = xsdp->pointer().data(); + + system_description_pointer = reinterpret_cast(data); + } + else if (auto const & rsdp = mbi->maybe_acpi_rsdp()) + { + auto data = rsdp->pointer().data(); + system_description_pointer = reinterpret_cast(data); + } + + return kstd::make_observer(system_description_pointer); + } + + auto create_local_interrupt_controller(std::size_t major, std::size_t minor, std::uint64_t hardware_id, + memory::physical_address address) -> kstd::unique_ptr + { + return kstd::make_unique(major, minor, hardware_id, address); + } + +} // namespace kapi::acpi \ No newline at end of file diff --git a/arch/x86_64/src/devices/init.cpp b/arch/x86_64/src/devices/init.cpp index 1099ad6..6cba986 100644 --- a/arch/x86_64/src/devices/init.cpp +++ b/arch/x86_64/src/devices/init.cpp @@ -1,10 +1,7 @@ #include "arch/devices/init.hpp" -#include "kapi/acpi.hpp" -#include "kapi/boot.hpp" #include "kapi/devices.hpp" -#include "arch/boot/boot.hpp" #include "arch/bus/isa.hpp" #include "arch/devices/legacy_pit.hpp" @@ -22,34 +19,7 @@ namespace arch::devices constexpr auto pit_frequency_in_hz = std::uint32_t{100u}; } - auto init_acpi_devices() -> void - { - auto const & mbi = kapi::boot::bootstrap_information.mbi; - auto system_description_pointer = static_cast(nullptr); - - if (auto const & xsdp = mbi->maybe_acpi_xsdp()) - { - auto data = xsdp->pointer().data(); - system_description_pointer = reinterpret_cast(data); - } - else if (auto const & rsdp = mbi->maybe_acpi_rsdp()) - { - auto data = rsdp->pointer().data(); - system_description_pointer = reinterpret_cast(data); - } - - if (system_description_pointer) - { - if (!kapi::acpi::init(*system_description_pointer)) - { - kstd::println(kstd::print_sink::stderr, "[x86_64:DEV] ACPI initialization failed. No tables loaded."); - } - } - else - { - kstd::println(kstd::print_sink::stderr, "[x86_64:DEV] No ACPI RSDP found. Most devices will not be available."); - } - } + auto init_acpi_devices() -> void {} auto init_legacy_devices() -> void { diff --git a/arch/x86_64/src/devices/local_apic.cpp b/arch/x86_64/src/devices/local_apic.cpp new file mode 100644 index 0000000..beb75ef --- /dev/null +++ b/arch/x86_64/src/devices/local_apic.cpp @@ -0,0 +1,28 @@ +#include "arch/devices/local_apic.hpp" + +#include "kapi/devices.hpp" +#include "kapi/memory.hpp" + +#include + +#include +#include + +namespace arch::devices +{ + + local_apic::local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id, + kapi::memory::physical_address base) + : kapi::devices::device{major, minor, "lapic"} + , m_hardware_id{hardware_id} + , m_base{base} + {} + + auto local_apic::init() -> bool + { + kstd::println("[x86_64:DEV] Initializing local APIC on core {}", m_hardware_id); + + return true; + } + +} // namespace arch::devices \ No newline at end of file diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index 11068d1..5d55d08 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -1,8 +1,10 @@ #ifndef TEACHOS_KAPI_ACPI_HPP #define TEACHOS_KAPI_ACPI_HPP +#include "kapi/devices.hpp" #include "kapi/memory.hpp" +#include #include #include @@ -14,9 +16,38 @@ namespace kapi::acpi { - //! @addtogroup kapi-acpi + //! @addtogroup kapi-acpi-kernel-defined //! @{ + struct [[gnu::packed]] root_system_description_pointer + { + [[nodiscard]] auto oem_id() const noexcept -> std::string_view; + [[nodiscard]] auto revision() const noexcept -> std::uint8_t; + [[nodiscard]] auto signature() const noexcept -> std::string_view; + [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; + [[nodiscard]] auto validate() const noexcept -> bool; + + private: + std::array m_signature; + [[maybe_unused]] std::uint8_t m_checksum; + std::array m_oem_id; + std::uint8_t m_revision; + std::array m_rsdt_address; + }; + + struct [[gnu::packed]] extended_system_description_pointer : root_system_description_pointer + { + [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; + [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; + [[nodiscard]] auto validate() const noexcept -> bool; + + private: + std::uint32_t m_length; + std::array m_xsdt_address; + [[maybe_unused]] std::uint8_t m_extended_checksum; + [[maybe_unused]] std::array m_reserved; + }; + struct [[gnu::packed]] system_description_table_header { [[nodiscard]] auto checksum() const noexcept -> std::uint8_t; @@ -41,38 +72,36 @@ namespace kapi::acpi std::uint32_t m_creator_revision; }; - //! @} + struct [[gnu::packed]] madt_header : system_description_table_header + { + [[nodiscard]] auto local_interrupt_controller_address() const noexcept -> memory::physical_address; + [[nodiscard]] auto flags() const noexcept -> std::uint32_t; - //! @addtogroup kapi-acpi-kernel-defined - //! @{ + private: + std::uint32_t m_local_interrupt_controller_address; + std::uint32_t m_flags; + }; - struct [[gnu::packed]] root_system_description_pointer + struct [[gnu::packed]] madt_subtable_header { - [[nodiscard]] auto oem_id() const noexcept -> std::string_view; - [[nodiscard]] auto revision() const noexcept -> std::uint8_t; - [[nodiscard]] auto signature() const noexcept -> std::string_view; - [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; - [[nodiscard]] auto validate() const noexcept -> bool; + [[nodiscard]] auto type() const noexcept -> std::uint8_t; + [[nodiscard]] auto length() const noexcept -> std::size_t; private: - std::array m_signature; - [[maybe_unused]] std::uint8_t m_checksum; - std::array m_oem_id; - std::uint8_t m_revision; - std::array m_rsdt_address; + std::uint8_t m_type; + std::uint8_t m_length; }; - struct [[gnu::packed]] extended_system_description_pointer : root_system_description_pointer + struct [[gnu::packed]] madt_local_apic : madt_subtable_header { - [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; - [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; - [[nodiscard]] auto validate() const noexcept -> bool; + [[nodiscard]] auto apic_id() const noexcept -> std::uint8_t; + [[nodiscard]] auto flags() const noexcept -> std::uint32_t; + [[nodiscard]] auto processor_id() const noexcept -> std::uint32_t; private: - std::uint32_t m_length; - std::array m_xsdt_address; - [[maybe_unused]] std::uint8_t m_extended_checksum; - [[maybe_unused]] std::array m_reserved; + std::uint8_t m_processor_id; + std::uint8_t m_apic_id; + std::uint32_t m_flags; }; //! Initialize the ACPI subsystem and discover the available tables. @@ -86,6 +115,22 @@ namespace kapi::acpi //! @return true iff. the checksum is valid, false otherwise. auto validate_checksum(std::span data) -> bool; + //! Get an ACPI table by its signature. + //! + //! @param signature The signature of the table to get. + //! @return A pointer to the table if found, nullptr otherwise. + auto get_table(std::string_view signature) -> kstd::observer_ptr; + + //! @} + + //! @addtogroup kapi-acpi-platform-defined + //! @{ + + auto get_root_pointer() -> kstd::observer_ptr; + + auto create_local_interrupt_controller(std::size_t major, std::size_t minor, std::uint64_t hardware_id, + memory::physical_address address) -> kstd::unique_ptr; + //! @} } // namespace kapi::acpi diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index 052c062..a384291 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -1,6 +1,8 @@ #ifndef TEACHOS_KAPI_DEVICES_BUS_HPP #define TEACHOS_KAPI_DEVICES_BUS_HPP +// IWYU pragma: private, include "kapi/devices.hpp" + #include "kapi/devices/device.hpp" #include "kapi/devices/manager.hpp" #include "kapi/system.hpp" diff --git a/kapi/include/kapi/devices/device.hpp b/kapi/include/kapi/devices/device.hpp index b7b6bba..ca5033e 100644 --- a/kapi/include/kapi/devices/device.hpp +++ b/kapi/include/kapi/devices/device.hpp @@ -1,6 +1,8 @@ #ifndef TEACH_OS_KAPI_DEVICES_DEVICE_HPP #define TEACH_OS_KAPI_DEVICES_DEVICE_HPP +// IWYU pragma: private, include "kapi/devices.hpp" + #include #include diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index fc01723..92e2bda 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -20,6 +20,7 @@ add_library("kernel_objs" OBJECT "src/memory.cpp" "src/devices/block_device.cpp" "src/devices/block_device_utils.cpp" + "src/devices/cpu.cpp" "src/devices/root_bus.cpp" "src/devices/storage/controller.cpp" "src/devices/storage/management.cpp" diff --git a/kernel/include/kernel/acpi/manager.hpp b/kernel/include/kernel/acpi/manager.hpp index 437d4c8..fae59a6 100644 --- a/kernel/include/kernel/acpi/manager.hpp +++ b/kernel/include/kernel/acpi/manager.hpp @@ -4,6 +4,7 @@ #include "kapi/acpi.hpp" #include +#include #include #include @@ -17,6 +18,8 @@ namespace kernel::acpi auto load_tables() -> bool; + auto get_table(std::string_view signature) -> kstd::observer_ptr; + private: kapi::acpi::root_system_description_pointer const * m_sdp{}; kapi::acpi::system_description_table_header const * m_rsdt{}; diff --git a/kernel/include/kernel/devices/cpu.hpp b/kernel/include/kernel/devices/cpu.hpp new file mode 100644 index 0000000..b056665 --- /dev/null +++ b/kernel/include/kernel/devices/cpu.hpp @@ -0,0 +1,33 @@ +#ifndef TEACHOS_KERNEL_DEVICES_CPU_HPP +#define TEACHOS_KERNEL_DEVICES_CPU_HPP + +#include "kapi/devices.hpp" + +#include +#include + +namespace kernel::devices +{ + + struct cpu final : kapi::devices::bus + { + struct core final : kapi::devices::bus + { + explicit core(std::size_t major_number, std::size_t minor_number, std::uint64_t hardware_id, bool is_bsp); + + [[nodiscard]] auto hardware_id() const -> std::uint64_t; + [[nodiscard]] auto is_bsp() const -> bool; + + private: + std::uint64_t m_hardware_id; + bool m_is_bsp; + }; + + explicit cpu(std::size_t major_number); + + auto probe() -> bool final; + }; + +} // namespace kernel::devices + +#endif \ No newline at end of file diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp index 998d7a0..1283d7e 100644 --- a/kernel/kapi/acpi.cpp +++ b/kernel/kapi/acpi.cpp @@ -5,6 +5,7 @@ #include "kernel/acpi/manager.hpp" +#include #include #include @@ -110,12 +111,47 @@ namespace kapi::acpi return {m_signature.data(), m_signature.size()}; } + [[nodiscard]] auto madt_header::local_interrupt_controller_address() const noexcept -> memory::physical_address + { + return memory::physical_address{static_cast(m_local_interrupt_controller_address)}; + } + + [[nodiscard]] auto madt_header::flags() const noexcept -> std::uint32_t + { + return m_flags; + } + + [[nodiscard]] auto madt_subtable_header::type() const noexcept -> std::uint8_t + { + return m_type; + } + + [[nodiscard]] auto madt_subtable_header::length() const noexcept -> std::size_t + { + return m_length; + } + + [[nodiscard]] auto madt_local_apic::apic_id() const noexcept -> std::uint8_t + { + return m_apic_id; + } + + [[nodiscard]] auto madt_local_apic::flags() const noexcept -> std::uint32_t + { + return m_flags; + } + + [[nodiscard]] auto madt_local_apic::processor_id() const noexcept -> std::uint32_t + { + return m_processor_id; + } + auto init(root_system_description_pointer const & sdp) -> bool { auto static constinit initialized = std::atomic_flag{}; if (initialized.test_and_set()) { - system::panic("[OS::ACPI] The ACPI manager has already been initialized!"); + system::panic("[OS:ACPI] The ACPI manager has already been initialized!"); } manager.emplace(sdp); @@ -130,4 +166,9 @@ namespace kapi::acpi return sum == 0; } + auto get_table(std::string_view signature) -> kstd::observer_ptr + { + return manager->get_table(signature); + } + }; // namespace kapi::acpi diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index 031f2c9..b2911b0 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -1,7 +1,9 @@ #include "kapi/devices.hpp" +#include "kapi/acpi.hpp" #include "kapi/system.hpp" +#include "kernel/devices/cpu.hpp" #include "kernel/devices/root_bus.hpp" #include @@ -35,6 +37,14 @@ namespace kapi::devices auto & bus = root_bus.emplace(); register_device(bus); bus.init(); + + auto madt = kapi::acpi::get_table("APIC"); + if (madt) + { + auto cpu_major = allocate_major_number(); + auto cpu = kstd::make_unique(cpu_major); + bus.add_child(std::move(cpu)); + } } auto get_root_bus() -> bus & diff --git a/kernel/src/acpi/manager.cpp b/kernel/src/acpi/manager.cpp index 180d458..300b85e 100644 --- a/kernel/src/acpi/manager.cpp +++ b/kernel/src/acpi/manager.cpp @@ -4,10 +4,12 @@ #include "kapi/memory.hpp" #include "kapi/system.hpp" +#include #include #include #include +#include namespace kernel::acpi { @@ -80,8 +82,7 @@ namespace kernel::acpi } else { - kstd::println("[OS:ACPI] Found table: {}@{:#x}/{:#x} OEM: {} Rev: {}", table->signature(), linear_table_address, - physical_table_address, table->oem_id(), table->creator_revision()); + kstd::println("[OS:ACPI] Found '{}' ACPI table", table->signature()); m_tables.emplace(table->signature(), table); } } @@ -89,4 +90,14 @@ namespace kernel::acpi return !m_tables.empty(); } + auto manager::get_table(std::string_view signature) + -> kstd::observer_ptr + { + if (m_tables.contains(signature)) + { + return kstd::make_observer(m_tables.at(signature)); + } + return nullptr; + } + } // namespace kernel::acpi diff --git a/kernel/src/devices/cpu.cpp b/kernel/src/devices/cpu.cpp new file mode 100644 index 0000000..7147368 --- /dev/null +++ b/kernel/src/devices/cpu.cpp @@ -0,0 +1,97 @@ +#include "kernel/devices/cpu.hpp" + +#include "kapi/acpi.hpp" +#include "kapi/devices.hpp" + +#include +#include + +#include +#include +#include + +namespace kernel::devices +{ + + namespace + { + + auto process_madt(kstd::observer_ptr madt, kapi::devices::bus & cpu) -> void + { + auto static const core_major = kapi::devices::allocate_major_number(); + auto static const lapic_major = kapi::devices::allocate_major_number(); + + auto lapic_address = madt->local_interrupt_controller_address(); + + auto const * current = reinterpret_cast(madt.get()) + sizeof(kapi::acpi::madt_header); + auto const * end = reinterpret_cast(madt.get()) + madt->length(); + + auto bsp_found = false; + auto core_index = 0; + + while (current < end) + { + auto const * sub_table = reinterpret_cast(current); + if (sub_table->type() == 0) + { + auto const * local_apic = reinterpret_cast(sub_table); + if (local_apic->flags() & 0b11) + { + auto is_bsp = !bsp_found; + bsp_found = true; + + auto apic_id = local_apic->apic_id(); + auto core = kstd::make_unique(core_major, core_index, apic_id, is_bsp); + auto lapic = kapi::acpi::create_local_interrupt_controller(lapic_major, core_index, apic_id, lapic_address); + + core->add_child(std::move(lapic)); + cpu.add_child(std::move(core)); + + ++core_index; + } + } + + current += sub_table->length(); + } + + kstd::println("[OS:DEV] Discovered {} CPU cores", core_index); + } + + } // namespace + + cpu::core::core(std::size_t major_number, std::size_t minor_number, std::uint64_t hardware_id, bool is_bsp) + : kapi::devices::bus{major_number, minor_number, "cpu_core"} + , m_hardware_id{hardware_id} + , m_is_bsp{is_bsp} + {} + + auto cpu::core::hardware_id() const -> std::uint64_t + { + return m_hardware_id; + } + + auto cpu::core::is_bsp() const -> bool + { + return m_is_bsp; + } + + cpu::cpu(std::size_t major_number) + : kapi::devices::bus{major_number, 0, "cpu"} + {} + + auto cpu::probe() -> bool + { + auto madt = kapi::acpi::get_table("APIC"); + if (!madt) + { + kstd::println("[OS:DEV] Failed to find ACPI APIC table"); + return false; + } + + auto madt_header = static_cast(madt.get()); + process_madt(kstd::make_observer(madt_header), *this); + + return true; + } + +} // namespace kernel::devices \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 2eaa2d8..e704955 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,3 +1,4 @@ +#include "kapi/acpi.hpp" #include "kapi/boot_modules.hpp" #include "kapi/cio.hpp" #include "kapi/cpu.hpp" @@ -179,6 +180,15 @@ auto main() -> int kstd::println("[OS] Memory subsystem initialized."); kapi::system::memory_initialized(); + auto acpi_root_pointer = kapi::acpi::get_root_pointer(); + if (acpi_root_pointer && acpi_root_pointer->validate()) + { + if (kapi::acpi::init(*acpi_root_pointer)) + { + kstd::println("[OS] ACPI subsystem initialized."); + } + } + kapi::devices::init(); kstd::println("[OS] System root bus initialized."); -- cgit v1.2.3 From 8d08b3b905d211e989848e1abf3a8ff2535836c8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 18:13:21 +0200 Subject: kapi: extract more code to the kernel --- kapi/include/kapi/devices/bus.hpp | 54 ++++-------------------------- kapi/include/kapi/devices/device.hpp | 28 ++++------------ kernel/CMakeLists.txt | 2 ++ kernel/kapi/devices/bus.cpp | 65 ++++++++++++++++++++++++++++++++++++ kernel/kapi/devices/device.cpp | 35 +++++++++++++++++++ 5 files changed, 114 insertions(+), 70 deletions(-) create mode 100644 kernel/kapi/devices/bus.cpp create mode 100644 kernel/kapi/devices/device.cpp diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index a384291..7b2d41f 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -4,23 +4,19 @@ // IWYU pragma: private, include "kapi/devices.hpp" #include "kapi/devices/device.hpp" -#include "kapi/devices/manager.hpp" -#include "kapi/system.hpp" #include #include #include #include -#include #include #include -#include namespace kapi::devices { - //! @addtogroup kapi-devices + //! @addtogroup kapi-devices-kernel-defined //! @{ //! A bus device that represents a logical/physical tree of devices and busses. @@ -31,65 +27,27 @@ namespace kapi::devices //! @param major The major number of the bus. //! @param minor The minor number of the bus. //! @param name The name of the bus. - bus(std::size_t major, std::size_t minor, kstd::string const & name) - : device(major, minor, name) - {} + bus(std::size_t major, std::size_t minor, kstd::string const & name); //! Initialize the bus and all of its children. //! //! @return true iff. the bus and all of its children are healthy, false otherwise. - auto init() -> bool final - { - if (m_initialized.test_and_set()) - { - return true; - } - - if (!probe()) - { - return false; - } - - return std::ranges::fold_left(m_devices, true, [&](bool acc, auto & child) -> bool { - kstd::println("[kAPI:BUS] Initializing child device {}@{}", child->name(), name()); - return child->init() && acc; - }); - } + auto init() -> bool final; //! Attach a child device to this bus. //! //! Whenever a device is attached to a bus, the bus takes sole ownership of the device. //! //! @param child The child device to attach. - auto add_child(kstd::unique_ptr child) -> void - { - auto observer = m_observers.emplace_back(child.get()); - m_devices.push_back(std::move(child)); - kapi::devices::register_device(*observer); - - if (m_initialized.test()) - { - kstd::println("[kAPI:BUS] Initializing child device {}@{}", observer->name(), name()); - if (!observer->init()) - { - kapi::system::panic("[kAPI:BUS] Failed to initialize child device"); - } - } - } + auto add_child(kstd::unique_ptr child) -> void; - [[nodiscard]] auto children() const -> kstd::vector> const & - { - return m_observers; - } + [[nodiscard]] auto children() const -> kstd::vector> const &; protected: //! Probe the bus hardware state. //! //! @return true iff. the bus hardware is healthy, false otherwise. - auto virtual probe() -> bool - { - return true; - } + auto virtual probe() -> bool; private: kstd::vector> m_devices; diff --git a/kapi/include/kapi/devices/device.hpp b/kapi/include/kapi/devices/device.hpp index ca5033e..b3647da 100644 --- a/kapi/include/kapi/devices/device.hpp +++ b/kapi/include/kapi/devices/device.hpp @@ -10,7 +10,7 @@ namespace kapi::devices { - //! @addtogroup kapi-devices + //! @addtogroup kapi-devices-kernel-defined //! @{ /** @@ -24,11 +24,7 @@ namespace kapi::devices * @param minor Device minor number. * @param name Device name. */ - device(size_t major, size_t minor, kstd::string const & name) - : m_major(major) - , m_minor(minor) - , m_name(name) - {} + device(size_t major, size_t minor, kstd::string const & name); /** * @brief Virtual destructor for device. @@ -45,37 +41,25 @@ namespace kapi::devices * @brief Returns the major number of the device. * @return Device major number. */ - [[nodiscard]] auto major() const -> size_t - { - return m_major; - } + [[nodiscard]] auto major() const -> size_t; /** * @brief Returns the minor number of the device. * @return Device minor number. */ - [[nodiscard]] auto minor() const -> size_t - { - return m_minor; - } + [[nodiscard]] auto minor() const -> size_t; /** * @brief Returns the name of the device. * @return Device name. */ - [[nodiscard]] auto name() const -> kstd::string const & - { - return m_name; - } + [[nodiscard]] auto name() const -> kstd::string const &; /** * @brief Check if the device is a block device. * @return true if this device is a block device, false otherwise. */ - [[nodiscard]] virtual auto is_block_device() const -> bool - { - return false; - } + [[nodiscard]] virtual auto is_block_device() const -> bool; private: size_t m_major; diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 92e2bda..6e081bd 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -5,6 +5,8 @@ add_library("kernel_objs" OBJECT "kapi/cio.cpp" "kapi/cpu.cpp" "kapi/devices.cpp" + "kapi/devices/bus.cpp" + "kapi/devices/device.cpp" "kapi/interrupts.cpp" "kapi/memory.cpp" "kapi/system.cpp" diff --git a/kernel/kapi/devices/bus.cpp b/kernel/kapi/devices/bus.cpp new file mode 100644 index 0000000..68874c4 --- /dev/null +++ b/kernel/kapi/devices/bus.cpp @@ -0,0 +1,65 @@ +#include "kapi/devices/bus.hpp" + +#include "kapi/devices.hpp" +#include "kapi/system.hpp" + +#include +#include +#include +#include + +#include +#include +#include + +namespace kapi::devices +{ + bus::bus(std::size_t major, std::size_t minor, kstd::string const & name) + : device(major, minor, name) + {} + + auto bus::init() -> bool + { + if (m_initialized.test_and_set()) + { + return true; + } + + if (!probe()) + { + return false; + } + + return std::ranges::fold_left(m_devices, true, [&](bool acc, auto & child) -> bool { + kstd::println("[OS:DEV] Initializing child device {}@{}", child->name(), name()); + return child->init() && acc; + }); + } + + auto bus::add_child(kstd::unique_ptr child) -> void + { + auto observer = m_observers.emplace_back(child.get()); + m_devices.push_back(std::move(child)); + kapi::devices::register_device(*observer); + + if (m_initialized.test()) + { + kstd::println("[OS:DEV] Initializing child device {}@{}", observer->name(), name()); + if (!observer->init()) + { + kapi::system::panic("[OS:DEV] Failed to initialize child device"); + } + } + } + + [[nodiscard]] auto bus::children() const -> kstd::vector> const & + { + return m_observers; + } + + auto bus::probe() -> bool + { + return true; + } + +} // namespace kapi::devices \ No newline at end of file diff --git a/kernel/kapi/devices/device.cpp b/kernel/kapi/devices/device.cpp new file mode 100644 index 0000000..9f7a404 --- /dev/null +++ b/kernel/kapi/devices/device.cpp @@ -0,0 +1,35 @@ +#include "kapi/devices/device.hpp" + +#include + +#include + +namespace kapi::devices +{ + device::device(size_t major, size_t minor, kstd::string const & name) + : m_major(major) + , m_minor(minor) + , m_name(name) + {} + + [[nodiscard]] auto device::major() const -> size_t + { + return m_major; + } + + [[nodiscard]] auto device::minor() const -> size_t + { + return m_minor; + } + + [[nodiscard]] auto device::name() const -> kstd::string const & + { + return m_name; + } + + [[nodiscard]] auto device::is_block_device() const -> bool + { + return false; + } + +} // namespace kapi::devices \ No newline at end of file -- cgit v1.2.3 From f50815110789a0f8f6e5ca66ffd49b26578791a9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 18:43:28 +0200 Subject: kernel: generalize CPU discovery --- arch/x86_64/CMakeLists.txt | 1 + arch/x86_64/kapi/acpi.cpp | 13 -------- arch/x86_64/kapi/platform.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++ kapi/include/kapi/acpi.hpp | 4 --- kapi/include/kapi/platform.hpp | 37 ++++++++++++++++++++++ kernel/CMakeLists.txt | 1 + kernel/kapi/devices.cpp | 11 ++----- kernel/kapi/platform.cpp | 32 +++++++++++++++++++ kernel/src/devices/cpu.cpp | 58 ++-------------------------------- 9 files changed, 147 insertions(+), 80 deletions(-) create mode 100644 arch/x86_64/kapi/platform.cpp create mode 100644 kapi/include/kapi/platform.hpp create mode 100644 kernel/kapi/platform.cpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 62a2aad..87cb98c 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -19,6 +19,7 @@ target_sources("x86_64" PRIVATE "kapi/devices.cpp" "kapi/interrupts.cpp" "kapi/memory.cpp" + "kapi/platform.cpp" "kapi/system.cpp" # CPU Initialization diff --git a/arch/x86_64/kapi/acpi.cpp b/arch/x86_64/kapi/acpi.cpp index 9766154..ec38aee 100644 --- a/arch/x86_64/kapi/acpi.cpp +++ b/arch/x86_64/kapi/acpi.cpp @@ -1,16 +1,9 @@ #include "kapi/acpi.hpp" -#include "kapi/devices.hpp" -#include "kapi/memory.hpp" - #include "arch/boot/boot.hpp" -#include "arch/devices/local_apic.hpp" #include -#include -#include - namespace kapi::acpi { @@ -34,10 +27,4 @@ namespace kapi::acpi return kstd::make_observer(system_description_pointer); } - auto create_local_interrupt_controller(std::size_t major, std::size_t minor, std::uint64_t hardware_id, - memory::physical_address address) -> kstd::unique_ptr - { - return kstd::make_unique(major, minor, hardware_id, address); - } - } // namespace kapi::acpi \ No newline at end of file diff --git a/arch/x86_64/kapi/platform.cpp b/arch/x86_64/kapi/platform.cpp new file mode 100644 index 0000000..2e7c7e4 --- /dev/null +++ b/arch/x86_64/kapi/platform.cpp @@ -0,0 +1,70 @@ +#include "kapi/platform.hpp" + +#include "kapi/acpi.hpp" +#include "kapi/devices.hpp" +#include "kapi/memory.hpp" + +#include "arch/devices/local_apic.hpp" + +#include +#include + +#include +#include + +namespace kapi::platform +{ + + namespace + { + constexpr auto default_lapic_address = kapi::memory::physical_address{0xFEE0'0000}; + } + + auto discover_cpu_topology(kapi::devices::bus & bus) -> bool + { + auto madt = kapi::acpi::get_table("APIC"); + if (!madt) + { + kstd::println("[x86_64:PLT] Failed to find ACPI APIC table"); + return false; + } + + auto const * current = reinterpret_cast(madt.get()) + sizeof(kapi::acpi::madt_header); + auto const * end = reinterpret_cast(madt.get()) + madt->length(); + + auto bsp_found = false; + auto core_count = 0uz; + + while (current < end) + { + auto const * sub_table = reinterpret_cast(current); + if (sub_table->type() == 0) + { + auto const * local_apic = reinterpret_cast(sub_table); + if (local_apic->flags() & 0b11) + { + auto is_bsp = !bsp_found; + bsp_found = true; + + if (kapi::platform::cpu_detected(bus, local_apic->processor_id(), is_bsp)) + { + ++core_count; + } + } + } + + current += sub_table->length(); + } + + kstd::println("[x86_64:PLT] Found {} CPU cores", core_count); + + return core_count > 0; + } + + auto create_core_interrupt_controller(std::size_t major, std::size_t minor, std::uint64_t hardware_id) + -> kstd::unique_ptr + { + return kstd::make_unique(major, minor, hardware_id, default_lapic_address); + } + +} // namespace kapi::platform \ No newline at end of file diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index 5d55d08..75b6c48 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -1,7 +1,6 @@ #ifndef TEACHOS_KAPI_ACPI_HPP #define TEACHOS_KAPI_ACPI_HPP -#include "kapi/devices.hpp" #include "kapi/memory.hpp" #include @@ -128,9 +127,6 @@ namespace kapi::acpi auto get_root_pointer() -> kstd::observer_ptr; - auto create_local_interrupt_controller(std::size_t major, std::size_t minor, std::uint64_t hardware_id, - memory::physical_address address) -> kstd::unique_ptr; - //! @} } // namespace kapi::acpi diff --git a/kapi/include/kapi/platform.hpp b/kapi/include/kapi/platform.hpp new file mode 100644 index 0000000..6aae795 --- /dev/null +++ b/kapi/include/kapi/platform.hpp @@ -0,0 +1,37 @@ +#ifndef TEACHOS_KAPI_PLATFORM_HPP +#define TEACHOS_KAPI_PLATFORM_HPP + +#include "kapi/devices.hpp" + +#include + +#include +#include + +namespace kapi::platform +{ + + //! @addtogroup kapi-platform-kernel-defined + //! @{ + + auto cpu_detected(kapi::devices::bus & bus, std::uint64_t hardware_id, bool is_bsp) -> bool; + + //! @} + + //! @addtogroup kapi-platform-platform-defined + //! @{ + + //! Discover the CPU topology of the platform and attach it to the given CPU bus. + //! + //! @param bus The bus to attach the CPU topology to. + //! @return true iff. the CPU topology was discovered successfully, false otherwise. + auto discover_cpu_topology(kapi::devices::bus & bus) -> bool; + + auto create_core_interrupt_controller(std::size_t major, std::size_t minor, std::uint64_t hardware_id) + -> kstd::unique_ptr; + + //! @} + +} // namespace kapi::platform + +#endif \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 6e081bd..22513ea 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -9,6 +9,7 @@ add_library("kernel_objs" OBJECT "kapi/devices/device.cpp" "kapi/interrupts.cpp" "kapi/memory.cpp" + "kapi/platform.cpp" "kapi/system.cpp" # KSTD OS Implementation diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index b2911b0..dad1fe4 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -1,6 +1,5 @@ #include "kapi/devices.hpp" -#include "kapi/acpi.hpp" #include "kapi/system.hpp" #include "kernel/devices/cpu.hpp" @@ -38,13 +37,9 @@ namespace kapi::devices register_device(bus); bus.init(); - auto madt = kapi::acpi::get_table("APIC"); - if (madt) - { - auto cpu_major = allocate_major_number(); - auto cpu = kstd::make_unique(cpu_major); - bus.add_child(std::move(cpu)); - } + auto cpu_major = allocate_major_number(); + auto cpu = kstd::make_unique(cpu_major); + bus.add_child(std::move(cpu)); } auto get_root_bus() -> bus & diff --git a/kernel/kapi/platform.cpp b/kernel/kapi/platform.cpp new file mode 100644 index 0000000..deb72de --- /dev/null +++ b/kernel/kapi/platform.cpp @@ -0,0 +1,32 @@ +#include "kapi/platform.hpp" + +#include "kapi/devices.hpp" + +#include "kernel/devices/cpu.hpp" + +#include + +#include +#include + +namespace kapi::platform +{ + + auto cpu_detected(kapi::devices::bus & bus, std::uint64_t hardware_id, bool is_bsp) -> bool + { + auto static const core_major = kapi::devices::allocate_major_number(); + auto static const interrupt_controller_major = kapi::devices::allocate_major_number(); + auto static core_index = 0uz; + + auto core = kstd::make_unique(core_major, core_index, hardware_id, is_bsp); + auto lapic = kapi::platform::create_core_interrupt_controller(interrupt_controller_major, core_index, hardware_id); + + core->add_child(std::move(lapic)); + bus.add_child(std::move(core)); + + ++core_index; + + return true; + } + +} // namespace kapi::platform \ No newline at end of file diff --git a/kernel/src/devices/cpu.cpp b/kernel/src/devices/cpu.cpp index 7147368..eb10d74 100644 --- a/kernel/src/devices/cpu.cpp +++ b/kernel/src/devices/cpu.cpp @@ -1,64 +1,16 @@ #include "kernel/devices/cpu.hpp" -#include "kapi/acpi.hpp" #include "kapi/devices.hpp" +#include "kapi/platform.hpp" -#include #include #include #include -#include namespace kernel::devices { - namespace - { - - auto process_madt(kstd::observer_ptr madt, kapi::devices::bus & cpu) -> void - { - auto static const core_major = kapi::devices::allocate_major_number(); - auto static const lapic_major = kapi::devices::allocate_major_number(); - - auto lapic_address = madt->local_interrupt_controller_address(); - - auto const * current = reinterpret_cast(madt.get()) + sizeof(kapi::acpi::madt_header); - auto const * end = reinterpret_cast(madt.get()) + madt->length(); - - auto bsp_found = false; - auto core_index = 0; - - while (current < end) - { - auto const * sub_table = reinterpret_cast(current); - if (sub_table->type() == 0) - { - auto const * local_apic = reinterpret_cast(sub_table); - if (local_apic->flags() & 0b11) - { - auto is_bsp = !bsp_found; - bsp_found = true; - - auto apic_id = local_apic->apic_id(); - auto core = kstd::make_unique(core_major, core_index, apic_id, is_bsp); - auto lapic = kapi::acpi::create_local_interrupt_controller(lapic_major, core_index, apic_id, lapic_address); - - core->add_child(std::move(lapic)); - cpu.add_child(std::move(core)); - - ++core_index; - } - } - - current += sub_table->length(); - } - - kstd::println("[OS:DEV] Discovered {} CPU cores", core_index); - } - - } // namespace - cpu::core::core(std::size_t major_number, std::size_t minor_number, std::uint64_t hardware_id, bool is_bsp) : kapi::devices::bus{major_number, minor_number, "cpu_core"} , m_hardware_id{hardware_id} @@ -81,16 +33,12 @@ namespace kernel::devices auto cpu::probe() -> bool { - auto madt = kapi::acpi::get_table("APIC"); - if (!madt) + if (!kapi::platform::discover_cpu_topology(*this)) { - kstd::println("[OS:DEV] Failed to find ACPI APIC table"); + kstd::println("[OS:DEV] Failed to discover CPU topology"); return false; } - auto madt_header = static_cast(madt.get()); - process_madt(kstd::make_observer(madt_header), *this); - return true; } -- cgit v1.2.3 From 6f9f070db1c4af517be93cf07c452e65d8cea8cf Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 18:46:47 +0200 Subject: qemu: enable multiple cores --- .vscode/tasks.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index e07afd2..d673a7a 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -11,6 +11,8 @@ "32M", "-machine", "q35", + "-smp", + "4,sockets=1,cores=4,threads=1", "-display", "curses", "-debugcon", @@ -52,6 +54,8 @@ "32M", "-machine", "q35", + "-smp", + "4,sockets=1,cores=4,threads=1", "-display", "curses", "-debugcon", -- cgit v1.2.3 From d5c2e101d62f6b4b69c45c127e7a729d246da566 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 19:04:16 +0200 Subject: kapi/platform: invert discovery dependencies --- arch/x86_64/kapi/platform.cpp | 17 +++++++++-------- kapi/include/kapi/platform.hpp | 7 ++----- kernel/kapi/platform.cpp | 15 +++++---------- 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/arch/x86_64/kapi/platform.cpp b/arch/x86_64/kapi/platform.cpp index 2e7c7e4..380fc66 100644 --- a/arch/x86_64/kapi/platform.cpp +++ b/arch/x86_64/kapi/platform.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include namespace kapi::platform { @@ -22,6 +22,10 @@ namespace kapi::platform auto discover_cpu_topology(kapi::devices::bus & bus) -> bool { + auto static const core_major = kapi::devices::allocate_major_number(); + auto static const interrupt_controller_major = kapi::devices::allocate_major_number(); + auto static core_index = 0uz; + auto madt = kapi::acpi::get_table("APIC"); if (!madt) { @@ -46,7 +50,10 @@ namespace kapi::platform auto is_bsp = !bsp_found; bsp_found = true; - if (kapi::platform::cpu_detected(bus, local_apic->processor_id(), is_bsp)) + auto lapic = kstd::make_unique(interrupt_controller_major, core_index, + local_apic->apic_id(), default_lapic_address); + if (kapi::platform::cpu_detected(bus, core_major, core_index, local_apic->processor_id(), is_bsp, + std::move(lapic))) { ++core_count; } @@ -61,10 +68,4 @@ namespace kapi::platform return core_count > 0; } - auto create_core_interrupt_controller(std::size_t major, std::size_t minor, std::uint64_t hardware_id) - -> kstd::unique_ptr - { - return kstd::make_unique(major, minor, hardware_id, default_lapic_address); - } - } // namespace kapi::platform \ No newline at end of file diff --git a/kapi/include/kapi/platform.hpp b/kapi/include/kapi/platform.hpp index 6aae795..e1e267e 100644 --- a/kapi/include/kapi/platform.hpp +++ b/kapi/include/kapi/platform.hpp @@ -14,8 +14,8 @@ namespace kapi::platform //! @addtogroup kapi-platform-kernel-defined //! @{ - auto cpu_detected(kapi::devices::bus & bus, std::uint64_t hardware_id, bool is_bsp) -> bool; - + auto cpu_detected(kapi::devices::bus & bus, std::size_t major, std::size_t minor, std::uint64_t hardware_id, + bool is_bsp, kstd::unique_ptr core_interrupt_controller) -> bool; //! @} //! @addtogroup kapi-platform-platform-defined @@ -27,9 +27,6 @@ namespace kapi::platform //! @return true iff. the CPU topology was discovered successfully, false otherwise. auto discover_cpu_topology(kapi::devices::bus & bus) -> bool; - auto create_core_interrupt_controller(std::size_t major, std::size_t minor, std::uint64_t hardware_id) - -> kstd::unique_ptr; - //! @} } // namespace kapi::platform diff --git a/kernel/kapi/platform.cpp b/kernel/kapi/platform.cpp index deb72de..7638cf9 100644 --- a/kernel/kapi/platform.cpp +++ b/kernel/kapi/platform.cpp @@ -6,26 +6,21 @@ #include +#include #include #include namespace kapi::platform { - auto cpu_detected(kapi::devices::bus & bus, std::uint64_t hardware_id, bool is_bsp) -> bool + auto cpu_detected(kapi::devices::bus & bus, std::size_t major, std::size_t minor, std::uint64_t hardware_id, + bool is_bsp, kstd::unique_ptr core_interrupt_controller) -> bool { - auto static const core_major = kapi::devices::allocate_major_number(); - auto static const interrupt_controller_major = kapi::devices::allocate_major_number(); - auto static core_index = 0uz; + auto core = kstd::make_unique(major, minor, hardware_id, is_bsp); - auto core = kstd::make_unique(core_major, core_index, hardware_id, is_bsp); - auto lapic = kapi::platform::create_core_interrupt_controller(interrupt_controller_major, core_index, hardware_id); - - core->add_child(std::move(lapic)); + core->add_child(std::move(core_interrupt_controller)); bus.add_child(std::move(core)); - ++core_index; - return true; } -- cgit v1.2.3 From 8ad4fad2440c20aa19e26ade8cdb881cab7734e2 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 6 Apr 2026 19:09:34 +0200 Subject: kernel/tests: fix link issue --- .lazy.lua | 13 ++++++ .nvim.lua | 78 +++++++++++++++++++++++++++++++ kernel/CMakeLists.txt | 1 + kernel/src/test_support/kapi/platform.cpp | 14 ++++++ 4 files changed, 106 insertions(+) create mode 100644 .lazy.lua create mode 100644 .nvim.lua create mode 100644 kernel/src/test_support/kapi/platform.cpp diff --git a/.lazy.lua b/.lazy.lua new file mode 100644 index 0000000..1c2df6e --- /dev/null +++ b/.lazy.lua @@ -0,0 +1,13 @@ +return { + { + "nvim-neo-tree/neo-tree.nvim", + opts = { + filesystem = { + filtered_items = { + visible = false, + hide_gitignored = true, + }, + }, + } + } +} diff --git a/.nvim.lua b/.nvim.lua new file mode 100644 index 0000000..2cd34d6 --- /dev/null +++ b/.nvim.lua @@ -0,0 +1,78 @@ +local workspace_folder = vim.fn.getcwd() + +local function safe_require(module) + local ok, mod = pcall(require, module) + if not ok then return nil end + return mod +end + +-- C++ +local default_clangd_config = vim.deepcopy(vim.lsp.config["clangd"]) or {} +default_clangd_config.cmd = { + "clangd", + "--background-index", + "--clang-tidy", + "--header-insertion=iwyu", + "--completion-style=detailed", + string.format("--compile-commands-dir=%s/build", workspace_folder), + "--query-driver=**/x86_64-pc-elf-g++" +} + +vim.lsp.config("clangd", default_clangd_config) + +vim.filetype.add({ + pattern = { + [".*/kstd/include/kstd/.*"] = "cpp", + } +}) + +-- Debugging +local dap = require("dap") +local qemu_job = nil + +dap.adapters.teachos_qemu_mi = function(callback, config) + if qemu_job then + qemu_job:kill(9) + end + + local artifact_path = config.program + if not artifact_path then + vim.notify("Fatal: No artifact path resolved by CMake", vim.log.levels.ERROR) + return + end + + local elf_path = string.gsub(artifact_path, "%.sym$", ".elf") + + qemu_job = vim.system({ + "qemu-system-x86_64", + "-kernel", elf_path, + "-s", "-S", + "-nographic" + }, { text = true }) + + vim.defer_fn(function() + callback({ + type = "executable", + command = vim.fn.stdpath("data") .. "/mason/bin/OpenDebugAD7", + }) + end, 500) +end + +dap.listeners.after.event_terminated["teachos_qemu_teardown"] = function() + if qemu_job then + qemu_job:kill(9) + qemu_job = nil + end +end + +require("cmake-tools").setup({ + cmake_dap_configuration = { + name = "(gdb) QEMU MI Attach", + type = "teachos_qemu_mi", + request = "launch", + miDebuggerServerAddress = "localhost:1234", + miDebuggerPath = "x86_64-pc-elf-gdb", + stopAtEntry = true, + } +}) + diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 22513ea..ef73586 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -101,6 +101,7 @@ else() "src/test_support/kapi/cio.cpp" "src/test_support/kapi/interrupts.cpp" "src/test_support/kapi/memory.cpp" + "src/test_support/kapi/platform.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" "src/test_support/page_mapper.cpp" diff --git a/kernel/src/test_support/kapi/platform.cpp b/kernel/src/test_support/kapi/platform.cpp new file mode 100644 index 0000000..497c992 --- /dev/null +++ b/kernel/src/test_support/kapi/platform.cpp @@ -0,0 +1,14 @@ +#include "kapi/platform.hpp" + +#include "kapi/devices.hpp" + +namespace kapi::platform +{ + + auto discover_cpu_topology(devices::bus &) -> bool + { + // TODO: implement more meaningful simulated CPU topology discovery + return true; + } + +} // namespace kapi::platform \ No newline at end of file -- cgit v1.2.3 From 482f431213b77b3ec72d4cfb5c77e23b34e8e68f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 7 Apr 2026 13:50:17 +0200 Subject: ide: enable debugging in neovim --- .nvim.lua | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 94 insertions(+), 15 deletions(-) diff --git a/.nvim.lua b/.nvim.lua index 2cd34d6..1cec8af 100644 --- a/.nvim.lua +++ b/.nvim.lua @@ -28,51 +28,130 @@ vim.filetype.add({ -- Debugging local dap = require("dap") -local qemu_job = nil +local qemu_job_id = nil +local qemu_bufnr = nil -dap.adapters.teachos_qemu_mi = function(callback, config) - if qemu_job then - qemu_job:kill(9) +local function resolve_build_paths(artifact_path) + local base_path = string.gsub(artifact_path, "%.[%w]+$", "") + local build_type = artifact_path:match("bin/([^/]+)/") or "Debug" + return { + iso = base_path .. ".iso", + elf = base_path .. ".elf", + sym = base_path .. ".sym", + build_type = build_type, + } +end + +local function launch_qemu(artifact_path, is_debug) + if qemu_job_id then + vim.fn.jobstop(qemu_job_id) + qemu_job_id = nil + end + + local paths = resolve_build_paths(artifact_path) + + local cmd = { + "qemu-system-x86_64", + "-m", "32M", + "-machine", "q35", + "-smp", "4,sockets=1,cores=4,threads=1", + "-display", "curses", + "-debugcon", string.format("file:%s/qemu-debugcon-%s.log", workspace_folder, paths.build_type), + "-cdrom", paths.iso + } + + if is_debug then + table.insert(cmd, 2, "-s") + table.insert(cmd, 3, "-no-reboot") + table.insert(cmd, 4, "-d") + table.insert(cmd, 5, "int,cpu_reset") end + local shell_cmd = string.format("%s 2>%s/qemu-stderr-%s.log", table.concat(cmd, " "), workspace_folder, paths.build_type) + + vim.cmd("botright vsplit") + vim.cmd("enew") + qemu_bufnr = vim.api.nvim_get_current_buf() + + vim.api.nvim_buf_set_name(qemu_bufnr, "TeachOS QEMU") + vim.opt_local.number = false + vim.opt_local.relativenumber = false + vim.opt_local.signcolumn = "no" + + qemu_job_id = vim.fn.termopen({ "bash", "-c", shell_cmd }, { + on_exit = function() + qemu_job_id = nil + end + }) + + vim.cmd("wincmd p") +end + +dap.adapters.teachos_qemu_mi = function(callback, config) local artifact_path = config.program if not artifact_path then vim.notify("Fatal: No artifact path resolved by CMake", vim.log.levels.ERROR) return end - local elf_path = string.gsub(artifact_path, "%.sym$", ".elf") + launch_qemu(artifact_path, true) - qemu_job = vim.system({ - "qemu-system-x86_64", - "-kernel", elf_path, - "-s", "-S", - "-nographic" - }, { text = true }) + local paths = resolve_build_paths(artifact_path) + config.setupCommands = { + { description = "Enable pretty-printing for gdb", text = "-enable-pretty-printing" }, + { description = "Load code", text = string.format("-file-exec-file %s", paths.elf) }, + { description = "Load symbols", text = string.format("-file-exec-and-symbols %s", paths.sym) }, + } + config.program = paths.sym vim.defer_fn(function() callback({ type = "executable", command = vim.fn.stdpath("data") .. "/mason/bin/OpenDebugAD7", + id = "cppdbg", }) end, 500) end -dap.listeners.after.event_terminated["teachos_qemu_teardown"] = function() - if qemu_job then - qemu_job:kill(9) - qemu_job = nil +local function teardown_qemu() + if qemu_job_id then + vim.fn.jobstop(qemu_job_id) + qemu_job_id = nil + end + + if qemu_bufnr and vim.api.nvim_buf_is_valid(qemu_bufnr) then + vim.api.nvim_buf_delete(qemu_bufnr, { force = true }) + qemu_bufnr = nil end end +dap.listeners.after.event_terminated["teachos_qemu_teardown"] = teardown_qemu +dap.listeners.after.event_exited["teachos_qemu_teardown"] = teardown_qemu +dap.listeners.after.disconnect["teachos_qemu_teardown"] = teardown_qemu + require("cmake-tools").setup({ cmake_dap_configuration = { name = "(gdb) QEMU MI Attach", type = "teachos_qemu_mi", request = "launch", + MIMode = "gdb", + cwd = workspace_folder, miDebuggerServerAddress = "localhost:1234", miDebuggerPath = "x86_64-pc-elf-gdb", stopAtEntry = true, } }) +vim.api.nvim_create_user_command("TeachOSRun", function() + local cmake = safe_require("cmake-tools") + if not cmake then return end + + local is_ready, target = pcall(cmake.get_launch_target_path) + if is_ready and target then + launch_qemu(target, false) + else + vim.notify("Fatal: Cannot determine CMake target path.", vim.log.levels.ERROR) + end +end, {}) + +vim.keymap.set('n', '', 'TeachOSRun', { noremap = true, desc = "Run TeachOS QEMU (No Debug)" }) -- cgit v1.2.3 From 3bf3c748eec38c776dd3c54cbb30be1d44ffb5c3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 7 Apr 2026 14:01:18 +0200 Subject: ide: simplify overall configuration --- .nvim.lua | 34 +++++++--------------------------- .vscode/tasks.json | 36 +++++++----------------------------- scripts/qemu-wrapper.sh | 30 ++++++++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 58 deletions(-) diff --git a/.nvim.lua b/.nvim.lua index 1cec8af..ba8f78b 100644 --- a/.nvim.lua +++ b/.nvim.lua @@ -49,39 +49,19 @@ local function launch_qemu(artifact_path, is_debug) end local paths = resolve_build_paths(artifact_path) + local debug_flag = is_debug and "1" or "0" + local shell_cmd = string.format("%s/scripts/qemu-wrapper.sh '%s' '%s' '%s' '%s'", + workspace_folder, workspace_folder, paths.build_type, paths.iso, debug_flag) - local cmd = { - "qemu-system-x86_64", - "-m", "32M", - "-machine", "q35", - "-smp", "4,sockets=1,cores=4,threads=1", - "-display", "curses", - "-debugcon", string.format("file:%s/qemu-debugcon-%s.log", workspace_folder, paths.build_type), - "-cdrom", paths.iso - } - - if is_debug then - table.insert(cmd, 2, "-s") - table.insert(cmd, 3, "-no-reboot") - table.insert(cmd, 4, "-d") - table.insert(cmd, 5, "int,cpu_reset") - end - - local shell_cmd = string.format("%s 2>%s/qemu-stderr-%s.log", table.concat(cmd, " "), workspace_folder, paths.build_type) - - vim.cmd("botright vsplit") - vim.cmd("enew") + vim.cmd("botright vsplit | enew") qemu_bufnr = vim.api.nvim_get_current_buf() - - vim.api.nvim_buf_set_name(qemu_bufnr, "TeachOS QEMU") + vim.api.nvim_buf_set_name(qemu_bufnr, "TeachOS QEMU (" .. paths.build_type .. ")") vim.opt_local.number = false vim.opt_local.relativenumber = false vim.opt_local.signcolumn = "no" - qemu_job_id = vim.fn.termopen({ "bash", "-c", shell_cmd }, { - on_exit = function() - qemu_job_id = nil - end + qemu_job_id = vim.fn.termopen(shell_cmd, { + on_exit = function() qemu_job_id = nil end }) vim.cmd("wincmd p") diff --git a/.vscode/tasks.json b/.vscode/tasks.json index d673a7a..1aa53d9 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -6,23 +6,10 @@ "command": "${workspaceFolder}/scripts/qemu-wrapper.sh", "type": "shell", "args": [ - "-s", - "-m", - "32M", - "-machine", - "q35", - "-smp", - "4,sockets=1,cores=4,threads=1", - "-display", - "curses", - "-debugcon", - "file:${workspaceFolder}/qemu-debugcon-${command:cmake.buildType}.log", - "-no-reboot", - "-d", - "int,cpu_reset", - "-cdrom", + "${workspaceFolder}", + "${command:cmake.buildType}", "${command:cmake.buildDirectory}/bin/${command:cmake.buildType}/kernel.iso", - "2>${workspaceFolder}/qemu-stderr-${command:cmake.buildType}.log" + "1" ], "isBackground": true, "presentation": { @@ -47,22 +34,13 @@ }, { "label": "QEMU", - "command": "qemu-system-x86_64", + "command": "${workspaceFolder}/scripts/qemu-wrapper.sh", "type": "shell", "args": [ - "-m", - "32M", - "-machine", - "q35", - "-smp", - "4,sockets=1,cores=4,threads=1", - "-display", - "curses", - "-debugcon", - "file:${workspaceFolder}/qemu-debugcon-${command:cmake.buildType}.log", - "-cdrom", + "${workspaceFolder}", + "${command:cmake.buildType}", "${command:cmake.buildDirectory}/bin/${command:cmake.buildType}/kernel.iso", - "2>${workspaceFolder}/qemu-stderr-${command:cmake.buildType}.log" + "0" ], "isBackground": true, "presentation": { diff --git a/scripts/qemu-wrapper.sh b/scripts/qemu-wrapper.sh index fa89ac5..49c01ec 100755 --- a/scripts/qemu-wrapper.sh +++ b/scripts/qemu-wrapper.sh @@ -1,4 +1,30 @@ #!/bin/bash +# scripts/qemu-wrapper.sh -echo "QEMU WRAPPER" -qemu-system-x86_64 $@ +WORKSPACE_DIR=${1:-.} +BUILD_TYPE=${2:-Debug} +ISO_PATH=$3 +IS_DEBUG=${4:-0} + +if [ -z "$ISO_PATH" ]; then + echo "Usage: $0 [is_debug]" + exit 1 +fi + +ARGS=( + "-m" "32M" + "-machine" "q35" + "-smp" "4,sockets=1,cores=4,threads=1" + "-display" "curses" + "-debugcon" "file:${WORKSPACE_DIR}/qemu-debugcon-${BUILD_TYPE}.log" + "-cdrom" "${ISO_PATH}" +) + +if [ "$IS_DEBUG" == "1" ]; then + ARGS=( "-s" "-no-reboot" "-d" "int,cpu_reset" "${ARGS[@]}" ) +fi + +echo "QEMU WRAPPER: Executing TeachOS" +echo "Arguments: ${ARGS[@]}" + +qemu-system-x86_64 "${ARGS[@]}" 2> "${WORKSPACE_DIR}/qemu-stderr-${BUILD_TYPE}.log" -- cgit v1.2.3 From 48cafb5656851bfee6a6cfa6b9fcb27d63151ae4 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 8 Apr 2026 07:35:29 +0200 Subject: docs: remove old documentation --- docs/pre/arch/x86_64.rst | 9 --------- docs/pre/arch/x86_64/boot.rst | 9 --------- docs/pre/arch/x86_64/boot/pointers.rst | 5 ----- docs/pre/arch/x86_64/context_switching.rst | 9 --------- .../x86_64/context_switching/interrupt_descriptor_table.rst | 9 --------- .../interrupt_descriptor_table/gate_descriptor.rst | 5 ----- .../interrupt_descriptor_table/idt_flags.rst | 5 ----- .../interrupt_descriptor_table copy.rst | 5 ----- .../interrupt_descriptor_table_pointer copy.rst | 5 ----- .../interrupt_descriptor_table/ist_offset.rst | 5 ----- .../interrupt_descriptor_table/segment_selector.rst | 5 ----- docs/pre/arch/x86_64/context_switching/main.rst | 5 ----- .../x86_64/context_switching/segment_descriptor_table.rst | 9 --------- .../segment_descriptor_table/access_byte.rst | 5 ----- .../context_switching/segment_descriptor_table/gdt_flags.rst | 5 ----- .../segment_descriptor_table/global_descriptor_table.rst | 5 ----- .../global_descriptor_table_pointer.rst | 5 ----- .../segment_descriptor_table/segment_descriptor_base.rst | 5 ----- .../segment_descriptor_table/segment_descriptor_extension.rst | 5 ----- .../segment_descriptor_table/segment_descriptor_type.rst | 5 ----- .../segment_descriptor_table/task_state_segment.rst | 5 ----- docs/pre/arch/x86_64/context_switching/syscall.rst | 9 --------- docs/pre/arch/x86_64/context_switching/syscall/main.rst | 5 ----- .../arch/x86_64/context_switching/syscall/syscall_enable.rst | 5 ----- .../arch/x86_64/context_switching/syscall/syscall_handler.rst | 5 ----- docs/pre/arch/x86_64/exception_handling.rst | 9 --------- docs/pre/arch/x86_64/exception_handling/assert.rst | 5 ----- docs/pre/arch/x86_64/exception_handling/panic.rst | 5 ----- docs/pre/arch/x86_64/interrupt_handling.rst | 9 --------- .../x86_64/interrupt_handling/generic_interrupt_handler.rst | 5 ----- docs/pre/arch/x86_64/io.rst | 9 --------- docs/pre/arch/x86_64/io/port_io.rst | 6 ------ docs/pre/arch/x86_64/kernel.rst | 9 --------- docs/pre/arch/x86_64/kernel/cpu.rst | 9 --------- docs/pre/arch/x86_64/kernel/cpu/call.rst | 5 ----- docs/pre/arch/x86_64/kernel/cpu/control_register.rst | 5 ----- docs/pre/arch/x86_64/kernel/cpu/gdtr.rst | 5 ----- docs/pre/arch/x86_64/kernel/cpu/idtr.rst | 5 ----- docs/pre/arch/x86_64/kernel/cpu/if.rst | 5 ----- docs/pre/arch/x86_64/kernel/cpu/msr.rst | 5 ----- docs/pre/arch/x86_64/kernel/cpu/segment_register.rst | 5 ----- docs/pre/arch/x86_64/kernel/cpu/tlb.rst | 5 ----- docs/pre/arch/x86_64/kernel/cpu/tr.rst | 5 ----- docs/pre/arch/x86_64/kernel/halt.rst | 5 ----- docs/pre/arch/x86_64/kernel/main.rst | 5 ----- docs/pre/arch/x86_64/memory.rst | 9 --------- docs/pre/arch/x86_64/memory/allocator.rst | 9 --------- .../pre/arch/x86_64/memory/allocator/area_frame_allocator.rst | 5 ----- docs/pre/arch/x86_64/memory/allocator/concept.rst | 5 ----- docs/pre/arch/x86_64/memory/allocator/physical_frame.rst | 5 ----- .../pre/arch/x86_64/memory/allocator/tiny_frame_allocator.rst | 5 ----- docs/pre/arch/x86_64/memory/cpu.rst | 9 --------- docs/pre/arch/x86_64/memory/heap.rst | 9 --------- docs/pre/arch/x86_64/memory/heap/bump_allocator.rst | 5 ----- docs/pre/arch/x86_64/memory/heap/global_heap_allocator.rst | 5 ----- docs/pre/arch/x86_64/memory/heap/heap_allocator.rst | 5 ----- docs/pre/arch/x86_64/memory/heap/linked_list_allocator.rst | 5 ----- docs/pre/arch/x86_64/memory/heap/memory_block.rst | 5 ----- docs/pre/arch/x86_64/memory/heap/user_heap_allocator.rst | 5 ----- docs/pre/arch/x86_64/memory/main.rst | 5 ----- docs/pre/arch/x86_64/memory/multiboot.rst | 9 --------- docs/pre/arch/x86_64/memory/multiboot/elf_symbols_section.rst | 5 ----- docs/pre/arch/x86_64/memory/multiboot/info.rst | 5 ----- docs/pre/arch/x86_64/memory/multiboot/memory_map.rst | 5 ----- docs/pre/arch/x86_64/memory/multiboot/reader.rst | 5 ----- docs/pre/arch/x86_64/memory/paging.rst | 9 --------- docs/pre/arch/x86_64/memory/paging/active_page_table.rst | 5 ----- docs/pre/arch/x86_64/memory/paging/inactive_page_table.rst | 5 ----- docs/pre/arch/x86_64/memory/paging/kernel_mapper.rst | 5 ----- docs/pre/arch/x86_64/memory/paging/page_entry.rst | 5 ----- docs/pre/arch/x86_64/memory/paging/page_table.rst | 5 ----- docs/pre/arch/x86_64/memory/paging/temporary_page.rst | 5 ----- docs/pre/arch/x86_64/memory/paging/virtual_page.rst | 5 ----- docs/pre/arch/x86_64/stl.rst | 9 --------- docs/pre/arch/x86_64/stl/container.rst | 5 ----- docs/pre/arch/x86_64/stl/contiguous_pointer_iterator.rst | 5 ----- docs/pre/arch/x86_64/stl/forward_value_iterator.rst | 5 ----- docs/pre/arch/x86_64/stl/mutex.rst | 5 ----- docs/pre/arch/x86_64/stl/shared_pointer.rst | 5 ----- docs/pre/arch/x86_64/stl/stack.rst | 5 ----- docs/pre/arch/x86_64/stl/unique_pointer.rst | 5 ----- docs/pre/arch/x86_64/stl/vector.rst | 5 ----- docs/pre/arch/x86_64/user.rst | 9 --------- docs/pre/arch/x86_64/user/main.rst | 5 ----- docs/pre/arch/x86_64/video.rst | 9 --------- docs/pre/arch/x86_64/video/vga.rst | 9 --------- docs/pre/arch/x86_64/video/vga/io.rst | 4 ---- docs/pre/arch/x86_64/video/vga/text.rst | 5 ----- docs/pre/cross/memory.rst | 11 ----------- docs/pre/cross/memory/asm_pointer.rst | 10 ---------- 90 files changed, 545 deletions(-) delete mode 100644 docs/pre/arch/x86_64.rst delete mode 100644 docs/pre/arch/x86_64/boot.rst delete mode 100644 docs/pre/arch/x86_64/boot/pointers.rst delete mode 100644 docs/pre/arch/x86_64/context_switching.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/gate_descriptor.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/idt_flags.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/interrupt_descriptor_table copy.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer copy.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/ist_offset.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/segment_selector.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/main.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/segment_descriptor_table.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/segment_descriptor_table/access_byte.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/segment_descriptor_table/gdt_flags.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/segment_descriptor_table/global_descriptor_table.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/segment_descriptor_table/global_descriptor_table_pointer.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/segment_descriptor_table/segment_descriptor_base.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/segment_descriptor_table/segment_descriptor_extension.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/segment_descriptor_table/segment_descriptor_type.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/segment_descriptor_table/task_state_segment.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/syscall.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/syscall/main.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/syscall/syscall_enable.rst delete mode 100644 docs/pre/arch/x86_64/context_switching/syscall/syscall_handler.rst delete mode 100644 docs/pre/arch/x86_64/exception_handling.rst delete mode 100644 docs/pre/arch/x86_64/exception_handling/assert.rst delete mode 100644 docs/pre/arch/x86_64/exception_handling/panic.rst delete mode 100644 docs/pre/arch/x86_64/interrupt_handling.rst delete mode 100644 docs/pre/arch/x86_64/interrupt_handling/generic_interrupt_handler.rst delete mode 100644 docs/pre/arch/x86_64/io.rst delete mode 100644 docs/pre/arch/x86_64/io/port_io.rst delete mode 100644 docs/pre/arch/x86_64/kernel.rst delete mode 100644 docs/pre/arch/x86_64/kernel/cpu.rst delete mode 100644 docs/pre/arch/x86_64/kernel/cpu/call.rst delete mode 100644 docs/pre/arch/x86_64/kernel/cpu/control_register.rst delete mode 100644 docs/pre/arch/x86_64/kernel/cpu/gdtr.rst delete mode 100644 docs/pre/arch/x86_64/kernel/cpu/idtr.rst delete mode 100644 docs/pre/arch/x86_64/kernel/cpu/if.rst delete mode 100644 docs/pre/arch/x86_64/kernel/cpu/msr.rst delete mode 100644 docs/pre/arch/x86_64/kernel/cpu/segment_register.rst delete mode 100644 docs/pre/arch/x86_64/kernel/cpu/tlb.rst delete mode 100644 docs/pre/arch/x86_64/kernel/cpu/tr.rst delete mode 100644 docs/pre/arch/x86_64/kernel/halt.rst delete mode 100644 docs/pre/arch/x86_64/kernel/main.rst delete mode 100644 docs/pre/arch/x86_64/memory.rst delete mode 100644 docs/pre/arch/x86_64/memory/allocator.rst delete mode 100644 docs/pre/arch/x86_64/memory/allocator/area_frame_allocator.rst delete mode 100644 docs/pre/arch/x86_64/memory/allocator/concept.rst delete mode 100644 docs/pre/arch/x86_64/memory/allocator/physical_frame.rst delete mode 100644 docs/pre/arch/x86_64/memory/allocator/tiny_frame_allocator.rst delete mode 100644 docs/pre/arch/x86_64/memory/cpu.rst delete mode 100644 docs/pre/arch/x86_64/memory/heap.rst delete mode 100644 docs/pre/arch/x86_64/memory/heap/bump_allocator.rst delete mode 100644 docs/pre/arch/x86_64/memory/heap/global_heap_allocator.rst delete mode 100644 docs/pre/arch/x86_64/memory/heap/heap_allocator.rst delete mode 100644 docs/pre/arch/x86_64/memory/heap/linked_list_allocator.rst delete mode 100644 docs/pre/arch/x86_64/memory/heap/memory_block.rst delete mode 100644 docs/pre/arch/x86_64/memory/heap/user_heap_allocator.rst delete mode 100644 docs/pre/arch/x86_64/memory/main.rst delete mode 100644 docs/pre/arch/x86_64/memory/multiboot.rst delete mode 100644 docs/pre/arch/x86_64/memory/multiboot/elf_symbols_section.rst delete mode 100644 docs/pre/arch/x86_64/memory/multiboot/info.rst delete mode 100644 docs/pre/arch/x86_64/memory/multiboot/memory_map.rst delete mode 100644 docs/pre/arch/x86_64/memory/multiboot/reader.rst delete mode 100644 docs/pre/arch/x86_64/memory/paging.rst delete mode 100644 docs/pre/arch/x86_64/memory/paging/active_page_table.rst delete mode 100644 docs/pre/arch/x86_64/memory/paging/inactive_page_table.rst delete mode 100644 docs/pre/arch/x86_64/memory/paging/kernel_mapper.rst delete mode 100644 docs/pre/arch/x86_64/memory/paging/page_entry.rst delete mode 100644 docs/pre/arch/x86_64/memory/paging/page_table.rst delete mode 100644 docs/pre/arch/x86_64/memory/paging/temporary_page.rst delete mode 100644 docs/pre/arch/x86_64/memory/paging/virtual_page.rst delete mode 100644 docs/pre/arch/x86_64/stl.rst delete mode 100644 docs/pre/arch/x86_64/stl/container.rst delete mode 100644 docs/pre/arch/x86_64/stl/contiguous_pointer_iterator.rst delete mode 100644 docs/pre/arch/x86_64/stl/forward_value_iterator.rst delete mode 100644 docs/pre/arch/x86_64/stl/mutex.rst delete mode 100644 docs/pre/arch/x86_64/stl/shared_pointer.rst delete mode 100644 docs/pre/arch/x86_64/stl/stack.rst delete mode 100644 docs/pre/arch/x86_64/stl/unique_pointer.rst delete mode 100644 docs/pre/arch/x86_64/stl/vector.rst delete mode 100644 docs/pre/arch/x86_64/user.rst delete mode 100644 docs/pre/arch/x86_64/user/main.rst delete mode 100644 docs/pre/arch/x86_64/video.rst delete mode 100644 docs/pre/arch/x86_64/video/vga.rst delete mode 100644 docs/pre/arch/x86_64/video/vga/io.rst delete mode 100644 docs/pre/arch/x86_64/video/vga/text.rst delete mode 100644 docs/pre/cross/memory.rst delete mode 100644 docs/pre/cross/memory/asm_pointer.rst diff --git a/docs/pre/arch/x86_64.rst b/docs/pre/arch/x86_64.rst deleted file mode 100644 index dc432f1..0000000 --- a/docs/pre/arch/x86_64.rst +++ /dev/null @@ -1,9 +0,0 @@ -x86_64 -====== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - x86_64/* \ No newline at end of file diff --git a/docs/pre/arch/x86_64/boot.rst b/docs/pre/arch/x86_64/boot.rst deleted file mode 100644 index 8be2a57..0000000 --- a/docs/pre/arch/x86_64/boot.rst +++ /dev/null @@ -1,9 +0,0 @@ -Boot Information Subsystem -====================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - boot/* \ No newline at end of file diff --git a/docs/pre/arch/x86_64/boot/pointers.rst b/docs/pre/arch/x86_64/boot/pointers.rst deleted file mode 100644 index 3ec626a..0000000 --- a/docs/pre/arch/x86_64/boot/pointers.rst +++ /dev/null @@ -1,5 +0,0 @@ -Boot Information Structure -======================= - -.. doxygenfile:: arch/x86_64/include/arch/boot/pointers.hpp - diff --git a/docs/pre/arch/x86_64/context_switching.rst b/docs/pre/arch/x86_64/context_switching.rst deleted file mode 100644 index c3b3b03..0000000 --- a/docs/pre/arch/x86_64/context_switching.rst +++ /dev/null @@ -1,9 +0,0 @@ -Context Switching Subsystem -====================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - context_switching/* \ No newline at end of file diff --git a/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table.rst b/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table.rst deleted file mode 100644 index dd6e478..0000000 --- a/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table.rst +++ /dev/null @@ -1,9 +0,0 @@ -Interrupt Descriptor Subsystem -=========== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - interrupt_descriptor_table/* diff --git a/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/gate_descriptor.rst b/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/gate_descriptor.rst deleted file mode 100644 index 29e7586..0000000 --- a/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/gate_descriptor.rst +++ /dev/null @@ -1,5 +0,0 @@ -Interrupt Gate Descriptor -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/gate_descriptor.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/idt_flags.rst b/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/idt_flags.rst deleted file mode 100644 index 60e8c37..0000000 --- a/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/idt_flags.rst +++ /dev/null @@ -1,5 +0,0 @@ -Interrupt Descriptor Flags -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/idt_flags.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/interrupt_descriptor_table copy.rst b/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/interrupt_descriptor_table copy.rst deleted file mode 100644 index a2b8997..0000000 --- a/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/interrupt_descriptor_table copy.rst +++ /dev/null @@ -1,5 +0,0 @@ -Interrupt Descriptor Table -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer copy.rst b/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer copy.rst deleted file mode 100644 index 3a8c259..0000000 --- a/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer copy.rst +++ /dev/null @@ -1,5 +0,0 @@ -Interrupt Descriptor Table Pointer -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table_pointer.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/ist_offset.rst b/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/ist_offset.rst deleted file mode 100644 index ddba6ee..0000000 --- a/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/ist_offset.rst +++ /dev/null @@ -1,5 +0,0 @@ -Interrupt Stack Table Offset -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/ist_offset.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/segment_selector.rst b/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/segment_selector.rst deleted file mode 100644 index 2da142e..0000000 --- a/docs/pre/arch/x86_64/context_switching/interrupt_descriptor_table/segment_selector.rst +++ /dev/null @@ -1,5 +0,0 @@ -Segment Selector -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/interrupt_descriptor_table/segment_selector.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/main.rst b/docs/pre/arch/x86_64/context_switching/main.rst deleted file mode 100644 index e9e8a35..0000000 --- a/docs/pre/arch/x86_64/context_switching/main.rst +++ /dev/null @@ -1,5 +0,0 @@ -Context Switching Main -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/main.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table.rst b/docs/pre/arch/x86_64/context_switching/segment_descriptor_table.rst deleted file mode 100644 index 449622d..0000000 --- a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table.rst +++ /dev/null @@ -1,9 +0,0 @@ -Segment Descriptor Subsystem -=========== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - segment_descriptor_table/* diff --git a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/access_byte.rst b/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/access_byte.rst deleted file mode 100644 index f2e7d67..0000000 --- a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/access_byte.rst +++ /dev/null @@ -1,5 +0,0 @@ -Access Byte -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/segment_descriptor_table/access_byte.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/gdt_flags.rst b/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/gdt_flags.rst deleted file mode 100644 index faa2ffc..0000000 --- a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/gdt_flags.rst +++ /dev/null @@ -1,5 +0,0 @@ -Global Descriptor Table Flags -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/segment_descriptor_table/gdt_flags.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/global_descriptor_table.rst b/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/global_descriptor_table.rst deleted file mode 100644 index 35403db..0000000 --- a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/global_descriptor_table.rst +++ /dev/null @@ -1,5 +0,0 @@ -Global Descriptor Table -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/global_descriptor_table_pointer.rst b/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/global_descriptor_table_pointer.rst deleted file mode 100644 index 41ceffd..0000000 --- a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/global_descriptor_table_pointer.rst +++ /dev/null @@ -1,5 +0,0 @@ -Global Descriptor Table Pointer -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/segment_descriptor_table/global_descriptor_table_pointer.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/segment_descriptor_base.rst b/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/segment_descriptor_base.rst deleted file mode 100644 index 952ab2a..0000000 --- a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/segment_descriptor_base.rst +++ /dev/null @@ -1,5 +0,0 @@ -Segment Descriptor Base (32-bit) -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_base.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/segment_descriptor_extension.rst b/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/segment_descriptor_extension.rst deleted file mode 100644 index 874d1cb..0000000 --- a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/segment_descriptor_extension.rst +++ /dev/null @@ -1,5 +0,0 @@ -Segment Descriptor Extension (64-bit) -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_extension.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/segment_descriptor_type.rst b/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/segment_descriptor_type.rst deleted file mode 100644 index e45b0a5..0000000 --- a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/segment_descriptor_type.rst +++ /dev/null @@ -1,5 +0,0 @@ -Segment Descriptor Type -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/segment_descriptor_table/segment_descriptor_type.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/task_state_segment.rst b/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/task_state_segment.rst deleted file mode 100644 index 731d7bb..0000000 --- a/docs/pre/arch/x86_64/context_switching/segment_descriptor_table/task_state_segment.rst +++ /dev/null @@ -1,5 +0,0 @@ -Task State Segment -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/segment_descriptor_table/task_state_segment.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/syscall.rst b/docs/pre/arch/x86_64/context_switching/syscall.rst deleted file mode 100644 index 28acf28..0000000 --- a/docs/pre/arch/x86_64/context_switching/syscall.rst +++ /dev/null @@ -1,9 +0,0 @@ -System Call Subsystem -=========== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - syscall/* diff --git a/docs/pre/arch/x86_64/context_switching/syscall/main.rst b/docs/pre/arch/x86_64/context_switching/syscall/main.rst deleted file mode 100644 index 6be577b..0000000 --- a/docs/pre/arch/x86_64/context_switching/syscall/main.rst +++ /dev/null @@ -1,5 +0,0 @@ -System Call Main -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/syscall/main.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/syscall/syscall_enable.rst b/docs/pre/arch/x86_64/context_switching/syscall/syscall_enable.rst deleted file mode 100644 index e9162f1..0000000 --- a/docs/pre/arch/x86_64/context_switching/syscall/syscall_enable.rst +++ /dev/null @@ -1,5 +0,0 @@ -System Call Configuration -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/syscall/syscall_enable.hpp - diff --git a/docs/pre/arch/x86_64/context_switching/syscall/syscall_handler.rst b/docs/pre/arch/x86_64/context_switching/syscall/syscall_handler.rst deleted file mode 100644 index 0e86780..0000000 --- a/docs/pre/arch/x86_64/context_switching/syscall/syscall_handler.rst +++ /dev/null @@ -1,5 +0,0 @@ -System Call Handler -======================= - -.. doxygenfile:: arch/x86_64/include/arch/context_switching/syscall/syscall_handler.hpp - diff --git a/docs/pre/arch/x86_64/exception_handling.rst b/docs/pre/arch/x86_64/exception_handling.rst deleted file mode 100644 index 3bf2770..0000000 --- a/docs/pre/arch/x86_64/exception_handling.rst +++ /dev/null @@ -1,9 +0,0 @@ -Exception Handling Subsystem -====================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - exception_handling/* \ No newline at end of file diff --git a/docs/pre/arch/x86_64/exception_handling/assert.rst b/docs/pre/arch/x86_64/exception_handling/assert.rst deleted file mode 100644 index 053cf66..0000000 --- a/docs/pre/arch/x86_64/exception_handling/assert.rst +++ /dev/null @@ -1,5 +0,0 @@ -Exception Handling Assertion -======================= - -.. doxygenfile:: arch/x86_64/include/arch/exception_handling/assert.hpp - diff --git a/docs/pre/arch/x86_64/exception_handling/panic.rst b/docs/pre/arch/x86_64/exception_handling/panic.rst deleted file mode 100644 index 50b6284..0000000 --- a/docs/pre/arch/x86_64/exception_handling/panic.rst +++ /dev/null @@ -1,5 +0,0 @@ -Exception Handling Panic -======================= - -.. doxygenfile:: arch/x86_64/include/arch/exception_handling/panic.hpp - diff --git a/docs/pre/arch/x86_64/interrupt_handling.rst b/docs/pre/arch/x86_64/interrupt_handling.rst deleted file mode 100644 index d4ff94a..0000000 --- a/docs/pre/arch/x86_64/interrupt_handling.rst +++ /dev/null @@ -1,9 +0,0 @@ -Interrupt Handling Subsystem -====================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - interrupt_handling/* \ No newline at end of file diff --git a/docs/pre/arch/x86_64/interrupt_handling/generic_interrupt_handler.rst b/docs/pre/arch/x86_64/interrupt_handling/generic_interrupt_handler.rst deleted file mode 100644 index 6099170..0000000 --- a/docs/pre/arch/x86_64/interrupt_handling/generic_interrupt_handler.rst +++ /dev/null @@ -1,5 +0,0 @@ -Generic Interrupt Handler -======================= - -.. doxygenfile:: arch/x86_64/include/arch/interrupt_handling/generic_interrupt_handler.hpp - diff --git a/docs/pre/arch/x86_64/io.rst b/docs/pre/arch/x86_64/io.rst deleted file mode 100644 index 7082bd5..0000000 --- a/docs/pre/arch/x86_64/io.rst +++ /dev/null @@ -1,9 +0,0 @@ -General Input/Output Subsystem -============================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - io/* \ No newline at end of file diff --git a/docs/pre/arch/x86_64/io/port_io.rst b/docs/pre/arch/x86_64/io/port_io.rst deleted file mode 100644 index 18a9f6a..0000000 --- a/docs/pre/arch/x86_64/io/port_io.rst +++ /dev/null @@ -1,6 +0,0 @@ -Port-based Input/Output -======================= - -.. doxygenstruct:: teachos::arch::io::port - :members: - diff --git a/docs/pre/arch/x86_64/kernel.rst b/docs/pre/arch/x86_64/kernel.rst deleted file mode 100644 index 650e3a6..0000000 --- a/docs/pre/arch/x86_64/kernel.rst +++ /dev/null @@ -1,9 +0,0 @@ -Kernel Main Subsystem -====================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - kernel/* \ No newline at end of file diff --git a/docs/pre/arch/x86_64/kernel/cpu.rst b/docs/pre/arch/x86_64/kernel/cpu.rst deleted file mode 100644 index da3dfc0..0000000 --- a/docs/pre/arch/x86_64/kernel/cpu.rst +++ /dev/null @@ -1,9 +0,0 @@ -Kernel CPU Registers -=========== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - cpu/* diff --git a/docs/pre/arch/x86_64/kernel/cpu/call.rst b/docs/pre/arch/x86_64/kernel/cpu/call.rst deleted file mode 100644 index 33d15ec..0000000 --- a/docs/pre/arch/x86_64/kernel/cpu/call.rst +++ /dev/null @@ -1,5 +0,0 @@ -Far Call -======================= - -.. doxygenfile:: arch/x86_64/include/arch/kernel/cpu/call.hpp - diff --git a/docs/pre/arch/x86_64/kernel/cpu/control_register.rst b/docs/pre/arch/x86_64/kernel/cpu/control_register.rst deleted file mode 100644 index a45c6d9..0000000 --- a/docs/pre/arch/x86_64/kernel/cpu/control_register.rst +++ /dev/null @@ -1,5 +0,0 @@ -Control Register -======================= - -.. doxygenfile:: arch/x86_64/include/arch/kernel/cpu/control_register.hpp - diff --git a/docs/pre/arch/x86_64/kernel/cpu/gdtr.rst b/docs/pre/arch/x86_64/kernel/cpu/gdtr.rst deleted file mode 100644 index 41c0f6b..0000000 --- a/docs/pre/arch/x86_64/kernel/cpu/gdtr.rst +++ /dev/null @@ -1,5 +0,0 @@ -Global Descriptor Table Register -======================= - -.. doxygenfile:: arch/x86_64/include/arch/kernel/cpu/gdtr.hpp - diff --git a/docs/pre/arch/x86_64/kernel/cpu/idtr.rst b/docs/pre/arch/x86_64/kernel/cpu/idtr.rst deleted file mode 100644 index b4c4bb0..0000000 --- a/docs/pre/arch/x86_64/kernel/cpu/idtr.rst +++ /dev/null @@ -1,5 +0,0 @@ -Interrupt Descriptor Table Register -======================= - -.. doxygenfile:: arch/x86_64/include/arch/kernel/cpu/idtr.hpp - diff --git a/docs/pre/arch/x86_64/kernel/cpu/if.rst b/docs/pre/arch/x86_64/kernel/cpu/if.rst deleted file mode 100644 index 2dd07b4..0000000 --- a/docs/pre/arch/x86_64/kernel/cpu/if.rst +++ /dev/null @@ -1,5 +0,0 @@ -Interrupt Flag -======================= - -.. doxygenfile:: arch/x86_64/include/arch/kernel/cpu/if.hpp - diff --git a/docs/pre/arch/x86_64/kernel/cpu/msr.rst b/docs/pre/arch/x86_64/kernel/cpu/msr.rst deleted file mode 100644 index 75c4f47..0000000 --- a/docs/pre/arch/x86_64/kernel/cpu/msr.rst +++ /dev/null @@ -1,5 +0,0 @@ -Model Specific Register -======================= - -.. doxygenfile:: arch/x86_64/include/arch/kernel/cpu/msr.hpp - diff --git a/docs/pre/arch/x86_64/kernel/cpu/segment_register.rst b/docs/pre/arch/x86_64/kernel/cpu/segment_register.rst deleted file mode 100644 index 8159369..0000000 --- a/docs/pre/arch/x86_64/kernel/cpu/segment_register.rst +++ /dev/null @@ -1,5 +0,0 @@ -CPU Segment Register -======================= - -.. doxygenfile:: arch/x86_64/include/arch/kernel/cpu/segment_register.hpp - diff --git a/docs/pre/arch/x86_64/kernel/cpu/tlb.rst b/docs/pre/arch/x86_64/kernel/cpu/tlb.rst deleted file mode 100644 index 1ceec1d..0000000 --- a/docs/pre/arch/x86_64/kernel/cpu/tlb.rst +++ /dev/null @@ -1,5 +0,0 @@ -Translation Lookaside Buffer -======================= - -.. doxygenfile:: arch/x86_64/include/arch/kernel/cpu/tlb.hpp - diff --git a/docs/pre/arch/x86_64/kernel/cpu/tr.rst b/docs/pre/arch/x86_64/kernel/cpu/tr.rst deleted file mode 100644 index a2b234b..0000000 --- a/docs/pre/arch/x86_64/kernel/cpu/tr.rst +++ /dev/null @@ -1,5 +0,0 @@ -Task Register -======================= - -.. doxygenfile:: arch/x86_64/include/arch/kernel/cpu/tr.hpp - diff --git a/docs/pre/arch/x86_64/kernel/halt.rst b/docs/pre/arch/x86_64/kernel/halt.rst deleted file mode 100644 index c425e81..0000000 --- a/docs/pre/arch/x86_64/kernel/halt.rst +++ /dev/null @@ -1,5 +0,0 @@ -Kernel Halt -======================= - -.. doxygenfile:: arch/x86_64/include/arch/kernel/halt.hpp - diff --git a/docs/pre/arch/x86_64/kernel/main.rst b/docs/pre/arch/x86_64/kernel/main.rst deleted file mode 100644 index 194bd85..0000000 --- a/docs/pre/arch/x86_64/kernel/main.rst +++ /dev/null @@ -1,5 +0,0 @@ -Kernel Main -======================= - -.. doxygenfile:: arch/x86_64/include/arch/kernel/main.hpp - diff --git a/docs/pre/arch/x86_64/memory.rst b/docs/pre/arch/x86_64/memory.rst deleted file mode 100644 index 58d12e9..0000000 --- a/docs/pre/arch/x86_64/memory.rst +++ /dev/null @@ -1,9 +0,0 @@ -Kernel Memory Subsystem -====================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - memory/* \ No newline at end of file diff --git a/docs/pre/arch/x86_64/memory/allocator.rst b/docs/pre/arch/x86_64/memory/allocator.rst deleted file mode 100644 index 6ce0a74..0000000 --- a/docs/pre/arch/x86_64/memory/allocator.rst +++ /dev/null @@ -1,9 +0,0 @@ -Physical Frame Allocator Subsystem -=========== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - allocator/* diff --git a/docs/pre/arch/x86_64/memory/allocator/area_frame_allocator.rst b/docs/pre/arch/x86_64/memory/allocator/area_frame_allocator.rst deleted file mode 100644 index 422f33c..0000000 --- a/docs/pre/arch/x86_64/memory/allocator/area_frame_allocator.rst +++ /dev/null @@ -1,5 +0,0 @@ -Area Physical Frame Allocator -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/allocator/area_frame_allocator.hpp - diff --git a/docs/pre/arch/x86_64/memory/allocator/concept.rst b/docs/pre/arch/x86_64/memory/allocator/concept.rst deleted file mode 100644 index 734a2ce..0000000 --- a/docs/pre/arch/x86_64/memory/allocator/concept.rst +++ /dev/null @@ -1,5 +0,0 @@ -Physical Frame Allocator Concept -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/allocator/concept.hpp - diff --git a/docs/pre/arch/x86_64/memory/allocator/physical_frame.rst b/docs/pre/arch/x86_64/memory/allocator/physical_frame.rst deleted file mode 100644 index c5d0fd2..0000000 --- a/docs/pre/arch/x86_64/memory/allocator/physical_frame.rst +++ /dev/null @@ -1,5 +0,0 @@ -Physical Frame -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/allocator/physical_frame.hpp - diff --git a/docs/pre/arch/x86_64/memory/allocator/tiny_frame_allocator.rst b/docs/pre/arch/x86_64/memory/allocator/tiny_frame_allocator.rst deleted file mode 100644 index 27401b2..0000000 --- a/docs/pre/arch/x86_64/memory/allocator/tiny_frame_allocator.rst +++ /dev/null @@ -1,5 +0,0 @@ -Tiny Physical Frame Allocator -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/allocator/tiny_frame_allocator.hpp - diff --git a/docs/pre/arch/x86_64/memory/cpu.rst b/docs/pre/arch/x86_64/memory/cpu.rst deleted file mode 100644 index 4cb5af0..0000000 --- a/docs/pre/arch/x86_64/memory/cpu.rst +++ /dev/null @@ -1,9 +0,0 @@ -CPU Registers Subsystem -=========== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - cpu/* diff --git a/docs/pre/arch/x86_64/memory/heap.rst b/docs/pre/arch/x86_64/memory/heap.rst deleted file mode 100644 index 409d93a..0000000 --- a/docs/pre/arch/x86_64/memory/heap.rst +++ /dev/null @@ -1,9 +0,0 @@ -Heap Memory Subsystem -=========== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - heap/* diff --git a/docs/pre/arch/x86_64/memory/heap/bump_allocator.rst b/docs/pre/arch/x86_64/memory/heap/bump_allocator.rst deleted file mode 100644 index b20916e..0000000 --- a/docs/pre/arch/x86_64/memory/heap/bump_allocator.rst +++ /dev/null @@ -1,5 +0,0 @@ -Kernel Heap Bump Allocator -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/heap/bump_allocator.hpp - diff --git a/docs/pre/arch/x86_64/memory/heap/global_heap_allocator.rst b/docs/pre/arch/x86_64/memory/heap/global_heap_allocator.rst deleted file mode 100644 index 60ec0b5..0000000 --- a/docs/pre/arch/x86_64/memory/heap/global_heap_allocator.rst +++ /dev/null @@ -1,5 +0,0 @@ -Global Heap Allocator -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/heap/global_heap_allocator.hpp - diff --git a/docs/pre/arch/x86_64/memory/heap/heap_allocator.rst b/docs/pre/arch/x86_64/memory/heap/heap_allocator.rst deleted file mode 100644 index b410e41..0000000 --- a/docs/pre/arch/x86_64/memory/heap/heap_allocator.rst +++ /dev/null @@ -1,5 +0,0 @@ -Kernel Heap Allocator -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/heap/heap_allocator.hpp - diff --git a/docs/pre/arch/x86_64/memory/heap/linked_list_allocator.rst b/docs/pre/arch/x86_64/memory/heap/linked_list_allocator.rst deleted file mode 100644 index d156852..0000000 --- a/docs/pre/arch/x86_64/memory/heap/linked_list_allocator.rst +++ /dev/null @@ -1,5 +0,0 @@ -Kernel Heap Linked List Allocator -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/heap/linked_list_allocator.hpp - diff --git a/docs/pre/arch/x86_64/memory/heap/memory_block.rst b/docs/pre/arch/x86_64/memory/heap/memory_block.rst deleted file mode 100644 index 8ed6566..0000000 --- a/docs/pre/arch/x86_64/memory/heap/memory_block.rst +++ /dev/null @@ -1,5 +0,0 @@ -Heap Linked List Free Memory Block -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/heap/memory_block.hpp - diff --git a/docs/pre/arch/x86_64/memory/heap/user_heap_allocator.rst b/docs/pre/arch/x86_64/memory/heap/user_heap_allocator.rst deleted file mode 100644 index d0febb6..0000000 --- a/docs/pre/arch/x86_64/memory/heap/user_heap_allocator.rst +++ /dev/null @@ -1,5 +0,0 @@ -User Heap Linked List Allocator -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/heap/user_heap_allocator.hpp - diff --git a/docs/pre/arch/x86_64/memory/main.rst b/docs/pre/arch/x86_64/memory/main.rst deleted file mode 100644 index d9a9f39..0000000 --- a/docs/pre/arch/x86_64/memory/main.rst +++ /dev/null @@ -1,5 +0,0 @@ -Memory Main -=========== - -.. doxygenfile:: arch/x86_64/include/arch/memory/main.hpp - diff --git a/docs/pre/arch/x86_64/memory/multiboot.rst b/docs/pre/arch/x86_64/memory/multiboot.rst deleted file mode 100644 index 22ec3f2..0000000 --- a/docs/pre/arch/x86_64/memory/multiboot.rst +++ /dev/null @@ -1,9 +0,0 @@ -Kernel Multiboot Subsystem -=========== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - multiboot/* diff --git a/docs/pre/arch/x86_64/memory/multiboot/elf_symbols_section.rst b/docs/pre/arch/x86_64/memory/multiboot/elf_symbols_section.rst deleted file mode 100644 index bbd6dfb..0000000 --- a/docs/pre/arch/x86_64/memory/multiboot/elf_symbols_section.rst +++ /dev/null @@ -1,5 +0,0 @@ -Multiboot ELF Header Symbols Section Structure -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/multiboot/elf_symbols_section.hpp - diff --git a/docs/pre/arch/x86_64/memory/multiboot/info.rst b/docs/pre/arch/x86_64/memory/multiboot/info.rst deleted file mode 100644 index 847870d..0000000 --- a/docs/pre/arch/x86_64/memory/multiboot/info.rst +++ /dev/null @@ -1,5 +0,0 @@ -Multiboot Header Information Structure -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/multiboot/info.hpp - diff --git a/docs/pre/arch/x86_64/memory/multiboot/memory_map.rst b/docs/pre/arch/x86_64/memory/multiboot/memory_map.rst deleted file mode 100644 index 9c77331..0000000 --- a/docs/pre/arch/x86_64/memory/multiboot/memory_map.rst +++ /dev/null @@ -1,5 +0,0 @@ -Multiboot Memory Map Header Structure -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/multiboot/memory_map.hpp - diff --git a/docs/pre/arch/x86_64/memory/multiboot/reader.rst b/docs/pre/arch/x86_64/memory/multiboot/reader.rst deleted file mode 100644 index fac98e2..0000000 --- a/docs/pre/arch/x86_64/memory/multiboot/reader.rst +++ /dev/null @@ -1,5 +0,0 @@ -Multiboot Reader -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/multiboot/reader.hpp - diff --git a/docs/pre/arch/x86_64/memory/paging.rst b/docs/pre/arch/x86_64/memory/paging.rst deleted file mode 100644 index 10cd976..0000000 --- a/docs/pre/arch/x86_64/memory/paging.rst +++ /dev/null @@ -1,9 +0,0 @@ -Virtual Page Table Paging Subsystem -=========== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - paging/* diff --git a/docs/pre/arch/x86_64/memory/paging/active_page_table.rst b/docs/pre/arch/x86_64/memory/paging/active_page_table.rst deleted file mode 100644 index 5710131..0000000 --- a/docs/pre/arch/x86_64/memory/paging/active_page_table.rst +++ /dev/null @@ -1,5 +0,0 @@ -Active Page Table -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/paging/active_page_table.hpp - diff --git a/docs/pre/arch/x86_64/memory/paging/inactive_page_table.rst b/docs/pre/arch/x86_64/memory/paging/inactive_page_table.rst deleted file mode 100644 index 5732e64..0000000 --- a/docs/pre/arch/x86_64/memory/paging/inactive_page_table.rst +++ /dev/null @@ -1,5 +0,0 @@ -Inactive Page Table -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/paging/inactive_page_table.hpp - diff --git a/docs/pre/arch/x86_64/memory/paging/kernel_mapper.rst b/docs/pre/arch/x86_64/memory/paging/kernel_mapper.rst deleted file mode 100644 index 9948e4e..0000000 --- a/docs/pre/arch/x86_64/memory/paging/kernel_mapper.rst +++ /dev/null @@ -1,5 +0,0 @@ -Kernel Mapper -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/paging/kernel_mapper.hpp - diff --git a/docs/pre/arch/x86_64/memory/paging/page_entry.rst b/docs/pre/arch/x86_64/memory/paging/page_entry.rst deleted file mode 100644 index 8900b0e..0000000 --- a/docs/pre/arch/x86_64/memory/paging/page_entry.rst +++ /dev/null @@ -1,5 +0,0 @@ -Virtual Page Table Entry -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/paging/page_entry.hpp - diff --git a/docs/pre/arch/x86_64/memory/paging/page_table.rst b/docs/pre/arch/x86_64/memory/paging/page_table.rst deleted file mode 100644 index c5ab8c7..0000000 --- a/docs/pre/arch/x86_64/memory/paging/page_table.rst +++ /dev/null @@ -1,5 +0,0 @@ -Virtual Page Table -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/paging/page_table.hpp - diff --git a/docs/pre/arch/x86_64/memory/paging/temporary_page.rst b/docs/pre/arch/x86_64/memory/paging/temporary_page.rst deleted file mode 100644 index 0c63899..0000000 --- a/docs/pre/arch/x86_64/memory/paging/temporary_page.rst +++ /dev/null @@ -1,5 +0,0 @@ -Temporary Virtual Page Table -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/paging/temporary_page.hpp - diff --git a/docs/pre/arch/x86_64/memory/paging/virtual_page.rst b/docs/pre/arch/x86_64/memory/paging/virtual_page.rst deleted file mode 100644 index dd42f47..0000000 --- a/docs/pre/arch/x86_64/memory/paging/virtual_page.rst +++ /dev/null @@ -1,5 +0,0 @@ -Virtual Page -======================= - -.. doxygenfile:: arch/x86_64/include/arch/memory/paging/virtual_page.hpp - diff --git a/docs/pre/arch/x86_64/stl.rst b/docs/pre/arch/x86_64/stl.rst deleted file mode 100644 index bb21f9a..0000000 --- a/docs/pre/arch/x86_64/stl.rst +++ /dev/null @@ -1,9 +0,0 @@ -Standard Library Subsystem -====================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - stl/* \ No newline at end of file diff --git a/docs/pre/arch/x86_64/stl/container.rst b/docs/pre/arch/x86_64/stl/container.rst deleted file mode 100644 index 19c735b..0000000 --- a/docs/pre/arch/x86_64/stl/container.rst +++ /dev/null @@ -1,5 +0,0 @@ -Container Structure -======================= - -.. doxygenfile:: arch/x86_64/include/arch/stl/container.hpp - diff --git a/docs/pre/arch/x86_64/stl/contiguous_pointer_iterator.rst b/docs/pre/arch/x86_64/stl/contiguous_pointer_iterator.rst deleted file mode 100644 index 47f88c4..0000000 --- a/docs/pre/arch/x86_64/stl/contiguous_pointer_iterator.rst +++ /dev/null @@ -1,5 +0,0 @@ -Contiguous Pointer Iterator -======================= - -.. doxygenfile:: arch/x86_64/include/arch/stl/contiguous_pointer_iterator.hpp - diff --git a/docs/pre/arch/x86_64/stl/forward_value_iterator.rst b/docs/pre/arch/x86_64/stl/forward_value_iterator.rst deleted file mode 100644 index 72270de..0000000 --- a/docs/pre/arch/x86_64/stl/forward_value_iterator.rst +++ /dev/null @@ -1,5 +0,0 @@ -Forward Value Iterator -======================= - -.. doxygenfile:: arch/x86_64/include/arch/stl/forward_value_iterator.hpp - diff --git a/docs/pre/arch/x86_64/stl/mutex.rst b/docs/pre/arch/x86_64/stl/mutex.rst deleted file mode 100644 index 2098113..0000000 --- a/docs/pre/arch/x86_64/stl/mutex.rst +++ /dev/null @@ -1,5 +0,0 @@ -Mutex -======================= - -.. doxygenfile:: arch/x86_64/include/arch/stl/mutex.hpp - diff --git a/docs/pre/arch/x86_64/stl/shared_pointer.rst b/docs/pre/arch/x86_64/stl/shared_pointer.rst deleted file mode 100644 index 46ddb65..0000000 --- a/docs/pre/arch/x86_64/stl/shared_pointer.rst +++ /dev/null @@ -1,5 +0,0 @@ -Shared Pointer -======================= - -.. doxygenfile:: arch/x86_64/include/arch/stl/shared_pointer.hpp - diff --git a/docs/pre/arch/x86_64/stl/stack.rst b/docs/pre/arch/x86_64/stl/stack.rst deleted file mode 100644 index a554387..0000000 --- a/docs/pre/arch/x86_64/stl/stack.rst +++ /dev/null @@ -1,5 +0,0 @@ -Stack -======================= - -.. doxygenfile:: arch/x86_64/include/arch/stl/stack.hpp - diff --git a/docs/pre/arch/x86_64/stl/unique_pointer.rst b/docs/pre/arch/x86_64/stl/unique_pointer.rst deleted file mode 100644 index f508763..0000000 --- a/docs/pre/arch/x86_64/stl/unique_pointer.rst +++ /dev/null @@ -1,5 +0,0 @@ -Unique Pointer -======================= - -.. doxygenfile:: arch/x86_64/include/arch/stl/unique_pointer.hpp - diff --git a/docs/pre/arch/x86_64/stl/vector.rst b/docs/pre/arch/x86_64/stl/vector.rst deleted file mode 100644 index b60023a..0000000 --- a/docs/pre/arch/x86_64/stl/vector.rst +++ /dev/null @@ -1,5 +0,0 @@ -Vector -======================= - -.. doxygenfile:: arch/x86_64/include/arch/stl/vector.hpp - diff --git a/docs/pre/arch/x86_64/user.rst b/docs/pre/arch/x86_64/user.rst deleted file mode 100644 index 3be32bb..0000000 --- a/docs/pre/arch/x86_64/user.rst +++ /dev/null @@ -1,9 +0,0 @@ -User Subsystem -====================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - user/* \ No newline at end of file diff --git a/docs/pre/arch/x86_64/user/main.rst b/docs/pre/arch/x86_64/user/main.rst deleted file mode 100644 index 0f641b2..0000000 --- a/docs/pre/arch/x86_64/user/main.rst +++ /dev/null @@ -1,5 +0,0 @@ -User Main -=========== - -.. doxygenfile:: arch/x86_64/include/arch/user/main.hpp - diff --git a/docs/pre/arch/x86_64/video.rst b/docs/pre/arch/x86_64/video.rst deleted file mode 100644 index bbae5ed..0000000 --- a/docs/pre/arch/x86_64/video.rst +++ /dev/null @@ -1,9 +0,0 @@ -Video Output Subsystem -====================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - video/* \ No newline at end of file diff --git a/docs/pre/arch/x86_64/video/vga.rst b/docs/pre/arch/x86_64/video/vga.rst deleted file mode 100644 index 2c32bb2..0000000 --- a/docs/pre/arch/x86_64/video/vga.rst +++ /dev/null @@ -1,9 +0,0 @@ -VGA Support -=========== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - :glob: - - vga/* diff --git a/docs/pre/arch/x86_64/video/vga/io.rst b/docs/pre/arch/x86_64/video/vga/io.rst deleted file mode 100644 index 39609c9..0000000 --- a/docs/pre/arch/x86_64/video/vga/io.rst +++ /dev/null @@ -1,4 +0,0 @@ -VGA Input/Output Types and Constants -==================================== - -.. doxygenfile:: arch/x86_64/include/arch/video/vga/io.hpp diff --git a/docs/pre/arch/x86_64/video/vga/text.rst b/docs/pre/arch/x86_64/video/vga/text.rst deleted file mode 100644 index 592cdd5..0000000 --- a/docs/pre/arch/x86_64/video/vga/text.rst +++ /dev/null @@ -1,5 +0,0 @@ -VGA Text Mode -============= - -.. doxygennamespace:: teachos::arch::video::vga::text - :members: diff --git a/docs/pre/cross/memory.rst b/docs/pre/cross/memory.rst deleted file mode 100644 index 3a2c1c4..0000000 --- a/docs/pre/cross/memory.rst +++ /dev/null @@ -1,11 +0,0 @@ -Memory Access and Management -============================ - -This sections details the platform-**independent** infrastructure for memory access and management. - -.. toctree:: - :maxdepth: 1 - :glob: - :caption: Types: - - memory/* diff --git a/docs/pre/cross/memory/asm_pointer.rst b/docs/pre/cross/memory/asm_pointer.rst deleted file mode 100644 index 70f5c01..0000000 --- a/docs/pre/cross/memory/asm_pointer.rst +++ /dev/null @@ -1,10 +0,0 @@ -Access to Pointers Defined in Assembly -====================================== - -.. doxygenstruct:: teachos::memory::asm_pointer - :members: - -Specializations ---------------- - -.. doxygenstruct:: teachos::memory::asm_pointer< Type const > \ No newline at end of file -- cgit v1.2.3 From 0bbec5ceba0df5668ab1aedcbf2905bf599f6eba Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 8 Apr 2026 10:54:14 +0200 Subject: ide: clean up neovim configuration --- .nvim.lua | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.nvim.lua b/.nvim.lua index ba8f78b..e8205cb 100644 --- a/.nvim.lua +++ b/.nvim.lua @@ -1,3 +1,6 @@ +vim.opt.exrc = false +vim.g.autoformat = true + local workspace_folder = vim.fn.getcwd() local function safe_require(module) @@ -20,12 +23,6 @@ default_clangd_config.cmd = { vim.lsp.config("clangd", default_clangd_config) -vim.filetype.add({ - pattern = { - [".*/kstd/include/kstd/.*"] = "cpp", - } -}) - -- Debugging local dap = require("dap") local qemu_job_id = nil @@ -110,6 +107,10 @@ dap.listeners.after.event_exited["teachos_qemu_teardown"] = teardown_qemu dap.listeners.after.disconnect["teachos_qemu_teardown"] = teardown_qemu require("cmake-tools").setup({ + cmake_compile_commands_options = { + action = "soft_link", + target = string.format("%s/build", workspace_folder), + }, cmake_dap_configuration = { name = "(gdb) QEMU MI Attach", type = "teachos_qemu_mi", -- cgit v1.2.3 From 878852c94c4d56f303366cec177b3edef9b3b9c5 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 8 Apr 2026 13:54:52 +0200 Subject: kapi: add basic support for MMIO mapping --- arch/x86_64/include/arch/memory/page_table.hpp | 6 +- arch/x86_64/kapi/memory.cpp | 1 + kapi/include/kapi/memory.hpp | 30 +++++++ kernel/CMakeLists.txt | 41 ++++----- kernel/include/kernel/memory/mmio_allocator.hpp | 41 +++++++++ kernel/kapi/memory.cpp | 25 ++++++ kernel/src/memory/mmio_allocator.cpp | 107 ++++++++++++++++++++++++ 7 files changed, 228 insertions(+), 23 deletions(-) create mode 100644 kernel/include/kernel/memory/mmio_allocator.hpp create mode 100644 kernel/src/memory/mmio_allocator.cpp diff --git a/arch/x86_64/include/arch/memory/page_table.hpp b/arch/x86_64/include/arch/memory/page_table.hpp index 3cbb0af..c75ccaf 100644 --- a/arch/x86_64/include/arch/memory/page_table.hpp +++ b/arch/x86_64/include/arch/memory/page_table.hpp @@ -174,7 +174,7 @@ namespace arch::memory result |= mapper_flags::writable; } - if ((flags & table_flags::disable_cache) != table_flags::empty) + if ((flags & (table_flags::disable_cache | table_flags::write_through)) != table_flags::empty) { result |= mapper_flags::uncached; } @@ -211,7 +211,7 @@ namespace arch::memory if ((flags & mapper_flags::uncached) != mapper_flags::empty) { - result |= table_flags::disable_cache; + result |= (table_flags::disable_cache | table_flags::write_through); } if ((flags & mapper_flags::supervisor_only) == mapper_flags::empty) @@ -229,4 +229,4 @@ namespace arch::memory } // namespace arch::memory -#endif \ No newline at end of file +#endif diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp index 5f12e5c..853639c 100644 --- a/arch/x86_64/kapi/memory.cpp +++ b/arch/x86_64/kapi/memory.cpp @@ -245,6 +245,7 @@ namespace kapi::memory [](auto const & region) { return region.base + region.size_in_B; })); init_pmm(frame::containing(physical_address{highest_byte}).number() + 1, handoff_to_kernel_pmm); + init_mmio(mmio_base, 1_GiB / page::size); kstd::println("[x86_64:MEM] Releasing bootstrap memory allocators."); region_based_allocator.reset(); diff --git a/kapi/include/kapi/memory.hpp b/kapi/include/kapi/memory.hpp index 914ca61..ae33904 100644 --- a/kapi/include/kapi/memory.hpp +++ b/kapi/include/kapi/memory.hpp @@ -79,6 +79,36 @@ namespace kapi::memory //! @param page The page to unmap auto unmap(page page) -> void; + //! Initialize the Memory-mapped I/O region system. + //! + //! @param base The base address for the MMIO region. + //! @param page_count The number of pages the MMIO region is spans. + auto init_mmio(linear_address base, std::size_t page_count) -> void; + + //! Allocate a Memory-mapped I/O region of the given size. + //! + //! @warning This function will panic if the MMIO system has not been initialized! + //! @param page_count The number of pages to allocate. + auto allocate_mmio_region(std::size_t page_count) -> linear_address; + + //! Map a region of Memory-mapped I/O address space to a given hardware address using the given flags. + //! + //! @warning This function will panic if no page mapper has been registered, or the page has already been mapped. + //! This function will not ensure that the frame is not already in use. + //! + //! This function will always set the @p uncached flag. + //! + //! @param base The base of the virtual region. + //! @param hw_base The base of the hardware region. + //! @param flags The flags to apply. + auto map_mmio_region(linear_address base, physical_address hw_base, page_mapper::flags flags = {}) -> std::byte *; + + //! Release a Memory-mapped I/O region. + //! + //! @warning This function will panic if the MMIO system has not been initialized! + //! @param base The start address of the region to release. + auto release_mmio_region(linear_address base) -> void; + //! @} //! @addtogroup kapi-memory-platform-defined diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index ef73586..67db0a8 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -20,6 +20,7 @@ add_library("kernel_objs" OBJECT "src/acpi/manager.cpp" "src/memory/bitmap_allocator.cpp" "src/memory/block_list_allocator.cpp" + "src/memory/mmio_allocator.cpp" "src/memory.cpp" "src/devices/block_device.cpp" "src/devices/block_device_utils.cpp" @@ -66,37 +67,37 @@ target_sources("kernel_objs" PUBLIC add_library("os::kernel" ALIAS "kernel_objs") if(CMAKE_CROSSCOMPILING) - add_executable("kernel" + add_executable("kernel" "src/main.cpp" "src/memory/operators.cpp" ) - target_link_libraries("kernel" PRIVATE + target_link_libraries("kernel" PRIVATE "os::arch" "os::kernel" ) - target_link_options("kernel" PRIVATE + target_link_options("kernel" PRIVATE "-T${KERNEL_LINKER_SCRIPT}" "-no-pie" "-nostdlib" ) - set_property(TARGET "kernel" + 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") + target_generate_bootable_iso("kernel") else() - enable_coverage("kernel_objs") + enable_coverage("kernel_objs") - add_library("kernel_test_support" OBJECT + add_library("kernel_test_support" OBJECT "src/test_support/kapi/cpu.cpp" "src/test_support/kapi/cio.cpp" "src/test_support/kapi/interrupts.cpp" @@ -108,21 +109,21 @@ else() "src/test_support/simulated_memory.cpp" "src/test_support/state_reset_listener.cpp" ) - add_library("os::kernel_test_support" ALIAS "kernel_test_support") + add_library("os::kernel_test_support" ALIAS "kernel_test_support") - target_link_libraries("kernel_test_support" PUBLIC + target_link_libraries("kernel_test_support" PUBLIC "os::kernel" "Catch2::Catch2WithMain" ) - add_executable("kernel_tests" + 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" @@ -130,19 +131,19 @@ else() # Storage Subsystem Tests "src/devices/storage/ram_disk/device.tests.cpp" ) - add_executable("os::kernel_tests" ALIAS "kernel_tests") + add_executable("os::kernel_tests" ALIAS "kernel_tests") - target_link_libraries("kernel_tests" PRIVATE + target_link_libraries("kernel_tests" PRIVATE "os::kernel" "os::kernel_test_support" ) - set_target_properties("kernel_tests" PROPERTIES + set_target_properties("kernel_tests" PROPERTIES C_CLANG_TIDY "" CXX_CLANG_TIDY "" ) - enable_coverage("kernel_tests") - catch_discover_tests("os::kernel_tests") + enable_coverage("kernel_tests") + catch_discover_tests("os::kernel_tests") endif() diff --git a/kernel/include/kernel/memory/mmio_allocator.hpp b/kernel/include/kernel/memory/mmio_allocator.hpp new file mode 100644 index 0000000..4ec6bec --- /dev/null +++ b/kernel/include/kernel/memory/mmio_allocator.hpp @@ -0,0 +1,41 @@ +#ifndef TEACHOS_KERNEL_MEMORY_MMIO_ALLOCATOR_HPP +#define TEACHOS_KERNEL_MEMORY_MMIO_ALLOCATOR_HPP + +#include "kapi/memory.hpp" + +#include +#include + +#include + +namespace kernel::memory +{ + + struct mmio_allocator + { + mmio_allocator(kapi::memory::linear_address base, std::size_t pages); + + [[nodiscard]] auto allocate(std::size_t page_count) -> kapi::memory::linear_address; + auto release(kapi::memory::linear_address base) -> void; + + private: + struct node + { + kapi::memory::linear_address base{}; + std::size_t page_count{}; + node * next{}; + node * previous{}; + bool is_free{}; + }; + + auto make_node(kapi::memory::linear_address base, std::size_t page_count, node * next, node * previous, + bool is_free) -> node *; + auto destroy_node(node *) -> void; + + [[no_unique_address]] kstd::allocator m_allocator{}; + node * m_head{}; + }; + +} // namespace kernel::memory + +#endif diff --git a/kernel/kapi/memory.cpp b/kernel/kapi/memory.cpp index 06a3165..b224c50 100644 --- a/kernel/kapi/memory.cpp +++ b/kernel/kapi/memory.cpp @@ -3,6 +3,7 @@ #include "kapi/system.hpp" #include "kernel/memory/bitmap_allocator.hpp" +#include "kernel/memory/mmio_allocator.hpp" #include #include @@ -63,6 +64,7 @@ namespace kapi::memory constinit bad_frame_allocator bad_frame_allocator::instance{}; constinit bad_page_mapper bad_page_mapper::instance{}; auto constinit allocator = std::optional{}; + auto constinit mmio_allocator = std::optional{}; } // namespace constinit auto static active_frame_allocator = static_cast(&bad_frame_allocator::instance); @@ -147,4 +149,27 @@ namespace kapi::memory kstd::println("[OS:MEM] Physical memory manager initialized."); } + auto init_mmio(linear_address base, std::size_t page_count) -> void + { + mmio_allocator.emplace(base, page_count); + } + + auto allocate_mmio_region(std::size_t page_count) -> linear_address + { + auto region = mmio_allocator->allocate(page_count); + return region; + } + + auto map_mmio_region(linear_address base, physical_address hw_base, page_mapper::flags flags) -> std::byte * + { + auto start_page = page::containing(base); + auto start_frame = frame::containing(hw_base); + return map(start_page, start_frame, flags | page_mapper::flags::uncached); + } + + auto release_mmio_region(linear_address base) -> void + { + mmio_allocator->release(base); + } + } // namespace kapi::memory diff --git a/kernel/src/memory/mmio_allocator.cpp b/kernel/src/memory/mmio_allocator.cpp new file mode 100644 index 0000000..f77f14f --- /dev/null +++ b/kernel/src/memory/mmio_allocator.cpp @@ -0,0 +1,107 @@ +#include "kernel/memory/mmio_allocator.hpp" + +#include "kapi/memory.hpp" +#include "kapi/system.hpp" + +#include + +#include +#include +#include + +namespace kernel::memory +{ + + mmio_allocator::mmio_allocator(kapi::memory::linear_address base, std::size_t pages) + : m_head{make_node(base, pages, nullptr, nullptr, true)} + {} + + auto mmio_allocator::allocate(std::size_t count) -> kapi::memory::linear_address + { + if (count == 0 || !m_head) + { + return {}; + } + + auto current = m_head; + while (current) + { + if (current->page_count > count) + { + auto new_base = current->base + (count * kapi::memory::page::size); + auto split_node = make_node(new_base, current->page_count - count, std::move(current->next), current, true); + + if (current->next) + { + current->next->previous = split_node; + } + current->next = split_node; + current->page_count = count; + } + + current->is_free = false; + return current->base; + } + + kapi::system::panic("[OS:MEM] MMIO alloctor out of memory!"); + return {}; + } + + auto mmio_allocator::release(kapi::memory::linear_address base) -> void + { + auto current = m_head; + + while (current) + { + if (current->base == base && !current->is_free) + { + current->is_free = true; + + if (current->next && current->next->is_free) + { + auto removed = current->next; + current->page_count += removed->page_count; + current->next = removed->next; + if (current->next) + { + current->next->previous = current; + } + destroy_node(removed); + } + + if (current->previous && current->previous->is_free) + { + auto removed = current; + removed->previous->page_count += removed->page_count; + removed->previous->next = removed->next; + if (removed->next) + { + removed->next->previous = removed->previous; + } + destroy_node(removed); + } + return; + } + current = current->next; + } + } + + auto mmio_allocator::make_node(kapi::memory::linear_address base, std::size_t page_count, node * next, + node * previous, bool is_free) -> node * + { + using traits = std::allocator_traits>; + + auto new_node = traits::allocate(m_allocator, 1); + traits::construct(m_allocator, new_node, base, page_count, next, previous, is_free); + return new_node; + } + + auto mmio_allocator::destroy_node(node * instance) -> void + { + using traits = std::allocator_traits>; + + traits::destroy(m_allocator, instance); + traits::deallocate(m_allocator, instance, 1); + } + +} // namespace kernel::memory -- cgit v1.2.3 From 1c52a859d105f6b0f8afb16565b10435fa728882 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 8 Apr 2026 14:15:56 +0200 Subject: kapi: fix mmio initialization --- arch/x86_64/kapi/memory.cpp | 1 - kernel/src/main.cpp | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp index 853639c..5f12e5c 100644 --- a/arch/x86_64/kapi/memory.cpp +++ b/arch/x86_64/kapi/memory.cpp @@ -245,7 +245,6 @@ namespace kapi::memory [](auto const & region) { return region.base + region.size_in_B; })); init_pmm(frame::containing(physical_address{highest_byte}).number() + 1, handoff_to_kernel_pmm); - init_mmio(mmio_base, 1_GiB / page::size); kstd::println("[x86_64:MEM] Releasing bootstrap memory allocators."); region_based_allocator.reset(); diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index e704955..4b61948 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -19,11 +19,14 @@ #include #include #include +#include #include #include #include +using namespace kstd::units_literals; + auto test_device_names() -> void { auto storage_mgmt = kernel::devices::storage::management::get(); @@ -177,8 +180,9 @@ auto main() -> int kapi::memory::init(); kernel::memory::init_heap(kapi::memory::heap_base); - kstd::println("[OS] Memory subsystem initialized."); kapi::system::memory_initialized(); + kapi::memory::init_mmio(kapi::memory::mmio_base, 1_GiB / kapi::memory::page::size); + kstd::println("[OS] Memory subsystem initialized."); auto acpi_root_pointer = kapi::acpi::get_root_pointer(); if (acpi_root_pointer && acpi_root_pointer->validate()) -- cgit v1.2.3 From 77b408b4e18fabb29f4cab5e899e7c8db1bc1204 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 8 Apr 2026 14:16:13 +0200 Subject: kernel: fix mmio allocator --- kernel/src/memory/mmio_allocator.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/kernel/src/memory/mmio_allocator.cpp b/kernel/src/memory/mmio_allocator.cpp index f77f14f..ddbdfc2 100644 --- a/kernel/src/memory/mmio_allocator.cpp +++ b/kernel/src/memory/mmio_allocator.cpp @@ -26,21 +26,25 @@ namespace kernel::memory auto current = m_head; while (current) { - if (current->page_count > count) + if (current->is_free && current->page_count >= count) { - auto new_base = current->base + (count * kapi::memory::page::size); - auto split_node = make_node(new_base, current->page_count - count, std::move(current->next), current, true); - - if (current->next) + if (current->page_count > count) { - current->next->previous = split_node; + auto new_base = current->base + (count * kapi::memory::page::size); + auto split_node = make_node(new_base, current->page_count - count, std::move(current->next), current, true); + + if (current->next) + { + current->next->previous = split_node; + } + current->next = split_node; + current->page_count = count; } - current->next = split_node; - current->page_count = count; - } - current->is_free = false; - return current->base; + current->is_free = false; + return current->base; + } + current = current->next; } kapi::system::panic("[OS:MEM] MMIO alloctor out of memory!"); -- cgit v1.2.3 From ce47b93e8834bb4811e788737ae1a6ea750af79c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 8 Apr 2026 14:20:48 +0200 Subject: x86_64: implement LAPIC initialization --- arch/x86_64/include/arch/devices/local_apic.hpp | 6 ++++- arch/x86_64/src/devices/local_apic.cpp | 33 +++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/arch/x86_64/include/arch/devices/local_apic.hpp b/arch/x86_64/include/arch/devices/local_apic.hpp index 71e9758..7f125c3 100644 --- a/arch/x86_64/include/arch/devices/local_apic.hpp +++ b/arch/x86_64/include/arch/devices/local_apic.hpp @@ -17,10 +17,14 @@ namespace arch::devices auto init() -> bool override; private: + [[nodiscard]] auto read_register(std::size_t offset) const -> std::uint32_t; + auto write_register(std::size_t offset, std::uint32_t value) -> void; + std::uint64_t m_hardware_id{}; kapi::memory::physical_address m_base{}; + kapi::memory::linear_address m_virtual_base{}; }; } // namespace arch::devices -#endif \ No newline at end of file +#endif diff --git a/arch/x86_64/src/devices/local_apic.cpp b/arch/x86_64/src/devices/local_apic.cpp index beb75ef..91e907a 100644 --- a/arch/x86_64/src/devices/local_apic.cpp +++ b/arch/x86_64/src/devices/local_apic.cpp @@ -11,6 +11,14 @@ namespace arch::devices { + namespace + { + constexpr auto lapic_sivr_register = 0x00F0uz; + + constexpr auto lapic_enable_bit = 0x100u; + constexpr auto spurious_interrupt_vector = 0xFFu; + } // namespace + local_apic::local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id, kapi::memory::physical_address base) : kapi::devices::device{major, minor, "lapic"} @@ -20,9 +28,30 @@ namespace arch::devices auto local_apic::init() -> bool { - kstd::println("[x86_64:DEV] Initializing local APIC on core {}", m_hardware_id); + m_virtual_base = kapi::memory::allocate_mmio_region(1); + if (!kapi::memory::map_mmio_region(m_virtual_base, m_base, kapi::memory::page_mapper::flags::writable)) + { + kstd::println("[x86_64:DEV] LAPIC {} MMIO mapping failed!", m_hardware_id); + return false; + } + + write_register(lapic_sivr_register, lapic_enable_bit | spurious_interrupt_vector); + + kstd::println("[x86_64:DEV] LAPIC {} initialized. {:#018x}@{:#018x}", m_hardware_id, m_base, m_virtual_base); return true; } -} // namespace arch::devices \ No newline at end of file + auto local_apic::read_register(std::size_t offset) const -> std::uint32_t + { + auto reg = static_cast(m_virtual_base + offset); + return *reg; + } + + auto local_apic::write_register(std::size_t offset, std::uint32_t value) -> void + { + auto reg = static_cast(m_virtual_base + offset); + *reg = value; + } + +} // namespace arch::devices -- cgit v1.2.3 From 296742bfa509524dc8effc3dcae4b6231d37705f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 8 Apr 2026 14:30:53 +0200 Subject: x86_64: don't hardcode the LAPIC address --- arch/x86_64/kapi/platform.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/arch/x86_64/kapi/platform.cpp b/arch/x86_64/kapi/platform.cpp index 380fc66..c6e41d2 100644 --- a/arch/x86_64/kapi/platform.cpp +++ b/arch/x86_64/kapi/platform.cpp @@ -2,7 +2,6 @@ #include "kapi/acpi.hpp" #include "kapi/devices.hpp" -#include "kapi/memory.hpp" #include "arch/devices/local_apic.hpp" @@ -15,11 +14,6 @@ namespace kapi::platform { - namespace - { - constexpr auto default_lapic_address = kapi::memory::physical_address{0xFEE0'0000}; - } - auto discover_cpu_topology(kapi::devices::bus & bus) -> bool { auto static const core_major = kapi::devices::allocate_major_number(); @@ -33,8 +27,9 @@ namespace kapi::platform return false; } - auto const * current = reinterpret_cast(madt.get()) + sizeof(kapi::acpi::madt_header); - auto const * end = reinterpret_cast(madt.get()) + madt->length(); + auto real_madt = static_cast(madt.get()); + auto current = reinterpret_cast(madt.get()) + sizeof(kapi::acpi::madt_header); + auto end = reinterpret_cast(madt.get()) + madt->length(); auto bsp_found = false; auto core_count = 0uz; @@ -49,9 +44,9 @@ namespace kapi::platform { auto is_bsp = !bsp_found; bsp_found = true; - auto lapic = kstd::make_unique(interrupt_controller_major, core_index, - local_apic->apic_id(), default_lapic_address); + local_apic->apic_id(), + real_madt->local_interrupt_controller_address()); if (kapi::platform::cpu_detected(bus, core_major, core_index, local_apic->processor_id(), is_bsp, std::move(lapic))) { @@ -68,4 +63,4 @@ namespace kapi::platform return core_count > 0; } -} // namespace kapi::platform \ No newline at end of file +} // namespace kapi::platform -- cgit v1.2.3 From 2ed34cc51a534171f0fe08808634834bc22cf84d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 8 Apr 2026 14:55:18 +0200 Subject: x86_64: only initialize BSP LAPIC --- arch/x86_64/include/arch/devices/local_apic.hpp | 4 +++- arch/x86_64/kapi/platform.cpp | 6 ++--- arch/x86_64/src/devices/local_apic.cpp | 30 +++++++++++++++++++------ 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/arch/x86_64/include/arch/devices/local_apic.hpp b/arch/x86_64/include/arch/devices/local_apic.hpp index 7f125c3..7ae0a02 100644 --- a/arch/x86_64/include/arch/devices/local_apic.hpp +++ b/arch/x86_64/include/arch/devices/local_apic.hpp @@ -12,7 +12,8 @@ namespace arch::devices struct local_apic : kapi::devices::device { - local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id, kapi::memory::physical_address base); + local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id, kapi::memory::physical_address base, + bool is_bsp); auto init() -> bool override; @@ -23,6 +24,7 @@ namespace arch::devices std::uint64_t m_hardware_id{}; kapi::memory::physical_address m_base{}; kapi::memory::linear_address m_virtual_base{}; + bool m_is_bsp{}; }; } // namespace arch::devices diff --git a/arch/x86_64/kapi/platform.cpp b/arch/x86_64/kapi/platform.cpp index c6e41d2..d881f8a 100644 --- a/arch/x86_64/kapi/platform.cpp +++ b/arch/x86_64/kapi/platform.cpp @@ -44,9 +44,9 @@ namespace kapi::platform { auto is_bsp = !bsp_found; bsp_found = true; - auto lapic = kstd::make_unique(interrupt_controller_major, core_index, - local_apic->apic_id(), - real_madt->local_interrupt_controller_address()); + auto lapic = kstd::make_unique( + interrupt_controller_major, core_index, local_apic->apic_id(), + real_madt->local_interrupt_controller_address(), is_bsp); if (kapi::platform::cpu_detected(bus, core_major, core_index, local_apic->processor_id(), is_bsp, std::move(lapic))) { diff --git a/arch/x86_64/src/devices/local_apic.cpp b/arch/x86_64/src/devices/local_apic.cpp index 91e907a..e24e1d4 100644 --- a/arch/x86_64/src/devices/local_apic.cpp +++ b/arch/x86_64/src/devices/local_apic.cpp @@ -20,24 +20,40 @@ namespace arch::devices } // namespace local_apic::local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id, - kapi::memory::physical_address base) + kapi::memory::physical_address base, bool is_bsp) : kapi::devices::device{major, minor, "lapic"} , m_hardware_id{hardware_id} , m_base{base} + , m_is_bsp{is_bsp} {} auto local_apic::init() -> bool { - m_virtual_base = kapi::memory::allocate_mmio_region(1); - if (!kapi::memory::map_mmio_region(m_virtual_base, m_base, kapi::memory::page_mapper::flags::writable)) + auto static shared_virtual_base = kapi::memory::allocate_mmio_region(1); + auto static is_mapped = false; + + if (!is_mapped) { - kstd::println("[x86_64:DEV] LAPIC {} MMIO mapping failed!", m_hardware_id); - return false; + if (!kapi::memory::map_mmio_region(shared_virtual_base, m_base, kapi::memory::page_mapper::flags::writable)) + { + kstd::println("[x86_64:DEV] LAPIC {} MMIO mapping failed!", m_hardware_id); + return false; + } + is_mapped = true; } - write_register(lapic_sivr_register, lapic_enable_bit | spurious_interrupt_vector); + m_virtual_base = shared_virtual_base; + + if (m_is_bsp) + { + write_register(lapic_sivr_register, lapic_enable_bit | spurious_interrupt_vector); - kstd::println("[x86_64:DEV] LAPIC {} initialized. {:#018x}@{:#018x}", m_hardware_id, m_base, m_virtual_base); + kstd::println("[x86_64:DEV] LAPIC {} initialized. {:#018x}@{:#018x}", m_hardware_id, m_base, m_virtual_base); + } + else + { + kstd::println("[x86_64:DEV] LAPIC {} is not on the BSP, deferring intialization.", m_hardware_id); + } return true; } -- cgit v1.2.3 From 0e92017837490d3ce806cf511977ada06d11a2a7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 8 Apr 2026 15:50:48 +0200 Subject: kapi/bus: fix eager initialization --- kapi/include/kapi/devices/bus.hpp | 1 + kernel/kapi/devices/bus.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index 7b2d41f..60134ff 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -52,6 +52,7 @@ namespace kapi::devices private: kstd::vector> m_devices; kstd::vector> m_observers; + std::atomic_flag m_init_was_called{}; std::atomic_flag m_initialized{}; }; diff --git a/kernel/kapi/devices/bus.cpp b/kernel/kapi/devices/bus.cpp index 68874c4..66b84d2 100644 --- a/kernel/kapi/devices/bus.cpp +++ b/kernel/kapi/devices/bus.cpp @@ -20,20 +20,26 @@ namespace kapi::devices auto bus::init() -> bool { - if (m_initialized.test_and_set()) + if (m_init_was_called.test_and_set()) { + kstd::println(kstd::print_sink::stderr, "[OS:DEV] Bus {}:{}:{} already initialized", name(), major(), minor()); return true; } if (!probe()) { + kstd::println(kstd::print_sink::stderr, "[OS:DEV] Bus {}:{}:{} probe failed", name(), major(), minor()); return false; } - return std::ranges::fold_left(m_devices, true, [&](bool acc, auto & child) -> bool { + auto child_status = std::ranges::fold_left(m_devices, true, [&](bool acc, auto & child) -> bool { kstd::println("[OS:DEV] Initializing child device {}@{}", child->name(), name()); return child->init() && acc; }); + + m_initialized.test_and_set(); + + return child_status; } auto bus::add_child(kstd::unique_ptr child) -> void -- cgit v1.2.3 From f36cbd54bb6319050404165e8a3280ccbda05cf3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 8 Apr 2026 15:51:04 +0200 Subject: x86_64: fix CPU enumeration --- arch/x86_64/kapi/platform.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/x86_64/kapi/platform.cpp b/arch/x86_64/kapi/platform.cpp index d881f8a..4ee35c7 100644 --- a/arch/x86_64/kapi/platform.cpp +++ b/arch/x86_64/kapi/platform.cpp @@ -18,7 +18,6 @@ namespace kapi::platform { auto static const core_major = kapi::devices::allocate_major_number(); auto static const interrupt_controller_major = kapi::devices::allocate_major_number(); - auto static core_index = 0uz; auto madt = kapi::acpi::get_table("APIC"); if (!madt) @@ -45,9 +44,9 @@ namespace kapi::platform auto is_bsp = !bsp_found; bsp_found = true; auto lapic = kstd::make_unique( - interrupt_controller_major, core_index, local_apic->apic_id(), + interrupt_controller_major, core_count, local_apic->apic_id(), real_madt->local_interrupt_controller_address(), is_bsp); - if (kapi::platform::cpu_detected(bus, core_major, core_index, local_apic->processor_id(), is_bsp, + if (kapi::platform::cpu_detected(bus, core_major, core_count, local_apic->processor_id(), is_bsp, std::move(lapic))) { ++core_count; -- cgit v1.2.3 From 1ecc0c223a1bacfa1aee183a3573f57c265318df Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 8 Apr 2026 17:07:27 +0200 Subject: x86_64: extend LAPIC initialization --- arch/x86_64/include/arch/devices/local_apic.hpp | 9 ++- arch/x86_64/src/devices/local_apic.cpp | 77 ++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/arch/x86_64/include/arch/devices/local_apic.hpp b/arch/x86_64/include/arch/devices/local_apic.hpp index 7ae0a02..946e4af 100644 --- a/arch/x86_64/include/arch/devices/local_apic.hpp +++ b/arch/x86_64/include/arch/devices/local_apic.hpp @@ -18,13 +18,18 @@ namespace arch::devices auto init() -> bool override; private: - [[nodiscard]] auto read_register(std::size_t offset) const -> std::uint32_t; - auto write_register(std::size_t offset, std::uint32_t value) -> void; + enum struct registers : std::ptrdiff_t; + + [[nodiscard]] auto read_register(registers id) const -> std::uint32_t; + auto write_register(registers id, std::uint32_t value) -> void; std::uint64_t m_hardware_id{}; kapi::memory::physical_address m_base{}; kapi::memory::linear_address m_virtual_base{}; bool m_is_bsp{}; + std::uint8_t m_version{}; + std::uint8_t m_highest_lvt_entry_index{}; + bool m_supports_eoi_broadcast_suppression{}; }; } // namespace arch::devices diff --git a/arch/x86_64/src/devices/local_apic.cpp b/arch/x86_64/src/devices/local_apic.cpp index e24e1d4..54d87a6 100644 --- a/arch/x86_64/src/devices/local_apic.cpp +++ b/arch/x86_64/src/devices/local_apic.cpp @@ -7,18 +7,73 @@ #include #include +#include namespace arch::devices { namespace { - constexpr auto lapic_sivr_register = 0x00F0uz; - constexpr auto lapic_enable_bit = 0x100u; constexpr auto spurious_interrupt_vector = 0xFFu; + + constexpr auto offset_of_version = 0u; + constexpr auto offset_of_max_lvt_entry = 16u; + constexpr auto offset_of_eoi_suppression = 24u; + } // namespace + enum struct local_apic::registers : std::ptrdiff_t + { + id = 0x020, + version = 0x030, + task_priority = 0x080, + arbitration_priority = 0x090, + processor_priority = 0x0a0, + eoi = 0x0b0, + remote_read = 0x0c0, + logical_destination = 0x0d0, + destination_format = 0x0e0, + spurious_interrupt_vector = 0x0f0, + in_service_0 = 0x100, + in_service_1 = 0x110, + in_service_2 = 0x120, + in_service_3 = 0x130, + in_service_4 = 0x140, + in_service_5 = 0x150, + in_service_6 = 0x160, + in_service_7 = 0x170, + trigger_mode_0 = 0x180, + trigger_mode_1 = 0x190, + trigger_mode_2 = 0x1a0, + trigger_mode_3 = 0x1b0, + trigger_mode_4 = 0x1c0, + trigger_mode_5 = 0x1d0, + trigger_mode_6 = 0x1e0, + trigger_mode_7 = 0x1f0, + interrupt_request_0 = 0x200, + interrupt_request_1 = 0x210, + interrupt_request_2 = 0x220, + interrupt_request_3 = 0x230, + interrupt_request_4 = 0x240, + interrupt_request_5 = 0x250, + interrupt_request_6 = 0x260, + interrupt_request_7 = 0x270, + error_status = 0x280, + lvt_corrected_machine_check_interrupt = 0x2f0, + interrupt_command_0 = 0x300, + interrupt_command_1 = 0x310, + lvt_timer = 0x320, + lvt_thermal_sensors = 0x330, + lvt_performance_monitoring_counters = 0x340, + lvt_local_interrupt_0 = 0x350, + lvt_local_interrupt_1 = 0x360, + lvt_error = 0x370, + initial_count = 0x380, + current_count = 0x390, + divide_configuration = 0x3e0, + }; + local_apic::local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id, kapi::memory::physical_address base, bool is_bsp) : kapi::devices::device{major, minor, "lapic"} @@ -46,9 +101,15 @@ namespace arch::devices if (m_is_bsp) { - write_register(lapic_sivr_register, lapic_enable_bit | spurious_interrupt_vector); + auto raw_version = read_register(registers::version); + m_version = (raw_version >> offset_of_version) & 0xff; + m_highest_lvt_entry_index = (raw_version >> offset_of_max_lvt_entry) & 0xff; + m_supports_eoi_broadcast_suppression = (raw_version >> offset_of_eoi_suppression) & 0x1; + + write_register(registers::spurious_interrupt_vector, lapic_enable_bit | spurious_interrupt_vector); - kstd::println("[x86_64:DEV] LAPIC {} initialized. {:#018x}@{:#018x}", m_hardware_id, m_base, m_virtual_base); + kstd::println("[x86_64:DEV] LAPIC initialized. version: {#x} | max_lvt_entry: {} | eoi_suppresion: {:s}", + m_version, m_highest_lvt_entry_index, m_supports_eoi_broadcast_suppression); } else { @@ -58,15 +119,15 @@ namespace arch::devices return true; } - auto local_apic::read_register(std::size_t offset) const -> std::uint32_t + auto local_apic::read_register(registers id) const -> std::uint32_t { - auto reg = static_cast(m_virtual_base + offset); + auto reg = static_cast(m_virtual_base + std::to_underlying(id)); return *reg; } - auto local_apic::write_register(std::size_t offset, std::uint32_t value) -> void + auto local_apic::write_register(registers id, std::uint32_t value) -> void { - auto reg = static_cast(m_virtual_base + offset); + auto reg = static_cast(m_virtual_base + std::to_underlying(id)); *reg = value; } -- cgit v1.2.3 From aa208226f992523865328d4612ae4a7679f57a04 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 8 Apr 2026 17:17:11 +0200 Subject: kapi: return region pair for MMIO allocation --- arch/x86_64/include/arch/devices/local_apic.hpp | 2 +- arch/x86_64/src/devices/local_apic.cpp | 10 +++++----- kapi/include/kapi/memory.hpp | 12 +++++++----- kernel/kapi/memory.cpp | 25 ++++++++++++++++++------- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/arch/x86_64/include/arch/devices/local_apic.hpp b/arch/x86_64/include/arch/devices/local_apic.hpp index 946e4af..ee1073f 100644 --- a/arch/x86_64/include/arch/devices/local_apic.hpp +++ b/arch/x86_64/include/arch/devices/local_apic.hpp @@ -25,7 +25,7 @@ namespace arch::devices std::uint64_t m_hardware_id{}; kapi::memory::physical_address m_base{}; - kapi::memory::linear_address m_virtual_base{}; + kapi::memory::mmio_region m_mapped_region{}; bool m_is_bsp{}; std::uint8_t m_version{}; std::uint8_t m_highest_lvt_entry_index{}; diff --git a/arch/x86_64/src/devices/local_apic.cpp b/arch/x86_64/src/devices/local_apic.cpp index 54d87a6..4a81de8 100644 --- a/arch/x86_64/src/devices/local_apic.cpp +++ b/arch/x86_64/src/devices/local_apic.cpp @@ -97,7 +97,7 @@ namespace arch::devices is_mapped = true; } - m_virtual_base = shared_virtual_base; + m_mapped_region = shared_virtual_base; if (m_is_bsp) { @@ -108,12 +108,12 @@ namespace arch::devices write_register(registers::spurious_interrupt_vector, lapic_enable_bit | spurious_interrupt_vector); - kstd::println("[x86_64:DEV] LAPIC initialized. version: {#x} | max_lvt_entry: {} | eoi_suppresion: {:s}", + kstd::println("[x86_64:DEV] LAPIC initialized. version: {#x} | max_lvt_entry: {} | eoi_suppression: {:s}", m_version, m_highest_lvt_entry_index, m_supports_eoi_broadcast_suppression); } else { - kstd::println("[x86_64:DEV] LAPIC {} is not on the BSP, deferring intialization.", m_hardware_id); + kstd::println("[x86_64:DEV] LAPIC {} is not on the BSP, deferring initialization.", m_hardware_id); } return true; @@ -121,13 +121,13 @@ namespace arch::devices auto local_apic::read_register(registers id) const -> std::uint32_t { - auto reg = static_cast(m_virtual_base + std::to_underlying(id)); + auto reg = static_cast(m_mapped_region.first + std::to_underlying(id)); return *reg; } auto local_apic::write_register(registers id, std::uint32_t value) -> void { - auto reg = static_cast(m_virtual_base + std::to_underlying(id)); + auto reg = static_cast(m_mapped_region.first + std::to_underlying(id)); *reg = value; } diff --git a/kapi/include/kapi/memory.hpp b/kapi/include/kapi/memory.hpp index ae33904..f5e126a 100644 --- a/kapi/include/kapi/memory.hpp +++ b/kapi/include/kapi/memory.hpp @@ -16,6 +16,8 @@ namespace kapi::memory { + using mmio_region = std::pair; + //! @addtogroup kapi-memory-kernel-defined //! @{ @@ -89,7 +91,7 @@ namespace kapi::memory //! //! @warning This function will panic if the MMIO system has not been initialized! //! @param page_count The number of pages to allocate. - auto allocate_mmio_region(std::size_t page_count) -> linear_address; + auto allocate_mmio_region(std::size_t page_count) -> mmio_region; //! Map a region of Memory-mapped I/O address space to a given hardware address using the given flags. //! @@ -98,16 +100,16 @@ namespace kapi::memory //! //! This function will always set the @p uncached flag. //! - //! @param base The base of the virtual region. + //! @param region The region to map. //! @param hw_base The base of the hardware region. //! @param flags The flags to apply. - auto map_mmio_region(linear_address base, physical_address hw_base, page_mapper::flags flags = {}) -> std::byte *; + auto map_mmio_region(mmio_region region, physical_address hw_base, page_mapper::flags flags = {}) -> std::byte *; //! Release a Memory-mapped I/O region. //! //! @warning This function will panic if the MMIO system has not been initialized! - //! @param base The start address of the region to release. - auto release_mmio_region(linear_address base) -> void; + //! @param region The region to release. + auto release_mmio_region(mmio_region region) -> void; //! @} diff --git a/kernel/kapi/memory.cpp b/kernel/kapi/memory.cpp index b224c50..31cd1f4 100644 --- a/kernel/kapi/memory.cpp +++ b/kernel/kapi/memory.cpp @@ -154,22 +154,33 @@ namespace kapi::memory mmio_allocator.emplace(base, page_count); } - auto allocate_mmio_region(std::size_t page_count) -> linear_address + auto allocate_mmio_region(std::size_t page_count) -> mmio_region { auto region = mmio_allocator->allocate(page_count); - return region; + return {region, page_count}; } - auto map_mmio_region(linear_address base, physical_address hw_base, page_mapper::flags flags) -> std::byte * + auto map_mmio_region(mmio_region region, physical_address hw_base, page_mapper::flags flags) -> std::byte * { - auto start_page = page::containing(base); + auto start_page = page::containing(region.first); auto start_frame = frame::containing(hw_base); - return map(start_page, start_frame, flags | page_mapper::flags::uncached); + + flags |= page_mapper::flags::uncached; + + auto start = map(start_page, start_frame, flags); + + std::ranges::for_each(std::views::iota(1uz, region.second), [&](auto index) { + auto page = page::containing(region.first + index * page::size); + auto frame = frame::containing(hw_base + index * page::size); + map(page, frame, flags); + }); + + return start; } - auto release_mmio_region(linear_address base) -> void + auto release_mmio_region(mmio_region region) -> void { - mmio_allocator->release(base); + mmio_allocator->release(region.first); } } // namespace kapi::memory -- cgit v1.2.3 From f6bea6a5f1939f3261392633f6caca186befd555 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 9 Apr 2026 15:54:04 +0200 Subject: kapi: restructure ACPI implementation --- arch/x86_64/kapi/platform.cpp | 49 ++++---- kapi/include/kapi/acpi.hpp | 107 +++--------------- .../kapi/acpi/multiple_apic_description_table.hpp | 104 +++++++++++++++++ kapi/include/kapi/acpi/pointers.hpp | 79 +++++++++++++ .../kapi/acpi/system_description_table_header.hpp | 76 +++++++++++++ kapi/include/kapi/acpi/table_iterator.hpp | 64 +++++++++++ kapi/include/kapi/acpi/table_type.hpp | 22 ++++ kernel/CMakeLists.txt | 3 + kernel/kapi/acpi.cpp | 124 --------------------- .../kapi/acpi/multiple_apic_description_table.cpp | 70 ++++++++++++ kernel/kapi/acpi/pointers.cpp | 55 +++++++++ .../kapi/acpi/system_description_table_header.cpp | 51 +++++++++ 12 files changed, 565 insertions(+), 239 deletions(-) create mode 100644 kapi/include/kapi/acpi/multiple_apic_description_table.hpp create mode 100644 kapi/include/kapi/acpi/pointers.hpp create mode 100644 kapi/include/kapi/acpi/system_description_table_header.hpp create mode 100644 kapi/include/kapi/acpi/table_iterator.hpp create mode 100644 kapi/include/kapi/acpi/table_type.hpp create mode 100644 kernel/kapi/acpi/multiple_apic_description_table.cpp create mode 100644 kernel/kapi/acpi/pointers.cpp create mode 100644 kernel/kapi/acpi/system_description_table_header.cpp diff --git a/arch/x86_64/kapi/platform.cpp b/arch/x86_64/kapi/platform.cpp index 4ee35c7..fb27329 100644 --- a/arch/x86_64/kapi/platform.cpp +++ b/arch/x86_64/kapi/platform.cpp @@ -8,57 +8,54 @@ #include #include -#include +#include #include namespace kapi::platform { + namespace + { + constexpr auto candidate_flags = acpi::processor_local_apic::flags::processor_enabled // + | acpi::processor_local_apic::flags::online_capable; + } auto discover_cpu_topology(kapi::devices::bus & bus) -> bool { auto static const core_major = kapi::devices::allocate_major_number(); auto static const interrupt_controller_major = kapi::devices::allocate_major_number(); - auto madt = kapi::acpi::get_table("APIC"); + auto madt = kapi::acpi::get_table(); if (!madt) { kstd::println("[x86_64:PLT] Failed to find ACPI APIC table"); return false; } - auto real_madt = static_cast(madt.get()); - auto current = reinterpret_cast(madt.get()) + sizeof(kapi::acpi::madt_header); - auto end = reinterpret_cast(madt.get()) + madt->length(); - auto bsp_found = false; auto core_count = 0uz; + auto local_apic_address = madt->local_interrupt_controller_address(); - while (current < end) + auto lapic_entries = *madt | std::views::filter([](auto const & entry) { + return entry.type() == acpi::multiple_apic_description_table_entry::types::processor_local_apic; + }) | std::views::transform([](auto const & entry) { + return static_cast(entry); + }) | std::views::filter([](auto const & entry) { + return static_cast(entry.active_flags() & candidate_flags); + }); + + for (auto const & apic : lapic_entries) { - auto const * sub_table = reinterpret_cast(current); - if (sub_table->type() == 0) + auto is_bsp = !bsp_found; + bsp_found = true; + auto instance = kstd::make_unique(interrupt_controller_major, core_count, + apic.apic_id(), local_apic_address, is_bsp); + if (kapi::platform::cpu_detected(bus, core_major, core_count, apic.processor_id(), is_bsp, std::move(instance))) { - auto const * local_apic = reinterpret_cast(sub_table); - if (local_apic->flags() & 0b11) - { - auto is_bsp = !bsp_found; - bsp_found = true; - auto lapic = kstd::make_unique( - interrupt_controller_major, core_count, local_apic->apic_id(), - real_madt->local_interrupt_controller_address(), is_bsp); - if (kapi::platform::cpu_detected(bus, core_major, core_count, local_apic->processor_id(), is_bsp, - std::move(lapic))) - { - ++core_count; - } - } + ++core_count; } - - current += sub_table->length(); } kstd::println("[x86_64:PLT] Found {} CPU cores", core_count); - return core_count > 0; } diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index 75b6c48..5323fee 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -1,14 +1,15 @@ #ifndef TEACHOS_KAPI_ACPI_HPP #define TEACHOS_KAPI_ACPI_HPP -#include "kapi/memory.hpp" +#include "kapi/acpi/multiple_apic_description_table.hpp" // IWYU pragma: export +#include "kapi/acpi/pointers.hpp" // IWYU pragma: export +#include "kapi/acpi/system_description_table_header.hpp" // IWYU pragma: export +#include "kapi/acpi/table_type.hpp" #include #include -#include #include -#include #include #include @@ -18,91 +19,6 @@ namespace kapi::acpi //! @addtogroup kapi-acpi-kernel-defined //! @{ - struct [[gnu::packed]] root_system_description_pointer - { - [[nodiscard]] auto oem_id() const noexcept -> std::string_view; - [[nodiscard]] auto revision() const noexcept -> std::uint8_t; - [[nodiscard]] auto signature() const noexcept -> std::string_view; - [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; - [[nodiscard]] auto validate() const noexcept -> bool; - - private: - std::array m_signature; - [[maybe_unused]] std::uint8_t m_checksum; - std::array m_oem_id; - std::uint8_t m_revision; - std::array m_rsdt_address; - }; - - struct [[gnu::packed]] extended_system_description_pointer : root_system_description_pointer - { - [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; - [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; - [[nodiscard]] auto validate() const noexcept -> bool; - - private: - std::uint32_t m_length; - std::array m_xsdt_address; - [[maybe_unused]] std::uint8_t m_extended_checksum; - [[maybe_unused]] std::array m_reserved; - }; - - struct [[gnu::packed]] system_description_table_header - { - [[nodiscard]] auto checksum() const noexcept -> std::uint8_t; - [[nodiscard]] auto creator_revision() const noexcept -> std::uint32_t; - [[nodiscard]] auto creator_id() const noexcept -> std::uint32_t; - [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; - [[nodiscard]] auto oem_id() const noexcept -> std::string_view; - [[nodiscard]] auto oem_revision() const noexcept -> std::uint32_t; - [[nodiscard]] auto oem_table_id() const noexcept -> std::string_view; - [[nodiscard]] auto revision() const noexcept -> std::uint8_t; - [[nodiscard]] auto signature() const noexcept -> std::string_view; - - private: - std::array m_signature; - std::uint32_t m_length; - std::uint8_t m_revision; - std::uint8_t m_checksum; - std::array m_oem_id; - std::array m_oem_table_id; - std::uint32_t m_oem_revision; - std::uint32_t m_creator_id; - std::uint32_t m_creator_revision; - }; - - struct [[gnu::packed]] madt_header : system_description_table_header - { - [[nodiscard]] auto local_interrupt_controller_address() const noexcept -> memory::physical_address; - [[nodiscard]] auto flags() const noexcept -> std::uint32_t; - - private: - std::uint32_t m_local_interrupt_controller_address; - std::uint32_t m_flags; - }; - - struct [[gnu::packed]] madt_subtable_header - { - [[nodiscard]] auto type() const noexcept -> std::uint8_t; - [[nodiscard]] auto length() const noexcept -> std::size_t; - - private: - std::uint8_t m_type; - std::uint8_t m_length; - }; - - struct [[gnu::packed]] madt_local_apic : madt_subtable_header - { - [[nodiscard]] auto apic_id() const noexcept -> std::uint8_t; - [[nodiscard]] auto flags() const noexcept -> std::uint32_t; - [[nodiscard]] auto processor_id() const noexcept -> std::uint32_t; - - private: - std::uint8_t m_processor_id; - std::uint8_t m_apic_id; - std::uint32_t m_flags; - }; - //! Initialize the ACPI subsystem and discover the available tables. //! //! @return true iff. a valid system description tabled was found, false otherwise. @@ -114,17 +30,30 @@ namespace kapi::acpi //! @return true iff. the checksum is valid, false otherwise. auto validate_checksum(std::span data) -> bool; - //! Get an ACPI table by its signature. + //! Get a pointer to an ACPI table by its signature. //! //! @param signature The signature of the table to get. //! @return A pointer to the table if found, nullptr otherwise. auto get_table(std::string_view signature) -> kstd::observer_ptr; + //! Get a type-cast pointer to an ACPI table by its signature. + //! + //! @tparam Signature The signature of the table to get + //! @return A pointer to the table if found, nullptr otherwise. + template + auto get_table() -> kstd::observer_ptr const> + { + return kstd::make_observer(static_cast const *>(get_table(Signature).get())); + } + //! @} //! @addtogroup kapi-acpi-platform-defined //! @{ + //! Retrieve the RSDP or XSDP for this system. + //! + //! @return a pointer to either the RSDP or XSDP data structure, or @p nullptr if neither is present. auto get_root_pointer() -> kstd::observer_ptr; //! @} diff --git a/kapi/include/kapi/acpi/multiple_apic_description_table.hpp b/kapi/include/kapi/acpi/multiple_apic_description_table.hpp new file mode 100644 index 0000000..33acac9 --- /dev/null +++ b/kapi/include/kapi/acpi/multiple_apic_description_table.hpp @@ -0,0 +1,104 @@ +#ifndef TEACHOS_KAPI_ACPI_MUTIPLE_APIC_DESCRIPTION_TABLE_HEADER_HPP +#define TEACHOS_KAPI_ACPI_MUTIPLE_APIC_DESCRIPTION_TABLE_HEADER_HPP + +// IWYU pragma: private, include "kapi/acpi.hpp" + +#include "kapi/acpi/system_description_table_header.hpp" +#include "kapi/acpi/table_iterator.hpp" +#include "kapi/acpi/table_type.hpp" +#include "kapi/memory.hpp" + +#include +#include + +#include +#include +#include + +namespace kapi::acpi +{ + + //! @addtogroup kapi-acpi-kernel-defined + //! @{ + + struct [[gnu::packed]] multiple_apic_description_table_entry + { + enum struct types : std::uint8_t + { + processor_local_apic, + io_apic, + io_apic_interrupt_source_override, + io_apic_nmi_source, + local_apic_nmi_interrupts, + local_apic_address_override, + processor_local_x2_apic, + }; + + [[nodiscard]] auto type() const noexcept -> types; + [[nodiscard]] auto length() const noexcept -> std::size_t; + + private: + std::uint8_t m_type; + std::uint8_t m_length; + }; + + //! The common header for all + struct [[gnu::packed]] multiple_apic_description_table : system_description_table_header + { + using iterator = table_iterator; + using const_iterator = iterator; + + [[nodiscard]] auto local_interrupt_controller_address() const noexcept -> memory::physical_address; + [[nodiscard]] auto flags() const noexcept -> std::uint32_t; + + [[nodiscard]] auto begin() const noexcept -> iterator; + [[nodiscard]] auto cbegin() const noexcept -> iterator; + [[nodiscard]] auto end() const noexcept -> iterator; + [[nodiscard]] auto cend() const noexcept -> iterator; + + private: + std::uint32_t m_local_interrupt_controller_address; + std::uint32_t m_flags; + }; + + struct [[gnu::packed]] processor_local_apic : multiple_apic_description_table_entry + { + enum struct flags : std::uint32_t + { + processor_enabled = 1, + online_capable = 2, + }; + + [[nodiscard]] auto apic_id() const noexcept -> std::uint8_t; + [[nodiscard]] auto active_flags() const noexcept -> flags; + [[nodiscard]] auto processor_id() const noexcept -> std::uint32_t; + + private: + std::uint8_t m_processor_id; + std::uint8_t m_apic_id; + std::uint32_t m_flags; + }; + + //! @} + + //! @addtogroup kapi-acpi + //! @{ + + constexpr char const madt_table_signature[] = "APIC"; // NOLINT + + template<> + struct table_type + { + using type = multiple_apic_description_table; + }; + + //! @} + +} // namespace kapi::acpi + +template<> +struct kstd::ext::is_bitfield_enum : std::true_type +{ +}; + +#endif diff --git a/kapi/include/kapi/acpi/pointers.hpp b/kapi/include/kapi/acpi/pointers.hpp new file mode 100644 index 0000000..2b88720 --- /dev/null +++ b/kapi/include/kapi/acpi/pointers.hpp @@ -0,0 +1,79 @@ +#ifndef TEACHOS_KAPI_ACPI_POINTERS_HPP +#define TEACHOS_KAPI_ACPI_POINTERS_HPP + +// IWYU pragma: private, include "kapi/acpi.hpp" + +#include "kapi/memory.hpp" + +#include + +#include +#include +#include +#include + +namespace kapi::acpi +{ + + //! @addtogroup kapi-acpi-kernel-defined + //! @{ + + //! A pointer to the Root System Description Table. + struct [[gnu::packed]] root_system_description_pointer + { + //! Get the ID of the OEM, usually a vendor name. + [[nodiscard]] auto oem_id() const noexcept -> std::string_view; + + //! Get the revision of the ACPI Root System Description Table. + //! + //! @note Revisions greater or equal to two indicate that the system uses the Extended System Description Table, and + //! as such that table should be use to query information. + [[nodiscard]] auto revision() const noexcept -> std::uint8_t; + + //! Get the signature of this pointer. + //! + //! A valid RSDP must always carry the signature "RSD PTR ". + [[nodiscard]] auto signature() const noexcept -> std::string_view; + + //! Get the physical address of the pointed-to Root System Description Table. + [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; + + //! Validate the checksum of this RSDP. + //! + //! @return @p true iff. the checksum of this RSDP is valid, @p false otherwise. + [[nodiscard]] auto validate() const noexcept -> bool; + + private: + std::array m_signature; + [[maybe_unused]] std::uint8_t m_checksum; + std::array m_oem_id; + std::uint8_t m_revision; + std::array m_rsdt_address; + }; + + //! A pointer to the Extended System Description Table. + struct [[gnu::packed]] extended_system_description_pointer : root_system_description_pointer + { + //! Get the length of the data contained in this pointer. + [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; + + //! Get the address of the pointed-to Extended System Description Table. + [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; + + //! Validate the checksum of this XSDP. + //! + //! @return @p true iff. the checksum of this RSDP is valid, @p false otherwise. + [[nodiscard]] auto validate() const noexcept -> bool; + + private: + std::uint32_t m_length; + std::array m_xsdt_address; + [[maybe_unused]] std::uint8_t m_extended_checksum; + [[maybe_unused]] std::array m_reserved; + }; + + //! @} + +} // namespace kapi::acpi + +#endif diff --git a/kapi/include/kapi/acpi/system_description_table_header.hpp b/kapi/include/kapi/acpi/system_description_table_header.hpp new file mode 100644 index 0000000..b336ea1 --- /dev/null +++ b/kapi/include/kapi/acpi/system_description_table_header.hpp @@ -0,0 +1,76 @@ +#ifndef TEACHOS_KAPI_ACPI_SYSTEM_DESCRIPTION_TABLE_HEADER_HPP +#define TEACHOS_KAPI_ACPI_SYSTEM_DESCRIPTION_TABLE_HEADER_HPP + +// IWYU pragma: private, include "kapi/acpi.hpp" + +#include "kapi/acpi/table_type.hpp" + +#include + +#include +#include +#include + +namespace kapi::acpi +{ + + //! @addtogroup kapi-acpi-kernel-defined + //! @{ + + //! The common header of all System Description Tables. + struct [[gnu::packed]] system_description_table_header + { + //! Get the revision of the utility used to create this table. + [[nodiscard]] auto creator_revision() const noexcept -> std::uint32_t; + + //! Get the vendor ID of the utility used to create this table. + [[nodiscard]] auto creator_id() const noexcept -> std::uint32_t; + + //! Get the length of the entire table, including this header. + [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; + + //! Get the ID of the OEM. + [[nodiscard]] auto oem_id() const noexcept -> std::string_view; + + //! Get the OEMs revision number of this table. + [[nodiscard]] auto oem_revision() const noexcept -> std::uint32_t; + + //! Get the OEMs ID of this table. + [[nodiscard]] auto oem_table_id() const noexcept -> std::string_view; + + //! Get the revision number of the structure of this table. + [[nodiscard]] auto revision() const noexcept -> std::uint8_t; + + //! Get the signature of this table. + [[nodiscard]] auto signature() const noexcept -> std::string_view; + + private: + std::array m_signature; + std::uint32_t m_length; + std::uint8_t m_revision; + [[maybe_unused]] std::uint8_t m_checksum; + std::array m_oem_id; + std::array m_oem_table_id; + std::uint32_t m_oem_revision; + std::uint32_t m_creator_id; + std::uint32_t m_creator_revision; + }; + + //! @} + // + //! @addtogroup kapi-acpi + //! @{ + + constexpr char const rsdt_table_signature[] = "RSDT"; // NOLINT + + template<> + struct table_type + { + using type = system_description_table_header; + }; + + //! @} + +} // namespace kapi::acpi + +#endif diff --git a/kapi/include/kapi/acpi/table_iterator.hpp b/kapi/include/kapi/acpi/table_iterator.hpp new file mode 100644 index 0000000..998d6d6 --- /dev/null +++ b/kapi/include/kapi/acpi/table_iterator.hpp @@ -0,0 +1,64 @@ +#ifndef TEACHOS_KAPI_ACPI_TABLE_ITERATOR_HPP +#define TEACHOS_KAPI_ACPI_TABLE_ITERATOR_HPP + +#include +#include +#include + +namespace kapi::acpi +{ + + template + struct table_iterator + { + using iterator_category = std::forward_iterator_tag; + using value_type = EntryType; + using pointer = value_type *; + using reference = value_type &; + using difference_type = std::ptrdiff_t; + + constexpr table_iterator() noexcept = default; + + constexpr table_iterator(pointer entry, pointer limit) noexcept + : m_entry{entry} + , m_limit{limit} + {} + + constexpr auto operator++() noexcept -> table_iterator & + { + auto decayed = std::bit_cast(m_entry); + decayed += m_entry->length(); + m_entry = std::bit_cast(decayed); + return *this; + } + + constexpr auto operator++(int) noexcept -> table_iterator + { + auto copy = *this; + ++*this; + return copy; + } + + constexpr auto operator*() const noexcept -> reference + { + return *m_entry; + } + + constexpr auto operator==(table_iterator const & other) const noexcept -> bool + { + return m_entry == other.m_entry || (is_end() && other.is_end()); + } + + private: + [[nodiscard]] constexpr auto is_end() const noexcept -> bool + { + return m_entry == m_limit; + } + + pointer m_entry{}; + pointer m_limit{}; + }; + +} // namespace kapi::acpi + +#endif diff --git a/kapi/include/kapi/acpi/table_type.hpp b/kapi/include/kapi/acpi/table_type.hpp new file mode 100644 index 0000000..e088a24 --- /dev/null +++ b/kapi/include/kapi/acpi/table_type.hpp @@ -0,0 +1,22 @@ +#ifndef TEACHOS_KAPI_ACPI_TABLE_TYPE_HPP +#define TEACHOS_KAPI_ACPI_TABLE_TYPE_HPP + +// IWYU pragma: private + +namespace kapi::acpi +{ + + //! @addtogroup kapi-acpi-kernel-defined + //! @{ + + template + struct table_type; + + template + using table_type_t = typename table_type::type; + + //! @} + +} // namespace kapi::acpi + +#endif diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 67db0a8..8af6fe7 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -1,6 +1,9 @@ add_library("kernel_objs" OBJECT # Platform-independent KAPI implementation "kapi/acpi.cpp" + "kapi/acpi/multiple_apic_description_table.cpp" + "kapi/acpi/pointers.cpp" + "kapi/acpi/system_description_table_header.cpp" "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp index 1283d7e..e7c4921 100644 --- a/kernel/kapi/acpi.cpp +++ b/kernel/kapi/acpi.cpp @@ -1,16 +1,13 @@ #include "kapi/acpi.hpp" -#include "kapi/memory.hpp" #include "kapi/system.hpp" #include "kernel/acpi/manager.hpp" #include -#include #include #include -#include #include #include #include @@ -25,127 +22,6 @@ namespace kapi::acpi auto constinit manager = std::optional{}; } // namespace - auto root_system_description_pointer::oem_id() const noexcept -> std::string_view - { - return {m_oem_id.data(), m_oem_id.size()}; - } - - auto root_system_description_pointer::revision() const noexcept -> std::uint8_t - { - return m_revision; - } - - auto root_system_description_pointer::signature() const noexcept -> std::string_view - { - return {m_signature.data(), m_signature.size()}; - } - - auto root_system_description_pointer::table_address() const noexcept -> memory::physical_address - { - auto raw = std::bit_cast(m_rsdt_address); - return memory::physical_address{static_cast(raw)}; - } - - auto root_system_description_pointer::validate() const noexcept -> bool - { - return validate_checksum({reinterpret_cast(this), sizeof(root_system_description_pointer)}); - } - - auto extended_system_description_pointer::length() const noexcept -> kstd::units::bytes - { - return kstd::units::bytes{m_length}; - } - - auto extended_system_description_pointer::table_address() const noexcept -> memory::physical_address - { - return memory::physical_address{std::bit_cast(m_xsdt_address)}; - } - - auto extended_system_description_pointer::validate() const noexcept -> bool - { - return validate_checksum({reinterpret_cast(this), m_length}); - } - - [[nodiscard]] auto system_description_table_header::checksum() const noexcept -> std::uint8_t - { - return m_checksum; - } - - [[nodiscard]] auto system_description_table_header::creator_revision() const noexcept -> std::uint32_t - { - return m_creator_revision; - } - - [[nodiscard]] auto system_description_table_header::creator_id() const noexcept -> std::uint32_t - { - return m_creator_id; - } - - [[nodiscard]] auto system_description_table_header::length() const noexcept -> kstd::units::bytes - { - return kstd::units::bytes{m_length}; - } - - [[nodiscard]] auto system_description_table_header::oem_id() const noexcept -> std::string_view - { - return {m_oem_id.data(), m_oem_id.size()}; - } - - [[nodiscard]] auto system_description_table_header::oem_revision() const noexcept -> std::uint32_t - { - return m_oem_revision; - } - - [[nodiscard]] auto system_description_table_header::oem_table_id() const noexcept -> std::string_view - { - return {m_oem_table_id.data(), m_oem_table_id.size()}; - } - - [[nodiscard]] auto system_description_table_header::revision() const noexcept -> std::uint8_t - { - return m_revision; - } - - [[nodiscard]] auto system_description_table_header::signature() const noexcept -> std::string_view - { - return {m_signature.data(), m_signature.size()}; - } - - [[nodiscard]] auto madt_header::local_interrupt_controller_address() const noexcept -> memory::physical_address - { - return memory::physical_address{static_cast(m_local_interrupt_controller_address)}; - } - - [[nodiscard]] auto madt_header::flags() const noexcept -> std::uint32_t - { - return m_flags; - } - - [[nodiscard]] auto madt_subtable_header::type() const noexcept -> std::uint8_t - { - return m_type; - } - - [[nodiscard]] auto madt_subtable_header::length() const noexcept -> std::size_t - { - return m_length; - } - - [[nodiscard]] auto madt_local_apic::apic_id() const noexcept -> std::uint8_t - { - return m_apic_id; - } - - [[nodiscard]] auto madt_local_apic::flags() const noexcept -> std::uint32_t - { - return m_flags; - } - - [[nodiscard]] auto madt_local_apic::processor_id() const noexcept -> std::uint32_t - { - return m_processor_id; - } - auto init(root_system_description_pointer const & sdp) -> bool { auto static constinit initialized = std::atomic_flag{}; diff --git a/kernel/kapi/acpi/multiple_apic_description_table.cpp b/kernel/kapi/acpi/multiple_apic_description_table.cpp new file mode 100644 index 0000000..c0360a3 --- /dev/null +++ b/kernel/kapi/acpi/multiple_apic_description_table.cpp @@ -0,0 +1,70 @@ +#include "kapi/acpi.hpp" +#include "kapi/memory.hpp" + +#include +#include + +namespace kapi::acpi +{ + + auto multiple_apic_description_table::local_interrupt_controller_address() const noexcept -> memory::physical_address + { + return memory::physical_address{static_cast(m_local_interrupt_controller_address)}; + } + + auto multiple_apic_description_table::flags() const noexcept -> std::uint32_t + { + return m_flags; + } + + auto multiple_apic_description_table_entry::type() const noexcept -> types + { + return static_cast(m_type); + } + + auto multiple_apic_description_table_entry::length() const noexcept -> std::size_t + { + return m_length; + } + + auto processor_local_apic::apic_id() const noexcept -> std::uint8_t + { + return m_apic_id; + } + + auto processor_local_apic::active_flags() const noexcept -> flags + { + return static_cast(m_flags); + } + + auto processor_local_apic::processor_id() const noexcept -> std::uint32_t + { + return m_processor_id; + } + + auto multiple_apic_description_table::begin() const noexcept -> iterator + { + auto base = reinterpret_cast(this); + base += sizeof(multiple_apic_description_table); + auto limit = reinterpret_cast(this); + limit += length().value; + return iterator{reinterpret_cast(base), + reinterpret_cast(limit)}; + } + + auto multiple_apic_description_table::cbegin() const noexcept -> iterator + { + return begin(); + } + + auto multiple_apic_description_table::end() const noexcept -> iterator + { + return {}; + } + + auto multiple_apic_description_table::cend() const noexcept -> iterator + { + return end(); + } + +} // namespace kapi::acpi diff --git a/kernel/kapi/acpi/pointers.cpp b/kernel/kapi/acpi/pointers.cpp new file mode 100644 index 0000000..63831e9 --- /dev/null +++ b/kernel/kapi/acpi/pointers.cpp @@ -0,0 +1,55 @@ +#include "kapi/acpi.hpp" +#include "kapi/memory.hpp" + +#include + +#include +#include +#include +#include + +namespace kapi::acpi +{ + + auto root_system_description_pointer::oem_id() const noexcept -> std::string_view + { + return {m_oem_id.data(), m_oem_id.size()}; + } + + auto root_system_description_pointer::revision() const noexcept -> std::uint8_t + { + return m_revision; + } + + auto root_system_description_pointer::signature() const noexcept -> std::string_view + { + return {m_signature.data(), m_signature.size()}; + } + + auto root_system_description_pointer::table_address() const noexcept -> memory::physical_address + { + auto raw = std::bit_cast(m_rsdt_address); + return memory::physical_address{static_cast(raw)}; + } + + auto root_system_description_pointer::validate() const noexcept -> bool + { + return validate_checksum({reinterpret_cast(this), sizeof(root_system_description_pointer)}); + } + + auto extended_system_description_pointer::length() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{m_length}; + } + + auto extended_system_description_pointer::table_address() const noexcept -> memory::physical_address + { + return memory::physical_address{std::bit_cast(m_xsdt_address)}; + } + + auto extended_system_description_pointer::validate() const noexcept -> bool + { + return validate_checksum({reinterpret_cast(this), m_length}); + } + +} // namespace kapi::acpi diff --git a/kernel/kapi/acpi/system_description_table_header.cpp b/kernel/kapi/acpi/system_description_table_header.cpp new file mode 100644 index 0000000..f688b4d --- /dev/null +++ b/kernel/kapi/acpi/system_description_table_header.cpp @@ -0,0 +1,51 @@ +#include "kapi/acpi.hpp" + +#include + +#include +#include + +namespace kapi::acpi +{ + + [[nodiscard]] auto system_description_table_header::creator_revision() const noexcept -> std::uint32_t + { + return m_creator_revision; + } + + [[nodiscard]] auto system_description_table_header::creator_id() const noexcept -> std::uint32_t + { + return m_creator_id; + } + + [[nodiscard]] auto system_description_table_header::length() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{m_length}; + } + + [[nodiscard]] auto system_description_table_header::oem_id() const noexcept -> std::string_view + { + return {m_oem_id.data(), m_oem_id.size()}; + } + + [[nodiscard]] auto system_description_table_header::oem_revision() const noexcept -> std::uint32_t + { + return m_oem_revision; + } + + [[nodiscard]] auto system_description_table_header::oem_table_id() const noexcept -> std::string_view + { + return {m_oem_table_id.data(), m_oem_table_id.size()}; + } + + [[nodiscard]] auto system_description_table_header::revision() const noexcept -> std::uint8_t + { + return m_revision; + } + + [[nodiscard]] auto system_description_table_header::signature() const noexcept -> std::string_view + { + return {m_signature.data(), m_signature.size()}; + } + +} // namespace kapi::acpi -- cgit v1.2.3 From 65fe71d4c2cc4fa7b09a83671dccb5d4483f0524 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 9 Apr 2026 15:57:22 +0200 Subject: ide: update neovim configuration --- .nvim.lua | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.nvim.lua b/.nvim.lua index e8205cb..ad007bb 100644 --- a/.nvim.lua +++ b/.nvim.lua @@ -1,5 +1,7 @@ vim.opt.exrc = false vim.g.autoformat = true +vim.g.load_doxygen_syntax = true +vim.opt.fixeol = false local workspace_folder = vim.fn.getcwd() @@ -23,6 +25,16 @@ default_clangd_config.cmd = { vim.lsp.config("clangd", default_clangd_config) +vim.filetype.add({ + pattern = { + [".*/include/kstd/.*"] = function(path, _) + if not path:match("%.[^/]+$") then + return "cpp" + end + end + } +}) + -- Debugging local dap = require("dap") local qemu_job_id = nil @@ -76,8 +88,8 @@ dap.adapters.teachos_qemu_mi = function(callback, config) local paths = resolve_build_paths(artifact_path) config.setupCommands = { { description = "Enable pretty-printing for gdb", text = "-enable-pretty-printing" }, - { description = "Load code", text = string.format("-file-exec-file %s", paths.elf) }, - { description = "Load symbols", text = string.format("-file-exec-and-symbols %s", paths.sym) }, + { description = "Load code", text = string.format("-file-exec-file %s", paths.elf) }, + { description = "Load symbols", text = string.format("-file-exec-and-symbols %s", paths.sym) }, } config.program = paths.sym @@ -108,7 +120,7 @@ dap.listeners.after.disconnect["teachos_qemu_teardown"] = teardown_qemu require("cmake-tools").setup({ cmake_compile_commands_options = { - action = "soft_link", + action = "copy", target = string.format("%s/build", workspace_folder), }, cmake_dap_configuration = { @@ -126,7 +138,6 @@ require("cmake-tools").setup({ vim.api.nvim_create_user_command("TeachOSRun", function() local cmake = safe_require("cmake-tools") if not cmake then return end - local is_ready, target = pcall(cmake.get_launch_target_path) if is_ready and target then launch_qemu(target, false) -- cgit v1.2.3 From 3ad230fab8dc17758559aac3c20ba67a8c619878 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 10 Apr 2026 09:01:59 +0200 Subject: kapi: move platform functions to CPU --- arch/x86_64/CMakeLists.txt | 1 - arch/x86_64/kapi/cpu.cpp | 54 ++++++++++++++++++++++++++++++++++++ arch/x86_64/kapi/platform.cpp | 62 ------------------------------------------ kapi/include/kapi/cpu.hpp | 20 ++++++++++++++ kapi/include/kapi/platform.hpp | 34 ----------------------- kernel/CMakeLists.txt | 1 - kernel/kapi/cpu.cpp | 19 +++++++++++++ kernel/kapi/platform.cpp | 27 ------------------ kernel/src/devices/cpu.cpp | 4 +-- 9 files changed, 95 insertions(+), 127 deletions(-) delete mode 100644 arch/x86_64/kapi/platform.cpp delete mode 100644 kapi/include/kapi/platform.hpp delete mode 100644 kernel/kapi/platform.cpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 87cb98c..62a2aad 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -19,7 +19,6 @@ target_sources("x86_64" PRIVATE "kapi/devices.cpp" "kapi/interrupts.cpp" "kapi/memory.cpp" - "kapi/platform.cpp" "kapi/system.cpp" # CPU Initialization diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index 12edb0f..1382e08 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -1,14 +1,28 @@ #include "kapi/cpu.hpp" +#include "kapi/acpi.hpp" +#include "kapi/devices.hpp" #include "kapi/system.hpp" #include "arch/cpu/initialization.hpp" +#include "arch/devices/local_apic.hpp" + +#include +#include #include +#include +#include namespace kapi::cpu { + namespace + { + constexpr auto candidate_flags = acpi::processor_local_apic::flags::processor_enabled // + | acpi::processor_local_apic::flags::online_capable; + } + auto init() -> void { auto static constinit is_initialized = std::atomic_flag{}; @@ -28,4 +42,44 @@ namespace kapi::cpu __builtin_unreachable(); } + auto discover_topology(kapi::devices::bus & bus) -> bool + { + auto static const core_major = kapi::devices::allocate_major_number(); + auto static const interrupt_controller_major = kapi::devices::allocate_major_number(); + + auto madt = kapi::acpi::get_table(); + if (!madt) + { + kstd::println("[x86_64:PLT] Failed to find ACPI APIC table"); + return false; + } + + auto bsp_found = false; + auto core_count = 0uz; + auto local_apic_address = madt->local_interrupt_controller_address(); + + auto lapic_entries = *madt | std::views::filter([](auto const & entry) { + return entry.type() == acpi::multiple_apic_description_table_entry::types::processor_local_apic; + }) | std::views::transform([](auto const & entry) { + return static_cast(entry); + }) | std::views::filter([](auto const & entry) { + return static_cast(entry.active_flags() & candidate_flags); + }); + + for (auto const & apic : lapic_entries) + { + auto is_bsp = !bsp_found; + bsp_found = true; + auto instance = kstd::make_unique(interrupt_controller_major, core_count, + apic.apic_id(), local_apic_address, is_bsp); + if (core_detected(bus, core_major, core_count, apic.processor_id(), is_bsp, std::move(instance))) + { + ++core_count; + } + } + + kstd::println("[x86_64:PLT] Found {} CPU cores", core_count); + return core_count > 0; + } + } // namespace kapi::cpu diff --git a/arch/x86_64/kapi/platform.cpp b/arch/x86_64/kapi/platform.cpp deleted file mode 100644 index fb27329..0000000 --- a/arch/x86_64/kapi/platform.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include "kapi/platform.hpp" - -#include "kapi/acpi.hpp" -#include "kapi/devices.hpp" - -#include "arch/devices/local_apic.hpp" - -#include -#include - -#include -#include - -namespace kapi::platform -{ - namespace - { - constexpr auto candidate_flags = acpi::processor_local_apic::flags::processor_enabled // - | acpi::processor_local_apic::flags::online_capable; - } - - auto discover_cpu_topology(kapi::devices::bus & bus) -> bool - { - auto static const core_major = kapi::devices::allocate_major_number(); - auto static const interrupt_controller_major = kapi::devices::allocate_major_number(); - - auto madt = kapi::acpi::get_table(); - if (!madt) - { - kstd::println("[x86_64:PLT] Failed to find ACPI APIC table"); - return false; - } - - auto bsp_found = false; - auto core_count = 0uz; - auto local_apic_address = madt->local_interrupt_controller_address(); - - auto lapic_entries = *madt | std::views::filter([](auto const & entry) { - return entry.type() == acpi::multiple_apic_description_table_entry::types::processor_local_apic; - }) | std::views::transform([](auto const & entry) { - return static_cast(entry); - }) | std::views::filter([](auto const & entry) { - return static_cast(entry.active_flags() & candidate_flags); - }); - - for (auto const & apic : lapic_entries) - { - auto is_bsp = !bsp_found; - bsp_found = true; - auto instance = kstd::make_unique(interrupt_controller_major, core_count, - apic.apic_id(), local_apic_address, is_bsp); - if (kapi::platform::cpu_detected(bus, core_major, core_count, apic.processor_id(), is_bsp, std::move(instance))) - { - ++core_count; - } - } - - kstd::println("[x86_64:PLT] Found {} CPU cores", core_count); - return core_count > 0; - } - -} // namespace kapi::platform diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index d90365a..c643cd7 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -1,10 +1,13 @@ #ifndef TEACHOS_KAPI_CPU_HPP #define TEACHOS_KAPI_CPU_HPP +#include "kapi/devices.hpp" #include "kapi/memory.hpp" #include +#include +#include #include namespace kapi::cpu @@ -72,6 +75,17 @@ namespace kapi::cpu //! @return Whether the exception was handled. [[nodiscard]] auto dispatch(exception const & context) -> bool; + //! A hook that is called when the platform detects a CPU core during topology discovery. + //! + //! @param bus The bus the CPU was detected on. + //! @param major The major number of the CPU device. + //! @param minor The minor number of the CPU device. + //! @param hardware_id The hardware specific ID of the CPU core. + //! @param is_bsp Whether the reported CPU is the the bootstrap processor. + //! @param core_interrupt_controller The local interrupt controller of this CPU core. + auto core_detected(kapi::devices::bus & bus, std::size_t major, std::size_t minor, std::uint64_t hardware_id, + bool is_bsp, kstd::unique_ptr core_interrupt_controller) -> bool; + //! @} //! @addtogroup kapi-cpu-platform-defined @@ -88,6 +102,12 @@ namespace kapi::cpu //! interrupts itself. auto init() -> void; + //! Discover the CPU topology of the platform and attach it to the given CPU bus. + //! + //! @param bus The bus to attach the CPU topology to. + //! @return true iff. the CPU topology was discovered successfully, false otherwise. + auto discover_topology(kapi::devices::bus & bus) -> bool; + //! @} } // namespace kapi::cpu diff --git a/kapi/include/kapi/platform.hpp b/kapi/include/kapi/platform.hpp deleted file mode 100644 index e1e267e..0000000 --- a/kapi/include/kapi/platform.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef TEACHOS_KAPI_PLATFORM_HPP -#define TEACHOS_KAPI_PLATFORM_HPP - -#include "kapi/devices.hpp" - -#include - -#include -#include - -namespace kapi::platform -{ - - //! @addtogroup kapi-platform-kernel-defined - //! @{ - - auto cpu_detected(kapi::devices::bus & bus, std::size_t major, std::size_t minor, std::uint64_t hardware_id, - bool is_bsp, kstd::unique_ptr core_interrupt_controller) -> bool; - //! @} - - //! @addtogroup kapi-platform-platform-defined - //! @{ - - //! Discover the CPU topology of the platform and attach it to the given CPU bus. - //! - //! @param bus The bus to attach the CPU topology to. - //! @return true iff. the CPU topology was discovered successfully, false otherwise. - auto discover_cpu_topology(kapi::devices::bus & bus) -> bool; - - //! @} - -} // namespace kapi::platform - -#endif \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 8af6fe7..61bc6f1 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -12,7 +12,6 @@ add_library("kernel_objs" OBJECT "kapi/devices/device.cpp" "kapi/interrupts.cpp" "kapi/memory.cpp" - "kapi/platform.cpp" "kapi/system.cpp" # KSTD OS Implementation diff --git a/kernel/kapi/cpu.cpp b/kernel/kapi/cpu.cpp index 13de584..d632628 100644 --- a/kernel/kapi/cpu.cpp +++ b/kernel/kapi/cpu.cpp @@ -1,9 +1,17 @@ #include "kapi/cpu.hpp" +#include "kapi/devices.hpp" #include "kapi/system.hpp" +#include "kernel/devices/cpu.hpp" + +#include #include +#include +#include +#include + namespace kapi::cpu { @@ -32,4 +40,15 @@ namespace kapi::cpu } } + auto core_detected(kapi::devices::bus & bus, std::size_t major, std::size_t minor, std::uint64_t hardware_id, + bool is_bsp, kstd::unique_ptr core_interrupt_controller) -> bool + { + auto core = kstd::make_unique(major, minor, hardware_id, is_bsp); + + core->add_child(std::move(core_interrupt_controller)); + bus.add_child(std::move(core)); + + return true; + } + } // namespace kapi::cpu \ No newline at end of file diff --git a/kernel/kapi/platform.cpp b/kernel/kapi/platform.cpp deleted file mode 100644 index 7638cf9..0000000 --- a/kernel/kapi/platform.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "kapi/platform.hpp" - -#include "kapi/devices.hpp" - -#include "kernel/devices/cpu.hpp" - -#include - -#include -#include -#include - -namespace kapi::platform -{ - - auto cpu_detected(kapi::devices::bus & bus, std::size_t major, std::size_t minor, std::uint64_t hardware_id, - bool is_bsp, kstd::unique_ptr core_interrupt_controller) -> bool - { - auto core = kstd::make_unique(major, minor, hardware_id, is_bsp); - - core->add_child(std::move(core_interrupt_controller)); - bus.add_child(std::move(core)); - - return true; - } - -} // namespace kapi::platform \ No newline at end of file diff --git a/kernel/src/devices/cpu.cpp b/kernel/src/devices/cpu.cpp index eb10d74..85f4d47 100644 --- a/kernel/src/devices/cpu.cpp +++ b/kernel/src/devices/cpu.cpp @@ -1,7 +1,7 @@ #include "kernel/devices/cpu.hpp" +#include "kapi/cpu.hpp" #include "kapi/devices.hpp" -#include "kapi/platform.hpp" #include @@ -33,7 +33,7 @@ namespace kernel::devices auto cpu::probe() -> bool { - if (!kapi::platform::discover_cpu_topology(*this)) + if (!kapi::cpu::discover_topology(*this)) { kstd::println("[OS:DEV] Failed to discover CPU topology"); return false; -- cgit v1.2.3 From ed663488a66383e136534a5c43c66ef5868cb2d3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 10 Apr 2026 09:53:25 +0200 Subject: fixup! kapi: move platform functions to CPU --- kernel/CMakeLists.txt | 1 - kernel/src/test_support/kapi/cpu.cpp | 7 +++++++ kernel/src/test_support/kapi/platform.cpp | 14 -------------- 3 files changed, 7 insertions(+), 15 deletions(-) delete mode 100644 kernel/src/test_support/kapi/platform.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 61bc6f1..ca8fae7 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -104,7 +104,6 @@ else() "src/test_support/kapi/cio.cpp" "src/test_support/kapi/interrupts.cpp" "src/test_support/kapi/memory.cpp" - "src/test_support/kapi/platform.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" "src/test_support/page_mapper.cpp" diff --git a/kernel/src/test_support/kapi/cpu.cpp b/kernel/src/test_support/kapi/cpu.cpp index 96dd10a..a89bec8 100644 --- a/kernel/src/test_support/kapi/cpu.cpp +++ b/kernel/src/test_support/kapi/cpu.cpp @@ -1,5 +1,6 @@ #include "kernel/test_support/cpu.hpp" +#include "kapi/devices.hpp" #include #include @@ -28,6 +29,12 @@ namespace kapi::cpu throw kernel::tests::cpu::halt{}; } + auto discover_topology(devices::bus &) -> bool + { + // TODO: implement more meaningful simulated CPU topology discovery + return true; + } + } // namespace kapi::cpu namespace kernel::tests::cpu diff --git a/kernel/src/test_support/kapi/platform.cpp b/kernel/src/test_support/kapi/platform.cpp deleted file mode 100644 index 497c992..0000000 --- a/kernel/src/test_support/kapi/platform.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "kapi/platform.hpp" - -#include "kapi/devices.hpp" - -namespace kapi::platform -{ - - auto discover_cpu_topology(devices::bus &) -> bool - { - // TODO: implement more meaningful simulated CPU topology discovery - return true; - } - -} // namespace kapi::platform \ No newline at end of file -- cgit v1.2.3 From dd8dfa3e674d05927e9ed4b7efcb634a634bfdcc Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 10 Apr 2026 10:30:32 +0200 Subject: kapi: move CPU to kapi --- arch/x86_64/include/arch/cpu/initialization.hpp | 5 ++- arch/x86_64/kapi/cpu.cpp | 26 ++++++++------ kapi/include/kapi/cpu.hpp | 16 +-------- kapi/include/kapi/devices.hpp | 1 + kapi/include/kapi/devices/cpu.hpp | 37 ++++++++++++++++++++ kernel/CMakeLists.txt | 2 +- kernel/include/kernel/devices/cpu.hpp | 33 ------------------ kernel/kapi/cpu.cpp | 19 ----------- kernel/kapi/devices.cpp | 7 ++-- kernel/kapi/devices/cpu.cpp | 31 +++++++++++++++++ kernel/src/devices/cpu.cpp | 45 ------------------------- kernel/src/test_support/kapi/cpu.cpp | 3 +- 12 files changed, 93 insertions(+), 132 deletions(-) create mode 100644 kapi/include/kapi/devices/cpu.hpp delete mode 100644 kernel/include/kernel/devices/cpu.hpp create mode 100644 kernel/kapi/devices/cpu.cpp delete mode 100644 kernel/src/devices/cpu.cpp diff --git a/arch/x86_64/include/arch/cpu/initialization.hpp b/arch/x86_64/include/arch/cpu/initialization.hpp index 71186d4..564c544 100644 --- a/arch/x86_64/include/arch/cpu/initialization.hpp +++ b/arch/x86_64/include/arch/cpu/initialization.hpp @@ -1,4 +1,5 @@ -#pragma once +#ifndef TEACHOS_ARCH_X86_64_CPU_INITIALIZATION_HPP +#define TEACHOS_ARCH_X86_64_CPU_INITIALIZATION_HPP namespace arch::cpu { @@ -7,3 +8,5 @@ namespace arch::cpu auto initialize_legacy_interrupts() -> void; } // namespace arch::cpu + +#endif diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index 1382e08..726ec6a 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -2,6 +2,7 @@ #include "kapi/acpi.hpp" #include "kapi/devices.hpp" +#include "kapi/devices/cpu.hpp" #include "kapi/system.hpp" #include "arch/cpu/initialization.hpp" @@ -42,8 +43,9 @@ namespace kapi::cpu __builtin_unreachable(); } - auto discover_topology(kapi::devices::bus & bus) -> bool + auto discover_topology() -> bool { + auto static const cpu_major = kapi::devices::allocate_major_number(); auto static const core_major = kapi::devices::allocate_major_number(); auto static const interrupt_controller_major = kapi::devices::allocate_major_number(); @@ -54,10 +56,6 @@ namespace kapi::cpu return false; } - auto bsp_found = false; - auto core_count = 0uz; - auto local_apic_address = madt->local_interrupt_controller_address(); - auto lapic_entries = *madt | std::views::filter([](auto const & entry) { return entry.type() == acpi::multiple_apic_description_table_entry::types::processor_local_apic; }) | std::views::transform([](auto const & entry) { @@ -66,18 +64,24 @@ namespace kapi::cpu return static_cast(entry.active_flags() & candidate_flags); }); + auto bsp_found = false; + auto core_count = 0uz; + auto local_apic_address = madt->local_interrupt_controller_address(); + auto cpu_bus = kstd::make_unique(cpu_major, 0); + for (auto const & apic : lapic_entries) { auto is_bsp = !bsp_found; bsp_found = true; - auto instance = kstd::make_unique(interrupt_controller_major, core_count, - apic.apic_id(), local_apic_address, is_bsp); - if (core_detected(bus, core_major, core_count, apic.processor_id(), is_bsp, std::move(instance))) - { - ++core_count; - } + auto core = kstd::make_unique(core_major, core_count, apic.processor_id(), is_bsp); + core->add_child(kstd::make_unique(interrupt_controller_major, core_count, + apic.apic_id(), local_apic_address, is_bsp)); + cpu_bus->add_child(std::move(core)); + ++core_count; } + devices::get_root_bus().add_child(std::move(cpu_bus)); + kstd::println("[x86_64:PLT] Found {} CPU cores", core_count); return core_count > 0; } diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index c643cd7..e736be1 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -1,13 +1,11 @@ #ifndef TEACHOS_KAPI_CPU_HPP #define TEACHOS_KAPI_CPU_HPP -#include "kapi/devices.hpp" #include "kapi/memory.hpp" #include #include -#include #include namespace kapi::cpu @@ -75,17 +73,6 @@ namespace kapi::cpu //! @return Whether the exception was handled. [[nodiscard]] auto dispatch(exception const & context) -> bool; - //! A hook that is called when the platform detects a CPU core during topology discovery. - //! - //! @param bus The bus the CPU was detected on. - //! @param major The major number of the CPU device. - //! @param minor The minor number of the CPU device. - //! @param hardware_id The hardware specific ID of the CPU core. - //! @param is_bsp Whether the reported CPU is the the bootstrap processor. - //! @param core_interrupt_controller The local interrupt controller of this CPU core. - auto core_detected(kapi::devices::bus & bus, std::size_t major, std::size_t minor, std::uint64_t hardware_id, - bool is_bsp, kstd::unique_ptr core_interrupt_controller) -> bool; - //! @} //! @addtogroup kapi-cpu-platform-defined @@ -104,9 +91,8 @@ namespace kapi::cpu //! Discover the CPU topology of the platform and attach it to the given CPU bus. //! - //! @param bus The bus to attach the CPU topology to. //! @return true iff. the CPU topology was discovered successfully, false otherwise. - auto discover_topology(kapi::devices::bus & bus) -> bool; + auto discover_topology() -> bool; //! @} diff --git a/kapi/include/kapi/devices.hpp b/kapi/include/kapi/devices.hpp index c26efb9..ec154a5 100644 --- a/kapi/include/kapi/devices.hpp +++ b/kapi/include/kapi/devices.hpp @@ -2,6 +2,7 @@ #define TEACHOS_KAPI_DEVICES_HPP #include "kapi/devices/bus.hpp" // IWYU pragma: export +#include "kapi/devices/cpu.hpp" // IWYU pragma: export #include "kapi/devices/device.hpp" // IWYU pragma: export #include "kapi/devices/manager.hpp" // IWYU pragma: export diff --git a/kapi/include/kapi/devices/cpu.hpp b/kapi/include/kapi/devices/cpu.hpp new file mode 100644 index 0000000..00766b5 --- /dev/null +++ b/kapi/include/kapi/devices/cpu.hpp @@ -0,0 +1,37 @@ +#ifndef TEACHOS_KAPI_DEVICES_CPU_HPP +#define TEACHOS_KAPI_DEVICES_CPU_HPP + +#include "kapi/devices/bus.hpp" + +#include +#include + +namespace kapi::devices +{ + + //! A virtual CPU bus to host all CPUs in the system. + struct cpu final : kapi::devices::bus + { + //! A virtual CPU Core to host all core local devices. + struct core final : kapi::devices::bus + { + explicit core(std::size_t major_number, std::size_t minor_number, std::uint64_t hardware_id, bool is_bsp); + + [[nodiscard]] auto hardware_id() const -> std::uint64_t; + [[nodiscard]] auto is_bsp() const -> bool; + + private: + std::uint64_t m_hardware_id; + bool m_is_bsp; + }; + + //! Create a new CPU with the given major and minor numbers. + //! + //! @param major The major number of this CPU + //! @param minor The minor number of this CPU, identifying the physical CPU + cpu(std::size_t major, std::size_t minor); + }; + +} // namespace kapi::devices + +#endif \ No newline at end of file diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index ca8fae7..b9e01eb 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -9,6 +9,7 @@ add_library("kernel_objs" OBJECT "kapi/cpu.cpp" "kapi/devices.cpp" "kapi/devices/bus.cpp" + "kapi/devices/cpu.cpp" "kapi/devices/device.cpp" "kapi/interrupts.cpp" "kapi/memory.cpp" @@ -26,7 +27,6 @@ add_library("kernel_objs" OBJECT "src/memory.cpp" "src/devices/block_device.cpp" "src/devices/block_device_utils.cpp" - "src/devices/cpu.cpp" "src/devices/root_bus.cpp" "src/devices/storage/controller.cpp" "src/devices/storage/management.cpp" diff --git a/kernel/include/kernel/devices/cpu.hpp b/kernel/include/kernel/devices/cpu.hpp deleted file mode 100644 index b056665..0000000 --- a/kernel/include/kernel/devices/cpu.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef TEACHOS_KERNEL_DEVICES_CPU_HPP -#define TEACHOS_KERNEL_DEVICES_CPU_HPP - -#include "kapi/devices.hpp" - -#include -#include - -namespace kernel::devices -{ - - struct cpu final : kapi::devices::bus - { - struct core final : kapi::devices::bus - { - explicit core(std::size_t major_number, std::size_t minor_number, std::uint64_t hardware_id, bool is_bsp); - - [[nodiscard]] auto hardware_id() const -> std::uint64_t; - [[nodiscard]] auto is_bsp() const -> bool; - - private: - std::uint64_t m_hardware_id; - bool m_is_bsp; - }; - - explicit cpu(std::size_t major_number); - - auto probe() -> bool final; - }; - -} // namespace kernel::devices - -#endif \ No newline at end of file diff --git a/kernel/kapi/cpu.cpp b/kernel/kapi/cpu.cpp index d632628..13de584 100644 --- a/kernel/kapi/cpu.cpp +++ b/kernel/kapi/cpu.cpp @@ -1,17 +1,9 @@ #include "kapi/cpu.hpp" -#include "kapi/devices.hpp" #include "kapi/system.hpp" -#include "kernel/devices/cpu.hpp" - -#include #include -#include -#include -#include - namespace kapi::cpu { @@ -40,15 +32,4 @@ namespace kapi::cpu } } - auto core_detected(kapi::devices::bus & bus, std::size_t major, std::size_t minor, std::uint64_t hardware_id, - bool is_bsp, kstd::unique_ptr core_interrupt_controller) -> bool - { - auto core = kstd::make_unique(major, minor, hardware_id, is_bsp); - - core->add_child(std::move(core_interrupt_controller)); - bus.add_child(std::move(core)); - - return true; - } - } // namespace kapi::cpu \ No newline at end of file diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index dad1fe4..b8aa44b 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -1,8 +1,8 @@ #include "kapi/devices.hpp" +#include "kapi/cpu.hpp" #include "kapi/system.hpp" -#include "kernel/devices/cpu.hpp" #include "kernel/devices/root_bus.hpp" #include @@ -17,7 +17,6 @@ namespace kapi::devices { - namespace { auto constinit next_major_number = std::atomic_size_t{1}; @@ -37,9 +36,7 @@ namespace kapi::devices register_device(bus); bus.init(); - auto cpu_major = allocate_major_number(); - auto cpu = kstd::make_unique(cpu_major); - bus.add_child(std::move(cpu)); + kapi::cpu::discover_topology(); } auto get_root_bus() -> bus & diff --git a/kernel/kapi/devices/cpu.cpp b/kernel/kapi/devices/cpu.cpp new file mode 100644 index 0000000..9de5f94 --- /dev/null +++ b/kernel/kapi/devices/cpu.cpp @@ -0,0 +1,31 @@ +#include "kapi/devices/cpu.hpp" + +#include "kapi/devices.hpp" + +#include +#include + +namespace kapi::devices +{ + + cpu::core::core(std::size_t major_number, std::size_t minor_number, std::uint64_t hardware_id, bool is_bsp) + : kapi::devices::bus{major_number, minor_number, "cpu_core"} + , m_hardware_id{hardware_id} + , m_is_bsp{is_bsp} + {} + + auto cpu::core::hardware_id() const -> std::uint64_t + { + return m_hardware_id; + } + + auto cpu::core::is_bsp() const -> bool + { + return m_is_bsp; + } + + cpu::cpu(std::size_t major, std::size_t minor) + : kapi::devices::bus{major, minor, "cpu"} + {} + +} // namespace kapi::devices \ No newline at end of file diff --git a/kernel/src/devices/cpu.cpp b/kernel/src/devices/cpu.cpp deleted file mode 100644 index 85f4d47..0000000 --- a/kernel/src/devices/cpu.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "kernel/devices/cpu.hpp" - -#include "kapi/cpu.hpp" -#include "kapi/devices.hpp" - -#include - -#include -#include - -namespace kernel::devices -{ - - cpu::core::core(std::size_t major_number, std::size_t minor_number, std::uint64_t hardware_id, bool is_bsp) - : kapi::devices::bus{major_number, minor_number, "cpu_core"} - , m_hardware_id{hardware_id} - , m_is_bsp{is_bsp} - {} - - auto cpu::core::hardware_id() const -> std::uint64_t - { - return m_hardware_id; - } - - auto cpu::core::is_bsp() const -> bool - { - return m_is_bsp; - } - - cpu::cpu(std::size_t major_number) - : kapi::devices::bus{major_number, 0, "cpu"} - {} - - auto cpu::probe() -> bool - { - if (!kapi::cpu::discover_topology(*this)) - { - kstd::println("[OS:DEV] Failed to discover CPU topology"); - return false; - } - - return true; - } - -} // namespace kernel::devices \ 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 a89bec8..671097e 100644 --- a/kernel/src/test_support/kapi/cpu.cpp +++ b/kernel/src/test_support/kapi/cpu.cpp @@ -1,6 +1,5 @@ #include "kernel/test_support/cpu.hpp" -#include "kapi/devices.hpp" #include #include @@ -29,7 +28,7 @@ namespace kapi::cpu throw kernel::tests::cpu::halt{}; } - auto discover_topology(devices::bus &) -> bool + auto discover_topology() -> bool { // TODO: implement more meaningful simulated CPU topology discovery return true; -- cgit v1.2.3 From 5a6b6ab376e67b173ef36f831445ccba7e86e038 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 10 Apr 2026 10:38:51 +0200 Subject: kapi/devices: add parent back-pointer to device --- kapi/include/kapi/devices/device.hpp | 6 ++++++ kernel/kapi/devices/bus.cpp | 1 + kernel/kapi/devices/device.cpp | 8 ++++++++ 3 files changed, 15 insertions(+) diff --git a/kapi/include/kapi/devices/device.hpp b/kapi/include/kapi/devices/device.hpp index b3647da..9939494 100644 --- a/kapi/include/kapi/devices/device.hpp +++ b/kapi/include/kapi/devices/device.hpp @@ -3,6 +3,7 @@ // IWYU pragma: private, include "kapi/devices.hpp" +#include #include #include @@ -62,9 +63,14 @@ namespace kapi::devices [[nodiscard]] virtual auto is_block_device() const -> bool; private: + friend struct bus; + + auto set_parent(kstd::observer_ptr parent) -> void; + size_t m_major; size_t m_minor; kstd::string m_name; + kstd::observer_ptr m_parent; }; //! @} diff --git a/kernel/kapi/devices/bus.cpp b/kernel/kapi/devices/bus.cpp index 66b84d2..5f0dfcd 100644 --- a/kernel/kapi/devices/bus.cpp +++ b/kernel/kapi/devices/bus.cpp @@ -45,6 +45,7 @@ namespace kapi::devices auto bus::add_child(kstd::unique_ptr child) -> void { auto observer = m_observers.emplace_back(child.get()); + child->set_parent(kstd::make_observer(this)); m_devices.push_back(std::move(child)); kapi::devices::register_device(*observer); diff --git a/kernel/kapi/devices/device.cpp b/kernel/kapi/devices/device.cpp index 9f7a404..41d96dc 100644 --- a/kernel/kapi/devices/device.cpp +++ b/kernel/kapi/devices/device.cpp @@ -1,5 +1,8 @@ #include "kapi/devices/device.hpp" +#include "kapi/devices/bus.hpp" + +#include #include #include @@ -32,4 +35,9 @@ namespace kapi::devices return false; } + auto device::set_parent(kstd::observer_ptr parent) -> void + { + m_parent = parent; + } + } // namespace kapi::devices \ No newline at end of file -- cgit v1.2.3 From c3f7b747f02a79b34ed914c54ce74be973b17af1 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 10 Apr 2026 17:39:14 +0200 Subject: kapi: extract ACPI functionality to libs --- arch/x86_64/kapi/acpi.cpp | 10 +- arch/x86_64/kapi/cpu.cpp | 15 +-- kapi/CMakeLists.txt | 1 + kapi/include/kapi/acpi.hpp | 25 ++--- .../kapi/acpi/multiple_apic_description_table.hpp | 104 ------------------ kapi/include/kapi/acpi/pointers.hpp | 79 -------------- .../kapi/acpi/system_description_table_header.hpp | 76 ------------- kapi/include/kapi/acpi/table_iterator.hpp | 64 ----------- kapi/include/kapi/acpi/table_type.hpp | 22 ---- kernel/CMakeLists.txt | 3 - kernel/include/kernel/acpi/manager.hpp | 14 +-- kernel/kapi/acpi.cpp | 18 +--- .../kapi/acpi/multiple_apic_description_table.cpp | 70 ------------ kernel/kapi/acpi/pointers.cpp | 55 ---------- .../kapi/acpi/system_description_table_header.cpp | 51 --------- kernel/src/acpi/manager.cpp | 29 +++-- libs/CMakeLists.txt | 3 +- libs/acpi/CMakeLists.txt | 29 +++++ libs/acpi/acpi/acpi.hpp | 10 ++ libs/acpi/acpi/checksum.cpp | 19 ++++ libs/acpi/acpi/checksum.hpp | 20 ++++ libs/acpi/acpi/madt.cpp | 68 ++++++++++++ libs/acpi/acpi/madt.hpp | 92 ++++++++++++++++ libs/acpi/acpi/pointers.cpp | 55 ++++++++++ libs/acpi/acpi/pointers.hpp | 72 +++++++++++++ libs/acpi/acpi/sdt.cpp | 51 +++++++++ libs/acpi/acpi/sdt.hpp | 119 +++++++++++++++++++++ libs/acpi/acpi/table_type.hpp | 17 +++ 28 files changed, 602 insertions(+), 589 deletions(-) delete mode 100644 kapi/include/kapi/acpi/multiple_apic_description_table.hpp delete mode 100644 kapi/include/kapi/acpi/pointers.hpp delete mode 100644 kapi/include/kapi/acpi/system_description_table_header.hpp delete mode 100644 kapi/include/kapi/acpi/table_iterator.hpp delete mode 100644 kapi/include/kapi/acpi/table_type.hpp delete mode 100644 kernel/kapi/acpi/multiple_apic_description_table.cpp delete mode 100644 kernel/kapi/acpi/pointers.cpp delete mode 100644 kernel/kapi/acpi/system_description_table_header.cpp create mode 100644 libs/acpi/CMakeLists.txt create mode 100644 libs/acpi/acpi/acpi.hpp create mode 100644 libs/acpi/acpi/checksum.cpp create mode 100644 libs/acpi/acpi/checksum.hpp create mode 100644 libs/acpi/acpi/madt.cpp create mode 100644 libs/acpi/acpi/madt.hpp create mode 100644 libs/acpi/acpi/pointers.cpp create mode 100644 libs/acpi/acpi/pointers.hpp create mode 100644 libs/acpi/acpi/sdt.cpp create mode 100644 libs/acpi/acpi/sdt.hpp create mode 100644 libs/acpi/acpi/table_type.hpp diff --git a/arch/x86_64/kapi/acpi.cpp b/arch/x86_64/kapi/acpi.cpp index ec38aee..40b7160 100644 --- a/arch/x86_64/kapi/acpi.cpp +++ b/arch/x86_64/kapi/acpi.cpp @@ -4,24 +4,26 @@ #include +#include + namespace kapi::acpi { - auto get_root_pointer() -> kstd::observer_ptr + auto get_root_pointer() -> kstd::observer_ptr<::acpi::rsdp const> { auto const & mbi = kapi::boot::bootstrap_information.mbi; - auto system_description_pointer = static_cast(nullptr); + auto system_description_pointer = static_cast<::acpi::rsdp const *>(nullptr); if (auto const & xsdp = mbi->maybe_acpi_xsdp()) { auto data = xsdp->pointer().data(); - system_description_pointer = reinterpret_cast(data); + system_description_pointer = reinterpret_cast<::acpi::xsdp const *>(data); } else if (auto const & rsdp = mbi->maybe_acpi_rsdp()) { auto data = rsdp->pointer().data(); - system_description_pointer = reinterpret_cast(data); + system_description_pointer = reinterpret_cast<::acpi::rsdp const *>(data); } return kstd::make_observer(system_description_pointer); diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index 726ec6a..a836b20 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -3,6 +3,7 @@ #include "kapi/acpi.hpp" #include "kapi/devices.hpp" #include "kapi/devices/cpu.hpp" +#include "kapi/memory.hpp" #include "kapi/system.hpp" #include "arch/cpu/initialization.hpp" @@ -11,6 +12,8 @@ #include #include +#include + #include #include #include @@ -20,8 +23,8 @@ namespace kapi::cpu namespace { - constexpr auto candidate_flags = acpi::processor_local_apic::flags::processor_enabled // - | acpi::processor_local_apic::flags::online_capable; + constexpr auto candidate_flags = ::acpi::processor_local_apic::flags::processor_enabled // + | ::acpi::processor_local_apic::flags::online_capable; } auto init() -> void @@ -49,7 +52,7 @@ namespace kapi::cpu auto static const core_major = kapi::devices::allocate_major_number(); auto static const interrupt_controller_major = kapi::devices::allocate_major_number(); - auto madt = kapi::acpi::get_table(); + auto madt = kapi::acpi::get_table<::acpi::madt_table_signature>(); if (!madt) { kstd::println("[x86_64:PLT] Failed to find ACPI APIC table"); @@ -57,16 +60,16 @@ namespace kapi::cpu } auto lapic_entries = *madt | std::views::filter([](auto const & entry) { - return entry.type() == acpi::multiple_apic_description_table_entry::types::processor_local_apic; + return entry.type() == ::acpi::madt_entry::types::processor_local_apic; }) | std::views::transform([](auto const & entry) { - return static_cast(entry); + return static_cast<::acpi::processor_local_apic const &>(entry); }) | std::views::filter([](auto const & entry) { return static_cast(entry.active_flags() & candidate_flags); }); auto bsp_found = false; auto core_count = 0uz; - auto local_apic_address = madt->local_interrupt_controller_address(); + auto local_apic_address = memory::physical_address{madt->local_interrupt_controller_address()}; auto cpu_bus = kstd::make_unique(cpu_major, 0); for (auto const & apic : lapic_entries) diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index c9aa23f..99a8725 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -15,6 +15,7 @@ target_include_directories("kapi" INTERFACE ) target_link_libraries("kapi" INTERFACE + "libs::acpi" "libs::kstd" "gcc" diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index 5323fee..01fd113 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -1,16 +1,11 @@ #ifndef TEACHOS_KAPI_ACPI_HPP #define TEACHOS_KAPI_ACPI_HPP -#include "kapi/acpi/multiple_apic_description_table.hpp" // IWYU pragma: export -#include "kapi/acpi/pointers.hpp" // IWYU pragma: export -#include "kapi/acpi/system_description_table_header.hpp" // IWYU pragma: export -#include "kapi/acpi/table_type.hpp" - #include #include -#include -#include +#include + #include namespace kapi::acpi @@ -22,28 +17,22 @@ namespace kapi::acpi //! Initialize the ACPI subsystem and discover the available tables. //! //! @return true iff. a valid system description tabled was found, false otherwise. - auto init(root_system_description_pointer const & sdp) -> bool; - - //! Validate and ACPI entity checksum. - //! - //! @param data The data to validate the checksum of. - //! @return true iff. the checksum is valid, false otherwise. - auto validate_checksum(std::span data) -> bool; + auto init(::acpi::rsdp const & sdp) -> bool; //! Get a pointer to an ACPI table by its signature. //! //! @param signature The signature of the table to get. //! @return A pointer to the table if found, nullptr otherwise. - auto get_table(std::string_view signature) -> kstd::observer_ptr; + auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::sdt const>; //! Get a type-cast pointer to an ACPI table by its signature. //! //! @tparam Signature The signature of the table to get //! @return A pointer to the table if found, nullptr otherwise. template - auto get_table() -> kstd::observer_ptr const> + auto get_table() -> kstd::observer_ptr<::acpi::table_type_t const> { - return kstd::make_observer(static_cast const *>(get_table(Signature).get())); + return kstd::make_observer(static_cast<::acpi::table_type_t const *>(get_table(Signature).get())); } //! @} @@ -54,7 +43,7 @@ namespace kapi::acpi //! Retrieve the RSDP or XSDP for this system. //! //! @return a pointer to either the RSDP or XSDP data structure, or @p nullptr if neither is present. - auto get_root_pointer() -> kstd::observer_ptr; + auto get_root_pointer() -> kstd::observer_ptr<::acpi::rsdp const>; //! @} diff --git a/kapi/include/kapi/acpi/multiple_apic_description_table.hpp b/kapi/include/kapi/acpi/multiple_apic_description_table.hpp deleted file mode 100644 index 33acac9..0000000 --- a/kapi/include/kapi/acpi/multiple_apic_description_table.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef TEACHOS_KAPI_ACPI_MUTIPLE_APIC_DESCRIPTION_TABLE_HEADER_HPP -#define TEACHOS_KAPI_ACPI_MUTIPLE_APIC_DESCRIPTION_TABLE_HEADER_HPP - -// IWYU pragma: private, include "kapi/acpi.hpp" - -#include "kapi/acpi/system_description_table_header.hpp" -#include "kapi/acpi/table_iterator.hpp" -#include "kapi/acpi/table_type.hpp" -#include "kapi/memory.hpp" - -#include -#include - -#include -#include -#include - -namespace kapi::acpi -{ - - //! @addtogroup kapi-acpi-kernel-defined - //! @{ - - struct [[gnu::packed]] multiple_apic_description_table_entry - { - enum struct types : std::uint8_t - { - processor_local_apic, - io_apic, - io_apic_interrupt_source_override, - io_apic_nmi_source, - local_apic_nmi_interrupts, - local_apic_address_override, - processor_local_x2_apic, - }; - - [[nodiscard]] auto type() const noexcept -> types; - [[nodiscard]] auto length() const noexcept -> std::size_t; - - private: - std::uint8_t m_type; - std::uint8_t m_length; - }; - - //! The common header for all - struct [[gnu::packed]] multiple_apic_description_table : system_description_table_header - { - using iterator = table_iterator; - using const_iterator = iterator; - - [[nodiscard]] auto local_interrupt_controller_address() const noexcept -> memory::physical_address; - [[nodiscard]] auto flags() const noexcept -> std::uint32_t; - - [[nodiscard]] auto begin() const noexcept -> iterator; - [[nodiscard]] auto cbegin() const noexcept -> iterator; - [[nodiscard]] auto end() const noexcept -> iterator; - [[nodiscard]] auto cend() const noexcept -> iterator; - - private: - std::uint32_t m_local_interrupt_controller_address; - std::uint32_t m_flags; - }; - - struct [[gnu::packed]] processor_local_apic : multiple_apic_description_table_entry - { - enum struct flags : std::uint32_t - { - processor_enabled = 1, - online_capable = 2, - }; - - [[nodiscard]] auto apic_id() const noexcept -> std::uint8_t; - [[nodiscard]] auto active_flags() const noexcept -> flags; - [[nodiscard]] auto processor_id() const noexcept -> std::uint32_t; - - private: - std::uint8_t m_processor_id; - std::uint8_t m_apic_id; - std::uint32_t m_flags; - }; - - //! @} - - //! @addtogroup kapi-acpi - //! @{ - - constexpr char const madt_table_signature[] = "APIC"; // NOLINT - - template<> - struct table_type - { - using type = multiple_apic_description_table; - }; - - //! @} - -} // namespace kapi::acpi - -template<> -struct kstd::ext::is_bitfield_enum : std::true_type -{ -}; - -#endif diff --git a/kapi/include/kapi/acpi/pointers.hpp b/kapi/include/kapi/acpi/pointers.hpp deleted file mode 100644 index 2b88720..0000000 --- a/kapi/include/kapi/acpi/pointers.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef TEACHOS_KAPI_ACPI_POINTERS_HPP -#define TEACHOS_KAPI_ACPI_POINTERS_HPP - -// IWYU pragma: private, include "kapi/acpi.hpp" - -#include "kapi/memory.hpp" - -#include - -#include -#include -#include -#include - -namespace kapi::acpi -{ - - //! @addtogroup kapi-acpi-kernel-defined - //! @{ - - //! A pointer to the Root System Description Table. - struct [[gnu::packed]] root_system_description_pointer - { - //! Get the ID of the OEM, usually a vendor name. - [[nodiscard]] auto oem_id() const noexcept -> std::string_view; - - //! Get the revision of the ACPI Root System Description Table. - //! - //! @note Revisions greater or equal to two indicate that the system uses the Extended System Description Table, and - //! as such that table should be use to query information. - [[nodiscard]] auto revision() const noexcept -> std::uint8_t; - - //! Get the signature of this pointer. - //! - //! A valid RSDP must always carry the signature "RSD PTR ". - [[nodiscard]] auto signature() const noexcept -> std::string_view; - - //! Get the physical address of the pointed-to Root System Description Table. - [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; - - //! Validate the checksum of this RSDP. - //! - //! @return @p true iff. the checksum of this RSDP is valid, @p false otherwise. - [[nodiscard]] auto validate() const noexcept -> bool; - - private: - std::array m_signature; - [[maybe_unused]] std::uint8_t m_checksum; - std::array m_oem_id; - std::uint8_t m_revision; - std::array m_rsdt_address; - }; - - //! A pointer to the Extended System Description Table. - struct [[gnu::packed]] extended_system_description_pointer : root_system_description_pointer - { - //! Get the length of the data contained in this pointer. - [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; - - //! Get the address of the pointed-to Extended System Description Table. - [[nodiscard]] auto table_address() const noexcept -> memory::physical_address; - - //! Validate the checksum of this XSDP. - //! - //! @return @p true iff. the checksum of this RSDP is valid, @p false otherwise. - [[nodiscard]] auto validate() const noexcept -> bool; - - private: - std::uint32_t m_length; - std::array m_xsdt_address; - [[maybe_unused]] std::uint8_t m_extended_checksum; - [[maybe_unused]] std::array m_reserved; - }; - - //! @} - -} // namespace kapi::acpi - -#endif diff --git a/kapi/include/kapi/acpi/system_description_table_header.hpp b/kapi/include/kapi/acpi/system_description_table_header.hpp deleted file mode 100644 index b336ea1..0000000 --- a/kapi/include/kapi/acpi/system_description_table_header.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef TEACHOS_KAPI_ACPI_SYSTEM_DESCRIPTION_TABLE_HEADER_HPP -#define TEACHOS_KAPI_ACPI_SYSTEM_DESCRIPTION_TABLE_HEADER_HPP - -// IWYU pragma: private, include "kapi/acpi.hpp" - -#include "kapi/acpi/table_type.hpp" - -#include - -#include -#include -#include - -namespace kapi::acpi -{ - - //! @addtogroup kapi-acpi-kernel-defined - //! @{ - - //! The common header of all System Description Tables. - struct [[gnu::packed]] system_description_table_header - { - //! Get the revision of the utility used to create this table. - [[nodiscard]] auto creator_revision() const noexcept -> std::uint32_t; - - //! Get the vendor ID of the utility used to create this table. - [[nodiscard]] auto creator_id() const noexcept -> std::uint32_t; - - //! Get the length of the entire table, including this header. - [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; - - //! Get the ID of the OEM. - [[nodiscard]] auto oem_id() const noexcept -> std::string_view; - - //! Get the OEMs revision number of this table. - [[nodiscard]] auto oem_revision() const noexcept -> std::uint32_t; - - //! Get the OEMs ID of this table. - [[nodiscard]] auto oem_table_id() const noexcept -> std::string_view; - - //! Get the revision number of the structure of this table. - [[nodiscard]] auto revision() const noexcept -> std::uint8_t; - - //! Get the signature of this table. - [[nodiscard]] auto signature() const noexcept -> std::string_view; - - private: - std::array m_signature; - std::uint32_t m_length; - std::uint8_t m_revision; - [[maybe_unused]] std::uint8_t m_checksum; - std::array m_oem_id; - std::array m_oem_table_id; - std::uint32_t m_oem_revision; - std::uint32_t m_creator_id; - std::uint32_t m_creator_revision; - }; - - //! @} - // - //! @addtogroup kapi-acpi - //! @{ - - constexpr char const rsdt_table_signature[] = "RSDT"; // NOLINT - - template<> - struct table_type - { - using type = system_description_table_header; - }; - - //! @} - -} // namespace kapi::acpi - -#endif diff --git a/kapi/include/kapi/acpi/table_iterator.hpp b/kapi/include/kapi/acpi/table_iterator.hpp deleted file mode 100644 index 998d6d6..0000000 --- a/kapi/include/kapi/acpi/table_iterator.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef TEACHOS_KAPI_ACPI_TABLE_ITERATOR_HPP -#define TEACHOS_KAPI_ACPI_TABLE_ITERATOR_HPP - -#include -#include -#include - -namespace kapi::acpi -{ - - template - struct table_iterator - { - using iterator_category = std::forward_iterator_tag; - using value_type = EntryType; - using pointer = value_type *; - using reference = value_type &; - using difference_type = std::ptrdiff_t; - - constexpr table_iterator() noexcept = default; - - constexpr table_iterator(pointer entry, pointer limit) noexcept - : m_entry{entry} - , m_limit{limit} - {} - - constexpr auto operator++() noexcept -> table_iterator & - { - auto decayed = std::bit_cast(m_entry); - decayed += m_entry->length(); - m_entry = std::bit_cast(decayed); - return *this; - } - - constexpr auto operator++(int) noexcept -> table_iterator - { - auto copy = *this; - ++*this; - return copy; - } - - constexpr auto operator*() const noexcept -> reference - { - return *m_entry; - } - - constexpr auto operator==(table_iterator const & other) const noexcept -> bool - { - return m_entry == other.m_entry || (is_end() && other.is_end()); - } - - private: - [[nodiscard]] constexpr auto is_end() const noexcept -> bool - { - return m_entry == m_limit; - } - - pointer m_entry{}; - pointer m_limit{}; - }; - -} // namespace kapi::acpi - -#endif diff --git a/kapi/include/kapi/acpi/table_type.hpp b/kapi/include/kapi/acpi/table_type.hpp deleted file mode 100644 index e088a24..0000000 --- a/kapi/include/kapi/acpi/table_type.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef TEACHOS_KAPI_ACPI_TABLE_TYPE_HPP -#define TEACHOS_KAPI_ACPI_TABLE_TYPE_HPP - -// IWYU pragma: private - -namespace kapi::acpi -{ - - //! @addtogroup kapi-acpi-kernel-defined - //! @{ - - template - struct table_type; - - template - using table_type_t = typename table_type::type; - - //! @} - -} // namespace kapi::acpi - -#endif diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index b9e01eb..74233cb 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -1,9 +1,6 @@ add_library("kernel_objs" OBJECT # Platform-independent KAPI implementation "kapi/acpi.cpp" - "kapi/acpi/multiple_apic_description_table.cpp" - "kapi/acpi/pointers.cpp" - "kapi/acpi/system_description_table_header.cpp" "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" diff --git a/kernel/include/kernel/acpi/manager.hpp b/kernel/include/kernel/acpi/manager.hpp index fae59a6..420b44a 100644 --- a/kernel/include/kernel/acpi/manager.hpp +++ b/kernel/include/kernel/acpi/manager.hpp @@ -1,12 +1,12 @@ #ifndef TEACHOS_KERNEL_ACPI_MANAGER_HPP #define TEACHOS_KERNEL_ACPI_MANAGER_HPP -#include "kapi/acpi.hpp" - #include #include #include +#include + #include namespace kernel::acpi @@ -14,16 +14,16 @@ namespace kernel::acpi struct manager { - explicit manager(kapi::acpi::root_system_description_pointer const & sdp); + explicit manager(::acpi::rsdp const & sdp); auto load_tables() -> bool; - auto get_table(std::string_view signature) -> kstd::observer_ptr; + auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::sdt const>; private: - kapi::acpi::root_system_description_pointer const * m_sdp{}; - kapi::acpi::system_description_table_header const * m_rsdt{}; - kstd::flat_map m_tables{}; + ::acpi::rsdp const * m_sdp{}; + ::acpi::sdt const * m_rsdt{}; + kstd::flat_map m_tables{}; bool m_extended{}; }; diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp index e7c4921..df2bf05 100644 --- a/kernel/kapi/acpi.cpp +++ b/kernel/kapi/acpi.cpp @@ -6,12 +6,10 @@ #include -#include +#include + #include -#include -#include #include -#include #include namespace kapi::acpi @@ -22,7 +20,7 @@ namespace kapi::acpi auto constinit manager = std::optional{}; } // namespace - auto init(root_system_description_pointer const & sdp) -> bool + auto init(::acpi::rsdp const & sdp) -> bool { auto static constinit initialized = std::atomic_flag{}; if (initialized.test_and_set()) @@ -34,15 +32,7 @@ namespace kapi::acpi return manager->load_tables(); } - auto validate_checksum(std::span data) -> bool - { - auto sum = std::ranges::fold_left(data, std::uint8_t{}, [](auto acc, auto byte) { - return static_cast(acc + static_cast(byte)); - }); - return sum == 0; - } - - auto get_table(std::string_view signature) -> kstd::observer_ptr + auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::sdt const> { return manager->get_table(signature); } diff --git a/kernel/kapi/acpi/multiple_apic_description_table.cpp b/kernel/kapi/acpi/multiple_apic_description_table.cpp deleted file mode 100644 index c0360a3..0000000 --- a/kernel/kapi/acpi/multiple_apic_description_table.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "kapi/acpi.hpp" -#include "kapi/memory.hpp" - -#include -#include - -namespace kapi::acpi -{ - - auto multiple_apic_description_table::local_interrupt_controller_address() const noexcept -> memory::physical_address - { - return memory::physical_address{static_cast(m_local_interrupt_controller_address)}; - } - - auto multiple_apic_description_table::flags() const noexcept -> std::uint32_t - { - return m_flags; - } - - auto multiple_apic_description_table_entry::type() const noexcept -> types - { - return static_cast(m_type); - } - - auto multiple_apic_description_table_entry::length() const noexcept -> std::size_t - { - return m_length; - } - - auto processor_local_apic::apic_id() const noexcept -> std::uint8_t - { - return m_apic_id; - } - - auto processor_local_apic::active_flags() const noexcept -> flags - { - return static_cast(m_flags); - } - - auto processor_local_apic::processor_id() const noexcept -> std::uint32_t - { - return m_processor_id; - } - - auto multiple_apic_description_table::begin() const noexcept -> iterator - { - auto base = reinterpret_cast(this); - base += sizeof(multiple_apic_description_table); - auto limit = reinterpret_cast(this); - limit += length().value; - return iterator{reinterpret_cast(base), - reinterpret_cast(limit)}; - } - - auto multiple_apic_description_table::cbegin() const noexcept -> iterator - { - return begin(); - } - - auto multiple_apic_description_table::end() const noexcept -> iterator - { - return {}; - } - - auto multiple_apic_description_table::cend() const noexcept -> iterator - { - return end(); - } - -} // namespace kapi::acpi diff --git a/kernel/kapi/acpi/pointers.cpp b/kernel/kapi/acpi/pointers.cpp deleted file mode 100644 index 63831e9..0000000 --- a/kernel/kapi/acpi/pointers.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "kapi/acpi.hpp" -#include "kapi/memory.hpp" - -#include - -#include -#include -#include -#include - -namespace kapi::acpi -{ - - auto root_system_description_pointer::oem_id() const noexcept -> std::string_view - { - return {m_oem_id.data(), m_oem_id.size()}; - } - - auto root_system_description_pointer::revision() const noexcept -> std::uint8_t - { - return m_revision; - } - - auto root_system_description_pointer::signature() const noexcept -> std::string_view - { - return {m_signature.data(), m_signature.size()}; - } - - auto root_system_description_pointer::table_address() const noexcept -> memory::physical_address - { - auto raw = std::bit_cast(m_rsdt_address); - return memory::physical_address{static_cast(raw)}; - } - - auto root_system_description_pointer::validate() const noexcept -> bool - { - return validate_checksum({reinterpret_cast(this), sizeof(root_system_description_pointer)}); - } - - auto extended_system_description_pointer::length() const noexcept -> kstd::units::bytes - { - return kstd::units::bytes{m_length}; - } - - auto extended_system_description_pointer::table_address() const noexcept -> memory::physical_address - { - return memory::physical_address{std::bit_cast(m_xsdt_address)}; - } - - auto extended_system_description_pointer::validate() const noexcept -> bool - { - return validate_checksum({reinterpret_cast(this), m_length}); - } - -} // namespace kapi::acpi diff --git a/kernel/kapi/acpi/system_description_table_header.cpp b/kernel/kapi/acpi/system_description_table_header.cpp deleted file mode 100644 index f688b4d..0000000 --- a/kernel/kapi/acpi/system_description_table_header.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "kapi/acpi.hpp" - -#include - -#include -#include - -namespace kapi::acpi -{ - - [[nodiscard]] auto system_description_table_header::creator_revision() const noexcept -> std::uint32_t - { - return m_creator_revision; - } - - [[nodiscard]] auto system_description_table_header::creator_id() const noexcept -> std::uint32_t - { - return m_creator_id; - } - - [[nodiscard]] auto system_description_table_header::length() const noexcept -> kstd::units::bytes - { - return kstd::units::bytes{m_length}; - } - - [[nodiscard]] auto system_description_table_header::oem_id() const noexcept -> std::string_view - { - return {m_oem_id.data(), m_oem_id.size()}; - } - - [[nodiscard]] auto system_description_table_header::oem_revision() const noexcept -> std::uint32_t - { - return m_oem_revision; - } - - [[nodiscard]] auto system_description_table_header::oem_table_id() const noexcept -> std::string_view - { - return {m_oem_table_id.data(), m_oem_table_id.size()}; - } - - [[nodiscard]] auto system_description_table_header::revision() const noexcept -> std::uint8_t - { - return m_revision; - } - - [[nodiscard]] auto system_description_table_header::signature() const noexcept -> std::string_view - { - return {m_signature.data(), m_signature.size()}; - } - -} // namespace kapi::acpi diff --git a/kernel/src/acpi/manager.cpp b/kernel/src/acpi/manager.cpp index 300b85e..501ce92 100644 --- a/kernel/src/acpi/manager.cpp +++ b/kernel/src/acpi/manager.cpp @@ -1,12 +1,13 @@ #include "kernel/acpi/manager.hpp" -#include "kapi/acpi.hpp" #include "kapi/memory.hpp" #include "kapi/system.hpp" #include #include +#include + #include #include #include @@ -14,7 +15,7 @@ namespace kernel::acpi { - manager::manager(kapi::acpi::root_system_description_pointer const & sdp) + manager::manager(::acpi::rsdp const & sdp) : m_sdp{&sdp} { if (m_sdp->signature() != "RSD PTR ") @@ -24,14 +25,14 @@ namespace kernel::acpi if (m_sdp->revision() >= 2) { - auto const xsdp = static_cast(m_sdp); + auto const xsdp = static_cast<::acpi::xsdp const *>(m_sdp); if (!xsdp->validate()) { kapi::system::panic("[OS:ACPI] Invalid XSDP signature!"); } - auto physical_extended_table_address = xsdp->table_address(); + auto physical_extended_table_address = kapi::memory::physical_address{xsdp->table_address()}; auto linear_extended_table_address = kapi::memory::hhdm_to_linear(physical_extended_table_address); - m_rsdt = static_cast(linear_extended_table_address); + m_rsdt = static_cast<::acpi::sdt const *>(linear_extended_table_address); m_extended = true; } else @@ -40,23 +41,22 @@ namespace kernel::acpi { kapi::system::panic("[OS:ACPI] Invalid RSDP checksum!"); } - auto physical_root_table_address = m_sdp->table_address(); + auto physical_root_table_address = kapi::memory::physical_address{m_sdp->table_address()}; auto linear_root_table_address = kapi::memory::hhdm_to_linear(physical_root_table_address); - m_rsdt = static_cast(linear_root_table_address); + m_rsdt = static_cast<::acpi::sdt const *>(linear_root_table_address); } } auto manager::load_tables() -> bool { - if (!kapi::acpi::validate_checksum({reinterpret_cast(m_rsdt), m_rsdt->length().value})) + if (!::acpi::validate_checksum({reinterpret_cast(m_rsdt), m_rsdt->length().value})) { kapi::system::panic("[OS:ACPI] Invalid RSDT checksum!"); } auto entry_size = m_extended ? sizeof(std::uint64_t) : sizeof(std::uint32_t); - auto entry_count = (m_rsdt->length().value - sizeof(kapi::acpi::system_description_table_header)) / entry_size; - auto entries_base = - reinterpret_cast(m_rsdt) + sizeof(kapi::acpi::system_description_table_header); + auto entry_count = (m_rsdt->length().value - sizeof(::acpi::sdt)) / entry_size; + auto entries_base = reinterpret_cast(m_rsdt) + sizeof(::acpi::sdt); for (std::size_t i = 0; i < entry_count; ++i) { @@ -74,9 +74,9 @@ namespace kernel::acpi } auto linear_table_address = kapi::memory::hhdm_to_linear(physical_table_address); - auto table = static_cast(linear_table_address); + auto table = static_cast<::acpi::sdt const *>(linear_table_address); - if (!kapi::acpi::validate_checksum({reinterpret_cast(table), table->length().value})) + if (!::acpi::validate_checksum({reinterpret_cast(table), table->length().value})) { kstd::println(kstd::print_sink::stderr, "[OS:ACPI] Invalid table checksum!"); } @@ -90,8 +90,7 @@ namespace kernel::acpi return !m_tables.empty(); } - auto manager::get_table(std::string_view signature) - -> kstd::observer_ptr + auto manager::get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::sdt const> { if (m_tables.contains(signature)) { diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 58d9796..2b61992 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory("acpi" EXCLUDE_FROM_ALL SYSTEM) add_subdirectory("elf" EXCLUDE_FROM_ALL SYSTEM) add_subdirectory("kstd" EXCLUDE_FROM_ALL SYSTEM) -add_subdirectory("multiboot2" EXCLUDE_FROM_ALL SYSTEM) \ No newline at end of file +add_subdirectory("multiboot2" EXCLUDE_FROM_ALL SYSTEM) diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt new file mode 100644 index 0000000..c6e63b9 --- /dev/null +++ b/libs/acpi/CMakeLists.txt @@ -0,0 +1,29 @@ +add_library("acpi" STATIC) +add_library("libs::acpi" ALIAS "acpi") + +target_include_directories("acpi" PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" +) + +file(GLOB_RECURSE ACPI_HEADERS + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + "acpi/*.hpp" +) + +target_sources("acpi" PRIVATE + "acpi/checksum.cpp" + "acpi/madt.cpp" + "acpi/pointers.cpp" + "acpi/sdt.cpp" +) + +target_sources("acpi" PUBLIC + FILE_SET HEADERS + BASE_DIRS "acpi" + FILES + ${ACPI_HEADERS} +) + +target_link_libraries("acpi" PUBLIC + "libs::kstd" +) diff --git a/libs/acpi/acpi/acpi.hpp b/libs/acpi/acpi/acpi.hpp new file mode 100644 index 0000000..fb358cc --- /dev/null +++ b/libs/acpi/acpi/acpi.hpp @@ -0,0 +1,10 @@ +#ifndef ACPI_ACPI_HPP +#define ACPI_ACPI_HPP + +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export + +#endif diff --git a/libs/acpi/acpi/checksum.cpp b/libs/acpi/acpi/checksum.cpp new file mode 100644 index 0000000..56275c8 --- /dev/null +++ b/libs/acpi/acpi/checksum.cpp @@ -0,0 +1,19 @@ +#include + +#include +#include +#include +#include + +namespace acpi +{ + + auto validate_checksum(std::span data) -> bool + { + auto sum = std::ranges::fold_left(data, std::uint8_t{}, [](auto acc, auto byte) { + return static_cast(acc + static_cast(byte)); + }); + return sum == 0; + } + +} // namespace acpi diff --git a/libs/acpi/acpi/checksum.hpp b/libs/acpi/acpi/checksum.hpp new file mode 100644 index 0000000..a92c242 --- /dev/null +++ b/libs/acpi/acpi/checksum.hpp @@ -0,0 +1,20 @@ +#ifndef ACPI_CHECKSUM_HPP +#define ACPI_CHECKSUM_HPP + +// IWYU pragma: private, include + +#include +#include + +namespace acpi +{ + + //! Validate and ACPI entity checksum. + //! + //! @param data The data to validate the checksum of. + //! @return true iff. the checksum is valid, false otherwise. + auto validate_checksum(std::span data) -> bool; + +} // namespace acpi + +#endif diff --git a/libs/acpi/acpi/madt.cpp b/libs/acpi/acpi/madt.cpp new file mode 100644 index 0000000..40289cf --- /dev/null +++ b/libs/acpi/acpi/madt.cpp @@ -0,0 +1,68 @@ +#include + +#include +#include + +namespace acpi +{ + + auto madt::local_interrupt_controller_address() const noexcept -> std::uintptr_t + { + return static_cast(m_local_interrupt_controller_address); + } + + auto madt::flags() const noexcept -> std::uint32_t + { + return m_flags; + } + + auto madt_entry::type() const noexcept -> types + { + return static_cast(m_type); + } + + auto madt_entry::length() const noexcept -> std::size_t + { + return m_length; + } + + auto processor_local_apic::apic_id() const noexcept -> std::uint8_t + { + return m_apic_id; + } + + auto processor_local_apic::active_flags() const noexcept -> flags + { + return static_cast(m_flags); + } + + auto processor_local_apic::processor_id() const noexcept -> std::uint32_t + { + return m_processor_id; + } + + auto madt::begin() const noexcept -> iterator + { + auto base = reinterpret_cast(this); + base += sizeof(madt); + auto limit = reinterpret_cast(this); + limit += length().value; + return iterator{reinterpret_cast(base), reinterpret_cast(limit)}; + } + + auto madt::cbegin() const noexcept -> iterator + { + return begin(); + } + + auto madt::end() const noexcept -> iterator + { + return {}; + } + + auto madt::cend() const noexcept -> iterator + { + return end(); + } + +} // namespace acpi diff --git a/libs/acpi/acpi/madt.hpp b/libs/acpi/acpi/madt.hpp new file mode 100644 index 0000000..f680430 --- /dev/null +++ b/libs/acpi/acpi/madt.hpp @@ -0,0 +1,92 @@ +#ifndef ACPI_ACPI_MADT_HPP +#define ACPI_ACPI_MADT_HPP + +// IWYU pragma: private, include + +#include +#include + +#include +#include + +#include +#include +#include + +namespace acpi +{ + + struct [[gnu::packed]] madt_entry + { + enum struct types : std::uint8_t + { + processor_local_apic, + io_apic, + io_apic_interrupt_source_override, + io_apic_nmi_source, + local_apic_nmi_interrupts, + local_apic_address_override, + processor_local_x2_apic, + }; + + [[nodiscard]] auto type() const noexcept -> types; + [[nodiscard]] auto length() const noexcept -> std::size_t; + + private: + std::uint8_t m_type; + std::uint8_t m_length; + }; + + //! The common header for all + struct [[gnu::packed]] madt : sdt + { + using iterator = sdt_iterator; + using const_iterator = iterator; + + [[nodiscard]] auto local_interrupt_controller_address() const noexcept -> std::uintptr_t; + [[nodiscard]] auto flags() const noexcept -> std::uint32_t; + + [[nodiscard]] auto begin() const noexcept -> iterator; + [[nodiscard]] auto cbegin() const noexcept -> iterator; + [[nodiscard]] auto end() const noexcept -> iterator; + [[nodiscard]] auto cend() const noexcept -> iterator; + + private: + std::uint32_t m_local_interrupt_controller_address; + std::uint32_t m_flags; + }; + + struct [[gnu::packed]] processor_local_apic : madt_entry + { + enum struct flags : std::uint32_t + { + processor_enabled = 1, + online_capable = 2, + }; + + [[nodiscard]] auto apic_id() const noexcept -> std::uint8_t; + [[nodiscard]] auto active_flags() const noexcept -> flags; + [[nodiscard]] auto processor_id() const noexcept -> std::uint32_t; + + private: + std::uint8_t m_processor_id; + std::uint8_t m_apic_id; + std::uint32_t m_flags; + }; + + constexpr char const madt_table_signature[] = "APIC"; // NOLINT + + template<> + struct table_type + { + using type = madt; + }; + +} // namespace acpi + +template<> +struct kstd::ext::is_bitfield_enum : std::true_type +{ +}; + +#endif diff --git a/libs/acpi/acpi/pointers.cpp b/libs/acpi/acpi/pointers.cpp new file mode 100644 index 0000000..b206cc6 --- /dev/null +++ b/libs/acpi/acpi/pointers.cpp @@ -0,0 +1,55 @@ +#include + +#include +#include + +#include +#include +#include +#include + +namespace acpi +{ + + auto rsdp::oem_id() const noexcept -> std::string_view + { + return {m_oem_id.data(), m_oem_id.size()}; + } + + auto rsdp::revision() const noexcept -> std::uint8_t + { + return m_revision; + } + + auto rsdp::signature() const noexcept -> std::string_view + { + return {m_signature.data(), m_signature.size()}; + } + + auto rsdp::table_address() const noexcept -> std::uintptr_t + { + auto raw = std::bit_cast(m_rsdt_address); + return static_cast(raw); + } + + auto rsdp::validate() const noexcept -> bool + { + return validate_checksum({reinterpret_cast(this), sizeof(rsdp)}); + } + + auto xsdp::length() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{m_length}; + } + + auto xsdp::table_address() const noexcept -> std::uintptr_t + { + return std::bit_cast(m_xsdt_address); + } + + auto xsdp::validate() const noexcept -> bool + { + return validate_checksum({reinterpret_cast(this), m_length}); + } + +} // namespace acpi diff --git a/libs/acpi/acpi/pointers.hpp b/libs/acpi/acpi/pointers.hpp new file mode 100644 index 0000000..5771e7d --- /dev/null +++ b/libs/acpi/acpi/pointers.hpp @@ -0,0 +1,72 @@ +#ifndef ACPI_POINTERS_HPP +#define ACPI_POINTERS_HPP + +// IWYU pragma: private, include + +#include + +#include +#include +#include +#include + +namespace acpi +{ + + //! A pointer to the Root System Description Table. + struct [[gnu::packed]] rsdp + { + //! Get the ID of the OEM, usually a vendor name. + [[nodiscard]] auto oem_id() const noexcept -> std::string_view; + + //! Get the revision of the ACPI Root System Description Table. + //! + //! @note Revisions greater or equal to two indicate that the system uses the Extended System Description Table, and + //! as such that table should be use to query information. + [[nodiscard]] auto revision() const noexcept -> std::uint8_t; + + //! Get the signature of this pointer. + //! + //! A valid RSDP must always carry the signature "RSD PTR ". + [[nodiscard]] auto signature() const noexcept -> std::string_view; + + //! Get the physical address of the pointed-to Root System Description Table. + [[nodiscard]] auto table_address() const noexcept -> std::uintptr_t; + + //! Validate the checksum of this RSDP. + //! + //! @return @p true iff. the checksum of this RSDP is valid, @p false otherwise. + [[nodiscard]] auto validate() const noexcept -> bool; + + private: + std::array m_signature; + [[maybe_unused]] std::uint8_t m_checksum; + std::array m_oem_id; + std::uint8_t m_revision; + std::array m_rsdt_address; + }; + + //! A pointer to the Extended System Description Table. + struct [[gnu::packed]] xsdp : rsdp + { + //! Get the length of the data contained in this pointer. + [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; + + //! Get the physical address of the pointed-to Extended System Description Table. + [[nodiscard]] auto table_address() const noexcept -> std::uintptr_t; + + //! Validate the checksum of this XSDP. + //! + //! @return @p true iff. the checksum of this RSDP is valid, @p false otherwise. + [[nodiscard]] auto validate() const noexcept -> bool; + + private: + std::uint32_t m_length; + std::array m_xsdt_address; + [[maybe_unused]] std::uint8_t m_extended_checksum; + [[maybe_unused]] std::array m_reserved; + }; + +} // namespace acpi + +#endif diff --git a/libs/acpi/acpi/sdt.cpp b/libs/acpi/acpi/sdt.cpp new file mode 100644 index 0000000..c2b9d68 --- /dev/null +++ b/libs/acpi/acpi/sdt.cpp @@ -0,0 +1,51 @@ +#include + +#include + +#include +#include + +namespace acpi +{ + + auto sdt::creator_revision() const noexcept -> std::uint32_t + { + return m_creator_revision; + } + + auto sdt::creator_id() const noexcept -> std::uint32_t + { + return m_creator_id; + } + + auto sdt::length() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{m_length}; + } + + auto sdt::oem_id() const noexcept -> std::string_view + { + return {m_oem_id.data(), m_oem_id.size()}; + } + + auto sdt::oem_revision() const noexcept -> std::uint32_t + { + return m_oem_revision; + } + + auto sdt::oem_table_id() const noexcept -> std::string_view + { + return {m_oem_table_id.data(), m_oem_table_id.size()}; + } + + auto sdt::revision() const noexcept -> std::uint8_t + { + return m_revision; + } + + auto sdt::signature() const noexcept -> std::string_view + { + return {m_signature.data(), m_signature.size()}; + } + +} // namespace acpi diff --git a/libs/acpi/acpi/sdt.hpp b/libs/acpi/acpi/sdt.hpp new file mode 100644 index 0000000..9e5ec89 --- /dev/null +++ b/libs/acpi/acpi/sdt.hpp @@ -0,0 +1,119 @@ +#ifndef ACPI_SDT_HPP +#define ACPI_SDT_HPP + +// IWYU pragma: private, include + +#include + +#include + +#include +#include +#include +#include +#include + +namespace acpi +{ + + template + struct sdt_iterator + { + using iterator_category = std::forward_iterator_tag; + using value_type = EntryType; + using pointer = value_type *; + using reference = value_type &; + using difference_type = std::ptrdiff_t; + + constexpr sdt_iterator() noexcept = default; + + constexpr sdt_iterator(pointer entry, pointer limit) noexcept + : m_entry{entry} + , m_limit{limit} + {} + + constexpr auto operator++() noexcept -> sdt_iterator & + { + auto decayed = std::bit_cast(m_entry); + decayed += m_entry->length(); + m_entry = std::bit_cast(decayed); + return *this; + } + + constexpr auto operator++(int) noexcept -> sdt_iterator + { + auto copy = *this; + ++*this; + return copy; + } + + constexpr auto operator*() const noexcept -> reference + { + return *m_entry; + } + + constexpr auto operator==(sdt_iterator const & other) const noexcept -> bool + { + return m_entry == other.m_entry || (is_end() && other.is_end()); + } + + private: + [[nodiscard]] constexpr auto is_end() const noexcept -> bool + { + return m_entry == m_limit; + } + + pointer m_entry{}; + pointer m_limit{}; + }; + + //! The common base of all System Description Tables. + struct [[gnu::packed]] sdt + { + //! Get the revision of the utility used to create this table. + [[nodiscard]] auto creator_revision() const noexcept -> std::uint32_t; + + //! Get the vendor ID of the utility used to create this table. + [[nodiscard]] auto creator_id() const noexcept -> std::uint32_t; + + //! Get the length of the entire table, including this header. + [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; + + //! Get the ID of the OEM. + [[nodiscard]] auto oem_id() const noexcept -> std::string_view; + + //! Get the OEMs revision number of this table. + [[nodiscard]] auto oem_revision() const noexcept -> std::uint32_t; + + //! Get the OEMs ID of this table. + [[nodiscard]] auto oem_table_id() const noexcept -> std::string_view; + + //! Get the revision number of the structure of this table. + [[nodiscard]] auto revision() const noexcept -> std::uint8_t; + + //! Get the signature of this table. + [[nodiscard]] auto signature() const noexcept -> std::string_view; + + private: + std::array m_signature; + std::uint32_t m_length; + std::uint8_t m_revision; + [[maybe_unused]] std::uint8_t m_checksum; + std::array m_oem_id; + std::array m_oem_table_id; + std::uint32_t m_oem_revision; + std::uint32_t m_creator_id; + std::uint32_t m_creator_revision; + }; + + constexpr char const rsdt_table_signature[] = "RSDT"; // NOLINT + + template<> + struct table_type + { + using type = sdt; + }; + +} // namespace acpi + +#endif diff --git a/libs/acpi/acpi/table_type.hpp b/libs/acpi/acpi/table_type.hpp new file mode 100644 index 0000000..7fdd7e3 --- /dev/null +++ b/libs/acpi/acpi/table_type.hpp @@ -0,0 +1,17 @@ +#ifndef ACPI_TABLE_TYPE_HPP +#define ACPI_TABLE_TYPE_HPP + +// IWYU pragma: private, include + +namespace acpi +{ + + template + struct table_type; + + template + using table_type_t = typename table_type::type; + +} // namespace acpi + +#endif -- cgit v1.2.3 From 21fd1281cf19572e202d583689b99c33ec68da50 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 10 Apr 2026 17:49:40 +0200 Subject: kernel: let arch initialize the ACPI manager --- arch/x86_64/CMakeLists.txt | 1 - arch/x86_64/kapi/acpi.cpp | 32 ------------------------------- arch/x86_64/src/devices/init.cpp | 41 ++++++++++++++++++++++++++++++++++++++-- kapi/include/kapi/acpi.hpp | 12 ------------ kernel/kapi/devices.cpp | 3 --- kernel/src/main.cpp | 10 ---------- 6 files changed, 39 insertions(+), 60 deletions(-) delete mode 100644 arch/x86_64/kapi/acpi.cpp diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 62a2aad..f182651 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -12,7 +12,6 @@ target_link_libraries("x86_64" PUBLIC target_sources("x86_64" PRIVATE # Platform-dependent KAPI implementation - "kapi/acpi.cpp" "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" diff --git a/arch/x86_64/kapi/acpi.cpp b/arch/x86_64/kapi/acpi.cpp deleted file mode 100644 index 40b7160..0000000 --- a/arch/x86_64/kapi/acpi.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "kapi/acpi.hpp" - -#include "arch/boot/boot.hpp" - -#include - -#include - -namespace kapi::acpi -{ - - auto get_root_pointer() -> kstd::observer_ptr<::acpi::rsdp const> - { - auto const & mbi = kapi::boot::bootstrap_information.mbi; - auto system_description_pointer = static_cast<::acpi::rsdp const *>(nullptr); - - if (auto const & xsdp = mbi->maybe_acpi_xsdp()) - { - auto data = xsdp->pointer().data(); - - system_description_pointer = reinterpret_cast<::acpi::xsdp const *>(data); - } - else if (auto const & rsdp = mbi->maybe_acpi_rsdp()) - { - auto data = rsdp->pointer().data(); - system_description_pointer = reinterpret_cast<::acpi::rsdp const *>(data); - } - - return kstd::make_observer(system_description_pointer); - } - -} // namespace kapi::acpi \ No newline at end of file diff --git a/arch/x86_64/src/devices/init.cpp b/arch/x86_64/src/devices/init.cpp index 6cba986..7f0faa4 100644 --- a/arch/x86_64/src/devices/init.cpp +++ b/arch/x86_64/src/devices/init.cpp @@ -1,13 +1,18 @@ #include "arch/devices/init.hpp" +#include "kapi/acpi.hpp" +#include "kapi/cpu.hpp" #include "kapi/devices.hpp" +#include "arch/boot/boot.hpp" #include "arch/bus/isa.hpp" #include "arch/devices/legacy_pit.hpp" #include #include +#include + #include #include @@ -17,9 +22,41 @@ namespace arch::devices namespace { constexpr auto pit_frequency_in_hz = std::uint32_t{100u}; - } - auto init_acpi_devices() -> void {} + auto get_acpi_root_pointer() -> kstd::observer_ptr<::acpi::rsdp const> + { + auto const & mbi = kapi::boot::bootstrap_information.mbi; + auto system_description_pointer = static_cast<::acpi::rsdp const *>(nullptr); + + if (auto const & xsdp = mbi->maybe_acpi_xsdp()) + { + auto data = xsdp->pointer().data(); + + system_description_pointer = reinterpret_cast<::acpi::xsdp const *>(data); + } + else if (auto const & rsdp = mbi->maybe_acpi_rsdp()) + { + auto data = rsdp->pointer().data(); + system_description_pointer = reinterpret_cast<::acpi::rsdp const *>(data); + } + + return kstd::make_observer(system_description_pointer); + } + } // namespace + + auto init_acpi_devices() -> void + { + auto acpi_root_pointer = get_acpi_root_pointer(); + if (acpi_root_pointer && acpi_root_pointer->validate()) + { + if (kapi::acpi::init(*acpi_root_pointer)) + { + kstd::println("[x86_64:DEV] ACPI subsystem initialized."); + } + } + + kapi::cpu::discover_topology(); + } auto init_legacy_devices() -> void { diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index 01fd113..2835496 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -35,18 +35,6 @@ namespace kapi::acpi return kstd::make_observer(static_cast<::acpi::table_type_t const *>(get_table(Signature).get())); } - //! @} - - //! @addtogroup kapi-acpi-platform-defined - //! @{ - - //! Retrieve the RSDP or XSDP for this system. - //! - //! @return a pointer to either the RSDP or XSDP data structure, or @p nullptr if neither is present. - auto get_root_pointer() -> kstd::observer_ptr<::acpi::rsdp const>; - - //! @} - } // namespace kapi::acpi #endif diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index b8aa44b..2250319 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -1,6 +1,5 @@ #include "kapi/devices.hpp" -#include "kapi/cpu.hpp" #include "kapi/system.hpp" #include "kernel/devices/root_bus.hpp" @@ -35,8 +34,6 @@ namespace kapi::devices auto & bus = root_bus.emplace(); register_device(bus); bus.init(); - - kapi::cpu::discover_topology(); } auto get_root_bus() -> bus & diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 4b61948..b920674 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,4 +1,3 @@ -#include "kapi/acpi.hpp" #include "kapi/boot_modules.hpp" #include "kapi/cio.hpp" #include "kapi/cpu.hpp" @@ -184,15 +183,6 @@ auto main() -> int kapi::memory::init_mmio(kapi::memory::mmio_base, 1_GiB / kapi::memory::page::size); kstd::println("[OS] Memory subsystem initialized."); - auto acpi_root_pointer = kapi::acpi::get_root_pointer(); - if (acpi_root_pointer && acpi_root_pointer->validate()) - { - if (kapi::acpi::init(*acpi_root_pointer)) - { - kstd::println("[OS] ACPI subsystem initialized."); - } - } - kapi::devices::init(); kstd::println("[OS] System root bus initialized."); -- cgit v1.2.3 From 72b40ecf33fb0ef2d4232b80560642296c79399c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 09:49:17 +0200 Subject: automatically detect the mounted file system type by trial-and-error --- kernel/include/kernel/filesystem/filesystem.hpp | 3 ++- kernel/src/filesystem/filesystem.cpp | 29 +++++++++++++++++++++---- kernel/src/filesystem/vfs.cpp | 5 +---- kernel/src/main.cpp | 4 ++-- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 1d86178..05f96dc 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -2,6 +2,7 @@ #define TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP #include "kapi/devices/device.hpp" + #include "kernel/filesystem/inode.hpp" #include @@ -15,7 +16,7 @@ namespace kernel::filesystem { virtual ~filesystem() = default; - virtual auto mount(kstd::shared_ptr const & device) -> int; + auto static mount(kstd::shared_ptr const & device) -> kstd::shared_ptr; virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr = 0; [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index 0ac9cf8..e0c760f 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -1,20 +1,41 @@ #include "kernel/filesystem/filesystem.hpp" #include "kapi/devices/device.hpp" +#include "kapi/system.hpp" + +#include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include +#include + namespace kernel::filesystem { - auto filesystem::mount(kstd::shared_ptr const & device) -> int + namespace + { + constexpr auto static filesystem_factories = std::array{ + []() { return kstd::make_shared(); }, + }; + } // namespace + + auto filesystem::mount(kstd::shared_ptr const & device) -> kstd::shared_ptr { if (!device) { - return -1; // TODO BA-FS26 panic or errorcode? + kapi::system::panic("[FILESYSTEM] cannot mount filesystem: device is null."); } - m_device = device; - return 0; + + for (auto & factory : filesystem_factories) + { + auto fs = factory(); + if (fs->mount(device) == 0) + { + return fs; + } + } + + kapi::system::panic("[FILESYSTEM] cannot mount filesystem: no suitable filesystem found on device."); } auto filesystem::root_inode() const -> kstd::shared_ptr const & diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 06214d2..66aa91e 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -5,7 +5,6 @@ #include "kernel/devices/storage/management.hpp" #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/devfs/filesystem.hpp" -#include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/mount.hpp" #include "kernel/filesystem/open_file_description.hpp" @@ -46,9 +45,7 @@ namespace kernel::filesystem auto storage_mgmt = devices::storage::management::get(); if (auto boot_device = storage_mgmt.determine_boot_device()) { - // TODO BA-FS26 detect fs type from boot device and load corresponding fs, for now just assume ext2 - auto boot_root_fs = kstd::make_shared(); - boot_root_fs->mount(boot_device); + auto boot_root_fs = kernel::filesystem::filesystem::mount(boot_device); do_mount_internal("/", root_fs_root_dentry, boot_root_fs); } diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index b920674..382f12d 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -130,9 +130,9 @@ auto test_file_lookup() -> void kstd::os::panic("test code failed"); } - auto new_filesystem = kstd::make_shared(); auto device = storage_mgmt.device_by_major_minor(1, 16); - new_filesystem->mount(device); + auto new_filesystem = kernel::filesystem::filesystem::mount(device); + if (vfs.do_mount("/a/b", new_filesystem) != 0) { kstd::os::panic("test code failed"); -- cgit v1.2.3 From 93bca4c2a7c1852fc89df6965c835a7dbbdd6512 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 09:50:14 +0200 Subject: read ext2 superblock and check the magic number --- .../include/kernel/filesystem/ext2/filesystem.hpp | 11 +++- kernel/src/filesystem/ext2/filesystem.cpp | 66 ++++++++++++---------- 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 078da31..176d83c 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -2,20 +2,29 @@ #define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP #include "kapi/devices/device.hpp" + +#include "kernel/filesystem/ext2/superblock.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include +#include #include namespace kernel::filesystem::ext2 { struct filesystem : kernel::filesystem::filesystem { - auto mount(kstd::shared_ptr const & device) -> int override; + auto mount(kstd::shared_ptr const & device) -> int; auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; + + private: + auto get_block_size() -> size_t; + auto get_inode_size() -> size_t; + + superblock m_superblock; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index eb9edc4..a3425b5 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -1,10 +1,10 @@ #include "kernel/filesystem/ext2/filesystem.hpp" -#include "kernel/devices/block_device_utils.hpp" #include "kapi/devices/device.hpp" + +#include "kernel/devices/block_device_utils.hpp" #include "kernel/filesystem/ext2/inode.hpp" #include "kernel/filesystem/ext2/superblock.hpp" -#include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include @@ -17,37 +17,35 @@ namespace kernel::filesystem::ext2 { namespace { - // constexpr size_t SUPERBLOCK_OFFSET = 1024; - // constexpr uint16_t EXT2_MAGIC = 0xEF53; - - // // Mode bits - // constexpr uint16_t S_IFMT = 0xF000; - // constexpr uint16_t S_IFREG = 0x8000; - // constexpr uint16_t S_IFDIR = 0x4000; - - // auto S_ISREG(uint16_t mode) -> bool - // { - // return (mode & S_IFMT) == S_IFREG; - // } - // auto S_ISDIR(uint16_t mode) -> bool - // { - // return (mode & S_IFMT) == S_IFDIR; - // } - - // auto get_block_size(superblock const & superblock) -> size_t - // { - // return 1024U << superblock.log_block_size; - // } - - // auto get_inode_size(superblock const & superblock) -> size_t - // { - // return superblock.rev_level == 0 ? 128 : superblock.inode_size; - // } + constexpr size_t SUPERBLOCK_OFFSET = 1024; + constexpr uint16_t EXT2_MAGIC = 0xEF53; + + // Mode bits + constexpr uint16_t S_IFMT = 0xF000; + constexpr uint16_t S_IFREG = 0x8000; + constexpr uint16_t S_IFDIR = 0x4000; + + auto S_ISREG(uint16_t mode) -> bool + { + return (mode & S_IFMT) == S_IFREG; + } + auto S_ISDIR(uint16_t mode) -> bool + { + return (mode & S_IFMT) == S_IFDIR; + } } // namespace auto filesystem::mount(kstd::shared_ptr const & device) -> int { - kernel::filesystem::filesystem::mount(device); // TODO BA-FS26 error handling? + m_device = device; + + kernel::devices::block_device_utils::read(m_device, &m_superblock, SUPERBLOCK_OFFSET, sizeof(m_superblock)); + + if (m_superblock.magic != EXT2_MAGIC) + { + return -1; + } + // TODO BA-FS26 load proper root inode from ext2 metadata // m_root_inode = inode{inode_kind::directory}; @@ -69,4 +67,14 @@ namespace kernel::filesystem::ext2 return kstd::make_shared(); } + + auto filesystem::get_block_size() -> size_t + { + return 1024U << m_superblock.log_block_size; + } + + auto filesystem::get_inode_size() -> size_t + { + return m_superblock.rev_level == 0 ? 128 : m_superblock.inode_size; + } } // namespace kernel::filesystem::ext2 -- cgit v1.2.3 From 1dcf253fdf8169a3b2b71bfac68f2f25951af1a8 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 10:04:04 +0200 Subject: fix build, refactoring --- .../include/kernel/filesystem/devfs/filesystem.hpp | 1 + .../include/kernel/filesystem/ext2/filesystem.hpp | 2 +- kernel/include/kernel/filesystem/filesystem.hpp | 4 +++- .../kernel/filesystem/rootfs/filesystem.hpp | 1 + kernel/src/filesystem/ext2/filesystem.cpp | 27 +++++++++++----------- kernel/src/filesystem/filesystem.cpp | 9 +++++++- kernel/src/filesystem/rootfs/filesystem.cpp | 1 + kernel/src/filesystem/vfs.cpp | 2 +- kernel/src/main.cpp | 4 ++-- 9 files changed, 32 insertions(+), 19 deletions(-) diff --git a/kernel/include/kernel/filesystem/devfs/filesystem.hpp b/kernel/include/kernel/filesystem/devfs/filesystem.hpp index 29ae388..3edeabb 100644 --- a/kernel/include/kernel/filesystem/devfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/devfs/filesystem.hpp @@ -2,6 +2,7 @@ #define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_FILESYSTEM_HPP #include "kapi/devices/device.hpp" + #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 176d83c..f6cd17f 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -16,7 +16,7 @@ namespace kernel::filesystem::ext2 { struct filesystem : kernel::filesystem::filesystem { - auto mount(kstd::shared_ptr const & device) -> int; + auto mount(kstd::shared_ptr const & device) -> int override; auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 05f96dc..1c45377 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -16,7 +16,9 @@ namespace kernel::filesystem { virtual ~filesystem() = default; - auto static mount(kstd::shared_ptr const & device) -> kstd::shared_ptr; + auto static probe_and_mount(kstd::shared_ptr const & device) -> kstd::shared_ptr; + + virtual auto mount(kstd::shared_ptr const & device) -> int; virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr = 0; [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; diff --git a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp index 5632d86..7931d87 100644 --- a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp @@ -2,6 +2,7 @@ #define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_FILESYSTEM_HPP #include "kapi/devices/device.hpp" + #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index a3425b5..0ebb243 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -5,6 +5,7 @@ #include "kernel/devices/block_device_utils.hpp" #include "kernel/filesystem/ext2/inode.hpp" #include "kernel/filesystem/ext2/superblock.hpp" +#include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include @@ -21,23 +22,23 @@ namespace kernel::filesystem::ext2 constexpr uint16_t EXT2_MAGIC = 0xEF53; // Mode bits - constexpr uint16_t S_IFMT = 0xF000; - constexpr uint16_t S_IFREG = 0x8000; - constexpr uint16_t S_IFDIR = 0x4000; - - auto S_ISREG(uint16_t mode) -> bool - { - return (mode & S_IFMT) == S_IFREG; - } - auto S_ISDIR(uint16_t mode) -> bool - { - return (mode & S_IFMT) == S_IFDIR; - } + // constexpr uint16_t S_IFMT = 0xF000; + // constexpr uint16_t S_IFREG = 0x8000; + // constexpr uint16_t S_IFDIR = 0x4000; + + // auto S_ISREG(uint16_t mode) -> bool + // { + // return (mode & S_IFMT) == S_IFREG; + // } + // auto S_ISDIR(uint16_t mode) -> bool + // { + // return (mode & S_IFMT) == S_IFDIR; + // } } // namespace auto filesystem::mount(kstd::shared_ptr const & device) -> int { - m_device = device; + kernel::filesystem::filesystem::mount(device); kernel::devices::block_device_utils::read(m_device, &m_superblock, SUPERBLOCK_OFFSET, sizeof(m_superblock)); diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index e0c760f..a06eb80 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -19,7 +19,8 @@ namespace kernel::filesystem }; } // namespace - auto filesystem::mount(kstd::shared_ptr const & device) -> kstd::shared_ptr + auto filesystem::probe_and_mount(kstd::shared_ptr const & device) + -> kstd::shared_ptr { if (!device) { @@ -38,6 +39,12 @@ namespace kernel::filesystem kapi::system::panic("[FILESYSTEM] cannot mount filesystem: no suitable filesystem found on device."); } + auto filesystem::mount(kstd::shared_ptr const & device) -> int + { + m_device = device; + return 0; + } + auto filesystem::root_inode() const -> kstd::shared_ptr const & { return m_root_inode; diff --git a/kernel/src/filesystem/rootfs/filesystem.cpp b/kernel/src/filesystem/rootfs/filesystem.cpp index 37bf588..a7c746e 100644 --- a/kernel/src/filesystem/rootfs/filesystem.cpp +++ b/kernel/src/filesystem/rootfs/filesystem.cpp @@ -1,6 +1,7 @@ #include "kernel/filesystem/rootfs/filesystem.hpp" #include "kapi/devices/device.hpp" + #include "kernel/filesystem/inode.hpp" #include "kernel/filesystem/rootfs/inode.hpp" diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 66aa91e..d611fc9 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -45,7 +45,7 @@ namespace kernel::filesystem auto storage_mgmt = devices::storage::management::get(); if (auto boot_device = storage_mgmt.determine_boot_device()) { - auto boot_root_fs = kernel::filesystem::filesystem::mount(boot_device); + auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device); do_mount_internal("/", root_fs_root_dentry, boot_root_fs); } diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 382f12d..8df6767 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -8,8 +8,8 @@ #include "kernel/devices/storage/management.hpp" #include "kernel/filesystem/device_inode.hpp" -#include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/file_descriptor_table.hpp" +#include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/open_file_description.hpp" #include "kernel/filesystem/vfs.hpp" #include "kernel/memory.hpp" @@ -131,7 +131,7 @@ auto test_file_lookup() -> void } auto device = storage_mgmt.device_by_major_minor(1, 16); - auto new_filesystem = kernel::filesystem::filesystem::mount(device); + auto new_filesystem = kernel::filesystem::filesystem::probe_and_mount(device); if (vfs.do_mount("/a/b", new_filesystem) != 0) { -- cgit v1.2.3 From 16b854e991bb791694268d09eb696c719cdff42f Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 11:09:13 +0200 Subject: read block_group_descriptors --- kernel/include/kernel/filesystem/ext2/filesystem.hpp | 3 +++ kernel/src/filesystem/ext2/filesystem.cpp | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index f6cd17f..59b9cba 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -3,11 +3,13 @@ #include "kapi/devices/device.hpp" +#include "kernel/filesystem/ext2/block_group_descriptor.hpp" #include "kernel/filesystem/ext2/superblock.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include +#include #include #include @@ -25,6 +27,7 @@ namespace kernel::filesystem::ext2 auto get_inode_size() -> size_t; superblock m_superblock; + kstd::vector m_block_group_descriptors; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 0ebb243..96c1589 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -3,12 +3,14 @@ #include "kapi/devices/device.hpp" #include "kernel/devices/block_device_utils.hpp" +#include "kernel/filesystem/ext2/block_group_descriptor.hpp" #include "kernel/filesystem/ext2/inode.hpp" #include "kernel/filesystem/ext2/superblock.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include +#include #include #include @@ -47,6 +49,17 @@ namespace kernel::filesystem::ext2 return -1; } + auto const block_size = get_block_size(); + auto const blocks_per_group = m_superblock.blocks_per_group; + auto const num_block_groups = (m_superblock.blocks_count + blocks_per_group - 1) / blocks_per_group; + + m_block_group_descriptors = kstd::vector(num_block_groups); + + auto const block_group_descriptor_table_offset = block_size == 1024 ? 2 * block_size : block_size; + kernel::devices::block_device_utils::read(m_device, m_block_group_descriptors.data(), + block_group_descriptor_table_offset, + num_block_groups * sizeof(block_group_descriptor)); + // TODO BA-FS26 load proper root inode from ext2 metadata // m_root_inode = inode{inode_kind::directory}; -- cgit v1.2.3 From 91db623189133cb14693ae60ee54ac293cec3b54 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 11:33:14 +0200 Subject: remove todos --- kernel/include/kernel/filesystem/ext2/superblock.hpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/superblock.hpp b/kernel/include/kernel/filesystem/ext2/superblock.hpp index 8600b4c..e7e15f2 100644 --- a/kernel/include/kernel/filesystem/ext2/superblock.hpp +++ b/kernel/include/kernel/filesystem/ext2/superblock.hpp @@ -41,11 +41,8 @@ namespace kernel::filesystem::ext2 uint32_t feature_compat; uint32_t feature_incompat; uint32_t feature_ro_compat; - // uint8_t uuid[16]; // TODO BA-FS26 really correct? std::array uuid; - // uint8_t volume_name[16]; // TODO BA-FS26 really correct? std::array volume_name; - // uint8_t last_mounted[64]; // TODO BA-FS26 really correct? std::array last_mounted; uint32_t algorithm_usage_bitmap; @@ -55,14 +52,12 @@ namespace kernel::filesystem::ext2 uint16_t padding1; // Journaling Support - // uint8_t journal_uuid[16]; // TODO BA-FS26 really correct? std::array journal_uuid; uint32_t journal_inum; uint32_t journal_dev; uint32_t last_orphan; // Directory Indexing Support - // uint32_t hash_seed[4]; // TODO BA-FS26 really correct? std::array hash_seed; uint8_t def_hash_version; std::array padding2; -- cgit v1.2.3 From baf63039d5430c0b3b1e6235b561c12f60e97f49 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 15:03:06 +0200 Subject: implement read_inode --- .../filesystem/ext2/block_group_descriptor.hpp | 2 +- .../include/kernel/filesystem/ext2/filesystem.hpp | 4 +++ kernel/include/kernel/filesystem/ext2/inode.hpp | 19 +++++++----- .../filesystem/ext2/linked_directory_entry.hpp | 2 +- .../include/kernel/filesystem/ext2/superblock.hpp | 2 +- kernel/src/filesystem/ext2/filesystem.cpp | 35 +++++++++++++++++----- 6 files changed, 45 insertions(+), 19 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp b/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp index a23c045..2ff91d9 100644 --- a/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp +++ b/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp @@ -6,7 +6,7 @@ namespace kernel::filesystem::ext2 { - struct block_group_descriptor + struct [[gnu::packed]] block_group_descriptor { uint32_t block_bitmap; uint32_t inode_bitmap; diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 59b9cba..ccee172 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -4,6 +4,7 @@ #include "kapi/devices/device.hpp" #include "kernel/filesystem/ext2/block_group_descriptor.hpp" +#include "kernel/filesystem/ext2/inode.hpp" #include "kernel/filesystem/ext2/superblock.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" @@ -12,6 +13,7 @@ #include #include +#include #include namespace kernel::filesystem::ext2 @@ -23,6 +25,8 @@ namespace kernel::filesystem::ext2 -> kstd::shared_ptr override; private: + auto read_inode(uint32_t inode_number) -> kstd::shared_ptr; + auto get_block_size() -> size_t; auto get_inode_size() -> size_t; diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index 2c27c17..4284e6f 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -11,13 +11,8 @@ namespace kernel::filesystem::ext2 { - struct inode : kernel::filesystem::inode + struct [[gnu::packed]] inode_data { - inode(); - - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; - auto write(void const * buffer, size_t offset, size_t size) -> size_t override; - uint16_t mode; uint16_t uid; uint32_t size; @@ -30,15 +25,23 @@ namespace kernel::filesystem::ext2 uint32_t blocks; uint32_t flags; uint32_t osd1; - // uint32_t block[15]; // TODO BA-FS26 really correct? std::array block; // NOLINT(readability-magic-numbers) uint32_t generation; uint32_t file_acl; uint32_t dir_acl; uint32_t faddr; - // uint8_t osd2[12]; // TODO BA-FS26 really correct? std::array osd2; // NOLINT(readability-magic-numbers) }; + + struct inode : kernel::filesystem::inode + { + inode(); + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + inode_data m_data{}; + }; } // namespace kernel::filesystem::ext2 #endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp index 8dd42a1..f44255a 100644 --- a/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp +++ b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp @@ -6,7 +6,7 @@ namespace kernel::filesystem::ext2 { - struct linked_directory_entry + struct [[gnu::packed]] linked_directory_entry { uint32_t inode; uint16_t rec_len; diff --git a/kernel/include/kernel/filesystem/ext2/superblock.hpp b/kernel/include/kernel/filesystem/ext2/superblock.hpp index e7e15f2..8e57ae7 100644 --- a/kernel/include/kernel/filesystem/ext2/superblock.hpp +++ b/kernel/include/kernel/filesystem/ext2/superblock.hpp @@ -6,7 +6,7 @@ namespace kernel::filesystem::ext2 { - struct superblock + struct [[gnu::packed]] superblock { uint32_t inodes_count; uint32_t blocks_count; diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 96c1589..d650e97 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -21,7 +21,9 @@ namespace kernel::filesystem::ext2 namespace { constexpr size_t SUPERBLOCK_OFFSET = 1024; - constexpr uint16_t EXT2_MAGIC = 0xEF53; + constexpr uint16_t MAGIC_NUMBER = 0xEF53; + + constexpr uint32_t ROOT_INODE_NUMBER = 2; // Mode bits // constexpr uint16_t S_IFMT = 0xF000; @@ -44,7 +46,7 @@ namespace kernel::filesystem::ext2 kernel::devices::block_device_utils::read(m_device, &m_superblock, SUPERBLOCK_OFFSET, sizeof(m_superblock)); - if (m_superblock.magic != EXT2_MAGIC) + if (m_superblock.magic != MAGIC_NUMBER) { return -1; } @@ -60,12 +62,7 @@ namespace kernel::filesystem::ext2 block_group_descriptor_table_offset, num_block_groups * sizeof(block_group_descriptor)); - // TODO BA-FS26 load proper root inode from ext2 metadata - // m_root_inode = inode{inode_kind::directory}; - - // TODO BA-FS26 implement - m_root_inode = kstd::make_shared(); - // devices::block_device_utils::read(device, nullptr, 0, 0); // TODO BA-FS26 just for testing + m_root_inode = read_inode(ROOT_INODE_NUMBER); return 0; } @@ -82,6 +79,28 @@ namespace kernel::filesystem::ext2 return kstd::make_shared(); } + auto filesystem::read_inode(uint32_t inode_number) -> kstd::shared_ptr + { + auto const block_size = get_block_size(); + auto const inodes_per_group = m_superblock.inodes_per_group; + auto const block_group_index = (inode_number - 1) / inodes_per_group; + auto const inode_index_within_group = (inode_number - 1) % inodes_per_group; + + if (block_group_index >= m_block_group_descriptors.size()) + { + return nullptr; + } + + auto const & block_group_descriptor = m_block_group_descriptors.at(block_group_index); + auto const inode_table_start_block = block_group_descriptor.inode_table; + auto const inode_table_offset = static_cast(inode_table_start_block) * block_size; + auto const inode_offset = inode_table_offset + inode_index_within_group * get_inode_size(); + + auto new_inode = kstd::make_shared(); + kernel::devices::block_device_utils::read(m_device, &new_inode->m_data, inode_offset, sizeof(inode_data)); + return new_inode; + } + auto filesystem::get_block_size() -> size_t { return 1024U << m_superblock.log_block_size; -- cgit v1.2.3 From 794de4a7f8dbea164d857ae9e4525536f338518d Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 2 Apr 2026 16:06:40 +0200 Subject: temporary implementation of inode kind --- kernel/include/kernel/filesystem/inode.hpp | 3 ++- kernel/src/filesystem/ext2/filesystem.cpp | 41 +++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index d97b5ab..59207df 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -25,7 +25,8 @@ namespace kernel::filesystem [[nodiscard]] auto is_regular() const -> bool; [[nodiscard]] auto is_device() const -> bool; - private: + // TODO BA-FS26 improve inode_kind really needed? + public: inode_kind m_kind{inode_kind::regular}; }; } // namespace kernel::filesystem diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index d650e97..31dc6f2 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -26,18 +26,18 @@ namespace kernel::filesystem::ext2 constexpr uint32_t ROOT_INODE_NUMBER = 2; // Mode bits - // constexpr uint16_t S_IFMT = 0xF000; - // constexpr uint16_t S_IFREG = 0x8000; - // constexpr uint16_t S_IFDIR = 0x4000; - - // auto S_ISREG(uint16_t mode) -> bool - // { - // return (mode & S_IFMT) == S_IFREG; - // } - // auto S_ISDIR(uint16_t mode) -> bool - // { - // return (mode & S_IFMT) == S_IFDIR; - // } + constexpr uint16_t S_IFMT = 0xF000; + constexpr uint16_t S_IFREG = 0x8000; + constexpr uint16_t S_IFDIR = 0x4000; + + auto S_ISREG(uint16_t mode) -> bool + { + return (mode & S_IFMT) == S_IFREG; + } + auto S_ISDIR(uint16_t mode) -> bool + { + return (mode & S_IFMT) == S_IFDIR; + } } // namespace auto filesystem::mount(kstd::shared_ptr const & device) -> int @@ -63,6 +63,7 @@ namespace kernel::filesystem::ext2 num_block_groups * sizeof(block_group_descriptor)); m_root_inode = read_inode(ROOT_INODE_NUMBER); + // TODO BA-FS26 check if root inode is valid and is a directory ?? return 0; } @@ -98,6 +99,22 @@ namespace kernel::filesystem::ext2 auto new_inode = kstd::make_shared(); kernel::devices::block_device_utils::read(m_device, &new_inode->m_data, inode_offset, sizeof(inode_data)); + + // TODO BA-FS26 improve inode_kind really needed? or just map it to the mode bits? + if (S_ISREG(new_inode->m_data.mode)) + { + new_inode->m_kind = inode::inode_kind::regular; + } + else if (S_ISDIR(new_inode->m_data.mode)) + { + new_inode->m_kind = inode::inode_kind::directory; + } + else + { + // TODO BA-FS26 really correct?? + return nullptr; + } + return new_inode; } -- cgit v1.2.3 From 82c8a4c16e3af3d62d7211e741b051da900de79c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 3 Apr 2026 10:16:36 +0200 Subject: first lookup draft --- kernel/src/filesystem/ext2/filesystem.cpp | 48 ++++++++++++++++++++++++++++++- kernel/src/main.cpp | 2 ++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 31dc6f2..82fcba9 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -5,11 +5,13 @@ #include "kernel/devices/block_device_utils.hpp" #include "kernel/filesystem/ext2/block_group_descriptor.hpp" #include "kernel/filesystem/ext2/inode.hpp" +#include "kernel/filesystem/ext2/linked_directory_entry.hpp" #include "kernel/filesystem/ext2/superblock.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" #include +#include #include #include @@ -24,6 +26,7 @@ namespace kernel::filesystem::ext2 constexpr uint16_t MAGIC_NUMBER = 0xEF53; constexpr uint32_t ROOT_INODE_NUMBER = 2; + constexpr size_t DIRECT_BLOCK_COUNT = 12; // Mode bits constexpr uint16_t S_IFMT = 0xF000; @@ -67,9 +70,52 @@ namespace kernel::filesystem::ext2 return 0; } - auto filesystem::lookup(kstd::shared_ptr const & /*parent*/, std::string_view name) + auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr { + if (!parent || !parent->is_directory()) + { + return nullptr; + } + + auto * ext2_parent = static_cast(parent.get()); + if (!ext2_parent) + { + return nullptr; + } + + auto const block_size = get_block_size(); + auto const & inode_data = ext2_parent->m_data; + kstd::vector buffer(block_size); + + for (size_t i = 0; i < DIRECT_BLOCK_COUNT && inode_data.block[i] != 0; ++i) + { + kernel::devices::block_device_utils::read(m_device, buffer.data(), inode_data.block[i] * block_size, block_size); + + size_t offset = 0; + while (offset < block_size) + { + auto * entry = reinterpret_cast(buffer.data() + offset); + if (entry->inode == 0) + break; + + // Stop if the directory entry is malformed to avoid overruns/loops. + // if (entry->rec_len < sizeof(linked_directory_entry) || offset + entry->rec_len > block_size) + // { + // break; + // } + + std::string_view entry_name(reinterpret_cast(entry->name.data()), entry->name_len); + if (entry_name == name) + { + return read_inode(entry->inode); + } + + offset += entry->rec_len; + } + } + + return nullptr; // TODO BA-FS26 implement ext2 directory traversal and inode loading if (name == "dev") { diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 8df6767..258c28b 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -117,6 +117,8 @@ auto test_file_lookup() -> void auto vfs = kernel::filesystem::vfs::get(); auto storage_mgmt = kernel::devices::storage::management::get(); + auto ofd_hello = vfs.open("/hello.txt"); + auto ofd1 = vfs.open("/a/b/c"); auto ofd2 = vfs.open("/dev/ram0"); auto ofd3 = vfs.open("/a/d/e"); -- cgit v1.2.3 From 15ea1551e54c36ebac26f21dc156636e326298c6 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 3 Apr 2026 14:29:32 +0200 Subject: fix linked_directory_entry struct --- kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp index f44255a..76eb6f5 100644 --- a/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp +++ b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp @@ -12,8 +12,7 @@ namespace kernel::filesystem::ext2 uint16_t rec_len; uint8_t name_len; uint8_t file_type; - uint8_t pad; - std::array name; // NOLINT(readability-magic-numbers) + std::array name; // NOLINT(readability-magic-numbers) }; } // namespace kernel::filesystem::ext2 -- cgit v1.2.3 From 7e6137b2725d5cf2b16f55678dcfb99091f03fe9 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 3 Apr 2026 14:30:20 +0200 Subject: implement map_inode_block_index_to_global_block_number and lookup --- .../include/kernel/filesystem/ext2/filesystem.hpp | 2 + kernel/src/filesystem/ext2/filesystem.cpp | 120 ++++++++++++++++----- 2 files changed, 98 insertions(+), 24 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index ccee172..dea059f 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -26,9 +26,11 @@ namespace kernel::filesystem::ext2 private: auto read_inode(uint32_t inode_number) -> kstd::shared_ptr; + auto map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t; auto get_block_size() -> size_t; auto get_inode_size() -> size_t; + auto get_inode_block_count(inode_data const & data) -> uint32_t; superblock m_superblock; kstd::vector m_block_group_descriptors; diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 82fcba9..a84414f 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -11,6 +11,7 @@ #include "kernel/filesystem/inode.hpp" #include +#include #include #include @@ -26,7 +27,11 @@ namespace kernel::filesystem::ext2 constexpr uint16_t MAGIC_NUMBER = 0xEF53; constexpr uint32_t ROOT_INODE_NUMBER = 2; + constexpr size_t DIRECT_BLOCK_COUNT = 12; + constexpr size_t INDIRECT_BLOCK_INDEX = DIRECT_BLOCK_COUNT; + constexpr size_t DOUBLY_INDIRECT_BLOCK_INDEX = INDIRECT_BLOCK_INDEX + 1; + constexpr size_t TRIPLY_INDIRECT_BLOCK_INDEX = DOUBLY_INDIRECT_BLOCK_INDEX + 1; // Mode bits constexpr uint16_t S_IFMT = 0xF000; @@ -37,6 +42,7 @@ namespace kernel::filesystem::ext2 { return (mode & S_IFMT) == S_IFREG; } + auto S_ISDIR(uint16_t mode) -> bool { return (mode & S_IFMT) == S_IFDIR; @@ -88,42 +94,29 @@ namespace kernel::filesystem::ext2 auto const & inode_data = ext2_parent->m_data; kstd::vector buffer(block_size); - for (size_t i = 0; i < DIRECT_BLOCK_COUNT && inode_data.block[i] != 0; ++i) + for (uint32_t i = 0; i < get_inode_block_count(inode_data); ++i) { - kernel::devices::block_device_utils::read(m_device, buffer.data(), inode_data.block[i] * block_size, block_size); - - size_t offset = 0; - while (offset < block_size) - { - auto * entry = reinterpret_cast(buffer.data() + offset); - if (entry->inode == 0) - break; + auto const global_block_number = map_inode_block_index_to_global_block_number(i, inode_data); + auto const block_offset = global_block_number * block_size; + kernel::devices::block_device_utils::read(m_device, buffer.data(), block_offset, block_size); - // Stop if the directory entry is malformed to avoid overruns/loops. - // if (entry->rec_len < sizeof(linked_directory_entry) || offset + entry->rec_len > block_size) - // { - // break; - // } + auto const * entry = reinterpret_cast(buffer.data()); + auto bytes_read = 0uz; - std::string_view entry_name(reinterpret_cast(entry->name.data()), entry->name_len); + while (bytes_read < block_size && entry->inode != 0) + { + auto const entry_name = std::string_view{entry->name.data(), entry->name_len}; if (entry_name == name) { return read_inode(entry->inode); } - offset += entry->rec_len; + bytes_read += entry->rec_len; + entry = reinterpret_cast(buffer.data() + bytes_read); } } return nullptr; - // TODO BA-FS26 implement ext2 directory traversal and inode loading - if (name == "dev") - { - // TODO BA-FS26 just for testing - return nullptr; - } - - return kstd::make_shared(); } auto filesystem::read_inode(uint32_t inode_number) -> kstd::shared_ptr @@ -164,6 +157,80 @@ namespace kernel::filesystem::ext2 return new_inode; } + auto filesystem::map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t + { + if (inode_block_index < DIRECT_BLOCK_COUNT) + { + return data.block.at(inode_block_index); + } + inode_block_index -= DIRECT_BLOCK_COUNT; + + auto const block_size = get_block_size(); + auto const numbers_per_block = block_size / sizeof(uint32_t); + uint32_t block_number_buffer = 0; + + auto const number_of_singly_indirect_blocks = numbers_per_block; + if (inode_block_index < number_of_singly_indirect_blocks) + { + auto const indirect_block_start_offset = data.block.at(INDIRECT_BLOCK_INDEX) * block_size; + auto const direct_block_number_index = inode_block_index; + auto const direct_block_number_offset = + indirect_block_start_offset + direct_block_number_index * sizeof(uint32_t); + kernel::devices::block_device_utils::read(m_device, &block_number_buffer, direct_block_number_offset, + sizeof(uint32_t)); + return block_number_buffer; + } + inode_block_index -= number_of_singly_indirect_blocks; + + auto const number_of_doubly_indirect_blocks = numbers_per_block * numbers_per_block; + if (inode_block_index < number_of_doubly_indirect_blocks) + { + auto const doubly_indirect_block_start_offset = data.block.at(DOUBLY_INDIRECT_BLOCK_INDEX) * block_size; + auto const indirect_block_number_index = inode_block_index / numbers_per_block; + auto const indirect_block_number_offset = + doubly_indirect_block_start_offset + indirect_block_number_index * sizeof(uint32_t); + kernel::devices::block_device_utils::read(m_device, &block_number_buffer, indirect_block_number_offset, + sizeof(uint32_t)); + + auto const indirect_block_start_offset = block_number_buffer * block_size; + auto const direct_block_number_index = inode_block_index % numbers_per_block; + auto const direct_block_number_offset = + indirect_block_start_offset + direct_block_number_index * sizeof(uint32_t); + kernel::devices::block_device_utils::read(m_device, &block_number_buffer, direct_block_number_offset, + sizeof(uint32_t)); + return block_number_buffer; + } + inode_block_index -= number_of_doubly_indirect_blocks; + + auto const number_of_triply_indirect_blocks = numbers_per_block * numbers_per_block * numbers_per_block; + if (inode_block_index < number_of_triply_indirect_blocks) + { + auto const triply_indirect_block_start_offset = data.block.at(TRIPLY_INDIRECT_BLOCK_INDEX) * block_size; + auto const doubly_indirect_block_number_index = inode_block_index / (numbers_per_block * numbers_per_block); + auto const doubly_indirect_block_number_offset = + triply_indirect_block_start_offset + doubly_indirect_block_number_index * sizeof(uint32_t); + kernel::devices::block_device_utils::read(m_device, &block_number_buffer, doubly_indirect_block_number_offset, + sizeof(uint32_t)); + + auto const doubly_indirect_block_start_offset = block_number_buffer * block_size; + auto const indirect_block_number_index = (inode_block_index / numbers_per_block) % numbers_per_block; + auto const indirect_block_number_offset = + doubly_indirect_block_start_offset + indirect_block_number_index * sizeof(uint32_t); + kernel::devices::block_device_utils::read(m_device, &block_number_buffer, indirect_block_number_offset, + sizeof(uint32_t)); + + auto const indirect_block_start_offset = block_number_buffer * block_size; + auto const direct_block_number_index = inode_block_index % numbers_per_block; + auto const direct_block_number_offset = + indirect_block_start_offset + direct_block_number_index * sizeof(uint32_t); + kernel::devices::block_device_utils::read(m_device, &block_number_buffer, direct_block_number_offset, + sizeof(uint32_t)); + return block_number_buffer; + } + + return 0; // TODO BA-FS26 really correct?? + } + auto filesystem::get_block_size() -> size_t { return 1024U << m_superblock.log_block_size; @@ -173,4 +240,9 @@ namespace kernel::filesystem::ext2 { return m_superblock.rev_level == 0 ? 128 : m_superblock.inode_size; } + + auto filesystem::get_inode_block_count(inode_data const & data) -> uint32_t + { + return data.blocks / (2 << m_superblock.log_block_size); + } } // namespace kernel::filesystem::ext2 -- cgit v1.2.3 From fe8706422605e466427ae2727ddb98ce5cd984f6 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 3 Apr 2026 15:38:16 +0200 Subject: refactoring map_inode_block_index_to_global_block_number --- .../include/kernel/filesystem/ext2/filesystem.hpp | 1 + kernel/src/filesystem/ext2/filesystem.cpp | 101 ++++++++++----------- 2 files changed, 49 insertions(+), 53 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index dea059f..9761903 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -27,6 +27,7 @@ namespace kernel::filesystem::ext2 private: auto read_inode(uint32_t inode_number) -> kstd::shared_ptr; auto map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t; + auto read_block_number_at_index(uint32_t block_number, uint32_t index) -> uint32_t; auto get_block_size() -> size_t; auto get_inode_size() -> size_t; diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index a84414f..56c0b88 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -29,8 +29,8 @@ namespace kernel::filesystem::ext2 constexpr uint32_t ROOT_INODE_NUMBER = 2; constexpr size_t DIRECT_BLOCK_COUNT = 12; - constexpr size_t INDIRECT_BLOCK_INDEX = DIRECT_BLOCK_COUNT; - constexpr size_t DOUBLY_INDIRECT_BLOCK_INDEX = INDIRECT_BLOCK_INDEX + 1; + constexpr size_t SINGLY_INDIRECT_BLOCK_INDEX = DIRECT_BLOCK_COUNT; + constexpr size_t DOUBLY_INDIRECT_BLOCK_INDEX = SINGLY_INDIRECT_BLOCK_INDEX + 1; constexpr size_t TRIPLY_INDIRECT_BLOCK_INDEX = DOUBLY_INDIRECT_BLOCK_INDEX + 1; // Mode bits @@ -167,70 +167,65 @@ namespace kernel::filesystem::ext2 auto const block_size = get_block_size(); auto const numbers_per_block = block_size / sizeof(uint32_t); - uint32_t block_number_buffer = 0; - auto const number_of_singly_indirect_blocks = numbers_per_block; - if (inode_block_index < number_of_singly_indirect_blocks) + auto const block_numbers_per_singly_indirect_block = numbers_per_block; + auto const block_numbers_per_doubly_indirect_block = numbers_per_block * block_numbers_per_singly_indirect_block; + auto const block_numbers_per_triply_indirect_block = numbers_per_block * block_numbers_per_doubly_indirect_block; + + if (inode_block_index < block_numbers_per_singly_indirect_block) { - auto const indirect_block_start_offset = data.block.at(INDIRECT_BLOCK_INDEX) * block_size; - auto const direct_block_number_index = inode_block_index; - auto const direct_block_number_offset = - indirect_block_start_offset + direct_block_number_index * sizeof(uint32_t); - kernel::devices::block_device_utils::read(m_device, &block_number_buffer, direct_block_number_offset, - sizeof(uint32_t)); - return block_number_buffer; + auto const singly_indirect_block_number = data.block.at(SINGLY_INDIRECT_BLOCK_INDEX); + return read_block_number_at_index(singly_indirect_block_number, inode_block_index); } - inode_block_index -= number_of_singly_indirect_blocks; + inode_block_index -= block_numbers_per_singly_indirect_block; - auto const number_of_doubly_indirect_blocks = numbers_per_block * numbers_per_block; - if (inode_block_index < number_of_doubly_indirect_blocks) + if (inode_block_index < block_numbers_per_doubly_indirect_block) { - auto const doubly_indirect_block_start_offset = data.block.at(DOUBLY_INDIRECT_BLOCK_INDEX) * block_size; - auto const indirect_block_number_index = inode_block_index / numbers_per_block; - auto const indirect_block_number_offset = - doubly_indirect_block_start_offset + indirect_block_number_index * sizeof(uint32_t); - kernel::devices::block_device_utils::read(m_device, &block_number_buffer, indirect_block_number_offset, - sizeof(uint32_t)); - - auto const indirect_block_start_offset = block_number_buffer * block_size; - auto const direct_block_number_index = inode_block_index % numbers_per_block; - auto const direct_block_number_offset = - indirect_block_start_offset + direct_block_number_index * sizeof(uint32_t); - kernel::devices::block_device_utils::read(m_device, &block_number_buffer, direct_block_number_offset, - sizeof(uint32_t)); - return block_number_buffer; + auto const doubly_indirect_block_number = data.block.at(DOUBLY_INDIRECT_BLOCK_INDEX); + auto const singly_indirect_block_index_in_doubly_indirect_block = + inode_block_index / block_numbers_per_singly_indirect_block; + auto const singly_indirect_block_number = read_block_number_at_index( + doubly_indirect_block_number, singly_indirect_block_index_in_doubly_indirect_block); + + auto const block_index_in_singly_indirect_block = inode_block_index % block_numbers_per_singly_indirect_block; + return read_block_number_at_index(singly_indirect_block_number, block_index_in_singly_indirect_block); } - inode_block_index -= number_of_doubly_indirect_blocks; + inode_block_index -= block_numbers_per_doubly_indirect_block; - auto const number_of_triply_indirect_blocks = numbers_per_block * numbers_per_block * numbers_per_block; - if (inode_block_index < number_of_triply_indirect_blocks) + if (inode_block_index < block_numbers_per_triply_indirect_block) { - auto const triply_indirect_block_start_offset = data.block.at(TRIPLY_INDIRECT_BLOCK_INDEX) * block_size; - auto const doubly_indirect_block_number_index = inode_block_index / (numbers_per_block * numbers_per_block); - auto const doubly_indirect_block_number_offset = - triply_indirect_block_start_offset + doubly_indirect_block_number_index * sizeof(uint32_t); - kernel::devices::block_device_utils::read(m_device, &block_number_buffer, doubly_indirect_block_number_offset, - sizeof(uint32_t)); - - auto const doubly_indirect_block_start_offset = block_number_buffer * block_size; - auto const indirect_block_number_index = (inode_block_index / numbers_per_block) % numbers_per_block; - auto const indirect_block_number_offset = - doubly_indirect_block_start_offset + indirect_block_number_index * sizeof(uint32_t); - kernel::devices::block_device_utils::read(m_device, &block_number_buffer, indirect_block_number_offset, - sizeof(uint32_t)); - - auto const indirect_block_start_offset = block_number_buffer * block_size; - auto const direct_block_number_index = inode_block_index % numbers_per_block; - auto const direct_block_number_offset = - indirect_block_start_offset + direct_block_number_index * sizeof(uint32_t); - kernel::devices::block_device_utils::read(m_device, &block_number_buffer, direct_block_number_offset, - sizeof(uint32_t)); - return block_number_buffer; + auto const triply_indirect_block_number = data.block.at(TRIPLY_INDIRECT_BLOCK_INDEX); + auto const doubly_indirect_block_index_in_triply_indirect_block = + inode_block_index / block_numbers_per_doubly_indirect_block; + auto const doubly_indirect_block_number = read_block_number_at_index( + triply_indirect_block_number, doubly_indirect_block_index_in_triply_indirect_block); + + auto const remaining_block_numbers = inode_block_index % block_numbers_per_doubly_indirect_block; + + auto const singly_indirect_block_index_in_doubly_indirect_block = + remaining_block_numbers / block_numbers_per_singly_indirect_block; + auto const singly_indirect_block_number = read_block_number_at_index( + doubly_indirect_block_number, singly_indirect_block_index_in_doubly_indirect_block); + + auto const block_index_in_singly_indirect_block = + remaining_block_numbers % block_numbers_per_singly_indirect_block; + return read_block_number_at_index(singly_indirect_block_number, block_index_in_singly_indirect_block); } return 0; // TODO BA-FS26 really correct?? } + auto filesystem::read_block_number_at_index(uint32_t block_number, uint32_t index) -> uint32_t + { + uint32_t block_number_buffer = 0; + + auto const block_start_offset = block_number * get_block_size(); + auto const number_start_address = block_start_offset + index * sizeof(uint32_t); + kernel::devices::block_device_utils::read(m_device, &block_number_buffer, number_start_address, sizeof(uint32_t)); + + return block_number_buffer; + } + auto filesystem::get_block_size() -> size_t { return 1024U << m_superblock.log_block_size; -- cgit v1.2.3 From 725116d22e850c502e6cb8d42b100da1080dfec0 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 6 Apr 2026 10:35:45 +0200 Subject: Add file system pointer to ext2 inode --- kernel/include/kernel/filesystem/ext2/inode.hpp | 5 ++++- kernel/src/filesystem/ext2/filesystem.cpp | 2 +- kernel/src/filesystem/ext2/inode.cpp | 12 ++++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index 4284e6f..9318008 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -11,6 +11,8 @@ namespace kernel::filesystem::ext2 { + struct filesystem; + struct [[gnu::packed]] inode_data { uint16_t mode; @@ -35,12 +37,13 @@ namespace kernel::filesystem::ext2 struct inode : kernel::filesystem::inode { - inode(); + explicit inode(filesystem * fs); auto read(void * buffer, size_t offset, size_t size) const -> size_t override; auto write(void const * buffer, size_t offset, size_t size) -> size_t override; inode_data m_data{}; + filesystem * m_filesystem; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 56c0b88..514bb59 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -136,7 +136,7 @@ namespace kernel::filesystem::ext2 auto const inode_table_offset = static_cast(inode_table_start_block) * block_size; auto const inode_offset = inode_table_offset + inode_index_within_group * get_inode_size(); - auto new_inode = kstd::make_shared(); + auto new_inode = kstd::make_shared(this); kernel::devices::block_device_utils::read(m_device, &new_inode->m_data, inode_offset, sizeof(inode_data)); // TODO BA-FS26 improve inode_kind really needed? or just map it to the mode bits? diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index b75969a..4d36e66 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -1,14 +1,22 @@ #include "kernel/filesystem/ext2/inode.hpp" +#include "kapi/system.hpp" + #include "kernel/filesystem/inode.hpp" #include namespace kernel::filesystem::ext2 { - inode::inode() + inode::inode(filesystem * fs) : kernel::filesystem::inode(inode_kind::regular) - {} + , m_filesystem(fs) + { + if (!m_filesystem) + { + kapi::system::panic("[EXT2] ext2::inode constructed with filesystem null pointer"); + } + } auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t { -- cgit v1.2.3 From 3ab0e537368e57dc67b2f7f21aea06ee4001f7a3 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 6 Apr 2026 10:44:46 +0200 Subject: Clean up boot modules --- arch/x86_64/support/grub.cfg.in | 2 -- arch/x86_64/support/modules/test.img | Bin 2097152 -> 0 bytes 2 files changed, 2 deletions(-) delete mode 100644 arch/x86_64/support/modules/test.img diff --git a/arch/x86_64/support/grub.cfg.in b/arch/x86_64/support/grub.cfg.in index 834345a..49f19ce 100644 --- a/arch/x86_64/support/grub.cfg.in +++ b/arch/x86_64/support/grub.cfg.in @@ -3,7 +3,5 @@ default=0 menuentry "TeachOS" { multiboot2 /$ - module2 /modules/test.img bbbbbbb - module2 /modules/test.img aaaa boot } \ No newline at end of file diff --git a/arch/x86_64/support/modules/test.img b/arch/x86_64/support/modules/test.img deleted file mode 100644 index 914fa7f..0000000 Binary files a/arch/x86_64/support/modules/test.img and /dev/null differ -- cgit v1.2.3 From 4a2d4fb3ab38a64c4b10832f5a6318b7240829cc Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 6 Apr 2026 11:40:12 +0200 Subject: Implement read data in ext2 inode --- .../include/kernel/filesystem/ext2/filesystem.hpp | 5 ++-- kernel/include/kernel/filesystem/filesystem.hpp | 1 + kernel/src/filesystem/ext2/inode.cpp | 33 ++++++++++++++++++++-- kernel/src/filesystem/filesystem.cpp | 5 ++++ 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 9761903..762f590 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -24,12 +24,13 @@ namespace kernel::filesystem::ext2 auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; + auto get_block_size() -> size_t; + auto map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t; + private: auto read_inode(uint32_t inode_number) -> kstd::shared_ptr; - auto map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t; auto read_block_number_at_index(uint32_t block_number, uint32_t index) -> uint32_t; - auto get_block_size() -> size_t; auto get_inode_size() -> size_t; auto get_inode_block_count(inode_data const & data) -> uint32_t; diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 1c45377..0f9de9f 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -22,6 +22,7 @@ namespace kernel::filesystem virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr = 0; [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; + [[nodiscard]] auto device() const -> kstd::shared_ptr const &; protected: kstd::shared_ptr m_root_inode{}; diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index 4d36e66..7f4cf69 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -2,9 +2,13 @@ #include "kapi/system.hpp" +#include "kernel/devices/block_device_utils.hpp" +#include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/inode.hpp" +#include #include +#include namespace kernel::filesystem::ext2 { @@ -18,10 +22,33 @@ namespace kernel::filesystem::ext2 } } - auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t + auto inode::read(void * buffer, size_t offset, size_t size) const -> size_t { - // TODO BA-FS26 implement - return 0; + auto block_index = offset / m_filesystem->get_block_size(); + auto in_block_offset = offset % m_filesystem->get_block_size(); + + auto bytes_read = 0uz; + + while (bytes_read < size) + { + auto const block_number = m_filesystem->map_inode_block_index_to_global_block_number(block_index, m_data); + if (block_number == 0) // TODO BA-FS26 really correct? + { + break; + } + + auto const block_start_offset = block_number * m_filesystem->get_block_size(); + auto const read_offset = block_start_offset + in_block_offset; + auto const bytes_to_read = std::min(size - bytes_read, m_filesystem->get_block_size() - in_block_offset); + + bytes_read += kernel::devices::block_device_utils::read( + m_filesystem->device(), static_cast(buffer) + bytes_read, read_offset, bytes_to_read); + + block_index++; + in_block_offset = 0; // After the first block, we always start at the beginning of the block + } + + return bytes_read; } auto inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index a06eb80..26c57b6 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -49,4 +49,9 @@ namespace kernel::filesystem { return m_root_inode; } + + auto filesystem::device() const -> kstd::shared_ptr const & + { + return m_device; + } } // namespace kernel::filesystem \ No newline at end of file -- cgit v1.2.3 From bf90441a49d9fa2ab3a1c315679f97289cb33dbe Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 6 Apr 2026 11:41:23 +0200 Subject: Add test for reading file --- kernel/src/main.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 258c28b..186d32e 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -23,6 +23,7 @@ #include #include +#include using namespace kstd::units_literals; @@ -119,6 +120,14 @@ auto test_file_lookup() -> void auto ofd_hello = vfs.open("/hello.txt"); + kstd::vector buffer(64); + auto number_of_read_bytes = ofd_hello->read(buffer.data(), buffer.size()); + kstd::println("read bytes: {}", number_of_read_bytes); + kstd::println("buffer: {::#04x}", buffer); + + std::string_view hello_str{reinterpret_cast(buffer.data()), number_of_read_bytes}; + kstd::println("hello_str: {}", hello_str); + auto ofd1 = vfs.open("/a/b/c"); auto ofd2 = vfs.open("/dev/ram0"); auto ofd3 = vfs.open("/a/d/e"); -- cgit v1.2.3 From 2240b9a36e4a9f6f8291c9271e6aac8f5536dbd7 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 7 Apr 2026 22:13:49 +0200 Subject: refactoring --- kernel/include/kernel/filesystem/ext2/filesystem.hpp | 2 +- kernel/src/filesystem/device_inode.cpp | 4 +--- kernel/src/filesystem/ext2/filesystem.cpp | 2 -- kernel/src/filesystem/vfs.cpp | 1 + kernel/src/main.cpp | 2 -- 5 files changed, 3 insertions(+), 8 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 762f590..abab0a6 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -34,7 +34,7 @@ namespace kernel::filesystem::ext2 auto get_inode_size() -> size_t; auto get_inode_block_count(inode_data const & data) -> uint32_t; - superblock m_superblock; + superblock m_superblock; // TODO BA-FS26 initialize kstd::vector m_block_group_descriptors; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index af8cecc..397a0fd 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -1,14 +1,12 @@ #include "kernel/filesystem/device_inode.hpp" +#include "kapi/devices/device.hpp" #include "kapi/system.hpp" #include "kernel/devices/block_device_utils.hpp" -#include "kapi/devices/device.hpp" #include "kernel/filesystem/inode.hpp" -#include #include -#include #include diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 514bb59..d7c989c 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -11,8 +11,6 @@ #include "kernel/filesystem/inode.hpp" #include -#include -#include #include #include diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index d611fc9..f9a051a 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -74,6 +74,7 @@ namespace kernel::filesystem return nullptr; } + // TODO BA-FS26 implement unmount auto vfs::do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int { if (!filesystem) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 186d32e..a2c531f 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -88,8 +88,6 @@ auto test_file_description_manually() -> void auto test_device_with_vfs() -> void { - // TODO BA-FS26 - auto vfs = kernel::filesystem::vfs::get(); auto ofd = vfs.open("/dev/ram0"); if (!ofd) -- cgit v1.2.3 From dd330e7a05905713acfa87ec109956bfe78f78c4 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 09:31:32 +0200 Subject: add descriptions, some refactoring --- .../include/kernel/devices/block_device_utils.hpp | 29 +++++++++++++- kernel/include/kernel/filesystem/dentry.hpp | 46 ++++++++++++++++++++++ .../include/kernel/filesystem/devfs/filesystem.hpp | 18 +++++++++ kernel/include/kernel/filesystem/devfs/inode.hpp | 22 +++++++++++ kernel/include/kernel/filesystem/device_inode.hpp | 31 +++++++++++++++ .../filesystem/ext2/block_group_descriptor.hpp | 3 ++ .../include/kernel/filesystem/ext2/filesystem.hpp | 29 +++++++++++++- kernel/include/kernel/filesystem/ext2/inode.hpp | 27 +++++++++++++ .../filesystem/ext2/linked_directory_entry.hpp | 3 ++ .../include/kernel/filesystem/ext2/superblock.hpp | 3 ++ .../kernel/filesystem/file_descriptor_table.hpp | 34 ++++++++++++++++ kernel/include/kernel/filesystem/filesystem.hpp | 37 +++++++++++++++++ kernel/include/kernel/filesystem/inode.hpp | 44 +++++++++++++++++++++ kernel/include/kernel/filesystem/mount.hpp | 28 ++++++++++++- kernel/include/kernel/filesystem/mount_table.hpp | 16 +++++++- .../kernel/filesystem/open_file_description.hpp | 28 +++++++++++++ .../kernel/filesystem/rootfs/filesystem.hpp | 18 +++++++++ kernel/include/kernel/filesystem/rootfs/inode.hpp | 34 ++++++++++++++++ kernel/include/kernel/filesystem/vfs.hpp | 31 +++++++++++++++ kernel/src/filesystem/ext2/filesystem.cpp | 12 +++++- kernel/src/filesystem/ext2/inode.cpp | 2 +- kernel/src/filesystem/filesystem.cpp | 3 +- kernel/src/filesystem/mount.cpp | 2 +- kernel/src/filesystem/mount_table.cpp | 2 +- kernel/src/filesystem/vfs.cpp | 19 +++++---- 25 files changed, 500 insertions(+), 21 deletions(-) diff --git a/kernel/include/kernel/devices/block_device_utils.hpp b/kernel/include/kernel/devices/block_device_utils.hpp index 5e862ba..7b1daec 100644 --- a/kernel/include/kernel/devices/block_device_utils.hpp +++ b/kernel/include/kernel/devices/block_device_utils.hpp @@ -9,7 +9,34 @@ namespace kernel::devices::block_device_utils { - auto read(kstd::shared_ptr const & device, void * buffer, size_t offset, size_t size) -> size_t; + /** + @brief Utility functions for block devices, such as reading/writing data at specific offsets. These functions handle + the necessary logic to interact with block devices, such as calculating block boundaries and ensuring proper access + patterns. They abstract away the details of block device interactions, providing a simple interface for reading and + writing data to block devices. + */ + + /** + @brief Reads data from a @p device into a @p buffer, starting at a specific @p offset and for a given @p size. + @warning Panics if @p buffer or @p device is null. + @param device The block device to read from. + @param buffer The buffer to read data into. + @param offset The offset on the block device to start reading from. + @param size The number of bytes to read. + @return The number of bytes actually read, which may be less than the requested size. + */ + auto read(kstd::shared_ptr const & device, void * buffer, size_t offset, size_t size) + -> size_t; + + /** + @brief Writes data from a @p buffer to a @p device, starting at a specific @p offset and for a given @p size. + @warning Panics if @p buffer or @p device is null. + @param device The block device to write to. + @param buffer The buffer to write data from. + @param offset The offset on the block device to start writing to. + @param size The number of bytes to write. + @return The number of bytes actually written, which may be less than the requested size. + */ auto write(kstd::shared_ptr const & device, void const * buffer, size_t offset, size_t size) -> size_t; } // namespace kernel::devices::block_device_utils diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index fc85a7d..72d758f 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -12,23 +12,69 @@ namespace kernel::filesystem { + /** + @brief Represents a directory entry (dentry) in the filesystem. A dentry is a node in the directory tree that + represents a file or directory. It contains a reference to its parent dentry, a reference to the associated real + filesystem inode, and a list of child dentries. + */ struct dentry { + /** + @brief Flags for the dentry. + */ enum class dentry_flags : uint32_t { dcache_mounted = 1 << 15 }; + /** + @brief Create a dentry with the given @p parent, associated @p node, and optional @p name. The dentry is initialized + with the provided parent and inode, and the name is stored for lookup purposes. + */ dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node, std::string_view name = {}); + /** + @brief Get the associated inode. + @return A reference to the associated inode. + */ [[nodiscard]] auto get_inode() const -> kstd::shared_ptr const &; + + /** + @brief Get the parent dentry. + @return A reference to the parent dentry. + */ [[nodiscard]] auto get_parent() const -> kstd::shared_ptr const &; + /** + @brief Add a @p child dentry. + @param child The child dentry to add. + */ auto add_child(kstd::shared_ptr const & child) -> void; + + /** + @brief Find a child dentry by @p name. + @param name The name of the child dentry to find. + @return A pointer to the found child dentry, or a null pointer if not found. + */ [[nodiscard]] auto find_child(std::string_view name) const -> kstd::shared_ptr; + /** + @brief Set a @p flag for the dentry. + @param flag The flag to set. + */ auto set_flag(dentry_flags flag) -> void; + + /** + @brief Unset a @p flag for the dentry. + @param flag The flag to unset. + */ auto unset_flag(dentry_flags flag) -> void; + + /** + @brief Check if the dentry has a specific @p flag. + @param flag The flag to check. + @return True if the dentry has the flag, false otherwise. + */ [[nodiscard]] auto has_flag(dentry_flags flag) const -> bool; private: diff --git a/kernel/include/kernel/filesystem/devfs/filesystem.hpp b/kernel/include/kernel/filesystem/devfs/filesystem.hpp index 3edeabb..60c39cf 100644 --- a/kernel/include/kernel/filesystem/devfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/devfs/filesystem.hpp @@ -13,9 +13,27 @@ namespace kernel::filesystem::devfs { + /** + @brief A filesystem for managing device nodes in the virtual filesystem. This filesystem provides a way to represent + devices as files in the /dev directory, allowing user-space applications to interact with devices using standard file + operations. The devfs filesystem dynamically creates inodes for devices registered in the system, enabling seamless + access to device functionality through the filesystem interface. + */ struct filesystem : kernel::filesystem::filesystem { + /** + @brief Initializes the devfs instance and builds the device inode table. + @param device Backing device passed by the generic filesystem interface (not required by devfs). + @return 0 on success, -1 on failure. + */ auto mount(kstd::shared_ptr const & device) -> int override; + + /** + @brief Looks up an inode by @p name within a @p parent directory. + @param parent The parent directory inode. + @param name The name of the inode to look up. + @return A pointer to the found inode, or a null pointer if not found. + */ auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; diff --git a/kernel/include/kernel/filesystem/devfs/inode.hpp b/kernel/include/kernel/filesystem/devfs/inode.hpp index 9c11edf..c117bd2 100644 --- a/kernel/include/kernel/filesystem/devfs/inode.hpp +++ b/kernel/include/kernel/filesystem/devfs/inode.hpp @@ -7,11 +7,33 @@ namespace kernel::filesystem::devfs { + /** + @brief Inode implementation for the devfs filesystem. + This inode represents root device node in the /dev directory. + */ struct inode : kernel::filesystem::inode { + /** + @brief Create a devfs inode. The inode is initialized with the appropriate kind (directory). + */ inode(); + /** + @brief Reads from the devfs directory inode. + @param buffer Destination buffer. + @param offset Read offset in bytes. + @param size Number of bytes requested. + @return Number of bytes read (always 0 because this inode does not expose file data). + */ auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + + /** + @brief Writes to the devfs directory inode. + @param buffer Source buffer. + @param offset Write offset in bytes. + @param size Number of bytes requested. + @return Number of bytes written (always 0 because writes are not supported for this inode). + */ auto write(void const * buffer, size_t offset, size_t size) -> size_t override; }; } // namespace kernel::filesystem::devfs diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index 18a98f5..c33be2a 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -2,6 +2,7 @@ #define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP #include "kapi/devices/device.hpp" + #include "kernel/filesystem/inode.hpp" #include @@ -10,13 +11,43 @@ namespace kernel::filesystem { + /** + @brief Inode implementation for device inodes in the filesystem. This inode represents a device file that provides + access to a device registered in the system. The device inode allows reading from and writing to the associated + device. + */ struct device_inode : inode { + /** + @brief Create a device inode with the given @p device. + @param device The device to associate with the inode. + */ explicit device_inode(kstd::shared_ptr const & device); + /** + @brief Read data from the device inode (and in the background from the associated device) into a @p buffer, starting + at @p offset and reading @p size bytes. + @param buffer The buffer to read data into. + @param offset The offset to read from. + @param size The number of bytes to read. + @return The number of bytes read. + */ auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + + /** + @brief Write data to the device inode (and in the background from the associated device) from a @p buffer, starting + at @p offset and writing @p size bytes. + @param buffer The buffer containing data to write. + @param offset The offset to write to. + @param size The number of bytes to write. + @return The number of bytes written. + */ auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + /** + @brief Get the associated device. + @return A reference to the associated device. + */ [[nodiscard]] auto device() const -> kstd::shared_ptr const &; private: diff --git a/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp b/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp index 2ff91d9..7fbba3f 100644 --- a/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp +++ b/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp @@ -6,6 +6,9 @@ namespace kernel::filesystem::ext2 { + /** + @brief Represents a block group descriptor in the ext2 filesystem. + */ struct [[gnu::packed]] block_group_descriptor { uint32_t block_bitmap; diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index abab0a6..32374dc 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -18,13 +18,40 @@ namespace kernel::filesystem::ext2 { + /** + @brief A filesystem implementation for the ext2 filesystem format. This class provides methods for mounting an ext2 + filesystem, and looking up inodes. + */ struct filesystem : kernel::filesystem::filesystem { + /** + @brief Initializes the ext2 filesystem with the given @p device. + @param device The device to mount. + @return 0 on success, negative error code on failure. + */ auto mount(kstd::shared_ptr const & device) -> int override; + + /** + @brief Looks up an inode by @p name within a @p parent directory. + @param parent The parent directory inode. + @param name The name of the inode to look up. + @return A pointer to the found inode, or a null pointer if not found. + */ auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; + /** + @brief Gets the size of a block in the filesystem. + @return The size of a block in bytes. + */ auto get_block_size() -> size_t; + + /** + @brief Maps an inode block index to a global block number. + @param inode_block_index The index of the block within the inode. + @param data The inode data. + @return The global block number. + */ auto map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t; private: @@ -34,7 +61,7 @@ namespace kernel::filesystem::ext2 auto get_inode_size() -> size_t; auto get_inode_block_count(inode_data const & data) -> uint32_t; - superblock m_superblock; // TODO BA-FS26 initialize + superblock m_superblock{}; kstd::vector m_block_group_descriptors; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index 9318008..a1645cd 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -13,6 +13,9 @@ namespace kernel::filesystem::ext2 { struct filesystem; + /** + @brief Represents the data associated with an ext2 inode. + */ struct [[gnu::packed]] inode_data { uint16_t mode; @@ -37,12 +40,36 @@ namespace kernel::filesystem::ext2 struct inode : kernel::filesystem::inode { + /** + @brief Create an ext2 inode associated with the given filesystem. + */ explicit inode(filesystem * fs); + /** + @brief Reads from the ext2 inode into a @p buffer, starting at the specified @p offset and for a given @p size. + @param buffer Destination buffer. + @param offset Read offset in bytes. + @param size Number of bytes requested. + @return Number of bytes read. + */ auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + + /** + @brief Writes to the ext2 inode into a @p buffer, starting at the specified @p offset and for a given @p size. + @warning This method is not implemented yet and will panic if called. + @param buffer Source buffer. + @param offset Write offset in bytes. + @param size Number of bytes requested. + @return Number of bytes written. + */ auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + /** + @brief The raw inode data as read from the disk. + */ inode_data m_data{}; + + private: filesystem * m_filesystem; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp index 76eb6f5..4097cbb 100644 --- a/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp +++ b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp @@ -6,6 +6,9 @@ namespace kernel::filesystem::ext2 { + /** + @brief Represents a linked directory entry in the ext2 filesystem. + */ struct [[gnu::packed]] linked_directory_entry { uint32_t inode; diff --git a/kernel/include/kernel/filesystem/ext2/superblock.hpp b/kernel/include/kernel/filesystem/ext2/superblock.hpp index 8e57ae7..41ad935 100644 --- a/kernel/include/kernel/filesystem/ext2/superblock.hpp +++ b/kernel/include/kernel/filesystem/ext2/superblock.hpp @@ -6,6 +6,9 @@ namespace kernel::filesystem::ext2 { + /** + @brief Represents the superblock in the ext2 filesystem. + */ struct [[gnu::packed]] superblock { uint32_t inodes_count; diff --git a/kernel/include/kernel/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/filesystem/file_descriptor_table.hpp index 91e2960..5d52c19 100644 --- a/kernel/include/kernel/filesystem/file_descriptor_table.hpp +++ b/kernel/include/kernel/filesystem/file_descriptor_table.hpp @@ -8,15 +8,49 @@ namespace kernel::filesystem { + /** + @brief A table for managing file descriptors in the filesystem. This class provides methods for adding, retrieving, + and removing open file descriptions. + */ struct file_descriptor_table { + /** + @brief Initialize the global file descriptor table. This method creates the singleton instance of the file + descriptor table. + @warning Panics if called more than once. + */ auto static init() -> void; + + /** + @brief Get the global file descriptor table instance. + @return A reference to the global file descriptor table. + @warning Panics if the file descriptor table has not been initialized. + */ auto static get() -> file_descriptor_table &; + /** + @brief Destructor for the file descriptor table. + */ ~file_descriptor_table() = default; + /** + @brief Add a file to the descriptor table. + @param f The file description to add. + @return The file descriptor index assigned to the file, or -1 on failure. + */ auto add_file(kstd::shared_ptr const & f) -> int; + + /** + @brief Get a file from the descriptor table. + @param fd The file descriptor index to retrieve. + @return A pointer to the requested file description, or a null pointer if not found. + */ [[nodiscard]] auto get_file(int fd) const -> kstd::shared_ptr; + + /** + @brief Remove a file from the descriptor table. + @param fd The file descriptor index to remove. + */ auto remove_file(int fd) -> void; private: diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 0f9de9f..f855380 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -12,16 +12,53 @@ namespace kernel::filesystem { + /** + @brief A base class for implementing filesystems in the kernel. This class provides a common interface for managing + files and directories within the virtual filesystem. + */ struct filesystem { + /** + @brief Virtual destructor for the filesystem. + */ virtual ~filesystem() = default; + /** + @brief Probes the given @p device to determine if it contains a recognizable filesystem, and if so, mounts it and + returns a pointer to the mounted filesystem instance. This method iterates through known filesystem types and + attempts to initialize it with the device until the mount was successful or all types have been tried. + @param device The device to probe and mount. + @return A pointer to the mounted filesystem instance if successful, or a null pointer if no recognizable filesystem + is found on the device. + @warning Panics if @p device is null. + */ auto static probe_and_mount(kstd::shared_ptr const & device) -> kstd::shared_ptr; + /** + @brief Initializes the filesystem with the given @p device. + @param device The device to mount. + @return 0 on success, or a negative error code on failure. + */ virtual auto mount(kstd::shared_ptr const & device) -> int; + + /** + @brief Looks up a child inode within the given @p parent inode with the specified @p name. This method must be + implemented by concrete filesystem subclasses to provide the logic for traversing the filesystem structure and + finding the requested inode. + @param parent The parent inode. + @param name The name of the child inode to look up. + @return A pointer to the requested child inode, or a null pointer if not found. + */ virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr = 0; + /** + @brief Returns a reference to the root inode of the filesystem. + */ [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; + + /** + @brief Returns a reference to the device associated with the filesystem. + */ [[nodiscard]] auto device() const -> kstd::shared_ptr const &; protected: diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index 59207df..7237184 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -5,8 +5,14 @@ namespace kernel::filesystem { + /** + @brief Represents an inode in the filesystem. + */ struct inode { + /** + @brief Represents the kind of an inode. + */ enum class inode_kind { regular, @@ -14,15 +20,53 @@ namespace kernel::filesystem device }; + /** + @brief Create an inode with the given @p kind. + @param kind The kind of the inode. + */ explicit inode(inode_kind kind); + /** + @brief Virtual destructor for the inode. + */ virtual ~inode() = default; + /** + @brief Reads from the inode into a @p buffer, starting at the specified @p offset and for a given @p size. This + method must be implemented by concrete inode subclasses. + @param buffer Destination buffer. + @param offset Read offset in bytes. + @param size Number of bytes requested. + @return Number of bytes read. + */ virtual auto read(void * buffer, size_t offset, size_t size) const -> size_t = 0; + + /** + @brief Writes to the inode into a @p buffer, starting at the specified @p offset and for a given @p size. This + method must be implemented by concrete inode subclasses. + @param buffer Source buffer. + @param offset Write offset in bytes. + @param size Number of bytes to write. + @return Number of bytes written. + */ virtual auto write(void const * buffer, size_t offset, size_t size) -> size_t = 0; + /** + @brief Returns whether the inode is a directory. + @return true if the inode is a directory, false otherwise. + */ [[nodiscard]] auto is_directory() const -> bool; + + /** + @brief Returns whether the inode is a regular file. + @return true if the inode is a regular file, false otherwise. + */ [[nodiscard]] auto is_regular() const -> bool; + + /** + @brief Returns whether the inode is a device. + @return true if the inode is a device, false otherwise. + */ [[nodiscard]] auto is_device() const -> bool; // TODO BA-FS26 improve inode_kind really needed? diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index a054750..3e3c69f 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -11,15 +11,41 @@ namespace kernel::filesystem { + /** + @brief Represents a mounted filesystem in the kernel. + */ struct mount { + /** + @brief Creates a mount for the given @p filesystem at the specified @p mount_path. The @p mount_dentry represents + the dentry where the filesystem is mounted, and the @p root_dentry represents the root dentry of the mounted + filesystem. + @param mount_dentry The dentry where the filesystem is mounted. + @param root_dentry The root dentry of the mounted filesystem. + @param fs The filesystem instance being mounted. + @param mount_path The path at which the filesystem is mounted. + */ mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, kstd::shared_ptr const & fs, std::string_view mount_path); - [[nodiscard]] auto get_mount_dentry() const -> kstd::shared_ptr; + /** + @brief Get the dentry where the filesystem is mounted. + */ + [[nodiscard]] auto get_mount_dentry() const -> kstd::shared_ptr const &; + + /** + @brief Get the root dentry of the mounted filesystem. + */ [[nodiscard]] auto root_dentry() const -> kstd::shared_ptr const &; + /** + @brief Get the filesystem instance being mounted. + */ [[nodiscard]] auto get_filesystem() const -> kstd::shared_ptr const &; + + /** + @brief Get the path at which the filesystem is mounted. + */ [[nodiscard]] auto get_mount_path() const -> std::string_view; private: diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp index 6dc2218..a8ef59e 100644 --- a/kernel/include/kernel/filesystem/mount_table.hpp +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -10,11 +10,23 @@ namespace kernel::filesystem { + /** + @brief A table for managing mounted filesystems in the kernel. + */ struct mount_table { - public: - void add_mount(kstd::shared_ptr); + /** + @brief Adds a mount to the table. + @param mount The mount to add. + */ + void add_mount(kstd::shared_ptr const & mount); + /** + @brief Finds the mount with the longest prefix matching the given @p path. This method is used to determine which + mounted filesystem should handle a given path lookup. + @param path The path to match against the mount paths in the table. + @return A pointer to the mount with the longest matching prefix, or a null pointer if no mount matches the path. + */ [[nodiscard]] auto find_longest_prefix_mount(std::string_view path) const -> kstd::shared_ptr; private: diff --git a/kernel/include/kernel/filesystem/open_file_description.hpp b/kernel/include/kernel/filesystem/open_file_description.hpp index 45719cf..ed878a7 100644 --- a/kernel/include/kernel/filesystem/open_file_description.hpp +++ b/kernel/include/kernel/filesystem/open_file_description.hpp @@ -9,13 +9,41 @@ namespace kernel::filesystem { + /** + @brief Represents an open file description in the filesystem. This class encapsulates the state of an open file, + including a reference to the associated inode and the current file offset. + */ struct open_file_description { + /** + @brief Constructs an open file description for the given @p inode. + @param inode The inode to associate with the open file description. + */ explicit open_file_description(kstd::shared_ptr const & inode); + /** + @brief Destructor for the open file description. + */ ~open_file_description() = default; + /** + @brief Reads data from the open file description into a @p buffer, starting at the current file offset and for a + given + @p size. The file offset is advanced by the number of bytes read. + @param buffer The buffer to read data into. + @param size The number of bytes to read. + @return The number of bytes read. + */ auto read(void * buffer, size_t size) -> size_t; + + /** + @brief Writes data to the open file description from a @p buffer, starting at the current file offset and for a + given + @p size. The file offset is advanced by the number of bytes written. + @param buffer The buffer to write data from. + @param size The number of bytes to write. + @return The number of bytes written. + */ auto write(void const * buffer, size_t size) -> size_t; private: diff --git a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp index 7931d87..b7e7c6f 100644 --- a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp @@ -14,9 +14,27 @@ namespace kernel::filesystem::rootfs { + /** + @brief A filesystem for the root filesystem. This filesystem provides access to the root directory and its contents, + which are typically populated by the init process during system startup. The rootfs filesystem serves as the top-level + directory in the filesystem hierarchy. It is responsible for providing a stable and consistent interface to the root + directory. + */ struct filesystem : kernel::filesystem::filesystem { + /** + @brief Initializes the rootfs filesystem with the given @p device. + @param device The device to mount (not required by rootfs). + @return 0 on success, negative error code on failure. + */ auto mount(kstd::shared_ptr const & device) -> int override; + + /** + @brief Looks up an inode by @p name within a @p parent directory. + @param parent The parent directory inode. + @param name The name of the inode to look up. + @return A pointer to the found inode, or a null pointer if not found. + */ auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; }; diff --git a/kernel/include/kernel/filesystem/rootfs/inode.hpp b/kernel/include/kernel/filesystem/rootfs/inode.hpp index 24d3e6b..469e47a 100644 --- a/kernel/include/kernel/filesystem/rootfs/inode.hpp +++ b/kernel/include/kernel/filesystem/rootfs/inode.hpp @@ -13,14 +13,48 @@ namespace kernel::filesystem::rootfs { + /** + @brief Represents an inode in the rootfs filesystem. This inode represents a directory in the root filesystem and + maintains a list of child inodes corresponding to files and subdirectories within the root directory. The rootfs inode + provides methods for reading and writing data (which are no-ops for the root directory), as well as adding and looking + up child inodes by name. + */ struct inode : kernel::filesystem::inode { + /** + @brief Create a rootfs inode. The inode is initialized with the appropriate kind (directory). + */ inode(); + /** + @brief Reads from the rootfs directory inode. + @param buffer Destination buffer. + @param offset Read offset in bytes. + @param size Number of bytes requested. + @return Number of bytes read (always 0 because this inode does not expose file data). + */ auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + + /** + @brief Writes to the rootfs directory inode. + @param buffer Source buffer. + @param offset Write offset in bytes. + @param size Number of bytes requested. + @return Number of bytes written (always 0 because writes are not supported for this inode). + */ auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + /** + @brief Adds a child inode to the rootfs directory inode with the specified @p name. + @param name The name of the child inode. + */ auto add_child(std::string_view name) -> void; + + /** + @brief Looks up a child inode by @p name. + @param name The name of the child inode to look up. + @return A pointer to the found child inode, or a null pointer if not found. + */ auto lookup_child(std::string_view name) -> kstd::shared_ptr; private: diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 5823a83..2d05765 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -12,14 +12,45 @@ namespace kernel::filesystem { + /** + @brief The virtual filesystem (VFS) is responsible for managing mounted filesystems and providing a unified interface + for file operations across different filesystem types. The VFS maintains a mount table to keep track of mounted + filesystems and their associated mount points. It provides methods for opening files by path, which involvesresolving + the path to the appropriate mounted filesystem and delegating the file operation to that filesystem's implementation. + */ struct vfs { + /** + @brief Initialize the virtual filesystem. + @warning Panics if the VFS has already been initialized. + */ auto static init() -> void; + + /** + @brief Get the singleton instance of the virtual filesystem. + @return A reference to the VFS instance. + @warning Panics if the VFS has not been initialized yet. + */ auto static get() -> vfs &; + /** + @brief Destructor for the VFS. + */ ~vfs() = default; + /** + @brief Open a file by its @p path. This method resolves the path and creates an open file description. + @param path The path to the file to open. + @return A shared pointer to the open file description or a null pointer if the file could not be opened. + */ auto open(std::string_view path) -> kstd::shared_ptr; + + /** + @brief Mount a @p filesystem at a specific @p path. + @param path The path where the filesystem should be mounted. + @param filesystem The filesystem to mount. + @return 0 on success, or a negative error code on failure. + */ auto do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int; private: diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index d7c989c..7ee1072 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -36,6 +36,10 @@ namespace kernel::filesystem::ext2 constexpr uint16_t S_IFREG = 0x8000; constexpr uint16_t S_IFDIR = 0x4000; + // Error codes + constexpr int INVALID_MAGIC_NUMBER = -1; + constexpr int INVALID_ROOT_INODE = -2; + auto S_ISREG(uint16_t mode) -> bool { return (mode & S_IFMT) == S_IFREG; @@ -55,7 +59,7 @@ namespace kernel::filesystem::ext2 if (m_superblock.magic != MAGIC_NUMBER) { - return -1; + return INVALID_MAGIC_NUMBER; } auto const block_size = get_block_size(); @@ -70,7 +74,11 @@ namespace kernel::filesystem::ext2 num_block_groups * sizeof(block_group_descriptor)); m_root_inode = read_inode(ROOT_INODE_NUMBER); - // TODO BA-FS26 check if root inode is valid and is a directory ?? + + if (!m_root_inode || !m_root_inode->is_directory()) + { + return INVALID_ROOT_INODE; + } return 0; } diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index 7f4cf69..a29bb3b 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -53,7 +53,7 @@ namespace kernel::filesystem::ext2 auto inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t { - // TODO BA-FS26 implement + kapi::system::panic("[EXT2] inode::write is not implemented yet"); return 0; } } // namespace kernel::filesystem::ext2 \ No newline at end of file diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index 26c57b6..b08b520 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -36,11 +36,12 @@ namespace kernel::filesystem } } - kapi::system::panic("[FILESYSTEM] cannot mount filesystem: no suitable filesystem found on device."); + return nullptr; } auto filesystem::mount(kstd::shared_ptr const & device) -> int { + // TODO BA-FS26 maybe check if device is null and panic here already? m_device = device; return 0; } diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index f9e709c..a6d2f7e 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -25,7 +25,7 @@ namespace kernel::filesystem } } - auto mount::get_mount_dentry() const -> kstd::shared_ptr + auto mount::get_mount_dentry() const -> kstd::shared_ptr const & { return m_mount_dentry; } diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 737434e..195f48a 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -9,7 +9,7 @@ namespace kernel::filesystem { - void mount_table::add_mount(kstd::shared_ptr mount) + void mount_table::add_mount(kstd::shared_ptr const & mount) { m_mounts.push_back(mount); } diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index f9a051a..2578036 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -21,6 +21,11 @@ namespace kernel::filesystem namespace { constinit auto static active_vfs = std::optional{}; + + // Error codes + constexpr int INVALID_PATH = -1; + constexpr int MOUNT_POINT_NOT_FOUND = -2; + constexpr int FILESYSTEM_NULL = -3; } // namespace auto vfs::init() -> void @@ -79,18 +84,12 @@ namespace kernel::filesystem { if (!filesystem) { - return -1; // TODO BA-FS26 panic or errorcode? - } - - if (path.empty() || path.front() != '/') - { - return -1; // TODO BA-FS26 panic or errorcode? + return FILESYSTEM_NULL; } - // TODO BA-FS26 better path validation - if ((path.size() > 1 && path.back() == '/')) + if (path.empty() || path.front() != '/' || (path.size() > 1 && path.back() == '/')) { - return -1; // TODO BA-FS26 panic or errorcode? + return INVALID_PATH; } if (auto mount_point_dentry = resolve_path(path)) @@ -99,7 +98,7 @@ namespace kernel::filesystem return 0; } - return -1; + return MOUNT_POINT_NOT_FOUND; } auto vfs::do_mount_internal(std::string_view path, kstd::shared_ptr const & mount_point_dentry, -- cgit v1.2.3 From 9330cc6345e0eed83e16f8af5aae54484c059177 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 09:42:01 +0200 Subject: fix build when modules folder is missing --- cmake/Modules/GenerateBootableIso.cmake | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmake/Modules/GenerateBootableIso.cmake b/cmake/Modules/GenerateBootableIso.cmake index b798787..39a0ebd 100644 --- a/cmake/Modules/GenerateBootableIso.cmake +++ b/cmake/Modules/GenerateBootableIso.cmake @@ -3,6 +3,14 @@ include_guard(GLOBAL) function(target_generate_bootable_iso TARGET) find_package("grub-mkrescue") + set(MODULES_DIR "${PROJECT_SOURCE_DIR}/arch/${CMAKE_SYSTEM_PROCESSOR}/support/modules") + set(COPY_MODULES_COMMAND) + if(EXISTS "${MODULES_DIR}") + set(COPY_MODULES_COMMAND + COMMAND "${CMAKE_COMMAND}" -E copy_directory "${MODULES_DIR}" "$/isofs/modules" + ) + endif() + file(GENERATE OUTPUT "$/isofs/boot/grub/grub.cfg" INPUT "${PROJECT_SOURCE_DIR}/arch/${CMAKE_SYSTEM_PROCESSOR}/support/grub.cfg.in" @@ -11,7 +19,7 @@ function(target_generate_bootable_iso TARGET) add_custom_command(TARGET "${TARGET}" POST_BUILD COMMAND "${CMAKE_COMMAND}" -E make_directory "$/isofs" - COMMAND "${CMAKE_COMMAND}" -E copy_directory "${PROJECT_SOURCE_DIR}/arch/${CMAKE_SYSTEM_PROCESSOR}/support/modules" "$/isofs/modules" + ${COPY_MODULES_COMMAND} COMMAND "${GRUB_MKRESCUE_EXE}" "-o" "$/${TARGET}.iso" -- cgit v1.2.3 From 9c0fb15aa67a4dda6beed3cbdfc4cc510674313f Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 14:13:19 +0200 Subject: add test with multiple correct formatted ext2 file systems increase QEMU memory --- kernel/src/main.cpp | 69 +++++++++++++++++++++---------------------------- scripts/qemu-wrapper.sh | 2 +- 2 files changed, 30 insertions(+), 41 deletions(-) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index a2c531f..1893b84 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -111,51 +111,40 @@ auto test_device_with_vfs() -> void auto test_file_lookup() -> void { - // TODO BA-FS26 implement a more complete test with multiple files and directories and mounts etc. - auto vfs = kernel::filesystem::vfs::get(); - auto storage_mgmt = kernel::devices::storage::management::get(); - - auto ofd_hello = vfs.open("/hello.txt"); - - kstd::vector buffer(64); - auto number_of_read_bytes = ofd_hello->read(buffer.data(), buffer.size()); - kstd::println("read bytes: {}", number_of_read_bytes); - kstd::println("buffer: {::#04x}", buffer); - - std::string_view hello_str{reinterpret_cast(buffer.data()), number_of_read_bytes}; - kstd::println("hello_str: {}", hello_str); - - auto ofd1 = vfs.open("/a/b/c"); - auto ofd2 = vfs.open("/dev/ram0"); - auto ofd3 = vfs.open("/a/d/e"); - if (!ofd1 || !ofd2 || !ofd3) - { - kstd::os::panic("test code failed"); - } - - if (auto ofd4 = vfs.open("/dev/xxx")) - { - kstd::os::panic("test code failed"); - } + auto read_and_write_file = [&vfs](std::string_view path) { + kstd::println("[TEST] Reading and writing file at path: {}", path); + auto ofd = vfs.open(path); + if (!ofd) + { + kstd::os::panic("test code failed"); + } + + kstd::vector buffer{32}; + auto number_of_read_bytes = ofd->read(buffer.data(), buffer.size()); + kstd::println("read bytes: {}", number_of_read_bytes); + kstd::println("buffer: {::#04x}", buffer); + + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), number_of_read_bytes}; + kstd::println("buffer_as_str: {}", buffer_as_str); + }; + + read_and_write_file("/info.txt"); + read_and_write_file("/enclosures/info.txt"); + read_and_write_file("/enclosures/aquarium/tank_1/fish_4.txt"); + read_and_write_file("/enclosures/elephant_house/elephant_1.txt"); + read_and_write_file( + "/enclosures/aquarium/tank_2/" + "this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_" + "limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_30.txt"); + auto storage_mgmt = kernel::devices::storage::management::get(); auto device = storage_mgmt.device_by_major_minor(1, 16); auto new_filesystem = kernel::filesystem::filesystem::probe_and_mount(device); - if (vfs.do_mount("/a/b", new_filesystem) != 0) - { - kstd::os::panic("test code failed"); - } - auto ofd5 = vfs.open("/a/b/c"); - if (!ofd5) - { - kstd::os::panic("test code failed"); - } - - if (auto ofd6 = vfs.open("x/y/z")) - { - kstd::os::panic("test code failed"); - } + vfs.do_mount("/enclosures/aquarium", new_filesystem); + read_and_write_file("/enclosures/aquarium/closed.txt"); + read_and_write_file("/enclosures/aquarium/information/info_2.txt"); } auto run_test_code() -> void diff --git a/scripts/qemu-wrapper.sh b/scripts/qemu-wrapper.sh index 49c01ec..dd0200d 100755 --- a/scripts/qemu-wrapper.sh +++ b/scripts/qemu-wrapper.sh @@ -12,7 +12,7 @@ if [ -z "$ISO_PATH" ]; then fi ARGS=( - "-m" "32M" + "-m" "64M" "-machine" "q35" "-smp" "4,sockets=1,cores=4,threads=1" "-display" "curses" -- cgit v1.2.3 From 2793770dc6eba30b73b4a4993618d2cbe184790e Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 15:21:10 +0200 Subject: implement unmount, improve error codes --- .../include/kernel/filesystem/devfs/filesystem.hpp | 4 +- .../include/kernel/filesystem/ext2/filesystem.hpp | 4 +- kernel/include/kernel/filesystem/filesystem.hpp | 12 +++- kernel/include/kernel/filesystem/mount.hpp | 10 +++- kernel/include/kernel/filesystem/mount_table.hpp | 21 ++++++- .../kernel/filesystem/rootfs/filesystem.hpp | 4 +- kernel/include/kernel/filesystem/vfs.hpp | 23 +++++++- kernel/src/filesystem/devfs/filesystem.cpp | 5 +- kernel/src/filesystem/ext2/filesystem.cpp | 12 ++-- kernel/src/filesystem/filesystem.cpp | 6 +- kernel/src/filesystem/mount.cpp | 9 ++- kernel/src/filesystem/mount_table.cpp | 69 +++++++++++++++++++++- kernel/src/filesystem/rootfs/filesystem.cpp | 4 +- kernel/src/filesystem/vfs.cpp | 45 +++++++++----- kernel/src/main.cpp | 3 + 15 files changed, 187 insertions(+), 44 deletions(-) diff --git a/kernel/include/kernel/filesystem/devfs/filesystem.hpp b/kernel/include/kernel/filesystem/devfs/filesystem.hpp index 60c39cf..137eca3 100644 --- a/kernel/include/kernel/filesystem/devfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/devfs/filesystem.hpp @@ -24,9 +24,9 @@ namespace kernel::filesystem::devfs /** @brief Initializes the devfs instance and builds the device inode table. @param device Backing device passed by the generic filesystem interface (not required by devfs). - @return 0 on success, -1 on failure. + @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & device) -> int override; + auto mount(kstd::shared_ptr const & device) -> operation_result override; /** @brief Looks up an inode by @p name within a @p parent directory. diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 32374dc..65324c8 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -27,9 +27,9 @@ namespace kernel::filesystem::ext2 /** @brief Initializes the ext2 filesystem with the given @p device. @param device The device to mount. - @return 0 on success, negative error code on failure. + @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & device) -> int override; + auto mount(kstd::shared_ptr const & device) -> operation_result override; /** @brief Looks up an inode by @p name within a @p parent directory. diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index f855380..ef6929a 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -18,6 +18,14 @@ namespace kernel::filesystem */ struct filesystem { + enum class operation_result : int + { + success = 0, + invalid_magic_number = -1, + invalid_root_inode = -2, + unmount_failed = -3 + }; + /** @brief Virtual destructor for the filesystem. */ @@ -37,9 +45,9 @@ namespace kernel::filesystem /** @brief Initializes the filesystem with the given @p device. @param device The device to mount. - @return 0 on success, or a negative error code on failure. + @return The result of the mount operation. */ - virtual auto mount(kstd::shared_ptr const & device) -> int; + virtual auto mount(kstd::shared_ptr const & device) -> operation_result; /** @brief Looks up a child inode within the given @p parent inode with the specified @p name. This method must be diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index 3e3c69f..0ac6b2f 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -24,9 +24,11 @@ namespace kernel::filesystem @param root_dentry The root dentry of the mounted filesystem. @param fs The filesystem instance being mounted. @param mount_path The path at which the filesystem is mounted. + @param parent_mount The parent mount that this mount is attached beneath. */ mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, - kstd::shared_ptr const & fs, std::string_view mount_path); + kstd::shared_ptr const & fs, std::string_view mount_path, + kstd::shared_ptr const & parent_mount); /** @brief Get the dentry where the filesystem is mounted. @@ -48,11 +50,17 @@ namespace kernel::filesystem */ [[nodiscard]] auto get_mount_path() const -> std::string_view; + /** + @brief Get the parent mount that this mount was attached beneath. + */ + [[nodiscard]] auto get_parent_mount() const -> kstd::shared_ptr const &; + private: kstd::string m_mount_path; kstd::shared_ptr m_mount_dentry; kstd::shared_ptr m_root_dentry; kstd::shared_ptr m_filesystem{}; + kstd::shared_ptr m_parent_mount{}; }; } // namespace kernel::filesystem diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp index a8ef59e..a5cdde6 100644 --- a/kernel/include/kernel/filesystem/mount_table.hpp +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -15,11 +15,28 @@ namespace kernel::filesystem */ struct mount_table { + /** + @brief Results for mount table operations. + */ + enum class operation_result : int + { + removed = 0, + has_child_mounts = -1, + mount_not_found = -2 + }; + /** @brief Adds a mount to the table. @param mount The mount to add. */ - void add_mount(kstd::shared_ptr const & mount); + auto add_mount(kstd::shared_ptr const & mount) -> void; + + /** + @brief Removes the topmost mount at the given @p path. + @param path The mount path to remove. + @return The result of the removal operation. + */ + [[nodiscard]] auto remove_mount(std::string_view path) -> operation_result; /** @brief Finds the mount with the longest prefix matching the given @p path. This method is used to determine which @@ -30,6 +47,8 @@ namespace kernel::filesystem [[nodiscard]] auto find_longest_prefix_mount(std::string_view path) const -> kstd::shared_ptr; private: + [[nodiscard]] auto has_child_mounts(kstd::shared_ptr const & parent_mount) const -> bool; + kstd::vector> m_mounts; }; } // namespace kernel::filesystem diff --git a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp index b7e7c6f..0155c41 100644 --- a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp @@ -25,9 +25,9 @@ namespace kernel::filesystem::rootfs /** @brief Initializes the rootfs filesystem with the given @p device. @param device The device to mount (not required by rootfs). - @return 0 on success, negative error code on failure. + @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & device) -> int override; + auto mount(kstd::shared_ptr const & device) -> operation_result override; /** @brief Looks up an inode by @p name within a @p parent directory. diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 2d05765..4dd2a83 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -20,6 +20,18 @@ namespace kernel::filesystem */ struct vfs { + /** + @brief Results for VFS operations. + */ + enum class operation_result : int + { + success = 0, + invalid_path = -1, + mount_point_not_found = -2, + filesystem_null = -3, + unmount_failed = -4 + }; + /** @brief Initialize the virtual filesystem. @warning Panics if the VFS has already been initialized. @@ -49,9 +61,16 @@ namespace kernel::filesystem @brief Mount a @p filesystem at a specific @p path. @param path The path where the filesystem should be mounted. @param filesystem The filesystem to mount. - @return 0 on success, or a negative error code on failure. + @return The result of the mount operation. + */ + auto do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> operation_result; + + /** + @brief Unmount the filesystem mounted at the specified @p path. + @param path The path where the filesystem is mounted. + @return The result of the unmount operation. */ - auto do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int; + auto unmount(std::string_view path) -> operation_result; private: vfs() = default; diff --git a/kernel/src/filesystem/devfs/filesystem.cpp b/kernel/src/filesystem/devfs/filesystem.cpp index 9043ac5..03b4218 100644 --- a/kernel/src/filesystem/devfs/filesystem.cpp +++ b/kernel/src/filesystem/devfs/filesystem.cpp @@ -1,6 +1,7 @@ #include "kernel/filesystem/devfs/filesystem.hpp" #include "kapi/devices/device.hpp" + #include "kernel/devices/storage/management.hpp" #include "kernel/filesystem/devfs/inode.hpp" #include "kernel/filesystem/device_inode.hpp" @@ -13,12 +14,12 @@ namespace kernel::filesystem::devfs { - auto filesystem::mount(kstd::shared_ptr const &) -> int + auto filesystem::mount(kstd::shared_ptr const &) -> operation_result { m_root_inode = kstd::make_shared(); build_device_inode_table(); - return 0; + return operation_result::success; } auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 7ee1072..6d5960e 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -36,10 +36,6 @@ namespace kernel::filesystem::ext2 constexpr uint16_t S_IFREG = 0x8000; constexpr uint16_t S_IFDIR = 0x4000; - // Error codes - constexpr int INVALID_MAGIC_NUMBER = -1; - constexpr int INVALID_ROOT_INODE = -2; - auto S_ISREG(uint16_t mode) -> bool { return (mode & S_IFMT) == S_IFREG; @@ -51,7 +47,7 @@ namespace kernel::filesystem::ext2 } } // namespace - auto filesystem::mount(kstd::shared_ptr const & device) -> int + auto filesystem::mount(kstd::shared_ptr const & device) -> operation_result { kernel::filesystem::filesystem::mount(device); @@ -59,7 +55,7 @@ namespace kernel::filesystem::ext2 if (m_superblock.magic != MAGIC_NUMBER) { - return INVALID_MAGIC_NUMBER; + return operation_result::invalid_magic_number; } auto const block_size = get_block_size(); @@ -77,9 +73,9 @@ namespace kernel::filesystem::ext2 if (!m_root_inode || !m_root_inode->is_directory()) { - return INVALID_ROOT_INODE; + return operation_result::invalid_root_inode; } - return 0; + return operation_result::success; } auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index b08b520..da2838d 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -30,7 +30,7 @@ namespace kernel::filesystem for (auto & factory : filesystem_factories) { auto fs = factory(); - if (fs->mount(device) == 0) + if (fs->mount(device) == operation_result::success) { return fs; } @@ -39,11 +39,11 @@ namespace kernel::filesystem return nullptr; } - auto filesystem::mount(kstd::shared_ptr const & device) -> int + auto filesystem::mount(kstd::shared_ptr const & device) -> operation_result { // TODO BA-FS26 maybe check if device is null and panic here already? m_device = device; - return 0; + return operation_result::success; } auto filesystem::root_inode() const -> kstd::shared_ptr const & diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index a6d2f7e..d165385 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -13,11 +13,13 @@ namespace kernel::filesystem { mount::mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, - kstd::shared_ptr const & fs, std::string_view mount_path) + kstd::shared_ptr const & fs, std::string_view mount_path, + kstd::shared_ptr const & parent_mount) : m_mount_path(mount_path) , m_mount_dentry(mount_dentry) , m_root_dentry(root_dentry) , m_filesystem(fs) + , m_parent_mount(parent_mount) { if (!m_filesystem) { @@ -44,4 +46,9 @@ namespace kernel::filesystem { return m_mount_path.view(); } + + auto mount::get_parent_mount() const -> kstd::shared_ptr const & + { + return m_parent_mount; + } } // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 195f48a..3b1dee3 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -1,17 +1,83 @@ #include "kernel/filesystem/mount_table.hpp" +#include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/mount.hpp" #include +#include +#include #include +#include #include namespace kernel::filesystem { + namespace + { + auto is_descendant_of(kstd::shared_ptr const & candidate, kstd::shared_ptr const & ancestor) -> bool + { + for (auto current = candidate; current; current = current->get_parent_mount()) + { + if (current == ancestor) + { + return true; + } + } + + return false; + } + + auto is_strict_prefix(std::string_view prefix, std::string_view path) -> bool + { + return prefix != "/" && path.starts_with(prefix) && path.size() > prefix.size() && path[prefix.size()] == '/'; + } + + auto is_visible_mount(kstd::shared_ptr const & candidate, + kstd::vector> const & mounts) -> bool + { + return std::ranges::none_of(mounts, [&](auto const & other) { + return other != candidate && is_strict_prefix(other->get_mount_path(), candidate->get_mount_path()) && + !is_descendant_of(candidate, other); + }); + } + } // namespace + + auto mount_table::has_child_mounts(kstd::shared_ptr const & parent_mount) const -> bool + { + return std::ranges::any_of( + m_mounts, [&parent_mount](auto const & mount) { return mount->get_parent_mount() == parent_mount; }); + } + void mount_table::add_mount(kstd::shared_ptr const & mount) { m_mounts.push_back(mount); + if (auto mount_dentry = mount->get_mount_dentry()) + { + mount_dentry->set_flag(dentry::dentry_flags::dcache_mounted); + } + } + + auto mount_table::remove_mount(std::string_view path) -> operation_result + { + auto mount_it = std::ranges::find_if(std::ranges::reverse_view(m_mounts), [&](auto const & mount) { + return mount->get_mount_path() == path && is_visible_mount(mount, m_mounts); + }); + + if (mount_it == std::ranges::reverse_view(m_mounts).end()) + { + return operation_result::mount_not_found; + } + + auto const & mount = *mount_it; + if (has_child_mounts(mount)) + { + return operation_result::has_child_mounts; + } + + mount->get_mount_dentry()->unset_flag(dentry::dentry_flags::dcache_mounted); + m_mounts.erase(std::ranges::find(m_mounts, mount)); + return operation_result::removed; } auto mount_table::find_longest_prefix_mount(std::string_view path) const -> kstd::shared_ptr @@ -25,8 +91,9 @@ namespace kernel::filesystem // /a/b/c should match /a/b but not /a/bb or /a/b/c/d, / should match everything bool is_prefix = path.starts_with(mp) && (mp == "/" || path.size() == mp.size() || path[mp.size()] == '/'); + bool visible = is_visible_mount(mount, m_mounts); - if (is_prefix && mp.size() >= best_len) + if (is_prefix && visible && mp.size() >= best_len) { mount_with_longest_prefix = mount; best_len = mp.size(); diff --git a/kernel/src/filesystem/rootfs/filesystem.cpp b/kernel/src/filesystem/rootfs/filesystem.cpp index a7c746e..dffef99 100644 --- a/kernel/src/filesystem/rootfs/filesystem.cpp +++ b/kernel/src/filesystem/rootfs/filesystem.cpp @@ -11,13 +11,13 @@ namespace kernel::filesystem::rootfs { - auto filesystem::mount(kstd::shared_ptr const &) -> int + auto filesystem::mount(kstd::shared_ptr const &) -> operation_result { auto rfs_inode = kstd::make_shared(); rfs_inode->add_child("dev"); m_root_inode = rfs_inode; - return 0; + return operation_result::success; } auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 2578036..45ae053 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -7,6 +7,7 @@ #include "kernel/filesystem/devfs/filesystem.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/mount.hpp" +#include "kernel/filesystem/mount_table.hpp" #include "kernel/filesystem/open_file_description.hpp" #include "kernel/filesystem/rootfs/filesystem.hpp" @@ -21,11 +22,6 @@ namespace kernel::filesystem namespace { constinit auto static active_vfs = std::optional{}; - - // Error codes - constexpr int INVALID_PATH = -1; - constexpr int MOUNT_POINT_NOT_FOUND = -2; - constexpr int FILESYSTEM_NULL = -3; } // namespace auto vfs::init() -> void @@ -45,7 +41,7 @@ namespace kernel::filesystem root_fs->mount(nullptr); auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode()); - m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, "")); + m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, "", nullptr)); auto storage_mgmt = devices::storage::management::get(); if (auto boot_device = storage_mgmt.determine_boot_device()) @@ -79,36 +75,55 @@ namespace kernel::filesystem return nullptr; } - // TODO BA-FS26 implement unmount - auto vfs::do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> int + auto vfs::do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> operation_result { if (!filesystem) { - return FILESYSTEM_NULL; + return operation_result::filesystem_null; } if (path.empty() || path.front() != '/' || (path.size() > 1 && path.back() == '/')) { - return INVALID_PATH; + return operation_result::invalid_path; } if (auto mount_point_dentry = resolve_path(path)) { do_mount_internal(path, mount_point_dentry, filesystem); - return 0; + return operation_result::success; + } + + return operation_result::mount_point_not_found; + } + + auto vfs::unmount(std::string_view path) -> operation_result + { + if (path.empty() || path.front() != '/' || (path.size() > 1 && path.back() == '/')) + { + return operation_result::invalid_path; + } + + auto remove_result = m_mount_table.remove_mount(path); + if (remove_result == mount_table::operation_result::removed) + { + return operation_result::success; + } + + if (remove_result == mount_table::operation_result::has_child_mounts) + { + return operation_result::unmount_failed; } - return MOUNT_POINT_NOT_FOUND; + return operation_result::mount_point_not_found; } auto vfs::do_mount_internal(std::string_view path, kstd::shared_ptr const & mount_point_dentry, kstd::shared_ptr const & fs) -> void { - // TODO BA-FS26 check if mount point is already mounted and handle it (unmount old fs, fail, etc.) + auto parent_mount = m_mount_table.find_longest_prefix_mount(path); auto new_fs_root = kstd::make_shared(mount_point_dentry, fs->root_inode()); - auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, path); + auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, path, parent_mount); m_mount_table.add_mount(new_mount); - mount_point_dentry->set_flag(dentry::dentry_flags::dcache_mounted); } auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 1893b84..1d73e20 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -145,6 +145,9 @@ auto test_file_lookup() -> void vfs.do_mount("/enclosures/aquarium", new_filesystem); read_and_write_file("/enclosures/aquarium/closed.txt"); read_and_write_file("/enclosures/aquarium/information/info_2.txt"); + + vfs.unmount("/enclosures/aquarium"); + read_and_write_file("/enclosures/aquarium/tank_1/fish_4.txt"); } auto run_test_code() -> void -- cgit v1.2.3 From edef121fccb75e98ce4a252c4a969f5c85d547ec Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 15:25:55 +0200 Subject: track *.img with git lfs --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5713ac7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.img filter=lfs diff=lfs merge=lfs -text -- cgit v1.2.3 From eef84863f56a9453aaf086391a85b8d78d722358 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 15:26:27 +0200 Subject: add test images --- arch/x86_64/support/grub.cfg.in | 2 ++ arch/x86_64/support/modules/ext2_1KB_fs.img | 3 +++ arch/x86_64/support/modules/ext2_4KB_fs.img | 3 +++ 3 files changed, 8 insertions(+) create mode 100644 arch/x86_64/support/modules/ext2_1KB_fs.img create mode 100644 arch/x86_64/support/modules/ext2_4KB_fs.img diff --git a/arch/x86_64/support/grub.cfg.in b/arch/x86_64/support/grub.cfg.in index 49f19ce..35621f9 100644 --- a/arch/x86_64/support/grub.cfg.in +++ b/arch/x86_64/support/grub.cfg.in @@ -3,5 +3,7 @@ default=0 menuentry "TeachOS" { multiboot2 /$ + module2 /modules/ext2_4KB_fs.img + module2 /modules/ext2_1KB_fs.img boot } \ No newline at end of file diff --git a/arch/x86_64/support/modules/ext2_1KB_fs.img b/arch/x86_64/support/modules/ext2_1KB_fs.img new file mode 100644 index 0000000..9f1ee4a --- /dev/null +++ b/arch/x86_64/support/modules/ext2_1KB_fs.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:94d3988bc309eb9e81f06042c72bf4c4fb5991cd7fdd597eb00c518a96c792d8 +size 10485760 diff --git a/arch/x86_64/support/modules/ext2_4KB_fs.img b/arch/x86_64/support/modules/ext2_4KB_fs.img new file mode 100644 index 0000000..3aaceb8 --- /dev/null +++ b/arch/x86_64/support/modules/ext2_4KB_fs.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4ce6a1aea277906e1af6de223c017ff900b96569f076b4d99fc04eaa1ee986f4 +size 10485760 -- cgit v1.2.3 From 5865dac062f3291b8081eca39c2835015d2c43c6 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 15:48:32 +0200 Subject: extend tests with another filesystem --- arch/x86_64/support/grub.cfg.in | 1 + arch/x86_64/support/modules/ext2_2KB_fs.img | 3 +++ kernel/src/main.cpp | 28 +++++++++++++++++++++++++--- 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 arch/x86_64/support/modules/ext2_2KB_fs.img diff --git a/arch/x86_64/support/grub.cfg.in b/arch/x86_64/support/grub.cfg.in index 35621f9..45c3356 100644 --- a/arch/x86_64/support/grub.cfg.in +++ b/arch/x86_64/support/grub.cfg.in @@ -5,5 +5,6 @@ menuentry "TeachOS" { multiboot2 /$ module2 /modules/ext2_4KB_fs.img module2 /modules/ext2_1KB_fs.img + module2 /modules/ext2_2KB_fs.img boot } \ No newline at end of file diff --git a/arch/x86_64/support/modules/ext2_2KB_fs.img b/arch/x86_64/support/modules/ext2_2KB_fs.img new file mode 100644 index 0000000..1880911 --- /dev/null +++ b/arch/x86_64/support/modules/ext2_2KB_fs.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a13da5abb9c65c737105b1da0d4344c7cd7604c7952c762c4f4e3d3f96fd42d +size 5242880 diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 1d73e20..79ed703 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -139,15 +139,37 @@ auto test_file_lookup() -> void "limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_30.txt"); auto storage_mgmt = kernel::devices::storage::management::get(); - auto device = storage_mgmt.device_by_major_minor(1, 16); - auto new_filesystem = kernel::filesystem::filesystem::probe_and_mount(device); + auto device_1 = storage_mgmt.device_by_major_minor(1, 16); + auto fs_1 = kernel::filesystem::filesystem::probe_and_mount(device_1); - vfs.do_mount("/enclosures/aquarium", new_filesystem); + vfs.do_mount("/enclosures/aquarium", fs_1); read_and_write_file("/enclosures/aquarium/closed.txt"); read_and_write_file("/enclosures/aquarium/information/info_2.txt"); vfs.unmount("/enclosures/aquarium"); read_and_write_file("/enclosures/aquarium/tank_1/fish_4.txt"); + + auto device_2 = storage_mgmt.device_by_major_minor(1, 32); + auto fs_2 = kernel::filesystem::filesystem::probe_and_mount(device_2); + + vfs.do_mount("/enclosures/elephant_house", fs_2); + read_and_write_file("/enclosures/elephant_house/monkey_house/infrastructure/info.txt"); + + vfs.do_mount("/enclosures/elephant_house/monkey_house", fs_1); + read_and_write_file("/enclosures/elephant_house/monkey_house/information/info_2.txt"); + + auto result = vfs.unmount("/enclosures/elephant_house"); + if (result == kernel::filesystem::vfs::operation_result::unmount_failed) + { + kstd::println("[TEST] Unmount failed as expected due to active child mount."); + } + + vfs.unmount("/enclosures/elephant_house/monkey_house"); + result = vfs.unmount("/enclosures/elephant_house"); + if (result == kernel::filesystem::vfs::operation_result::success) + { + kstd::println("[TEST] Unmount succeeded after unmounting child mount."); + } } auto run_test_code() -> void -- cgit v1.2.3 From 210d55251dc7143da9e099c6fc6b8d0f465d4153 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 15:54:33 +0200 Subject: add notes about the content of the test images --- arch/x86_64/support/modules/README.md | 82 +++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 arch/x86_64/support/modules/README.md diff --git a/arch/x86_64/support/modules/README.md b/arch/x86_64/support/modules/README.md new file mode 100644 index 0000000..f3955fa --- /dev/null +++ b/arch/x86_64/support/modules/README.md @@ -0,0 +1,82 @@ +# Default images +The default images contain predefined data and structures that are specifically designed for testing purposes. +The ext2_4KB_fs image is intentionally fragmented, as some files were created and deleted before additional files were added, resulting in a non-contiguous layout. + +## ext2_1KB_fs +. +./lost+found +./information +./information/info_1.txt +./information/info_2.txt +./closed.txt + +## ext2_2KB_fs +. +./lost+found +./monkey_house +./monkey_house/infrastructure +./monkey_house/infrastructure/info.txt +./monkey_house/infrastructure/water.txt +./monkey_house/monkey_1.txt +./monkey_house/monkey_2.txt +./monkey_house/monkey_3.txt +./monkey_house/caretaker +./monkey_house/caretaker/isabelle.txt +./monkey_house/caretaker/peter.txt + +## ext2_4KB_fs +. +./lost+found +./entrance +./entrance/tickets.txt +./entrance/map.txt +./enclosures +./enclosures/lion_house +./enclosures/lion_house/cage_a +./enclosures/lion_house/cage_a/history.txt +./enclosures/lion_house/cage_a/animals.txt +./enclosures/lion_house/cage_b +./enclosures/lion_house/cage_b/animals.txt +./enclosures/lion_house/cage_b/history.txt +./enclosures/elephant_house +./enclosures/elephant_house/elephant_1.txt +./enclosures/aquarium +./enclosures/aquarium/tank_1 +./enclosures/aquarium/tank_1/fish_1.txt +./enclosures/aquarium/tank_1/fish_2.txt +./enclosures/aquarium/tank_1/fish_3.txt +./enclosures/aquarium/tank_1/fish_4.txt +./enclosures/aquarium/tank_2 +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_1.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_2.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_3.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_4.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_5.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_6.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_7.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_8.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_9.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_10.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_11.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_12.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_13.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_14.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_15.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_16.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_17.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_18.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_19.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_20.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_21.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_22.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_23.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_24.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_25.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_26.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_27.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_28.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_29.txt +./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_30.txt +./enclosures/aquarium/spawn_fish.sh +./enclosures/info.txt +./info.txt -- cgit v1.2.3 From e2c08ddb3d79f946399ca5d3bc07b4e6c4de9328 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 16:21:33 +0200 Subject: add dentry tests --- kernel/CMakeLists.txt | 3 + kernel/include/kernel/filesystem/dentry.hpp | 15 ++- kernel/src/filesystem/dentry.cpp | 9 +- kernel/src/filesystem/dentry.tests.cpp | 136 ++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 kernel/src/filesystem/dentry.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 74233cb..85c7661 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -126,6 +126,9 @@ else() "src/memory/bitmap_allocator.tests.cpp" "src/memory/block_list_allocator.tests.cpp" + # Filesystem Subsystem Tests + "src/filesystem/dentry.tests.cpp" + # Storage Subsystem Tests "src/devices/storage/ram_disk/device.tests.cpp" ) diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index 72d758f..58a918f 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -28,10 +28,13 @@ namespace kernel::filesystem }; /** - @brief Create a dentry with the given @p parent, associated @p node, and optional @p name. The dentry is initialized - with the provided parent and inode, and the name is stored for lookup purposes. + @brief Create a dentry with the given @p parent, associated @p inode, and optional @p name. The dentry is + initialized with the provided parent and inode, and the name is stored for lookup purposes. + @param parent The parent dentry. + @param inode The associated inode for this dentry. + @param name The name of the dentry (optional). */ - dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node, std::string_view name = {}); + dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & inode, std::string_view name = {}); /** @brief Get the associated inode. @@ -45,6 +48,12 @@ namespace kernel::filesystem */ [[nodiscard]] auto get_parent() const -> kstd::shared_ptr const &; + /** + @brief Get the name of the dentry. + @return The name of the dentry. + */ + [[nodiscard]] auto get_name() const -> std::string_view; + /** @brief Add a @p child dentry. @param child The child dentry to add. diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index 2f99e91..6591011 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -12,10 +12,10 @@ namespace kernel::filesystem { - dentry::dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & node, std::string_view name) + dentry::dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & inode, std::string_view name) : m_name(name) , m_parent(parent) - , m_inode(node) + , m_inode(inode) { if (!m_inode) { @@ -33,6 +33,11 @@ namespace kernel::filesystem return m_parent; } + auto dentry::get_name() const -> std::string_view + { + return m_name.view(); + } + auto dentry::add_child(kstd::shared_ptr const & child) -> void { m_children.push_back(child); diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp new file mode 100644 index 0000000..f82024a --- /dev/null +++ b/kernel/src/filesystem/dentry.tests.cpp @@ -0,0 +1,136 @@ +#include "kernel/filesystem/dentry.hpp" + +#include "kernel/filesystem/devfs/inode.hpp" +#include "kernel/test_support/cio.hpp" +#include "kernel/test_support/cpu.hpp" + +#include +#include + +#include + +SCENARIO("Dentry construction", "[filesystem][dentry]") +{ + GIVEN("A parent dentry and inode") + { + auto inode = kstd::make_shared(); + auto parent_dentry = kstd::make_shared(nullptr, inode); + + WHEN("constructing a dentry") + { + auto child_dentry = kernel::filesystem::dentry{parent_dentry, inode, "child"}; + + THEN("the dentry has the correct parent, inode, and name") + { + REQUIRE(child_dentry.get_parent() == parent_dentry); + REQUIRE(child_dentry.get_inode() == inode); + REQUIRE(child_dentry.get_name() == "child"); + } + + THEN("no flag is set") + { + REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + } + } + + WHEN("constructing a dentry with an empty name") + { + auto child_dentry = kernel::filesystem::dentry{parent_dentry, inode}; + + THEN("the dentry has the correct parent and inode, and an empty name") + { + REQUIRE(child_dentry.get_parent() == parent_dentry); + REQUIRE(child_dentry.get_inode() == inode); + REQUIRE(child_dentry.get_name().empty()); + } + + THEN("no flag is set") + { + REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + } + } + + WHEN("constructing a dentry with a null parent") + { + auto child_dentry = kernel::filesystem::dentry{nullptr, inode, "child"}; + + THEN("the dentry has a null parent, the correct inode, and the correct name") + { + REQUIRE(child_dentry.get_parent() == nullptr); + REQUIRE(child_dentry.get_inode() == inode); + REQUIRE(child_dentry.get_name() == "child"); + } + + THEN("no flag is set") + { + REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + } + } + + WHEN("constructing a dentry with a null inode") + { + THEN("the system panics") + { + REQUIRE_THROWS_AS((kernel::filesystem::dentry{parent_dentry, nullptr, "child"}), kernel::tests::cpu::halt); + } + } + } +} + +SCENARIO("Dentry child logic", "[filesystem][dentry]") +{ + GIVEN("A parent dentry and inode") + { + auto inode = kstd::make_shared(); + auto parent_dentry = kstd::make_shared(nullptr, inode); + + WHEN("adding child dentries") + { + auto child1 = kstd::make_shared(parent_dentry, inode, "child1"); + auto child2 = kstd::make_shared(parent_dentry, inode, "child2"); + parent_dentry->add_child(child1); + parent_dentry->add_child(child2); + + THEN("the children can be found by name") + { + REQUIRE(parent_dentry->find_child("child1") == child1); + REQUIRE(parent_dentry->find_child("child2") == child2); + } + + THEN("finding a non-existent child returns null") + { + REQUIRE(parent_dentry->find_child("nonexistent") == nullptr); + } + } + } +} + +SCENARIO("Dentry Flag logic", "[filesystem][dentry]") +{ + GIVEN("A dentry") + { + auto inode = kstd::make_shared(); + auto dentry = kernel::filesystem::dentry{nullptr, inode, "test"}; + + WHEN("setting a flag") + { + dentry.set_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted); + + THEN("the flag is set") + { + REQUIRE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + } + } + + WHEN("unsetting a flag") + { + dentry.set_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted); + dentry.unset_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted); + + THEN("the flag is unset") + { + REQUIRE_FALSE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + } + } + } +} -- cgit v1.2.3 From 648fc5c801fbebaed5fe0825e88b359476f57a84 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 18:20:04 +0200 Subject: add gdb and ms-vscode.hexeditor to devcontainer --- .devcontainer/x86-64/devcontainer.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.devcontainer/x86-64/devcontainer.json b/.devcontainer/x86-64/devcontainer.json index 00e33ad..6bf1616 100644 --- a/.devcontainer/x86-64/devcontainer.json +++ b/.devcontainer/x86-64/devcontainer.json @@ -5,7 +5,7 @@ "ghcr.io/devcontainers/features/git:1": {}, "ghcr.io/devcontainers/features/git-lfs:1": {}, "ghcr.io/devcontainers-extra/features/apt-packages:1": { - "packages": "build-essential,clang-tidy,clangd,cmake,grub2-common,grub-pc,mtools,ninja-build,qemu-system-x86,ssh,xorriso" + "packages": "build-essential,clang-tidy,clangd,cmake,grub2-common,grub-pc,mtools,ninja-build,qemu-system-x86,ssh,xorriso,gdb" } }, "customizations": { @@ -17,10 +17,11 @@ "matepek.vscode-catch2-test-adapter", "ms-vscode.cmake-tools", "KylinIdeTeam.cppdebug", - "zixuanwang.linkerscript" + "zixuanwang.linkerscript", + "ms-vscode.hexeditor" ] } }, "remoteUser": "ubuntu", "updateRemoteUserUID": true -} +} \ No newline at end of file -- cgit v1.2.3 From 869ff69ebae160006e31eb0f24ed927bb65f3c63 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 18:51:30 +0200 Subject: implement vector resize --- libs/kstd/include/kstd/vector | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 9e41cb6..e51cbac 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -559,6 +559,45 @@ namespace kstd m_size = old_size; } + //! Resize this vector to contain @p new_size elements. + constexpr auto resize(size_type new_size) -> void + { + resize(new_size, value_type{}); + } + + //! Resize this vector to contain @p new_size elements, filling new elements with @p value. + constexpr auto resize(size_type new_size, const_reference value) -> void + { + if (new_size < size()) + { + destroy_n(begin() + new_size, size() - new_size); + m_size = new_size; + return; + } + + if (new_size == size()) + { + return; + } + + if (new_size > max_size()) + { + kstd::os::panic("[kstd:vector] Tried to resize more space than theoretically possible."); + } + + if (new_size > capacity()) + { + reserve(new_size); + } + + for (auto i = size(); i < new_size; ++i) + { + std::allocator_traits::construct(m_allocator, m_data + i, value); + } + + m_size = new_size; + } + //! Get the number of element this vector has currently space for, including elements currently in this vector. [[nodiscard]] constexpr auto capacity() const noexcept -> size_type { -- cgit v1.2.3 From bf90905a5ded3995af5677685bc31228d485e64b Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 18:52:11 +0200 Subject: add test block_device --- kernel/CMakeLists.txt | 1 + .../kernel/test_support/devices/block_device.hpp | 31 ++++++++++++ kernel/src/test_support/devices/block_device.cpp | 58 ++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 kernel/include/kernel/test_support/devices/block_device.hpp create mode 100644 kernel/src/test_support/devices/block_device.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 85c7661..12683a1 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -101,6 +101,7 @@ else() "src/test_support/kapi/cio.cpp" "src/test_support/kapi/interrupts.cpp" "src/test_support/kapi/memory.cpp" + "src/test_support/devices/block_device.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" "src/test_support/page_mapper.cpp" diff --git a/kernel/include/kernel/test_support/devices/block_device.hpp b/kernel/include/kernel/test_support/devices/block_device.hpp new file mode 100644 index 0000000..2327fc4 --- /dev/null +++ b/kernel/include/kernel/test_support/devices/block_device.hpp @@ -0,0 +1,31 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_BLOCK_DEVICE_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_BLOCK_DEVICE_HPP + +#include "kernel/devices/block_device.hpp" + +#include +#include + +#include +#include + +namespace kernel::tests::devices +{ + + struct block_device : kernel::devices::block_device + { + block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size); + + auto init() -> bool override; + + auto read_block(size_t block_index, void * buffer) const -> void override; + auto write_block(size_t block_index, void const * buffer) -> void override; + + [[nodiscard]] auto size() const -> size_t override; + + kstd::vector data{}; + }; + +} // namespace kernel::tests::devices + +#endif \ No newline at end of file diff --git a/kernel/src/test_support/devices/block_device.cpp b/kernel/src/test_support/devices/block_device.cpp new file mode 100644 index 0000000..bce415f --- /dev/null +++ b/kernel/src/test_support/devices/block_device.cpp @@ -0,0 +1,58 @@ +#include "kernel/test_support/devices/block_device.hpp" + +#include "kernel/devices/block_device.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +namespace kernel::tests::devices +{ + block_device::block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size) + : kernel::devices::block_device(major, minor, name, block_size) + {} + + auto block_device::init() -> bool + { + return true; + } + + auto block_device::read_block(size_t block_index, void * buffer) const -> void + { + auto const offset = block_index * block_size(); + if (offset >= data.size()) + { + kstd::libc::memset(buffer, 0, block_size()); + return; + } + + auto const bytes_to_read = std::min(block_size(), data.size() - offset); + kstd::libc::memcpy(buffer, data.data() + offset, bytes_to_read); + if (bytes_to_read < block_size()) + { + kstd::libc::memset(static_cast(buffer) + bytes_to_read, 0, block_size() - bytes_to_read); + } + } + + auto block_device::write_block(size_t block_index, void const * buffer) -> void + { + auto const offset = block_index * block_size(); + auto const write_end = offset + block_size(); + if (write_end > data.size()) + { + data.resize(write_end, 0); + } + + kstd::libc::memcpy(data.data() + offset, static_cast(buffer), block_size()); + } + + auto block_device::size() const -> size_t + { + return data.size(); + } +} // namespace kernel::tests::devices \ No newline at end of file -- cgit v1.2.3 From 63c6299262411fc06afca4f224e00e1b2d25eb25 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 19:01:16 +0200 Subject: clean up includes --- kernel/src/filesystem/dentry.tests.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp index f82024a..94aa48d 100644 --- a/kernel/src/filesystem/dentry.tests.cpp +++ b/kernel/src/filesystem/dentry.tests.cpp @@ -1,7 +1,6 @@ #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/devfs/inode.hpp" -#include "kernel/test_support/cio.hpp" #include "kernel/test_support/cpu.hpp" #include -- cgit v1.2.3 From ad2a744960ef8359a40e25c81f2b5fee0d8952c4 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 19:17:08 +0200 Subject: add block_device_utils tests --- kernel/CMakeLists.txt | 1 + kernel/src/devices/block_device_utils.cpp | 2 +- kernel/src/devices/block_device_utils.tests.cpp | 189 ++++++++++++++++++++++++ 3 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 kernel/src/devices/block_device_utils.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 12683a1..be21fc3 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -131,6 +131,7 @@ else() "src/filesystem/dentry.tests.cpp" # Storage Subsystem Tests + "src/devices/block_device_utils.tests.cpp" "src/devices/storage/ram_disk/device.tests.cpp" ) add_executable("os::kernel_tests" ALIAS "kernel_tests") diff --git a/kernel/src/devices/block_device_utils.cpp b/kernel/src/devices/block_device_utils.cpp index 6fe89fe..a1fd5e3 100644 --- a/kernel/src/devices/block_device_utils.cpp +++ b/kernel/src/devices/block_device_utils.cpp @@ -1,9 +1,9 @@ #include "kernel/devices/block_device_utils.hpp" +#include "kapi/devices/device.hpp" #include "kapi/system.hpp" #include "kernel/devices/block_device.hpp" -#include "kapi/devices/device.hpp" #include #include diff --git a/kernel/src/devices/block_device_utils.tests.cpp b/kernel/src/devices/block_device_utils.tests.cpp new file mode 100644 index 0000000..7ecd202 --- /dev/null +++ b/kernel/src/devices/block_device_utils.tests.cpp @@ -0,0 +1,189 @@ +#include "kernel/devices/block_device_utils.hpp" + +#include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/devices/block_device.hpp" + +#include +#include +#include + +#include + +#include +#include + +SCENARIO("reading from a block device with block_device_utils", "[devices][block_device_utils]") +{ + GIVEN("a block device with known data") + { + auto const block_size = 512; + auto device = kstd::make_shared(0, 0, "test_block_device", block_size); + kstd::vector block_data(block_size); + for (size_t i = 0; i < block_data.size(); ++i) + { + block_data[i] = static_cast(i % 256); + } + device->write_block(0, block_data.data()); + device->write_block(1, block_data.data()); + + WHEN("reading from the block device using block_device_utils") + { + kstd::vector read_buffer(block_size); + auto bytes_read = kernel::devices::block_device_utils::read(device, read_buffer.data(), 0, read_buffer.size()); + + THEN("the correct number of bytes is read") + { + REQUIRE(bytes_read == read_buffer.size()); + } + + THEN("the data read matches the data written to the block device") + { + REQUIRE(read_buffer == block_data); + } + } + + WHEN("reading over block boundaries") + { + kstd::vector read_buffer(1024); + auto bytes_read = kernel::devices::block_device_utils::read(device, read_buffer.data(), 256, read_buffer.size()); + + THEN("the correct number of bytes is read") + { + REQUIRE(bytes_read == 1.5 * block_size); + } + + THEN("the data read matches the expected data across block boundaries") + { + for (size_t i = 0; i < bytes_read; ++i) + { + uint8_t expected_value = static_cast((256 + i) % 256); + REQUIRE(read_buffer[i] == expected_value); + } + } + } + + WHEN("reading beyond the device capacity") + { + kstd::vector read_buffer(block_size); + auto bytes_read = kernel::devices::block_device_utils::read(device, read_buffer.data(), 1024, read_buffer.size()); + + THEN("no bytes are read") + { + REQUIRE(bytes_read == 0); + } + } + + WHEN("reading nothing") + { + kstd::vector read_buffer(block_size); + auto bytes_read = kernel::devices::block_device_utils::read(device, read_buffer.data(), 0, 0); + + THEN("no bytes are read") + { + REQUIRE(bytes_read == 0); + } + } + + WHEN("reading with a null buffer") + { + THEN("the system panics") + { + REQUIRE_THROWS_AS(kernel::devices::block_device_utils::read(device, nullptr, 0, 512), kernel::tests::cpu::halt); + } + } + } +} + +SCENARIO("writing to a block device using block_device_utils", "[devices][block_device_utils]") +{ + GIVEN("a block device") + { + auto const block_size = 512; + auto device = kstd::make_shared(0, 0, "test_block_device", block_size); + device->data.resize(2 * block_size); + + WHEN("writing to the block device using block_device_utils") + { + kstd::vector write_buffer(block_size); + for (size_t i = 0; i < write_buffer.size(); ++i) + { + write_buffer[i] = static_cast(i % 256); + } + + auto bytes_written = + kernel::devices::block_device_utils::write(device, write_buffer.data(), 0, write_buffer.size()); + + THEN("the correct number of bytes is written") + { + REQUIRE(bytes_written == write_buffer.size()); + } + + THEN("the data written matches the data read back from the block device") + { + kstd::vector read_buffer(block_size); + device->read_block(0, read_buffer.data()); + REQUIRE(read_buffer == write_buffer); + } + } + + WHEN("writing over block boundaries") + { + kstd::vector write_buffer(2 * block_size); + for (size_t i = 0; i < write_buffer.size(); ++i) + { + write_buffer[i] = static_cast(i % 256); + } + + auto bytes_written = + kernel::devices::block_device_utils::write(device, write_buffer.data(), 256, write_buffer.size()); + + THEN("the correct number of bytes is written") + { + REQUIRE(bytes_written == 1.5 * block_size); + } + + THEN("the data written matches the data read back from the block device across block boundaries") + { + kstd::vector read_buffer(2 * block_size); + auto bytes_read = kernel::devices::block_device_utils::read(device, read_buffer.data(), 256, 2 * block_size); + + for (size_t i = 0; i < bytes_read; ++i) + { + REQUIRE(read_buffer[i] == write_buffer[i]); + } + } + } + + WHEN("writing beyond the device capacity") + { + kstd::vector write_buffer(block_size); + auto bytes_written = + kernel::devices::block_device_utils::write(device, write_buffer.data(), 1024, write_buffer.size()); + + THEN("no bytes are written") + { + REQUIRE(bytes_written == 0); + } + } + + WHEN("writing nothing") + { + kstd::vector write_buffer(block_size); + auto bytes_written = kernel::devices::block_device_utils::write(device, write_buffer.data(), 0, 0); + + THEN("no bytes are written") + { + REQUIRE(bytes_written == 0); + } + } + + WHEN("writing with a null buffer") + { + THEN("the system panics") + { + REQUIRE_THROWS_AS(kernel::devices::block_device_utils::write(device, nullptr, 0, block_size), + kernel::tests::cpu::halt); + } + } + } +} \ No newline at end of file -- cgit v1.2.3 From 8e9e9ffd0528ffa554c336871f1a484c15613bfc Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 19:28:54 +0200 Subject: add block_device tests --- kernel/CMakeLists.txt | 1 + .../kernel/test_support/devices/block_device.hpp | 2 +- kernel/src/devices/block_device.cpp | 3 +- kernel/src/devices/block_device.tests.cpp | 46 ++++++++++++++++++++++ kernel/src/devices/block_device_utils.tests.cpp | 4 +- kernel/src/test_support/devices/block_device.cpp | 7 +++- 6 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 kernel/src/devices/block_device.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index be21fc3..a360f17 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -132,6 +132,7 @@ else() # Storage Subsystem Tests "src/devices/block_device_utils.tests.cpp" + "src/devices/block_device.tests.cpp" "src/devices/storage/ram_disk/device.tests.cpp" ) add_executable("os::kernel_tests" ALIAS "kernel_tests") diff --git a/kernel/include/kernel/test_support/devices/block_device.hpp b/kernel/include/kernel/test_support/devices/block_device.hpp index 2327fc4..110872f 100644 --- a/kernel/include/kernel/test_support/devices/block_device.hpp +++ b/kernel/include/kernel/test_support/devices/block_device.hpp @@ -14,7 +14,7 @@ namespace kernel::tests::devices struct block_device : kernel::devices::block_device { - block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size); + block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size, size_t initial_size = 0); auto init() -> bool override; diff --git a/kernel/src/devices/block_device.cpp b/kernel/src/devices/block_device.cpp index c006198..b7cb26e 100644 --- a/kernel/src/devices/block_device.cpp +++ b/kernel/src/devices/block_device.cpp @@ -1,8 +1,7 @@ #include "kernel/devices/block_device.hpp" -#include "kapi/system.hpp" - #include "kapi/devices/device.hpp" +#include "kapi/system.hpp" #include diff --git a/kernel/src/devices/block_device.tests.cpp b/kernel/src/devices/block_device.tests.cpp new file mode 100644 index 0000000..378437e --- /dev/null +++ b/kernel/src/devices/block_device.tests.cpp @@ -0,0 +1,46 @@ +#include "kernel/test_support/devices/block_device.hpp" + +#include "kernel/test_support/cpu.hpp" + +#include +#include +#include +#include + +#include + +#include + +SCENARIO("Block device construction", "[devices][block_device]") +{ + GIVEN("parameters for a block device") + { + size_t major = 1; + size_t minor = 0; + kstd::string name = "test_block_device"; + size_t block_size = 512; + + WHEN("constructing a block device") + { + auto device = + kstd::make_shared(major, minor, name, block_size, 3 * block_size); + + THEN("the block device has the correct properties") + { + REQUIRE(device->major() == major); + REQUIRE(device->minor() == minor); + REQUIRE(device->name() == name); + REQUIRE(device->block_size() == block_size); + REQUIRE(device->capacity() == 3 * 512); + } + } + + WHEN("constructing a block device with zero block size") + { + THEN("the constructor panics") + { + REQUIRE_THROWS_AS((kernel::tests::devices::block_device(major, minor, name, 0)), kernel::tests::cpu::halt); + } + } + } +} diff --git a/kernel/src/devices/block_device_utils.tests.cpp b/kernel/src/devices/block_device_utils.tests.cpp index 7ecd202..5f27a9b 100644 --- a/kernel/src/devices/block_device_utils.tests.cpp +++ b/kernel/src/devices/block_device_utils.tests.cpp @@ -99,8 +99,8 @@ SCENARIO("writing to a block device using block_device_utils", "[devices][block_ GIVEN("a block device") { auto const block_size = 512; - auto device = kstd::make_shared(0, 0, "test_block_device", block_size); - device->data.resize(2 * block_size); + auto device = + kstd::make_shared(0, 0, "test_block_device", block_size, 2 * block_size); WHEN("writing to the block device using block_device_utils") { diff --git a/kernel/src/test_support/devices/block_device.cpp b/kernel/src/test_support/devices/block_device.cpp index bce415f..d1d4101 100644 --- a/kernel/src/test_support/devices/block_device.cpp +++ b/kernel/src/test_support/devices/block_device.cpp @@ -13,9 +13,12 @@ namespace kernel::tests::devices { - block_device::block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size) + block_device::block_device(size_t major, size_t minor, kstd::string const & name, size_t block_size, + size_t initial_size) : kernel::devices::block_device(major, minor, name, block_size) - {} + { + data.resize(initial_size, 0); + } auto block_device::init() -> bool { -- cgit v1.2.3 From 6d72142158970119ee0d36d9149c0e0572dedf5f Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 19:43:15 +0200 Subject: add non block device for tests --- kernel/CMakeLists.txt | 1 + .../test_support/devices/character_device.hpp | 23 ++++++++++++++++++++++ .../src/test_support/devices/character_device.cpp | 20 +++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 kernel/include/kernel/test_support/devices/character_device.hpp create mode 100644 kernel/src/test_support/devices/character_device.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index a360f17..df20704 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -102,6 +102,7 @@ else() "src/test_support/kapi/interrupts.cpp" "src/test_support/kapi/memory.cpp" "src/test_support/devices/block_device.cpp" + "src/test_support/devices/character_device.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" "src/test_support/page_mapper.cpp" diff --git a/kernel/include/kernel/test_support/devices/character_device.hpp b/kernel/include/kernel/test_support/devices/character_device.hpp new file mode 100644 index 0000000..a106cfb --- /dev/null +++ b/kernel/include/kernel/test_support/devices/character_device.hpp @@ -0,0 +1,23 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_CHARACTER_DEVICE_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_CHARACTER_DEVICE_HPP + +#include "kapi/devices/device.hpp" + +#include +#include + +#include + +namespace kernel::tests::devices +{ + // TODO fix inheritance when character devices are implemented + struct character_device : kapi::devices::device + { + character_device(size_t major, size_t minor, kstd::string const & name); + + auto init() -> bool override; + }; + +} // namespace kernel::tests::devices + +#endif \ No newline at end of file diff --git a/kernel/src/test_support/devices/character_device.cpp b/kernel/src/test_support/devices/character_device.cpp new file mode 100644 index 0000000..9e9227d --- /dev/null +++ b/kernel/src/test_support/devices/character_device.cpp @@ -0,0 +1,20 @@ + +#include "kernel/test_support/devices/character_device.hpp" + +#include "kapi/devices.hpp" + +#include + +#include + +namespace kernel::tests::devices +{ + character_device::character_device(size_t major, size_t minor, kstd::string const & name) + : kapi::devices::device(major, minor, name) + {} + + auto character_device::init() -> bool + { + return true; + } +} // namespace kernel::tests::devices \ No newline at end of file -- cgit v1.2.3 From e599e359f727be29415b63c83f3df620d6e4c53c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 19:50:38 +0200 Subject: fix is_block_device check, add device_inode and non-block device tests --- kernel/CMakeLists.txt | 1 + kernel/src/devices/block_device_utils.cpp | 5 +- kernel/src/devices/block_device_utils.tests.cpp | 32 ++++++- kernel/src/filesystem/device_inode.tests.cpp | 108 ++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 kernel/src/filesystem/device_inode.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index df20704..53ed107 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -130,6 +130,7 @@ else() # Filesystem Subsystem Tests "src/filesystem/dentry.tests.cpp" + "src/filesystem/device_inode.tests.cpp" # Storage Subsystem Tests "src/devices/block_device_utils.tests.cpp" diff --git a/kernel/src/devices/block_device_utils.cpp b/kernel/src/devices/block_device_utils.cpp index a1fd5e3..59e9b97 100644 --- a/kernel/src/devices/block_device_utils.cpp +++ b/kernel/src/devices/block_device_utils.cpp @@ -31,12 +31,13 @@ namespace kernel::devices::block_device_utils return 0; } - auto * block_dev = static_cast(device.get()); - if (block_dev == nullptr) + if (!device->is_block_device()) { kapi::system::panic("[FILESYSTEM] device_file: expected block_device."); } + auto * block_dev = static_cast(device.get()); + size_t const block_size = block_dev->block_size(); size_t const capacity = block_dev->capacity(); diff --git a/kernel/src/devices/block_device_utils.tests.cpp b/kernel/src/devices/block_device_utils.tests.cpp index 5f27a9b..f78e477 100644 --- a/kernel/src/devices/block_device_utils.tests.cpp +++ b/kernel/src/devices/block_device_utils.tests.cpp @@ -2,6 +2,7 @@ #include "kernel/test_support/cpu.hpp" #include "kernel/test_support/devices/block_device.hpp" +#include "kernel/test_support/devices/character_device.hpp" #include #include @@ -186,4 +187,33 @@ SCENARIO("writing to a block device using block_device_utils", "[devices][block_ } } } -} \ No newline at end of file +} + +SCENARIO("block_device_utils with a non-block device", "[devices][block_device_utils]") +{ + GIVEN("a non-block device") + { + auto device = kstd::make_shared(0, 0, "test_character_device"); + + WHEN("attempting to read from the non-block device using block_device_utils") + { + kstd::vector read_buffer(512); + THEN("the system panics") + { + REQUIRE_THROWS_AS(kernel::devices::block_device_utils::read(device, read_buffer.data(), 0, read_buffer.size()), + kernel::tests::cpu::halt); + } + } + + WHEN("attempting to write to the non-block device using block_device_utils") + { + kstd::vector write_buffer(512); + THEN("the system panics") + { + REQUIRE_THROWS_AS( + kernel::devices::block_device_utils::write(device, write_buffer.data(), 0, write_buffer.size()), + kernel::tests::cpu::halt); + } + } + } +} diff --git a/kernel/src/filesystem/device_inode.tests.cpp b/kernel/src/filesystem/device_inode.tests.cpp new file mode 100644 index 0000000..4e31812 --- /dev/null +++ b/kernel/src/filesystem/device_inode.tests.cpp @@ -0,0 +1,108 @@ +#include "kernel/filesystem/device_inode.hpp" + +#include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/devices/block_device.hpp" +#include "kernel/test_support/devices/character_device.hpp" + +#include +#include +#include + +#include + +#include +#include + +SCENARIO("Device inode construction", "[filesystem][device_inode]") +{ + GIVEN("a block device") + { + auto device = kstd::make_shared(0, 0, "test_block_device", 512, 3 * 512); + + WHEN("constructing a device inode with the block device") + { + auto inode = kernel::filesystem::device_inode{device}; + + THEN("the device inode has the correct device") + { + REQUIRE(inode.device() == device); + } + + THEN("the device inode has the correct kind") + { + REQUIRE(inode.is_device()); + REQUIRE_FALSE(inode.is_directory()); + REQUIRE_FALSE(inode.is_regular()); + } + } + + WHEN("constructing a device inode with a null device") + { + THEN("the constructor panics") + { + REQUIRE_THROWS_AS((kernel::filesystem::device_inode{nullptr}), kernel::tests::cpu::halt); + } + } + } +} + +SCENARIO("Device inode read/write", "[filesystem][device_inode]") +{ + GIVEN("a block device and a device inode for that device") + { + auto device = kstd::make_shared(0, 0, "test_block_device", 512, 3 * 512); + auto inode = kernel::filesystem::device_inode{device}; + + WHEN("writing to the device inode") + { + kstd::vector write_buffer(1024); + for (size_t i = 0; i < write_buffer.size(); ++i) + { + write_buffer[i] = static_cast(i % 256); + } + + auto bytes_written = inode.write(write_buffer.data(), 256, write_buffer.size()); + + THEN("the correct number of bytes is written") + { + REQUIRE(bytes_written == 1024); + } + + THEN("the data written matches the data read back from the device inode") + { + kstd::vector read_buffer(1024); + auto bytes_read = inode.read(read_buffer.data(), 256, read_buffer.size()); + + REQUIRE(bytes_read == write_buffer.size()); + REQUIRE(read_buffer == write_buffer); + } + } + } +} + +SCENARIO("Device inode read/write with a non-block device", "[filesystem][device_inode]") +{ + GIVEN("a non-block device and a device inode for that device") + { + auto device = kstd::make_shared(0, 0, "test_character_device"); + auto inode = kernel::filesystem::device_inode{device}; + + WHEN("reading from the device inode") + { + kstd::vector read_buffer(512); + THEN("the system panics") + { + REQUIRE_THROWS_AS(inode.read(read_buffer.data(), 0, read_buffer.size()), kernel::tests::cpu::halt); + } + } + + WHEN("writing to the device inode") + { + kstd::vector write_buffer(512); + THEN("the system panics") + { + REQUIRE_THROWS_AS(inode.write(write_buffer.data(), 0, write_buffer.size()), kernel::tests::cpu::halt); + } + } + } +} -- cgit v1.2.3 From 153d061ddfcc6cb9bff1f691a3fe5415f94a3615 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 20:41:49 +0200 Subject: remove todos --- kernel/include/kernel/filesystem/inode.hpp | 1 - kernel/src/filesystem/filesystem.cpp | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index 7237184..072f198 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -69,7 +69,6 @@ namespace kernel::filesystem */ [[nodiscard]] auto is_device() const -> bool; - // TODO BA-FS26 improve inode_kind really needed? public: inode_kind m_kind{inode_kind::regular}; }; diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index da2838d..d8b04eb 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -41,7 +41,11 @@ namespace kernel::filesystem auto filesystem::mount(kstd::shared_ptr const & device) -> operation_result { - // TODO BA-FS26 maybe check if device is null and panic here already? + if (!device) + { + kapi::system::panic("[FILESYSTEM] cannot mount filesystem: device is null."); + } + m_device = device; return operation_result::success; } -- cgit v1.2.3 From 597251886a0934315468f5ba4dc595910cbf734c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 20:42:12 +0200 Subject: use separate test inode --- kernel/CMakeLists.txt | 1 + .../kernel/test_support/filesystem/inode.hpp | 19 +++++++++++++++++++ kernel/src/filesystem/dentry.tests.cpp | 8 ++++---- kernel/src/test_support/filesystem/inode.cpp | 22 ++++++++++++++++++++++ 4 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 kernel/include/kernel/test_support/filesystem/inode.hpp create mode 100644 kernel/src/test_support/filesystem/inode.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 53ed107..0025e4c 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -103,6 +103,7 @@ else() "src/test_support/kapi/memory.cpp" "src/test_support/devices/block_device.cpp" "src/test_support/devices/character_device.cpp" + "src/test_support/filesystem/inode.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" "src/test_support/page_mapper.cpp" diff --git a/kernel/include/kernel/test_support/filesystem/inode.hpp b/kernel/include/kernel/test_support/filesystem/inode.hpp new file mode 100644 index 0000000..6568f24 --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/inode.hpp @@ -0,0 +1,19 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_INODE_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_INODE_HPP + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace kernel::tests::filesystem +{ + struct inode : kernel::filesystem::inode + { + inode(); + + auto read(void * buffer, size_t offset, size_t size) const -> size_t override; + auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + }; +} // namespace kernel::tests::filesystem + +#endif diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp index 94aa48d..a6620d3 100644 --- a/kernel/src/filesystem/dentry.tests.cpp +++ b/kernel/src/filesystem/dentry.tests.cpp @@ -1,7 +1,7 @@ #include "kernel/filesystem/dentry.hpp" -#include "kernel/filesystem/devfs/inode.hpp" #include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/filesystem/inode.hpp" #include #include @@ -12,7 +12,7 @@ SCENARIO("Dentry construction", "[filesystem][dentry]") { GIVEN("A parent dentry and inode") { - auto inode = kstd::make_shared(); + auto inode = kstd::make_shared(); auto parent_dentry = kstd::make_shared(nullptr, inode); WHEN("constructing a dentry") @@ -80,7 +80,7 @@ SCENARIO("Dentry child logic", "[filesystem][dentry]") { GIVEN("A parent dentry and inode") { - auto inode = kstd::make_shared(); + auto inode = kstd::make_shared(); auto parent_dentry = kstd::make_shared(nullptr, inode); WHEN("adding child dentries") @@ -108,7 +108,7 @@ SCENARIO("Dentry Flag logic", "[filesystem][dentry]") { GIVEN("A dentry") { - auto inode = kstd::make_shared(); + auto inode = kstd::make_shared(); auto dentry = kernel::filesystem::dentry{nullptr, inode, "test"}; WHEN("setting a flag") diff --git a/kernel/src/test_support/filesystem/inode.cpp b/kernel/src/test_support/filesystem/inode.cpp new file mode 100644 index 0000000..6352d5a --- /dev/null +++ b/kernel/src/test_support/filesystem/inode.cpp @@ -0,0 +1,22 @@ +#include "kernel/test_support/filesystem/inode.hpp" + +#include "kernel/filesystem/inode.hpp" + +#include + +namespace kernel::tests::filesystem +{ + inode::inode() + : kernel::filesystem::inode(inode_kind::regular) + {} + + auto inode::read(void *, size_t, size_t) const -> size_t + { + return 0; + } + + auto inode::write(void const *, size_t, size_t) -> size_t + { + return 0; + } +} // namespace kernel::tests::filesystem \ No newline at end of file -- cgit v1.2.3 From 2500898ec94d5071fddb32432ed8041b8e9de26c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 20:55:49 +0200 Subject: add test_support filesystem --- kernel/CMakeLists.txt | 1 + .../kernel/test_support/filesystem/filesystem.hpp | 22 ++++++++++++++++++++++ kernel/src/test_support/filesystem/filesystem.cpp | 17 +++++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 kernel/include/kernel/test_support/filesystem/filesystem.hpp create mode 100644 kernel/src/test_support/filesystem/filesystem.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 0025e4c..502efcc 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -104,6 +104,7 @@ else() "src/test_support/devices/block_device.cpp" "src/test_support/devices/character_device.cpp" "src/test_support/filesystem/inode.cpp" + "src/test_support/filesystem/filesystem.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" "src/test_support/page_mapper.cpp" diff --git a/kernel/include/kernel/test_support/filesystem/filesystem.hpp b/kernel/include/kernel/test_support/filesystem/filesystem.hpp new file mode 100644 index 0000000..13aade4 --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/filesystem.hpp @@ -0,0 +1,22 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_FILESYSTEM_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_FILESYSTEM_HPP + +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/filesystem/inode.hpp" + +#include + +#include + +namespace kernel::tests::filesystem +{ + struct filesystem : kernel::filesystem::filesystem + { + filesystem() = default; + + auto lookup(kstd::shared_ptr const & parent, std::string_view name) + -> kstd::shared_ptr override; + }; +} // namespace kernel::tests::filesystem + +#endif diff --git a/kernel/src/test_support/filesystem/filesystem.cpp b/kernel/src/test_support/filesystem/filesystem.cpp new file mode 100644 index 0000000..225d096 --- /dev/null +++ b/kernel/src/test_support/filesystem/filesystem.cpp @@ -0,0 +1,17 @@ +#include "kernel/test_support/filesystem/filesystem.hpp" + +#include "kernel/filesystem/inode.hpp" +#include "kernel/test_support/filesystem/inode.hpp" + +#include + +#include + +namespace kernel::tests::filesystem +{ + auto filesystem::lookup(kstd::shared_ptr const &, std::string_view) + -> kstd::shared_ptr + { + return kstd::make_shared(); + } +} // namespace kernel::tests::filesystem \ No newline at end of file -- cgit v1.2.3 From b36f4ed031bf8da10ccf2b97c9a61d71e672621e Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 20:56:06 +0200 Subject: add mount tests --- kernel/CMakeLists.txt | 1 + kernel/src/filesystem/mount.tests.cpp | 49 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 kernel/src/filesystem/mount.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 502efcc..c00ec14 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -133,6 +133,7 @@ else() # Filesystem Subsystem Tests "src/filesystem/dentry.tests.cpp" "src/filesystem/device_inode.tests.cpp" + "src/filesystem/mount.tests.cpp" # Storage Subsystem Tests "src/devices/block_device_utils.tests.cpp" diff --git a/kernel/src/filesystem/mount.tests.cpp b/kernel/src/filesystem/mount.tests.cpp new file mode 100644 index 0000000..4c4393a --- /dev/null +++ b/kernel/src/filesystem/mount.tests.cpp @@ -0,0 +1,49 @@ +#include "kernel/filesystem/mount.hpp" + +#include "kernel/filesystem/dentry.hpp" +#include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/filesystem/filesystem.hpp" +#include "kernel/test_support/filesystem/inode.hpp" + +#include +#include +#include + +#include + +SCENARIO("Mount construction", "[filesystem][mount]") +{ + GIVEN("a filesystem and a root dentry") + { + auto fs = kstd::make_shared(); + auto root_inode = kstd::make_shared(); + auto root_dentry = kstd::make_shared(nullptr, root_inode, "/"); + + WHEN("constructing a mount with the filesystem and root dentry") + { + auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, "/", nullptr}; + + THEN("the mount has the correct filesystem, root dentry, mount dentry, and mount path") + { + REQUIRE(mount.get_filesystem() == fs); + REQUIRE(mount.root_dentry() == root_dentry); + REQUIRE(mount.get_mount_dentry() == root_dentry); + REQUIRE(mount.get_mount_path() == "/"); + } + + THEN("the mount has no parent mount") + { + REQUIRE(mount.get_parent_mount() == nullptr); + } + } + + WHEN("constructing a mount with a null filesystem") + { + THEN("the constructor panics") + { + REQUIRE_THROWS_AS((kernel::filesystem::mount{root_dentry, root_dentry, nullptr, "/", nullptr}), + kernel::tests::cpu::halt); + } + } + } +} -- cgit v1.2.3 From f4e210b1e6169df99db621ca624555027047bc50 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 21:08:08 +0200 Subject: add mount_table tests --- kernel/CMakeLists.txt | 1 + kernel/src/filesystem/mount_table.tests.cpp | 164 ++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 kernel/src/filesystem/mount_table.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index c00ec14..d7f2cc4 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -133,6 +133,7 @@ else() # Filesystem Subsystem Tests "src/filesystem/dentry.tests.cpp" "src/filesystem/device_inode.tests.cpp" + "src/filesystem/mount_table.tests.cpp" "src/filesystem/mount.tests.cpp" # Storage Subsystem Tests diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp new file mode 100644 index 0000000..9f390c6 --- /dev/null +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -0,0 +1,164 @@ +#include "kernel/filesystem/mount_table.hpp" + +#include "kernel/filesystem/dentry.hpp" +#include "kernel/filesystem/mount.hpp" +#include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/filesystem/filesystem.hpp" +#include "kernel/test_support/filesystem/inode.hpp" + +#include +#include +#include + +#include + +#include + +SCENARIO("Mount table construction", "[filesystem][mount_table]") +{ + GIVEN("an empty mount table") + { + kernel::filesystem::mount_table table; + + THEN("finding any mount returns null") + { + REQUIRE(table.find_longest_prefix_mount("/") == nullptr); + REQUIRE(table.find_longest_prefix_mount("/any/path") == nullptr); + } + + THEN("removing any mount returns mount_not_found") + { + REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::mount_not_found); + REQUIRE(table.remove_mount("/any/path") == kernel::filesystem::mount_table::operation_result::mount_not_found); + } + } +} + +SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem][mount_table]") +{ + GIVEN("a mount table and some mounts") + { + kernel::filesystem::mount_table table; + + auto fs1 = kstd::make_shared(); + auto root_inode1 = kstd::make_shared(); + auto root_dentry1 = kstd::make_shared(nullptr, root_inode1, "/"); + auto mount1 = kstd::make_shared(root_dentry1, root_dentry1, fs1, "/", nullptr); + + auto fs2 = kstd::make_shared(); + auto root_inode2 = kstd::make_shared(); + auto root_dentry2 = kstd::make_shared(nullptr, root_inode2, "/"); + auto mount2 = kstd::make_shared(root_dentry1, root_dentry2, fs2, "/mnt", nullptr); + + table.add_mount(mount1); + table.add_mount(mount2); + + THEN("dentry flags are set correctly for mounted dentries") + { + REQUIRE(root_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + } + + THEN("finding mounts by path returns the correct mount") + { + REQUIRE(table.find_longest_prefix_mount("/") == mount1); + REQUIRE(table.find_longest_prefix_mount("/file") == mount1); + REQUIRE(table.find_longest_prefix_mount("/mnt") == mount2); + REQUIRE(table.find_longest_prefix_mount("/mnt/file") == mount2); + REQUIRE(table.find_longest_prefix_mount("/other") == mount1); + } + + THEN("removing a mount that has no child mounts succeeds") + { + REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::removed); + REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(table.find_longest_prefix_mount("/mnt") == nullptr); + } + + THEN("removing a mount that does not exist returns mount_not_found") + { + REQUIRE(table.remove_mount("/nonexistent") == kernel::filesystem::mount_table::operation_result::mount_not_found); + } + } + + GIVEN("multiple mounts with the same path") + { + kernel::filesystem::mount_table table; + + auto fs1 = kstd::make_shared(); + auto root_inode1 = kstd::make_shared(); + auto root_dentry1 = kstd::make_shared(nullptr, root_inode1, "/"); + auto mount1 = kstd::make_shared(root_dentry1, root_dentry1, fs1, "/", nullptr); + + auto fs2 = kstd::make_shared(); + auto root_inode2 = kstd::make_shared(); + auto root_dentry2 = kstd::make_shared(nullptr, root_inode2, "/"); + auto mount2 = kstd::make_shared(root_dentry1, root_dentry2, fs2, "/", mount1); + + table.add_mount(mount1); + table.add_mount(mount2); + + THEN("finding mounts by path returns the correct mount based on longest prefix") + { + REQUIRE(table.find_longest_prefix_mount("/") == mount2); + REQUIRE(table.find_longest_prefix_mount("/file") == mount2); + REQUIRE(table.find_longest_prefix_mount("/mnt") == mount2); + REQUIRE(table.find_longest_prefix_mount("/mnt/file") == mount2); + REQUIRE(table.find_longest_prefix_mount("/other") == mount2); + } + + THEN("removing the topmost mount with the same path succeeds") + { + REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::removed); + REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(table.find_longest_prefix_mount("/") == mount1); + } + } + + GIVEN("a mount with child mounts") + { + kernel::filesystem::mount_table table; + + auto fs1 = kstd::make_shared(); + auto root_inode1 = kstd::make_shared(); + auto root_dentry1 = kstd::make_shared(nullptr, root_inode1, "/"); + auto mount1 = kstd::make_shared(root_dentry1, root_dentry1, fs1, "/", nullptr); + + auto fs2 = kstd::make_shared(); + auto root_inode2 = kstd::make_shared(); + auto root_dentry2 = kstd::make_shared(nullptr, root_inode2, "/"); + auto mount2 = kstd::make_shared(root_dentry1, root_dentry2, fs2, "/mnt", mount1); + + auto fs3 = kstd::make_shared(); + auto root_inode3 = kstd::make_shared(); + auto root_dentry3 = kstd::make_shared(nullptr, root_inode3, "/"); + auto mount3 = kstd::make_shared(root_dentry2, root_dentry3, fs3, "/mnt/submnt", mount2); + + table.add_mount(mount1); + table.add_mount(mount2); + table.add_mount(mount3); + + THEN("finding mounts by path returns the correct mount based on longest prefix") + { + REQUIRE(table.find_longest_prefix_mount("/") == mount1); + REQUIRE(table.find_longest_prefix_mount("/file") == mount1); + REQUIRE(table.find_longest_prefix_mount("/mnt") == mount2); + REQUIRE(table.find_longest_prefix_mount("/mnt/file") == mount2); + REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == mount3); + REQUIRE(table.find_longest_prefix_mount("/other") == mount1); + } + + THEN("removing a mount with child mounts returns has_child_mounts") + { + REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::has_child_mounts); + REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::has_child_mounts); + } + + THEN("removing a leaf mount succeeds") + { + REQUIRE(table.remove_mount("/mnt/submnt") == kernel::filesystem::mount_table::operation_result::removed); + REQUIRE(!root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == nullptr); + } + } +} -- cgit v1.2.3 From 7a7c0106257665358e68bbbc99b41acc0c87c0ba Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 21:16:14 +0200 Subject: fix mount table tests --- kernel/src/filesystem/mount_table.tests.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp index 9f390c6..439fe97 100644 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -2,7 +2,6 @@ #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/mount.hpp" -#include "kernel/test_support/cpu.hpp" #include "kernel/test_support/filesystem/filesystem.hpp" #include "kernel/test_support/filesystem/inode.hpp" @@ -56,7 +55,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("dentry flags are set correctly for mounted dentries") { REQUIRE(root_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); - REQUIRE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); } THEN("finding mounts by path returns the correct mount") @@ -72,7 +71,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] { REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::removed); REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); - REQUIRE(table.find_longest_prefix_mount("/mnt") == nullptr); + REQUIRE(table.find_longest_prefix_mount("/mnt") == mount1); } THEN("removing a mount that does not exist returns mount_not_found") @@ -158,7 +157,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] { REQUIRE(table.remove_mount("/mnt/submnt") == kernel::filesystem::mount_table::operation_result::removed); REQUIRE(!root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); - REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == nullptr); + REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == mount2); } } } -- cgit v1.2.3 From af048d3e550bc2a7a6526f4c9714871e32bf7cf4 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 21:38:50 +0200 Subject: add open_file_description tests --- kernel/CMakeLists.txt | 1 + .../kernel/filesystem/open_file_description.hpp | 6 +++ kernel/src/filesystem/open_file_description.cpp | 6 +++ .../src/filesystem/open_file_description.tests.cpp | 60 ++++++++++++++++++++++ kernel/src/test_support/filesystem/inode.cpp | 8 +-- 5 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 kernel/src/filesystem/open_file_description.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index d7f2cc4..f578632 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -135,6 +135,7 @@ else() "src/filesystem/device_inode.tests.cpp" "src/filesystem/mount_table.tests.cpp" "src/filesystem/mount.tests.cpp" + "src/filesystem/open_file_description.tests.cpp" # Storage Subsystem Tests "src/devices/block_device_utils.tests.cpp" diff --git a/kernel/include/kernel/filesystem/open_file_description.hpp b/kernel/include/kernel/filesystem/open_file_description.hpp index ed878a7..738afd4 100644 --- a/kernel/include/kernel/filesystem/open_file_description.hpp +++ b/kernel/include/kernel/filesystem/open_file_description.hpp @@ -46,6 +46,12 @@ namespace kernel::filesystem */ auto write(void const * buffer, size_t size) -> size_t; + /** + @brief Returns the current file offset for this open file description. + @return The current file offset in bytes. + */ + [[nodiscard]] auto offset() const -> size_t; + private: kstd::shared_ptr m_inode; size_t m_offset; diff --git a/kernel/src/filesystem/open_file_description.cpp b/kernel/src/filesystem/open_file_description.cpp index 8c04225..f049a34 100644 --- a/kernel/src/filesystem/open_file_description.cpp +++ b/kernel/src/filesystem/open_file_description.cpp @@ -32,4 +32,10 @@ namespace kernel::filesystem m_offset += written_bytes; return written_bytes; } + + auto open_file_description::offset() const -> size_t + { + return m_offset; + } + } // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/open_file_description.tests.cpp b/kernel/src/filesystem/open_file_description.tests.cpp new file mode 100644 index 0000000..f045094 --- /dev/null +++ b/kernel/src/filesystem/open_file_description.tests.cpp @@ -0,0 +1,60 @@ +#include "kernel/filesystem/open_file_description.hpp" + +#include "kernel/test_support/filesystem/inode.hpp" + +#include +#include +#include + +#include + +SCENARIO("Open file description construction", "[filesystem][open_file_description]") +{ + GIVEN("an inode and an open file description for that inode") + { + auto inode = kstd::make_shared(); + auto file_description = kernel::filesystem::open_file_description{inode}; + + THEN("the initial offset is zero") + { + REQUIRE(file_description.offset() == 0); + } + } +} + +SCENARIO("Open file description read/write offset management", "[filesystem][open_file_description]") +{ + GIVEN("an inode that tracks read/write calls and an open file description for that inode") + { + auto inode = kstd::make_shared(); + auto file_description = kernel::filesystem::open_file_description{inode}; + + THEN("the offset is updated correctly after reads") + { + REQUIRE(file_description.read(nullptr, 100) == 100); + REQUIRE(file_description.offset() == 100); + REQUIRE(file_description.read(nullptr, 50) == 50); + REQUIRE(file_description.offset() == 150); + } + + THEN("the offset is updated correctly after writes") + { + REQUIRE(file_description.write(nullptr, 200) == 200); + REQUIRE(file_description.offset() == 200); + REQUIRE(file_description.write(nullptr, 25) == 25); + REQUIRE(file_description.offset() == 225); + } + + THEN("reads and writes both update the same offset") + { + REQUIRE(file_description.read(nullptr, 10) == 10); + REQUIRE(file_description.offset() == 10); + REQUIRE(file_description.write(nullptr, 20) == 20); + REQUIRE(file_description.offset() == 30); + REQUIRE(file_description.read(nullptr, 5) == 5); + REQUIRE(file_description.offset() == 35); + REQUIRE(file_description.write(nullptr, 15) == 15); + REQUIRE(file_description.offset() == 50); + } + } +} diff --git a/kernel/src/test_support/filesystem/inode.cpp b/kernel/src/test_support/filesystem/inode.cpp index 6352d5a..5df7bcd 100644 --- a/kernel/src/test_support/filesystem/inode.cpp +++ b/kernel/src/test_support/filesystem/inode.cpp @@ -10,13 +10,13 @@ namespace kernel::tests::filesystem : kernel::filesystem::inode(inode_kind::regular) {} - auto inode::read(void *, size_t, size_t) const -> size_t + auto inode::read(void *, size_t, size_t size) const -> size_t { - return 0; + return size; } - auto inode::write(void const *, size_t, size_t) -> size_t + auto inode::write(void const *, size_t, size_t size) -> size_t { - return 0; + return size; } } // namespace kernel::tests::filesystem \ No newline at end of file -- cgit v1.2.3 From 530a478b3e755ef4585b27422a33bd0de1556ce2 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 22:12:51 +0200 Subject: add file_descriptor_table tests --- kernel/CMakeLists.txt | 1 + .../kernel/filesystem/file_descriptor_table.hpp | 6 + kernel/src/filesystem/file_descriptor_table.cpp | 5 + .../src/filesystem/file_descriptor_table.tests.cpp | 155 +++++++++++++++++++++ 4 files changed, 167 insertions(+) create mode 100644 kernel/src/filesystem/file_descriptor_table.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index f578632..b664def 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -133,6 +133,7 @@ else() # Filesystem Subsystem Tests "src/filesystem/dentry.tests.cpp" "src/filesystem/device_inode.tests.cpp" + "src/filesystem/file_descriptor_table.tests.cpp" "src/filesystem/mount_table.tests.cpp" "src/filesystem/mount.tests.cpp" "src/filesystem/open_file_description.tests.cpp" diff --git a/kernel/include/kernel/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/filesystem/file_descriptor_table.hpp index 5d52c19..cc31511 100644 --- a/kernel/include/kernel/filesystem/file_descriptor_table.hpp +++ b/kernel/include/kernel/filesystem/file_descriptor_table.hpp @@ -28,6 +28,12 @@ namespace kernel::filesystem */ auto static get() -> file_descriptor_table &; + /** + @brief Reset the file descriptor table to an empty state. This method is intended for testing purposes to allow + resetting the state of the file descriptor table between test cases. + */ + auto static reset() -> void; + /** @brief Destructor for the file descriptor table. */ diff --git a/kernel/src/filesystem/file_descriptor_table.cpp b/kernel/src/filesystem/file_descriptor_table.cpp index 287aea2..a31e2e6 100644 --- a/kernel/src/filesystem/file_descriptor_table.cpp +++ b/kernel/src/filesystem/file_descriptor_table.cpp @@ -37,6 +37,11 @@ namespace kernel::filesystem return *global_file_descriptor_table; } + auto file_descriptor_table::reset() -> void + { + global_file_descriptor_table.reset(); + } + auto file_descriptor_table::add_file(kstd::shared_ptr const & file_description) -> int { if (!file_description) diff --git a/kernel/src/filesystem/file_descriptor_table.tests.cpp b/kernel/src/filesystem/file_descriptor_table.tests.cpp new file mode 100644 index 0000000..c431d01 --- /dev/null +++ b/kernel/src/filesystem/file_descriptor_table.tests.cpp @@ -0,0 +1,155 @@ +#include "kernel/filesystem/file_descriptor_table.hpp" + +#include "kernel/filesystem/open_file_description.hpp" +#include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/filesystem/inode.hpp" + +#include +#include +#include + +#include + +struct file_descriptor_table_RAII +{ + file_descriptor_table_RAII() + { + kernel::filesystem::file_descriptor_table::init(); + } + ~file_descriptor_table_RAII() + { + kernel::filesystem::file_descriptor_table::reset(); + } +}; + +struct file_descriptor_table_reset_only +{ + ~file_descriptor_table_reset_only() + { + kernel::filesystem::file_descriptor_table::reset(); + } +}; + +SCENARIO_METHOD(file_descriptor_table_reset_only, "File descriptor table initialization", + "[filesystem][file_descriptor_table]") +{ + THEN("accessing the file descriptor table before initialization panics") + { + REQUIRE_THROWS_AS(kernel::filesystem::file_descriptor_table::get(), kernel::tests::cpu::halt); + } + + THEN("the file descriptor table can be initialized and accessed") + { + kernel::filesystem::file_descriptor_table::init(); + REQUIRE_NOTHROW(kernel::filesystem::file_descriptor_table::get()); + } + + THEN("initializing the file descriptor table more than once panics") + { + kernel::filesystem::file_descriptor_table::init(); + REQUIRE_THROWS_AS(kernel::filesystem::file_descriptor_table::init(), kernel::tests::cpu::halt); + } +} + +SCENARIO_METHOD(file_descriptor_table_RAII, "File descriptor table add/get file", "[filesystem][file_descriptor_table]") +{ + GIVEN("a file descriptor table and an open file description") + { + auto & table = kernel::filesystem::file_descriptor_table::get(); + auto inode = kstd::make_shared(); + auto file_description_1 = kstd::make_shared(inode); + auto file_description_2 = kstd::make_shared(inode); + + WHEN("adding the open file description to the file descriptor table") + { + auto fd_1 = table.add_file(file_description_1); + auto fd_2 = table.add_file(file_description_2); + auto fd_3 = table.add_file(file_description_2); + + THEN("a valid file descriptor is returned") + { + REQUIRE(fd_1 == 0); + REQUIRE(fd_2 == 1); + REQUIRE(fd_3 == 1); + } + + THEN("the file description can be retrieved using the returned file descriptor") + { + auto retrieved_description = table.get_file(fd_1); + REQUIRE(retrieved_description == file_description_1); + } + } + } + + GIVEN("a invalid open file description") + { + auto & table = kernel::filesystem::file_descriptor_table::get(); + + THEN("adding a null file description returns an error code") + { + auto fd = table.add_file(nullptr); + REQUIRE(fd == -1); + } + + THEN("retrieving a file description with a negative file descriptor returns a null pointer") + { + auto retrieved_description = table.get_file(-1); + REQUIRE(retrieved_description == nullptr); + } + + THEN("retrieving a file description with an out-of-bounds file descriptor returns a null pointer") + { + auto retrieved_description = table.get_file(1000); + REQUIRE(retrieved_description == nullptr); + } + } +} + +SCENARIO_METHOD(file_descriptor_table_RAII, "File descriptor table remove file", "[filesystem][file_descriptor_table]") +{ + GIVEN("a file descriptor table with an open file description") + { + auto & table = kernel::filesystem::file_descriptor_table::get(); + auto inode = kstd::make_shared(); + auto file_description = kstd::make_shared(inode); + auto fd = table.add_file(file_description); + + WHEN("removing the file description using the file descriptor") + { + table.remove_file(fd); + + THEN("the file description can no longer be retrieved using the file descriptor") + { + auto retrieved_description = table.get_file(fd); + REQUIRE(retrieved_description == nullptr); + } + } + + WHEN("removing a file description the other file descriptor keep the same index") + { + auto fd2 = table.add_file(file_description); + table.remove_file(fd); + + THEN("the second file description can still be retrieved using its file descriptor") + { + auto retrieved_description = table.get_file(fd2); + REQUIRE(retrieved_description == file_description); + } + } + } + + GIVEN("an invalid file descriptor") + { + auto & table = kernel::filesystem::file_descriptor_table::get(); + + THEN("removing a file with a negative file descriptor does nothing") + { + REQUIRE_NOTHROW(table.remove_file(-1)); + } + + THEN("removing a file with an out-of-bounds file descriptor does nothing") + { + REQUIRE_NOTHROW(table.remove_file(1000)); + } + } +} \ No newline at end of file -- cgit v1.2.3 From 643cbff68aa0a5e029b9ce46eb69846c8b01dca7 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 22:17:15 +0200 Subject: fix test --- kernel/src/filesystem/file_descriptor_table.tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/src/filesystem/file_descriptor_table.tests.cpp b/kernel/src/filesystem/file_descriptor_table.tests.cpp index c431d01..d8c05ca 100644 --- a/kernel/src/filesystem/file_descriptor_table.tests.cpp +++ b/kernel/src/filesystem/file_descriptor_table.tests.cpp @@ -70,7 +70,7 @@ SCENARIO_METHOD(file_descriptor_table_RAII, "File descriptor table add/get file" { REQUIRE(fd_1 == 0); REQUIRE(fd_2 == 1); - REQUIRE(fd_3 == 1); + REQUIRE(fd_3 == 2); } THEN("the file description can be retrieved using the returned file descriptor") -- cgit v1.2.3 From 60118b1df5196c4e416ddd6ad2a40be062f68251 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 22:57:55 +0200 Subject: add devfs and rootfs inode tests --- kernel/CMakeLists.txt | 2 + kernel/src/filesystem/devfs/inode.tests.cpp | 54 ++++++++++++++++++ kernel/src/filesystem/rootfs/inode.tests.cpp | 83 ++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 kernel/src/filesystem/devfs/inode.tests.cpp create mode 100644 kernel/src/filesystem/rootfs/inode.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index b664def..e3a357b 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -131,6 +131,8 @@ else() "src/memory/block_list_allocator.tests.cpp" # Filesystem Subsystem Tests + "src/filesystem/devfs/inode.tests.cpp" + "src/filesystem/rootfs/inode.tests.cpp" "src/filesystem/dentry.tests.cpp" "src/filesystem/device_inode.tests.cpp" "src/filesystem/file_descriptor_table.tests.cpp" diff --git a/kernel/src/filesystem/devfs/inode.tests.cpp b/kernel/src/filesystem/devfs/inode.tests.cpp new file mode 100644 index 0000000..50e34a7 --- /dev/null +++ b/kernel/src/filesystem/devfs/inode.tests.cpp @@ -0,0 +1,54 @@ +#include "kernel/filesystem/devfs/inode.hpp" + +#include +#include +#include + +#include + +#include + +SCENARIO("Devfs inode creation", "[filesystem][devfs][inode]") +{ + GIVEN("a devfs inode") + { + auto inode = kernel::filesystem::devfs::inode{}; + + THEN("the inode has the correct kind") + { + REQUIRE(inode.is_directory()); + REQUIRE_FALSE(inode.is_device()); + REQUIRE_FALSE(inode.is_regular()); + } + } +} + +SCENARIO("Devfs inode read/write", "[filesystem][devfs][inode]") +{ + GIVEN("a devfs inode") + { + auto inode = kernel::filesystem::devfs::inode{}; + + WHEN("attempting to read from the devfs inode") + { + kstd::vector buffer(512); + auto bytes_read = inode.read(buffer.data(), 0, buffer.size()); + + THEN("no bytes are read") + { + REQUIRE(bytes_read == 0); + } + } + + WHEN("attempting to write to the devfs inode") + { + kstd::vector buffer(512); + auto bytes_written = inode.write(buffer.data(), 0, buffer.size()); + + THEN("no bytes are written") + { + REQUIRE(bytes_written == 0); + } + } + } +} diff --git a/kernel/src/filesystem/rootfs/inode.tests.cpp b/kernel/src/filesystem/rootfs/inode.tests.cpp new file mode 100644 index 0000000..a0c5938 --- /dev/null +++ b/kernel/src/filesystem/rootfs/inode.tests.cpp @@ -0,0 +1,83 @@ +#include "kernel/filesystem/rootfs/inode.hpp" + +#include +#include +#include + +#include + +SCENARIO("Rootfs inode creation", "[filesystem][rootfs][inode]") +{ + GIVEN("a rootfs inode") + { + auto inode = kernel::filesystem::rootfs::inode{}; + + THEN("the inode has the correct kind") + { + REQUIRE(inode.is_directory()); + REQUIRE_FALSE(inode.is_device()); + REQUIRE_FALSE(inode.is_regular()); + } + + THEN("the inode has no children") + { + REQUIRE(inode.lookup_child("child") == nullptr); + } + } +} + +SCENARIO("Rootfs inode child management", "[filesystem][rootfs][inode]") +{ + GIVEN("a rootfs inode") + { + auto inode = kernel::filesystem::rootfs::inode{}; + + WHEN("adding a child inode") + { + inode.add_child("child"); + inode.add_child("another child"); + + THEN("the child can be looked up by name") + { + auto child_inode = inode.lookup_child("child"); + REQUIRE(child_inode != nullptr); + REQUIRE(child_inode->is_directory()); + } + + THEN("looking up a non-existent child returns null") + { + REQUIRE(inode.lookup_child("nonexistent") == nullptr); + } + } + } +} + +SCENARIO("Rootfs inode read/write", "[filesystem][rootfs][inode]") +{ + GIVEN("a rootfs inode") + { + auto inode = kernel::filesystem::rootfs::inode{}; + + WHEN("reading from the inode") + { + kstd::vector buffer(10); + auto bytes_read = inode.read(buffer.data(), 0, buffer.size()); + + THEN("no bytes are read") + { + REQUIRE(bytes_read == 0); + } + } + + WHEN("writing to the inode") + { + kstd::vector buffer(10, 'x'); + auto bytes_written = inode.write(buffer.data(), 0, buffer.size()); + + THEN("no bytes are written") + { + REQUIRE(bytes_written == 0); + } + } + } +} -- cgit v1.2.3 From 9ce8ed3dd3aa5f6e21b53d02bac4f62eb8b3f337 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 8 Apr 2026 23:04:40 +0200 Subject: add rootfs filesystem tests --- kernel/CMakeLists.txt | 1 + kernel/src/filesystem/rootfs/filesystem.tests.cpp | 45 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 kernel/src/filesystem/rootfs/filesystem.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index e3a357b..1cbb9e4 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -132,6 +132,7 @@ else() # Filesystem Subsystem Tests "src/filesystem/devfs/inode.tests.cpp" + "src/filesystem/rootfs/filesystem.tests.cpp" "src/filesystem/rootfs/inode.tests.cpp" "src/filesystem/dentry.tests.cpp" "src/filesystem/device_inode.tests.cpp" diff --git a/kernel/src/filesystem/rootfs/filesystem.tests.cpp b/kernel/src/filesystem/rootfs/filesystem.tests.cpp new file mode 100644 index 0000000..b013f78 --- /dev/null +++ b/kernel/src/filesystem/rootfs/filesystem.tests.cpp @@ -0,0 +1,45 @@ +#include "kernel/filesystem/rootfs/filesystem.hpp" + +#include "kernel/filesystem/filesystem.hpp" + +#include +#include +#include + +#include + +SCENARIO("Rootfs filesystem mount and lookup", "[filesystem][rootfs][filesystem]") +{ + GIVEN("a mounted rootfs filesystem") + { + auto fs = kernel::filesystem::rootfs::filesystem{}; + auto result = fs.mount(nullptr); + + THEN("the filesystem can be mounted successfully") + { + REQUIRE(result == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.root_inode() != nullptr); + } + + THEN("looking up the 'dev' directory returns a valid inode") + { + auto dev_inode = fs.lookup(fs.root_inode(), "dev"); + REQUIRE(dev_inode != nullptr); + REQUIRE(dev_inode->is_directory()); + } + + THEN("looking up a non-existent directory returns null") + { + auto non_existent_inode_1 = fs.lookup(fs.root_inode(), ""); + REQUIRE(non_existent_inode_1 == nullptr); + auto non_existent_inode_2 = fs.lookup(fs.root_inode(), "nonexistent"); + REQUIRE(non_existent_inode_2 == nullptr); + } + + THEN("looking up with a null parent inode returns null") + { + auto result = fs.lookup(nullptr, "dev"); + REQUIRE(result == nullptr); + } + } +} -- cgit v1.2.3 From 3c9ad45492d7417c65594fa7fa2fb9a8d5439276 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 9 Apr 2026 08:32:51 +0200 Subject: add deinit functions for singletons in tests --- .../kernel/filesystem/file_descriptor_table.hpp | 6 --- .../include/kernel/test_support/boot_modules.hpp | 10 +++++ .../test_support/devices/storage/management.hpp | 10 +++++ .../filesystem/file_descriptor_table.hpp | 10 +++++ .../include/kernel/test_support/filesystem/vfs.hpp | 10 +++++ kernel/kapi/boot_modules.cpp | 19 ++++++--- kernel/src/devices/storage/management.cpp | 28 ++++++++----- kernel/src/filesystem/file_descriptor_table.cpp | 25 ++++++------ .../src/filesystem/file_descriptor_table.tests.cpp | 46 +--------------------- kernel/src/filesystem/vfs.cpp | 24 +++++++---- kernel/src/test_support/state_reset_listener.cpp | 12 ++++++ 11 files changed, 116 insertions(+), 84 deletions(-) create mode 100644 kernel/include/kernel/test_support/boot_modules.hpp create mode 100644 kernel/include/kernel/test_support/devices/storage/management.hpp create mode 100644 kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp create mode 100644 kernel/include/kernel/test_support/filesystem/vfs.hpp diff --git a/kernel/include/kernel/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/filesystem/file_descriptor_table.hpp index cc31511..5d52c19 100644 --- a/kernel/include/kernel/filesystem/file_descriptor_table.hpp +++ b/kernel/include/kernel/filesystem/file_descriptor_table.hpp @@ -28,12 +28,6 @@ namespace kernel::filesystem */ auto static get() -> file_descriptor_table &; - /** - @brief Reset the file descriptor table to an empty state. This method is intended for testing purposes to allow - resetting the state of the file descriptor table between test cases. - */ - auto static reset() -> void; - /** @brief Destructor for the file descriptor table. */ diff --git a/kernel/include/kernel/test_support/boot_modules.hpp b/kernel/include/kernel/test_support/boot_modules.hpp new file mode 100644 index 0000000..2343a10 --- /dev/null +++ b/kernel/include/kernel/test_support/boot_modules.hpp @@ -0,0 +1,10 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_BOOT_MODULES_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_BOOT_MODULES_HPP + +namespace kernel::tests::boot_modules +{ + //! Deinitialize the boot module registry. + auto deinit() -> void; +} // namespace kernel::tests::boot_modules + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/test_support/devices/storage/management.hpp b/kernel/include/kernel/test_support/devices/storage/management.hpp new file mode 100644 index 0000000..7c0d304 --- /dev/null +++ b/kernel/include/kernel/test_support/devices/storage/management.hpp @@ -0,0 +1,10 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_STORAGE_MANAGEMENT_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_STORAGE_MANAGEMENT_HPP + +namespace kernel::tests::devices::storage +{ + //! Deinitialize the storage management singleton. + auto deinit() -> void; +} // namespace kernel::tests::devices::storage + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp new file mode 100644 index 0000000..bbc0f4a --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp @@ -0,0 +1,10 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP + +namespace kernel::tests::filesystem::file_descriptor_table +{ + //! Deinitialize the file descriptor table singleton. + auto deinit() -> void; +} // namespace kernel::tests::filesystem::file_descriptor_table + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/test_support/filesystem/vfs.hpp b/kernel/include/kernel/test_support/filesystem/vfs.hpp new file mode 100644 index 0000000..739e353 --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/vfs.hpp @@ -0,0 +1,10 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_VFS_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_VFS_HPP + +namespace kernel::tests::filesystem::vfs +{ + //! Deinitialize the VFS singleton. + auto deinit() -> void; +} // namespace kernel::tests::filesystem::vfs + +#endif \ No newline at end of file diff --git a/kernel/kapi/boot_modules.cpp b/kernel/kapi/boot_modules.cpp index 5a0ef7f..0549368 100644 --- a/kernel/kapi/boot_modules.cpp +++ b/kernel/kapi/boot_modules.cpp @@ -4,14 +4,13 @@ #include -namespace kapi::boot_modules +namespace { + constinit auto static registry = std::optional{}; +} // namespace - namespace - { - constinit auto static registry = std::optional{}; - } // namespace - +namespace kapi::boot_modules +{ auto set_boot_module_registry(boot_module_registry & new_registry) -> void { if (registry) @@ -32,3 +31,11 @@ namespace kapi::boot_modules return *registry; } } // namespace kapi::boot_modules + +namespace kernel::tests::boot_modules +{ + auto deinit() -> void + { + registry.reset(); + } +} // namespace kernel::tests::boot_modules diff --git a/kernel/src/devices/storage/management.cpp b/kernel/src/devices/storage/management.cpp index d440bf0..14a045a 100644 --- a/kernel/src/devices/storage/management.cpp +++ b/kernel/src/devices/storage/management.cpp @@ -1,9 +1,9 @@ #include "kernel/devices/storage/management.hpp" #include "kapi/boot_modules.hpp" +#include "kapi/devices/device.hpp" #include "kapi/system.hpp" -#include "kapi/devices/device.hpp" #include "kernel/devices/storage/controller.hpp" #include "kernel/devices/storage/ram_disk/controller.hpp" @@ -14,17 +14,17 @@ #include #include -namespace kernel::devices::storage +namespace { - namespace - { - constexpr size_t static MINORS_PER_DEVICE = 16; - constexpr size_t static START_MAJOR = 1; - constinit size_t static next_free_major = START_MAJOR; + constexpr size_t static MINORS_PER_DEVICE = 16; + constexpr size_t static START_MAJOR = 1; + constinit size_t static next_free_major = START_MAJOR; - constinit auto static active_storage_management = std::optional{}; - } // namespace + constinit auto static active_storage_management = std::optional{}; +} // namespace +namespace kernel::devices::storage +{ auto management::init() -> void { if (active_storage_management) @@ -81,5 +81,13 @@ namespace kernel::devices::storage { return device_by_major_minor(START_MAJOR, 0); } +} // namespace kernel::devices::storage -} // namespace kernel::devices::storage \ No newline at end of file +namespace kernel::tests::devices::storage +{ + auto deinit() -> void + { + active_storage_management.reset(); + next_free_major = START_MAJOR; + } +} // namespace kernel::tests::devices::storage diff --git a/kernel/src/filesystem/file_descriptor_table.cpp b/kernel/src/filesystem/file_descriptor_table.cpp index a31e2e6..1c062a1 100644 --- a/kernel/src/filesystem/file_descriptor_table.cpp +++ b/kernel/src/filesystem/file_descriptor_table.cpp @@ -10,13 +10,13 @@ #include #include -namespace kernel::filesystem +namespace { - namespace - { - constinit auto static global_file_descriptor_table = std::optional{}; - } // namespace + constinit auto static global_file_descriptor_table = std::optional{}; +} // namespace +namespace kernel::filesystem +{ auto file_descriptor_table::init() -> void { if (global_file_descriptor_table) @@ -37,11 +37,6 @@ namespace kernel::filesystem return *global_file_descriptor_table; } - auto file_descriptor_table::reset() -> void - { - global_file_descriptor_table.reset(); - } - auto file_descriptor_table::add_file(kstd::shared_ptr const & file_description) -> int { if (!file_description) @@ -92,4 +87,12 @@ namespace kernel::filesystem m_open_files.at(index) = nullptr; } -} // namespace kernel::filesystem \ No newline at end of file +} // namespace kernel::filesystem + +namespace kernel::tests::filesystem::file_descriptor_table +{ + auto deinit() -> void + { + global_file_descriptor_table.reset(); + } +} // namespace kernel::tests::filesystem::file_descriptor_table diff --git a/kernel/src/filesystem/file_descriptor_table.tests.cpp b/kernel/src/filesystem/file_descriptor_table.tests.cpp index d8c05ca..5aeadb2 100644 --- a/kernel/src/filesystem/file_descriptor_table.tests.cpp +++ b/kernel/src/filesystem/file_descriptor_table.tests.cpp @@ -1,7 +1,6 @@ #include "kernel/filesystem/file_descriptor_table.hpp" #include "kernel/filesystem/open_file_description.hpp" -#include "kernel/test_support/cpu.hpp" #include "kernel/test_support/filesystem/inode.hpp" #include @@ -10,48 +9,7 @@ #include -struct file_descriptor_table_RAII -{ - file_descriptor_table_RAII() - { - kernel::filesystem::file_descriptor_table::init(); - } - ~file_descriptor_table_RAII() - { - kernel::filesystem::file_descriptor_table::reset(); - } -}; - -struct file_descriptor_table_reset_only -{ - ~file_descriptor_table_reset_only() - { - kernel::filesystem::file_descriptor_table::reset(); - } -}; - -SCENARIO_METHOD(file_descriptor_table_reset_only, "File descriptor table initialization", - "[filesystem][file_descriptor_table]") -{ - THEN("accessing the file descriptor table before initialization panics") - { - REQUIRE_THROWS_AS(kernel::filesystem::file_descriptor_table::get(), kernel::tests::cpu::halt); - } - - THEN("the file descriptor table can be initialized and accessed") - { - kernel::filesystem::file_descriptor_table::init(); - REQUIRE_NOTHROW(kernel::filesystem::file_descriptor_table::get()); - } - - THEN("initializing the file descriptor table more than once panics") - { - kernel::filesystem::file_descriptor_table::init(); - REQUIRE_THROWS_AS(kernel::filesystem::file_descriptor_table::init(), kernel::tests::cpu::halt); - } -} - -SCENARIO_METHOD(file_descriptor_table_RAII, "File descriptor table add/get file", "[filesystem][file_descriptor_table]") +SCENARIO("File descriptor table add/get file", "[filesystem][file_descriptor_table]") { GIVEN("a file descriptor table and an open file description") { @@ -105,7 +63,7 @@ SCENARIO_METHOD(file_descriptor_table_RAII, "File descriptor table add/get file" } } -SCENARIO_METHOD(file_descriptor_table_RAII, "File descriptor table remove file", "[filesystem][file_descriptor_table]") +SCENARIO("File descriptor table remove file", "[filesystem][file_descriptor_table]") { GIVEN("a file descriptor table with an open file description") { diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 45ae053..67d1af2 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -17,13 +17,13 @@ #include #include -namespace kernel::filesystem +namespace { - namespace - { - constinit auto static active_vfs = std::optional{}; - } // namespace + constinit auto static active_vfs = std::optional{}; +} // namespace +namespace kernel::filesystem +{ auto vfs::init() -> void { if (active_vfs) @@ -47,7 +47,10 @@ namespace kernel::filesystem if (auto boot_device = storage_mgmt.determine_boot_device()) { auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device); - do_mount_internal("/", root_fs_root_dentry, boot_root_fs); + if (boot_root_fs) + { + do_mount_internal("/", root_fs_root_dentry, boot_root_fs); + } } auto device_fs = kstd::make_shared(); @@ -172,5 +175,12 @@ namespace kernel::filesystem return current_dentry; } +} // namespace kernel::filesystem -} // namespace kernel::filesystem \ No newline at end of file +namespace kernel::tests::filesystem::vfs +{ + auto deinit() -> void + { + active_vfs.reset(); + } +} // namespace kernel::tests::filesystem::vfs diff --git a/kernel/src/test_support/state_reset_listener.cpp b/kernel/src/test_support/state_reset_listener.cpp index 8c95cc8..af1091c 100644 --- a/kernel/src/test_support/state_reset_listener.cpp +++ b/kernel/src/test_support/state_reset_listener.cpp @@ -2,8 +2,13 @@ #include "kapi/cpu.hpp" #include "kapi/memory.hpp" +#include "kernel/filesystem/file_descriptor_table.hpp" +#include "kernel/test_support/boot_modules.hpp" #include "kernel/test_support/cio.hpp" #include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/devices/storage/management.hpp" +#include "kernel/test_support/filesystem/file_descriptor_table.hpp" +#include "kernel/test_support/filesystem/vfs.hpp" #include "kernel/test_support/memory.hpp" #include @@ -17,6 +22,8 @@ struct state_reset_listener : Catch::EventListenerBase void testCaseStarting(Catch::TestCaseInfo const &) override { + kernel::filesystem::file_descriptor_table::init(); + kapi::cio::init(); kapi::cpu::init(); kapi::memory::init(); @@ -24,6 +31,11 @@ struct state_reset_listener : Catch::EventListenerBase void testCaseEnded(Catch::TestCaseStats const &) override { + kernel::tests::filesystem::file_descriptor_table::deinit(); + kernel::tests::filesystem::vfs::deinit(); + kernel::tests::devices::storage::deinit(); + kernel::tests::boot_modules::deinit(); + kernel::tests::memory::deinit(); kernel::tests::cpu::deinit(); kernel::tests::cio::deinit(); -- cgit v1.2.3 From 97e83ad1466d5962a1e5f5f83fa4c951bfeafb2c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 9 Apr 2026 09:13:09 +0200 Subject: add devfs filesystem tests, and storage_boot_module_fixture to mock real boot modules --- kernel/CMakeLists.txt | 2 + .../test_support/devices/storage/management.hpp | 4 +- .../filesystem/storage_boot_module_fixture.hpp | 23 ++++++++ kernel/src/devices/storage/management.cpp | 4 +- kernel/src/filesystem/devfs/filesystem.tests.cpp | 63 ++++++++++++++++++++++ .../filesystem/storage_boot_module_fixture.cpp | 43 +++++++++++++++ kernel/src/test_support/state_reset_listener.cpp | 2 +- 7 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp create mode 100644 kernel/src/filesystem/devfs/filesystem.tests.cpp create mode 100644 kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 1cbb9e4..6a2e4b5 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -105,6 +105,7 @@ else() "src/test_support/devices/character_device.cpp" "src/test_support/filesystem/inode.cpp" "src/test_support/filesystem/filesystem.cpp" + "src/test_support/filesystem/storage_boot_module_fixture.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" "src/test_support/page_mapper.cpp" @@ -131,6 +132,7 @@ else() "src/memory/block_list_allocator.tests.cpp" # Filesystem Subsystem Tests + "src/filesystem/devfs/filesystem.tests.cpp" "src/filesystem/devfs/inode.tests.cpp" "src/filesystem/rootfs/filesystem.tests.cpp" "src/filesystem/rootfs/inode.tests.cpp" diff --git a/kernel/include/kernel/test_support/devices/storage/management.hpp b/kernel/include/kernel/test_support/devices/storage/management.hpp index 7c0d304..581aa91 100644 --- a/kernel/include/kernel/test_support/devices/storage/management.hpp +++ b/kernel/include/kernel/test_support/devices/storage/management.hpp @@ -1,10 +1,10 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_STORAGE_MANAGEMENT_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_STORAGE_MANAGEMENT_HPP -namespace kernel::tests::devices::storage +namespace kernel::tests::devices::storage::management { //! Deinitialize the storage management singleton. auto deinit() -> void; -} // namespace kernel::tests::devices::storage +} // namespace kernel::tests::devices::storage::management #endif \ No newline at end of file diff --git a/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp b/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp new file mode 100644 index 0000000..a57659b --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp @@ -0,0 +1,23 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_STORAGE_BOOT_MODULE_FIXTURE_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_STORAGE_BOOT_MODULE_FIXTURE_HPP + +#include "kapi/boot_module/boot_module_registry.hpp" + +#include +#include +#include + +namespace kernel::tests::filesystem +{ + struct storage_boot_module_fixture + { + auto setup_modules(std::size_t module_count, std::size_t module_size = 4096) -> void; + + protected: + kapi::boot_modules::boot_module_registry registry{}; + std::vector module_names{}; + std::vector> module_data{}; + }; +} // namespace kernel::tests::filesystem + +#endif \ No newline at end of file diff --git a/kernel/src/devices/storage/management.cpp b/kernel/src/devices/storage/management.cpp index 14a045a..c9fa0a8 100644 --- a/kernel/src/devices/storage/management.cpp +++ b/kernel/src/devices/storage/management.cpp @@ -83,11 +83,11 @@ namespace kernel::devices::storage } } // namespace kernel::devices::storage -namespace kernel::tests::devices::storage +namespace kernel::tests::devices::storage::management { auto deinit() -> void { active_storage_management.reset(); next_free_major = START_MAJOR; } -} // namespace kernel::tests::devices::storage +} // namespace kernel::tests::devices::storage::management diff --git a/kernel/src/filesystem/devfs/filesystem.tests.cpp b/kernel/src/filesystem/devfs/filesystem.tests.cpp new file mode 100644 index 0000000..1d82bf9 --- /dev/null +++ b/kernel/src/filesystem/devfs/filesystem.tests.cpp @@ -0,0 +1,63 @@ +#include "kernel/filesystem/devfs/filesystem.hpp" + +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" + +#include + +SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, + "Devfs filesystem lookup uses storage management devices", "[filesystem][devfs][filesystem]") +{ + GIVEN("a boot module registry with one module") + { + setup_modules(1); + + auto fs = kernel::filesystem::devfs::filesystem{}; + auto result = fs.mount(nullptr); + + THEN("mount succeeds") + { + REQUIRE(result == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.root_inode() != nullptr); + } + + THEN("lookup on root finds ram0 device inode") + { + auto inode = fs.lookup(fs.root_inode(), "ram0"); + REQUIRE(inode != nullptr); + REQUIRE(inode->is_device()); + } + + THEN("lookup of an unknown device returns null") + { + auto inode = fs.lookup(fs.root_inode(), "ram99"); + REQUIRE(inode == nullptr); + } + + THEN("lookup with a non-directory parent returns null") + { + auto non_directory_inode = fs.lookup(fs.root_inode(), "ram0"); + REQUIRE(non_directory_inode != nullptr); + REQUIRE(!non_directory_inode->is_directory()); + + auto result = fs.lookup(non_directory_inode, "anything"); + REQUIRE(result == nullptr); + } + } + + GIVEN("a boot module registry with three modules") + { + setup_modules(3, 2048); + + auto fs = kernel::filesystem::devfs::filesystem{}; + auto result = fs.mount(nullptr); + REQUIRE(result == kernel::filesystem::filesystem::operation_result::success); + + THEN("lookup finds all generated RAM devices") + { + REQUIRE(fs.lookup(fs.root_inode(), "ram0") != nullptr); + REQUIRE(fs.lookup(fs.root_inode(), "ram16") != nullptr); + REQUIRE(fs.lookup(fs.root_inode(), "ram32") != nullptr); + } + } +} diff --git a/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp b/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp new file mode 100644 index 0000000..8bbf194 --- /dev/null +++ b/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp @@ -0,0 +1,43 @@ +#include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" + +#include "kapi/boot_module/boot_module.hpp" +#include "kapi/boot_modules.hpp" +#include "kapi/memory.hpp" + +#include "kernel/devices/storage/management.hpp" +#include "kernel/test_support/boot_modules.hpp" +#include "kernel/test_support/devices/storage/management.hpp" + +#include +#include + +namespace kernel::tests::filesystem +{ + auto storage_boot_module_fixture::setup_modules(std::size_t module_count, std::size_t module_size) -> void + { + kernel::tests::devices::storage::management::deinit(); + kernel::tests::boot_modules::deinit(); + + module_names.clear(); + module_data.clear(); + registry = {}; + + module_names.reserve(module_count); + module_data.reserve(module_count); + + for (std::size_t i = 0; i < module_count; ++i) + { + module_names.push_back("test_mod" + std::to_string(i)); + module_data.emplace_back(module_size, std::byte{static_cast(0x40 + (i % 16))}); + } + + for (std::size_t i = 0; i < module_count; ++i) + { + registry.add_boot_module(kapi::boot_modules::boot_module{ + module_names[i], kapi::memory::linear_address{module_data[i].data()}, module_data[i].size()}); + } + + kapi::boot_modules::set_boot_module_registry(registry); + kernel::devices::storage::management::init(); + } +} // namespace kernel::tests::filesystem \ No newline at end of file diff --git a/kernel/src/test_support/state_reset_listener.cpp b/kernel/src/test_support/state_reset_listener.cpp index af1091c..9120c89 100644 --- a/kernel/src/test_support/state_reset_listener.cpp +++ b/kernel/src/test_support/state_reset_listener.cpp @@ -33,8 +33,8 @@ struct state_reset_listener : Catch::EventListenerBase { kernel::tests::filesystem::file_descriptor_table::deinit(); kernel::tests::filesystem::vfs::deinit(); - kernel::tests::devices::storage::deinit(); kernel::tests::boot_modules::deinit(); + kernel::tests::devices::storage::management::deinit(); kernel::tests::memory::deinit(); kernel::tests::cpu::deinit(); -- cgit v1.2.3 From 186bc5c9a08c5d6e0d306ce8b4fe3d75f4782cd2 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 9 Apr 2026 09:19:40 +0200 Subject: add test for devfs edge case --- kernel/src/filesystem/devfs/filesystem.tests.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/kernel/src/filesystem/devfs/filesystem.tests.cpp b/kernel/src/filesystem/devfs/filesystem.tests.cpp index 1d82bf9..f8c4764 100644 --- a/kernel/src/filesystem/devfs/filesystem.tests.cpp +++ b/kernel/src/filesystem/devfs/filesystem.tests.cpp @@ -34,6 +34,15 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, REQUIRE(inode == nullptr); } + THEN("lookup with wrong parent returns null") + { + auto other_fs = kernel::filesystem::devfs::filesystem{}; + other_fs.mount(nullptr); + + auto inode = fs.lookup(other_fs.root_inode(), "ram0"); + REQUIRE(inode == nullptr); + } + THEN("lookup with a non-directory parent returns null") { auto non_directory_inode = fs.lookup(fs.root_inode(), "ram0"); -- cgit v1.2.3 From 787671aac288590e40c5cabfc9f82a31f21629fe Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 9 Apr 2026 10:33:38 +0200 Subject: add vfs tests with real ext2 images --- kernel/CMakeLists.txt | 9 +- .../filesystem/storage_boot_module_fixture.hpp | 19 ++- .../filesystem/storage_boot_module_vfs_fixture.hpp | 24 +++ kernel/src/filesystem/vfs.tests.cpp | 166 +++++++++++++++++++++ .../filesystem/storage_boot_module_fixture.cpp | 90 +++++++++-- .../filesystem/storage_boot_module_vfs_fixture.cpp | 32 ++++ .../filesystem/test_assets/ext2_1KB_fs.img | 3 + .../filesystem/test_assets/ext2_2KB_fs.img | 3 + .../filesystem/test_assets/ext2_4KB_fs.img | 3 + 9 files changed, 330 insertions(+), 19 deletions(-) create mode 100644 kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp create mode 100644 kernel/src/filesystem/vfs.tests.cpp create mode 100644 kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp create mode 100644 kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img create mode 100644 kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img create mode 100644 kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 6a2e4b5..88d420b 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -106,7 +106,7 @@ else() "src/test_support/filesystem/inode.cpp" "src/test_support/filesystem/filesystem.cpp" "src/test_support/filesystem/storage_boot_module_fixture.cpp" - "src/test_support/log_buffer.cpp" + "src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" "src/test_support/page_mapper.cpp" "src/test_support/simulated_memory.cpp" @@ -142,7 +142,8 @@ else() "src/filesystem/mount_table.tests.cpp" "src/filesystem/mount.tests.cpp" "src/filesystem/open_file_description.tests.cpp" - + "src/filesystem/vfs.tests.cpp" + # Storage Subsystem Tests "src/devices/block_device_utils.tests.cpp" "src/devices/block_device.tests.cpp" @@ -155,6 +156,10 @@ else() "os::kernel_test_support" ) + target_compile_definitions("kernel_tests" PRIVATE + KERNEL_TEST_ASSETS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/src/test_support/filesystem/test_assets" + ) + set_target_properties("kernel_tests" PROPERTIES C_CLANG_TIDY "" CXX_CLANG_TIDY "" diff --git a/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp b/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp index a57659b..ee658e2 100644 --- a/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp +++ b/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp @@ -3,20 +3,29 @@ #include "kapi/boot_module/boot_module_registry.hpp" +#include +#include + #include -#include -#include +#include namespace kernel::tests::filesystem { struct storage_boot_module_fixture { + ~storage_boot_module_fixture(); + auto setup_modules(std::size_t module_count, std::size_t module_size = 4096) -> void; + auto setup_modules_from_img(kstd::vector const & module_names, + kstd::vector const & img_paths) -> void; protected: - kapi::boot_modules::boot_module_registry registry{}; - std::vector module_names{}; - std::vector> module_data{}; + kapi::boot_modules::boot_module_registry m_registry{}; + kstd::vector m_module_names{}; + kstd::vector> m_module_data{}; + + private: + auto setup_module_from_img(kstd::string const & module_name, std::filesystem::path const & img_path) -> void; }; } // namespace kernel::tests::filesystem diff --git a/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp b/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp new file mode 100644 index 0000000..98012b0 --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp @@ -0,0 +1,24 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_STORAGE_BOOT_MODULE_VFS_FIXTURE_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_STORAGE_BOOT_MODULE_VFS_FIXTURE_HPP + +#include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" + +#include +#include + +#include +#include + +namespace kernel::tests::filesystem +{ + struct storage_boot_module_vfs_fixture : storage_boot_module_fixture + { + ~storage_boot_module_vfs_fixture(); + + auto setup_modules_and_init_vfs(std::size_t module_count, std::size_t module_size = 4096) -> void; + auto setup_modules_from_img_and_init_vfs(kstd::vector const & module_names, + kstd::vector const & img_paths) -> void; + }; +} // namespace kernel::tests::filesystem + +#endif \ No newline at end of file diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp new file mode 100644 index 0000000..fec32e1 --- /dev/null +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -0,0 +1,166 @@ +#include "kernel/filesystem/vfs.hpp" + +#include "kernel/devices/storage/management.hpp" +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp" + +#include + +#include + +SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS with dummy modules", + "[filesystem][vfs]") +{ + GIVEN("an initialized boot module registry with multiple modules") + { + REQUIRE_NOTHROW(setup_modules_and_init_vfs(5)); + + THEN("vfs initializes and provides /dev mount") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto dev = vfs.open("/dev"); + + REQUIRE(dev != nullptr); + } + + THEN("vfs initializes root filesystem with boot device if boot module is present") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto root_file = vfs.open("/"); + + REQUIRE(root_file != nullptr); + } + } +} + +SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS with file backed image", + "[filesystem][vfs][img]") +{ + auto const image_path_1 = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; + auto const image_path_2 = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_2KB_fs.img"; + auto const image_path_3 = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_4KB_fs.img"; + + GIVEN("a real image file") + { + REQUIRE(std::filesystem::exists(image_path_1)); + REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module"}, {image_path_1})); + + THEN("vfs initializes and provides expected mount points") + { + auto & volatilefs = kernel::filesystem::vfs::get(); + auto root = volatilefs.open("/"); + auto dev = volatilefs.open("/dev"); + auto information = volatilefs.open("/information/info_1.txt"); + + REQUIRE(root != nullptr); + REQUIRE(dev != nullptr); + REQUIRE(information != nullptr); + } + } + + GIVEN("three real image files") + { + REQUIRE(std::filesystem::exists(image_path_1)); + REQUIRE(std::filesystem::exists(image_path_2)); + REQUIRE(std::filesystem::exists(image_path_3)); + REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1", "test_img_module_2", "test_img_module_3"}, + {image_path_1, image_path_2, image_path_3})); + + auto & vfs = kernel::filesystem::vfs::get(); + auto storage_mgmt = kernel::devices::storage::management::get(); + auto device_1 = storage_mgmt.device_by_major_minor(1, 16); + auto fs_1 = kernel::filesystem::filesystem::probe_and_mount(device_1); + auto device_2 = storage_mgmt.device_by_major_minor(1, 32); + auto fs_2 = kernel::filesystem::filesystem::probe_and_mount(device_2); + + THEN("vfs initializes first module as root") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto info1 = vfs.open("/information/info_1.txt"); + auto info2 = vfs.open("/information/info_2.txt"); + + REQUIRE(info1 != nullptr); + REQUIRE(info2 != nullptr); + } + + THEN("second image can be mounted, data retrieved and unmounted again") + { + REQUIRE(vfs.do_mount("/information", fs_1) == kernel::filesystem::vfs::operation_result::success); + + auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); + REQUIRE(mounted_monkey_1 != nullptr); + + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); + auto unmounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); + REQUIRE(unmounted_monkey_1 == nullptr); + + auto info_1 = vfs.open("/information/info_1.txt"); + REQUIRE(info_1 != nullptr); + } + + THEN("third image can be mounted in a mounted file system, unmount only if no child mount exists") + { + REQUIRE(vfs.do_mount("/information", fs_1) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/information/monkey_house/infrastructure", fs_2) == + kernel::filesystem::vfs::operation_result::success); + + auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); + auto mounted_fish1 = vfs.open("/information/monkey_house/infrastructure/enclosures/aquarium/tank_1/fish_1.txt"); + + REQUIRE(mounted_monkey_1 != nullptr); + REQUIRE(mounted_fish1 != nullptr); + + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); + REQUIRE(vfs.unmount("/information/monkey_house/infrastructure") == + kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); + } + + THEN("images can be stacked mounted and correct file system is unmounted again") + { + REQUIRE(vfs.do_mount("/information", fs_1) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/information", fs_2) == kernel::filesystem::vfs::operation_result::success); + + auto mounted_tickets = vfs.open("/information/entrance/tickets.txt"); + REQUIRE(mounted_tickets != nullptr); + + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); + mounted_tickets = vfs.open("/information/entrance/tickets.txt"); + REQUIRE(mounted_tickets == nullptr); + + auto mounted_monkey = vfs.open("/information/monkey_house/monkey_1.txt"); + REQUIRE(mounted_monkey != nullptr); + } + + THEN("mount with null file system fails") + { + REQUIRE(vfs.do_mount("/information", nullptr) == kernel::filesystem::vfs::operation_result::filesystem_null); + } + + THEN("mount with invalid path fails") + { + REQUIRE(vfs.do_mount("", fs_1) == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(vfs.do_mount("information", fs_1) == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(vfs.do_mount("/information/", fs_1) == kernel::filesystem::vfs::operation_result::invalid_path); + } + + THEN("mount with non-existent mount point fails") + { + REQUIRE(vfs.do_mount("/information/nonexistent", fs_1) == + kernel::filesystem::vfs::operation_result::mount_point_not_found); + } + + THEN("unmount with invalid path fails") + { + REQUIRE(vfs.unmount("") == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(vfs.unmount("information") == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(vfs.unmount("/information/") == kernel::filesystem::vfs::operation_result::invalid_path); + } + + THEN("unmounting non-existent mount point returns expected error code") + { + REQUIRE(vfs.unmount("/information/nonexistent") == + kernel::filesystem::vfs::operation_result::mount_point_not_found); + } + } +} diff --git a/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp b/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp index 8bbf194..a139f63 100644 --- a/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp +++ b/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp @@ -8,36 +8,102 @@ #include "kernel/test_support/boot_modules.hpp" #include "kernel/test_support/devices/storage/management.hpp" +#include +#include + #include -#include +#include +#include +#include +#include namespace kernel::tests::filesystem { - auto storage_boot_module_fixture::setup_modules(std::size_t module_count, std::size_t module_size) -> void + storage_boot_module_fixture::~storage_boot_module_fixture() { kernel::tests::devices::storage::management::deinit(); kernel::tests::boot_modules::deinit(); + } - module_names.clear(); - module_data.clear(); - registry = {}; + auto storage_boot_module_fixture::setup_modules(std::size_t module_count, std::size_t module_size) -> void + { + m_module_names.clear(); + m_module_data.clear(); + m_registry = {}; - module_names.reserve(module_count); - module_data.reserve(module_count); + m_module_names.reserve(module_count); + m_module_data.reserve(module_count); for (std::size_t i = 0; i < module_count; ++i) { - module_names.push_back("test_mod" + std::to_string(i)); - module_data.emplace_back(module_size, std::byte{static_cast(0x40 + (i % 16))}); + m_module_names.push_back("test_mod" + kstd::to_string(i)); + m_module_data.emplace_back(module_size, std::byte{static_cast(0x40 + (i % 16))}); } for (std::size_t i = 0; i < module_count; ++i) { - registry.add_boot_module(kapi::boot_modules::boot_module{ - module_names[i], kapi::memory::linear_address{module_data[i].data()}, module_data[i].size()}); + m_registry.add_boot_module(kapi::boot_modules::boot_module{ + m_module_names[i].view(), kapi::memory::linear_address{m_module_data[i].data()}, m_module_data[i].size()}); + } + + kapi::boot_modules::set_boot_module_registry(m_registry); + kernel::devices::storage::management::init(); + } + + auto storage_boot_module_fixture::setup_modules_from_img(kstd::vector const & module_names, + kstd::vector const & img_paths) + -> void + { + m_module_names.clear(); + m_module_data.clear(); + m_registry = {}; + + if (module_names.size() != img_paths.size()) + { + throw std::invalid_argument{"Module names and image paths vectors must have the same size."}; + } + + for (size_t i = 0; i < module_names.size(); ++i) + { + setup_module_from_img(module_names[i], img_paths[i]); } - kapi::boot_modules::set_boot_module_registry(registry); + kapi::boot_modules::set_boot_module_registry(m_registry); kernel::devices::storage::management::init(); } + + auto storage_boot_module_fixture::setup_module_from_img(kstd::string const & module_name, + std::filesystem::path const & img_path) -> void + { + auto file = std::ifstream{img_path, std::ios::binary | std::ios::ate}; + if (!file) + { + throw std::runtime_error{"Failed to open image file for test boot module: " + img_path.string()}; + } + + auto const end_pos = file.tellg(); + if (end_pos < 0) + { + throw std::runtime_error{"Failed to determine image file size for test boot module: " + img_path.string()}; + } + + auto const size = static_cast(end_pos); + file.seekg(0, std::ios::beg); + + m_module_names.push_back(module_name); + m_module_data.emplace_back(size); + + if (size > 0) + { + file.read(reinterpret_cast(m_module_data.back().data()), static_cast(size)); + if (!file) + { + throw std::runtime_error{"Failed to read image file content for test boot module: " + img_path.string()}; + } + } + + m_registry.add_boot_module(kapi::boot_modules::boot_module{ + m_module_names.back().view(), kapi::memory::linear_address{m_module_data.back().data()}, + m_module_data.back().size()}); + } } // namespace kernel::tests::filesystem \ No newline at end of file diff --git a/kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp b/kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp new file mode 100644 index 0000000..cba7278 --- /dev/null +++ b/kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp @@ -0,0 +1,32 @@ +#include "kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp" + +#include "kernel/filesystem/vfs.hpp" +#include "kernel/test_support/filesystem/vfs.hpp" + +#include +#include + +#include +#include + +namespace kernel::tests::filesystem +{ + storage_boot_module_vfs_fixture::~storage_boot_module_vfs_fixture() + { + kernel::tests::filesystem::vfs::deinit(); + } + + auto storage_boot_module_vfs_fixture::setup_modules_and_init_vfs(std::size_t module_count, std::size_t module_size) + -> void + { + setup_modules(module_count, module_size); + kernel::filesystem::vfs::init(); + } + + auto storage_boot_module_vfs_fixture::setup_modules_from_img_and_init_vfs( + kstd::vector const & module_names, kstd::vector const & img_paths) -> void + { + setup_modules_from_img(module_names, img_paths); + kernel::filesystem::vfs::init(); + } +} // namespace kernel::tests::filesystem \ No newline at end of file diff --git a/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img b/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img new file mode 100644 index 0000000..9f1ee4a --- /dev/null +++ b/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:94d3988bc309eb9e81f06042c72bf4c4fb5991cd7fdd597eb00c518a96c792d8 +size 10485760 diff --git a/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img b/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img new file mode 100644 index 0000000..1880911 --- /dev/null +++ b/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a13da5abb9c65c737105b1da0d4344c7cd7604c7952c762c4f4e3d3f96fd42d +size 5242880 diff --git a/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img b/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img new file mode 100644 index 0000000..3aaceb8 --- /dev/null +++ b/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4ce6a1aea277906e1af6de223c017ff900b96569f076b4d99fc04eaa1ee986f4 +size 10485760 -- cgit v1.2.3 From a03d4d9acc5115ec3f651b06f94ced9a7c0e192f Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 9 Apr 2026 10:56:25 +0200 Subject: add open_file_description tests with real image --- .../src/filesystem/open_file_description.tests.cpp | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/kernel/src/filesystem/open_file_description.tests.cpp b/kernel/src/filesystem/open_file_description.tests.cpp index f045094..db8eb49 100644 --- a/kernel/src/filesystem/open_file_description.tests.cpp +++ b/kernel/src/filesystem/open_file_description.tests.cpp @@ -1,6 +1,8 @@ #include "kernel/filesystem/open_file_description.hpp" +#include "kernel/filesystem/vfs.hpp" #include "kernel/test_support/filesystem/inode.hpp" +#include "kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp" #include #include @@ -8,6 +10,10 @@ #include +#include +#include +#include + SCENARIO("Open file description construction", "[filesystem][open_file_description]") { GIVEN("an inode and an open file description for that inode") @@ -58,3 +64,51 @@ SCENARIO("Open file description read/write offset management", "[filesystem][ope } } } + +SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, + "Open file description read with real image", "[filesystem][open_file_description][img]") +{ + auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; + + GIVEN("an open file description for a file in a real image") + { + REQUIRE(std::filesystem::exists(image_path)); + REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module"}, {image_path})); + + auto & vfs = kernel::filesystem::vfs::get(); + auto ofd = vfs.open("/information/info_1.txt"); + REQUIRE(ofd != nullptr); + + THEN("the file can be read and the offset is updated") + { + kstd::vector buffer(32); + auto bytes_read = ofd->read(buffer.data(), buffer.size()); + REQUIRE(bytes_read == 32); + REQUIRE(ofd->offset() == 32); + + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; + auto const content_end = buffer_as_str.find('\0'); + REQUIRE(buffer_as_str.substr(0, content_end) == "info_1\n"); + + for (auto i = content_end; i < buffer_as_str.size(); ++i) + { + REQUIRE(buffer_as_str[i] == '\0'); + } + } + + THEN("the file can be read multiple times") + { + kstd::vector buffer(4); + auto bytes_read_1 = ofd->read(buffer.data(), buffer.size() / 2); + REQUIRE(bytes_read_1 == buffer.size() / 2); + REQUIRE(ofd->offset() == buffer.size() / 2); + + auto bytes_read_2 = ofd->read(buffer.data() + buffer.size() / 2, buffer.size() / 2); + REQUIRE(bytes_read_2 == buffer.size() / 2); + REQUIRE(ofd->offset() == buffer.size()); + + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read_1 + bytes_read_2}; + REQUIRE(buffer_as_str == "info"); + } + } +} -- cgit v1.2.3 From 01d5f1b29fa04c69ac9f942a1075354ce5b25da2 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 9 Apr 2026 12:32:50 +0200 Subject: add ext2 inode and filesystem tests --- kernel/CMakeLists.txt | 3 + .../include/kernel/filesystem/ext2/filesystem.hpp | 21 +++ .../kernel/test_support/filesystem/ext2.hpp | 17 +++ kernel/src/filesystem/ext2/filesystem.cpp | 38 ++--- kernel/src/filesystem/ext2/filesystem.tests.cpp | 132 +++++++++++++++++ kernel/src/filesystem/ext2/inode.tests.cpp | 159 +++++++++++++++++++++ kernel/src/test_support/filesystem/ext2.cpp | 62 ++++++++ 7 files changed, 406 insertions(+), 26 deletions(-) create mode 100644 kernel/include/kernel/test_support/filesystem/ext2.hpp create mode 100644 kernel/src/filesystem/ext2/filesystem.tests.cpp create mode 100644 kernel/src/filesystem/ext2/inode.tests.cpp create mode 100644 kernel/src/test_support/filesystem/ext2.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 88d420b..2f6113a 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -105,6 +105,7 @@ else() "src/test_support/devices/character_device.cpp" "src/test_support/filesystem/inode.cpp" "src/test_support/filesystem/filesystem.cpp" + "src/test_support/filesystem/ext2.cpp" "src/test_support/filesystem/storage_boot_module_fixture.cpp" "src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp" "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" @@ -134,6 +135,8 @@ else() # Filesystem Subsystem Tests "src/filesystem/devfs/filesystem.tests.cpp" "src/filesystem/devfs/inode.tests.cpp" + "src/filesystem/ext2/filesystem.tests.cpp" + "src/filesystem/ext2/inode.tests.cpp" "src/filesystem/rootfs/filesystem.tests.cpp" "src/filesystem/rootfs/inode.tests.cpp" "src/filesystem/dentry.tests.cpp" diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 65324c8..a71385f 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -18,6 +18,27 @@ namespace kernel::filesystem::ext2 { + /** + @brief Constants related to the ext2 filesystem. + */ + namespace constants + { + constexpr size_t inline base_block_size = 1024; + constexpr size_t inline superblock_offset = base_block_size; + constexpr uint16_t inline magic_number = 0xEF53; + + constexpr uint32_t inline root_inode_number = 2; + + constexpr size_t inline direct_block_count = 12; + constexpr size_t inline singly_indirect_block_index = direct_block_count; + constexpr size_t inline doubly_indirect_block_index = singly_indirect_block_index + 1; + constexpr size_t inline triply_indirect_block_index = doubly_indirect_block_index + 1; + + constexpr uint16_t inline mode_mask = 0xF000; + constexpr uint16_t inline mode_regular = 0x8000; + constexpr uint16_t inline mode_directory = 0x4000; + } // namespace constants + /** @brief A filesystem implementation for the ext2 filesystem format. This class provides methods for mounting an ext2 filesystem, and looking up inodes. diff --git a/kernel/include/kernel/test_support/filesystem/ext2.hpp b/kernel/include/kernel/test_support/filesystem/ext2.hpp new file mode 100644 index 0000000..edbda29 --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/ext2.hpp @@ -0,0 +1,17 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_EXT2_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_EXT2_HPP + +#include "kernel/test_support/devices/block_device.hpp" + +#include +#include + +namespace kernel::tests::filesystem::ext2 +{ + auto write_bytes(kernel::tests::devices::block_device & device, size_t offset, void const * source, size_t size) + -> void; + auto write_u32(kernel::tests::devices::block_device & device, size_t offset, uint32_t value) -> void; + auto setup_mock_ext2_layout(kernel::tests::devices::block_device & device) -> void; +} // namespace kernel::tests::filesystem::ext2 + +#endif \ No newline at end of file diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 6d5960e..0ad5c97 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -21,29 +21,14 @@ namespace kernel::filesystem::ext2 { namespace { - constexpr size_t SUPERBLOCK_OFFSET = 1024; - constexpr uint16_t MAGIC_NUMBER = 0xEF53; - - constexpr uint32_t ROOT_INODE_NUMBER = 2; - - constexpr size_t DIRECT_BLOCK_COUNT = 12; - constexpr size_t SINGLY_INDIRECT_BLOCK_INDEX = DIRECT_BLOCK_COUNT; - constexpr size_t DOUBLY_INDIRECT_BLOCK_INDEX = SINGLY_INDIRECT_BLOCK_INDEX + 1; - constexpr size_t TRIPLY_INDIRECT_BLOCK_INDEX = DOUBLY_INDIRECT_BLOCK_INDEX + 1; - - // Mode bits - constexpr uint16_t S_IFMT = 0xF000; - constexpr uint16_t S_IFREG = 0x8000; - constexpr uint16_t S_IFDIR = 0x4000; - auto S_ISREG(uint16_t mode) -> bool { - return (mode & S_IFMT) == S_IFREG; + return (mode & constants::mode_mask) == constants::mode_regular; } auto S_ISDIR(uint16_t mode) -> bool { - return (mode & S_IFMT) == S_IFDIR; + return (mode & constants::mode_mask) == constants::mode_directory; } } // namespace @@ -51,9 +36,10 @@ namespace kernel::filesystem::ext2 { kernel::filesystem::filesystem::mount(device); - kernel::devices::block_device_utils::read(m_device, &m_superblock, SUPERBLOCK_OFFSET, sizeof(m_superblock)); + kernel::devices::block_device_utils::read(m_device, &m_superblock, constants::superblock_offset, + sizeof(m_superblock)); - if (m_superblock.magic != MAGIC_NUMBER) + if (m_superblock.magic != constants::magic_number) { return operation_result::invalid_magic_number; } @@ -69,7 +55,7 @@ namespace kernel::filesystem::ext2 block_group_descriptor_table_offset, num_block_groups * sizeof(block_group_descriptor)); - m_root_inode = read_inode(ROOT_INODE_NUMBER); + m_root_inode = read_inode(constants::root_inode_number); if (!m_root_inode || !m_root_inode->is_directory()) { @@ -161,11 +147,11 @@ namespace kernel::filesystem::ext2 auto filesystem::map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t { - if (inode_block_index < DIRECT_BLOCK_COUNT) + if (inode_block_index < constants::direct_block_count) { return data.block.at(inode_block_index); } - inode_block_index -= DIRECT_BLOCK_COUNT; + inode_block_index -= constants::direct_block_count; auto const block_size = get_block_size(); auto const numbers_per_block = block_size / sizeof(uint32_t); @@ -176,14 +162,14 @@ namespace kernel::filesystem::ext2 if (inode_block_index < block_numbers_per_singly_indirect_block) { - auto const singly_indirect_block_number = data.block.at(SINGLY_INDIRECT_BLOCK_INDEX); + auto const singly_indirect_block_number = data.block.at(constants::singly_indirect_block_index); return read_block_number_at_index(singly_indirect_block_number, inode_block_index); } inode_block_index -= block_numbers_per_singly_indirect_block; if (inode_block_index < block_numbers_per_doubly_indirect_block) { - auto const doubly_indirect_block_number = data.block.at(DOUBLY_INDIRECT_BLOCK_INDEX); + auto const doubly_indirect_block_number = data.block.at(constants::doubly_indirect_block_index); auto const singly_indirect_block_index_in_doubly_indirect_block = inode_block_index / block_numbers_per_singly_indirect_block; auto const singly_indirect_block_number = read_block_number_at_index( @@ -196,7 +182,7 @@ namespace kernel::filesystem::ext2 if (inode_block_index < block_numbers_per_triply_indirect_block) { - auto const triply_indirect_block_number = data.block.at(TRIPLY_INDIRECT_BLOCK_INDEX); + auto const triply_indirect_block_number = data.block.at(constants::triply_indirect_block_index); auto const doubly_indirect_block_index_in_triply_indirect_block = inode_block_index / block_numbers_per_doubly_indirect_block; auto const doubly_indirect_block_number = read_block_number_at_index( @@ -230,7 +216,7 @@ namespace kernel::filesystem::ext2 auto filesystem::get_block_size() -> size_t { - return 1024U << m_superblock.log_block_size; + return constants::base_block_size << m_superblock.log_block_size; } auto filesystem::get_inode_size() -> size_t diff --git a/kernel/src/filesystem/ext2/filesystem.tests.cpp b/kernel/src/filesystem/ext2/filesystem.tests.cpp new file mode 100644 index 0000000..b13ebf3 --- /dev/null +++ b/kernel/src/filesystem/ext2/filesystem.tests.cpp @@ -0,0 +1,132 @@ +#include "kernel/filesystem/ext2/filesystem.hpp" + +#include "kernel/devices/storage/management.hpp" +#include "kernel/filesystem/ext2/inode.hpp" +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/test_support/devices/block_device.hpp" +#include "kernel/test_support/filesystem/ext2.hpp" +#include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" + +#include +#include + +#include + +#include +#include +#include + +SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, + "Ext2 filesystem mount and lookup with real image", "[filesystem][ext2][filesystem][img]") +{ + auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; + + GIVEN("a mounted ext2 filesystem from a real image") + { + REQUIRE(std::filesystem::exists(image_path)); + REQUIRE_NOTHROW(setup_modules_from_img({"test_img_module"}, {image_path})); + + auto boot_device = kernel::devices::storage::management::get().determine_boot_device(); + REQUIRE(boot_device != nullptr); + + auto fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(boot_device) == kernel::filesystem::filesystem::operation_result::success); + + THEN("the root inode is available and is a directory") + { + REQUIRE(fs.root_inode() != nullptr); + REQUIRE(fs.root_inode()->is_directory()); + } + + THEN("lookup resolves known entries from the image") + { + auto information = fs.lookup(fs.root_inode(), "information"); + REQUIRE(information != nullptr); + REQUIRE(information->is_directory()); + + auto info_1 = fs.lookup(information, "info_1.txt"); + REQUIRE(info_1 != nullptr); + REQUIRE(info_1->is_regular()); + } + + THEN("lookup returns null for invalid inputs") + { + REQUIRE(fs.lookup(nullptr, "information") == nullptr); + + auto information = fs.lookup(fs.root_inode(), "information"); + REQUIRE(information != nullptr); + auto info_1 = fs.lookup(information, "info_1.txt"); + REQUIRE(info_1 != nullptr); + + REQUIRE(fs.lookup(info_1, "anything") == nullptr); + REQUIRE(fs.lookup(fs.root_inode(), "does_not_exist") == nullptr); + } + } +} + +SCENARIO("Ext2 filesystem rejects invalid magic", "[filesystem][ext2][filesystem]") +{ + auto const block_size = 1024; + GIVEN("a block device that does not contain an ext2 superblock") + { + auto device = kstd::make_shared(0, 0, "mock", block_size, 2 * block_size); + REQUIRE(device != nullptr); + + auto fs = kernel::filesystem::ext2::filesystem{}; + + THEN("mount fails with invalid_magic_number") + { + REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::invalid_magic_number); + } + } +} + +SCENARIO("Ext2 block mapping includes direct and all indirect levels", "[filesystem][ext2][filesystem]") +{ + auto const block_size = 1024; + + GIVEN("a minimally valid ext2 layout with configured indirect block tables") + { + auto device = kstd::make_shared(0, 0, "mock", block_size, 128 * block_size); + REQUIRE(device != nullptr); + + kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); + + auto fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::success); + + auto inode_data = kernel::filesystem::ext2::inode_data{}; + inode_data.block[0] = 7; + + inode_data.block[12] = 30; + kernel::tests::filesystem::ext2::write_u32(*device, 30 * block_size, 31); + + inode_data.block[13] = 40; + kernel::tests::filesystem::ext2::write_u32(*device, 40 * block_size, 41); + kernel::tests::filesystem::ext2::write_u32(*device, 41 * block_size, 42); + + inode_data.block[14] = 50; + kernel::tests::filesystem::ext2::write_u32(*device, 50 * block_size, 51); + kernel::tests::filesystem::ext2::write_u32(*device, 51 * block_size, 52); + kernel::tests::filesystem::ext2::write_u32(*device, 52 * block_size, 53); + + auto const numbers_per_block = static_cast(block_size / sizeof(uint32_t)); + auto const singly_start = static_cast(kernel::filesystem::ext2::constants::direct_block_count); + auto const doubly_start = singly_start + numbers_per_block; + auto const triply_start = doubly_start + numbers_per_block * numbers_per_block; + + THEN("mapping resolves direct, singly, doubly and triply indirect indexes") + { + REQUIRE(fs.map_inode_block_index_to_global_block_number(0, inode_data) == 7); + REQUIRE(fs.map_inode_block_index_to_global_block_number(singly_start, inode_data) == 31); + REQUIRE(fs.map_inode_block_index_to_global_block_number(doubly_start, inode_data) == 42); + REQUIRE(fs.map_inode_block_index_to_global_block_number(triply_start, inode_data) == 53); + } + + THEN("mapping returns zero for out-of-range indexes") + { + auto const beyond_triply = triply_start + numbers_per_block * numbers_per_block * numbers_per_block; + REQUIRE(fs.map_inode_block_index_to_global_block_number(beyond_triply, inode_data) == 0); + } + } +} diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp new file mode 100644 index 0000000..795ff10 --- /dev/null +++ b/kernel/src/filesystem/ext2/inode.tests.cpp @@ -0,0 +1,159 @@ +#include "kernel/filesystem/ext2/inode.hpp" + +#include "kernel/devices/storage/management.hpp" +#include "kernel/filesystem/ext2/filesystem.hpp" +#include "kernel/filesystem/filesystem.hpp" +#include "kernel/test_support/cpu.hpp" +#include "kernel/test_support/devices/block_device.hpp" +#include "kernel/test_support/filesystem/ext2.hpp" +#include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" + +#include +#include + +#include + +#include +#include +#include + +SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]") +{ + GIVEN("an ext2 filesystem") + { + auto fs = kernel::filesystem::ext2::filesystem{}; + + THEN("the inode is initialized and has the kind regular") + { + auto inode = kernel::filesystem::ext2::inode{&fs}; + REQUIRE(inode.is_regular()); + REQUIRE(!inode.is_directory()); + REQUIRE(!inode.is_device()); + } + } + + GIVEN("no filesystem (null pointer)") + { + THEN("constructing an inode with a null filesystem pointer panics") + { + REQUIRE_THROWS_AS(kernel::filesystem::ext2::inode{nullptr}, kernel::tests::cpu::halt); + } + } +} + +SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 inode reads from real image", + "[filesystem][ext2][inode][img]") +{ + auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; + + GIVEN("a mounted ext2 filesystem and a regular file inode") + { + REQUIRE(std::filesystem::exists(image_path)); + REQUIRE_NOTHROW(setup_modules_from_img({"test_img_module"}, {image_path})); + + auto boot_device = kernel::devices::storage::management::get().determine_boot_device(); + REQUIRE(boot_device != nullptr); + + auto fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(boot_device) == kernel::filesystem::filesystem::operation_result::success); + + auto information = fs.lookup(fs.root_inode(), "information"); + REQUIRE(information != nullptr); + auto file = fs.lookup(information, "info_1.txt"); + REQUIRE(file != nullptr); + REQUIRE(file->is_regular()); + + THEN("reading from offset zero returns expected file prefix") + { + auto buffer = kstd::vector(6); + auto const bytes_read = file->read(buffer.data(), 0, buffer.size()); + + REQUIRE(bytes_read == 6); + + auto const text = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; + REQUIRE(text == "info_1"); + } + + THEN("reading with an offset returns the expected byte") + { + auto buffer = kstd::vector(1); + auto const bytes_read = file->read(buffer.data(), 5, buffer.size()); + + REQUIRE(bytes_read == 1); + REQUIRE(static_cast(buffer[0]) == '1'); + } + } +} + +SCENARIO("Ext2 inode read stops when block mapping resolves to zero", "[filesystem][ext2][inode]") +{ + auto const block_size = 1024; + GIVEN("an ext2 inode without mapped data blocks") + { + auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); + REQUIRE(device != nullptr); + kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); + + auto fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::success); + + auto inode = kernel::filesystem::ext2::inode{&fs}; + inode.m_data.blocks = 2; + inode.m_data.block[0] = 0; + + auto buffer = kstd::vector(32, std::byte{0xAB}); + + THEN("no bytes are read") + { + auto const bytes_read = inode.read(buffer.data(), 0, buffer.size()); + REQUIRE(bytes_read == 0); + } + } +} + +SCENARIO("Ext2 inode read across block boundaries", "[filesystem][ext2][inode]") +{ + auto const block_size = 1024; + GIVEN("an ext2 inode with two direct blocks and a block size of 1024 bytes") + { + auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); + REQUIRE(device != nullptr); + kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); + + auto fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::success); + + auto inode = kernel::filesystem::ext2::inode{&fs}; + inode.m_data.blocks = 2; + inode.m_data.block[0] = 20; + kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size - 6, "Hello ", 6); + inode.m_data.block[1] = 21; + kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size, "World!", 6); + + auto buffer = kstd::vector(12, std::byte{0x00}); + + THEN("reading across the block boundary returns the combined content") + { + auto const bytes_read = inode.read(buffer.data(), block_size - 6, buffer.size()); + REQUIRE(bytes_read == 12); + + auto const text = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; + REQUIRE(text == "Hello World!"); + } + } +} + +SCENARIO("Ext2 inode write is not implemented", "[filesystem][ext2][inode]") +{ + GIVEN("an ext2 inode") + { + auto fs = kernel::filesystem::ext2::filesystem{}; + auto inode = kernel::filesystem::ext2::inode{&fs}; + + THEN("writing to the inode panics") + { + auto buffer = kstd::vector(32, std::byte{0x00}); + REQUIRE_THROWS_AS(inode.write(buffer.data(), 0, buffer.size()), kernel::tests::cpu::halt); + } + } +} diff --git a/kernel/src/test_support/filesystem/ext2.cpp b/kernel/src/test_support/filesystem/ext2.cpp new file mode 100644 index 0000000..3627373 --- /dev/null +++ b/kernel/src/test_support/filesystem/ext2.cpp @@ -0,0 +1,62 @@ +#include "kernel/test_support/filesystem/ext2.hpp" + +#include "kernel/filesystem/ext2/block_group_descriptor.hpp" +#include "kernel/filesystem/ext2/filesystem.hpp" +#include "kernel/filesystem/ext2/inode.hpp" +#include "kernel/filesystem/ext2/superblock.hpp" +#include "kernel/test_support/devices/block_device.hpp" + +#include +#include + +namespace kernel::tests::filesystem::ext2 +{ + namespace + { + constexpr uint32_t root_directory_data_block = 20; + } // namespace + + auto write_bytes(kernel::tests::devices::block_device & device, size_t offset, void const * source, size_t size) + -> void + { + auto const required_size = offset + size; + if (device.data.size() < required_size) + { + device.data.resize(required_size, 0); + } + + std::memcpy(device.data.data() + offset, source, size); + } + + auto write_u32(kernel::tests::devices::block_device & device, size_t offset, uint32_t value) -> void + { + write_bytes(device, offset, &value, sizeof(value)); + } + + auto setup_mock_ext2_layout(kernel::tests::devices::block_device & device) -> void + { + auto superblock = kernel::filesystem::ext2::superblock{}; + superblock.magic = kernel::filesystem::ext2::constants::magic_number; + superblock.log_block_size = 0; + superblock.blocks_count = 64; + superblock.blocks_per_group = 64; + superblock.inodes_per_group = 32; + superblock.rev_level = 1; + superblock.inode_size = 128; + write_bytes(device, kernel::filesystem::ext2::constants::superblock_offset, &superblock, sizeof(superblock)); + + auto group_descriptor = kernel::filesystem::ext2::block_group_descriptor{}; + group_descriptor.inode_table = 5; + write_bytes(device, 2048, &group_descriptor, sizeof(group_descriptor)); + + auto root_inode_data = kernel::filesystem::ext2::inode_data{}; + root_inode_data.mode = kernel::filesystem::ext2::constants::mode_directory; + root_inode_data.blocks = 2; + root_inode_data.block[0] = root_directory_data_block; + + auto const root_inode_offset = + static_cast(group_descriptor.inode_table) * kernel::filesystem::ext2::constants::base_block_size + + (kernel::filesystem::ext2::constants::root_inode_number - 1) * superblock.inode_size; + write_bytes(device, root_inode_offset, &root_inode_data, sizeof(root_inode_data)); + } +} // namespace kernel::tests::filesystem::ext2 \ No newline at end of file -- cgit v1.2.3 From 48d01e976cc250c2630c1b599a02cea60b56d32d Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Sun, 12 Apr 2026 19:08:50 +0200 Subject: Rename --- kernel/src/filesystem/vfs.tests.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index fec32e1..f363041 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -47,10 +47,10 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("vfs initializes and provides expected mount points") { - auto & volatilefs = kernel::filesystem::vfs::get(); - auto root = volatilefs.open("/"); - auto dev = volatilefs.open("/dev"); - auto information = volatilefs.open("/information/info_1.txt"); + auto & vfs = kernel::filesystem::vfs::get(); + auto root = vfs.open("/"); + auto dev = vfs.open("/dev"); + auto information = vfs.open("/information/info_1.txt"); REQUIRE(root != nullptr); REQUIRE(dev != nullptr); -- cgit v1.2.3 From cb7edbe6d4454ee5b217b522f62f4a7b92475a32 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Sun, 12 Apr 2026 19:12:15 +0200 Subject: Add comment --- kernel/include/kernel/filesystem/inode.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index 072f198..5208be2 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -69,6 +69,7 @@ namespace kernel::filesystem */ [[nodiscard]] auto is_device() const -> bool; + // TODO BA-FS26 avoid public member public: inode_kind m_kind{inode_kind::regular}; }; -- cgit v1.2.3 From bb01b9f2d29524974881e9a88ffb6c229836ddba Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Sun, 12 Apr 2026 20:45:23 +0200 Subject: Add fs syscall handler --- kapi/include/kapi/syscall_handler/filesystem.hpp | 19 +++++++ .../kernel/filesystem/file_descriptor_table.hpp | 3 +- kernel/kapi/syscall_handler/filesystem.cpp | 59 ++++++++++++++++++++++ kernel/src/filesystem/file_descriptor_table.cpp | 8 +-- 4 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 kapi/include/kapi/syscall_handler/filesystem.hpp create mode 100644 kernel/kapi/syscall_handler/filesystem.cpp diff --git a/kapi/include/kapi/syscall_handler/filesystem.hpp b/kapi/include/kapi/syscall_handler/filesystem.hpp new file mode 100644 index 0000000..86ae4d2 --- /dev/null +++ b/kapi/include/kapi/syscall_handler/filesystem.hpp @@ -0,0 +1,19 @@ +#ifndef TEACHOS_KAPI_SYSCALL_HANDLER_FILESYSTEM_HPP +#define TEACHOS_KAPI_SYSCALL_HANDLER_FILESYSTEM_HPP + +#include +#include + +namespace kapi::syscall_handler::filesystem +{ + auto mount(char const * source, char const * target) -> int; + auto umount(char const * target) -> int; + + auto open(char const * path) -> int; + auto close(int fd) -> int; + + auto read(int fd, void * buffer, size_t size) -> ssize_t; + auto write(int fd, void const * buffer, size_t size) -> ssize_t; +} // namespace kapi::syscall_handler::filesystem + +#endif // TEACHOS_KAPI_SYSCALL_HANDLER_FILESYSTEM_HPP \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/filesystem/file_descriptor_table.hpp index 5d52c19..5dd91e7 100644 --- a/kernel/include/kernel/filesystem/file_descriptor_table.hpp +++ b/kernel/include/kernel/filesystem/file_descriptor_table.hpp @@ -50,8 +50,9 @@ namespace kernel::filesystem /** @brief Remove a file from the descriptor table. @param fd The file descriptor index to remove. + @return 0 on success, or -1 on failure. */ - auto remove_file(int fd) -> void; + auto remove_file(int fd) -> int; private: file_descriptor_table() = default; diff --git a/kernel/kapi/syscall_handler/filesystem.cpp b/kernel/kapi/syscall_handler/filesystem.cpp new file mode 100644 index 0000000..a6e8027 --- /dev/null +++ b/kernel/kapi/syscall_handler/filesystem.cpp @@ -0,0 +1,59 @@ +#include "kapi/syscall_handler/filesystem.hpp" + +#include "kernel/filesystem/file_descriptor_table.hpp" +#include "kernel/filesystem/vfs.hpp" + +#include +#include + +namespace kapi::syscall_handler::filesystem +{ + auto mount(char const * source, char const * target) -> int + { + // TODO BA-FS26 + } + + auto umount(char const * target) -> int + { + if (kernel::filesystem::vfs::get().unmount(target) == kernel::filesystem::vfs::operation_result::success) + { + return 0; + } + return -1; + } + + auto open(char const * path) -> int + { + if (auto open_file_description = kernel::filesystem::vfs::get().open(path)) + { + return kernel::filesystem::file_descriptor_table::get().add_file(open_file_description); + } + + return -1; + } + + auto close(int fd) -> int + { + return kernel::filesystem::file_descriptor_table::get().remove_file(fd); + } + + auto read(int fd, void * buffer, size_t size) -> ssize_t + { + if (auto open_file_description = kernel::filesystem::file_descriptor_table::get().get_file(fd)) + { + return open_file_description->read(buffer, size); + } + + return -1; + } + + auto write(int fd, void const * buffer, size_t size) -> ssize_t + { + if (auto open_file_description = kernel::filesystem::file_descriptor_table::get().get_file(fd)) + { + return open_file_description->write(buffer, size); + } + + return -1; + } +} // namespace kapi::syscall_handler::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/file_descriptor_table.cpp b/kernel/src/filesystem/file_descriptor_table.cpp index 1c062a1..7569cea 100644 --- a/kernel/src/filesystem/file_descriptor_table.cpp +++ b/kernel/src/filesystem/file_descriptor_table.cpp @@ -41,7 +41,6 @@ namespace kernel::filesystem { if (!file_description) { - // TODO BA-FS26 panic or errorcode? return -1; } @@ -72,20 +71,21 @@ namespace kernel::filesystem return m_open_files.at(index); } - auto file_descriptor_table::remove_file(int fd) -> void + auto file_descriptor_table::remove_file(int fd) -> int { if (fd < 0) { - return; + return -1; } auto const index = static_cast(fd); if (index >= m_open_files.size()) { - return; + return -1; } m_open_files.at(index) = nullptr; + return 0; } } // namespace kernel::filesystem -- cgit v1.2.3 From e38fd2056a8cab7a3c6f3da8150fed69530bde6c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Apr 2026 14:14:08 +0200 Subject: kstd: introduce output buffer for formatting --- libs/kstd/include/kstd/bits/format/context.hpp | 12 ++++----- .../include/kstd/bits/format/output_buffer.hpp | 30 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 libs/kstd/include/kstd/bits/format/output_buffer.hpp diff --git a/libs/kstd/include/kstd/bits/format/context.hpp b/libs/kstd/include/kstd/bits/format/context.hpp index 478a48f..e8d0302 100644 --- a/libs/kstd/include/kstd/bits/format/context.hpp +++ b/libs/kstd/include/kstd/bits/format/context.hpp @@ -4,6 +4,7 @@ // IWYU pragma: private, include #include "arg.hpp" +#include "kstd/bits/format/output_buffer.hpp" #include @@ -29,11 +30,7 @@ namespace kstd struct format_context { - using writer_function = void(void *, std::string_view); using format_args = std::span; - - writer_function * writer{}; - void * user_data{}; format_args args{}; [[nodiscard]] auto arg(std::size_t const id) const -> format_arg const & @@ -47,13 +44,16 @@ namespace kstd constexpr auto push(std::string_view string) -> void { - writer(user_data, string); + m_buffer->push(string); } constexpr auto push(char character) -> void { - writer(user_data, std::string_view(&character, 1)); + m_buffer->push(character); } + + private: + bits::format::output_buffer * m_buffer; }; } // namespace kstd diff --git a/libs/kstd/include/kstd/bits/format/output_buffer.hpp b/libs/kstd/include/kstd/bits/format/output_buffer.hpp new file mode 100644 index 0000000..be2034f --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/output_buffer.hpp @@ -0,0 +1,30 @@ +#ifndef KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP +#define KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP + +#include + +namespace kstd::bits::format +{ + + //! An abstract interface for formatted output buffers. + //! + //! This interface is intended to be use for functions dealing with string formatting, like the print and the format + //! family. + struct output_buffer + { + virtual ~output_buffer() = default; + + //! Push a text segment into the buffer. + //! + //! @param text The text segment to push. + virtual auto push(std::string_view text) -> void = 0; + + //! Push a single character into the buffer. + //! + //! @param character The character to push into the buffer. + virtual auto push(char character) -> void = 0; + }; + +} // namespace kstd::bits::format + +#endif \ No newline at end of file -- cgit v1.2.3 From 98fc294deb6f7c27eda3f9ed3b616572a1ab2009 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Apr 2026 14:37:20 +0200 Subject: kstd/format: hook up vformat_to --- kernel/kstd/print.cpp | 381 +++++++++++---------- .../include/kstd/bits/format/output_buffer.hpp | 2 + libs/kstd/include/kstd/bits/format/vformat.hpp | 65 ++++ libs/kstd/include/kstd/format | 2 + 4 files changed, 261 insertions(+), 189 deletions(-) create mode 100644 libs/kstd/include/kstd/bits/format/vformat.hpp diff --git a/kernel/kstd/print.cpp b/kernel/kstd/print.cpp index beee2e5..a0bb7d6 100644 --- a/kernel/kstd/print.cpp +++ b/kernel/kstd/print.cpp @@ -1,12 +1,13 @@ #include "kapi/cio.hpp" +#include #include #include #include +#include #include #include -#include #include namespace kstd::os @@ -14,7 +15,7 @@ namespace kstd::os namespace { - struct write_buffer + struct write_buffer final : kstd::bits::format::output_buffer { using output_stream = kapi::cio::output_stream; @@ -29,7 +30,7 @@ namespace kstd::os : m_stream{stream} {} - ~write_buffer() noexcept + ~write_buffer() noexcept final { flush(); } @@ -44,17 +45,18 @@ namespace kstd::os } } - auto static callback(void * object, std::string_view text) -> void + auto push(std::string_view text) -> void final { - auto * self = static_cast(object); - for (char const character : text) + std::ranges::for_each(text, [this](auto c) { this->push(c); }); + } + + auto push(char character) -> void final + { + if (m_position >= size) { - if (self->m_position >= size) - { - self->flush(); - } - self->m_buffer.at(self->m_position++) = character; + flush(); } + m_buffer.at(m_position++) = character; } private: @@ -69,184 +71,185 @@ namespace kstd::os { auto writer = write_buffer{(sink == print_sink::stderr) ? kapi::cio::output_stream::stderr : kapi::cio::output_stream::stdout}; - auto context = kstd::format_context{.writer = write_buffer::callback, .user_data = &writer, .args = args}; - auto parse_context = kstd::format_parse_context{format, args.size()}; - - auto it = parse_context.begin(); - auto end = parse_context.end(); - - while (it != end) - { - if (*it != '{' && *it != '}') - { - auto start = it; - while (it != end && *it != '{' && *it != '}') - { - std::advance(it, 1); - } - parse_context.advance_to(it); - context.push(std::string_view(start, it - start)); - continue; - } - - if (*it == '{') - { - std::advance(it, 1); - if (it != end && *it == '{') - { - context.push('{'); - std::advance(it, 1); - parse_context.advance_to(it); - continue; - } - - parse_context.advance_to(it); - auto index = 0uz; - - if (it != end && *it >= '0' && *it <= '9') - { - while (it != end && *it >= '0' && *it <= '9') - { - index = index * 10 + static_cast(*it - '0'); - std::advance(it, 1); - } - parse_context.check_arg_id(index); - } - else - { - index = parse_context.next_arg_id(); - } - - if (it != end && *it == ':') - { - std::advance(it, 1); - } - - parse_context.advance_to(it); - - if (index < args.size()) - { - auto const & arg = args[index]; - switch (arg.type) - { - case kstd::bits::format::arg_type::boolean: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.boolean, context); - break; - } - case kstd::bits::format::arg_type::character: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.character, context); - break; - } - case kstd::bits::format::arg_type::integer: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.integer, context); - break; - } - case kstd::bits::format::arg_type::unsigned_integer: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.unsigned_integer, context); - break; - } - case kstd::bits::format::arg_type::string_view: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.string_view, context); - break; - } - case kstd::bits::format::arg_type::c_string: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.c_string, context); - break; - } - case kstd::bits::format::arg_type::pointer: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.pointer, context); - break; - } - case kstd::bits::format::arg_type::user_defined: - { - if (arg.value.user_defined.format) - { - arg.value.user_defined.format(arg.value.user_defined.pointer, parse_context, context); - } - else - { - context.push("{?}"); - } - break; - } - default: - { - context.push("{fmt-err: unknown-type}"); - break; - } - } - } - else - { - context.push("{fmt-err: bound}"); - } - - it = parse_context.begin(); - - if (it != end && *it == '}') - { - std::advance(it, 1); - parse_context.advance_to(it); - } - else - { - context.push("{fmt-err: unconsumed}"); - while (it != end && *it != '}') - { - std::advance(it, 1); - } - - if (it != end) - { - std::advance(it, 1); - parse_context.advance_to(it); - } - } - } - else if (*it == '}') - { - std::advance(it, 1); - if (it != end && *it == '}') - { - context.push('}'); - std::advance(it, 1); - parse_context.advance_to(it); - } - else - { - context.push("{fmt-err: unescaped}"); - parse_context.advance_to(it); - } - } - } + kstd::bits::format::vformat_to(writer, format, args); + // auto context = kstd::format_context{.writer = write_buffer::callback, .user_data = &writer, .args = args}; + // auto parse_context = kstd::format_parse_context{format, args.size()}; + + // auto it = parse_context.begin(); + // auto end = parse_context.end(); + + // while (it != end) + // { + // if (*it != '{' && *it != '}') + // { + // auto start = it; + // while (it != end && *it != '{' && *it != '}') + // { + // std::advance(it, 1); + // } + // parse_context.advance_to(it); + // context.push(std::string_view(start, it - start)); + // continue; + // } + + // if (*it == '{') + // { + // std::advance(it, 1); + // if (it != end && *it == '{') + // { + // context.push('{'); + // std::advance(it, 1); + // parse_context.advance_to(it); + // continue; + // } + + // parse_context.advance_to(it); + // auto index = 0uz; + + // if (it != end && *it >= '0' && *it <= '9') + // { + // while (it != end && *it >= '0' && *it <= '9') + // { + // index = index * 10 + static_cast(*it - '0'); + // std::advance(it, 1); + // } + // parse_context.check_arg_id(index); + // } + // else + // { + // index = parse_context.next_arg_id(); + // } + + // if (it != end && *it == ':') + // { + // std::advance(it, 1); + // } + + // parse_context.advance_to(it); + + // if (index < args.size()) + // { + // auto const & arg = args[index]; + // switch (arg.type) + // { + // case kstd::bits::format::arg_type::boolean: + // { + // auto fmt = kstd::formatter{}; + // auto const parsed = fmt.parse(parse_context); + // parse_context.advance_to(parsed); + // fmt.format(arg.value.boolean, context); + // break; + // } + // case kstd::bits::format::arg_type::character: + // { + // auto fmt = kstd::formatter{}; + // auto const parsed = fmt.parse(parse_context); + // parse_context.advance_to(parsed); + // fmt.format(arg.value.character, context); + // break; + // } + // case kstd::bits::format::arg_type::integer: + // { + // auto fmt = kstd::formatter{}; + // auto const parsed = fmt.parse(parse_context); + // parse_context.advance_to(parsed); + // fmt.format(arg.value.integer, context); + // break; + // } + // case kstd::bits::format::arg_type::unsigned_integer: + // { + // auto fmt = kstd::formatter{}; + // auto const parsed = fmt.parse(parse_context); + // parse_context.advance_to(parsed); + // fmt.format(arg.value.unsigned_integer, context); + // break; + // } + // case kstd::bits::format::arg_type::string_view: + // { + // auto fmt = kstd::formatter{}; + // auto const parsed = fmt.parse(parse_context); + // parse_context.advance_to(parsed); + // fmt.format(arg.value.string_view, context); + // break; + // } + // case kstd::bits::format::arg_type::c_string: + // { + // auto fmt = kstd::formatter{}; + // auto const parsed = fmt.parse(parse_context); + // parse_context.advance_to(parsed); + // fmt.format(arg.value.c_string, context); + // break; + // } + // case kstd::bits::format::arg_type::pointer: + // { + // auto fmt = kstd::formatter{}; + // auto const parsed = fmt.parse(parse_context); + // parse_context.advance_to(parsed); + // fmt.format(arg.value.pointer, context); + // break; + // } + // case kstd::bits::format::arg_type::user_defined: + // { + // if (arg.value.user_defined.format) + // { + // arg.value.user_defined.format(arg.value.user_defined.pointer, parse_context, context); + // } + // else + // { + // context.push("{?}"); + // } + // break; + // } + // default: + // { + // context.push("{fmt-err: unknown-type}"); + // break; + // } + // } + // } + // else + // { + // context.push("{fmt-err: bound}"); + // } + + // it = parse_context.begin(); + + // if (it != end && *it == '}') + // { + // std::advance(it, 1); + // parse_context.advance_to(it); + // } + // else + // { + // context.push("{fmt-err: unconsumed}"); + // while (it != end && *it != '}') + // { + // std::advance(it, 1); + // } + + // if (it != end) + // { + // std::advance(it, 1); + // parse_context.advance_to(it); + // } + // } + // } + // else if (*it == '}') + // { + // std::advance(it, 1); + // if (it != end && *it == '}') + // { + // context.push('}'); + // std::advance(it, 1); + // parse_context.advance_to(it); + // } + // else + // { + // context.push("{fmt-err: unescaped}"); + // parse_context.advance_to(it); + // } + // } + // } } } // namespace kstd::os diff --git a/libs/kstd/include/kstd/bits/format/output_buffer.hpp b/libs/kstd/include/kstd/bits/format/output_buffer.hpp index be2034f..fd7a2b4 100644 --- a/libs/kstd/include/kstd/bits/format/output_buffer.hpp +++ b/libs/kstd/include/kstd/bits/format/output_buffer.hpp @@ -1,6 +1,8 @@ #ifndef KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP #define KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP +// IWYU pragma: private, include + #include namespace kstd::bits::format diff --git a/libs/kstd/include/kstd/bits/format/vformat.hpp b/libs/kstd/include/kstd/bits/format/vformat.hpp new file mode 100644 index 0000000..90b74a9 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/vformat.hpp @@ -0,0 +1,65 @@ +#ifndef KSTD_BITS_FORMAT_VFORMAT_HPP +#define KSTD_BITS_FORMAT_VFORMAT_HPP + +// IWYU pragma: private, include + +#include +#include +#include +#include + +#include +#include + +namespace kstd +{ + + namespace bits::format + { + //! Format a string with the given arguments into the given buffer. + //! + //! External implementations of `vprint` may call this function. + //! + //! @param buffer The buffer to format into. + //! @param format The format string to use. + //! @param args The arguments for the format string. + auto vformat_to(output_buffer & buffer, std::string_view format, format_args args) -> void; + + struct string_writer : output_buffer + { + auto push(std::string_view text) -> void override; + auto push(char character) -> void override; + + auto release() -> string &&; + + private: + string m_result{}; + }; + + struct string_iterator_writer : output_buffer + { + auto push(std::string_view text) -> void override; + auto push(char character) -> void override; + + private: + string::iterator m_iter{}; + }; + + } // namespace bits::format + + //! Format a given string with the provided arguments. + //! + //! @param format The format string. + //! @param args The arguments for the format string. + //! @return A new string containing the result of the format operation. + template + auto format(format_string...> format, ArgumentTypes &&... args) -> string + { + auto buffer = bits::format::string_writer{}; + bits::format::vformat_to(buffer, format.str_view, std::forward(args)...); + return buffer.release(); + } + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/format b/libs/kstd/include/kstd/format index adee5f8..d11c221 100644 --- a/libs/kstd/include/kstd/format +++ b/libs/kstd/include/kstd/format @@ -13,7 +13,9 @@ #include "bits/format/formatter/pointer.hpp" // IWYU pragma: export #include "bits/format/formatter/range.hpp" // IWYU pragma: export #include "bits/format/formatter/string_view.hpp" // IWYU pragma: export +#include "bits/format/output_buffer.hpp" // IWYU pragma: export #include "bits/format/parse_context.hpp" // IWYU pragma: export #include "bits/format/string.hpp" // IWYU pragma: export +#include "bits/format/vformat.hpp" // IWYU pragma: export #endif \ No newline at end of file -- cgit v1.2.3 From 45091789eb9e49856ea6c2f3606639d43f4584e7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Apr 2026 14:56:39 +0200 Subject: kstd: move formatting implementation to kstd --- kernel/kstd/print.cpp | 198 +----------------- libs/kstd/CMakeLists.txt | 1 + libs/kstd/include/kstd/bits/format/context.hpp | 5 + .../include/kstd/bits/format/formatter/bool.hpp | 4 +- .../kstd/bits/format/formatter/integral.hpp | 4 +- libs/kstd/include/kstd/bits/format/specifiers.hpp | 6 +- libs/kstd/include/kstd/bits/format/vformat.hpp | 2 + libs/kstd/include/kstd/vector | 2 +- libs/kstd/src/vformat.cpp | 224 +++++++++++++++++++++ 9 files changed, 250 insertions(+), 196 deletions(-) create mode 100644 libs/kstd/src/vformat.cpp diff --git a/kernel/kstd/print.cpp b/kernel/kstd/print.cpp index a0bb7d6..7854027 100644 --- a/kernel/kstd/print.cpp +++ b/kernel/kstd/print.cpp @@ -35,16 +35,6 @@ namespace kstd::os flush(); } - auto flush() noexcept -> void - { - if (m_position > 0) - { - std::string_view chunk{m_buffer.data(), m_position}; - kapi::cio::write(m_stream, chunk); - m_position = 0; - } - } - auto push(std::string_view text) -> void final { std::ranges::for_each(text, [this](auto c) { this->push(c); }); @@ -60,6 +50,16 @@ namespace kstd::os } private: + auto flush() noexcept -> void + { + if (m_position > 0) + { + std::string_view chunk{m_buffer.data(), m_position}; + kapi::cio::write(m_stream, chunk); + m_position = 0; + } + } + output_stream m_stream; std::array m_buffer{}; std::size_t m_position{}; @@ -72,184 +72,6 @@ namespace kstd::os auto writer = write_buffer{(sink == print_sink::stderr) ? kapi::cio::output_stream::stderr : kapi::cio::output_stream::stdout}; kstd::bits::format::vformat_to(writer, format, args); - // auto context = kstd::format_context{.writer = write_buffer::callback, .user_data = &writer, .args = args}; - // auto parse_context = kstd::format_parse_context{format, args.size()}; - - // auto it = parse_context.begin(); - // auto end = parse_context.end(); - - // while (it != end) - // { - // if (*it != '{' && *it != '}') - // { - // auto start = it; - // while (it != end && *it != '{' && *it != '}') - // { - // std::advance(it, 1); - // } - // parse_context.advance_to(it); - // context.push(std::string_view(start, it - start)); - // continue; - // } - - // if (*it == '{') - // { - // std::advance(it, 1); - // if (it != end && *it == '{') - // { - // context.push('{'); - // std::advance(it, 1); - // parse_context.advance_to(it); - // continue; - // } - - // parse_context.advance_to(it); - // auto index = 0uz; - - // if (it != end && *it >= '0' && *it <= '9') - // { - // while (it != end && *it >= '0' && *it <= '9') - // { - // index = index * 10 + static_cast(*it - '0'); - // std::advance(it, 1); - // } - // parse_context.check_arg_id(index); - // } - // else - // { - // index = parse_context.next_arg_id(); - // } - - // if (it != end && *it == ':') - // { - // std::advance(it, 1); - // } - - // parse_context.advance_to(it); - - // if (index < args.size()) - // { - // auto const & arg = args[index]; - // switch (arg.type) - // { - // case kstd::bits::format::arg_type::boolean: - // { - // auto fmt = kstd::formatter{}; - // auto const parsed = fmt.parse(parse_context); - // parse_context.advance_to(parsed); - // fmt.format(arg.value.boolean, context); - // break; - // } - // case kstd::bits::format::arg_type::character: - // { - // auto fmt = kstd::formatter{}; - // auto const parsed = fmt.parse(parse_context); - // parse_context.advance_to(parsed); - // fmt.format(arg.value.character, context); - // break; - // } - // case kstd::bits::format::arg_type::integer: - // { - // auto fmt = kstd::formatter{}; - // auto const parsed = fmt.parse(parse_context); - // parse_context.advance_to(parsed); - // fmt.format(arg.value.integer, context); - // break; - // } - // case kstd::bits::format::arg_type::unsigned_integer: - // { - // auto fmt = kstd::formatter{}; - // auto const parsed = fmt.parse(parse_context); - // parse_context.advance_to(parsed); - // fmt.format(arg.value.unsigned_integer, context); - // break; - // } - // case kstd::bits::format::arg_type::string_view: - // { - // auto fmt = kstd::formatter{}; - // auto const parsed = fmt.parse(parse_context); - // parse_context.advance_to(parsed); - // fmt.format(arg.value.string_view, context); - // break; - // } - // case kstd::bits::format::arg_type::c_string: - // { - // auto fmt = kstd::formatter{}; - // auto const parsed = fmt.parse(parse_context); - // parse_context.advance_to(parsed); - // fmt.format(arg.value.c_string, context); - // break; - // } - // case kstd::bits::format::arg_type::pointer: - // { - // auto fmt = kstd::formatter{}; - // auto const parsed = fmt.parse(parse_context); - // parse_context.advance_to(parsed); - // fmt.format(arg.value.pointer, context); - // break; - // } - // case kstd::bits::format::arg_type::user_defined: - // { - // if (arg.value.user_defined.format) - // { - // arg.value.user_defined.format(arg.value.user_defined.pointer, parse_context, context); - // } - // else - // { - // context.push("{?}"); - // } - // break; - // } - // default: - // { - // context.push("{fmt-err: unknown-type}"); - // break; - // } - // } - // } - // else - // { - // context.push("{fmt-err: bound}"); - // } - - // it = parse_context.begin(); - - // if (it != end && *it == '}') - // { - // std::advance(it, 1); - // parse_context.advance_to(it); - // } - // else - // { - // context.push("{fmt-err: unconsumed}"); - // while (it != end && *it != '}') - // { - // std::advance(it, 1); - // } - - // if (it != end) - // { - // std::advance(it, 1); - // parse_context.advance_to(it); - // } - // } - // } - // else if (*it == '}') - // { - // std::advance(it, 1); - // if (it != end && *it == '}') - // { - // context.push('}'); - // std::advance(it, 1); - // parse_context.advance_to(it); - // } - // else - // { - // context.push("{fmt-err: unescaped}"); - // parse_context.advance_to(it); - // } - // } - // } } } // namespace kstd::os diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 240118e..f7c771b 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -21,6 +21,7 @@ target_sources("kstd" PRIVATE "src/os/error.cpp" "src/mutex.cpp" + "src/vformat.cpp" ) file(GLOB_RECURSE KSTD_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/kstd/*") diff --git a/libs/kstd/include/kstd/bits/format/context.hpp b/libs/kstd/include/kstd/bits/format/context.hpp index e8d0302..7f392a0 100644 --- a/libs/kstd/include/kstd/bits/format/context.hpp +++ b/libs/kstd/include/kstd/bits/format/context.hpp @@ -33,6 +33,11 @@ namespace kstd using format_args = std::span; format_args args{}; + format_context(bits::format::output_buffer & buffer, format_args args) + : args{args} + , m_buffer{&buffer} + {} + [[nodiscard]] auto arg(std::size_t const id) const -> format_arg const & { if (id >= args.size()) diff --git a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp index 336e1b0..e371cec 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp @@ -51,11 +51,11 @@ namespace kstd auto const text = value ? std::string_view{"true"} : std::string_view{"false"}; auto final_width = 0uz; - if (specifiers.width_mode == bits::format::width_mode::static_value) + if (specifiers.mode == bits::format::width_mode::static_value) { final_width = specifiers.width_value; } - else if (specifiers.width_mode == bits::format::width_mode::dynamic_argument_id) + else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id) { auto const & arg = context.arg(specifiers.width_value); final_width = bits::format::extrat_dynamic_width(arg); diff --git a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp index 4912a44..e5a234a 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp @@ -64,11 +64,11 @@ namespace kstd auto format(T value, format_context & context) const -> void { auto final_width = 0uz; - if (specifiers.width_mode == bits::format::width_mode::static_value) + if (specifiers.mode == bits::format::width_mode::static_value) { final_width = specifiers.width_value; } - else if (specifiers.width_mode == bits::format::width_mode::dynamic_argument_id) + else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id) { auto const & arg = context.arg(specifiers.width_value); final_width = bits::format::extrat_dynamic_width(arg); diff --git a/libs/kstd/include/kstd/bits/format/specifiers.hpp b/libs/kstd/include/kstd/bits/format/specifiers.hpp index 9bc66c7..18c6f66 100644 --- a/libs/kstd/include/kstd/bits/format/specifiers.hpp +++ b/libs/kstd/include/kstd/bits/format/specifiers.hpp @@ -43,7 +43,7 @@ namespace kstd::bits::format bool alternative_form{}; bool zero_pad{}; - width_mode width_mode{}; + width_mode mode{}; std::size_t width_value{}; char type{}; }; @@ -133,7 +133,7 @@ namespace kstd::bits::format if (it != end && *it == '{') { - specs.width_mode = width_mode::dynamic_argument_id; + specs.mode = width_mode::dynamic_argument_id; std::advance(it, 1); auto argument_id = 0uz; @@ -160,7 +160,7 @@ namespace kstd::bits::format } else if (it != end && *it >= '0' && *it <= '9') { - specs.width_mode = width_mode::static_value; + specs.mode = width_mode::static_value; while (it != end && *it >= '0' && *it <= '9') { specs.width_value = specs.width_value * 10 + static_cast(*it - '0'); diff --git a/libs/kstd/include/kstd/bits/format/vformat.hpp b/libs/kstd/include/kstd/bits/format/vformat.hpp index 90b74a9..d032c3a 100644 --- a/libs/kstd/include/kstd/bits/format/vformat.hpp +++ b/libs/kstd/include/kstd/bits/format/vformat.hpp @@ -38,6 +38,8 @@ namespace kstd struct string_iterator_writer : output_buffer { + explicit string_iterator_writer(string::iterator iterator); + auto push(std::string_view text) -> void override; auto push(char character) -> void override; diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index e51cbac..79593c6 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -111,7 +111,7 @@ namespace kstd allocator_type const & allocator = allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) : m_allocator{allocator} - , m_size{std::ranges::distance(first, last)} + , m_size{static_cast(std::ranges::distance(first, last))} , m_capacity{m_size} , m_data{allocate_n(m_capacity)} { diff --git a/libs/kstd/src/vformat.cpp b/libs/kstd/src/vformat.cpp new file mode 100644 index 0000000..51aca84 --- /dev/null +++ b/libs/kstd/src/vformat.cpp @@ -0,0 +1,224 @@ +#include +#include + +#include +#include +#include +#include +#include + +namespace kstd::bits::format +{ + + auto vformat_to(output_buffer & buffer, std::string_view format, format_args args) -> void + { + auto context = kstd::format_context{buffer, args}; + auto parse_context = kstd::format_parse_context{format, args.size()}; + + auto it = parse_context.begin(); + auto end = parse_context.end(); + + while (it != end) + { + if (*it != '{' && *it != '}') + { + auto start = it; + while (it != end && *it != '{' && *it != '}') + { + std::advance(it, 1); + } + parse_context.advance_to(it); + context.push(std::string_view(start, it - start)); + continue; + } + + if (*it == '{') + { + std::advance(it, 1); + if (it != end && *it == '{') + { + context.push('{'); + std::advance(it, 1); + parse_context.advance_to(it); + continue; + } + + parse_context.advance_to(it); + auto index = 0uz; + + if (it != end && *it >= '0' && *it <= '9') + { + while (it != end && *it >= '0' && *it <= '9') + { + index = index * 10 + static_cast(*it - '0'); + std::advance(it, 1); + } + parse_context.check_arg_id(index); + } + else + { + index = parse_context.next_arg_id(); + } + + if (it != end && *it == ':') + { + std::advance(it, 1); + } + + parse_context.advance_to(it); + + if (index < args.size()) + { + auto const & arg = args[index]; + switch (arg.type) + { + case kstd::bits::format::arg_type::boolean: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.boolean, context); + break; + } + case kstd::bits::format::arg_type::character: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.character, context); + break; + } + case kstd::bits::format::arg_type::integer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.integer, context); + break; + } + case kstd::bits::format::arg_type::unsigned_integer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.unsigned_integer, context); + break; + } + case kstd::bits::format::arg_type::string_view: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.string_view, context); + break; + } + case kstd::bits::format::arg_type::c_string: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.c_string, context); + break; + } + case kstd::bits::format::arg_type::pointer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.pointer, context); + break; + } + case kstd::bits::format::arg_type::user_defined: + { + if (arg.value.user_defined.format) + { + arg.value.user_defined.format(arg.value.user_defined.pointer, parse_context, context); + } + else + { + context.push("{?}"); + } + break; + } + default: + { + context.push("{fmt-err: unknown-type}"); + break; + } + } + } + else + { + context.push("{fmt-err: bound}"); + } + + it = parse_context.begin(); + + if (it != end && *it == '}') + { + std::advance(it, 1); + parse_context.advance_to(it); + } + else + { + context.push("{fmt-err: unconsumed}"); + while (it != end && *it != '}') + { + std::advance(it, 1); + } + + if (it != end) + { + std::advance(it, 1); + parse_context.advance_to(it); + } + } + } + else if (*it == '}') + { + std::advance(it, 1); + if (it != end && *it == '}') + { + context.push('}'); + std::advance(it, 1); + parse_context.advance_to(it); + } + else + { + context.push("{fmt-err: unescaped}"); + parse_context.advance_to(it); + } + } + } + } + + auto string_writer::push(std::string_view text) -> void + { + m_result.append(text); + } + + auto string_writer::push(char character) -> void + { + m_result.push_back(character); + } + + auto string_writer::release() -> string && + { + return std::move(m_result); + } + + string_iterator_writer::string_iterator_writer(string::iterator it) + : m_iter{it} + {} + + auto string_iterator_writer::push(std::string_view text) -> void + { + std::ranges::for_each(text, [this](auto c) { push(c); }); + } + + auto string_iterator_writer::push(char character) -> void + { + *m_iter++ = character; + } + +} // namespace kstd::bits::format \ No newline at end of file -- cgit v1.2.3 From a0720bbe3cdfe3174e3d064356b21f0fcd37832e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Apr 2026 15:05:38 +0200 Subject: kstd/format: add kstd::format_to --- libs/kstd/include/kstd/bits/format/vformat.hpp | 42 +++++++++++++++++++++++--- libs/kstd/src/vformat.cpp | 15 --------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/libs/kstd/include/kstd/bits/format/vformat.hpp b/libs/kstd/include/kstd/bits/format/vformat.hpp index d032c3a..69c7f33 100644 --- a/libs/kstd/include/kstd/bits/format/vformat.hpp +++ b/libs/kstd/include/kstd/bits/format/vformat.hpp @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include @@ -36,15 +38,30 @@ namespace kstd string m_result{}; }; - struct string_iterator_writer : output_buffer + template Output> + struct iterator_writer : output_buffer { - explicit string_iterator_writer(string::iterator iterator); + explicit iterator_writer(Output iterator) + : m_output{iterator} + {} - auto push(std::string_view text) -> void override; - auto push(char character) -> void override; + auto iterator() const -> Output + { + return m_output; + } + + auto push(std::string_view text) -> void override + { + m_output = std::ranges::copy(text, m_output); + } + + auto push(char character) -> void override + { + *m_output++ = character; + } private: - string::iterator m_iter{}; + Output m_output{}; }; } // namespace bits::format @@ -62,6 +79,21 @@ namespace kstd return buffer.release(); } + //! Format a given string with the provided arguments. + //! + //! @param iterator The iterator to write to. + //! @param format The format string. + //! @param args The arguments for the format string. + //! @return An iterator past the last element written. + template Output, typename... ArgumentTypes> + auto format_to(Output iterator, format_string...> format, + ArgumentTypes &&... args) -> Output + { + auto buffer = bits::format::iterator_writer{iterator}; + bits::format::vformat_to(buffer, format.str_view, std::forward(args)...); + return buffer.iterator(); + } + } // namespace kstd #endif \ No newline at end of file diff --git a/libs/kstd/src/vformat.cpp b/libs/kstd/src/vformat.cpp index 51aca84..b7c5121 100644 --- a/libs/kstd/src/vformat.cpp +++ b/libs/kstd/src/vformat.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include #include @@ -207,18 +206,4 @@ namespace kstd::bits::format return std::move(m_result); } - string_iterator_writer::string_iterator_writer(string::iterator it) - : m_iter{it} - {} - - auto string_iterator_writer::push(std::string_view text) -> void - { - std::ranges::for_each(text, [this](auto c) { push(c); }); - } - - auto string_iterator_writer::push(char character) -> void - { - *m_iter++ = character; - } - } // namespace kstd::bits::format \ No newline at end of file -- cgit v1.2.3 From cd1dd2037cbe1d5f1362202d3127640406b468b8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Apr 2026 15:38:08 +0200 Subject: kstd: add basic format and format_to tests --- libs/kstd/CMakeLists.txt | 1 + libs/kstd/include/kstd/bits/format/vformat.hpp | 6 +- libs/kstd/include/kstd/string | 20 +++++ libs/kstd/tests/src/format.cpp | 116 +++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 libs/kstd/tests/src/format.cpp diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index f7c771b..ced3138 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -44,6 +44,7 @@ if(CMAKE_CROSSCOMPILING) else() add_executable("kstd_tests" "tests/src/flat_map.cpp" + "tests/src/format.cpp" "tests/src/vector.cpp" "tests/src/observer_ptr.cpp" "tests/src/os_panic.cpp" diff --git a/libs/kstd/include/kstd/bits/format/vformat.hpp b/libs/kstd/include/kstd/bits/format/vformat.hpp index 69c7f33..4fec7dd 100644 --- a/libs/kstd/include/kstd/bits/format/vformat.hpp +++ b/libs/kstd/include/kstd/bits/format/vformat.hpp @@ -52,7 +52,7 @@ namespace kstd auto push(std::string_view text) -> void override { - m_output = std::ranges::copy(text, m_output); + m_output = std::ranges::copy(text, m_output).out; } auto push(char character) -> void override @@ -75,7 +75,7 @@ namespace kstd auto format(format_string...> format, ArgumentTypes &&... args) -> string { auto buffer = bits::format::string_writer{}; - bits::format::vformat_to(buffer, format.str_view, std::forward(args)...); + bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward(args)...).args); return buffer.release(); } @@ -90,7 +90,7 @@ namespace kstd ArgumentTypes &&... args) -> Output { auto buffer = bits::format::iterator_writer{iterator}; - bits::format::vformat_to(buffer, format.str_view, std::forward(args)...); + bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward(args)...).args); return buffer.iterator(); } diff --git a/libs/kstd/include/kstd/string b/libs/kstd/include/kstd/string index 4ce19ce..58c4a08 100644 --- a/libs/kstd/include/kstd/string +++ b/libs/kstd/include/kstd/string @@ -347,6 +347,26 @@ namespace kstd return !(lhs == rhs); } + [[nodiscard]] constexpr auto inline operator==(string const & lhs, char const * rhs) -> bool + { + return lhs.view() == std::string_view{rhs}; + } + + [[nodiscard]] constexpr auto inline operator!=(string const & lhs, char const * rhs) -> bool + { + return !(lhs == rhs); + } + + [[nodiscard]] constexpr auto inline operator==(char const * lhs, string const & rhs) -> bool + { + return std::string_view{lhs} == rhs.view(); + } + + [[nodiscard]] constexpr auto inline operator!=(char const * lhs, string const & rhs) -> bool + { + return !(lhs == rhs); + } + template<> struct formatter : formatter { diff --git a/libs/kstd/tests/src/format.cpp b/libs/kstd/tests/src/format.cpp new file mode 100644 index 0000000..4915e50 --- /dev/null +++ b/libs/kstd/tests/src/format.cpp @@ -0,0 +1,116 @@ +#include +#include +#include + +#include + +#include +#include + +SCENARIO("Formatting to a new string", "[format]") +{ + GIVEN("a format string without any placeholders") + { + auto const & fmt = "This is a test"; + + WHEN("calling format with without any arguments.") + { + auto result = kstd::format(fmt); + + THEN("the result is the unmodified string") + { + REQUIRE(result == "This is a test"); + } + } + + WHEN("calling format with additional arguments") + { + auto result = kstd::format(fmt, 1, 2, 3); + + THEN("the result is the unmodified string") + { + REQUIRE(result == "This is a test"); + } + } + } + + GIVEN("a format string with placeholders") + { + auto const & fmt = "Here are some placeholders: {} {} {}"; + + WHEN("calling format with the same number of arguments as there are placeholders") + { + auto result = kstd::format(fmt, 1, true, -100); + + THEN("the result is the formatted string") + { + REQUIRE(result == "Here are some placeholders: 1 true -100"); + } + } + + WHEN("calling format with too many arguments") + { + auto result = kstd::format(fmt, 2, false, -200, 4, 5, 6); + + THEN("the result is the formatted string") + { + REQUIRE(result == "Here are some placeholders: 2 false -200"); + } + } + } +} + +SCENARIO("Formatting to an output iterator", "[format]") +{ + auto buffer = std::ostringstream{}; + + GIVEN("a format string without any placeholders") + { + auto const & fmt = "This is a test"; + + WHEN("calling format with without any arguments.") + { + kstd::format_to(std::ostream_iterator{buffer}, fmt); + + THEN("the unmodified string is written to the iterator") + { + REQUIRE(buffer.str() == "This is a test"); + } + } + + WHEN("calling format with additional arguments") + { + kstd::format_to(std::ostream_iterator{buffer}, fmt, 1, 2, 3); + + THEN("the unmodified string is written to the iterator") + { + REQUIRE(buffer.str() == "This is a test"); + } + } + } + + GIVEN("a format string with placeholders") + { + auto const & fmt = "Here are some placeholders: {} {} {}"; + + WHEN("calling format with the same number of arguments as there are placeholders") + { + kstd::format_to(std::ostream_iterator{buffer}, fmt, 1, true, -100); + + THEN("the formatted string is written to the iterator") + { + REQUIRE(buffer.str() == "Here are some placeholders: 1 true -100"); + } + } + + WHEN("calling format with too many arguments") + { + kstd::format_to(std::ostream_iterator{buffer}, fmt, 2, false, -200, 4, 5, 6); + + THEN("the formatted string is written to the iterator") + { + REQUIRE(buffer.str() == "Here are some placeholders: 2 false -200"); + } + } + } +} \ No newline at end of file -- cgit v1.2.3 From 3795115641bf5c1d1a3d60313408ba462057ba18 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 13 Apr 2026 16:18:00 +0200 Subject: kstd/format: add support for char formatting --- .../include/kstd/bits/format/formatter/char.hpp | 94 ++++++++++++++++++++++ libs/kstd/include/kstd/format | 1 + libs/kstd/tests/src/format.cpp | 8 +- 3 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 libs/kstd/include/kstd/bits/format/formatter/char.hpp diff --git a/libs/kstd/include/kstd/bits/format/formatter/char.hpp b/libs/kstd/include/kstd/bits/format/formatter/char.hpp new file mode 100644 index 0000000..ddfefe5 --- /dev/null +++ b/libs/kstd/include/kstd/bits/format/formatter/char.hpp @@ -0,0 +1,94 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_CHAR_HPP +#define KSTD_BITS_FORMAT_FORMATTER_CHAR_HPP + +#include "../context.hpp" +#include "../error.hpp" +#include "../formatter.hpp" +#include "../parse_context.hpp" +#include "../specifiers.hpp" +#include "integral.hpp" + +#include + +namespace kstd +{ + + template<> + struct formatter : formatter + { + bits::format::specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + + auto it = context.begin(); + auto const end = context.end(); + + if (specifiers.alternative_form || specifiers.zero_pad || specifiers.sign != bits::format::sign_mode::none) + { + bits::format::error("Invalid format specifiers for 'char'"); + } + + if (it != end && *it != '}') + { + if (*it == 'c' || *it == 'b' || *it == 'B' || *it == 'd' || *it == 'o' || *it == 'x' || *it == 'X') + { + specifiers.type = *it; + std::advance(it, 1); + } + else + { + bits::format::error("Invalid type specifier for char."); + } + } + + if (it != end && *it != '}') + { + bits::format::error("Missing terminating '}' in format string."); + } + + return it; + } + + auto format(char value, format_context & context) const -> void + { + if (specifiers.type == '\0' || specifiers.type == 'c') + { + auto final_width = 0uz; + + if (specifiers.mode == bits::format::width_mode::static_value) + { + final_width = specifiers.width_value; + } + else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id) + { + auto const & arg = context.arg(specifiers.width_value); + final_width = bits::format::extrat_dynamic_width(arg); + } + + auto padding = + bits::format::calculate_format_padding(final_width, 1, specifiers.align, bits::format::alignment::left); + + for (auto i = 0uz; i < padding.left; ++i) + { + context.push(specifiers.fill); + } + + context.push(value); + + for (auto i = 0uz; i < padding.right; ++i) + { + context.push(specifiers.fill); + } + } + else + { + formatter::format(static_cast(value), context); + } + } + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/format b/libs/kstd/include/kstd/format index d11c221..047ea5c 100644 --- a/libs/kstd/include/kstd/format +++ b/libs/kstd/include/kstd/format @@ -7,6 +7,7 @@ #include "bits/format/formatter.hpp" // IWYU pragma: export #include "bits/format/formatter/bool.hpp" // IWYU pragma: export #include "bits/format/formatter/byte.hpp" // IWYU pragma: export +#include "bits/format/formatter/char.hpp" // IWYU pragma: export #include "bits/format/formatter/cstring.hpp" // IWYU pragma: export #include "bits/format/formatter/integral.hpp" // IWYU pragma: export #include "bits/format/formatter/ordering.hpp" // IWYU pragma: export diff --git a/libs/kstd/tests/src/format.cpp b/libs/kstd/tests/src/format.cpp index 4915e50..73c8102 100644 --- a/libs/kstd/tests/src/format.cpp +++ b/libs/kstd/tests/src/format.cpp @@ -40,21 +40,21 @@ SCENARIO("Formatting to a new string", "[format]") WHEN("calling format with the same number of arguments as there are placeholders") { - auto result = kstd::format(fmt, 1, true, -100); + auto result = kstd::format(fmt, 1, true, 'a'); THEN("the result is the formatted string") { - REQUIRE(result == "Here are some placeholders: 1 true -100"); + REQUIRE(result == "Here are some placeholders: 1 true a"); } } WHEN("calling format with too many arguments") { - auto result = kstd::format(fmt, 2, false, -200, 4, 5, 6); + auto result = kstd::format(fmt, 2, false, 'b', 4, 5, 6); THEN("the result is the formatted string") { - REQUIRE(result == "Here are some placeholders: 2 false -200"); + REQUIRE(result == "Here are some placeholders: 2 false b"); } } } -- cgit v1.2.3 From eb36544624c18a284debdf78b43fe627f40a8371 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 13 Apr 2026 18:49:21 +0200 Subject: Rename and refactor --- kapi/include/kapi/filesystem.hpp | 20 ++++++++ kapi/include/kapi/syscall_handler/filesystem.hpp | 19 -------- kernel/kapi/filesystem.cpp | 60 ++++++++++++++++++++++++ kernel/kapi/syscall_handler/filesystem.cpp | 59 ----------------------- 4 files changed, 80 insertions(+), 78 deletions(-) create mode 100644 kapi/include/kapi/filesystem.hpp delete mode 100644 kapi/include/kapi/syscall_handler/filesystem.hpp create mode 100644 kernel/kapi/filesystem.cpp delete mode 100644 kernel/kapi/syscall_handler/filesystem.cpp diff --git a/kapi/include/kapi/filesystem.hpp b/kapi/include/kapi/filesystem.hpp new file mode 100644 index 0000000..dba5d54 --- /dev/null +++ b/kapi/include/kapi/filesystem.hpp @@ -0,0 +1,20 @@ +#ifndef TEACHOS_KAPI_FILESYSTEM_HPP +#define TEACHOS_KAPI_FILESYSTEM_HPP + +#include +#include +#include + +namespace kapi::filesystem +{ + auto mount(std::string_view source, std::string_view target) -> int; + auto umount(std::string_view target) -> int; + + auto open(std::string_view path) -> int; + auto close(int fd) -> int; + + auto read(int fd, void * buffer, size_t size) -> ssize_t; + auto write(int fd, void const * buffer, size_t size) -> ssize_t; +} // namespace kapi::filesystem + +#endif // TEACHOS_KAPI_FILESYSTEM_HPP \ No newline at end of file diff --git a/kapi/include/kapi/syscall_handler/filesystem.hpp b/kapi/include/kapi/syscall_handler/filesystem.hpp deleted file mode 100644 index 86ae4d2..0000000 --- a/kapi/include/kapi/syscall_handler/filesystem.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef TEACHOS_KAPI_SYSCALL_HANDLER_FILESYSTEM_HPP -#define TEACHOS_KAPI_SYSCALL_HANDLER_FILESYSTEM_HPP - -#include -#include - -namespace kapi::syscall_handler::filesystem -{ - auto mount(char const * source, char const * target) -> int; - auto umount(char const * target) -> int; - - auto open(char const * path) -> int; - auto close(int fd) -> int; - - auto read(int fd, void * buffer, size_t size) -> ssize_t; - auto write(int fd, void const * buffer, size_t size) -> ssize_t; -} // namespace kapi::syscall_handler::filesystem - -#endif // TEACHOS_KAPI_SYSCALL_HANDLER_FILESYSTEM_HPP \ No newline at end of file diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp new file mode 100644 index 0000000..a734ac0 --- /dev/null +++ b/kernel/kapi/filesystem.cpp @@ -0,0 +1,60 @@ +#include "kapi/filesystem.hpp" + +#include "kernel/filesystem/file_descriptor_table.hpp" +#include "kernel/filesystem/vfs.hpp" + +#include +#include +#include + +namespace kapi::filesystem +{ + auto mount(std::string_view source, std::string_view target) -> int + { + // TODO BA-FS26 + } + + auto umount(std::string_view target) -> int + { + if (kernel::filesystem::vfs::get().unmount(target) == kernel::filesystem::vfs::operation_result::success) + { + return 0; + } + return -1; + } + + auto open(std::string_view path) -> int + { + if (auto open_file_description = kernel::filesystem::vfs::get().open(path)) + { + return kernel::filesystem::file_descriptor_table::get().add_file(open_file_description); + } + + return -1; + } + + auto close(int fd) -> int + { + return kernel::filesystem::file_descriptor_table::get().remove_file(fd); + } + + auto read(int fd, void * buffer, size_t size) -> ssize_t + { + if (auto open_file_description = kernel::filesystem::file_descriptor_table::get().get_file(fd)) + { + return open_file_description->read(buffer, size); + } + + return -1; + } + + auto write(int fd, void const * buffer, size_t size) -> ssize_t + { + if (auto open_file_description = kernel::filesystem::file_descriptor_table::get().get_file(fd)) + { + return open_file_description->write(buffer, size); + } + + return -1; + } +} // namespace kapi::filesystem \ No newline at end of file diff --git a/kernel/kapi/syscall_handler/filesystem.cpp b/kernel/kapi/syscall_handler/filesystem.cpp deleted file mode 100644 index a6e8027..0000000 --- a/kernel/kapi/syscall_handler/filesystem.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "kapi/syscall_handler/filesystem.hpp" - -#include "kernel/filesystem/file_descriptor_table.hpp" -#include "kernel/filesystem/vfs.hpp" - -#include -#include - -namespace kapi::syscall_handler::filesystem -{ - auto mount(char const * source, char const * target) -> int - { - // TODO BA-FS26 - } - - auto umount(char const * target) -> int - { - if (kernel::filesystem::vfs::get().unmount(target) == kernel::filesystem::vfs::operation_result::success) - { - return 0; - } - return -1; - } - - auto open(char const * path) -> int - { - if (auto open_file_description = kernel::filesystem::vfs::get().open(path)) - { - return kernel::filesystem::file_descriptor_table::get().add_file(open_file_description); - } - - return -1; - } - - auto close(int fd) -> int - { - return kernel::filesystem::file_descriptor_table::get().remove_file(fd); - } - - auto read(int fd, void * buffer, size_t size) -> ssize_t - { - if (auto open_file_description = kernel::filesystem::file_descriptor_table::get().get_file(fd)) - { - return open_file_description->read(buffer, size); - } - - return -1; - } - - auto write(int fd, void const * buffer, size_t size) -> ssize_t - { - if (auto open_file_description = kernel::filesystem::file_descriptor_table::get().get_file(fd)) - { - return open_file_description->write(buffer, size); - } - - return -1; - } -} // namespace kapi::syscall_handler::filesystem \ No newline at end of file -- cgit v1.2.3 From 5e183b418b0e65dcdffa02a43702a0d6deb43b04 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 13 Apr 2026 21:17:50 +0200 Subject: Back filesystem by inode and not device --- .../include/kernel/filesystem/devfs/filesystem.hpp | 6 ++-- .../include/kernel/filesystem/ext2/filesystem.hpp | 8 ++--- kernel/include/kernel/filesystem/filesystem.hpp | 29 ++++++++-------- .../kernel/filesystem/rootfs/filesystem.hpp | 8 ++--- kernel/include/kernel/filesystem/vfs.hpp | 15 +++++---- kernel/kapi/filesystem.cpp | 6 +++- kernel/src/filesystem/devfs/filesystem.cpp | 2 +- kernel/src/filesystem/ext2/filesystem.cpp | 21 +++++------- kernel/src/filesystem/ext2/filesystem.tests.cpp | 13 ++++++-- kernel/src/filesystem/ext2/inode.cpp | 7 ++-- kernel/src/filesystem/ext2/inode.tests.cpp | 13 ++++++-- kernel/src/filesystem/filesystem.cpp | 22 ++++++------ kernel/src/filesystem/rootfs/filesystem.cpp | 4 +-- kernel/src/filesystem/vfs.cpp | 39 +++++++++++----------- kernel/src/filesystem/vfs.tests.cpp | 32 +++++++++--------- kernel/src/main.cpp | 14 ++------ 16 files changed, 117 insertions(+), 122 deletions(-) diff --git a/kernel/include/kernel/filesystem/devfs/filesystem.hpp b/kernel/include/kernel/filesystem/devfs/filesystem.hpp index 137eca3..3a52403 100644 --- a/kernel/include/kernel/filesystem/devfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/devfs/filesystem.hpp @@ -1,8 +1,6 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVFS_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_FILESYSTEM_HPP -#include "kapi/devices/device.hpp" - #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" @@ -23,10 +21,10 @@ namespace kernel::filesystem::devfs { /** @brief Initializes the devfs instance and builds the device inode table. - @param device Backing device passed by the generic filesystem interface (not required by devfs). + @param backing_inode Backing inode passed by the vfs (not required by devfs). @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & device) -> operation_result override; + auto mount(kstd::shared_ptr const & backing_inode) -> operation_result override; /** @brief Looks up an inode by @p name within a @p parent directory. diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index a71385f..9112866 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -1,8 +1,6 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP -#include "kapi/devices/device.hpp" - #include "kernel/filesystem/ext2/block_group_descriptor.hpp" #include "kernel/filesystem/ext2/inode.hpp" #include "kernel/filesystem/ext2/superblock.hpp" @@ -46,11 +44,11 @@ namespace kernel::filesystem::ext2 struct filesystem : kernel::filesystem::filesystem { /** - @brief Initializes the ext2 filesystem with the given @p device. - @param device The device to mount. + @brief Initializes the ext2 filesystem with the given @p backing_inode. + @param backing_inode The backing inode to mount. @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & device) -> operation_result override; + auto mount(kstd::shared_ptr const & backing_inode) -> operation_result override; /** @brief Looks up an inode by @p name within a @p parent directory. diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index ef6929a..099caee 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -1,8 +1,6 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP -#include "kapi/devices/device.hpp" - #include "kernel/filesystem/inode.hpp" #include @@ -32,22 +30,23 @@ namespace kernel::filesystem virtual ~filesystem() = default; /** - @brief Probes the given @p device to determine if it contains a recognizable filesystem, and if so, mounts it and - returns a pointer to the mounted filesystem instance. This method iterates through known filesystem types and - attempts to initialize it with the device until the mount was successful or all types have been tried. - @param device The device to probe and mount. + @brief Probes the given @p backing_inode to determine if it contains a recognizable filesystem, and if so, mounts it + and returns a pointer to the mounted filesystem instance. This method iterates through known filesystem types and + attempts to initialize it with the backing inode until the mount was successful or all types have been tried. + @param backing_inode The inode to probe and mount. @return A pointer to the mounted filesystem instance if successful, or a null pointer if no recognizable filesystem - is found on the device. - @warning Panics if @p device is null. + is found on the backing inode. + @warning Panics if @p backing_inode is null. */ - auto static probe_and_mount(kstd::shared_ptr const & device) -> kstd::shared_ptr; + auto static probe_and_mount(kstd::shared_ptr const & backing_inode) -> kstd::shared_ptr; /** - @brief Initializes the filesystem with the given @p device. - @param device The device to mount. + @brief Initializes the filesystem with the given @p backing_inode. + @param backing_inode The inode to use as the backing inode for the filesystem. (This is typically the inode + representing the block device or another inode which contains the filesystem data.) @return The result of the mount operation. */ - virtual auto mount(kstd::shared_ptr const & device) -> operation_result; + virtual auto mount(kstd::shared_ptr const & backing_inode) -> operation_result; /** @brief Looks up a child inode within the given @p parent inode with the specified @p name. This method must be @@ -65,13 +64,13 @@ namespace kernel::filesystem [[nodiscard]] auto root_inode() const -> kstd::shared_ptr const &; /** - @brief Returns a reference to the device associated with the filesystem. + @brief Returns a reference to the backing inode of the filesystem. */ - [[nodiscard]] auto device() const -> kstd::shared_ptr const &; + [[nodiscard]] auto backing_inode() const -> kstd::shared_ptr const &; protected: kstd::shared_ptr m_root_inode{}; - kstd::shared_ptr m_device{}; + kstd::shared_ptr m_backing_inode{}; kstd::vector> m_inodes{}; }; diff --git a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp index 0155c41..e14a1ee 100644 --- a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp @@ -1,8 +1,6 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_FILESYSTEM_HPP -#include "kapi/devices/device.hpp" - #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" @@ -23,11 +21,11 @@ namespace kernel::filesystem::rootfs struct filesystem : kernel::filesystem::filesystem { /** - @brief Initializes the rootfs filesystem with the given @p device. - @param device The device to mount (not required by rootfs). + @brief Initializes the rootfs filesystem with the given @p backing_inode. + @param backing_inode The backing inode to mount (not required by rootfs). @return The result of the mount operation. */ - auto mount(kstd::shared_ptr const & device) -> operation_result override; + auto mount(kstd::shared_ptr const & backing_inode) -> operation_result override; /** @brief Looks up an inode by @p name within a @p parent directory. diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 4dd2a83..2a9d5f7 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -27,9 +27,10 @@ namespace kernel::filesystem { success = 0, invalid_path = -1, - mount_point_not_found = -2, - filesystem_null = -3, - unmount_failed = -4 + non_existent_path = -2, + mount_point_not_found = -3, + unmount_failed = -4, + invalid_filesystem = -5, }; /** @@ -58,12 +59,12 @@ namespace kernel::filesystem auto open(std::string_view path) -> kstd::shared_ptr; /** - @brief Mount a @p filesystem at a specific @p path. - @param path The path where the filesystem should be mounted. - @param filesystem The filesystem to mount. + @brief Mount a @p source path to a specific @p target path. + @param source The source of the filesystem to mount. + @param target The path where the filesystem should be mounted. @return The result of the mount operation. */ - auto do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> operation_result; + auto do_mount(std::string_view source, std::string_view target) -> operation_result; /** @brief Unmount the filesystem mounted at the specified @p path. diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index a734ac0..30201b7 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -11,7 +11,11 @@ namespace kapi::filesystem { auto mount(std::string_view source, std::string_view target) -> int { - // TODO BA-FS26 + if (kernel::filesystem::vfs::get().do_mount(source, target) == kernel::filesystem::vfs::operation_result::success) + { + return 0; + } + return -1; } auto umount(std::string_view target) -> int diff --git a/kernel/src/filesystem/devfs/filesystem.cpp b/kernel/src/filesystem/devfs/filesystem.cpp index 03b4218..dd60c5d 100644 --- a/kernel/src/filesystem/devfs/filesystem.cpp +++ b/kernel/src/filesystem/devfs/filesystem.cpp @@ -14,7 +14,7 @@ namespace kernel::filesystem::devfs { - auto filesystem::mount(kstd::shared_ptr const &) -> operation_result + auto filesystem::mount(kstd::shared_ptr const &) -> operation_result { m_root_inode = kstd::make_shared(); build_device_inode_table(); diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 0ad5c97..c0f97ed 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -1,8 +1,5 @@ #include "kernel/filesystem/ext2/filesystem.hpp" -#include "kapi/devices/device.hpp" - -#include "kernel/devices/block_device_utils.hpp" #include "kernel/filesystem/ext2/block_group_descriptor.hpp" #include "kernel/filesystem/ext2/inode.hpp" #include "kernel/filesystem/ext2/linked_directory_entry.hpp" @@ -32,12 +29,11 @@ namespace kernel::filesystem::ext2 } } // namespace - auto filesystem::mount(kstd::shared_ptr const & device) -> operation_result + auto filesystem::mount(kstd::shared_ptr const & backing_inode) -> operation_result { - kernel::filesystem::filesystem::mount(device); + kernel::filesystem::filesystem::mount(backing_inode); - kernel::devices::block_device_utils::read(m_device, &m_superblock, constants::superblock_offset, - sizeof(m_superblock)); + m_backing_inode->read(&m_superblock, constants::superblock_offset, sizeof(m_superblock)); if (m_superblock.magic != constants::magic_number) { @@ -51,9 +47,8 @@ namespace kernel::filesystem::ext2 m_block_group_descriptors = kstd::vector(num_block_groups); auto const block_group_descriptor_table_offset = block_size == 1024 ? 2 * block_size : block_size; - kernel::devices::block_device_utils::read(m_device, m_block_group_descriptors.data(), - block_group_descriptor_table_offset, - num_block_groups * sizeof(block_group_descriptor)); + m_backing_inode->read(m_block_group_descriptors.data(), block_group_descriptor_table_offset, + num_block_groups * sizeof(block_group_descriptor)); m_root_inode = read_inode(constants::root_inode_number); @@ -86,7 +81,7 @@ namespace kernel::filesystem::ext2 { auto const global_block_number = map_inode_block_index_to_global_block_number(i, inode_data); auto const block_offset = global_block_number * block_size; - kernel::devices::block_device_utils::read(m_device, buffer.data(), block_offset, block_size); + m_backing_inode->read(buffer.data(), block_offset, block_size); auto const * entry = reinterpret_cast(buffer.data()); auto bytes_read = 0uz; @@ -125,7 +120,7 @@ namespace kernel::filesystem::ext2 auto const inode_offset = inode_table_offset + inode_index_within_group * get_inode_size(); auto new_inode = kstd::make_shared(this); - kernel::devices::block_device_utils::read(m_device, &new_inode->m_data, inode_offset, sizeof(inode_data)); + m_backing_inode->read(&new_inode->m_data, inode_offset, sizeof(inode_data)); // TODO BA-FS26 improve inode_kind really needed? or just map it to the mode bits? if (S_ISREG(new_inode->m_data.mode)) @@ -209,7 +204,7 @@ namespace kernel::filesystem::ext2 auto const block_start_offset = block_number * get_block_size(); auto const number_start_address = block_start_offset + index * sizeof(uint32_t); - kernel::devices::block_device_utils::read(m_device, &block_number_buffer, number_start_address, sizeof(uint32_t)); + m_backing_inode->read(&block_number_buffer, number_start_address, sizeof(uint32_t)); return block_number_buffer; } diff --git a/kernel/src/filesystem/ext2/filesystem.tests.cpp b/kernel/src/filesystem/ext2/filesystem.tests.cpp index b13ebf3..a7b8d5b 100644 --- a/kernel/src/filesystem/ext2/filesystem.tests.cpp +++ b/kernel/src/filesystem/ext2/filesystem.tests.cpp @@ -1,6 +1,7 @@ #include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/devices/storage/management.hpp" +#include "kernel/filesystem/device_inode.hpp" #include "kernel/filesystem/ext2/inode.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/test_support/devices/block_device.hpp" @@ -29,8 +30,10 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, auto boot_device = kernel::devices::storage::management::get().determine_boot_device(); REQUIRE(boot_device != nullptr); + auto dev_inode = kstd::make_shared(boot_device); + auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(boot_device) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); THEN("the root inode is available and is a directory") { @@ -72,11 +75,13 @@ SCENARIO("Ext2 filesystem rejects invalid magic", "[filesystem][ext2][filesystem auto device = kstd::make_shared(0, 0, "mock", block_size, 2 * block_size); REQUIRE(device != nullptr); + auto dev_inode = kstd::make_shared(device); + auto fs = kernel::filesystem::ext2::filesystem{}; THEN("mount fails with invalid_magic_number") { - REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::invalid_magic_number); + REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::invalid_magic_number); } } } @@ -92,8 +97,10 @@ SCENARIO("Ext2 block mapping includes direct and all indirect levels", "[filesys kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); + auto dev_inode = kstd::make_shared(device); + auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); auto inode_data = kernel::filesystem::ext2::inode_data{}; inode_data.block[0] = 7; diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index a29bb3b..bf3f0cf 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -2,7 +2,6 @@ #include "kapi/system.hpp" -#include "kernel/devices/block_device_utils.hpp" #include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/inode.hpp" @@ -41,8 +40,8 @@ namespace kernel::filesystem::ext2 auto const read_offset = block_start_offset + in_block_offset; auto const bytes_to_read = std::min(size - bytes_read, m_filesystem->get_block_size() - in_block_offset); - bytes_read += kernel::devices::block_device_utils::read( - m_filesystem->device(), static_cast(buffer) + bytes_read, read_offset, bytes_to_read); + bytes_read += + m_filesystem->backing_inode()->read(static_cast(buffer) + bytes_read, read_offset, bytes_to_read); block_index++; in_block_offset = 0; // After the first block, we always start at the beginning of the block @@ -56,4 +55,4 @@ namespace kernel::filesystem::ext2 kapi::system::panic("[EXT2] inode::write is not implemented yet"); return 0; } -} // namespace kernel::filesystem::ext2 \ No newline at end of file +} // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp index 795ff10..ae66aff 100644 --- a/kernel/src/filesystem/ext2/inode.tests.cpp +++ b/kernel/src/filesystem/ext2/inode.tests.cpp @@ -1,6 +1,7 @@ #include "kernel/filesystem/ext2/inode.hpp" #include "kernel/devices/storage/management.hpp" +#include "kernel/filesystem/device_inode.hpp" #include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/test_support/cpu.hpp" @@ -54,8 +55,10 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 in auto boot_device = kernel::devices::storage::management::get().determine_boot_device(); REQUIRE(boot_device != nullptr); + auto dev_inode = kstd::make_shared(boot_device); + auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(boot_device) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); auto information = fs.lookup(fs.root_inode(), "information"); REQUIRE(information != nullptr); @@ -94,8 +97,10 @@ SCENARIO("Ext2 inode read stops when block mapping resolves to zero", "[filesyst REQUIRE(device != nullptr); kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); + auto dev_inode = kstd::make_shared(device); + auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); auto inode = kernel::filesystem::ext2::inode{&fs}; inode.m_data.blocks = 2; @@ -120,8 +125,10 @@ SCENARIO("Ext2 inode read across block boundaries", "[filesystem][ext2][inode]") REQUIRE(device != nullptr); kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); + auto dev_inode = kstd::make_shared(device); + auto fs = kernel::filesystem::ext2::filesystem{}; - REQUIRE(fs.mount(device) == kernel::filesystem::filesystem::operation_result::success); + REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); auto inode = kernel::filesystem::ext2::inode{&fs}; inode.m_data.blocks = 2; diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index d8b04eb..99e7456 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -1,6 +1,5 @@ #include "kernel/filesystem/filesystem.hpp" -#include "kapi/devices/device.hpp" #include "kapi/system.hpp" #include "kernel/filesystem/ext2/filesystem.hpp" @@ -19,18 +18,17 @@ namespace kernel::filesystem }; } // namespace - auto filesystem::probe_and_mount(kstd::shared_ptr const & device) - -> kstd::shared_ptr + auto filesystem::probe_and_mount(kstd::shared_ptr const & backing_inode) -> kstd::shared_ptr { - if (!device) + if (!backing_inode) { - kapi::system::panic("[FILESYSTEM] cannot mount filesystem: device is null."); + kapi::system::panic("[FILESYSTEM] cannot mount filesystem: backing inode is null."); } for (auto & factory : filesystem_factories) { auto fs = factory(); - if (fs->mount(device) == operation_result::success) + if (fs->mount(backing_inode) == operation_result::success) { return fs; } @@ -39,14 +37,14 @@ namespace kernel::filesystem return nullptr; } - auto filesystem::mount(kstd::shared_ptr const & device) -> operation_result + auto filesystem::mount(kstd::shared_ptr const & backing_inode) -> operation_result { - if (!device) + if (!backing_inode) { - kapi::system::panic("[FILESYSTEM] cannot mount filesystem: device is null."); + kapi::system::panic("[FILESYSTEM] cannot mount filesystem: backing inode is null."); } - m_device = device; + m_backing_inode = backing_inode; return operation_result::success; } @@ -55,8 +53,8 @@ namespace kernel::filesystem return m_root_inode; } - auto filesystem::device() const -> kstd::shared_ptr const & + auto filesystem::backing_inode() const -> kstd::shared_ptr const & { - return m_device; + return m_backing_inode; } } // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/rootfs/filesystem.cpp b/kernel/src/filesystem/rootfs/filesystem.cpp index dffef99..f718c72 100644 --- a/kernel/src/filesystem/rootfs/filesystem.cpp +++ b/kernel/src/filesystem/rootfs/filesystem.cpp @@ -1,7 +1,5 @@ #include "kernel/filesystem/rootfs/filesystem.hpp" -#include "kapi/devices/device.hpp" - #include "kernel/filesystem/inode.hpp" #include "kernel/filesystem/rootfs/inode.hpp" @@ -11,7 +9,7 @@ namespace kernel::filesystem::rootfs { - auto filesystem::mount(kstd::shared_ptr const &) -> operation_result + auto filesystem::mount(kstd::shared_ptr const &) -> operation_result { auto rfs_inode = kstd::make_shared(); rfs_inode->add_child("dev"); diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 67d1af2..394e926 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -2,7 +2,6 @@ #include "kapi/system.hpp" -#include "kernel/devices/storage/management.hpp" #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/devfs/filesystem.hpp" #include "kernel/filesystem/filesystem.hpp" @@ -43,19 +42,17 @@ namespace kernel::filesystem auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode()); m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, "", nullptr)); - auto storage_mgmt = devices::storage::management::get(); - if (auto boot_device = storage_mgmt.determine_boot_device()) + auto device_fs = kstd::make_shared(); + device_fs->mount(nullptr); + do_mount_internal("/dev", root_fs_root_dentry, device_fs); + + if (auto boot_device_dentry = resolve_path("/dev/ram0")) // TODO BA-FS26 better boot device detection { - auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device); - if (boot_root_fs) + if (auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device_dentry->get_inode())) { do_mount_internal("/", root_fs_root_dentry, boot_root_fs); } } - - auto device_fs = kstd::make_shared(); - device_fs->mount(nullptr); - do_mount_internal("/dev", root_fs_root_dentry, device_fs); } auto vfs::get() -> vfs & @@ -78,24 +75,26 @@ namespace kernel::filesystem return nullptr; } - auto vfs::do_mount(std::string_view path, kstd::shared_ptr const & filesystem) -> operation_result + auto vfs::do_mount(std::string_view source, std::string_view target) -> operation_result { - if (!filesystem) - { - return operation_result::filesystem_null; - } - - if (path.empty() || path.front() != '/' || (path.size() > 1 && path.back() == '/')) + if (target.empty() || target.front() != '/' || (target.size() > 1 && target.back() == '/')) { return operation_result::invalid_path; } - if (auto mount_point_dentry = resolve_path(path)) + if (auto mount_point_dentry = resolve_path(target)) { - do_mount_internal(path, mount_point_dentry, filesystem); - return operation_result::success; + if (auto source_dentry = resolve_path(source)) + { + if (auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode())) + { + do_mount_internal(target, mount_point_dentry, fs); + return operation_result::success; + } + return operation_result::invalid_filesystem; + } + return operation_result::non_existent_path; } - return operation_result::mount_point_not_found; } diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index f363041..8c963c6 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -67,11 +67,6 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS {image_path_1, image_path_2, image_path_3})); auto & vfs = kernel::filesystem::vfs::get(); - auto storage_mgmt = kernel::devices::storage::management::get(); - auto device_1 = storage_mgmt.device_by_major_minor(1, 16); - auto fs_1 = kernel::filesystem::filesystem::probe_and_mount(device_1); - auto device_2 = storage_mgmt.device_by_major_minor(1, 32); - auto fs_2 = kernel::filesystem::filesystem::probe_and_mount(device_2); THEN("vfs initializes first module as root") { @@ -85,7 +80,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("second image can be mounted, data retrieved and unmounted again") { - REQUIRE(vfs.do_mount("/information", fs_1) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); REQUIRE(mounted_monkey_1 != nullptr); @@ -100,8 +95,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("third image can be mounted in a mounted file system, unmount only if no child mount exists") { - REQUIRE(vfs.do_mount("/information", fs_1) == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.do_mount("/information/monkey_house/infrastructure", fs_2) == + REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram32", "/information/monkey_house/infrastructure") == kernel::filesystem::vfs::operation_result::success); auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); @@ -118,8 +113,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("images can be stacked mounted and correct file system is unmounted again") { - REQUIRE(vfs.do_mount("/information", fs_1) == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.do_mount("/information", fs_2) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/dev/ram32", "/information") == kernel::filesystem::vfs::operation_result::success); auto mounted_tickets = vfs.open("/information/entrance/tickets.txt"); REQUIRE(mounted_tickets != nullptr); @@ -134,19 +129,26 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("mount with null file system fails") { - REQUIRE(vfs.do_mount("/information", nullptr) == kernel::filesystem::vfs::operation_result::filesystem_null); + REQUIRE(vfs.do_mount("/closed.txt", "/information") == + kernel::filesystem::vfs::operation_result::invalid_filesystem); } THEN("mount with invalid path fails") { - REQUIRE(vfs.do_mount("", fs_1) == kernel::filesystem::vfs::operation_result::invalid_path); - REQUIRE(vfs.do_mount("information", fs_1) == kernel::filesystem::vfs::operation_result::invalid_path); - REQUIRE(vfs.do_mount("/information/", fs_1) == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(vfs.do_mount("/dev/ram16", "") == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(vfs.do_mount("/dev/ram16", "information") == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(vfs.do_mount("/dev/ram16", "/information/") == kernel::filesystem::vfs::operation_result::invalid_path); + } + + THEN("mount with non-existent source path fails") + { + REQUIRE(vfs.do_mount("/dev/nonexistent", "/information") == + kernel::filesystem::vfs::operation_result::non_existent_path); } THEN("mount with non-existent mount point fails") { - REQUIRE(vfs.do_mount("/information/nonexistent", fs_1) == + REQUIRE(vfs.do_mount("/dev/ram16", "/information/nonexistent") == kernel::filesystem::vfs::operation_result::mount_point_not_found); } diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 79ed703..e296bd5 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -9,7 +9,6 @@ #include "kernel/devices/storage/management.hpp" #include "kernel/filesystem/device_inode.hpp" #include "kernel/filesystem/file_descriptor_table.hpp" -#include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/open_file_description.hpp" #include "kernel/filesystem/vfs.hpp" #include "kernel/memory.hpp" @@ -138,24 +137,17 @@ auto test_file_lookup() -> void "this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_" "limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_30.txt"); - auto storage_mgmt = kernel::devices::storage::management::get(); - auto device_1 = storage_mgmt.device_by_major_minor(1, 16); - auto fs_1 = kernel::filesystem::filesystem::probe_and_mount(device_1); - - vfs.do_mount("/enclosures/aquarium", fs_1); + vfs.do_mount("/dev/ram16", "/enclosures/aquarium"); read_and_write_file("/enclosures/aquarium/closed.txt"); read_and_write_file("/enclosures/aquarium/information/info_2.txt"); vfs.unmount("/enclosures/aquarium"); read_and_write_file("/enclosures/aquarium/tank_1/fish_4.txt"); - auto device_2 = storage_mgmt.device_by_major_minor(1, 32); - auto fs_2 = kernel::filesystem::filesystem::probe_and_mount(device_2); - - vfs.do_mount("/enclosures/elephant_house", fs_2); + vfs.do_mount("/dev/ram32", "/enclosures/elephant_house"); read_and_write_file("/enclosures/elephant_house/monkey_house/infrastructure/info.txt"); - vfs.do_mount("/enclosures/elephant_house/monkey_house", fs_1); + vfs.do_mount("/dev/ram16", "/enclosures/elephant_house/monkey_house"); read_and_write_file("/enclosures/elephant_house/monkey_house/information/info_2.txt"); auto result = vfs.unmount("/enclosures/elephant_house"); -- cgit v1.2.3 From 5354654a486296be674ecc7ba5e92ca01cc29107 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 14 Apr 2026 08:44:41 +0200 Subject: add tests to mount filesystems backed by a file --- arch/x86_64/support/modules/README.md | 26 +++++++++ arch/x86_64/support/modules/ext2_1KB_fs.img | 2 +- kernel/src/filesystem/vfs.tests.cpp | 67 +++++++++++++++++++++- .../filesystem/test_assets/ext2_1KB_fs.img | 2 +- 4 files changed, 93 insertions(+), 4 deletions(-) diff --git a/arch/x86_64/support/modules/README.md b/arch/x86_64/support/modules/README.md index f3955fa..fb64767 100644 --- a/arch/x86_64/support/modules/README.md +++ b/arch/x86_64/support/modules/README.md @@ -5,11 +5,37 @@ The ext2_4KB_fs image is intentionally fragmented, as some files were created an ## ext2_1KB_fs . ./lost+found +./archiv +./archiv/2024.img +./archiv/2025.img ./information ./information/info_1.txt ./information/info_2.txt ./closed.txt +### 2024.img +(2KB Block size) +. +./lost+found +./sheep_1.txt +./sheep_2.txt +./stable/pig_1.txt +./stable/pig_2.txt +./stable/pig_3.txt + +### 2025.img +(4KB Block size) +. +./lost+found +./snake_1.txt +./snake_2.txt +./petting_zoo/goat_1.txt +./petting_zoo/goat_2.txt +./petting_zoo/chicken_coop +./petting_zoo/chicken_coop/chicken_1.txt +./petting_zoo/chicken_coop/chicken_2.txt +./petting_zoo/chicken_coop/chicken_3.txt + ## ext2_2KB_fs . ./lost+found diff --git a/arch/x86_64/support/modules/ext2_1KB_fs.img b/arch/x86_64/support/modules/ext2_1KB_fs.img index 9f1ee4a..5bbb76d 100644 --- a/arch/x86_64/support/modules/ext2_1KB_fs.img +++ b/arch/x86_64/support/modules/ext2_1KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94d3988bc309eb9e81f06042c72bf4c4fb5991cd7fdd597eb00c518a96c792d8 +oid sha256:c2ef9536a439825520d9e230eedaa9ae327f9763350eddbc0f24bf5b9b5d2bf2 size 10485760 diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 8c963c6..12dce84 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -1,12 +1,14 @@ #include "kernel/filesystem/vfs.hpp" -#include "kernel/devices/storage/management.hpp" -#include "kernel/filesystem/filesystem.hpp" #include "kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp" +#include + #include +#include #include +#include SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS with dummy modules", "[filesystem][vfs]") @@ -165,4 +167,65 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS kernel::filesystem::vfs::operation_result::mount_point_not_found); } } + + GIVEN("A real image file containing as filesystem formatted files") + { + REQUIRE(std::filesystem::exists(image_path_1)); + REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1"}, {image_path_1})); + + THEN("the file-filesystem in the image can be mounted, files can be read and unmounted again") + { + auto & vfs = kernel::filesystem::vfs::get(); + REQUIRE(vfs.do_mount("/archiv/2024.img", "/information") == kernel::filesystem::vfs::operation_result::success); + + auto sheep_1 = vfs.open("/information/sheep_1.txt"); + REQUIRE(sheep_1 != nullptr); + + kstd::vector buffer(7); + auto bytes_read = sheep_1->read(buffer.data(), buffer.size()); + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read}; + REQUIRE(buffer_as_str == "sheep_1"); + + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); + auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); + REQUIRE(unmounted_sheep_1 == nullptr); + } + + THEN("the file-filesystem in the image can be mounted and in this filesystem can another file-filesystem be " + "mounted, files can be read and unmounted again") + { + auto & vfs = kernel::filesystem::vfs::get(); + REQUIRE(vfs.do_mount("/archiv/2024.img", "/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/archiv/2025.img", "/information/stable") == + kernel::filesystem::vfs::operation_result::success); + + auto sheep_1 = vfs.open("/information/sheep_1.txt"); + auto goat_1 = vfs.open("/information/stable/petting_zoo/goat_1.txt"); + REQUIRE(sheep_1 != nullptr); + REQUIRE(goat_1 != nullptr); + + kstd::vector sheep_buffer(7); + auto bytes_read = sheep_1->read(sheep_buffer.data(), sheep_buffer.size()); + std::string_view buffer_as_str{reinterpret_cast(sheep_buffer.data()), bytes_read}; + REQUIRE(buffer_as_str == "sheep_1"); + + kstd::vector goat_buffer(6); + bytes_read = goat_1->read(goat_buffer.data(), goat_buffer.size()); + buffer_as_str = std::string_view{reinterpret_cast(goat_buffer.data()), bytes_read}; + REQUIRE(buffer_as_str == "goat_1"); + + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); + + REQUIRE(vfs.unmount("/information/stable") == kernel::filesystem::vfs::operation_result::success); + auto unmounted_goat_1 = vfs.open("/information/stable/petting_zoo/goat_1.txt"); + REQUIRE(unmounted_goat_1 == nullptr); + + auto still_mounted_sheep_1 = vfs.open("/information/sheep_1.txt"); + REQUIRE(still_mounted_sheep_1 != nullptr); + + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); + auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); + REQUIRE(unmounted_sheep_1 == nullptr); + } + } } diff --git a/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img b/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img index 9f1ee4a..5bbb76d 100644 --- a/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img +++ b/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94d3988bc309eb9e81f06042c72bf4c4fb5991cd7fdd597eb00c518a96c792d8 +oid sha256:c2ef9536a439825520d9e230eedaa9ae327f9763350eddbc0f24bf5b9b5d2bf2 size 10485760 -- cgit v1.2.3 From e70ea2357a80386b0a12138201b353d942910296 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 14 Apr 2026 09:43:45 +0200 Subject: add kapi filesystem tests --- kernel/CMakeLists.txt | 2 + kernel/kapi/filesystem.tests.cpp | 128 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 kernel/kapi/filesystem.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 2f6113a..e83e529 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -8,6 +8,7 @@ add_library("kernel_objs" OBJECT "kapi/devices/bus.cpp" "kapi/devices/cpu.cpp" "kapi/devices/device.cpp" + "kapi/filesystem.cpp" "kapi/interrupts.cpp" "kapi/memory.cpp" "kapi/system.cpp" @@ -124,6 +125,7 @@ else() # KAPI Shim Tests "kapi/cpu.tests.cpp" "kapi/system.tests.cpp" + "kapi/filesystem.tests.cpp" # KSTD Shim Tests "kstd/print.tests.cpp" diff --git a/kernel/kapi/filesystem.tests.cpp b/kernel/kapi/filesystem.tests.cpp new file mode 100644 index 0000000..aa24aed --- /dev/null +++ b/kernel/kapi/filesystem.tests.cpp @@ -0,0 +1,128 @@ +#include "kapi/filesystem.hpp" + +#include "kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp" + +#include + +#include +#include +#include +#include +#include + +SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kapi filesystem with real images", + "[kapi][filesystem]") +{ + auto const image_path_1 = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; + auto const image_path_2 = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_2KB_fs.img"; + + GIVEN("a real image file") + { + REQUIRE(std::filesystem::exists(image_path_1)); + REQUIRE(std::filesystem::exists(image_path_2)); + REQUIRE_NOTHROW( + setup_modules_from_img_and_init_vfs({"test_img_module_1", "test_img_module_2"}, {image_path_1, image_path_2})); + + THEN("files can be opened, read and closed again") + { + auto fd = kapi::filesystem::open("/information/info_1.txt"); + REQUIRE(fd >= 0); + + auto buffer = std::vector(6); + auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); + REQUIRE(bytes_read >= 0); + + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; + REQUIRE(buffer_as_str == "info_1"); + + REQUIRE(kapi::filesystem::close(fd) == 0); + } + + THEN("a filesystem can be mounted, files can be opened, read and closed again and unmounted") + { + REQUIRE(kapi::filesystem::mount("/dev/ram16", "/information") == 0); + + auto fd = kapi::filesystem::open("/information/monkey_house/monkey_1.txt"); + REQUIRE(fd >= 0); + + auto buffer = std::vector(8); + auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); + REQUIRE(bytes_read >= 0); + + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; + REQUIRE(buffer_as_str == "monkey_1"); + + REQUIRE(kapi::filesystem::close(fd) == 0); + REQUIRE(kapi::filesystem::umount("/information") == 0); + } + + THEN("device can be opened as file and read from") + { + auto fd = kapi::filesystem::open("/dev/ram0"); + REQUIRE(fd >= 0); + + auto buffer = std::vector(512); + auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); + REQUIRE(bytes_read >= 0); + + REQUIRE(kapi::filesystem::close(fd) == 0); + } + + THEN("device can be opened as file and written to and read from again") + { + auto read_fd = kapi::filesystem::open("/dev/ram16"); + REQUIRE(read_fd >= 0); + + auto buffer = std::vector(512, std::byte{0xAB}); + auto bytes_written = kapi::filesystem::write(read_fd, buffer.data(), buffer.size()); + REQUIRE(bytes_written >= 0); + + auto write_fd = kapi::filesystem::open("/dev/ram16"); + REQUIRE(write_fd >= 0); + + auto read_buffer = std::vector(512); + auto bytes_read = kapi::filesystem::read(write_fd, read_buffer.data(), read_buffer.size()); + REQUIRE(bytes_read >= 0); + + REQUIRE(std::equal(buffer.begin(), buffer.end(), read_buffer.begin())); + + REQUIRE(kapi::filesystem::close(write_fd) == 0); + REQUIRE(kapi::filesystem::close(read_fd) == 0); + } + + THEN("invalid paths cannot be mounted or unmounted") + { + REQUIRE(kapi::filesystem::mount("/dev/ram16", "invalid_path") < 0); + } + + THEN("invalid paths cannot be unmounted") + { + REQUIRE(kapi::filesystem::umount("invalid_path") < 0); + } + + THEN("non existent files cannot be opened") + { + auto fd = kapi::filesystem::open("/information/non_existent.txt"); + REQUIRE(fd < 0); + } + + THEN("not opened files cannot closed") + { + REQUIRE(kapi::filesystem::close(999) < 0); + } + + THEN("not opened files cannot be read from") + { + std::vector buffer(10); + auto bytes_read = kapi::filesystem::read(999, buffer.data(), buffer.size()); + REQUIRE(bytes_read < 0); + } + + THEN("not opened files cannot be written to") + { + std::vector buffer(10); + auto bytes_written = kapi::filesystem::write(999, buffer.data(), buffer.size()); + REQUIRE(bytes_written < 0); + } + } +} -- cgit v1.2.3 From 2f4591dbb173d602368437e1dd8c788b0bedd4aa Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 14 Apr 2026 13:23:52 +0200 Subject: kstd: add basic flat_map::try_insert --- libs/kstd/include/kstd/flat_map | 27 +++++++++++++++++++++++++ libs/kstd/include/kstd/vector | 39 +++++++++++++++++++++++++++++++++++ libs/kstd/tests/src/vector.cpp | 45 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+) diff --git a/libs/kstd/include/kstd/flat_map b/libs/kstd/include/kstd/flat_map index 3b13754..f12b1b5 100644 --- a/libs/kstd/include/kstd/flat_map +++ b/libs/kstd/include/kstd/flat_map @@ -256,6 +256,33 @@ namespace kstd }; } + //! Try to insert a element for the given key into this map. + //! + //! This function does nothing if the key is already present. + //! + //! @param key The key to insert a value for. + //! @param args The arguments to use to construct the mapped value. + template + auto try_emplace(key_type const & key, Args &&... args) -> std::pair + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(*found, key) && !m_comparator(key, *found)) + { + return {found, false}; + } + + auto offset = std::distance(m_containers.keys.cbegin(), found); + auto intersertion_point = m_containers.value.begin() + offset; + + auto inserted_key = m_containers.keys.emplace(key); + auto inserted_mapped = m_containers.values.emplace(std::forward(args)...); + + return { + iterator{inserted_key, inserted_mapped}, + true + }; + } + //! Find an element with an equivalent key. //! //! @param key The key to look up. diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 79593c6..e1b8b38 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -728,6 +728,45 @@ namespace kstd return begin() + prefix_size; } + template + constexpr auto emplace(const_iterator position, Args &&... args) -> iterator + { + auto prefix_size = std::ranges::distance(begin(), position); + auto suffix_size = std::ranges::distance(position, end()); + + if (position == end()) + { + emplace_back(std::forward(args)...); + return begin() + prefix_size; + } + + if (m_capacity == m_size) + { + auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + std::allocator_traits::construct(m_allocator, new_data + prefix_size, + std::forward(args)...); + uninitialized_move_with_allocator(begin(), new_data, prefix_size); + uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); + destroy_n(begin(), old_size); + deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size; + } + else + { + auto insert_position = begin() + prefix_size; + std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); + std::ranges::move_backward(insert_position, end() - 1, end()); + *insert_position = value_type{std::forward(args)...}; + } + + ++m_size; + return begin() + prefix_size; + } + //! Erase an element at a given position. //! //! @note This function will panic if position == end() diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 81bf32f..5faaaff 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -463,6 +463,33 @@ SCENARIO("Vector modifiers", "[vector]") } } + WHEN("emplace is called with the end iterator and constructor arguments") + { + v.emplace(v.end(), 20); + + THEN("the element is appended and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v.back() == 20); + } + } + + WHEN("emplace is called while capacity is sufficient") + { + v.reserve(10); + auto const capacity = v.capacity(); + + v.emplace(v.end(), 20); + + THEN("the element is appended without reallocation") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() == capacity); + REQUIRE(v.back() == 20); + } + } + WHEN("inserting an element") { auto it = v.insert(v.cbegin(), 42); @@ -526,6 +553,24 @@ SCENARIO("Vector modifiers", "[vector]") } } + WHEN("emplace is called with an iterator and constructor arguments") + { + auto it = v.emplace(v.begin() + 2, 25); + + THEN("the element is inserted and the size and capacity increase") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() >= initial_capacity); + REQUIRE(v.at(2) == 25); + } + + THEN("the returned iterator points to the inserted element") + { + REQUIRE(it == v.cbegin() + 2); + REQUIRE(*it == 25); + } + } + WHEN("push_back is called with a reference to an internal element") { v.shrink_to_fit(); -- cgit v1.2.3 From eacc1becd1308a01a7ffcddf7c8910c8dc708939 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 14 Apr 2026 13:52:19 +0200 Subject: acpi: begin test implementation --- .clangd | 1 + .devcontainer/x86-64/devcontainer.json | 2 +- libs/acpi/CMakeLists.txt | 19 +++++++++++++++++++ libs/acpi/acpi/pointers.cpp | 4 ++-- libs/acpi/acpi/pointers.test.cpp | 29 +++++++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 libs/acpi/acpi/pointers.test.cpp diff --git a/.clangd b/.clangd index fac5c82..ce14df5 100644 --- a/.clangd +++ b/.clangd @@ -11,6 +11,7 @@ Documentation: If: PathMatch: - "libs/.*/tests/.*\\.cpp" + - "libs/.*/.*\\.test\\.cpp" - "kernel/.*\\.tests.cpp" Diagnostics: ClangTidy: diff --git a/.devcontainer/x86-64/devcontainer.json b/.devcontainer/x86-64/devcontainer.json index 6bf1616..775da72 100644 --- a/.devcontainer/x86-64/devcontainer.json +++ b/.devcontainer/x86-64/devcontainer.json @@ -5,7 +5,7 @@ "ghcr.io/devcontainers/features/git:1": {}, "ghcr.io/devcontainers/features/git-lfs:1": {}, "ghcr.io/devcontainers-extra/features/apt-packages:1": { - "packages": "build-essential,clang-tidy,clangd,cmake,grub2-common,grub-pc,mtools,ninja-build,qemu-system-x86,ssh,xorriso,gdb" + "packages": "acpica-tools,build-essential,clang-tidy,clangd,cmake,grub2-common,grub-pc,mtools,ninja-build,qemu-system-x86,ssh,xorriso,gdb" } }, "customizations": { diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index c6e63b9..b8face4 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -27,3 +27,22 @@ target_sources("acpi" PUBLIC target_link_libraries("acpi" PUBLIC "libs::kstd" ) + +if(NOT CMAKE_CROSSCOMPILING) + add_executable("acpi_tests" + "acpi/pointers.test.cpp" + ) + + target_link_libraries("acpi_tests" PRIVATE + "Catch2::Catch2WithMain" + "libs::acpi" + ) + + set_target_properties("acpi_tests" PROPERTIES + C_CLANG_TIDY "" + CXX_CLANG_TIDY "" + EXCLUDE_FROM_ALL NO + ) + + catch_discover_tests("acpi_tests") +endif() diff --git a/libs/acpi/acpi/pointers.cpp b/libs/acpi/acpi/pointers.cpp index b206cc6..8a8629f 100644 --- a/libs/acpi/acpi/pointers.cpp +++ b/libs/acpi/acpi/pointers.cpp @@ -34,7 +34,7 @@ namespace acpi auto rsdp::validate() const noexcept -> bool { - return validate_checksum({reinterpret_cast(this), sizeof(rsdp)}); + return signature() == "RSD PTR " && validate_checksum({reinterpret_cast(this), sizeof(rsdp)}); } auto xsdp::length() const noexcept -> kstd::units::bytes @@ -49,7 +49,7 @@ namespace acpi auto xsdp::validate() const noexcept -> bool { - return validate_checksum({reinterpret_cast(this), m_length}); + return signature() == "RSD PTR " && validate_checksum({reinterpret_cast(this), m_length}); } } // namespace acpi diff --git a/libs/acpi/acpi/pointers.test.cpp b/libs/acpi/acpi/pointers.test.cpp new file mode 100644 index 0000000..06ce1a4 --- /dev/null +++ b/libs/acpi/acpi/pointers.test.cpp @@ -0,0 +1,29 @@ +#include +#include + +#include +#include +#include + +SCENARIO("ACPI root pointer parsing", "[acpi]") +{ + GIVEN("A null-filled pointer") + { + auto data = std::array{}; + + WHEN("parsing the data") + { + auto rsdp = std::bit_cast(data); + + THEN("the signature is invalid") + { + REQUIRE(rsdp.signature() != "RSD PTR "); + } + + THEN("validate returns false") + { + REQUIRE_FALSE(rsdp.validate()); + } + } + } +} -- cgit v1.2.3 From 1fa31688a0e237dec1170dcea9e8f0a0571a25e5 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 15 Apr 2026 08:55:03 +0200 Subject: acpi: add basic MADT tests --- arch/x86_64/kapi/cpu.cpp | 2 +- libs/acpi/CMakeLists.txt | 34 +++++++++++++++++++++++ libs/acpi/acpi/madt.cpp | 10 +++++-- libs/acpi/acpi/madt.hpp | 38 +++++++++++++++++++++++--- libs/acpi/acpi/madt.test.cpp | 55 ++++++++++++++++++++++++++++++++++++++ libs/acpi/acpi/sdt.cpp | 7 +++++ libs/acpi/acpi/sdt.hpp | 8 ++++++ libs/acpi/test_data/basic_madt.asl | 29 ++++++++++++++++++++ libs/acpi/test_data/basic_rsdt.asl | 26 ++++++++++++++++++ libs/acpi/test_data/tables.S | 14 ++++++++++ libs/acpi/test_data/tables.hpp | 25 +++++++++++++++++ 11 files changed, 242 insertions(+), 6 deletions(-) create mode 100644 libs/acpi/acpi/madt.test.cpp create mode 100644 libs/acpi/test_data/basic_madt.asl create mode 100644 libs/acpi/test_data/basic_rsdt.asl create mode 100644 libs/acpi/test_data/tables.S create mode 100644 libs/acpi/test_data/tables.hpp diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index a836b20..22936c2 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -60,7 +60,7 @@ namespace kapi::cpu } auto lapic_entries = *madt | std::views::filter([](auto const & entry) { - return entry.type() == ::acpi::madt_entry::types::processor_local_apic; + return entry.type() == ::acpi::madt_entry::type::processor_local_apic; }) | std::views::transform([](auto const & entry) { return static_cast<::acpi::processor_local_apic const &>(entry); }) | std::views::filter([](auto const & entry) { diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index b8face4..8ace42d 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -1,3 +1,11 @@ +cmake_minimum_required(VERSION "3.27.0") + +project("acpi" + DESCRIPTION "An ACPI parsing library" + VERSION "0.0.1" + LANGUAGES ASM CXX +) + add_library("acpi" STATIC) add_library("libs::acpi" ALIAS "acpi") @@ -29,8 +37,34 @@ target_link_libraries("acpi" PUBLIC ) if(NOT CMAKE_CROSSCOMPILING) + find_program(IASL_EXE NAMES "iasl" REQUIRED) + + set(TEST_TABLES + "basic_madt" + "basic_rsdt" + ) + + foreach(TABLE IN LISTS TEST_TABLES) + add_custom_command(OUTPUT "test_data/${TABLE}.aml" + COMMAND "${IASL_EXE}" -p "test_data/${TABLE}.aml" "${CMAKE_CURRENT_SOURCE_DIR}/test_data/${TABLE}.asl" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/test_data/${TABLE}.asl" + COMMENT "Compiling test_data/${TABLE}.asl" + VERBATIM + ) + list(APPEND GENERATED_TABLE_BLOBS "${CMAKE_CURRENT_BINARY_DIR}/test_data/${TABLE}.aml") + endforeach() + + set_source_files_properties("test_data/tables.S" PROPERTIES OBJECT_DEPENDS "${GENERATED_TABLE_BLOBS}") + add_executable("acpi_tests" + "acpi/madt.test.cpp" "acpi/pointers.test.cpp" + + "test_data/tables.S" + ) + + target_include_directories("acpi_tests" PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/test_data" ) target_link_libraries("acpi_tests" PRIVATE diff --git a/libs/acpi/acpi/madt.cpp b/libs/acpi/acpi/madt.cpp index 40289cf..6a62f07 100644 --- a/libs/acpi/acpi/madt.cpp +++ b/libs/acpi/acpi/madt.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -16,9 +17,14 @@ namespace acpi return m_flags; } - auto madt_entry::type() const noexcept -> types + auto madt::validate() const noexcept -> bool { - return static_cast(m_type); + return signature() == madt_table_signature && sdt::validate(); + } + + auto madt_entry::type() const noexcept -> enum type + { + return static_cast(m_type); } auto madt_entry::length() const noexcept -> std::size_t diff --git a/libs/acpi/acpi/madt.hpp b/libs/acpi/acpi/madt.hpp index f680430..8b75f58 100644 --- a/libs/acpi/acpi/madt.hpp +++ b/libs/acpi/acpi/madt.hpp @@ -4,6 +4,7 @@ // IWYU pragma: private, include #include +#include #include #include @@ -11,6 +12,7 @@ #include #include +#include #include namespace acpi @@ -18,7 +20,8 @@ namespace acpi struct [[gnu::packed]] madt_entry { - enum struct types : std::uint8_t + //! The type of an MADT entry. + enum struct type : std::uint8_t { processor_local_apic, io_apic, @@ -29,28 +32,55 @@ namespace acpi processor_local_x2_apic, }; - [[nodiscard]] auto type() const noexcept -> types; + //! Get the type of this entry. + [[nodiscard]] auto type() const noexcept -> enum type; + + //! Get the length of this entry. [[nodiscard]] auto length() const noexcept -> std::size_t; + //! Cast this entry to the given concrete entry type. + //! + //! @warning This function will terminate execution if the desired target type does not match up with this entry's + //! actual type. + template + [[nodiscard]] auto as() const -> EntryType const & + { + if (type() != EntryType::this_type) + { + kstd::os::panic("Invalid cast"); + } + return reinterpret_cast(*this); + } + private: std::uint8_t m_type; std::uint8_t m_length; }; - //! The common header for all + //! The Multiple APIC Description Table (MADT) struct [[gnu::packed]] madt : sdt { using iterator = sdt_iterator; using const_iterator = iterator; + //! Get the physical address of the local interrupt controllers described by this entry. [[nodiscard]] auto local_interrupt_controller_address() const noexcept -> std::uintptr_t; + [[nodiscard]] auto flags() const noexcept -> std::uint32_t; + [[nodiscard]] auto validate() const noexcept -> bool; [[nodiscard]] auto begin() const noexcept -> iterator; [[nodiscard]] auto cbegin() const noexcept -> iterator; [[nodiscard]] auto end() const noexcept -> iterator; [[nodiscard]] auto cend() const noexcept -> iterator; + template + [[nodiscard]] auto only() const noexcept + { + return *this | std::views::filter([](auto const & e) { return e.type() == EntryType::this_type; }) | + std::views::transform([](auto const & e) { return e.template as(); }); + } + private: std::uint32_t m_local_interrupt_controller_address; std::uint32_t m_flags; @@ -58,6 +88,8 @@ namespace acpi struct [[gnu::packed]] processor_local_apic : madt_entry { + constexpr auto static this_type = type::processor_local_apic; + enum struct flags : std::uint32_t { processor_enabled = 1, diff --git a/libs/acpi/acpi/madt.test.cpp b/libs/acpi/acpi/madt.test.cpp new file mode 100644 index 0000000..8926192 --- /dev/null +++ b/libs/acpi/acpi/madt.test.cpp @@ -0,0 +1,55 @@ +#include + +#include +#include +#include + +#include + +SCENARIO("MADT parsing", "[madt]") +{ + GIVEN("The basic compiled MADT containing a single LAPIC entry and the default x86 LAPIC address") + { + auto data = acpi::test_data::tables::basic_madt(); + + WHEN("parsing the table") + { + auto madt = reinterpret_cast(data.data()); + + THEN("the signature is correct") + { + REQUIRE(madt->signature() == "APIC"); + } + + THEN("validate returns true") + { + REQUIRE(madt->validate()); + } + + THEN("there is a single entry in the table") + { + REQUIRE(std::distance(madt->begin(), madt->end()) == 1); + } + + THEN("the LAPIC address is 0xfee00000") + { + REQUIRE(madt->local_interrupt_controller_address() == 0xfee0'0000); + } + + THEN("the length is sizeof(madt) + sizeof(processor_local_apic)") + { + REQUIRE(madt->length() == kstd::type_size + kstd::type_size); + } + + THEN("the first entry has type processor_local_apic") + { + REQUIRE(madt->cbegin()->type() == acpi::madt_entry::type::processor_local_apic); + } + + THEN("`only` can be used to get a view of all processor_local_apic entries") + { + REQUIRE(std::ranges::distance(madt->only()) == 1); + } + } + } +} diff --git a/libs/acpi/acpi/sdt.cpp b/libs/acpi/acpi/sdt.cpp index c2b9d68..6c6cb47 100644 --- a/libs/acpi/acpi/sdt.cpp +++ b/libs/acpi/acpi/sdt.cpp @@ -1,7 +1,9 @@ #include +#include #include +#include #include #include @@ -48,4 +50,9 @@ namespace acpi return {m_signature.data(), m_signature.size()}; } + auto sdt::validate() const noexcept -> bool + { + return acpi::validate_checksum({reinterpret_cast(this), length().value}); + } + } // namespace acpi diff --git a/libs/acpi/acpi/sdt.hpp b/libs/acpi/acpi/sdt.hpp index 9e5ec89..72b3896 100644 --- a/libs/acpi/acpi/sdt.hpp +++ b/libs/acpi/acpi/sdt.hpp @@ -52,6 +52,11 @@ namespace acpi return *m_entry; } + constexpr auto operator->() const noexcept -> pointer + { + return m_entry; + } + constexpr auto operator==(sdt_iterator const & other) const noexcept -> bool { return m_entry == other.m_entry || (is_end() && other.is_end()); @@ -94,6 +99,9 @@ namespace acpi //! Get the signature of this table. [[nodiscard]] auto signature() const noexcept -> std::string_view; + //! Valide this table's checksum + [[nodiscard]] auto validate() const noexcept -> bool; + private: std::array m_signature; std::uint32_t m_length; diff --git a/libs/acpi/test_data/basic_madt.asl b/libs/acpi/test_data/basic_madt.asl new file mode 100644 index 0000000..cd6958a --- /dev/null +++ b/libs/acpi/test_data/basic_madt.asl @@ -0,0 +1,29 @@ +/* + * Intel ACPI Component Architecture + * iASL Compiler/Disassembler version 20251212 (64-bit version) + * Copyright (c) 2000 - 2025 Intel Corporation + * + * Template for [APIC] ACPI Table (static data table) + * Format: [ByteLength] FieldName : HexFieldValue + */ +[0004] Signature : "APIC" [Multiple APIC Description Table (MADT)] +[0004] Table Length : 00000000 +[0001] Revision : 07 +[0001] Checksum : 00 +[0006] Oem ID : "INTEL " +[0008] Oem Table ID : "Template" +[0004] Oem Revision : 00000001 +[0004] Asl Compiler ID : "INTL" +[0004] Asl Compiler Revision : 20230628 + +[0004] Local Apic Address : FEE00000 +[0004] Flags (decoded below) : 00000001 + PC-AT Compatibility : 1 + +[0001] Subtable Type : 00 [Processor Local APIC] +[0001] Length : 08 +[0001] Processor ID : 00 +[0001] Local Apic ID : 00 +[0004] Flags (decoded below) : 00000001 + Processor Enabled : 1 + Runtime Online Capable : 0 diff --git a/libs/acpi/test_data/basic_rsdt.asl b/libs/acpi/test_data/basic_rsdt.asl new file mode 100644 index 0000000..6cf4c7a --- /dev/null +++ b/libs/acpi/test_data/basic_rsdt.asl @@ -0,0 +1,26 @@ +/* + * Intel ACPI Component Architecture + * iASL Compiler/Disassembler version 20251212 (64-bit version) + * Copyright (c) 2000 - 2025 Intel Corporation + * + * Template for [RSDT] ACPI Table (static data table) + * Format: [ByteLength] FieldName : HexFieldValue + */ +[0004] Signature : "RSDT" [Root System Description Table] +[0004] Table Length : 00000000 +[0001] Revision : 01 +[0001] Checksum : 00 +[0006] Oem ID : "INTEL " +[0008] Oem Table ID : "TEMPLATE" +[0004] Oem Revision : 00000001 +[0004] Asl Compiler ID : "INTL" +[0004] Asl Compiler Revision : 20100528 + +[0004] ACPI Table Address 0 : 00000010 +[0004] ACPI Table Address 1 : 00000020 +[0004] ACPI Table Address 2 : 00000030 +[0004] ACPI Table Address 3 : 00000040 +[0004] ACPI Table Address 4 : 00000050 +[0004] ACPI Table Address 5 : 00000060 +[0004] ACPI Table Address 6 : 00000070 +[0004] ACPI Table Address 7 : 00000080 diff --git a/libs/acpi/test_data/tables.S b/libs/acpi/test_data/tables.S new file mode 100644 index 0000000..af58109 --- /dev/null +++ b/libs/acpi/test_data/tables.S @@ -0,0 +1,14 @@ +.section .rodata + +.balign 16 + +#define TABLE(name, file) \ + .global name##_start; \ + .global name##_end; \ + name##_start: .incbin file; \ + name##_end: + +TABLE(basic_madt, "basic_madt.aml") +TABLE(basic_rsdt, "basic_rsdt.aml") + +#undef TABLE diff --git a/libs/acpi/test_data/tables.hpp b/libs/acpi/test_data/tables.hpp new file mode 100644 index 0000000..2b3874f --- /dev/null +++ b/libs/acpi/test_data/tables.hpp @@ -0,0 +1,25 @@ +#ifndef ACPI_TEST_DATA_TABLES_HPP +#define ACPI_TEST_DATA_TABLES_HPP + +#include +#include + +#define TABLE(name) \ + extern "C" std::byte const name##_start; \ + extern "C" std::byte const name##_end; \ + auto inline name()->std::span \ + { \ + return {&name##_start, &name##_end}; \ + } + +namespace acpi::test_data::tables +{ + + TABLE(basic_madt); + TABLE(basic_rsdt); + +} // namespace acpi::test_data::tables + +#undef TABLE + +#endif -- cgit v1.2.3 From 1113e812359a66591b0854a9f723ab8cd8b09274 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 15 Apr 2026 09:04:35 +0200 Subject: acpi: add rsdp test data --- libs/acpi/CMakeLists.txt | 1 + libs/acpi/test_data/basic_rsdp.asl | 16 ++++++++++++++++ libs/acpi/test_data/tables.S | 1 + libs/acpi/test_data/tables.hpp | 1 + 4 files changed, 19 insertions(+) create mode 100644 libs/acpi/test_data/basic_rsdp.asl diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index 8ace42d..30e1aca 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -42,6 +42,7 @@ if(NOT CMAKE_CROSSCOMPILING) set(TEST_TABLES "basic_madt" "basic_rsdt" + "basic_rsdp" ) foreach(TABLE IN LISTS TEST_TABLES) diff --git a/libs/acpi/test_data/basic_rsdp.asl b/libs/acpi/test_data/basic_rsdp.asl new file mode 100644 index 0000000..8274c0f --- /dev/null +++ b/libs/acpi/test_data/basic_rsdp.asl @@ -0,0 +1,16 @@ +/* + * Intel ACPI Component Architecture + * iASL Compiler/Disassembler version 20251212 (64-bit version) + * Copyright (c) 2000 - 2025 Intel Corporation + * + * Template for [RSDP] ACPI Table (AML byte code table) + */ +[0008] Signature : "RSD PTR " +[0001] Checksum : 43 +[0006] Oem ID : "INTEL " +[0001] Revision : 02 +[0004] RSDT Address : 00000000 +[0004] Length : 00000024 +[0008] XSDT Address : 0000000000000000 +[0001] Extended Checksum : DC +[0003] Reserved : 000000 diff --git a/libs/acpi/test_data/tables.S b/libs/acpi/test_data/tables.S index af58109..f40f070 100644 --- a/libs/acpi/test_data/tables.S +++ b/libs/acpi/test_data/tables.S @@ -10,5 +10,6 @@ TABLE(basic_madt, "basic_madt.aml") TABLE(basic_rsdt, "basic_rsdt.aml") +TABLE(basic_rsdp, "basic_rsdp.aml") #undef TABLE diff --git a/libs/acpi/test_data/tables.hpp b/libs/acpi/test_data/tables.hpp index 2b3874f..510cbda 100644 --- a/libs/acpi/test_data/tables.hpp +++ b/libs/acpi/test_data/tables.hpp @@ -17,6 +17,7 @@ namespace acpi::test_data::tables TABLE(basic_madt); TABLE(basic_rsdt); + TABLE(basic_rsdp); } // namespace acpi::test_data::tables -- cgit v1.2.3 From 6344a2a81b94a00aaaa987d0e0d40993ed581d5e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 15 Apr 2026 16:19:00 +0200 Subject: acpi: reimplement the common table header --- .vscode/settings.json | 1 + libs/acpi/CMakeLists.txt | 3 + libs/acpi/acpi/common/table_header.cpp | 130 ++++++++++++++++++++++++++++ libs/acpi/acpi/common/table_header.hpp | 57 ++++++++++++ libs/acpi/acpi/common/table_header.test.cpp | 58 +++++++++++++ libs/acpi/test_data/table_header.asl | 9 ++ libs/acpi/test_data/tables.S | 1 + libs/acpi/test_data/tables.hpp | 1 + 8 files changed, 260 insertions(+) create mode 100644 libs/acpi/acpi/common/table_header.cpp create mode 100644 libs/acpi/acpi/common/table_header.hpp create mode 100644 libs/acpi/acpi/common/table_header.test.cpp create mode 100644 libs/acpi/test_data/table_header.asl diff --git a/.vscode/settings.json b/.vscode/settings.json index bebda51..1a69637 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -32,6 +32,7 @@ "crtc", "crtp", "efer", + "FACS", "functors", "hhdm", "idtr", diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index 30e1aca..b9c607d 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -20,6 +20,7 @@ file(GLOB_RECURSE ACPI_HEADERS target_sources("acpi" PRIVATE "acpi/checksum.cpp" + "acpi/common/table_header.cpp" "acpi/madt.cpp" "acpi/pointers.cpp" "acpi/sdt.cpp" @@ -43,6 +44,7 @@ if(NOT CMAKE_CROSSCOMPILING) "basic_madt" "basic_rsdt" "basic_rsdp" + "table_header" ) foreach(TABLE IN LISTS TEST_TABLES) @@ -58,6 +60,7 @@ if(NOT CMAKE_CROSSCOMPILING) set_source_files_properties("test_data/tables.S" PROPERTIES OBJECT_DEPENDS "${GENERATED_TABLE_BLOBS}") add_executable("acpi_tests" + "acpi/common/table_header.test.cpp" "acpi/madt.test.cpp" "acpi/pointers.test.cpp" diff --git a/libs/acpi/acpi/common/table_header.cpp b/libs/acpi/acpi/common/table_header.cpp new file mode 100644 index 0000000..17a8219 --- /dev/null +++ b/libs/acpi/acpi/common/table_header.cpp @@ -0,0 +1,130 @@ +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace acpi +{ + + struct common_table_header_data + { + std::array signature; + std::uint32_t length; + std::uint8_t revision; + std::uint8_t checksum; + std::array oem_id; + std::array oem_table_id; + std::uint32_t oem_revision; + std::array creator_id; + std::uint32_t creator_revision; + }; + + static_assert(sizeof(common_table_header_data) == common_table_header_size); + + auto common_table_header::creator_revision() const noexcept -> decltype(common_table_header_data::creator_revision) + { + using type = decltype(common_table_header_data::creator_revision); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(common_table_header_data, creator_revision); + + auto const data = std::span{m_data.data() + offset, size}; + auto value = type{}; + + kstd::libc::memcpy(&value, data.data(), size); + + return value; + } + + auto common_table_header::creator_id() const noexcept -> std::string_view + { + constexpr auto size = sizeof(common_table_header_data::creator_id); + constexpr auto offset = offsetof(common_table_header_data, creator_id); + + auto const base = reinterpret_cast(m_data.data()); + + return std::string_view{base + offset, size}; + } + + auto common_table_header::length() const noexcept -> kstd::units::bytes + { + using type = decltype(common_table_header_data::length); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(common_table_header_data, length); + + auto const data = std::span{m_data.data() + offset, size}; + auto raw_value = type{}; + + kstd::libc::memcpy(&raw_value, data.data(), size); + + return kstd::units::bytes{raw_value}; + } + + auto common_table_header::oem_id() const noexcept -> std::string_view + { + constexpr auto size = sizeof(common_table_header_data::oem_id); + constexpr auto offset = offsetof(common_table_header_data, oem_id); + + auto const base = reinterpret_cast(m_data.data()); + + return std::string_view{base + offset, size}; + } + + auto common_table_header::oem_table_id() const noexcept -> std::string_view + { + constexpr auto size = sizeof(common_table_header_data::oem_table_id); + constexpr auto offset = offsetof(common_table_header_data, oem_table_id); + + auto const base = reinterpret_cast(m_data.data()); + + return std::string_view{base + offset, size}; + } + + auto common_table_header::oem_revision() const noexcept -> decltype(common_table_header_data::oem_revision) + { + using type = decltype(common_table_header_data::oem_revision); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(common_table_header_data, oem_revision); + + auto const data = std::span{m_data.data() + offset, size}; + auto value = type{}; + + kstd::libc::memcpy(&value, data.data(), size); + + return value; + } + + auto common_table_header::revision() const noexcept -> decltype(common_table_header_data::revision) + { + using type = decltype(common_table_header_data::revision); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(common_table_header_data, revision); + + auto const data = std::span{m_data.data() + offset, size}; + auto value = type{}; + + kstd::libc::memcpy(&value, data.data(), size); + + return value; + } + + auto common_table_header::signature() const noexcept -> std::string_view + { + constexpr auto size = sizeof(common_table_header_data::signature); + constexpr auto offset = offsetof(common_table_header_data, signature); + + auto const base = reinterpret_cast(m_data.data()); + + return std::string_view{base + offset, size}; + } + +} // namespace acpi \ No newline at end of file diff --git a/libs/acpi/acpi/common/table_header.hpp b/libs/acpi/acpi/common/table_header.hpp new file mode 100644 index 0000000..8ecfd0a --- /dev/null +++ b/libs/acpi/acpi/common/table_header.hpp @@ -0,0 +1,57 @@ +#ifndef ACPI_COMMON_TABLE_HEADER_HPP +#define ACPI_COMMON_TABLE_HEADER_HPP + +#include + +#include +#include +#include +#include + +namespace acpi +{ + + //! The size of the common table header as defined in the ACPI specification. + constexpr auto common_table_header_size = 36; + + //! The common header for all ACPI tables, except the FACS. + //! + //! Multibyte fields in the header are not guaranteed to be naturally aligned by the firmware. Therefore, no direct + //! access to multibyte fields is provided. Instead, the provided member functions handle alignment of the multibyte + //! values and thus provide a safe interface to the header fields. + struct common_table_header + { + //! Get the revision of the utility used to create this table. + [[nodiscard]] auto creator_revision() const noexcept -> std::uint32_t; + + //! Get the vendor ID of the utility used to create this table. + [[nodiscard]] auto creator_id() const noexcept -> std::string_view; + + //! Get the length of the entire table, including this header. + [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; + + //! Get the ID of the OEM. + [[nodiscard]] auto oem_id() const noexcept -> std::string_view; + + //! Get the OEMs revision number of this table. + [[nodiscard]] auto oem_revision() const noexcept -> std::uint32_t; + + //! Get the OEMs ID of this table. + [[nodiscard]] auto oem_table_id() const noexcept -> std::string_view; + + //! Get the revision number of the structure of this table. + [[nodiscard]] auto revision() const noexcept -> std::uint8_t; + + //! Get the signature of this table. + [[nodiscard]] auto signature() const noexcept -> std::string_view; + + //! Validate this table's checksum + [[nodiscard]] auto validate() const noexcept -> bool; + + private: + std::array m_data; + }; + +} // namespace acpi + +#endif \ No newline at end of file diff --git a/libs/acpi/acpi/common/table_header.test.cpp b/libs/acpi/acpi/common/table_header.test.cpp new file mode 100644 index 0000000..e43e403 --- /dev/null +++ b/libs/acpi/acpi/common/table_header.test.cpp @@ -0,0 +1,58 @@ +#include + +#include +#include +#include + +SCENARIO("Common table header parsing", "[common_table_header]") +{ + GIVEN("A valid compiled table header") + { + auto data = acpi::test_data::tables::table_header(); + + WHEN("parsing the header") + { + auto header = reinterpret_cast(data.data()); + + THEN("the signature is correct") + { + REQUIRE(header->signature() == "TEST"); + } + + THEN("the revision is correct") + { + REQUIRE(header->revision() == 1); + } + + THEN("the length is correct") + { + REQUIRE(header->length() == kstd::type_size); + } + + THEN("the oem id is correct") + { + REQUIRE(header->oem_id() == "FEMO "); + } + + THEN("the oem table id is correct") + { + REQUIRE(header->oem_table_id() == "HDRTEST "); + } + + THEN("the oem revision is correct") + { + REQUIRE(header->oem_revision() == 1); + } + + THEN("the creator id is correct") + { + REQUIRE(header->creator_id() == "INTL"); + } + + THEN("the creator revision is non-zero") + { + REQUIRE(header->creator_revision() != 0); + } + } + } +} diff --git a/libs/acpi/test_data/table_header.asl b/libs/acpi/test_data/table_header.asl new file mode 100644 index 0000000..8cddc03 --- /dev/null +++ b/libs/acpi/test_data/table_header.asl @@ -0,0 +1,9 @@ +[0004] Signature : "TEST" +[0004] Table Length : 00000000 +[0001] Revision : 01 +[0001] Checksum : 00 +[0006] Oem ID : "FEMO " +[0008] Oem Table ID : "HDRTEST " +[0004] Oem Revision : 00000001 +[0004] Asl Compiler ID : "INTL" +[0004] Asl Compiler Revision : 00000000 diff --git a/libs/acpi/test_data/tables.S b/libs/acpi/test_data/tables.S index f40f070..da9a12f 100644 --- a/libs/acpi/test_data/tables.S +++ b/libs/acpi/test_data/tables.S @@ -11,5 +11,6 @@ TABLE(basic_madt, "basic_madt.aml") TABLE(basic_rsdt, "basic_rsdt.aml") TABLE(basic_rsdp, "basic_rsdp.aml") +TABLE(table_header, "table_header.aml") #undef TABLE diff --git a/libs/acpi/test_data/tables.hpp b/libs/acpi/test_data/tables.hpp index 510cbda..dc39b37 100644 --- a/libs/acpi/test_data/tables.hpp +++ b/libs/acpi/test_data/tables.hpp @@ -18,6 +18,7 @@ namespace acpi::test_data::tables TABLE(basic_madt); TABLE(basic_rsdt); TABLE(basic_rsdp); + TABLE(table_header); } // namespace acpi::test_data::tables -- cgit v1.2.3 From 69068165f49ca204025410ab02546318997edaa3 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 15 Apr 2026 16:53:36 +0200 Subject: kernel: improve bht speed using memory mapping --- .../filesystem/storage_boot_module_fixture.hpp | 32 +++++-- .../filesystem/storage_boot_module_vfs_fixture.hpp | 9 +- .../filesystem/storage_boot_module_fixture.cpp | 105 +++++++++++++-------- .../filesystem/storage_boot_module_vfs_fixture.cpp | 7 +- 4 files changed, 98 insertions(+), 55 deletions(-) diff --git a/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp b/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp index ee658e2..4b49684 100644 --- a/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp +++ b/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp @@ -3,11 +3,10 @@ #include "kapi/boot_module/boot_module_registry.hpp" -#include -#include - #include #include +#include +#include namespace kernel::tests::filesystem { @@ -16,16 +15,33 @@ namespace kernel::tests::filesystem ~storage_boot_module_fixture(); auto setup_modules(std::size_t module_count, std::size_t module_size = 4096) -> void; - auto setup_modules_from_img(kstd::vector const & module_names, - kstd::vector const & img_paths) -> void; + auto setup_modules_from_img(std::vector const & module_names, + std::vector const & img_paths) -> void; protected: + struct mapped_image + { + explicit mapped_image(std::filesystem::path path); + ~mapped_image(); + + mapped_image(mapped_image const &) = delete; + auto operator=(mapped_image const &) -> mapped_image & = delete; + + mapped_image(mapped_image &&) noexcept; + auto operator=(mapped_image &&) noexcept -> mapped_image &; + + int file_descriptor; + std::byte * mapping; + std::size_t size; + }; + kapi::boot_modules::boot_module_registry m_registry{}; - kstd::vector m_module_names{}; - kstd::vector> m_module_data{}; + std::vector m_module_names{}; + std::vector> m_module_data{}; + std::vector m_mapped_images{}; private: - auto setup_module_from_img(kstd::string const & module_name, std::filesystem::path const & img_path) -> void; + auto setup_module_from_img(std::string const & module_name, std::filesystem::path const & img_path) -> void; }; } // namespace kernel::tests::filesystem diff --git a/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp b/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp index 98012b0..bd1c289 100644 --- a/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp +++ b/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp @@ -3,11 +3,10 @@ #include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" -#include -#include - #include #include +#include +#include namespace kernel::tests::filesystem { @@ -16,8 +15,8 @@ namespace kernel::tests::filesystem ~storage_boot_module_vfs_fixture(); auto setup_modules_and_init_vfs(std::size_t module_count, std::size_t module_size = 4096) -> void; - auto setup_modules_from_img_and_init_vfs(kstd::vector const & module_names, - kstd::vector const & img_paths) -> void; + auto setup_modules_from_img_and_init_vfs(std::vector const & module_names, + std::vector const & img_paths) -> void; }; } // namespace kernel::tests::filesystem diff --git a/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp b/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp index a139f63..cd8360b 100644 --- a/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp +++ b/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp @@ -8,17 +8,72 @@ #include "kernel/test_support/boot_modules.hpp" #include "kernel/test_support/devices/storage/management.hpp" -#include -#include - #include +#include #include -#include -#include +#include #include +#include +#include +#include +#include +#include +#include namespace kernel::tests::filesystem { + storage_boot_module_fixture::mapped_image::mapped_image(std::filesystem::path path) + : file_descriptor(::open(path.c_str(), O_RDWR)) + { + if (file_descriptor < 0) + { + throw std::runtime_error{"Failed to open image file for test boot module: " + path.string()}; + } + + struct stat statistics{}; + if (::fstat(file_descriptor, &statistics) < 0) + { + throw std::runtime_error{"Failed to get statistics for image file: " + path.string()}; + } + + size = static_cast(statistics.st_size); + + mapping = static_cast(::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, file_descriptor, 0)); + if (mapping == MAP_FAILED) + { + throw std::runtime_error{"Failed to map image file for test boot module: " + path.string()}; + } + } + + storage_boot_module_fixture::mapped_image::~mapped_image() + { + if (mapping != nullptr) + { + ::munmap(mapping, size); + } + if (file_descriptor >= 0) + { + ::close(file_descriptor); + } + } + + storage_boot_module_fixture::mapped_image::mapped_image(mapped_image && other) noexcept + : file_descriptor{std::exchange(other.file_descriptor, -1)} + , mapping{std::exchange(other.mapping, nullptr)} + , size{std::exchange(other.size, 0)} + {} + + auto storage_boot_module_fixture::mapped_image::operator=(mapped_image && other) noexcept -> mapped_image & + { + if (this != &other) + { + file_descriptor = std::exchange(other.file_descriptor, -1); + mapping = std::exchange(other.mapping, nullptr); + size = std::exchange(other.size, 0); + } + return *this; + } + storage_boot_module_fixture::~storage_boot_module_fixture() { kernel::tests::devices::storage::management::deinit(); @@ -36,23 +91,22 @@ namespace kernel::tests::filesystem for (std::size_t i = 0; i < module_count; ++i) { - m_module_names.push_back("test_mod" + kstd::to_string(i)); + m_module_names.push_back(std::format("test_mod{}", i)); m_module_data.emplace_back(module_size, std::byte{static_cast(0x40 + (i % 16))}); } for (std::size_t i = 0; i < module_count; ++i) { m_registry.add_boot_module(kapi::boot_modules::boot_module{ - m_module_names[i].view(), kapi::memory::linear_address{m_module_data[i].data()}, m_module_data[i].size()}); + m_module_names[i].c_str(), kapi::memory::linear_address{m_module_data[i].data()}, m_module_data[i].size()}); } kapi::boot_modules::set_boot_module_registry(m_registry); kernel::devices::storage::management::init(); } - auto storage_boot_module_fixture::setup_modules_from_img(kstd::vector const & module_names, - kstd::vector const & img_paths) - -> void + auto storage_boot_module_fixture::setup_modules_from_img(std::vector const & module_names, + std::vector const & img_paths) -> void { m_module_names.clear(); m_module_data.clear(); @@ -72,38 +126,13 @@ namespace kernel::tests::filesystem kernel::devices::storage::management::init(); } - auto storage_boot_module_fixture::setup_module_from_img(kstd::string const & module_name, + auto storage_boot_module_fixture::setup_module_from_img(std::string const & module_name, std::filesystem::path const & img_path) -> void { - auto file = std::ifstream{img_path, std::ios::binary | std::ios::ate}; - if (!file) - { - throw std::runtime_error{"Failed to open image file for test boot module: " + img_path.string()}; - } - - auto const end_pos = file.tellg(); - if (end_pos < 0) - { - throw std::runtime_error{"Failed to determine image file size for test boot module: " + img_path.string()}; - } - - auto const size = static_cast(end_pos); - file.seekg(0, std::ios::beg); - m_module_names.push_back(module_name); - m_module_data.emplace_back(size); - - if (size > 0) - { - file.read(reinterpret_cast(m_module_data.back().data()), static_cast(size)); - if (!file) - { - throw std::runtime_error{"Failed to read image file content for test boot module: " + img_path.string()}; - } - } + auto & mapped_image = m_mapped_images.emplace_back(img_path); m_registry.add_boot_module(kapi::boot_modules::boot_module{ - m_module_names.back().view(), kapi::memory::linear_address{m_module_data.back().data()}, - m_module_data.back().size()}); + m_module_names.back().c_str(), kapi::memory::linear_address{mapped_image.mapping}, mapped_image.size}); } } // namespace kernel::tests::filesystem \ No newline at end of file diff --git a/kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp b/kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp index cba7278..1a71b57 100644 --- a/kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp +++ b/kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp @@ -3,11 +3,10 @@ #include "kernel/filesystem/vfs.hpp" #include "kernel/test_support/filesystem/vfs.hpp" -#include -#include - #include #include +#include +#include namespace kernel::tests::filesystem { @@ -24,7 +23,7 @@ namespace kernel::tests::filesystem } auto storage_boot_module_vfs_fixture::setup_modules_from_img_and_init_vfs( - kstd::vector const & module_names, kstd::vector const & img_paths) -> void + std::vector const & module_names, std::vector const & img_paths) -> void { setup_modules_from_img(module_names, img_paths); kernel::filesystem::vfs::init(); -- cgit v1.2.3 From 27c654f3f0a069113b6abb70817cfe2c5096711e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 15 Apr 2026 20:46:29 +0200 Subject: acpi: add basic table type --- libs/acpi/CMakeLists.txt | 4 +++- libs/acpi/acpi/acpi.hpp | 10 +++++----- libs/acpi/acpi/checksum.cpp | 19 ------------------- libs/acpi/acpi/checksum.hpp | 20 -------------------- libs/acpi/acpi/common/basic_table.cpp | 26 ++++++++++++++++++++++++++ libs/acpi/acpi/common/basic_table.hpp | 26 ++++++++++++++++++++++++++ libs/acpi/acpi/common/basic_table.test.cpp | 21 +++++++++++++++++++++ libs/acpi/acpi/common/checksum.cpp | 19 +++++++++++++++++++ libs/acpi/acpi/common/checksum.hpp | 20 ++++++++++++++++++++ libs/acpi/acpi/common/table_header.cpp | 16 ++++++++-------- libs/acpi/acpi/common/table_header.hpp | 5 +---- libs/acpi/acpi/common/table_header.test.cpp | 4 ++-- libs/acpi/acpi/pointers.cpp | 2 +- 13 files changed, 132 insertions(+), 60 deletions(-) delete mode 100644 libs/acpi/acpi/checksum.cpp delete mode 100644 libs/acpi/acpi/checksum.hpp create mode 100644 libs/acpi/acpi/common/basic_table.cpp create mode 100644 libs/acpi/acpi/common/basic_table.hpp create mode 100644 libs/acpi/acpi/common/basic_table.test.cpp create mode 100644 libs/acpi/acpi/common/checksum.cpp create mode 100644 libs/acpi/acpi/common/checksum.hpp diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index b9c607d..f850fe4 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -19,7 +19,8 @@ file(GLOB_RECURSE ACPI_HEADERS ) target_sources("acpi" PRIVATE - "acpi/checksum.cpp" + "acpi/common/basic_table.cpp" + "acpi/common/checksum.cpp" "acpi/common/table_header.cpp" "acpi/madt.cpp" "acpi/pointers.cpp" @@ -60,6 +61,7 @@ if(NOT CMAKE_CROSSCOMPILING) set_source_files_properties("test_data/tables.S" PROPERTIES OBJECT_DEPENDS "${GENERATED_TABLE_BLOBS}") add_executable("acpi_tests" + "acpi/common/basic_table.test.cpp" "acpi/common/table_header.test.cpp" "acpi/madt.test.cpp" "acpi/pointers.test.cpp" diff --git a/libs/acpi/acpi/acpi.hpp b/libs/acpi/acpi/acpi.hpp index fb358cc..47050f2 100644 --- a/libs/acpi/acpi/acpi.hpp +++ b/libs/acpi/acpi/acpi.hpp @@ -1,10 +1,10 @@ #ifndef ACPI_ACPI_HPP #define ACPI_ACPI_HPP -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #endif diff --git a/libs/acpi/acpi/checksum.cpp b/libs/acpi/acpi/checksum.cpp deleted file mode 100644 index 56275c8..0000000 --- a/libs/acpi/acpi/checksum.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include - -#include -#include -#include -#include - -namespace acpi -{ - - auto validate_checksum(std::span data) -> bool - { - auto sum = std::ranges::fold_left(data, std::uint8_t{}, [](auto acc, auto byte) { - return static_cast(acc + static_cast(byte)); - }); - return sum == 0; - } - -} // namespace acpi diff --git a/libs/acpi/acpi/checksum.hpp b/libs/acpi/acpi/checksum.hpp deleted file mode 100644 index a92c242..0000000 --- a/libs/acpi/acpi/checksum.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef ACPI_CHECKSUM_HPP -#define ACPI_CHECKSUM_HPP - -// IWYU pragma: private, include - -#include -#include - -namespace acpi -{ - - //! Validate and ACPI entity checksum. - //! - //! @param data The data to validate the checksum of. - //! @return true iff. the checksum is valid, false otherwise. - auto validate_checksum(std::span data) -> bool; - -} // namespace acpi - -#endif diff --git a/libs/acpi/acpi/common/basic_table.cpp b/libs/acpi/acpi/common/basic_table.cpp new file mode 100644 index 0000000..5cec91a --- /dev/null +++ b/libs/acpi/acpi/common/basic_table.cpp @@ -0,0 +1,26 @@ +#include +#include +#include + +#include +#include + +namespace acpi +{ + + auto basic_table::validate_checksum() const noexcept -> bool + { + return acpi::validate_checksum({reinterpret_cast(this), m_header.length().value}); + } + + auto basic_table::as_span() const noexcept -> std::span + { + return {reinterpret_cast(this), m_header.length().value}; + } + + auto basic_table::header() const noexcept -> table_header const & + { + return m_header; + } + +} // namespace acpi \ No newline at end of file diff --git a/libs/acpi/acpi/common/basic_table.hpp b/libs/acpi/acpi/common/basic_table.hpp new file mode 100644 index 0000000..43bf533 --- /dev/null +++ b/libs/acpi/acpi/common/basic_table.hpp @@ -0,0 +1,26 @@ +#ifndef ACPI_COMMON_BASIC_TABLE_HPP +#define ACPI_COMMON_BASIC_TABLE_HPP + +#include + +#include +#include + +namespace acpi +{ + + struct basic_table + { + [[nodiscard]] auto validate_checksum() const noexcept -> bool; + + [[nodiscard]] auto as_span() const noexcept -> std::span; + + [[nodiscard]] auto header() const noexcept -> table_header const &; + + private: + table_header m_header; + }; + +} // namespace acpi + +#endif \ No newline at end of file diff --git a/libs/acpi/acpi/common/basic_table.test.cpp b/libs/acpi/acpi/common/basic_table.test.cpp new file mode 100644 index 0000000..e292b9f --- /dev/null +++ b/libs/acpi/acpi/common/basic_table.test.cpp @@ -0,0 +1,21 @@ +#include +#include +#include + +SCENARIO("Basic table functions", "[basic_table]") +{ + GIVEN("A valid compiled table header") + { + auto data = acpi::test_data::tables::table_header(); + + WHEN("parsing the table") + { + auto table = reinterpret_cast(data.data()); + + THEN("the checksum is valid") + { + REQUIRE(table->validate_checksum()); + } + } + } +} diff --git a/libs/acpi/acpi/common/checksum.cpp b/libs/acpi/acpi/common/checksum.cpp new file mode 100644 index 0000000..09425dd --- /dev/null +++ b/libs/acpi/acpi/common/checksum.cpp @@ -0,0 +1,19 @@ +#include + +#include +#include +#include +#include + +namespace acpi +{ + + auto validate_checksum(std::span data) -> bool + { + auto sum = std::ranges::fold_left(data, std::uint8_t{}, [](auto acc, auto byte) { + return static_cast(acc + static_cast(byte)); + }); + return sum == 0; + } + +} // namespace acpi diff --git a/libs/acpi/acpi/common/checksum.hpp b/libs/acpi/acpi/common/checksum.hpp new file mode 100644 index 0000000..a92c242 --- /dev/null +++ b/libs/acpi/acpi/common/checksum.hpp @@ -0,0 +1,20 @@ +#ifndef ACPI_CHECKSUM_HPP +#define ACPI_CHECKSUM_HPP + +// IWYU pragma: private, include + +#include +#include + +namespace acpi +{ + + //! Validate and ACPI entity checksum. + //! + //! @param data The data to validate the checksum of. + //! @return true iff. the checksum is valid, false otherwise. + auto validate_checksum(std::span data) -> bool; + +} // namespace acpi + +#endif diff --git a/libs/acpi/acpi/common/table_header.cpp b/libs/acpi/acpi/common/table_header.cpp index 17a8219..c69ff5d 100644 --- a/libs/acpi/acpi/common/table_header.cpp +++ b/libs/acpi/acpi/common/table_header.cpp @@ -27,7 +27,7 @@ namespace acpi static_assert(sizeof(common_table_header_data) == common_table_header_size); - auto common_table_header::creator_revision() const noexcept -> decltype(common_table_header_data::creator_revision) + auto table_header::creator_revision() const noexcept -> decltype(common_table_header_data::creator_revision) { using type = decltype(common_table_header_data::creator_revision); @@ -42,7 +42,7 @@ namespace acpi return value; } - auto common_table_header::creator_id() const noexcept -> std::string_view + auto table_header::creator_id() const noexcept -> std::string_view { constexpr auto size = sizeof(common_table_header_data::creator_id); constexpr auto offset = offsetof(common_table_header_data, creator_id); @@ -52,7 +52,7 @@ namespace acpi return std::string_view{base + offset, size}; } - auto common_table_header::length() const noexcept -> kstd::units::bytes + auto table_header::length() const noexcept -> kstd::units::bytes { using type = decltype(common_table_header_data::length); @@ -67,7 +67,7 @@ namespace acpi return kstd::units::bytes{raw_value}; } - auto common_table_header::oem_id() const noexcept -> std::string_view + auto table_header::oem_id() const noexcept -> std::string_view { constexpr auto size = sizeof(common_table_header_data::oem_id); constexpr auto offset = offsetof(common_table_header_data, oem_id); @@ -77,7 +77,7 @@ namespace acpi return std::string_view{base + offset, size}; } - auto common_table_header::oem_table_id() const noexcept -> std::string_view + auto table_header::oem_table_id() const noexcept -> std::string_view { constexpr auto size = sizeof(common_table_header_data::oem_table_id); constexpr auto offset = offsetof(common_table_header_data, oem_table_id); @@ -87,7 +87,7 @@ namespace acpi return std::string_view{base + offset, size}; } - auto common_table_header::oem_revision() const noexcept -> decltype(common_table_header_data::oem_revision) + auto table_header::oem_revision() const noexcept -> decltype(common_table_header_data::oem_revision) { using type = decltype(common_table_header_data::oem_revision); @@ -102,7 +102,7 @@ namespace acpi return value; } - auto common_table_header::revision() const noexcept -> decltype(common_table_header_data::revision) + auto table_header::revision() const noexcept -> decltype(common_table_header_data::revision) { using type = decltype(common_table_header_data::revision); @@ -117,7 +117,7 @@ namespace acpi return value; } - auto common_table_header::signature() const noexcept -> std::string_view + auto table_header::signature() const noexcept -> std::string_view { constexpr auto size = sizeof(common_table_header_data::signature); constexpr auto offset = offsetof(common_table_header_data, signature); diff --git a/libs/acpi/acpi/common/table_header.hpp b/libs/acpi/acpi/common/table_header.hpp index 8ecfd0a..529da81 100644 --- a/libs/acpi/acpi/common/table_header.hpp +++ b/libs/acpi/acpi/common/table_header.hpp @@ -19,7 +19,7 @@ namespace acpi //! Multibyte fields in the header are not guaranteed to be naturally aligned by the firmware. Therefore, no direct //! access to multibyte fields is provided. Instead, the provided member functions handle alignment of the multibyte //! values and thus provide a safe interface to the header fields. - struct common_table_header + struct table_header { //! Get the revision of the utility used to create this table. [[nodiscard]] auto creator_revision() const noexcept -> std::uint32_t; @@ -45,9 +45,6 @@ namespace acpi //! Get the signature of this table. [[nodiscard]] auto signature() const noexcept -> std::string_view; - //! Validate this table's checksum - [[nodiscard]] auto validate() const noexcept -> bool; - private: std::array m_data; }; diff --git a/libs/acpi/acpi/common/table_header.test.cpp b/libs/acpi/acpi/common/table_header.test.cpp index e43e403..d6976b3 100644 --- a/libs/acpi/acpi/common/table_header.test.cpp +++ b/libs/acpi/acpi/common/table_header.test.cpp @@ -12,7 +12,7 @@ SCENARIO("Common table header parsing", "[common_table_header]") WHEN("parsing the header") { - auto header = reinterpret_cast(data.data()); + auto header = reinterpret_cast(data.data()); THEN("the signature is correct") { @@ -26,7 +26,7 @@ SCENARIO("Common table header parsing", "[common_table_header]") THEN("the length is correct") { - REQUIRE(header->length() == kstd::type_size); + REQUIRE(header->length() == kstd::type_size); } THEN("the oem id is correct") diff --git a/libs/acpi/acpi/pointers.cpp b/libs/acpi/acpi/pointers.cpp index 8a8629f..45a42ce 100644 --- a/libs/acpi/acpi/pointers.cpp +++ b/libs/acpi/acpi/pointers.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include #include -- cgit v1.2.3 From 252df23b061b2bf54206da73e41faca600541ccc Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 16 Apr 2026 09:26:52 +0200 Subject: acpi: introduce VLA table --- kapi/include/kapi/acpi.hpp | 2 +- kernel/include/kernel/acpi/manager.hpp | 6 +- kernel/kapi/acpi.cpp | 2 +- kernel/src/acpi/manager.cpp | 12 +-- libs/acpi/CMakeLists.txt | 3 - libs/acpi/acpi/acpi.hpp | 10 +-- libs/acpi/acpi/common/basic_table.cpp | 26 ------ libs/acpi/acpi/common/basic_table.hpp | 38 +++++++-- libs/acpi/acpi/common/basic_table.test.cpp | 21 ----- libs/acpi/acpi/common/table_header.hpp | 2 + libs/acpi/acpi/common/table_signature.hpp | 17 ++++ libs/acpi/acpi/common/table_type.hpp | 17 ++++ libs/acpi/acpi/common/vla_table.hpp | 115 ++++++++++++++++++++++++++ libs/acpi/acpi/madt.cpp | 104 +++++++++++++++-------- libs/acpi/acpi/madt.hpp | 57 ++++++------- libs/acpi/acpi/madt.test.cpp | 4 +- libs/acpi/acpi/sdt.cpp | 58 ------------- libs/acpi/acpi/sdt.hpp | 127 ----------------------------- libs/acpi/acpi/table_type.hpp | 17 ---- 19 files changed, 298 insertions(+), 340 deletions(-) delete mode 100644 libs/acpi/acpi/common/basic_table.cpp delete mode 100644 libs/acpi/acpi/common/basic_table.test.cpp create mode 100644 libs/acpi/acpi/common/table_signature.hpp create mode 100644 libs/acpi/acpi/common/table_type.hpp create mode 100644 libs/acpi/acpi/common/vla_table.hpp delete mode 100644 libs/acpi/acpi/sdt.cpp delete mode 100644 libs/acpi/acpi/sdt.hpp delete mode 100644 libs/acpi/acpi/table_type.hpp diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index 2835496..b607ee0 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -23,7 +23,7 @@ namespace kapi::acpi //! //! @param signature The signature of the table to get. //! @return A pointer to the table if found, nullptr otherwise. - auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::sdt const>; + auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::table_header const>; //! Get a type-cast pointer to an ACPI table by its signature. //! diff --git a/kernel/include/kernel/acpi/manager.hpp b/kernel/include/kernel/acpi/manager.hpp index 420b44a..860d609 100644 --- a/kernel/include/kernel/acpi/manager.hpp +++ b/kernel/include/kernel/acpi/manager.hpp @@ -18,12 +18,12 @@ namespace kernel::acpi auto load_tables() -> bool; - auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::sdt const>; + auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::table_header const>; private: ::acpi::rsdp const * m_sdp{}; - ::acpi::sdt const * m_rsdt{}; - kstd::flat_map m_tables{}; + ::acpi::table_header const * m_rsdt{}; + kstd::flat_map m_tables{}; bool m_extended{}; }; diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp index df2bf05..5a2f227 100644 --- a/kernel/kapi/acpi.cpp +++ b/kernel/kapi/acpi.cpp @@ -32,7 +32,7 @@ namespace kapi::acpi return manager->load_tables(); } - auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::sdt const> + auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::table_header const> { return manager->get_table(signature); } diff --git a/kernel/src/acpi/manager.cpp b/kernel/src/acpi/manager.cpp index 501ce92..41469c2 100644 --- a/kernel/src/acpi/manager.cpp +++ b/kernel/src/acpi/manager.cpp @@ -32,7 +32,7 @@ namespace kernel::acpi } auto physical_extended_table_address = kapi::memory::physical_address{xsdp->table_address()}; auto linear_extended_table_address = kapi::memory::hhdm_to_linear(physical_extended_table_address); - m_rsdt = static_cast<::acpi::sdt const *>(linear_extended_table_address); + m_rsdt = static_cast<::acpi::table_header const *>(linear_extended_table_address); m_extended = true; } else @@ -43,7 +43,7 @@ namespace kernel::acpi } auto physical_root_table_address = kapi::memory::physical_address{m_sdp->table_address()}; auto linear_root_table_address = kapi::memory::hhdm_to_linear(physical_root_table_address); - m_rsdt = static_cast<::acpi::sdt const *>(linear_root_table_address); + m_rsdt = static_cast<::acpi::table_header const *>(linear_root_table_address); } } @@ -55,8 +55,8 @@ namespace kernel::acpi } auto entry_size = m_extended ? sizeof(std::uint64_t) : sizeof(std::uint32_t); - auto entry_count = (m_rsdt->length().value - sizeof(::acpi::sdt)) / entry_size; - auto entries_base = reinterpret_cast(m_rsdt) + sizeof(::acpi::sdt); + auto entry_count = (m_rsdt->length().value - sizeof(::acpi::table_header)) / entry_size; + auto entries_base = reinterpret_cast(m_rsdt) + sizeof(::acpi::table_header); for (std::size_t i = 0; i < entry_count; ++i) { @@ -74,7 +74,7 @@ namespace kernel::acpi } auto linear_table_address = kapi::memory::hhdm_to_linear(physical_table_address); - auto table = static_cast<::acpi::sdt const *>(linear_table_address); + auto table = static_cast<::acpi::table_header const *>(linear_table_address); if (!::acpi::validate_checksum({reinterpret_cast(table), table->length().value})) { @@ -90,7 +90,7 @@ namespace kernel::acpi return !m_tables.empty(); } - auto manager::get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::sdt const> + auto manager::get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::table_header const> { if (m_tables.contains(signature)) { diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index f850fe4..97c351b 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -19,12 +19,10 @@ file(GLOB_RECURSE ACPI_HEADERS ) target_sources("acpi" PRIVATE - "acpi/common/basic_table.cpp" "acpi/common/checksum.cpp" "acpi/common/table_header.cpp" "acpi/madt.cpp" "acpi/pointers.cpp" - "acpi/sdt.cpp" ) target_sources("acpi" PUBLIC @@ -61,7 +59,6 @@ if(NOT CMAKE_CROSSCOMPILING) set_source_files_properties("test_data/tables.S" PROPERTIES OBJECT_DEPENDS "${GENERATED_TABLE_BLOBS}") add_executable("acpi_tests" - "acpi/common/basic_table.test.cpp" "acpi/common/table_header.test.cpp" "acpi/madt.test.cpp" "acpi/pointers.test.cpp" diff --git a/libs/acpi/acpi/acpi.hpp b/libs/acpi/acpi/acpi.hpp index 47050f2..2962c7a 100644 --- a/libs/acpi/acpi/acpi.hpp +++ b/libs/acpi/acpi/acpi.hpp @@ -1,10 +1,10 @@ #ifndef ACPI_ACPI_HPP #define ACPI_ACPI_HPP -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #endif diff --git a/libs/acpi/acpi/common/basic_table.cpp b/libs/acpi/acpi/common/basic_table.cpp deleted file mode 100644 index 5cec91a..0000000 --- a/libs/acpi/acpi/common/basic_table.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include -#include - -#include -#include - -namespace acpi -{ - - auto basic_table::validate_checksum() const noexcept -> bool - { - return acpi::validate_checksum({reinterpret_cast(this), m_header.length().value}); - } - - auto basic_table::as_span() const noexcept -> std::span - { - return {reinterpret_cast(this), m_header.length().value}; - } - - auto basic_table::header() const noexcept -> table_header const & - { - return m_header; - } - -} // namespace acpi \ No newline at end of file diff --git a/libs/acpi/acpi/common/basic_table.hpp b/libs/acpi/acpi/common/basic_table.hpp index 43bf533..b0ead96 100644 --- a/libs/acpi/acpi/common/basic_table.hpp +++ b/libs/acpi/acpi/common/basic_table.hpp @@ -1,21 +1,49 @@ #ifndef ACPI_COMMON_BASIC_TABLE_HPP #define ACPI_COMMON_BASIC_TABLE_HPP +#include #include +#include #include #include +#include namespace acpi { + template struct basic_table { - [[nodiscard]] auto validate_checksum() const noexcept -> bool; - - [[nodiscard]] auto as_span() const noexcept -> std::span; - - [[nodiscard]] auto header() const noexcept -> table_header const &; + [[nodiscard]] auto validate_checksum() const noexcept -> bool + { + return acpi::validate_checksum({reinterpret_cast(this), m_header.length().value}); + } + + [[nodiscard]] auto as_span() const noexcept -> std::span + { + return {reinterpret_cast(this), m_header.length().value}; + } + + [[nodiscard]] auto header() const noexcept -> table_header const & + { + return m_header; + } + + [[nodiscard]] auto length() const noexcept -> std::size_t + { + return m_header.length().value; + } + + [[nodiscard]] auto signature() const noexcept -> std::string_view + { + return m_header.signature(); + } + + [[nodiscard]] auto validate() const noexcept -> bool + { + return signature() == table_signature_v && validate_checksum(); + } private: table_header m_header; diff --git a/libs/acpi/acpi/common/basic_table.test.cpp b/libs/acpi/acpi/common/basic_table.test.cpp deleted file mode 100644 index e292b9f..0000000 --- a/libs/acpi/acpi/common/basic_table.test.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include -#include - -SCENARIO("Basic table functions", "[basic_table]") -{ - GIVEN("A valid compiled table header") - { - auto data = acpi::test_data::tables::table_header(); - - WHEN("parsing the table") - { - auto table = reinterpret_cast(data.data()); - - THEN("the checksum is valid") - { - REQUIRE(table->validate_checksum()); - } - } - } -} diff --git a/libs/acpi/acpi/common/table_header.hpp b/libs/acpi/acpi/common/table_header.hpp index 529da81..471fed8 100644 --- a/libs/acpi/acpi/common/table_header.hpp +++ b/libs/acpi/acpi/common/table_header.hpp @@ -1,6 +1,8 @@ #ifndef ACPI_COMMON_TABLE_HEADER_HPP #define ACPI_COMMON_TABLE_HEADER_HPP +// IWYU pragma: private, include + #include #include diff --git a/libs/acpi/acpi/common/table_signature.hpp b/libs/acpi/acpi/common/table_signature.hpp new file mode 100644 index 0000000..f0c2d7a --- /dev/null +++ b/libs/acpi/acpi/common/table_signature.hpp @@ -0,0 +1,17 @@ +#ifndef ACPI_COMMON_TABLE_SIGNATURE_HPP +#define ACPI_COMMON_TABLE_SIGNATURE_HPP + +// IWYU pragma: private, include + +namespace acpi +{ + + template + struct table_signature; + + template + constexpr auto table_signature_v = table_signature::value; + +} // namespace acpi + +#endif diff --git a/libs/acpi/acpi/common/table_type.hpp b/libs/acpi/acpi/common/table_type.hpp new file mode 100644 index 0000000..bc427c7 --- /dev/null +++ b/libs/acpi/acpi/common/table_type.hpp @@ -0,0 +1,17 @@ +#ifndef ACPI_COMMON_TABLE_TYPE_HPP +#define ACPI_COMMON_TABLE_TYPE_HPP + +// IWYU pragma: private, include + +namespace acpi +{ + + template + struct table_type; + + template + using table_type_t = typename table_type::type; + +} // namespace acpi + +#endif diff --git a/libs/acpi/acpi/common/vla_table.hpp b/libs/acpi/acpi/common/vla_table.hpp new file mode 100644 index 0000000..450209c --- /dev/null +++ b/libs/acpi/acpi/common/vla_table.hpp @@ -0,0 +1,115 @@ +#ifndef ACPI_COMMON_VLA_TABLE_HPP +#define ACPI_COMMON_VLA_TABLE_HPP + +#include + +#include +#include + +namespace acpi +{ + + template + struct vla_table_iterator + { + using iterator_category = std::forward_iterator_tag; + using value_type = EntryType; + using pointer = value_type *; + using reference = value_type &; + using difference_type = std::ptrdiff_t; + + constexpr vla_table_iterator() noexcept = default; + + constexpr vla_table_iterator(pointer entry, pointer limit) noexcept + : m_entry{entry} + , m_limit{limit} + {} + + constexpr auto operator++() noexcept -> vla_table_iterator & + { + auto decayed = std::bit_cast(m_entry); + decayed += m_entry->length(); + m_entry = std::bit_cast(decayed); + return *this; + } + + constexpr auto operator++(int) noexcept -> vla_table_iterator + { + auto copy = *this; + ++*this; + return copy; + } + + constexpr auto operator*() const noexcept -> reference + { + return *m_entry; + } + + constexpr auto operator->() const noexcept -> pointer + { + return m_entry; + } + + constexpr auto operator==(vla_table_iterator const & other) const noexcept -> bool + { + return m_entry == other.m_entry || (is_end() && other.is_end()); + } + + private: + [[nodiscard]] constexpr auto is_end() const noexcept -> bool + { + return m_entry == m_limit; + } + + pointer m_entry{}; + pointer m_limit{}; + }; + + template + struct vla_table : basic_table + { + using entry_type = EntryType; + using iterator = vla_table_iterator; + using const_iterator = vla_table_iterator; + + [[nodiscard]] auto begin() noexcept -> iterator + { + auto base = std::bit_cast(this); + base += sizeof(TableType); + auto limit = std::bit_cast(this); + limit += this->length(); + return iterator{std::bit_cast(base), std::bit_cast(limit)}; + } + + [[nodiscard]] auto end() noexcept -> iterator + { + return iterator{}; + } + + [[nodiscard]] auto begin() const noexcept -> const_iterator + { + auto base = std::bit_cast(this); + base += sizeof(TableType); + auto limit = std::bit_cast(this); + limit += this->length(); + return const_iterator{std::bit_cast(base), std::bit_cast(limit)}; + } + + [[nodiscard]] auto end() const noexcept -> const_iterator + { + return const_iterator{}; + } + + [[nodiscard]] auto cbegin() const noexcept -> const_iterator + { + return begin(); + } + + [[nodiscard]] auto cend() const noexcept -> const_iterator + { + return end(); + } + }; +} // namespace acpi + +#endif \ No newline at end of file diff --git a/libs/acpi/acpi/madt.cpp b/libs/acpi/acpi/madt.cpp index 6a62f07..c6830ad 100644 --- a/libs/acpi/acpi/madt.cpp +++ b/libs/acpi/acpi/madt.cpp @@ -1,74 +1,112 @@ -#include +#include + #include +#include #include #include +#include namespace acpi { + struct madt_data + { + std::uint32_t local_interrupt_controller_address; + std::uint32_t flags; + }; + + static_assert(sizeof(madt_data) == 8); + auto madt::local_interrupt_controller_address() const noexcept -> std::uintptr_t { - return static_cast(m_local_interrupt_controller_address); + using type = decltype(madt_data::local_interrupt_controller_address); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(madt_data, local_interrupt_controller_address); + + auto const data = std::span{m_data.data() + offset, size}; + auto value = type{}; + + kstd::libc::memcpy(&value, data.data(), size); + + return value; } auto madt::flags() const noexcept -> std::uint32_t { - return m_flags; + using type = decltype(madt_data::flags); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(madt_data, flags); + + auto const data = std::span{m_data.data() + offset, size}; + auto value = type{}; + + kstd::libc::memcpy(&value, data.data(), size); + + return value; } - auto madt::validate() const noexcept -> bool + struct madt_entry_data { - return signature() == madt_table_signature && sdt::validate(); - } + std::uint8_t type; + std::uint8_t length; + }; + + static_assert(sizeof(madt_entry_data) == 2); auto madt_entry::type() const noexcept -> enum type { - return static_cast(m_type); + constexpr auto field_offset = offsetof(madt_entry_data, type); + + return std::bit_cast(*(m_data.data() + field_offset)); } auto madt_entry::length() const noexcept -> std::size_t { - return m_length; - } + constexpr auto field_offset = offsetof(madt_entry_data, length); - auto processor_local_apic::apic_id() const noexcept -> std::uint8_t - { - return m_apic_id; + return static_cast(*(m_data.data() + field_offset)); } - auto processor_local_apic::active_flags() const noexcept -> flags + struct [[gnu::packed]] processor_local_apic_entry_data { - return static_cast(m_flags); - } + std::uint8_t apic_id; + std::uint8_t processor_id; + std::uint32_t flags; + }; - auto processor_local_apic::processor_id() const noexcept -> std::uint32_t - { - return m_processor_id; - } + static_assert(sizeof(processor_local_apic_entry_data) == 6); - auto madt::begin() const noexcept -> iterator + auto processor_local_apic_entry::id() const noexcept -> std::uint8_t { - auto base = reinterpret_cast(this); - base += sizeof(madt); - auto limit = reinterpret_cast(this); - limit += length().value; - return iterator{reinterpret_cast(base), reinterpret_cast(limit)}; - } + constexpr auto field_offset = offsetof(processor_local_apic_entry_data, apic_id); - auto madt::cbegin() const noexcept -> iterator - { - return begin(); + return static_cast(*(m_data.data() + field_offset)); } - auto madt::end() const noexcept -> iterator + auto processor_local_apic_entry::flags() const noexcept -> enum flags { - return {}; + using type = decltype(processor_local_apic_entry_data::flags); + static_assert(sizeof(type) == sizeof(enum flags)); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(processor_local_apic_entry_data, flags); + + auto const data = std::span{m_data.data() + offset, size}; + auto value = type{}; + + kstd::libc::memcpy(&value, data.data(), size); + + return static_cast(value); } - auto madt::cend() const noexcept -> iterator + auto processor_local_apic_entry::processor_id() const noexcept -> std::uint32_t { - return end(); + constexpr auto field_offset = offsetof(processor_local_apic_entry_data, processor_id); + + return static_cast(*(m_data.data() + field_offset)); } } // namespace acpi diff --git a/libs/acpi/acpi/madt.hpp b/libs/acpi/acpi/madt.hpp index 8b75f58..8307826 100644 --- a/libs/acpi/acpi/madt.hpp +++ b/libs/acpi/acpi/madt.hpp @@ -7,9 +7,11 @@ #include #include -#include -#include +#include +#include +#include +#include #include #include #include @@ -18,7 +20,19 @@ namespace acpi { - struct [[gnu::packed]] madt_entry + template<> + struct table_signature + { + constexpr char static const value[] = "APIC"; // NOLINT + }; + + template<> + struct table_type> + { + using type = struct madt; + }; + + struct madt_entry { //! The type of an MADT entry. enum struct type : std::uint8_t @@ -53,26 +67,16 @@ namespace acpi } private: - std::uint8_t m_type; - std::uint8_t m_length; + std::array m_data{}; }; //! The Multiple APIC Description Table (MADT) - struct [[gnu::packed]] madt : sdt + struct madt : vla_table { - using iterator = sdt_iterator; - using const_iterator = iterator; - //! Get the physical address of the local interrupt controllers described by this entry. [[nodiscard]] auto local_interrupt_controller_address() const noexcept -> std::uintptr_t; [[nodiscard]] auto flags() const noexcept -> std::uint32_t; - [[nodiscard]] auto validate() const noexcept -> bool; - - [[nodiscard]] auto begin() const noexcept -> iterator; - [[nodiscard]] auto cbegin() const noexcept -> iterator; - [[nodiscard]] auto end() const noexcept -> iterator; - [[nodiscard]] auto cend() const noexcept -> iterator; template [[nodiscard]] auto only() const noexcept @@ -82,11 +86,10 @@ namespace acpi } private: - std::uint32_t m_local_interrupt_controller_address; - std::uint32_t m_flags; + std::array m_data{}; }; - struct [[gnu::packed]] processor_local_apic : madt_entry + struct processor_local_apic_entry : madt_entry { constexpr auto static this_type = type::processor_local_apic; @@ -96,28 +99,18 @@ namespace acpi online_capable = 2, }; - [[nodiscard]] auto apic_id() const noexcept -> std::uint8_t; - [[nodiscard]] auto active_flags() const noexcept -> flags; + [[nodiscard]] auto id() const noexcept -> std::uint8_t; + [[nodiscard]] auto flags() const noexcept -> flags; [[nodiscard]] auto processor_id() const noexcept -> std::uint32_t; private: - std::uint8_t m_processor_id; - std::uint8_t m_apic_id; - std::uint32_t m_flags; - }; - - constexpr char const madt_table_signature[] = "APIC"; // NOLINT - - template<> - struct table_type - { - using type = madt; + std::array m_data{}; }; } // namespace acpi template<> -struct kstd::ext::is_bitfield_enum : std::true_type +struct kstd::ext::is_bitfield_enum : std::true_type { }; diff --git a/libs/acpi/acpi/madt.test.cpp b/libs/acpi/acpi/madt.test.cpp index 8926192..899a377 100644 --- a/libs/acpi/acpi/madt.test.cpp +++ b/libs/acpi/acpi/madt.test.cpp @@ -38,7 +38,7 @@ SCENARIO("MADT parsing", "[madt]") THEN("the length is sizeof(madt) + sizeof(processor_local_apic)") { - REQUIRE(madt->length() == kstd::type_size + kstd::type_size); + REQUIRE(madt->length() == sizeof(acpi::madt) + sizeof(acpi::processor_local_apic_entry)); } THEN("the first entry has type processor_local_apic") @@ -48,7 +48,7 @@ SCENARIO("MADT parsing", "[madt]") THEN("`only` can be used to get a view of all processor_local_apic entries") { - REQUIRE(std::ranges::distance(madt->only()) == 1); + REQUIRE(std::ranges::distance(madt->only()) == 1); } } } diff --git a/libs/acpi/acpi/sdt.cpp b/libs/acpi/acpi/sdt.cpp deleted file mode 100644 index 6c6cb47..0000000 --- a/libs/acpi/acpi/sdt.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include - -#include -#include - -#include -#include -#include - -namespace acpi -{ - - auto sdt::creator_revision() const noexcept -> std::uint32_t - { - return m_creator_revision; - } - - auto sdt::creator_id() const noexcept -> std::uint32_t - { - return m_creator_id; - } - - auto sdt::length() const noexcept -> kstd::units::bytes - { - return kstd::units::bytes{m_length}; - } - - auto sdt::oem_id() const noexcept -> std::string_view - { - return {m_oem_id.data(), m_oem_id.size()}; - } - - auto sdt::oem_revision() const noexcept -> std::uint32_t - { - return m_oem_revision; - } - - auto sdt::oem_table_id() const noexcept -> std::string_view - { - return {m_oem_table_id.data(), m_oem_table_id.size()}; - } - - auto sdt::revision() const noexcept -> std::uint8_t - { - return m_revision; - } - - auto sdt::signature() const noexcept -> std::string_view - { - return {m_signature.data(), m_signature.size()}; - } - - auto sdt::validate() const noexcept -> bool - { - return acpi::validate_checksum({reinterpret_cast(this), length().value}); - } - -} // namespace acpi diff --git a/libs/acpi/acpi/sdt.hpp b/libs/acpi/acpi/sdt.hpp deleted file mode 100644 index 72b3896..0000000 --- a/libs/acpi/acpi/sdt.hpp +++ /dev/null @@ -1,127 +0,0 @@ -#ifndef ACPI_SDT_HPP -#define ACPI_SDT_HPP - -// IWYU pragma: private, include - -#include - -#include - -#include -#include -#include -#include -#include - -namespace acpi -{ - - template - struct sdt_iterator - { - using iterator_category = std::forward_iterator_tag; - using value_type = EntryType; - using pointer = value_type *; - using reference = value_type &; - using difference_type = std::ptrdiff_t; - - constexpr sdt_iterator() noexcept = default; - - constexpr sdt_iterator(pointer entry, pointer limit) noexcept - : m_entry{entry} - , m_limit{limit} - {} - - constexpr auto operator++() noexcept -> sdt_iterator & - { - auto decayed = std::bit_cast(m_entry); - decayed += m_entry->length(); - m_entry = std::bit_cast(decayed); - return *this; - } - - constexpr auto operator++(int) noexcept -> sdt_iterator - { - auto copy = *this; - ++*this; - return copy; - } - - constexpr auto operator*() const noexcept -> reference - { - return *m_entry; - } - - constexpr auto operator->() const noexcept -> pointer - { - return m_entry; - } - - constexpr auto operator==(sdt_iterator const & other) const noexcept -> bool - { - return m_entry == other.m_entry || (is_end() && other.is_end()); - } - - private: - [[nodiscard]] constexpr auto is_end() const noexcept -> bool - { - return m_entry == m_limit; - } - - pointer m_entry{}; - pointer m_limit{}; - }; - - //! The common base of all System Description Tables. - struct [[gnu::packed]] sdt - { - //! Get the revision of the utility used to create this table. - [[nodiscard]] auto creator_revision() const noexcept -> std::uint32_t; - - //! Get the vendor ID of the utility used to create this table. - [[nodiscard]] auto creator_id() const noexcept -> std::uint32_t; - - //! Get the length of the entire table, including this header. - [[nodiscard]] auto length() const noexcept -> kstd::units::bytes; - - //! Get the ID of the OEM. - [[nodiscard]] auto oem_id() const noexcept -> std::string_view; - - //! Get the OEMs revision number of this table. - [[nodiscard]] auto oem_revision() const noexcept -> std::uint32_t; - - //! Get the OEMs ID of this table. - [[nodiscard]] auto oem_table_id() const noexcept -> std::string_view; - - //! Get the revision number of the structure of this table. - [[nodiscard]] auto revision() const noexcept -> std::uint8_t; - - //! Get the signature of this table. - [[nodiscard]] auto signature() const noexcept -> std::string_view; - - //! Valide this table's checksum - [[nodiscard]] auto validate() const noexcept -> bool; - - private: - std::array m_signature; - std::uint32_t m_length; - std::uint8_t m_revision; - [[maybe_unused]] std::uint8_t m_checksum; - std::array m_oem_id; - std::array m_oem_table_id; - std::uint32_t m_oem_revision; - std::uint32_t m_creator_id; - std::uint32_t m_creator_revision; - }; - - constexpr char const rsdt_table_signature[] = "RSDT"; // NOLINT - - template<> - struct table_type - { - using type = sdt; - }; - -} // namespace acpi - -#endif diff --git a/libs/acpi/acpi/table_type.hpp b/libs/acpi/acpi/table_type.hpp deleted file mode 100644 index 7fdd7e3..0000000 --- a/libs/acpi/acpi/table_type.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef ACPI_TABLE_TYPE_HPP -#define ACPI_TABLE_TYPE_HPP - -// IWYU pragma: private, include - -namespace acpi -{ - - template - struct table_type; - - template - using table_type_t = typename table_type::type; - -} // namespace acpi - -#endif -- cgit v1.2.3 From 89f1c730fb9daf4a5da0748934ca5befd90eb731 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 16 Apr 2026 09:28:16 +0200 Subject: acpi: move madt to data --- libs/acpi/CMakeLists.txt | 4 +- libs/acpi/acpi/acpi.hpp | 2 +- libs/acpi/acpi/data/madt.cpp | 112 ++++++++++++++++++++++++++++++++++++ libs/acpi/acpi/data/madt.hpp | 117 ++++++++++++++++++++++++++++++++++++++ libs/acpi/acpi/data/madt.test.cpp | 55 ++++++++++++++++++ libs/acpi/acpi/madt.cpp | 112 ------------------------------------ libs/acpi/acpi/madt.hpp | 117 -------------------------------------- libs/acpi/acpi/madt.test.cpp | 55 ------------------ 8 files changed, 287 insertions(+), 287 deletions(-) create mode 100644 libs/acpi/acpi/data/madt.cpp create mode 100644 libs/acpi/acpi/data/madt.hpp create mode 100644 libs/acpi/acpi/data/madt.test.cpp delete mode 100644 libs/acpi/acpi/madt.cpp delete mode 100644 libs/acpi/acpi/madt.hpp delete mode 100644 libs/acpi/acpi/madt.test.cpp diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index 97c351b..f6e484c 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -21,7 +21,7 @@ file(GLOB_RECURSE ACPI_HEADERS target_sources("acpi" PRIVATE "acpi/common/checksum.cpp" "acpi/common/table_header.cpp" - "acpi/madt.cpp" + "acpi/data/madt.cpp" "acpi/pointers.cpp" ) @@ -60,7 +60,7 @@ if(NOT CMAKE_CROSSCOMPILING) add_executable("acpi_tests" "acpi/common/table_header.test.cpp" - "acpi/madt.test.cpp" + "acpi/data/madt.test.cpp" "acpi/pointers.test.cpp" "test_data/tables.S" diff --git a/libs/acpi/acpi/acpi.hpp b/libs/acpi/acpi/acpi.hpp index 2962c7a..4cfcede 100644 --- a/libs/acpi/acpi/acpi.hpp +++ b/libs/acpi/acpi/acpi.hpp @@ -4,7 +4,7 @@ #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export -#include // IWYU pragma: export +#include // IWYU pragma: export #include // IWYU pragma: export #endif diff --git a/libs/acpi/acpi/data/madt.cpp b/libs/acpi/acpi/data/madt.cpp new file mode 100644 index 0000000..a8d4741 --- /dev/null +++ b/libs/acpi/acpi/data/madt.cpp @@ -0,0 +1,112 @@ +#include + +#include + +#include +#include +#include +#include + +namespace acpi +{ + + struct madt_data + { + std::uint32_t local_interrupt_controller_address; + std::uint32_t flags; + }; + + static_assert(sizeof(madt_data) == 8); + + auto madt::local_interrupt_controller_address() const noexcept -> std::uintptr_t + { + using type = decltype(madt_data::local_interrupt_controller_address); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(madt_data, local_interrupt_controller_address); + + auto const data = std::span{m_data.data() + offset, size}; + auto value = type{}; + + kstd::libc::memcpy(&value, data.data(), size); + + return value; + } + + auto madt::flags() const noexcept -> std::uint32_t + { + using type = decltype(madt_data::flags); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(madt_data, flags); + + auto const data = std::span{m_data.data() + offset, size}; + auto value = type{}; + + kstd::libc::memcpy(&value, data.data(), size); + + return value; + } + + struct madt_entry_data + { + std::uint8_t type; + std::uint8_t length; + }; + + static_assert(sizeof(madt_entry_data) == 2); + + auto madt_entry::type() const noexcept -> enum type + { + constexpr auto field_offset = offsetof(madt_entry_data, type); + + return std::bit_cast(*(m_data.data() + field_offset)); + } + + auto madt_entry::length() const noexcept -> std::size_t + { + constexpr auto field_offset = offsetof(madt_entry_data, length); + + return static_cast(*(m_data.data() + field_offset)); + } + + struct [[gnu::packed]] processor_local_apic_entry_data + { + std::uint8_t apic_id; + std::uint8_t processor_id; + std::uint32_t flags; + }; + + static_assert(sizeof(processor_local_apic_entry_data) == 6); + + auto processor_local_apic_entry::id() const noexcept -> std::uint8_t + { + constexpr auto field_offset = offsetof(processor_local_apic_entry_data, apic_id); + + return static_cast(*(m_data.data() + field_offset)); + } + + auto processor_local_apic_entry::flags() const noexcept -> enum flags + { + using type = decltype(processor_local_apic_entry_data::flags); + static_assert(sizeof(type) == sizeof(enum flags)); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(processor_local_apic_entry_data, flags); + + auto const data = std::span{m_data.data() + offset, size}; + auto value = type{}; + + kstd::libc::memcpy(&value, data.data(), size); + + return static_cast(value); + } + + auto processor_local_apic_entry::processor_id() const noexcept -> std::uint32_t + { + constexpr auto field_offset = offsetof(processor_local_apic_entry_data, processor_id); + + return static_cast(*(m_data.data() + field_offset)); + } + +} // namespace acpi diff --git a/libs/acpi/acpi/data/madt.hpp b/libs/acpi/acpi/data/madt.hpp new file mode 100644 index 0000000..8307826 --- /dev/null +++ b/libs/acpi/acpi/data/madt.hpp @@ -0,0 +1,117 @@ +#ifndef ACPI_ACPI_MADT_HPP +#define ACPI_ACPI_MADT_HPP + +// IWYU pragma: private, include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace acpi +{ + + template<> + struct table_signature + { + constexpr char static const value[] = "APIC"; // NOLINT + }; + + template<> + struct table_type> + { + using type = struct madt; + }; + + struct madt_entry + { + //! The type of an MADT entry. + enum struct type : std::uint8_t + { + processor_local_apic, + io_apic, + io_apic_interrupt_source_override, + io_apic_nmi_source, + local_apic_nmi_interrupts, + local_apic_address_override, + processor_local_x2_apic, + }; + + //! Get the type of this entry. + [[nodiscard]] auto type() const noexcept -> enum type; + + //! Get the length of this entry. + [[nodiscard]] auto length() const noexcept -> std::size_t; + + //! Cast this entry to the given concrete entry type. + //! + //! @warning This function will terminate execution if the desired target type does not match up with this entry's + //! actual type. + template + [[nodiscard]] auto as() const -> EntryType const & + { + if (type() != EntryType::this_type) + { + kstd::os::panic("Invalid cast"); + } + return reinterpret_cast(*this); + } + + private: + std::array m_data{}; + }; + + //! The Multiple APIC Description Table (MADT) + struct madt : vla_table + { + //! Get the physical address of the local interrupt controllers described by this entry. + [[nodiscard]] auto local_interrupt_controller_address() const noexcept -> std::uintptr_t; + + [[nodiscard]] auto flags() const noexcept -> std::uint32_t; + + template + [[nodiscard]] auto only() const noexcept + { + return *this | std::views::filter([](auto const & e) { return e.type() == EntryType::this_type; }) | + std::views::transform([](auto const & e) { return e.template as(); }); + } + + private: + std::array m_data{}; + }; + + struct processor_local_apic_entry : madt_entry + { + constexpr auto static this_type = type::processor_local_apic; + + enum struct flags : std::uint32_t + { + processor_enabled = 1, + online_capable = 2, + }; + + [[nodiscard]] auto id() const noexcept -> std::uint8_t; + [[nodiscard]] auto flags() const noexcept -> flags; + [[nodiscard]] auto processor_id() const noexcept -> std::uint32_t; + + private: + std::array m_data{}; + }; + +} // namespace acpi + +template<> +struct kstd::ext::is_bitfield_enum : std::true_type +{ +}; + +#endif diff --git a/libs/acpi/acpi/data/madt.test.cpp b/libs/acpi/acpi/data/madt.test.cpp new file mode 100644 index 0000000..2b74d4c --- /dev/null +++ b/libs/acpi/acpi/data/madt.test.cpp @@ -0,0 +1,55 @@ +#include + +#include +#include +#include + +#include + +SCENARIO("MADT parsing", "[madt]") +{ + GIVEN("The basic compiled MADT containing a single LAPIC entry and the default x86 LAPIC address") + { + auto data = acpi::test_data::tables::basic_madt(); + + WHEN("parsing the table") + { + auto madt = reinterpret_cast(data.data()); + + THEN("the signature is correct") + { + REQUIRE(madt->signature() == "APIC"); + } + + THEN("validate returns true") + { + REQUIRE(madt->validate()); + } + + THEN("there is a single entry in the table") + { + REQUIRE(std::distance(madt->begin(), madt->end()) == 1); + } + + THEN("the LAPIC address is 0xfee00000") + { + REQUIRE(madt->local_interrupt_controller_address() == 0xfee0'0000); + } + + THEN("the length is sizeof(madt) + sizeof(processor_local_apic)") + { + REQUIRE(madt->length() == sizeof(acpi::madt) + sizeof(acpi::processor_local_apic_entry)); + } + + THEN("the first entry has type processor_local_apic") + { + REQUIRE(madt->cbegin()->type() == acpi::madt_entry::type::processor_local_apic); + } + + THEN("`only` can be used to get a view of all processor_local_apic entries") + { + REQUIRE(std::ranges::distance(madt->only()) == 1); + } + } + } +} diff --git a/libs/acpi/acpi/madt.cpp b/libs/acpi/acpi/madt.cpp deleted file mode 100644 index c6830ad..0000000 --- a/libs/acpi/acpi/madt.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include - -#include - -#include -#include -#include -#include - -namespace acpi -{ - - struct madt_data - { - std::uint32_t local_interrupt_controller_address; - std::uint32_t flags; - }; - - static_assert(sizeof(madt_data) == 8); - - auto madt::local_interrupt_controller_address() const noexcept -> std::uintptr_t - { - using type = decltype(madt_data::local_interrupt_controller_address); - - constexpr auto size = sizeof(type); - constexpr auto offset = offsetof(madt_data, local_interrupt_controller_address); - - auto const data = std::span{m_data.data() + offset, size}; - auto value = type{}; - - kstd::libc::memcpy(&value, data.data(), size); - - return value; - } - - auto madt::flags() const noexcept -> std::uint32_t - { - using type = decltype(madt_data::flags); - - constexpr auto size = sizeof(type); - constexpr auto offset = offsetof(madt_data, flags); - - auto const data = std::span{m_data.data() + offset, size}; - auto value = type{}; - - kstd::libc::memcpy(&value, data.data(), size); - - return value; - } - - struct madt_entry_data - { - std::uint8_t type; - std::uint8_t length; - }; - - static_assert(sizeof(madt_entry_data) == 2); - - auto madt_entry::type() const noexcept -> enum type - { - constexpr auto field_offset = offsetof(madt_entry_data, type); - - return std::bit_cast(*(m_data.data() + field_offset)); - } - - auto madt_entry::length() const noexcept -> std::size_t - { - constexpr auto field_offset = offsetof(madt_entry_data, length); - - return static_cast(*(m_data.data() + field_offset)); - } - - struct [[gnu::packed]] processor_local_apic_entry_data - { - std::uint8_t apic_id; - std::uint8_t processor_id; - std::uint32_t flags; - }; - - static_assert(sizeof(processor_local_apic_entry_data) == 6); - - auto processor_local_apic_entry::id() const noexcept -> std::uint8_t - { - constexpr auto field_offset = offsetof(processor_local_apic_entry_data, apic_id); - - return static_cast(*(m_data.data() + field_offset)); - } - - auto processor_local_apic_entry::flags() const noexcept -> enum flags - { - using type = decltype(processor_local_apic_entry_data::flags); - static_assert(sizeof(type) == sizeof(enum flags)); - - constexpr auto size = sizeof(type); - constexpr auto offset = offsetof(processor_local_apic_entry_data, flags); - - auto const data = std::span{m_data.data() + offset, size}; - auto value = type{}; - - kstd::libc::memcpy(&value, data.data(), size); - - return static_cast(value); - } - - auto processor_local_apic_entry::processor_id() const noexcept -> std::uint32_t - { - constexpr auto field_offset = offsetof(processor_local_apic_entry_data, processor_id); - - return static_cast(*(m_data.data() + field_offset)); - } - -} // namespace acpi diff --git a/libs/acpi/acpi/madt.hpp b/libs/acpi/acpi/madt.hpp deleted file mode 100644 index 8307826..0000000 --- a/libs/acpi/acpi/madt.hpp +++ /dev/null @@ -1,117 +0,0 @@ -#ifndef ACPI_ACPI_MADT_HPP -#define ACPI_ACPI_MADT_HPP - -// IWYU pragma: private, include - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace acpi -{ - - template<> - struct table_signature - { - constexpr char static const value[] = "APIC"; // NOLINT - }; - - template<> - struct table_type> - { - using type = struct madt; - }; - - struct madt_entry - { - //! The type of an MADT entry. - enum struct type : std::uint8_t - { - processor_local_apic, - io_apic, - io_apic_interrupt_source_override, - io_apic_nmi_source, - local_apic_nmi_interrupts, - local_apic_address_override, - processor_local_x2_apic, - }; - - //! Get the type of this entry. - [[nodiscard]] auto type() const noexcept -> enum type; - - //! Get the length of this entry. - [[nodiscard]] auto length() const noexcept -> std::size_t; - - //! Cast this entry to the given concrete entry type. - //! - //! @warning This function will terminate execution if the desired target type does not match up with this entry's - //! actual type. - template - [[nodiscard]] auto as() const -> EntryType const & - { - if (type() != EntryType::this_type) - { - kstd::os::panic("Invalid cast"); - } - return reinterpret_cast(*this); - } - - private: - std::array m_data{}; - }; - - //! The Multiple APIC Description Table (MADT) - struct madt : vla_table - { - //! Get the physical address of the local interrupt controllers described by this entry. - [[nodiscard]] auto local_interrupt_controller_address() const noexcept -> std::uintptr_t; - - [[nodiscard]] auto flags() const noexcept -> std::uint32_t; - - template - [[nodiscard]] auto only() const noexcept - { - return *this | std::views::filter([](auto const & e) { return e.type() == EntryType::this_type; }) | - std::views::transform([](auto const & e) { return e.template as(); }); - } - - private: - std::array m_data{}; - }; - - struct processor_local_apic_entry : madt_entry - { - constexpr auto static this_type = type::processor_local_apic; - - enum struct flags : std::uint32_t - { - processor_enabled = 1, - online_capable = 2, - }; - - [[nodiscard]] auto id() const noexcept -> std::uint8_t; - [[nodiscard]] auto flags() const noexcept -> flags; - [[nodiscard]] auto processor_id() const noexcept -> std::uint32_t; - - private: - std::array m_data{}; - }; - -} // namespace acpi - -template<> -struct kstd::ext::is_bitfield_enum : std::true_type -{ -}; - -#endif diff --git a/libs/acpi/acpi/madt.test.cpp b/libs/acpi/acpi/madt.test.cpp deleted file mode 100644 index 899a377..0000000 --- a/libs/acpi/acpi/madt.test.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include - -#include -#include -#include - -#include - -SCENARIO("MADT parsing", "[madt]") -{ - GIVEN("The basic compiled MADT containing a single LAPIC entry and the default x86 LAPIC address") - { - auto data = acpi::test_data::tables::basic_madt(); - - WHEN("parsing the table") - { - auto madt = reinterpret_cast(data.data()); - - THEN("the signature is correct") - { - REQUIRE(madt->signature() == "APIC"); - } - - THEN("validate returns true") - { - REQUIRE(madt->validate()); - } - - THEN("there is a single entry in the table") - { - REQUIRE(std::distance(madt->begin(), madt->end()) == 1); - } - - THEN("the LAPIC address is 0xfee00000") - { - REQUIRE(madt->local_interrupt_controller_address() == 0xfee0'0000); - } - - THEN("the length is sizeof(madt) + sizeof(processor_local_apic)") - { - REQUIRE(madt->length() == sizeof(acpi::madt) + sizeof(acpi::processor_local_apic_entry)); - } - - THEN("the first entry has type processor_local_apic") - { - REQUIRE(madt->cbegin()->type() == acpi::madt_entry::type::processor_local_apic); - } - - THEN("`only` can be used to get a view of all processor_local_apic entries") - { - REQUIRE(std::ranges::distance(madt->only()) == 1); - } - } - } -} -- cgit v1.2.3 From b31c47b32d91b0b85245ed30f1751cd5cbc397cf Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 16 Apr 2026 09:37:41 +0200 Subject: acpi: add rsdt type --- libs/acpi/CMakeLists.txt | 2 ++ libs/acpi/acpi/data/rsdt.cpp | 37 +++++++++++++++++++++++++++++++ libs/acpi/acpi/data/rsdt.hpp | 42 +++++++++++++++++++++++++++++++++++ libs/acpi/acpi/data/rsdt.test.cpp | 46 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+) create mode 100644 libs/acpi/acpi/data/rsdt.cpp create mode 100644 libs/acpi/acpi/data/rsdt.hpp create mode 100644 libs/acpi/acpi/data/rsdt.test.cpp diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index f6e484c..833dcea 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -22,6 +22,7 @@ target_sources("acpi" PRIVATE "acpi/common/checksum.cpp" "acpi/common/table_header.cpp" "acpi/data/madt.cpp" + "acpi/data/rsdt.cpp" "acpi/pointers.cpp" ) @@ -61,6 +62,7 @@ if(NOT CMAKE_CROSSCOMPILING) add_executable("acpi_tests" "acpi/common/table_header.test.cpp" "acpi/data/madt.test.cpp" + "acpi/data/rsdt.test.cpp" "acpi/pointers.test.cpp" "test_data/tables.S" diff --git a/libs/acpi/acpi/data/rsdt.cpp b/libs/acpi/acpi/data/rsdt.cpp new file mode 100644 index 0000000..8e20b6c --- /dev/null +++ b/libs/acpi/acpi/data/rsdt.cpp @@ -0,0 +1,37 @@ +#include + +#include +#include + +#include +#include + +namespace acpi +{ + + struct rsdt_entry_data + { + std::uint32_t address; + }; + + [[nodiscard]] auto rsdt_entry::address() const noexcept -> table_header * + { + using type = decltype(rsdt_entry_data::address); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(rsdt_entry_data, address); + + auto const data = std::span{m_data.data() + offset, size}; + auto value = type{}; + + kstd::libc::memcpy(&value, data.data(), size); + + return reinterpret_cast(static_cast(value)); + } + + [[nodiscard]] auto rsdt_entry::length() const noexcept -> std::size_t + { + return sizeof(m_data); + } + +} // namespace acpi \ No newline at end of file diff --git a/libs/acpi/acpi/data/rsdt.hpp b/libs/acpi/acpi/data/rsdt.hpp new file mode 100644 index 0000000..b43f066 --- /dev/null +++ b/libs/acpi/acpi/data/rsdt.hpp @@ -0,0 +1,42 @@ +#ifndef ACPI_DATA_RSDT_HPP +#define ACPI_DATA_RSDT_HPP + +#include +#include +#include +#include + +#include +#include + +namespace acpi +{ + + template<> + struct table_signature + { + constexpr char static const value[] = "RSDT"; // NOLINT + }; + + template<> + struct table_type> + { + using type = struct rsdt; + }; + + struct rsdt_entry + { + [[nodiscard]] auto address() const noexcept -> table_header *; + [[nodiscard]] auto length() const noexcept -> std::size_t; + + private: + std::array m_data{}; + }; + + struct rsdt : vla_table + { + }; + +} // namespace acpi + +#endif \ No newline at end of file diff --git a/libs/acpi/acpi/data/rsdt.test.cpp b/libs/acpi/acpi/data/rsdt.test.cpp new file mode 100644 index 0000000..e1bd1df --- /dev/null +++ b/libs/acpi/acpi/data/rsdt.test.cpp @@ -0,0 +1,46 @@ +#include + +#include +#include +#include +#include + +#include + +SCENARIO("RSDT parsing", "[rsdt]") +{ + GIVEN("The basic compiled RSDT containing 8 table pointers") + { + auto data = acpi::test_data::tables::basic_rsdt(); + + WHEN("parsing the table") + { + auto rsdt = reinterpret_cast(data.data()); + + THEN("the signature is correct") + { + REQUIRE(rsdt->signature() == "RSDT"); + } + + THEN("validate returns true") + { + REQUIRE(rsdt->validate()); + } + + THEN("there are 8 entries in the table") + { + REQUIRE(std::distance(rsdt->begin(), rsdt->end()) == 8); + } + + THEN("the first entry has address 0x10") + { + REQUIRE(rsdt->cbegin()->address() == reinterpret_cast(0x10)); + } + + THEN("the length is sizeof(rsdt) + 8 * sizeof(rsdt_entry)") + { + REQUIRE(rsdt->length() == sizeof(acpi::rsdt) + 8 * sizeof(acpi::rsdt_entry)); + } + } + } +} -- cgit v1.2.3 From 28cae58fe117e5fcfc46fd6378e19387cd73b2fe Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 16 Apr 2026 09:40:12 +0200 Subject: acpi: derive basic table from table header --- kernel/src/acpi/manager.cpp | 2 +- libs/acpi/acpi/acpi.hpp | 1 + libs/acpi/acpi/common/basic_table.hpp | 25 +++---------------------- libs/acpi/acpi/common/vla_table.hpp | 4 ++-- libs/acpi/acpi/data/madt.test.cpp | 2 +- libs/acpi/acpi/data/rsdt.cpp | 1 + libs/acpi/acpi/data/rsdt.hpp | 1 - libs/acpi/acpi/data/rsdt.test.cpp | 2 +- 8 files changed, 10 insertions(+), 28 deletions(-) diff --git a/kernel/src/acpi/manager.cpp b/kernel/src/acpi/manager.cpp index 41469c2..2f2a1bd 100644 --- a/kernel/src/acpi/manager.cpp +++ b/kernel/src/acpi/manager.cpp @@ -43,7 +43,7 @@ namespace kernel::acpi } auto physical_root_table_address = kapi::memory::physical_address{m_sdp->table_address()}; auto linear_root_table_address = kapi::memory::hhdm_to_linear(physical_root_table_address); - m_rsdt = static_cast<::acpi::table_header const *>(linear_root_table_address); + m_rsdt = static_cast<::acpi::rsdt const *>(linear_root_table_address); } } diff --git a/libs/acpi/acpi/acpi.hpp b/libs/acpi/acpi/acpi.hpp index 4cfcede..385ddc9 100644 --- a/libs/acpi/acpi/acpi.hpp +++ b/libs/acpi/acpi/acpi.hpp @@ -5,6 +5,7 @@ #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export +#include // IWYU pragma: export #include // IWYU pragma: export #endif diff --git a/libs/acpi/acpi/common/basic_table.hpp b/libs/acpi/acpi/common/basic_table.hpp index b0ead96..33f23d5 100644 --- a/libs/acpi/acpi/common/basic_table.hpp +++ b/libs/acpi/acpi/common/basic_table.hpp @@ -7,46 +7,27 @@ #include #include -#include namespace acpi { template - struct basic_table + struct basic_table : table_header { [[nodiscard]] auto validate_checksum() const noexcept -> bool { - return acpi::validate_checksum({reinterpret_cast(this), m_header.length().value}); + return acpi::validate_checksum(as_span()); } [[nodiscard]] auto as_span() const noexcept -> std::span { - return {reinterpret_cast(this), m_header.length().value}; - } - - [[nodiscard]] auto header() const noexcept -> table_header const & - { - return m_header; - } - - [[nodiscard]] auto length() const noexcept -> std::size_t - { - return m_header.length().value; - } - - [[nodiscard]] auto signature() const noexcept -> std::string_view - { - return m_header.signature(); + return {reinterpret_cast(this), length().value}; } [[nodiscard]] auto validate() const noexcept -> bool { return signature() == table_signature_v && validate_checksum(); } - - private: - table_header m_header; }; } // namespace acpi diff --git a/libs/acpi/acpi/common/vla_table.hpp b/libs/acpi/acpi/common/vla_table.hpp index 450209c..a65a28e 100644 --- a/libs/acpi/acpi/common/vla_table.hpp +++ b/libs/acpi/acpi/common/vla_table.hpp @@ -77,7 +77,7 @@ namespace acpi auto base = std::bit_cast(this); base += sizeof(TableType); auto limit = std::bit_cast(this); - limit += this->length(); + limit += this->length().value; return iterator{std::bit_cast(base), std::bit_cast(limit)}; } @@ -91,7 +91,7 @@ namespace acpi auto base = std::bit_cast(this); base += sizeof(TableType); auto limit = std::bit_cast(this); - limit += this->length(); + limit += this->length().value; return const_iterator{std::bit_cast(base), std::bit_cast(limit)}; } diff --git a/libs/acpi/acpi/data/madt.test.cpp b/libs/acpi/acpi/data/madt.test.cpp index 2b74d4c..6795499 100644 --- a/libs/acpi/acpi/data/madt.test.cpp +++ b/libs/acpi/acpi/data/madt.test.cpp @@ -38,7 +38,7 @@ SCENARIO("MADT parsing", "[madt]") THEN("the length is sizeof(madt) + sizeof(processor_local_apic)") { - REQUIRE(madt->length() == sizeof(acpi::madt) + sizeof(acpi::processor_local_apic_entry)); + REQUIRE(madt->length().value == sizeof(acpi::madt) + sizeof(acpi::processor_local_apic_entry)); } THEN("the first entry has type processor_local_apic") diff --git a/libs/acpi/acpi/data/rsdt.cpp b/libs/acpi/acpi/data/rsdt.cpp index 8e20b6c..fe108c7 100644 --- a/libs/acpi/acpi/data/rsdt.cpp +++ b/libs/acpi/acpi/data/rsdt.cpp @@ -5,6 +5,7 @@ #include #include +#include namespace acpi { diff --git a/libs/acpi/acpi/data/rsdt.hpp b/libs/acpi/acpi/data/rsdt.hpp index b43f066..88c5fd1 100644 --- a/libs/acpi/acpi/data/rsdt.hpp +++ b/libs/acpi/acpi/data/rsdt.hpp @@ -1,7 +1,6 @@ #ifndef ACPI_DATA_RSDT_HPP #define ACPI_DATA_RSDT_HPP -#include #include #include #include diff --git a/libs/acpi/acpi/data/rsdt.test.cpp b/libs/acpi/acpi/data/rsdt.test.cpp index e1bd1df..937dce0 100644 --- a/libs/acpi/acpi/data/rsdt.test.cpp +++ b/libs/acpi/acpi/data/rsdt.test.cpp @@ -39,7 +39,7 @@ SCENARIO("RSDT parsing", "[rsdt]") THEN("the length is sizeof(rsdt) + 8 * sizeof(rsdt_entry)") { - REQUIRE(rsdt->length() == sizeof(acpi::rsdt) + 8 * sizeof(acpi::rsdt_entry)); + REQUIRE(rsdt->length().value == sizeof(acpi::rsdt) + 8 * sizeof(acpi::rsdt_entry)); } } } -- cgit v1.2.3 From 776ab2749d5af0a34fd2aa6103a377ddc04d4c53 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 16 Apr 2026 10:03:35 +0200 Subject: acpi: introduce XSDT type --- arch/x86_64/kapi/cpu.cpp | 16 ++++++------- kernel/src/acpi/manager.cpp | 44 +++++++++++++++++------------------- libs/acpi/CMakeLists.txt | 3 +++ libs/acpi/acpi/acpi.hpp | 1 + libs/acpi/acpi/data/rsdt.hpp | 1 + libs/acpi/acpi/data/xsdt.cpp | 38 +++++++++++++++++++++++++++++++ libs/acpi/acpi/data/xsdt.hpp | 42 ++++++++++++++++++++++++++++++++++ libs/acpi/acpi/data/xsdt.test.cpp | 46 ++++++++++++++++++++++++++++++++++++++ libs/acpi/test_data/basic_xsdt.asl | 26 +++++++++++++++++++++ libs/acpi/test_data/tables.S | 1 + libs/acpi/test_data/tables.hpp | 1 + 11 files changed, 187 insertions(+), 32 deletions(-) create mode 100644 libs/acpi/acpi/data/xsdt.cpp create mode 100644 libs/acpi/acpi/data/xsdt.hpp create mode 100644 libs/acpi/acpi/data/xsdt.test.cpp create mode 100644 libs/acpi/test_data/basic_xsdt.asl diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index 22936c2..965998c 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -23,8 +23,8 @@ namespace kapi::cpu namespace { - constexpr auto candidate_flags = ::acpi::processor_local_apic::flags::processor_enabled // - | ::acpi::processor_local_apic::flags::online_capable; + constexpr auto candidate_flags = ::acpi::processor_local_apic_entry::flags::processor_enabled // + | ::acpi::processor_local_apic_entry::flags::online_capable; } auto init() -> void @@ -52,7 +52,7 @@ namespace kapi::cpu auto static const core_major = kapi::devices::allocate_major_number(); auto static const interrupt_controller_major = kapi::devices::allocate_major_number(); - auto madt = kapi::acpi::get_table<::acpi::madt_table_signature>(); + auto madt = kapi::acpi::get_table<::acpi::table_signature_v<::acpi::madt>>(); if (!madt) { kstd::println("[x86_64:PLT] Failed to find ACPI APIC table"); @@ -62,10 +62,8 @@ namespace kapi::cpu auto lapic_entries = *madt | std::views::filter([](auto const & entry) { return entry.type() == ::acpi::madt_entry::type::processor_local_apic; }) | std::views::transform([](auto const & entry) { - return static_cast<::acpi::processor_local_apic const &>(entry); - }) | std::views::filter([](auto const & entry) { - return static_cast(entry.active_flags() & candidate_flags); - }); + return static_cast<::acpi::processor_local_apic_entry const &>(entry); + }) | std::views::filter([](auto const & entry) { return static_cast(entry.flags() & candidate_flags); }); auto bsp_found = false; auto core_count = 0uz; @@ -77,8 +75,8 @@ namespace kapi::cpu auto is_bsp = !bsp_found; bsp_found = true; auto core = kstd::make_unique(core_major, core_count, apic.processor_id(), is_bsp); - core->add_child(kstd::make_unique(interrupt_controller_major, core_count, - apic.apic_id(), local_apic_address, is_bsp)); + core->add_child(kstd::make_unique(interrupt_controller_major, core_count, apic.id(), + local_apic_address, is_bsp)); cpu_bus->add_child(std::move(core)); ++core_count; } diff --git a/kernel/src/acpi/manager.cpp b/kernel/src/acpi/manager.cpp index 2f2a1bd..5876799 100644 --- a/kernel/src/acpi/manager.cpp +++ b/kernel/src/acpi/manager.cpp @@ -8,8 +8,10 @@ #include +#include #include #include +#include #include namespace kernel::acpi @@ -32,7 +34,7 @@ namespace kernel::acpi } auto physical_extended_table_address = kapi::memory::physical_address{xsdp->table_address()}; auto linear_extended_table_address = kapi::memory::hhdm_to_linear(physical_extended_table_address); - m_rsdt = static_cast<::acpi::table_header const *>(linear_extended_table_address); + m_rsdt = static_cast<::acpi::xsdt const *>(linear_extended_table_address); m_extended = true; } else @@ -54,29 +56,12 @@ namespace kernel::acpi kapi::system::panic("[OS:ACPI] Invalid RSDT checksum!"); } - auto entry_size = m_extended ? sizeof(std::uint64_t) : sizeof(std::uint32_t); - auto entry_count = (m_rsdt->length().value - sizeof(::acpi::table_header)) / entry_size; - auto entries_base = reinterpret_cast(m_rsdt) + sizeof(::acpi::table_header); + auto check_and_register_table = [&](auto table_address) -> void { + auto physical_table_address = kapi::memory::physical_address{reinterpret_cast(table_address)}; + auto mapped_table = kapi::memory::hhdm_to_linear(physical_table_address); + auto table = static_cast<::acpi::table_header const *>(mapped_table); - for (std::size_t i = 0; i < entry_count; ++i) - { - auto physical_table_address = kapi::memory::physical_address{}; - - if (m_extended) - { - auto entry = reinterpret_cast(entries_base + (i * entry_size)); - physical_table_address = kapi::memory::physical_address{*entry}; - } - else - { - auto entry = reinterpret_cast(entries_base + (i * entry_size)); - physical_table_address = kapi::memory::physical_address{*entry}; - } - - auto linear_table_address = kapi::memory::hhdm_to_linear(physical_table_address); - auto table = static_cast<::acpi::table_header const *>(linear_table_address); - - if (!::acpi::validate_checksum({reinterpret_cast(table), table->length().value})) + if (!::acpi::validate_checksum({static_cast(mapped_table), table->length().value})) { kstd::println(kstd::print_sink::stderr, "[OS:ACPI] Invalid table checksum!"); } @@ -85,6 +70,19 @@ namespace kernel::acpi kstd::println("[OS:ACPI] Found '{}' ACPI table", table->signature()); m_tables.emplace(table->signature(), table); } + }; + + if (m_extended) + { + auto xsdt = static_cast<::acpi::xsdt const *>(m_rsdt); + std::ranges::for_each(*xsdt | std::views::transform([](auto const & entry) { return entry.address(); }), + check_and_register_table); + } + else + { + auto rsdt = static_cast<::acpi::rsdt const *>(m_rsdt); + std::ranges::for_each(*rsdt | std::views::transform([](auto const & entry) { return entry.address(); }), + check_and_register_table); } return !m_tables.empty(); diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index 833dcea..55d5b54 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -23,6 +23,7 @@ target_sources("acpi" PRIVATE "acpi/common/table_header.cpp" "acpi/data/madt.cpp" "acpi/data/rsdt.cpp" + "acpi/data/xsdt.cpp" "acpi/pointers.cpp" ) @@ -44,6 +45,7 @@ if(NOT CMAKE_CROSSCOMPILING) "basic_madt" "basic_rsdt" "basic_rsdp" + "basic_xsdt" "table_header" ) @@ -63,6 +65,7 @@ if(NOT CMAKE_CROSSCOMPILING) "acpi/common/table_header.test.cpp" "acpi/data/madt.test.cpp" "acpi/data/rsdt.test.cpp" + "acpi/data/xsdt.test.cpp" "acpi/pointers.test.cpp" "test_data/tables.S" diff --git a/libs/acpi/acpi/acpi.hpp b/libs/acpi/acpi/acpi.hpp index 385ddc9..0da44a8 100644 --- a/libs/acpi/acpi/acpi.hpp +++ b/libs/acpi/acpi/acpi.hpp @@ -6,6 +6,7 @@ #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export +#include // IWYU pragma: export #include // IWYU pragma: export #endif diff --git a/libs/acpi/acpi/data/rsdt.hpp b/libs/acpi/acpi/data/rsdt.hpp index 88c5fd1..a661187 100644 --- a/libs/acpi/acpi/data/rsdt.hpp +++ b/libs/acpi/acpi/data/rsdt.hpp @@ -1,6 +1,7 @@ #ifndef ACPI_DATA_RSDT_HPP #define ACPI_DATA_RSDT_HPP +#include #include #include #include diff --git a/libs/acpi/acpi/data/xsdt.cpp b/libs/acpi/acpi/data/xsdt.cpp new file mode 100644 index 0000000..b4202b8 --- /dev/null +++ b/libs/acpi/acpi/data/xsdt.cpp @@ -0,0 +1,38 @@ +#include + +#include +#include + +#include +#include +#include + +namespace acpi +{ + + struct xsdt_entry_data + { + std::uint64_t address; + }; + + [[nodiscard]] auto xsdt_entry::address() const noexcept -> table_header * + { + using type = decltype(xsdt_entry_data::address); + + constexpr auto size = sizeof(type); + constexpr auto offset = offsetof(xsdt_entry_data, address); + + auto const data = std::span{m_data.data() + offset, size}; + auto value = type{}; + + kstd::libc::memcpy(&value, data.data(), size); + + return reinterpret_cast(static_cast(value)); + } + + [[nodiscard]] auto xsdt_entry::length() const noexcept -> std::size_t + { + return sizeof(m_data); + } + +} // namespace acpi \ No newline at end of file diff --git a/libs/acpi/acpi/data/xsdt.hpp b/libs/acpi/acpi/data/xsdt.hpp new file mode 100644 index 0000000..965f23c --- /dev/null +++ b/libs/acpi/acpi/data/xsdt.hpp @@ -0,0 +1,42 @@ +#ifndef ACPI_DATA_XSDT_HPP +#define ACPI_DATA_XSDT_HPP + +#include +#include +#include +#include + +#include +#include + +namespace acpi +{ + + template<> + struct table_signature + { + constexpr char static const value[] = "XSDT"; // NOLINT + }; + + template<> + struct table_type> + { + using type = struct xsdt; + }; + + struct xsdt_entry + { + [[nodiscard]] auto address() const noexcept -> table_header *; + [[nodiscard]] auto length() const noexcept -> std::size_t; + + private: + std::array m_data{}; + }; + + struct xsdt : vla_table + { + }; + +} // namespace acpi + +#endif \ No newline at end of file diff --git a/libs/acpi/acpi/data/xsdt.test.cpp b/libs/acpi/acpi/data/xsdt.test.cpp new file mode 100644 index 0000000..7fb564c --- /dev/null +++ b/libs/acpi/acpi/data/xsdt.test.cpp @@ -0,0 +1,46 @@ +#include + +#include +#include +#include +#include + +#include + +SCENARIO("XSDT parsing", "[xsdt]") +{ + GIVEN("The basic compiled XSDT containing 8 table pointers") + { + auto data = acpi::test_data::tables::basic_xsdt(); + + WHEN("parsing the table") + { + auto xsdt = reinterpret_cast(data.data()); + + THEN("the signature is correct") + { + REQUIRE(xsdt->signature() == "XSDT"); + } + + THEN("validate returns true") + { + REQUIRE(xsdt->validate()); + } + + THEN("there are 8 entries in the table") + { + REQUIRE(std::distance(xsdt->begin(), xsdt->end()) == 8); + } + + THEN("the first entry has address 0x10") + { + REQUIRE(xsdt->cbegin()->address() == reinterpret_cast(0x10)); + } + + THEN("the length is sizeof(xsdt) + 8 * sizeof(xsdt_entry)") + { + REQUIRE(xsdt->length().value == sizeof(acpi::xsdt) + 8 * sizeof(acpi::xsdt_entry)); + } + } + } +} diff --git a/libs/acpi/test_data/basic_xsdt.asl b/libs/acpi/test_data/basic_xsdt.asl new file mode 100644 index 0000000..d8589f9 --- /dev/null +++ b/libs/acpi/test_data/basic_xsdt.asl @@ -0,0 +1,26 @@ +/* + * Intel ACPI Component Architecture + * iASL Compiler/Disassembler version 20251212 (64-bit version) + * Copyright (c) 2000 - 2025 Intel Corporation + * + * Template for [XSDT] ACPI Table (static data table) + * Format: [ByteLength] FieldName : HexFieldValue + */ +[0004] Signature : "XSDT" [Extended System Description Table] +[0004] Table Length : 00000064 +[0001] Revision : 01 +[0001] Checksum : 8B +[0006] Oem ID : "INTEL " +[0008] Oem Table ID : "TEMPLATE" +[0004] Oem Revision : 00000001 +[0004] Asl Compiler ID : "INTL" +[0004] Asl Compiler Revision : 20100528 + +[0008] ACPI Table Address 0 : 0000000000000010 +[0008] ACPI Table Address 1 : 0000000000000020 +[0008] ACPI Table Address 2 : 0000000000000030 +[0008] ACPI Table Address 3 : 0000000000000040 +[0008] ACPI Table Address 4 : 0000000000000050 +[0008] ACPI Table Address 5 : 0000000000000060 +[0008] ACPI Table Address 6 : 0000000000000070 +[0008] ACPI Table Address 7 : 0000000000000080 diff --git a/libs/acpi/test_data/tables.S b/libs/acpi/test_data/tables.S index da9a12f..641db6a 100644 --- a/libs/acpi/test_data/tables.S +++ b/libs/acpi/test_data/tables.S @@ -11,6 +11,7 @@ TABLE(basic_madt, "basic_madt.aml") TABLE(basic_rsdt, "basic_rsdt.aml") TABLE(basic_rsdp, "basic_rsdp.aml") +TABLE(basic_xsdt, "basic_xsdt.aml") TABLE(table_header, "table_header.aml") #undef TABLE diff --git a/libs/acpi/test_data/tables.hpp b/libs/acpi/test_data/tables.hpp index dc39b37..e91f1a5 100644 --- a/libs/acpi/test_data/tables.hpp +++ b/libs/acpi/test_data/tables.hpp @@ -18,6 +18,7 @@ namespace acpi::test_data::tables TABLE(basic_madt); TABLE(basic_rsdt); TABLE(basic_rsdp); + TABLE(basic_xsdt); TABLE(table_header); } // namespace acpi::test_data::tables -- cgit v1.2.3 From f9dde928add359a1dff0db402dc1454e72aea633 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 16 Apr 2026 10:15:28 +0200 Subject: ide: enable file grouping --- .vscode/settings.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 1a69637..80844ee 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,6 +12,10 @@ "--clang-tidy", "--header-insertion=iwyu" ], + "explorer.fileNesting.enabled": true, + "explorer.fileNesting.patterns": { + "*.hpp": "${capture}.cpp, ${capture}.test.cpp" + }, "files.associations": { "**/kstd/include/kstd/**": "cpp", }, @@ -76,5 +80,5 @@ "env": "${envObject}", "environment": "${envObjArray}", "sourceFileMap": "${sourceFileMapObj}" - } + }, } \ No newline at end of file -- cgit v1.2.3 From 25d50b311544a21a0d126cf236c23a0912a3eedc Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 19 Apr 2026 09:39:37 +0200 Subject: improve readme of default modules --- arch/x86_64/support/modules/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86_64/support/modules/README.md b/arch/x86_64/support/modules/README.md index fb64767..a04b9af 100644 --- a/arch/x86_64/support/modules/README.md +++ b/arch/x86_64/support/modules/README.md @@ -14,7 +14,7 @@ The ext2_4KB_fs image is intentionally fragmented, as some files were created an ./closed.txt ### 2024.img -(2KB Block size) +(ext2 filesystem with 2KB Block size) . ./lost+found ./sheep_1.txt @@ -24,7 +24,7 @@ The ext2_4KB_fs image is intentionally fragmented, as some files were created an ./stable/pig_3.txt ### 2025.img -(4KB Block size) +(ext2 filesystem 4KB Block size) . ./lost+found ./snake_1.txt -- cgit v1.2.3 From 44b5f84ac7d563be0e2f518db71c273760aba8a3 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 19 Apr 2026 09:40:09 +0200 Subject: add todo to support sparse files --- kernel/src/filesystem/ext2/inode.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index bf3f0cf..07a5525 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -31,7 +31,9 @@ namespace kernel::filesystem::ext2 while (bytes_read < size) { auto const block_number = m_filesystem->map_inode_block_index_to_global_block_number(block_index, m_data); - if (block_number == 0) // TODO BA-FS26 really correct? + // TODO BA-FS26 really correct? sparse files -> 0 means a full block with zeros --> function + // map_inode_block_index_to_global_block_number should return 0 if not possible to find an block + if (block_number == 0) { break; } -- cgit v1.2.3 From 3d4d65e2c1cb869755f9033376545785d37ad5a3 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 19 Apr 2026 09:40:43 +0200 Subject: move m_inodes member into subclass --- kernel/include/kernel/filesystem/devfs/filesystem.hpp | 3 +++ kernel/include/kernel/filesystem/filesystem.hpp | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/include/kernel/filesystem/devfs/filesystem.hpp b/kernel/include/kernel/filesystem/devfs/filesystem.hpp index 3a52403..53bb87d 100644 --- a/kernel/include/kernel/filesystem/devfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/devfs/filesystem.hpp @@ -1,6 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVFS_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_FILESYSTEM_HPP +#include "kernel/filesystem/device_inode.hpp" #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/inode.hpp" @@ -37,6 +38,8 @@ namespace kernel::filesystem::devfs private: auto build_device_inode_table() -> void; + + kstd::vector> m_inodes{}; }; } // namespace kernel::filesystem::devfs diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 099caee..16c07ad 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -71,7 +71,6 @@ namespace kernel::filesystem protected: kstd::shared_ptr m_root_inode{}; kstd::shared_ptr m_backing_inode{}; - kstd::vector> m_inodes{}; }; } // namespace kernel::filesystem -- cgit v1.2.3 From e9837b17eeee5ba69f5067e5bfe3f40aca0a9277 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 19 Apr 2026 09:41:45 +0200 Subject: check that after mount the old file isn't available anymore --- kernel/src/filesystem/vfs.tests.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 12dce84..0a7b6c7 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -178,6 +178,9 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto & vfs = kernel::filesystem::vfs::get(); REQUIRE(vfs.do_mount("/archiv/2024.img", "/information") == kernel::filesystem::vfs::operation_result::success); + auto info_1 = vfs.open("/information/info_1.txt"); + REQUIRE(info_1 == nullptr); + auto sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(sheep_1 != nullptr); -- cgit v1.2.3 From 1356405c9fc6d54cf9da4d5f6fd54c55d51ce66d Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 19 Apr 2026 10:01:52 +0200 Subject: vfs open returns the dentry not the open file description --- kernel/include/kernel/filesystem/vfs.hpp | 6 +++--- kernel/kapi/filesystem.cpp | 6 +++++- kernel/src/filesystem/open_file_description.tests.cpp | 5 +++-- kernel/src/filesystem/vfs.cpp | 10 ++-------- kernel/src/filesystem/vfs.tests.cpp | 16 +++++++++++----- kernel/src/main.cpp | 10 ++++++---- 6 files changed, 30 insertions(+), 23 deletions(-) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 2a9d5f7..678e645 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -52,11 +52,11 @@ namespace kernel::filesystem ~vfs() = default; /** - @brief Open a file by its @p path. This method resolves the path and creates an open file description. + @brief Open a file by its @p path. This method resolves the path and returns the corresponding dentry. @param path The path to the file to open. - @return A shared pointer to the open file description or a null pointer if the file could not be opened. + @return A shared pointer to the dentry or a null pointer if the file could not be opened. */ - auto open(std::string_view path) -> kstd::shared_ptr; + auto open(std::string_view path) -> kstd::shared_ptr; /** @brief Mount a @p source path to a specific @p target path. diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index 30201b7..d3aa617 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -1,8 +1,11 @@ #include "kapi/filesystem.hpp" #include "kernel/filesystem/file_descriptor_table.hpp" +#include "kernel/filesystem/open_file_description.hpp" #include "kernel/filesystem/vfs.hpp" +#include + #include #include #include @@ -29,8 +32,9 @@ namespace kapi::filesystem auto open(std::string_view path) -> int { - if (auto open_file_description = kernel::filesystem::vfs::get().open(path)) + if (auto dentry = kernel::filesystem::vfs::get().open(path)) { + auto open_file_description = kstd::make_shared(dentry->get_inode()); return kernel::filesystem::file_descriptor_table::get().add_file(open_file_description); } diff --git a/kernel/src/filesystem/open_file_description.tests.cpp b/kernel/src/filesystem/open_file_description.tests.cpp index db8eb49..ec35546 100644 --- a/kernel/src/filesystem/open_file_description.tests.cpp +++ b/kernel/src/filesystem/open_file_description.tests.cpp @@ -76,8 +76,9 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module"}, {image_path})); auto & vfs = kernel::filesystem::vfs::get(); - auto ofd = vfs.open("/information/info_1.txt"); - REQUIRE(ofd != nullptr); + auto dentry = vfs.open("/information/info_1.txt"); + REQUIRE(dentry != nullptr); + auto ofd = kstd::make_shared(dentry->get_inode()); THEN("the file can be read and the offset is updated") { diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 394e926..84c8047 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -7,7 +7,6 @@ #include "kernel/filesystem/filesystem.hpp" #include "kernel/filesystem/mount.hpp" #include "kernel/filesystem/mount_table.hpp" -#include "kernel/filesystem/open_file_description.hpp" #include "kernel/filesystem/rootfs/filesystem.hpp" #include @@ -65,14 +64,9 @@ namespace kernel::filesystem return *active_vfs; } - auto vfs::open(std::string_view path) -> kstd::shared_ptr + auto vfs::open(std::string_view path) -> kstd::shared_ptr { - if (auto dentry = resolve_path(path)) - { - return kstd::make_shared(dentry->get_inode()); - } - - return nullptr; + return resolve_path(path); } auto vfs::do_mount(std::string_view source, std::string_view target) -> operation_result diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 0a7b6c7..eba157d 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -1,7 +1,9 @@ #include "kernel/filesystem/vfs.hpp" +#include "kernel/filesystem/open_file_description.hpp" #include "kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp" +#include #include #include @@ -181,11 +183,12 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 == nullptr); - auto sheep_1 = vfs.open("/information/sheep_1.txt"); - REQUIRE(sheep_1 != nullptr); + auto dentry = vfs.open("/information/sheep_1.txt"); + REQUIRE(dentry != nullptr); + auto sheep_1_ofd = kstd::make_shared(dentry->get_inode()); kstd::vector buffer(7); - auto bytes_read = sheep_1->read(buffer.data(), buffer.size()); + auto bytes_read = sheep_1_ofd->read(buffer.data(), buffer.size()); std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "sheep_1"); @@ -207,13 +210,16 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(sheep_1 != nullptr); REQUIRE(goat_1 != nullptr); + auto sheep_1_ofd = kstd::make_shared(sheep_1->get_inode()); + auto goat_1_ofd = kstd::make_shared(goat_1->get_inode()); + kstd::vector sheep_buffer(7); - auto bytes_read = sheep_1->read(sheep_buffer.data(), sheep_buffer.size()); + auto bytes_read = sheep_1_ofd->read(sheep_buffer.data(), sheep_buffer.size()); std::string_view buffer_as_str{reinterpret_cast(sheep_buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "sheep_1"); kstd::vector goat_buffer(6); - bytes_read = goat_1->read(goat_buffer.data(), goat_buffer.size()); + bytes_read = goat_1_ofd->read(goat_buffer.data(), goat_buffer.size()); buffer_as_str = std::string_view{reinterpret_cast(goat_buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "goat_1"); diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index e296bd5..aee3579 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -88,13 +88,14 @@ auto test_file_description_manually() -> void auto test_device_with_vfs() -> void { auto vfs = kernel::filesystem::vfs::get(); - auto ofd = vfs.open("/dev/ram0"); - if (!ofd) + auto dentry = vfs.open("/dev/ram0"); + if (!dentry) { kstd::os::panic("test code failed"); } auto fd_table = kernel::filesystem::file_descriptor_table::get(); + auto ofd = kstd::make_shared(dentry->get_inode()); auto fd = fd_table.add_file(ofd); kstd::vector buffer{2}; auto file = fd_table.get_file(fd); @@ -113,13 +114,14 @@ auto test_file_lookup() -> void auto vfs = kernel::filesystem::vfs::get(); auto read_and_write_file = [&vfs](std::string_view path) { kstd::println("[TEST] Reading and writing file at path: {}", path); - auto ofd = vfs.open(path); - if (!ofd) + auto dentry = vfs.open(path); + if (!dentry) { kstd::os::panic("test code failed"); } kstd::vector buffer{32}; + auto ofd = kstd::make_shared(dentry->get_inode()); auto number_of_read_bytes = ofd->read(buffer.data(), buffer.size()); kstd::println("read bytes: {}", number_of_read_bytes); kstd::println("buffer: {::#04x}", buffer); -- cgit v1.2.3 From 9b4cbc6ba3f8059278a20a4893780717851ce8e4 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 21 Apr 2026 13:06:35 +0200 Subject: build: clean up configuration --- CMakeLists.txt | 15 ++--- CMakePresets.json | 5 +- arch/x86_64/CMakeLists.txt | 37 ++++++++--- kapi/CMakeLists.txt | 16 +++-- kernel/CMakeLists.txt | 147 +++++++++++++++++++++++++++-------------- libs/acpi/CMakeLists.txt | 64 ++++++++++++++---- libs/elf/CMakeLists.txt | 43 +++++++++++- libs/kstd/CMakeLists.txt | 97 ++++++++++++++++++++------- libs/multiboot2/CMakeLists.txt | 49 ++++++++++++-- 9 files changed, 355 insertions(+), 118 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 002ab0c..972422c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,8 @@ set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/Modules") include("ElfTransformations") include("GenerateBootableIso") +include("CTest") +include("EnableCoverage") #[============================================================================[ # External Dependencies @@ -18,7 +20,7 @@ include("GenerateBootableIso") include("FetchContent") -if (NOT CMAKE_CROSSCOMPILING) +if (BUILD_TESTING) FetchContent_Declare( "Catch2" URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" @@ -28,6 +30,9 @@ if (NOT CMAKE_CROSSCOMPILING) ) FetchContent_MakeAvailable("Catch2") + + find_package("Catch2") + include("Catch") endif() #[============================================================================[ @@ -92,13 +97,7 @@ endif() # Build Host Testing #]============================================================================] -if(NOT CMAKE_CROSSCOMPILING) - include("EnableCoverage") - - enable_testing() - find_package("Catch2") - include("Catch") - +if(BUILD_TESTING) if(TARGET "Catch2" AND TARGET "Catch2WithMain") set_target_properties("Catch2" "Catch2WithMain" PROPERTIES C_CLANG_TIDY "" diff --git a/CMakePresets.json b/CMakePresets.json index 2ea281f..0e5dd88 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -16,7 +16,10 @@ "name": "x86_64", "description": "Target x86-64", "inherits": "base", - "toolchainFile": "cmake/Platforms/x86_64.cmake" + "toolchainFile": "cmake/Platforms/x86_64.cmake", + "cacheVariables": { + "BUILD_TESTING": false + } }, { "name": "bht", diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index f182651..9e346ef 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -1,14 +1,9 @@ -add_library("x86_64" OBJECT) -add_library("os::arch" ALIAS "x86_64") - -target_include_directories("x86_64" PUBLIC - "include" -) +#[============================================================================[ +# Library +#]============================================================================] -target_link_libraries("x86_64" PUBLIC - "os::kapi" - "libs::multiboot2" -) +add_library("x86_64" OBJECT) +add_library("arch::lib" ALIAS "x86_64") target_sources("x86_64" PRIVATE # Platform-dependent KAPI implementation @@ -54,7 +49,11 @@ target_sources("x86_64" PRIVATE "src/vga/text/device.cpp" ) -file(GLOB_RECURSE ARCH_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") +file(GLOB_RECURSE ARCH_HEADERS + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + CONFIGURE_DEPENDS + "include/**.hpp" +) target_sources("x86_64" PUBLIC FILE_SET HEADERS @@ -62,7 +61,23 @@ target_sources("x86_64" PUBLIC FILES ${ARCH_HEADERS} ) +target_include_directories("x86_64" PUBLIC + "include" +) + +target_link_libraries("x86_64" PUBLIC + "acpi::lib" + "elf::lib" + "kapi::lib" + "kstd::lib" + "multiboot2::lib" +) + set(KERNEL_LINKER_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/scripts/kernel.ld" PARENT_SCOPE ) + +set_target_properties("x86_64" PROPERTIES + VERIFY_INTERFACE_HEADER_SETS YES +) diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index 99a8725..d15b923 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -1,7 +1,11 @@ add_library("kapi" INTERFACE) -add_library("os::kapi" ALIAS "kapi") +add_library("kapi::lib" ALIAS "kapi") -file(GLOB_RECURSE KAPI_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") +file(GLOB_RECURSE KAPI_HEADERS + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + CONFIGURE_DEPENDS + "include/**.hpp" +) target_sources("kapi" PUBLIC FILE_SET HEADERS @@ -15,9 +19,13 @@ target_include_directories("kapi" INTERFACE ) target_link_libraries("kapi" INTERFACE - "libs::acpi" - "libs::kstd" + "acpi::lib" + "kstd::lib" "gcc" "stdc++" ) + +set_target_properties("kapi" PROPERTIES + VERIFY_INTERFACE_HEADER_SETS YES +) diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index e83e529..d4484cd 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -1,5 +1,12 @@ -add_library("kernel_objs" OBJECT - # Platform-independent KAPI implementation +#[============================================================================[ +# Library +#]============================================================================] + +add_library("kernel_lib" OBJECT) +add_library("kernel::lib" ALIAS "kernel_lib") + +target_sources("kernel_lib" PRIVATE + # Kernel-defined KAPI Implementation "kapi/acpi.cpp" "kapi/boot_modules.cpp" "kapi/cio.cpp" @@ -13,27 +20,31 @@ add_library("kernel_objs" OBJECT "kapi/memory.cpp" "kapi/system.cpp" - # KSTD OS Implementation + # Kernel-defined KSTD Implementation "kstd/os.cpp" "kstd/print.cpp" - # Kernel Implementation + # ACPI Subsystem "src/acpi/manager.cpp" + + # Memory Subsystem "src/memory/bitmap_allocator.cpp" "src/memory/block_list_allocator.cpp" "src/memory/mmio_allocator.cpp" "src/memory.cpp" + + # Device Subsystem "src/devices/block_device.cpp" "src/devices/block_device_utils.cpp" "src/devices/root_bus.cpp" + + # Storage Device Subsystem "src/devices/storage/controller.cpp" "src/devices/storage/management.cpp" "src/devices/storage/ram_disk/controller.cpp" "src/devices/storage/ram_disk/device.cpp" - "src/filesystem/devfs/filesystem.cpp" - "src/filesystem/devfs/inode.cpp" - "src/filesystem/ext2/filesystem.cpp" - "src/filesystem/ext2/inode.cpp" + + # Filesystem Subsystem "src/filesystem/dentry.cpp" "src/filesystem/device_inode.cpp" "src/filesystem/file_descriptor_table.cpp" @@ -42,86 +53,124 @@ add_library("kernel_objs" OBJECT "src/filesystem/mount_table.cpp" "src/filesystem/mount.cpp" "src/filesystem/open_file_description.cpp" - "src/filesystem/rootfs/filesystem.cpp" - "src/filesystem/rootfs/inode.cpp" "src/filesystem/vfs.cpp" -) -target_include_directories("kernel_objs" PUBLIC - "include" -) + # DevFS Filesystem + "src/filesystem/devfs/filesystem.cpp" + "src/filesystem/devfs/inode.cpp" -target_link_libraries("kernel_objs" PUBLIC - "os::kapi" + # ext2 Filesystem + "src/filesystem/ext2/filesystem.cpp" + "src/filesystem/ext2/inode.cpp" + + # Rootfs Filesystem + "src/filesystem/rootfs/filesystem.cpp" + "src/filesystem/rootfs/inode.cpp" ) -file(GLOB_RECURSE KERNEL_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") +file(GLOB_RECURSE KERNEL_HEADERS + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + CONFIGURE_DEPENDS + "include/**.hpp" +) -target_sources("kernel_objs" PUBLIC +target_sources("kernel_lib" PUBLIC FILE_SET HEADERS BASE_DIRS "include" FILES ${KERNEL_HEADERS} ) -add_library("os::kernel" ALIAS "kernel_objs") +target_include_directories("kernel_lib" PUBLIC + "include" +) + +target_link_libraries("kernel_lib" PUBLIC + "kapi::lib" + "kstd::lib" + "acpi::lib" +) + +set_target_properties("kernel_lib" PROPERTIES + VERIFY_INTERFACE_HEADER_SETS YES +) -if(CMAKE_CROSSCOMPILING) - add_executable("kernel" +#[============================================================================[ +# Executable +#]============================================================================] + +if(NOT BUILD_TESTING) + add_executable("kernel") + add_executable(kernel::exe ALIAS "kernel") + + target_sources("kernel" PRIVATE "src/main.cpp" "src/memory/operators.cpp" ) - target_link_libraries("kernel" PRIVATE - "os::arch" - "os::kernel" + target_link_libraries("kernel" PRIVATE + "kernel::lib" + "arch::lib" ) - target_link_options("kernel" PRIVATE + target_link_options("kernel" PRIVATE "-T${KERNEL_LINKER_SCRIPT}" "-no-pie" "-nostdlib" ) - set_property(TARGET "kernel" + 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() + +#[============================================================================[ +# Tests +#]============================================================================] + +if(BUILD_TESTING) + enable_coverage("kernel_lib") - target_generate_bootable_iso("kernel") -else() - enable_coverage("kernel_objs") + add_executable("kernel_tests") + add_executable("kernel::tests" ALIAS "kernel_tests") - add_library("kernel_test_support" OBJECT + target_sources("kernel_tests" PRIVATE + # Platform-defined KAPI "src/test_support/kapi/cpu.cpp" "src/test_support/kapi/cio.cpp" "src/test_support/kapi/interrupts.cpp" "src/test_support/kapi/memory.cpp" + + # Device Subsystem Support "src/test_support/devices/block_device.cpp" "src/test_support/devices/character_device.cpp" + + # Filesystem Subsystem Support "src/test_support/filesystem/inode.cpp" "src/test_support/filesystem/filesystem.cpp" "src/test_support/filesystem/ext2.cpp" "src/test_support/filesystem/storage_boot_module_fixture.cpp" - "src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp" "src/test_support/log_buffer.cpp" + "src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp" + + # I/O Support + "src/test_support/log_buffer.cpp" "src/test_support/output_device.cpp" + + # Memory Support "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" - ) + # Support System Listener + "src/test_support/state_reset_listener.cpp" - add_executable("kernel_tests" # KAPI Shim Tests "kapi/cpu.tests.cpp" "kapi/system.tests.cpp" @@ -154,23 +203,21 @@ else() "src/devices/block_device.tests.cpp" "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" + target_link_libraries("kernel_tests" PRIVATE + "kernel::lib" + "Catch2::Catch2WithMain" ) target_compile_definitions("kernel_tests" PRIVATE KERNEL_TEST_ASSETS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/src/test_support/filesystem/test_assets" ) - set_target_properties("kernel_tests" PROPERTIES + set_target_properties("kernel_tests" PROPERTIES C_CLANG_TIDY "" CXX_CLANG_TIDY "" ) - enable_coverage("kernel_tests") - catch_discover_tests("os::kernel_tests") + enable_coverage("kernel_tests") + catch_discover_tests("kernel_tests") endif() - diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index 55d5b54..b0fc48f 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -6,17 +6,35 @@ project("acpi" LANGUAGES ASM CXX ) -add_library("acpi" STATIC) -add_library("libs::acpi" ALIAS "acpi") +include("CTest") -target_include_directories("acpi" PUBLIC - "${CMAKE_CURRENT_SOURCE_DIR}" -) +#[============================================================================[ +# External Dependencies +#]============================================================================] -file(GLOB_RECURSE ACPI_HEADERS - RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} - "acpi/*.hpp" -) +include("FetchContent") + +if (BUILD_TESTING) + FetchContent_Declare( + "Catch2" + URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" + URL_HASH "SHA256=c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c" + EXCLUDE_FROM_ALL + FIND_PACKAGE_ARGS + ) + + FetchContent_MakeAvailable("Catch2") + + find_package("Catch2") + include("Catch") +endif() + +#[============================================================================[ +# Library +#]============================================================================] + +add_library("acpi" STATIC) +add_library("acpi::lib" ALIAS "acpi") target_sources("acpi" PRIVATE "acpi/common/checksum.cpp" @@ -27,6 +45,11 @@ target_sources("acpi" PRIVATE "acpi/pointers.cpp" ) +file(GLOB_RECURSE ACPI_HEADERS + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + "acpi/*.hpp" +) + target_sources("acpi" PUBLIC FILE_SET HEADERS BASE_DIRS "acpi" @@ -34,11 +57,23 @@ target_sources("acpi" PUBLIC ${ACPI_HEADERS} ) +target_include_directories("acpi" PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" +) + target_link_libraries("acpi" PUBLIC - "libs::kstd" + "kstd::lib" ) -if(NOT CMAKE_CROSSCOMPILING) +set_target_properties("acpi" PROPERTIES + VERIFY_INTERFACE_HEADER_SETS YES +) + +#[============================================================================[ +# Tests +#]============================================================================] + +if(BUILD_TESTING) find_program(IASL_EXE NAMES "iasl" REQUIRED) set(TEST_TABLES @@ -61,7 +96,10 @@ if(NOT CMAKE_CROSSCOMPILING) set_source_files_properties("test_data/tables.S" PROPERTIES OBJECT_DEPENDS "${GENERATED_TABLE_BLOBS}") - add_executable("acpi_tests" + add_executable("acpi_tests") + add_executable("acpi::tests" ALIAS "acpi_tests") + + target_sources("acpi_tests" PRIVATE "acpi/common/table_header.test.cpp" "acpi/data/madt.test.cpp" "acpi/data/rsdt.test.cpp" @@ -77,7 +115,7 @@ if(NOT CMAKE_CROSSCOMPILING) target_link_libraries("acpi_tests" PRIVATE "Catch2::Catch2WithMain" - "libs::acpi" + "acpi::lib" ) set_target_properties("acpi_tests" PROPERTIES diff --git a/libs/elf/CMakeLists.txt b/libs/elf/CMakeLists.txt index f254094..f1f5275 100644 --- a/libs/elf/CMakeLists.txt +++ b/libs/elf/CMakeLists.txt @@ -1,7 +1,46 @@ +cmake_minimum_required(VERSION "3.27.0") + +project("elf" + DESCRIPTION "ELF file format library" + VERSION "0.0.1" + LANGUAGES CXX +) + +include("CTest") + +#[============================================================================[ +# External Dependencies +#]============================================================================] + +include("FetchContent") + +if (BUILD_TESTING) + FetchContent_Declare( + "Catch2" + URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" + URL_HASH "SHA256=c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c" + EXCLUDE_FROM_ALL + FIND_PACKAGE_ARGS + ) + + FetchContent_MakeAvailable("Catch2") + + find_package("Catch2") + include("Catch") +endif() + +#[============================================================================[ +# Library +#]============================================================================] + add_library("elf" INTERFACE) -add_library("libs::elf" ALIAS "elf") +add_library("elf::lib" ALIAS "elf") -file(GLOB_RECURSE ELF_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") +file(GLOB_RECURSE ELF_HEADERS + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + CONFIGURE_DEPENDS + "include/**.hpp" +) target_sources("elf" INTERFACE FILE_SET HEADERS diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index ced3138..ee84ff1 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -1,30 +1,52 @@ -add_library("kstd" STATIC) -add_library("libs::kstd" ALIAS "kstd") +cmake_minimum_required(VERSION "3.27.0") -if(CMAKE_CROSSCOMPILING) - set(KSTD_LIBC_SYMBOLS - "abort" - "strlen" - "memcmp" - "memcpy" - ) +project("kstd" + DESCRIPTION "A kernel STL implementation" + VERSION "0.0.1" + LANGUAGES CXX +) - set(KSTD_LIBC_SOURCES - "src/libc/stdlib.cpp" - "src/libc/string.cpp" - ) +include("CTest") + +#[============================================================================[ +# External Dependencies +#]============================================================================] + +include("FetchContent") + +if (BUILD_TESTING) + FetchContent_Declare( + "Catch2" + URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" + URL_HASH "SHA256=c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c" + EXCLUDE_FROM_ALL + FIND_PACKAGE_ARGS + ) + + FetchContent_MakeAvailable("Catch2") + + find_package("Catch2") + include("Catch") endif() -target_sources("kstd" PRIVATE - ${KSTD_LIBC_SOURCES} +#[============================================================================[ +# Library +#]============================================================================] - "src/os/error.cpp" +add_library("kstd" STATIC) +add_library("kstd::lib" ALIAS "kstd") +target_sources("kstd" PRIVATE + "src/os/error.cpp" "src/mutex.cpp" "src/vformat.cpp" ) -file(GLOB_RECURSE KSTD_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/kstd/*") +file(GLOB_RECURSE KSTD_HEADERS + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + CONFIGURE_DEPENDS + "include/kstd/*" +) target_sources("kstd" PUBLIC FILE_SET HEADERS @@ -37,12 +59,37 @@ target_include_directories("kstd" PUBLIC "include" ) -if(CMAKE_CROSSCOMPILING) +set_target_properties("kstd" PROPERTIES + VERIFY_INTERFACE_HEADER_SETS YES +) + +if(NOT BUILD_TESTING) + target_sources("kstd" PRIVATE + "src/libc/stdlib.cpp" + "src/libc/string.cpp" + ) + + set(KSTD_LIBC_SYMBOLS + "abort" + "strlen" + "memcmp" + "memcpy" + ) + list(TRANSFORM KSTD_LIBC_SYMBOLS PREPEND "-Wl,--undefined=") target_link_options("kstd" INTERFACE ${KSTD_LIBC_SYMBOLS}) -else() - add_executable("kstd_tests" +endif() + +#[============================================================================[ +# Tests +#]============================================================================] + +if(BUILD_TESTING) + add_executable("kstd_tests") + add_executable("kstd::tests" ALIAS "kstd_tests") + + target_sources("kstd_tests" PRIVATE "tests/src/flat_map.cpp" "tests/src/format.cpp" "tests/src/vector.cpp" @@ -57,7 +104,7 @@ else() target_link_libraries("kstd_tests" PRIVATE "Catch2::Catch2WithMain" - "libs::kstd" + "kstd::lib" ) set_target_properties("kstd_tests" PROPERTIES @@ -66,8 +113,10 @@ else() EXCLUDE_FROM_ALL NO ) - enable_coverage("kstd") - enable_coverage("kstd_tests") + if(COMMAND "enable_coverage") + enable_coverage("kstd") + enable_coverage("kstd_tests") + endif() - catch_discover_tests("kstd_tests") + catch_discover_tests("kstd::tests") endif() \ No newline at end of file diff --git a/libs/multiboot2/CMakeLists.txt b/libs/multiboot2/CMakeLists.txt index 7384a3d..b56b0ba 100644 --- a/libs/multiboot2/CMakeLists.txt +++ b/libs/multiboot2/CMakeLists.txt @@ -1,7 +1,46 @@ +cmake_minimum_required(VERSION "3.27.0") + +project("multiboot2" + DESCRIPTION "Multiboot2 bootloader specification library" + VERSION "0.0.1" + LANGUAGES CXX +) + +include("CTest") + +#[============================================================================[ +# External Dependencies +#]============================================================================] + +include("FetchContent") + +if (BUILD_TESTING) + FetchContent_Declare( + "Catch2" + URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" + URL_HASH "SHA256=c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c" + EXCLUDE_FROM_ALL + FIND_PACKAGE_ARGS + ) + + FetchContent_MakeAvailable("Catch2") + + find_package("Catch2") + include("Catch") +endif() + +#[============================================================================[ +# Library +#]============================================================================] + add_library("multiboot2" INTERFACE) -add_library("libs::multiboot2" ALIAS "multiboot2") +add_library("multiboot2::lib" ALIAS "multiboot2") -file(GLOB_RECURSE MULTIBOOT2_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "include/**.hpp") +file(GLOB_RECURSE MULTIBOOT2_HEADERS + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + CONFIGURE_DEPENDS + "include/**.hpp" +) target_sources("multiboot2" INTERFACE FILE_SET HEADERS @@ -15,10 +54,10 @@ target_include_directories("multiboot2" INTERFACE ) target_link_libraries("multiboot2" INTERFACE - "libs::elf" - "libs::kstd" + "elf::lib" + "kstd::lib" ) set_target_properties("multiboot2" PROPERTIES VERIFY_INTERFACE_HEADER_SETS YES -) \ No newline at end of file +) -- cgit v1.2.3 From e3fa6b1adbd7fce3b080d75fd0959949b7d3bef4 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 21 Apr 2026 14:13:55 +0200 Subject: acpi: enable test coverage --- .clang-tidy | 15 ++++++++------- .clangd | 13 ------------- CMakePresets.json | 5 ++++- libs/acpi/CMakeLists.txt | 5 +++++ 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 61ae9c9..26356ea 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -34,7 +34,7 @@ Checks: - cppcoreguidelines-slicing - cppcoreguidelines-use-enum-class - cppcoreguidelines-virtual-class-destructor - + - misc-definitions-in-headers - misc-include-cleaner - misc-no-recursion @@ -55,18 +55,19 @@ Checks: - readability-magic-numbers CheckOptions: + cppcoreguidelines-avoid-do-while.IgnoreMacros: true cppcoreguidelines-avoid-non-const-global-variables.AllowInternalLinkage: true - modernize-use-std-print.ReplacementPrintFunction: 'kstd::print' - modernize-use-std-print.ReplacementPrintlnFunction: 'kstd::println' - modernize-use-std-print.PrintHeader: 'kstd/print' + modernize-use-std-print.ReplacementPrintFunction: "kstd::print" + modernize-use-std-print.ReplacementPrintlnFunction: "kstd::println" + modernize-use-std-print.PrintHeader: "kstd/print" modernize-use-trailing-return-type.TransformLambdas: none - readability-magic-numbers.IgnoredIntegerValues: '1;2;3;4;5;6;7;10;255' + readability-magic-numbers.IgnoredIntegerValues: "1;2;3;4;5;6;7;10;255" readability-magic-numbers.IgnorePowersOf2IntegerValues: true readability-magic-numbers.IgnoreBitFieldsWidths: true readability-magic-numbers.IgnoreTypeAliases: true FormatStyle: file -HeaderFilterRegex: '(.*/kstd/include/.*)|(arch|kernel|kapi)/.*\.hpp' +HeaderFilterRegex: "(.*/kstd/include/.*)|(arch|kernel|kapi)/.*\\.hpp" SystemHeaders: true RemovedArgs: - - -fcondition-coverage \ No newline at end of file + - -fcondition-coverage diff --git a/.clangd b/.clangd index ce14df5..55f84ae 100644 --- a/.clangd +++ b/.clangd @@ -6,16 +6,3 @@ CompileFlags: - -fcondition-coverage Documentation: CommentFormat: Doxygen - ---- -If: - PathMatch: - - "libs/.*/tests/.*\\.cpp" - - "libs/.*/.*\\.test\\.cpp" - - "kernel/.*\\.tests.cpp" -Diagnostics: - ClangTidy: - Remove: "*" -CompileFlags: - Add: - - -Wno-c2y-extensions diff --git a/CMakePresets.json b/CMakePresets.json index 0e5dd88..d5123a2 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -24,7 +24,10 @@ { "name": "bht", "inherits": "base", - "description": "Build-host Testing" + "description": "Build-host Testing", + "cacheVariables": { + "CATCH_CONFIG_NO_COUNTER": true + } } ], "buildPresets": [ diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index b0fc48f..b4d11d9 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -46,6 +46,7 @@ target_sources("acpi" PRIVATE ) file(GLOB_RECURSE ACPI_HEADERS + CONFIGURE_DEPENDS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "acpi/*.hpp" ) @@ -96,6 +97,10 @@ if(BUILD_TESTING) set_source_files_properties("test_data/tables.S" PROPERTIES OBJECT_DEPENDS "${GENERATED_TABLE_BLOBS}") + if(COMMAND "enable_coverage") + enable_coverage("acpi") + endif() + add_executable("acpi_tests") add_executable("acpi::tests" ALIAS "acpi_tests") -- cgit v1.2.3 From e8ce02d63f096147fc54824f0a45c23e3a3ced25 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 21 Apr 2026 14:44:01 +0200 Subject: libs: prepare for clang-tidy --- .clang-tidy | 3 ++- libs/acpi/CMakeLists.txt | 2 -- libs/kstd/CMakeLists.txt | 2 -- libs/kstd/include/kstd/bits/format/vformat.hpp | 2 +- libs/kstd/tests/src/format.cpp | 2 -- libs/kstd/tests/src/observer_ptr.cpp | 3 ++- libs/kstd/tests/src/string.cpp | 8 ++++---- libs/kstd/tests/src/vector.cpp | 18 +++++++++--------- 8 files changed, 18 insertions(+), 22 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 26356ea..7f40ad1 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -61,7 +61,8 @@ CheckOptions: modernize-use-std-print.ReplacementPrintlnFunction: "kstd::println" modernize-use-std-print.PrintHeader: "kstd/print" modernize-use-trailing-return-type.TransformLambdas: none - readability-magic-numbers.IgnoredIntegerValues: "1;2;3;4;5;6;7;10;255" + readability-magic-numbers.IgnoredIntegerValues: "1;2;3;4;5;6;7;10;15;20;25;30;3\ + 5;40;45;50;60;70;80;90;100;200;300;400;255" readability-magic-numbers.IgnorePowersOf2IntegerValues: true readability-magic-numbers.IgnoreBitFieldsWidths: true readability-magic-numbers.IgnoreTypeAliases: true diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index b4d11d9..e73c6b3 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -124,8 +124,6 @@ if(BUILD_TESTING) ) set_target_properties("acpi_tests" PROPERTIES - C_CLANG_TIDY "" - CXX_CLANG_TIDY "" EXCLUDE_FROM_ALL NO ) diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index ee84ff1..2b5ee12 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -108,8 +108,6 @@ if(BUILD_TESTING) ) set_target_properties("kstd_tests" PROPERTIES - C_CLANG_TIDY "" - CXX_CLANG_TIDY "" EXCLUDE_FROM_ALL NO ) diff --git a/libs/kstd/include/kstd/bits/format/vformat.hpp b/libs/kstd/include/kstd/bits/format/vformat.hpp index 4fec7dd..994fae5 100644 --- a/libs/kstd/include/kstd/bits/format/vformat.hpp +++ b/libs/kstd/include/kstd/bits/format/vformat.hpp @@ -45,7 +45,7 @@ namespace kstd : m_output{iterator} {} - auto iterator() const -> Output + [[nodiscard]] auto iterator() const -> Output { return m_output; } diff --git a/libs/kstd/tests/src/format.cpp b/libs/kstd/tests/src/format.cpp index 73c8102..624779a 100644 --- a/libs/kstd/tests/src/format.cpp +++ b/libs/kstd/tests/src/format.cpp @@ -1,6 +1,4 @@ -#include #include -#include #include diff --git a/libs/kstd/tests/src/observer_ptr.cpp b/libs/kstd/tests/src/observer_ptr.cpp index 006ebde..0c94892 100644 --- a/libs/kstd/tests/src/observer_ptr.cpp +++ b/libs/kstd/tests/src/observer_ptr.cpp @@ -3,6 +3,7 @@ #include +#include #include #include #include @@ -313,7 +314,7 @@ SCENARIO("Observer pointer comparisons", "[observer_ptr]") { GIVEN("Observer pointers to elements of an array") { - int arr[] = {1, 2}; + auto arr = std::array{1, 2}; auto ptr1 = kstd::observer_ptr{&arr[0]}; auto ptr2 = kstd::observer_ptr{&arr[1]}; diff --git a/libs/kstd/tests/src/string.cpp b/libs/kstd/tests/src/string.cpp index 43e9a6b..53d7c9a 100644 --- a/libs/kstd/tests/src/string.cpp +++ b/libs/kstd/tests/src/string.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include SCENARIO("String initialization and construction", "[string]") @@ -59,7 +59,7 @@ SCENARIO("String initialization and construction", "[string]") THEN("the string is not empty and has the same size as the C-style string") { REQUIRE(!str.empty()); - REQUIRE(str.size() == strlen(c_str)); + REQUIRE(str.size() == std::strlen(c_str)); } THEN("the string contains the same characters as the C-style string") @@ -266,8 +266,8 @@ SCENARIO("String conversion and comparison", "[string]") { GIVEN("An unsigned integer") { - auto value1 = 12345u; - auto value2 = 0u; + constexpr auto value1 = 12345u; + constexpr auto value2 = 0u; WHEN("converting the unsigned integer to a string") { diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 5faaaff..b826820 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -492,27 +492,27 @@ SCENARIO("Vector modifiers", "[vector]") WHEN("inserting an element") { - auto it = v.insert(v.cbegin(), 42); + auto it = v.insert(v.cbegin(), 40); THEN("the size and capacity increase and the element is inserted") { REQUIRE(v.size() == 1); REQUIRE(v.capacity() >= 1); - REQUIRE(v[0] == 42); + REQUIRE(v[0] == 40); REQUIRE(it == v.begin()); } } WHEN("inserting an lvalue element") { - auto const value = 42; + auto const value = 40; auto it = v.insert(v.cbegin(), value); THEN("the size and capacity increase and the element is inserted") { REQUIRE(v.size() == 1); REQUIRE(v.capacity() >= 1); - REQUIRE(v[0] == 42); + REQUIRE(v[0] == 40); REQUIRE(it == v.begin()); } } @@ -927,7 +927,7 @@ SCENARIO("Vector with non-default-constructible types", "[vector]") WHEN("using emplace_back") { auto v = kstd::vector{}; - v.emplace_back(42); + v.emplace_back(40); THEN("the element is added and the size and capacity increase") { @@ -965,7 +965,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") GIVEN("An empty vector and a move tracker element") { auto v = kstd::vector{}; - auto tracker = kstd::tests::special_member_tracker{42}; + auto tracker = kstd::tests::special_member_tracker{40}; WHEN("push_back is called with the move tracker") { @@ -976,7 +976,7 @@ SCENARIO("Vector modifier move semantics", "[vector]") REQUIRE(v.size() == 1); REQUIRE(v.back().move_constructed_count == 1); REQUIRE(v.back().copy_constructed_count == 0); - REQUIRE(v.back().value == 42); + REQUIRE(v.back().value == 40); } THEN("the original tracker is left in a valid but unspecified state") @@ -992,14 +992,14 @@ SCENARIO("Vector modifier move semantics", "[vector]") WHEN("emplace_back is called with constructor arguments") { - v.emplace_back(42); + v.emplace_back(40); THEN("the element is constructed directly, without moves or copies") { REQUIRE(v.size() == 1); REQUIRE(v.back().move_constructed_count == 0); REQUIRE(v.back().copy_constructed_count == 0); - REQUIRE(v.back().value == 42); + REQUIRE(v.back().value == 40); } } } -- cgit v1.2.3 From 743b41956d5dab989a7e408fd7e69ac0bc5afb8c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 22 Apr 2026 06:56:07 +0200 Subject: kstd: fix vector tests --- libs/kstd/tests/src/vector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index b826820..97460b4 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -932,7 +932,7 @@ SCENARIO("Vector with non-default-constructible types", "[vector]") THEN("the element is added and the size and capacity increase") { REQUIRE(v.size() == 1); - REQUIRE(v.back() == kstd::tests::non_default_constructible{42}); + REQUIRE(v.back() == kstd::tests::non_default_constructible{40}); } } } -- cgit v1.2.3 From 4a3d755ca8d64fe81930b93b1f90411f290976fd Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 22 Apr 2026 09:31:03 +0200 Subject: multiboot2: add test MBI dump --- .gitattributes | 1 + libs/multiboot2/test_data/mbi.bin | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 libs/multiboot2/test_data/mbi.bin diff --git a/.gitattributes b/.gitattributes index 5713ac7..219d96f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ *.img filter=lfs diff=lfs merge=lfs -text +*.bin filter=lfs diff=lfs merge=lfs -text diff --git a/libs/multiboot2/test_data/mbi.bin b/libs/multiboot2/test_data/mbi.bin new file mode 100644 index 0000000..4985526 --- /dev/null +++ b/libs/multiboot2/test_data/mbi.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b67ca95183fde8e7dc8dac2d20af9331122128127926ebe6f8bd80ca9fed7c3 +size 1176 -- cgit v1.2.3 From dec3c3b0387ec477125db21e741bc492d3475db5 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 22 Apr 2026 09:32:22 +0200 Subject: multiboot2: rearrange project structure --- libs/multiboot2/CMakeLists.txt | 2 +- libs/multiboot2/include/multiboot2/constants.hpp | 15 - .../multiboot2/constants/architecture_id.hpp | 22 -- .../multiboot2/constants/information_id.hpp | 86 ------ .../include/multiboot2/constants/memory_type.hpp | 33 --- .../include/multiboot2/constants/tag_id.hpp | 29 -- libs/multiboot2/include/multiboot2/information.hpp | 308 --------------------- .../include/multiboot2/information/data.hpp | 184 ------------ .../include/multiboot2/information/iterator.hpp | 73 ----- .../include/multiboot2/information/tag.hpp | 206 -------------- libs/multiboot2/multiboot2/constants.hpp | 15 + .../multiboot2/constants/architecture_id.hpp | 22 ++ .../multiboot2/constants/information_id.hpp | 86 ++++++ .../multiboot2/constants/memory_type.hpp | 33 +++ libs/multiboot2/multiboot2/constants/tag_id.hpp | 29 ++ libs/multiboot2/multiboot2/information.hpp | 308 +++++++++++++++++++++ libs/multiboot2/multiboot2/information/data.hpp | 184 ++++++++++++ .../multiboot2/multiboot2/information/iterator.hpp | 73 +++++ libs/multiboot2/multiboot2/information/tag.hpp | 206 ++++++++++++++ 19 files changed, 957 insertions(+), 957 deletions(-) delete mode 100644 libs/multiboot2/include/multiboot2/constants.hpp delete mode 100644 libs/multiboot2/include/multiboot2/constants/architecture_id.hpp delete mode 100644 libs/multiboot2/include/multiboot2/constants/information_id.hpp delete mode 100644 libs/multiboot2/include/multiboot2/constants/memory_type.hpp delete mode 100644 libs/multiboot2/include/multiboot2/constants/tag_id.hpp delete mode 100644 libs/multiboot2/include/multiboot2/information.hpp delete mode 100644 libs/multiboot2/include/multiboot2/information/data.hpp delete mode 100644 libs/multiboot2/include/multiboot2/information/iterator.hpp delete mode 100644 libs/multiboot2/include/multiboot2/information/tag.hpp create mode 100644 libs/multiboot2/multiboot2/constants.hpp create mode 100644 libs/multiboot2/multiboot2/constants/architecture_id.hpp create mode 100644 libs/multiboot2/multiboot2/constants/information_id.hpp create mode 100644 libs/multiboot2/multiboot2/constants/memory_type.hpp create mode 100644 libs/multiboot2/multiboot2/constants/tag_id.hpp create mode 100644 libs/multiboot2/multiboot2/information.hpp create mode 100644 libs/multiboot2/multiboot2/information/data.hpp create mode 100644 libs/multiboot2/multiboot2/information/iterator.hpp create mode 100644 libs/multiboot2/multiboot2/information/tag.hpp diff --git a/libs/multiboot2/CMakeLists.txt b/libs/multiboot2/CMakeLists.txt index b56b0ba..da5fb53 100644 --- a/libs/multiboot2/CMakeLists.txt +++ b/libs/multiboot2/CMakeLists.txt @@ -50,7 +50,7 @@ target_sources("multiboot2" INTERFACE ) target_include_directories("multiboot2" INTERFACE - "include" + "${CMAKE_CURRENT_SOURCE_DIR}" ) target_link_libraries("multiboot2" INTERFACE diff --git a/libs/multiboot2/include/multiboot2/constants.hpp b/libs/multiboot2/include/multiboot2/constants.hpp deleted file mode 100644 index 2198210..0000000 --- a/libs/multiboot2/include/multiboot2/constants.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef MULTIBOOT2_CONSTANTS_HPP -#define MULTIBOOT2_CONSTANTS_HPP - -#include "constants/architecture_id.hpp" // IWYU pragma: export - -#include - -namespace multiboot2 -{ - - constexpr auto inline header_magic = std::uint32_t{0xe852'50d6}; - -} // namespace multiboot2 - -#endif diff --git a/libs/multiboot2/include/multiboot2/constants/architecture_id.hpp b/libs/multiboot2/include/multiboot2/constants/architecture_id.hpp deleted file mode 100644 index e13c471..0000000 --- a/libs/multiboot2/include/multiboot2/constants/architecture_id.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef MULTIBOOT2_CONSTANTS_ARCHITECTURE_ID_HPP -#define MULTIBOOT2_CONSTANTS_ARCHITECTURE_ID_HPP - -// IWYU pragma: private, include - -#include - -namespace multiboot2 -{ - - //! The IDs of the supported system architectures. - enum struct architecture_id : std::uint32_t - { - //! 32-bit protected mode i386 - i386 = 0, - //! 32-bit MIPS - mips32 = 4, - }; - -} // namespace multiboot2 - -#endif \ No newline at end of file diff --git a/libs/multiboot2/include/multiboot2/constants/information_id.hpp b/libs/multiboot2/include/multiboot2/constants/information_id.hpp deleted file mode 100644 index 27c5300..0000000 --- a/libs/multiboot2/include/multiboot2/constants/information_id.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef MULTIBOOT2_CONSTANTS_INFORMATION_ID_HPP -#define MULTIBOOT2_CONSTANTS_INFORMATION_ID_HPP - -// IWYU pragma: private, include - -#include - -namespace multiboot2 -{ - - //! The IDs of all Multiboot2 information tags. - //! - //! Each tag provided by the boot loader will be tagged with one of these IDs. A tags data format can thus be deduced - //! from it's ID. - enum struct information_id : std::uint32_t - { - //! Signals final tag for the multiboot2 information structure. - end, - - //! The command line string. - command_line, - - //! The name of the boot loader booting the kernel. - boot_loader_name, - - //! Indicates the boot module which was loaded along the kernel image. - module, - - //! The amount of lower and upper (above 1 MiB) memory. - basic_memory_information, - - //! Indicates which BIOS disk device the hoot loader has loaded the OS image from. - boot_device, - - //! Describes the memory layout of the system with individual areas and their flags. - memory_map, - - //! Includes information to access and utilize the device GPU. - vbe_information, - - //! VBE framebuffer information. - framebuferr_information, - - //! Includes list of all section headers from the loaded ELF kernel. - elf_sections, - - //! Advanced Power Management information. - apm_information, - - //! The pointer to the EFI 32 bit system table. - efi32_system_table_pointer, - - //! The pointer to the EFI 64 bit system table. - efi64_system_table_pointer, - - //! A copy of all System Management BIOS tables. - smbios_tables, - - //! A copy of RSDP as defined per ACPI 1.0 specification. - acpi_rsdp, - - //! A copy of XSDP as defined per ACPI 2.0 or later specification. - acpi_xsdp, - - //! The network information specified specified as per DHCP. - networking_information, - - //! The memory map as provided by the EFI. - efi_memory_map, - - //! Indicates ExitBootServices wasn't called. - boot_services_not_terminated, - - //! EFI 32 bit image handle pointer. - efi32_image_handle_pointer, - - //! EFI 64 bit image handle pointer. - efi64_image_handle_pointer, - - //! The physical image load base address. - image_load_base_address - }; - -} // namespace multiboot2 - -#endif diff --git a/libs/multiboot2/include/multiboot2/constants/memory_type.hpp b/libs/multiboot2/include/multiboot2/constants/memory_type.hpp deleted file mode 100644 index 6be94bd..0000000 --- a/libs/multiboot2/include/multiboot2/constants/memory_type.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef MULTIBOOT2_CONSTANTS_MEMORY_TYPE_HPP -#define MULTIBOOT2_CONSTANTS_MEMORY_TYPE_HPP - -// IWYU pragma: private, include - -#include - -namespace multiboot2 -{ - - //! The types of memory potentially present in the detailed memory map. - enum struct memory_type : std::uint32_t - { - //! The memory is available for general use by the operating system. - available = 1, - - //! The memory is reserved for firmware or other purposes and must not be used for general purposes by the operating - //! system. - reserved, - - //! The memory contains ACPI data and can be reclaimed when ACPI is not in use. - acpi_reclaimable, - - //! The memory is reserved for non-volatile storage. - non_volatile_storage, - - //! The memory range is occupied by defective RAM. - bad_ram, - }; - -} // namespace multiboot2 - -#endif \ No newline at end of file diff --git a/libs/multiboot2/include/multiboot2/constants/tag_id.hpp b/libs/multiboot2/include/multiboot2/constants/tag_id.hpp deleted file mode 100644 index 23d39cc..0000000 --- a/libs/multiboot2/include/multiboot2/constants/tag_id.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef MULTIBOOT2_CONSTANTS_TAG_ID_HPP -#define MULTIBOOT2_CONSTANTS_TAG_ID_HPP - -// IWYU pragma: private, include - -#include - -namespace multiboot2 -{ - - //! The IDs for tags to be used in the image header. - enum struct tag_id : std::uint32_t - { - end, - information_request, - addresses, - entry_address, - console_flags, - preferred_framebuffer_mode, - page_align_modules, - efi_boot_services_supported, - efi32_entry_address, - efi64_entry_address, - relocatable_image, - }; - -} // namespace multiboot2 - -#endif \ No newline at end of file diff --git a/libs/multiboot2/include/multiboot2/information.hpp b/libs/multiboot2/include/multiboot2/information.hpp deleted file mode 100644 index a2ded56..0000000 --- a/libs/multiboot2/include/multiboot2/information.hpp +++ /dev/null @@ -1,308 +0,0 @@ -#ifndef MULTIBOOT2_INFORMATION_HPP -#define MULTIBOOT2_INFORMATION_HPP - -#include "information/data.hpp" // IWYU pragma: export -#include "information/iterator.hpp" // IWYU pragma: export -#include "information/tag.hpp" // IWYU pragma: export - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace multiboot2 -{ - - /** - * @copydoc multiboot2::data::basic_memory - */ - struct basic_memory : tag - { - using tag::tag; - }; - - /** - * @copydoc multiboot2::data::bios_boot_device - */ - struct bios_boot_device : tag - { - using tag::tag; - }; - - /** - * @copydoc multiboot2::data::command_line - */ - struct command_line : vla_tag - { - using vla_tag::vla_tag; - - /** - * @brief The command line string - */ - [[nodiscard]] auto string() const noexcept -> range_type - { - return {data(), size()}; - } - }; - - /** - * @copydoc multiboot2::data::elf_symbols - */ - template - struct elf_symbols : vla_tag const, std::span> - { - using base = vla_tag const, std::span>; - using base::base; - - [[nodiscard]] auto name(elf::section_header const & section) const noexcept -> std::string_view - { - if (!this->string_table_index) - { - std::abort(); - } - - auto string_table = this->begin()[this->string_table_index]; - auto name_offset = section.name_offset; - auto name_array = std::bit_cast(string_table.virtual_load_address); - return name_array + name_offset; - } - }; - - /** - * @copydoc multiboot2::data::loader_name - */ - struct loader_name : vla_tag - { - using vla_tag::vla_tag; - - /** - * @brief The name of the bootloader - */ - [[nodiscard]] auto string() const noexcept -> std::string_view - { - return {data(), size()}; - } - }; - - /** - * @copydoc multiboot2::data::memory_map - */ - struct memory_map : vla_tag - { - using vla_tag::vla_tag; - - /** - * @brief The available memory regions - */ - [[nodiscard]] auto regions() const noexcept -> range_type - { - return {data(), size()}; - } - }; - - /** - * @copydoc multiboot2::data::module - */ - struct module : vla_tag - { - using vla_tag::vla_tag; - - /** - * @brief The module command line or name. - */ - [[nodiscard]] auto string() const noexcept -> std::string_view - { - return {data(), vla_tag::size()}; - } - - [[nodiscard]] constexpr auto size() const noexcept -> kstd::units::bytes - { - return kstd::units::bytes{end_address - start_address}; - } - }; - - struct acpi_rsdp : vla_tag - { - using vla_tag::vla_tag; - - [[nodiscard]] auto pointer() const noexcept -> range_type - { - return {data(), size()}; - } - }; - - struct acpi_xsdp : vla_tag - { - using vla_tag::vla_tag; - - [[nodiscard]] auto pointer() const noexcept -> range_type - { - return {data(), size()}; - } - }; - - struct information_view - { - using iterator = iterator; - using value_type = iterator::value_type; - using pointer = iterator::pointer; - using reference = iterator::reference; - - [[nodiscard]] auto size() const noexcept -> kstd::units::bytes - { - return kstd::units::bytes{m_size}; - } - - // Range access - - [[nodiscard]] auto begin() const noexcept -> iterator - { - return iterator{&m_tags}; - } - - [[nodiscard]] auto end() const noexcept -> iterator - { - return iterator{}; - } - - // Tag access - - template - [[nodiscard]] auto has() const noexcept -> bool - { - return get().has_value(); - } - - [[nodiscard]] auto maybe_basic_memory() const noexcept -> std::optional - { - return get(); - } - - [[nodiscard]] auto basic_memory() const -> basic_memory - { - return maybe_basic_memory().value(); - } - - [[nodiscard]] auto maybe_bios_boot_device() const noexcept -> std::optional - { - return get(); - } - - [[nodiscard]] auto bios_boot_device() const -> bios_boot_device - { - return maybe_bios_boot_device().value(); - } - - [[nodiscard]] auto maybe_command_line() const noexcept -> std::optional - { - return get(); - } - - [[nodiscard]] auto command_line() const -> command_line - { - return maybe_command_line().value(); - } - - template - [[nodiscard]] auto maybe_elf_symbols() const noexcept -> std::optional> - { - return get>().and_then( - [](auto x) -> std::optional> { - if (x.entry_size_in_B == elf::section_header_size) - { - return std::optional{x}; - } - else - { - return std::nullopt; - } - }); - } - - template - [[nodiscard]] auto elf_symbols() const -> elf_symbols - { - return maybe_elf_symbols().value(); - } - - [[nodiscard]] auto maybe_loader_name() const noexcept -> std::optional - { - return get(); - } - - [[nodiscard]] auto loader_name() const -> loader_name - { - return maybe_loader_name().value(); - } - - [[nodiscard]] auto maybe_memory_map() const noexcept -> std::optional - { - return get(); - } - - [[nodiscard]] auto memory_map() const -> memory_map - { - return maybe_memory_map().value(); - } - - [[nodiscard]] auto modules() const noexcept - { - auto filter_modules = [](auto const & tag) { - return tag.information_id() == module::id; - }; - auto transform_module = [](auto const & tag) { - return module{&tag}; - }; - return std::ranges::subrange(begin(), end()) | std::views::filter(filter_modules) | - std::views::transform(transform_module); - } - - [[nodiscard]] auto maybe_acpi_rsdp() const noexcept -> std::optional - { - return get(); - } - - [[nodiscard]] auto acpi_rsdp() const noexcept -> acpi_rsdp - { - return maybe_acpi_rsdp().value(); - } - - [[nodiscard]] auto maybe_acpi_xsdp() const noexcept -> std::optional - { - return get(); - } - - [[nodiscard]] auto acpi_xsdp() const noexcept -> acpi_xsdp - { - return maybe_acpi_xsdp().value(); - } - - private: - template - [[nodiscard]] constexpr auto get() const noexcept -> std::optional - { - if (auto found = std::ranges::find_if(*this, [](auto tag) { return tag.information_id() == Tag::id; }); - found != end()) - { - return Tag{&*found}; - } - return std::nullopt; - } - - uint32_t m_size{}; - uint32_t : 32; - tag_header m_tags{}; - }; - -} // namespace multiboot2 - -#endif diff --git a/libs/multiboot2/include/multiboot2/information/data.hpp b/libs/multiboot2/include/multiboot2/information/data.hpp deleted file mode 100644 index 315eb39..0000000 --- a/libs/multiboot2/include/multiboot2/information/data.hpp +++ /dev/null @@ -1,184 +0,0 @@ -#ifndef MULTIBOOT2_INFORMATION_DATA_HPP -#define MULTIBOOT2_INFORMATION_DATA_HPP - -// IWYU pragma: private, include - -#include "multiboot2/constants/information_id.hpp" -#include "multiboot2/constants/memory_type.hpp" - -#include - -#include - -namespace multiboot2 -{ - //! A simple base mixin providing all data classes with an ID accessor. - template - struct tag_data - { - //! The ID of this data class. - constexpr auto static inline id = Id; - }; - - namespace data - { - - //! Basic system memory information - //! - //! This tag contains the amount of lower memory and upper memory present in the system as detected by the boot - //! loader. - struct basic_memory : tag_data - { - [[nodiscard]] constexpr auto lower() const noexcept -> kstd::units::bytes - { - return kstd::units::bytes{lower_KiB * 1024}; - } - - [[nodiscard]] constexpr auto upper() const noexcept -> kstd::units::bytes - { - return kstd::units::bytes{upper_KiB * 1024}; - } - - //! The amount of lower memory available to the system. - //! - //! Any memory below the 1 MiB address boundary is considered to be lower memory. The maximum possible value for - //! this field is 640 KiB. - std::uint32_t lower_KiB; - - //! The amount of upper memory available to the system. - //! - //! Any memory above the 1 MiB address boundary is considered to be upper memory. The maximum possible value for - //! this field is the address of the first upper memory hole minus 1MiB. - std::uint32_t upper_KiB; - }; - - //! The BIOS disk the image got loaded from - //! - //! This tag is present iff. the image was loaded from a BIOS disk device. - struct bios_boot_device : tag_data - { - //! BIOS device number the image was loaded from, as understood by INT 13h. - //! - //! For example, the first floppy drive will receive id 0x00, while the first hard disk will receive id 0x80. - std::uint32_t device_number; - - //! The partition number of the primary partition the image was loaded from. - std::uint32_t partition_number; - - //! The sub-partition number of the primary partition the image was loaded from. - std::uint32_t sub_partition_number; - }; - - //! The command line supplied to the image during boot. - //! - //! @note This structure is intentionally left blank, since it is of variable size and the contained information - //! starts at the first byte of this structure. - struct command_line : tag_data - { - }; - - //! The ELF sections of the image. - //! - //! @note The actual section header array is not part of this type's definition. The reason being that the size of - //! the array, in terms of entry count, as well as the size and format of each array element is unknown at compile - //! time. The array begins after the last member of this structure. - struct elf_symbols : tag_data - { - [[nodiscard]] constexpr auto entry_size() const noexcept -> kstd::units::bytes - { - return kstd::units::bytes{entry_size_in_B}; - } - - //! The number of section header table entries. - std::uint32_t count; - - //! The size of each section header table entry. - std::uint32_t entry_size_in_B; - - //! The section number of the string table containing the section names. - std::uint32_t string_table_index; - }; - - //! The name of the boot loader that loaded the image. - //! - //! @note This structure is intentionally left blank, since it is of variable size and the contained information - //! starts at the first byte of this structure. - struct loader_name : tag_data - { - }; - - //! The detailed memory map of this system. - struct memory_map : tag_data - { - //! A single region of memory - struct region - { - //! Check if the memory described by this region is available for use. - [[nodiscard]] constexpr auto available() const noexcept - { - return type == memory_type::available; - } - - [[nodiscard]] constexpr auto size() const noexcept -> kstd::units::bytes - { - return kstd::units::bytes{size_in_B}; - } - - //! The physical start address of this region - std::uint64_t base; - - //! The size of this region in bytes. - std::uint64_t size_in_B; - - //! The type of this region. - //! - //! @see multiboot::memory_types - memory_type type; - - //! This field is reserved for padding and use in future extensions. - std::uint32_t : 0; - }; - - [[nodiscard]] constexpr auto entry_size() const noexcept -> kstd::units::bytes - { - return kstd::units::bytes{entry_size_in_B}; - } - - //! The size of each entry present in the map. - //! - //! This field is present to allow for future extension of the entry format. Each entry's size is guaranteed to - //! always be an integer multiple of 8. - std::uint32_t entry_size_in_B; - - //! The version of each entry present in the map - std::uint32_t entry_version; - }; - - //! A module loaded by the bootloader. - //! - //! @note the command line associated with this module is not part of this structure, since it is of variable size - //! and the contained information starts at the end of this structure. - struct module : tag_data - { - //! The physical start address of this module. - std::uint32_t start_address; - - //! The physical end address of this module. - std::uint32_t end_address; - }; - - //! A copy of the ACPI RSDP - struct acpi_rsdp : tag_data - { - }; - - //! A copy of the ACPI XSDP - struct acpi_xsdp : tag_data - { - }; - - } // namespace data - -} // namespace multiboot2 - -#endif diff --git a/libs/multiboot2/include/multiboot2/information/iterator.hpp b/libs/multiboot2/include/multiboot2/information/iterator.hpp deleted file mode 100644 index 62c267d..0000000 --- a/libs/multiboot2/include/multiboot2/information/iterator.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef MULTIBOOT2_INFORMATION_ITERATOR_HPP -#define MULTIBOOT2_INFORMATION_ITERATOR_HPP - -// IWYU pragma: private, include - -#include "multiboot2/constants/information_id.hpp" -#include "tag.hpp" - -#include -#include -#include - -namespace multiboot2 -{ - - struct iterator - { - using iterator_category = std::forward_iterator_tag; - using value_type = tag_header; - using pointer = value_type const *; - using reference = value_type const &; - using difference_type = std::ptrdiff_t; - - constexpr iterator() = default; - - constexpr explicit iterator(tag_header const * offset) - : m_current(offset) - {} - - constexpr auto operator==(iterator const &) const noexcept -> bool = default; - - constexpr auto operator*() const noexcept -> reference - { - return *(m_current.value()); - } - - constexpr auto operator->() const noexcept -> pointer - { - return m_current.value(); - } - - constexpr auto operator++() noexcept -> iterator & - { - if (m_current) - { - if (auto next = m_current.value()->next(); next->information_id() != information_id::end) - { - m_current = next; - } - else - { - m_current.reset(); - } - } - return *this; - } - - constexpr auto operator++(int) noexcept -> iterator - { - auto copy = *this; - ++(*this); - return copy; - } - - private: - std::optional m_current{}; - }; - - static_assert(std::input_or_output_iterator); - -} // namespace multiboot2 - -#endif \ No newline at end of file diff --git a/libs/multiboot2/include/multiboot2/information/tag.hpp b/libs/multiboot2/include/multiboot2/information/tag.hpp deleted file mode 100644 index 8d90790..0000000 --- a/libs/multiboot2/include/multiboot2/information/tag.hpp +++ /dev/null @@ -1,206 +0,0 @@ -#ifndef MULTIBOOT2_INFORMATION_TAG_HPP -#define MULTIBOOT2_INFORMATION_TAG_HPP - -// IWYU pragma: private, include - -#include "multiboot2/constants/information_id.hpp" - -#include -#include -#include -#include - -namespace multiboot2 -{ - - /** - * @brief Header data and functionality shared by all tags. - */ - struct tag_header - { - tag_header() - : m_id{} - , m_size{} - {} - - tag_header(tag_header const * data) - : tag_header{*data} - {} - - [[nodiscard]] auto full_size() const noexcept -> std::size_t - { - return (m_size + 7) & (~7); - } - - [[nodiscard]] auto information_id() const noexcept -> information_id const & - { - return m_id; - } - - [[nodiscard]] auto next() const noexcept -> tag_header const * - { - return std::bit_cast(std::bit_cast(this) + full_size()); - } - - [[nodiscard]] auto unaligned_size() const noexcept -> std::uint32_t - { - return m_size; - } - - private: - enum information_id m_id; - std::uint32_t m_size; - }; - - /** - * @brief A tag containing no variable length array data. - */ - template - struct tag : tag_header, Data - { - tag() - : tag_header{} - , Data{} - {} - - explicit tag(tag_header const * header) - requires(sizeof(tag) > sizeof(tag_header)) - : tag_header{header} - , Data{*std::bit_cast(header + 1)} - {} - - explicit tag(tag_header const * header) - requires(sizeof(tag) == sizeof(tag_header)) - : tag_header{header} - , Data{} - {} - }; - - /** - * @brief A tag containing variable length array data. - */ - template typename Range> - struct vla_tag : tag - { - using range_type = Range; - - using value_type = range_type::value_type; - using reference = range_type::const_reference; - using const_reference = range_type::const_reference; - using pointer = range_type::const_pointer; - using const_pointer = range_type::const_pointer; - - using iterator = range_type::const_iterator; - using const_iterator = range_type::const_iterator; - using reverse_iterator = range_type::const_reverse_iterator; - using const_reverse_iterator = range_type::const_reverse_iterator; - using size_type = range_type::size_type; - using difference_type = range_type::difference_type; - - vla_tag() - : tag{} - , m_vla{} - {} - - explicit vla_tag(tag_header const * header) - : tag{header} - , m_vla{vla_start(header), vla_size(header)} - {} - - [[nodiscard]] auto begin() const noexcept -> const_iterator - { - return m_vla.begin(); - } - - [[nodiscard]] auto end() const noexcept -> const_iterator - { - return m_vla.end(); - } - - [[nodiscard]] auto cbegin() const noexcept -> const_iterator - { - return begin(); - } - - [[nodiscard]] auto cend() const noexcept -> const_iterator - { - return end(); - } - - [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator - { - return m_vla.rbegin(); - } - - [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator - { - return m_vla.rend(); - } - - [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator - { - return rbegin(); - } - - [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator - { - return rend(); - } - - [[nodiscard]] auto front() const noexcept -> const_reference - { - return m_vla.front(); - } - - [[nodiscard]] auto back() const noexcept -> const_reference - { - return m_vla.back(); - } - - [[nodiscard]] auto size() const noexcept -> std::size_t - { - return m_vla.size(); - } - - [[nodiscard]] auto empty() const noexcept -> bool - { - return m_vla.empty(); - } - - [[nodiscard]] auto data() const noexcept -> const_pointer - { - return m_vla.data(); - } - - [[nodiscard]] auto at(std::size_t index) const -> const_reference - { - return m_vla.at(index); - } - - [[nodiscard]] auto operator[](std::size_t index) const noexcept -> const_reference - { - return m_vla[index]; - } - - private: - auto static vla_start(tag_header const * header) noexcept -> VlaData * - { - auto raw = std::bit_cast(header); - auto start = raw + sizeof(tag); - return std::bit_cast(start); - } - - auto static vla_size(tag_header const * header) noexcept -> std::size_t - { - auto size = (header->unaligned_size() - sizeof(tag) - - std::is_same_v> * 1) / - sizeof(VlaData); - return size; - } - - range_type m_vla; - }; - -} // namespace multiboot2 - -#endif \ No newline at end of file diff --git a/libs/multiboot2/multiboot2/constants.hpp b/libs/multiboot2/multiboot2/constants.hpp new file mode 100644 index 0000000..2198210 --- /dev/null +++ b/libs/multiboot2/multiboot2/constants.hpp @@ -0,0 +1,15 @@ +#ifndef MULTIBOOT2_CONSTANTS_HPP +#define MULTIBOOT2_CONSTANTS_HPP + +#include "constants/architecture_id.hpp" // IWYU pragma: export + +#include + +namespace multiboot2 +{ + + constexpr auto inline header_magic = std::uint32_t{0xe852'50d6}; + +} // namespace multiboot2 + +#endif diff --git a/libs/multiboot2/multiboot2/constants/architecture_id.hpp b/libs/multiboot2/multiboot2/constants/architecture_id.hpp new file mode 100644 index 0000000..e13c471 --- /dev/null +++ b/libs/multiboot2/multiboot2/constants/architecture_id.hpp @@ -0,0 +1,22 @@ +#ifndef MULTIBOOT2_CONSTANTS_ARCHITECTURE_ID_HPP +#define MULTIBOOT2_CONSTANTS_ARCHITECTURE_ID_HPP + +// IWYU pragma: private, include + +#include + +namespace multiboot2 +{ + + //! The IDs of the supported system architectures. + enum struct architecture_id : std::uint32_t + { + //! 32-bit protected mode i386 + i386 = 0, + //! 32-bit MIPS + mips32 = 4, + }; + +} // namespace multiboot2 + +#endif \ No newline at end of file diff --git a/libs/multiboot2/multiboot2/constants/information_id.hpp b/libs/multiboot2/multiboot2/constants/information_id.hpp new file mode 100644 index 0000000..27c5300 --- /dev/null +++ b/libs/multiboot2/multiboot2/constants/information_id.hpp @@ -0,0 +1,86 @@ +#ifndef MULTIBOOT2_CONSTANTS_INFORMATION_ID_HPP +#define MULTIBOOT2_CONSTANTS_INFORMATION_ID_HPP + +// IWYU pragma: private, include + +#include + +namespace multiboot2 +{ + + //! The IDs of all Multiboot2 information tags. + //! + //! Each tag provided by the boot loader will be tagged with one of these IDs. A tags data format can thus be deduced + //! from it's ID. + enum struct information_id : std::uint32_t + { + //! Signals final tag for the multiboot2 information structure. + end, + + //! The command line string. + command_line, + + //! The name of the boot loader booting the kernel. + boot_loader_name, + + //! Indicates the boot module which was loaded along the kernel image. + module, + + //! The amount of lower and upper (above 1 MiB) memory. + basic_memory_information, + + //! Indicates which BIOS disk device the hoot loader has loaded the OS image from. + boot_device, + + //! Describes the memory layout of the system with individual areas and their flags. + memory_map, + + //! Includes information to access and utilize the device GPU. + vbe_information, + + //! VBE framebuffer information. + framebuferr_information, + + //! Includes list of all section headers from the loaded ELF kernel. + elf_sections, + + //! Advanced Power Management information. + apm_information, + + //! The pointer to the EFI 32 bit system table. + efi32_system_table_pointer, + + //! The pointer to the EFI 64 bit system table. + efi64_system_table_pointer, + + //! A copy of all System Management BIOS tables. + smbios_tables, + + //! A copy of RSDP as defined per ACPI 1.0 specification. + acpi_rsdp, + + //! A copy of XSDP as defined per ACPI 2.0 or later specification. + acpi_xsdp, + + //! The network information specified specified as per DHCP. + networking_information, + + //! The memory map as provided by the EFI. + efi_memory_map, + + //! Indicates ExitBootServices wasn't called. + boot_services_not_terminated, + + //! EFI 32 bit image handle pointer. + efi32_image_handle_pointer, + + //! EFI 64 bit image handle pointer. + efi64_image_handle_pointer, + + //! The physical image load base address. + image_load_base_address + }; + +} // namespace multiboot2 + +#endif diff --git a/libs/multiboot2/multiboot2/constants/memory_type.hpp b/libs/multiboot2/multiboot2/constants/memory_type.hpp new file mode 100644 index 0000000..6be94bd --- /dev/null +++ b/libs/multiboot2/multiboot2/constants/memory_type.hpp @@ -0,0 +1,33 @@ +#ifndef MULTIBOOT2_CONSTANTS_MEMORY_TYPE_HPP +#define MULTIBOOT2_CONSTANTS_MEMORY_TYPE_HPP + +// IWYU pragma: private, include + +#include + +namespace multiboot2 +{ + + //! The types of memory potentially present in the detailed memory map. + enum struct memory_type : std::uint32_t + { + //! The memory is available for general use by the operating system. + available = 1, + + //! The memory is reserved for firmware or other purposes and must not be used for general purposes by the operating + //! system. + reserved, + + //! The memory contains ACPI data and can be reclaimed when ACPI is not in use. + acpi_reclaimable, + + //! The memory is reserved for non-volatile storage. + non_volatile_storage, + + //! The memory range is occupied by defective RAM. + bad_ram, + }; + +} // namespace multiboot2 + +#endif \ No newline at end of file diff --git a/libs/multiboot2/multiboot2/constants/tag_id.hpp b/libs/multiboot2/multiboot2/constants/tag_id.hpp new file mode 100644 index 0000000..23d39cc --- /dev/null +++ b/libs/multiboot2/multiboot2/constants/tag_id.hpp @@ -0,0 +1,29 @@ +#ifndef MULTIBOOT2_CONSTANTS_TAG_ID_HPP +#define MULTIBOOT2_CONSTANTS_TAG_ID_HPP + +// IWYU pragma: private, include + +#include + +namespace multiboot2 +{ + + //! The IDs for tags to be used in the image header. + enum struct tag_id : std::uint32_t + { + end, + information_request, + addresses, + entry_address, + console_flags, + preferred_framebuffer_mode, + page_align_modules, + efi_boot_services_supported, + efi32_entry_address, + efi64_entry_address, + relocatable_image, + }; + +} // namespace multiboot2 + +#endif \ No newline at end of file diff --git a/libs/multiboot2/multiboot2/information.hpp b/libs/multiboot2/multiboot2/information.hpp new file mode 100644 index 0000000..a2ded56 --- /dev/null +++ b/libs/multiboot2/multiboot2/information.hpp @@ -0,0 +1,308 @@ +#ifndef MULTIBOOT2_INFORMATION_HPP +#define MULTIBOOT2_INFORMATION_HPP + +#include "information/data.hpp" // IWYU pragma: export +#include "information/iterator.hpp" // IWYU pragma: export +#include "information/tag.hpp" // IWYU pragma: export + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace multiboot2 +{ + + /** + * @copydoc multiboot2::data::basic_memory + */ + struct basic_memory : tag + { + using tag::tag; + }; + + /** + * @copydoc multiboot2::data::bios_boot_device + */ + struct bios_boot_device : tag + { + using tag::tag; + }; + + /** + * @copydoc multiboot2::data::command_line + */ + struct command_line : vla_tag + { + using vla_tag::vla_tag; + + /** + * @brief The command line string + */ + [[nodiscard]] auto string() const noexcept -> range_type + { + return {data(), size()}; + } + }; + + /** + * @copydoc multiboot2::data::elf_symbols + */ + template + struct elf_symbols : vla_tag const, std::span> + { + using base = vla_tag const, std::span>; + using base::base; + + [[nodiscard]] auto name(elf::section_header const & section) const noexcept -> std::string_view + { + if (!this->string_table_index) + { + std::abort(); + } + + auto string_table = this->begin()[this->string_table_index]; + auto name_offset = section.name_offset; + auto name_array = std::bit_cast(string_table.virtual_load_address); + return name_array + name_offset; + } + }; + + /** + * @copydoc multiboot2::data::loader_name + */ + struct loader_name : vla_tag + { + using vla_tag::vla_tag; + + /** + * @brief The name of the bootloader + */ + [[nodiscard]] auto string() const noexcept -> std::string_view + { + return {data(), size()}; + } + }; + + /** + * @copydoc multiboot2::data::memory_map + */ + struct memory_map : vla_tag + { + using vla_tag::vla_tag; + + /** + * @brief The available memory regions + */ + [[nodiscard]] auto regions() const noexcept -> range_type + { + return {data(), size()}; + } + }; + + /** + * @copydoc multiboot2::data::module + */ + struct module : vla_tag + { + using vla_tag::vla_tag; + + /** + * @brief The module command line or name. + */ + [[nodiscard]] auto string() const noexcept -> std::string_view + { + return {data(), vla_tag::size()}; + } + + [[nodiscard]] constexpr auto size() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{end_address - start_address}; + } + }; + + struct acpi_rsdp : vla_tag + { + using vla_tag::vla_tag; + + [[nodiscard]] auto pointer() const noexcept -> range_type + { + return {data(), size()}; + } + }; + + struct acpi_xsdp : vla_tag + { + using vla_tag::vla_tag; + + [[nodiscard]] auto pointer() const noexcept -> range_type + { + return {data(), size()}; + } + }; + + struct information_view + { + using iterator = iterator; + using value_type = iterator::value_type; + using pointer = iterator::pointer; + using reference = iterator::reference; + + [[nodiscard]] auto size() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{m_size}; + } + + // Range access + + [[nodiscard]] auto begin() const noexcept -> iterator + { + return iterator{&m_tags}; + } + + [[nodiscard]] auto end() const noexcept -> iterator + { + return iterator{}; + } + + // Tag access + + template + [[nodiscard]] auto has() const noexcept -> bool + { + return get().has_value(); + } + + [[nodiscard]] auto maybe_basic_memory() const noexcept -> std::optional + { + return get(); + } + + [[nodiscard]] auto basic_memory() const -> basic_memory + { + return maybe_basic_memory().value(); + } + + [[nodiscard]] auto maybe_bios_boot_device() const noexcept -> std::optional + { + return get(); + } + + [[nodiscard]] auto bios_boot_device() const -> bios_boot_device + { + return maybe_bios_boot_device().value(); + } + + [[nodiscard]] auto maybe_command_line() const noexcept -> std::optional + { + return get(); + } + + [[nodiscard]] auto command_line() const -> command_line + { + return maybe_command_line().value(); + } + + template + [[nodiscard]] auto maybe_elf_symbols() const noexcept -> std::optional> + { + return get>().and_then( + [](auto x) -> std::optional> { + if (x.entry_size_in_B == elf::section_header_size) + { + return std::optional{x}; + } + else + { + return std::nullopt; + } + }); + } + + template + [[nodiscard]] auto elf_symbols() const -> elf_symbols + { + return maybe_elf_symbols().value(); + } + + [[nodiscard]] auto maybe_loader_name() const noexcept -> std::optional + { + return get(); + } + + [[nodiscard]] auto loader_name() const -> loader_name + { + return maybe_loader_name().value(); + } + + [[nodiscard]] auto maybe_memory_map() const noexcept -> std::optional + { + return get(); + } + + [[nodiscard]] auto memory_map() const -> memory_map + { + return maybe_memory_map().value(); + } + + [[nodiscard]] auto modules() const noexcept + { + auto filter_modules = [](auto const & tag) { + return tag.information_id() == module::id; + }; + auto transform_module = [](auto const & tag) { + return module{&tag}; + }; + return std::ranges::subrange(begin(), end()) | std::views::filter(filter_modules) | + std::views::transform(transform_module); + } + + [[nodiscard]] auto maybe_acpi_rsdp() const noexcept -> std::optional + { + return get(); + } + + [[nodiscard]] auto acpi_rsdp() const noexcept -> acpi_rsdp + { + return maybe_acpi_rsdp().value(); + } + + [[nodiscard]] auto maybe_acpi_xsdp() const noexcept -> std::optional + { + return get(); + } + + [[nodiscard]] auto acpi_xsdp() const noexcept -> acpi_xsdp + { + return maybe_acpi_xsdp().value(); + } + + private: + template + [[nodiscard]] constexpr auto get() const noexcept -> std::optional + { + if (auto found = std::ranges::find_if(*this, [](auto tag) { return tag.information_id() == Tag::id; }); + found != end()) + { + return Tag{&*found}; + } + return std::nullopt; + } + + uint32_t m_size{}; + uint32_t : 32; + tag_header m_tags{}; + }; + +} // namespace multiboot2 + +#endif diff --git a/libs/multiboot2/multiboot2/information/data.hpp b/libs/multiboot2/multiboot2/information/data.hpp new file mode 100644 index 0000000..315eb39 --- /dev/null +++ b/libs/multiboot2/multiboot2/information/data.hpp @@ -0,0 +1,184 @@ +#ifndef MULTIBOOT2_INFORMATION_DATA_HPP +#define MULTIBOOT2_INFORMATION_DATA_HPP + +// IWYU pragma: private, include + +#include "multiboot2/constants/information_id.hpp" +#include "multiboot2/constants/memory_type.hpp" + +#include + +#include + +namespace multiboot2 +{ + //! A simple base mixin providing all data classes with an ID accessor. + template + struct tag_data + { + //! The ID of this data class. + constexpr auto static inline id = Id; + }; + + namespace data + { + + //! Basic system memory information + //! + //! This tag contains the amount of lower memory and upper memory present in the system as detected by the boot + //! loader. + struct basic_memory : tag_data + { + [[nodiscard]] constexpr auto lower() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{lower_KiB * 1024}; + } + + [[nodiscard]] constexpr auto upper() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{upper_KiB * 1024}; + } + + //! The amount of lower memory available to the system. + //! + //! Any memory below the 1 MiB address boundary is considered to be lower memory. The maximum possible value for + //! this field is 640 KiB. + std::uint32_t lower_KiB; + + //! The amount of upper memory available to the system. + //! + //! Any memory above the 1 MiB address boundary is considered to be upper memory. The maximum possible value for + //! this field is the address of the first upper memory hole minus 1MiB. + std::uint32_t upper_KiB; + }; + + //! The BIOS disk the image got loaded from + //! + //! This tag is present iff. the image was loaded from a BIOS disk device. + struct bios_boot_device : tag_data + { + //! BIOS device number the image was loaded from, as understood by INT 13h. + //! + //! For example, the first floppy drive will receive id 0x00, while the first hard disk will receive id 0x80. + std::uint32_t device_number; + + //! The partition number of the primary partition the image was loaded from. + std::uint32_t partition_number; + + //! The sub-partition number of the primary partition the image was loaded from. + std::uint32_t sub_partition_number; + }; + + //! The command line supplied to the image during boot. + //! + //! @note This structure is intentionally left blank, since it is of variable size and the contained information + //! starts at the first byte of this structure. + struct command_line : tag_data + { + }; + + //! The ELF sections of the image. + //! + //! @note The actual section header array is not part of this type's definition. The reason being that the size of + //! the array, in terms of entry count, as well as the size and format of each array element is unknown at compile + //! time. The array begins after the last member of this structure. + struct elf_symbols : tag_data + { + [[nodiscard]] constexpr auto entry_size() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{entry_size_in_B}; + } + + //! The number of section header table entries. + std::uint32_t count; + + //! The size of each section header table entry. + std::uint32_t entry_size_in_B; + + //! The section number of the string table containing the section names. + std::uint32_t string_table_index; + }; + + //! The name of the boot loader that loaded the image. + //! + //! @note This structure is intentionally left blank, since it is of variable size and the contained information + //! starts at the first byte of this structure. + struct loader_name : tag_data + { + }; + + //! The detailed memory map of this system. + struct memory_map : tag_data + { + //! A single region of memory + struct region + { + //! Check if the memory described by this region is available for use. + [[nodiscard]] constexpr auto available() const noexcept + { + return type == memory_type::available; + } + + [[nodiscard]] constexpr auto size() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{size_in_B}; + } + + //! The physical start address of this region + std::uint64_t base; + + //! The size of this region in bytes. + std::uint64_t size_in_B; + + //! The type of this region. + //! + //! @see multiboot::memory_types + memory_type type; + + //! This field is reserved for padding and use in future extensions. + std::uint32_t : 0; + }; + + [[nodiscard]] constexpr auto entry_size() const noexcept -> kstd::units::bytes + { + return kstd::units::bytes{entry_size_in_B}; + } + + //! The size of each entry present in the map. + //! + //! This field is present to allow for future extension of the entry format. Each entry's size is guaranteed to + //! always be an integer multiple of 8. + std::uint32_t entry_size_in_B; + + //! The version of each entry present in the map + std::uint32_t entry_version; + }; + + //! A module loaded by the bootloader. + //! + //! @note the command line associated with this module is not part of this structure, since it is of variable size + //! and the contained information starts at the end of this structure. + struct module : tag_data + { + //! The physical start address of this module. + std::uint32_t start_address; + + //! The physical end address of this module. + std::uint32_t end_address; + }; + + //! A copy of the ACPI RSDP + struct acpi_rsdp : tag_data + { + }; + + //! A copy of the ACPI XSDP + struct acpi_xsdp : tag_data + { + }; + + } // namespace data + +} // namespace multiboot2 + +#endif diff --git a/libs/multiboot2/multiboot2/information/iterator.hpp b/libs/multiboot2/multiboot2/information/iterator.hpp new file mode 100644 index 0000000..62c267d --- /dev/null +++ b/libs/multiboot2/multiboot2/information/iterator.hpp @@ -0,0 +1,73 @@ +#ifndef MULTIBOOT2_INFORMATION_ITERATOR_HPP +#define MULTIBOOT2_INFORMATION_ITERATOR_HPP + +// IWYU pragma: private, include + +#include "multiboot2/constants/information_id.hpp" +#include "tag.hpp" + +#include +#include +#include + +namespace multiboot2 +{ + + struct iterator + { + using iterator_category = std::forward_iterator_tag; + using value_type = tag_header; + using pointer = value_type const *; + using reference = value_type const &; + using difference_type = std::ptrdiff_t; + + constexpr iterator() = default; + + constexpr explicit iterator(tag_header const * offset) + : m_current(offset) + {} + + constexpr auto operator==(iterator const &) const noexcept -> bool = default; + + constexpr auto operator*() const noexcept -> reference + { + return *(m_current.value()); + } + + constexpr auto operator->() const noexcept -> pointer + { + return m_current.value(); + } + + constexpr auto operator++() noexcept -> iterator & + { + if (m_current) + { + if (auto next = m_current.value()->next(); next->information_id() != information_id::end) + { + m_current = next; + } + else + { + m_current.reset(); + } + } + return *this; + } + + constexpr auto operator++(int) noexcept -> iterator + { + auto copy = *this; + ++(*this); + return copy; + } + + private: + std::optional m_current{}; + }; + + static_assert(std::input_or_output_iterator); + +} // namespace multiboot2 + +#endif \ No newline at end of file diff --git a/libs/multiboot2/multiboot2/information/tag.hpp b/libs/multiboot2/multiboot2/information/tag.hpp new file mode 100644 index 0000000..8d90790 --- /dev/null +++ b/libs/multiboot2/multiboot2/information/tag.hpp @@ -0,0 +1,206 @@ +#ifndef MULTIBOOT2_INFORMATION_TAG_HPP +#define MULTIBOOT2_INFORMATION_TAG_HPP + +// IWYU pragma: private, include + +#include "multiboot2/constants/information_id.hpp" + +#include +#include +#include +#include + +namespace multiboot2 +{ + + /** + * @brief Header data and functionality shared by all tags. + */ + struct tag_header + { + tag_header() + : m_id{} + , m_size{} + {} + + tag_header(tag_header const * data) + : tag_header{*data} + {} + + [[nodiscard]] auto full_size() const noexcept -> std::size_t + { + return (m_size + 7) & (~7); + } + + [[nodiscard]] auto information_id() const noexcept -> information_id const & + { + return m_id; + } + + [[nodiscard]] auto next() const noexcept -> tag_header const * + { + return std::bit_cast(std::bit_cast(this) + full_size()); + } + + [[nodiscard]] auto unaligned_size() const noexcept -> std::uint32_t + { + return m_size; + } + + private: + enum information_id m_id; + std::uint32_t m_size; + }; + + /** + * @brief A tag containing no variable length array data. + */ + template + struct tag : tag_header, Data + { + tag() + : tag_header{} + , Data{} + {} + + explicit tag(tag_header const * header) + requires(sizeof(tag) > sizeof(tag_header)) + : tag_header{header} + , Data{*std::bit_cast(header + 1)} + {} + + explicit tag(tag_header const * header) + requires(sizeof(tag) == sizeof(tag_header)) + : tag_header{header} + , Data{} + {} + }; + + /** + * @brief A tag containing variable length array data. + */ + template typename Range> + struct vla_tag : tag + { + using range_type = Range; + + using value_type = range_type::value_type; + using reference = range_type::const_reference; + using const_reference = range_type::const_reference; + using pointer = range_type::const_pointer; + using const_pointer = range_type::const_pointer; + + using iterator = range_type::const_iterator; + using const_iterator = range_type::const_iterator; + using reverse_iterator = range_type::const_reverse_iterator; + using const_reverse_iterator = range_type::const_reverse_iterator; + using size_type = range_type::size_type; + using difference_type = range_type::difference_type; + + vla_tag() + : tag{} + , m_vla{} + {} + + explicit vla_tag(tag_header const * header) + : tag{header} + , m_vla{vla_start(header), vla_size(header)} + {} + + [[nodiscard]] auto begin() const noexcept -> const_iterator + { + return m_vla.begin(); + } + + [[nodiscard]] auto end() const noexcept -> const_iterator + { + return m_vla.end(); + } + + [[nodiscard]] auto cbegin() const noexcept -> const_iterator + { + return begin(); + } + + [[nodiscard]] auto cend() const noexcept -> const_iterator + { + return end(); + } + + [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator + { + return m_vla.rbegin(); + } + + [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator + { + return m_vla.rend(); + } + + [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator + { + return rbegin(); + } + + [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator + { + return rend(); + } + + [[nodiscard]] auto front() const noexcept -> const_reference + { + return m_vla.front(); + } + + [[nodiscard]] auto back() const noexcept -> const_reference + { + return m_vla.back(); + } + + [[nodiscard]] auto size() const noexcept -> std::size_t + { + return m_vla.size(); + } + + [[nodiscard]] auto empty() const noexcept -> bool + { + return m_vla.empty(); + } + + [[nodiscard]] auto data() const noexcept -> const_pointer + { + return m_vla.data(); + } + + [[nodiscard]] auto at(std::size_t index) const -> const_reference + { + return m_vla.at(index); + } + + [[nodiscard]] auto operator[](std::size_t index) const noexcept -> const_reference + { + return m_vla[index]; + } + + private: + auto static vla_start(tag_header const * header) noexcept -> VlaData * + { + auto raw = std::bit_cast(header); + auto start = raw + sizeof(tag); + return std::bit_cast(start); + } + + auto static vla_size(tag_header const * header) noexcept -> std::size_t + { + auto size = (header->unaligned_size() - sizeof(tag) - + std::is_same_v> * 1) / + sizeof(VlaData); + return size; + } + + range_type m_vla; + }; + +} // namespace multiboot2 + +#endif \ No newline at end of file -- cgit v1.2.3 From 2d8fed40bd0d0f8144783b6b344dc79944291b72 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 23 Apr 2026 13:31:17 +0200 Subject: chore: organize includes --- .clang-format | 34 ++++++++++++++++------ .../include/arch/cpu/global_descriptor_table.hpp | 4 +-- arch/x86_64/include/arch/cpu/interrupts.hpp | 4 +-- arch/x86_64/include/arch/cpu/registers.hpp | 4 +-- arch/x86_64/include/arch/debug/qemu_output.hpp | 4 +-- .../include/arch/memory/higher_half_mapper.hpp | 4 +-- arch/x86_64/include/arch/memory/kernel_mapper.hpp | 1 + arch/x86_64/include/arch/vga/text/device.hpp | 4 +-- arch/x86_64/kapi/boot_modules.cpp | 6 ++-- arch/x86_64/kapi/cpu.cpp | 8 ++--- arch/x86_64/kapi/memory.cpp | 6 ++-- arch/x86_64/src/cpu/interrupts.cpp | 6 ++-- arch/x86_64/src/devices/init.cpp | 10 +++---- arch/x86_64/src/devices/legacy_pit.cpp | 4 +-- arch/x86_64/src/memory/higher_half_mapper.cpp | 6 ++-- arch/x86_64/src/memory/kernel_mapper.cpp | 7 +++-- arch/x86_64/src/memory/mmu.cpp | 4 +-- arch/x86_64/src/vga/text/device.cpp | 4 +-- kapi/include/kapi/acpi.hpp | 4 +-- kapi/include/kapi/filesystem.hpp | 1 + kernel/include/kernel/acpi/manager.hpp | 4 +-- .../include/kernel/devices/storage/management.hpp | 3 +- .../kernel/devices/storage/ram_disk/controller.hpp | 4 +-- .../kernel/devices/storage/ram_disk/device.hpp | 4 +-- kernel/include/kernel/filesystem/device_inode.hpp | 4 +-- kernel/include/kernel/memory.hpp | 4 +-- .../include/kernel/memory/block_list_allocator.hpp | 4 +-- kernel/include/kernel/test_support/cio.hpp | 4 +-- kernel/include/kernel/test_support/page_mapper.hpp | 4 +-- .../kernel/test_support/simulated_memory.hpp | 1 + kernel/kapi/acpi.cpp | 6 ++-- kernel/kapi/devices.cpp | 4 +-- kernel/kapi/filesystem.cpp | 1 + kernel/kapi/memory.cpp | 4 +-- kernel/kstd/print.cpp | 3 +- kernel/src/acpi/manager.cpp | 4 +-- kernel/src/devices/block_device_utils.cpp | 4 +-- kernel/src/devices/storage/management.cpp | 6 ++-- kernel/src/devices/storage/ram_disk/controller.cpp | 4 +-- kernel/src/devices/storage/ram_disk/device.cpp | 4 +-- .../src/devices/storage/ram_disk/device.tests.cpp | 5 ++-- kernel/src/filesystem/dentry.cpp | 4 +-- kernel/src/filesystem/devfs/filesystem.cpp | 4 +-- kernel/src/filesystem/device_inode.cpp | 6 ++-- kernel/src/filesystem/ext2/inode.cpp | 4 +-- kernel/src/filesystem/file_descriptor_table.cpp | 4 +-- kernel/src/filesystem/filesystem.cpp | 4 +-- kernel/src/filesystem/mount.cpp | 8 ++--- kernel/src/filesystem/vfs.cpp | 4 +-- kernel/src/main.cpp | 14 ++++----- kernel/src/memory.cpp | 6 ++-- kernel/src/memory/bitmap_allocator.tests.cpp | 1 - kernel/src/memory/block_list_allocator.cpp | 4 +-- kernel/src/memory/block_list_allocator.tests.cpp | 4 +-- kernel/src/memory/operators.cpp | 4 +-- .../filesystem/storage_boot_module_fixture.cpp | 13 +++++---- kernel/src/test_support/kapi/cio.cpp | 4 +-- kernel/src/test_support/kapi/memory.cpp | 4 +-- kernel/src/test_support/output_device.cpp | 4 +-- kernel/src/test_support/simulated_memory.cpp | 3 +- kernel/src/test_support/state_reset_listener.cpp | 8 ++--- libs/acpi/acpi/common/table_header.cpp | 4 +-- libs/acpi/acpi/common/table_header.test.cpp | 4 ++- libs/acpi/acpi/data/madt.cpp | 4 +-- libs/acpi/acpi/data/madt.hpp | 8 ++--- libs/acpi/acpi/data/madt.test.cpp | 6 ++-- libs/acpi/acpi/data/rsdt.cpp | 5 ++-- libs/acpi/acpi/data/rsdt.test.cpp | 9 ++++-- libs/acpi/acpi/data/xsdt.cpp | 5 ++-- libs/acpi/acpi/data/xsdt.test.cpp | 9 ++++-- libs/acpi/acpi/pointers.cpp | 5 ++-- libs/acpi/acpi/pointers.test.cpp | 1 + libs/kstd/include/kstd/bits/format/context.hpp | 3 +- libs/kstd/include/kstd/stack | 1 + libs/kstd/include/kstd/string | 7 ++--- libs/kstd/include/kstd/units | 5 ++-- libs/kstd/tests/src/flat_map.cpp | 1 + libs/kstd/tests/src/vector.cpp | 4 +-- libs/multiboot2/multiboot2/information.hpp | 12 ++++---- libs/multiboot2/multiboot2/information/data.hpp | 4 +-- .../multiboot2/multiboot2/information/iterator.hpp | 2 +- 81 files changed, 224 insertions(+), 188 deletions(-) diff --git a/.clang-format b/.clang-format index e54cb03..57204cc 100644 --- a/.clang-format +++ b/.clang-format @@ -55,23 +55,39 @@ DerivePointerAlignment: "false" FixNamespaceComments: "true" IncludeBlocks: Regroup IncludeCategories: - - Regex: 'kapi/[[:alnum:]._\/]+\.hpp' + # Platform Headers + - Regex: 'arch/[[:alnum:]._\/]+\.hpp' Priority: 100 - - Regex: 'x86_64/[[:alnum:]._\/]+\.hpp' - Priority: 110 - - Regex: '"[[:alnum:]._\/]+\.hpp"' - Priority: 300 - - Regex: '' - Priority: 400 - - Regex: '<[[:alnum:]._\/]+\.hpp>' + # Kernel Headers + - Regex: 'kernel/[[:alnum:]._\/]+\.hpp' + Priority: 125 + # KAPI Headers + - Regex: 'kapi/[[:alnum:]._\/]+\.hpp' + Priority: 150 + # Library Headers + - Regex: 'acpi/[[:alnum:]._\/]+\.hpp' + Priority: 200 + - Regex: 'elf/[[:alnum:]._\/]+\.hpp' + Priority: 210 + - Regex: 'kstd/[[:alnum:]._\/]+(\.hpp)?' + Priority: 220 + - Regex: 'multiboot2/[[:alnum:]._\/]+\.hpp' + Priority: 230 + # Catch2 Headers + - Regex: 'catch2/[[:alnum:]._\/]+\.hpp' Priority: 600 - - Regex: '<[[:alnum:]._]+(?!\.(h|hpp))>' + # Standard Headers + - Regex: '<[[:alnum:]._]+>' Priority: 900 + # Local Headers + - Regex: '".*"' + Priority: 10 IndentCaseLabels: "true" IndentPPDirectives: None IndentWidth: "2" KeepEmptyLinesAtTheStartOfBlocks: "false" Language: Cpp +MainIncludeChar: "Any" MaxEmptyLinesToKeep: "1" NamespaceIndentation: All PointerAlignment: Middle diff --git a/arch/x86_64/include/arch/cpu/global_descriptor_table.hpp b/arch/x86_64/include/arch/cpu/global_descriptor_table.hpp index 402c87d..bc7d328 100644 --- a/arch/x86_64/include/arch/cpu/global_descriptor_table.hpp +++ b/arch/x86_64/include/arch/cpu/global_descriptor_table.hpp @@ -1,10 +1,10 @@ #ifndef TEACHOS_X86_64_GLOBAL_DESCRIPTOR_TABLE_HPP #define TEACHOS_X86_64_GLOBAL_DESCRIPTOR_TABLE_HPP -#include "kapi/memory.hpp" - #include "arch/cpu/segment_descriptor.hpp" +#include "kapi/memory.hpp" + #include #include #include diff --git a/arch/x86_64/include/arch/cpu/interrupts.hpp b/arch/x86_64/include/arch/cpu/interrupts.hpp index b9adb6e..24b72e9 100644 --- a/arch/x86_64/include/arch/cpu/interrupts.hpp +++ b/arch/x86_64/include/arch/cpu/interrupts.hpp @@ -1,10 +1,10 @@ #ifndef TEACHOS_X86_64_CPU_INTERRUPTS_HPP #define TEACHOS_X86_64_CPU_INTERRUPTS_HPP -#include "kapi/memory.hpp" - #include "arch/cpu/segment_selector.hpp" +#include "kapi/memory.hpp" + #include #include #include diff --git a/arch/x86_64/include/arch/cpu/registers.hpp b/arch/x86_64/include/arch/cpu/registers.hpp index d7def10..62206bf 100644 --- a/arch/x86_64/include/arch/cpu/registers.hpp +++ b/arch/x86_64/include/arch/cpu/registers.hpp @@ -1,11 +1,11 @@ #ifndef TEACHOS_X86_64_CPU_REGISTERS_HPP #define TEACHOS_X86_64_CPU_REGISTERS_HPP -#include "kapi/memory.hpp" - #include "arch/cpu/control_register.hpp" // IWYU pragma: export #include "arch/cpu/model_specific_register.hpp" // IWYU pragma: export +#include "kapi/memory.hpp" + namespace arch::cpu { diff --git a/arch/x86_64/include/arch/debug/qemu_output.hpp b/arch/x86_64/include/arch/debug/qemu_output.hpp index e72eb39..f43e147 100644 --- a/arch/x86_64/include/arch/debug/qemu_output.hpp +++ b/arch/x86_64/include/arch/debug/qemu_output.hpp @@ -1,10 +1,10 @@ #ifndef TEACHOS_X86_64_DEBUG_QEMU_OUTPUT_HPP #define TEACHOS_X86_64_DEBUG_QEMU_OUTPUT_HPP -#include "kapi/cio.hpp" - #include "arch/device_io/port_io.hpp" +#include "kapi/cio.hpp" + #include namespace arch::debug diff --git a/arch/x86_64/include/arch/memory/higher_half_mapper.hpp b/arch/x86_64/include/arch/memory/higher_half_mapper.hpp index c8df40c..24bea17 100644 --- a/arch/x86_64/include/arch/memory/higher_half_mapper.hpp +++ b/arch/x86_64/include/arch/memory/higher_half_mapper.hpp @@ -1,10 +1,10 @@ #ifndef TEACHOS_X86_64_HIGHER_HALF_MAPPER_HPP #define TEACHOS_X86_64_HIGHER_HALF_MAPPER_HPP -#include "kapi/memory.hpp" - #include "arch/memory/page_table.hpp" +#include "kapi/memory.hpp" + #include namespace arch::memory diff --git a/arch/x86_64/include/arch/memory/kernel_mapper.hpp b/arch/x86_64/include/arch/memory/kernel_mapper.hpp index 4329f1b..ae593a5 100644 --- a/arch/x86_64/include/arch/memory/kernel_mapper.hpp +++ b/arch/x86_64/include/arch/memory/kernel_mapper.hpp @@ -5,6 +5,7 @@ #include #include + #include #include diff --git a/arch/x86_64/include/arch/vga/text/device.hpp b/arch/x86_64/include/arch/vga/text/device.hpp index 11b3281..d0eb45d 100644 --- a/arch/x86_64/include/arch/vga/text/device.hpp +++ b/arch/x86_64/include/arch/vga/text/device.hpp @@ -3,10 +3,10 @@ // IWYU pragma: private, include "arch/vga/text.hpp" -#include "kapi/cio.hpp" - #include "arch/vga/text/buffer.hpp" +#include "kapi/cio.hpp" + #include namespace arch::vga::text diff --git a/arch/x86_64/kapi/boot_modules.cpp b/arch/x86_64/kapi/boot_modules.cpp index ba01285..1a588cf 100644 --- a/arch/x86_64/kapi/boot_modules.cpp +++ b/arch/x86_64/kapi/boot_modules.cpp @@ -1,14 +1,14 @@ #include "kapi/boot_modules.hpp" +#include "arch/boot/boot.hpp" +#include "arch/boot/ld.hpp" + #include "kapi/boot.hpp" #include "kapi/boot_module/boot_module.hpp" #include "kapi/boot_module/boot_module_registry.hpp" #include "kapi/memory.hpp" #include "kapi/system.hpp" -#include "arch/boot/boot.hpp" -#include "arch/boot/ld.hpp" - #include #include diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index 965998c..baeab4b 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -1,19 +1,19 @@ #include "kapi/cpu.hpp" +#include "arch/cpu/initialization.hpp" +#include "arch/devices/local_apic.hpp" + #include "kapi/acpi.hpp" #include "kapi/devices.hpp" #include "kapi/devices/cpu.hpp" #include "kapi/memory.hpp" #include "kapi/system.hpp" -#include "arch/cpu/initialization.hpp" -#include "arch/devices/local_apic.hpp" +#include #include #include -#include - #include #include #include diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp index 5f12e5c..423913d 100644 --- a/arch/x86_64/kapi/memory.cpp +++ b/arch/x86_64/kapi/memory.cpp @@ -1,8 +1,5 @@ #include "kapi/memory.hpp" -#include "kapi/boot.hpp" -#include "kapi/system.hpp" - #include "arch/boot/boot.hpp" #include "arch/boot/ld.hpp" #include "arch/cpu/registers.hpp" @@ -12,6 +9,9 @@ #include "arch/memory/page_utilities.hpp" #include "arch/memory/region_allocator.hpp" +#include "kapi/boot.hpp" +#include "kapi/system.hpp" + #include #include diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index 907f289..f58b851 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -1,12 +1,12 @@ #include "arch/cpu/interrupts.hpp" +#include "arch/cpu/legacy_pic.hpp" +#include "arch/cpu/segment_selector.hpp" + #include "kapi/cpu.hpp" #include "kapi/interrupts.hpp" #include "kapi/memory.hpp" -#include "arch/cpu/legacy_pic.hpp" -#include "arch/cpu/segment_selector.hpp" - #include #include diff --git a/arch/x86_64/src/devices/init.cpp b/arch/x86_64/src/devices/init.cpp index 7f0faa4..8c96b38 100644 --- a/arch/x86_64/src/devices/init.cpp +++ b/arch/x86_64/src/devices/init.cpp @@ -1,18 +1,18 @@ #include "arch/devices/init.hpp" +#include "arch/boot/boot.hpp" +#include "arch/bus/isa.hpp" +#include "arch/devices/legacy_pit.hpp" + #include "kapi/acpi.hpp" #include "kapi/cpu.hpp" #include "kapi/devices.hpp" -#include "arch/boot/boot.hpp" -#include "arch/bus/isa.hpp" -#include "arch/devices/legacy_pit.hpp" +#include #include #include -#include - #include #include diff --git a/arch/x86_64/src/devices/legacy_pit.cpp b/arch/x86_64/src/devices/legacy_pit.cpp index a8df3c3..44ff499 100644 --- a/arch/x86_64/src/devices/legacy_pit.cpp +++ b/arch/x86_64/src/devices/legacy_pit.cpp @@ -1,11 +1,11 @@ #include "arch/devices/legacy_pit.hpp" +#include "arch/device_io/port_io.hpp" + #include "kapi/devices.hpp" #include "kapi/devices/device.hpp" #include "kapi/interrupts.hpp" -#include "arch/device_io/port_io.hpp" - #include #include diff --git a/arch/x86_64/src/memory/higher_half_mapper.cpp b/arch/x86_64/src/memory/higher_half_mapper.cpp index abb54a3..b0d1995 100644 --- a/arch/x86_64/src/memory/higher_half_mapper.cpp +++ b/arch/x86_64/src/memory/higher_half_mapper.cpp @@ -1,11 +1,11 @@ #include "arch/memory/higher_half_mapper.hpp" -#include "kapi/memory.hpp" -#include "kapi/system.hpp" - #include "arch/memory/page_table.hpp" #include "arch/memory/page_utilities.hpp" +#include "kapi/memory.hpp" +#include "kapi/system.hpp" + #include #include #include diff --git a/arch/x86_64/src/memory/kernel_mapper.cpp b/arch/x86_64/src/memory/kernel_mapper.cpp index ced8a14..46d4dca 100644 --- a/arch/x86_64/src/memory/kernel_mapper.cpp +++ b/arch/x86_64/src/memory/kernel_mapper.cpp @@ -1,15 +1,16 @@ #include "arch/memory/kernel_mapper.hpp" +#include "arch/boot/ld.hpp" + #include "kapi/memory.hpp" #include "kapi/system.hpp" -#include "arch/boot/ld.hpp" +#include +#include #include #include -#include -#include #include #include diff --git a/arch/x86_64/src/memory/mmu.cpp b/arch/x86_64/src/memory/mmu.cpp index ea23278..6d185a0 100644 --- a/arch/x86_64/src/memory/mmu.cpp +++ b/arch/x86_64/src/memory/mmu.cpp @@ -1,9 +1,9 @@ #include "arch/memory/mmu.hpp" -#include "kapi/memory.hpp" - #include "arch/cpu/registers.hpp" +#include "kapi/memory.hpp" + namespace arch::memory { auto tlb_flush(kapi::memory::linear_address address) -> void diff --git a/arch/x86_64/src/vga/text/device.cpp b/arch/x86_64/src/vga/text/device.cpp index dcacd8c..0ecbef9 100644 --- a/arch/x86_64/src/vga/text/device.cpp +++ b/arch/x86_64/src/vga/text/device.cpp @@ -1,10 +1,10 @@ -#include "kapi/cio.hpp" - #include "arch/boot/boot.hpp" #include "arch/boot/ld.hpp" #include "arch/vga/crtc.hpp" #include "arch/vga/text.hpp" +#include "kapi/cio.hpp" + #include #include #include diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp index b607ee0..885fcde 100644 --- a/kapi/include/kapi/acpi.hpp +++ b/kapi/include/kapi/acpi.hpp @@ -1,11 +1,11 @@ #ifndef TEACHOS_KAPI_ACPI_HPP #define TEACHOS_KAPI_ACPI_HPP +#include + #include #include -#include - #include namespace kapi::acpi diff --git a/kapi/include/kapi/filesystem.hpp b/kapi/include/kapi/filesystem.hpp index dba5d54..db77bda 100644 --- a/kapi/include/kapi/filesystem.hpp +++ b/kapi/include/kapi/filesystem.hpp @@ -3,6 +3,7 @@ #include #include + #include namespace kapi::filesystem diff --git a/kernel/include/kernel/acpi/manager.hpp b/kernel/include/kernel/acpi/manager.hpp index 860d609..1e8c1e8 100644 --- a/kernel/include/kernel/acpi/manager.hpp +++ b/kernel/include/kernel/acpi/manager.hpp @@ -1,12 +1,12 @@ #ifndef TEACHOS_KERNEL_ACPI_MANAGER_HPP #define TEACHOS_KERNEL_ACPI_MANAGER_HPP +#include + #include #include #include -#include - #include namespace kernel::acpi diff --git a/kernel/include/kernel/devices/storage/management.hpp b/kernel/include/kernel/devices/storage/management.hpp index 0176ce1..b2f42d1 100644 --- a/kernel/include/kernel/devices/storage/management.hpp +++ b/kernel/include/kernel/devices/storage/management.hpp @@ -1,9 +1,10 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_MANAGEMENT_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_MANAGEMENT_HPP -#include "kapi/devices/device.hpp" #include "kernel/devices/storage/controller.hpp" +#include "kapi/devices/device.hpp" + #include #include diff --git a/kernel/include/kernel/devices/storage/ram_disk/controller.hpp b/kernel/include/kernel/devices/storage/ram_disk/controller.hpp index ad8b29f..febec95 100644 --- a/kernel/include/kernel/devices/storage/ram_disk/controller.hpp +++ b/kernel/include/kernel/devices/storage/ram_disk/controller.hpp @@ -1,10 +1,10 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP -#include "kapi/boot_module/boot_module_registry.hpp" - #include "kernel/devices/storage/controller.hpp" +#include "kapi/boot_module/boot_module_registry.hpp" + namespace kernel::devices::storage::ram_disk { /** diff --git a/kernel/include/kernel/devices/storage/ram_disk/device.hpp b/kernel/include/kernel/devices/storage/ram_disk/device.hpp index e17416e..0ee0f8f 100644 --- a/kernel/include/kernel/devices/storage/ram_disk/device.hpp +++ b/kernel/include/kernel/devices/storage/ram_disk/device.hpp @@ -1,10 +1,10 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP -#include "kapi/boot_module/boot_module.hpp" - #include "kernel/devices/block_device.hpp" +#include "kapi/boot_module/boot_module.hpp" + #include namespace kernel::devices::storage::ram_disk diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index c33be2a..6afe5bc 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -1,10 +1,10 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP -#include "kapi/devices/device.hpp" - #include "kernel/filesystem/inode.hpp" +#include "kapi/devices/device.hpp" + #include #include diff --git a/kernel/include/kernel/memory.hpp b/kernel/include/kernel/memory.hpp index f09c519..568dd24 100644 --- a/kernel/include/kernel/memory.hpp +++ b/kernel/include/kernel/memory.hpp @@ -1,10 +1,10 @@ #ifndef TEACHOS_KERNEL_MEMORY_HPP #define TEACHOS_KERNEL_MEMORY_HPP -#include "kapi/memory.hpp" - #include "kernel/memory/heap_allocator.hpp" // IWYU pragma: export +#include "kapi/memory.hpp" + namespace kernel::memory { diff --git a/kernel/include/kernel/memory/block_list_allocator.hpp b/kernel/include/kernel/memory/block_list_allocator.hpp index de89f3b..c2cb468 100644 --- a/kernel/include/kernel/memory/block_list_allocator.hpp +++ b/kernel/include/kernel/memory/block_list_allocator.hpp @@ -1,10 +1,10 @@ #ifndef TEACHOS_KERNEL_MEMORY_BLOCK_LIST_ALLOCATOR_HPP #define TEACHOS_KERNEL_MEMORY_BLOCK_LIST_ALLOCATOR_HPP -#include "kapi/memory.hpp" - #include "kernel/memory/heap_allocator.hpp" +#include "kapi/memory.hpp" + #include #include diff --git a/kernel/include/kernel/test_support/cio.hpp b/kernel/include/kernel/test_support/cio.hpp index f7bac2d..e990825 100644 --- a/kernel/include/kernel/test_support/cio.hpp +++ b/kernel/include/kernel/test_support/cio.hpp @@ -1,10 +1,10 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_CIO_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_CIO_HPP -#include "kapi/cio.hpp" - #include "kernel/test_support/log_buffer.hpp" +#include "kapi/cio.hpp" + #include namespace kernel::tests::cio diff --git a/kernel/include/kernel/test_support/page_mapper.hpp b/kernel/include/kernel/test_support/page_mapper.hpp index 7ae9a4f..05f4359 100644 --- a/kernel/include/kernel/test_support/page_mapper.hpp +++ b/kernel/include/kernel/test_support/page_mapper.hpp @@ -1,10 +1,10 @@ #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 "kapi/memory.hpp" + #include #include diff --git a/kernel/include/kernel/test_support/simulated_memory.hpp b/kernel/include/kernel/test_support/simulated_memory.hpp index fa78aed..ef4e3ec 100644 --- a/kernel/include/kernel/test_support/simulated_memory.hpp +++ b/kernel/include/kernel/test_support/simulated_memory.hpp @@ -6,6 +6,7 @@ #include #include + #include namespace kernel::tests diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp index 5a2f227..fc9ff31 100644 --- a/kernel/kapi/acpi.cpp +++ b/kernel/kapi/acpi.cpp @@ -1,13 +1,13 @@ #include "kapi/acpi.hpp" -#include "kapi/system.hpp" - #include "kernel/acpi/manager.hpp" -#include +#include "kapi/system.hpp" #include +#include + #include #include #include diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index 2250319..53bab8c 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -1,9 +1,9 @@ #include "kapi/devices.hpp" -#include "kapi/system.hpp" - #include "kernel/devices/root_bus.hpp" +#include "kapi/system.hpp" + #include #include #include diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index d3aa617..eee3b8b 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -8,6 +8,7 @@ #include #include + #include namespace kapi::filesystem diff --git a/kernel/kapi/memory.cpp b/kernel/kapi/memory.cpp index 31cd1f4..66ccb9c 100644 --- a/kernel/kapi/memory.cpp +++ b/kernel/kapi/memory.cpp @@ -1,10 +1,10 @@ #include "kapi/memory.hpp" -#include "kapi/system.hpp" - #include "kernel/memory/bitmap_allocator.hpp" #include "kernel/memory/mmio_allocator.hpp" +#include "kapi/system.hpp" + #include #include diff --git a/kernel/kstd/print.cpp b/kernel/kstd/print.cpp index 7854027..a2e7fe7 100644 --- a/kernel/kstd/print.cpp +++ b/kernel/kstd/print.cpp @@ -1,8 +1,9 @@ +#include + #include "kapi/cio.hpp" #include #include -#include #include #include diff --git a/kernel/src/acpi/manager.cpp b/kernel/src/acpi/manager.cpp index 5876799..f17c9cb 100644 --- a/kernel/src/acpi/manager.cpp +++ b/kernel/src/acpi/manager.cpp @@ -3,11 +3,11 @@ #include "kapi/memory.hpp" #include "kapi/system.hpp" +#include + #include #include -#include - #include #include #include diff --git a/kernel/src/devices/block_device_utils.cpp b/kernel/src/devices/block_device_utils.cpp index 59e9b97..3c77308 100644 --- a/kernel/src/devices/block_device_utils.cpp +++ b/kernel/src/devices/block_device_utils.cpp @@ -1,10 +1,10 @@ #include "kernel/devices/block_device_utils.hpp" +#include "kernel/devices/block_device.hpp" + #include "kapi/devices/device.hpp" #include "kapi/system.hpp" -#include "kernel/devices/block_device.hpp" - #include #include #include diff --git a/kernel/src/devices/storage/management.cpp b/kernel/src/devices/storage/management.cpp index c9fa0a8..8ff1b06 100644 --- a/kernel/src/devices/storage/management.cpp +++ b/kernel/src/devices/storage/management.cpp @@ -1,12 +1,12 @@ #include "kernel/devices/storage/management.hpp" +#include "kernel/devices/storage/controller.hpp" +#include "kernel/devices/storage/ram_disk/controller.hpp" + #include "kapi/boot_modules.hpp" #include "kapi/devices/device.hpp" #include "kapi/system.hpp" -#include "kernel/devices/storage/controller.hpp" -#include "kernel/devices/storage/ram_disk/controller.hpp" - #include #include diff --git a/kernel/src/devices/storage/ram_disk/controller.cpp b/kernel/src/devices/storage/ram_disk/controller.cpp index 040e61f..d230533 100644 --- a/kernel/src/devices/storage/ram_disk/controller.cpp +++ b/kernel/src/devices/storage/ram_disk/controller.cpp @@ -1,9 +1,9 @@ #include "kernel/devices/storage/ram_disk/controller.hpp" -#include "kapi/boot_module/boot_module_registry.hpp" - #include "kernel/devices/storage/ram_disk/device.hpp" +#include "kapi/boot_module/boot_module_registry.hpp" + #include #include diff --git a/kernel/src/devices/storage/ram_disk/device.cpp b/kernel/src/devices/storage/ram_disk/device.cpp index 8fc3b2a..c6a1363 100644 --- a/kernel/src/devices/storage/ram_disk/device.cpp +++ b/kernel/src/devices/storage/ram_disk/device.cpp @@ -1,10 +1,10 @@ #include "kernel/devices/storage/ram_disk/device.hpp" +#include "kernel/devices/block_device.hpp" + #include "kapi/boot_module/boot_module.hpp" #include "kapi/system.hpp" -#include "kernel/devices/block_device.hpp" - #include #include diff --git a/kernel/src/devices/storage/ram_disk/device.tests.cpp b/kernel/src/devices/storage/ram_disk/device.tests.cpp index eacdb04..b475c4b 100644 --- a/kernel/src/devices/storage/ram_disk/device.tests.cpp +++ b/kernel/src/devices/storage/ram_disk/device.tests.cpp @@ -3,10 +3,9 @@ #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 diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index 6591011..af5ceab 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -1,9 +1,9 @@ #include "kernel/filesystem/dentry.hpp" -#include "kapi/system.hpp" - #include "kernel/filesystem/inode.hpp" +#include "kapi/system.hpp" + #include #include diff --git a/kernel/src/filesystem/devfs/filesystem.cpp b/kernel/src/filesystem/devfs/filesystem.cpp index dd60c5d..76b9489 100644 --- a/kernel/src/filesystem/devfs/filesystem.cpp +++ b/kernel/src/filesystem/devfs/filesystem.cpp @@ -1,12 +1,12 @@ #include "kernel/filesystem/devfs/filesystem.hpp" -#include "kapi/devices/device.hpp" - #include "kernel/devices/storage/management.hpp" #include "kernel/filesystem/devfs/inode.hpp" #include "kernel/filesystem/device_inode.hpp" #include "kernel/filesystem/inode.hpp" +#include "kapi/devices/device.hpp" + #include #include diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index 397a0fd..5793bfc 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -1,11 +1,11 @@ #include "kernel/filesystem/device_inode.hpp" -#include "kapi/devices/device.hpp" -#include "kapi/system.hpp" - #include "kernel/devices/block_device_utils.hpp" #include "kernel/filesystem/inode.hpp" +#include "kapi/devices/device.hpp" +#include "kapi/system.hpp" + #include #include diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index 07a5525..6b42db6 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -1,10 +1,10 @@ #include "kernel/filesystem/ext2/inode.hpp" -#include "kapi/system.hpp" - #include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/inode.hpp" +#include "kapi/system.hpp" + #include #include #include diff --git a/kernel/src/filesystem/file_descriptor_table.cpp b/kernel/src/filesystem/file_descriptor_table.cpp index 7569cea..4160429 100644 --- a/kernel/src/filesystem/file_descriptor_table.cpp +++ b/kernel/src/filesystem/file_descriptor_table.cpp @@ -1,9 +1,9 @@ #include "kernel/filesystem/file_descriptor_table.hpp" -#include "kapi/system.hpp" - #include "kernel/filesystem/open_file_description.hpp" +#include "kapi/system.hpp" + #include #include diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index 99e7456..f958660 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -1,10 +1,10 @@ #include "kernel/filesystem/filesystem.hpp" -#include "kapi/system.hpp" - #include "kernel/filesystem/ext2/filesystem.hpp" #include "kernel/filesystem/inode.hpp" +#include "kapi/system.hpp" + #include #include diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index d165385..9c8584b 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -1,10 +1,10 @@ #include "kernel/filesystem/mount.hpp" -#include "kapi/system.hpp" - #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/filesystem.hpp" +#include "kapi/system.hpp" + #include #include @@ -13,8 +13,8 @@ namespace kernel::filesystem { mount::mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, - kstd::shared_ptr const & fs, std::string_view mount_path, - kstd::shared_ptr const & parent_mount) + kstd::shared_ptr const & fs, std::string_view mount_path, + kstd::shared_ptr const & parent_mount) : m_mount_path(mount_path) , m_mount_dentry(mount_dentry) , m_root_dentry(root_dentry) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 84c8047..23ced4c 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -1,7 +1,5 @@ #include "kernel/filesystem/vfs.hpp" -#include "kapi/system.hpp" - #include "kernel/filesystem/dentry.hpp" #include "kernel/filesystem/devfs/filesystem.hpp" #include "kernel/filesystem/filesystem.hpp" @@ -9,6 +7,8 @@ #include "kernel/filesystem/mount_table.hpp" #include "kernel/filesystem/rootfs/filesystem.hpp" +#include "kapi/system.hpp" + #include #include diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index aee3579..52cbe2e 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,3 +1,10 @@ +#include "kernel/devices/storage/management.hpp" +#include "kernel/filesystem/device_inode.hpp" +#include "kernel/filesystem/file_descriptor_table.hpp" +#include "kernel/filesystem/open_file_description.hpp" +#include "kernel/filesystem/vfs.hpp" +#include "kernel/memory.hpp" + #include "kapi/boot_modules.hpp" #include "kapi/cio.hpp" #include "kapi/cpu.hpp" @@ -6,13 +13,6 @@ #include "kapi/memory.hpp" #include "kapi/system.hpp" -#include "kernel/devices/storage/management.hpp" -#include "kernel/filesystem/device_inode.hpp" -#include "kernel/filesystem/file_descriptor_table.hpp" -#include "kernel/filesystem/open_file_description.hpp" -#include "kernel/filesystem/vfs.hpp" -#include "kernel/memory.hpp" - #include #include #include diff --git a/kernel/src/memory.cpp b/kernel/src/memory.cpp index 4a8e203..b5f65fa 100644 --- a/kernel/src/memory.cpp +++ b/kernel/src/memory.cpp @@ -1,11 +1,11 @@ #include "kernel/memory.hpp" -#include "kapi/memory.hpp" -#include "kapi/system.hpp" - #include "kernel/memory/block_list_allocator.hpp" #include "kernel/memory/heap_allocator.hpp" +#include "kapi/memory.hpp" +#include "kapi/system.hpp" + #include #include diff --git a/kernel/src/memory/bitmap_allocator.tests.cpp b/kernel/src/memory/bitmap_allocator.tests.cpp index 56ec0a4..eff6d85 100644 --- a/kernel/src/memory/bitmap_allocator.tests.cpp +++ b/kernel/src/memory/bitmap_allocator.tests.cpp @@ -4,7 +4,6 @@ #include "catch2/matchers/catch_matchers.hpp" #include "catch2/matchers/catch_matchers_range_equals.hpp" - #include #include diff --git a/kernel/src/memory/block_list_allocator.cpp b/kernel/src/memory/block_list_allocator.cpp index fbc5945..5c416fa 100644 --- a/kernel/src/memory/block_list_allocator.cpp +++ b/kernel/src/memory/block_list_allocator.cpp @@ -1,10 +1,10 @@ #include "kernel/memory/block_list_allocator.hpp" +#include "kernel/memory/heap_allocator.hpp" + #include "kapi/memory.hpp" #include "kapi/system.hpp" -#include "kernel/memory/heap_allocator.hpp" - #include #include diff --git a/kernel/src/memory/block_list_allocator.tests.cpp b/kernel/src/memory/block_list_allocator.tests.cpp index 0571441..fccdad5 100644 --- a/kernel/src/memory/block_list_allocator.tests.cpp +++ b/kernel/src/memory/block_list_allocator.tests.cpp @@ -1,9 +1,9 @@ #include "kernel/memory/block_list_allocator.hpp" -#include "kapi/memory.hpp" - #include "kernel/test_support/memory.hpp" +#include "kapi/memory.hpp" + #include #include diff --git a/kernel/src/memory/operators.cpp b/kernel/src/memory/operators.cpp index 25b5f24..dd3b4fa 100644 --- a/kernel/src/memory/operators.cpp +++ b/kernel/src/memory/operators.cpp @@ -1,7 +1,7 @@ -#include "kapi/system.hpp" - #include "kernel/memory.hpp" +#include "kapi/system.hpp" + #include #include diff --git a/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp b/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp index cd8360b..7fa2cb8 100644 --- a/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp +++ b/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp @@ -1,25 +1,26 @@ #include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" -#include "kapi/boot_module/boot_module.hpp" -#include "kapi/boot_modules.hpp" -#include "kapi/memory.hpp" - #include "kernel/devices/storage/management.hpp" #include "kernel/test_support/boot_modules.hpp" #include "kernel/test_support/devices/storage/management.hpp" +#include "kapi/boot_module/boot_module.hpp" +#include "kapi/boot_modules.hpp" +#include "kapi/memory.hpp" + #include #include #include #include #include #include -#include -#include #include #include #include +#include +#include + namespace kernel::tests::filesystem { storage_boot_module_fixture::mapped_image::mapped_image(std::filesystem::path path) diff --git a/kernel/src/test_support/kapi/cio.cpp b/kernel/src/test_support/kapi/cio.cpp index 4a1cd1f..5cdd462 100644 --- a/kernel/src/test_support/kapi/cio.cpp +++ b/kernel/src/test_support/kapi/cio.cpp @@ -1,9 +1,9 @@ #include "kernel/test_support/cio.hpp" -#include - #include "kernel/test_support/log_buffer.hpp" +#include + #include #include #include diff --git a/kernel/src/test_support/kapi/memory.cpp b/kernel/src/test_support/kapi/memory.cpp index e926ba6..3b4a1bd 100644 --- a/kernel/src/test_support/kapi/memory.cpp +++ b/kernel/src/test_support/kapi/memory.cpp @@ -1,10 +1,10 @@ #include "kapi/memory.hpp" -#include - #include "kernel/test_support/bump_frame_allocator.hpp" #include "kernel/test_support/page_mapper.hpp" +#include + #include #include diff --git a/kernel/src/test_support/output_device.cpp b/kernel/src/test_support/output_device.cpp index 83dcbcc..505e385 100644 --- a/kernel/src/test_support/output_device.cpp +++ b/kernel/src/test_support/output_device.cpp @@ -1,8 +1,8 @@ -#include "kapi/cio.hpp" - #include "kernel/test_support/cio.hpp" #include "kernel/test_support/log_buffer.hpp" +#include "kapi/cio.hpp" + #include #include #include diff --git a/kernel/src/test_support/simulated_memory.cpp b/kernel/src/test_support/simulated_memory.cpp index 03dac1d..2a4f15b 100644 --- a/kernel/src/test_support/simulated_memory.cpp +++ b/kernel/src/test_support/simulated_memory.cpp @@ -9,9 +9,10 @@ #include #include #include +#include + #include #include -#include namespace kernel::tests { diff --git a/kernel/src/test_support/state_reset_listener.cpp b/kernel/src/test_support/state_reset_listener.cpp index 9120c89..1967743 100644 --- a/kernel/src/test_support/state_reset_listener.cpp +++ b/kernel/src/test_support/state_reset_listener.cpp @@ -1,7 +1,3 @@ -#include "kapi/cio.hpp" -#include "kapi/cpu.hpp" -#include "kapi/memory.hpp" - #include "kernel/filesystem/file_descriptor_table.hpp" #include "kernel/test_support/boot_modules.hpp" #include "kernel/test_support/cio.hpp" @@ -11,6 +7,10 @@ #include "kernel/test_support/filesystem/vfs.hpp" #include "kernel/test_support/memory.hpp" +#include "kapi/cio.hpp" +#include "kapi/cpu.hpp" +#include "kapi/memory.hpp" + #include #include #include diff --git a/libs/acpi/acpi/common/table_header.cpp b/libs/acpi/acpi/common/table_header.cpp index c69ff5d..6a7a4dc 100644 --- a/libs/acpi/acpi/common/table_header.cpp +++ b/libs/acpi/acpi/common/table_header.cpp @@ -1,8 +1,8 @@ +#include + #include #include -#include - #include #include #include diff --git a/libs/acpi/acpi/common/table_header.test.cpp b/libs/acpi/acpi/common/table_header.test.cpp index d6976b3..53cdb26 100644 --- a/libs/acpi/acpi/common/table_header.test.cpp +++ b/libs/acpi/acpi/common/table_header.test.cpp @@ -1,7 +1,9 @@ +#include + #include -#include #include + #include SCENARIO("Common table header parsing", "[common_table_header]") diff --git a/libs/acpi/acpi/data/madt.cpp b/libs/acpi/acpi/data/madt.cpp index a8d4741..1a8b6d3 100644 --- a/libs/acpi/acpi/data/madt.cpp +++ b/libs/acpi/acpi/data/madt.cpp @@ -1,7 +1,7 @@ -#include - #include +#include + #include #include #include diff --git a/libs/acpi/acpi/data/madt.hpp b/libs/acpi/acpi/data/madt.hpp index 8307826..b76daa4 100644 --- a/libs/acpi/acpi/data/madt.hpp +++ b/libs/acpi/acpi/data/madt.hpp @@ -3,14 +3,14 @@ // IWYU pragma: private, include -#include -#include -#include - #include #include #include +#include +#include +#include + #include #include #include diff --git a/libs/acpi/acpi/data/madt.test.cpp b/libs/acpi/acpi/data/madt.test.cpp index 6795499..5d3b366 100644 --- a/libs/acpi/acpi/data/madt.test.cpp +++ b/libs/acpi/acpi/data/madt.test.cpp @@ -1,11 +1,13 @@ +#include + #include -#include #include -#include #include +#include + SCENARIO("MADT parsing", "[madt]") { GIVEN("The basic compiled MADT containing a single LAPIC entry and the default x86 LAPIC address") diff --git a/libs/acpi/acpi/data/rsdt.cpp b/libs/acpi/acpi/data/rsdt.cpp index fe108c7..80e209d 100644 --- a/libs/acpi/acpi/data/rsdt.cpp +++ b/libs/acpi/acpi/data/rsdt.cpp @@ -1,7 +1,8 @@ -#include +#include #include -#include + +#include #include #include diff --git a/libs/acpi/acpi/data/rsdt.test.cpp b/libs/acpi/acpi/data/rsdt.test.cpp index 937dce0..a6dd416 100644 --- a/libs/acpi/acpi/data/rsdt.test.cpp +++ b/libs/acpi/acpi/data/rsdt.test.cpp @@ -1,12 +1,15 @@ -#include +#include #include -#include + +#include + #include -#include #include +#include + SCENARIO("RSDT parsing", "[rsdt]") { GIVEN("The basic compiled RSDT containing 8 table pointers") diff --git a/libs/acpi/acpi/data/xsdt.cpp b/libs/acpi/acpi/data/xsdt.cpp index b4202b8..b77aeab 100644 --- a/libs/acpi/acpi/data/xsdt.cpp +++ b/libs/acpi/acpi/data/xsdt.cpp @@ -1,7 +1,8 @@ -#include +#include #include -#include + +#include #include #include diff --git a/libs/acpi/acpi/data/xsdt.test.cpp b/libs/acpi/acpi/data/xsdt.test.cpp index 7fb564c..cc18a66 100644 --- a/libs/acpi/acpi/data/xsdt.test.cpp +++ b/libs/acpi/acpi/data/xsdt.test.cpp @@ -1,12 +1,15 @@ -#include +#include #include -#include + +#include + #include -#include #include +#include + SCENARIO("XSDT parsing", "[xsdt]") { GIVEN("The basic compiled XSDT containing 8 table pointers") diff --git a/libs/acpi/acpi/pointers.cpp b/libs/acpi/acpi/pointers.cpp index 45a42ce..2ac8d31 100644 --- a/libs/acpi/acpi/pointers.cpp +++ b/libs/acpi/acpi/pointers.cpp @@ -1,7 +1,8 @@ -#include +#include #include -#include + +#include #include #include diff --git a/libs/acpi/acpi/pointers.test.cpp b/libs/acpi/acpi/pointers.test.cpp index 06ce1a4..d7b700d 100644 --- a/libs/acpi/acpi/pointers.test.cpp +++ b/libs/acpi/acpi/pointers.test.cpp @@ -1,4 +1,5 @@ #include + #include #include diff --git a/libs/kstd/include/kstd/bits/format/context.hpp b/libs/kstd/include/kstd/bits/format/context.hpp index 7f392a0..1883fc8 100644 --- a/libs/kstd/include/kstd/bits/format/context.hpp +++ b/libs/kstd/include/kstd/bits/format/context.hpp @@ -3,9 +3,8 @@ // IWYU pragma: private, include -#include "arg.hpp" +#include "kstd/bits/format/arg.hpp" #include "kstd/bits/format/output_buffer.hpp" - #include #include diff --git a/libs/kstd/include/kstd/stack b/libs/kstd/include/kstd/stack index 9750376..77e6bfd 100644 --- a/libs/kstd/include/kstd/stack +++ b/libs/kstd/include/kstd/stack @@ -2,6 +2,7 @@ #define KSTD_STACK_HPP #include "kstd/vector" + #include #include diff --git a/libs/kstd/include/kstd/string b/libs/kstd/include/kstd/string index 58c4a08..e228a04 100644 --- a/libs/kstd/include/kstd/string +++ b/libs/kstd/include/kstd/string @@ -1,10 +1,9 @@ #ifndef KSTD_STRING_HPP #define KSTD_STRING_HPP -#include "kstd/bits/format/context.hpp" -#include "kstd/bits/format/formatter.hpp" -#include "kstd/bits/format/formatter/string_view.hpp" - +#include +#include +#include #include #include #include diff --git a/libs/kstd/include/kstd/units b/libs/kstd/include/kstd/units index bc7e1b9..df5eb37 100644 --- a/libs/kstd/include/kstd/units +++ b/libs/kstd/include/kstd/units @@ -15,9 +15,8 @@ namespace kstd using value_type = ValueType; constexpr basic_unit() noexcept - : value{} - { - } + : value{} + {} explicit constexpr basic_unit(value_type value) noexcept : value{value} diff --git a/libs/kstd/tests/src/flat_map.cpp b/libs/kstd/tests/src/flat_map.cpp index eb599af..2b793d9 100644 --- a/libs/kstd/tests/src/flat_map.cpp +++ b/libs/kstd/tests/src/flat_map.cpp @@ -1,4 +1,5 @@ #include + #include #include diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 97460b4..415ca8e 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -1,8 +1,8 @@ -#include "kstd/tests/os_panic.hpp" +#include +#include "kstd/tests/os_panic.hpp" #include #include -#include #include diff --git a/libs/multiboot2/multiboot2/information.hpp b/libs/multiboot2/multiboot2/information.hpp index a2ded56..5f75fc8 100644 --- a/libs/multiboot2/multiboot2/information.hpp +++ b/libs/multiboot2/multiboot2/information.hpp @@ -1,14 +1,14 @@ #ifndef MULTIBOOT2_INFORMATION_HPP #define MULTIBOOT2_INFORMATION_HPP -#include "information/data.hpp" // IWYU pragma: export -#include "information/iterator.hpp" // IWYU pragma: export -#include "information/tag.hpp" // IWYU pragma: export +#include +#include #include -#include -#include +#include "multiboot2/information/data.hpp" // IWYU pragma: export +#include "multiboot2/information/iterator.hpp" // IWYU pragma: export +#include "multiboot2/information/tag.hpp" // IWYU pragma: export #include #include @@ -129,7 +129,7 @@ namespace multiboot2 return kstd::units::bytes{end_address - start_address}; } }; - + struct acpi_rsdp : vla_tag { using vla_tag::vla_tag; diff --git a/libs/multiboot2/multiboot2/information/data.hpp b/libs/multiboot2/multiboot2/information/data.hpp index 315eb39..531e2f3 100644 --- a/libs/multiboot2/multiboot2/information/data.hpp +++ b/libs/multiboot2/multiboot2/information/data.hpp @@ -3,11 +3,11 @@ // IWYU pragma: private, include +#include + #include "multiboot2/constants/information_id.hpp" #include "multiboot2/constants/memory_type.hpp" -#include - #include namespace multiboot2 diff --git a/libs/multiboot2/multiboot2/information/iterator.hpp b/libs/multiboot2/multiboot2/information/iterator.hpp index 62c267d..ec0f4a2 100644 --- a/libs/multiboot2/multiboot2/information/iterator.hpp +++ b/libs/multiboot2/multiboot2/information/iterator.hpp @@ -4,7 +4,7 @@ // IWYU pragma: private, include #include "multiboot2/constants/information_id.hpp" -#include "tag.hpp" +#include "multiboot2/information/tag.hpp" #include #include -- cgit v1.2.3 From f6f10575f75ac23d06e1d94f7861611503daa7af Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 23 Apr 2026 14:03:28 +0200 Subject: chore: banish relative includes --- arch/x86_64/include/arch/boot/boot.hpp | 2 +- arch/x86_64/include/arch/bus/isa.hpp | 2 +- arch/x86_64/include/arch/cpu/control_register.hpp | 4 +-- .../include/arch/cpu/global_descriptor_table.hpp | 4 +-- arch/x86_64/include/arch/cpu/interrupts.hpp | 4 +-- arch/x86_64/include/arch/cpu/legacy_pic.hpp | 2 +- .../include/arch/cpu/model_specific_register.hpp | 2 +- arch/x86_64/include/arch/cpu/registers.hpp | 6 ++-- arch/x86_64/include/arch/debug/qemu_output.hpp | 4 +-- arch/x86_64/include/arch/devices/legacy_pit.hpp | 4 +-- arch/x86_64/include/arch/devices/local_apic.hpp | 4 +-- .../include/arch/memory/higher_half_mapper.hpp | 4 +-- arch/x86_64/include/arch/memory/kernel_mapper.hpp | 2 +- arch/x86_64/include/arch/memory/mmu.hpp | 2 +- arch/x86_64/include/arch/memory/page_table.hpp | 2 +- arch/x86_64/include/arch/memory/page_utilities.hpp | 2 +- .../include/arch/memory/region_allocator.hpp | 6 ++-- arch/x86_64/include/arch/vga/crtc.hpp | 2 +- arch/x86_64/include/arch/vga/text.hpp | 10 +++---- arch/x86_64/include/arch/vga/text/attribute.hpp | 6 ++-- arch/x86_64/include/arch/vga/text/buffer.hpp | 4 +-- arch/x86_64/include/arch/vga/text/color.hpp | 2 +- .../include/arch/vga/text/common_attributes.hpp | 8 ++--- arch/x86_64/include/arch/vga/text/device.hpp | 6 ++-- arch/x86_64/include/arch/vga/text/flags.hpp | 2 +- arch/x86_64/kapi/boot_modules.cpp | 16 +++++----- arch/x86_64/kapi/cio.cpp | 6 ++-- arch/x86_64/kapi/cpu.cpp | 16 +++++----- arch/x86_64/kapi/devices.cpp | 4 +-- arch/x86_64/kapi/interrupts.cpp | 2 +- arch/x86_64/kapi/memory.cpp | 26 ++++++++--------- arch/x86_64/kapi/system.cpp | 2 +- .../pre/include/arch/context_switching/main.hpp | 4 +-- arch/x86_64/pre/src/context_switching/main.cpp | 16 +++++----- .../pre/src/context_switching/syscall/main.cpp | 2 +- .../context_switching/syscall/syscall_enable.cpp | 8 ++--- .../context_switching/syscall/syscall_handler.cpp | 14 ++++----- arch/x86_64/pre/src/kernel/main.cpp | 24 +++++++-------- arch/x86_64/pre/src/user/main.cpp | 6 ++-- arch/x86_64/src/boot/boot32.S | 2 +- arch/x86_64/src/bus/isa.cpp | 4 +-- arch/x86_64/src/cpu/initialization.cpp | 12 ++++---- arch/x86_64/src/cpu/interrupts.cpp | 12 ++++---- arch/x86_64/src/debug/qemu_output.cpp | 4 +-- arch/x86_64/src/devices/init.cpp | 14 ++++----- arch/x86_64/src/devices/legacy_pit.cpp | 10 +++---- arch/x86_64/src/devices/local_apic.cpp | 6 ++-- arch/x86_64/src/memory/higher_half_mapper.cpp | 10 +++---- arch/x86_64/src/memory/kernel_mapper.cpp | 8 ++--- arch/x86_64/src/memory/mmu.cpp | 6 ++-- arch/x86_64/src/memory/page_table.cpp | 4 +-- arch/x86_64/src/memory/region_allocator.cpp | 6 ++-- arch/x86_64/src/vga/text/buffer.cpp | 4 +-- arch/x86_64/src/vga/text/device.cpp | 10 +++---- kapi/include/kapi/boot_module/boot_module.hpp | 2 +- .../kapi/boot_module/boot_module_registry.hpp | 2 +- kapi/include/kapi/boot_modules.hpp | 2 +- kapi/include/kapi/cio.hpp | 2 +- kapi/include/kapi/cio/output_device.hpp | 2 +- kapi/include/kapi/cpu.hpp | 2 +- kapi/include/kapi/devices.hpp | 8 ++--- kapi/include/kapi/devices/bus.hpp | 4 +-- kapi/include/kapi/devices/cpu.hpp | 2 +- kapi/include/kapi/devices/device.hpp | 2 +- kapi/include/kapi/devices/manager.hpp | 4 +-- kapi/include/kapi/memory.hpp | 14 ++++----- kapi/include/kapi/memory/address.hpp | 2 +- kapi/include/kapi/memory/chunk.hpp | 2 +- kapi/include/kapi/memory/frame.hpp | 8 ++--- kapi/include/kapi/memory/frame_allocator.hpp | 4 +-- kapi/include/kapi/memory/layout.hpp | 4 +-- kapi/include/kapi/memory/page.hpp | 8 ++--- kapi/include/kapi/memory/page_mapper.hpp | 6 ++-- kernel/include/kernel/devices/block_device.hpp | 2 +- .../include/kernel/devices/block_device_utils.hpp | 2 +- kernel/include/kernel/devices/root_bus.hpp | 2 +- .../include/kernel/devices/storage/controller.hpp | 2 +- .../include/kernel/devices/storage/management.hpp | 4 +-- .../kernel/devices/storage/ram_disk/controller.hpp | 4 +-- .../kernel/devices/storage/ram_disk/device.hpp | 4 +-- kernel/include/kernel/filesystem/dentry.hpp | 2 +- .../include/kernel/filesystem/devfs/filesystem.hpp | 6 ++-- kernel/include/kernel/filesystem/devfs/inode.hpp | 2 +- kernel/include/kernel/filesystem/device_inode.hpp | 4 +-- .../include/kernel/filesystem/ext2/filesystem.hpp | 10 +++---- kernel/include/kernel/filesystem/ext2/inode.hpp | 2 +- .../kernel/filesystem/file_descriptor_table.hpp | 2 +- kernel/include/kernel/filesystem/filesystem.hpp | 2 +- kernel/include/kernel/filesystem/mount.hpp | 4 +-- kernel/include/kernel/filesystem/mount_table.hpp | 2 +- .../kernel/filesystem/open_file_description.hpp | 2 +- .../kernel/filesystem/rootfs/filesystem.hpp | 4 +-- kernel/include/kernel/filesystem/rootfs/inode.hpp | 2 +- kernel/include/kernel/filesystem/vfs.hpp | 8 ++--- kernel/include/kernel/memory.hpp | 4 +-- kernel/include/kernel/memory/bitmap_allocator.hpp | 2 +- .../include/kernel/memory/block_list_allocator.hpp | 4 +-- kernel/include/kernel/memory/mmio_allocator.hpp | 2 +- .../kernel/test_support/bump_frame_allocator.hpp | 2 +- kernel/include/kernel/test_support/cio.hpp | 4 +-- .../kernel/test_support/devices/block_device.hpp | 2 +- .../test_support/devices/character_device.hpp | 2 +- .../kernel/test_support/filesystem/ext2.hpp | 2 +- .../kernel/test_support/filesystem/filesystem.hpp | 4 +-- .../kernel/test_support/filesystem/inode.hpp | 2 +- .../filesystem/storage_boot_module_fixture.hpp | 2 +- .../filesystem/storage_boot_module_vfs_fixture.hpp | 2 +- kernel/include/kernel/test_support/memory.hpp | 2 +- kernel/include/kernel/test_support/page_mapper.hpp | 4 +-- .../kernel/test_support/simulated_memory.hpp | 2 +- kernel/kapi/acpi.cpp | 6 ++-- kernel/kapi/boot_modules.cpp | 4 +-- kernel/kapi/cio.cpp | 2 +- kernel/kapi/cpu.cpp | 4 +-- kernel/kapi/cpu.tests.cpp | 4 +-- kernel/kapi/devices.cpp | 6 ++-- kernel/kapi/devices/bus.cpp | 6 ++-- kernel/kapi/devices/cpu.cpp | 4 +-- kernel/kapi/devices/device.cpp | 4 +-- kernel/kapi/filesystem.cpp | 8 ++--- kernel/kapi/filesystem.tests.cpp | 4 +-- kernel/kapi/interrupts.cpp | 2 +- kernel/kapi/memory.cpp | 8 ++--- kernel/kapi/system.cpp | 4 +-- kernel/kapi/system.tests.cpp | 6 ++-- kernel/kstd/os.cpp | 2 +- kernel/kstd/print.cpp | 2 +- kernel/kstd/print.tests.cpp | 4 +-- kernel/src/acpi/manager.cpp | 6 ++-- kernel/src/devices/block_device.cpp | 6 ++-- kernel/src/devices/block_device.tests.cpp | 4 +-- kernel/src/devices/block_device_utils.cpp | 8 ++--- kernel/src/devices/block_device_utils.tests.cpp | 8 ++--- kernel/src/devices/root_bus.cpp | 4 +-- kernel/src/devices/storage/controller.cpp | 4 +-- kernel/src/devices/storage/management.cpp | 12 ++++---- kernel/src/devices/storage/ram_disk/controller.cpp | 6 ++-- kernel/src/devices/storage/ram_disk/device.cpp | 8 ++--- .../src/devices/storage/ram_disk/device.tests.cpp | 6 ++-- kernel/src/filesystem/dentry.cpp | 6 ++-- kernel/src/filesystem/dentry.tests.cpp | 6 ++-- kernel/src/filesystem/devfs/filesystem.cpp | 12 ++++---- kernel/src/filesystem/devfs/filesystem.tests.cpp | 6 ++-- kernel/src/filesystem/devfs/inode.cpp | 4 +-- kernel/src/filesystem/devfs/inode.tests.cpp | 2 +- kernel/src/filesystem/device_inode.cpp | 10 +++---- kernel/src/filesystem/device_inode.tests.cpp | 8 ++--- kernel/src/filesystem/ext2/filesystem.cpp | 16 +++++----- kernel/src/filesystem/ext2/filesystem.tests.cpp | 18 ++++++------ kernel/src/filesystem/ext2/inode.cpp | 8 ++--- kernel/src/filesystem/ext2/inode.tests.cpp | 20 ++++++------- kernel/src/filesystem/file_descriptor_table.cpp | 6 ++-- .../src/filesystem/file_descriptor_table.tests.cpp | 6 ++-- kernel/src/filesystem/filesystem.cpp | 8 ++--- kernel/src/filesystem/inode.cpp | 2 +- kernel/src/filesystem/mount.cpp | 8 ++--- kernel/src/filesystem/mount.tests.cpp | 10 +++---- kernel/src/filesystem/mount_table.cpp | 6 ++-- kernel/src/filesystem/mount_table.tests.cpp | 10 +++---- kernel/src/filesystem/open_file_description.cpp | 4 +-- .../src/filesystem/open_file_description.tests.cpp | 8 ++--- kernel/src/filesystem/rootfs/filesystem.cpp | 6 ++-- kernel/src/filesystem/rootfs/filesystem.tests.cpp | 4 +-- kernel/src/filesystem/rootfs/inode.cpp | 4 +-- kernel/src/filesystem/rootfs/inode.tests.cpp | 2 +- kernel/src/filesystem/vfs.cpp | 16 +++++----- kernel/src/filesystem/vfs.tests.cpp | 6 ++-- kernel/src/main.cpp | 28 +++++++++--------- kernel/src/memory.cpp | 10 +++---- kernel/src/memory/bitmap_allocator.cpp | 4 +-- kernel/src/memory/bitmap_allocator.tests.cpp | 8 ++--- kernel/src/memory/block_list_allocator.cpp | 8 ++--- kernel/src/memory/block_list_allocator.tests.cpp | 6 ++-- kernel/src/memory/mmio_allocator.cpp | 6 ++-- kernel/src/memory/operators.cpp | 4 +-- kernel/src/test_support/devices/block_device.cpp | 4 +-- .../src/test_support/devices/character_device.cpp | 5 ++-- kernel/src/test_support/filesystem/ext2.cpp | 12 ++++---- kernel/src/test_support/filesystem/filesystem.cpp | 6 ++-- kernel/src/test_support/filesystem/inode.cpp | 4 +-- .../filesystem/storage_boot_module_fixture.cpp | 14 ++++----- .../filesystem/storage_boot_module_vfs_fixture.cpp | 6 ++-- kernel/src/test_support/kapi/cio.cpp | 4 +-- kernel/src/test_support/kapi/cpu.cpp | 2 +- kernel/src/test_support/kapi/memory.cpp | 6 ++-- kernel/src/test_support/log_buffer.cpp | 2 +- kernel/src/test_support/output_device.cpp | 6 ++-- kernel/src/test_support/page_mapper.cpp | 4 +-- kernel/src/test_support/simulated_memory.cpp | 4 +-- kernel/src/test_support/state_reset_listener.cpp | 24 +++++++-------- libs/elf/include/elf/section_header.hpp | 2 +- libs/kstd/include/kstd/bits/format/arg.hpp | 4 +-- libs/kstd/include/kstd/bits/format/args.hpp | 8 ++--- libs/kstd/include/kstd/bits/format/context.hpp | 4 +-- libs/kstd/include/kstd/bits/format/error.hpp | 2 +- libs/kstd/include/kstd/bits/format/formatter.hpp | 6 ++-- .../include/kstd/bits/format/formatter/bool.hpp | 10 +++---- .../include/kstd/bits/format/formatter/byte.hpp | 6 ++-- .../include/kstd/bits/format/formatter/char.hpp | 12 ++++---- .../include/kstd/bits/format/formatter/cstring.hpp | 6 ++-- .../kstd/bits/format/formatter/integral.hpp | 10 +++---- .../kstd/bits/format/formatter/ordering.hpp | 8 ++--- .../include/kstd/bits/format/formatter/pointer.hpp | 10 +++---- .../include/kstd/bits/format/formatter/range.hpp | 2 +- .../kstd/bits/format/formatter/string_view.hpp | 8 ++--- .../include/kstd/bits/format/parse_context.hpp | 2 +- libs/kstd/include/kstd/bits/format/specifiers.hpp | 4 +-- libs/kstd/include/kstd/bits/format/string.hpp | 8 ++--- libs/kstd/include/kstd/bits/observer_ptr.hpp | 2 +- libs/kstd/include/kstd/format | 34 +++++++++++----------- libs/kstd/include/kstd/memory | 6 ++-- libs/kstd/include/kstd/os/print.hpp | 4 +-- libs/kstd/include/kstd/print | 5 ++-- libs/kstd/include/kstd/stack | 2 +- libs/kstd/src/libc/stdlib.cpp | 2 +- libs/kstd/src/mutex.cpp | 4 +-- libs/kstd/src/os/error.cpp | 2 +- libs/kstd/tests/src/os_panic.cpp | 2 +- libs/kstd/tests/src/vector.cpp | 2 +- libs/multiboot2/multiboot2/constants.hpp | 2 +- libs/multiboot2/multiboot2/information.hpp | 6 ++-- libs/multiboot2/multiboot2/information/data.hpp | 4 +-- .../multiboot2/multiboot2/information/iterator.hpp | 4 +-- libs/multiboot2/multiboot2/information/tag.hpp | 2 +- 224 files changed, 646 insertions(+), 648 deletions(-) diff --git a/arch/x86_64/include/arch/boot/boot.hpp b/arch/x86_64/include/arch/boot/boot.hpp index 3a598f5..7df61c4 100644 --- a/arch/x86_64/include/arch/boot/boot.hpp +++ b/arch/x86_64/include/arch/boot/boot.hpp @@ -31,7 +31,7 @@ // clang-format on #else -#include "kapi/boot.hpp" // IWYU pragma: export +#include // IWYU pragma: export #include diff --git a/arch/x86_64/include/arch/bus/isa.hpp b/arch/x86_64/include/arch/bus/isa.hpp index 5deed25..e56f56a 100644 --- a/arch/x86_64/include/arch/bus/isa.hpp +++ b/arch/x86_64/include/arch/bus/isa.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_BUS_ISA_HPP #define TEACHOS_X86_64_BUS_ISA_HPP -#include "kapi/devices/bus.hpp" +#include #include diff --git a/arch/x86_64/include/arch/cpu/control_register.hpp b/arch/x86_64/include/arch/cpu/control_register.hpp index fafbfc7..9cedc35 100644 --- a/arch/x86_64/include/arch/cpu/control_register.hpp +++ b/arch/x86_64/include/arch/cpu/control_register.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_X86_64_CPU_CONTROL_REGISTERS_HPP #define TEACHOS_X86_64_CPU_CONTROL_REGISTERS_HPP -// IWYU pragma: private, include "arch/cpu/registers.hpp" +// IWYU pragma: private, include -#include "kapi/memory.hpp" +#include #include diff --git a/arch/x86_64/include/arch/cpu/global_descriptor_table.hpp b/arch/x86_64/include/arch/cpu/global_descriptor_table.hpp index bc7d328..b17c509 100644 --- a/arch/x86_64/include/arch/cpu/global_descriptor_table.hpp +++ b/arch/x86_64/include/arch/cpu/global_descriptor_table.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_X86_64_GLOBAL_DESCRIPTOR_TABLE_HPP #define TEACHOS_X86_64_GLOBAL_DESCRIPTOR_TABLE_HPP -#include "arch/cpu/segment_descriptor.hpp" +#include -#include "kapi/memory.hpp" +#include #include #include diff --git a/arch/x86_64/include/arch/cpu/interrupts.hpp b/arch/x86_64/include/arch/cpu/interrupts.hpp index 24b72e9..6162f56 100644 --- a/arch/x86_64/include/arch/cpu/interrupts.hpp +++ b/arch/x86_64/include/arch/cpu/interrupts.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_X86_64_CPU_INTERRUPTS_HPP #define TEACHOS_X86_64_CPU_INTERRUPTS_HPP -#include "arch/cpu/segment_selector.hpp" +#include -#include "kapi/memory.hpp" +#include #include #include diff --git a/arch/x86_64/include/arch/cpu/legacy_pic.hpp b/arch/x86_64/include/arch/cpu/legacy_pic.hpp index 9f53d86..56ca9c4 100644 --- a/arch/x86_64/include/arch/cpu/legacy_pic.hpp +++ b/arch/x86_64/include/arch/cpu/legacy_pic.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_CPU_LEGACY_PIC_HPP #define TEACHOS_X86_64_CPU_LEGACY_PIC_HPP -#include "arch/device_io/port_io.hpp" +#include #include diff --git a/arch/x86_64/include/arch/cpu/model_specific_register.hpp b/arch/x86_64/include/arch/cpu/model_specific_register.hpp index 8539a24..bd4aff9 100644 --- a/arch/x86_64/include/arch/cpu/model_specific_register.hpp +++ b/arch/x86_64/include/arch/cpu/model_specific_register.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_CPU_MODEL_SPECIFIC_REGISTER_HPP #define TEACHOS_X86_64_CPU_MODEL_SPECIFIC_REGISTER_HPP -// IWYU pragma: private, include "x86_64/cpu/registers.hpp" +// IWYU pragma: private, include #include diff --git a/arch/x86_64/include/arch/cpu/registers.hpp b/arch/x86_64/include/arch/cpu/registers.hpp index 62206bf..58633f6 100644 --- a/arch/x86_64/include/arch/cpu/registers.hpp +++ b/arch/x86_64/include/arch/cpu/registers.hpp @@ -1,10 +1,10 @@ #ifndef TEACHOS_X86_64_CPU_REGISTERS_HPP #define TEACHOS_X86_64_CPU_REGISTERS_HPP -#include "arch/cpu/control_register.hpp" // IWYU pragma: export -#include "arch/cpu/model_specific_register.hpp" // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export -#include "kapi/memory.hpp" +#include namespace arch::cpu { diff --git a/arch/x86_64/include/arch/debug/qemu_output.hpp b/arch/x86_64/include/arch/debug/qemu_output.hpp index f43e147..5ddd4be 100644 --- a/arch/x86_64/include/arch/debug/qemu_output.hpp +++ b/arch/x86_64/include/arch/debug/qemu_output.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_X86_64_DEBUG_QEMU_OUTPUT_HPP #define TEACHOS_X86_64_DEBUG_QEMU_OUTPUT_HPP -#include "arch/device_io/port_io.hpp" +#include -#include "kapi/cio.hpp" +#include #include diff --git a/arch/x86_64/include/arch/devices/legacy_pit.hpp b/arch/x86_64/include/arch/devices/legacy_pit.hpp index de742ae..356895c 100644 --- a/arch/x86_64/include/arch/devices/legacy_pit.hpp +++ b/arch/x86_64/include/arch/devices/legacy_pit.hpp @@ -1,8 +1,8 @@ #ifndef TEACHOS_ARCH_X86_64_DEVICES_LEGACY_PIT_HPP #define TEACHOS_ARCH_X86_64_DEVICES_LEGACY_PIT_HPP -#include "kapi/devices/device.hpp" -#include "kapi/interrupts.hpp" +#include +#include #include #include diff --git a/arch/x86_64/include/arch/devices/local_apic.hpp b/arch/x86_64/include/arch/devices/local_apic.hpp index ee1073f..f8f080d 100644 --- a/arch/x86_64/include/arch/devices/local_apic.hpp +++ b/arch/x86_64/include/arch/devices/local_apic.hpp @@ -1,8 +1,8 @@ #ifndef TEACHOS_ARCH_X86_64_DEVICES_LOCAL_APIC_HPP #define TEACHOS_ARCH_X86_64_DEVICES_LOCAL_APIC_HPP -#include "kapi/devices/device.hpp" -#include "kapi/memory.hpp" +#include +#include #include #include diff --git a/arch/x86_64/include/arch/memory/higher_half_mapper.hpp b/arch/x86_64/include/arch/memory/higher_half_mapper.hpp index 24bea17..9b02ee6 100644 --- a/arch/x86_64/include/arch/memory/higher_half_mapper.hpp +++ b/arch/x86_64/include/arch/memory/higher_half_mapper.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_X86_64_HIGHER_HALF_MAPPER_HPP #define TEACHOS_X86_64_HIGHER_HALF_MAPPER_HPP -#include "arch/memory/page_table.hpp" +#include -#include "kapi/memory.hpp" +#include #include diff --git a/arch/x86_64/include/arch/memory/kernel_mapper.hpp b/arch/x86_64/include/arch/memory/kernel_mapper.hpp index ae593a5..adbf688 100644 --- a/arch/x86_64/include/arch/memory/kernel_mapper.hpp +++ b/arch/x86_64/include/arch/memory/kernel_mapper.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_KERNEL_MAPPER_HPP #define TEACHOS_X86_64_KERNEL_MAPPER_HPP -#include "kapi/memory.hpp" +#include #include #include diff --git a/arch/x86_64/include/arch/memory/mmu.hpp b/arch/x86_64/include/arch/memory/mmu.hpp index 2d64184..64373f4 100644 --- a/arch/x86_64/include/arch/memory/mmu.hpp +++ b/arch/x86_64/include/arch/memory/mmu.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_MEMORY_MMU_HPP #define TEACHOS_X86_64_MEMORY_MMU_HPP -#include "kapi/memory/address.hpp" +#include namespace arch::memory { diff --git a/arch/x86_64/include/arch/memory/page_table.hpp b/arch/x86_64/include/arch/memory/page_table.hpp index c75ccaf..ce3d3a1 100644 --- a/arch/x86_64/include/arch/memory/page_table.hpp +++ b/arch/x86_64/include/arch/memory/page_table.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_PAGE_TABLE_HPP #define TEACHOS_X86_64_PAGE_TABLE_HPP -#include "kapi/memory.hpp" +#include #include #include diff --git a/arch/x86_64/include/arch/memory/page_utilities.hpp b/arch/x86_64/include/arch/memory/page_utilities.hpp index c48e74f..068e824 100644 --- a/arch/x86_64/include/arch/memory/page_utilities.hpp +++ b/arch/x86_64/include/arch/memory/page_utilities.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_PAGE_UTILITIES_HPP #define TEACHOS_X86_64_PAGE_UTILITIES_HPP -#include "kapi/memory.hpp" +#include #include diff --git a/arch/x86_64/include/arch/memory/region_allocator.hpp b/arch/x86_64/include/arch/memory/region_allocator.hpp index c7a836f..5d9da2e 100644 --- a/arch/x86_64/include/arch/memory/region_allocator.hpp +++ b/arch/x86_64/include/arch/memory/region_allocator.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_X86_64_MEMORY_REGION_ALLOCATOR_HPP #define TEACHOS_X86_64_MEMORY_REGION_ALLOCATOR_HPP -#include "kapi/memory/address.hpp" -#include "kapi/memory/frame.hpp" -#include "kapi/memory/frame_allocator.hpp" +#include +#include +#include #include diff --git a/arch/x86_64/include/arch/vga/crtc.hpp b/arch/x86_64/include/arch/vga/crtc.hpp index dbdc365..a8bec93 100644 --- a/arch/x86_64/include/arch/vga/crtc.hpp +++ b/arch/x86_64/include/arch/vga/crtc.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_VGA_IO_HPP #define TEACHOS_X86_64_VGA_IO_HPP -#include "arch/device_io/port_io.hpp" +#include #include diff --git a/arch/x86_64/include/arch/vga/text.hpp b/arch/x86_64/include/arch/vga/text.hpp index f81ab60..2e73dd2 100644 --- a/arch/x86_64/include/arch/vga/text.hpp +++ b/arch/x86_64/include/arch/vga/text.hpp @@ -1,10 +1,10 @@ #ifndef TEACHOS_X86_64_VGA_TEXT_HPP #define TEACHOS_X86_64_VGA_TEXT_HPP -#include "text/attribute.hpp" // IWYU pragma: export -#include "text/color.hpp" // IWYU pragma: export -#include "text/common_attributes.hpp" // IWYU pragma: export -#include "text/device.hpp" // IWYU pragma: export -#include "text/flags.hpp" // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #endif // TEACHOS_ARCH_X86_64_VIDEO_VGA_TEXT_HPP \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text/attribute.hpp b/arch/x86_64/include/arch/vga/text/attribute.hpp index 6a0f995..6395aed 100644 --- a/arch/x86_64/include/arch/vga/text/attribute.hpp +++ b/arch/x86_64/include/arch/vga/text/attribute.hpp @@ -1,10 +1,10 @@ #ifndef TEACHOS_X86_64_VGA_TEXT_ATTRIBUTE_HPP #define TEACHOS_X86_64_VGA_TEXT_ATTRIBUTE_HPP -// IWYU pragma: private, include "arch/vga/text.hpp" +// IWYU pragma: private, include -#include "arch/vga/text/color.hpp" -#include "arch/vga/text/flags.hpp" +#include +#include namespace arch::vga::text { diff --git a/arch/x86_64/include/arch/vga/text/buffer.hpp b/arch/x86_64/include/arch/vga/text/buffer.hpp index 648d37a..8eb6645 100644 --- a/arch/x86_64/include/arch/vga/text/buffer.hpp +++ b/arch/x86_64/include/arch/vga/text/buffer.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_X86_64_VGA_TEXT_BUFFER_HPP #define TEACHOS_X86_64_VGA_TEXT_BUFFER_HPP -// IWYU pragma: private, include "arch/vga/text.hpp" +// IWYU pragma: private, include -#include "arch/vga/text/attribute.hpp" +#include #include #include diff --git a/arch/x86_64/include/arch/vga/text/color.hpp b/arch/x86_64/include/arch/vga/text/color.hpp index a541830..e0ad6df 100644 --- a/arch/x86_64/include/arch/vga/text/color.hpp +++ b/arch/x86_64/include/arch/vga/text/color.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_VGA_TEXT_COLOR_HPP #define TEACHOS_X86_64_VGA_TEXT_COLOR_HPP -// IWYU pragma: private, include "arch/vga/text.hpp" +// IWYU pragma: private, include #include diff --git a/arch/x86_64/include/arch/vga/text/common_attributes.hpp b/arch/x86_64/include/arch/vga/text/common_attributes.hpp index 9bd61a5..3d8929f 100644 --- a/arch/x86_64/include/arch/vga/text/common_attributes.hpp +++ b/arch/x86_64/include/arch/vga/text/common_attributes.hpp @@ -1,11 +1,11 @@ #ifndef TEACHOS_X86_64_VGA_TEXT_COMMON_ATTRIBUTES_HPP #define TEACHOS_X86_64_VGA_TEXT_COMMON_ATTRIBUTES_HPP -// IWYU pragma: private, include "arch/vga/text.hpp" +// IWYU pragma: private, include -#include "arch/vga/text/attribute.hpp" -#include "arch/vga/text/color.hpp" -#include "arch/vga/text/flags.hpp" +#include +#include +#include namespace arch::vga::text { diff --git a/arch/x86_64/include/arch/vga/text/device.hpp b/arch/x86_64/include/arch/vga/text/device.hpp index d0eb45d..0a0e017 100644 --- a/arch/x86_64/include/arch/vga/text/device.hpp +++ b/arch/x86_64/include/arch/vga/text/device.hpp @@ -1,11 +1,11 @@ #ifndef TEACHOS_X86_64_VGA_TEXT_DEVICE_HPP #define TEACHOS_X86_64_VGA_TEXT_DEVICE_HPP -// IWYU pragma: private, include "arch/vga/text.hpp" +// IWYU pragma: private, include -#include "arch/vga/text/buffer.hpp" +#include -#include "kapi/cio.hpp" +#include #include diff --git a/arch/x86_64/include/arch/vga/text/flags.hpp b/arch/x86_64/include/arch/vga/text/flags.hpp index 67c6c11..7a29e33 100644 --- a/arch/x86_64/include/arch/vga/text/flags.hpp +++ b/arch/x86_64/include/arch/vga/text/flags.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_VGA_TEXT_FLAGS_HPP #define TEACHOS_X86_64_VGA_TEXT_FLAGS_HPP -// IWYU pragma: private, include "arch/vga/text.hpp" +// IWYU pragma: private, include namespace arch::vga::text { diff --git a/arch/x86_64/kapi/boot_modules.cpp b/arch/x86_64/kapi/boot_modules.cpp index 1a588cf..fb6bf46 100644 --- a/arch/x86_64/kapi/boot_modules.cpp +++ b/arch/x86_64/kapi/boot_modules.cpp @@ -1,13 +1,13 @@ -#include "kapi/boot_modules.hpp" +#include -#include "arch/boot/boot.hpp" -#include "arch/boot/ld.hpp" +#include +#include -#include "kapi/boot.hpp" -#include "kapi/boot_module/boot_module.hpp" -#include "kapi/boot_module/boot_module_registry.hpp" -#include "kapi/memory.hpp" -#include "kapi/system.hpp" +#include +#include +#include +#include +#include #include diff --git a/arch/x86_64/kapi/cio.cpp b/arch/x86_64/kapi/cio.cpp index 015cf5e..b33c6e0 100644 --- a/arch/x86_64/kapi/cio.cpp +++ b/arch/x86_64/kapi/cio.cpp @@ -1,7 +1,7 @@ -#include "kapi/cio.hpp" +#include -#include "arch/debug/qemu_output.hpp" -#include "arch/vga/text.hpp" +#include +#include #include diff --git a/arch/x86_64/kapi/cpu.cpp b/arch/x86_64/kapi/cpu.cpp index baeab4b..40dc228 100644 --- a/arch/x86_64/kapi/cpu.cpp +++ b/arch/x86_64/kapi/cpu.cpp @@ -1,13 +1,13 @@ -#include "kapi/cpu.hpp" +#include -#include "arch/cpu/initialization.hpp" -#include "arch/devices/local_apic.hpp" +#include +#include -#include "kapi/acpi.hpp" -#include "kapi/devices.hpp" -#include "kapi/devices/cpu.hpp" -#include "kapi/memory.hpp" -#include "kapi/system.hpp" +#include +#include +#include +#include +#include #include diff --git a/arch/x86_64/kapi/devices.cpp b/arch/x86_64/kapi/devices.cpp index 47c7f8c..f188c92 100644 --- a/arch/x86_64/kapi/devices.cpp +++ b/arch/x86_64/kapi/devices.cpp @@ -1,6 +1,6 @@ -#include "kapi/devices.hpp" +#include -#include "arch/devices/init.hpp" +#include namespace kapi::devices { diff --git a/arch/x86_64/kapi/interrupts.cpp b/arch/x86_64/kapi/interrupts.cpp index cf1defa..85acc0f 100644 --- a/arch/x86_64/kapi/interrupts.cpp +++ b/arch/x86_64/kapi/interrupts.cpp @@ -1,4 +1,4 @@ -#include "kapi/interrupts.hpp" +#include namespace kapi::interrupts { diff --git a/arch/x86_64/kapi/memory.cpp b/arch/x86_64/kapi/memory.cpp index 423913d..5b870d5 100644 --- a/arch/x86_64/kapi/memory.cpp +++ b/arch/x86_64/kapi/memory.cpp @@ -1,16 +1,16 @@ -#include "kapi/memory.hpp" - -#include "arch/boot/boot.hpp" -#include "arch/boot/ld.hpp" -#include "arch/cpu/registers.hpp" -#include "arch/memory/higher_half_mapper.hpp" -#include "arch/memory/kernel_mapper.hpp" -#include "arch/memory/page_table.hpp" -#include "arch/memory/page_utilities.hpp" -#include "arch/memory/region_allocator.hpp" - -#include "kapi/boot.hpp" -#include "kapi/system.hpp" +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include #include #include diff --git a/arch/x86_64/kapi/system.cpp b/arch/x86_64/kapi/system.cpp index 09c7152..73a77e5 100644 --- a/arch/x86_64/kapi/system.cpp +++ b/arch/x86_64/kapi/system.cpp @@ -1,4 +1,4 @@ -#include "kapi/system.hpp" +#include namespace kapi::system { diff --git a/arch/x86_64/pre/include/arch/context_switching/main.hpp b/arch/x86_64/pre/include/arch/context_switching/main.hpp index f8477ea..07e00e8 100644 --- a/arch/x86_64/pre/include/arch/context_switching/main.hpp +++ b/arch/x86_64/pre/include/arch/context_switching/main.hpp @@ -1,8 +1,8 @@ #ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_MAIN_HPP #define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_MAIN_HPP -#include "arch/context_switching/interrupt_descriptor_table/interrupt_descriptor_table.hpp" -#include "arch/context_switching/segment_descriptor_table/global_descriptor_table.hpp" +#include +#include namespace teachos::arch::context_switching { diff --git a/arch/x86_64/pre/src/context_switching/main.cpp b/arch/x86_64/pre/src/context_switching/main.cpp index 3eb6dae..0961499 100644 --- a/arch/x86_64/pre/src/context_switching/main.cpp +++ b/arch/x86_64/pre/src/context_switching/main.cpp @@ -1,12 +1,12 @@ -#include "arch/context_switching/main.hpp" +#include -#include "arch/boot/pointers.hpp" -#include "arch/context_switching/syscall/syscall_enable.hpp" -#include "arch/kernel/cpu/call.hpp" -#include "arch/kernel/cpu/if.hpp" -#include "arch/kernel/cpu/segment_register.hpp" -#include "arch/kernel/cpu/tr.hpp" -#include "arch/user/main.hpp" +#include +#include +#include +#include +#include +#include +#include namespace teachos::arch::context_switching { diff --git a/arch/x86_64/pre/src/context_switching/syscall/main.cpp b/arch/x86_64/pre/src/context_switching/syscall/main.cpp index b4ab468..10bd087 100644 --- a/arch/x86_64/pre/src/context_switching/syscall/main.cpp +++ b/arch/x86_64/pre/src/context_switching/syscall/main.cpp @@ -1,4 +1,4 @@ -#include "arch/context_switching/syscall/main.hpp" +#include namespace teachos::arch::context_switching::syscall { diff --git a/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp b/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp index dbb3ed9..f9f070a 100644 --- a/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp +++ b/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp @@ -1,8 +1,8 @@ -#include "arch/context_switching/syscall/syscall_enable.hpp" +#include -#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp" -#include "arch/context_switching/syscall/syscall_handler.hpp" -#include "arch/kernel/cpu/msr.hpp" +#include +#include +#include namespace teachos::arch::context_switching::syscall { diff --git a/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp b/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp index c120f77..430d65c 100644 --- a/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp +++ b/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp @@ -1,11 +1,11 @@ -#include "arch/context_switching/syscall/syscall_handler.hpp" +#include -#include "arch/context_switching/syscall/main.hpp" -#include "arch/exception_handling/assert.hpp" -#include "arch/exception_handling/panic.hpp" -#include "arch/memory/heap/global_heap_allocator.hpp" -#include "arch/memory/main.hpp" -#include "arch/video/vga/text.hpp" +#include +#include +#include +#include +#include +#include namespace teachos::arch::context_switching::syscall { diff --git a/arch/x86_64/pre/src/kernel/main.cpp b/arch/x86_64/pre/src/kernel/main.cpp index 43b5f90..2658678 100644 --- a/arch/x86_64/pre/src/kernel/main.cpp +++ b/arch/x86_64/pre/src/kernel/main.cpp @@ -1,16 +1,16 @@ -#include "arch/kernel/main.hpp" +#include -#include "arch/boot/pointers.hpp" -#include "arch/context_switching/interrupt_descriptor_table/segment_selector.hpp" -#include "arch/context_switching/main.hpp" -#include "arch/kernel/cpu/if.hpp" -#include "arch/kernel/cpu/segment_register.hpp" -#include "arch/memory/heap/bump_allocator.hpp" -#include "arch/memory/heap/global_heap_allocator.hpp" -#include "arch/memory/main.hpp" -#include "arch/memory/multiboot/reader.hpp" -#include "arch/stl/vector.hpp" -#include "arch/video/vga/text.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace teachos::arch::kernel { diff --git a/arch/x86_64/pre/src/user/main.cpp b/arch/x86_64/pre/src/user/main.cpp index 8b07e4a..10a17db 100644 --- a/arch/x86_64/pre/src/user/main.cpp +++ b/arch/x86_64/pre/src/user/main.cpp @@ -1,7 +1,7 @@ -#include "arch/user/main.hpp" +#include -#include "arch/context_switching/syscall/main.hpp" -#include "arch/memory/heap/global_heap_allocator.hpp" +#include +#include #include #include diff --git a/arch/x86_64/src/boot/boot32.S b/arch/x86_64/src/boot/boot32.S index 1c2fdaf..e6fcd85 100644 --- a/arch/x86_64/src/boot/boot32.S +++ b/arch/x86_64/src/boot/boot32.S @@ -1,4 +1,4 @@ -#include "arch/boot/boot.hpp" +#include /** * @brief Uninitialized data for the bootstrapping process. diff --git a/arch/x86_64/src/bus/isa.cpp b/arch/x86_64/src/bus/isa.cpp index ff4ad71..f6cc72d 100644 --- a/arch/x86_64/src/bus/isa.cpp +++ b/arch/x86_64/src/bus/isa.cpp @@ -1,6 +1,6 @@ -#include "arch/bus/isa.hpp" +#include -#include "kapi/devices.hpp" +#include #include diff --git a/arch/x86_64/src/cpu/initialization.cpp b/arch/x86_64/src/cpu/initialization.cpp index b808c76..1be9c82 100644 --- a/arch/x86_64/src/cpu/initialization.cpp +++ b/arch/x86_64/src/cpu/initialization.cpp @@ -1,10 +1,10 @@ -#include "arch/cpu/initialization.hpp" +#include -#include "arch/cpu/global_descriptor_table.hpp" -#include "arch/cpu/interrupts.hpp" -#include "arch/cpu/legacy_pic.hpp" -#include "arch/cpu/segment_descriptor.hpp" -#include "arch/cpu/task_state_segment.hpp" +#include +#include +#include +#include +#include #include diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp index f58b851..f40422f 100644 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ b/arch/x86_64/src/cpu/interrupts.cpp @@ -1,11 +1,11 @@ -#include "arch/cpu/interrupts.hpp" +#include -#include "arch/cpu/legacy_pic.hpp" -#include "arch/cpu/segment_selector.hpp" +#include +#include -#include "kapi/cpu.hpp" -#include "kapi/interrupts.hpp" -#include "kapi/memory.hpp" +#include +#include +#include #include diff --git a/arch/x86_64/src/debug/qemu_output.cpp b/arch/x86_64/src/debug/qemu_output.cpp index 535017d..71acede 100644 --- a/arch/x86_64/src/debug/qemu_output.cpp +++ b/arch/x86_64/src/debug/qemu_output.cpp @@ -1,6 +1,6 @@ -#include "arch/debug/qemu_output.hpp" +#include -#include "kapi/cio.hpp" +#include #include #include diff --git a/arch/x86_64/src/devices/init.cpp b/arch/x86_64/src/devices/init.cpp index 8c96b38..c30e8cf 100644 --- a/arch/x86_64/src/devices/init.cpp +++ b/arch/x86_64/src/devices/init.cpp @@ -1,12 +1,12 @@ -#include "arch/devices/init.hpp" +#include -#include "arch/boot/boot.hpp" -#include "arch/bus/isa.hpp" -#include "arch/devices/legacy_pit.hpp" +#include +#include +#include -#include "kapi/acpi.hpp" -#include "kapi/cpu.hpp" -#include "kapi/devices.hpp" +#include +#include +#include #include diff --git a/arch/x86_64/src/devices/legacy_pit.cpp b/arch/x86_64/src/devices/legacy_pit.cpp index 44ff499..d542d47 100644 --- a/arch/x86_64/src/devices/legacy_pit.cpp +++ b/arch/x86_64/src/devices/legacy_pit.cpp @@ -1,10 +1,10 @@ -#include "arch/devices/legacy_pit.hpp" +#include -#include "arch/device_io/port_io.hpp" +#include -#include "kapi/devices.hpp" -#include "kapi/devices/device.hpp" -#include "kapi/interrupts.hpp" +#include +#include +#include #include #include diff --git a/arch/x86_64/src/devices/local_apic.cpp b/arch/x86_64/src/devices/local_apic.cpp index 4a81de8..660921b 100644 --- a/arch/x86_64/src/devices/local_apic.cpp +++ b/arch/x86_64/src/devices/local_apic.cpp @@ -1,7 +1,7 @@ -#include "arch/devices/local_apic.hpp" +#include -#include "kapi/devices.hpp" -#include "kapi/memory.hpp" +#include +#include #include diff --git a/arch/x86_64/src/memory/higher_half_mapper.cpp b/arch/x86_64/src/memory/higher_half_mapper.cpp index b0d1995..75adb3c 100644 --- a/arch/x86_64/src/memory/higher_half_mapper.cpp +++ b/arch/x86_64/src/memory/higher_half_mapper.cpp @@ -1,10 +1,10 @@ -#include "arch/memory/higher_half_mapper.hpp" +#include -#include "arch/memory/page_table.hpp" -#include "arch/memory/page_utilities.hpp" +#include +#include -#include "kapi/memory.hpp" -#include "kapi/system.hpp" +#include +#include #include #include diff --git a/arch/x86_64/src/memory/kernel_mapper.cpp b/arch/x86_64/src/memory/kernel_mapper.cpp index 46d4dca..74272a0 100644 --- a/arch/x86_64/src/memory/kernel_mapper.cpp +++ b/arch/x86_64/src/memory/kernel_mapper.cpp @@ -1,9 +1,9 @@ -#include "arch/memory/kernel_mapper.hpp" +#include -#include "arch/boot/ld.hpp" +#include -#include "kapi/memory.hpp" -#include "kapi/system.hpp" +#include +#include #include #include diff --git a/arch/x86_64/src/memory/mmu.cpp b/arch/x86_64/src/memory/mmu.cpp index 6d185a0..2b53fa4 100644 --- a/arch/x86_64/src/memory/mmu.cpp +++ b/arch/x86_64/src/memory/mmu.cpp @@ -1,8 +1,8 @@ -#include "arch/memory/mmu.hpp" +#include -#include "arch/cpu/registers.hpp" +#include -#include "kapi/memory.hpp" +#include namespace arch::memory { diff --git a/arch/x86_64/src/memory/page_table.cpp b/arch/x86_64/src/memory/page_table.cpp index 26cdd29..2180420 100644 --- a/arch/x86_64/src/memory/page_table.cpp +++ b/arch/x86_64/src/memory/page_table.cpp @@ -1,6 +1,6 @@ -#include "arch/memory/page_table.hpp" +#include -#include "kapi/memory.hpp" +#include #include #include diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp index 4ee3ca4..4086a10 100644 --- a/arch/x86_64/src/memory/region_allocator.cpp +++ b/arch/x86_64/src/memory/region_allocator.cpp @@ -1,7 +1,7 @@ -#include "arch/memory/region_allocator.hpp" +#include -#include "kapi/memory/address.hpp" -#include "kapi/memory/frame.hpp" +#include +#include #include diff --git a/arch/x86_64/src/vga/text/buffer.cpp b/arch/x86_64/src/vga/text/buffer.cpp index 7112573..498b9a3 100644 --- a/arch/x86_64/src/vga/text/buffer.cpp +++ b/arch/x86_64/src/vga/text/buffer.cpp @@ -1,6 +1,6 @@ -#include "arch/vga/text/buffer.hpp" +#include -#include "arch/vga/text/attribute.hpp" +#include #include #include diff --git a/arch/x86_64/src/vga/text/device.cpp b/arch/x86_64/src/vga/text/device.cpp index 0ecbef9..8468358 100644 --- a/arch/x86_64/src/vga/text/device.cpp +++ b/arch/x86_64/src/vga/text/device.cpp @@ -1,9 +1,9 @@ -#include "arch/boot/boot.hpp" -#include "arch/boot/ld.hpp" -#include "arch/vga/crtc.hpp" -#include "arch/vga/text.hpp" +#include +#include +#include +#include -#include "kapi/cio.hpp" +#include #include #include diff --git a/kapi/include/kapi/boot_module/boot_module.hpp b/kapi/include/kapi/boot_module/boot_module.hpp index 85a1ac5..9b4b165 100644 --- a/kapi/include/kapi/boot_module/boot_module.hpp +++ b/kapi/include/kapi/boot_module/boot_module.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_HPP #define TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_HPP -#include "kapi/memory.hpp" +#include #include #include diff --git a/kapi/include/kapi/boot_module/boot_module_registry.hpp b/kapi/include/kapi/boot_module/boot_module_registry.hpp index 0692d37..fc3590f 100644 --- a/kapi/include/kapi/boot_module/boot_module_registry.hpp +++ b/kapi/include/kapi/boot_module/boot_module_registry.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_REGISTRY_HPP #define TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_REGISTRY_HPP -#include "kapi/boot_module/boot_module.hpp" +#include #include diff --git a/kapi/include/kapi/boot_modules.hpp b/kapi/include/kapi/boot_modules.hpp index 6eee169..026479d 100644 --- a/kapi/include/kapi/boot_modules.hpp +++ b/kapi/include/kapi/boot_modules.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KAPI_BOOT_MODULES_HPP #define TEACHOS_KAPI_BOOT_MODULES_HPP -#include "kapi/boot_module/boot_module_registry.hpp" // IWYU pragma: export +#include // IWYU pragma: export namespace kapi::boot_modules { diff --git a/kapi/include/kapi/cio.hpp b/kapi/include/kapi/cio.hpp index 8941a9f..9bbf7fa 100644 --- a/kapi/include/kapi/cio.hpp +++ b/kapi/include/kapi/cio.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KAPI_CIO_HPP #define TEACHOS_KAPI_CIO_HPP -#include "kapi/cio/output_device.hpp" // IWYU pragma: export +#include // IWYU pragma: export #include diff --git a/kapi/include/kapi/cio/output_device.hpp b/kapi/include/kapi/cio/output_device.hpp index f08d7ba..9fe2557 100644 --- a/kapi/include/kapi/cio/output_device.hpp +++ b/kapi/include/kapi/cio/output_device.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KAPI_CIO_OUTPUT_DEVICE_HPP #define TEACHOS_KAPI_CIO_OUTPUT_DEVICE_HPP -// IWYU pragma: private, include "kapi/cio.hpp" +// IWYU pragma: private, include #include diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp index e736be1..deaf5cd 100644 --- a/kapi/include/kapi/cpu.hpp +++ b/kapi/include/kapi/cpu.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KAPI_CPU_HPP #define TEACHOS_KAPI_CPU_HPP -#include "kapi/memory.hpp" +#include #include #include diff --git a/kapi/include/kapi/devices.hpp b/kapi/include/kapi/devices.hpp index ec154a5..b597aa8 100644 --- a/kapi/include/kapi/devices.hpp +++ b/kapi/include/kapi/devices.hpp @@ -1,10 +1,10 @@ #ifndef TEACHOS_KAPI_DEVICES_HPP #define TEACHOS_KAPI_DEVICES_HPP -#include "kapi/devices/bus.hpp" // IWYU pragma: export -#include "kapi/devices/cpu.hpp" // IWYU pragma: export -#include "kapi/devices/device.hpp" // IWYU pragma: export -#include "kapi/devices/manager.hpp" // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export namespace kapi::devices { diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp index 60134ff..59f49f7 100644 --- a/kapi/include/kapi/devices/bus.hpp +++ b/kapi/include/kapi/devices/bus.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_KAPI_DEVICES_BUS_HPP #define TEACHOS_KAPI_DEVICES_BUS_HPP -// IWYU pragma: private, include "kapi/devices.hpp" +// IWYU pragma: private, include -#include "kapi/devices/device.hpp" +#include #include #include diff --git a/kapi/include/kapi/devices/cpu.hpp b/kapi/include/kapi/devices/cpu.hpp index 00766b5..f8ff60c 100644 --- a/kapi/include/kapi/devices/cpu.hpp +++ b/kapi/include/kapi/devices/cpu.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KAPI_DEVICES_CPU_HPP #define TEACHOS_KAPI_DEVICES_CPU_HPP -#include "kapi/devices/bus.hpp" +#include #include #include diff --git a/kapi/include/kapi/devices/device.hpp b/kapi/include/kapi/devices/device.hpp index 9939494..70cf01f 100644 --- a/kapi/include/kapi/devices/device.hpp +++ b/kapi/include/kapi/devices/device.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KAPI_DEVICES_DEVICE_HPP #define TEACH_OS_KAPI_DEVICES_DEVICE_HPP -// IWYU pragma: private, include "kapi/devices.hpp" +// IWYU pragma: private, include #include #include diff --git a/kapi/include/kapi/devices/manager.hpp b/kapi/include/kapi/devices/manager.hpp index f19366e..c9b90b4 100644 --- a/kapi/include/kapi/devices/manager.hpp +++ b/kapi/include/kapi/devices/manager.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_KAPI_DEVICES_MANAGER_HPP #define TEACHOS_KAPI_DEVICES_MANAGER_HPP -// IWYU pragma: private, include "kapi/devices.hpp" +// IWYU pragma: private, include -#include "kapi/devices/device.hpp" +#include #include diff --git a/kapi/include/kapi/memory.hpp b/kapi/include/kapi/memory.hpp index f5e126a..8ad8d6e 100644 --- a/kapi/include/kapi/memory.hpp +++ b/kapi/include/kapi/memory.hpp @@ -1,13 +1,13 @@ #ifndef TEACHOS_KAPI_MEMORY_HPP #define TEACHOS_KAPI_MEMORY_HPP -#include "kapi/memory/address.hpp" // IWYU pragma: export -#include "kapi/memory/chunk.hpp" // IWYU pragma: export -#include "kapi/memory/frame.hpp" // IWYU pragma: export -#include "kapi/memory/frame_allocator.hpp" // IWYU pragma: export -#include "kapi/memory/layout.hpp" // IWYU pragma: export -#include "kapi/memory/page.hpp" // IWYU pragma: export -#include "kapi/memory/page_mapper.hpp" // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #include #include diff --git a/kapi/include/kapi/memory/address.hpp b/kapi/include/kapi/memory/address.hpp index 13bdf4c..9231cfc 100644 --- a/kapi/include/kapi/memory/address.hpp +++ b/kapi/include/kapi/memory/address.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KAPI_MEMORY_ADDRESS_HPP #define TEACHOS_KAPI_MEMORY_ADDRESS_HPP -// IWYU pragma: private, include "kapi/memory.hpp" +// IWYU pragma: private, include #include #include diff --git a/kapi/include/kapi/memory/chunk.hpp b/kapi/include/kapi/memory/chunk.hpp index a046221..485a890 100644 --- a/kapi/include/kapi/memory/chunk.hpp +++ b/kapi/include/kapi/memory/chunk.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KAPI_MEMORY_CHUNK_HPP #define TEACHOS_KAPI_MEMORY_CHUNK_HPP -// IWYU pragma: private, include "kapi/memory.hpp" +// IWYU pragma: private, include #include diff --git a/kapi/include/kapi/memory/frame.hpp b/kapi/include/kapi/memory/frame.hpp index a55b6ff..e423fa4 100644 --- a/kapi/include/kapi/memory/frame.hpp +++ b/kapi/include/kapi/memory/frame.hpp @@ -1,11 +1,11 @@ #ifndef TEACHOS_KAPI_MEMORY_FRAME_HPP #define TEACHOS_KAPI_MEMORY_FRAME_HPP -// IWYU pragma: private, include "kapi/memory.hpp" +// IWYU pragma: private, include -#include "kapi/memory/address.hpp" -#include "kapi/memory/chunk.hpp" -#include "kapi/memory/layout.hpp" +#include +#include +#include #include diff --git a/kapi/include/kapi/memory/frame_allocator.hpp b/kapi/include/kapi/memory/frame_allocator.hpp index cfa8a1c..784ea93 100644 --- a/kapi/include/kapi/memory/frame_allocator.hpp +++ b/kapi/include/kapi/memory/frame_allocator.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_KAPI_MEMORY_FRAME_ALLOCATOR_HPP #define TEACHOS_KAPI_MEMORY_FRAME_ALLOCATOR_HPP -// IWYU pragma: private, include "kapi/memory.hpp" +// IWYU pragma: private, include -#include "kapi/memory/frame.hpp" +#include #include #include diff --git a/kapi/include/kapi/memory/layout.hpp b/kapi/include/kapi/memory/layout.hpp index 26b48d8..733fa96 100644 --- a/kapi/include/kapi/memory/layout.hpp +++ b/kapi/include/kapi/memory/layout.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_KAPI_MEMORY_LAYOUT_HPP #define TEACHOS_KAPI_MEMORY_LAYOUT_HPP -// IWYU pragma: private, include "kapi/memory.hpp" +// IWYU pragma: private, include -#include "kapi/memory/address.hpp" +#include #include diff --git a/kapi/include/kapi/memory/page.hpp b/kapi/include/kapi/memory/page.hpp index aa161ee..d987534 100644 --- a/kapi/include/kapi/memory/page.hpp +++ b/kapi/include/kapi/memory/page.hpp @@ -1,11 +1,11 @@ #ifndef TEACHOS_KAPI_MEMORY_PAGE_HPP #define TEACHOS_KAPI_MEMORY_PAGE_HPP -// IWYU pragma: private, include "kapi/memory.hpp" +// IWYU pragma: private, include -#include "kapi/memory/address.hpp" -#include "kapi/memory/chunk.hpp" -#include "kapi/memory/layout.hpp" +#include +#include +#include #include diff --git a/kapi/include/kapi/memory/page_mapper.hpp b/kapi/include/kapi/memory/page_mapper.hpp index c6052e9..fb600b2 100644 --- a/kapi/include/kapi/memory/page_mapper.hpp +++ b/kapi/include/kapi/memory/page_mapper.hpp @@ -1,10 +1,10 @@ #ifndef TEACHOS_KAPI_MEMORY_PAGE_MAPPER_HPP #define TEACHOS_KAPI_MEMORY_PAGE_MAPPER_HPP -// IWYU pragma: private, include "kapi/memory.hpp" +// IWYU pragma: private, include -#include "kapi/memory/frame.hpp" -#include "kapi/memory/page.hpp" +#include +#include #include diff --git a/kernel/include/kernel/devices/block_device.hpp b/kernel/include/kernel/devices/block_device.hpp index 43e6511..a6d68ee 100644 --- a/kernel/include/kernel/devices/block_device.hpp +++ b/kernel/include/kernel/devices/block_device.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP #define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP -#include "kapi/devices/device.hpp" +#include #include diff --git a/kernel/include/kernel/devices/block_device_utils.hpp b/kernel/include/kernel/devices/block_device_utils.hpp index 7b1daec..8be75b6 100644 --- a/kernel/include/kernel/devices/block_device_utils.hpp +++ b/kernel/include/kernel/devices/block_device_utils.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_UTILS_HPP #define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_UTILS_HPP -#include "kapi/devices/device.hpp" +#include #include diff --git a/kernel/include/kernel/devices/root_bus.hpp b/kernel/include/kernel/devices/root_bus.hpp index 660b715..c8fee52 100644 --- a/kernel/include/kernel/devices/root_bus.hpp +++ b/kernel/include/kernel/devices/root_bus.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KERNEL_DEVICES_ROOT_BUS_HPP #define TEACHOS_KERNEL_DEVICES_ROOT_BUS_HPP -#include "kapi/devices/bus.hpp" +#include namespace kernel::devices { diff --git a/kernel/include/kernel/devices/storage/controller.hpp b/kernel/include/kernel/devices/storage/controller.hpp index a91e452..bea18f3 100644 --- a/kernel/include/kernel/devices/storage/controller.hpp +++ b/kernel/include/kernel/devices/storage/controller.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_CONTROLLER_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_CONTROLLER_HPP -#include "kapi/devices/device.hpp" +#include #include #include diff --git a/kernel/include/kernel/devices/storage/management.hpp b/kernel/include/kernel/devices/storage/management.hpp index b2f42d1..9a84087 100644 --- a/kernel/include/kernel/devices/storage/management.hpp +++ b/kernel/include/kernel/devices/storage/management.hpp @@ -1,9 +1,9 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_MANAGEMENT_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_MANAGEMENT_HPP -#include "kernel/devices/storage/controller.hpp" +#include -#include "kapi/devices/device.hpp" +#include #include #include diff --git a/kernel/include/kernel/devices/storage/ram_disk/controller.hpp b/kernel/include/kernel/devices/storage/ram_disk/controller.hpp index febec95..93cf30d 100644 --- a/kernel/include/kernel/devices/storage/ram_disk/controller.hpp +++ b/kernel/include/kernel/devices/storage/ram_disk/controller.hpp @@ -1,9 +1,9 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_CONTROLLER_HPP -#include "kernel/devices/storage/controller.hpp" +#include -#include "kapi/boot_module/boot_module_registry.hpp" +#include namespace kernel::devices::storage::ram_disk { diff --git a/kernel/include/kernel/devices/storage/ram_disk/device.hpp b/kernel/include/kernel/devices/storage/ram_disk/device.hpp index 0ee0f8f..89789ea 100644 --- a/kernel/include/kernel/devices/storage/ram_disk/device.hpp +++ b/kernel/include/kernel/devices/storage/ram_disk/device.hpp @@ -1,9 +1,9 @@ #ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP #define TEACH_OS_KERNEL_DEVICES_STORAGE_RAM_DISK_DEVICE_HPP -#include "kernel/devices/block_device.hpp" +#include -#include "kapi/boot_module/boot_module.hpp" +#include #include diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index 58a918f..94fa39a 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_DENTRY_HPP #define TEACH_OS_KERNEL_FILESYSTEM_DENTRY_HPP -#include "kernel/filesystem/inode.hpp" +#include #include #include diff --git a/kernel/include/kernel/filesystem/devfs/filesystem.hpp b/kernel/include/kernel/filesystem/devfs/filesystem.hpp index 53bb87d..8d96555 100644 --- a/kernel/include/kernel/filesystem/devfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/devfs/filesystem.hpp @@ -1,9 +1,9 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVFS_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_FILESYSTEM_HPP -#include "kernel/filesystem/device_inode.hpp" -#include "kernel/filesystem/filesystem.hpp" -#include "kernel/filesystem/inode.hpp" +#include +#include +#include #include #include diff --git a/kernel/include/kernel/filesystem/devfs/inode.hpp b/kernel/include/kernel/filesystem/devfs/inode.hpp index c117bd2..0fab280 100644 --- a/kernel/include/kernel/filesystem/devfs/inode.hpp +++ b/kernel/include/kernel/filesystem/devfs/inode.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVFS_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_INODE_HPP -#include "kernel/filesystem/inode.hpp" +#include #include diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index 6afe5bc..2f79fca 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -1,9 +1,9 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP -#include "kernel/filesystem/inode.hpp" +#include -#include "kapi/devices/device.hpp" +#include #include diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 9112866..516e71d 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -1,11 +1,11 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_EXT2_FILESYSTEM_HPP -#include "kernel/filesystem/ext2/block_group_descriptor.hpp" -#include "kernel/filesystem/ext2/inode.hpp" -#include "kernel/filesystem/ext2/superblock.hpp" -#include "kernel/filesystem/filesystem.hpp" -#include "kernel/filesystem/inode.hpp" +#include +#include +#include +#include +#include #include #include diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index a1645cd..9291eea 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP -#include "kernel/filesystem/inode.hpp" +#include #include diff --git a/kernel/include/kernel/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/filesystem/file_descriptor_table.hpp index 5dd91e7..293dc36 100644 --- a/kernel/include/kernel/filesystem/file_descriptor_table.hpp +++ b/kernel/include/kernel/filesystem/file_descriptor_table.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP -#include "open_file_description.hpp" +#include #include #include diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 16c07ad..2fdc0ed 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP -#include "kernel/filesystem/inode.hpp" +#include #include #include diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index 0ac6b2f..72855a0 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -1,8 +1,8 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_MOUNT_HPP #define TEACH_OS_KERNEL_FILESYSTEM_MOUNT_HPP -#include "kernel/filesystem/dentry.hpp" -#include "kernel/filesystem/filesystem.hpp" +#include +#include #include #include diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp index a5cdde6..00277ea 100644 --- a/kernel/include/kernel/filesystem/mount_table.hpp +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_MOUNT_TABLE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_MOUNT_TABLE_HPP -#include "kernel/filesystem/mount.hpp" +#include #include #include diff --git a/kernel/include/kernel/filesystem/open_file_description.hpp b/kernel/include/kernel/filesystem/open_file_description.hpp index 738afd4..fad41e4 100644 --- a/kernel/include/kernel/filesystem/open_file_description.hpp +++ b/kernel/include/kernel/filesystem/open_file_description.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP #define TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP -#include "kernel/filesystem/inode.hpp" +#include #include diff --git a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp index e14a1ee..cc778d8 100644 --- a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp @@ -1,8 +1,8 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_FILESYSTEM_HPP #define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_FILESYSTEM_HPP -#include "kernel/filesystem/filesystem.hpp" -#include "kernel/filesystem/inode.hpp" +#include +#include #include #include diff --git a/kernel/include/kernel/filesystem/rootfs/inode.hpp b/kernel/include/kernel/filesystem/rootfs/inode.hpp index 469e47a..37d0a30 100644 --- a/kernel/include/kernel/filesystem/rootfs/inode.hpp +++ b/kernel/include/kernel/filesystem/rootfs/inode.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_INODE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_INODE_HPP -#include "kernel/filesystem/inode.hpp" +#include #include #include diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 678e645..5b5c868 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -1,10 +1,10 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP #define TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP -#include "kernel/filesystem/dentry.hpp" -#include "kernel/filesystem/filesystem.hpp" -#include "kernel/filesystem/mount_table.hpp" -#include "kernel/filesystem/open_file_description.hpp" +#include +#include +#include +#include #include diff --git a/kernel/include/kernel/memory.hpp b/kernel/include/kernel/memory.hpp index 568dd24..17fb82b 100644 --- a/kernel/include/kernel/memory.hpp +++ b/kernel/include/kernel/memory.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_KERNEL_MEMORY_HPP #define TEACHOS_KERNEL_MEMORY_HPP -#include "kernel/memory/heap_allocator.hpp" // IWYU pragma: export +#include // IWYU pragma: export -#include "kapi/memory.hpp" +#include namespace kernel::memory { diff --git a/kernel/include/kernel/memory/bitmap_allocator.hpp b/kernel/include/kernel/memory/bitmap_allocator.hpp index fb5bf55..370ce64 100644 --- a/kernel/include/kernel/memory/bitmap_allocator.hpp +++ b/kernel/include/kernel/memory/bitmap_allocator.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KERNEL_MEMORY_BITMAP_ALLOCATOR_HPP #define TEACHOS_KERNEL_MEMORY_BITMAP_ALLOCATOR_HPP -#include "kapi/memory.hpp" +#include #include #include diff --git a/kernel/include/kernel/memory/block_list_allocator.hpp b/kernel/include/kernel/memory/block_list_allocator.hpp index c2cb468..51b226e 100644 --- a/kernel/include/kernel/memory/block_list_allocator.hpp +++ b/kernel/include/kernel/memory/block_list_allocator.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_KERNEL_MEMORY_BLOCK_LIST_ALLOCATOR_HPP #define TEACHOS_KERNEL_MEMORY_BLOCK_LIST_ALLOCATOR_HPP -#include "kernel/memory/heap_allocator.hpp" +#include -#include "kapi/memory.hpp" +#include #include #include diff --git a/kernel/include/kernel/memory/mmio_allocator.hpp b/kernel/include/kernel/memory/mmio_allocator.hpp index 4ec6bec..c7a8ed0 100644 --- a/kernel/include/kernel/memory/mmio_allocator.hpp +++ b/kernel/include/kernel/memory/mmio_allocator.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KERNEL_MEMORY_MMIO_ALLOCATOR_HPP #define TEACHOS_KERNEL_MEMORY_MMIO_ALLOCATOR_HPP -#include "kapi/memory.hpp" +#include #include #include diff --git a/kernel/include/kernel/test_support/bump_frame_allocator.hpp b/kernel/include/kernel/test_support/bump_frame_allocator.hpp index 6380294..a8ffd48 100644 --- a/kernel/include/kernel/test_support/bump_frame_allocator.hpp +++ b/kernel/include/kernel/test_support/bump_frame_allocator.hpp @@ -1,7 +1,7 @@ #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 diff --git a/kernel/include/kernel/test_support/cio.hpp b/kernel/include/kernel/test_support/cio.hpp index e990825..afe27e0 100644 --- a/kernel/include/kernel/test_support/cio.hpp +++ b/kernel/include/kernel/test_support/cio.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_CIO_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_CIO_HPP -#include "kernel/test_support/log_buffer.hpp" +#include -#include "kapi/cio.hpp" +#include #include diff --git a/kernel/include/kernel/test_support/devices/block_device.hpp b/kernel/include/kernel/test_support/devices/block_device.hpp index 110872f..89a2bf1 100644 --- a/kernel/include/kernel/test_support/devices/block_device.hpp +++ b/kernel/include/kernel/test_support/devices/block_device.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_BLOCK_DEVICE_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_BLOCK_DEVICE_HPP -#include "kernel/devices/block_device.hpp" +#include #include #include diff --git a/kernel/include/kernel/test_support/devices/character_device.hpp b/kernel/include/kernel/test_support/devices/character_device.hpp index a106cfb..aba183a 100644 --- a/kernel/include/kernel/test_support/devices/character_device.hpp +++ b/kernel/include/kernel/test_support/devices/character_device.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_CHARACTER_DEVICE_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_DEVICES_CHARACTER_DEVICE_HPP -#include "kapi/devices/device.hpp" +#include #include #include diff --git a/kernel/include/kernel/test_support/filesystem/ext2.hpp b/kernel/include/kernel/test_support/filesystem/ext2.hpp index edbda29..107e5a4 100644 --- a/kernel/include/kernel/test_support/filesystem/ext2.hpp +++ b/kernel/include/kernel/test_support/filesystem/ext2.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_EXT2_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_EXT2_HPP -#include "kernel/test_support/devices/block_device.hpp" +#include #include #include diff --git a/kernel/include/kernel/test_support/filesystem/filesystem.hpp b/kernel/include/kernel/test_support/filesystem/filesystem.hpp index 13aade4..dab0892 100644 --- a/kernel/include/kernel/test_support/filesystem/filesystem.hpp +++ b/kernel/include/kernel/test_support/filesystem/filesystem.hpp @@ -1,8 +1,8 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_FILESYSTEM_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_FILESYSTEM_HPP -#include "kernel/filesystem/filesystem.hpp" -#include "kernel/filesystem/inode.hpp" +#include +#include #include diff --git a/kernel/include/kernel/test_support/filesystem/inode.hpp b/kernel/include/kernel/test_support/filesystem/inode.hpp index 6568f24..9d17917 100644 --- a/kernel/include/kernel/test_support/filesystem/inode.hpp +++ b/kernel/include/kernel/test_support/filesystem/inode.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_INODE_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_INODE_HPP -#include "kernel/filesystem/inode.hpp" +#include #include diff --git a/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp b/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp index 4b49684..94a6668 100644 --- a/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp +++ b/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_STORAGE_BOOT_MODULE_FIXTURE_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_STORAGE_BOOT_MODULE_FIXTURE_HPP -#include "kapi/boot_module/boot_module_registry.hpp" +#include #include #include diff --git a/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp b/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp index bd1c289..55f4b29 100644 --- a/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp +++ b/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_STORAGE_BOOT_MODULE_VFS_FIXTURE_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_STORAGE_BOOT_MODULE_VFS_FIXTURE_HPP -#include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" +#include #include #include diff --git a/kernel/include/kernel/test_support/memory.hpp b/kernel/include/kernel/test_support/memory.hpp index 6034a1e..21030a4 100644 --- a/kernel/include/kernel/test_support/memory.hpp +++ b/kernel/include/kernel/test_support/memory.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_MEMORY_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_MEMORY_HPP -#include "kapi/memory.hpp" +#include namespace kernel::tests::memory { diff --git a/kernel/include/kernel/test_support/page_mapper.hpp b/kernel/include/kernel/test_support/page_mapper.hpp index 05f4359..be4403b 100644 --- a/kernel/include/kernel/test_support/page_mapper.hpp +++ b/kernel/include/kernel/test_support/page_mapper.hpp @@ -1,9 +1,9 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_PAGE_MAPPER_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_PAGE_MAPPER_HPP -#include "kernel/test_support/simulated_memory.hpp" +#include -#include "kapi/memory.hpp" +#include #include diff --git a/kernel/include/kernel/test_support/simulated_memory.hpp b/kernel/include/kernel/test_support/simulated_memory.hpp index ef4e3ec..a201c3d 100644 --- a/kernel/include/kernel/test_support/simulated_memory.hpp +++ b/kernel/include/kernel/test_support/simulated_memory.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_SIMULATED_MEMORY_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_SIMULATED_MEMORY_HPP -#include "kapi/memory.hpp" +#include #include diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp index fc9ff31..b6d5cdf 100644 --- a/kernel/kapi/acpi.cpp +++ b/kernel/kapi/acpi.cpp @@ -1,8 +1,8 @@ -#include "kapi/acpi.hpp" +#include -#include "kernel/acpi/manager.hpp" +#include -#include "kapi/system.hpp" +#include #include diff --git a/kernel/kapi/boot_modules.cpp b/kernel/kapi/boot_modules.cpp index 0549368..1ad541b 100644 --- a/kernel/kapi/boot_modules.cpp +++ b/kernel/kapi/boot_modules.cpp @@ -1,6 +1,6 @@ -#include "kapi/boot_modules.hpp" +#include -#include "kapi/system.hpp" +#include #include diff --git a/kernel/kapi/cio.cpp b/kernel/kapi/cio.cpp index d447a6a..96f043c 100644 --- a/kernel/kapi/cio.cpp +++ b/kernel/kapi/cio.cpp @@ -1,4 +1,4 @@ -#include "kapi/cio.hpp" +#include #include #include diff --git a/kernel/kapi/cpu.cpp b/kernel/kapi/cpu.cpp index 13de584..7b1a43b 100644 --- a/kernel/kapi/cpu.cpp +++ b/kernel/kapi/cpu.cpp @@ -1,6 +1,6 @@ -#include "kapi/cpu.hpp" +#include -#include "kapi/system.hpp" +#include #include diff --git a/kernel/kapi/cpu.tests.cpp b/kernel/kapi/cpu.tests.cpp index 85b20fd..9ce2917 100644 --- a/kernel/kapi/cpu.tests.cpp +++ b/kernel/kapi/cpu.tests.cpp @@ -1,6 +1,6 @@ -#include "kapi/cpu.hpp" +#include -#include "kernel/test_support/cpu.hpp" +#include #include diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp index 53bab8c..572227e 100644 --- a/kernel/kapi/devices.cpp +++ b/kernel/kapi/devices.cpp @@ -1,8 +1,8 @@ -#include "kapi/devices.hpp" +#include -#include "kernel/devices/root_bus.hpp" +#include -#include "kapi/system.hpp" +#include #include #include diff --git a/kernel/kapi/devices/bus.cpp b/kernel/kapi/devices/bus.cpp index 5f0dfcd..59753f7 100644 --- a/kernel/kapi/devices/bus.cpp +++ b/kernel/kapi/devices/bus.cpp @@ -1,7 +1,7 @@ -#include "kapi/devices/bus.hpp" +#include -#include "kapi/devices.hpp" -#include "kapi/system.hpp" +#include +#include #include #include diff --git a/kernel/kapi/devices/cpu.cpp b/kernel/kapi/devices/cpu.cpp index 9de5f94..f0e1d72 100644 --- a/kernel/kapi/devices/cpu.cpp +++ b/kernel/kapi/devices/cpu.cpp @@ -1,6 +1,6 @@ -#include "kapi/devices/cpu.hpp" +#include -#include "kapi/devices.hpp" +#include #include #include diff --git a/kernel/kapi/devices/device.cpp b/kernel/kapi/devices/device.cpp index 41d96dc..8b5d6b9 100644 --- a/kernel/kapi/devices/device.cpp +++ b/kernel/kapi/devices/device.cpp @@ -1,6 +1,6 @@ -#include "kapi/devices/device.hpp" +#include -#include "kapi/devices/bus.hpp" +#include #include #include diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index eee3b8b..c8d4c6d 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -1,8 +1,8 @@ -#include "kapi/filesystem.hpp" +#include -#include "kernel/filesystem/file_descriptor_table.hpp" -#include "kernel/filesystem/open_file_description.hpp" -#include "kernel/filesystem/vfs.hpp" +#include +#include +#include #include diff --git a/kernel/kapi/filesystem.tests.cpp b/kernel/kapi/filesystem.tests.cpp index aa24aed..baa8613 100644 --- a/kernel/kapi/filesystem.tests.cpp +++ b/kernel/kapi/filesystem.tests.cpp @@ -1,6 +1,6 @@ -#include "kapi/filesystem.hpp" +#include -#include "kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp" +#include #include diff --git a/kernel/kapi/interrupts.cpp b/kernel/kapi/interrupts.cpp index 0e37bc3..4efcaa3 100644 --- a/kernel/kapi/interrupts.cpp +++ b/kernel/kapi/interrupts.cpp @@ -1,4 +1,4 @@ -#include "kapi/interrupts.hpp" +#include #include #include diff --git a/kernel/kapi/memory.cpp b/kernel/kapi/memory.cpp index 66ccb9c..5ea08b1 100644 --- a/kernel/kapi/memory.cpp +++ b/kernel/kapi/memory.cpp @@ -1,9 +1,9 @@ -#include "kapi/memory.hpp" +#include -#include "kernel/memory/bitmap_allocator.hpp" -#include "kernel/memory/mmio_allocator.hpp" +#include +#include -#include "kapi/system.hpp" +#include #include #include diff --git a/kernel/kapi/system.cpp b/kernel/kapi/system.cpp index a17d9b9..9819ceb 100644 --- a/kernel/kapi/system.cpp +++ b/kernel/kapi/system.cpp @@ -1,6 +1,6 @@ -#include "kapi/system.hpp" +#include -#include "kapi/cpu.hpp" +#include #include diff --git a/kernel/kapi/system.tests.cpp b/kernel/kapi/system.tests.cpp index 687053e..1e30031 100644 --- a/kernel/kapi/system.tests.cpp +++ b/kernel/kapi/system.tests.cpp @@ -1,7 +1,7 @@ -#include "kapi/system.hpp" +#include -#include "kernel/test_support/cio.hpp" -#include "kernel/test_support/cpu.hpp" +#include +#include #include diff --git a/kernel/kstd/os.cpp b/kernel/kstd/os.cpp index 21254c4..ae69e7e 100644 --- a/kernel/kstd/os.cpp +++ b/kernel/kstd/os.cpp @@ -1,4 +1,4 @@ -#include "kapi/system.hpp" +#include #include diff --git a/kernel/kstd/print.cpp b/kernel/kstd/print.cpp index a2e7fe7..d0611b2 100644 --- a/kernel/kstd/print.cpp +++ b/kernel/kstd/print.cpp @@ -1,6 +1,6 @@ #include -#include "kapi/cio.hpp" +#include #include #include diff --git a/kernel/kstd/print.tests.cpp b/kernel/kstd/print.tests.cpp index 8deeb17..4963f46 100644 --- a/kernel/kstd/print.tests.cpp +++ b/kernel/kstd/print.tests.cpp @@ -1,6 +1,6 @@ -#include "kstd/print" +#include -#include "kernel/test_support/cio.hpp" +#include #include diff --git a/kernel/src/acpi/manager.cpp b/kernel/src/acpi/manager.cpp index f17c9cb..99c8860 100644 --- a/kernel/src/acpi/manager.cpp +++ b/kernel/src/acpi/manager.cpp @@ -1,7 +1,7 @@ -#include "kernel/acpi/manager.hpp" +#include -#include "kapi/memory.hpp" -#include "kapi/system.hpp" +#include +#include #include diff --git a/kernel/src/devices/block_device.cpp b/kernel/src/devices/block_device.cpp index b7cb26e..13d73ac 100644 --- a/kernel/src/devices/block_device.cpp +++ b/kernel/src/devices/block_device.cpp @@ -1,7 +1,7 @@ -#include "kernel/devices/block_device.hpp" +#include -#include "kapi/devices/device.hpp" -#include "kapi/system.hpp" +#include +#include #include diff --git a/kernel/src/devices/block_device.tests.cpp b/kernel/src/devices/block_device.tests.cpp index 378437e..a2ddd2b 100644 --- a/kernel/src/devices/block_device.tests.cpp +++ b/kernel/src/devices/block_device.tests.cpp @@ -1,6 +1,6 @@ -#include "kernel/test_support/devices/block_device.hpp" +#include -#include "kernel/test_support/cpu.hpp" +#include #include #include diff --git a/kernel/src/devices/block_device_utils.cpp b/kernel/src/devices/block_device_utils.cpp index 3c77308..cb8ecba 100644 --- a/kernel/src/devices/block_device_utils.cpp +++ b/kernel/src/devices/block_device_utils.cpp @@ -1,9 +1,9 @@ -#include "kernel/devices/block_device_utils.hpp" +#include -#include "kernel/devices/block_device.hpp" +#include -#include "kapi/devices/device.hpp" -#include "kapi/system.hpp" +#include +#include #include #include diff --git a/kernel/src/devices/block_device_utils.tests.cpp b/kernel/src/devices/block_device_utils.tests.cpp index f78e477..e2e1e65 100644 --- a/kernel/src/devices/block_device_utils.tests.cpp +++ b/kernel/src/devices/block_device_utils.tests.cpp @@ -1,8 +1,8 @@ -#include "kernel/devices/block_device_utils.hpp" +#include -#include "kernel/test_support/cpu.hpp" -#include "kernel/test_support/devices/block_device.hpp" -#include "kernel/test_support/devices/character_device.hpp" +#include +#include +#include #include #include diff --git a/kernel/src/devices/root_bus.cpp b/kernel/src/devices/root_bus.cpp index 43a35bf..1b754f2 100644 --- a/kernel/src/devices/root_bus.cpp +++ b/kernel/src/devices/root_bus.cpp @@ -1,6 +1,6 @@ -#include "kernel/devices/root_bus.hpp" +#include -#include "kapi/devices.hpp" +#include namespace kernel::devices { diff --git a/kernel/src/devices/storage/controller.cpp b/kernel/src/devices/storage/controller.cpp index 1bef670..171b918 100644 --- a/kernel/src/devices/storage/controller.cpp +++ b/kernel/src/devices/storage/controller.cpp @@ -1,6 +1,6 @@ -#include "kernel/devices/storage/controller.hpp" +#include -#include "kapi/devices/device.hpp" +#include #include #include diff --git a/kernel/src/devices/storage/management.cpp b/kernel/src/devices/storage/management.cpp index 8ff1b06..7361cd5 100644 --- a/kernel/src/devices/storage/management.cpp +++ b/kernel/src/devices/storage/management.cpp @@ -1,11 +1,11 @@ -#include "kernel/devices/storage/management.hpp" +#include -#include "kernel/devices/storage/controller.hpp" -#include "kernel/devices/storage/ram_disk/controller.hpp" +#include +#include -#include "kapi/boot_modules.hpp" -#include "kapi/devices/device.hpp" -#include "kapi/system.hpp" +#include +#include +#include #include #include diff --git a/kernel/src/devices/storage/ram_disk/controller.cpp b/kernel/src/devices/storage/ram_disk/controller.cpp index d230533..30441fa 100644 --- a/kernel/src/devices/storage/ram_disk/controller.cpp +++ b/kernel/src/devices/storage/ram_disk/controller.cpp @@ -1,8 +1,8 @@ -#include "kernel/devices/storage/ram_disk/controller.hpp" +#include -#include "kernel/devices/storage/ram_disk/device.hpp" +#include -#include "kapi/boot_module/boot_module_registry.hpp" +#include #include diff --git a/kernel/src/devices/storage/ram_disk/device.cpp b/kernel/src/devices/storage/ram_disk/device.cpp index c6a1363..21b0000 100644 --- a/kernel/src/devices/storage/ram_disk/device.cpp +++ b/kernel/src/devices/storage/ram_disk/device.cpp @@ -1,9 +1,9 @@ -#include "kernel/devices/storage/ram_disk/device.hpp" +#include -#include "kernel/devices/block_device.hpp" +#include -#include "kapi/boot_module/boot_module.hpp" -#include "kapi/system.hpp" +#include +#include #include #include diff --git a/kernel/src/devices/storage/ram_disk/device.tests.cpp b/kernel/src/devices/storage/ram_disk/device.tests.cpp index b475c4b..d0fab76 100644 --- a/kernel/src/devices/storage/ram_disk/device.tests.cpp +++ b/kernel/src/devices/storage/ram_disk/device.tests.cpp @@ -1,7 +1,7 @@ -#include "kernel/devices/storage/ram_disk/device.hpp" +#include -#include "kapi/boot_module/boot_module.hpp" -#include "kapi/memory.hpp" +#include +#include #include #include diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index af5ceab..572dd82 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -1,8 +1,8 @@ -#include "kernel/filesystem/dentry.hpp" +#include -#include "kernel/filesystem/inode.hpp" +#include -#include "kapi/system.hpp" +#include #include diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp index a6620d3..f81c260 100644 --- a/kernel/src/filesystem/dentry.tests.cpp +++ b/kernel/src/filesystem/dentry.tests.cpp @@ -1,7 +1,7 @@ -#include "kernel/filesystem/dentry.hpp" +#include -#include "kernel/test_support/cpu.hpp" -#include "kernel/test_support/filesystem/inode.hpp" +#include +#include #include #include diff --git a/kernel/src/filesystem/devfs/filesystem.cpp b/kernel/src/filesystem/devfs/filesystem.cpp index 76b9489..96e40a8 100644 --- a/kernel/src/filesystem/devfs/filesystem.cpp +++ b/kernel/src/filesystem/devfs/filesystem.cpp @@ -1,11 +1,11 @@ -#include "kernel/filesystem/devfs/filesystem.hpp" +#include -#include "kernel/devices/storage/management.hpp" -#include "kernel/filesystem/devfs/inode.hpp" -#include "kernel/filesystem/device_inode.hpp" -#include "kernel/filesystem/inode.hpp" +#include +#include +#include +#include -#include "kapi/devices/device.hpp" +#include #include diff --git a/kernel/src/filesystem/devfs/filesystem.tests.cpp b/kernel/src/filesystem/devfs/filesystem.tests.cpp index f8c4764..2b6c09b 100644 --- a/kernel/src/filesystem/devfs/filesystem.tests.cpp +++ b/kernel/src/filesystem/devfs/filesystem.tests.cpp @@ -1,7 +1,7 @@ -#include "kernel/filesystem/devfs/filesystem.hpp" +#include -#include "kernel/filesystem/filesystem.hpp" -#include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" +#include +#include #include diff --git a/kernel/src/filesystem/devfs/inode.cpp b/kernel/src/filesystem/devfs/inode.cpp index 52cf6fa..0ed66ad 100644 --- a/kernel/src/filesystem/devfs/inode.cpp +++ b/kernel/src/filesystem/devfs/inode.cpp @@ -1,6 +1,6 @@ -#include "kernel/filesystem/devfs/inode.hpp" +#include -#include "kernel/filesystem/inode.hpp" +#include #include diff --git a/kernel/src/filesystem/devfs/inode.tests.cpp b/kernel/src/filesystem/devfs/inode.tests.cpp index 50e34a7..030d709 100644 --- a/kernel/src/filesystem/devfs/inode.tests.cpp +++ b/kernel/src/filesystem/devfs/inode.tests.cpp @@ -1,4 +1,4 @@ -#include "kernel/filesystem/devfs/inode.hpp" +#include #include #include diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index 5793bfc..3bafe06 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -1,10 +1,10 @@ -#include "kernel/filesystem/device_inode.hpp" +#include -#include "kernel/devices/block_device_utils.hpp" -#include "kernel/filesystem/inode.hpp" +#include +#include -#include "kapi/devices/device.hpp" -#include "kapi/system.hpp" +#include +#include #include diff --git a/kernel/src/filesystem/device_inode.tests.cpp b/kernel/src/filesystem/device_inode.tests.cpp index 4e31812..8ac4eff 100644 --- a/kernel/src/filesystem/device_inode.tests.cpp +++ b/kernel/src/filesystem/device_inode.tests.cpp @@ -1,8 +1,8 @@ -#include "kernel/filesystem/device_inode.hpp" +#include -#include "kernel/test_support/cpu.hpp" -#include "kernel/test_support/devices/block_device.hpp" -#include "kernel/test_support/devices/character_device.hpp" +#include +#include +#include #include #include diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index c0f97ed..41572ee 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -1,11 +1,11 @@ -#include "kernel/filesystem/ext2/filesystem.hpp" - -#include "kernel/filesystem/ext2/block_group_descriptor.hpp" -#include "kernel/filesystem/ext2/inode.hpp" -#include "kernel/filesystem/ext2/linked_directory_entry.hpp" -#include "kernel/filesystem/ext2/superblock.hpp" -#include "kernel/filesystem/filesystem.hpp" -#include "kernel/filesystem/inode.hpp" +#include + +#include +#include +#include +#include +#include +#include #include #include diff --git a/kernel/src/filesystem/ext2/filesystem.tests.cpp b/kernel/src/filesystem/ext2/filesystem.tests.cpp index a7b8d5b..31c4c29 100644 --- a/kernel/src/filesystem/ext2/filesystem.tests.cpp +++ b/kernel/src/filesystem/ext2/filesystem.tests.cpp @@ -1,12 +1,12 @@ -#include "kernel/filesystem/ext2/filesystem.hpp" - -#include "kernel/devices/storage/management.hpp" -#include "kernel/filesystem/device_inode.hpp" -#include "kernel/filesystem/ext2/inode.hpp" -#include "kernel/filesystem/filesystem.hpp" -#include "kernel/test_support/devices/block_device.hpp" -#include "kernel/test_support/filesystem/ext2.hpp" -#include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" +#include + +#include +#include +#include +#include +#include +#include +#include #include #include diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index 6b42db6..c45c41e 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -1,9 +1,9 @@ -#include "kernel/filesystem/ext2/inode.hpp" +#include -#include "kernel/filesystem/ext2/filesystem.hpp" -#include "kernel/filesystem/inode.hpp" +#include +#include -#include "kapi/system.hpp" +#include #include #include diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp index ae66aff..4d61790 100644 --- a/kernel/src/filesystem/ext2/inode.tests.cpp +++ b/kernel/src/filesystem/ext2/inode.tests.cpp @@ -1,13 +1,13 @@ -#include "kernel/filesystem/ext2/inode.hpp" - -#include "kernel/devices/storage/management.hpp" -#include "kernel/filesystem/device_inode.hpp" -#include "kernel/filesystem/ext2/filesystem.hpp" -#include "kernel/filesystem/filesystem.hpp" -#include "kernel/test_support/cpu.hpp" -#include "kernel/test_support/devices/block_device.hpp" -#include "kernel/test_support/filesystem/ext2.hpp" -#include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" +#include + +#include +#include +#include +#include +#include +#include +#include +#include #include #include diff --git a/kernel/src/filesystem/file_descriptor_table.cpp b/kernel/src/filesystem/file_descriptor_table.cpp index 4160429..db7692b 100644 --- a/kernel/src/filesystem/file_descriptor_table.cpp +++ b/kernel/src/filesystem/file_descriptor_table.cpp @@ -1,8 +1,8 @@ -#include "kernel/filesystem/file_descriptor_table.hpp" +#include -#include "kernel/filesystem/open_file_description.hpp" +#include -#include "kapi/system.hpp" +#include #include diff --git a/kernel/src/filesystem/file_descriptor_table.tests.cpp b/kernel/src/filesystem/file_descriptor_table.tests.cpp index 5aeadb2..86ed7bf 100644 --- a/kernel/src/filesystem/file_descriptor_table.tests.cpp +++ b/kernel/src/filesystem/file_descriptor_table.tests.cpp @@ -1,7 +1,7 @@ -#include "kernel/filesystem/file_descriptor_table.hpp" +#include -#include "kernel/filesystem/open_file_description.hpp" -#include "kernel/test_support/filesystem/inode.hpp" +#include +#include #include #include diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index f958660..24d0e22 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -1,9 +1,9 @@ -#include "kernel/filesystem/filesystem.hpp" +#include -#include "kernel/filesystem/ext2/filesystem.hpp" -#include "kernel/filesystem/inode.hpp" +#include +#include -#include "kapi/system.hpp" +#include #include diff --git a/kernel/src/filesystem/inode.cpp b/kernel/src/filesystem/inode.cpp index 1cbead8..2f0764c 100644 --- a/kernel/src/filesystem/inode.cpp +++ b/kernel/src/filesystem/inode.cpp @@ -1,4 +1,4 @@ -#include "kernel/filesystem/inode.hpp" +#include namespace kernel::filesystem { diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index 9c8584b..67450b7 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -1,9 +1,9 @@ -#include "kernel/filesystem/mount.hpp" +#include -#include "kernel/filesystem/dentry.hpp" -#include "kernel/filesystem/filesystem.hpp" +#include +#include -#include "kapi/system.hpp" +#include #include #include diff --git a/kernel/src/filesystem/mount.tests.cpp b/kernel/src/filesystem/mount.tests.cpp index 4c4393a..d630464 100644 --- a/kernel/src/filesystem/mount.tests.cpp +++ b/kernel/src/filesystem/mount.tests.cpp @@ -1,9 +1,9 @@ -#include "kernel/filesystem/mount.hpp" +#include -#include "kernel/filesystem/dentry.hpp" -#include "kernel/test_support/cpu.hpp" -#include "kernel/test_support/filesystem/filesystem.hpp" -#include "kernel/test_support/filesystem/inode.hpp" +#include +#include +#include +#include #include #include diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 3b1dee3..da3c451 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -1,7 +1,7 @@ -#include "kernel/filesystem/mount_table.hpp" +#include -#include "kernel/filesystem/dentry.hpp" -#include "kernel/filesystem/mount.hpp" +#include +#include #include #include diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp index 439fe97..747ffdf 100644 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -1,9 +1,9 @@ -#include "kernel/filesystem/mount_table.hpp" +#include -#include "kernel/filesystem/dentry.hpp" -#include "kernel/filesystem/mount.hpp" -#include "kernel/test_support/filesystem/filesystem.hpp" -#include "kernel/test_support/filesystem/inode.hpp" +#include +#include +#include +#include #include #include diff --git a/kernel/src/filesystem/open_file_description.cpp b/kernel/src/filesystem/open_file_description.cpp index f049a34..3033e2b 100644 --- a/kernel/src/filesystem/open_file_description.cpp +++ b/kernel/src/filesystem/open_file_description.cpp @@ -1,6 +1,6 @@ -#include "kernel/filesystem/open_file_description.hpp" +#include -#include "kernel/filesystem/inode.hpp" +#include #include #include diff --git a/kernel/src/filesystem/open_file_description.tests.cpp b/kernel/src/filesystem/open_file_description.tests.cpp index ec35546..ce3c81a 100644 --- a/kernel/src/filesystem/open_file_description.tests.cpp +++ b/kernel/src/filesystem/open_file_description.tests.cpp @@ -1,8 +1,8 @@ -#include "kernel/filesystem/open_file_description.hpp" +#include -#include "kernel/filesystem/vfs.hpp" -#include "kernel/test_support/filesystem/inode.hpp" -#include "kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp" +#include +#include +#include #include #include diff --git a/kernel/src/filesystem/rootfs/filesystem.cpp b/kernel/src/filesystem/rootfs/filesystem.cpp index f718c72..6187c3c 100644 --- a/kernel/src/filesystem/rootfs/filesystem.cpp +++ b/kernel/src/filesystem/rootfs/filesystem.cpp @@ -1,7 +1,7 @@ -#include "kernel/filesystem/rootfs/filesystem.hpp" +#include -#include "kernel/filesystem/inode.hpp" -#include "kernel/filesystem/rootfs/inode.hpp" +#include +#include #include diff --git a/kernel/src/filesystem/rootfs/filesystem.tests.cpp b/kernel/src/filesystem/rootfs/filesystem.tests.cpp index b013f78..81ac9e4 100644 --- a/kernel/src/filesystem/rootfs/filesystem.tests.cpp +++ b/kernel/src/filesystem/rootfs/filesystem.tests.cpp @@ -1,6 +1,6 @@ -#include "kernel/filesystem/rootfs/filesystem.hpp" +#include -#include "kernel/filesystem/filesystem.hpp" +#include #include #include diff --git a/kernel/src/filesystem/rootfs/inode.cpp b/kernel/src/filesystem/rootfs/inode.cpp index 3ca9c02..eeea3fe 100644 --- a/kernel/src/filesystem/rootfs/inode.cpp +++ b/kernel/src/filesystem/rootfs/inode.cpp @@ -1,6 +1,6 @@ -#include "kernel/filesystem/inode.hpp" +#include -#include "kernel/filesystem/rootfs/inode.hpp" +#include #include #include diff --git a/kernel/src/filesystem/rootfs/inode.tests.cpp b/kernel/src/filesystem/rootfs/inode.tests.cpp index a0c5938..879818c 100644 --- a/kernel/src/filesystem/rootfs/inode.tests.cpp +++ b/kernel/src/filesystem/rootfs/inode.tests.cpp @@ -1,4 +1,4 @@ -#include "kernel/filesystem/rootfs/inode.hpp" +#include #include #include diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 23ced4c..5b454f6 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -1,13 +1,13 @@ -#include "kernel/filesystem/vfs.hpp" +#include -#include "kernel/filesystem/dentry.hpp" -#include "kernel/filesystem/devfs/filesystem.hpp" -#include "kernel/filesystem/filesystem.hpp" -#include "kernel/filesystem/mount.hpp" -#include "kernel/filesystem/mount_table.hpp" -#include "kernel/filesystem/rootfs/filesystem.hpp" +#include +#include +#include +#include +#include +#include -#include "kapi/system.hpp" +#include #include diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index eba157d..9cadb4d 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -1,7 +1,7 @@ -#include "kernel/filesystem/vfs.hpp" +#include -#include "kernel/filesystem/open_file_description.hpp" -#include "kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp" +#include +#include #include #include diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 52cbe2e..60b6e6e 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,17 +1,17 @@ -#include "kernel/devices/storage/management.hpp" -#include "kernel/filesystem/device_inode.hpp" -#include "kernel/filesystem/file_descriptor_table.hpp" -#include "kernel/filesystem/open_file_description.hpp" -#include "kernel/filesystem/vfs.hpp" -#include "kernel/memory.hpp" - -#include "kapi/boot_modules.hpp" -#include "kapi/cio.hpp" -#include "kapi/cpu.hpp" -#include "kapi/devices.hpp" -#include "kapi/interrupts.hpp" -#include "kapi/memory.hpp" -#include "kapi/system.hpp" +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include #include #include diff --git a/kernel/src/memory.cpp b/kernel/src/memory.cpp index b5f65fa..6a85c0e 100644 --- a/kernel/src/memory.cpp +++ b/kernel/src/memory.cpp @@ -1,10 +1,10 @@ -#include "kernel/memory.hpp" +#include -#include "kernel/memory/block_list_allocator.hpp" -#include "kernel/memory/heap_allocator.hpp" +#include +#include -#include "kapi/memory.hpp" -#include "kapi/system.hpp" +#include +#include #include #include diff --git a/kernel/src/memory/bitmap_allocator.cpp b/kernel/src/memory/bitmap_allocator.cpp index caaf5a4..240e2af 100644 --- a/kernel/src/memory/bitmap_allocator.cpp +++ b/kernel/src/memory/bitmap_allocator.cpp @@ -1,6 +1,6 @@ -#include "kernel/memory/bitmap_allocator.hpp" +#include -#include "kapi/memory.hpp" +#include #include #include diff --git a/kernel/src/memory/bitmap_allocator.tests.cpp b/kernel/src/memory/bitmap_allocator.tests.cpp index eff6d85..05d11a3 100644 --- a/kernel/src/memory/bitmap_allocator.tests.cpp +++ b/kernel/src/memory/bitmap_allocator.tests.cpp @@ -1,10 +1,10 @@ -#include "kernel/memory/bitmap_allocator.hpp" +#include -#include "kapi/memory.hpp" +#include -#include "catch2/matchers/catch_matchers.hpp" -#include "catch2/matchers/catch_matchers_range_equals.hpp" #include +#include +#include #include #include diff --git a/kernel/src/memory/block_list_allocator.cpp b/kernel/src/memory/block_list_allocator.cpp index 5c416fa..6e68ada 100644 --- a/kernel/src/memory/block_list_allocator.cpp +++ b/kernel/src/memory/block_list_allocator.cpp @@ -1,9 +1,9 @@ -#include "kernel/memory/block_list_allocator.hpp" +#include -#include "kernel/memory/heap_allocator.hpp" +#include -#include "kapi/memory.hpp" -#include "kapi/system.hpp" +#include +#include #include #include diff --git a/kernel/src/memory/block_list_allocator.tests.cpp b/kernel/src/memory/block_list_allocator.tests.cpp index fccdad5..c5f84c5 100644 --- a/kernel/src/memory/block_list_allocator.tests.cpp +++ b/kernel/src/memory/block_list_allocator.tests.cpp @@ -1,8 +1,8 @@ -#include "kernel/memory/block_list_allocator.hpp" +#include -#include "kernel/test_support/memory.hpp" +#include -#include "kapi/memory.hpp" +#include #include diff --git a/kernel/src/memory/mmio_allocator.cpp b/kernel/src/memory/mmio_allocator.cpp index ddbdfc2..ba23dbd 100644 --- a/kernel/src/memory/mmio_allocator.cpp +++ b/kernel/src/memory/mmio_allocator.cpp @@ -1,7 +1,7 @@ -#include "kernel/memory/mmio_allocator.hpp" +#include -#include "kapi/memory.hpp" -#include "kapi/system.hpp" +#include +#include #include diff --git a/kernel/src/memory/operators.cpp b/kernel/src/memory/operators.cpp index dd3b4fa..5673d68 100644 --- a/kernel/src/memory/operators.cpp +++ b/kernel/src/memory/operators.cpp @@ -1,6 +1,6 @@ -#include "kernel/memory.hpp" +#include -#include "kapi/system.hpp" +#include #include diff --git a/kernel/src/test_support/devices/block_device.cpp b/kernel/src/test_support/devices/block_device.cpp index d1d4101..9a9e544 100644 --- a/kernel/src/test_support/devices/block_device.cpp +++ b/kernel/src/test_support/devices/block_device.cpp @@ -1,6 +1,6 @@ -#include "kernel/test_support/devices/block_device.hpp" +#include -#include "kernel/devices/block_device.hpp" +#include #include #include diff --git a/kernel/src/test_support/devices/character_device.cpp b/kernel/src/test_support/devices/character_device.cpp index 9e9227d..3806654 100644 --- a/kernel/src/test_support/devices/character_device.cpp +++ b/kernel/src/test_support/devices/character_device.cpp @@ -1,7 +1,6 @@ +#include -#include "kernel/test_support/devices/character_device.hpp" - -#include "kapi/devices.hpp" +#include #include diff --git a/kernel/src/test_support/filesystem/ext2.cpp b/kernel/src/test_support/filesystem/ext2.cpp index 3627373..5a27b63 100644 --- a/kernel/src/test_support/filesystem/ext2.cpp +++ b/kernel/src/test_support/filesystem/ext2.cpp @@ -1,10 +1,10 @@ -#include "kernel/test_support/filesystem/ext2.hpp" +#include -#include "kernel/filesystem/ext2/block_group_descriptor.hpp" -#include "kernel/filesystem/ext2/filesystem.hpp" -#include "kernel/filesystem/ext2/inode.hpp" -#include "kernel/filesystem/ext2/superblock.hpp" -#include "kernel/test_support/devices/block_device.hpp" +#include +#include +#include +#include +#include #include #include diff --git a/kernel/src/test_support/filesystem/filesystem.cpp b/kernel/src/test_support/filesystem/filesystem.cpp index 225d096..12d43e0 100644 --- a/kernel/src/test_support/filesystem/filesystem.cpp +++ b/kernel/src/test_support/filesystem/filesystem.cpp @@ -1,7 +1,7 @@ -#include "kernel/test_support/filesystem/filesystem.hpp" +#include -#include "kernel/filesystem/inode.hpp" -#include "kernel/test_support/filesystem/inode.hpp" +#include +#include #include diff --git a/kernel/src/test_support/filesystem/inode.cpp b/kernel/src/test_support/filesystem/inode.cpp index 5df7bcd..54bd7e0 100644 --- a/kernel/src/test_support/filesystem/inode.cpp +++ b/kernel/src/test_support/filesystem/inode.cpp @@ -1,6 +1,6 @@ -#include "kernel/test_support/filesystem/inode.hpp" +#include -#include "kernel/filesystem/inode.hpp" +#include #include diff --git a/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp b/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp index 7fa2cb8..aabaace 100644 --- a/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp +++ b/kernel/src/test_support/filesystem/storage_boot_module_fixture.cpp @@ -1,12 +1,12 @@ -#include "kernel/test_support/filesystem/storage_boot_module_fixture.hpp" +#include -#include "kernel/devices/storage/management.hpp" -#include "kernel/test_support/boot_modules.hpp" -#include "kernel/test_support/devices/storage/management.hpp" +#include +#include +#include -#include "kapi/boot_module/boot_module.hpp" -#include "kapi/boot_modules.hpp" -#include "kapi/memory.hpp" +#include +#include +#include #include #include diff --git a/kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp b/kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp index 1a71b57..02ccfec 100644 --- a/kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp +++ b/kernel/src/test_support/filesystem/storage_boot_module_vfs_fixture.cpp @@ -1,7 +1,7 @@ -#include "kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp" +#include -#include "kernel/filesystem/vfs.hpp" -#include "kernel/test_support/filesystem/vfs.hpp" +#include +#include #include #include diff --git a/kernel/src/test_support/kapi/cio.cpp b/kernel/src/test_support/kapi/cio.cpp index 5cdd462..98bc99d 100644 --- a/kernel/src/test_support/kapi/cio.cpp +++ b/kernel/src/test_support/kapi/cio.cpp @@ -1,6 +1,6 @@ -#include "kernel/test_support/cio.hpp" +#include -#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 671097e..5d95633 100644 --- a/kernel/src/test_support/kapi/cpu.cpp +++ b/kernel/src/test_support/kapi/cpu.cpp @@ -1,4 +1,4 @@ -#include "kernel/test_support/cpu.hpp" +#include #include diff --git a/kernel/src/test_support/kapi/memory.cpp b/kernel/src/test_support/kapi/memory.cpp index 3b4a1bd..7fc95cb 100644 --- a/kernel/src/test_support/kapi/memory.cpp +++ b/kernel/src/test_support/kapi/memory.cpp @@ -1,7 +1,7 @@ -#include "kapi/memory.hpp" +#include -#include "kernel/test_support/bump_frame_allocator.hpp" -#include "kernel/test_support/page_mapper.hpp" +#include +#include #include diff --git a/kernel/src/test_support/log_buffer.cpp b/kernel/src/test_support/log_buffer.cpp index 9cff596..04d875b 100644 --- a/kernel/src/test_support/log_buffer.cpp +++ b/kernel/src/test_support/log_buffer.cpp @@ -1,4 +1,4 @@ -#include "kernel/test_support/log_buffer.hpp" +#include #include #include diff --git a/kernel/src/test_support/output_device.cpp b/kernel/src/test_support/output_device.cpp index 505e385..45fb4bc 100644 --- a/kernel/src/test_support/output_device.cpp +++ b/kernel/src/test_support/output_device.cpp @@ -1,7 +1,7 @@ -#include "kernel/test_support/cio.hpp" -#include "kernel/test_support/log_buffer.hpp" +#include +#include -#include "kapi/cio.hpp" +#include #include #include diff --git a/kernel/src/test_support/page_mapper.cpp b/kernel/src/test_support/page_mapper.cpp index 9236deb..3d50ff1 100644 --- a/kernel/src/test_support/page_mapper.cpp +++ b/kernel/src/test_support/page_mapper.cpp @@ -1,6 +1,6 @@ -#include "kernel/test_support/page_mapper.hpp" +#include -#include "kapi/memory.hpp" +#include #include diff --git a/kernel/src/test_support/simulated_memory.cpp b/kernel/src/test_support/simulated_memory.cpp index 2a4f15b..074e6b1 100644 --- a/kernel/src/test_support/simulated_memory.cpp +++ b/kernel/src/test_support/simulated_memory.cpp @@ -1,6 +1,6 @@ -#include "kernel/test_support/simulated_memory.hpp" +#include -#include "kapi/memory.hpp" +#include #include diff --git a/kernel/src/test_support/state_reset_listener.cpp b/kernel/src/test_support/state_reset_listener.cpp index 1967743..1815110 100644 --- a/kernel/src/test_support/state_reset_listener.cpp +++ b/kernel/src/test_support/state_reset_listener.cpp @@ -1,15 +1,15 @@ -#include "kernel/filesystem/file_descriptor_table.hpp" -#include "kernel/test_support/boot_modules.hpp" -#include "kernel/test_support/cio.hpp" -#include "kernel/test_support/cpu.hpp" -#include "kernel/test_support/devices/storage/management.hpp" -#include "kernel/test_support/filesystem/file_descriptor_table.hpp" -#include "kernel/test_support/filesystem/vfs.hpp" -#include "kernel/test_support/memory.hpp" - -#include "kapi/cio.hpp" -#include "kapi/cpu.hpp" -#include "kapi/memory.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include #include #include diff --git a/libs/elf/include/elf/section_header.hpp b/libs/elf/include/elf/section_header.hpp index 2b907cb..b1305ec 100644 --- a/libs/elf/include/elf/section_header.hpp +++ b/libs/elf/include/elf/section_header.hpp @@ -1,7 +1,7 @@ #ifndef ELF_SECTION_HEADER_HPP #define ELF_SECTION_HEADER_HPP -#include "format.hpp" +#include #include #include diff --git a/libs/kstd/include/kstd/bits/format/arg.hpp b/libs/kstd/include/kstd/bits/format/arg.hpp index a9a6ab5..e65b26f 100644 --- a/libs/kstd/include/kstd/bits/format/arg.hpp +++ b/libs/kstd/include/kstd/bits/format/arg.hpp @@ -3,8 +3,8 @@ // IWYU pragma: private, include -#include "error.hpp" -#include "fwd.hpp" +#include +#include #include #include diff --git a/libs/kstd/include/kstd/bits/format/args.hpp b/libs/kstd/include/kstd/bits/format/args.hpp index d1586ac..e8e3114 100644 --- a/libs/kstd/include/kstd/bits/format/args.hpp +++ b/libs/kstd/include/kstd/bits/format/args.hpp @@ -3,10 +3,10 @@ // IWYU pragma: private, include -#include "arg.hpp" -#include "context.hpp" -#include "fwd.hpp" -#include "parse_context.hpp" +#include +#include +#include +#include #include #include diff --git a/libs/kstd/include/kstd/bits/format/context.hpp b/libs/kstd/include/kstd/bits/format/context.hpp index 1883fc8..c166ba9 100644 --- a/libs/kstd/include/kstd/bits/format/context.hpp +++ b/libs/kstd/include/kstd/bits/format/context.hpp @@ -3,8 +3,8 @@ // IWYU pragma: private, include -#include "kstd/bits/format/arg.hpp" -#include "kstd/bits/format/output_buffer.hpp" +#include +#include #include #include diff --git a/libs/kstd/include/kstd/bits/format/error.hpp b/libs/kstd/include/kstd/bits/format/error.hpp index f0863eb..c0cb53d 100644 --- a/libs/kstd/include/kstd/bits/format/error.hpp +++ b/libs/kstd/include/kstd/bits/format/error.hpp @@ -1,7 +1,7 @@ #ifndef KSTD_BITS_FORMAT_ERROR_HPP #define KSTD_BITS_FORMAT_ERROR_HPP -#include "kstd/os/error.hpp" +#include namespace kstd::bits::format { diff --git a/libs/kstd/include/kstd/bits/format/formatter.hpp b/libs/kstd/include/kstd/bits/format/formatter.hpp index 096168d..eb28829 100644 --- a/libs/kstd/include/kstd/bits/format/formatter.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter.hpp @@ -3,9 +3,9 @@ // IWYU pragma: private, include -#include "context.hpp" -#include "error.hpp" -#include "parse_context.hpp" +#include +#include +#include #include #include diff --git a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp index e371cec..cc8d190 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp @@ -1,11 +1,11 @@ #ifndef KSTD_BITS_FORMAT_FORMATTER_BOOL_HPP #define KSTD_BITS_FORMAT_FORMATTER_BOOL_HPP -#include "../context.hpp" -#include "../error.hpp" -#include "../formatter.hpp" -#include "../parse_context.hpp" -#include "../specifiers.hpp" +#include +#include +#include +#include +#include #include #include diff --git a/libs/kstd/include/kstd/bits/format/formatter/byte.hpp b/libs/kstd/include/kstd/bits/format/formatter/byte.hpp index 70d98f4..cc8aece 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/byte.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/byte.hpp @@ -1,9 +1,9 @@ #ifndef KSTD_BITS_FORMAT_FORMATTER_BYTE_HPP #define KSTD_BITS_FORMAT_FORMATTER_BYTE_HPP -#include "../context.hpp" -#include "../formatter.hpp" -#include "integral.hpp" +#include +#include +#include #include diff --git a/libs/kstd/include/kstd/bits/format/formatter/char.hpp b/libs/kstd/include/kstd/bits/format/formatter/char.hpp index ddfefe5..92489a1 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/char.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/char.hpp @@ -1,12 +1,12 @@ #ifndef KSTD_BITS_FORMAT_FORMATTER_CHAR_HPP #define KSTD_BITS_FORMAT_FORMATTER_CHAR_HPP -#include "../context.hpp" -#include "../error.hpp" -#include "../formatter.hpp" -#include "../parse_context.hpp" -#include "../specifiers.hpp" -#include "integral.hpp" +#include +#include +#include +#include +#include +#include #include diff --git a/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp index 9afb974..553c8ca 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp @@ -1,9 +1,9 @@ #ifndef KSTD_BITS_FORMAT_FORMATTER_CSTRING_HPP #define KSTD_BITS_FORMAT_FORMATTER_CSTRING_HPP -#include "../context.hpp" -#include "../formatter.hpp" -#include "string_view.hpp" +#include +#include +#include #include diff --git a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp index e5a234a..d17dc95 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp @@ -1,11 +1,11 @@ #ifndef KSTD_BITS_FORMAT_FORMATTER_INTEGRAL_HPP #define KSTD_BITS_FORMAT_FORMATTER_INTEGRAL_HPP -#include "../context.hpp" -#include "../error.hpp" -#include "../formatter.hpp" -#include "../parse_context.hpp" -#include "../specifiers.hpp" +#include +#include +#include +#include +#include #include #include diff --git a/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp b/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp index 758285d..7832226 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp @@ -1,10 +1,10 @@ #ifndef KSTD_BITS_FORMAT_FORMATTER_ORDERING_HPP #define KSTD_BITS_FORMAT_FORMATTER_ORDERING_HPP -#include "../context.hpp" -#include "../formatter.hpp" -#include "../parse_context.hpp" -#include "../specifiers.hpp" +#include +#include +#include +#include #include diff --git a/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp b/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp index fe75a2f..15f9a5b 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp @@ -1,11 +1,11 @@ #ifndef KSTD_BITS_FORMAT_FORMATTER_POINTER_HPP #define KSTD_BITS_FORMAT_FORMATTER_POINTER_HPP -#include "../context.hpp" -#include "../formatter.hpp" -#include "../parse_context.hpp" -#include "../specifiers.hpp" -#include "integral.hpp" +#include +#include +#include +#include +#include #include #include diff --git a/libs/kstd/include/kstd/bits/format/formatter/range.hpp b/libs/kstd/include/kstd/bits/format/formatter/range.hpp index 54ee7fb..05af06f 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/range.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/range.hpp @@ -1,7 +1,7 @@ #ifndef KSTD_BITS_FORMAT_FORMATTER_RANGE_HPP #define KSTD_BITS_FORMAT_FORMATTER_RANGE_HPP -#include "../formatter.hpp" +#include #include #include diff --git a/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp b/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp index f5b698e..7d74579 100644 --- a/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp +++ b/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp @@ -1,10 +1,10 @@ #ifndef KSTD_BITS_FORMAT_FORMATTER_STRING_VIEW_HPP #define KSTD_BITS_FORMAT_FORMATTER_STRING_VIEW_HPP -#include "../context.hpp" -#include "../error.hpp" -#include "../formatter.hpp" -#include "../parse_context.hpp" +#include +#include +#include +#include #include diff --git a/libs/kstd/include/kstd/bits/format/parse_context.hpp b/libs/kstd/include/kstd/bits/format/parse_context.hpp index 063263b..cab8d72 100644 --- a/libs/kstd/include/kstd/bits/format/parse_context.hpp +++ b/libs/kstd/include/kstd/bits/format/parse_context.hpp @@ -3,7 +3,7 @@ // IWYU pragma: private, include -#include "error.hpp" +#include #include #include diff --git a/libs/kstd/include/kstd/bits/format/specifiers.hpp b/libs/kstd/include/kstd/bits/format/specifiers.hpp index 18c6f66..211c95d 100644 --- a/libs/kstd/include/kstd/bits/format/specifiers.hpp +++ b/libs/kstd/include/kstd/bits/format/specifiers.hpp @@ -3,8 +3,8 @@ // IWYU pragma: private -#include "error.hpp" -#include "parse_context.hpp" +#include +#include #include #include diff --git a/libs/kstd/include/kstd/bits/format/string.hpp b/libs/kstd/include/kstd/bits/format/string.hpp index 40282e4..e7e4088 100644 --- a/libs/kstd/include/kstd/bits/format/string.hpp +++ b/libs/kstd/include/kstd/bits/format/string.hpp @@ -3,10 +3,10 @@ // IWYU pragma: private, include -#include "context.hpp" -#include "error.hpp" -#include "formatter.hpp" -#include "parse_context.hpp" +#include +#include +#include +#include #include #include diff --git a/libs/kstd/include/kstd/bits/observer_ptr.hpp b/libs/kstd/include/kstd/bits/observer_ptr.hpp index 1c5da15..2593d7a 100644 --- a/libs/kstd/include/kstd/bits/observer_ptr.hpp +++ b/libs/kstd/include/kstd/bits/observer_ptr.hpp @@ -3,7 +3,7 @@ // IWYU pragma: private, include -#include "kstd/os/error.hpp" +#include #include #include diff --git a/libs/kstd/include/kstd/format b/libs/kstd/include/kstd/format index 047ea5c..e04b79a 100644 --- a/libs/kstd/include/kstd/format +++ b/libs/kstd/include/kstd/format @@ -1,22 +1,22 @@ #ifndef KSTD_FORMAT_HPP #define KSTD_FORMAT_HPP -#include "bits/format/arg.hpp" // IWYU pragma: export -#include "bits/format/args.hpp" // IWYU pragma: export -#include "bits/format/context.hpp" // IWYU pragma: export -#include "bits/format/formatter.hpp" // IWYU pragma: export -#include "bits/format/formatter/bool.hpp" // IWYU pragma: export -#include "bits/format/formatter/byte.hpp" // IWYU pragma: export -#include "bits/format/formatter/char.hpp" // IWYU pragma: export -#include "bits/format/formatter/cstring.hpp" // IWYU pragma: export -#include "bits/format/formatter/integral.hpp" // IWYU pragma: export -#include "bits/format/formatter/ordering.hpp" // IWYU pragma: export -#include "bits/format/formatter/pointer.hpp" // IWYU pragma: export -#include "bits/format/formatter/range.hpp" // IWYU pragma: export -#include "bits/format/formatter/string_view.hpp" // IWYU pragma: export -#include "bits/format/output_buffer.hpp" // IWYU pragma: export -#include "bits/format/parse_context.hpp" // IWYU pragma: export -#include "bits/format/string.hpp" // IWYU pragma: export -#include "bits/format/vformat.hpp" // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/memory b/libs/kstd/include/kstd/memory index 493f49a..f108c6d 100644 --- a/libs/kstd/include/kstd/memory +++ b/libs/kstd/include/kstd/memory @@ -1,8 +1,8 @@ #ifndef KSTD_MEMORY_HPP #define KSTD_MEMORY_HPP -#include "kstd/bits/observer_ptr.hpp" // IWYU pragma: export -#include "kstd/bits/shared_ptr.hpp" // IWYU pragma: export -#include "kstd/bits/unique_ptr.hpp" // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/os/print.hpp b/libs/kstd/include/kstd/os/print.hpp index b8e0732..36cb43d 100644 --- a/libs/kstd/include/kstd/os/print.hpp +++ b/libs/kstd/include/kstd/os/print.hpp @@ -1,8 +1,8 @@ #ifndef KSTD_OS_PRINT_HPP #define KSTD_OS_PRINT_HPP -#include "kstd/bits/format/args.hpp" -#include "kstd/bits/print_sink.hpp" +#include +#include #include diff --git a/libs/kstd/include/kstd/print b/libs/kstd/include/kstd/print index f91cb04..1033f72 100644 --- a/libs/kstd/include/kstd/print +++ b/libs/kstd/include/kstd/print @@ -1,10 +1,9 @@ #ifndef KSTD_PRINT #define KSTD_PRINT -#include "bits/print_sink.hpp" // IWYU pragma: export -#include "os/print.hpp" - +#include // IWYU pragma: export #include +#include #include diff --git a/libs/kstd/include/kstd/stack b/libs/kstd/include/kstd/stack index 77e6bfd..02e44ea 100644 --- a/libs/kstd/include/kstd/stack +++ b/libs/kstd/include/kstd/stack @@ -1,7 +1,7 @@ #ifndef KSTD_STACK_HPP #define KSTD_STACK_HPP -#include "kstd/vector" +#include #include #include diff --git a/libs/kstd/src/libc/stdlib.cpp b/libs/kstd/src/libc/stdlib.cpp index bb40605..a18fed0 100644 --- a/libs/kstd/src/libc/stdlib.cpp +++ b/libs/kstd/src/libc/stdlib.cpp @@ -1,4 +1,4 @@ -#include "kstd/os/error.hpp" +#include namespace kstd::libc { diff --git a/libs/kstd/src/mutex.cpp b/libs/kstd/src/mutex.cpp index d66cb98..7387657 100644 --- a/libs/kstd/src/mutex.cpp +++ b/libs/kstd/src/mutex.cpp @@ -1,6 +1,6 @@ -#include "kstd/mutex" +#include -#include "kstd/os/error.hpp" +#include #include diff --git a/libs/kstd/src/os/error.cpp b/libs/kstd/src/os/error.cpp index b82158d..f969cb5 100644 --- a/libs/kstd/src/os/error.cpp +++ b/libs/kstd/src/os/error.cpp @@ -1,4 +1,4 @@ -#include "kstd/os/error.hpp" +#include namespace kstd::os { diff --git a/libs/kstd/tests/src/os_panic.cpp b/libs/kstd/tests/src/os_panic.cpp index 3eae6ff..0759763 100644 --- a/libs/kstd/tests/src/os_panic.cpp +++ b/libs/kstd/tests/src/os_panic.cpp @@ -1,4 +1,4 @@ -#include "kstd/tests/os_panic.hpp" +#include #include #include diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index 415ca8e..b838f42 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -1,7 +1,7 @@ #include -#include "kstd/tests/os_panic.hpp" #include +#include #include #include diff --git a/libs/multiboot2/multiboot2/constants.hpp b/libs/multiboot2/multiboot2/constants.hpp index 2198210..57fa940 100644 --- a/libs/multiboot2/multiboot2/constants.hpp +++ b/libs/multiboot2/multiboot2/constants.hpp @@ -1,7 +1,7 @@ #ifndef MULTIBOOT2_CONSTANTS_HPP #define MULTIBOOT2_CONSTANTS_HPP -#include "constants/architecture_id.hpp" // IWYU pragma: export +#include // IWYU pragma: export #include diff --git a/libs/multiboot2/multiboot2/information.hpp b/libs/multiboot2/multiboot2/information.hpp index 5f75fc8..f688fe5 100644 --- a/libs/multiboot2/multiboot2/information.hpp +++ b/libs/multiboot2/multiboot2/information.hpp @@ -6,9 +6,9 @@ #include -#include "multiboot2/information/data.hpp" // IWYU pragma: export -#include "multiboot2/information/iterator.hpp" // IWYU pragma: export -#include "multiboot2/information/tag.hpp" // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #include #include diff --git a/libs/multiboot2/multiboot2/information/data.hpp b/libs/multiboot2/multiboot2/information/data.hpp index 531e2f3..f39a6cb 100644 --- a/libs/multiboot2/multiboot2/information/data.hpp +++ b/libs/multiboot2/multiboot2/information/data.hpp @@ -5,8 +5,8 @@ #include -#include "multiboot2/constants/information_id.hpp" -#include "multiboot2/constants/memory_type.hpp" +#include +#include #include diff --git a/libs/multiboot2/multiboot2/information/iterator.hpp b/libs/multiboot2/multiboot2/information/iterator.hpp index ec0f4a2..bded43e 100644 --- a/libs/multiboot2/multiboot2/information/iterator.hpp +++ b/libs/multiboot2/multiboot2/information/iterator.hpp @@ -3,8 +3,8 @@ // IWYU pragma: private, include -#include "multiboot2/constants/information_id.hpp" -#include "multiboot2/information/tag.hpp" +#include +#include #include #include diff --git a/libs/multiboot2/multiboot2/information/tag.hpp b/libs/multiboot2/multiboot2/information/tag.hpp index 8d90790..0c29299 100644 --- a/libs/multiboot2/multiboot2/information/tag.hpp +++ b/libs/multiboot2/multiboot2/information/tag.hpp @@ -3,7 +3,7 @@ // IWYU pragma: private, include -#include "multiboot2/constants/information_id.hpp" +#include #include #include -- cgit v1.2.3 From 97afdf08922956627073d929bcb2529306333313 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 25 Apr 2026 23:29:27 +0200 Subject: add demo code --- kernel/src/main.cpp | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 60b6e6e..9f4ec1e 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -187,6 +188,64 @@ auto run_test_code() -> void kstd::println("---------------------------------"); } +auto run_demo() -> void +{ + // 1) open a file + auto fd_1 = kapi::filesystem::open("/entrance/tickets.txt"); + if (fd_1 == -1) + { + kstd::os::panic("demo failed"); + } + + // 2) read from the file + kstd::vector buffer_1{10}; + auto bytes_read = kapi::filesystem::read(fd_1, buffer_1.data(), buffer_1.size()); + auto buffer_as_str = std::string_view{reinterpret_cast(buffer_1.data()), static_cast(bytes_read)}; + kstd::println("Read {} bytes from /entrance/tickets.txt: {}", bytes_read, buffer_as_str); + + // 3) show that /entrance/information/info_1.txt is not accessible before mounting + auto fd_before_mount = kapi::filesystem::open("/entrance/information/info_1.txt"); + if (fd_before_mount == -1) + { + kstd::println("/entrance/information/info_1.txt is not accessible before mounting, as expected."); + } + + // 4) mount a new filesystem on top of /entrance + kapi::filesystem::mount("/dev/ram16", "/entrance"); + + // 5) open a file from the new filesystem + auto fd_2 = kapi::filesystem::open("/entrance/information/info_1.txt"); + if (fd_2 == -1) + { + kstd::os::panic("demo failed"); + } + + // 6) read from the new file + kstd::vector buffer_2{10}; + bytes_read = kapi::filesystem::read(fd_2, buffer_2.data(), buffer_2.size()); + buffer_as_str = std::string_view{reinterpret_cast(buffer_2.data()), static_cast(bytes_read)}; + kstd::println("Read {} bytes from /entrance/information/info_1.txt: {}", bytes_read, buffer_as_str); + + // 7) open device as file + auto fd_3 = kapi::filesystem::open("/dev/ram48"); + if (fd_3 == -1) + { + kstd::os::panic("demo failed"); + } + + // 8) read from the device file + kstd::vector buffer_3{2}; + bytes_read = kapi::filesystem::read(fd_3, buffer_3.data(), buffer_3.size()); + kstd::println("Read {} bytes from /dev/ram48: {::#04x}", bytes_read, buffer_3); + + // 9) write to the device file + kstd::vector write_buffer{std::byte{0xAA}, std::byte{0xAA}}; + auto bytes_written = kapi::filesystem::write(fd_3, write_buffer.data(), write_buffer.size()); + kstd::println("Written {} bytes to /dev/ram48: {::#04x}", bytes_written, write_buffer); + + // 10) do memory dump to show that the write to the device file had an effect +} + auto main() -> int { kapi::cio::init(); @@ -221,6 +280,10 @@ auto main() -> int kernel::filesystem::vfs::init(); kstd::println("[OS] Virtual filesystem initialized."); + // TODO BA-FS26 remove demo and test code again? + // run_demo(); + + // TODO BA-FS26 remove demo and test code again? run_test_code(); kstd::println("[TEST] All tests completed."); -- cgit v1.2.3 From 3b82b63aab8dcede2e099b8cbf12e7f3f0407896 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 25 Apr 2026 23:29:54 +0200 Subject: remove unused include --- kernel/include/kernel/filesystem/vfs.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 5b5c868..881f458 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include -- cgit v1.2.3 From c002a6fe53375d8757d43c48c59ac7f327f412b5 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 25 Apr 2026 23:31:26 +0200 Subject: add documentation, refactoring --- kapi/include/kapi/filesystem.hpp | 58 +++++++++++++++++++++++++++++++++++++--- kernel/kapi/filesystem.cpp | 12 ++++----- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/kapi/include/kapi/filesystem.hpp b/kapi/include/kapi/filesystem.hpp index db77bda..94d42ce 100644 --- a/kapi/include/kapi/filesystem.hpp +++ b/kapi/include/kapi/filesystem.hpp @@ -8,14 +8,66 @@ namespace kapi::filesystem { + /** + @brief The kapi::filesystem namespace provides the interface for filesystem operations in the kernel. It includes + functions for mounting and unmounting filesystems, as well as basic file operations such as opening, closing, reading + from, and writing to files. The actual implementation of these functions is in the kernel's filesystem subsystem, + which will handle the specifics of different filesystem types and their interactions with the underlying storage + devices. + */ + + /** + @brief Mounts a filesystem from the specified @p source at the specified @p target path. + @param source The source device or filesystem to mount. + @param target The target mount point. + @return 0 on success, -1 on failure. + @qualifier kernel-defined + */ auto mount(std::string_view source, std::string_view target) -> int; + + /** + @brief Unmounts a filesystem from the specified @p target path. + @param target The target mount point to unmount. + @return 0 on success, -1 on failure. + @qualifier kernel-defined + */ auto umount(std::string_view target) -> int; + /** + @brief Opens a file at the specified @p path. + @param path The path to the file to open. + @return A file descriptor on success, -1 on failure. + @qualifier kernel-defined + */ auto open(std::string_view path) -> int; - auto close(int fd) -> int; - auto read(int fd, void * buffer, size_t size) -> ssize_t; - auto write(int fd, void const * buffer, size_t size) -> ssize_t; + /** + @brief Closes a @p file_descriptor. + @param file_descriptor The file descriptor to close. + @return 0 on success, -1 on failure. + @qualifier kernel-defined + */ + auto close(int file_descriptor) -> int; + + /** + @brief Reads @p size bytes into @p buffer from a @p file_descriptor. + @param file_descriptor The file descriptor to read from. + @param buffer The buffer to store the read data. + @param size The number of bytes to read. + @return The number of bytes read on success, -1 on failure. + @qualifier kernel-defined + */ + auto read(int file_descriptor, void * buffer, size_t size) -> ssize_t; + + /** + @brief Writes @p size bytes from @p buffer to a @p file_descriptor. + @param file_descriptor The file descriptor to write to. + @param buffer The buffer containing the data to write. + @param size The number of bytes to write. + @return The number of bytes written on success, -1 on failure. + @qualifier kernel-defined + */ + auto write(int file_descriptor, void const * buffer, size_t size) -> ssize_t; } // namespace kapi::filesystem #endif // TEACHOS_KAPI_FILESYSTEM_HPP \ No newline at end of file diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index c8d4c6d..741875a 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -42,14 +42,14 @@ namespace kapi::filesystem return -1; } - auto close(int fd) -> int + auto close(int file_descriptor) -> int { - return kernel::filesystem::file_descriptor_table::get().remove_file(fd); + return kernel::filesystem::file_descriptor_table::get().remove_file(file_descriptor); } - auto read(int fd, void * buffer, size_t size) -> ssize_t + auto read(int file_descriptor, void * buffer, size_t size) -> ssize_t { - if (auto open_file_description = kernel::filesystem::file_descriptor_table::get().get_file(fd)) + if (auto open_file_description = kernel::filesystem::file_descriptor_table::get().get_file(file_descriptor)) { return open_file_description->read(buffer, size); } @@ -57,9 +57,9 @@ namespace kapi::filesystem return -1; } - auto write(int fd, void const * buffer, size_t size) -> ssize_t + auto write(int file_descriptor, void const * buffer, size_t size) -> ssize_t { - if (auto open_file_description = kernel::filesystem::file_descriptor_table::get().get_file(fd)) + if (auto open_file_description = kernel::filesystem::file_descriptor_table::get().get_file(file_descriptor)) { return open_file_description->write(buffer, size); } -- cgit v1.2.3 From 4e2624b63236fa309c9ecf53a694b6ac9babf4e6 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 26 Apr 2026 10:11:24 +0200 Subject: rename open_file_description to open_file_descriptor --- kernel/CMakeLists.txt | 4 +- .../kernel/filesystem/file_descriptor_table.hpp | 14 +-- .../kernel/filesystem/open_file_description.hpp | 62 ----------- .../kernel/filesystem/open_file_descriptor.hpp | 62 +++++++++++ kernel/kapi/filesystem.cpp | 14 +-- kernel/src/filesystem/file_descriptor_table.cpp | 12 +-- .../src/filesystem/file_descriptor_table.tests.cpp | 62 +++++------ kernel/src/filesystem/open_file_description.cpp | 41 -------- .../src/filesystem/open_file_description.tests.cpp | 115 --------------------- kernel/src/filesystem/open_file_descriptor.cpp | 40 +++++++ .../src/filesystem/open_file_descriptor.tests.cpp | 114 ++++++++++++++++++++ kernel/src/filesystem/vfs.tests.cpp | 8 +- kernel/src/main.cpp | 16 +-- 13 files changed, 281 insertions(+), 283 deletions(-) delete mode 100644 kernel/include/kernel/filesystem/open_file_description.hpp create mode 100644 kernel/include/kernel/filesystem/open_file_descriptor.hpp delete mode 100644 kernel/src/filesystem/open_file_description.cpp delete mode 100644 kernel/src/filesystem/open_file_description.tests.cpp create mode 100644 kernel/src/filesystem/open_file_descriptor.cpp create mode 100644 kernel/src/filesystem/open_file_descriptor.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index d4484cd..cddec97 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -52,7 +52,7 @@ target_sources("kernel_lib" PRIVATE "src/filesystem/inode.cpp" "src/filesystem/mount_table.cpp" "src/filesystem/mount.cpp" - "src/filesystem/open_file_description.cpp" + "src/filesystem/open_file_descriptor.cpp" "src/filesystem/vfs.cpp" # DevFS Filesystem @@ -195,7 +195,7 @@ if(BUILD_TESTING) "src/filesystem/file_descriptor_table.tests.cpp" "src/filesystem/mount_table.tests.cpp" "src/filesystem/mount.tests.cpp" - "src/filesystem/open_file_description.tests.cpp" + "src/filesystem/open_file_descriptor.tests.cpp" "src/filesystem/vfs.tests.cpp" # Storage Subsystem Tests diff --git a/kernel/include/kernel/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/filesystem/file_descriptor_table.hpp index 293dc36..b0e699b 100644 --- a/kernel/include/kernel/filesystem/file_descriptor_table.hpp +++ b/kernel/include/kernel/filesystem/file_descriptor_table.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP #define TEACH_OS_KERNEL_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP -#include +#include #include #include @@ -10,7 +10,7 @@ namespace kernel::filesystem { /** @brief A table for managing file descriptors in the filesystem. This class provides methods for adding, retrieving, - and removing open file descriptions. + and removing open file descriptors. */ struct file_descriptor_table { @@ -35,17 +35,17 @@ namespace kernel::filesystem /** @brief Add a file to the descriptor table. - @param f The file description to add. + @param f The file descriptor to add. @return The file descriptor index assigned to the file, or -1 on failure. */ - auto add_file(kstd::shared_ptr const & f) -> int; + auto add_file(kstd::shared_ptr const & f) -> int; /** @brief Get a file from the descriptor table. @param fd The file descriptor index to retrieve. - @return A pointer to the requested file description, or a null pointer if not found. + @return A pointer to the requested file descriptor, or a null pointer if not found. */ - [[nodiscard]] auto get_file(int fd) const -> kstd::shared_ptr; + [[nodiscard]] auto get_file(int fd) const -> kstd::shared_ptr; /** @brief Remove a file from the descriptor table. @@ -57,7 +57,7 @@ namespace kernel::filesystem private: file_descriptor_table() = default; - kstd::vector> m_open_files{}; + kstd::vector> m_open_files{}; }; } // namespace kernel::filesystem diff --git a/kernel/include/kernel/filesystem/open_file_description.hpp b/kernel/include/kernel/filesystem/open_file_description.hpp deleted file mode 100644 index fad41e4..0000000 --- a/kernel/include/kernel/filesystem/open_file_description.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTION_HPP - -#include - -#include - -#include - -namespace kernel::filesystem -{ - /** - @brief Represents an open file description in the filesystem. This class encapsulates the state of an open file, - including a reference to the associated inode and the current file offset. - */ - struct open_file_description - { - /** - @brief Constructs an open file description for the given @p inode. - @param inode The inode to associate with the open file description. - */ - explicit open_file_description(kstd::shared_ptr const & inode); - - /** - @brief Destructor for the open file description. - */ - ~open_file_description() = default; - - /** - @brief Reads data from the open file description into a @p buffer, starting at the current file offset and for a - given - @p size. The file offset is advanced by the number of bytes read. - @param buffer The buffer to read data into. - @param size The number of bytes to read. - @return The number of bytes read. - */ - auto read(void * buffer, size_t size) -> size_t; - - /** - @brief Writes data to the open file description from a @p buffer, starting at the current file offset and for a - given - @p size. The file offset is advanced by the number of bytes written. - @param buffer The buffer to write data from. - @param size The number of bytes to write. - @return The number of bytes written. - */ - auto write(void const * buffer, size_t size) -> size_t; - - /** - @brief Returns the current file offset for this open file description. - @return The current file offset in bytes. - */ - [[nodiscard]] auto offset() const -> size_t; - - private: - kstd::shared_ptr m_inode; - size_t m_offset; - }; - -} // namespace kernel::filesystem - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/open_file_descriptor.hpp b/kernel/include/kernel/filesystem/open_file_descriptor.hpp new file mode 100644 index 0000000..036dcf0 --- /dev/null +++ b/kernel/include/kernel/filesystem/open_file_descriptor.hpp @@ -0,0 +1,62 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTOR_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTOR_HPP + +#include + +#include + +#include + +namespace kernel::filesystem +{ + /** + @brief Represents an open file descriptor in the filesystem. This class encapsulates the state of an open file, + including a reference to the associated inode and the current file offset. + */ + struct open_file_descriptor + { + /** + @brief Constructs an open file descriptor for the given @p inode. + @param inode The inode to associate with the open file descriptor. + */ + explicit open_file_descriptor(kstd::shared_ptr const & inode); + + /** + @brief Destructor for the open file descriptor. + */ + ~open_file_descriptor() = default; + + /** + @brief Reads data from the open file descriptor into a @p buffer, starting at the current file offset and for a + given + @p size. The file offset is advanced by the number of bytes read. + @param buffer The buffer to read data into. + @param size The number of bytes to read. + @return The number of bytes read. + */ + auto read(void * buffer, size_t size) -> size_t; + + /** + @brief Writes data to the open file descriptor from a @p buffer, starting at the current file offset and for a + given + @p size. The file offset is advanced by the number of bytes written. + @param buffer The buffer to write data from. + @param size The number of bytes to write. + @return The number of bytes written. + */ + auto write(void const * buffer, size_t size) -> size_t; + + /** + @brief Returns the current file offset for this open file descriptor. + @return The current file offset in bytes. + */ + [[nodiscard]] auto offset() const -> size_t; + + private: + kstd::shared_ptr m_inode; + size_t m_offset; + }; + +} // namespace kernel::filesystem + +#endif \ No newline at end of file diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index 741875a..8481f20 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include @@ -35,8 +35,8 @@ namespace kapi::filesystem { if (auto dentry = kernel::filesystem::vfs::get().open(path)) { - auto open_file_description = kstd::make_shared(dentry->get_inode()); - return kernel::filesystem::file_descriptor_table::get().add_file(open_file_description); + auto open_file_descriptor = kstd::make_shared(dentry->get_inode()); + return kernel::filesystem::file_descriptor_table::get().add_file(open_file_descriptor); } return -1; @@ -49,9 +49,9 @@ namespace kapi::filesystem auto read(int file_descriptor, void * buffer, size_t size) -> ssize_t { - if (auto open_file_description = kernel::filesystem::file_descriptor_table::get().get_file(file_descriptor)) + if (auto open_file_descriptor = kernel::filesystem::file_descriptor_table::get().get_file(file_descriptor)) { - return open_file_description->read(buffer, size); + return open_file_descriptor->read(buffer, size); } return -1; @@ -59,9 +59,9 @@ namespace kapi::filesystem auto write(int file_descriptor, void const * buffer, size_t size) -> ssize_t { - if (auto open_file_description = kernel::filesystem::file_descriptor_table::get().get_file(file_descriptor)) + if (auto open_file_descriptor = kernel::filesystem::file_descriptor_table::get().get_file(file_descriptor)) { - return open_file_description->write(buffer, size); + return open_file_descriptor->write(buffer, size); } return -1; diff --git a/kernel/src/filesystem/file_descriptor_table.cpp b/kernel/src/filesystem/file_descriptor_table.cpp index db7692b..9361f37 100644 --- a/kernel/src/filesystem/file_descriptor_table.cpp +++ b/kernel/src/filesystem/file_descriptor_table.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include @@ -37,9 +37,9 @@ namespace kernel::filesystem return *global_file_descriptor_table; } - auto file_descriptor_table::add_file(kstd::shared_ptr const & file_description) -> int + auto file_descriptor_table::add_file(kstd::shared_ptr const & file_descriptor) -> int { - if (!file_description) + if (!file_descriptor) { return -1; } @@ -47,15 +47,15 @@ namespace kernel::filesystem auto it = std::ranges::find_if(m_open_files, [](auto const & open_file) { return open_file == nullptr; }); if (it != m_open_files.end()) { - *it = file_description; + *it = file_descriptor; return static_cast(it - m_open_files.begin()); } - m_open_files.push_back(file_description); + m_open_files.push_back(file_descriptor); return static_cast(m_open_files.size() - 1); } - auto file_descriptor_table::get_file(int fd) const -> kstd::shared_ptr + auto file_descriptor_table::get_file(int fd) const -> kstd::shared_ptr { if (fd < 0) { diff --git a/kernel/src/filesystem/file_descriptor_table.tests.cpp b/kernel/src/filesystem/file_descriptor_table.tests.cpp index 86ed7bf..dd04e00 100644 --- a/kernel/src/filesystem/file_descriptor_table.tests.cpp +++ b/kernel/src/filesystem/file_descriptor_table.tests.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include #include @@ -11,18 +11,18 @@ SCENARIO("File descriptor table add/get file", "[filesystem][file_descriptor_table]") { - GIVEN("a file descriptor table and an open file description") + GIVEN("a file descriptor table and an open file descriptor") { auto & table = kernel::filesystem::file_descriptor_table::get(); auto inode = kstd::make_shared(); - auto file_description_1 = kstd::make_shared(inode); - auto file_description_2 = kstd::make_shared(inode); + auto file_descriptor_1 = kstd::make_shared(inode); + auto file_descriptor_2 = kstd::make_shared(inode); - WHEN("adding the open file description to the file descriptor table") + WHEN("adding the open file descriptor to the file descriptor table") { - auto fd_1 = table.add_file(file_description_1); - auto fd_2 = table.add_file(file_description_2); - auto fd_3 = table.add_file(file_description_2); + auto fd_1 = table.add_file(file_descriptor_1); + auto fd_2 = table.add_file(file_descriptor_2); + auto fd_3 = table.add_file(file_descriptor_2); THEN("a valid file descriptor is returned") { @@ -31,67 +31,67 @@ SCENARIO("File descriptor table add/get file", "[filesystem][file_descriptor_tab REQUIRE(fd_3 == 2); } - THEN("the file description can be retrieved using the returned file descriptor") + THEN("the file descriptor can be retrieved using the returned file descriptor") { - auto retrieved_description = table.get_file(fd_1); - REQUIRE(retrieved_description == file_description_1); + auto retrieved_descriptor = table.get_file(fd_1); + REQUIRE(retrieved_descriptor == file_descriptor_1); } } } - GIVEN("a invalid open file description") + GIVEN("a invalid open file descriptor") { auto & table = kernel::filesystem::file_descriptor_table::get(); - THEN("adding a null file description returns an error code") + THEN("adding a null file descriptor returns an error code") { auto fd = table.add_file(nullptr); REQUIRE(fd == -1); } - THEN("retrieving a file description with a negative file descriptor returns a null pointer") + THEN("retrieving a file descriptor with a negative file descriptor returns a null pointer") { - auto retrieved_description = table.get_file(-1); - REQUIRE(retrieved_description == nullptr); + auto retrieved_descriptor = table.get_file(-1); + REQUIRE(retrieved_descriptor == nullptr); } - THEN("retrieving a file description with an out-of-bounds file descriptor returns a null pointer") + THEN("retrieving a file descriptor with an out-of-bounds file descriptor returns a null pointer") { - auto retrieved_description = table.get_file(1000); - REQUIRE(retrieved_description == nullptr); + auto retrieved_descriptor = table.get_file(1000); + REQUIRE(retrieved_descriptor == nullptr); } } } SCENARIO("File descriptor table remove file", "[filesystem][file_descriptor_table]") { - GIVEN("a file descriptor table with an open file description") + GIVEN("a file descriptor table with an open file descriptor") { auto & table = kernel::filesystem::file_descriptor_table::get(); auto inode = kstd::make_shared(); - auto file_description = kstd::make_shared(inode); - auto fd = table.add_file(file_description); + auto file_descriptor = kstd::make_shared(inode); + auto fd = table.add_file(file_descriptor); - WHEN("removing the file description using the file descriptor") + WHEN("removing the file descriptor using the file descriptor") { table.remove_file(fd); - THEN("the file description can no longer be retrieved using the file descriptor") + THEN("the file descriptor can no longer be retrieved using the file descriptor") { - auto retrieved_description = table.get_file(fd); - REQUIRE(retrieved_description == nullptr); + auto retrieved_descriptor = table.get_file(fd); + REQUIRE(retrieved_descriptor == nullptr); } } - WHEN("removing a file description the other file descriptor keep the same index") + WHEN("removing a file descriptor the other file descriptor keep the same index") { - auto fd2 = table.add_file(file_description); + auto fd2 = table.add_file(file_descriptor); table.remove_file(fd); - THEN("the second file description can still be retrieved using its file descriptor") + THEN("the second file descriptor can still be retrieved using its file descriptor") { - auto retrieved_description = table.get_file(fd2); - REQUIRE(retrieved_description == file_description); + auto retrieved_descriptor = table.get_file(fd2); + REQUIRE(retrieved_descriptor == file_descriptor); } } } diff --git a/kernel/src/filesystem/open_file_description.cpp b/kernel/src/filesystem/open_file_description.cpp deleted file mode 100644 index 3033e2b..0000000 --- a/kernel/src/filesystem/open_file_description.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include - -#include - -#include -#include - -#include - -namespace kernel::filesystem -{ - open_file_description::open_file_description(kstd::shared_ptr const & inode) - : m_inode(inode) - , m_offset(0) - { - if (!inode) - { - kstd::os::panic("[FILESYSTEM] open_file_description constructed with null inode."); - } - } - - auto open_file_description::read(void * buffer, size_t size) -> size_t - { - auto read_bytes = m_inode->read(buffer, m_offset, size); - m_offset += read_bytes; - return read_bytes; - } - - auto open_file_description::write(void const * buffer, size_t size) -> size_t - { - auto written_bytes = m_inode->write(buffer, m_offset, size); - m_offset += written_bytes; - return written_bytes; - } - - auto open_file_description::offset() const -> size_t - { - return m_offset; - } - -} // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/open_file_description.tests.cpp b/kernel/src/filesystem/open_file_description.tests.cpp deleted file mode 100644 index ce3c81a..0000000 --- a/kernel/src/filesystem/open_file_description.tests.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include - -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -SCENARIO("Open file description construction", "[filesystem][open_file_description]") -{ - GIVEN("an inode and an open file description for that inode") - { - auto inode = kstd::make_shared(); - auto file_description = kernel::filesystem::open_file_description{inode}; - - THEN("the initial offset is zero") - { - REQUIRE(file_description.offset() == 0); - } - } -} - -SCENARIO("Open file description read/write offset management", "[filesystem][open_file_description]") -{ - GIVEN("an inode that tracks read/write calls and an open file description for that inode") - { - auto inode = kstd::make_shared(); - auto file_description = kernel::filesystem::open_file_description{inode}; - - THEN("the offset is updated correctly after reads") - { - REQUIRE(file_description.read(nullptr, 100) == 100); - REQUIRE(file_description.offset() == 100); - REQUIRE(file_description.read(nullptr, 50) == 50); - REQUIRE(file_description.offset() == 150); - } - - THEN("the offset is updated correctly after writes") - { - REQUIRE(file_description.write(nullptr, 200) == 200); - REQUIRE(file_description.offset() == 200); - REQUIRE(file_description.write(nullptr, 25) == 25); - REQUIRE(file_description.offset() == 225); - } - - THEN("reads and writes both update the same offset") - { - REQUIRE(file_description.read(nullptr, 10) == 10); - REQUIRE(file_description.offset() == 10); - REQUIRE(file_description.write(nullptr, 20) == 20); - REQUIRE(file_description.offset() == 30); - REQUIRE(file_description.read(nullptr, 5) == 5); - REQUIRE(file_description.offset() == 35); - REQUIRE(file_description.write(nullptr, 15) == 15); - REQUIRE(file_description.offset() == 50); - } - } -} - -SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, - "Open file description read with real image", "[filesystem][open_file_description][img]") -{ - auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; - - GIVEN("an open file description for a file in a real image") - { - REQUIRE(std::filesystem::exists(image_path)); - REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module"}, {image_path})); - - auto & vfs = kernel::filesystem::vfs::get(); - auto dentry = vfs.open("/information/info_1.txt"); - REQUIRE(dentry != nullptr); - auto ofd = kstd::make_shared(dentry->get_inode()); - - THEN("the file can be read and the offset is updated") - { - kstd::vector buffer(32); - auto bytes_read = ofd->read(buffer.data(), buffer.size()); - REQUIRE(bytes_read == 32); - REQUIRE(ofd->offset() == 32); - - std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; - auto const content_end = buffer_as_str.find('\0'); - REQUIRE(buffer_as_str.substr(0, content_end) == "info_1\n"); - - for (auto i = content_end; i < buffer_as_str.size(); ++i) - { - REQUIRE(buffer_as_str[i] == '\0'); - } - } - - THEN("the file can be read multiple times") - { - kstd::vector buffer(4); - auto bytes_read_1 = ofd->read(buffer.data(), buffer.size() / 2); - REQUIRE(bytes_read_1 == buffer.size() / 2); - REQUIRE(ofd->offset() == buffer.size() / 2); - - auto bytes_read_2 = ofd->read(buffer.data() + buffer.size() / 2, buffer.size() / 2); - REQUIRE(bytes_read_2 == buffer.size() / 2); - REQUIRE(ofd->offset() == buffer.size()); - - std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read_1 + bytes_read_2}; - REQUIRE(buffer_as_str == "info"); - } - } -} diff --git a/kernel/src/filesystem/open_file_descriptor.cpp b/kernel/src/filesystem/open_file_descriptor.cpp new file mode 100644 index 0000000..25bffbd --- /dev/null +++ b/kernel/src/filesystem/open_file_descriptor.cpp @@ -0,0 +1,40 @@ +#include +#include + +#include +#include + +#include + +namespace kernel::filesystem +{ + open_file_descriptor::open_file_descriptor(kstd::shared_ptr const & inode) + : m_inode(inode) + , m_offset(0) + { + if (!inode) + { + kstd::os::panic("[FILESYSTEM] open_file_descriptor constructed with null inode."); + } + } + + auto open_file_descriptor::read(void * buffer, size_t size) -> size_t + { + auto read_bytes = m_inode->read(buffer, m_offset, size); + m_offset += read_bytes; + return read_bytes; + } + + auto open_file_descriptor::write(void const * buffer, size_t size) -> size_t + { + auto written_bytes = m_inode->write(buffer, m_offset, size); + m_offset += written_bytes; + return written_bytes; + } + + auto open_file_descriptor::offset() const -> size_t + { + return m_offset; + } + +} // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/open_file_descriptor.tests.cpp b/kernel/src/filesystem/open_file_descriptor.tests.cpp new file mode 100644 index 0000000..095e203 --- /dev/null +++ b/kernel/src/filesystem/open_file_descriptor.tests.cpp @@ -0,0 +1,114 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +SCENARIO("Open file descriptor construction", "[filesystem][open_file_descriptor]") +{ + GIVEN("an inode and an open file descriptor for that inode") + { + auto inode = kstd::make_shared(); + auto file_descriptor = kernel::filesystem::open_file_descriptor{inode}; + + THEN("the initial offset is zero") + { + REQUIRE(file_descriptor.offset() == 0); + } + } +} + +SCENARIO("Open file descriptor read/write offset management", "[filesystem][open_file_descriptor]") +{ + GIVEN("an inode that tracks read/write calls and an open file descriptor for that inode") + { + auto inode = kstd::make_shared(); + auto file_descriptor = kernel::filesystem::open_file_descriptor{inode}; + + THEN("the offset is updated correctly after reads") + { + REQUIRE(file_descriptor.read(nullptr, 100) == 100); + REQUIRE(file_descriptor.offset() == 100); + REQUIRE(file_descriptor.read(nullptr, 50) == 50); + REQUIRE(file_descriptor.offset() == 150); + } + + THEN("the offset is updated correctly after writes") + { + REQUIRE(file_descriptor.write(nullptr, 200) == 200); + REQUIRE(file_descriptor.offset() == 200); + REQUIRE(file_descriptor.write(nullptr, 25) == 25); + REQUIRE(file_descriptor.offset() == 225); + } + + THEN("reads and writes both update the same offset") + { + REQUIRE(file_descriptor.read(nullptr, 10) == 10); + REQUIRE(file_descriptor.offset() == 10); + REQUIRE(file_descriptor.write(nullptr, 20) == 20); + REQUIRE(file_descriptor.offset() == 30); + REQUIRE(file_descriptor.read(nullptr, 5) == 5); + REQUIRE(file_descriptor.offset() == 35); + REQUIRE(file_descriptor.write(nullptr, 15) == 15); + REQUIRE(file_descriptor.offset() == 50); + } + } +} + +SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Open file descriptor read with real image", + "[filesystem][open_file_descriptor][img]") +{ + auto const image_path = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; + + GIVEN("an open file descriptor for a file in a real image") + { + REQUIRE(std::filesystem::exists(image_path)); + REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module"}, {image_path})); + + auto & vfs = kernel::filesystem::vfs::get(); + auto dentry = vfs.open("/information/info_1.txt"); + REQUIRE(dentry != nullptr); + auto ofd = kstd::make_shared(dentry->get_inode()); + + THEN("the file can be read and the offset is updated") + { + kstd::vector buffer(32); + auto bytes_read = ofd->read(buffer.data(), buffer.size()); + REQUIRE(bytes_read == 32); + REQUIRE(ofd->offset() == 32); + + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; + auto const content_end = buffer_as_str.find('\0'); + REQUIRE(buffer_as_str.substr(0, content_end) == "info_1\n"); + + for (auto i = content_end; i < buffer_as_str.size(); ++i) + { + REQUIRE(buffer_as_str[i] == '\0'); + } + } + + THEN("the file can be read multiple times") + { + kstd::vector buffer(4); + auto bytes_read_1 = ofd->read(buffer.data(), buffer.size() / 2); + REQUIRE(bytes_read_1 == buffer.size() / 2); + REQUIRE(ofd->offset() == buffer.size() / 2); + + auto bytes_read_2 = ofd->read(buffer.data() + buffer.size() / 2, buffer.size() / 2); + REQUIRE(bytes_read_2 == buffer.size() / 2); + REQUIRE(ofd->offset() == buffer.size()); + + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read_1 + bytes_read_2}; + REQUIRE(buffer_as_str == "info"); + } + } +} diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 9cadb4d..979ea42 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include #include @@ -185,7 +185,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto dentry = vfs.open("/information/sheep_1.txt"); REQUIRE(dentry != nullptr); - auto sheep_1_ofd = kstd::make_shared(dentry->get_inode()); + auto sheep_1_ofd = kstd::make_shared(dentry->get_inode()); kstd::vector buffer(7); auto bytes_read = sheep_1_ofd->read(buffer.data(), buffer.size()); @@ -210,8 +210,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(sheep_1 != nullptr); REQUIRE(goat_1 != nullptr); - auto sheep_1_ofd = kstd::make_shared(sheep_1->get_inode()); - auto goat_1_ofd = kstd::make_shared(goat_1->get_inode()); + auto sheep_1_ofd = kstd::make_shared(sheep_1->get_inode()); + auto goat_1_ofd = kstd::make_shared(goat_1->get_inode()); kstd::vector sheep_buffer(7); auto bytes_read = sheep_1_ofd->read(sheep_buffer.data(), sheep_buffer.size()); diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 9f4ec1e..9878d20 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include @@ -36,7 +36,7 @@ auto test_device_names() -> void }); } -auto test_file_description_manually() -> void +auto test_file_descriptor_manually() -> void { // setup auto fd_table = kernel::filesystem::file_descriptor_table::get(); @@ -45,7 +45,7 @@ auto test_file_description_manually() -> void auto dev_node = kstd::make_shared(device); - auto ofd = kstd::make_shared(dev_node); + auto ofd = kstd::make_shared(dev_node); auto fd_index = fd_table.add_file(ofd); // use: read two bytes and write two again @@ -71,7 +71,7 @@ auto test_file_description_manually() -> void fd_table.remove_file(fd_index); // use: read four bytes again -> two old bytes two new bytes - auto ofd1 = kstd::make_shared(dev_node); + auto ofd1 = kstd::make_shared(dev_node); fd_index = fd_table.add_file(ofd1); auto fd1 = fd_table.get_file(fd_index); @@ -96,7 +96,7 @@ auto test_device_with_vfs() -> void } auto fd_table = kernel::filesystem::file_descriptor_table::get(); - auto ofd = kstd::make_shared(dentry->get_inode()); + auto ofd = kstd::make_shared(dentry->get_inode()); auto fd = fd_table.add_file(ofd); kstd::vector buffer{2}; auto file = fd_table.get_file(fd); @@ -122,7 +122,7 @@ auto test_file_lookup() -> void } kstd::vector buffer{32}; - auto ofd = kstd::make_shared(dentry->get_inode()); + auto ofd = kstd::make_shared(dentry->get_inode()); auto number_of_read_bytes = ofd->read(buffer.data(), buffer.size()); kstd::println("read bytes: {}", number_of_read_bytes); kstd::println("buffer: {::#04x}", buffer); @@ -175,8 +175,8 @@ auto run_test_code() -> void test_device_names(); kstd::println("---------------------------------"); - kstd::println("[TEST] file description manually"); - test_file_description_manually(); + kstd::println("[TEST] file descriptor manually"); + test_file_descriptor_manually(); kstd::println("---------------------------------"); kstd::println("[TEST] device with VFS"); -- cgit v1.2.3 From e0854bc0aad9e59d67fbf300cb223b116b127ffc Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 26 Apr 2026 12:36:26 +0200 Subject: rename file_descriptor_table to open_file_table --- kernel/CMakeLists.txt | 4 +- .../kernel/filesystem/file_descriptor_table.hpp | 64 ------------ .../include/kernel/filesystem/open_file_table.hpp | 63 ++++++++++++ .../filesystem/file_descriptor_table.hpp | 10 +- kernel/kapi/filesystem.cpp | 10 +- kernel/src/filesystem/file_descriptor_table.cpp | 98 ------------------ .../src/filesystem/file_descriptor_table.tests.cpp | 113 --------------------- kernel/src/filesystem/open_file_table.cpp | 98 ++++++++++++++++++ kernel/src/filesystem/open_file_table.tests.cpp | 113 +++++++++++++++++++++ kernel/src/main.cpp | 10 +- kernel/src/test_support/state_reset_listener.cpp | 8 +- 11 files changed, 295 insertions(+), 296 deletions(-) delete mode 100644 kernel/include/kernel/filesystem/file_descriptor_table.hpp create mode 100644 kernel/include/kernel/filesystem/open_file_table.hpp delete mode 100644 kernel/src/filesystem/file_descriptor_table.cpp delete mode 100644 kernel/src/filesystem/file_descriptor_table.tests.cpp create mode 100644 kernel/src/filesystem/open_file_table.cpp create mode 100644 kernel/src/filesystem/open_file_table.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index cddec97..909ccf7 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -47,12 +47,12 @@ target_sources("kernel_lib" PRIVATE # Filesystem Subsystem "src/filesystem/dentry.cpp" "src/filesystem/device_inode.cpp" - "src/filesystem/file_descriptor_table.cpp" "src/filesystem/filesystem.cpp" "src/filesystem/inode.cpp" "src/filesystem/mount_table.cpp" "src/filesystem/mount.cpp" "src/filesystem/open_file_descriptor.cpp" + "src/filesystem/open_file_table.cpp" "src/filesystem/vfs.cpp" # DevFS Filesystem @@ -192,10 +192,10 @@ if(BUILD_TESTING) "src/filesystem/rootfs/inode.tests.cpp" "src/filesystem/dentry.tests.cpp" "src/filesystem/device_inode.tests.cpp" - "src/filesystem/file_descriptor_table.tests.cpp" "src/filesystem/mount_table.tests.cpp" "src/filesystem/mount.tests.cpp" "src/filesystem/open_file_descriptor.tests.cpp" + "src/filesystem/open_file_table.tests.cpp" "src/filesystem/vfs.tests.cpp" # Storage Subsystem Tests diff --git a/kernel/include/kernel/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/filesystem/file_descriptor_table.hpp deleted file mode 100644 index b0e699b..0000000 --- a/kernel/include/kernel/filesystem/file_descriptor_table.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef TEACH_OS_KERNEL_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP -#define TEACH_OS_KERNEL_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP - -#include - -#include -#include - -namespace kernel::filesystem -{ - /** - @brief A table for managing file descriptors in the filesystem. This class provides methods for adding, retrieving, - and removing open file descriptors. - */ - struct file_descriptor_table - { - /** - @brief Initialize the global file descriptor table. This method creates the singleton instance of the file - descriptor table. - @warning Panics if called more than once. - */ - auto static init() -> void; - - /** - @brief Get the global file descriptor table instance. - @return A reference to the global file descriptor table. - @warning Panics if the file descriptor table has not been initialized. - */ - auto static get() -> file_descriptor_table &; - - /** - @brief Destructor for the file descriptor table. - */ - ~file_descriptor_table() = default; - - /** - @brief Add a file to the descriptor table. - @param f The file descriptor to add. - @return The file descriptor index assigned to the file, or -1 on failure. - */ - auto add_file(kstd::shared_ptr const & f) -> int; - - /** - @brief Get a file from the descriptor table. - @param fd The file descriptor index to retrieve. - @return A pointer to the requested file descriptor, or a null pointer if not found. - */ - [[nodiscard]] auto get_file(int fd) const -> kstd::shared_ptr; - - /** - @brief Remove a file from the descriptor table. - @param fd The file descriptor index to remove. - @return 0 on success, or -1 on failure. - */ - auto remove_file(int fd) -> int; - - private: - file_descriptor_table() = default; - - kstd::vector> m_open_files{}; - }; -} // namespace kernel::filesystem - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/open_file_table.hpp b/kernel/include/kernel/filesystem/open_file_table.hpp new file mode 100644 index 0000000..2f9a421 --- /dev/null +++ b/kernel/include/kernel/filesystem/open_file_table.hpp @@ -0,0 +1,63 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_TABLE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_TABLE_HPP + +#include + +#include +#include + +namespace kernel::filesystem +{ + /** + @brief A table for managing file descriptors in the filesystem. This class provides methods for adding, retrieving, + and removing open file descriptors. + */ + struct open_file_table + { + /** + @brief Initialize the global open file table. This method creates the singleton instance of the open file table. + @warning Panics if called more than once. + */ + auto static init() -> void; + + /** + @brief Get the global open file table instance. + @return A reference to the global open file table. + @warning Panics if the open file table has not been initialized. + */ + auto static get() -> open_file_table &; + + /** + @brief Destructor for the open file table. + */ + ~open_file_table() = default; + + /** + @brief Add a file to the open file table. + @param f The file descriptor to add. + @return The file descriptor index assigned to the file, or -1 on failure. + */ + auto add_file(kstd::shared_ptr const & f) -> int; + + /** + @brief Get a file from the open file table. + @param fd The file descriptor index to retrieve. + @return A pointer to the requested file descriptor, or a null pointer if not found. + */ + [[nodiscard]] auto get_file(int fd) const -> kstd::shared_ptr; + + /** + @brief Remove a file from the open file table. + @param fd The file descriptor index to remove. + @return 0 on success, or -1 on failure. + */ + auto remove_file(int fd) -> int; + + private: + open_file_table() = default; + + kstd::vector> m_open_files{}; + }; +} // namespace kernel::filesystem + +#endif \ No newline at end of file diff --git a/kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp index bbc0f4a..46b0334 100644 --- a/kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp +++ b/kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp @@ -1,10 +1,10 @@ -#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP -#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_FILE_DESCRIPTOR_TABLE_HPP +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_OPEN_FILE_TABLE_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_OPEN_FILE_TABLE_HPP -namespace kernel::tests::filesystem::file_descriptor_table +namespace kernel::tests::filesystem::open_file_table { - //! Deinitialize the file descriptor table singleton. + //! Deinitialize the open file table singleton. auto deinit() -> void; -} // namespace kernel::tests::filesystem::file_descriptor_table +} // namespace kernel::tests::filesystem::open_file_table #endif \ No newline at end of file diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index 8481f20..4c68f28 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -1,7 +1,7 @@ #include -#include #include +#include #include #include @@ -36,7 +36,7 @@ namespace kapi::filesystem if (auto dentry = kernel::filesystem::vfs::get().open(path)) { auto open_file_descriptor = kstd::make_shared(dentry->get_inode()); - return kernel::filesystem::file_descriptor_table::get().add_file(open_file_descriptor); + return kernel::filesystem::open_file_table::get().add_file(open_file_descriptor); } return -1; @@ -44,12 +44,12 @@ namespace kapi::filesystem auto close(int file_descriptor) -> int { - return kernel::filesystem::file_descriptor_table::get().remove_file(file_descriptor); + return kernel::filesystem::open_file_table::get().remove_file(file_descriptor); } auto read(int file_descriptor, void * buffer, size_t size) -> ssize_t { - if (auto open_file_descriptor = kernel::filesystem::file_descriptor_table::get().get_file(file_descriptor)) + if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().get_file(file_descriptor)) { return open_file_descriptor->read(buffer, size); } @@ -59,7 +59,7 @@ namespace kapi::filesystem auto write(int file_descriptor, void const * buffer, size_t size) -> ssize_t { - if (auto open_file_descriptor = kernel::filesystem::file_descriptor_table::get().get_file(file_descriptor)) + if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().get_file(file_descriptor)) { return open_file_descriptor->write(buffer, size); } diff --git a/kernel/src/filesystem/file_descriptor_table.cpp b/kernel/src/filesystem/file_descriptor_table.cpp deleted file mode 100644 index 9361f37..0000000 --- a/kernel/src/filesystem/file_descriptor_table.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include - -#include - -#include - -#include - -#include -#include -#include - -namespace -{ - constinit auto static global_file_descriptor_table = std::optional{}; -} // namespace - -namespace kernel::filesystem -{ - auto file_descriptor_table::init() -> void - { - if (global_file_descriptor_table) - { - kapi::system::panic("[FILESYSTEM] File descriptor table has already been initialized."); - } - - global_file_descriptor_table.emplace(file_descriptor_table{}); - } - - auto file_descriptor_table::get() -> file_descriptor_table & - { - if (!global_file_descriptor_table) - { - kapi::system::panic("[FILESYSTEM] File descriptor table has not been initialized."); - } - - return *global_file_descriptor_table; - } - - auto file_descriptor_table::add_file(kstd::shared_ptr const & file_descriptor) -> int - { - if (!file_descriptor) - { - return -1; - } - - auto it = std::ranges::find_if(m_open_files, [](auto const & open_file) { return open_file == nullptr; }); - if (it != m_open_files.end()) - { - *it = file_descriptor; - return static_cast(it - m_open_files.begin()); - } - - m_open_files.push_back(file_descriptor); - return static_cast(m_open_files.size() - 1); - } - - auto file_descriptor_table::get_file(int fd) const -> kstd::shared_ptr - { - if (fd < 0) - { - return nullptr; - } - - auto const index = static_cast(fd); - if (index >= m_open_files.size()) - { - return nullptr; - } - - return m_open_files.at(index); - } - - auto file_descriptor_table::remove_file(int fd) -> int - { - if (fd < 0) - { - return -1; - } - - auto const index = static_cast(fd); - if (index >= m_open_files.size()) - { - return -1; - } - - m_open_files.at(index) = nullptr; - return 0; - } -} // namespace kernel::filesystem - -namespace kernel::tests::filesystem::file_descriptor_table -{ - auto deinit() -> void - { - global_file_descriptor_table.reset(); - } -} // namespace kernel::tests::filesystem::file_descriptor_table diff --git a/kernel/src/filesystem/file_descriptor_table.tests.cpp b/kernel/src/filesystem/file_descriptor_table.tests.cpp deleted file mode 100644 index dd04e00..0000000 --- a/kernel/src/filesystem/file_descriptor_table.tests.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include - -#include -#include - -#include -#include -#include - -#include - -SCENARIO("File descriptor table add/get file", "[filesystem][file_descriptor_table]") -{ - GIVEN("a file descriptor table and an open file descriptor") - { - auto & table = kernel::filesystem::file_descriptor_table::get(); - auto inode = kstd::make_shared(); - auto file_descriptor_1 = kstd::make_shared(inode); - auto file_descriptor_2 = kstd::make_shared(inode); - - WHEN("adding the open file descriptor to the file descriptor table") - { - auto fd_1 = table.add_file(file_descriptor_1); - auto fd_2 = table.add_file(file_descriptor_2); - auto fd_3 = table.add_file(file_descriptor_2); - - THEN("a valid file descriptor is returned") - { - REQUIRE(fd_1 == 0); - REQUIRE(fd_2 == 1); - REQUIRE(fd_3 == 2); - } - - THEN("the file descriptor can be retrieved using the returned file descriptor") - { - auto retrieved_descriptor = table.get_file(fd_1); - REQUIRE(retrieved_descriptor == file_descriptor_1); - } - } - } - - GIVEN("a invalid open file descriptor") - { - auto & table = kernel::filesystem::file_descriptor_table::get(); - - THEN("adding a null file descriptor returns an error code") - { - auto fd = table.add_file(nullptr); - REQUIRE(fd == -1); - } - - THEN("retrieving a file descriptor with a negative file descriptor returns a null pointer") - { - auto retrieved_descriptor = table.get_file(-1); - REQUIRE(retrieved_descriptor == nullptr); - } - - THEN("retrieving a file descriptor with an out-of-bounds file descriptor returns a null pointer") - { - auto retrieved_descriptor = table.get_file(1000); - REQUIRE(retrieved_descriptor == nullptr); - } - } -} - -SCENARIO("File descriptor table remove file", "[filesystem][file_descriptor_table]") -{ - GIVEN("a file descriptor table with an open file descriptor") - { - auto & table = kernel::filesystem::file_descriptor_table::get(); - auto inode = kstd::make_shared(); - auto file_descriptor = kstd::make_shared(inode); - auto fd = table.add_file(file_descriptor); - - WHEN("removing the file descriptor using the file descriptor") - { - table.remove_file(fd); - - THEN("the file descriptor can no longer be retrieved using the file descriptor") - { - auto retrieved_descriptor = table.get_file(fd); - REQUIRE(retrieved_descriptor == nullptr); - } - } - - WHEN("removing a file descriptor the other file descriptor keep the same index") - { - auto fd2 = table.add_file(file_descriptor); - table.remove_file(fd); - - THEN("the second file descriptor can still be retrieved using its file descriptor") - { - auto retrieved_descriptor = table.get_file(fd2); - REQUIRE(retrieved_descriptor == file_descriptor); - } - } - } - - GIVEN("an invalid file descriptor") - { - auto & table = kernel::filesystem::file_descriptor_table::get(); - - THEN("removing a file with a negative file descriptor does nothing") - { - REQUIRE_NOTHROW(table.remove_file(-1)); - } - - THEN("removing a file with an out-of-bounds file descriptor does nothing") - { - REQUIRE_NOTHROW(table.remove_file(1000)); - } - } -} \ No newline at end of file diff --git a/kernel/src/filesystem/open_file_table.cpp b/kernel/src/filesystem/open_file_table.cpp new file mode 100644 index 0000000..e47d229 --- /dev/null +++ b/kernel/src/filesystem/open_file_table.cpp @@ -0,0 +1,98 @@ +#include + +#include + +#include + +#include + +#include +#include +#include + +namespace +{ + constinit auto static global_open_file_table = std::optional{}; +} // namespace + +namespace kernel::filesystem +{ + auto open_file_table::init() -> void + { + if (global_open_file_table) + { + kapi::system::panic("[FILESYSTEM] Open file table has already been initialized."); + } + + global_open_file_table.emplace(open_file_table{}); + } + + auto open_file_table::get() -> open_file_table & + { + if (!global_open_file_table) + { + kapi::system::panic("[FILESYSTEM] Open file table has not been initialized."); + } + + return *global_open_file_table; + } + + auto open_file_table::add_file(kstd::shared_ptr const & file_descriptor) -> int + { + if (!file_descriptor) + { + return -1; + } + + auto it = std::ranges::find_if(m_open_files, [](auto const & open_file) { return open_file == nullptr; }); + if (it != m_open_files.end()) + { + *it = file_descriptor; + return static_cast(it - m_open_files.begin()); + } + + m_open_files.push_back(file_descriptor); + return static_cast(m_open_files.size() - 1); + } + + auto open_file_table::get_file(int fd) const -> kstd::shared_ptr + { + if (fd < 0) + { + return nullptr; + } + + auto const index = static_cast(fd); + if (index >= m_open_files.size()) + { + return nullptr; + } + + return m_open_files.at(index); + } + + auto open_file_table::remove_file(int fd) -> int + { + if (fd < 0) + { + return -1; + } + + auto const index = static_cast(fd); + if (index >= m_open_files.size()) + { + return -1; + } + + m_open_files.at(index) = nullptr; + return 0; + } +} // namespace kernel::filesystem + +namespace kernel::tests::filesystem::open_file_table +{ + auto deinit() -> void + { + global_open_file_table.reset(); + } +} // namespace kernel::tests::filesystem::open_file_table diff --git a/kernel/src/filesystem/open_file_table.tests.cpp b/kernel/src/filesystem/open_file_table.tests.cpp new file mode 100644 index 0000000..a5c791d --- /dev/null +++ b/kernel/src/filesystem/open_file_table.tests.cpp @@ -0,0 +1,113 @@ +#include + +#include +#include + +#include +#include +#include + +#include + +SCENARIO("Open file table add/get file", "[filesystem][open_file_table]") +{ + GIVEN("a open file table and an open file descriptor") + { + auto & table = kernel::filesystem::open_file_table::get(); + auto inode = kstd::make_shared(); + auto file_descriptor_1 = kstd::make_shared(inode); + auto file_descriptor_2 = kstd::make_shared(inode); + + WHEN("adding the open file descriptor to the open file table") + { + auto fd_1 = table.add_file(file_descriptor_1); + auto fd_2 = table.add_file(file_descriptor_2); + auto fd_3 = table.add_file(file_descriptor_2); + + THEN("a valid file descriptor is returned") + { + REQUIRE(fd_1 == 0); + REQUIRE(fd_2 == 1); + REQUIRE(fd_3 == 2); + } + + THEN("the file descriptor can be retrieved using the returned file descriptor") + { + auto retrieved_descriptor = table.get_file(fd_1); + REQUIRE(retrieved_descriptor == file_descriptor_1); + } + } + } + + GIVEN("a invalid open file descriptor") + { + auto & table = kernel::filesystem::open_file_table::get(); + + THEN("adding a null file descriptor returns an error code") + { + auto fd = table.add_file(nullptr); + REQUIRE(fd == -1); + } + + THEN("retrieving a file descriptor with a negative file descriptor returns a null pointer") + { + auto retrieved_descriptor = table.get_file(-1); + REQUIRE(retrieved_descriptor == nullptr); + } + + THEN("retrieving a file descriptor with an out-of-bounds file descriptor returns a null pointer") + { + auto retrieved_descriptor = table.get_file(1000); + REQUIRE(retrieved_descriptor == nullptr); + } + } +} + +SCENARIO("Open file table remove file", "[filesystem][open_file_table]") +{ + GIVEN("a open file table with an open file descriptor") + { + auto & table = kernel::filesystem::open_file_table::get(); + auto inode = kstd::make_shared(); + auto file_descriptor = kstd::make_shared(inode); + auto fd = table.add_file(file_descriptor); + + WHEN("removing the file descriptor using the file descriptor") + { + table.remove_file(fd); + + THEN("the file descriptor can no longer be retrieved using the file descriptor") + { + auto retrieved_descriptor = table.get_file(fd); + REQUIRE(retrieved_descriptor == nullptr); + } + } + + WHEN("removing a file descriptor the other file descriptor keep the same index") + { + auto fd2 = table.add_file(file_descriptor); + table.remove_file(fd); + + THEN("the second file descriptor can still be retrieved using its file descriptor") + { + auto retrieved_descriptor = table.get_file(fd2); + REQUIRE(retrieved_descriptor == file_descriptor); + } + } + } + + GIVEN("an invalid file descriptor") + { + auto & table = kernel::filesystem::open_file_table::get(); + + THEN("removing a file with a negative file descriptor does nothing") + { + REQUIRE_NOTHROW(table.remove_file(-1)); + } + + THEN("removing a file with an out-of-bounds file descriptor does nothing") + { + REQUIRE_NOTHROW(table.remove_file(1000)); + } + } +} \ No newline at end of file diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 9878d20..ffea979 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,7 +1,7 @@ #include #include -#include #include +#include #include #include @@ -39,7 +39,7 @@ auto test_device_names() -> void auto test_file_descriptor_manually() -> void { // setup - auto fd_table = kernel::filesystem::file_descriptor_table::get(); + auto fd_table = kernel::filesystem::open_file_table::get(); auto storage_mgmt = kernel::devices::storage::management::get(); auto device = storage_mgmt.device_by_major_minor(1, 0); @@ -95,7 +95,7 @@ auto test_device_with_vfs() -> void kstd::os::panic("test code failed"); } - auto fd_table = kernel::filesystem::file_descriptor_table::get(); + auto fd_table = kernel::filesystem::open_file_table::get(); auto ofd = kstd::make_shared(dentry->get_inode()); auto fd = fd_table.add_file(ofd); kstd::vector buffer{2}; @@ -274,8 +274,8 @@ auto main() -> int kernel::devices::storage::management::init(); kstd::println("[OS] Storage management initialized."); - kernel::filesystem::file_descriptor_table::init(); - kstd::println("[OS] Global file descriptor table initialized."); + kernel::filesystem::open_file_table::init(); + kstd::println("[OS] Global open file table initialized."); kernel::filesystem::vfs::init(); kstd::println("[OS] Virtual filesystem initialized."); diff --git a/kernel/src/test_support/state_reset_listener.cpp b/kernel/src/test_support/state_reset_listener.cpp index 1815110..6bb7537 100644 --- a/kernel/src/test_support/state_reset_listener.cpp +++ b/kernel/src/test_support/state_reset_listener.cpp @@ -1,9 +1,9 @@ -#include +#include #include #include #include #include -#include +#include #include #include @@ -22,7 +22,7 @@ struct state_reset_listener : Catch::EventListenerBase void testCaseStarting(Catch::TestCaseInfo const &) override { - kernel::filesystem::file_descriptor_table::init(); + kernel::filesystem::open_file_table::init(); kapi::cio::init(); kapi::cpu::init(); @@ -31,7 +31,7 @@ struct state_reset_listener : Catch::EventListenerBase void testCaseEnded(Catch::TestCaseStats const &) override { - kernel::tests::filesystem::file_descriptor_table::deinit(); + kernel::tests::filesystem::open_file_table::deinit(); kernel::tests::filesystem::vfs::deinit(); kernel::tests::boot_modules::deinit(); kernel::tests::devices::storage::management::deinit(); -- cgit v1.2.3 From d349812c2e1e6a7d62f53d1c959137794e8a648d Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 26 Apr 2026 12:41:43 +0200 Subject: fix bht build --- .../kernel/test_support/filesystem/file_descriptor_table.hpp | 10 ---------- .../include/kernel/test_support/filesystem/open_file_table.hpp | 10 ++++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) delete mode 100644 kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp create mode 100644 kernel/include/kernel/test_support/filesystem/open_file_table.hpp diff --git a/kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp b/kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp deleted file mode 100644 index 46b0334..0000000 --- a/kernel/include/kernel/test_support/filesystem/file_descriptor_table.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_OPEN_FILE_TABLE_HPP -#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_OPEN_FILE_TABLE_HPP - -namespace kernel::tests::filesystem::open_file_table -{ - //! Deinitialize the open file table singleton. - auto deinit() -> void; -} // namespace kernel::tests::filesystem::open_file_table - -#endif \ No newline at end of file diff --git a/kernel/include/kernel/test_support/filesystem/open_file_table.hpp b/kernel/include/kernel/test_support/filesystem/open_file_table.hpp new file mode 100644 index 0000000..46b0334 --- /dev/null +++ b/kernel/include/kernel/test_support/filesystem/open_file_table.hpp @@ -0,0 +1,10 @@ +#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_OPEN_FILE_TABLE_HPP +#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_OPEN_FILE_TABLE_HPP + +namespace kernel::tests::filesystem::open_file_table +{ + //! Deinitialize the open file table singleton. + auto deinit() -> void; +} // namespace kernel::tests::filesystem::open_file_table + +#endif \ No newline at end of file -- cgit v1.2.3 From e6c6bda14c9af0df9f4c185701b1e7939db6e1f9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 23 Apr 2026 17:12:31 +0200 Subject: elf: restructure according to p1204 --- libs/elf/CMakeLists.txt | 6 +- libs/elf/elf/format.hpp | 19 +++++ libs/elf/elf/section_header.hpp | 120 ++++++++++++++++++++++++++++++++ libs/elf/include/elf/format.hpp | 19 ----- libs/elf/include/elf/section_header.hpp | 120 -------------------------------- 5 files changed, 142 insertions(+), 142 deletions(-) create mode 100644 libs/elf/elf/format.hpp create mode 100644 libs/elf/elf/section_header.hpp delete mode 100644 libs/elf/include/elf/format.hpp delete mode 100644 libs/elf/include/elf/section_header.hpp diff --git a/libs/elf/CMakeLists.txt b/libs/elf/CMakeLists.txt index f1f5275..22ca200 100644 --- a/libs/elf/CMakeLists.txt +++ b/libs/elf/CMakeLists.txt @@ -39,18 +39,18 @@ add_library("elf::lib" ALIAS "elf") file(GLOB_RECURSE ELF_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS - "include/**.hpp" + "elf/**.hpp" ) target_sources("elf" INTERFACE FILE_SET HEADERS - BASE_DIRS "include" + BASE_DIRS "elf" FILES ${ELF_HEADERS} ) target_include_directories("elf" INTERFACE - "include" + "${CMAKE_CURRENT_SOURCE_DIR}" ) set_target_properties("elf" PROPERTIES diff --git a/libs/elf/elf/format.hpp b/libs/elf/elf/format.hpp new file mode 100644 index 0000000..bc85101 --- /dev/null +++ b/libs/elf/elf/format.hpp @@ -0,0 +1,19 @@ +#ifndef ELF_FORMAT_HPP +#define ELF_FORMAT_HPP + +namespace elf +{ + + //! The format of the ELF file being processed. + //! + //! Certain information structures in the ELF are heavily dependent on the bitness of the target architecture. The + //! values of this enumeration type are used to distinguish between the different variants. + enum struct format + { + elf32, + elf64, + }; + +} // namespace elf + +#endif \ No newline at end of file diff --git a/libs/elf/elf/section_header.hpp b/libs/elf/elf/section_header.hpp new file mode 100644 index 0000000..b1305ec --- /dev/null +++ b/libs/elf/elf/section_header.hpp @@ -0,0 +1,120 @@ +#ifndef ELF_SECTION_HEADER_HPP +#define ELF_SECTION_HEADER_HPP + +#include + +#include +#include +#include +#include +#include + +namespace elf +{ + + //! The platform dependent header size. + //! + //! The size of a section header table in ELF is dependent on the bitness of the platform the file is designed for. + //! This constant allows compile-time parametrization of objects and functions for a specific architecture. + template + constexpr auto inline section_header_size = std::numeric_limits::max(); + + //! @copydoc elf::section_header_size + //! + //! @note This specialization provides the section header size for 32-bit ELF files. + template<> + constexpr auto inline section_header_size = 40uz; + + //! @copydoc elf::section_header_size + //! + //! @note This specialization provides the section header size for 64-bit ELF files. + template<> + constexpr auto inline section_header_size = 64uz; + + //! An ELF section header table entry. + //! + //! In the ELF, the section header table describes the layout and properties of the sections present in the loadable + //! files. This information is used to map and load data from the file according to their use. + template + struct section_header + { + //! A platform dependent unsigned integer. + //! + //! The size of certain fields in a section header of the ELF is dependent on the bitness of the target platform, + //! accounting for the differing sizes of 32 and 64 bit section header table entries. + using format_uint = std::conditional_t; + + //! The type of the section described by this header. + enum struct header_type : std::uint32_t + { + null = 0, ///< Is inactive + program_data = 1, ///< Contains program data + symbol_table = 2, ///< Contains a symbol table + string_table = 3, ///< Contains a string table + relocation_entries_with_addends = 4, ///< Contains relocation information with addends + hash_table = 5, ///< Contains a symbol hash table + dynamic_linking_entries = 6, ///< Contains dynamic linking information + notes = 7, ///< Contains additional notes about the object file + no_content = 8, ///< Contains no data + relocation_entries_without_addends = 9, ///< Contains relocation information without addends + reserved = 10, ///< Reserved for future use + dynamic_linker_symbol_table = 11, ///< Contains the dynamic linker symbol table + init_array = 14, ///< Contains an array of constructor pointers + fini_array = 15, ///< Contains an array of destructor pointers + preinit_array = 16, ///< Contains an array of pre-constructor pointers + group_table = 17, ///< Defines a section group + extended_section_header_indices = 18, ///< Contains extended section header indices + }; + + //! The properties of the section describe by this header. + enum struct header_flags : format_uint + { + writable = 0x1, ///< Contains writable data + allocated = 0x2, ///< Occupies memory during execution + executable = 0x4, ///< Contains executable instructions + mergeable = 0x10, ///< Contained data may be merged for deduplication + strings = 0x20, ///< Contains null-terminated strings + info_link = 0x40, ///< Contains the section header index of linked section + link_order = 0x80, ///< Must respect linking location relative to linked section + os_specific = 0x100, ///< Must be handled in an OS specific way + group_member = 0x200, ///< Is a member of a section group + thread_local_storage = 0x400, ///< Contains thread local storage data + compressed = 0x800, ///< Is compressed + }; + + //! Check if the section is allocated + [[nodiscard]] constexpr auto allocated() const noexcept -> bool + { + return std::to_underlying(flags) & std::to_underlying(header_flags::allocated); + } + + //! Check if the section is executable + [[nodiscard]] constexpr auto executable() const noexcept -> bool + { + return std::to_underlying(flags) & std::to_underlying(header_flags::executable); + } + + //! Check if the section is writable + [[nodiscard]] constexpr auto writable() const noexcept -> bool + { + return std::to_underlying(flags) & std::to_underlying(header_flags::writable); + } + + std::uint32_t name_offset; ///< Offset into the section header string table, defining the section name + header_type type; ///< Type of this section + header_flags flags; ///< Flags of this section + format_uint virtual_load_address; ///< Virtual address where this section is loaded + format_uint file_offset; ///< Offset of the start of this section's data in the file + format_uint size; ///< Size of this section in memory + std::uint32_t linked_section; ///< Index of a section this section is linked to + std::uint32_t extra_info; ///< Additional information for this section (type and flag dependent) + format_uint alignment; ///< Alignment requirement of this section in memory + format_uint entry_size; ///< Size of the entries inside this section (if any) + }; + + static_assert(sizeof(section_header) == section_header_size); + static_assert(sizeof(section_header) == section_header_size); + +} // namespace elf + +#endif \ No newline at end of file diff --git a/libs/elf/include/elf/format.hpp b/libs/elf/include/elf/format.hpp deleted file mode 100644 index bc85101..0000000 --- a/libs/elf/include/elf/format.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef ELF_FORMAT_HPP -#define ELF_FORMAT_HPP - -namespace elf -{ - - //! The format of the ELF file being processed. - //! - //! Certain information structures in the ELF are heavily dependent on the bitness of the target architecture. The - //! values of this enumeration type are used to distinguish between the different variants. - enum struct format - { - elf32, - elf64, - }; - -} // namespace elf - -#endif \ No newline at end of file diff --git a/libs/elf/include/elf/section_header.hpp b/libs/elf/include/elf/section_header.hpp deleted file mode 100644 index b1305ec..0000000 --- a/libs/elf/include/elf/section_header.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef ELF_SECTION_HEADER_HPP -#define ELF_SECTION_HEADER_HPP - -#include - -#include -#include -#include -#include -#include - -namespace elf -{ - - //! The platform dependent header size. - //! - //! The size of a section header table in ELF is dependent on the bitness of the platform the file is designed for. - //! This constant allows compile-time parametrization of objects and functions for a specific architecture. - template - constexpr auto inline section_header_size = std::numeric_limits::max(); - - //! @copydoc elf::section_header_size - //! - //! @note This specialization provides the section header size for 32-bit ELF files. - template<> - constexpr auto inline section_header_size = 40uz; - - //! @copydoc elf::section_header_size - //! - //! @note This specialization provides the section header size for 64-bit ELF files. - template<> - constexpr auto inline section_header_size = 64uz; - - //! An ELF section header table entry. - //! - //! In the ELF, the section header table describes the layout and properties of the sections present in the loadable - //! files. This information is used to map and load data from the file according to their use. - template - struct section_header - { - //! A platform dependent unsigned integer. - //! - //! The size of certain fields in a section header of the ELF is dependent on the bitness of the target platform, - //! accounting for the differing sizes of 32 and 64 bit section header table entries. - using format_uint = std::conditional_t; - - //! The type of the section described by this header. - enum struct header_type : std::uint32_t - { - null = 0, ///< Is inactive - program_data = 1, ///< Contains program data - symbol_table = 2, ///< Contains a symbol table - string_table = 3, ///< Contains a string table - relocation_entries_with_addends = 4, ///< Contains relocation information with addends - hash_table = 5, ///< Contains a symbol hash table - dynamic_linking_entries = 6, ///< Contains dynamic linking information - notes = 7, ///< Contains additional notes about the object file - no_content = 8, ///< Contains no data - relocation_entries_without_addends = 9, ///< Contains relocation information without addends - reserved = 10, ///< Reserved for future use - dynamic_linker_symbol_table = 11, ///< Contains the dynamic linker symbol table - init_array = 14, ///< Contains an array of constructor pointers - fini_array = 15, ///< Contains an array of destructor pointers - preinit_array = 16, ///< Contains an array of pre-constructor pointers - group_table = 17, ///< Defines a section group - extended_section_header_indices = 18, ///< Contains extended section header indices - }; - - //! The properties of the section describe by this header. - enum struct header_flags : format_uint - { - writable = 0x1, ///< Contains writable data - allocated = 0x2, ///< Occupies memory during execution - executable = 0x4, ///< Contains executable instructions - mergeable = 0x10, ///< Contained data may be merged for deduplication - strings = 0x20, ///< Contains null-terminated strings - info_link = 0x40, ///< Contains the section header index of linked section - link_order = 0x80, ///< Must respect linking location relative to linked section - os_specific = 0x100, ///< Must be handled in an OS specific way - group_member = 0x200, ///< Is a member of a section group - thread_local_storage = 0x400, ///< Contains thread local storage data - compressed = 0x800, ///< Is compressed - }; - - //! Check if the section is allocated - [[nodiscard]] constexpr auto allocated() const noexcept -> bool - { - return std::to_underlying(flags) & std::to_underlying(header_flags::allocated); - } - - //! Check if the section is executable - [[nodiscard]] constexpr auto executable() const noexcept -> bool - { - return std::to_underlying(flags) & std::to_underlying(header_flags::executable); - } - - //! Check if the section is writable - [[nodiscard]] constexpr auto writable() const noexcept -> bool - { - return std::to_underlying(flags) & std::to_underlying(header_flags::writable); - } - - std::uint32_t name_offset; ///< Offset into the section header string table, defining the section name - header_type type; ///< Type of this section - header_flags flags; ///< Flags of this section - format_uint virtual_load_address; ///< Virtual address where this section is loaded - format_uint file_offset; ///< Offset of the start of this section's data in the file - format_uint size; ///< Size of this section in memory - std::uint32_t linked_section; ///< Index of a section this section is linked to - std::uint32_t extra_info; ///< Additional information for this section (type and flag dependent) - format_uint alignment; ///< Alignment requirement of this section in memory - format_uint entry_size; ///< Size of the entries inside this section (if any) - }; - - static_assert(sizeof(section_header) == section_header_size); - static_assert(sizeof(section_header) == section_header_size); - -} // namespace elf - -#endif \ No newline at end of file -- cgit v1.2.3 From d906d70c94c2a40d5fc6fd26056c7bc57d540002 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 23 Apr 2026 17:16:34 +0200 Subject: acpi: move test data header --- libs/acpi/CMakeLists.txt | 4 ++-- libs/acpi/acpi/common/table_header.test.cpp | 4 ++-- libs/acpi/acpi/data/madt.test.cpp | 4 ++-- libs/acpi/acpi/data/rsdt.test.cpp | 3 +-- libs/acpi/acpi/data/xsdt.test.cpp | 3 +-- libs/acpi/acpi/test_data/tables.S | 17 +++++++++++++++++ libs/acpi/acpi/test_data/tables.hpp | 28 ++++++++++++++++++++++++++++ libs/acpi/test_data/tables.S | 17 ----------------- libs/acpi/test_data/tables.hpp | 28 ---------------------------- 9 files changed, 53 insertions(+), 55 deletions(-) create mode 100644 libs/acpi/acpi/test_data/tables.S create mode 100644 libs/acpi/acpi/test_data/tables.hpp delete mode 100644 libs/acpi/test_data/tables.S delete mode 100644 libs/acpi/test_data/tables.hpp diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index e73c6b3..d6d607a 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -95,7 +95,7 @@ if(BUILD_TESTING) list(APPEND GENERATED_TABLE_BLOBS "${CMAKE_CURRENT_BINARY_DIR}/test_data/${TABLE}.aml") endforeach() - set_source_files_properties("test_data/tables.S" PROPERTIES OBJECT_DEPENDS "${GENERATED_TABLE_BLOBS}") + set_source_files_properties("acpi/test_data/tables.S" PROPERTIES OBJECT_DEPENDS "${GENERATED_TABLE_BLOBS}") if(COMMAND "enable_coverage") enable_coverage("acpi") @@ -111,7 +111,7 @@ if(BUILD_TESTING) "acpi/data/xsdt.test.cpp" "acpi/pointers.test.cpp" - "test_data/tables.S" + "acpi/test_data/tables.S" ) target_include_directories("acpi_tests" PRIVATE diff --git a/libs/acpi/acpi/common/table_header.test.cpp b/libs/acpi/acpi/common/table_header.test.cpp index 53cdb26..ddc879e 100644 --- a/libs/acpi/acpi/common/table_header.test.cpp +++ b/libs/acpi/acpi/common/table_header.test.cpp @@ -1,11 +1,11 @@ #include +#include + #include #include -#include - SCENARIO("Common table header parsing", "[common_table_header]") { GIVEN("A valid compiled table header") diff --git a/libs/acpi/acpi/data/madt.test.cpp b/libs/acpi/acpi/data/madt.test.cpp index 5d3b366..1b95a74 100644 --- a/libs/acpi/acpi/data/madt.test.cpp +++ b/libs/acpi/acpi/data/madt.test.cpp @@ -1,13 +1,13 @@ #include +#include + #include #include #include -#include - SCENARIO("MADT parsing", "[madt]") { GIVEN("The basic compiled MADT containing a single LAPIC entry and the default x86 LAPIC address") diff --git a/libs/acpi/acpi/data/rsdt.test.cpp b/libs/acpi/acpi/data/rsdt.test.cpp index a6dd416..47992ce 100644 --- a/libs/acpi/acpi/data/rsdt.test.cpp +++ b/libs/acpi/acpi/data/rsdt.test.cpp @@ -1,6 +1,7 @@ #include #include +#include #include @@ -8,8 +9,6 @@ #include -#include - SCENARIO("RSDT parsing", "[rsdt]") { GIVEN("The basic compiled RSDT containing 8 table pointers") diff --git a/libs/acpi/acpi/data/xsdt.test.cpp b/libs/acpi/acpi/data/xsdt.test.cpp index cc18a66..77a5340 100644 --- a/libs/acpi/acpi/data/xsdt.test.cpp +++ b/libs/acpi/acpi/data/xsdt.test.cpp @@ -1,6 +1,7 @@ #include #include +#include #include @@ -8,8 +9,6 @@ #include -#include - SCENARIO("XSDT parsing", "[xsdt]") { GIVEN("The basic compiled XSDT containing 8 table pointers") diff --git a/libs/acpi/acpi/test_data/tables.S b/libs/acpi/acpi/test_data/tables.S new file mode 100644 index 0000000..641db6a --- /dev/null +++ b/libs/acpi/acpi/test_data/tables.S @@ -0,0 +1,17 @@ +.section .rodata + +.balign 16 + +#define TABLE(name, file) \ + .global name##_start; \ + .global name##_end; \ + name##_start: .incbin file; \ + name##_end: + +TABLE(basic_madt, "basic_madt.aml") +TABLE(basic_rsdt, "basic_rsdt.aml") +TABLE(basic_rsdp, "basic_rsdp.aml") +TABLE(basic_xsdt, "basic_xsdt.aml") +TABLE(table_header, "table_header.aml") + +#undef TABLE diff --git a/libs/acpi/acpi/test_data/tables.hpp b/libs/acpi/acpi/test_data/tables.hpp new file mode 100644 index 0000000..e91f1a5 --- /dev/null +++ b/libs/acpi/acpi/test_data/tables.hpp @@ -0,0 +1,28 @@ +#ifndef ACPI_TEST_DATA_TABLES_HPP +#define ACPI_TEST_DATA_TABLES_HPP + +#include +#include + +#define TABLE(name) \ + extern "C" std::byte const name##_start; \ + extern "C" std::byte const name##_end; \ + auto inline name()->std::span \ + { \ + return {&name##_start, &name##_end}; \ + } + +namespace acpi::test_data::tables +{ + + TABLE(basic_madt); + TABLE(basic_rsdt); + TABLE(basic_rsdp); + TABLE(basic_xsdt); + TABLE(table_header); + +} // namespace acpi::test_data::tables + +#undef TABLE + +#endif diff --git a/libs/acpi/test_data/tables.S b/libs/acpi/test_data/tables.S deleted file mode 100644 index 641db6a..0000000 --- a/libs/acpi/test_data/tables.S +++ /dev/null @@ -1,17 +0,0 @@ -.section .rodata - -.balign 16 - -#define TABLE(name, file) \ - .global name##_start; \ - .global name##_end; \ - name##_start: .incbin file; \ - name##_end: - -TABLE(basic_madt, "basic_madt.aml") -TABLE(basic_rsdt, "basic_rsdt.aml") -TABLE(basic_rsdp, "basic_rsdp.aml") -TABLE(basic_xsdt, "basic_xsdt.aml") -TABLE(table_header, "table_header.aml") - -#undef TABLE diff --git a/libs/acpi/test_data/tables.hpp b/libs/acpi/test_data/tables.hpp deleted file mode 100644 index e91f1a5..0000000 --- a/libs/acpi/test_data/tables.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef ACPI_TEST_DATA_TABLES_HPP -#define ACPI_TEST_DATA_TABLES_HPP - -#include -#include - -#define TABLE(name) \ - extern "C" std::byte const name##_start; \ - extern "C" std::byte const name##_end; \ - auto inline name()->std::span \ - { \ - return {&name##_start, &name##_end}; \ - } - -namespace acpi::test_data::tables -{ - - TABLE(basic_madt); - TABLE(basic_rsdt); - TABLE(basic_rsdp); - TABLE(basic_xsdt); - TABLE(table_header); - -} // namespace acpi::test_data::tables - -#undef TABLE - -#endif -- cgit v1.2.3 From 1b964278762dde86b0b737bd9a34fec569339f54 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 16 Apr 2026 10:29:30 +0200 Subject: x86_64: use p1204 project layout --- .vscode/settings.json | 3 +- arch/x86_64/CMakeLists.txt | 50 ++- arch/x86_64/arch/boot/boot.hpp | 56 +++ arch/x86_64/arch/boot/boot32.S | 443 +++++++++++++++++++++ arch/x86_64/arch/boot/entry64.s | 62 +++ arch/x86_64/arch/boot/initialize_runtime.cpp | 22 + arch/x86_64/arch/boot/ld.hpp | 61 +++ arch/x86_64/arch/boot/multiboot.s | 33 ++ arch/x86_64/arch/bus/isa.cpp | 14 + arch/x86_64/arch/bus/isa.hpp | 18 + arch/x86_64/arch/cpu/control_register.hpp | 249 ++++++++++++ arch/x86_64/arch/cpu/global_descriptor_table.hpp | 91 +++++ arch/x86_64/arch/cpu/initialization.cpp | 164 ++++++++ arch/x86_64/arch/cpu/initialization.hpp | 12 + arch/x86_64/arch/cpu/interrupts.S | 112 ++++++ arch/x86_64/arch/cpu/interrupts.cpp | 203 ++++++++++ arch/x86_64/arch/cpu/interrupts.hpp | 116 ++++++ arch/x86_64/arch/cpu/legacy_pic.hpp | 19 + arch/x86_64/arch/cpu/model_specific_register.hpp | 151 +++++++ arch/x86_64/arch/cpu/registers.hpp | 32 ++ arch/x86_64/arch/cpu/segment_descriptor.hpp | 61 +++ arch/x86_64/arch/cpu/segment_selector.hpp | 21 + arch/x86_64/arch/cpu/task_state_segment.hpp | 30 ++ arch/x86_64/arch/debug/qemu_output.cpp | 25 ++ arch/x86_64/arch/debug/qemu_output.hpp | 43 ++ arch/x86_64/arch/device_io/port_io.hpp | 112 ++++++ arch/x86_64/arch/devices/init.cpp | 76 ++++ arch/x86_64/arch/devices/init.hpp | 12 + arch/x86_64/arch/devices/legacy_pit.cpp | 60 +++ arch/x86_64/arch/devices/legacy_pit.hpp | 29 ++ arch/x86_64/arch/devices/local_apic.cpp | 134 +++++++ arch/x86_64/arch/devices/local_apic.hpp | 37 ++ arch/x86_64/arch/memory/higher_half_mapper.cpp | 119 ++++++ arch/x86_64/arch/memory/higher_half_mapper.hpp | 44 ++ arch/x86_64/arch/memory/kernel_mapper.cpp | 117 ++++++ arch/x86_64/arch/memory/kernel_mapper.hpp | 35 ++ arch/x86_64/arch/memory/mmu.cpp | 19 + arch/x86_64/arch/memory/mmu.hpp | 27 ++ arch/x86_64/arch/memory/page_table.cpp | 82 ++++ arch/x86_64/arch/memory/page_table.hpp | 232 +++++++++++ arch/x86_64/arch/memory/page_utilities.hpp | 29 ++ arch/x86_64/arch/memory/region_allocator.cpp | 165 ++++++++ arch/x86_64/arch/memory/region_allocator.hpp | 93 +++++ arch/x86_64/arch/vga/crtc.hpp | 35 ++ arch/x86_64/arch/vga/text.hpp | 10 + arch/x86_64/arch/vga/text/attribute.hpp | 30 ++ arch/x86_64/arch/vga/text/buffer.cpp | 101 +++++ arch/x86_64/arch/vga/text/buffer.hpp | 101 +++++ arch/x86_64/arch/vga/text/color.hpp | 35 ++ arch/x86_64/arch/vga/text/common_attributes.hpp | 37 ++ arch/x86_64/arch/vga/text/device.cpp | 60 +++ arch/x86_64/arch/vga/text/device.hpp | 45 +++ arch/x86_64/arch/vga/text/flags.hpp | 38 ++ arch/x86_64/include/arch/boot/boot.hpp | 56 --- arch/x86_64/include/arch/boot/ld.hpp | 61 --- arch/x86_64/include/arch/bus/isa.hpp | 18 - arch/x86_64/include/arch/cpu/control_register.hpp | 249 ------------ .../include/arch/cpu/global_descriptor_table.hpp | 91 ----- arch/x86_64/include/arch/cpu/initialization.hpp | 12 - arch/x86_64/include/arch/cpu/interrupts.hpp | 116 ------ arch/x86_64/include/arch/cpu/legacy_pic.hpp | 19 - .../include/arch/cpu/model_specific_register.hpp | 151 ------- arch/x86_64/include/arch/cpu/registers.hpp | 32 -- .../x86_64/include/arch/cpu/segment_descriptor.hpp | 61 --- arch/x86_64/include/arch/cpu/segment_selector.hpp | 21 - .../x86_64/include/arch/cpu/task_state_segment.hpp | 30 -- arch/x86_64/include/arch/debug/qemu_output.hpp | 43 -- arch/x86_64/include/arch/device_io/port_io.hpp | 112 ------ arch/x86_64/include/arch/devices/init.hpp | 12 - arch/x86_64/include/arch/devices/legacy_pit.hpp | 29 -- arch/x86_64/include/arch/devices/local_apic.hpp | 37 -- .../include/arch/memory/higher_half_mapper.hpp | 44 -- arch/x86_64/include/arch/memory/kernel_mapper.hpp | 35 -- arch/x86_64/include/arch/memory/mmu.hpp | 27 -- arch/x86_64/include/arch/memory/page_table.hpp | 232 ----------- arch/x86_64/include/arch/memory/page_utilities.hpp | 29 -- .../include/arch/memory/region_allocator.hpp | 93 ----- arch/x86_64/include/arch/vga/crtc.hpp | 35 -- arch/x86_64/include/arch/vga/text.hpp | 10 - arch/x86_64/include/arch/vga/text/attribute.hpp | 30 -- arch/x86_64/include/arch/vga/text/buffer.hpp | 101 ----- arch/x86_64/include/arch/vga/text/color.hpp | 35 -- .../include/arch/vga/text/common_attributes.hpp | 37 -- arch/x86_64/include/arch/vga/text/device.hpp | 45 --- arch/x86_64/include/arch/vga/text/flags.hpp | 38 -- .../pre/include/arch/context_switching/main.hpp | 51 --- .../arch/context_switching/syscall/main.hpp | 91 ----- .../context_switching/syscall/syscall_enable.hpp | 18 - .../context_switching/syscall/syscall_handler.hpp | 18 - arch/x86_64/pre/include/arch/kernel/halt.hpp | 13 - arch/x86_64/pre/include/arch/kernel/main.hpp | 13 - arch/x86_64/pre/include/arch/user/main.hpp | 16 - arch/x86_64/pre/src/context_switching/main.cpp | 66 --- .../pre/src/context_switching/syscall/main.cpp | 35 -- .../context_switching/syscall/syscall_enable.cpp | 32 -- .../context_switching/syscall/syscall_handler.cpp | 121 ------ arch/x86_64/pre/src/kernel/main.cpp | 71 ---- arch/x86_64/pre/src/user/main.cpp | 35 -- arch/x86_64/src/boot/boot32.S | 443 --------------------- arch/x86_64/src/boot/entry64.s | 62 --- arch/x86_64/src/boot/initialize_runtime.cpp | 22 - arch/x86_64/src/boot/multiboot.s | 33 -- arch/x86_64/src/bus/isa.cpp | 14 - arch/x86_64/src/cpu/initialization.cpp | 164 -------- arch/x86_64/src/cpu/interrupt_stubs.S | 112 ------ arch/x86_64/src/cpu/interrupts.cpp | 203 ---------- arch/x86_64/src/debug/qemu_output.cpp | 25 -- arch/x86_64/src/devices/init.cpp | 76 ---- arch/x86_64/src/devices/legacy_pit.cpp | 60 --- arch/x86_64/src/devices/local_apic.cpp | 134 ------- arch/x86_64/src/memory/higher_half_mapper.cpp | 119 ------ arch/x86_64/src/memory/kernel_mapper.cpp | 117 ------ arch/x86_64/src/memory/mmu.cpp | 19 - arch/x86_64/src/memory/page_table.cpp | 82 ---- arch/x86_64/src/memory/region_allocator.cpp | 165 -------- arch/x86_64/src/vga/text/buffer.cpp | 101 ----- arch/x86_64/src/vga/text/device.cpp | 60 --- 117 files changed, 3984 insertions(+), 4553 deletions(-) create mode 100644 arch/x86_64/arch/boot/boot.hpp create mode 100644 arch/x86_64/arch/boot/boot32.S create mode 100644 arch/x86_64/arch/boot/entry64.s create mode 100644 arch/x86_64/arch/boot/initialize_runtime.cpp create mode 100644 arch/x86_64/arch/boot/ld.hpp create mode 100644 arch/x86_64/arch/boot/multiboot.s create mode 100644 arch/x86_64/arch/bus/isa.cpp create mode 100644 arch/x86_64/arch/bus/isa.hpp create mode 100644 arch/x86_64/arch/cpu/control_register.hpp create mode 100644 arch/x86_64/arch/cpu/global_descriptor_table.hpp create mode 100644 arch/x86_64/arch/cpu/initialization.cpp create mode 100644 arch/x86_64/arch/cpu/initialization.hpp create mode 100644 arch/x86_64/arch/cpu/interrupts.S create mode 100644 arch/x86_64/arch/cpu/interrupts.cpp create mode 100644 arch/x86_64/arch/cpu/interrupts.hpp create mode 100644 arch/x86_64/arch/cpu/legacy_pic.hpp create mode 100644 arch/x86_64/arch/cpu/model_specific_register.hpp create mode 100644 arch/x86_64/arch/cpu/registers.hpp create mode 100644 arch/x86_64/arch/cpu/segment_descriptor.hpp create mode 100644 arch/x86_64/arch/cpu/segment_selector.hpp create mode 100644 arch/x86_64/arch/cpu/task_state_segment.hpp create mode 100644 arch/x86_64/arch/debug/qemu_output.cpp create mode 100644 arch/x86_64/arch/debug/qemu_output.hpp create mode 100644 arch/x86_64/arch/device_io/port_io.hpp create mode 100644 arch/x86_64/arch/devices/init.cpp create mode 100644 arch/x86_64/arch/devices/init.hpp create mode 100644 arch/x86_64/arch/devices/legacy_pit.cpp create mode 100644 arch/x86_64/arch/devices/legacy_pit.hpp create mode 100644 arch/x86_64/arch/devices/local_apic.cpp create mode 100644 arch/x86_64/arch/devices/local_apic.hpp create mode 100644 arch/x86_64/arch/memory/higher_half_mapper.cpp create mode 100644 arch/x86_64/arch/memory/higher_half_mapper.hpp create mode 100644 arch/x86_64/arch/memory/kernel_mapper.cpp create mode 100644 arch/x86_64/arch/memory/kernel_mapper.hpp create mode 100644 arch/x86_64/arch/memory/mmu.cpp create mode 100644 arch/x86_64/arch/memory/mmu.hpp create mode 100644 arch/x86_64/arch/memory/page_table.cpp create mode 100644 arch/x86_64/arch/memory/page_table.hpp create mode 100644 arch/x86_64/arch/memory/page_utilities.hpp create mode 100644 arch/x86_64/arch/memory/region_allocator.cpp create mode 100644 arch/x86_64/arch/memory/region_allocator.hpp create mode 100644 arch/x86_64/arch/vga/crtc.hpp create mode 100644 arch/x86_64/arch/vga/text.hpp create mode 100644 arch/x86_64/arch/vga/text/attribute.hpp create mode 100644 arch/x86_64/arch/vga/text/buffer.cpp create mode 100644 arch/x86_64/arch/vga/text/buffer.hpp create mode 100644 arch/x86_64/arch/vga/text/color.hpp create mode 100644 arch/x86_64/arch/vga/text/common_attributes.hpp create mode 100644 arch/x86_64/arch/vga/text/device.cpp create mode 100644 arch/x86_64/arch/vga/text/device.hpp create mode 100644 arch/x86_64/arch/vga/text/flags.hpp delete mode 100644 arch/x86_64/include/arch/boot/boot.hpp delete mode 100644 arch/x86_64/include/arch/boot/ld.hpp delete mode 100644 arch/x86_64/include/arch/bus/isa.hpp delete mode 100644 arch/x86_64/include/arch/cpu/control_register.hpp delete mode 100644 arch/x86_64/include/arch/cpu/global_descriptor_table.hpp delete mode 100644 arch/x86_64/include/arch/cpu/initialization.hpp delete mode 100644 arch/x86_64/include/arch/cpu/interrupts.hpp delete mode 100644 arch/x86_64/include/arch/cpu/legacy_pic.hpp delete mode 100644 arch/x86_64/include/arch/cpu/model_specific_register.hpp delete mode 100644 arch/x86_64/include/arch/cpu/registers.hpp delete mode 100644 arch/x86_64/include/arch/cpu/segment_descriptor.hpp delete mode 100644 arch/x86_64/include/arch/cpu/segment_selector.hpp delete mode 100644 arch/x86_64/include/arch/cpu/task_state_segment.hpp delete mode 100644 arch/x86_64/include/arch/debug/qemu_output.hpp delete mode 100644 arch/x86_64/include/arch/device_io/port_io.hpp delete mode 100644 arch/x86_64/include/arch/devices/init.hpp delete mode 100644 arch/x86_64/include/arch/devices/legacy_pit.hpp delete mode 100644 arch/x86_64/include/arch/devices/local_apic.hpp delete mode 100644 arch/x86_64/include/arch/memory/higher_half_mapper.hpp delete mode 100644 arch/x86_64/include/arch/memory/kernel_mapper.hpp delete mode 100644 arch/x86_64/include/arch/memory/mmu.hpp delete mode 100644 arch/x86_64/include/arch/memory/page_table.hpp delete mode 100644 arch/x86_64/include/arch/memory/page_utilities.hpp delete mode 100644 arch/x86_64/include/arch/memory/region_allocator.hpp delete mode 100644 arch/x86_64/include/arch/vga/crtc.hpp delete mode 100644 arch/x86_64/include/arch/vga/text.hpp delete mode 100644 arch/x86_64/include/arch/vga/text/attribute.hpp delete mode 100644 arch/x86_64/include/arch/vga/text/buffer.hpp delete mode 100644 arch/x86_64/include/arch/vga/text/color.hpp delete mode 100644 arch/x86_64/include/arch/vga/text/common_attributes.hpp delete mode 100644 arch/x86_64/include/arch/vga/text/device.hpp delete mode 100644 arch/x86_64/include/arch/vga/text/flags.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/main.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/syscall/main.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/syscall/syscall_enable.hpp delete mode 100644 arch/x86_64/pre/include/arch/context_switching/syscall/syscall_handler.hpp delete mode 100644 arch/x86_64/pre/include/arch/kernel/halt.hpp delete mode 100644 arch/x86_64/pre/include/arch/kernel/main.hpp delete mode 100644 arch/x86_64/pre/include/arch/user/main.hpp delete mode 100644 arch/x86_64/pre/src/context_switching/main.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/syscall/main.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp delete mode 100644 arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp delete mode 100644 arch/x86_64/pre/src/kernel/main.cpp delete mode 100644 arch/x86_64/pre/src/user/main.cpp delete mode 100644 arch/x86_64/src/boot/boot32.S delete mode 100644 arch/x86_64/src/boot/entry64.s delete mode 100644 arch/x86_64/src/boot/initialize_runtime.cpp delete mode 100644 arch/x86_64/src/boot/multiboot.s delete mode 100644 arch/x86_64/src/bus/isa.cpp delete mode 100644 arch/x86_64/src/cpu/initialization.cpp delete mode 100644 arch/x86_64/src/cpu/interrupt_stubs.S delete mode 100644 arch/x86_64/src/cpu/interrupts.cpp delete mode 100644 arch/x86_64/src/debug/qemu_output.cpp delete mode 100644 arch/x86_64/src/devices/init.cpp delete mode 100644 arch/x86_64/src/devices/legacy_pit.cpp delete mode 100644 arch/x86_64/src/devices/local_apic.cpp delete mode 100644 arch/x86_64/src/memory/higher_half_mapper.cpp delete mode 100644 arch/x86_64/src/memory/kernel_mapper.cpp delete mode 100644 arch/x86_64/src/memory/mmu.cpp delete mode 100644 arch/x86_64/src/memory/page_table.cpp delete mode 100644 arch/x86_64/src/memory/region_allocator.cpp delete mode 100644 arch/x86_64/src/vga/text/buffer.cpp delete mode 100644 arch/x86_64/src/vga/text/device.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 80844ee..79c4971 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,8 +13,9 @@ "--header-insertion=iwyu" ], "explorer.fileNesting.enabled": true, + "explorer.fileNesting.expand": false, "explorer.fileNesting.patterns": { - "*.hpp": "${capture}.cpp, ${capture}.test.cpp" + "*.hpp": "${capture}.cpp, ${capture}.test.cpp, ${capture}.S" }, "files.associations": { "**/kstd/include/kstd/**": "cpp", diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 9e346ef..5657010 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -5,8 +5,16 @@ add_library("x86_64" OBJECT) add_library("arch::lib" ALIAS "x86_64") +target_include_directories("x86_64" PUBLIC + "${CMAKE_CURRENT_SOURCE_DIR}" +) + +target_link_libraries("x86_64" PUBLIC + "os::kapi" + "libs::multiboot2" +) + target_sources("x86_64" PRIVATE - # Platform-dependent KAPI implementation "kapi/boot_modules.cpp" "kapi/cio.cpp" "kapi/cpu.cpp" @@ -14,39 +22,41 @@ target_sources("x86_64" PRIVATE "kapi/interrupts.cpp" "kapi/memory.cpp" "kapi/system.cpp" +) +target_sources("x86_64" PRIVATE # CPU Initialization - "src/cpu/initialization.cpp" - "src/cpu/interrupts.cpp" - "src/cpu/interrupt_stubs.S" + "arch/cpu/initialization.cpp" + "arch/cpu/interrupts.cpp" + "arch/cpu/interrupts.S" # Bus Initialization - "src/bus/isa.cpp" + "arch/bus/isa.cpp" # Low-level bootstrap - "src/boot/boot32.S" - "src/boot/entry64.s" - "src/boot/initialize_runtime.cpp" - "src/boot/multiboot.s" + "arch/boot/boot32.S" + "arch/boot/entry64.s" + "arch/boot/initialize_runtime.cpp" + "arch/boot/multiboot.s" # Debug interfaces - "src/debug/qemu_output.cpp" + "arch/debug/qemu_output.cpp" # Devices - "src/devices/init.cpp" - "src/devices/legacy_pit.cpp" - "src/devices/local_apic.cpp" + "arch/devices/init.cpp" + "arch/devices/legacy_pit.cpp" + "arch/devices/local_apic.cpp" # Memory management - "src/memory/kernel_mapper.cpp" - "src/memory/higher_half_mapper.cpp" - "src/memory/mmu.cpp" - "src/memory/page_table.cpp" - "src/memory/region_allocator.cpp" + "arch/memory/kernel_mapper.cpp" + "arch/memory/higher_half_mapper.cpp" + "arch/memory/mmu.cpp" + "arch/memory/page_table.cpp" + "arch/memory/region_allocator.cpp" # VGA text mode - "src/vga/text/buffer.cpp" - "src/vga/text/device.cpp" + "arch/vga/text/buffer.cpp" + "arch/vga/text/device.cpp" ) file(GLOB_RECURSE ARCH_HEADERS diff --git a/arch/x86_64/arch/boot/boot.hpp b/arch/x86_64/arch/boot/boot.hpp new file mode 100644 index 0000000..7df61c4 --- /dev/null +++ b/arch/x86_64/arch/boot/boot.hpp @@ -0,0 +1,56 @@ +#ifndef TEACHOS_X86_64_BOOT_BOOT_HPP +#define TEACHOS_X86_64_BOOT_BOOT_HPP + +#ifdef __ASSEMBLER__ +// clang-format off + +//! The number of huge pages to map during bootstrap. +#define HUGE_PAGES_TO_MAP (16) + +//! The magic value to be set in eax by the multiboot 2 loader. +#define MULTIBOOT2_MAGIC (0x36d76289) + +//! The "A" bit in a GDT entry. +#define GDT_ACCESSED (1 << 40) + +//! The "R/W" bit in a GDT entry +#define GDT_READ_WRITE (1 << 41) + +//! The "E" bit in a GDT entry. +#define GDT_EXECUTABLE (1 << 43) + +//! The "S" bit in a GDT entry. +#define GDT_DESCRIPTOR_TYPE (1 << 44) + +//! The "P" bit in a GDT entry. +#define GDT_PRESENT (1 << 47) + +//! The "L" bit in a GDT entry. +#define GDT_LONG_MODE (1 << 53) + +// clang-format on +#else + +#include // IWYU pragma: export + +#include + +#include + +namespace kapi::boot +{ + + struct information + { + //! A pointer to the loader provided Multiboot2 Information structure. + multiboot2::information_view const * mbi; + + //! The index of the next character to be written in the VGA text buffer after handoff. + std::size_t vga_buffer_index; + }; + +} // namespace kapi::boot + +#endif + +#endif diff --git a/arch/x86_64/arch/boot/boot32.S b/arch/x86_64/arch/boot/boot32.S new file mode 100644 index 0000000..e6fcd85 --- /dev/null +++ b/arch/x86_64/arch/boot/boot32.S @@ -0,0 +1,443 @@ +#include + +/** + * @brief Uninitialized data for the bootstrapping process. + */ +.section .boot_bss, "aw", @nobits + +page_maps_start: +page_map_level_4: .skip 512 * 8 +page_map_level_3_high: .skip 512 * 8 +page_map_level_3_low: .skip 512 * 8 +page_map_level_2: .skip 512 * 8 +page_maps_end = . +page_maps_size = page_maps_end - page_maps_start + +/** + * @brief Storage for the multiboot2 information pointer. + */ +.global multiboot_information_pointer +multiboot_information_pointer: .skip 8 + +/** + * @brief Storage for the bootstrap stack. + */ +.section .boot_stack, "aw", @nobits +.align 16 + +early_stack_bottom: .skip 1 << 8 +early_stack_top: +early_stack_size = early_stack_top - early_stack_bottom + +/** + * @brief Constants for the bootstrapping process. + */ +.section .boot_rodata, "a", @progbits + +.global global_descriptor_table_data + +/** + * @brief A basic GDT for long mode. + */ +global_descriptor_table: +global_descriptor_table_null = . - global_descriptor_table +.quad 0 +global_descriptor_table_code = . - global_descriptor_table +.quad GDT_READ_WRITE | GDT_EXECUTABLE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT | GDT_LONG_MODE | (1 << 55) +global_descriptor_table_data = . - global_descriptor_table +.quad GDT_READ_WRITE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT | (1 << 54) | (1 << 55) +global_descriptor_table_end: + +message_prefix_panic: .string "Panic: " +message_not_loaded_by_multiboot2: .string "The operating system was not loaded by a Multiboot 2 loader." +message_cpuid_instruction_no_supported: .string "The 'cpuid' instruction is not supported on this platform." +message_long_mode_not_supported: .string "Long mode is not supported by this platform." +message_return_from_kernel_main: .string "Execution returned from kernel main." + +/** + * @brief Initialized data for the bootstrapping process. + */ +.section .boot_data, "aw", @progbits + +/** + * @brief A pointer to the current position within the VGA text buffer. + */ +.global vga_buffer_pointer +vga_buffer_pointer: .quad 0xb8000 + +/** + * @brief Code for the bootstrapping process. + */ +.section .boot_text, "ax", @progbits +.align 16 +.code32 + +.macro pie_base + push %esi + call 0f + 0: + pop %esi +.endm + +.macro function_start + push %ebp + mov %esp, %ebp +.endm + +.macro function_end + leave + ret +.endm + +.macro pie_function_start + function_start + pie_base +.endm + +.macro pie_function_end + pop %esi + function_end +.endm + +/** + * @brief Prepare the environment and start the kernel. + * + * This function performs all necessary checks to ensure the system was loaded + * by the expected loader and supports all features required to run the kernel. + * If successful, it prepares the system by setting up memory virtualization + * and then start the kernel proper. + * + * @param %eax The Multiboot 2 magic marker. + * @param %ebx The Multiboot 2 information pointer. + * @return void This function does not return. + */ +.global _start +_start: + call 0f +0: + pop %esi + + lea (early_stack_top - 0b)(%esi), %ecx + + mov %ecx, %esp + mov %esp, %ebp + + call _assert_loaded_by_multiboot2_loader + call _save_multiboot_information_pointer + + call _assert_cpuid_instruction_is_supported + call _assert_cpu_supports_long_mode + + push $HUGE_PAGES_TO_MAP + call _prepare_page_maps + add $4, %esp + + call _enable_paging + call _enable_sse + call _reload_gdt + + lea (_entry64 - 0b)(%esi), %eax + pushl $global_descriptor_table_code + pushl %eax + lret + +/** + * @brief Halt the system. + * + * This function will instruct the CPU to halt. It will try to keep the CPU + * halted, even if interrupts occur. + * + * @return This function never returns. + */ +_halt: + function_start + +1: + hlt + jmp 1b + + function_end + +/** + * @brief Print a message via the VGA text buffer. + * + * @param ebp+12 The message to print. + * @param ebp+8 The color to print the message in. + */ +_print: + pie_function_start + + push %edi + push %ebx + + mov 8(%ebp), %al + mov 12(%ebp), %edx + mov $0, %ecx + lea (vga_buffer_pointer - 0b)(%esi), %edi + mov (%edi), %edi + +1: + mov (%edx, %ecx), %bl + test %bl, %bl + je 2f + mov %bl, (%edi, %ecx, 2) + mov %al, 1(%edi, %ecx, 2) + inc %ecx + jmp 1b + +2: + shl $1, %ecx + add %ecx, %edi + lea (vga_buffer_pointer - 0b)(%esi), %ecx + mov %edi, (%ecx) + + pop %ebx + pop %edi + + pie_function_end + +/** + * @brief Print a given panic message and then halt the machine as if by calling ::halt() + * + * @param ebp+4 A message to print. + * @return This function does not return. + */ +_panic: + pie_function_start + + lea (message_prefix_panic - 0b)(%esi), %eax + + push %eax + push $0x4f + call _print + + mov 8(%ebp), %eax + mov %eax, 4(%esp) + call _print + add $8, %esp + + call _halt + + pie_function_end + +/** + * Assert that we were loaded by a Multiboot 2 compliant bootloader. + * + * This assertion will panic the system if the magic signature was not found. + * If we were loaded my an appropriate bootloader, this function also saves + * the provided MBI pointer to `multiboot_information_pointer`. + */ +_assert_loaded_by_multiboot2_loader: + pie_function_start + + cmp $MULTIBOOT2_MAGIC, %eax + je 1f + lea (message_not_loaded_by_multiboot2 - 0b)(%esi), %eax + push %eax + call _panic +1: + pie_function_end + +/** + * @brief Store the multiboot 2 information pointer in the global memory. + * + * @return void + */ +_save_multiboot_information_pointer: + pie_function_start + + lea (multiboot_information_pointer - 0b)(%esi), %eax + mov %ebx, (%eax) + + pie_function_end + +/** + * @brief Assert that the CPU supports the CPUID instruction. + * + * The primary way to check for support of the instruction is to flip the ID + * bin in EFLAGS and then check if this changed was accepted. If so, the CPU + * supports the CPUID instruction, otherwise it most-likely doesn't. + */ +_assert_cpuid_instruction_is_supported: + pie_function_start + + pushfl + pop %eax + mov %eax, %ecx + + xor $(1 << 21), %eax /* Flip the ID bit */ + push %eax /* Move the new bitset on the stack for loading */ + popfl /* Load the flags with ID set back into EFLAGS */ + pushfl /* Copy the flags back onto the stack */ + pop %eax /* Load the flags for further checking */ + + push %ecx + popfl + + cmp %ecx, %eax + jne 1f + lea (message_cpuid_instruction_no_supported - 0b)(%esi), %eax + push %eax + call _panic + +1: + pie_function_end + +/** + * @brief Assert that the CPU supports going into long mode. + */ +_assert_cpu_supports_long_mode: + pie_function_start + + mov $0x80000000, %eax + cpuid + cmp $0x80000001, %eax + jb 1f + + mov $0x80000001, %eax + cpuid + test $(1 << 29), %edx + jnz 2f +1: + lea (message_long_mode_not_supported - 0b)(%esi), %eax + push %eax + call _panic +2: + pie_function_end + +/** + * @brief Prepare a basic page map hierarchy + * + * @param ebp+8 The number of huge pages to map + * @return void + */ +_prepare_page_maps: + pie_function_start + + push %edi + + call _clear_page_map_memory + + lea (page_map_level_4 - 0b)(%esi), %edi + lea (page_map_level_3_low - 0b)(%esi), %eax + or $0b11, %eax + mov %eax, (%edi) + + lea (page_map_level_3_high - 0b)(%esi), %eax + or $0b11, %eax + mov %eax, (511 * 8)(%edi) + + lea (page_map_level_3_low - 0b)(%esi), %edi + lea (page_map_level_2 - 0b)(%esi), %eax + or $0b11, %eax + mov %eax, (%edi) + + lea (page_map_level_3_high - 0b)(%esi), %edi + lea (page_map_level_2 - 0b)(%esi), %eax + or $0b11, %eax + mov %eax, (510 * 8)(%edi) + + lea (page_map_level_2 - 0b)(%esi), %edi + mov 8(%ebp), %ecx + +1: + dec %ecx + mov $(1 << 21), %eax + mul %ecx + or $0b10000011, %eax + mov %eax, (%edi, %ecx, 8) + + test %ecx, %ecx + jnz 1b + + pop %edi + + pie_function_end + +/** + * @brief Clear all page map memory by filling it with 0s. + * + * @return void + */ +_clear_page_map_memory: + pie_function_start + + push %edi + + xor %eax, %eax + mov $page_maps_size, %ecx + shr $2, %ecx + lea (page_maps_start - 0b)(%esi), %edi + rep stosl + + pop %edi + + pie_function_end + +/** + * @p Enable memory virtualization via paging. + * + * Note: This routine expects for there to be a valid set of page maps already + * set up for use. + * + * @return void + */ +_enable_paging: + pie_function_start + + lea (page_map_level_4 - 0b)(%esi), %eax + mov %eax, %cr3 + + /* Enable Physical Address Extension */ + mov %cr4, %eax + or $(1 << 5), %eax + mov %eax, %cr4 + + /* Enable long mode support */ + mov $0xC0000080, %ecx + rdmsr + or $(1 << 8), %eax + wrmsr + + /* Enable paging */ + mov %cr0, %eax + or $(1 << 31), %eax + mov %eax, %cr0 + + pie_function_end + +/** + * @brief Enable use of SSE instructions. + */ +_enable_sse: + function_start + + mov %cr0, %eax + and $0xfffffffb, %eax + or $0x00000002, %eax + mov %eax, %cr0 + + mov %cr4, %eax + or $(3 << 9), %eax + mov %eax, %cr4 + + function_end + +/** + * @brief Prepare a new GTD and load make it active. + * + * @return void + */ +_reload_gdt: + pie_function_start + + sub $10, %esp + lea (global_descriptor_table - 0b)(%esi), %eax + movw $(global_descriptor_table_end - global_descriptor_table -1), (%esp) + mov %eax, 2(%esp) + movl $0, 6(%esp) + + lgdt (%esp) + add $10, %esp + + pie_function_end diff --git a/arch/x86_64/arch/boot/entry64.s b/arch/x86_64/arch/boot/entry64.s new file mode 100644 index 0000000..29fb778 --- /dev/null +++ b/arch/x86_64/arch/boot/entry64.s @@ -0,0 +1,62 @@ +.section .stack, "aw", @nobits + +.align 16 +.global stack_top +stack_bottom: .skip 1 << 20 +stack_top: +stack_size = stack_top - stack_bottom + +.section .bss, "aw", @nobits + +//! A structure containing information gathered during the bootstrap process. +//! Expected layout (as described by teachos::boot::information): +//! +//! struct +//! { +//! multiboot2::information_view const * mbi; +//! std::size_t vga_buffer_index; +//! } +.global bootstrap_information +bootstrap_information: .skip 16 + +.section .boot_text, "ax", @progbits +.code64 + +.global _entry64 +_entry64: + mov $global_descriptor_table_data, %rax + mov %rax, %ss + mov %rax, %ds + mov %rax, %es + mov %rax, %fs + mov %rax, %gs + + mov $stack_top, %rsp + mov %rsp, %rbp + + mov multiboot_information_pointer, %rax + or $TEACHOS_VMA, %rax + mov vga_buffer_pointer, %rdx + sub $0xb8000, %rdx + mov %rax, (bootstrap_information) + mov %rdx, (bootstrap_information + 8) + + call invoke_global_constructors + + xor %rax, %rax + mov %rax, %rbp + mov %rax, %rdx + mov %rax, %rsi + + mov $stack_size, %rcx + shr $3, %rcx + lea (stack_bottom), %rdi + rep stosq + + mov %rax, %rdi + + call main + +1: + hlt + jmp 1b diff --git a/arch/x86_64/arch/boot/initialize_runtime.cpp b/arch/x86_64/arch/boot/initialize_runtime.cpp new file mode 100644 index 0000000..b08c13c --- /dev/null +++ b/arch/x86_64/arch/boot/initialize_runtime.cpp @@ -0,0 +1,22 @@ +#include +#include +#include + +namespace arch::boot +{ + + extern "C" + { + using global_initializer = auto (*)() -> void; + + extern global_initializer __init_array_start; + extern global_initializer __init_array_end; + + auto invoke_global_constructors() -> void + { + auto initializers = std::span{&__init_array_start, &__init_array_end}; + std::ranges::for_each(initializers, [](auto invokable) { std::invoke(invokable); }); + } + } + +} // namespace arch::boot \ No newline at end of file diff --git a/arch/x86_64/arch/boot/ld.hpp b/arch/x86_64/arch/boot/ld.hpp new file mode 100644 index 0000000..988723d --- /dev/null +++ b/arch/x86_64/arch/boot/ld.hpp @@ -0,0 +1,61 @@ +//! @file +//! The interface to linker script defined symbols. +//! +//! This header provides declarations for symbols that are defined in the linker script itself. The symbols declared +//! here provide important information, for example the start and end of the kernel image in virtual and physical +//! memory. +//! +//! Any variables defined in this file must not be read themselves, but rather their address shall be taken, yielding a +//! pointer to the memory location the represent. +//! +//! @note The symbols declared in this header are declared using C-language linkage in order to suppress name mangling. +//! +//! @see arch/x86_64/scripts/kernel.ld + +#ifndef TEACHOS_X86_64_BOOT_LD_HPP +#define TEACHOS_X86_64_BOOT_LD_HPP + +#include + +namespace arch::boot +{ + + extern "C" + { + //! The beginning of the kernel image in physical memory + //! + //! This symbol marks the start of the kernel image in physical memory. + //! + //! @see _end_physical + extern std::byte _start_physical; + + //! The first byte after the loaded kernel image. + //! + //! This symbol marks the end of the kernel image in physical memory. + //! + //! @see _start_physical + extern std::byte _end_physical; + + //! The first byte of the loaded kernel image in the virtual address space. + //! + //! This symbol and marks the start of the kernel image in virtual memory. + //! + //! @see _end_virtual + extern std::byte _start_virtual; + + //! The first byte after the loaded kernel image in the virtual address space. + //! + //! This symbol marks the end of the kernel image in virtual memory. + //! + //! @see _start_virtual + extern std::byte _end_virtual; + + //! The first byte of the kernel's virtual address space. + //! + //! This symbol marks beginning of the kernel virtual address space. + extern std::byte TEACHOS_VMA; + } + +} // namespace arch::boot + +#endif diff --git a/arch/x86_64/arch/boot/multiboot.s b/arch/x86_64/arch/boot/multiboot.s new file mode 100644 index 0000000..37d8afe --- /dev/null +++ b/arch/x86_64/arch/boot/multiboot.s @@ -0,0 +1,33 @@ +.section .boot_mbh, "a" +.align 8 + +multiboot_header_start: +.Lmagic: + .long 0xe85250d6 +.Larch: + .long 0 +.Llength: + .long multiboot_header_end - multiboot_header_start +.Lchecksum: + .long 0x100000000 - (0xe85250d6 + 0 + (multiboot_header_end - multiboot_header_start)) +.align 8 +.Lflags_start: + .word 4 + .word 1 + .long .Lflags_end - .Lflags_start + .long 3 +.Lflags_end: +.align 8 +.Linformation_request_start: + .word 1 + .word 0 + .long .Linformation_request_end - .Linformation_request_start + .long 3 +.Linformation_request_end: +.align 8 +.Lend_start: + .word 0 + .word 0 + .long .Lend_end - .Lend_start +.Lend_end: +multiboot_header_end: diff --git a/arch/x86_64/arch/bus/isa.cpp b/arch/x86_64/arch/bus/isa.cpp new file mode 100644 index 0000000..f6cc72d --- /dev/null +++ b/arch/x86_64/arch/bus/isa.cpp @@ -0,0 +1,14 @@ +#include + +#include + +#include + +namespace arch::bus +{ + + isa::isa(std::size_t major) + : kapi::devices::bus{major, 0, "isa"} + {} + +} // namespace arch::bus \ No newline at end of file diff --git a/arch/x86_64/arch/bus/isa.hpp b/arch/x86_64/arch/bus/isa.hpp new file mode 100644 index 0000000..e56f56a --- /dev/null +++ b/arch/x86_64/arch/bus/isa.hpp @@ -0,0 +1,18 @@ +#ifndef TEACHOS_X86_64_BUS_ISA_HPP +#define TEACHOS_X86_64_BUS_ISA_HPP + +#include + +#include + +namespace arch::bus +{ + + struct isa final : public kapi::devices::bus + { + isa(std::size_t major); + }; + +} // namespace arch::bus + +#endif // TEACHOS_X86_64_BUS_ISA_HPP diff --git a/arch/x86_64/arch/cpu/control_register.hpp b/arch/x86_64/arch/cpu/control_register.hpp new file mode 100644 index 0000000..9cedc35 --- /dev/null +++ b/arch/x86_64/arch/cpu/control_register.hpp @@ -0,0 +1,249 @@ +#ifndef TEACHOS_X86_64_CPU_CONTROL_REGISTERS_HPP +#define TEACHOS_X86_64_CPU_CONTROL_REGISTERS_HPP + +// IWYU pragma: private, include + +#include + +#include + +#include +#include +#include +#include + +namespace arch::cpu +{ + namespace impl + { + //! The assembler templates used to access (r/w) CR0; + constexpr auto static cr0_asm = std::pair{"mov %%cr0, %0", "mov %0, %%cr0"}; + + //! The assembler templates used to access (r/w) CR2; + constexpr auto static cr2_asm = std::pair{"mov %%cr2, %0", "mov %0, %%cr2"}; + + //! The assembler templates used to access (r/w) CR3; + constexpr auto static cr3_asm = std::pair{"mov %%cr3, %0", "mov %0, %%cr3"}; + } // namespace impl + + //! The flags that can be set on CR0 configuration register. + enum struct cr0_flags : uint64_t + { + //! Enable protected mode. + protection_enable = 1uz << 0, + //! Enable wait-monitoring of the coprocessor after task switching. + monitor_coprocessor = 1uz << 1, + //! Emulate floating point coprocessor. + emulation = 1uz << 2, + //! Marks that a task switch has occurred. + task_switched = 1uz << 3, + //! Marks Intel 387 DX math coprocessor as available + extension_type = 1uz << 4, + //! Numeric error handling mode. + numeric_error = 1uz << 5, + //! Disable writing to read-only marked memory. + write_protect = 1uz << 16, + //! Enable Ring-3 alignment checks + alignment_check = 1uz << 18, + //! Disable write through + not_write_through = 1uz << 29, + //! Disable caching of memory accesses + cache_disable = 1uz << 30, + //! Enable paging + paging = 1uz << 31 + }; + + enum struct cr3_flags : std::uint64_t + { + page_level_write_through = 1uz << 0, + page_level_cache_disable = 1uz << 1, + }; +} // namespace arch::cpu + +namespace kstd::ext +{ + template<> + struct is_bitfield_enum : std::true_type + { + }; + + template<> + struct is_bitfield_enum : std::true_type + { + }; +} // namespace kstd::ext + +namespace arch::cpu +{ + //! A mixin for flag-oriented control registers. + //! + //! This mixin provides additional functionality for flag-oriented, or partially flag-oriented, control registers. A + //! control register is flag-oriented, if it comprises a bitfield and zero or more additional non-bitfield parts. + //! + //! @tparam Derived The class deriving from this mixin. + //! @tparam ValueType The value type of the class deriving from this mixin. + template + struct control_register_with_flags + { + private: + constexpr control_register_with_flags() noexcept = default; + friend Derived; + }; + + //! @copydoc control_register_with_flags + //! + //! @note This specialization provides the implementation for the case in which the value type of the control register + //! is an enum. + template + struct control_register_with_flags>> + { + //! The type of the flags used by this control register + using flags = ValueType; + + //! Set one or more flags in this control register. + //! + //! @warning This function is to be considered **UNSAFE**. Setting flags in a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value One or a combination of flags to be set in the control register. + auto static set(ValueType value) -> void + { + auto current = Derived::read(); + current |= value; + Derived::write(current); + } + + //! Clear one or more flags in this control register. + //! + //! @warning This function is to be considered **UNSAFE**. Clearing flags in a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value One or a combination of flags to be cleared in the control register. + auto static clear(ValueType value) -> void + { + auto current = Derived::read(); + current &= ~value; + Derived::write(current); + } + }; + + //! A CPU control register. + //! + //! CPU control registers are used to configure builtin features of the CPU, for example memory protection and FPU + //! error reporting. Writing to a control register is inherently dangerous, since a misconfiguration can leave the CPU + //! in an invalid/undefined state. + template + struct control_register : control_register_with_flags, ValueType> + { + //! Read the current value of the control register. + //! + //! @return The currently set value of the control register. + [[nodiscard]] auto static read() -> ValueType + { + auto value = ValueType{}; + asm volatile((AssemblerTemplates->first) : "=r"(value)); + return value; + } + + //! Write a new value to the control register. + //! + //! @warning This function should be considered **UNSAFE**. Writing values to a control register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param value The new value to write to the control register. + auto static write(ValueType value) -> void + { + asm volatile((AssemblerTemplates->second) : : "r"(value)); + } + }; + + //! The value type of the CR3 control register. + //! + //! The CR3 control register holds the root configuration of the virtual memory protection mechanism. It contains the + //! page aligned physical address of the root page map, as well as the root paging configuration flags. + struct cr3_value + { + //! Contstruct a 0-value CR3 value. + constexpr cr3_value() = default; + + //! Construct a CR3 value using the given root page map address and flags. + //! + //! @param address The physical address of the root page map + //! @param flags The root configuration flags of the paging system. + constexpr cr3_value(kapi::memory::physical_address address, cr3_flags flags = static_cast(0)) + : m_flags{static_cast(flags)} + , m_address{static_cast(address.raw())} + {} + + //! Extract the physical address of the root page map from this value. + //! + //! @return The physical address of the root page map. + [[nodiscard]] constexpr auto address() const -> kapi::memory::physical_address + { + constexpr auto address_shift = 12uz; + return kapi::memory::physical_address{m_address << address_shift}; + } + + //! Encode the frame aligned physical address of the root page map into this value. + //! + //! @param frame The frame containing a PML4. + constexpr auto frame(kapi::memory::frame frame) -> void + { + m_address = static_cast(frame.number()); + } + + //! Extract the root paging configuration flags from this value. + //! + //! @return The root paging configuration flags. + [[nodiscard]] constexpr auto flags() const -> cr3_flags + { + return static_cast(m_flags); + } + + //! Encode the root paging configuration flags into this value. + //! + //! @param flags The root paging configuration flags. + constexpr auto flags(cr3_flags flags) -> void + { + m_flags = static_cast(flags); + } + + //! Add the given flags to the current set of encoded root configuration flags of this value. + //! + //! @param flags The root configuration flags to add. + //! @return A reference to this value. + constexpr auto operator|=(cr3_flags flags) -> cr3_value & + { + m_flags |= static_cast(flags); + return *this; + } + + //! Mask the root configuration flags of this value. + //! + //! @param mask The mask to apply to the root configuration flags. + //! @return A reference to this value. + constexpr auto operator&=(cr3_flags mask) -> cr3_value & + { + m_flags &= static_cast(mask); + return *this; + } + + private: + //! Reserved bits. + std::uint64_t : 3; + //! The root paging configuration flags. + std::uint64_t m_flags : 2 {}; + //! Reserved bits. + std::uint64_t : 7; + //! The page aligned physical address of the root page map. + std::uint64_t m_address : 52 {}; + }; + + static_assert(sizeof(cr3_value) == sizeof(std::uint64_t)); + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/cpu/global_descriptor_table.hpp b/arch/x86_64/arch/cpu/global_descriptor_table.hpp new file mode 100644 index 0000000..b17c509 --- /dev/null +++ b/arch/x86_64/arch/cpu/global_descriptor_table.hpp @@ -0,0 +1,91 @@ +#ifndef TEACHOS_X86_64_GLOBAL_DESCRIPTOR_TABLE_HPP +#define TEACHOS_X86_64_GLOBAL_DESCRIPTOR_TABLE_HPP + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace arch::cpu +{ + + template + struct global_descriptor_table; + + struct [[gnu::packed]] global_descriptor_table_pointer + { + template + global_descriptor_table_pointer(global_descriptor_table const & gdt) + : size{GdtSize * sizeof(segment_descriptor) - 1} + , address{kapi::memory::physical_address{std::bit_cast(&gdt)}.raw()} + {} + + auto load() -> void + { + asm volatile("lgdt %0" : : "m"(*this)); + } + + std::uint16_t size{}; + std::uint64_t address{}; + }; + + static_assert(sizeof(global_descriptor_table_pointer) == sizeof(std::uint16_t) + sizeof(std::uint64_t)); + + template + struct global_descriptor_table + { + template... SegmentDescriptors> + constexpr global_descriptor_table(SegmentDescriptors const &... descriptors) + : m_descriptors{} + { + auto descriptor_data = std::array{ + std::pair{std::bit_cast(&descriptors), sizeof(descriptors)} + ... + }; + auto written_size = 0uz; + std::ranges::for_each(descriptor_data, [&written_size, this](auto entry) { + std::ranges::copy(entry.first, entry.first + entry.second, m_descriptors.begin() + written_size); + written_size += entry.second; + }); + } + + auto load(std::size_t code_segment_index, std::size_t data_segment_index) const -> void + { + auto pointer = global_descriptor_table_pointer{*this}; + pointer.load(); + + asm volatile("push %0\n" + "lea 1f(%%rip), %%rax\n" + "push %%rax\n" + "lretq\n" + "1:\n" + "mov %1, %%rax\n" + "mov %%rax, %%ss\n" + "mov %%rax, %%ds\n" + "mov %%rax, %%es\n" + "mov %%rax, %%fs\n" + "mov %%rax, %%gs\n" + : + : "X"(code_segment_index * sizeof(segment_descriptor)), + "X"(data_segment_index * sizeof(segment_descriptor)) + : "rax"); + } + + private: + std::array m_descriptors; + }; + + template... SegmentDescriptors> + global_descriptor_table(SegmentDescriptors const &... descriptors) + -> global_descriptor_table<(sizeof(SegmentDescriptors) + ...)>; + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/cpu/initialization.cpp b/arch/x86_64/arch/cpu/initialization.cpp new file mode 100644 index 0000000..1be9c82 --- /dev/null +++ b/arch/x86_64/arch/cpu/initialization.cpp @@ -0,0 +1,164 @@ +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace arch::cpu +{ + + namespace + { + constexpr auto gdt_null_descriptor = segment_descriptor{}; + + constexpr auto gdt_kernel_code_descriptor = segment_descriptor{ + .limit_low = 0xffff, + .base_low = 0, + .accessed = false, + .read_write = false, + .direction_or_conforming = false, + .executable = true, + .type = segment_type::code_or_data, + .privilege_level = 0, + .present = true, + .limit_high = 0xf, + .long_mode = true, + .protected_mode = false, + .granularity = segment_granularity::page, + .base_high = 0, + }; + + constexpr auto gdt_kernel_data_descriptor = segment_descriptor{ + .limit_low = 0xffff, + .base_low = 0, + .accessed = false, + .read_write = true, + .direction_or_conforming = false, + .executable = false, + .type = segment_type::code_or_data, + .privilege_level = 0, + .present = true, + .limit_high = 0xf, + .long_mode = false, + .protected_mode = true, + .granularity = segment_granularity::page, + .base_high = 0, + }; + + constexpr auto gdt_user_code_descriptor = segment_descriptor{ + .limit_low = 0xffff, + .base_low = 0, + .accessed = false, + .read_write = false, + .direction_or_conforming = false, + .executable = true, + .type = segment_type::code_or_data, + .privilege_level = 3, + .present = true, + .limit_high = 0xf, + .long_mode = true, + .protected_mode = false, + .granularity = segment_granularity::page, + .base_high = 0, + }; + + constexpr auto gdt_user_data_descriptor = segment_descriptor{ + .limit_low = 0xffff, + .base_low = 0, + .accessed = false, + .read_write = true, + .direction_or_conforming = false, + .executable = false, + .type = segment_type::code_or_data, + .privilege_level = 3, + .present = true, + .limit_high = 0xf, + .long_mode = false, + .protected_mode = false, + .granularity = segment_granularity::page, + .base_high = 0, + }; + + constexpr auto make_tss_descriptor(task_state_segment const * tss_ptr) -> system_segment_descriptor + { + auto const address = std::bit_cast(tss_ptr); + auto const limit = sizeof(task_state_segment) - 1; + + return system_segment_descriptor{ + { + .limit_low = limit & 0xffff, // NOLINT(readability-magic-numbers) + .base_low = address & 0xffffff, // NOLINT(readability-magic-numbers) + .accessed = false, + .read_write = false, + .direction_or_conforming = false, + .executable = false, + .type = segment_type::system, + .privilege_level = 0, + .present = true, + .limit_high = (limit >> 16) & 0xf, // NOLINT(readability-magic-numbers) + .long_mode = false, + .protected_mode = false, + .granularity = segment_granularity::byte, + .base_high = (address >> 24) & 0xff, // NOLINT(readability-magic-numbers) + }, + (address >> 32) & 0xffff'ffff, // NOLINT(readability-magic-numbers) + }; + } + } // namespace + + auto initialize_descriptors() -> void + { + auto static tss = task_state_segment{}; + auto static tss_descriptor = make_tss_descriptor(&tss); + + auto static gdt = global_descriptor_table{ + gdt_null_descriptor, gdt_kernel_code_descriptor, gdt_kernel_data_descriptor, + gdt_user_code_descriptor, gdt_user_data_descriptor, tss_descriptor, + }; + + kstd::println("[x86_64:SYS] Reloading Global Descriptor Table."); + gdt.load(1, 2); + + kstd::println("[x86_64:SYS] Initializing Interrupt Descriptor Table."); + auto static idt = interrupt_descriptor_table{}; + idt.load(); + } + + auto initialize_legacy_interrupts() -> void + { + constexpr auto pic_init_command = std::uint8_t{0x11}; + constexpr auto pic_master_offset = std::uint8_t{0x20}; + constexpr auto pic_slave_offset = std::uint8_t{0x28}; + constexpr auto pic_cascade_address = std::uint8_t{0x04}; + constexpr auto pic_cascade_slave_identity = std::uint8_t{0x02}; + constexpr auto pic_use_8086_mode = std::uint8_t{0x01}; + constexpr auto pic_master_mask = std::uint8_t{0x00}; + constexpr auto pic_slave_mask = std::uint8_t{0x00}; + + pic_master_control_port::write(pic_init_command); + pic_slave_control_port::write(pic_init_command); + + pic_master_data_port::write(pic_master_offset); + pic_slave_data_port::write(pic_slave_offset); + + pic_master_data_port::write(pic_cascade_address); + pic_slave_data_port::write(pic_cascade_slave_identity); + + pic_master_data_port::write(pic_use_8086_mode); + pic_slave_data_port::write(pic_use_8086_mode); + + pic_master_data_port::write(pic_master_mask); + pic_slave_data_port::write(pic_slave_mask); + + pic_master_data_port::write(pic_master_mask); + pic_slave_data_port::write(pic_slave_mask); + } + +} // namespace arch::cpu diff --git a/arch/x86_64/arch/cpu/initialization.hpp b/arch/x86_64/arch/cpu/initialization.hpp new file mode 100644 index 0000000..564c544 --- /dev/null +++ b/arch/x86_64/arch/cpu/initialization.hpp @@ -0,0 +1,12 @@ +#ifndef TEACHOS_ARCH_X86_64_CPU_INITIALIZATION_HPP +#define TEACHOS_ARCH_X86_64_CPU_INITIALIZATION_HPP + +namespace arch::cpu +{ + auto initialize_descriptors() -> void; + + auto initialize_legacy_interrupts() -> void; + +} // namespace arch::cpu + +#endif diff --git a/arch/x86_64/arch/cpu/interrupts.S b/arch/x86_64/arch/cpu/interrupts.S new file mode 100644 index 0000000..e59bdd2 --- /dev/null +++ b/arch/x86_64/arch/cpu/interrupts.S @@ -0,0 +1,112 @@ +.altmacro + +.macro ISR_WITHOUT_ERROR_CODE vector + .global isr\vector + isr\vector: + pushq $0 + pushq $\vector + jmp common_interrupt_handler +.endm + +.macro ISR_WITH_ERROR_CODE vector + .global isr\vector + isr\vector: + pushq $\vector + jmp common_interrupt_handler +.endm + +.macro ISR_TABLE_ENTRY vector + .quad isr\vector +.endm + +.section .rodata +.global isr_stub_table +.align 16 + +isr_stub_table: +.set i, 0 +.rept 256 + ISR_TABLE_ENTRY %i + .set i, i + 1 +.endr + + +.section .text + +common_interrupt_handler: + push %rax + push %rbx + push %rcx + push %rdx + push %rbp + push %rsi + push %rdi + push %r8 + push %r9 + push %r10 + push %r11 + push %r12 + push %r13 + push %r14 + push %r15 + + mov %rsp, %rdi + call interrupt_dispatch + + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %r11 + pop %r10 + pop %r9 + pop %r8 + pop %rdi + pop %rsi + pop %rbp + pop %rdx + pop %rcx + pop %rbx + pop %rax + + add $16, %rsp + iretq + +ISR_WITHOUT_ERROR_CODE 0 +ISR_WITHOUT_ERROR_CODE 1 +ISR_WITHOUT_ERROR_CODE 2 +ISR_WITHOUT_ERROR_CODE 3 +ISR_WITHOUT_ERROR_CODE 4 +ISR_WITHOUT_ERROR_CODE 5 +ISR_WITHOUT_ERROR_CODE 6 +ISR_WITHOUT_ERROR_CODE 7 +ISR_WITH_ERROR_CODE 8 +ISR_WITHOUT_ERROR_CODE 9 +ISR_WITH_ERROR_CODE 10 +ISR_WITH_ERROR_CODE 11 +ISR_WITH_ERROR_CODE 12 +ISR_WITH_ERROR_CODE 13 +ISR_WITH_ERROR_CODE 14 +ISR_WITHOUT_ERROR_CODE 15 +ISR_WITHOUT_ERROR_CODE 16 +ISR_WITH_ERROR_CODE 17 +ISR_WITHOUT_ERROR_CODE 18 +ISR_WITHOUT_ERROR_CODE 19 +ISR_WITHOUT_ERROR_CODE 20 +ISR_WITH_ERROR_CODE 21 +ISR_WITHOUT_ERROR_CODE 22 +ISR_WITHOUT_ERROR_CODE 23 +ISR_WITHOUT_ERROR_CODE 24 +ISR_WITHOUT_ERROR_CODE 25 +ISR_WITHOUT_ERROR_CODE 26 +ISR_WITHOUT_ERROR_CODE 27 +ISR_WITHOUT_ERROR_CODE 28 +ISR_WITH_ERROR_CODE 29 +ISR_WITH_ERROR_CODE 30 +ISR_WITHOUT_ERROR_CODE 31 + +.set i, 32 +.rept 256 - 32 + ISR_WITHOUT_ERROR_CODE %i + .set i, i + 1 +.endr diff --git a/arch/x86_64/arch/cpu/interrupts.cpp b/arch/x86_64/arch/cpu/interrupts.cpp new file mode 100644 index 0000000..f40422f --- /dev/null +++ b/arch/x86_64/arch/cpu/interrupts.cpp @@ -0,0 +1,203 @@ +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +namespace arch::cpu +{ + + namespace + { + enum struct exception + { + divide_error, + debug_exception, + non_maskable_interrupt, + breakpoint, + overflow, + bound_range_exceeded, + invalid_opcode, + device_not_available, + double_fault, + coprocessor_segment_overrun, + invalid_tss, + segment_not_present, + stack_segment_fault, + general_protection_fault, + page_fault, + x87_fpu_floating_point_error = 16, + alignment_check, + machine_check, + simd_floating_point_error, + virtualization_exception, + control_protection_exception, + hypervisor_injection_exception = 28, + vmm_communication_exception, + security_exception, + }; + + constexpr auto number_of_exception_vectors = 32u; + + constexpr auto pic_master_irq_start = 0x20; + constexpr auto pic_master_irq_end = pic_master_irq_start + 8; + constexpr auto pic_slave_irq_start = pic_master_irq_end; + + constexpr auto to_exception_type(exception e) + { + switch (e) + { + case exception::divide_error: + case exception::x87_fpu_floating_point_error: + case exception::simd_floating_point_error: + return kapi::cpu::exception::type::arithmetic_error; + case exception::breakpoint: + return kapi::cpu::exception::type::breakpoint; + case exception::invalid_opcode: + return kapi::cpu::exception::type::illegal_instruction; + case exception::stack_segment_fault: + return kapi::cpu::exception::type::memory_access_fault; + case exception::general_protection_fault: + return kapi::cpu::exception::type::privilege_violation; + case exception::page_fault: + return kapi::cpu::exception::type::page_fault; + case exception::alignment_check: + return kapi::cpu::exception::type::alignment_fault; + default: + return kapi::cpu::exception::type::unknown; + } + } + + constexpr auto has_error_code(exception e) + { + switch (e) + { + case exception::double_fault: + case exception::invalid_tss: + case exception::segment_not_present: + case exception::stack_segment_fault: + case exception::general_protection_fault: + case exception::page_fault: + case exception::alignment_check: + case exception::control_protection_exception: + case exception::security_exception: + return true; + default: + return false; + } + } + + auto dispatch_exception(interrupt_frame * frame) -> bool + { + auto type = to_exception_type(static_cast(frame->interrupt.number)); + auto fault_address = kapi::memory::linear_address{}; + auto instruction_pointer = frame->cpu_saved.rip; + + switch (type) + { + case kapi::cpu::exception::type::page_fault: + { + asm volatile("mov %%cr2, %0" : "=r"(fault_address)); + auto present = (frame->interrupt.error_code & 0x1) != 0; + auto write = (frame->interrupt.error_code & 0x2) != 0; + auto user = (frame->interrupt.error_code & 0x4) != 0; + + return kapi::cpu::dispatch({type, instruction_pointer, fault_address, present, write, user}); + } + default: + return kapi::cpu::dispatch({type, instruction_pointer}); + } + } + + auto acknowledge_pic_interrupt(interrupt_frame * frame) -> void + { + if (frame->interrupt.number >= pic_slave_irq_start) + { + pic_slave_control_port::write(pic_end_of_interrupt); + } + pic_master_control_port::write(pic_end_of_interrupt); + } + } // namespace + + extern "C" + { + extern std::uintptr_t const isr_stub_table[256]; + + auto interrupt_dispatch(interrupt_frame * frame) -> void + { + auto [number, code] = frame->interrupt; + + if (number < number_of_exception_vectors) + { + if (!dispatch_exception(frame)) + { + if (has_error_code(static_cast(number))) + { + kstd::println(kstd::print_sink::stderr, + "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); + } + else + { + kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled exception number {:#04x} received", number); + } + } + } + else + { + auto irq_number = number - number_of_exception_vectors; + + if (kapi::interrupts::dispatch(irq_number) == kapi::interrupts::status::unhandled) + { + kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x} (IRQ{})", number, + irq_number); + } + + acknowledge_pic_interrupt(frame); + } + } + } + + interrupt_descriptor_table::interrupt_descriptor_table() noexcept + { + for (auto i = 0uz; i < 256; ++i) + { + m_descriptors[i] = gate_descriptor{ + .offset_low = static_cast(isr_stub_table[i] & 0xffff), // NOLINT(readability-magic-numbers) + .m_code_segment = segment_selector{0, false, 1}, + .interrupt_stack_table_selector = 0, + .gate_type = (i < 32 && i != 2) ? gate_type::trap_gate : gate_type::interrupt_gate, + .descriptor_privilege_level = 0, + .present = true, + .offset_middle = + static_cast((isr_stub_table[i] >> 16) & 0xffff), // NOLINT(readability-magic-numbers) + .offset_high = + static_cast((isr_stub_table[i] >> 32) & 0xffff'ffff), // NOLINT(readability-magic-numbers) + }; + } + } + + auto interrupt_descriptor_table::load() const -> void + { + interrupt_descriptor_table_register{.limit = sizeof(m_descriptors) - 1, .base = m_descriptors.data()}.load(); + } + + auto interrupt_descriptor_table_register::load() const -> void + { + asm volatile("lidt %0" : : "m"(*this)); + } + + auto interrupt_descriptor_table_register::read() -> interrupt_descriptor_table_register + { + interrupt_descriptor_table_register idtr{}; + asm volatile("sidt %0" : : "m"(idtr)); + return idtr; + } + +} // namespace arch::cpu \ No newline at end of file diff --git a/arch/x86_64/arch/cpu/interrupts.hpp b/arch/x86_64/arch/cpu/interrupts.hpp new file mode 100644 index 0000000..6162f56 --- /dev/null +++ b/arch/x86_64/arch/cpu/interrupts.hpp @@ -0,0 +1,116 @@ +#ifndef TEACHOS_X86_64_CPU_INTERRUPTS_HPP +#define TEACHOS_X86_64_CPU_INTERRUPTS_HPP + +#include + +#include + +#include +#include +#include + +namespace arch::cpu +{ + + //! The types of supported gates. + enum struct gate_type : std::uint8_t + { + //! A gate entered through the @p INT instruction. + interrupt_gate = 0b1110, + //! A gate entered via an exception. + trap_gate = 0b1111, + }; + + struct alignas(std::uint64_t) gate_descriptor + { + //! The lowest 16 bits of the address of this gate's handler function. + std::uint16_t offset_low : 16; + //! The code segment used when handling the respective interrupt. + segment_selector m_code_segment; + //! The index into the Interrupt Stack Table naming the stack to use. + std::uint8_t interrupt_stack_table_selector : 3; + //! Reserved + std::uint8_t : 5; + //! The type of this gate. + enum gate_type gate_type : 4; + //! Reserved + std::uint8_t : 1; + //! The privilege level required to enter through this gate. + std::uint8_t descriptor_privilege_level : 2; + //! Whether this gate is present or not. + std::uint8_t present : 1; + //! The middle 16 bits of the address of this gate's handler function. + std::uint16_t offset_middle : 16; + //! The highest 32 bits of the address of this gate's handler function. + std::uint32_t offset_high : 32; + //! Reserved + std::uint32_t : 32; + }; + + static_assert(sizeof(gate_descriptor) == 2 * sizeof(std::uint64_t)); + static_assert(std::is_aggregate_v); + + //! The stack frame as established by the low-level assembly interrupt stubs. + //! + //! @note The layout of this struct is reverse than what would reasonably be expected. The reason for this reversal is + //! that fact that it represents a stack frame. Stack frames on x86_64 grow towards lower addresses, meaning the first + //! item on the stack is the last item in C++ memory layout order. + struct interrupt_frame + { + struct + { + std::uint64_t r15{}; + std::uint64_t r14{}; + std::uint64_t r13{}; + std::uint64_t r12{}; + std::uint64_t r11{}; + std::uint64_t r10{}; + std::uint64_t r9{}; + std::uint64_t r8{}; + std::uint64_t rdi{}; + std::uint64_t rsi{}; + std::uint64_t rbp{}; + std::uint64_t rdx{}; + std::uint64_t rcx{}; + std::uint64_t rbx{}; + std::uint64_t rax{}; + } handler_saved; + + struct + { + std::uint64_t number{}; + std::uint64_t error_code{}; + } interrupt; + + struct + { + kapi::memory::linear_address rip{}; + std::uint64_t cs{}; + std::uint64_t rflags{}; + kapi::memory::linear_address rsp{}; + std::uint64_t ss{}; + } cpu_saved; + }; + + struct interrupt_descriptor_table + { + interrupt_descriptor_table() noexcept; + + auto load() const -> void; + + private: + std::array m_descriptors{}; + }; + + struct [[gnu::packed]] interrupt_descriptor_table_register + { + std::uint16_t limit; + gate_descriptor const * base; + + auto load() const -> void; + auto static read() -> interrupt_descriptor_table_register; + }; + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/cpu/legacy_pic.hpp b/arch/x86_64/arch/cpu/legacy_pic.hpp new file mode 100644 index 0000000..56ca9c4 --- /dev/null +++ b/arch/x86_64/arch/cpu/legacy_pic.hpp @@ -0,0 +1,19 @@ +#ifndef TEACHOS_X86_64_CPU_LEGACY_PIC_HPP +#define TEACHOS_X86_64_CPU_LEGACY_PIC_HPP + +#include + +#include + +namespace arch::cpu +{ + using pic_master_control_port = io::port<0x20, std::uint8_t, io::port_read, io::port_write>; + using pic_master_data_port = io::port<0x21, std::uint8_t, io::port_read, io::port_write>; + using pic_slave_control_port = io::port<0xa0, std::uint8_t, io::port_read, io::port_write>; + using pic_slave_data_port = io::port<0xa1, std::uint8_t, io::port_read, io::port_write>; + + constexpr auto pic_end_of_interrupt = std::uint8_t{0x20}; + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/cpu/model_specific_register.hpp b/arch/x86_64/arch/cpu/model_specific_register.hpp new file mode 100644 index 0000000..bd4aff9 --- /dev/null +++ b/arch/x86_64/arch/cpu/model_specific_register.hpp @@ -0,0 +1,151 @@ +#ifndef TEACHOS_X86_64_CPU_MODEL_SPECIFIC_REGISTER_HPP +#define TEACHOS_X86_64_CPU_MODEL_SPECIFIC_REGISTER_HPP + +// IWYU pragma: private, include + +#include + +#include +#include +#include + +namespace arch::cpu +{ + + //! The flags of the IA32_EFER (Extended Features Enable Register) MSR. + enum struct ia32_efer_flags : std::uint64_t + { + //! Enable the syscall and sysret instructions. + syscall_enable = 1uz << 0, + //! Enable IA-32e mode operation. + ia32e_mode_enable = 1uz << 8, + //! Indicates IA-32e mode is active (read-only) + ia32e_mode_active = 1uz << 10, + //! Enable the use of the NX page table bit. + execute_disable_bit_enable = 1uz << 11, + }; + +} // namespace arch::cpu + +namespace kstd::ext +{ + + template<> + struct is_bitfield_enum : std::true_type + { + }; + +} // namespace kstd::ext + +namespace arch::cpu +{ + //! The MSR number for the IA32_EFER MSR + constexpr auto ia32_efer_number = 0xC000'0080u; + + //! A mixin for flag-oriented model specific registers. + //! + //! This mixin provides additional functionality for a flag-oriented model specific register. A models specific + //! register is flag-oriented, if it comprises a single field of bitfield. + //! + //! @tparam Derived The class deriving from this mixin. + //! @tparam ValueType The value type of the class deriving from this mixin. + template + struct model_specific_register_with_flags + { + private: + constexpr model_specific_register_with_flags() noexcept = default; + friend Derived; + }; + + //! @copydoc model_specific_register_with_flags + //! + //! @note This specialization provides the implementation for the case in which the value type of the model specific + //! register is a bitfield enum. + template + struct model_specific_register_with_flags>> + { + //! The of the flags used by this model specific register. + using flags = ValueType; + + //! Set one or more flags in this model specific register. + //! + //! @warning This function is to be considered **UNSAFE**. Setting flags in a model specific register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param flag One or a combination of flags to be set in the model specific register. + auto static set(flags flag) -> void + { + auto current = Derived::read(); + current |= flag; + Derived::write(current); + } + + //! Clear one or more flags in this model specific register. + //! + //! @warning This function is to be considered **UNSAFE**. Clearing flags in a model specific register may lead to + //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function + //! will perform no additional checks, and may, by extension, crash the system. + //! + //! @param flag One or a combination of flags to be cleared in the model specific register. + auto static clear(flags flag) -> void + { + auto current = Derived::read(); + current &= ~flag; + Derived::write(current); + } + + //! Test one or more flags in this model specific register + //! + //! @param flag One or a combination of flags to test for. + auto test(flags flag) -> flags + { + return Derived::read() & flag; + } + }; + + //! A model specific register (MSR) + //! + //! Model specific register are used to configure CPU features that a not necessarily present on all CPUs generations. + //! In the past, some MSRs have been defined to be architectural, meaning all CPUs of a given architecture (x86-64 in + //! this case) support them. Writing to a MSR is inherently dangerous, since a misconfiguration cal leave the CPU in + //! an invalid/undefined state. + //! + //! @tparam Number The register number of this MSR + //! @tparam ValueType The value type of this MSR + template + struct model_specific_register + : model_specific_register_with_flags, ValueType> + { + //! A raw MSR value, comprising two halfs. + //! + //! MSRs have been 64-bit in size even in the 32-bit intel architecture, and are thus written in two halfs. + struct raw_value + { + std::uint32_t low_half; //!< The lower half of the register value + std::uint32_t high_half; //!< The upper half of the register value + }; + + //! Read the current value of this MSR. + //! + //! @return The current value of this MSR. + auto static read() -> ValueType + { + auto raw = raw_value{}; + asm volatile("rdmsr" : "=a"(raw.low_half), "=d"(raw.high_half) : "c"(Number)); + return static_cast(std::bit_cast(raw)); + } + + //! Write a new value to this MSR. + //! + //! @param value The new value for this MSR. + auto static write(ValueType value) -> void + { + auto raw = std::bit_cast(static_cast(value)); + asm volatile("wrmsr" : : "a"(raw.low_half), "d"(raw.high_half), "c"(Number)); + } + }; + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/cpu/registers.hpp b/arch/x86_64/arch/cpu/registers.hpp new file mode 100644 index 0000000..58633f6 --- /dev/null +++ b/arch/x86_64/arch/cpu/registers.hpp @@ -0,0 +1,32 @@ +#ifndef TEACHOS_X86_64_CPU_REGISTERS_HPP +#define TEACHOS_X86_64_CPU_REGISTERS_HPP + +#include // IWYU pragma: export +#include // IWYU pragma: export + +#include + +namespace arch::cpu +{ + + //! Configuration Register 0. + //! + //! This configuration register holds various control flags to configure the configure the basic operation of the CPU. + using cr0 = control_register; + + //! Configuration Register 2. + //! + //! This configuration register holds the memory address the access to which has triggered the most recent page fault. + using cr2 = control_register; + + //! Configuration Register 3. + //! + //! This register holds the configuration of the virtual memory protection configuration. + using cr3 = control_register; + + //! The I32_EFER (Extended Feature Enable Register) MSR + using i32_efer = model_specific_register; + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/cpu/segment_descriptor.hpp b/arch/x86_64/arch/cpu/segment_descriptor.hpp new file mode 100644 index 0000000..9570670 --- /dev/null +++ b/arch/x86_64/arch/cpu/segment_descriptor.hpp @@ -0,0 +1,61 @@ +#ifndef TEACHOS_X86_64_SEGMENT_DESCRIPTOR_HPP +#define TEACHOS_X86_64_SEGMENT_DESCRIPTOR_HPP + +#include + +namespace arch::cpu +{ + + //! The type of segment described by a segment_descriptor. + enum struct segment_type : std::uint8_t + { + //! A system (TSS or LDT) segment + system = 0, + //! A code or data segment + code_or_data = 1, + }; + + //! The granularity of a segment described by a segment_descriptor + enum struct segment_granularity : std::uint8_t + { + //! The limit of the segment is defined in bytes. + byte = 0, + //! The limit of the segment is defined in pages (4KiB) + page = 1, + }; + + //! An entry in a segment descriptor table + struct segment_descriptor + { + std::uint64_t limit_low : 16; + std::uint64_t base_low : 24; + bool accessed : 1; + bool read_write : 1; + bool direction_or_conforming : 1; + bool executable : 1; + segment_type type : 1; + std::uint64_t privilege_level : 2; + bool present : 1; + std::uint64_t limit_high : 4; + std::uint64_t : 1; + bool long_mode : 1; + bool protected_mode : 1; + segment_granularity granularity : 1; + std::uint64_t base_high : 8; + }; + + static_assert(sizeof(segment_descriptor) == sizeof(std::uint64_t)); + static_assert(alignof(segment_descriptor) == alignof(std::uint64_t)); + + struct system_segment_descriptor : segment_descriptor + { + std::uint64_t base_extended : 32; + std::uint64_t : 32; + }; + + static_assert(sizeof(system_segment_descriptor) == 2 * sizeof(std::uint64_t)); + static_assert(alignof(system_segment_descriptor) == alignof(segment_descriptor)); + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/cpu/segment_selector.hpp b/arch/x86_64/arch/cpu/segment_selector.hpp new file mode 100644 index 0000000..1a78c47 --- /dev/null +++ b/arch/x86_64/arch/cpu/segment_selector.hpp @@ -0,0 +1,21 @@ +#ifndef TEACHOS_X86_64_SEGMENT_SELECTOR_HPP +#define TEACHOS_X86_64_SEGMENT_SELECTOR_HPP + +#include + +namespace arch::cpu +{ + + struct segment_selector + { + std::uint16_t request_privilege_level : 2; + bool use_local_descriptor_table : 1; + std::uint16_t table_index : 13; + }; + + static_assert(sizeof(segment_selector) == sizeof(std::uint16_t)); + static_assert(alignof(segment_selector) == alignof(std::uint16_t)); + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/cpu/task_state_segment.hpp b/arch/x86_64/arch/cpu/task_state_segment.hpp new file mode 100644 index 0000000..57729dd --- /dev/null +++ b/arch/x86_64/arch/cpu/task_state_segment.hpp @@ -0,0 +1,30 @@ +#ifndef TEACHOS_X86_64_TASK_STATE_SEGMENT_HPP +#define TEACHOS_X86_64_TASK_STATE_SEGMENT_HPP + +#include + +namespace arch::cpu +{ + + struct [[gnu::packed]] task_state_segment + { + uint32_t : 32; + uint64_t rsp0 = {}; + uint64_t rsp1 = {}; + uint64_t rsp2 = {}; + uint64_t : 64; + uint64_t ist1 = {}; + uint64_t ist2 = {}; + uint64_t ist3 = {}; + uint64_t ist4 = {}; + uint64_t ist5 = {}; + uint64_t ist6 = {}; + uint64_t ist7 = {}; + uint64_t : 64; + uint16_t : 16; + uint16_t io_map_base_address = {}; + }; + +} // namespace arch::cpu + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/debug/qemu_output.cpp b/arch/x86_64/arch/debug/qemu_output.cpp new file mode 100644 index 0000000..71acede --- /dev/null +++ b/arch/x86_64/arch/debug/qemu_output.cpp @@ -0,0 +1,25 @@ +#include + +#include + +#include +#include + +namespace arch::debug +{ + + qemu_output::qemu_output(output_device & lower) + : m_lower{lower} + , m_present{port::read() == port::address} + {} + + auto qemu_output::write(kapi::cio::output_stream stream, std::string_view text) -> void + { + if (m_present) + { + std::ranges::for_each(text, port::write); + } + m_lower.write(stream, text); + } + +} // namespace arch::debug diff --git a/arch/x86_64/arch/debug/qemu_output.hpp b/arch/x86_64/arch/debug/qemu_output.hpp new file mode 100644 index 0000000..5ddd4be --- /dev/null +++ b/arch/x86_64/arch/debug/qemu_output.hpp @@ -0,0 +1,43 @@ +#ifndef TEACHOS_X86_64_DEBUG_QEMU_OUTPUT_HPP +#define TEACHOS_X86_64_DEBUG_QEMU_OUTPUT_HPP + +#include + +#include + +#include + +namespace arch::debug +{ + + //! A QEMU debug console output device. + //! + //! This device implements output to the port 0xE9 debug console present in QEMU in Bochs. It is designed to wrap a + //! different output device (e.g. a VGA text output device) and forwards the written data accordingly. + //! + //! @note Support for the device has to be enabled when the emulator is started. The device will try to detect if the + //! port is available. If the port is detected, any output to the device will be written to port before being + //! forwarded to the lower device. + struct qemu_output : kapi::cio::output_device + { + //! The port to write to. + using port = io::port<0xE9, unsigned char, io::port_write, io::port_read>; + + //! Construct a new debug device wrapper for the given output device. + //! + //! @param lower The device to forward the output to. + explicit qemu_output(output_device & lower); + + //! @copydoc kapi::cio::output_device + auto write(kapi::cio::output_stream stream, std::string_view text) -> void override; + + private: + //! The device to forward the output to. + output_device & m_lower; + //! Whether the device has been detected. + bool m_present; + }; + +} // namespace arch::debug + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/device_io/port_io.hpp b/arch/x86_64/arch/device_io/port_io.hpp new file mode 100644 index 0000000..4c8d66a --- /dev/null +++ b/arch/x86_64/arch/device_io/port_io.hpp @@ -0,0 +1,112 @@ +#ifndef TEACHOS_X86_64_IO_PORT_IO_HPP +#define TEACHOS_X86_64_IO_PORT_IO_HPP + +#include +#include +#include +#include +#include +#include + +namespace arch::io +{ + + //! The requirements imposed on a type usable for port I/O. + template + concept port_io_type = requires { + requires sizeof(ValueType) == 1 || sizeof(ValueType) == 2 || sizeof(ValueType) == 4; + requires std::default_initializable; + std::bit_cast( + std::conditional_t>{}); + }; + + template + struct port_read + { + //! Read from the I/O port. + //! + //! @return The data read from the I/O port. + auto static read() noexcept + { + auto data = typename Derived::value_type{}; + asm volatile((code[Derived::size / 2]) + : [data] "=m"(data) + : [port] "i"(Derived::address) + : "dx", (Derived::data_register)); + return data; + } + + private: + constexpr port_read() noexcept = default; + friend Derived; + + //! The assembly templates used for reading from an I/O port. + constexpr auto static code = std::array{ + std::string_view{"mov %[port], %%dx\nin %%dx, %%al\nmov %%al, %[data]"}, + std::string_view{"mov %[port], %%dx\nin %%dx, %%ax\nmov %%ax, %[data]"}, + std::string_view{"mov %[port], %%dx\nin %%dx, %%eax\nmov %%eax, %[data]"}, + }; + }; + + template + struct port_write + { + //! Write data to the I/O port. + //! + //! @param data The data to write to the I/O port. + auto static write(std::same_as auto data) noexcept -> void + { + asm volatile((code[Derived::size / 2]) + : + : [port] "i"(Derived::address), [data] "im"(data) + : "dx", (Derived::data_register)); + } + + private: + constexpr port_write() noexcept = default; + friend Derived; + + //! The assembly templates used for writing to an I/O port. + constexpr auto static code = std::array{ + std::string_view{"mov %[port], %%dx\nmov %[data], %%al\nout %%al, %%dx"}, + std::string_view{"mov %[port], %%dx\nmov %[data], %%ax\nout %%ax, %%dx"}, + std::string_view{"mov %[port], %%dx\nmov %[data], %%eax\nout %%eax, %%dx"}, + }; + }; + + //! An I/O port of a given size at a given address. + //! + //! Port I/O leverages a separate address space to communicate with devices via the memory bus, allowing for byte + //! to double-word sized transfers. + //! + //! @tparam Address The address (port number) of the I/O port. + //! @tparam Size The size (in bytes) of the I/O port. + //! @tparam Features The features (readable, writeable) + template typename... Features> + requires(sizeof...(Features) > 0) + struct port : Features>... + { + //! The type of the data of this port. + using value_type = ValueType; + + //! The address of this I/O port. + constexpr auto static address = Address; + + //! The size of this I/O port. + constexpr auto static size = sizeof(value_type); + + //! The register clobbered by the I/O operation. + constexpr auto static data_register = size == 1 ? std::string_view{"al"} + : size == 2 ? std::string_view{"ax"} + : std::string_view{"eax"}; + }; + + auto inline wait() -> void + { + port<0x80, std::uint8_t, port_write>::write(0); + } + +} // namespace arch::io + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/devices/init.cpp b/arch/x86_64/arch/devices/init.cpp new file mode 100644 index 0000000..c30e8cf --- /dev/null +++ b/arch/x86_64/arch/devices/init.cpp @@ -0,0 +1,76 @@ +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include + +namespace arch::devices +{ + + namespace + { + constexpr auto pit_frequency_in_hz = std::uint32_t{100u}; + + auto get_acpi_root_pointer() -> kstd::observer_ptr<::acpi::rsdp const> + { + auto const & mbi = kapi::boot::bootstrap_information.mbi; + auto system_description_pointer = static_cast<::acpi::rsdp const *>(nullptr); + + if (auto const & xsdp = mbi->maybe_acpi_xsdp()) + { + auto data = xsdp->pointer().data(); + + system_description_pointer = reinterpret_cast<::acpi::xsdp const *>(data); + } + else if (auto const & rsdp = mbi->maybe_acpi_rsdp()) + { + auto data = rsdp->pointer().data(); + system_description_pointer = reinterpret_cast<::acpi::rsdp const *>(data); + } + + return kstd::make_observer(system_description_pointer); + } + } // namespace + + auto init_acpi_devices() -> void + { + auto acpi_root_pointer = get_acpi_root_pointer(); + if (acpi_root_pointer && acpi_root_pointer->validate()) + { + if (kapi::acpi::init(*acpi_root_pointer)) + { + kstd::println("[x86_64:DEV] ACPI subsystem initialized."); + } + } + + kapi::cpu::discover_topology(); + } + + auto init_legacy_devices() -> void + { + kstd::println("[x86_64:DEV] Initializing ISA bus..."); + + auto isa_major_number = kapi::devices::allocate_major_number(); + auto isa_bus = kstd::make_unique(isa_major_number); + + auto pit_major_number = kapi::devices::allocate_major_number(); + auto pit = kstd::make_unique(pit_major_number, pit_frequency_in_hz); + isa_bus->add_child(std::move(pit)); + + auto & root_bus = kapi::devices::get_root_bus(); + root_bus.add_child(std::move(isa_bus)); + } + +} // namespace arch::devices diff --git a/arch/x86_64/arch/devices/init.hpp b/arch/x86_64/arch/devices/init.hpp new file mode 100644 index 0000000..c5fbf37 --- /dev/null +++ b/arch/x86_64/arch/devices/init.hpp @@ -0,0 +1,12 @@ +#ifndef TEACHOS_ARCH_X86_64_DEVICES_INIT_HPP +#define TEACHOS_ARCH_X86_64_DEVICES_INIT_HPP + +namespace arch::devices +{ + + auto init_acpi_devices() -> void; + auto init_legacy_devices() -> void; + +} // namespace arch::devices + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/devices/legacy_pit.cpp b/arch/x86_64/arch/devices/legacy_pit.cpp new file mode 100644 index 0000000..d542d47 --- /dev/null +++ b/arch/x86_64/arch/devices/legacy_pit.cpp @@ -0,0 +1,60 @@ +#include + +#include + +#include +#include +#include + +#include +#include + +namespace arch::devices +{ + + namespace + { + using command_port = io::port<0x43, std::uint8_t, io::port_write>; + using channel_0_port = io::port<0x40, std::uint8_t, io::port_write>; + using channel_1_port = io::port<0x41, std::uint8_t, io::port_write>; + using channel_2_port = io::port<0x42, std::uint8_t, io::port_write>; + + constexpr auto base_frequency = 1'193'182u; + constexpr auto square_wave_mode = 0x36; + } // namespace + + legacy_pit::legacy_pit(std::size_t major, std::uint32_t frequency_in_hz) + : kapi::devices::device{major, 0, "legacy_pit"} + , m_irq_number{0} + , m_frequency_in_hz{frequency_in_hz} + {} + + auto legacy_pit::init() -> bool + { + auto divisor = static_cast(base_frequency / m_frequency_in_hz); + + kapi::interrupts::register_handler(m_irq_number, *this); + + command_port::write(square_wave_mode); + io::wait(); + channel_0_port::write(divisor & 0xff); + io::wait(); + channel_0_port::write(divisor >> 8 & 0xff); + io::wait(); + + return true; + } + + auto legacy_pit::handle_interrupt(std::uint32_t irq_number) -> kapi::interrupts::status + { + if (irq_number != m_irq_number) + { + return kapi::interrupts::status::unhandled; + } + + ++m_ticks; + + return kapi::interrupts::status::handled; + } + +} // namespace arch::devices \ No newline at end of file diff --git a/arch/x86_64/arch/devices/legacy_pit.hpp b/arch/x86_64/arch/devices/legacy_pit.hpp new file mode 100644 index 0000000..356895c --- /dev/null +++ b/arch/x86_64/arch/devices/legacy_pit.hpp @@ -0,0 +1,29 @@ +#ifndef TEACHOS_ARCH_X86_64_DEVICES_LEGACY_PIT_HPP +#define TEACHOS_ARCH_X86_64_DEVICES_LEGACY_PIT_HPP + +#include +#include + +#include +#include + +namespace arch::devices +{ + + struct legacy_pit : kapi::devices::device, kapi::interrupts::handler + { + legacy_pit(std::size_t major, std::uint32_t frequency_in_hz); + + auto init() -> bool override; + + auto handle_interrupt(std::uint32_t irq_number) -> kapi::interrupts::status override; + + private: + std::uint32_t m_irq_number{}; + std::uint32_t m_frequency_in_hz{}; + std::uint64_t m_ticks{}; + }; + +} // namespace arch::devices + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/devices/local_apic.cpp b/arch/x86_64/arch/devices/local_apic.cpp new file mode 100644 index 0000000..660921b --- /dev/null +++ b/arch/x86_64/arch/devices/local_apic.cpp @@ -0,0 +1,134 @@ +#include + +#include +#include + +#include + +#include +#include +#include + +namespace arch::devices +{ + + namespace + { + constexpr auto lapic_enable_bit = 0x100u; + constexpr auto spurious_interrupt_vector = 0xFFu; + + constexpr auto offset_of_version = 0u; + constexpr auto offset_of_max_lvt_entry = 16u; + constexpr auto offset_of_eoi_suppression = 24u; + + } // namespace + + enum struct local_apic::registers : std::ptrdiff_t + { + id = 0x020, + version = 0x030, + task_priority = 0x080, + arbitration_priority = 0x090, + processor_priority = 0x0a0, + eoi = 0x0b0, + remote_read = 0x0c0, + logical_destination = 0x0d0, + destination_format = 0x0e0, + spurious_interrupt_vector = 0x0f0, + in_service_0 = 0x100, + in_service_1 = 0x110, + in_service_2 = 0x120, + in_service_3 = 0x130, + in_service_4 = 0x140, + in_service_5 = 0x150, + in_service_6 = 0x160, + in_service_7 = 0x170, + trigger_mode_0 = 0x180, + trigger_mode_1 = 0x190, + trigger_mode_2 = 0x1a0, + trigger_mode_3 = 0x1b0, + trigger_mode_4 = 0x1c0, + trigger_mode_5 = 0x1d0, + trigger_mode_6 = 0x1e0, + trigger_mode_7 = 0x1f0, + interrupt_request_0 = 0x200, + interrupt_request_1 = 0x210, + interrupt_request_2 = 0x220, + interrupt_request_3 = 0x230, + interrupt_request_4 = 0x240, + interrupt_request_5 = 0x250, + interrupt_request_6 = 0x260, + interrupt_request_7 = 0x270, + error_status = 0x280, + lvt_corrected_machine_check_interrupt = 0x2f0, + interrupt_command_0 = 0x300, + interrupt_command_1 = 0x310, + lvt_timer = 0x320, + lvt_thermal_sensors = 0x330, + lvt_performance_monitoring_counters = 0x340, + lvt_local_interrupt_0 = 0x350, + lvt_local_interrupt_1 = 0x360, + lvt_error = 0x370, + initial_count = 0x380, + current_count = 0x390, + divide_configuration = 0x3e0, + }; + + local_apic::local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id, + kapi::memory::physical_address base, bool is_bsp) + : kapi::devices::device{major, minor, "lapic"} + , m_hardware_id{hardware_id} + , m_base{base} + , m_is_bsp{is_bsp} + {} + + auto local_apic::init() -> bool + { + auto static shared_virtual_base = kapi::memory::allocate_mmio_region(1); + auto static is_mapped = false; + + if (!is_mapped) + { + if (!kapi::memory::map_mmio_region(shared_virtual_base, m_base, kapi::memory::page_mapper::flags::writable)) + { + kstd::println("[x86_64:DEV] LAPIC {} MMIO mapping failed!", m_hardware_id); + return false; + } + is_mapped = true; + } + + m_mapped_region = shared_virtual_base; + + if (m_is_bsp) + { + auto raw_version = read_register(registers::version); + m_version = (raw_version >> offset_of_version) & 0xff; + m_highest_lvt_entry_index = (raw_version >> offset_of_max_lvt_entry) & 0xff; + m_supports_eoi_broadcast_suppression = (raw_version >> offset_of_eoi_suppression) & 0x1; + + write_register(registers::spurious_interrupt_vector, lapic_enable_bit | spurious_interrupt_vector); + + kstd::println("[x86_64:DEV] LAPIC initialized. version: {#x} | max_lvt_entry: {} | eoi_suppression: {:s}", + m_version, m_highest_lvt_entry_index, m_supports_eoi_broadcast_suppression); + } + else + { + kstd::println("[x86_64:DEV] LAPIC {} is not on the BSP, deferring initialization.", m_hardware_id); + } + + return true; + } + + auto local_apic::read_register(registers id) const -> std::uint32_t + { + auto reg = static_cast(m_mapped_region.first + std::to_underlying(id)); + return *reg; + } + + auto local_apic::write_register(registers id, std::uint32_t value) -> void + { + auto reg = static_cast(m_mapped_region.first + std::to_underlying(id)); + *reg = value; + } + +} // namespace arch::devices diff --git a/arch/x86_64/arch/devices/local_apic.hpp b/arch/x86_64/arch/devices/local_apic.hpp new file mode 100644 index 0000000..f8f080d --- /dev/null +++ b/arch/x86_64/arch/devices/local_apic.hpp @@ -0,0 +1,37 @@ +#ifndef TEACHOS_ARCH_X86_64_DEVICES_LOCAL_APIC_HPP +#define TEACHOS_ARCH_X86_64_DEVICES_LOCAL_APIC_HPP + +#include +#include + +#include +#include + +namespace arch::devices +{ + + struct local_apic : kapi::devices::device + { + local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id, kapi::memory::physical_address base, + bool is_bsp); + + auto init() -> bool override; + + private: + enum struct registers : std::ptrdiff_t; + + [[nodiscard]] auto read_register(registers id) const -> std::uint32_t; + auto write_register(registers id, std::uint32_t value) -> void; + + std::uint64_t m_hardware_id{}; + kapi::memory::physical_address m_base{}; + kapi::memory::mmio_region m_mapped_region{}; + bool m_is_bsp{}; + std::uint8_t m_version{}; + std::uint8_t m_highest_lvt_entry_index{}; + bool m_supports_eoi_broadcast_suppression{}; + }; + +} // namespace arch::devices + +#endif diff --git a/arch/x86_64/arch/memory/higher_half_mapper.cpp b/arch/x86_64/arch/memory/higher_half_mapper.cpp new file mode 100644 index 0000000..75adb3c --- /dev/null +++ b/arch/x86_64/arch/memory/higher_half_mapper.cpp @@ -0,0 +1,119 @@ +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace arch::memory +{ + + higher_half_mapper::higher_half_mapper(page_table * root) + : m_root{root} + {} + + auto higher_half_mapper::map(kapi::memory::page page, kapi::memory::frame frame, flags flags) -> std::byte * + { + auto table = get_or_create_page_table(page); + if (!table) + { + return nullptr; + } + + auto const index = pml_index(1, page); + auto & entry = (*table)[index]; + + if (entry.present()) + { + kapi::system::panic("[x86_64:MEM] Tried to map a page that is already mapped!"); + } + + entry.frame(frame, to_table_flags(flags) | page_table::entry::flags::present); + + return static_cast(page.start_address()); + } + + auto higher_half_mapper::unmap(kapi::memory::page page) -> void + { + if (!try_unmap(page)) + { + kapi::system::panic("[x86_64:MEM] Tried to unmap a page that is not mapped!"); + } + } + + auto higher_half_mapper::try_unmap(kapi::memory::page page) noexcept -> bool + { + auto table_path = std::array, 4>{}; + table_path[0] = std::pair{m_root, pml_index(4, page)}; + + for (auto level = 4uz; level > 1uz; --level) + { + auto [table, index] = table_path[4 - level]; + auto & entry = (*table)[index]; + + if (!entry.present()) + { + return false; + } + + auto next_table = to_higher_half_pointer(entry.frame()->start_address()); + auto next_index = pml_index(4 - level - 1, page); + table_path[4 - level - 1] = std::pair{next_table, next_index}; + } + + std::ranges::for_each(std::views::reverse(table_path), [previous_was_empty = true](auto & step) mutable { + auto [table, index] = step; + auto & entry = (*table)[index]; + auto frame = entry.frame(); + + if (previous_was_empty) + { + entry.clear(); + previous_was_empty = table->empty(); + kapi::memory::get_frame_allocator().release(*frame); + } + }); + + return true; + } + + auto higher_half_mapper::get_or_create_page_table(kapi::memory::page page) noexcept -> page_table * + { + auto table = m_root; + + for (auto level = 4uz; level > 1uz; --level) + { + auto index = pml_index(level, page); + auto & entry = (*table)[index]; + + if (!entry.present()) + { + auto table_frame = kapi::memory::allocate_frame(); + if (!table_frame) + { + return nullptr; + } + + auto new_table = to_higher_half_pointer(table_frame->start_address()); + std::construct_at(new_table); + + auto const flags = page_table::entry::flags::present | page_table::entry::flags::writable | + page_table::entry::flags::user_accessible; + entry.frame(*table_frame, flags); + } + + table = to_higher_half_pointer(entry.frame()->start_address()); + } + + return table; + } + +} // namespace arch::memory \ No newline at end of file diff --git a/arch/x86_64/arch/memory/higher_half_mapper.hpp b/arch/x86_64/arch/memory/higher_half_mapper.hpp new file mode 100644 index 0000000..9b02ee6 --- /dev/null +++ b/arch/x86_64/arch/memory/higher_half_mapper.hpp @@ -0,0 +1,44 @@ +#ifndef TEACHOS_X86_64_HIGHER_HALF_MAPPER_HPP +#define TEACHOS_X86_64_HIGHER_HALF_MAPPER_HPP + +#include + +#include + +#include + +namespace arch::memory +{ + + //! A simple page mapper making use of a Higher Half Direct Map (HHDM) to access and modify page tables. + struct higher_half_mapper : kapi::memory::page_mapper + { + //! Construct a new mapper for a hierarchy rooted in the given PML. + //! + //! @param root The root of the hierarchy to operate on. + explicit higher_half_mapper(page_table * root); + + //! @copydoc kapi::memory::page_mapper::map + auto map(kapi::memory::page page, kapi::memory::frame frame, flags flags) -> std::byte * override; + + //! @copydoc kapi::memory::page_mapper::unmap + auto unmap(kapi::memory::page page) -> void override; + + //! @copydoc kapi::memory::page_mapper::try_unmap + auto try_unmap(kapi::memory::page page) noexcept -> bool override; + + private: + //! Try to retrieve the the PML1 responsible for mapping this page, creating one if necessary. + //! + //! This function will create a page table hierarchy leading to the target PML1 if it doesn't exist. + //! + //! @param page The page to get the PML1 for. + //! @return The PML1 that manages the given page, nullptr it the system runs out of memory. + auto get_or_create_page_table(kapi::memory::page page) noexcept -> page_table *; + + page_table * m_root; + }; + +} // namespace arch::memory + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/memory/kernel_mapper.cpp b/arch/x86_64/arch/memory/kernel_mapper.cpp new file mode 100644 index 0000000..74272a0 --- /dev/null +++ b/arch/x86_64/arch/memory/kernel_mapper.cpp @@ -0,0 +1,117 @@ +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace std::string_view_literals; +using namespace kstd::units_literals; + +namespace arch::memory +{ + + namespace + { + constexpr auto static ignored_section_prefixes = std::array{ + ".boot_"sv, + }; + + constexpr auto static user_accessible_prefixes = std::array{ + ".user"sv, + ".stl"sv, + }; + + } // namespace + + kernel_mapper::kernel_mapper(multiboot2::information_view const * mbi) + : m_mbi{std::move(mbi)} + , m_kernel_load_base{std::bit_cast(&arch::boot::TEACHOS_VMA)} + {} + + auto kernel_mapper::remap_kernel(kapi::memory::page_mapper & mapper) -> void + { + auto elf_information = m_mbi->maybe_elf_symbols(); + if (!elf_information) + { + kapi::system::panic("[x86_64:MEM] ELF section information is not available."); + } + + auto sections = *elf_information; + auto allocated_sections = + std::views::all(sections) | std::views::filter(&elf::section_header::allocated) | + std::views::filter([&](auto const & section) -> auto { + auto name = sections.name(section); + return !std::ranges::any_of(ignored_section_prefixes, + [&](auto const & prefix) -> auto { return name.starts_with(prefix); }); + }); + + if (allocated_sections.empty()) + { + kapi::system::panic("[x86_64:MEM] No allocated ELF sections were found."); + } + + std::ranges::for_each(allocated_sections, + [&](auto const & section) -> auto { map_section(section, sections.name(section), mapper); }); + } + + auto kernel_mapper::map_section(section_header_type const & section, std::string_view name, + kapi::memory::page_mapper & mapper) -> void + { + auto number_of_pages = + (kstd::units::bytes{section.size} + (kapi::memory::page::size - 1_B)) / kapi::memory::page::size; + auto linear_start_address = kapi::memory::linear_address{section.virtual_load_address}; + auto physical_start_address = kapi::memory::physical_address{section.virtual_load_address & ~m_kernel_load_base}; + + kstd::println("[x86_64:MEM] mapping {}" + "\n {} bytes -> page count: {}" + "\n {} @ {}", + name, section.size, number_of_pages, linear_start_address, physical_start_address); + + auto first_page = kapi::memory::page::containing(linear_start_address); + auto first_frame = kapi::memory::frame::containing(physical_start_address); + + auto page_flags = kapi::memory::page_mapper::flags::empty; + + if (section.writable()) + { + page_flags |= kapi::memory::page_mapper::flags::writable; + } + + if (section.executable()) + { + page_flags |= kapi::memory::page_mapper::flags::executable; + } + + auto is_prefix_of_name = [=](auto prefix) -> bool { + return name.starts_with(prefix); + }; + + if (!std::ranges::any_of(user_accessible_prefixes, is_prefix_of_name)) + { + page_flags |= kapi::memory::page_mapper::flags::supervisor_only; + } + + for (auto i = 0uz; i < number_of_pages; ++i) + { + mapper.map(first_page + i, first_frame + i, page_flags); + } + } + +} // namespace arch::memory \ No newline at end of file diff --git a/arch/x86_64/arch/memory/kernel_mapper.hpp b/arch/x86_64/arch/memory/kernel_mapper.hpp new file mode 100644 index 0000000..adbf688 --- /dev/null +++ b/arch/x86_64/arch/memory/kernel_mapper.hpp @@ -0,0 +1,35 @@ +#ifndef TEACHOS_X86_64_KERNEL_MAPPER_HPP +#define TEACHOS_X86_64_KERNEL_MAPPER_HPP + +#include + +#include +#include + +#include + +#include +#include + +namespace arch::memory +{ + + struct kernel_mapper + { + using section_header_type = elf::section_header; + + explicit kernel_mapper(multiboot2::information_view const * mbi); + + auto remap_kernel(kapi::memory::page_mapper & mapper) -> void; + + private: + auto map_section(section_header_type const & section, std::string_view name, kapi::memory::page_mapper & mapper) + -> void; + + multiboot2::information_view const * m_mbi; + std::uintptr_t m_kernel_load_base; + }; + +} // namespace arch::memory + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/memory/mmu.cpp b/arch/x86_64/arch/memory/mmu.cpp new file mode 100644 index 0000000..2b53fa4 --- /dev/null +++ b/arch/x86_64/arch/memory/mmu.cpp @@ -0,0 +1,19 @@ +#include + +#include + +#include + +namespace arch::memory +{ + auto tlb_flush(kapi::memory::linear_address address) -> void + { + asm volatile("invlpg (%[input])" : /* no output from call */ : [input] "r"(address) : "memory"); + } + + auto tlb_flush_all() -> void + { + auto paging_root = cpu::cr3::read(); + cpu::cr3::write(paging_root); + } +} // namespace arch::memory diff --git a/arch/x86_64/arch/memory/mmu.hpp b/arch/x86_64/arch/memory/mmu.hpp new file mode 100644 index 0000000..64373f4 --- /dev/null +++ b/arch/x86_64/arch/memory/mmu.hpp @@ -0,0 +1,27 @@ +#ifndef TEACHOS_X86_64_MEMORY_MMU_HPP +#define TEACHOS_X86_64_MEMORY_MMU_HPP + +#include + +namespace arch::memory +{ + /** + * @brief Invalidates any translation lookaside buffer (TLB) entry for the page table the given address is cotained + * in. See https://www.felixcloutier.com/x86/invlpg for more information on the used x86 instruction. + * + * @param address Memory address, which will be used to determine the contained page and flush the TLB entry for + * that page. + */ + auto tlb_flush(kapi::memory::linear_address address) -> void; + + /** + * @brief Invalidates the translation lookaside buffer (TLB) entry for all page tables. + * + * @note Simply reassigns the CR3 register the value of the CR3 register, causing a flush of the TLB buffer, because + * the system has to assume that the location of the level 4 page table moved. + */ + auto tlb_flush_all() -> void; + +} // namespace arch::memory + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/memory/page_table.cpp b/arch/x86_64/arch/memory/page_table.cpp new file mode 100644 index 0000000..2180420 --- /dev/null +++ b/arch/x86_64/arch/memory/page_table.cpp @@ -0,0 +1,82 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace arch::memory +{ + + auto page_table::entry::clear() noexcept -> void + { + m_raw = 0; + } + + auto page_table::entry::present() const noexcept -> bool + { + return (all_flags() & flags::present) != flags::empty; + } + + auto page_table::entry::huge() const noexcept -> bool + { + return (all_flags() & flags::huge_page) != flags::empty; + } + + auto page_table::entry::all_flags() const noexcept -> flags + { + return std::bit_cast(m_raw & ~frame_number_mask); + } + + auto page_table::entry::all_flags(flags flags) noexcept -> void + { + m_raw = (m_raw & ~frame_number_mask) | std::to_underlying(flags); + } + + auto page_table::entry::operator|=(flags rhs) noexcept -> page_table::entry & + { + auto raw_flags = std::to_underlying(rhs) & ~frame_number_mask; + m_raw |= raw_flags; + return *this; + } + + auto page_table::entry::frame() const noexcept -> std::optional + { + if (present()) + { + return kapi::memory::frame::containing(kapi::memory::physical_address{m_raw & frame_number_mask}); + } + return std::nullopt; + } + + auto page_table::entry::frame(kapi::memory::frame frame, flags flags) noexcept -> void + { + m_raw = (frame.start_address().raw() | static_cast(flags)); + }; + + auto page_table::operator[](std::size_t index) -> entry & + { + return m_entries.at(index); + } + + auto page_table::operator[](std::size_t index) const -> entry const & + { + return m_entries.at(index); + } + + auto page_table::clear() noexcept -> void + { + std::ranges::for_each(m_entries, &page_table::entry::clear); + } + + auto page_table::empty() const noexcept -> bool + { + return std::ranges::all_of(m_entries, + [](auto const & entry) -> auto { return entry.all_flags() == entry::flags::empty; }); + } + +} // namespace arch::memory diff --git a/arch/x86_64/arch/memory/page_table.hpp b/arch/x86_64/arch/memory/page_table.hpp new file mode 100644 index 0000000..ce3d3a1 --- /dev/null +++ b/arch/x86_64/arch/memory/page_table.hpp @@ -0,0 +1,232 @@ +#ifndef TEACHOS_X86_64_PAGE_TABLE_HPP +#define TEACHOS_X86_64_PAGE_TABLE_HPP + +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace arch::memory +{ + + //! A table containing page mapping entries. + //! + //! Page tables exist in a multi-level hierarchy and are used to map pages (virtual memory) onto frames (physical + //! memory). Conceptually, pages represent the data found in a virtual address space, while frames represent their + //! storage. In most cases, only a level 1 page table maps an actual page onto a frame. All other page tables on + //! higher levels do not map payload pages, but rather their subordinate page tables. The only exception to that rule + //! is the use of huge pages. + struct page_table + { + //! An entry in a page table. + //! + //! A page table entry is a combination of a frame number and a set of flags that determine the properties and + //! access rights to a mapped page. Entries at a higher level in the page hierarchy do not map a page directly, + //! unless that page is marked as huge on the relevant level, but rather map the next lower page table. + struct entry + { + //! Flags marking the state and configuration of an entry. + //! + //! An entry in a page table may have any combination of these flags active at the same time. The final flags of a + //! page are determined as the strictest combination (logical AND) of all flags along the hierarchy. + //! + //! @note This is a bitfield enum as defined by kstd::ext::bitfield_enum. + enum struct flags : std::uint64_t + { + empty = 0, + present = 1uz << 0, //!< The page is mapped. + writable = 1uz << 1, //!< The page is writable. + user_accessible = 1uz << 2, //!< The page is accessible in user mode. + write_through = 1uz << 3, //!< Any writes to the page must immediately hit memory. + disable_cache = 1uz << 4, //!< Any writes to the page must never be cached. + accessed = 1uz << 5, //!< The page was accessed. + dirty = 1uz << 6, //!< The page was written to. + huge_page = 1uz << 7, //!< The page is huge. + global = 1uz << 8, //!< The TLB entry for this page must not be flushed on context switches. + no_execute = 1uz << 63, //!< The data in this page must not be executed. + }; + + //! Construct an empty entry. + entry() = default; + + //! Clear this entry, ensuring all information is set to zero. + //! + //! This effectively marks the page represented by this entry as not present. In addition, it also removes any + //! information about the frame referenced by this entry. + auto clear() noexcept -> void; + + //! Check if the page represented by this entry is present. + //! + //! @note This function does not attempt to walk the page table hierarchy, but only performs a local check. This + //! means, that if the page is not mapped at a lower level, this will not be detected. + //! + //! @return @p true iff. the page is present at this level, @p false otherwise. + [[nodiscard]] auto present() const noexcept -> bool; + + //! Check if the page represented by this entry is a huge page. + //! + //! @note The effective size of the page depends on the level of the page table containing this entry. + //! + //! @return @p true iff. the page is marked as being huge, @p false otherwise. + [[nodiscard]] auto huge() const noexcept -> bool; + + //! Get all flags present in this entry. + //! + //! @return The flags that are currently set on this entry. + [[nodiscard]] auto all_flags() const noexcept -> flags; + + //! Set all flags of this entry. + //! + //! @param flags The flags to apply to this entry. + auto all_flags(flags flags) noexcept -> void; + + //! Add the given flags to the flags of this entry. + //! + //! @param rhs The flags to add to this entry's flags. + //! @return A reference to this entry. + auto operator|=(flags rhs) noexcept -> entry &; + + //! Get the frame number associated with this entry, if the referenced page is present. + //! + //! @return an engaged std::optional iff. this entry maps a page, std::nullopt otherwise. + [[nodiscard]] auto frame() const noexcept -> std::optional; + + //! Map this entry. + //! + //! @param frame The frame to map in this entry. + //! @param flags The flags to apply to this entry. + auto frame(kapi::memory::frame frame, flags flags) noexcept -> void; + + private: + //! A mask to retrieve, or exclude, the frame number from the raw entry. + //! + //! Page table entries in x86_64 are a compacted combination of the relevant flags and the frame number. This mask + //! represents the bits that make up the frame number in an entry. + constexpr auto static frame_number_mask{0x000f'ffff'ffff'f000uz}; + + //! The raw entry bytes. + //! + //! @see entry::frame_number_mask + std::uint64_t m_raw{}; + }; + + //! The maximum number of entries in this table. + constexpr auto static entry_count{kapi::memory::page::size / kstd::units::bytes{sizeof(entry)}}; + + //! Get the entry at the given index. + //! + //! @warning This function will panic if the entry index is out of bounds. + //! + //! @param index The index of the desired entry. + //! @return A reference to the entry at the given index. + [[nodiscard]] auto operator[](std::size_t index) -> entry &; + + //! @copydoc page_table::operator[] + [[nodiscard]] auto operator[](std::size_t index) const -> entry const &; + + //! Clear the entire page table. + //! + //! This function effectively marks the page table as not mapping any pages. + auto clear() noexcept -> void; + + //! Check if the page table is empty. + //! + //! @return @p true iff. this page table has no entries marked present, @p false otherwise. + [[nodiscard]] auto empty() const noexcept -> bool; + + private: + std::array m_entries{}; + }; + +} // namespace arch::memory + +namespace kstd::ext +{ + template<> + struct is_bitfield_enum : std::true_type + { + }; +} // namespace kstd::ext + +namespace arch::memory +{ + + constexpr auto to_mapper_flags(page_table::entry::flags flags) -> kapi::memory::page_mapper::flags + { + using table_flags = page_table::entry::flags; + using mapper_flags = kapi::memory::page_mapper::flags; + + auto result = mapper_flags{}; + + if ((flags & table_flags::no_execute) == table_flags::empty) + { + result |= mapper_flags::executable; + } + + if ((flags & table_flags::writable) != table_flags::empty) + { + result |= mapper_flags::writable; + } + + if ((flags & (table_flags::disable_cache | table_flags::write_through)) != table_flags::empty) + { + result |= mapper_flags::uncached; + } + + if ((flags & table_flags::user_accessible) == table_flags::empty) + { + result |= mapper_flags::supervisor_only; + } + + if ((flags & table_flags::global) != table_flags::empty) + { + result |= mapper_flags::global; + } + + return result; + } + + constexpr auto to_table_flags(kapi::memory::page_mapper::flags flags) -> page_table::entry::flags + { + using table_flags = page_table::entry::flags; + using mapper_flags = kapi::memory::page_mapper::flags; + + auto result = table_flags{}; + + if ((flags & mapper_flags::executable) == mapper_flags::empty) + { + result |= table_flags::no_execute; + } + + if ((flags & mapper_flags::writable) != mapper_flags::empty) + { + result |= table_flags::writable; + } + + if ((flags & mapper_flags::uncached) != mapper_flags::empty) + { + result |= (table_flags::disable_cache | table_flags::write_through); + } + + if ((flags & mapper_flags::supervisor_only) == mapper_flags::empty) + { + result |= table_flags::user_accessible; + } + + if ((flags & mapper_flags::global) != mapper_flags::empty) + { + result |= table_flags::global; + } + + return result; + } + +} // namespace arch::memory + +#endif diff --git a/arch/x86_64/arch/memory/page_utilities.hpp b/arch/x86_64/arch/memory/page_utilities.hpp new file mode 100644 index 0000000..068e824 --- /dev/null +++ b/arch/x86_64/arch/memory/page_utilities.hpp @@ -0,0 +1,29 @@ +#ifndef TEACHOS_X86_64_PAGE_UTILITIES_HPP +#define TEACHOS_X86_64_PAGE_UTILITIES_HPP + +#include + +#include + +namespace arch::memory +{ + + constexpr auto inline pml_index(std::size_t index, kapi::memory::page page) noexcept -> std::size_t + { + constexpr auto bits_per_level = 9; + auto shift_width = (index - 1) * bits_per_level; + constexpr auto index_mask = 0x1ffuz; + return page.number() >> shift_width & index_mask; + } + + template + [[nodiscard]] constexpr auto to_higher_half_pointer(kapi::memory::physical_address address) -> ValueType * + { + using namespace kapi::memory; + auto const higher_half_address = higher_half_direct_map_base + address.raw(); + return static_cast(higher_half_address); + } + +} // namespace arch::memory + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/memory/region_allocator.cpp b/arch/x86_64/arch/memory/region_allocator.cpp new file mode 100644 index 0000000..4086a10 --- /dev/null +++ b/arch/x86_64/arch/memory/region_allocator.cpp @@ -0,0 +1,165 @@ +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace arch::memory +{ + namespace + { + constexpr auto last_frame(multiboot2::memory_map::region const & region) + { + return kapi::memory::frame::containing(kapi::memory::physical_address{region.base + region.size_in_B - 1}); + } + + constexpr auto falls_within(kapi::memory::frame const & candidate, kapi::memory::frame const & start, + kapi::memory::frame const & end) + { + return candidate >= start && candidate <= end; + } + } // namespace + + region_allocator::region_allocator(memory_information const & mem_info) + : m_next_frame{} + , m_current_region{} + , m_memory_map{mem_info.memory_map} + , m_kernel_start{kapi::memory::frame::containing(mem_info.image_range.first)} + , m_kernel_end{kapi::memory::frame::containing(mem_info.image_range.second)} + , m_multiboot_start{kapi::memory::frame::containing(mem_info.mbi_range.first)} + , m_multiboot_end{kapi::memory::frame::containing(mem_info.mbi_range.second)} + , m_multiboot_information{mem_info.mbi} + { + choose_next_region(); + } + + auto region_allocator::choose_next_region() -> void + { + m_current_region.reset(); + + auto remaining_regions = + m_memory_map | // + std::views::filter(&multiboot2::memory_map::region::available) | + std::views::filter([this](auto const & region) -> bool { return last_frame(region) >= m_next_frame; }); + + auto lowest_region = + std::ranges::min_element(remaining_regions, [](auto lhs, auto rhs) -> bool { return lhs.base < rhs.base; }); + + if (lowest_region == remaining_regions.end()) + { + return; + } + + m_current_region = *lowest_region; + if (auto start_of_region = kapi::memory::frame::containing(kapi::memory::physical_address{m_current_region->base}); + start_of_region > m_next_frame) + { + m_next_frame = start_of_region; + } + } + + auto region_allocator::find_next_frame() -> std::optional + { + if (!m_current_region || m_next_frame > last_frame(*m_current_region)) + { + choose_next_region(); + if (!m_current_region) + { + return std::nullopt; + } + } + + auto advanced = true; + while (advanced) + { + advanced = false; + + if (falls_within(m_next_frame, m_kernel_start, m_kernel_end)) + { + m_next_frame = m_kernel_end + 1; + advanced = true; + continue; + } + + if (falls_within(m_next_frame, m_multiboot_start, m_multiboot_end)) + { + m_next_frame = m_multiboot_end + 1; + advanced = true; + continue; + } + + if (m_multiboot_information) + { + for (auto const & module : m_multiboot_information->modules()) + { + auto module_start = kapi::memory::frame::containing(kapi::memory::physical_address{module.start_address}); + auto module_end = kapi::memory::frame::containing(kapi::memory::physical_address{module.end_address}); + + if (falls_within(m_next_frame, module_start, module_end)) + { + m_next_frame = module_end + 1; + advanced = true; + break; + } + } + } + } + + if (m_next_frame > last_frame(*m_current_region)) + { + choose_next_region(); + } + + return m_current_region.transform([this](auto) -> auto { return m_next_frame; }); + } + + auto region_allocator::allocate_many(std::size_t count) noexcept + -> std::optional> + { + while (m_current_region) + { + auto result = find_next_frame(); + + if (result) + { + auto region_end = last_frame(*m_current_region); + if ((region_end.number() - result->number()) >= count) + { + m_next_frame = m_next_frame + count; + return std::make_pair(*result, count); + } + else + { + m_next_frame = region_end + 1; + choose_next_region(); + } + } + } + + return std::nullopt; + } + + auto region_allocator::mark_used(kapi::memory::frame frame) -> void + { + if (frame < m_next_frame) + { + m_next_frame = frame; + find_next_frame(); + } + } + + auto region_allocator::release_many(std::pair) -> void {} + + auto region_allocator::next_free_frame() noexcept -> std::optional + { + return find_next_frame(); + } + +} // namespace arch::memory diff --git a/arch/x86_64/arch/memory/region_allocator.hpp b/arch/x86_64/arch/memory/region_allocator.hpp new file mode 100644 index 0000000..5d9da2e --- /dev/null +++ b/arch/x86_64/arch/memory/region_allocator.hpp @@ -0,0 +1,93 @@ +#ifndef TEACHOS_X86_64_MEMORY_REGION_ALLOCATOR_HPP +#define TEACHOS_X86_64_MEMORY_REGION_ALLOCATOR_HPP + +#include +#include +#include + +#include + +#include +#include +#include + +namespace arch::memory +{ + //! A simple, memory-region based frame allocator. + //! + //! This frame allocator linearly allocates frames that are in available memory regions. It automatically skips any + //! frames occupied by the kernel image or any bootloader provided data. + //! + //! @note This allocator will never release frames. + struct region_allocator final : kapi::memory::frame_allocator + { + struct memory_information + { + //! The memory range occupied by the loaded kernel image. + //! + //! This includes all sections that are marked as occupying space in the kernel executable. The internal structure + //! of this area is more described in a more fine-grained manner by the ELF symbol information provided in the + //! Multiboot2 information by the loader. + std::pair image_range; + + //! The memory range occupied by the loader supplied Multiboot2 information structure. + //! + //! In general, this information is allocated somewhere in the range of the loaded image, but the loader protocol + //! does not guarantee this. It is thus imperative to be able to handle the cases where the loader chooses to + //! allocate the information structure outside of the image range. + std::pair mbi_range; + + //! The loader supplied map of memory regions. + //! + //! These include available, unavailable, and reclaimable regions. In general, only frames that are located in + //! non-reserved (as in available) regions should be allocated for page storage. + multiboot2::memory_map memory_map; + + //! The loader supplied Multiboot2 information structure. + //! + //! This is used to query boot module ranges so these frames can be excluded from early allocations. + multiboot2::information_view const * mbi; + }; + + using region = multiboot2::memory_map::region; + + //! Construct a new allocator using the provided memory information + //! + //! @param information The description of the detected memory regions as well as regions that are already occupied. + explicit region_allocator(memory_information const & information); + + //! @copydoc kapi::memory::frame_allocator::allocate_many + //! + //! @note As long as free frames are available, successive calls to this implementation are guaranteed to yield + //! frames in ascending order. + auto allocate_many(std::size_t count = 1) noexcept + -> std::optional> override; + + //! @copydoc kapi::memory::frame_allocator::mark_used + auto mark_used(kapi::memory::frame frame) -> void override; + + //! @copydoc kapi::memory::frame_allocator::release_many + //! + //! @note This implementation will never actually release any frames. + auto release_many(std::pair frame_set) -> void override; + + auto next_free_frame() noexcept -> std::optional; + + private: + //! Find the next memory area and write it into current_area. + auto choose_next_region() -> void; + auto find_next_frame() -> std::optional; + + kapi::memory::frame m_next_frame; //!< The next available frame. + std::optional m_current_region; //!< The memory region currently used for allocation + multiboot2::memory_map m_memory_map; //!< The boot loader supplied memory map. + kapi::memory::frame m_kernel_start; //!< The start of the kernel image in physical memory. + kapi::memory::frame m_kernel_end; //!< The end of the kernel image in physical memory. + kapi::memory::frame m_multiboot_start; //!< The start of the Multiboot2 information in physical memory. + kapi::memory::frame m_multiboot_end; //!< The end of the Multiboot2 information in physical memory. + multiboot2::information_view const * m_multiboot_information; //!< Source of Multiboot2 module ranges. + }; + +} // namespace arch::memory + +#endif diff --git a/arch/x86_64/arch/vga/crtc.hpp b/arch/x86_64/arch/vga/crtc.hpp new file mode 100644 index 0000000..a8bec93 --- /dev/null +++ b/arch/x86_64/arch/vga/crtc.hpp @@ -0,0 +1,35 @@ +#ifndef TEACHOS_X86_64_VGA_IO_HPP +#define TEACHOS_X86_64_VGA_IO_HPP + +#include + +#include + +namespace arch::vga::crtc +{ + /** + * @brief The address port of the CRT Controller. + */ + using address = io::port<0x3d4, std::byte, io::port_write>; + + /** + * @brief The data port of the CRT Controller. + */ + using data = io::port<0x3d5, std::byte, io::port_read, io::port_write>; + + namespace registers + { + /** + * @brief The address of the Cursor Start register of the CRTC. + */ + [[maybe_unused]] constexpr auto cursor_start = std::byte{0x0a}; + + /** + * @brief The address of the Cursor End register of the CRTC. + */ + [[maybe_unused]] constexpr auto cursor_end = std::byte{0x0b}; + } // namespace registers + +} // namespace arch::vga::crtc + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/vga/text.hpp b/arch/x86_64/arch/vga/text.hpp new file mode 100644 index 0000000..2e73dd2 --- /dev/null +++ b/arch/x86_64/arch/vga/text.hpp @@ -0,0 +1,10 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_HPP +#define TEACHOS_X86_64_VGA_TEXT_HPP + +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export + +#endif // TEACHOS_ARCH_X86_64_VIDEO_VGA_TEXT_HPP \ No newline at end of file diff --git a/arch/x86_64/arch/vga/text/attribute.hpp b/arch/x86_64/arch/vga/text/attribute.hpp new file mode 100644 index 0000000..6395aed --- /dev/null +++ b/arch/x86_64/arch/vga/text/attribute.hpp @@ -0,0 +1,30 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_ATTRIBUTE_HPP +#define TEACHOS_X86_64_VGA_TEXT_ATTRIBUTE_HPP + +// IWYU pragma: private, include + +#include +#include + +namespace arch::vga::text +{ + //! The VGA text mode attribute. + //! + //! @note In the text mode of VGA, every code point being presented is followed by an attribute description. This + //! allows for the modification of how the relevant "cell" is presented. + //! + //! @see text::foreground_flag + //! @see text::background_flag + struct attribute + { + color foreground_color : 3; ///< The foreground color of the cell, e.g. the color of the code point. + enum foreground_flag foreground_flag : 1; ///< The foreground color modification flag of the cell. + color background_color : 3; ///< The background color of the cell. + enum background_flag background_flag : 1; ///< The background color modification flag of the cell. + }; + + static_assert(sizeof(attribute) == 1, "The VGA text mode attribute must fit inside a single byte."); + +} // namespace arch::vga::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/vga/text/buffer.cpp b/arch/x86_64/arch/vga/text/buffer.cpp new file mode 100644 index 0000000..498b9a3 --- /dev/null +++ b/arch/x86_64/arch/vga/text/buffer.cpp @@ -0,0 +1,101 @@ +#include + +#include + +#include +#include +#include +#include +#include +#include + +namespace arch::vga::text +{ + buffer::buffer(std::size_t width, std::size_t height, cell * start, std::size_t position) + : m_width{width} + , m_height{height} + , m_buffer{start, m_width * m_height} + , m_position{position} + {} + + auto buffer::clear() -> void + { + m_position = 0; + std::ranges::fill(m_buffer, std::pair{'\0', static_cast(0x00)}); + } + + auto buffer::write(std::string_view code_points, attribute attribute) -> void + { + std::ranges::for_each(code_points, [&](auto code_point) -> void { write(code_point, attribute); }); + } + + auto buffer::write(char code_point, attribute attribute) -> void + { + if (m_position + 1 > m_height * m_width) + { + scroll(); + } + + if (!handle_special_code_point(code_point, attribute)) + { + do_write(code_point, attribute); + } + }; + + auto buffer::newline() -> void + { + auto free_glyphs_in_line = m_width - column(); + m_position += free_glyphs_in_line; + } + + auto buffer::scroll(std::size_t nof_lines) -> void + { + auto scroll_count = std::min(nof_lines, m_height); + + auto scroll_start = m_buffer.begin() + (scroll_count * m_width); + std::ranges::move(scroll_start, m_buffer.end(), m_buffer.begin()); + + auto clear_start = m_buffer.begin() + (m_height - scroll_count) * m_width; + std::ranges::fill(clear_start, m_buffer.end(), cell{}); + + m_position = (line() - scroll_count) * m_width; + } + + auto buffer::column() const noexcept -> std::ptrdiff_t + { + return m_position % m_width; + } + + auto buffer::line() const noexcept -> std::ptrdiff_t + { + return m_position / m_width; + } + auto buffer::handle_special_code_point(char code_point, attribute attribute) -> bool + { + switch (code_point) + { + case '\n': + newline(); + return true; + case '\r': + m_position -= column(); + return true; + case '\t': + do_write(" ", attribute); + return true; + default: + return false; + } + } + + auto buffer::do_write(std::string_view code_points, attribute attribute) -> void + { + std::ranges::for_each(code_points, [&](auto code_point) -> void { do_write(code_point, attribute); }); + } + + auto buffer::do_write(char code_point, attribute attribute) -> void + { + m_buffer[m_position++] = std::pair{code_point, std::bit_cast(attribute)}; + } + +} // namespace arch::vga::text diff --git a/arch/x86_64/arch/vga/text/buffer.hpp b/arch/x86_64/arch/vga/text/buffer.hpp new file mode 100644 index 0000000..8eb6645 --- /dev/null +++ b/arch/x86_64/arch/vga/text/buffer.hpp @@ -0,0 +1,101 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_BUFFER_HPP +#define TEACHOS_X86_64_VGA_TEXT_BUFFER_HPP + +// IWYU pragma: private, include + +#include + +#include +#include +#include +#include + +namespace arch::vga::text +{ + //! A VGA text buffer. + //! + //! VGA text mode presents a linear buffer of so-called cells. Each cell consists of a single code point and a + //! rendering attribute. The codepoint determines the character being rendered in a specific cell, while the attribute + //! determines the visual style of that cell. + //! + //! @see text::attribute + struct buffer + { + using cell = std::pair; + + //! Create a new buffer. + //! + //! @param width The width of the buffer + //! @param height The height of the buffer + //! @param start A pointer to the first byte of the buffer. + //! @param position The starting position for the first write to the buffer + buffer(std::size_t width, std::size_t height, cell * start, std::size_t position = 0); + + //! Clear the buffer. + //! + //! Clearing the buffer ensures it is filled with zeroes, effectively erasing all data and resetting the output + //! position to the start of the buffer. + auto clear() -> void; + + //! Write a string of formatted code points to the buffer. + //! + //! @param code_points A string of (8-bit) code points to write to the buffer. + //! @param attribute The formatting to apply to the written sequence of code points. + auto write(std::string_view code_points, attribute attribute) -> void; + + //! Write a single, formatted code point to the buffer. + //! + //! @param code_point A single (8-bit) code point + //! @param attribute The formatting to apply to the code point. + auto write(char code_point, attribute attribute) -> void; + + //! Move the output position to a new line and scroll the buffer if necessary. + auto newline() -> void; + + //! Scroll the buffer contents. + //! + //! @param nof_lines The number of lines to scroll up. + auto scroll(std::size_t nof_lines = 1) -> void; + + private: + //! Get column number of the current cell. + [[nodiscard]] auto column() const noexcept -> std::ptrdiff_t; + + //! Get the line number of the current cell. + [[nodiscard]] auto line() const noexcept -> std::ptrdiff_t; + + //! Process the semantics of special code points, for example newlines and carriage returns. + //! + //! @param code_point The code point to process. + //! @param attribute The attribute to use when writing to the text buffer. + //! @return @p true iff. the code point was handled, @p false otherwise. + auto handle_special_code_point(char code_point, attribute attribute) -> bool; + + //! Perform the actual output to the buffer. + //! + //! @param code_points The code points to output.. + //! @param attribute The attribute to use when writing to the text buffer. + auto do_write(std::string_view code_points, attribute attribute) -> void; + + //! Perform the actual output to the buffer. + //! + //! @param code_point The code point to output. + //! @param attribute The attribute to use when writing to the text buffer. + auto do_write(char code_point, attribute attribute) -> void; + + //! The width, in cells, of the buffer. + std::size_t m_width{}; + + //! The height, in cells, of the buffer. + std::size_t m_height{}; + + //! The text mode data buffer. + std::span m_buffer; + + //! The position of the next cell to be written to. + std::size_t m_position{}; + }; + +} // namespace arch::vga::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/vga/text/color.hpp b/arch/x86_64/arch/vga/text/color.hpp new file mode 100644 index 0000000..e0ad6df --- /dev/null +++ b/arch/x86_64/arch/vga/text/color.hpp @@ -0,0 +1,35 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_COLOR_HPP +#define TEACHOS_X86_64_VGA_TEXT_COLOR_HPP + +// IWYU pragma: private, include + +#include + +namespace arch::vga::text +{ + //! VGA Text Mode standard colors. + //! + //! Every color may be used as a foreground and/or background color, in any combination. + enum struct color : std::uint8_t + { + //! Equivalent to HTML color \#000000. + black, + //! Equivalent to HTML color \#0000AA. + blue, + //! Equivalent to HTML color \#00AA00. + green, + //! Equivalent to HTML color \#00AAAA. + cyan, + //! Equivalent to HTML color \#AA0000. + red, + //! Equivalent to HTML color \#AA00AA. + purple, + //! Equivalent to HTML color \#AA5500. + brown, + //! Equivalent to HTML color \#AAAAAA. + gray, + }; + +} // namespace arch::vga::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/vga/text/common_attributes.hpp b/arch/x86_64/arch/vga/text/common_attributes.hpp new file mode 100644 index 0000000..3d8929f --- /dev/null +++ b/arch/x86_64/arch/vga/text/common_attributes.hpp @@ -0,0 +1,37 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_COMMON_ATTRIBUTES_HPP +#define TEACHOS_X86_64_VGA_TEXT_COMMON_ATTRIBUTES_HPP + +// IWYU pragma: private, include + +#include +#include +#include + +namespace arch::vga::text +{ + //! Make the affected cell display with a gray foreground and black background. + [[maybe_unused]] constexpr auto gray_on_black = attribute{.foreground_color = color::gray, + .foreground_flag = foreground_flag::none, + .background_color = color::black, + .background_flag = background_flag::none}; + + //! Make the affected cell display with a green foreground and black background. + [[maybe_unused]] constexpr auto green_on_black = attribute{.foreground_color = color::green, + .foreground_flag = foreground_flag::none, + .background_color = color::black, + .background_flag = background_flag::none}; + + //! Make the affected cell display with a green foreground and black background. + [[maybe_unused]] constexpr auto red_on_black = attribute{.foreground_color = color::red, + .foreground_flag = foreground_flag::none, + .background_color = color::black, + .background_flag = background_flag::none}; + + //! Make the affected cell display with a white (gray + intense) foreground and red background. + [[maybe_unused]] constexpr auto white_on_red = attribute{.foreground_color = color::gray, + .foreground_flag = foreground_flag::intense, + .background_color = color::red, + .background_flag = background_flag::none}; +} // namespace arch::vga::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/vga/text/device.cpp b/arch/x86_64/arch/vga/text/device.cpp new file mode 100644 index 0000000..8468358 --- /dev/null +++ b/arch/x86_64/arch/vga/text/device.cpp @@ -0,0 +1,60 @@ +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace arch::vga::text +{ + namespace + { + constexpr auto default_buffer_address = std::uintptr_t{0xb8000}; + constexpr auto default_buffer_width = 80z; + constexpr auto default_buffer_height = 25z; + + constexpr auto bit_cursor_enabled = 5U; + } // namespace + + device::device() + : m_buffer{ + default_buffer_width, default_buffer_height, + std::bit_cast(default_buffer_address + std::bit_cast(&boot::TEACHOS_VMA)), + kapi::boot::bootstrap_information.vga_buffer_index} + { + clear(); + } + + auto device::clear() -> void + { + m_buffer.clear(); + } + + auto device::cursor(bool enabled) -> void + { + auto cursor_disable_byte = std::byte{!enabled} << bit_cursor_enabled; + + crtc::address::write(crtc::registers::cursor_start); + crtc::data::write(crtc::data::read() | cursor_disable_byte); + } + + auto device::write(kapi::cio::output_stream stream, std::string_view text) -> void + { + auto attributes = [&] -> attribute { + switch (stream) + { + case kapi::cio::output_stream::stderr: + return red_on_black; + default: + return green_on_black; + } + }(); + m_buffer.write(text, attributes); + } + +} // namespace arch::vga::text diff --git a/arch/x86_64/arch/vga/text/device.hpp b/arch/x86_64/arch/vga/text/device.hpp new file mode 100644 index 0000000..0a0e017 --- /dev/null +++ b/arch/x86_64/arch/vga/text/device.hpp @@ -0,0 +1,45 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_DEVICE_HPP +#define TEACHOS_X86_64_VGA_TEXT_DEVICE_HPP + +// IWYU pragma: private, include + +#include + +#include + +#include + +namespace arch::vga::text +{ + //! A VGA Text Mode device. + //! + //! VGA text mode presents a linear buffer of so-called cells. Each cell consists of a single code point and a + //! rendering attribute. The codepoint determines the character being rendered in a specific cell, while the attribute + //! determines the visual style of that cell. + //! + //! @see text::attribute + struct device final : kapi::cio::output_device + { + device(); + + //! Clear the screen. + //! + //! Clearing the screen ensures the text mode buffer is filled with zeroes, effectively erasing all displayed data + //! and resetting the output position to the start of the buffer. + auto clear() -> void; + + //! Enable or disable the VGA text mode cursor. + //! + //! @param enabled Whether to enable the cursor. + auto cursor(bool enabled) -> void; + + //! @copydoc kapi::cio::output_device + auto write(kapi::cio::output_stream stream, std::string_view text) -> void override; + + private: + buffer m_buffer; + }; + +} // namespace arch::vga::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/arch/vga/text/flags.hpp b/arch/x86_64/arch/vga/text/flags.hpp new file mode 100644 index 0000000..7a29e33 --- /dev/null +++ b/arch/x86_64/arch/vga/text/flags.hpp @@ -0,0 +1,38 @@ +#ifndef TEACHOS_X86_64_VGA_TEXT_FLAGS_HPP +#define TEACHOS_X86_64_VGA_TEXT_FLAGS_HPP + +// IWYU pragma: private, include + +namespace arch::vga::text +{ + + //! VGA Text Mode standard background modification flags. + enum struct background_flag : bool + { + //! Do not modify the foreground rendering. + none, + //! Render the background as blinking or intense. + //! + //! Whether this flag is interpreted as 'blink' or 'bright' depends on the currently active configuration of the VGA + //! device. + //! + //! @note The VGA standard does not specify the exact effect of this 'intense', however, most devices render such + //! colors brighter. + blink_or_bright, + }; + + //! VGA Text Mode standard foreground modification flags. + enum struct foreground_flag : bool + { + //! Do not modify the foreground rendering. + none, + //! Render the foreground as intense. + //! + //! @note The VGA standard does not specify the exact effect of this 'intense', however, most devices render such + //! colors brighter. + intense, + }; + +} // namespace arch::vga::text + +#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/boot/boot.hpp b/arch/x86_64/include/arch/boot/boot.hpp deleted file mode 100644 index 7df61c4..0000000 --- a/arch/x86_64/include/arch/boot/boot.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef TEACHOS_X86_64_BOOT_BOOT_HPP -#define TEACHOS_X86_64_BOOT_BOOT_HPP - -#ifdef __ASSEMBLER__ -// clang-format off - -//! The number of huge pages to map during bootstrap. -#define HUGE_PAGES_TO_MAP (16) - -//! The magic value to be set in eax by the multiboot 2 loader. -#define MULTIBOOT2_MAGIC (0x36d76289) - -//! The "A" bit in a GDT entry. -#define GDT_ACCESSED (1 << 40) - -//! The "R/W" bit in a GDT entry -#define GDT_READ_WRITE (1 << 41) - -//! The "E" bit in a GDT entry. -#define GDT_EXECUTABLE (1 << 43) - -//! The "S" bit in a GDT entry. -#define GDT_DESCRIPTOR_TYPE (1 << 44) - -//! The "P" bit in a GDT entry. -#define GDT_PRESENT (1 << 47) - -//! The "L" bit in a GDT entry. -#define GDT_LONG_MODE (1 << 53) - -// clang-format on -#else - -#include // IWYU pragma: export - -#include - -#include - -namespace kapi::boot -{ - - struct information - { - //! A pointer to the loader provided Multiboot2 Information structure. - multiboot2::information_view const * mbi; - - //! The index of the next character to be written in the VGA text buffer after handoff. - std::size_t vga_buffer_index; - }; - -} // namespace kapi::boot - -#endif - -#endif diff --git a/arch/x86_64/include/arch/boot/ld.hpp b/arch/x86_64/include/arch/boot/ld.hpp deleted file mode 100644 index 988723d..0000000 --- a/arch/x86_64/include/arch/boot/ld.hpp +++ /dev/null @@ -1,61 +0,0 @@ -//! @file -//! The interface to linker script defined symbols. -//! -//! This header provides declarations for symbols that are defined in the linker script itself. The symbols declared -//! here provide important information, for example the start and end of the kernel image in virtual and physical -//! memory. -//! -//! Any variables defined in this file must not be read themselves, but rather their address shall be taken, yielding a -//! pointer to the memory location the represent. -//! -//! @note The symbols declared in this header are declared using C-language linkage in order to suppress name mangling. -//! -//! @see arch/x86_64/scripts/kernel.ld - -#ifndef TEACHOS_X86_64_BOOT_LD_HPP -#define TEACHOS_X86_64_BOOT_LD_HPP - -#include - -namespace arch::boot -{ - - extern "C" - { - //! The beginning of the kernel image in physical memory - //! - //! This symbol marks the start of the kernel image in physical memory. - //! - //! @see _end_physical - extern std::byte _start_physical; - - //! The first byte after the loaded kernel image. - //! - //! This symbol marks the end of the kernel image in physical memory. - //! - //! @see _start_physical - extern std::byte _end_physical; - - //! The first byte of the loaded kernel image in the virtual address space. - //! - //! This symbol and marks the start of the kernel image in virtual memory. - //! - //! @see _end_virtual - extern std::byte _start_virtual; - - //! The first byte after the loaded kernel image in the virtual address space. - //! - //! This symbol marks the end of the kernel image in virtual memory. - //! - //! @see _start_virtual - extern std::byte _end_virtual; - - //! The first byte of the kernel's virtual address space. - //! - //! This symbol marks beginning of the kernel virtual address space. - extern std::byte TEACHOS_VMA; - } - -} // namespace arch::boot - -#endif diff --git a/arch/x86_64/include/arch/bus/isa.hpp b/arch/x86_64/include/arch/bus/isa.hpp deleted file mode 100644 index e56f56a..0000000 --- a/arch/x86_64/include/arch/bus/isa.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef TEACHOS_X86_64_BUS_ISA_HPP -#define TEACHOS_X86_64_BUS_ISA_HPP - -#include - -#include - -namespace arch::bus -{ - - struct isa final : public kapi::devices::bus - { - isa(std::size_t major); - }; - -} // namespace arch::bus - -#endif // TEACHOS_X86_64_BUS_ISA_HPP diff --git a/arch/x86_64/include/arch/cpu/control_register.hpp b/arch/x86_64/include/arch/cpu/control_register.hpp deleted file mode 100644 index 9cedc35..0000000 --- a/arch/x86_64/include/arch/cpu/control_register.hpp +++ /dev/null @@ -1,249 +0,0 @@ -#ifndef TEACHOS_X86_64_CPU_CONTROL_REGISTERS_HPP -#define TEACHOS_X86_64_CPU_CONTROL_REGISTERS_HPP - -// IWYU pragma: private, include - -#include - -#include - -#include -#include -#include -#include - -namespace arch::cpu -{ - namespace impl - { - //! The assembler templates used to access (r/w) CR0; - constexpr auto static cr0_asm = std::pair{"mov %%cr0, %0", "mov %0, %%cr0"}; - - //! The assembler templates used to access (r/w) CR2; - constexpr auto static cr2_asm = std::pair{"mov %%cr2, %0", "mov %0, %%cr2"}; - - //! The assembler templates used to access (r/w) CR3; - constexpr auto static cr3_asm = std::pair{"mov %%cr3, %0", "mov %0, %%cr3"}; - } // namespace impl - - //! The flags that can be set on CR0 configuration register. - enum struct cr0_flags : uint64_t - { - //! Enable protected mode. - protection_enable = 1uz << 0, - //! Enable wait-monitoring of the coprocessor after task switching. - monitor_coprocessor = 1uz << 1, - //! Emulate floating point coprocessor. - emulation = 1uz << 2, - //! Marks that a task switch has occurred. - task_switched = 1uz << 3, - //! Marks Intel 387 DX math coprocessor as available - extension_type = 1uz << 4, - //! Numeric error handling mode. - numeric_error = 1uz << 5, - //! Disable writing to read-only marked memory. - write_protect = 1uz << 16, - //! Enable Ring-3 alignment checks - alignment_check = 1uz << 18, - //! Disable write through - not_write_through = 1uz << 29, - //! Disable caching of memory accesses - cache_disable = 1uz << 30, - //! Enable paging - paging = 1uz << 31 - }; - - enum struct cr3_flags : std::uint64_t - { - page_level_write_through = 1uz << 0, - page_level_cache_disable = 1uz << 1, - }; -} // namespace arch::cpu - -namespace kstd::ext -{ - template<> - struct is_bitfield_enum : std::true_type - { - }; - - template<> - struct is_bitfield_enum : std::true_type - { - }; -} // namespace kstd::ext - -namespace arch::cpu -{ - //! A mixin for flag-oriented control registers. - //! - //! This mixin provides additional functionality for flag-oriented, or partially flag-oriented, control registers. A - //! control register is flag-oriented, if it comprises a bitfield and zero or more additional non-bitfield parts. - //! - //! @tparam Derived The class deriving from this mixin. - //! @tparam ValueType The value type of the class deriving from this mixin. - template - struct control_register_with_flags - { - private: - constexpr control_register_with_flags() noexcept = default; - friend Derived; - }; - - //! @copydoc control_register_with_flags - //! - //! @note This specialization provides the implementation for the case in which the value type of the control register - //! is an enum. - template - struct control_register_with_flags>> - { - //! The type of the flags used by this control register - using flags = ValueType; - - //! Set one or more flags in this control register. - //! - //! @warning This function is to be considered **UNSAFE**. Setting flags in a control register may lead to - //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function - //! will perform no additional checks, and may, by extension, crash the system. - //! - //! @param value One or a combination of flags to be set in the control register. - auto static set(ValueType value) -> void - { - auto current = Derived::read(); - current |= value; - Derived::write(current); - } - - //! Clear one or more flags in this control register. - //! - //! @warning This function is to be considered **UNSAFE**. Clearing flags in a control register may lead to - //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function - //! will perform no additional checks, and may, by extension, crash the system. - //! - //! @param value One or a combination of flags to be cleared in the control register. - auto static clear(ValueType value) -> void - { - auto current = Derived::read(); - current &= ~value; - Derived::write(current); - } - }; - - //! A CPU control register. - //! - //! CPU control registers are used to configure builtin features of the CPU, for example memory protection and FPU - //! error reporting. Writing to a control register is inherently dangerous, since a misconfiguration can leave the CPU - //! in an invalid/undefined state. - template - struct control_register : control_register_with_flags, ValueType> - { - //! Read the current value of the control register. - //! - //! @return The currently set value of the control register. - [[nodiscard]] auto static read() -> ValueType - { - auto value = ValueType{}; - asm volatile((AssemblerTemplates->first) : "=r"(value)); - return value; - } - - //! Write a new value to the control register. - //! - //! @warning This function should be considered **UNSAFE**. Writing values to a control register may lead to - //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function - //! will perform no additional checks, and may, by extension, crash the system. - //! - //! @param value The new value to write to the control register. - auto static write(ValueType value) -> void - { - asm volatile((AssemblerTemplates->second) : : "r"(value)); - } - }; - - //! The value type of the CR3 control register. - //! - //! The CR3 control register holds the root configuration of the virtual memory protection mechanism. It contains the - //! page aligned physical address of the root page map, as well as the root paging configuration flags. - struct cr3_value - { - //! Contstruct a 0-value CR3 value. - constexpr cr3_value() = default; - - //! Construct a CR3 value using the given root page map address and flags. - //! - //! @param address The physical address of the root page map - //! @param flags The root configuration flags of the paging system. - constexpr cr3_value(kapi::memory::physical_address address, cr3_flags flags = static_cast(0)) - : m_flags{static_cast(flags)} - , m_address{static_cast(address.raw())} - {} - - //! Extract the physical address of the root page map from this value. - //! - //! @return The physical address of the root page map. - [[nodiscard]] constexpr auto address() const -> kapi::memory::physical_address - { - constexpr auto address_shift = 12uz; - return kapi::memory::physical_address{m_address << address_shift}; - } - - //! Encode the frame aligned physical address of the root page map into this value. - //! - //! @param frame The frame containing a PML4. - constexpr auto frame(kapi::memory::frame frame) -> void - { - m_address = static_cast(frame.number()); - } - - //! Extract the root paging configuration flags from this value. - //! - //! @return The root paging configuration flags. - [[nodiscard]] constexpr auto flags() const -> cr3_flags - { - return static_cast(m_flags); - } - - //! Encode the root paging configuration flags into this value. - //! - //! @param flags The root paging configuration flags. - constexpr auto flags(cr3_flags flags) -> void - { - m_flags = static_cast(flags); - } - - //! Add the given flags to the current set of encoded root configuration flags of this value. - //! - //! @param flags The root configuration flags to add. - //! @return A reference to this value. - constexpr auto operator|=(cr3_flags flags) -> cr3_value & - { - m_flags |= static_cast(flags); - return *this; - } - - //! Mask the root configuration flags of this value. - //! - //! @param mask The mask to apply to the root configuration flags. - //! @return A reference to this value. - constexpr auto operator&=(cr3_flags mask) -> cr3_value & - { - m_flags &= static_cast(mask); - return *this; - } - - private: - //! Reserved bits. - std::uint64_t : 3; - //! The root paging configuration flags. - std::uint64_t m_flags : 2 {}; - //! Reserved bits. - std::uint64_t : 7; - //! The page aligned physical address of the root page map. - std::uint64_t m_address : 52 {}; - }; - - static_assert(sizeof(cr3_value) == sizeof(std::uint64_t)); - -} // namespace arch::cpu - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/cpu/global_descriptor_table.hpp b/arch/x86_64/include/arch/cpu/global_descriptor_table.hpp deleted file mode 100644 index b17c509..0000000 --- a/arch/x86_64/include/arch/cpu/global_descriptor_table.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef TEACHOS_X86_64_GLOBAL_DESCRIPTOR_TABLE_HPP -#define TEACHOS_X86_64_GLOBAL_DESCRIPTOR_TABLE_HPP - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace arch::cpu -{ - - template - struct global_descriptor_table; - - struct [[gnu::packed]] global_descriptor_table_pointer - { - template - global_descriptor_table_pointer(global_descriptor_table const & gdt) - : size{GdtSize * sizeof(segment_descriptor) - 1} - , address{kapi::memory::physical_address{std::bit_cast(&gdt)}.raw()} - {} - - auto load() -> void - { - asm volatile("lgdt %0" : : "m"(*this)); - } - - std::uint16_t size{}; - std::uint64_t address{}; - }; - - static_assert(sizeof(global_descriptor_table_pointer) == sizeof(std::uint16_t) + sizeof(std::uint64_t)); - - template - struct global_descriptor_table - { - template... SegmentDescriptors> - constexpr global_descriptor_table(SegmentDescriptors const &... descriptors) - : m_descriptors{} - { - auto descriptor_data = std::array{ - std::pair{std::bit_cast(&descriptors), sizeof(descriptors)} - ... - }; - auto written_size = 0uz; - std::ranges::for_each(descriptor_data, [&written_size, this](auto entry) { - std::ranges::copy(entry.first, entry.first + entry.second, m_descriptors.begin() + written_size); - written_size += entry.second; - }); - } - - auto load(std::size_t code_segment_index, std::size_t data_segment_index) const -> void - { - auto pointer = global_descriptor_table_pointer{*this}; - pointer.load(); - - asm volatile("push %0\n" - "lea 1f(%%rip), %%rax\n" - "push %%rax\n" - "lretq\n" - "1:\n" - "mov %1, %%rax\n" - "mov %%rax, %%ss\n" - "mov %%rax, %%ds\n" - "mov %%rax, %%es\n" - "mov %%rax, %%fs\n" - "mov %%rax, %%gs\n" - : - : "X"(code_segment_index * sizeof(segment_descriptor)), - "X"(data_segment_index * sizeof(segment_descriptor)) - : "rax"); - } - - private: - std::array m_descriptors; - }; - - template... SegmentDescriptors> - global_descriptor_table(SegmentDescriptors const &... descriptors) - -> global_descriptor_table<(sizeof(SegmentDescriptors) + ...)>; - -} // namespace arch::cpu - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/cpu/initialization.hpp b/arch/x86_64/include/arch/cpu/initialization.hpp deleted file mode 100644 index 564c544..0000000 --- a/arch/x86_64/include/arch/cpu/initialization.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CPU_INITIALIZATION_HPP -#define TEACHOS_ARCH_X86_64_CPU_INITIALIZATION_HPP - -namespace arch::cpu -{ - auto initialize_descriptors() -> void; - - auto initialize_legacy_interrupts() -> void; - -} // namespace arch::cpu - -#endif diff --git a/arch/x86_64/include/arch/cpu/interrupts.hpp b/arch/x86_64/include/arch/cpu/interrupts.hpp deleted file mode 100644 index 6162f56..0000000 --- a/arch/x86_64/include/arch/cpu/interrupts.hpp +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef TEACHOS_X86_64_CPU_INTERRUPTS_HPP -#define TEACHOS_X86_64_CPU_INTERRUPTS_HPP - -#include - -#include - -#include -#include -#include - -namespace arch::cpu -{ - - //! The types of supported gates. - enum struct gate_type : std::uint8_t - { - //! A gate entered through the @p INT instruction. - interrupt_gate = 0b1110, - //! A gate entered via an exception. - trap_gate = 0b1111, - }; - - struct alignas(std::uint64_t) gate_descriptor - { - //! The lowest 16 bits of the address of this gate's handler function. - std::uint16_t offset_low : 16; - //! The code segment used when handling the respective interrupt. - segment_selector m_code_segment; - //! The index into the Interrupt Stack Table naming the stack to use. - std::uint8_t interrupt_stack_table_selector : 3; - //! Reserved - std::uint8_t : 5; - //! The type of this gate. - enum gate_type gate_type : 4; - //! Reserved - std::uint8_t : 1; - //! The privilege level required to enter through this gate. - std::uint8_t descriptor_privilege_level : 2; - //! Whether this gate is present or not. - std::uint8_t present : 1; - //! The middle 16 bits of the address of this gate's handler function. - std::uint16_t offset_middle : 16; - //! The highest 32 bits of the address of this gate's handler function. - std::uint32_t offset_high : 32; - //! Reserved - std::uint32_t : 32; - }; - - static_assert(sizeof(gate_descriptor) == 2 * sizeof(std::uint64_t)); - static_assert(std::is_aggregate_v); - - //! The stack frame as established by the low-level assembly interrupt stubs. - //! - //! @note The layout of this struct is reverse than what would reasonably be expected. The reason for this reversal is - //! that fact that it represents a stack frame. Stack frames on x86_64 grow towards lower addresses, meaning the first - //! item on the stack is the last item in C++ memory layout order. - struct interrupt_frame - { - struct - { - std::uint64_t r15{}; - std::uint64_t r14{}; - std::uint64_t r13{}; - std::uint64_t r12{}; - std::uint64_t r11{}; - std::uint64_t r10{}; - std::uint64_t r9{}; - std::uint64_t r8{}; - std::uint64_t rdi{}; - std::uint64_t rsi{}; - std::uint64_t rbp{}; - std::uint64_t rdx{}; - std::uint64_t rcx{}; - std::uint64_t rbx{}; - std::uint64_t rax{}; - } handler_saved; - - struct - { - std::uint64_t number{}; - std::uint64_t error_code{}; - } interrupt; - - struct - { - kapi::memory::linear_address rip{}; - std::uint64_t cs{}; - std::uint64_t rflags{}; - kapi::memory::linear_address rsp{}; - std::uint64_t ss{}; - } cpu_saved; - }; - - struct interrupt_descriptor_table - { - interrupt_descriptor_table() noexcept; - - auto load() const -> void; - - private: - std::array m_descriptors{}; - }; - - struct [[gnu::packed]] interrupt_descriptor_table_register - { - std::uint16_t limit; - gate_descriptor const * base; - - auto load() const -> void; - auto static read() -> interrupt_descriptor_table_register; - }; - -} // namespace arch::cpu - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/cpu/legacy_pic.hpp b/arch/x86_64/include/arch/cpu/legacy_pic.hpp deleted file mode 100644 index 56ca9c4..0000000 --- a/arch/x86_64/include/arch/cpu/legacy_pic.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef TEACHOS_X86_64_CPU_LEGACY_PIC_HPP -#define TEACHOS_X86_64_CPU_LEGACY_PIC_HPP - -#include - -#include - -namespace arch::cpu -{ - using pic_master_control_port = io::port<0x20, std::uint8_t, io::port_read, io::port_write>; - using pic_master_data_port = io::port<0x21, std::uint8_t, io::port_read, io::port_write>; - using pic_slave_control_port = io::port<0xa0, std::uint8_t, io::port_read, io::port_write>; - using pic_slave_data_port = io::port<0xa1, std::uint8_t, io::port_read, io::port_write>; - - constexpr auto pic_end_of_interrupt = std::uint8_t{0x20}; - -} // namespace arch::cpu - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/cpu/model_specific_register.hpp b/arch/x86_64/include/arch/cpu/model_specific_register.hpp deleted file mode 100644 index bd4aff9..0000000 --- a/arch/x86_64/include/arch/cpu/model_specific_register.hpp +++ /dev/null @@ -1,151 +0,0 @@ -#ifndef TEACHOS_X86_64_CPU_MODEL_SPECIFIC_REGISTER_HPP -#define TEACHOS_X86_64_CPU_MODEL_SPECIFIC_REGISTER_HPP - -// IWYU pragma: private, include - -#include - -#include -#include -#include - -namespace arch::cpu -{ - - //! The flags of the IA32_EFER (Extended Features Enable Register) MSR. - enum struct ia32_efer_flags : std::uint64_t - { - //! Enable the syscall and sysret instructions. - syscall_enable = 1uz << 0, - //! Enable IA-32e mode operation. - ia32e_mode_enable = 1uz << 8, - //! Indicates IA-32e mode is active (read-only) - ia32e_mode_active = 1uz << 10, - //! Enable the use of the NX page table bit. - execute_disable_bit_enable = 1uz << 11, - }; - -} // namespace arch::cpu - -namespace kstd::ext -{ - - template<> - struct is_bitfield_enum : std::true_type - { - }; - -} // namespace kstd::ext - -namespace arch::cpu -{ - //! The MSR number for the IA32_EFER MSR - constexpr auto ia32_efer_number = 0xC000'0080u; - - //! A mixin for flag-oriented model specific registers. - //! - //! This mixin provides additional functionality for a flag-oriented model specific register. A models specific - //! register is flag-oriented, if it comprises a single field of bitfield. - //! - //! @tparam Derived The class deriving from this mixin. - //! @tparam ValueType The value type of the class deriving from this mixin. - template - struct model_specific_register_with_flags - { - private: - constexpr model_specific_register_with_flags() noexcept = default; - friend Derived; - }; - - //! @copydoc model_specific_register_with_flags - //! - //! @note This specialization provides the implementation for the case in which the value type of the model specific - //! register is a bitfield enum. - template - struct model_specific_register_with_flags>> - { - //! The of the flags used by this model specific register. - using flags = ValueType; - - //! Set one or more flags in this model specific register. - //! - //! @warning This function is to be considered **UNSAFE**. Setting flags in a model specific register may lead to - //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function - //! will perform no additional checks, and may, by extension, crash the system. - //! - //! @param flag One or a combination of flags to be set in the model specific register. - auto static set(flags flag) -> void - { - auto current = Derived::read(); - current |= flag; - Derived::write(current); - } - - //! Clear one or more flags in this model specific register. - //! - //! @warning This function is to be considered **UNSAFE**. Clearing flags in a model specific register may lead to - //! unexpected CPU behavior if the prerequisites imposed by the CPU specification are not fulfilled. This function - //! will perform no additional checks, and may, by extension, crash the system. - //! - //! @param flag One or a combination of flags to be cleared in the model specific register. - auto static clear(flags flag) -> void - { - auto current = Derived::read(); - current &= ~flag; - Derived::write(current); - } - - //! Test one or more flags in this model specific register - //! - //! @param flag One or a combination of flags to test for. - auto test(flags flag) -> flags - { - return Derived::read() & flag; - } - }; - - //! A model specific register (MSR) - //! - //! Model specific register are used to configure CPU features that a not necessarily present on all CPUs generations. - //! In the past, some MSRs have been defined to be architectural, meaning all CPUs of a given architecture (x86-64 in - //! this case) support them. Writing to a MSR is inherently dangerous, since a misconfiguration cal leave the CPU in - //! an invalid/undefined state. - //! - //! @tparam Number The register number of this MSR - //! @tparam ValueType The value type of this MSR - template - struct model_specific_register - : model_specific_register_with_flags, ValueType> - { - //! A raw MSR value, comprising two halfs. - //! - //! MSRs have been 64-bit in size even in the 32-bit intel architecture, and are thus written in two halfs. - struct raw_value - { - std::uint32_t low_half; //!< The lower half of the register value - std::uint32_t high_half; //!< The upper half of the register value - }; - - //! Read the current value of this MSR. - //! - //! @return The current value of this MSR. - auto static read() -> ValueType - { - auto raw = raw_value{}; - asm volatile("rdmsr" : "=a"(raw.low_half), "=d"(raw.high_half) : "c"(Number)); - return static_cast(std::bit_cast(raw)); - } - - //! Write a new value to this MSR. - //! - //! @param value The new value for this MSR. - auto static write(ValueType value) -> void - { - auto raw = std::bit_cast(static_cast(value)); - asm volatile("wrmsr" : : "a"(raw.low_half), "d"(raw.high_half), "c"(Number)); - } - }; - -} // namespace arch::cpu - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/cpu/registers.hpp b/arch/x86_64/include/arch/cpu/registers.hpp deleted file mode 100644 index 58633f6..0000000 --- a/arch/x86_64/include/arch/cpu/registers.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef TEACHOS_X86_64_CPU_REGISTERS_HPP -#define TEACHOS_X86_64_CPU_REGISTERS_HPP - -#include // IWYU pragma: export -#include // IWYU pragma: export - -#include - -namespace arch::cpu -{ - - //! Configuration Register 0. - //! - //! This configuration register holds various control flags to configure the configure the basic operation of the CPU. - using cr0 = control_register; - - //! Configuration Register 2. - //! - //! This configuration register holds the memory address the access to which has triggered the most recent page fault. - using cr2 = control_register; - - //! Configuration Register 3. - //! - //! This register holds the configuration of the virtual memory protection configuration. - using cr3 = control_register; - - //! The I32_EFER (Extended Feature Enable Register) MSR - using i32_efer = model_specific_register; - -} // namespace arch::cpu - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/cpu/segment_descriptor.hpp b/arch/x86_64/include/arch/cpu/segment_descriptor.hpp deleted file mode 100644 index 9570670..0000000 --- a/arch/x86_64/include/arch/cpu/segment_descriptor.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef TEACHOS_X86_64_SEGMENT_DESCRIPTOR_HPP -#define TEACHOS_X86_64_SEGMENT_DESCRIPTOR_HPP - -#include - -namespace arch::cpu -{ - - //! The type of segment described by a segment_descriptor. - enum struct segment_type : std::uint8_t - { - //! A system (TSS or LDT) segment - system = 0, - //! A code or data segment - code_or_data = 1, - }; - - //! The granularity of a segment described by a segment_descriptor - enum struct segment_granularity : std::uint8_t - { - //! The limit of the segment is defined in bytes. - byte = 0, - //! The limit of the segment is defined in pages (4KiB) - page = 1, - }; - - //! An entry in a segment descriptor table - struct segment_descriptor - { - std::uint64_t limit_low : 16; - std::uint64_t base_low : 24; - bool accessed : 1; - bool read_write : 1; - bool direction_or_conforming : 1; - bool executable : 1; - segment_type type : 1; - std::uint64_t privilege_level : 2; - bool present : 1; - std::uint64_t limit_high : 4; - std::uint64_t : 1; - bool long_mode : 1; - bool protected_mode : 1; - segment_granularity granularity : 1; - std::uint64_t base_high : 8; - }; - - static_assert(sizeof(segment_descriptor) == sizeof(std::uint64_t)); - static_assert(alignof(segment_descriptor) == alignof(std::uint64_t)); - - struct system_segment_descriptor : segment_descriptor - { - std::uint64_t base_extended : 32; - std::uint64_t : 32; - }; - - static_assert(sizeof(system_segment_descriptor) == 2 * sizeof(std::uint64_t)); - static_assert(alignof(system_segment_descriptor) == alignof(segment_descriptor)); - -} // namespace arch::cpu - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/cpu/segment_selector.hpp b/arch/x86_64/include/arch/cpu/segment_selector.hpp deleted file mode 100644 index 1a78c47..0000000 --- a/arch/x86_64/include/arch/cpu/segment_selector.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef TEACHOS_X86_64_SEGMENT_SELECTOR_HPP -#define TEACHOS_X86_64_SEGMENT_SELECTOR_HPP - -#include - -namespace arch::cpu -{ - - struct segment_selector - { - std::uint16_t request_privilege_level : 2; - bool use_local_descriptor_table : 1; - std::uint16_t table_index : 13; - }; - - static_assert(sizeof(segment_selector) == sizeof(std::uint16_t)); - static_assert(alignof(segment_selector) == alignof(std::uint16_t)); - -} // namespace arch::cpu - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/cpu/task_state_segment.hpp b/arch/x86_64/include/arch/cpu/task_state_segment.hpp deleted file mode 100644 index 57729dd..0000000 --- a/arch/x86_64/include/arch/cpu/task_state_segment.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef TEACHOS_X86_64_TASK_STATE_SEGMENT_HPP -#define TEACHOS_X86_64_TASK_STATE_SEGMENT_HPP - -#include - -namespace arch::cpu -{ - - struct [[gnu::packed]] task_state_segment - { - uint32_t : 32; - uint64_t rsp0 = {}; - uint64_t rsp1 = {}; - uint64_t rsp2 = {}; - uint64_t : 64; - uint64_t ist1 = {}; - uint64_t ist2 = {}; - uint64_t ist3 = {}; - uint64_t ist4 = {}; - uint64_t ist5 = {}; - uint64_t ist6 = {}; - uint64_t ist7 = {}; - uint64_t : 64; - uint16_t : 16; - uint16_t io_map_base_address = {}; - }; - -} // namespace arch::cpu - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/debug/qemu_output.hpp b/arch/x86_64/include/arch/debug/qemu_output.hpp deleted file mode 100644 index 5ddd4be..0000000 --- a/arch/x86_64/include/arch/debug/qemu_output.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef TEACHOS_X86_64_DEBUG_QEMU_OUTPUT_HPP -#define TEACHOS_X86_64_DEBUG_QEMU_OUTPUT_HPP - -#include - -#include - -#include - -namespace arch::debug -{ - - //! A QEMU debug console output device. - //! - //! This device implements output to the port 0xE9 debug console present in QEMU in Bochs. It is designed to wrap a - //! different output device (e.g. a VGA text output device) and forwards the written data accordingly. - //! - //! @note Support for the device has to be enabled when the emulator is started. The device will try to detect if the - //! port is available. If the port is detected, any output to the device will be written to port before being - //! forwarded to the lower device. - struct qemu_output : kapi::cio::output_device - { - //! The port to write to. - using port = io::port<0xE9, unsigned char, io::port_write, io::port_read>; - - //! Construct a new debug device wrapper for the given output device. - //! - //! @param lower The device to forward the output to. - explicit qemu_output(output_device & lower); - - //! @copydoc kapi::cio::output_device - auto write(kapi::cio::output_stream stream, std::string_view text) -> void override; - - private: - //! The device to forward the output to. - output_device & m_lower; - //! Whether the device has been detected. - bool m_present; - }; - -} // namespace arch::debug - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/device_io/port_io.hpp b/arch/x86_64/include/arch/device_io/port_io.hpp deleted file mode 100644 index 4c8d66a..0000000 --- a/arch/x86_64/include/arch/device_io/port_io.hpp +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef TEACHOS_X86_64_IO_PORT_IO_HPP -#define TEACHOS_X86_64_IO_PORT_IO_HPP - -#include -#include -#include -#include -#include -#include - -namespace arch::io -{ - - //! The requirements imposed on a type usable for port I/O. - template - concept port_io_type = requires { - requires sizeof(ValueType) == 1 || sizeof(ValueType) == 2 || sizeof(ValueType) == 4; - requires std::default_initializable; - std::bit_cast( - std::conditional_t>{}); - }; - - template - struct port_read - { - //! Read from the I/O port. - //! - //! @return The data read from the I/O port. - auto static read() noexcept - { - auto data = typename Derived::value_type{}; - asm volatile((code[Derived::size / 2]) - : [data] "=m"(data) - : [port] "i"(Derived::address) - : "dx", (Derived::data_register)); - return data; - } - - private: - constexpr port_read() noexcept = default; - friend Derived; - - //! The assembly templates used for reading from an I/O port. - constexpr auto static code = std::array{ - std::string_view{"mov %[port], %%dx\nin %%dx, %%al\nmov %%al, %[data]"}, - std::string_view{"mov %[port], %%dx\nin %%dx, %%ax\nmov %%ax, %[data]"}, - std::string_view{"mov %[port], %%dx\nin %%dx, %%eax\nmov %%eax, %[data]"}, - }; - }; - - template - struct port_write - { - //! Write data to the I/O port. - //! - //! @param data The data to write to the I/O port. - auto static write(std::same_as auto data) noexcept -> void - { - asm volatile((code[Derived::size / 2]) - : - : [port] "i"(Derived::address), [data] "im"(data) - : "dx", (Derived::data_register)); - } - - private: - constexpr port_write() noexcept = default; - friend Derived; - - //! The assembly templates used for writing to an I/O port. - constexpr auto static code = std::array{ - std::string_view{"mov %[port], %%dx\nmov %[data], %%al\nout %%al, %%dx"}, - std::string_view{"mov %[port], %%dx\nmov %[data], %%ax\nout %%ax, %%dx"}, - std::string_view{"mov %[port], %%dx\nmov %[data], %%eax\nout %%eax, %%dx"}, - }; - }; - - //! An I/O port of a given size at a given address. - //! - //! Port I/O leverages a separate address space to communicate with devices via the memory bus, allowing for byte - //! to double-word sized transfers. - //! - //! @tparam Address The address (port number) of the I/O port. - //! @tparam Size The size (in bytes) of the I/O port. - //! @tparam Features The features (readable, writeable) - template typename... Features> - requires(sizeof...(Features) > 0) - struct port : Features>... - { - //! The type of the data of this port. - using value_type = ValueType; - - //! The address of this I/O port. - constexpr auto static address = Address; - - //! The size of this I/O port. - constexpr auto static size = sizeof(value_type); - - //! The register clobbered by the I/O operation. - constexpr auto static data_register = size == 1 ? std::string_view{"al"} - : size == 2 ? std::string_view{"ax"} - : std::string_view{"eax"}; - }; - - auto inline wait() -> void - { - port<0x80, std::uint8_t, port_write>::write(0); - } - -} // namespace arch::io - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/devices/init.hpp b/arch/x86_64/include/arch/devices/init.hpp deleted file mode 100644 index c5fbf37..0000000 --- a/arch/x86_64/include/arch/devices/init.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_DEVICES_INIT_HPP -#define TEACHOS_ARCH_X86_64_DEVICES_INIT_HPP - -namespace arch::devices -{ - - auto init_acpi_devices() -> void; - auto init_legacy_devices() -> void; - -} // namespace arch::devices - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/devices/legacy_pit.hpp b/arch/x86_64/include/arch/devices/legacy_pit.hpp deleted file mode 100644 index 356895c..0000000 --- a/arch/x86_64/include/arch/devices/legacy_pit.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_DEVICES_LEGACY_PIT_HPP -#define TEACHOS_ARCH_X86_64_DEVICES_LEGACY_PIT_HPP - -#include -#include - -#include -#include - -namespace arch::devices -{ - - struct legacy_pit : kapi::devices::device, kapi::interrupts::handler - { - legacy_pit(std::size_t major, std::uint32_t frequency_in_hz); - - auto init() -> bool override; - - auto handle_interrupt(std::uint32_t irq_number) -> kapi::interrupts::status override; - - private: - std::uint32_t m_irq_number{}; - std::uint32_t m_frequency_in_hz{}; - std::uint64_t m_ticks{}; - }; - -} // namespace arch::devices - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/devices/local_apic.hpp b/arch/x86_64/include/arch/devices/local_apic.hpp deleted file mode 100644 index f8f080d..0000000 --- a/arch/x86_64/include/arch/devices/local_apic.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_DEVICES_LOCAL_APIC_HPP -#define TEACHOS_ARCH_X86_64_DEVICES_LOCAL_APIC_HPP - -#include -#include - -#include -#include - -namespace arch::devices -{ - - struct local_apic : kapi::devices::device - { - local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id, kapi::memory::physical_address base, - bool is_bsp); - - auto init() -> bool override; - - private: - enum struct registers : std::ptrdiff_t; - - [[nodiscard]] auto read_register(registers id) const -> std::uint32_t; - auto write_register(registers id, std::uint32_t value) -> void; - - std::uint64_t m_hardware_id{}; - kapi::memory::physical_address m_base{}; - kapi::memory::mmio_region m_mapped_region{}; - bool m_is_bsp{}; - std::uint8_t m_version{}; - std::uint8_t m_highest_lvt_entry_index{}; - bool m_supports_eoi_broadcast_suppression{}; - }; - -} // namespace arch::devices - -#endif diff --git a/arch/x86_64/include/arch/memory/higher_half_mapper.hpp b/arch/x86_64/include/arch/memory/higher_half_mapper.hpp deleted file mode 100644 index 9b02ee6..0000000 --- a/arch/x86_64/include/arch/memory/higher_half_mapper.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef TEACHOS_X86_64_HIGHER_HALF_MAPPER_HPP -#define TEACHOS_X86_64_HIGHER_HALF_MAPPER_HPP - -#include - -#include - -#include - -namespace arch::memory -{ - - //! A simple page mapper making use of a Higher Half Direct Map (HHDM) to access and modify page tables. - struct higher_half_mapper : kapi::memory::page_mapper - { - //! Construct a new mapper for a hierarchy rooted in the given PML. - //! - //! @param root The root of the hierarchy to operate on. - explicit higher_half_mapper(page_table * root); - - //! @copydoc kapi::memory::page_mapper::map - auto map(kapi::memory::page page, kapi::memory::frame frame, flags flags) -> std::byte * override; - - //! @copydoc kapi::memory::page_mapper::unmap - auto unmap(kapi::memory::page page) -> void override; - - //! @copydoc kapi::memory::page_mapper::try_unmap - auto try_unmap(kapi::memory::page page) noexcept -> bool override; - - private: - //! Try to retrieve the the PML1 responsible for mapping this page, creating one if necessary. - //! - //! This function will create a page table hierarchy leading to the target PML1 if it doesn't exist. - //! - //! @param page The page to get the PML1 for. - //! @return The PML1 that manages the given page, nullptr it the system runs out of memory. - auto get_or_create_page_table(kapi::memory::page page) noexcept -> page_table *; - - page_table * m_root; - }; - -} // namespace arch::memory - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/kernel_mapper.hpp b/arch/x86_64/include/arch/memory/kernel_mapper.hpp deleted file mode 100644 index adbf688..0000000 --- a/arch/x86_64/include/arch/memory/kernel_mapper.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef TEACHOS_X86_64_KERNEL_MAPPER_HPP -#define TEACHOS_X86_64_KERNEL_MAPPER_HPP - -#include - -#include -#include - -#include - -#include -#include - -namespace arch::memory -{ - - struct kernel_mapper - { - using section_header_type = elf::section_header; - - explicit kernel_mapper(multiboot2::information_view const * mbi); - - auto remap_kernel(kapi::memory::page_mapper & mapper) -> void; - - private: - auto map_section(section_header_type const & section, std::string_view name, kapi::memory::page_mapper & mapper) - -> void; - - multiboot2::information_view const * m_mbi; - std::uintptr_t m_kernel_load_base; - }; - -} // namespace arch::memory - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/mmu.hpp b/arch/x86_64/include/arch/memory/mmu.hpp deleted file mode 100644 index 64373f4..0000000 --- a/arch/x86_64/include/arch/memory/mmu.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TEACHOS_X86_64_MEMORY_MMU_HPP -#define TEACHOS_X86_64_MEMORY_MMU_HPP - -#include - -namespace arch::memory -{ - /** - * @brief Invalidates any translation lookaside buffer (TLB) entry for the page table the given address is cotained - * in. See https://www.felixcloutier.com/x86/invlpg for more information on the used x86 instruction. - * - * @param address Memory address, which will be used to determine the contained page and flush the TLB entry for - * that page. - */ - auto tlb_flush(kapi::memory::linear_address address) -> void; - - /** - * @brief Invalidates the translation lookaside buffer (TLB) entry for all page tables. - * - * @note Simply reassigns the CR3 register the value of the CR3 register, causing a flush of the TLB buffer, because - * the system has to assume that the location of the level 4 page table moved. - */ - auto tlb_flush_all() -> void; - -} // namespace arch::memory - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/page_table.hpp b/arch/x86_64/include/arch/memory/page_table.hpp deleted file mode 100644 index ce3d3a1..0000000 --- a/arch/x86_64/include/arch/memory/page_table.hpp +++ /dev/null @@ -1,232 +0,0 @@ -#ifndef TEACHOS_X86_64_PAGE_TABLE_HPP -#define TEACHOS_X86_64_PAGE_TABLE_HPP - -#include - -#include -#include - -#include -#include -#include -#include -#include - -namespace arch::memory -{ - - //! A table containing page mapping entries. - //! - //! Page tables exist in a multi-level hierarchy and are used to map pages (virtual memory) onto frames (physical - //! memory). Conceptually, pages represent the data found in a virtual address space, while frames represent their - //! storage. In most cases, only a level 1 page table maps an actual page onto a frame. All other page tables on - //! higher levels do not map payload pages, but rather their subordinate page tables. The only exception to that rule - //! is the use of huge pages. - struct page_table - { - //! An entry in a page table. - //! - //! A page table entry is a combination of a frame number and a set of flags that determine the properties and - //! access rights to a mapped page. Entries at a higher level in the page hierarchy do not map a page directly, - //! unless that page is marked as huge on the relevant level, but rather map the next lower page table. - struct entry - { - //! Flags marking the state and configuration of an entry. - //! - //! An entry in a page table may have any combination of these flags active at the same time. The final flags of a - //! page are determined as the strictest combination (logical AND) of all flags along the hierarchy. - //! - //! @note This is a bitfield enum as defined by kstd::ext::bitfield_enum. - enum struct flags : std::uint64_t - { - empty = 0, - present = 1uz << 0, //!< The page is mapped. - writable = 1uz << 1, //!< The page is writable. - user_accessible = 1uz << 2, //!< The page is accessible in user mode. - write_through = 1uz << 3, //!< Any writes to the page must immediately hit memory. - disable_cache = 1uz << 4, //!< Any writes to the page must never be cached. - accessed = 1uz << 5, //!< The page was accessed. - dirty = 1uz << 6, //!< The page was written to. - huge_page = 1uz << 7, //!< The page is huge. - global = 1uz << 8, //!< The TLB entry for this page must not be flushed on context switches. - no_execute = 1uz << 63, //!< The data in this page must not be executed. - }; - - //! Construct an empty entry. - entry() = default; - - //! Clear this entry, ensuring all information is set to zero. - //! - //! This effectively marks the page represented by this entry as not present. In addition, it also removes any - //! information about the frame referenced by this entry. - auto clear() noexcept -> void; - - //! Check if the page represented by this entry is present. - //! - //! @note This function does not attempt to walk the page table hierarchy, but only performs a local check. This - //! means, that if the page is not mapped at a lower level, this will not be detected. - //! - //! @return @p true iff. the page is present at this level, @p false otherwise. - [[nodiscard]] auto present() const noexcept -> bool; - - //! Check if the page represented by this entry is a huge page. - //! - //! @note The effective size of the page depends on the level of the page table containing this entry. - //! - //! @return @p true iff. the page is marked as being huge, @p false otherwise. - [[nodiscard]] auto huge() const noexcept -> bool; - - //! Get all flags present in this entry. - //! - //! @return The flags that are currently set on this entry. - [[nodiscard]] auto all_flags() const noexcept -> flags; - - //! Set all flags of this entry. - //! - //! @param flags The flags to apply to this entry. - auto all_flags(flags flags) noexcept -> void; - - //! Add the given flags to the flags of this entry. - //! - //! @param rhs The flags to add to this entry's flags. - //! @return A reference to this entry. - auto operator|=(flags rhs) noexcept -> entry &; - - //! Get the frame number associated with this entry, if the referenced page is present. - //! - //! @return an engaged std::optional iff. this entry maps a page, std::nullopt otherwise. - [[nodiscard]] auto frame() const noexcept -> std::optional; - - //! Map this entry. - //! - //! @param frame The frame to map in this entry. - //! @param flags The flags to apply to this entry. - auto frame(kapi::memory::frame frame, flags flags) noexcept -> void; - - private: - //! A mask to retrieve, or exclude, the frame number from the raw entry. - //! - //! Page table entries in x86_64 are a compacted combination of the relevant flags and the frame number. This mask - //! represents the bits that make up the frame number in an entry. - constexpr auto static frame_number_mask{0x000f'ffff'ffff'f000uz}; - - //! The raw entry bytes. - //! - //! @see entry::frame_number_mask - std::uint64_t m_raw{}; - }; - - //! The maximum number of entries in this table. - constexpr auto static entry_count{kapi::memory::page::size / kstd::units::bytes{sizeof(entry)}}; - - //! Get the entry at the given index. - //! - //! @warning This function will panic if the entry index is out of bounds. - //! - //! @param index The index of the desired entry. - //! @return A reference to the entry at the given index. - [[nodiscard]] auto operator[](std::size_t index) -> entry &; - - //! @copydoc page_table::operator[] - [[nodiscard]] auto operator[](std::size_t index) const -> entry const &; - - //! Clear the entire page table. - //! - //! This function effectively marks the page table as not mapping any pages. - auto clear() noexcept -> void; - - //! Check if the page table is empty. - //! - //! @return @p true iff. this page table has no entries marked present, @p false otherwise. - [[nodiscard]] auto empty() const noexcept -> bool; - - private: - std::array m_entries{}; - }; - -} // namespace arch::memory - -namespace kstd::ext -{ - template<> - struct is_bitfield_enum : std::true_type - { - }; -} // namespace kstd::ext - -namespace arch::memory -{ - - constexpr auto to_mapper_flags(page_table::entry::flags flags) -> kapi::memory::page_mapper::flags - { - using table_flags = page_table::entry::flags; - using mapper_flags = kapi::memory::page_mapper::flags; - - auto result = mapper_flags{}; - - if ((flags & table_flags::no_execute) == table_flags::empty) - { - result |= mapper_flags::executable; - } - - if ((flags & table_flags::writable) != table_flags::empty) - { - result |= mapper_flags::writable; - } - - if ((flags & (table_flags::disable_cache | table_flags::write_through)) != table_flags::empty) - { - result |= mapper_flags::uncached; - } - - if ((flags & table_flags::user_accessible) == table_flags::empty) - { - result |= mapper_flags::supervisor_only; - } - - if ((flags & table_flags::global) != table_flags::empty) - { - result |= mapper_flags::global; - } - - return result; - } - - constexpr auto to_table_flags(kapi::memory::page_mapper::flags flags) -> page_table::entry::flags - { - using table_flags = page_table::entry::flags; - using mapper_flags = kapi::memory::page_mapper::flags; - - auto result = table_flags{}; - - if ((flags & mapper_flags::executable) == mapper_flags::empty) - { - result |= table_flags::no_execute; - } - - if ((flags & mapper_flags::writable) != mapper_flags::empty) - { - result |= table_flags::writable; - } - - if ((flags & mapper_flags::uncached) != mapper_flags::empty) - { - result |= (table_flags::disable_cache | table_flags::write_through); - } - - if ((flags & mapper_flags::supervisor_only) == mapper_flags::empty) - { - result |= table_flags::user_accessible; - } - - if ((flags & mapper_flags::global) != mapper_flags::empty) - { - result |= table_flags::global; - } - - return result; - } - -} // namespace arch::memory - -#endif diff --git a/arch/x86_64/include/arch/memory/page_utilities.hpp b/arch/x86_64/include/arch/memory/page_utilities.hpp deleted file mode 100644 index 068e824..0000000 --- a/arch/x86_64/include/arch/memory/page_utilities.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef TEACHOS_X86_64_PAGE_UTILITIES_HPP -#define TEACHOS_X86_64_PAGE_UTILITIES_HPP - -#include - -#include - -namespace arch::memory -{ - - constexpr auto inline pml_index(std::size_t index, kapi::memory::page page) noexcept -> std::size_t - { - constexpr auto bits_per_level = 9; - auto shift_width = (index - 1) * bits_per_level; - constexpr auto index_mask = 0x1ffuz; - return page.number() >> shift_width & index_mask; - } - - template - [[nodiscard]] constexpr auto to_higher_half_pointer(kapi::memory::physical_address address) -> ValueType * - { - using namespace kapi::memory; - auto const higher_half_address = higher_half_direct_map_base + address.raw(); - return static_cast(higher_half_address); - } - -} // namespace arch::memory - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/memory/region_allocator.hpp b/arch/x86_64/include/arch/memory/region_allocator.hpp deleted file mode 100644 index 5d9da2e..0000000 --- a/arch/x86_64/include/arch/memory/region_allocator.hpp +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef TEACHOS_X86_64_MEMORY_REGION_ALLOCATOR_HPP -#define TEACHOS_X86_64_MEMORY_REGION_ALLOCATOR_HPP - -#include -#include -#include - -#include - -#include -#include -#include - -namespace arch::memory -{ - //! A simple, memory-region based frame allocator. - //! - //! This frame allocator linearly allocates frames that are in available memory regions. It automatically skips any - //! frames occupied by the kernel image or any bootloader provided data. - //! - //! @note This allocator will never release frames. - struct region_allocator final : kapi::memory::frame_allocator - { - struct memory_information - { - //! The memory range occupied by the loaded kernel image. - //! - //! This includes all sections that are marked as occupying space in the kernel executable. The internal structure - //! of this area is more described in a more fine-grained manner by the ELF symbol information provided in the - //! Multiboot2 information by the loader. - std::pair image_range; - - //! The memory range occupied by the loader supplied Multiboot2 information structure. - //! - //! In general, this information is allocated somewhere in the range of the loaded image, but the loader protocol - //! does not guarantee this. It is thus imperative to be able to handle the cases where the loader chooses to - //! allocate the information structure outside of the image range. - std::pair mbi_range; - - //! The loader supplied map of memory regions. - //! - //! These include available, unavailable, and reclaimable regions. In general, only frames that are located in - //! non-reserved (as in available) regions should be allocated for page storage. - multiboot2::memory_map memory_map; - - //! The loader supplied Multiboot2 information structure. - //! - //! This is used to query boot module ranges so these frames can be excluded from early allocations. - multiboot2::information_view const * mbi; - }; - - using region = multiboot2::memory_map::region; - - //! Construct a new allocator using the provided memory information - //! - //! @param information The description of the detected memory regions as well as regions that are already occupied. - explicit region_allocator(memory_information const & information); - - //! @copydoc kapi::memory::frame_allocator::allocate_many - //! - //! @note As long as free frames are available, successive calls to this implementation are guaranteed to yield - //! frames in ascending order. - auto allocate_many(std::size_t count = 1) noexcept - -> std::optional> override; - - //! @copydoc kapi::memory::frame_allocator::mark_used - auto mark_used(kapi::memory::frame frame) -> void override; - - //! @copydoc kapi::memory::frame_allocator::release_many - //! - //! @note This implementation will never actually release any frames. - auto release_many(std::pair frame_set) -> void override; - - auto next_free_frame() noexcept -> std::optional; - - private: - //! Find the next memory area and write it into current_area. - auto choose_next_region() -> void; - auto find_next_frame() -> std::optional; - - kapi::memory::frame m_next_frame; //!< The next available frame. - std::optional m_current_region; //!< The memory region currently used for allocation - multiboot2::memory_map m_memory_map; //!< The boot loader supplied memory map. - kapi::memory::frame m_kernel_start; //!< The start of the kernel image in physical memory. - kapi::memory::frame m_kernel_end; //!< The end of the kernel image in physical memory. - kapi::memory::frame m_multiboot_start; //!< The start of the Multiboot2 information in physical memory. - kapi::memory::frame m_multiboot_end; //!< The end of the Multiboot2 information in physical memory. - multiboot2::information_view const * m_multiboot_information; //!< Source of Multiboot2 module ranges. - }; - -} // namespace arch::memory - -#endif diff --git a/arch/x86_64/include/arch/vga/crtc.hpp b/arch/x86_64/include/arch/vga/crtc.hpp deleted file mode 100644 index a8bec93..0000000 --- a/arch/x86_64/include/arch/vga/crtc.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_IO_HPP -#define TEACHOS_X86_64_VGA_IO_HPP - -#include - -#include - -namespace arch::vga::crtc -{ - /** - * @brief The address port of the CRT Controller. - */ - using address = io::port<0x3d4, std::byte, io::port_write>; - - /** - * @brief The data port of the CRT Controller. - */ - using data = io::port<0x3d5, std::byte, io::port_read, io::port_write>; - - namespace registers - { - /** - * @brief The address of the Cursor Start register of the CRTC. - */ - [[maybe_unused]] constexpr auto cursor_start = std::byte{0x0a}; - - /** - * @brief The address of the Cursor End register of the CRTC. - */ - [[maybe_unused]] constexpr auto cursor_end = std::byte{0x0b}; - } // namespace registers - -} // namespace arch::vga::crtc - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text.hpp b/arch/x86_64/include/arch/vga/text.hpp deleted file mode 100644 index 2e73dd2..0000000 --- a/arch/x86_64/include/arch/vga/text.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_TEXT_HPP -#define TEACHOS_X86_64_VGA_TEXT_HPP - -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export - -#endif // TEACHOS_ARCH_X86_64_VIDEO_VGA_TEXT_HPP \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text/attribute.hpp b/arch/x86_64/include/arch/vga/text/attribute.hpp deleted file mode 100644 index 6395aed..0000000 --- a/arch/x86_64/include/arch/vga/text/attribute.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_TEXT_ATTRIBUTE_HPP -#define TEACHOS_X86_64_VGA_TEXT_ATTRIBUTE_HPP - -// IWYU pragma: private, include - -#include -#include - -namespace arch::vga::text -{ - //! The VGA text mode attribute. - //! - //! @note In the text mode of VGA, every code point being presented is followed by an attribute description. This - //! allows for the modification of how the relevant "cell" is presented. - //! - //! @see text::foreground_flag - //! @see text::background_flag - struct attribute - { - color foreground_color : 3; ///< The foreground color of the cell, e.g. the color of the code point. - enum foreground_flag foreground_flag : 1; ///< The foreground color modification flag of the cell. - color background_color : 3; ///< The background color of the cell. - enum background_flag background_flag : 1; ///< The background color modification flag of the cell. - }; - - static_assert(sizeof(attribute) == 1, "The VGA text mode attribute must fit inside a single byte."); - -} // namespace arch::vga::text - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text/buffer.hpp b/arch/x86_64/include/arch/vga/text/buffer.hpp deleted file mode 100644 index 8eb6645..0000000 --- a/arch/x86_64/include/arch/vga/text/buffer.hpp +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_TEXT_BUFFER_HPP -#define TEACHOS_X86_64_VGA_TEXT_BUFFER_HPP - -// IWYU pragma: private, include - -#include - -#include -#include -#include -#include - -namespace arch::vga::text -{ - //! A VGA text buffer. - //! - //! VGA text mode presents a linear buffer of so-called cells. Each cell consists of a single code point and a - //! rendering attribute. The codepoint determines the character being rendered in a specific cell, while the attribute - //! determines the visual style of that cell. - //! - //! @see text::attribute - struct buffer - { - using cell = std::pair; - - //! Create a new buffer. - //! - //! @param width The width of the buffer - //! @param height The height of the buffer - //! @param start A pointer to the first byte of the buffer. - //! @param position The starting position for the first write to the buffer - buffer(std::size_t width, std::size_t height, cell * start, std::size_t position = 0); - - //! Clear the buffer. - //! - //! Clearing the buffer ensures it is filled with zeroes, effectively erasing all data and resetting the output - //! position to the start of the buffer. - auto clear() -> void; - - //! Write a string of formatted code points to the buffer. - //! - //! @param code_points A string of (8-bit) code points to write to the buffer. - //! @param attribute The formatting to apply to the written sequence of code points. - auto write(std::string_view code_points, attribute attribute) -> void; - - //! Write a single, formatted code point to the buffer. - //! - //! @param code_point A single (8-bit) code point - //! @param attribute The formatting to apply to the code point. - auto write(char code_point, attribute attribute) -> void; - - //! Move the output position to a new line and scroll the buffer if necessary. - auto newline() -> void; - - //! Scroll the buffer contents. - //! - //! @param nof_lines The number of lines to scroll up. - auto scroll(std::size_t nof_lines = 1) -> void; - - private: - //! Get column number of the current cell. - [[nodiscard]] auto column() const noexcept -> std::ptrdiff_t; - - //! Get the line number of the current cell. - [[nodiscard]] auto line() const noexcept -> std::ptrdiff_t; - - //! Process the semantics of special code points, for example newlines and carriage returns. - //! - //! @param code_point The code point to process. - //! @param attribute The attribute to use when writing to the text buffer. - //! @return @p true iff. the code point was handled, @p false otherwise. - auto handle_special_code_point(char code_point, attribute attribute) -> bool; - - //! Perform the actual output to the buffer. - //! - //! @param code_points The code points to output.. - //! @param attribute The attribute to use when writing to the text buffer. - auto do_write(std::string_view code_points, attribute attribute) -> void; - - //! Perform the actual output to the buffer. - //! - //! @param code_point The code point to output. - //! @param attribute The attribute to use when writing to the text buffer. - auto do_write(char code_point, attribute attribute) -> void; - - //! The width, in cells, of the buffer. - std::size_t m_width{}; - - //! The height, in cells, of the buffer. - std::size_t m_height{}; - - //! The text mode data buffer. - std::span m_buffer; - - //! The position of the next cell to be written to. - std::size_t m_position{}; - }; - -} // namespace arch::vga::text - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text/color.hpp b/arch/x86_64/include/arch/vga/text/color.hpp deleted file mode 100644 index e0ad6df..0000000 --- a/arch/x86_64/include/arch/vga/text/color.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_TEXT_COLOR_HPP -#define TEACHOS_X86_64_VGA_TEXT_COLOR_HPP - -// IWYU pragma: private, include - -#include - -namespace arch::vga::text -{ - //! VGA Text Mode standard colors. - //! - //! Every color may be used as a foreground and/or background color, in any combination. - enum struct color : std::uint8_t - { - //! Equivalent to HTML color \#000000. - black, - //! Equivalent to HTML color \#0000AA. - blue, - //! Equivalent to HTML color \#00AA00. - green, - //! Equivalent to HTML color \#00AAAA. - cyan, - //! Equivalent to HTML color \#AA0000. - red, - //! Equivalent to HTML color \#AA00AA. - purple, - //! Equivalent to HTML color \#AA5500. - brown, - //! Equivalent to HTML color \#AAAAAA. - gray, - }; - -} // namespace arch::vga::text - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text/common_attributes.hpp b/arch/x86_64/include/arch/vga/text/common_attributes.hpp deleted file mode 100644 index 3d8929f..0000000 --- a/arch/x86_64/include/arch/vga/text/common_attributes.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_TEXT_COMMON_ATTRIBUTES_HPP -#define TEACHOS_X86_64_VGA_TEXT_COMMON_ATTRIBUTES_HPP - -// IWYU pragma: private, include - -#include -#include -#include - -namespace arch::vga::text -{ - //! Make the affected cell display with a gray foreground and black background. - [[maybe_unused]] constexpr auto gray_on_black = attribute{.foreground_color = color::gray, - .foreground_flag = foreground_flag::none, - .background_color = color::black, - .background_flag = background_flag::none}; - - //! Make the affected cell display with a green foreground and black background. - [[maybe_unused]] constexpr auto green_on_black = attribute{.foreground_color = color::green, - .foreground_flag = foreground_flag::none, - .background_color = color::black, - .background_flag = background_flag::none}; - - //! Make the affected cell display with a green foreground and black background. - [[maybe_unused]] constexpr auto red_on_black = attribute{.foreground_color = color::red, - .foreground_flag = foreground_flag::none, - .background_color = color::black, - .background_flag = background_flag::none}; - - //! Make the affected cell display with a white (gray + intense) foreground and red background. - [[maybe_unused]] constexpr auto white_on_red = attribute{.foreground_color = color::gray, - .foreground_flag = foreground_flag::intense, - .background_color = color::red, - .background_flag = background_flag::none}; -} // namespace arch::vga::text - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text/device.hpp b/arch/x86_64/include/arch/vga/text/device.hpp deleted file mode 100644 index 0a0e017..0000000 --- a/arch/x86_64/include/arch/vga/text/device.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_TEXT_DEVICE_HPP -#define TEACHOS_X86_64_VGA_TEXT_DEVICE_HPP - -// IWYU pragma: private, include - -#include - -#include - -#include - -namespace arch::vga::text -{ - //! A VGA Text Mode device. - //! - //! VGA text mode presents a linear buffer of so-called cells. Each cell consists of a single code point and a - //! rendering attribute. The codepoint determines the character being rendered in a specific cell, while the attribute - //! determines the visual style of that cell. - //! - //! @see text::attribute - struct device final : kapi::cio::output_device - { - device(); - - //! Clear the screen. - //! - //! Clearing the screen ensures the text mode buffer is filled with zeroes, effectively erasing all displayed data - //! and resetting the output position to the start of the buffer. - auto clear() -> void; - - //! Enable or disable the VGA text mode cursor. - //! - //! @param enabled Whether to enable the cursor. - auto cursor(bool enabled) -> void; - - //! @copydoc kapi::cio::output_device - auto write(kapi::cio::output_stream stream, std::string_view text) -> void override; - - private: - buffer m_buffer; - }; - -} // namespace arch::vga::text - -#endif \ No newline at end of file diff --git a/arch/x86_64/include/arch/vga/text/flags.hpp b/arch/x86_64/include/arch/vga/text/flags.hpp deleted file mode 100644 index 7a29e33..0000000 --- a/arch/x86_64/include/arch/vga/text/flags.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef TEACHOS_X86_64_VGA_TEXT_FLAGS_HPP -#define TEACHOS_X86_64_VGA_TEXT_FLAGS_HPP - -// IWYU pragma: private, include - -namespace arch::vga::text -{ - - //! VGA Text Mode standard background modification flags. - enum struct background_flag : bool - { - //! Do not modify the foreground rendering. - none, - //! Render the background as blinking or intense. - //! - //! Whether this flag is interpreted as 'blink' or 'bright' depends on the currently active configuration of the VGA - //! device. - //! - //! @note The VGA standard does not specify the exact effect of this 'intense', however, most devices render such - //! colors brighter. - blink_or_bright, - }; - - //! VGA Text Mode standard foreground modification flags. - enum struct foreground_flag : bool - { - //! Do not modify the foreground rendering. - none, - //! Render the foreground as intense. - //! - //! @note The VGA standard does not specify the exact effect of this 'intense', however, most devices render such - //! colors brighter. - intense, - }; - -} // namespace arch::vga::text - -#endif \ No newline at end of file diff --git a/arch/x86_64/pre/include/arch/context_switching/main.hpp b/arch/x86_64/pre/include/arch/context_switching/main.hpp deleted file mode 100644 index 07e00e8..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/main.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_MAIN_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_MAIN_HPP - -#include -#include - -namespace teachos::arch::context_switching -{ - /** - * @brief Contains the references to the tables required for context switching - */ - struct descriptor_tables - { - segment_descriptor_table::global_descriptor_table & gdt; ///< Reference to the global descriptor table. - interrupt_descriptor_table::interrupt_descriptor_table & idt; ///< Reference to the interrupt descriptor table. - }; - - /** - * @brief Creates the Interrupt Descriptor Table and Global Descriptor Table as a static variable the first time this - * method is called and update IDTR and GDTR registers values. - * - * @note Subsequent calls after the first one, will simply return the previously created tables, but not update the - * registers again. - * - * @return References to the statically created Interrupt Descriptor and Global Descriptor Table. - */ - auto initialize_descriptor_tables() -> descriptor_tables; - - /** - * @brief Switches from the current Kernel Mode (Level 0) to User Mode (Level 3). Will simply use predefined Segment - * Selectors for the User Data and User Code Segment, which are Index 3 and 4 in the GDT respectively. - */ - auto switch_to_user_mode() -> void; - - /** - * @brief Switches from the current Code and Data Segment to the given Code and Data Segment. - * - * @note This method will additionally call initialize_descriptor_tables, to ensure the GDTR and IDTR have been setup - * correctly before attempting to switch the context. This switch is achieved using a far return, which will once - * executed call the given void function. - * - * @param data_segment Data Segment that the SS, DS; ES, FS and GS register will be set too. - * @param code_segment Code Segment that the CS register will be set too. - * @param return_function Function that will be called once the switch has been achieved. - */ - auto switch_context(interrupt_descriptor_table::segment_selector data_segment, - interrupt_descriptor_table::segment_selector code_segment, void (*return_function)()) -> void; - -} // namespace teachos::arch::context_switching - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_MAIN_HPP diff --git a/arch/x86_64/pre/include/arch/context_switching/syscall/main.hpp b/arch/x86_64/pre/include/arch/context_switching/syscall/main.hpp deleted file mode 100644 index f507c61..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/syscall/main.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_MAIN_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_MAIN_HPP - -#include - -namespace teachos::arch::context_switching::syscall -{ - /** - * @brief Possible syscall implementation that should actually be called. - * - * @note Attempts to reflect the Linux interface partially. See https://filippo.io/linux-syscall-table/ for more - * information. - */ - enum class type : uint64_t - { - WRITE = 1U, ///< Loads the arg_0 parameter as an address pointing to a const char array, which will then be printed - ///< onto the VGA buffer screen. - EXPAND_HEAP = 2U, /// Expands the User Heap by additonally mapping 100 KiB of virtual page memory. Ignores the - /// parameters and uses them as out parameters instead, where arg_0 is the start of the newly - /// mapped heap area and arg_1 is the size of the entire area. Can be less than 100 KiB if less - /// space remains. - ASSERT = 3U, /// Loads the arg_0 parameter as a boolean which needs to be true or it will print the message in - /// arg_1 parameter onto the VGA buffer screen, keep it as a nullptr if it shouldn't print anything - /// and then it halts the further execution of the application. - }; - - /** - * @brief Possible error codes that can be returned by the different syscall methods called depending on the type - * enum. - */ - enum class error : uint8_t - { - OK = 0U, ///< No error occured in syscall. - OUT_OF_MEMORY = 1U, ///< Expanding heap failed because we have run out of mappable virtual address space. - }; - - /** - * @brief Allows to convert the error enum type into a boolean directly. Where any code besides 0 being no error, will - * return true. The remaining errors return true. - * - * @param e Error code that was returned by the syscall. - * @return Return true if there was no error and false otherwise. - */ - constexpr bool operator!(error e) - { - return e == error::OK; - } - - /** - * @brief Maximum amount of arguments that can be passed to a syscall. Default value is 0 and arguments are only ever - * used depending on the actual enum type and if the method requires thoose parameters. - */ - struct arguments - { - uint64_t arg_0{}; ///< First optional paramter to the syscall representing the RDI register. - uint64_t arg_1{}; ///< Second optional paramter to the syscall representing the RSI register. - uint64_t arg_2{}; ///< Third optional paramter to the syscall representing the RDX register. - uint64_t arg_3{}; ///< Fourth optional paramter to the syscall representing the R10 register. - uint64_t arg_4{}; ///< Fifth optional paramter to the syscall representing the R8 register. - uint64_t arg_5{}; ///< Sixth optional paramter to the syscall representing the R9 register. - }; - - /** - * @brief Response of a systemcall always containin an error code, signaling if the syscall even succeeded or not. - * Additionally it may contain up to 6 return values in the values struct. - */ - struct response - { - error error_code; ///< Error code returned by the syscall. If it failed all the values will be 0. - arguments values = {}; ///< Optional return values of the syscall implementation. - }; - - /** - * @brief Calls the method associated with the given syscall number and passes the given optional arguments to it, - * over the RDI, RSI, RDX, R10, R8 and R9 register. - * - * @param syscall_number Syscall method that should be called. See enum values in type for possible implemented - * methods. - * @param args Optional arguments passable to the different syscall methods, called depending on the syscall_number. - * Not passing the required parameters to the method, will result in passing 0 instead, which might make the fail or - * not function correctly. - * @return The syscall implementation always returns a bool-convertable error code converting to true if the syscall - * failed or false if it didn't. Additionally it might pase additional values besides the error code, they will be set - * in the arguments struct. So the value can be read and used for further processing. - */ - [[gnu::section(".user_text")]] - auto syscall(type syscall_number, arguments args = {}) -> response; - -} // namespace teachos::arch::context_switching::syscall - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_MAIN_HPP diff --git a/arch/x86_64/pre/include/arch/context_switching/syscall/syscall_enable.hpp b/arch/x86_64/pre/include/arch/context_switching/syscall/syscall_enable.hpp deleted file mode 100644 index 8cb468a..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/syscall/syscall_enable.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_ENABLE_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_ENABLE_HPP - -namespace teachos::arch::context_switching::syscall -{ - /** - * @brief Enables and sets up internal CPU registers to allow for syscall to function correctly and switch context - * temporarily back to the kernel level. - * - * @note Configures the Model Specific Register required by syscall (LSTAR, FMASK, STAR) with the correct values so - * that the syscall_handler is called and sets the System Call Extension bit on the EFER flags to allow for syscall - * to be used. - */ - auto enable_syscall() -> void; - -} // namespace teachos::arch::context_switching::syscall - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_ENABLE_HPP diff --git a/arch/x86_64/pre/include/arch/context_switching/syscall/syscall_handler.hpp b/arch/x86_64/pre/include/arch/context_switching/syscall/syscall_handler.hpp deleted file mode 100644 index 2e7bcd1..0000000 --- a/arch/x86_64/pre/include/arch/context_switching/syscall/syscall_handler.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_HANDLER_HPP -#define TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_HANDLER_HPP - -#include - -namespace teachos::arch::context_switching::syscall -{ - /** - * @brief Handler for SYSCALL instruction. Calls a specific implementation based - * on the register RAX. - * - * @return Returns with LEAVE, SYSRETQ - */ - auto syscall_handler() -> void; - -} // namespace teachos::arch::context_switching::syscall - -#endif // TEACHOS_ARCH_X86_64_CONTEXT_SWITCHING_SYSCALL_SYSCALL_HANDLER_HPP diff --git a/arch/x86_64/pre/include/arch/kernel/halt.hpp b/arch/x86_64/pre/include/arch/kernel/halt.hpp deleted file mode 100644 index 377acc0..0000000 --- a/arch/x86_64/pre/include/arch/kernel/halt.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_HALT_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_HALT_HPP - -namespace teachos::arch::kernel -{ - /** - * @brief Halts the kernel execution, meaning any code after a call to this will not run anymore. - */ - extern "C" [[noreturn]] auto halt() -> void; - -} // namespace teachos::arch::kernel - -#endif // TEACHOS_ARCH_X86_64_KERNEL_HALT_HPP diff --git a/arch/x86_64/pre/include/arch/kernel/main.hpp b/arch/x86_64/pre/include/arch/kernel/main.hpp deleted file mode 100644 index a13e5f4..0000000 --- a/arch/x86_64/pre/include/arch/kernel/main.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_KERNEL_MAIN_HPP -#define TEACHOS_ARCH_X86_64_KERNEL_MAIN_HPP - -namespace teachos::arch::kernel -{ - /** - * @brief Initalizes the kernel system. - */ - auto main() -> void; - -} // namespace teachos::arch::kernel - -#endif // TEACHOS_ARCH_X86_64_KERNEL_MAIN_HPP diff --git a/arch/x86_64/pre/include/arch/user/main.hpp b/arch/x86_64/pre/include/arch/user/main.hpp deleted file mode 100644 index c168a1f..0000000 --- a/arch/x86_64/pre/include/arch/user/main.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef TEACHOS_ARCH_X86_64_USER_MAIN_HPP -#define TEACHOS_ARCH_X86_64_USER_MAIN_HPP - -namespace teachos::arch::user -{ - /** - * @brief User Main method. If this method finishes there is no code left to run and the whole OS will shut down. - * Additionally this main method is executed at Ring 3 and accessing CPU Registers or Kernel level functionality can - * only be done over syscalls and not directly anymore. - */ - [[gnu::section(".user_text")]] - auto main() -> void; - -} // namespace teachos::arch::user - -#endif // TEACHOS_ARCH_X86_64_USER_MAIN_HPP diff --git a/arch/x86_64/pre/src/context_switching/main.cpp b/arch/x86_64/pre/src/context_switching/main.cpp deleted file mode 100644 index 0961499..0000000 --- a/arch/x86_64/pre/src/context_switching/main.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace teachos::arch::context_switching -{ - namespace - { - constexpr interrupt_descriptor_table::segment_selector KERNEL_CODE_SEGMENT_SELECTOR{ - 1U, interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_KERNEL}; - constexpr kernel::cpu::far_pointer KERNEL_CODE_POINTER{&kernel::cpu::reload_data_segment_registers, - KERNEL_CODE_SEGMENT_SELECTOR}; - constexpr context_switching::interrupt_descriptor_table::segment_selector USER_CODE_SEGMENT_SELECTOR{ - 3U, context_switching::interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_USER}; - constexpr context_switching::interrupt_descriptor_table::segment_selector USER_DATA_SEGMENT_SELECTOR{ - 4U, context_switching::interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_USER}; - - auto reload_gdtr() -> void - { - kernel::cpu::call(KERNEL_CODE_POINTER); - } - } // namespace - - auto initialize_descriptor_tables() -> descriptor_tables - { - bool static initalized = false; - - if (!initalized) - { - kernel::cpu::clear_interrupt_flag(); - - segment_descriptor_table::update_gdtr(); - interrupt_descriptor_table::update_interrupt_descriptor_table_register(); - - reload_gdtr(); - segment_descriptor_table::update_tss_register(); - - kernel::cpu::set_interrupt_flag(); - initalized = true; - } - - descriptor_tables tables = {segment_descriptor_table::get_or_create_gdt(), - interrupt_descriptor_table::get_or_create_interrupt_descriptor_table()}; - return tables; - } - - auto switch_to_user_mode() -> void - { - syscall::enable_syscall(); - switch_context(USER_DATA_SEGMENT_SELECTOR, USER_CODE_SEGMENT_SELECTOR, user::main); - } - - auto switch_context(interrupt_descriptor_table::segment_selector data_segment, - interrupt_descriptor_table::segment_selector code_segment, void (*return_function)()) -> void - { - (void)initialize_descriptor_tables(); - kernel::cpu::set_data_segment_registers(data_segment); - kernel::cpu::set_code_segment_register(data_segment, code_segment, reinterpret_cast(return_function)); - } -} // namespace teachos::arch::context_switching diff --git a/arch/x86_64/pre/src/context_switching/syscall/main.cpp b/arch/x86_64/pre/src/context_switching/syscall/main.cpp deleted file mode 100644 index 10bd087..0000000 --- a/arch/x86_64/pre/src/context_switching/syscall/main.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include - -namespace teachos::arch::context_switching::syscall -{ - auto syscall(type syscall_number, arguments args) -> response - { - asm volatile("mov %[input], %%rax" - : /* no output from call */ - : [input] "m"(syscall_number) - : "memory"); - - asm volatile("mov %[input], %%rdi " : /* no output from call */ : [input] "m"(args.arg_0) : "memory"); - asm volatile("mov %[input], %%rsi" : /* no output from call */ : [input] "m"(args.arg_1) : "memory"); - asm volatile("mov %[input], %%rdx" : /* no output from call */ : [input] "m"(args.arg_2) : "memory"); - asm volatile("mov %[input], %%r10" : /* no output from call */ : [input] "m"(args.arg_3) : "memory"); - asm volatile("mov %[input], %%r8" : /* no output from call */ : [input] "m"(args.arg_4) : "memory"); - asm volatile("mov %[input], %%r9" : /* no output from call */ : [input] "m"(args.arg_5) : "memory"); - - asm volatile("syscall"); - - arguments values{}; - asm volatile("mov %%rdi, %[output]" : [output] "=m"(values.arg_0)); - asm volatile("mov %%rsi, %[output]" : [output] "=m"(values.arg_1)); - asm volatile("mov %%rdx, %[output]" : [output] "=m"(values.arg_2)); - asm volatile("mov %%r10, %[output]" : [output] "=m"(values.arg_3)); - asm volatile("mov %%r8, %[output]" : [output] "=m"(values.arg_4)); - asm volatile("mov %%r9, %[output]" : [output] "=m"(values.arg_5)); - - error error_code{}; - asm volatile("mov %%al, %[output]" : [output] "=m"(error_code)); - - return {error_code, values}; - } - -} // namespace teachos::arch::context_switching::syscall diff --git a/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp b/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp deleted file mode 100644 index f9f070a..0000000 --- a/arch/x86_64/pre/src/context_switching/syscall/syscall_enable.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include - -#include -#include -#include - -namespace teachos::arch::context_switching::syscall -{ - namespace - { - constexpr interrupt_descriptor_table::segment_selector KERNEL_CODE_SEGMENT_SELECTOR{ - 1U, interrupt_descriptor_table::segment_selector::REQUEST_LEVEL_KERNEL}; - - constexpr auto IA32_STAR_ADDRESS = 0xC000'0081; - constexpr auto IA32_LSTAR_ADDRESS = 0xC000'0082; - constexpr auto IA32_FMASK_ADDRESS = 0xC000'0084; - - } // namespace - - auto enable_syscall() -> void - { - uint64_t const syscall_function = reinterpret_cast(syscall_handler); - kernel::cpu::write_msr(IA32_LSTAR_ADDRESS, syscall_function); - kernel::cpu::write_msr(IA32_FMASK_ADDRESS, 0U); - - uint64_t const kernel_cs = KERNEL_CODE_SEGMENT_SELECTOR; - uint64_t const star_value = (kernel_cs << 32) | (kernel_cs << 48); - kernel::cpu::write_msr(IA32_STAR_ADDRESS, star_value); - - kernel::cpu::set_efer_bit(kernel::cpu::efer_flags::SCE); - } -} // namespace teachos::arch::context_switching::syscall diff --git a/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp b/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp deleted file mode 100644 index 430d65c..0000000 --- a/arch/x86_64/pre/src/context_switching/syscall/syscall_handler.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include - -namespace teachos::arch::context_switching::syscall -{ - - namespace - { - auto write_to_vga_buffer(uint64_t buffer) -> response - { - video::vga::text::write(reinterpret_cast(buffer), - video::vga::text::common_attributes::green_on_black); - video::vga::text::newline(); - return {error::OK}; - } - - auto expand_user_heap() -> response - { - auto static current_heap_end = memory::heap::USER_HEAP_START; - uint64_t const heap_start = current_heap_end; - memory::remap_heap(heap_start, memory::heap::USER_HEAP_SIZE, memory::paging::entry::USER_ACCESSIBLE); - current_heap_end += memory::heap::USER_HEAP_SIZE; - return { - error::OK, - {heap_start, memory::heap::USER_HEAP_SIZE} - }; - } - } // namespace - - auto syscall_handler() -> void - { - // Saving state of rcx and r11 because it is required by sysretq to function. - // Calls to other functions potentially overwrite these registers, because of - // callee saved calling convention. - uint64_t return_instruction_pointer, rflags = {}; - asm volatile("mov %%rcx, %[output]" : [output] "=m"(return_instruction_pointer)); - asm volatile("mov %%r11, %[output]" : [output] "=m"(rflags)); - - uint64_t syscall_number, arg_0, arg_1, arg_2, arg_3, arg_4, arg_5 = {}; - asm volatile("mov %%rdi, %[output]" : [output] "=m"(arg_0)); - asm volatile("mov %%rsi, %[output]" : [output] "=m"(arg_1)); - asm volatile("mov %%rdx, %[output]" : [output] "=m"(arg_2)); - asm volatile("mov %%r10, %[output]" : [output] "=m"(arg_3)); - asm volatile("mov %%r8, %[output]" : [output] "=m"(arg_4)); - asm volatile("mov %%r9, %[output]" : [output] "=m"(arg_5)); - - // RAX is read last, because paired with our type enum, we can use it to check - // if the register has been written by the compiled code between executing the syscall - // and now. - asm volatile("mov %%rax, %[output]" : [output] "=m"(syscall_number)); - - response result; - switch (static_cast(syscall_number)) - { - case type::WRITE: - result = write_to_vga_buffer(arg_0); - break; - case type::EXPAND_HEAP: - result = expand_user_heap(); - break; - case type::ASSERT: - teachos::arch::exception_handling::assert(arg_0, reinterpret_cast(arg_1)); - break; - default: - teachos::arch::exception_handling::panic("[Syscall Handler] Invalid syscall number"); - break; - } - - asm volatile("mov %[input], %%rax" - : /* no output from call */ - : [input] "m"(result.error_code) - : "memory"); - - asm volatile("mov %[input], %%rdi" - : /* no output from call */ - : [input] "m"(result.values.arg_0) - : "memory"); - asm volatile("mov %[input], %%rsi" - : /* no output from call */ - : [input] "m"(result.values.arg_1) - : "memory"); - asm volatile("mov %[input], %%rdx" - : /* no output from call */ - : [input] "m"(result.values.arg_2) - : "memory"); - asm volatile("mov %[input], %%r10" - : /* no output from call */ - : [input] "m"(result.values.arg_3) - : "memory"); - asm volatile("mov %[input], %%r8" - : /* no output from call */ - : [input] "m"(result.values.arg_4) - : "memory"); - asm volatile("mov %[input], %%r9" - : /* no output from call */ - : [input] "m"(result.values.arg_5) - : "memory"); - - asm volatile("mov %[input], %%rcx" - : /* no output from call */ - : [input] "m"(return_instruction_pointer) - : "memory"); - asm volatile("mov %[input], %%r11" - : /* no output from call */ - : [input] "m"(rflags) - : "memory"); - - // Additionally call leave, because x86 allocates stack space for the internal variables. If we do not clean up this - // newly created stack frame the syscall instruction that landed in this syscall_handler, will never return to the - // method that originally called it, because the RIP has not been restored from the previous stack frame. - asm volatile("leave\n" - "sysretq"); - } - -} // namespace teachos::arch::context_switching::syscall diff --git a/arch/x86_64/pre/src/kernel/main.cpp b/arch/x86_64/pre/src/kernel/main.cpp deleted file mode 100644 index 2658678..0000000 --- a/arch/x86_64/pre/src/kernel/main.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace teachos::arch::kernel -{ - auto stack_overflow_test(int count) -> int - { - int test[5000] = {}; - if (test[0] == 0xFFFF) - { - return count; - } - count = stack_overflow_test(count); - return count++; - } - - auto heap_test() -> void - { - auto test2 = new memory::multiboot::memory_information{}; - auto test3 = new memory::multiboot::memory_information{}; - auto test4 = *test2; - auto test5 = *test3; - test4.kernel_end = 5000; - test5.kernel_end = 3000; - auto test6 = test4.kernel_end; - auto test7 = test5.kernel_end; - auto test8 = memory::multiboot::read_multiboot2(); - if (test6 && test7 && test8.kernel_end) - { - video::vga::text::write("Heap test successful", video::vga::text::common_attributes::green_on_black); - } - test2->kernel_end = 2000; - test2->kernel_start = 1000; - test2->multiboot_start = 2000; - delete test2; - delete test3; - - auto test9 = new int(50); - delete test9; - } - - auto main() -> void - { - video::vga::text::clear(); - video::vga::text::cursor(false); - video::vga::text::write("TeachOS is starting up...", video::vga::text::common_attributes::green_on_black); - video::vga::text::newline(); - - memory::initialize_memory_management(); - // stack_overflow_test(0); - - memory::heap::global_heap_allocator::register_heap_allocator(memory::heap::heap_allocator_type::LINKED_LIST); - // heap_test(); - - auto address = memory::heap::global_heap_allocator::kmalloc(8U); - (void)address; - - context_switching::switch_to_user_mode(); - } -} // namespace teachos::arch::kernel diff --git a/arch/x86_64/pre/src/user/main.cpp b/arch/x86_64/pre/src/user/main.cpp deleted file mode 100644 index 10a17db..0000000 --- a/arch/x86_64/pre/src/user/main.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include - -#include -#include - -#include -#include -#include -#include - -namespace teachos::arch::user -{ - auto main() -> void - { - constexpr char syscall_message[] = "Successfully entered user mode and wrote to VGA buffer via syscall!"; - context_switching::syscall::syscall(context_switching::syscall::type::WRITE, - {reinterpret_cast(&syscall_message)}); - - // Test C++ standard library - std::array, 4> array_test = {std::atomic{5}, std::atomic{10}, - std::atomic{15}, std::atomic{20}}; - std::ranges::for_each(array_test, [](auto & item) { - auto value = item.load(); - uint8_t max_value = std::max(value, uint8_t{10}); - item.exchange(max_value + 2); - }); - - auto address = new uint64_t{10U}; - (void)address; - - for (;;) - { - } - } -} // namespace teachos::arch::user diff --git a/arch/x86_64/src/boot/boot32.S b/arch/x86_64/src/boot/boot32.S deleted file mode 100644 index e6fcd85..0000000 --- a/arch/x86_64/src/boot/boot32.S +++ /dev/null @@ -1,443 +0,0 @@ -#include - -/** - * @brief Uninitialized data for the bootstrapping process. - */ -.section .boot_bss, "aw", @nobits - -page_maps_start: -page_map_level_4: .skip 512 * 8 -page_map_level_3_high: .skip 512 * 8 -page_map_level_3_low: .skip 512 * 8 -page_map_level_2: .skip 512 * 8 -page_maps_end = . -page_maps_size = page_maps_end - page_maps_start - -/** - * @brief Storage for the multiboot2 information pointer. - */ -.global multiboot_information_pointer -multiboot_information_pointer: .skip 8 - -/** - * @brief Storage for the bootstrap stack. - */ -.section .boot_stack, "aw", @nobits -.align 16 - -early_stack_bottom: .skip 1 << 8 -early_stack_top: -early_stack_size = early_stack_top - early_stack_bottom - -/** - * @brief Constants for the bootstrapping process. - */ -.section .boot_rodata, "a", @progbits - -.global global_descriptor_table_data - -/** - * @brief A basic GDT for long mode. - */ -global_descriptor_table: -global_descriptor_table_null = . - global_descriptor_table -.quad 0 -global_descriptor_table_code = . - global_descriptor_table -.quad GDT_READ_WRITE | GDT_EXECUTABLE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT | GDT_LONG_MODE | (1 << 55) -global_descriptor_table_data = . - global_descriptor_table -.quad GDT_READ_WRITE | GDT_DESCRIPTOR_TYPE | GDT_PRESENT | (1 << 54) | (1 << 55) -global_descriptor_table_end: - -message_prefix_panic: .string "Panic: " -message_not_loaded_by_multiboot2: .string "The operating system was not loaded by a Multiboot 2 loader." -message_cpuid_instruction_no_supported: .string "The 'cpuid' instruction is not supported on this platform." -message_long_mode_not_supported: .string "Long mode is not supported by this platform." -message_return_from_kernel_main: .string "Execution returned from kernel main." - -/** - * @brief Initialized data for the bootstrapping process. - */ -.section .boot_data, "aw", @progbits - -/** - * @brief A pointer to the current position within the VGA text buffer. - */ -.global vga_buffer_pointer -vga_buffer_pointer: .quad 0xb8000 - -/** - * @brief Code for the bootstrapping process. - */ -.section .boot_text, "ax", @progbits -.align 16 -.code32 - -.macro pie_base - push %esi - call 0f - 0: - pop %esi -.endm - -.macro function_start - push %ebp - mov %esp, %ebp -.endm - -.macro function_end - leave - ret -.endm - -.macro pie_function_start - function_start - pie_base -.endm - -.macro pie_function_end - pop %esi - function_end -.endm - -/** - * @brief Prepare the environment and start the kernel. - * - * This function performs all necessary checks to ensure the system was loaded - * by the expected loader and supports all features required to run the kernel. - * If successful, it prepares the system by setting up memory virtualization - * and then start the kernel proper. - * - * @param %eax The Multiboot 2 magic marker. - * @param %ebx The Multiboot 2 information pointer. - * @return void This function does not return. - */ -.global _start -_start: - call 0f -0: - pop %esi - - lea (early_stack_top - 0b)(%esi), %ecx - - mov %ecx, %esp - mov %esp, %ebp - - call _assert_loaded_by_multiboot2_loader - call _save_multiboot_information_pointer - - call _assert_cpuid_instruction_is_supported - call _assert_cpu_supports_long_mode - - push $HUGE_PAGES_TO_MAP - call _prepare_page_maps - add $4, %esp - - call _enable_paging - call _enable_sse - call _reload_gdt - - lea (_entry64 - 0b)(%esi), %eax - pushl $global_descriptor_table_code - pushl %eax - lret - -/** - * @brief Halt the system. - * - * This function will instruct the CPU to halt. It will try to keep the CPU - * halted, even if interrupts occur. - * - * @return This function never returns. - */ -_halt: - function_start - -1: - hlt - jmp 1b - - function_end - -/** - * @brief Print a message via the VGA text buffer. - * - * @param ebp+12 The message to print. - * @param ebp+8 The color to print the message in. - */ -_print: - pie_function_start - - push %edi - push %ebx - - mov 8(%ebp), %al - mov 12(%ebp), %edx - mov $0, %ecx - lea (vga_buffer_pointer - 0b)(%esi), %edi - mov (%edi), %edi - -1: - mov (%edx, %ecx), %bl - test %bl, %bl - je 2f - mov %bl, (%edi, %ecx, 2) - mov %al, 1(%edi, %ecx, 2) - inc %ecx - jmp 1b - -2: - shl $1, %ecx - add %ecx, %edi - lea (vga_buffer_pointer - 0b)(%esi), %ecx - mov %edi, (%ecx) - - pop %ebx - pop %edi - - pie_function_end - -/** - * @brief Print a given panic message and then halt the machine as if by calling ::halt() - * - * @param ebp+4 A message to print. - * @return This function does not return. - */ -_panic: - pie_function_start - - lea (message_prefix_panic - 0b)(%esi), %eax - - push %eax - push $0x4f - call _print - - mov 8(%ebp), %eax - mov %eax, 4(%esp) - call _print - add $8, %esp - - call _halt - - pie_function_end - -/** - * Assert that we were loaded by a Multiboot 2 compliant bootloader. - * - * This assertion will panic the system if the magic signature was not found. - * If we were loaded my an appropriate bootloader, this function also saves - * the provided MBI pointer to `multiboot_information_pointer`. - */ -_assert_loaded_by_multiboot2_loader: - pie_function_start - - cmp $MULTIBOOT2_MAGIC, %eax - je 1f - lea (message_not_loaded_by_multiboot2 - 0b)(%esi), %eax - push %eax - call _panic -1: - pie_function_end - -/** - * @brief Store the multiboot 2 information pointer in the global memory. - * - * @return void - */ -_save_multiboot_information_pointer: - pie_function_start - - lea (multiboot_information_pointer - 0b)(%esi), %eax - mov %ebx, (%eax) - - pie_function_end - -/** - * @brief Assert that the CPU supports the CPUID instruction. - * - * The primary way to check for support of the instruction is to flip the ID - * bin in EFLAGS and then check if this changed was accepted. If so, the CPU - * supports the CPUID instruction, otherwise it most-likely doesn't. - */ -_assert_cpuid_instruction_is_supported: - pie_function_start - - pushfl - pop %eax - mov %eax, %ecx - - xor $(1 << 21), %eax /* Flip the ID bit */ - push %eax /* Move the new bitset on the stack for loading */ - popfl /* Load the flags with ID set back into EFLAGS */ - pushfl /* Copy the flags back onto the stack */ - pop %eax /* Load the flags for further checking */ - - push %ecx - popfl - - cmp %ecx, %eax - jne 1f - lea (message_cpuid_instruction_no_supported - 0b)(%esi), %eax - push %eax - call _panic - -1: - pie_function_end - -/** - * @brief Assert that the CPU supports going into long mode. - */ -_assert_cpu_supports_long_mode: - pie_function_start - - mov $0x80000000, %eax - cpuid - cmp $0x80000001, %eax - jb 1f - - mov $0x80000001, %eax - cpuid - test $(1 << 29), %edx - jnz 2f -1: - lea (message_long_mode_not_supported - 0b)(%esi), %eax - push %eax - call _panic -2: - pie_function_end - -/** - * @brief Prepare a basic page map hierarchy - * - * @param ebp+8 The number of huge pages to map - * @return void - */ -_prepare_page_maps: - pie_function_start - - push %edi - - call _clear_page_map_memory - - lea (page_map_level_4 - 0b)(%esi), %edi - lea (page_map_level_3_low - 0b)(%esi), %eax - or $0b11, %eax - mov %eax, (%edi) - - lea (page_map_level_3_high - 0b)(%esi), %eax - or $0b11, %eax - mov %eax, (511 * 8)(%edi) - - lea (page_map_level_3_low - 0b)(%esi), %edi - lea (page_map_level_2 - 0b)(%esi), %eax - or $0b11, %eax - mov %eax, (%edi) - - lea (page_map_level_3_high - 0b)(%esi), %edi - lea (page_map_level_2 - 0b)(%esi), %eax - or $0b11, %eax - mov %eax, (510 * 8)(%edi) - - lea (page_map_level_2 - 0b)(%esi), %edi - mov 8(%ebp), %ecx - -1: - dec %ecx - mov $(1 << 21), %eax - mul %ecx - or $0b10000011, %eax - mov %eax, (%edi, %ecx, 8) - - test %ecx, %ecx - jnz 1b - - pop %edi - - pie_function_end - -/** - * @brief Clear all page map memory by filling it with 0s. - * - * @return void - */ -_clear_page_map_memory: - pie_function_start - - push %edi - - xor %eax, %eax - mov $page_maps_size, %ecx - shr $2, %ecx - lea (page_maps_start - 0b)(%esi), %edi - rep stosl - - pop %edi - - pie_function_end - -/** - * @p Enable memory virtualization via paging. - * - * Note: This routine expects for there to be a valid set of page maps already - * set up for use. - * - * @return void - */ -_enable_paging: - pie_function_start - - lea (page_map_level_4 - 0b)(%esi), %eax - mov %eax, %cr3 - - /* Enable Physical Address Extension */ - mov %cr4, %eax - or $(1 << 5), %eax - mov %eax, %cr4 - - /* Enable long mode support */ - mov $0xC0000080, %ecx - rdmsr - or $(1 << 8), %eax - wrmsr - - /* Enable paging */ - mov %cr0, %eax - or $(1 << 31), %eax - mov %eax, %cr0 - - pie_function_end - -/** - * @brief Enable use of SSE instructions. - */ -_enable_sse: - function_start - - mov %cr0, %eax - and $0xfffffffb, %eax - or $0x00000002, %eax - mov %eax, %cr0 - - mov %cr4, %eax - or $(3 << 9), %eax - mov %eax, %cr4 - - function_end - -/** - * @brief Prepare a new GTD and load make it active. - * - * @return void - */ -_reload_gdt: - pie_function_start - - sub $10, %esp - lea (global_descriptor_table - 0b)(%esi), %eax - movw $(global_descriptor_table_end - global_descriptor_table -1), (%esp) - mov %eax, 2(%esp) - movl $0, 6(%esp) - - lgdt (%esp) - add $10, %esp - - pie_function_end diff --git a/arch/x86_64/src/boot/entry64.s b/arch/x86_64/src/boot/entry64.s deleted file mode 100644 index 29fb778..0000000 --- a/arch/x86_64/src/boot/entry64.s +++ /dev/null @@ -1,62 +0,0 @@ -.section .stack, "aw", @nobits - -.align 16 -.global stack_top -stack_bottom: .skip 1 << 20 -stack_top: -stack_size = stack_top - stack_bottom - -.section .bss, "aw", @nobits - -//! A structure containing information gathered during the bootstrap process. -//! Expected layout (as described by teachos::boot::information): -//! -//! struct -//! { -//! multiboot2::information_view const * mbi; -//! std::size_t vga_buffer_index; -//! } -.global bootstrap_information -bootstrap_information: .skip 16 - -.section .boot_text, "ax", @progbits -.code64 - -.global _entry64 -_entry64: - mov $global_descriptor_table_data, %rax - mov %rax, %ss - mov %rax, %ds - mov %rax, %es - mov %rax, %fs - mov %rax, %gs - - mov $stack_top, %rsp - mov %rsp, %rbp - - mov multiboot_information_pointer, %rax - or $TEACHOS_VMA, %rax - mov vga_buffer_pointer, %rdx - sub $0xb8000, %rdx - mov %rax, (bootstrap_information) - mov %rdx, (bootstrap_information + 8) - - call invoke_global_constructors - - xor %rax, %rax - mov %rax, %rbp - mov %rax, %rdx - mov %rax, %rsi - - mov $stack_size, %rcx - shr $3, %rcx - lea (stack_bottom), %rdi - rep stosq - - mov %rax, %rdi - - call main - -1: - hlt - jmp 1b diff --git a/arch/x86_64/src/boot/initialize_runtime.cpp b/arch/x86_64/src/boot/initialize_runtime.cpp deleted file mode 100644 index b08c13c..0000000 --- a/arch/x86_64/src/boot/initialize_runtime.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include -#include - -namespace arch::boot -{ - - extern "C" - { - using global_initializer = auto (*)() -> void; - - extern global_initializer __init_array_start; - extern global_initializer __init_array_end; - - auto invoke_global_constructors() -> void - { - auto initializers = std::span{&__init_array_start, &__init_array_end}; - std::ranges::for_each(initializers, [](auto invokable) { std::invoke(invokable); }); - } - } - -} // namespace arch::boot \ No newline at end of file diff --git a/arch/x86_64/src/boot/multiboot.s b/arch/x86_64/src/boot/multiboot.s deleted file mode 100644 index 37d8afe..0000000 --- a/arch/x86_64/src/boot/multiboot.s +++ /dev/null @@ -1,33 +0,0 @@ -.section .boot_mbh, "a" -.align 8 - -multiboot_header_start: -.Lmagic: - .long 0xe85250d6 -.Larch: - .long 0 -.Llength: - .long multiboot_header_end - multiboot_header_start -.Lchecksum: - .long 0x100000000 - (0xe85250d6 + 0 + (multiboot_header_end - multiboot_header_start)) -.align 8 -.Lflags_start: - .word 4 - .word 1 - .long .Lflags_end - .Lflags_start - .long 3 -.Lflags_end: -.align 8 -.Linformation_request_start: - .word 1 - .word 0 - .long .Linformation_request_end - .Linformation_request_start - .long 3 -.Linformation_request_end: -.align 8 -.Lend_start: - .word 0 - .word 0 - .long .Lend_end - .Lend_start -.Lend_end: -multiboot_header_end: diff --git a/arch/x86_64/src/bus/isa.cpp b/arch/x86_64/src/bus/isa.cpp deleted file mode 100644 index f6cc72d..0000000 --- a/arch/x86_64/src/bus/isa.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include - -#include - -#include - -namespace arch::bus -{ - - isa::isa(std::size_t major) - : kapi::devices::bus{major, 0, "isa"} - {} - -} // namespace arch::bus \ No newline at end of file diff --git a/arch/x86_64/src/cpu/initialization.cpp b/arch/x86_64/src/cpu/initialization.cpp deleted file mode 100644 index 1be9c82..0000000 --- a/arch/x86_64/src/cpu/initialization.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include - -namespace arch::cpu -{ - - namespace - { - constexpr auto gdt_null_descriptor = segment_descriptor{}; - - constexpr auto gdt_kernel_code_descriptor = segment_descriptor{ - .limit_low = 0xffff, - .base_low = 0, - .accessed = false, - .read_write = false, - .direction_or_conforming = false, - .executable = true, - .type = segment_type::code_or_data, - .privilege_level = 0, - .present = true, - .limit_high = 0xf, - .long_mode = true, - .protected_mode = false, - .granularity = segment_granularity::page, - .base_high = 0, - }; - - constexpr auto gdt_kernel_data_descriptor = segment_descriptor{ - .limit_low = 0xffff, - .base_low = 0, - .accessed = false, - .read_write = true, - .direction_or_conforming = false, - .executable = false, - .type = segment_type::code_or_data, - .privilege_level = 0, - .present = true, - .limit_high = 0xf, - .long_mode = false, - .protected_mode = true, - .granularity = segment_granularity::page, - .base_high = 0, - }; - - constexpr auto gdt_user_code_descriptor = segment_descriptor{ - .limit_low = 0xffff, - .base_low = 0, - .accessed = false, - .read_write = false, - .direction_or_conforming = false, - .executable = true, - .type = segment_type::code_or_data, - .privilege_level = 3, - .present = true, - .limit_high = 0xf, - .long_mode = true, - .protected_mode = false, - .granularity = segment_granularity::page, - .base_high = 0, - }; - - constexpr auto gdt_user_data_descriptor = segment_descriptor{ - .limit_low = 0xffff, - .base_low = 0, - .accessed = false, - .read_write = true, - .direction_or_conforming = false, - .executable = false, - .type = segment_type::code_or_data, - .privilege_level = 3, - .present = true, - .limit_high = 0xf, - .long_mode = false, - .protected_mode = false, - .granularity = segment_granularity::page, - .base_high = 0, - }; - - constexpr auto make_tss_descriptor(task_state_segment const * tss_ptr) -> system_segment_descriptor - { - auto const address = std::bit_cast(tss_ptr); - auto const limit = sizeof(task_state_segment) - 1; - - return system_segment_descriptor{ - { - .limit_low = limit & 0xffff, // NOLINT(readability-magic-numbers) - .base_low = address & 0xffffff, // NOLINT(readability-magic-numbers) - .accessed = false, - .read_write = false, - .direction_or_conforming = false, - .executable = false, - .type = segment_type::system, - .privilege_level = 0, - .present = true, - .limit_high = (limit >> 16) & 0xf, // NOLINT(readability-magic-numbers) - .long_mode = false, - .protected_mode = false, - .granularity = segment_granularity::byte, - .base_high = (address >> 24) & 0xff, // NOLINT(readability-magic-numbers) - }, - (address >> 32) & 0xffff'ffff, // NOLINT(readability-magic-numbers) - }; - } - } // namespace - - auto initialize_descriptors() -> void - { - auto static tss = task_state_segment{}; - auto static tss_descriptor = make_tss_descriptor(&tss); - - auto static gdt = global_descriptor_table{ - gdt_null_descriptor, gdt_kernel_code_descriptor, gdt_kernel_data_descriptor, - gdt_user_code_descriptor, gdt_user_data_descriptor, tss_descriptor, - }; - - kstd::println("[x86_64:SYS] Reloading Global Descriptor Table."); - gdt.load(1, 2); - - kstd::println("[x86_64:SYS] Initializing Interrupt Descriptor Table."); - auto static idt = interrupt_descriptor_table{}; - idt.load(); - } - - auto initialize_legacy_interrupts() -> void - { - constexpr auto pic_init_command = std::uint8_t{0x11}; - constexpr auto pic_master_offset = std::uint8_t{0x20}; - constexpr auto pic_slave_offset = std::uint8_t{0x28}; - constexpr auto pic_cascade_address = std::uint8_t{0x04}; - constexpr auto pic_cascade_slave_identity = std::uint8_t{0x02}; - constexpr auto pic_use_8086_mode = std::uint8_t{0x01}; - constexpr auto pic_master_mask = std::uint8_t{0x00}; - constexpr auto pic_slave_mask = std::uint8_t{0x00}; - - pic_master_control_port::write(pic_init_command); - pic_slave_control_port::write(pic_init_command); - - pic_master_data_port::write(pic_master_offset); - pic_slave_data_port::write(pic_slave_offset); - - pic_master_data_port::write(pic_cascade_address); - pic_slave_data_port::write(pic_cascade_slave_identity); - - pic_master_data_port::write(pic_use_8086_mode); - pic_slave_data_port::write(pic_use_8086_mode); - - pic_master_data_port::write(pic_master_mask); - pic_slave_data_port::write(pic_slave_mask); - - pic_master_data_port::write(pic_master_mask); - pic_slave_data_port::write(pic_slave_mask); - } - -} // namespace arch::cpu diff --git a/arch/x86_64/src/cpu/interrupt_stubs.S b/arch/x86_64/src/cpu/interrupt_stubs.S deleted file mode 100644 index e59bdd2..0000000 --- a/arch/x86_64/src/cpu/interrupt_stubs.S +++ /dev/null @@ -1,112 +0,0 @@ -.altmacro - -.macro ISR_WITHOUT_ERROR_CODE vector - .global isr\vector - isr\vector: - pushq $0 - pushq $\vector - jmp common_interrupt_handler -.endm - -.macro ISR_WITH_ERROR_CODE vector - .global isr\vector - isr\vector: - pushq $\vector - jmp common_interrupt_handler -.endm - -.macro ISR_TABLE_ENTRY vector - .quad isr\vector -.endm - -.section .rodata -.global isr_stub_table -.align 16 - -isr_stub_table: -.set i, 0 -.rept 256 - ISR_TABLE_ENTRY %i - .set i, i + 1 -.endr - - -.section .text - -common_interrupt_handler: - push %rax - push %rbx - push %rcx - push %rdx - push %rbp - push %rsi - push %rdi - push %r8 - push %r9 - push %r10 - push %r11 - push %r12 - push %r13 - push %r14 - push %r15 - - mov %rsp, %rdi - call interrupt_dispatch - - pop %r15 - pop %r14 - pop %r13 - pop %r12 - pop %r11 - pop %r10 - pop %r9 - pop %r8 - pop %rdi - pop %rsi - pop %rbp - pop %rdx - pop %rcx - pop %rbx - pop %rax - - add $16, %rsp - iretq - -ISR_WITHOUT_ERROR_CODE 0 -ISR_WITHOUT_ERROR_CODE 1 -ISR_WITHOUT_ERROR_CODE 2 -ISR_WITHOUT_ERROR_CODE 3 -ISR_WITHOUT_ERROR_CODE 4 -ISR_WITHOUT_ERROR_CODE 5 -ISR_WITHOUT_ERROR_CODE 6 -ISR_WITHOUT_ERROR_CODE 7 -ISR_WITH_ERROR_CODE 8 -ISR_WITHOUT_ERROR_CODE 9 -ISR_WITH_ERROR_CODE 10 -ISR_WITH_ERROR_CODE 11 -ISR_WITH_ERROR_CODE 12 -ISR_WITH_ERROR_CODE 13 -ISR_WITH_ERROR_CODE 14 -ISR_WITHOUT_ERROR_CODE 15 -ISR_WITHOUT_ERROR_CODE 16 -ISR_WITH_ERROR_CODE 17 -ISR_WITHOUT_ERROR_CODE 18 -ISR_WITHOUT_ERROR_CODE 19 -ISR_WITHOUT_ERROR_CODE 20 -ISR_WITH_ERROR_CODE 21 -ISR_WITHOUT_ERROR_CODE 22 -ISR_WITHOUT_ERROR_CODE 23 -ISR_WITHOUT_ERROR_CODE 24 -ISR_WITHOUT_ERROR_CODE 25 -ISR_WITHOUT_ERROR_CODE 26 -ISR_WITHOUT_ERROR_CODE 27 -ISR_WITHOUT_ERROR_CODE 28 -ISR_WITH_ERROR_CODE 29 -ISR_WITH_ERROR_CODE 30 -ISR_WITHOUT_ERROR_CODE 31 - -.set i, 32 -.rept 256 - 32 - ISR_WITHOUT_ERROR_CODE %i - .set i, i + 1 -.endr diff --git a/arch/x86_64/src/cpu/interrupts.cpp b/arch/x86_64/src/cpu/interrupts.cpp deleted file mode 100644 index f40422f..0000000 --- a/arch/x86_64/src/cpu/interrupts.cpp +++ /dev/null @@ -1,203 +0,0 @@ -#include - -#include -#include - -#include -#include -#include - -#include - -#include - -namespace arch::cpu -{ - - namespace - { - enum struct exception - { - divide_error, - debug_exception, - non_maskable_interrupt, - breakpoint, - overflow, - bound_range_exceeded, - invalid_opcode, - device_not_available, - double_fault, - coprocessor_segment_overrun, - invalid_tss, - segment_not_present, - stack_segment_fault, - general_protection_fault, - page_fault, - x87_fpu_floating_point_error = 16, - alignment_check, - machine_check, - simd_floating_point_error, - virtualization_exception, - control_protection_exception, - hypervisor_injection_exception = 28, - vmm_communication_exception, - security_exception, - }; - - constexpr auto number_of_exception_vectors = 32u; - - constexpr auto pic_master_irq_start = 0x20; - constexpr auto pic_master_irq_end = pic_master_irq_start + 8; - constexpr auto pic_slave_irq_start = pic_master_irq_end; - - constexpr auto to_exception_type(exception e) - { - switch (e) - { - case exception::divide_error: - case exception::x87_fpu_floating_point_error: - case exception::simd_floating_point_error: - return kapi::cpu::exception::type::arithmetic_error; - case exception::breakpoint: - return kapi::cpu::exception::type::breakpoint; - case exception::invalid_opcode: - return kapi::cpu::exception::type::illegal_instruction; - case exception::stack_segment_fault: - return kapi::cpu::exception::type::memory_access_fault; - case exception::general_protection_fault: - return kapi::cpu::exception::type::privilege_violation; - case exception::page_fault: - return kapi::cpu::exception::type::page_fault; - case exception::alignment_check: - return kapi::cpu::exception::type::alignment_fault; - default: - return kapi::cpu::exception::type::unknown; - } - } - - constexpr auto has_error_code(exception e) - { - switch (e) - { - case exception::double_fault: - case exception::invalid_tss: - case exception::segment_not_present: - case exception::stack_segment_fault: - case exception::general_protection_fault: - case exception::page_fault: - case exception::alignment_check: - case exception::control_protection_exception: - case exception::security_exception: - return true; - default: - return false; - } - } - - auto dispatch_exception(interrupt_frame * frame) -> bool - { - auto type = to_exception_type(static_cast(frame->interrupt.number)); - auto fault_address = kapi::memory::linear_address{}; - auto instruction_pointer = frame->cpu_saved.rip; - - switch (type) - { - case kapi::cpu::exception::type::page_fault: - { - asm volatile("mov %%cr2, %0" : "=r"(fault_address)); - auto present = (frame->interrupt.error_code & 0x1) != 0; - auto write = (frame->interrupt.error_code & 0x2) != 0; - auto user = (frame->interrupt.error_code & 0x4) != 0; - - return kapi::cpu::dispatch({type, instruction_pointer, fault_address, present, write, user}); - } - default: - return kapi::cpu::dispatch({type, instruction_pointer}); - } - } - - auto acknowledge_pic_interrupt(interrupt_frame * frame) -> void - { - if (frame->interrupt.number >= pic_slave_irq_start) - { - pic_slave_control_port::write(pic_end_of_interrupt); - } - pic_master_control_port::write(pic_end_of_interrupt); - } - } // namespace - - extern "C" - { - extern std::uintptr_t const isr_stub_table[256]; - - auto interrupt_dispatch(interrupt_frame * frame) -> void - { - auto [number, code] = frame->interrupt; - - if (number < number_of_exception_vectors) - { - if (!dispatch_exception(frame)) - { - if (has_error_code(static_cast(number))) - { - kstd::println(kstd::print_sink::stderr, - "[x86_64:CPU] Unhandled exception number {:#04x} received with code {:#04x}", number, code); - } - else - { - kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled exception number {:#04x} received", number); - } - } - } - else - { - auto irq_number = number - number_of_exception_vectors; - - if (kapi::interrupts::dispatch(irq_number) == kapi::interrupts::status::unhandled) - { - kstd::println(kstd::print_sink::stderr, "[x86_64:CPU] Unhandled interrupt {:#04x} (IRQ{})", number, - irq_number); - } - - acknowledge_pic_interrupt(frame); - } - } - } - - interrupt_descriptor_table::interrupt_descriptor_table() noexcept - { - for (auto i = 0uz; i < 256; ++i) - { - m_descriptors[i] = gate_descriptor{ - .offset_low = static_cast(isr_stub_table[i] & 0xffff), // NOLINT(readability-magic-numbers) - .m_code_segment = segment_selector{0, false, 1}, - .interrupt_stack_table_selector = 0, - .gate_type = (i < 32 && i != 2) ? gate_type::trap_gate : gate_type::interrupt_gate, - .descriptor_privilege_level = 0, - .present = true, - .offset_middle = - static_cast((isr_stub_table[i] >> 16) & 0xffff), // NOLINT(readability-magic-numbers) - .offset_high = - static_cast((isr_stub_table[i] >> 32) & 0xffff'ffff), // NOLINT(readability-magic-numbers) - }; - } - } - - auto interrupt_descriptor_table::load() const -> void - { - interrupt_descriptor_table_register{.limit = sizeof(m_descriptors) - 1, .base = m_descriptors.data()}.load(); - } - - auto interrupt_descriptor_table_register::load() const -> void - { - asm volatile("lidt %0" : : "m"(*this)); - } - - auto interrupt_descriptor_table_register::read() -> interrupt_descriptor_table_register - { - interrupt_descriptor_table_register idtr{}; - asm volatile("sidt %0" : : "m"(idtr)); - return idtr; - } - -} // namespace arch::cpu \ No newline at end of file diff --git a/arch/x86_64/src/debug/qemu_output.cpp b/arch/x86_64/src/debug/qemu_output.cpp deleted file mode 100644 index 71acede..0000000 --- a/arch/x86_64/src/debug/qemu_output.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include - -#include - -#include -#include - -namespace arch::debug -{ - - qemu_output::qemu_output(output_device & lower) - : m_lower{lower} - , m_present{port::read() == port::address} - {} - - auto qemu_output::write(kapi::cio::output_stream stream, std::string_view text) -> void - { - if (m_present) - { - std::ranges::for_each(text, port::write); - } - m_lower.write(stream, text); - } - -} // namespace arch::debug diff --git a/arch/x86_64/src/devices/init.cpp b/arch/x86_64/src/devices/init.cpp deleted file mode 100644 index c30e8cf..0000000 --- a/arch/x86_64/src/devices/init.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include - -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include - -#include -#include - -namespace arch::devices -{ - - namespace - { - constexpr auto pit_frequency_in_hz = std::uint32_t{100u}; - - auto get_acpi_root_pointer() -> kstd::observer_ptr<::acpi::rsdp const> - { - auto const & mbi = kapi::boot::bootstrap_information.mbi; - auto system_description_pointer = static_cast<::acpi::rsdp const *>(nullptr); - - if (auto const & xsdp = mbi->maybe_acpi_xsdp()) - { - auto data = xsdp->pointer().data(); - - system_description_pointer = reinterpret_cast<::acpi::xsdp const *>(data); - } - else if (auto const & rsdp = mbi->maybe_acpi_rsdp()) - { - auto data = rsdp->pointer().data(); - system_description_pointer = reinterpret_cast<::acpi::rsdp const *>(data); - } - - return kstd::make_observer(system_description_pointer); - } - } // namespace - - auto init_acpi_devices() -> void - { - auto acpi_root_pointer = get_acpi_root_pointer(); - if (acpi_root_pointer && acpi_root_pointer->validate()) - { - if (kapi::acpi::init(*acpi_root_pointer)) - { - kstd::println("[x86_64:DEV] ACPI subsystem initialized."); - } - } - - kapi::cpu::discover_topology(); - } - - auto init_legacy_devices() -> void - { - kstd::println("[x86_64:DEV] Initializing ISA bus..."); - - auto isa_major_number = kapi::devices::allocate_major_number(); - auto isa_bus = kstd::make_unique(isa_major_number); - - auto pit_major_number = kapi::devices::allocate_major_number(); - auto pit = kstd::make_unique(pit_major_number, pit_frequency_in_hz); - isa_bus->add_child(std::move(pit)); - - auto & root_bus = kapi::devices::get_root_bus(); - root_bus.add_child(std::move(isa_bus)); - } - -} // namespace arch::devices diff --git a/arch/x86_64/src/devices/legacy_pit.cpp b/arch/x86_64/src/devices/legacy_pit.cpp deleted file mode 100644 index d542d47..0000000 --- a/arch/x86_64/src/devices/legacy_pit.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include - -#include - -#include -#include -#include - -#include -#include - -namespace arch::devices -{ - - namespace - { - using command_port = io::port<0x43, std::uint8_t, io::port_write>; - using channel_0_port = io::port<0x40, std::uint8_t, io::port_write>; - using channel_1_port = io::port<0x41, std::uint8_t, io::port_write>; - using channel_2_port = io::port<0x42, std::uint8_t, io::port_write>; - - constexpr auto base_frequency = 1'193'182u; - constexpr auto square_wave_mode = 0x36; - } // namespace - - legacy_pit::legacy_pit(std::size_t major, std::uint32_t frequency_in_hz) - : kapi::devices::device{major, 0, "legacy_pit"} - , m_irq_number{0} - , m_frequency_in_hz{frequency_in_hz} - {} - - auto legacy_pit::init() -> bool - { - auto divisor = static_cast(base_frequency / m_frequency_in_hz); - - kapi::interrupts::register_handler(m_irq_number, *this); - - command_port::write(square_wave_mode); - io::wait(); - channel_0_port::write(divisor & 0xff); - io::wait(); - channel_0_port::write(divisor >> 8 & 0xff); - io::wait(); - - return true; - } - - auto legacy_pit::handle_interrupt(std::uint32_t irq_number) -> kapi::interrupts::status - { - if (irq_number != m_irq_number) - { - return kapi::interrupts::status::unhandled; - } - - ++m_ticks; - - return kapi::interrupts::status::handled; - } - -} // namespace arch::devices \ No newline at end of file diff --git a/arch/x86_64/src/devices/local_apic.cpp b/arch/x86_64/src/devices/local_apic.cpp deleted file mode 100644 index 660921b..0000000 --- a/arch/x86_64/src/devices/local_apic.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include - -#include -#include - -#include - -#include -#include -#include - -namespace arch::devices -{ - - namespace - { - constexpr auto lapic_enable_bit = 0x100u; - constexpr auto spurious_interrupt_vector = 0xFFu; - - constexpr auto offset_of_version = 0u; - constexpr auto offset_of_max_lvt_entry = 16u; - constexpr auto offset_of_eoi_suppression = 24u; - - } // namespace - - enum struct local_apic::registers : std::ptrdiff_t - { - id = 0x020, - version = 0x030, - task_priority = 0x080, - arbitration_priority = 0x090, - processor_priority = 0x0a0, - eoi = 0x0b0, - remote_read = 0x0c0, - logical_destination = 0x0d0, - destination_format = 0x0e0, - spurious_interrupt_vector = 0x0f0, - in_service_0 = 0x100, - in_service_1 = 0x110, - in_service_2 = 0x120, - in_service_3 = 0x130, - in_service_4 = 0x140, - in_service_5 = 0x150, - in_service_6 = 0x160, - in_service_7 = 0x170, - trigger_mode_0 = 0x180, - trigger_mode_1 = 0x190, - trigger_mode_2 = 0x1a0, - trigger_mode_3 = 0x1b0, - trigger_mode_4 = 0x1c0, - trigger_mode_5 = 0x1d0, - trigger_mode_6 = 0x1e0, - trigger_mode_7 = 0x1f0, - interrupt_request_0 = 0x200, - interrupt_request_1 = 0x210, - interrupt_request_2 = 0x220, - interrupt_request_3 = 0x230, - interrupt_request_4 = 0x240, - interrupt_request_5 = 0x250, - interrupt_request_6 = 0x260, - interrupt_request_7 = 0x270, - error_status = 0x280, - lvt_corrected_machine_check_interrupt = 0x2f0, - interrupt_command_0 = 0x300, - interrupt_command_1 = 0x310, - lvt_timer = 0x320, - lvt_thermal_sensors = 0x330, - lvt_performance_monitoring_counters = 0x340, - lvt_local_interrupt_0 = 0x350, - lvt_local_interrupt_1 = 0x360, - lvt_error = 0x370, - initial_count = 0x380, - current_count = 0x390, - divide_configuration = 0x3e0, - }; - - local_apic::local_apic(std::size_t major, std::size_t minor, std::uint64_t hardware_id, - kapi::memory::physical_address base, bool is_bsp) - : kapi::devices::device{major, minor, "lapic"} - , m_hardware_id{hardware_id} - , m_base{base} - , m_is_bsp{is_bsp} - {} - - auto local_apic::init() -> bool - { - auto static shared_virtual_base = kapi::memory::allocate_mmio_region(1); - auto static is_mapped = false; - - if (!is_mapped) - { - if (!kapi::memory::map_mmio_region(shared_virtual_base, m_base, kapi::memory::page_mapper::flags::writable)) - { - kstd::println("[x86_64:DEV] LAPIC {} MMIO mapping failed!", m_hardware_id); - return false; - } - is_mapped = true; - } - - m_mapped_region = shared_virtual_base; - - if (m_is_bsp) - { - auto raw_version = read_register(registers::version); - m_version = (raw_version >> offset_of_version) & 0xff; - m_highest_lvt_entry_index = (raw_version >> offset_of_max_lvt_entry) & 0xff; - m_supports_eoi_broadcast_suppression = (raw_version >> offset_of_eoi_suppression) & 0x1; - - write_register(registers::spurious_interrupt_vector, lapic_enable_bit | spurious_interrupt_vector); - - kstd::println("[x86_64:DEV] LAPIC initialized. version: {#x} | max_lvt_entry: {} | eoi_suppression: {:s}", - m_version, m_highest_lvt_entry_index, m_supports_eoi_broadcast_suppression); - } - else - { - kstd::println("[x86_64:DEV] LAPIC {} is not on the BSP, deferring initialization.", m_hardware_id); - } - - return true; - } - - auto local_apic::read_register(registers id) const -> std::uint32_t - { - auto reg = static_cast(m_mapped_region.first + std::to_underlying(id)); - return *reg; - } - - auto local_apic::write_register(registers id, std::uint32_t value) -> void - { - auto reg = static_cast(m_mapped_region.first + std::to_underlying(id)); - *reg = value; - } - -} // namespace arch::devices diff --git a/arch/x86_64/src/memory/higher_half_mapper.cpp b/arch/x86_64/src/memory/higher_half_mapper.cpp deleted file mode 100644 index 75adb3c..0000000 --- a/arch/x86_64/src/memory/higher_half_mapper.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace arch::memory -{ - - higher_half_mapper::higher_half_mapper(page_table * root) - : m_root{root} - {} - - auto higher_half_mapper::map(kapi::memory::page page, kapi::memory::frame frame, flags flags) -> std::byte * - { - auto table = get_or_create_page_table(page); - if (!table) - { - return nullptr; - } - - auto const index = pml_index(1, page); - auto & entry = (*table)[index]; - - if (entry.present()) - { - kapi::system::panic("[x86_64:MEM] Tried to map a page that is already mapped!"); - } - - entry.frame(frame, to_table_flags(flags) | page_table::entry::flags::present); - - return static_cast(page.start_address()); - } - - auto higher_half_mapper::unmap(kapi::memory::page page) -> void - { - if (!try_unmap(page)) - { - kapi::system::panic("[x86_64:MEM] Tried to unmap a page that is not mapped!"); - } - } - - auto higher_half_mapper::try_unmap(kapi::memory::page page) noexcept -> bool - { - auto table_path = std::array, 4>{}; - table_path[0] = std::pair{m_root, pml_index(4, page)}; - - for (auto level = 4uz; level > 1uz; --level) - { - auto [table, index] = table_path[4 - level]; - auto & entry = (*table)[index]; - - if (!entry.present()) - { - return false; - } - - auto next_table = to_higher_half_pointer(entry.frame()->start_address()); - auto next_index = pml_index(4 - level - 1, page); - table_path[4 - level - 1] = std::pair{next_table, next_index}; - } - - std::ranges::for_each(std::views::reverse(table_path), [previous_was_empty = true](auto & step) mutable { - auto [table, index] = step; - auto & entry = (*table)[index]; - auto frame = entry.frame(); - - if (previous_was_empty) - { - entry.clear(); - previous_was_empty = table->empty(); - kapi::memory::get_frame_allocator().release(*frame); - } - }); - - return true; - } - - auto higher_half_mapper::get_or_create_page_table(kapi::memory::page page) noexcept -> page_table * - { - auto table = m_root; - - for (auto level = 4uz; level > 1uz; --level) - { - auto index = pml_index(level, page); - auto & entry = (*table)[index]; - - if (!entry.present()) - { - auto table_frame = kapi::memory::allocate_frame(); - if (!table_frame) - { - return nullptr; - } - - auto new_table = to_higher_half_pointer(table_frame->start_address()); - std::construct_at(new_table); - - auto const flags = page_table::entry::flags::present | page_table::entry::flags::writable | - page_table::entry::flags::user_accessible; - entry.frame(*table_frame, flags); - } - - table = to_higher_half_pointer(entry.frame()->start_address()); - } - - return table; - } - -} // namespace arch::memory \ No newline at end of file diff --git a/arch/x86_64/src/memory/kernel_mapper.cpp b/arch/x86_64/src/memory/kernel_mapper.cpp deleted file mode 100644 index 74272a0..0000000 --- a/arch/x86_64/src/memory/kernel_mapper.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include - -#include - -#include -#include - -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -using namespace std::string_view_literals; -using namespace kstd::units_literals; - -namespace arch::memory -{ - - namespace - { - constexpr auto static ignored_section_prefixes = std::array{ - ".boot_"sv, - }; - - constexpr auto static user_accessible_prefixes = std::array{ - ".user"sv, - ".stl"sv, - }; - - } // namespace - - kernel_mapper::kernel_mapper(multiboot2::information_view const * mbi) - : m_mbi{std::move(mbi)} - , m_kernel_load_base{std::bit_cast(&arch::boot::TEACHOS_VMA)} - {} - - auto kernel_mapper::remap_kernel(kapi::memory::page_mapper & mapper) -> void - { - auto elf_information = m_mbi->maybe_elf_symbols(); - if (!elf_information) - { - kapi::system::panic("[x86_64:MEM] ELF section information is not available."); - } - - auto sections = *elf_information; - auto allocated_sections = - std::views::all(sections) | std::views::filter(&elf::section_header::allocated) | - std::views::filter([&](auto const & section) -> auto { - auto name = sections.name(section); - return !std::ranges::any_of(ignored_section_prefixes, - [&](auto const & prefix) -> auto { return name.starts_with(prefix); }); - }); - - if (allocated_sections.empty()) - { - kapi::system::panic("[x86_64:MEM] No allocated ELF sections were found."); - } - - std::ranges::for_each(allocated_sections, - [&](auto const & section) -> auto { map_section(section, sections.name(section), mapper); }); - } - - auto kernel_mapper::map_section(section_header_type const & section, std::string_view name, - kapi::memory::page_mapper & mapper) -> void - { - auto number_of_pages = - (kstd::units::bytes{section.size} + (kapi::memory::page::size - 1_B)) / kapi::memory::page::size; - auto linear_start_address = kapi::memory::linear_address{section.virtual_load_address}; - auto physical_start_address = kapi::memory::physical_address{section.virtual_load_address & ~m_kernel_load_base}; - - kstd::println("[x86_64:MEM] mapping {}" - "\n {} bytes -> page count: {}" - "\n {} @ {}", - name, section.size, number_of_pages, linear_start_address, physical_start_address); - - auto first_page = kapi::memory::page::containing(linear_start_address); - auto first_frame = kapi::memory::frame::containing(physical_start_address); - - auto page_flags = kapi::memory::page_mapper::flags::empty; - - if (section.writable()) - { - page_flags |= kapi::memory::page_mapper::flags::writable; - } - - if (section.executable()) - { - page_flags |= kapi::memory::page_mapper::flags::executable; - } - - auto is_prefix_of_name = [=](auto prefix) -> bool { - return name.starts_with(prefix); - }; - - if (!std::ranges::any_of(user_accessible_prefixes, is_prefix_of_name)) - { - page_flags |= kapi::memory::page_mapper::flags::supervisor_only; - } - - for (auto i = 0uz; i < number_of_pages; ++i) - { - mapper.map(first_page + i, first_frame + i, page_flags); - } - } - -} // namespace arch::memory \ No newline at end of file diff --git a/arch/x86_64/src/memory/mmu.cpp b/arch/x86_64/src/memory/mmu.cpp deleted file mode 100644 index 2b53fa4..0000000 --- a/arch/x86_64/src/memory/mmu.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include - -#include - -#include - -namespace arch::memory -{ - auto tlb_flush(kapi::memory::linear_address address) -> void - { - asm volatile("invlpg (%[input])" : /* no output from call */ : [input] "r"(address) : "memory"); - } - - auto tlb_flush_all() -> void - { - auto paging_root = cpu::cr3::read(); - cpu::cr3::write(paging_root); - } -} // namespace arch::memory diff --git a/arch/x86_64/src/memory/page_table.cpp b/arch/x86_64/src/memory/page_table.cpp deleted file mode 100644 index 2180420..0000000 --- a/arch/x86_64/src/memory/page_table.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include - -#include - -#include -#include -#include -#include -#include -#include - -namespace arch::memory -{ - - auto page_table::entry::clear() noexcept -> void - { - m_raw = 0; - } - - auto page_table::entry::present() const noexcept -> bool - { - return (all_flags() & flags::present) != flags::empty; - } - - auto page_table::entry::huge() const noexcept -> bool - { - return (all_flags() & flags::huge_page) != flags::empty; - } - - auto page_table::entry::all_flags() const noexcept -> flags - { - return std::bit_cast(m_raw & ~frame_number_mask); - } - - auto page_table::entry::all_flags(flags flags) noexcept -> void - { - m_raw = (m_raw & ~frame_number_mask) | std::to_underlying(flags); - } - - auto page_table::entry::operator|=(flags rhs) noexcept -> page_table::entry & - { - auto raw_flags = std::to_underlying(rhs) & ~frame_number_mask; - m_raw |= raw_flags; - return *this; - } - - auto page_table::entry::frame() const noexcept -> std::optional - { - if (present()) - { - return kapi::memory::frame::containing(kapi::memory::physical_address{m_raw & frame_number_mask}); - } - return std::nullopt; - } - - auto page_table::entry::frame(kapi::memory::frame frame, flags flags) noexcept -> void - { - m_raw = (frame.start_address().raw() | static_cast(flags)); - }; - - auto page_table::operator[](std::size_t index) -> entry & - { - return m_entries.at(index); - } - - auto page_table::operator[](std::size_t index) const -> entry const & - { - return m_entries.at(index); - } - - auto page_table::clear() noexcept -> void - { - std::ranges::for_each(m_entries, &page_table::entry::clear); - } - - auto page_table::empty() const noexcept -> bool - { - return std::ranges::all_of(m_entries, - [](auto const & entry) -> auto { return entry.all_flags() == entry::flags::empty; }); - } - -} // namespace arch::memory diff --git a/arch/x86_64/src/memory/region_allocator.cpp b/arch/x86_64/src/memory/region_allocator.cpp deleted file mode 100644 index 4086a10..0000000 --- a/arch/x86_64/src/memory/region_allocator.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -namespace arch::memory -{ - namespace - { - constexpr auto last_frame(multiboot2::memory_map::region const & region) - { - return kapi::memory::frame::containing(kapi::memory::physical_address{region.base + region.size_in_B - 1}); - } - - constexpr auto falls_within(kapi::memory::frame const & candidate, kapi::memory::frame const & start, - kapi::memory::frame const & end) - { - return candidate >= start && candidate <= end; - } - } // namespace - - region_allocator::region_allocator(memory_information const & mem_info) - : m_next_frame{} - , m_current_region{} - , m_memory_map{mem_info.memory_map} - , m_kernel_start{kapi::memory::frame::containing(mem_info.image_range.first)} - , m_kernel_end{kapi::memory::frame::containing(mem_info.image_range.second)} - , m_multiboot_start{kapi::memory::frame::containing(mem_info.mbi_range.first)} - , m_multiboot_end{kapi::memory::frame::containing(mem_info.mbi_range.second)} - , m_multiboot_information{mem_info.mbi} - { - choose_next_region(); - } - - auto region_allocator::choose_next_region() -> void - { - m_current_region.reset(); - - auto remaining_regions = - m_memory_map | // - std::views::filter(&multiboot2::memory_map::region::available) | - std::views::filter([this](auto const & region) -> bool { return last_frame(region) >= m_next_frame; }); - - auto lowest_region = - std::ranges::min_element(remaining_regions, [](auto lhs, auto rhs) -> bool { return lhs.base < rhs.base; }); - - if (lowest_region == remaining_regions.end()) - { - return; - } - - m_current_region = *lowest_region; - if (auto start_of_region = kapi::memory::frame::containing(kapi::memory::physical_address{m_current_region->base}); - start_of_region > m_next_frame) - { - m_next_frame = start_of_region; - } - } - - auto region_allocator::find_next_frame() -> std::optional - { - if (!m_current_region || m_next_frame > last_frame(*m_current_region)) - { - choose_next_region(); - if (!m_current_region) - { - return std::nullopt; - } - } - - auto advanced = true; - while (advanced) - { - advanced = false; - - if (falls_within(m_next_frame, m_kernel_start, m_kernel_end)) - { - m_next_frame = m_kernel_end + 1; - advanced = true; - continue; - } - - if (falls_within(m_next_frame, m_multiboot_start, m_multiboot_end)) - { - m_next_frame = m_multiboot_end + 1; - advanced = true; - continue; - } - - if (m_multiboot_information) - { - for (auto const & module : m_multiboot_information->modules()) - { - auto module_start = kapi::memory::frame::containing(kapi::memory::physical_address{module.start_address}); - auto module_end = kapi::memory::frame::containing(kapi::memory::physical_address{module.end_address}); - - if (falls_within(m_next_frame, module_start, module_end)) - { - m_next_frame = module_end + 1; - advanced = true; - break; - } - } - } - } - - if (m_next_frame > last_frame(*m_current_region)) - { - choose_next_region(); - } - - return m_current_region.transform([this](auto) -> auto { return m_next_frame; }); - } - - auto region_allocator::allocate_many(std::size_t count) noexcept - -> std::optional> - { - while (m_current_region) - { - auto result = find_next_frame(); - - if (result) - { - auto region_end = last_frame(*m_current_region); - if ((region_end.number() - result->number()) >= count) - { - m_next_frame = m_next_frame + count; - return std::make_pair(*result, count); - } - else - { - m_next_frame = region_end + 1; - choose_next_region(); - } - } - } - - return std::nullopt; - } - - auto region_allocator::mark_used(kapi::memory::frame frame) -> void - { - if (frame < m_next_frame) - { - m_next_frame = frame; - find_next_frame(); - } - } - - auto region_allocator::release_many(std::pair) -> void {} - - auto region_allocator::next_free_frame() noexcept -> std::optional - { - return find_next_frame(); - } - -} // namespace arch::memory diff --git a/arch/x86_64/src/vga/text/buffer.cpp b/arch/x86_64/src/vga/text/buffer.cpp deleted file mode 100644 index 498b9a3..0000000 --- a/arch/x86_64/src/vga/text/buffer.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include - -#include - -#include -#include -#include -#include -#include -#include - -namespace arch::vga::text -{ - buffer::buffer(std::size_t width, std::size_t height, cell * start, std::size_t position) - : m_width{width} - , m_height{height} - , m_buffer{start, m_width * m_height} - , m_position{position} - {} - - auto buffer::clear() -> void - { - m_position = 0; - std::ranges::fill(m_buffer, std::pair{'\0', static_cast(0x00)}); - } - - auto buffer::write(std::string_view code_points, attribute attribute) -> void - { - std::ranges::for_each(code_points, [&](auto code_point) -> void { write(code_point, attribute); }); - } - - auto buffer::write(char code_point, attribute attribute) -> void - { - if (m_position + 1 > m_height * m_width) - { - scroll(); - } - - if (!handle_special_code_point(code_point, attribute)) - { - do_write(code_point, attribute); - } - }; - - auto buffer::newline() -> void - { - auto free_glyphs_in_line = m_width - column(); - m_position += free_glyphs_in_line; - } - - auto buffer::scroll(std::size_t nof_lines) -> void - { - auto scroll_count = std::min(nof_lines, m_height); - - auto scroll_start = m_buffer.begin() + (scroll_count * m_width); - std::ranges::move(scroll_start, m_buffer.end(), m_buffer.begin()); - - auto clear_start = m_buffer.begin() + (m_height - scroll_count) * m_width; - std::ranges::fill(clear_start, m_buffer.end(), cell{}); - - m_position = (line() - scroll_count) * m_width; - } - - auto buffer::column() const noexcept -> std::ptrdiff_t - { - return m_position % m_width; - } - - auto buffer::line() const noexcept -> std::ptrdiff_t - { - return m_position / m_width; - } - auto buffer::handle_special_code_point(char code_point, attribute attribute) -> bool - { - switch (code_point) - { - case '\n': - newline(); - return true; - case '\r': - m_position -= column(); - return true; - case '\t': - do_write(" ", attribute); - return true; - default: - return false; - } - } - - auto buffer::do_write(std::string_view code_points, attribute attribute) -> void - { - std::ranges::for_each(code_points, [&](auto code_point) -> void { do_write(code_point, attribute); }); - } - - auto buffer::do_write(char code_point, attribute attribute) -> void - { - m_buffer[m_position++] = std::pair{code_point, std::bit_cast(attribute)}; - } - -} // namespace arch::vga::text diff --git a/arch/x86_64/src/vga/text/device.cpp b/arch/x86_64/src/vga/text/device.cpp deleted file mode 100644 index 8468358..0000000 --- a/arch/x86_64/src/vga/text/device.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -namespace arch::vga::text -{ - namespace - { - constexpr auto default_buffer_address = std::uintptr_t{0xb8000}; - constexpr auto default_buffer_width = 80z; - constexpr auto default_buffer_height = 25z; - - constexpr auto bit_cursor_enabled = 5U; - } // namespace - - device::device() - : m_buffer{ - default_buffer_width, default_buffer_height, - std::bit_cast(default_buffer_address + std::bit_cast(&boot::TEACHOS_VMA)), - kapi::boot::bootstrap_information.vga_buffer_index} - { - clear(); - } - - auto device::clear() -> void - { - m_buffer.clear(); - } - - auto device::cursor(bool enabled) -> void - { - auto cursor_disable_byte = std::byte{!enabled} << bit_cursor_enabled; - - crtc::address::write(crtc::registers::cursor_start); - crtc::data::write(crtc::data::read() | cursor_disable_byte); - } - - auto device::write(kapi::cio::output_stream stream, std::string_view text) -> void - { - auto attributes = [&] -> attribute { - switch (stream) - { - case kapi::cio::output_stream::stderr: - return red_on_black; - default: - return green_on_black; - } - }(); - m_buffer.write(text, attributes); - } - -} // namespace arch::vga::text -- cgit v1.2.3 From aa332432321a902514d61c3ca30e7d9e6396e95e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 29 Apr 2026 09:19:36 +0200 Subject: ide: support nesting in neovim --- .nvim.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.nvim.lua b/.nvim.lua index ad007bb..c9a3e0a 100644 --- a/.nvim.lua +++ b/.nvim.lua @@ -35,6 +35,15 @@ vim.filetype.add({ } }) +require("neo-tree").setup({ + nesting_rules = { + ['*.hpp'] = { + pattern = "(.*).hpp", + files = { "%1.cpp" } + } + } +}) + -- Debugging local dap = require("dap") local qemu_job_id = nil -- cgit v1.2.3 From c3d5a155025b445ab9213f131681afe9410b4e66 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 29 Apr 2026 09:29:20 +0200 Subject: x86_64: fix library references --- arch/x86_64/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86_64/CMakeLists.txt b/arch/x86_64/CMakeLists.txt index 5657010..20a48f9 100644 --- a/arch/x86_64/CMakeLists.txt +++ b/arch/x86_64/CMakeLists.txt @@ -10,8 +10,8 @@ target_include_directories("x86_64" PUBLIC ) target_link_libraries("x86_64" PUBLIC - "os::kapi" - "libs::multiboot2" + "kapi::lib" + "multiboot2::lib" ) target_sources("x86_64" PRIVATE -- cgit v1.2.3 From 4b71a936183070e43a6ca73c2975eae70742e124 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 29 Apr 2026 09:25:53 +0200 Subject: chore: migrate 'kapi' to p1204 layout --- kapi/CMakeLists.txt | 2 +- kapi/include/kapi/acpi.hpp | 40 ---- kapi/include/kapi/boot.hpp | 17 -- kapi/include/kapi/boot_module/boot_module.hpp | 23 -- .../kapi/boot_module/boot_module_registry.hpp | 107 --------- kapi/include/kapi/boot_modules.hpp | 31 --- kapi/include/kapi/cio.hpp | 48 ---- kapi/include/kapi/cio/output_device.hpp | 39 ---- kapi/include/kapi/cpu.hpp | 135 ----------- kapi/include/kapi/devices.hpp | 37 --- kapi/include/kapi/devices/bus.hpp | 63 ------ kapi/include/kapi/devices/cpu.hpp | 37 --- kapi/include/kapi/devices/device.hpp | 80 ------- kapi/include/kapi/devices/manager.hpp | 53 ----- kapi/include/kapi/filesystem.hpp | 73 ------ kapi/include/kapi/interrupts.hpp | 74 ------ kapi/include/kapi/memory.hpp | 131 ----------- kapi/include/kapi/memory/address.hpp | 252 --------------------- kapi/include/kapi/memory/chunk.hpp | 109 --------- kapi/include/kapi/memory/frame.hpp | 48 ---- kapi/include/kapi/memory/frame_allocator.hpp | 66 ------ kapi/include/kapi/memory/layout.hpp | 54 ----- kapi/include/kapi/memory/page.hpp | 38 ---- kapi/include/kapi/memory/page_mapper.hpp | 90 -------- kapi/include/kapi/system.hpp | 32 --- kapi/kapi/acpi.hpp | 40 ++++ kapi/kapi/boot.hpp | 17 ++ kapi/kapi/boot_module/boot_module.hpp | 23 ++ kapi/kapi/boot_module/boot_module_registry.hpp | 107 +++++++++ kapi/kapi/boot_modules.hpp | 31 +++ kapi/kapi/cio.hpp | 48 ++++ kapi/kapi/cio/output_device.hpp | 39 ++++ kapi/kapi/cpu.hpp | 135 +++++++++++ kapi/kapi/devices.hpp | 37 +++ kapi/kapi/devices/bus.hpp | 63 ++++++ kapi/kapi/devices/cpu.hpp | 37 +++ kapi/kapi/devices/device.hpp | 80 +++++++ kapi/kapi/devices/manager.hpp | 53 +++++ kapi/kapi/filesystem.hpp | 73 ++++++ kapi/kapi/interrupts.hpp | 74 ++++++ kapi/kapi/memory.hpp | 131 +++++++++++ kapi/kapi/memory/address.hpp | 252 +++++++++++++++++++++ kapi/kapi/memory/chunk.hpp | 109 +++++++++ kapi/kapi/memory/frame.hpp | 48 ++++ kapi/kapi/memory/frame_allocator.hpp | 66 ++++++ kapi/kapi/memory/layout.hpp | 54 +++++ kapi/kapi/memory/page.hpp | 38 ++++ kapi/kapi/memory/page_mapper.hpp | 90 ++++++++ kapi/kapi/system.hpp | 32 +++ 49 files changed, 1678 insertions(+), 1678 deletions(-) delete mode 100644 kapi/include/kapi/acpi.hpp delete mode 100644 kapi/include/kapi/boot.hpp delete mode 100644 kapi/include/kapi/boot_module/boot_module.hpp delete mode 100644 kapi/include/kapi/boot_module/boot_module_registry.hpp delete mode 100644 kapi/include/kapi/boot_modules.hpp delete mode 100644 kapi/include/kapi/cio.hpp delete mode 100644 kapi/include/kapi/cio/output_device.hpp delete mode 100644 kapi/include/kapi/cpu.hpp delete mode 100644 kapi/include/kapi/devices.hpp delete mode 100644 kapi/include/kapi/devices/bus.hpp delete mode 100644 kapi/include/kapi/devices/cpu.hpp delete mode 100644 kapi/include/kapi/devices/device.hpp delete mode 100644 kapi/include/kapi/devices/manager.hpp delete mode 100644 kapi/include/kapi/filesystem.hpp delete mode 100644 kapi/include/kapi/interrupts.hpp delete mode 100644 kapi/include/kapi/memory.hpp delete mode 100644 kapi/include/kapi/memory/address.hpp delete mode 100644 kapi/include/kapi/memory/chunk.hpp delete mode 100644 kapi/include/kapi/memory/frame.hpp delete mode 100644 kapi/include/kapi/memory/frame_allocator.hpp delete mode 100644 kapi/include/kapi/memory/layout.hpp delete mode 100644 kapi/include/kapi/memory/page.hpp delete mode 100644 kapi/include/kapi/memory/page_mapper.hpp delete mode 100644 kapi/include/kapi/system.hpp create mode 100644 kapi/kapi/acpi.hpp create mode 100644 kapi/kapi/boot.hpp create mode 100644 kapi/kapi/boot_module/boot_module.hpp create mode 100644 kapi/kapi/boot_module/boot_module_registry.hpp create mode 100644 kapi/kapi/boot_modules.hpp create mode 100644 kapi/kapi/cio.hpp create mode 100644 kapi/kapi/cio/output_device.hpp create mode 100644 kapi/kapi/cpu.hpp create mode 100644 kapi/kapi/devices.hpp create mode 100644 kapi/kapi/devices/bus.hpp create mode 100644 kapi/kapi/devices/cpu.hpp create mode 100644 kapi/kapi/devices/device.hpp create mode 100644 kapi/kapi/devices/manager.hpp create mode 100644 kapi/kapi/filesystem.hpp create mode 100644 kapi/kapi/interrupts.hpp create mode 100644 kapi/kapi/memory.hpp create mode 100644 kapi/kapi/memory/address.hpp create mode 100644 kapi/kapi/memory/chunk.hpp create mode 100644 kapi/kapi/memory/frame.hpp create mode 100644 kapi/kapi/memory/frame_allocator.hpp create mode 100644 kapi/kapi/memory/layout.hpp create mode 100644 kapi/kapi/memory/page.hpp create mode 100644 kapi/kapi/memory/page_mapper.hpp create mode 100644 kapi/kapi/system.hpp diff --git a/kapi/CMakeLists.txt b/kapi/CMakeLists.txt index d15b923..f74cfb3 100644 --- a/kapi/CMakeLists.txt +++ b/kapi/CMakeLists.txt @@ -15,7 +15,7 @@ target_sources("kapi" PUBLIC ) target_include_directories("kapi" INTERFACE - "include" + "${CMAKE_CURRENT_SOURCE_DIR}" ) target_link_libraries("kapi" INTERFACE diff --git a/kapi/include/kapi/acpi.hpp b/kapi/include/kapi/acpi.hpp deleted file mode 100644 index 885fcde..0000000 --- a/kapi/include/kapi/acpi.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef TEACHOS_KAPI_ACPI_HPP -#define TEACHOS_KAPI_ACPI_HPP - -#include - -#include -#include - -#include - -namespace kapi::acpi -{ - - //! @addtogroup kapi-acpi-kernel-defined - //! @{ - - //! Initialize the ACPI subsystem and discover the available tables. - //! - //! @return true iff. a valid system description tabled was found, false otherwise. - auto init(::acpi::rsdp const & sdp) -> bool; - - //! Get a pointer to an ACPI table by its signature. - //! - //! @param signature The signature of the table to get. - //! @return A pointer to the table if found, nullptr otherwise. - auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::table_header const>; - - //! Get a type-cast pointer to an ACPI table by its signature. - //! - //! @tparam Signature The signature of the table to get - //! @return A pointer to the table if found, nullptr otherwise. - template - auto get_table() -> kstd::observer_ptr<::acpi::table_type_t const> - { - return kstd::make_observer(static_cast<::acpi::table_type_t const *>(get_table(Signature).get())); - } - -} // namespace kapi::acpi - -#endif diff --git a/kapi/include/kapi/boot.hpp b/kapi/include/kapi/boot.hpp deleted file mode 100644 index 55ca941..0000000 --- a/kapi/include/kapi/boot.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef TEACHOS_KAPI_BOOT_HPP -#define TEACHOS_KAPI_BOOT_HPP - -namespace kapi::boot -{ - //! @qualifier platform-defined - //! Information passed from the early pre-main stage to the kernel executable. - //! - //! The specific structure of this type is defined on a platform level. - struct information; - - //! @qualifier platform-defined - //! An object passed from the early pre-main stage to the kernel executable. - extern "C" information const bootstrap_information; -} // namespace kapi::boot - -#endif diff --git a/kapi/include/kapi/boot_module/boot_module.hpp b/kapi/include/kapi/boot_module/boot_module.hpp deleted file mode 100644 index 9b4b165..0000000 --- a/kapi/include/kapi/boot_module/boot_module.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_HPP -#define TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_HPP - -#include - -#include -#include - -namespace kapi::boot_modules -{ - // ! The boot module struct - // ! - // ! The boot module struct represents a module loaded by the bootloader, and contains information about it, such as - // ! its name, virtual start address, and size. - struct boot_module - { - std::string_view name{}; - memory::linear_address start_address{}; - std::size_t size{}; - }; -} // namespace kapi::boot_modules - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/boot_module/boot_module_registry.hpp b/kapi/include/kapi/boot_module/boot_module_registry.hpp deleted file mode 100644 index fc3590f..0000000 --- a/kapi/include/kapi/boot_module/boot_module_registry.hpp +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_REGISTRY_HPP -#define TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_REGISTRY_HPP - -#include - -#include - -#include - -namespace kapi::boot_modules -{ - - // ! The interface of the boot module registry - // ! - // ! The boot module registry is responsible for keeping track of the modules loaded by the bootloader, and - // ! providing access to them for the rest of the kernel. - struct boot_module_registry - { - using range_type = kstd::vector; - using value_type = range_type::value_type; - using const_reference = range_type::const_reference; - - using const_iterator = range_type::const_iterator; - using const_reverse_iterator = range_type::const_reverse_iterator; - using size_type = range_type::size_type; - - [[nodiscard]] auto begin() const noexcept -> const_iterator - { - return m_modules.begin(); - } - - [[nodiscard]] auto end() const noexcept -> const_iterator - { - return m_modules.end(); - } - - [[nodiscard]] auto cbegin() const noexcept -> const_iterator - { - return begin(); - } - - [[nodiscard]] auto cend() const noexcept -> const_iterator - { - return end(); - } - - [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator - { - return m_modules.rbegin(); - } - - [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator - { - return m_modules.rend(); - } - - [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator - { - return rbegin(); - } - - [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator - { - return rend(); - } - - [[nodiscard]] auto front() const noexcept -> const_reference - { - return m_modules.front(); - } - - [[nodiscard]] auto back() const noexcept -> const_reference - { - return m_modules.back(); - } - - [[nodiscard]] auto size() const noexcept -> std::size_t - { - return m_modules.size(); - } - - [[nodiscard]] auto empty() const noexcept -> bool - { - return m_modules.empty(); - } - - [[nodiscard]] auto at(std::size_t index) const -> const_reference - { - return m_modules.at(index); - } - - [[nodiscard]] auto operator[](std::size_t index) const noexcept -> const_reference - { - return m_modules[index]; - } - - auto add_boot_module(boot_module module) -> void - { - m_modules.push_back(module); - } - - private: - range_type m_modules{}; - }; -} // namespace kapi::boot_modules - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/boot_modules.hpp b/kapi/include/kapi/boot_modules.hpp deleted file mode 100644 index 026479d..0000000 --- a/kapi/include/kapi/boot_modules.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef TEACHOS_KAPI_BOOT_MODULES_HPP -#define TEACHOS_KAPI_BOOT_MODULES_HPP - -#include // IWYU pragma: export - -namespace kapi::boot_modules -{ - - //! @qualifier platform-defined - //! Initialize the boot module registry. - //! - //! @note This function must be implemented by the target platform. - //! - //! This function initializes the boot module registry, which is responsible for keeping track of the modules loaded - //! by the bootloader, and providing access to them for the rest of the kernel. - auto init() -> void; - - //! @qualifier kernel-defined - //! Set the boot module registry - //! - //! @param registry A new boot module registry. - auto set_boot_module_registry(boot_module_registry & registry) -> void; - - //! @qualifier kernel-defined - //! Get the boot module registry. - //! - //! @returns The boot module registry. - auto get_boot_module_registry() -> boot_module_registry &; - -} // namespace kapi::boot_modules -#endif \ No newline at end of file diff --git a/kapi/include/kapi/cio.hpp b/kapi/include/kapi/cio.hpp deleted file mode 100644 index 9bbf7fa..0000000 --- a/kapi/include/kapi/cio.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef TEACHOS_KAPI_CIO_HPP -#define TEACHOS_KAPI_CIO_HPP - -#include // IWYU pragma: export - -#include - -#include -#include - -namespace kapi::cio -{ - - //! @addtogroup kapi-cio - //! @{ - //! @} - - //! @addtogroup kapi-cio-kernel-defined - //! @{ - - //! Set the currently active output device. - //! - //! @param device A new output device. - //! @return The previously active output device. - auto set_output_device(output_device & device) -> std::optional; - - //! 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; - - //! @} - - //! @addtogroup kapi-cio-platform-defined - //! @{ - - //! Initialize the character I/O subsystem. - //! - //! If a platform support character output, it shall ensure that when this function returns, basic character - //! output can be performed on the system. - auto init() -> void; - - //! @} - -} // namespace kapi::cio - -#endif diff --git a/kapi/include/kapi/cio/output_device.hpp b/kapi/include/kapi/cio/output_device.hpp deleted file mode 100644 index 9fe2557..0000000 --- a/kapi/include/kapi/cio/output_device.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef TEACHOS_KAPI_CIO_OUTPUT_DEVICE_HPP -#define TEACHOS_KAPI_CIO_OUTPUT_DEVICE_HPP - -// IWYU pragma: private, include - -#include - -namespace kapi::cio -{ - - enum struct output_stream - { - stdout, - stderr, - }; - - //! The interface of a device able to perform character output on a platform. - struct output_device - { - output_device(output_device const &) = delete; - output_device(output_device &&) = delete; - auto operator=(output_device const &) -> output_device & = delete; - auto operator=(output_device &&) -> output_device & = delete; - - virtual ~output_device() = default; - - //! Write the given text to the output device. - //! - //! @param stream The stream to write to. - //! @param text The text to write. - auto virtual write(output_stream stream, std::string_view text) -> void = 0; - - protected: - output_device() = default; - }; - -} // namespace kapi::cio - -#endif diff --git a/kapi/include/kapi/cpu.hpp b/kapi/include/kapi/cpu.hpp deleted file mode 100644 index deaf5cd..0000000 --- a/kapi/include/kapi/cpu.hpp +++ /dev/null @@ -1,135 +0,0 @@ -#ifndef TEACHOS_KAPI_CPU_HPP -#define TEACHOS_KAPI_CPU_HPP - -#include - -#include -#include - -#include - -namespace kapi::cpu -{ - - //! @addtogroup kapi-cpu - //! @{ - - //! An exception originating from the CPU directly. - //! - //! Exception generally model interrupts that are synchronous to the instruction stream. This means that they do not - //! originate from external hardware, but rather are a product of program execution. - struct exception - { - //! The type of the exception, which identifies the reason for it being raised. - enum class type : std::uint8_t - { - //! The reason for the exception is unknown or platform-specific - unknown, - //! A page fault occurred - page_fault, - //! A memory access (either in the data or instruction stream) violated it's alignment constraints. - alignment_fault, - //! A memory access (either in the data or instruction stream) violated it's permissions. - memory_access_fault, - //! An invalid instruction was present in the instruction stream. - illegal_instruction, - //! The preconditions for the execution of an instruction were not met. - privilege_violation, - //! An arithmetic error occurred. - arithmetic_error, - //! A breakpoint was hit in the instruction stream. - breakpoint, - //! The CPU is single-stepping through the instruction stream. - single_step, - }; - - //! The type of this exception. - type type{}; - - //! The value of the instruction pointer at the time this exception was raised. - kapi::memory::linear_address instruction_pointer{}; - - //! The memory address that caused this exception. - kapi::memory::linear_address access_address{}; - - //! Whether the page was present at the time of the exception. - bool is_present{}; - - //! Whether the exception was caused by a write access. - bool is_write_access{}; - - //! Whether the exception was caused by a user mode access. - bool is_user_mode{}; - }; - - //! @} - - //! @addtogroup kapi-cpu-kernel-defined - //! @{ - - //! Dispatch an exception to the appropriate handler. - //! - //! @param context The exception context. - //! @return Whether the exception was handled. - [[nodiscard]] auto dispatch(exception const & context) -> bool; - - //! @} - - //! @addtogroup kapi-cpu-platform-defined - //! @{ - - //! Halt the CPU. - //! - //! This function terminates execution of the kernel. - [[noreturn]] auto halt() -> void; - - //! Perform early CPU initialization. - //! - //! When this function returns, the CPU is in a state where interrupt could be enabled. This function must not enable - //! interrupts itself. - auto init() -> void; - - //! Discover the CPU topology of the platform and attach it to the given CPU bus. - //! - //! @return true iff. the CPU topology was discovered successfully, false otherwise. - auto discover_topology() -> bool; - - //! @} - -} // namespace kapi::cpu - -template<> -struct kstd::formatter -{ - constexpr auto parse(kstd::format_parse_context & ctx) -> decltype(ctx.begin()) - { - return ctx.begin(); - } - - constexpr auto format(enum kapi::cpu::exception::type type, kstd::format_context & ctx) const -> void - { - switch (type) - { - case kapi::cpu::exception::type::unknown: - return ctx.push("unknown"); - case kapi::cpu::exception::type::page_fault: - return ctx.push("page fault"); - case kapi::cpu::exception::type::alignment_fault: - return ctx.push("alignment fault"); - case kapi::cpu::exception::type::memory_access_fault: - return ctx.push("memory access fault"); - case kapi::cpu::exception::type::illegal_instruction: - return ctx.push("illegal instruction"); - case kapi::cpu::exception::type::privilege_violation: - return ctx.push("privilege violation"); - case kapi::cpu::exception::type::arithmetic_error: - return ctx.push("arithmetic error"); - case kapi::cpu::exception::type::breakpoint: - return ctx.push("breakpoint"); - case kapi::cpu::exception::type::single_step: - return ctx.push("single step"); - } - } -}; - -#endif diff --git a/kapi/include/kapi/devices.hpp b/kapi/include/kapi/devices.hpp deleted file mode 100644 index b597aa8..0000000 --- a/kapi/include/kapi/devices.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef TEACHOS_KAPI_DEVICES_HPP -#define TEACHOS_KAPI_DEVICES_HPP - -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export - -namespace kapi::devices -{ - - //! @addtogroup kapi-devices-kernel-defined - //! @{ - - //! Initialize the kernel's device management subsystem. - auto init() -> void; - - //! Get the virtual system root bus. - //! - //! @warning This function will panic if the root bus has not been initialized. - //! - //! @return a reference to the root bus. - auto get_root_bus() -> bus &; - - //! @} - - //! @addtogroup kapi-devices-platform-defined - //! @{ - - //! Initialize the platform's device tree. - auto init_platform_devices() -> void; - - //! @} - -} // namespace kapi::devices - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/devices/bus.hpp b/kapi/include/kapi/devices/bus.hpp deleted file mode 100644 index 59f49f7..0000000 --- a/kapi/include/kapi/devices/bus.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef TEACHOS_KAPI_DEVICES_BUS_HPP -#define TEACHOS_KAPI_DEVICES_BUS_HPP - -// IWYU pragma: private, include - -#include - -#include -#include -#include -#include - -#include -#include - -namespace kapi::devices -{ - - //! @addtogroup kapi-devices-kernel-defined - //! @{ - - //! A bus device that represents a logical/physical tree of devices and busses. - struct bus : device - { - //! Construct a bus with the given major number, minor number, and name. - //! - //! @param major The major number of the bus. - //! @param minor The minor number of the bus. - //! @param name The name of the bus. - bus(std::size_t major, std::size_t minor, kstd::string const & name); - - //! Initialize the bus and all of its children. - //! - //! @return true iff. the bus and all of its children are healthy, false otherwise. - auto init() -> bool final; - - //! Attach a child device to this bus. - //! - //! Whenever a device is attached to a bus, the bus takes sole ownership of the device. - //! - //! @param child The child device to attach. - auto add_child(kstd::unique_ptr child) -> void; - - [[nodiscard]] auto children() const -> kstd::vector> const &; - - protected: - //! Probe the bus hardware state. - //! - //! @return true iff. the bus hardware is healthy, false otherwise. - auto virtual probe() -> bool; - - private: - kstd::vector> m_devices; - kstd::vector> m_observers; - std::atomic_flag m_init_was_called{}; - std::atomic_flag m_initialized{}; - }; - - //! @} - -} // namespace kapi::devices - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/devices/cpu.hpp b/kapi/include/kapi/devices/cpu.hpp deleted file mode 100644 index f8ff60c..0000000 --- a/kapi/include/kapi/devices/cpu.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef TEACHOS_KAPI_DEVICES_CPU_HPP -#define TEACHOS_KAPI_DEVICES_CPU_HPP - -#include - -#include -#include - -namespace kapi::devices -{ - - //! A virtual CPU bus to host all CPUs in the system. - struct cpu final : kapi::devices::bus - { - //! A virtual CPU Core to host all core local devices. - struct core final : kapi::devices::bus - { - explicit core(std::size_t major_number, std::size_t minor_number, std::uint64_t hardware_id, bool is_bsp); - - [[nodiscard]] auto hardware_id() const -> std::uint64_t; - [[nodiscard]] auto is_bsp() const -> bool; - - private: - std::uint64_t m_hardware_id; - bool m_is_bsp; - }; - - //! Create a new CPU with the given major and minor numbers. - //! - //! @param major The major number of this CPU - //! @param minor The minor number of this CPU, identifying the physical CPU - cpu(std::size_t major, std::size_t minor); - }; - -} // namespace kapi::devices - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/devices/device.hpp b/kapi/include/kapi/devices/device.hpp deleted file mode 100644 index 70cf01f..0000000 --- a/kapi/include/kapi/devices/device.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef TEACH_OS_KAPI_DEVICES_DEVICE_HPP -#define TEACH_OS_KAPI_DEVICES_DEVICE_HPP - -// IWYU pragma: private, include - -#include -#include - -#include - -namespace kapi::devices -{ - - //! @addtogroup kapi-devices-kernel-defined - //! @{ - - /** - * @brief Base device identified by a major, minor number and name. - */ - struct device - { - /** - * @brief Create a device identifier from @p major, @p minor and @p name. - * @param major Device major number. - * @param minor Device minor number. - * @param name Device name. - */ - device(size_t major, size_t minor, kstd::string const & name); - - /** - * @brief Virtual destructor for device. - */ - virtual ~device() = default; - - /** - * @brief Initialize the device. - * @return true on success, false otherwise. - */ - virtual auto init() -> bool = 0; - - /** - * @brief Returns the major number of the device. - * @return Device major number. - */ - [[nodiscard]] auto major() const -> size_t; - - /** - * @brief Returns the minor number of the device. - * @return Device minor number. - */ - [[nodiscard]] auto minor() const -> size_t; - - /** - * @brief Returns the name of the device. - * @return Device name. - */ - [[nodiscard]] auto name() const -> kstd::string const &; - - /** - * @brief Check if the device is a block device. - * @return true if this device is a block device, false otherwise. - */ - [[nodiscard]] virtual auto is_block_device() const -> bool; - - private: - friend struct bus; - - auto set_parent(kstd::observer_ptr parent) -> void; - - size_t m_major; - size_t m_minor; - kstd::string m_name; - kstd::observer_ptr m_parent; - }; - - //! @} - -} // namespace kapi::devices - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/devices/manager.hpp b/kapi/include/kapi/devices/manager.hpp deleted file mode 100644 index c9b90b4..0000000 --- a/kapi/include/kapi/devices/manager.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef TEACHOS_KAPI_DEVICES_MANAGER_HPP -#define TEACHOS_KAPI_DEVICES_MANAGER_HPP - -// IWYU pragma: private, include - -#include - -#include - -#include -#include - -namespace kapi::devices -{ - - //! @addtogroup kapi-devices-kernel-defined - //! @{ - - //! Ask the kernel to allocate a new major number. - //! - //! @return a new, unused major number. - auto allocate_major_number() -> std::size_t; - - //! Register a new device with the kernel's device manager. - //! - //! @param device The device to register. - //! @return true if the device was registered successfully, false otherwise. - auto register_device(device & device) -> bool; - - //! Unregister a device from the kernel's device manager. - //! - //! @param device The device to unregister. - //! @return true if the device was unregistered successfully, false otherwise. - auto unregister_device(device & device) -> bool; - - //! Find a device by its major and minor numbers. - //! - //! @param major the major number of the device. - //! @param minor the minor number of the device. - //! @return a pointer to the device iff. the device was found, nullptr otherwise. - auto find_device(std::size_t major, std::size_t minor) -> kstd::observer_ptr; - - //! Find a device by its name. - //! - //! @param name the name of the device. - //! @return a pointer to the device iff. the device was found, nullptr otherwise. - auto find_device(std::string_view name) -> kstd::observer_ptr; - - //! @} - -} // namespace kapi::devices - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/filesystem.hpp b/kapi/include/kapi/filesystem.hpp deleted file mode 100644 index 94d42ce..0000000 --- a/kapi/include/kapi/filesystem.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef TEACHOS_KAPI_FILESYSTEM_HPP -#define TEACHOS_KAPI_FILESYSTEM_HPP - -#include -#include - -#include - -namespace kapi::filesystem -{ - /** - @brief The kapi::filesystem namespace provides the interface for filesystem operations in the kernel. It includes - functions for mounting and unmounting filesystems, as well as basic file operations such as opening, closing, reading - from, and writing to files. The actual implementation of these functions is in the kernel's filesystem subsystem, - which will handle the specifics of different filesystem types and their interactions with the underlying storage - devices. - */ - - /** - @brief Mounts a filesystem from the specified @p source at the specified @p target path. - @param source The source device or filesystem to mount. - @param target The target mount point. - @return 0 on success, -1 on failure. - @qualifier kernel-defined - */ - auto mount(std::string_view source, std::string_view target) -> int; - - /** - @brief Unmounts a filesystem from the specified @p target path. - @param target The target mount point to unmount. - @return 0 on success, -1 on failure. - @qualifier kernel-defined - */ - auto umount(std::string_view target) -> int; - - /** - @brief Opens a file at the specified @p path. - @param path The path to the file to open. - @return A file descriptor on success, -1 on failure. - @qualifier kernel-defined - */ - auto open(std::string_view path) -> int; - - /** - @brief Closes a @p file_descriptor. - @param file_descriptor The file descriptor to close. - @return 0 on success, -1 on failure. - @qualifier kernel-defined - */ - auto close(int file_descriptor) -> int; - - /** - @brief Reads @p size bytes into @p buffer from a @p file_descriptor. - @param file_descriptor The file descriptor to read from. - @param buffer The buffer to store the read data. - @param size The number of bytes to read. - @return The number of bytes read on success, -1 on failure. - @qualifier kernel-defined - */ - auto read(int file_descriptor, void * buffer, size_t size) -> ssize_t; - - /** - @brief Writes @p size bytes from @p buffer to a @p file_descriptor. - @param file_descriptor The file descriptor to write to. - @param buffer The buffer containing the data to write. - @param size The number of bytes to write. - @return The number of bytes written on success, -1 on failure. - @qualifier kernel-defined - */ - auto write(int file_descriptor, void const * buffer, size_t size) -> ssize_t; -} // namespace kapi::filesystem - -#endif // TEACHOS_KAPI_FILESYSTEM_HPP \ No newline at end of file diff --git a/kapi/include/kapi/interrupts.hpp b/kapi/include/kapi/interrupts.hpp deleted file mode 100644 index 4ba0684..0000000 --- a/kapi/include/kapi/interrupts.hpp +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef TEACHOS_KAPI_INTERRUPTS_HPP -#define TEACHOS_KAPI_INTERRUPTS_HPP - -#include - -namespace kapi::interrupts -{ - - //! @addtogroup kapi-interrupts - //! @{ - - //! A status that indicates whether an interrupt was handled by a handler. - enum class status : bool - { - //! The interrupt was not handled by any handler. - unhandled, - //! The interrupt was handled by at least one handler. - handled, - }; - - //! The interface for all interrupt handlers. - struct handler - { - virtual ~handler() = default; - - //! Handle an interrupt with the given IRQ number. - // - //! This function will be called by the kernel in an interrupt context. As such, the function should complete its - //! task quickly and must take care when acquiring globally shared locks. - //! - //! @param irq_number The identifier of the interrupt request that triggered the handler. - //! @return status::handled if the handler successfully handled the interrupt, status::unhandled otherwise. - virtual auto handle_interrupt(std::uint32_t irq_number) -> status = 0; - }; - - //! @} - - //! @addtogroup kapi-interrupts-kernel-defined - //! @{ - - //! Register an interrupt handler for the given IRQ number. - //! - //! @param irq_number The IRQ number to register the handler for. - //! @param handler The interrupt handler to register. - auto register_handler(std::uint32_t irq_number, handler & handler) -> void; - - //! Unregister a previously registered interrupt handler. - //! - //! @param irq_number The IRQ number to unregister the handler for. - //! @param handler The interrupt handler to unregister. - auto unregister_handler(std::uint32_t irq_number, handler & handler) -> void; - - //! Dispatch an interrupt to all registered handlers. - //! - //! @param irq_number The IRQ number to dispatch. - //! @return status::handled if the interrupt was handled by at least one handler, status::unhandled otherwise. - auto dispatch(std::uint32_t irq_number) -> status; - - //! @} - - //! @addtogroup kapi-interrupts-platform-defined - //! @{ - - //! Enable external interrupts. - auto enable() -> void; - - //! Disable external interrupts. - auto disable() -> void; - - //! @} - -} // namespace kapi::interrupts - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/memory.hpp b/kapi/include/kapi/memory.hpp deleted file mode 100644 index 8ad8d6e..0000000 --- a/kapi/include/kapi/memory.hpp +++ /dev/null @@ -1,131 +0,0 @@ -#ifndef TEACHOS_KAPI_MEMORY_HPP -#define TEACHOS_KAPI_MEMORY_HPP - -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export - -#include -#include -#include - -namespace kapi::memory -{ - - using mmio_region = std::pair; - - //! @addtogroup kapi-memory-kernel-defined - //! @{ - - //! Initialize the physical memory manager. - //! - //! This function initializes the kernel-wide physical memory manager. The function will invoke the handoff handler to - //! transfer the platform-specific frame allocation state to the physical memory manager. - //! - //! @note Once this function returns, the global allocator has been replaced by the platform-agnostic, kernel-defined - //! allocator. Any state of the platform specific allocator may be released. - //! - //! @param frame_count The number of frames present in the system. - //! @param handoff_handler A function to be invoked to transfer the platform-specific frame allocation state. The - //! allocator to hand off to is passed to the handler. - auto init_pmm(std::size_t frame_count, void (&handoff_handler)(frame_allocator &)) -> void; - - //! Get the currently active frame allocator. - auto get_frame_allocator() -> frame_allocator &; - - //! Set the currently active frame allocator. - //! - //! @param allocator A new frame allocator. - //! @return The previously active frame allocator. - auto set_frame_allocator(frame_allocator & allocator) -> std::optional; - - //! Set the currently active page mapper. - //! - //! @param mapper A new page mapper. - //! @return The previously active page mapper. - auto set_page_mapper(page_mapper & mapper) -> std::optional; - - //! Allocate a new frame of physical memory - //! - //! @warning This function will panic if no frame allocator has been registered. - //! - //! @return An engaged std::optional iff. a frame could be allocated, std::nullopt otherwise. - auto allocate_frame() -> std::optional; - - //! Allocate multiple new frames of physical memory - //! - //! @warning This function will panic if no frame allocator has been registered. - //! - //! @return An engaged std::optional iff. @p count frames could be allocated, std::nullopt otherwise. - auto allocate_many_frames(std::size_t count) -> std::optional>; - - //! Map a page onto a frame. - //! - //! @warning This function will panic if no page mapper has been registered, or the page has already been mapped. - //! This function will not ensure that the frame is not already in use. - //! - //! @param page The page to map. - //! @param frame The frame to map the page into. - //! @param flags The flags to apply to this mapping. - //! @return A pointer to the first byte of the mapped page. - auto map(page page, frame frame, page_mapper::flags flags = page_mapper::flags::empty) -> std::byte *; - - //! Unmap a page. - //! - //! @warning This function will panic if no page mapper has been registered, or the page is not mapped. - //! - //! @param page The page to unmap - auto unmap(page page) -> void; - - //! Initialize the Memory-mapped I/O region system. - //! - //! @param base The base address for the MMIO region. - //! @param page_count The number of pages the MMIO region is spans. - auto init_mmio(linear_address base, std::size_t page_count) -> void; - - //! Allocate a Memory-mapped I/O region of the given size. - //! - //! @warning This function will panic if the MMIO system has not been initialized! - //! @param page_count The number of pages to allocate. - auto allocate_mmio_region(std::size_t page_count) -> mmio_region; - - //! Map a region of Memory-mapped I/O address space to a given hardware address using the given flags. - //! - //! @warning This function will panic if no page mapper has been registered, or the page has already been mapped. - //! This function will not ensure that the frame is not already in use. - //! - //! This function will always set the @p uncached flag. - //! - //! @param region The region to map. - //! @param hw_base The base of the hardware region. - //! @param flags The flags to apply. - auto map_mmio_region(mmio_region region, physical_address hw_base, page_mapper::flags flags = {}) -> std::byte *; - - //! Release a Memory-mapped I/O region. - //! - //! @warning This function will panic if the MMIO system has not been initialized! - //! @param region The region to release. - auto release_mmio_region(mmio_region region) -> void; - - //! @} - - //! @addtogroup kapi-memory-platform-defined - //! @{ - - //! Initialize the memory subsystem. - //! - //! @note This function must be implemented by the target platform. - //! - //! This function initializes the memory subsystem and activates the platform-specific frame allocator and page - //! mapper. When this function returns, a valid frame allocator and page mapper are expected to have been registered. - auto init() -> void; - - //! @} - -} // namespace kapi::memory - -#endif diff --git a/kapi/include/kapi/memory/address.hpp b/kapi/include/kapi/memory/address.hpp deleted file mode 100644 index 9231cfc..0000000 --- a/kapi/include/kapi/memory/address.hpp +++ /dev/null @@ -1,252 +0,0 @@ -#ifndef TEACHOS_KAPI_MEMORY_ADDRESS_HPP -#define TEACHOS_KAPI_MEMORY_ADDRESS_HPP - -// IWYU pragma: private, include - -#include -#include - -#include -#include -#include -#include -#include - -namespace kapi::memory -{ - - //! @qualifier kernel-defined - //! A tag for different address types. - enum struct address_type : bool - { - linear, - physical, - }; - - //! @qualifier kernel-defined - //! A physical or virtual address. - //! - //! This convenience wrapper type is used to ensure that no linear address is passed where a physical one is expected - //! and vice versa. - //! - //! @tparam Type The type of address. - template - struct address - { - //! Construct a null-address. - constexpr address() noexcept = default; - - //! Construct an address representing the given value. - //! - //! @param value The raw value to initialize this address with. - constexpr explicit address(std::uintptr_t value) noexcept - : m_value{value} - {} - - //! Construct an address representing the given pointer value. - //! - //! @param pointer The pointer value to initialize this address with. - explicit address(std::byte * pointer) noexcept - : m_value{std::bit_cast(pointer)} - {} - - //! Convert this address into a C++ pointer. - //! - //! @tparam T The type of the object this address should refer to. - //! @return This address as a typed pointer to the given type. - template - explicit operator ObjectType *() const noexcept - { - return std::bit_cast(m_value); - } - - //! Create a new address n beyond this one. - //! - //! @param n The amount to add to this address. - //! @return A new address, n further than this one. - [[nodiscard]] constexpr auto operator+(std::ptrdiff_t n) const noexcept -> address - { - return address{m_value + n}; - } - - //! Increment this address by a given amount. - //! - //! @param n The amount to Increment the address by. - //! @return A reference to this address. - constexpr auto operator+=(std::ptrdiff_t n) noexcept -> address & - { - m_value += n; - return *this; - } - - //! Increment this address by one. - //! - //! @return A reference to this address. - constexpr auto operator++() noexcept -> address & - { - return (*this += 1); - } - - //! Increment this address by one. - //! - //! @return A copy of this address before the increment. - constexpr auto operator++(int) noexcept -> address - { - auto copy = *this; - ++*this; - return copy; - } - - //! Create a new address n bytes before this one - //! - //! @param n The amount to subtract from this address - //! @return A nre address, @p n ahead of this one - [[nodiscard]] constexpr auto operator-(std::ptrdiff_t n) noexcept -> address - { - return address{m_value - n}; - } - - //! Decrement this address by a given amount. - //! - //! @param n The amount to Decrement the address by. - //! @return A reference to this address. - constexpr auto operator-=(std::ptrdiff_t n) noexcept -> address & - { - m_value -= n; - return *this; - } - - //! Decrement this address by one. - //! - //! @return A reference to this address. - constexpr auto operator--() noexcept -> address & - { - return (*this -= 1); - } - - //! Decrement this address by one. - //! - //! @return A copy of this address before the decrement. - constexpr auto operator--(int) noexcept -> address - { - auto copy = *this; - --*this; - return copy; - } - - //! Calculate the distance between this address and another one - //! - //! @param other The address to calculate the distance to. - //! @return The distance between this address and the given one. - [[nodiscard]] constexpr auto operator-(address const & other) noexcept -> std::ptrdiff_t - { - return m_value - other.m_value; - } - - //! Extract the lower bits of the address - //! - //! @note The only meaningful values for @p n are powers of two. - //! - //! @param n The divisor to use for extraction. - //! @return The lower bits of the address as defined by the divisor. - constexpr auto operator%(std::size_t n) const noexcept -> std::uintptr_t - { - return m_value % n; - } - - //! Extract the upper bits of the address - //! - //! @note The only meaningful values for @p n are powers of two. - //! - //! @param n The divisor to use for extraction. - //! @return The upper bits of the address as defined by the divisor. - constexpr auto operator/(std::size_t n) const noexcept -> std::uintptr_t - { - return m_value / n; - } - - //! Shift this address n bits to the right. - //! - //! @param n The width of the shift. - //! @return A new address representing the result of the shift operation. - constexpr auto operator>>(std::size_t n) const noexcept -> address - { - return address{m_value >> n}; - } - - //! Apply the given mask to this address. - //! - //! @param mask The mask to apply - //! @return A new address representing the result of the masking operation. - template - constexpr auto operator&(MaskType mask) const noexcept -> MaskType - { - return static_cast(m_value & mask); - } - - constexpr auto operator+(kstd::units::bytes n) const noexcept -> address - { - return address{m_value + n.value}; - } - - constexpr auto operator+=(kstd::units::bytes n) noexcept -> address & - { - return *this = *this + n; - } - - //! Check if this address is equal to another one. - constexpr auto operator==(address const &) const noexcept -> bool = default; - - //! Lexicographically compare this address with another one. - constexpr auto operator<=>(address const &) const noexcept -> std::strong_ordering = default; - - //! Extract the raw value from this address. - //! - //! @return The raw value of this address. - [[nodiscard]] constexpr auto raw() const noexcept -> std::uintptr_t - { - return m_value; - } - - private: - //! The raw address value. - std::uintptr_t m_value{}; - }; - - //! A linear/virtual address. - using linear_address = address; - - //! A physical address. - using physical_address = address; - -} // namespace kapi::memory - -namespace kstd -{ - - template - struct formatter> : kstd::formatter - { - constexpr auto static suffix = Type == kapi::memory::address_type::linear ? "%lin" : "%phy"; - - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - auto result = formatter::parse(context); - if (!this->specifiers.type) - { - this->specifiers.type = 'p'; - this->specifiers.alternative_form = true; - } - return result; - } - - auto format(kapi::memory::address const & address, format_context & context) const -> void - { - formatter::format(address.raw(), context); - context.push(suffix); - } - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/memory/chunk.hpp b/kapi/include/kapi/memory/chunk.hpp deleted file mode 100644 index 485a890..0000000 --- a/kapi/include/kapi/memory/chunk.hpp +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef TEACHOS_KAPI_MEMORY_CHUNK_HPP -#define TEACHOS_KAPI_MEMORY_CHUNK_HPP - -// IWYU pragma: private, include - -#include - -#include -#include - -namespace kapi::memory -{ - - //! @qualifier kernel-defined - //! A fixed-size unit of memory, indexed by a number. - //! - //! @tparam ChunkType The CRTP type of the deriving class - //! @tparam AddressType The type of addresses used to index this chunk - //! @tparam Size The size of this chunk. - template - struct chunk - { - //! The type of addresses used to index this chunk - using address_type = AddressType; - - //! The size of this chunk - constexpr auto static size = Size; - - //! Construct a new chunk handle for the chunk containing the given address. - //! - //! @param address An address contained by the desired chunk. - //! @return A handle to a chunk containing the given address. - constexpr auto static containing(address_type address) noexcept -> ChunkType - { - return ChunkType{address / size.value}; - } - - //! Get the start address of the chunk referenced by this handle. - //! - //! @return The address of the first byte contained by the chunk referenced by this handle. - [[nodiscard]] constexpr auto start_address() const noexcept -> address_type - { - return address_type{(m_number * size).value}; - } - - //! Get the number of the chunk referenced by this handle. - //! - //! @return The zero-based number of the chunk referenced by this handle. - [[nodiscard]] constexpr auto number() const noexcept -> std::size_t - { - return m_number; - } - - //! Get a handle n chunks after the one referenced by this handle. - //! - //! @param n The positive offset to this chunk. - //! @return A handle referencing the chunk n chunks after the one referenced by this handle. - constexpr auto operator+(std::size_t n) const noexcept -> ChunkType - { - return ChunkType{m_number + n}; - } - - //! Let this handle reference the next chunk after the currently reference one. - //! - //! @return A handle referencing the same chunk as this handle did before the operation. - constexpr auto operator++(int) noexcept -> ChunkType - { - auto copy = static_cast(*this); - ++*this; - return copy; - } - - //! Let this handle reference the next chunk after the currently reference one. - //! - //! @return A reference to this handle. - constexpr auto operator++() noexcept -> ChunkType & - { - ++m_number; - return static_cast(*this); - } - - //! Check if this chunk handle reference the same chunk as another one. - //! - //! @param other Another chunk handle. - constexpr auto operator==(chunk const & other) const noexcept -> bool = default; - - //! Compare the number of the chunk referenced by this handle to the one reference by another one. - //! - //! @param other Another chunk handle. - constexpr auto operator<=>(chunk const & other) const noexcept -> std::strong_ordering = default; - - private: - friend ChunkType; - - //! Construct a handle referencing the first chunk of the respective address space. - constexpr chunk() noexcept = default; - - //! Construct a handle referencing the chunk of memory with the given number. - explicit constexpr chunk(std::size_t number) noexcept - : m_number{number} - {} - - //! The number of the currently referenced chunk. - std::size_t m_number{}; - }; - -} // namespace kapi::memory - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/memory/frame.hpp b/kapi/include/kapi/memory/frame.hpp deleted file mode 100644 index e423fa4..0000000 --- a/kapi/include/kapi/memory/frame.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef TEACHOS_KAPI_MEMORY_FRAME_HPP -#define TEACHOS_KAPI_MEMORY_FRAME_HPP - -// IWYU pragma: private, include - -#include -#include -#include - -#include - -namespace kapi::memory -{ - - //! @qualifier kernel-defined - //! A handle to a frame of physical memory. - //! - //! @note Contrary to the address types, this type is modeled using inheritance to support future extensions. - struct frame : chunk - { - frame() = default; - - frame(std::size_t number) - : chunk{number} - {} - - using difference_type = std::ptrdiff_t; - - //! @copydoc chunk::containing - //! - //! @note This factory shadows the base factory to aid in type deduction. - constexpr auto static containing(physical_address address) noexcept -> frame - { - return frame{chunk::containing(address)}; - } - - //! Convert a base chunk into a page. - //! - //! This constructor allows for conversion from chunk to a frame for - //! convenience. It is deliberately not explicit. - constexpr frame(chunk other) - : chunk{other} - {} - }; - -} // namespace kapi::memory - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/memory/frame_allocator.hpp b/kapi/include/kapi/memory/frame_allocator.hpp deleted file mode 100644 index 784ea93..0000000 --- a/kapi/include/kapi/memory/frame_allocator.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef TEACHOS_KAPI_MEMORY_FRAME_ALLOCATOR_HPP -#define TEACHOS_KAPI_MEMORY_FRAME_ALLOCATOR_HPP - -// IWYU pragma: private, include - -#include - -#include -#include -#include - -namespace kapi::memory -{ - - //! The interface of all frame allocators. - //! - //! A frame allocator is responsible for the allocation, and deallocation, of frames of physical memory. Frames - //! obtained from an allocator shall only be deallocated, or released, via the same allocator. When a frame allocated - //! from one allocator is release via a different one (instance or type), the behavior is undefined. - struct frame_allocator - { - frame_allocator(frame_allocator const &) = delete; - frame_allocator(frame_allocator &&) = delete; - auto operator=(frame_allocator const &) -> frame_allocator & = delete; - auto operator=(frame_allocator &&) -> frame_allocator & = delete; - - virtual ~frame_allocator() = default; - - //! Allocate a frame of physical memory. - //! - //! @return An engaged std::optional iff. a new frame could be allocated, std::nullopt otherwise. - virtual auto allocate() noexcept -> std::optional - { - return allocate_many(1).transform([](auto result) { return result.first; }); - } - - //! Mark the given frame as used - virtual auto mark_used(frame frame) -> void = 0; - - //! Allocate multiple consecutive frames of physical memory. - //! - //! @param count The number of frames to allocate - //! @return an engaged optional iff. a block of consecutive frames could be allocated, std::nullopt otherwise. - virtual auto allocate_many(std::size_t count) noexcept -> std::optional> = 0; - - //! Release a frame of physical memory. - //! - //! @param frame A frame of physical memory, previously acquired by a call to the #allocate function. - virtual auto release(frame frame) -> void - { - return release_many({frame, 1}); - } - - //! Release a frame of physical memory. - //! - //! @param frame_set A set of frames of physical memory, previously acquired by a call to the #allocate_many - //! function. - virtual auto release_many(std::pair frame_set) -> void = 0; - - protected: - frame_allocator() = default; - }; - -} // namespace kapi::memory - -#endif // TEACHOS_KAPI_MEMORY_FRAME_ALLOCATOR_HPP \ No newline at end of file diff --git a/kapi/include/kapi/memory/layout.hpp b/kapi/include/kapi/memory/layout.hpp deleted file mode 100644 index 733fa96..0000000 --- a/kapi/include/kapi/memory/layout.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef TEACHOS_KAPI_MEMORY_LAYOUT_HPP -#define TEACHOS_KAPI_MEMORY_LAYOUT_HPP - -// IWYU pragma: private, include - -#include - -#include - -namespace kapi::memory -{ - - //! The size of a single page of virtual memory. - //! - //! Platforms that use different sizes of pages are expected to emulate 4 KiB pages towards the kernel. - constexpr auto page_size = kstd::units::KiB(4); - - //! The size of a single frame of physical memory. - //! - //! Platforms that use different sizes of frames are expected to emulate 4 KiB pages towards the kernel. - constexpr auto frame_size = kstd::units::KiB(4); - - //! The linear base address of the higher-half direct map. - //! - //! Platforms are expected to provide a mapping of at least the first 512 GiB of available memory at this address. - constexpr auto higher_half_direct_map_base = linear_address{0xffff'8000'0000'0000uz}; - - //! The linear base address of the kernel heap. - constexpr auto heap_base = linear_address{0xffff'c000'0000'0000uz}; - - //! The linear base address of the memory region reserved for the metadata required by the PMM. - constexpr auto pmm_metadata_base = linear_address{0xffff'd000'0000'0000uz}; - - //! The linear base address of all Memory Mapped I/O mappings. - constexpr auto mmio_base = linear_address{0xffff'e000'0000'0000uz}; - - //! The linear base address of the loaded kernel image. - constexpr auto kernel_base = linear_address{0xffff'ffff'8000'0000uz}; - - //! Convert a physical address to a linear address using the higher-half direct map. - constexpr auto hhdm_to_linear(physical_address address) -> linear_address - { - return linear_address{address.raw() + higher_half_direct_map_base.raw()}; - } - - //! Convert a linear address in the higher-half direct map to a physical address. - constexpr auto hhdm_to_physical(linear_address address) -> physical_address - { - return physical_address{address.raw() - higher_half_direct_map_base.raw()}; - } - -} // namespace kapi::memory - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/memory/page.hpp b/kapi/include/kapi/memory/page.hpp deleted file mode 100644 index d987534..0000000 --- a/kapi/include/kapi/memory/page.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef TEACHOS_KAPI_MEMORY_PAGE_HPP -#define TEACHOS_KAPI_MEMORY_PAGE_HPP - -// IWYU pragma: private, include - -#include -#include -#include - -#include - -namespace kapi::memory -{ - - //! @qualifier kernel-defined - //! A handle to a page of virtual memory. - //! - //! @note Contrary to the address types, this type is modeled using inheritance to support future extensions. - struct page : chunk - { - page() = default; - - page(std::size_t number) - : chunk{number} - {} - - //! Convert a base chunk into a page. - //! - //! This constructor allows for conversion from chunk to a page for convenience. - //! It is deliberately not explicit. - constexpr page(chunk other) - : chunk{other} - {} - }; - -} // namespace kapi::memory - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/memory/page_mapper.hpp b/kapi/include/kapi/memory/page_mapper.hpp deleted file mode 100644 index fb600b2..0000000 --- a/kapi/include/kapi/memory/page_mapper.hpp +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef TEACHOS_KAPI_MEMORY_PAGE_MAPPER_HPP -#define TEACHOS_KAPI_MEMORY_PAGE_MAPPER_HPP - -// IWYU pragma: private, include - -#include -#include - -#include - -#include -#include -#include - -namespace kapi::memory -{ - - //! @qualifier platform-implemented - //! The interface of a type allowing the mapping, and unmapping, of pages onto frames. - struct page_mapper - { - page_mapper(page_mapper const &) = delete; - page_mapper(page_mapper &&) = delete; - auto operator=(page_mapper const &) -> page_mapper & = delete; - auto operator=(page_mapper &&) -> page_mapper & = delete; - - //! Platform independent page mapping flags - enum struct flags : std::uint64_t - { - empty, - writable = 1 << 0, //! The page is writable. - executable = 1 << 1, //! The page contains executable instructions. - uncached = 1 << 2, //! The page contents must not be cached. - supervisor_only = 1 << 3, //! The page is only accessible in supervisor mode. - global = 1 << 4, //! The page translation persists across context switches. - }; - - virtual ~page_mapper() = default; - - //! Map a page into a given frame, applying the given flags. - //! - //! @param page The page to map. - //! @param frame The frame to map the page into. - //! @param flags The flags to map the page with. - //! @return A pointer to the first byte of mapped page. - virtual auto map(page page, frame frame, flags flags) -> std::byte * = 0; - - //! Unmap the given page. - //! - //! @warning If the provided page is not mapped, the behavior is undefined. Implementation are encourage to - //! terminate execution via a kernel panic. - //! - //! @param page The page to unmap. - virtual auto unmap(page page) -> void = 0; - - //! Try to unmap the given page. - //! - //! @param page The page to unmap - //! @return true iff. the page was successfully unmapped, false otherwise. - virtual auto try_unmap(page page) noexcept -> bool = 0; - - //! @qualifier kernel-defined - //! Map a page into a given frame, applyint the given flags. - //! - //! @tparam T The type of data contained in the page. - //! @param page The page to map. - //! @param frame The frame to map the page into. - //! @param flags The flags to map the page with. - //! @return A pointer to the first T in the page. - template - [[nodiscard]] auto map_as(page page, frame frame, flags flags) -> T * - { - return std::bit_cast(map(page, frame, flags)); - } - - protected: - page_mapper() = default; - }; - -} // namespace kapi::memory - -namespace kstd::ext -{ - template<> - struct is_bitfield_enum : std::true_type - { - }; -} // namespace kstd::ext - -#endif \ No newline at end of file diff --git a/kapi/include/kapi/system.hpp b/kapi/include/kapi/system.hpp deleted file mode 100644 index 8a20af9..0000000 --- a/kapi/include/kapi/system.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef TEACHOS_KAPI_SYSTEM_HPP -#define TEACHOS_KAPI_SYSTEM_HPP - -#include -#include - -namespace kapi::system -{ - - //! @addtogroup kapi-system-kernel-defined - //! @{ - - //! Terminate kernel execution with the given error message. - //! - //! This function terminates the execution of the kernel and attempts to issue the given error message to the user. - //! - //! @param message The message associated with the panic - [[noreturn]] auto panic(std::string_view message, std::source_location = std::source_location::current()) -> void; - - //! @} // end group kernel-defined - - //! @addtogroup kapi-system-platform-defined - //! @{ - - //! A hook that runs once the memory subsystem has been initialized. - auto memory_initialized() -> void; - - //! @} // end group platform-defined - -} // namespace kapi::system - -#endif diff --git a/kapi/kapi/acpi.hpp b/kapi/kapi/acpi.hpp new file mode 100644 index 0000000..885fcde --- /dev/null +++ b/kapi/kapi/acpi.hpp @@ -0,0 +1,40 @@ +#ifndef TEACHOS_KAPI_ACPI_HPP +#define TEACHOS_KAPI_ACPI_HPP + +#include + +#include +#include + +#include + +namespace kapi::acpi +{ + + //! @addtogroup kapi-acpi-kernel-defined + //! @{ + + //! Initialize the ACPI subsystem and discover the available tables. + //! + //! @return true iff. a valid system description tabled was found, false otherwise. + auto init(::acpi::rsdp const & sdp) -> bool; + + //! Get a pointer to an ACPI table by its signature. + //! + //! @param signature The signature of the table to get. + //! @return A pointer to the table if found, nullptr otherwise. + auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::table_header const>; + + //! Get a type-cast pointer to an ACPI table by its signature. + //! + //! @tparam Signature The signature of the table to get + //! @return A pointer to the table if found, nullptr otherwise. + template + auto get_table() -> kstd::observer_ptr<::acpi::table_type_t const> + { + return kstd::make_observer(static_cast<::acpi::table_type_t const *>(get_table(Signature).get())); + } + +} // namespace kapi::acpi + +#endif diff --git a/kapi/kapi/boot.hpp b/kapi/kapi/boot.hpp new file mode 100644 index 0000000..55ca941 --- /dev/null +++ b/kapi/kapi/boot.hpp @@ -0,0 +1,17 @@ +#ifndef TEACHOS_KAPI_BOOT_HPP +#define TEACHOS_KAPI_BOOT_HPP + +namespace kapi::boot +{ + //! @qualifier platform-defined + //! Information passed from the early pre-main stage to the kernel executable. + //! + //! The specific structure of this type is defined on a platform level. + struct information; + + //! @qualifier platform-defined + //! An object passed from the early pre-main stage to the kernel executable. + extern "C" information const bootstrap_information; +} // namespace kapi::boot + +#endif diff --git a/kapi/kapi/boot_module/boot_module.hpp b/kapi/kapi/boot_module/boot_module.hpp new file mode 100644 index 0000000..9b4b165 --- /dev/null +++ b/kapi/kapi/boot_module/boot_module.hpp @@ -0,0 +1,23 @@ +#ifndef TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_HPP +#define TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_HPP + +#include + +#include +#include + +namespace kapi::boot_modules +{ + // ! The boot module struct + // ! + // ! The boot module struct represents a module loaded by the bootloader, and contains information about it, such as + // ! its name, virtual start address, and size. + struct boot_module + { + std::string_view name{}; + memory::linear_address start_address{}; + std::size_t size{}; + }; +} // namespace kapi::boot_modules + +#endif \ No newline at end of file diff --git a/kapi/kapi/boot_module/boot_module_registry.hpp b/kapi/kapi/boot_module/boot_module_registry.hpp new file mode 100644 index 0000000..fc3590f --- /dev/null +++ b/kapi/kapi/boot_module/boot_module_registry.hpp @@ -0,0 +1,107 @@ +#ifndef TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_REGISTRY_HPP +#define TEACHOS_KAPI_BOOT_MODULE_BOOT_MODULE_REGISTRY_HPP + +#include + +#include + +#include + +namespace kapi::boot_modules +{ + + // ! The interface of the boot module registry + // ! + // ! The boot module registry is responsible for keeping track of the modules loaded by the bootloader, and + // ! providing access to them for the rest of the kernel. + struct boot_module_registry + { + using range_type = kstd::vector; + using value_type = range_type::value_type; + using const_reference = range_type::const_reference; + + using const_iterator = range_type::const_iterator; + using const_reverse_iterator = range_type::const_reverse_iterator; + using size_type = range_type::size_type; + + [[nodiscard]] auto begin() const noexcept -> const_iterator + { + return m_modules.begin(); + } + + [[nodiscard]] auto end() const noexcept -> const_iterator + { + return m_modules.end(); + } + + [[nodiscard]] auto cbegin() const noexcept -> const_iterator + { + return begin(); + } + + [[nodiscard]] auto cend() const noexcept -> const_iterator + { + return end(); + } + + [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator + { + return m_modules.rbegin(); + } + + [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator + { + return m_modules.rend(); + } + + [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator + { + return rbegin(); + } + + [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator + { + return rend(); + } + + [[nodiscard]] auto front() const noexcept -> const_reference + { + return m_modules.front(); + } + + [[nodiscard]] auto back() const noexcept -> const_reference + { + return m_modules.back(); + } + + [[nodiscard]] auto size() const noexcept -> std::size_t + { + return m_modules.size(); + } + + [[nodiscard]] auto empty() const noexcept -> bool + { + return m_modules.empty(); + } + + [[nodiscard]] auto at(std::size_t index) const -> const_reference + { + return m_modules.at(index); + } + + [[nodiscard]] auto operator[](std::size_t index) const noexcept -> const_reference + { + return m_modules[index]; + } + + auto add_boot_module(boot_module module) -> void + { + m_modules.push_back(module); + } + + private: + range_type m_modules{}; + }; +} // namespace kapi::boot_modules + +#endif \ No newline at end of file diff --git a/kapi/kapi/boot_modules.hpp b/kapi/kapi/boot_modules.hpp new file mode 100644 index 0000000..026479d --- /dev/null +++ b/kapi/kapi/boot_modules.hpp @@ -0,0 +1,31 @@ +#ifndef TEACHOS_KAPI_BOOT_MODULES_HPP +#define TEACHOS_KAPI_BOOT_MODULES_HPP + +#include // IWYU pragma: export + +namespace kapi::boot_modules +{ + + //! @qualifier platform-defined + //! Initialize the boot module registry. + //! + //! @note This function must be implemented by the target platform. + //! + //! This function initializes the boot module registry, which is responsible for keeping track of the modules loaded + //! by the bootloader, and providing access to them for the rest of the kernel. + auto init() -> void; + + //! @qualifier kernel-defined + //! Set the boot module registry + //! + //! @param registry A new boot module registry. + auto set_boot_module_registry(boot_module_registry & registry) -> void; + + //! @qualifier kernel-defined + //! Get the boot module registry. + //! + //! @returns The boot module registry. + auto get_boot_module_registry() -> boot_module_registry &; + +} // namespace kapi::boot_modules +#endif \ No newline at end of file diff --git a/kapi/kapi/cio.hpp b/kapi/kapi/cio.hpp new file mode 100644 index 0000000..9bbf7fa --- /dev/null +++ b/kapi/kapi/cio.hpp @@ -0,0 +1,48 @@ +#ifndef TEACHOS_KAPI_CIO_HPP +#define TEACHOS_KAPI_CIO_HPP + +#include // IWYU pragma: export + +#include + +#include +#include + +namespace kapi::cio +{ + + //! @addtogroup kapi-cio + //! @{ + //! @} + + //! @addtogroup kapi-cio-kernel-defined + //! @{ + + //! Set the currently active output device. + //! + //! @param device A new output device. + //! @return The previously active output device. + auto set_output_device(output_device & device) -> std::optional; + + //! 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; + + //! @} + + //! @addtogroup kapi-cio-platform-defined + //! @{ + + //! Initialize the character I/O subsystem. + //! + //! If a platform support character output, it shall ensure that when this function returns, basic character + //! output can be performed on the system. + auto init() -> void; + + //! @} + +} // namespace kapi::cio + +#endif diff --git a/kapi/kapi/cio/output_device.hpp b/kapi/kapi/cio/output_device.hpp new file mode 100644 index 0000000..9fe2557 --- /dev/null +++ b/kapi/kapi/cio/output_device.hpp @@ -0,0 +1,39 @@ +#ifndef TEACHOS_KAPI_CIO_OUTPUT_DEVICE_HPP +#define TEACHOS_KAPI_CIO_OUTPUT_DEVICE_HPP + +// IWYU pragma: private, include + +#include + +namespace kapi::cio +{ + + enum struct output_stream + { + stdout, + stderr, + }; + + //! The interface of a device able to perform character output on a platform. + struct output_device + { + output_device(output_device const &) = delete; + output_device(output_device &&) = delete; + auto operator=(output_device const &) -> output_device & = delete; + auto operator=(output_device &&) -> output_device & = delete; + + virtual ~output_device() = default; + + //! Write the given text to the output device. + //! + //! @param stream The stream to write to. + //! @param text The text to write. + auto virtual write(output_stream stream, std::string_view text) -> void = 0; + + protected: + output_device() = default; + }; + +} // namespace kapi::cio + +#endif diff --git a/kapi/kapi/cpu.hpp b/kapi/kapi/cpu.hpp new file mode 100644 index 0000000..deaf5cd --- /dev/null +++ b/kapi/kapi/cpu.hpp @@ -0,0 +1,135 @@ +#ifndef TEACHOS_KAPI_CPU_HPP +#define TEACHOS_KAPI_CPU_HPP + +#include + +#include +#include + +#include + +namespace kapi::cpu +{ + + //! @addtogroup kapi-cpu + //! @{ + + //! An exception originating from the CPU directly. + //! + //! Exception generally model interrupts that are synchronous to the instruction stream. This means that they do not + //! originate from external hardware, but rather are a product of program execution. + struct exception + { + //! The type of the exception, which identifies the reason for it being raised. + enum class type : std::uint8_t + { + //! The reason for the exception is unknown or platform-specific + unknown, + //! A page fault occurred + page_fault, + //! A memory access (either in the data or instruction stream) violated it's alignment constraints. + alignment_fault, + //! A memory access (either in the data or instruction stream) violated it's permissions. + memory_access_fault, + //! An invalid instruction was present in the instruction stream. + illegal_instruction, + //! The preconditions for the execution of an instruction were not met. + privilege_violation, + //! An arithmetic error occurred. + arithmetic_error, + //! A breakpoint was hit in the instruction stream. + breakpoint, + //! The CPU is single-stepping through the instruction stream. + single_step, + }; + + //! The type of this exception. + type type{}; + + //! The value of the instruction pointer at the time this exception was raised. + kapi::memory::linear_address instruction_pointer{}; + + //! The memory address that caused this exception. + kapi::memory::linear_address access_address{}; + + //! Whether the page was present at the time of the exception. + bool is_present{}; + + //! Whether the exception was caused by a write access. + bool is_write_access{}; + + //! Whether the exception was caused by a user mode access. + bool is_user_mode{}; + }; + + //! @} + + //! @addtogroup kapi-cpu-kernel-defined + //! @{ + + //! Dispatch an exception to the appropriate handler. + //! + //! @param context The exception context. + //! @return Whether the exception was handled. + [[nodiscard]] auto dispatch(exception const & context) -> bool; + + //! @} + + //! @addtogroup kapi-cpu-platform-defined + //! @{ + + //! Halt the CPU. + //! + //! This function terminates execution of the kernel. + [[noreturn]] auto halt() -> void; + + //! Perform early CPU initialization. + //! + //! When this function returns, the CPU is in a state where interrupt could be enabled. This function must not enable + //! interrupts itself. + auto init() -> void; + + //! Discover the CPU topology of the platform and attach it to the given CPU bus. + //! + //! @return true iff. the CPU topology was discovered successfully, false otherwise. + auto discover_topology() -> bool; + + //! @} + +} // namespace kapi::cpu + +template<> +struct kstd::formatter +{ + constexpr auto parse(kstd::format_parse_context & ctx) -> decltype(ctx.begin()) + { + return ctx.begin(); + } + + constexpr auto format(enum kapi::cpu::exception::type type, kstd::format_context & ctx) const -> void + { + switch (type) + { + case kapi::cpu::exception::type::unknown: + return ctx.push("unknown"); + case kapi::cpu::exception::type::page_fault: + return ctx.push("page fault"); + case kapi::cpu::exception::type::alignment_fault: + return ctx.push("alignment fault"); + case kapi::cpu::exception::type::memory_access_fault: + return ctx.push("memory access fault"); + case kapi::cpu::exception::type::illegal_instruction: + return ctx.push("illegal instruction"); + case kapi::cpu::exception::type::privilege_violation: + return ctx.push("privilege violation"); + case kapi::cpu::exception::type::arithmetic_error: + return ctx.push("arithmetic error"); + case kapi::cpu::exception::type::breakpoint: + return ctx.push("breakpoint"); + case kapi::cpu::exception::type::single_step: + return ctx.push("single step"); + } + } +}; + +#endif diff --git a/kapi/kapi/devices.hpp b/kapi/kapi/devices.hpp new file mode 100644 index 0000000..b597aa8 --- /dev/null +++ b/kapi/kapi/devices.hpp @@ -0,0 +1,37 @@ +#ifndef TEACHOS_KAPI_DEVICES_HPP +#define TEACHOS_KAPI_DEVICES_HPP + +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export + +namespace kapi::devices +{ + + //! @addtogroup kapi-devices-kernel-defined + //! @{ + + //! Initialize the kernel's device management subsystem. + auto init() -> void; + + //! Get the virtual system root bus. + //! + //! @warning This function will panic if the root bus has not been initialized. + //! + //! @return a reference to the root bus. + auto get_root_bus() -> bus &; + + //! @} + + //! @addtogroup kapi-devices-platform-defined + //! @{ + + //! Initialize the platform's device tree. + auto init_platform_devices() -> void; + + //! @} + +} // namespace kapi::devices + +#endif \ No newline at end of file diff --git a/kapi/kapi/devices/bus.hpp b/kapi/kapi/devices/bus.hpp new file mode 100644 index 0000000..59f49f7 --- /dev/null +++ b/kapi/kapi/devices/bus.hpp @@ -0,0 +1,63 @@ +#ifndef TEACHOS_KAPI_DEVICES_BUS_HPP +#define TEACHOS_KAPI_DEVICES_BUS_HPP + +// IWYU pragma: private, include + +#include + +#include +#include +#include +#include + +#include +#include + +namespace kapi::devices +{ + + //! @addtogroup kapi-devices-kernel-defined + //! @{ + + //! A bus device that represents a logical/physical tree of devices and busses. + struct bus : device + { + //! Construct a bus with the given major number, minor number, and name. + //! + //! @param major The major number of the bus. + //! @param minor The minor number of the bus. + //! @param name The name of the bus. + bus(std::size_t major, std::size_t minor, kstd::string const & name); + + //! Initialize the bus and all of its children. + //! + //! @return true iff. the bus and all of its children are healthy, false otherwise. + auto init() -> bool final; + + //! Attach a child device to this bus. + //! + //! Whenever a device is attached to a bus, the bus takes sole ownership of the device. + //! + //! @param child The child device to attach. + auto add_child(kstd::unique_ptr child) -> void; + + [[nodiscard]] auto children() const -> kstd::vector> const &; + + protected: + //! Probe the bus hardware state. + //! + //! @return true iff. the bus hardware is healthy, false otherwise. + auto virtual probe() -> bool; + + private: + kstd::vector> m_devices; + kstd::vector> m_observers; + std::atomic_flag m_init_was_called{}; + std::atomic_flag m_initialized{}; + }; + + //! @} + +} // namespace kapi::devices + +#endif \ No newline at end of file diff --git a/kapi/kapi/devices/cpu.hpp b/kapi/kapi/devices/cpu.hpp new file mode 100644 index 0000000..f8ff60c --- /dev/null +++ b/kapi/kapi/devices/cpu.hpp @@ -0,0 +1,37 @@ +#ifndef TEACHOS_KAPI_DEVICES_CPU_HPP +#define TEACHOS_KAPI_DEVICES_CPU_HPP + +#include + +#include +#include + +namespace kapi::devices +{ + + //! A virtual CPU bus to host all CPUs in the system. + struct cpu final : kapi::devices::bus + { + //! A virtual CPU Core to host all core local devices. + struct core final : kapi::devices::bus + { + explicit core(std::size_t major_number, std::size_t minor_number, std::uint64_t hardware_id, bool is_bsp); + + [[nodiscard]] auto hardware_id() const -> std::uint64_t; + [[nodiscard]] auto is_bsp() const -> bool; + + private: + std::uint64_t m_hardware_id; + bool m_is_bsp; + }; + + //! Create a new CPU with the given major and minor numbers. + //! + //! @param major The major number of this CPU + //! @param minor The minor number of this CPU, identifying the physical CPU + cpu(std::size_t major, std::size_t minor); + }; + +} // namespace kapi::devices + +#endif \ No newline at end of file diff --git a/kapi/kapi/devices/device.hpp b/kapi/kapi/devices/device.hpp new file mode 100644 index 0000000..70cf01f --- /dev/null +++ b/kapi/kapi/devices/device.hpp @@ -0,0 +1,80 @@ +#ifndef TEACH_OS_KAPI_DEVICES_DEVICE_HPP +#define TEACH_OS_KAPI_DEVICES_DEVICE_HPP + +// IWYU pragma: private, include + +#include +#include + +#include + +namespace kapi::devices +{ + + //! @addtogroup kapi-devices-kernel-defined + //! @{ + + /** + * @brief Base device identified by a major, minor number and name. + */ + struct device + { + /** + * @brief Create a device identifier from @p major, @p minor and @p name. + * @param major Device major number. + * @param minor Device minor number. + * @param name Device name. + */ + device(size_t major, size_t minor, kstd::string const & name); + + /** + * @brief Virtual destructor for device. + */ + virtual ~device() = default; + + /** + * @brief Initialize the device. + * @return true on success, false otherwise. + */ + virtual auto init() -> bool = 0; + + /** + * @brief Returns the major number of the device. + * @return Device major number. + */ + [[nodiscard]] auto major() const -> size_t; + + /** + * @brief Returns the minor number of the device. + * @return Device minor number. + */ + [[nodiscard]] auto minor() const -> size_t; + + /** + * @brief Returns the name of the device. + * @return Device name. + */ + [[nodiscard]] auto name() const -> kstd::string const &; + + /** + * @brief Check if the device is a block device. + * @return true if this device is a block device, false otherwise. + */ + [[nodiscard]] virtual auto is_block_device() const -> bool; + + private: + friend struct bus; + + auto set_parent(kstd::observer_ptr parent) -> void; + + size_t m_major; + size_t m_minor; + kstd::string m_name; + kstd::observer_ptr m_parent; + }; + + //! @} + +} // namespace kapi::devices + +#endif \ No newline at end of file diff --git a/kapi/kapi/devices/manager.hpp b/kapi/kapi/devices/manager.hpp new file mode 100644 index 0000000..c9b90b4 --- /dev/null +++ b/kapi/kapi/devices/manager.hpp @@ -0,0 +1,53 @@ +#ifndef TEACHOS_KAPI_DEVICES_MANAGER_HPP +#define TEACHOS_KAPI_DEVICES_MANAGER_HPP + +// IWYU pragma: private, include + +#include + +#include + +#include +#include + +namespace kapi::devices +{ + + //! @addtogroup kapi-devices-kernel-defined + //! @{ + + //! Ask the kernel to allocate a new major number. + //! + //! @return a new, unused major number. + auto allocate_major_number() -> std::size_t; + + //! Register a new device with the kernel's device manager. + //! + //! @param device The device to register. + //! @return true if the device was registered successfully, false otherwise. + auto register_device(device & device) -> bool; + + //! Unregister a device from the kernel's device manager. + //! + //! @param device The device to unregister. + //! @return true if the device was unregistered successfully, false otherwise. + auto unregister_device(device & device) -> bool; + + //! Find a device by its major and minor numbers. + //! + //! @param major the major number of the device. + //! @param minor the minor number of the device. + //! @return a pointer to the device iff. the device was found, nullptr otherwise. + auto find_device(std::size_t major, std::size_t minor) -> kstd::observer_ptr; + + //! Find a device by its name. + //! + //! @param name the name of the device. + //! @return a pointer to the device iff. the device was found, nullptr otherwise. + auto find_device(std::string_view name) -> kstd::observer_ptr; + + //! @} + +} // namespace kapi::devices + +#endif \ No newline at end of file diff --git a/kapi/kapi/filesystem.hpp b/kapi/kapi/filesystem.hpp new file mode 100644 index 0000000..94d42ce --- /dev/null +++ b/kapi/kapi/filesystem.hpp @@ -0,0 +1,73 @@ +#ifndef TEACHOS_KAPI_FILESYSTEM_HPP +#define TEACHOS_KAPI_FILESYSTEM_HPP + +#include +#include + +#include + +namespace kapi::filesystem +{ + /** + @brief The kapi::filesystem namespace provides the interface for filesystem operations in the kernel. It includes + functions for mounting and unmounting filesystems, as well as basic file operations such as opening, closing, reading + from, and writing to files. The actual implementation of these functions is in the kernel's filesystem subsystem, + which will handle the specifics of different filesystem types and their interactions with the underlying storage + devices. + */ + + /** + @brief Mounts a filesystem from the specified @p source at the specified @p target path. + @param source The source device or filesystem to mount. + @param target The target mount point. + @return 0 on success, -1 on failure. + @qualifier kernel-defined + */ + auto mount(std::string_view source, std::string_view target) -> int; + + /** + @brief Unmounts a filesystem from the specified @p target path. + @param target The target mount point to unmount. + @return 0 on success, -1 on failure. + @qualifier kernel-defined + */ + auto umount(std::string_view target) -> int; + + /** + @brief Opens a file at the specified @p path. + @param path The path to the file to open. + @return A file descriptor on success, -1 on failure. + @qualifier kernel-defined + */ + auto open(std::string_view path) -> int; + + /** + @brief Closes a @p file_descriptor. + @param file_descriptor The file descriptor to close. + @return 0 on success, -1 on failure. + @qualifier kernel-defined + */ + auto close(int file_descriptor) -> int; + + /** + @brief Reads @p size bytes into @p buffer from a @p file_descriptor. + @param file_descriptor The file descriptor to read from. + @param buffer The buffer to store the read data. + @param size The number of bytes to read. + @return The number of bytes read on success, -1 on failure. + @qualifier kernel-defined + */ + auto read(int file_descriptor, void * buffer, size_t size) -> ssize_t; + + /** + @brief Writes @p size bytes from @p buffer to a @p file_descriptor. + @param file_descriptor The file descriptor to write to. + @param buffer The buffer containing the data to write. + @param size The number of bytes to write. + @return The number of bytes written on success, -1 on failure. + @qualifier kernel-defined + */ + auto write(int file_descriptor, void const * buffer, size_t size) -> ssize_t; +} // namespace kapi::filesystem + +#endif // TEACHOS_KAPI_FILESYSTEM_HPP \ No newline at end of file diff --git a/kapi/kapi/interrupts.hpp b/kapi/kapi/interrupts.hpp new file mode 100644 index 0000000..4ba0684 --- /dev/null +++ b/kapi/kapi/interrupts.hpp @@ -0,0 +1,74 @@ +#ifndef TEACHOS_KAPI_INTERRUPTS_HPP +#define TEACHOS_KAPI_INTERRUPTS_HPP + +#include + +namespace kapi::interrupts +{ + + //! @addtogroup kapi-interrupts + //! @{ + + //! A status that indicates whether an interrupt was handled by a handler. + enum class status : bool + { + //! The interrupt was not handled by any handler. + unhandled, + //! The interrupt was handled by at least one handler. + handled, + }; + + //! The interface for all interrupt handlers. + struct handler + { + virtual ~handler() = default; + + //! Handle an interrupt with the given IRQ number. + // + //! This function will be called by the kernel in an interrupt context. As such, the function should complete its + //! task quickly and must take care when acquiring globally shared locks. + //! + //! @param irq_number The identifier of the interrupt request that triggered the handler. + //! @return status::handled if the handler successfully handled the interrupt, status::unhandled otherwise. + virtual auto handle_interrupt(std::uint32_t irq_number) -> status = 0; + }; + + //! @} + + //! @addtogroup kapi-interrupts-kernel-defined + //! @{ + + //! Register an interrupt handler for the given IRQ number. + //! + //! @param irq_number The IRQ number to register the handler for. + //! @param handler The interrupt handler to register. + auto register_handler(std::uint32_t irq_number, handler & handler) -> void; + + //! Unregister a previously registered interrupt handler. + //! + //! @param irq_number The IRQ number to unregister the handler for. + //! @param handler The interrupt handler to unregister. + auto unregister_handler(std::uint32_t irq_number, handler & handler) -> void; + + //! Dispatch an interrupt to all registered handlers. + //! + //! @param irq_number The IRQ number to dispatch. + //! @return status::handled if the interrupt was handled by at least one handler, status::unhandled otherwise. + auto dispatch(std::uint32_t irq_number) -> status; + + //! @} + + //! @addtogroup kapi-interrupts-platform-defined + //! @{ + + //! Enable external interrupts. + auto enable() -> void; + + //! Disable external interrupts. + auto disable() -> void; + + //! @} + +} // namespace kapi::interrupts + +#endif \ No newline at end of file diff --git a/kapi/kapi/memory.hpp b/kapi/kapi/memory.hpp new file mode 100644 index 0000000..8ad8d6e --- /dev/null +++ b/kapi/kapi/memory.hpp @@ -0,0 +1,131 @@ +#ifndef TEACHOS_KAPI_MEMORY_HPP +#define TEACHOS_KAPI_MEMORY_HPP + +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export + +#include +#include +#include + +namespace kapi::memory +{ + + using mmio_region = std::pair; + + //! @addtogroup kapi-memory-kernel-defined + //! @{ + + //! Initialize the physical memory manager. + //! + //! This function initializes the kernel-wide physical memory manager. The function will invoke the handoff handler to + //! transfer the platform-specific frame allocation state to the physical memory manager. + //! + //! @note Once this function returns, the global allocator has been replaced by the platform-agnostic, kernel-defined + //! allocator. Any state of the platform specific allocator may be released. + //! + //! @param frame_count The number of frames present in the system. + //! @param handoff_handler A function to be invoked to transfer the platform-specific frame allocation state. The + //! allocator to hand off to is passed to the handler. + auto init_pmm(std::size_t frame_count, void (&handoff_handler)(frame_allocator &)) -> void; + + //! Get the currently active frame allocator. + auto get_frame_allocator() -> frame_allocator &; + + //! Set the currently active frame allocator. + //! + //! @param allocator A new frame allocator. + //! @return The previously active frame allocator. + auto set_frame_allocator(frame_allocator & allocator) -> std::optional; + + //! Set the currently active page mapper. + //! + //! @param mapper A new page mapper. + //! @return The previously active page mapper. + auto set_page_mapper(page_mapper & mapper) -> std::optional; + + //! Allocate a new frame of physical memory + //! + //! @warning This function will panic if no frame allocator has been registered. + //! + //! @return An engaged std::optional iff. a frame could be allocated, std::nullopt otherwise. + auto allocate_frame() -> std::optional; + + //! Allocate multiple new frames of physical memory + //! + //! @warning This function will panic if no frame allocator has been registered. + //! + //! @return An engaged std::optional iff. @p count frames could be allocated, std::nullopt otherwise. + auto allocate_many_frames(std::size_t count) -> std::optional>; + + //! Map a page onto a frame. + //! + //! @warning This function will panic if no page mapper has been registered, or the page has already been mapped. + //! This function will not ensure that the frame is not already in use. + //! + //! @param page The page to map. + //! @param frame The frame to map the page into. + //! @param flags The flags to apply to this mapping. + //! @return A pointer to the first byte of the mapped page. + auto map(page page, frame frame, page_mapper::flags flags = page_mapper::flags::empty) -> std::byte *; + + //! Unmap a page. + //! + //! @warning This function will panic if no page mapper has been registered, or the page is not mapped. + //! + //! @param page The page to unmap + auto unmap(page page) -> void; + + //! Initialize the Memory-mapped I/O region system. + //! + //! @param base The base address for the MMIO region. + //! @param page_count The number of pages the MMIO region is spans. + auto init_mmio(linear_address base, std::size_t page_count) -> void; + + //! Allocate a Memory-mapped I/O region of the given size. + //! + //! @warning This function will panic if the MMIO system has not been initialized! + //! @param page_count The number of pages to allocate. + auto allocate_mmio_region(std::size_t page_count) -> mmio_region; + + //! Map a region of Memory-mapped I/O address space to a given hardware address using the given flags. + //! + //! @warning This function will panic if no page mapper has been registered, or the page has already been mapped. + //! This function will not ensure that the frame is not already in use. + //! + //! This function will always set the @p uncached flag. + //! + //! @param region The region to map. + //! @param hw_base The base of the hardware region. + //! @param flags The flags to apply. + auto map_mmio_region(mmio_region region, physical_address hw_base, page_mapper::flags flags = {}) -> std::byte *; + + //! Release a Memory-mapped I/O region. + //! + //! @warning This function will panic if the MMIO system has not been initialized! + //! @param region The region to release. + auto release_mmio_region(mmio_region region) -> void; + + //! @} + + //! @addtogroup kapi-memory-platform-defined + //! @{ + + //! Initialize the memory subsystem. + //! + //! @note This function must be implemented by the target platform. + //! + //! This function initializes the memory subsystem and activates the platform-specific frame allocator and page + //! mapper. When this function returns, a valid frame allocator and page mapper are expected to have been registered. + auto init() -> void; + + //! @} + +} // namespace kapi::memory + +#endif diff --git a/kapi/kapi/memory/address.hpp b/kapi/kapi/memory/address.hpp new file mode 100644 index 0000000..9231cfc --- /dev/null +++ b/kapi/kapi/memory/address.hpp @@ -0,0 +1,252 @@ +#ifndef TEACHOS_KAPI_MEMORY_ADDRESS_HPP +#define TEACHOS_KAPI_MEMORY_ADDRESS_HPP + +// IWYU pragma: private, include + +#include +#include + +#include +#include +#include +#include +#include + +namespace kapi::memory +{ + + //! @qualifier kernel-defined + //! A tag for different address types. + enum struct address_type : bool + { + linear, + physical, + }; + + //! @qualifier kernel-defined + //! A physical or virtual address. + //! + //! This convenience wrapper type is used to ensure that no linear address is passed where a physical one is expected + //! and vice versa. + //! + //! @tparam Type The type of address. + template + struct address + { + //! Construct a null-address. + constexpr address() noexcept = default; + + //! Construct an address representing the given value. + //! + //! @param value The raw value to initialize this address with. + constexpr explicit address(std::uintptr_t value) noexcept + : m_value{value} + {} + + //! Construct an address representing the given pointer value. + //! + //! @param pointer The pointer value to initialize this address with. + explicit address(std::byte * pointer) noexcept + : m_value{std::bit_cast(pointer)} + {} + + //! Convert this address into a C++ pointer. + //! + //! @tparam T The type of the object this address should refer to. + //! @return This address as a typed pointer to the given type. + template + explicit operator ObjectType *() const noexcept + { + return std::bit_cast(m_value); + } + + //! Create a new address n beyond this one. + //! + //! @param n The amount to add to this address. + //! @return A new address, n further than this one. + [[nodiscard]] constexpr auto operator+(std::ptrdiff_t n) const noexcept -> address + { + return address{m_value + n}; + } + + //! Increment this address by a given amount. + //! + //! @param n The amount to Increment the address by. + //! @return A reference to this address. + constexpr auto operator+=(std::ptrdiff_t n) noexcept -> address & + { + m_value += n; + return *this; + } + + //! Increment this address by one. + //! + //! @return A reference to this address. + constexpr auto operator++() noexcept -> address & + { + return (*this += 1); + } + + //! Increment this address by one. + //! + //! @return A copy of this address before the increment. + constexpr auto operator++(int) noexcept -> address + { + auto copy = *this; + ++*this; + return copy; + } + + //! Create a new address n bytes before this one + //! + //! @param n The amount to subtract from this address + //! @return A nre address, @p n ahead of this one + [[nodiscard]] constexpr auto operator-(std::ptrdiff_t n) noexcept -> address + { + return address{m_value - n}; + } + + //! Decrement this address by a given amount. + //! + //! @param n The amount to Decrement the address by. + //! @return A reference to this address. + constexpr auto operator-=(std::ptrdiff_t n) noexcept -> address & + { + m_value -= n; + return *this; + } + + //! Decrement this address by one. + //! + //! @return A reference to this address. + constexpr auto operator--() noexcept -> address & + { + return (*this -= 1); + } + + //! Decrement this address by one. + //! + //! @return A copy of this address before the decrement. + constexpr auto operator--(int) noexcept -> address + { + auto copy = *this; + --*this; + return copy; + } + + //! Calculate the distance between this address and another one + //! + //! @param other The address to calculate the distance to. + //! @return The distance between this address and the given one. + [[nodiscard]] constexpr auto operator-(address const & other) noexcept -> std::ptrdiff_t + { + return m_value - other.m_value; + } + + //! Extract the lower bits of the address + //! + //! @note The only meaningful values for @p n are powers of two. + //! + //! @param n The divisor to use for extraction. + //! @return The lower bits of the address as defined by the divisor. + constexpr auto operator%(std::size_t n) const noexcept -> std::uintptr_t + { + return m_value % n; + } + + //! Extract the upper bits of the address + //! + //! @note The only meaningful values for @p n are powers of two. + //! + //! @param n The divisor to use for extraction. + //! @return The upper bits of the address as defined by the divisor. + constexpr auto operator/(std::size_t n) const noexcept -> std::uintptr_t + { + return m_value / n; + } + + //! Shift this address n bits to the right. + //! + //! @param n The width of the shift. + //! @return A new address representing the result of the shift operation. + constexpr auto operator>>(std::size_t n) const noexcept -> address + { + return address{m_value >> n}; + } + + //! Apply the given mask to this address. + //! + //! @param mask The mask to apply + //! @return A new address representing the result of the masking operation. + template + constexpr auto operator&(MaskType mask) const noexcept -> MaskType + { + return static_cast(m_value & mask); + } + + constexpr auto operator+(kstd::units::bytes n) const noexcept -> address + { + return address{m_value + n.value}; + } + + constexpr auto operator+=(kstd::units::bytes n) noexcept -> address & + { + return *this = *this + n; + } + + //! Check if this address is equal to another one. + constexpr auto operator==(address const &) const noexcept -> bool = default; + + //! Lexicographically compare this address with another one. + constexpr auto operator<=>(address const &) const noexcept -> std::strong_ordering = default; + + //! Extract the raw value from this address. + //! + //! @return The raw value of this address. + [[nodiscard]] constexpr auto raw() const noexcept -> std::uintptr_t + { + return m_value; + } + + private: + //! The raw address value. + std::uintptr_t m_value{}; + }; + + //! A linear/virtual address. + using linear_address = address; + + //! A physical address. + using physical_address = address; + +} // namespace kapi::memory + +namespace kstd +{ + + template + struct formatter> : kstd::formatter + { + constexpr auto static suffix = Type == kapi::memory::address_type::linear ? "%lin" : "%phy"; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + auto result = formatter::parse(context); + if (!this->specifiers.type) + { + this->specifiers.type = 'p'; + this->specifiers.alternative_form = true; + } + return result; + } + + auto format(kapi::memory::address const & address, format_context & context) const -> void + { + formatter::format(address.raw(), context); + context.push(suffix); + } + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/kapi/kapi/memory/chunk.hpp b/kapi/kapi/memory/chunk.hpp new file mode 100644 index 0000000..485a890 --- /dev/null +++ b/kapi/kapi/memory/chunk.hpp @@ -0,0 +1,109 @@ +#ifndef TEACHOS_KAPI_MEMORY_CHUNK_HPP +#define TEACHOS_KAPI_MEMORY_CHUNK_HPP + +// IWYU pragma: private, include + +#include + +#include +#include + +namespace kapi::memory +{ + + //! @qualifier kernel-defined + //! A fixed-size unit of memory, indexed by a number. + //! + //! @tparam ChunkType The CRTP type of the deriving class + //! @tparam AddressType The type of addresses used to index this chunk + //! @tparam Size The size of this chunk. + template + struct chunk + { + //! The type of addresses used to index this chunk + using address_type = AddressType; + + //! The size of this chunk + constexpr auto static size = Size; + + //! Construct a new chunk handle for the chunk containing the given address. + //! + //! @param address An address contained by the desired chunk. + //! @return A handle to a chunk containing the given address. + constexpr auto static containing(address_type address) noexcept -> ChunkType + { + return ChunkType{address / size.value}; + } + + //! Get the start address of the chunk referenced by this handle. + //! + //! @return The address of the first byte contained by the chunk referenced by this handle. + [[nodiscard]] constexpr auto start_address() const noexcept -> address_type + { + return address_type{(m_number * size).value}; + } + + //! Get the number of the chunk referenced by this handle. + //! + //! @return The zero-based number of the chunk referenced by this handle. + [[nodiscard]] constexpr auto number() const noexcept -> std::size_t + { + return m_number; + } + + //! Get a handle n chunks after the one referenced by this handle. + //! + //! @param n The positive offset to this chunk. + //! @return A handle referencing the chunk n chunks after the one referenced by this handle. + constexpr auto operator+(std::size_t n) const noexcept -> ChunkType + { + return ChunkType{m_number + n}; + } + + //! Let this handle reference the next chunk after the currently reference one. + //! + //! @return A handle referencing the same chunk as this handle did before the operation. + constexpr auto operator++(int) noexcept -> ChunkType + { + auto copy = static_cast(*this); + ++*this; + return copy; + } + + //! Let this handle reference the next chunk after the currently reference one. + //! + //! @return A reference to this handle. + constexpr auto operator++() noexcept -> ChunkType & + { + ++m_number; + return static_cast(*this); + } + + //! Check if this chunk handle reference the same chunk as another one. + //! + //! @param other Another chunk handle. + constexpr auto operator==(chunk const & other) const noexcept -> bool = default; + + //! Compare the number of the chunk referenced by this handle to the one reference by another one. + //! + //! @param other Another chunk handle. + constexpr auto operator<=>(chunk const & other) const noexcept -> std::strong_ordering = default; + + private: + friend ChunkType; + + //! Construct a handle referencing the first chunk of the respective address space. + constexpr chunk() noexcept = default; + + //! Construct a handle referencing the chunk of memory with the given number. + explicit constexpr chunk(std::size_t number) noexcept + : m_number{number} + {} + + //! The number of the currently referenced chunk. + std::size_t m_number{}; + }; + +} // namespace kapi::memory + +#endif \ No newline at end of file diff --git a/kapi/kapi/memory/frame.hpp b/kapi/kapi/memory/frame.hpp new file mode 100644 index 0000000..e423fa4 --- /dev/null +++ b/kapi/kapi/memory/frame.hpp @@ -0,0 +1,48 @@ +#ifndef TEACHOS_KAPI_MEMORY_FRAME_HPP +#define TEACHOS_KAPI_MEMORY_FRAME_HPP + +// IWYU pragma: private, include + +#include +#include +#include + +#include + +namespace kapi::memory +{ + + //! @qualifier kernel-defined + //! A handle to a frame of physical memory. + //! + //! @note Contrary to the address types, this type is modeled using inheritance to support future extensions. + struct frame : chunk + { + frame() = default; + + frame(std::size_t number) + : chunk{number} + {} + + using difference_type = std::ptrdiff_t; + + //! @copydoc chunk::containing + //! + //! @note This factory shadows the base factory to aid in type deduction. + constexpr auto static containing(physical_address address) noexcept -> frame + { + return frame{chunk::containing(address)}; + } + + //! Convert a base chunk into a page. + //! + //! This constructor allows for conversion from chunk to a frame for + //! convenience. It is deliberately not explicit. + constexpr frame(chunk other) + : chunk{other} + {} + }; + +} // namespace kapi::memory + +#endif \ No newline at end of file diff --git a/kapi/kapi/memory/frame_allocator.hpp b/kapi/kapi/memory/frame_allocator.hpp new file mode 100644 index 0000000..784ea93 --- /dev/null +++ b/kapi/kapi/memory/frame_allocator.hpp @@ -0,0 +1,66 @@ +#ifndef TEACHOS_KAPI_MEMORY_FRAME_ALLOCATOR_HPP +#define TEACHOS_KAPI_MEMORY_FRAME_ALLOCATOR_HPP + +// IWYU pragma: private, include + +#include + +#include +#include +#include + +namespace kapi::memory +{ + + //! The interface of all frame allocators. + //! + //! A frame allocator is responsible for the allocation, and deallocation, of frames of physical memory. Frames + //! obtained from an allocator shall only be deallocated, or released, via the same allocator. When a frame allocated + //! from one allocator is release via a different one (instance or type), the behavior is undefined. + struct frame_allocator + { + frame_allocator(frame_allocator const &) = delete; + frame_allocator(frame_allocator &&) = delete; + auto operator=(frame_allocator const &) -> frame_allocator & = delete; + auto operator=(frame_allocator &&) -> frame_allocator & = delete; + + virtual ~frame_allocator() = default; + + //! Allocate a frame of physical memory. + //! + //! @return An engaged std::optional iff. a new frame could be allocated, std::nullopt otherwise. + virtual auto allocate() noexcept -> std::optional + { + return allocate_many(1).transform([](auto result) { return result.first; }); + } + + //! Mark the given frame as used + virtual auto mark_used(frame frame) -> void = 0; + + //! Allocate multiple consecutive frames of physical memory. + //! + //! @param count The number of frames to allocate + //! @return an engaged optional iff. a block of consecutive frames could be allocated, std::nullopt otherwise. + virtual auto allocate_many(std::size_t count) noexcept -> std::optional> = 0; + + //! Release a frame of physical memory. + //! + //! @param frame A frame of physical memory, previously acquired by a call to the #allocate function. + virtual auto release(frame frame) -> void + { + return release_many({frame, 1}); + } + + //! Release a frame of physical memory. + //! + //! @param frame_set A set of frames of physical memory, previously acquired by a call to the #allocate_many + //! function. + virtual auto release_many(std::pair frame_set) -> void = 0; + + protected: + frame_allocator() = default; + }; + +} // namespace kapi::memory + +#endif // TEACHOS_KAPI_MEMORY_FRAME_ALLOCATOR_HPP \ No newline at end of file diff --git a/kapi/kapi/memory/layout.hpp b/kapi/kapi/memory/layout.hpp new file mode 100644 index 0000000..733fa96 --- /dev/null +++ b/kapi/kapi/memory/layout.hpp @@ -0,0 +1,54 @@ +#ifndef TEACHOS_KAPI_MEMORY_LAYOUT_HPP +#define TEACHOS_KAPI_MEMORY_LAYOUT_HPP + +// IWYU pragma: private, include + +#include + +#include + +namespace kapi::memory +{ + + //! The size of a single page of virtual memory. + //! + //! Platforms that use different sizes of pages are expected to emulate 4 KiB pages towards the kernel. + constexpr auto page_size = kstd::units::KiB(4); + + //! The size of a single frame of physical memory. + //! + //! Platforms that use different sizes of frames are expected to emulate 4 KiB pages towards the kernel. + constexpr auto frame_size = kstd::units::KiB(4); + + //! The linear base address of the higher-half direct map. + //! + //! Platforms are expected to provide a mapping of at least the first 512 GiB of available memory at this address. + constexpr auto higher_half_direct_map_base = linear_address{0xffff'8000'0000'0000uz}; + + //! The linear base address of the kernel heap. + constexpr auto heap_base = linear_address{0xffff'c000'0000'0000uz}; + + //! The linear base address of the memory region reserved for the metadata required by the PMM. + constexpr auto pmm_metadata_base = linear_address{0xffff'd000'0000'0000uz}; + + //! The linear base address of all Memory Mapped I/O mappings. + constexpr auto mmio_base = linear_address{0xffff'e000'0000'0000uz}; + + //! The linear base address of the loaded kernel image. + constexpr auto kernel_base = linear_address{0xffff'ffff'8000'0000uz}; + + //! Convert a physical address to a linear address using the higher-half direct map. + constexpr auto hhdm_to_linear(physical_address address) -> linear_address + { + return linear_address{address.raw() + higher_half_direct_map_base.raw()}; + } + + //! Convert a linear address in the higher-half direct map to a physical address. + constexpr auto hhdm_to_physical(linear_address address) -> physical_address + { + return physical_address{address.raw() - higher_half_direct_map_base.raw()}; + } + +} // namespace kapi::memory + +#endif \ No newline at end of file diff --git a/kapi/kapi/memory/page.hpp b/kapi/kapi/memory/page.hpp new file mode 100644 index 0000000..d987534 --- /dev/null +++ b/kapi/kapi/memory/page.hpp @@ -0,0 +1,38 @@ +#ifndef TEACHOS_KAPI_MEMORY_PAGE_HPP +#define TEACHOS_KAPI_MEMORY_PAGE_HPP + +// IWYU pragma: private, include + +#include +#include +#include + +#include + +namespace kapi::memory +{ + + //! @qualifier kernel-defined + //! A handle to a page of virtual memory. + //! + //! @note Contrary to the address types, this type is modeled using inheritance to support future extensions. + struct page : chunk + { + page() = default; + + page(std::size_t number) + : chunk{number} + {} + + //! Convert a base chunk into a page. + //! + //! This constructor allows for conversion from chunk to a page for convenience. + //! It is deliberately not explicit. + constexpr page(chunk other) + : chunk{other} + {} + }; + +} // namespace kapi::memory + +#endif \ No newline at end of file diff --git a/kapi/kapi/memory/page_mapper.hpp b/kapi/kapi/memory/page_mapper.hpp new file mode 100644 index 0000000..fb600b2 --- /dev/null +++ b/kapi/kapi/memory/page_mapper.hpp @@ -0,0 +1,90 @@ +#ifndef TEACHOS_KAPI_MEMORY_PAGE_MAPPER_HPP +#define TEACHOS_KAPI_MEMORY_PAGE_MAPPER_HPP + +// IWYU pragma: private, include + +#include +#include + +#include + +#include +#include +#include + +namespace kapi::memory +{ + + //! @qualifier platform-implemented + //! The interface of a type allowing the mapping, and unmapping, of pages onto frames. + struct page_mapper + { + page_mapper(page_mapper const &) = delete; + page_mapper(page_mapper &&) = delete; + auto operator=(page_mapper const &) -> page_mapper & = delete; + auto operator=(page_mapper &&) -> page_mapper & = delete; + + //! Platform independent page mapping flags + enum struct flags : std::uint64_t + { + empty, + writable = 1 << 0, //! The page is writable. + executable = 1 << 1, //! The page contains executable instructions. + uncached = 1 << 2, //! The page contents must not be cached. + supervisor_only = 1 << 3, //! The page is only accessible in supervisor mode. + global = 1 << 4, //! The page translation persists across context switches. + }; + + virtual ~page_mapper() = default; + + //! Map a page into a given frame, applying the given flags. + //! + //! @param page The page to map. + //! @param frame The frame to map the page into. + //! @param flags The flags to map the page with. + //! @return A pointer to the first byte of mapped page. + virtual auto map(page page, frame frame, flags flags) -> std::byte * = 0; + + //! Unmap the given page. + //! + //! @warning If the provided page is not mapped, the behavior is undefined. Implementation are encourage to + //! terminate execution via a kernel panic. + //! + //! @param page The page to unmap. + virtual auto unmap(page page) -> void = 0; + + //! Try to unmap the given page. + //! + //! @param page The page to unmap + //! @return true iff. the page was successfully unmapped, false otherwise. + virtual auto try_unmap(page page) noexcept -> bool = 0; + + //! @qualifier kernel-defined + //! Map a page into a given frame, applyint the given flags. + //! + //! @tparam T The type of data contained in the page. + //! @param page The page to map. + //! @param frame The frame to map the page into. + //! @param flags The flags to map the page with. + //! @return A pointer to the first T in the page. + template + [[nodiscard]] auto map_as(page page, frame frame, flags flags) -> T * + { + return std::bit_cast(map(page, frame, flags)); + } + + protected: + page_mapper() = default; + }; + +} // namespace kapi::memory + +namespace kstd::ext +{ + template<> + struct is_bitfield_enum : std::true_type + { + }; +} // namespace kstd::ext + +#endif \ No newline at end of file diff --git a/kapi/kapi/system.hpp b/kapi/kapi/system.hpp new file mode 100644 index 0000000..8a20af9 --- /dev/null +++ b/kapi/kapi/system.hpp @@ -0,0 +1,32 @@ +#ifndef TEACHOS_KAPI_SYSTEM_HPP +#define TEACHOS_KAPI_SYSTEM_HPP + +#include +#include + +namespace kapi::system +{ + + //! @addtogroup kapi-system-kernel-defined + //! @{ + + //! Terminate kernel execution with the given error message. + //! + //! This function terminates the execution of the kernel and attempts to issue the given error message to the user. + //! + //! @param message The message associated with the panic + [[noreturn]] auto panic(std::string_view message, std::source_location = std::source_location::current()) -> void; + + //! @} // end group kernel-defined + + //! @addtogroup kapi-system-platform-defined + //! @{ + + //! A hook that runs once the memory subsystem has been initialized. + auto memory_initialized() -> void; + + //! @} // end group platform-defined + +} // namespace kapi::system + +#endif -- cgit v1.2.3 From c15bae85ebffbb137647c48fccd219d334a5f8ea Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 30 Apr 2026 09:38:51 +0200 Subject: ide: clean up neovim configuration --- .nvim.lua | 85 ++++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/.nvim.lua b/.nvim.lua index c9a3e0a..0748af9 100644 --- a/.nvim.lua +++ b/.nvim.lua @@ -1,9 +1,11 @@ -vim.opt.exrc = false +local workspace_folder = vim.fn.getcwd() + +-- Formatting vim.g.autoformat = true -vim.g.load_doxygen_syntax = true vim.opt.fixeol = false -local workspace_folder = vim.fn.getcwd() +-- Enable Doxygen +vim.g.load_doxygen_syntax = true local function safe_require(module) local ok, mod = pcall(require, module) @@ -35,14 +37,20 @@ vim.filetype.add({ } }) -require("neo-tree").setup({ - nesting_rules = { - ['*.hpp'] = { - pattern = "(.*).hpp", - files = { "%1.cpp" } +-- File Browser +local neo_tree = safe_require("neo-tree") +if neo_tree then + local current_config = neo_tree.config or {} + local project_config = vim.tbl_deep_extend("force", current_config, { + nesting_rules = { + ['*.hpp'] = { + pattern = "(.*).hpp", + files = { "%1.cpp", "%1.test.cpp", "%1.tests.cpp" } + } } - } -}) + }) + neo_tree.setup(project_config) +end -- Debugging local dap = require("dap") @@ -127,32 +135,35 @@ dap.listeners.after.event_terminated["teachos_qemu_teardown"] = teardown_qemu dap.listeners.after.event_exited["teachos_qemu_teardown"] = teardown_qemu dap.listeners.after.disconnect["teachos_qemu_teardown"] = teardown_qemu -require("cmake-tools").setup({ - cmake_compile_commands_options = { - action = "copy", - target = string.format("%s/build", workspace_folder), - }, - cmake_dap_configuration = { - name = "(gdb) QEMU MI Attach", - type = "teachos_qemu_mi", - request = "launch", - MIMode = "gdb", - cwd = workspace_folder, - miDebuggerServerAddress = "localhost:1234", - miDebuggerPath = "x86_64-pc-elf-gdb", - stopAtEntry = true, - } -}) +local cmake_tools = safe_require("cmake-tools") +if cmake_tools then + require("cmake-tools").setup({ + cmake_compile_commands_options = { + action = "copy", + target = string.format("%s/build", workspace_folder), + }, + cmake_dap_configuration = { + name = "(gdb) QEMU MI Attach", + type = "teachos_qemu_mi", + request = "launch", + MIMode = "gdb", + cwd = workspace_folder, + miDebuggerServerAddress = "localhost:1234", + miDebuggerPath = "x86_64-pc-elf-gdb", + stopAtEntry = true, + } + }) -vim.api.nvim_create_user_command("TeachOSRun", function() - local cmake = safe_require("cmake-tools") - if not cmake then return end - local is_ready, target = pcall(cmake.get_launch_target_path) - if is_ready and target then - launch_qemu(target, false) - else - vim.notify("Fatal: Cannot determine CMake target path.", vim.log.levels.ERROR) - end -end, {}) + vim.api.nvim_create_user_command("TeachOSRun", function() + local cmake = safe_require("cmake-tools") + if not cmake then return end + local is_ready, target = pcall(cmake.get_launch_target_path) + if is_ready and target then + launch_qemu(target, false) + else + vim.notify("Fatal: Cannot determine CMake target path.", vim.log.levels.ERROR) + end + end, {}) -vim.keymap.set('n', '', 'TeachOSRun', { noremap = true, desc = "Run TeachOS QEMU (No Debug)" }) + vim.keymap.set('n', '', 'TeachOSRun', { noremap = true, desc = "Run TeachOS QEMU (No Debug)" }) +end -- cgit v1.2.3 From 9fc9d3b011db40027e8c1220c535007a786d03ff Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 30 Apr 2026 18:01:23 +0200 Subject: build: upgrade to GCC 16 --- .devcontainer/x86-64/devcontainer.json | 2 +- kernel/src/devices/storage/management.cpp | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.devcontainer/x86-64/devcontainer.json b/.devcontainer/x86-64/devcontainer.json index 775da72..18e6419 100644 --- a/.devcontainer/x86-64/devcontainer.json +++ b/.devcontainer/x86-64/devcontainer.json @@ -1,6 +1,6 @@ { "name": "TeachOS on x86-64", - "image": "registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:15.2.0-4", + "image": "registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:16.1.0-1", "features": { "ghcr.io/devcontainers/features/git:1": {}, "ghcr.io/devcontainers/features/git-lfs:1": {}, diff --git a/kernel/src/devices/storage/management.cpp b/kernel/src/devices/storage/management.cpp index 7361cd5..1f2acba 100644 --- a/kernel/src/devices/storage/management.cpp +++ b/kernel/src/devices/storage/management.cpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace { @@ -63,18 +64,20 @@ namespace kernel::devices::storage auto management::device_by_major_minor(size_t major, size_t minor) -> kstd::shared_ptr { - kstd::shared_ptr found = nullptr; - - std::ranges::find_if(m_controllers, [&](auto const & controller) { + auto found = std::ranges::find_if(m_controllers, [=](auto const & controller) { if (controller != nullptr && controller->major() == major) { - found = controller->device_by_minor(minor); - return found != nullptr; + return controller->device_by_minor(minor) != nullptr; } return false; }); - return found; + if (found != std::ranges::cend(m_controllers)) + { + return found->get()->device_by_minor(minor); + } + + return nullptr; } auto management::determine_boot_device() -> kstd::shared_ptr -- cgit v1.2.3 From d3e02e6d9de4751910a8c51079e9489bd15948c7 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 30 Apr 2026 18:42:08 +0200 Subject: kernel/acpi: simplify manager constructor --- kernel/src/acpi/manager.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/kernel/src/acpi/manager.cpp b/kernel/src/acpi/manager.cpp index 99c8860..bb895fd 100644 --- a/kernel/src/acpi/manager.cpp +++ b/kernel/src/acpi/manager.cpp @@ -20,22 +20,19 @@ namespace kernel::acpi manager::manager(::acpi::rsdp const & sdp) : m_sdp{&sdp} { - if (m_sdp->signature() != "RSD PTR ") + if (!m_sdp->validate()) { kapi::system::panic("[OS:ACPI] Invalid RSDP signature!"); } - if (m_sdp->revision() >= 2) + m_extended = m_sdp->revision() >= 2; + + if (m_extended) { - auto const xsdp = static_cast<::acpi::xsdp const *>(m_sdp); - if (!xsdp->validate()) + if (!static_cast<::acpi::xsdp const *>(m_sdp)->validate()) { kapi::system::panic("[OS:ACPI] Invalid XSDP signature!"); } - auto physical_extended_table_address = kapi::memory::physical_address{xsdp->table_address()}; - auto linear_extended_table_address = kapi::memory::hhdm_to_linear(physical_extended_table_address); - m_rsdt = static_cast<::acpi::xsdt const *>(linear_extended_table_address); - m_extended = true; } else { @@ -43,10 +40,11 @@ namespace kernel::acpi { kapi::system::panic("[OS:ACPI] Invalid RSDP checksum!"); } - auto physical_root_table_address = kapi::memory::physical_address{m_sdp->table_address()}; - auto linear_root_table_address = kapi::memory::hhdm_to_linear(physical_root_table_address); - m_rsdt = static_cast<::acpi::rsdt const *>(linear_root_table_address); } + + auto physical_address = kapi::memory::physical_address{m_sdp->table_address()}; + auto linear_address = kapi::memory::hhdm_to_linear(physical_address); + m_rsdt = static_cast<::acpi::table_header const *>(linear_address); } auto manager::load_tables() -> bool -- cgit v1.2.3 From bea3a1c32c0a034b201be2c30a69a5f8cdf81896 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 30 Apr 2026 18:58:26 +0200 Subject: kstd: align vector with standard --- libs/kstd/include/kstd/vector | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index e1b8b38..97fdffe 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -37,9 +37,9 @@ namespace kstd //! The type of references to constant elements in this vector. using const_reference = value_type const &; //! The type of pointers to elements in this vector. - using pointer = value_type *; + using pointer = std::allocator_traits::pointer; //! The type of pointers to constant elements in this vector. - using const_pointer = value_type const *; + using const_pointer = std::allocator_traits::const_pointer; //! The type of iterators into this container. using iterator = pointer; //! The type of constant iterators into this container. -- cgit v1.2.3 From 9ff0dffb026eae3b80e3e0b8bbb941e3e3b8b01f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 1 May 2026 10:16:49 +0200 Subject: acpi: silence IASL compiler output --- libs/acpi/CMakeLists.txt | 7 ++++++- libs/acpi/cmake/Scripts/IaslCompile.cmake | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 libs/acpi/cmake/Scripts/IaslCompile.cmake diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index d6d607a..2c4d76d 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -87,7 +87,12 @@ if(BUILD_TESTING) foreach(TABLE IN LISTS TEST_TABLES) add_custom_command(OUTPUT "test_data/${TABLE}.aml" - COMMAND "${IASL_EXE}" -p "test_data/${TABLE}.aml" "${CMAKE_CURRENT_SOURCE_DIR}/test_data/${TABLE}.asl" + COMMAND ${CMAKE_COMMAND} + "-DIASL_EXE=${IASL_EXE}" + "-DIASL_OUTPUT=test_data/${TABLE}.aml" + "-DIASL_INPUT=${CMAKE_CURRENT_SOURCE_DIR}/test_data/${TABLE}.asl" + "-P" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Scripts/IaslCompile.cmake" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/test_data/${TABLE}.asl" COMMENT "Compiling test_data/${TABLE}.asl" VERBATIM diff --git a/libs/acpi/cmake/Scripts/IaslCompile.cmake b/libs/acpi/cmake/Scripts/IaslCompile.cmake new file mode 100644 index 0000000..ff73b34 --- /dev/null +++ b/libs/acpi/cmake/Scripts/IaslCompile.cmake @@ -0,0 +1,16 @@ +execute_process( + COMMAND + "${IASL_EXE}" + "-vs" + "-p" + "${IASL_OUTPUT}" + "${IASL_INPUT}" + OUTPUT_VARIABLE IASL_OUT + ERROR_VARIABLE IASL_ERR + RESULT_VARIABLE IASL_RES +) + +if(NOT IASL_RES EQUAL 0) + message(STATUS "${IASL_OUT}") + message(FATAL_ERROR "${IASL_ERR}") +endif() -- cgit v1.2.3 From bc7389dd19eee57fa2f34cf2e7ba7d1ebfad0878 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 1 May 2026 10:37:09 +0200 Subject: ci: clean up coverage setup --- .lcovrc | 2 +- cmake/Modules/EnableCoverage.cmake | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.lcovrc b/.lcovrc index d0758f0..0d0fdce 100644 --- a/.lcovrc +++ b/.lcovrc @@ -2,6 +2,6 @@ exclude = /usr/include/* exclude = build/bht/_deps/* exclude = tests/* exclude = **.tests.cpp -exclude = kapi/include/kapi/* +exclude = kapi/kapi/* ignore_errors = unused,empty,inconsistent \ No newline at end of file diff --git a/cmake/Modules/EnableCoverage.cmake b/cmake/Modules/EnableCoverage.cmake index 9602869..20be368 100644 --- a/cmake/Modules/EnableCoverage.cmake +++ b/cmake/Modules/EnableCoverage.cmake @@ -1,9 +1,9 @@ -function (enable_coverage TARGET) - target_compile_options("${TARGET}" PRIVATE +function(enable_coverage TARGET) + target_compile_options("${TARGET}" PUBLIC "$<$,$>:-fcondition-coverage>" "$<$,$>:--coverage>" ) - target_link_libraries("${TARGET}" PRIVATE - "$<$,$>:gcov>" + target_link_options("${TARGET}" PUBLIC + "$<$,$>:--coverage>" ) -endfunction () \ No newline at end of file +endfunction() -- cgit v1.2.3 From a958c515e4cfcd5572ac7e2c3a567e832630a12d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 1 May 2026 11:34:54 +0200 Subject: kstd/vector: implement append_range --- libs/kstd/include/kstd/bits/concepts.hpp | 15 +++++++ libs/kstd/include/kstd/vector | 69 +++++++++++++++++++++++++++++++- libs/kstd/tests/src/vector.cpp | 31 ++++++++++++++ 3 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 libs/kstd/include/kstd/bits/concepts.hpp diff --git a/libs/kstd/include/kstd/bits/concepts.hpp b/libs/kstd/include/kstd/bits/concepts.hpp new file mode 100644 index 0000000..74c25cb --- /dev/null +++ b/libs/kstd/include/kstd/bits/concepts.hpp @@ -0,0 +1,15 @@ +#ifndef KSTD_BITS_CONCEPTS_HPP +#define KSTD_BITS_CONCEPTS_HPP + +#include +#include +namespace kstd::bits +{ + + template + concept container_compatible_range = + std::ranges::input_range && std::convertible_to, ValueType>; + +} + +#endif diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 97fdffe..2ecb6a8 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -2,6 +2,7 @@ #define KSTD_VECTOR_HPP #include +#include #include #include @@ -882,6 +883,68 @@ namespace kstd return this->back(); } + //! Append the elements of a given range to this vector. + //! + //! @param range The range of elements to be appended. + //! @tparam SourceRange A container compatible range type. + template SourceRange> + requires requires(Allocator allocator, pointer destination, SourceRange range) { + std::allocator_traits::construct(allocator, destination, *std::ranges::begin(range)); + } + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward, misc-no-recursion) + constexpr auto append_range(SourceRange && range) -> void + { + if constexpr (std::ranges::forward_range || std::ranges::sized_range) + { + auto number_of_elements = static_cast(std::ranges::distance(range)); + + if (!capacity()) + { + reserve(number_of_elements); + } + + if (capacity() - size() >= number_of_elements) + { + uninitialized_copy_with_allocator(std::ranges::begin(range), end(), number_of_elements); + m_size += number_of_elements; + return; + } + + auto new_capacity = m_capacity + std::max(size(), number_of_elements); + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + + uninitialized_move_with_allocator(begin(), new_data, size()); + uninitialized_copy_with_allocator(std::ranges::begin(range), new_data + size(), number_of_elements); + clear_and_deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size + number_of_elements; + return; + } + + auto range_begin = std::ranges::begin(range); + auto range_end = std::ranges::end(range); + + for (auto i = capacity() - size(); i > 0; --i, ++range_begin) + { + emplace_back(*range_begin); + } + + if (range_begin == range_end) + { + return; + } + + auto remainder = vector{get_allocator()}; + for (; range_begin != range_end; ++range_begin) + { + remainder.emplace_back(*static_cast>(range_begin)); + } + reserve(size() + std::max(size(), remainder.size())); + append_range(remainder); + } + //! Remove the last element of this vector. //! //! If this vector is empty, the behavior is undefined. @@ -950,7 +1013,8 @@ namespace kstd //! @param from The start of the source range. //! @param to The start of the target range inside this vector. //! @param count The number of elements to copy - constexpr auto uninitialized_copy_with_allocator(const_iterator from, iterator to, size_type count) + template + constexpr auto uninitialized_copy_with_allocator(SourceIterator from, iterator to, size_type count) { for (auto i = 0uz; i < count; ++i) { @@ -963,7 +1027,8 @@ namespace kstd //! @param from The start of the source range. //! @param to The start of the target range inside this vector. //! @param count The number of elements to copy - constexpr auto uninitialized_move_with_allocator(iterator from, iterator to, size_type count) + template + constexpr auto uninitialized_move_with_allocator(SourceIterator from, iterator to, size_type count) { for (auto i = 0uz; i < count; ++i) { diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index b838f42..bbbd3d1 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -516,6 +516,21 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE(it == v.begin()); } } + + WHEN("appending a range") + { + auto const range = std::views::iota(0, 3); + v.append_range(range); + + THEN("the size and capacity increase and the elements are appended") + { + REQUIRE(v.size() == 3); + REQUIRE(v.capacity() >= 3); + REQUIRE(v[0] == 0); + REQUIRE(v[1] == 1); + REQUIRE(v[2] == 2); + } + } } GIVEN("A populated vector") @@ -832,6 +847,22 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE(it == v.begin() + 1); } } + + WHEN("appending a range") + { + auto initial_size = v.size(); + v.append_range(std::views::iota(0, 3)); + + THEN("capacity and size are increased and the elements are appended") + { + REQUIRE(v.capacity() >= initial_capacity); + REQUIRE(v.size() == initial_size + 3); + REQUIRE(v[initial_size + 0] == 0); + REQUIRE(v[initial_size + 1] == 1); + REQUIRE(v[initial_size + 2] == 2); + } + } + } } -- cgit v1.2.3 From 52b2fd214c8484a55c409c72c90929428e261949 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 1 May 2026 11:35:10 +0200 Subject: kstd: vector add resize tests --- libs/kstd/tests/src/vector.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index bbbd3d1..edf47d7 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -531,6 +531,34 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE(v[2] == 2); } } + + WHEN("resizing the vector to a greater size") + { + v.resize(3); + + THEN("the size and capacity increase and the elements are value initialized") + { + REQUIRE(v.size() == 3); + REQUIRE(v.capacity() >= 3); + REQUIRE(v[0] == 0); + REQUIRE(v[1] == 0); + REQUIRE(v[2] == 0); + } + } + + WHEN("resizing the vector to a greater size with initial value") + { + v.resize(3, 2); + + THEN("the size and capacity increase and the elements are initialized to the given value") + { + REQUIRE(v.size() == 3); + REQUIRE(v.capacity() >= 3); + REQUIRE(v[0] == 2); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 2); + } + } } GIVEN("A populated vector") @@ -863,6 +891,46 @@ SCENARIO("Vector modifiers", "[vector]") } } + WHEN("resizing the vector to a greater size") + { + auto initial_size = v.size(); + v.resize(initial_size + 3); + + THEN("the size and capacity increase and the elements are value initialized") + { + REQUIRE(v.size() == initial_size + 3); + REQUIRE(v.capacity() >= initial_size + 3); + REQUIRE(v[initial_size + 0] == 0); + REQUIRE(v[initial_size + 1] == 0); + REQUIRE(v[initial_size + 2] == 0); + } + } + + WHEN("resizing the vector to a greater size with initial value") + { + auto initial_size = v.size(); + v.resize(initial_size + 3, 2); + + THEN("the size and capacity increase and the elements are initialized to the given value") + { + REQUIRE(v.size() == initial_size + 3); + REQUIRE(v.capacity() >= initial_size + 3); + REQUIRE(v[initial_size + 0] == 2); + REQUIRE(v[initial_size + 1] == 2); + REQUIRE(v[initial_size + 2] == 2); + } + } + + WHEN("resizing the vector to a smaller size") + { + v.resize(1); + + THEN("the size decreases and the elements are destroyed") + { + REQUIRE(v.size() == 1); + REQUIRE(v[0] == 10); + } + } } } -- cgit v1.2.3 From 338d9b2b6fc517df2135089699234232495324d6 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 1 May 2026 13:11:37 +0200 Subject: kstd/vector: improve append_range tests --- libs/kstd/tests/include/kstd/tests/test_types.hpp | 27 +++++++- libs/kstd/tests/src/vector.cpp | 77 +++++++++++++++++++++-- 2 files changed, 98 insertions(+), 6 deletions(-) diff --git a/libs/kstd/tests/include/kstd/tests/test_types.hpp b/libs/kstd/tests/include/kstd/tests/test_types.hpp index 9207ee9..8231fd3 100644 --- a/libs/kstd/tests/include/kstd/tests/test_types.hpp +++ b/libs/kstd/tests/include/kstd/tests/test_types.hpp @@ -239,17 +239,29 @@ namespace kstd::tests struct test_input_iterator { using iterator_concept = std::input_iterator_tag; + using iterator_category = std::input_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = int; + using reference = int const &; + using pointer = int const *; //! The current element pointed to by the iterator. int const * current; + //! The number of elements in the range. + std::size_t count; + + explicit test_input_iterator() + : current{nullptr} + , count{0} + {} //! Construct a new test input iterator. //! //! @param current The current element pointed to by the iterator. - constexpr explicit test_input_iterator(int const * current) + //! @param count The number of elements in the range. + explicit test_input_iterator(int const * current, std::size_t count) : current{current} + , count{count} {} //! Dereference the iterator to get the current element. @@ -266,6 +278,7 @@ namespace kstd::tests auto operator++() -> test_input_iterator & { ++current; + --count; return *this; } @@ -281,7 +294,17 @@ namespace kstd::tests //! @return True if the two test input iterators are equal, false otherwise. [[nodiscard]] auto operator==(test_input_iterator const & other) const -> bool { - return current == other.current; + if (current == nullptr && other.current == nullptr) + { + return true; + } + + if (current == nullptr || other.current == nullptr) + { + return count == other.count; + } + + return current == other.current && count == other.count; } }; diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index edf47d7..a02ebf0 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -522,16 +522,77 @@ SCENARIO("Vector modifiers", "[vector]") auto const range = std::views::iota(0, 3); v.append_range(range); - THEN("the size and capacity increase and the elements are appended") + THEN("the size increases") { REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are appended") + { REQUIRE(v[0] == 0); REQUIRE(v[1] == 1); REQUIRE(v[2] == 2); } } + WHEN("appending from an input range") + { + auto const arr = std::array{1, 2, 3}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; + + v.append_range(std::ranges::subrange{first, last}); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are appended") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + + WHEN("appending from an input range with sufficient capacity") + { + v.reserve(3); + auto const arr = std::array{1, 2, 3}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; + + v.append_range(std::ranges::subrange{first, last}); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity stays the same") + { + REQUIRE(v.capacity() == 3); + } + + THEN("the elements are appended") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + WHEN("resizing the vector to a greater size") { v.resize(3); @@ -881,10 +942,18 @@ SCENARIO("Vector modifiers", "[vector]") auto initial_size = v.size(); v.append_range(std::views::iota(0, 3)); - THEN("capacity and size are increased and the elements are appended") + THEN("capacity is increased") { REQUIRE(v.capacity() >= initial_capacity); + } + + THEN("size is increased") + { REQUIRE(v.size() == initial_size + 3); + } + + THEN("the elements are appended") + { REQUIRE(v[initial_size + 0] == 0); REQUIRE(v[initial_size + 1] == 1); REQUIRE(v[initial_size + 2] == 2); @@ -1290,8 +1359,8 @@ SCENARIO("Vector advanced construction", "[vector]") WHEN("constructing from input iterators") { auto const arr = std::array{1, 2, 3}; - auto const first = kstd::tests::test_input_iterator{arr.data()}; - auto const last = kstd::tests::test_input_iterator{arr.data() + arr.size()}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; auto v = kstd::vector(first, last); -- cgit v1.2.3 From 2773e457773e3c88c212d6403950cef1fc96f880 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 1 May 2026 17:22:53 +0200 Subject: kstd/vector: implement insert_range --- libs/kstd/include/kstd/vector | 73 ++++++++++- libs/kstd/tests/src/vector.cpp | 291 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 362 insertions(+), 2 deletions(-) diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector index 2ecb6a8..c714957 100644 --- a/libs/kstd/include/kstd/vector +++ b/libs/kstd/include/kstd/vector @@ -147,8 +147,8 @@ namespace kstd //! //! template - requires(std::ranges::input_range && std::ranges::sized_range && - std::convertible_to, ValueType>) + requires((std::ranges::forward_range || std::ranges::sized_range) && + kstd::bits::container_compatible_range) constexpr vector(kstd::from_range_t, Range && range, allocator_type const & allocator = allocator_type{}) : m_allocator{allocator} , m_size{std::ranges::size(range)} @@ -729,6 +729,75 @@ namespace kstd return begin() + prefix_size; } + //! Insert the element of a given range into the vector at a given position. + //! + //! @param range The source range to insert elements from. + //! @tparam SourceRange A container compatible range type. + template + requires requires(allocator_type allocator, pointer destination, SourceRange range) { + requires kstd::bits::container_compatible_range; + requires std::move_constructible; + requires std::is_move_assignable_v; + requires std::swappable; + std::allocator_traits::construct(allocator, destination, *std::ranges::begin(range)); + } + // NOLINTNEXTLINE(misc-no-recursion) + constexpr auto insert_range(const_iterator position, SourceRange && range) -> iterator + { + auto prefix_size = std::ranges::distance(begin(), position); + + if (position == end()) + { + append_range(std::forward(range)); + return begin() + prefix_size; + } + + if constexpr (std::ranges::forward_range || std::ranges::sized_range) + { + auto number_of_elements = static_cast(std::ranges::distance(range)); + if (!number_of_elements) + { + return begin() + prefix_size; + } + + if (capacity() - size() < number_of_elements) + { + reserve(size() + std::max(size(), number_of_elements)); + } + + auto insert_position = begin() + prefix_size; + auto suffix_size = static_cast(std::ranges::distance(insert_position, end())); + + if (number_of_elements >= suffix_size) + { + uninitialized_move_with_allocator(insert_position, insert_position + number_of_elements, suffix_size); + auto result = std::ranges::copy_n(std::ranges::begin(range), suffix_size, insert_position); + uninitialized_copy_with_allocator(std::move(result.in), end(), number_of_elements - suffix_size); + } + else + { + uninitialized_move_with_allocator(end() - number_of_elements, end(), number_of_elements); + std::ranges::move_backward(insert_position, end() - number_of_elements, end()); + std::ranges::copy_n(std::ranges::begin(range), number_of_elements, insert_position); + } + + m_size += number_of_elements; + return insert_position; + } + + auto range_begin = std::ranges::begin(range); + auto range_end = std::ranges::end(range); + + auto remainder = vector{get_allocator()}; + for (; range_begin != range_end; ++range_begin) + { + remainder.emplace_back(*static_cast>(range_begin)); + } + reserve(size() + std::max(size(), remainder.size())); + + return insert_range(begin() + prefix_size, remainder); + } + template constexpr auto emplace(const_iterator position, Args &&... args) -> iterator { diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index a02ebf0..fd44437 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -620,6 +620,92 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE(v[2] == 2); } } + + WHEN("inserting a range at the beginning") + { + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.begin(), arr); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting a range at the end") + { + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.end(), arr); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting from an input range without sufficient capacity") + { + auto const arr = std::array{1, 2, 3}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; + auto it = v.insert_range(v.begin(), std::ranges::subrange{first, last}); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } } GIVEN("A populated vector") @@ -1000,6 +1086,211 @@ SCENARIO("Vector modifiers", "[vector]") REQUIRE(v[0] == 10); } } + + WHEN("inserting an empty range") + { + auto initial_size = v.size(); + auto it = v.insert_range(v.begin(), std::views::empty); + + THEN("the size does not change") + { + REQUIRE(v.size() == initial_size); + } + + THEN("the capacity does not change") + { + REQUIRE(v.capacity() == initial_capacity); + } + + THEN("the content is unchanged") + { + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + } + + THEN("the returned iterator points to the position of insertion") + { + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting a range at the beginning") + { + auto initial_size = v.size(); + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.begin(), arr); + + THEN("the size increases") + { + REQUIRE(v.size() == initial_size + 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= initial_size + 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + REQUIRE(v[3] == 10); + REQUIRE(v[4] == 20); + REQUIRE(v[5] == 30); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting a range at the end") + { + auto initial_size = v.size(); + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.end(), arr); + + THEN("the size increases") + { + REQUIRE(v.size() == initial_size + 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= initial_size + 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 1); + REQUIRE(v[4] == 2); + REQUIRE(v[5] == 3); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin() + initial_size); + } + } + + WHEN("inserting a range that causes reallocation") + { + auto initial_size = v.size(); + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.begin() + 1, arr); + + THEN("the size increases") + { + REQUIRE(v.size() == initial_size + 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= initial_size + 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 1); + REQUIRE(v[2] == 2); + REQUIRE(v[3] == 3); + REQUIRE(v[4] == 20); + REQUIRE(v[5] == 30); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting fewer elements than the suffix without reallocation") + { + v.reserve(10); + auto const capacity = v.capacity(); + auto const arr = std::array{1}; + auto it = v.insert_range(v.begin() + 1, arr); + + THEN("the elements are correctly placed") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 1); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + } + + THEN("no reallocation occurs") + { + REQUIRE(v.capacity() == capacity); + } + + THEN("the returned iterator points to the inserted element") + { + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting fewer elements than the suffix without sufficient capacity") + { + v.shrink_to_fit(); + auto const arr = std::array{1}; + auto it = v.insert_range(v.begin() + 1, arr); + + THEN("the elements are correctly placed") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 1); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + } + + THEN("the returned iterator points to the inserted element") + { + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting from an input range without sufficient capacity") + { + auto const arr = std::array{1, 2, 3}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; + auto it = v.insert_range(v.begin(), std::ranges::subrange{first, last}); + + THEN("the size increases") + { + REQUIRE(v.size() == 6); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 6); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + REQUIRE(v[3] == 10); + REQUIRE(v[4] == 20); + REQUIRE(v[5] == 30); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } } } -- cgit v1.2.3 From 3ba99c5c8643be35a2337c120b226b17ddfcf58f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 1 May 2026 17:27:00 +0200 Subject: kstd/vector: add missing emplace test --- libs/kstd/tests/src/vector.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp index fd44437..4fb3559 100644 --- a/libs/kstd/tests/src/vector.cpp +++ b/libs/kstd/tests/src/vector.cpp @@ -761,6 +761,26 @@ SCENARIO("Vector modifiers", "[vector]") } } + WHEN("emplace is called with an iterator and sufficient capacity") + { + v.reserve(v.size() + 1); + + auto it = v.emplace(v.begin() + 2, 25); + + THEN("the element is inserted and the size and capacity increase") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() >= initial_capacity); + REQUIRE(v.at(2) == 25); + } + + THEN("the returned iterator points to the inserted element") + { + REQUIRE(it == v.cbegin() + 2); + REQUIRE(*it == 25); + } + } + WHEN("push_back is called with a reference to an internal element") { v.shrink_to_fit(); -- cgit v1.2.3 From b1405c44434fbfa535348d310aa3243203aefb06 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 1 May 2026 19:20:30 +0200 Subject: ide: update devcontainer --- .devcontainer/x86-64/devcontainer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.devcontainer/x86-64/devcontainer.json b/.devcontainer/x86-64/devcontainer.json index 18e6419..6a5da88 100644 --- a/.devcontainer/x86-64/devcontainer.json +++ b/.devcontainer/x86-64/devcontainer.json @@ -1,11 +1,11 @@ { "name": "TeachOS on x86-64", - "image": "registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:16.1.0-1", + "image": "registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:16.1.0-2-py3.14", "features": { "ghcr.io/devcontainers/features/git:1": {}, "ghcr.io/devcontainers/features/git-lfs:1": {}, "ghcr.io/devcontainers-extra/features/apt-packages:1": { - "packages": "acpica-tools,build-essential,clang-tidy,clangd,cmake,grub2-common,grub-pc,mtools,ninja-build,qemu-system-x86,ssh,xorriso,gdb" + "packages": "acpica-tools,build-essential,clang-tidy,clangd,cmake,gdb,grub2-common,grub-pc,mtools,ninja-build,qemu-system-x86,ssh,wget,xorriso" } }, "customizations": { -- cgit v1.2.3 From 61949538ad6d114c1c2a788928a0b9706b4efc76 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 1 May 2026 20:46:21 +0200 Subject: debug: add support for kstd pretty printing --- .gitignore | 2 ++ .vscode/launch.json | 4 ++++ scripts/gdb/kstd/__init__.py | 17 +++++++++++++++++ scripts/gdb/kstd/std_types.py | 15 +++++++++++++++ scripts/gdb/kstd/string.py | 26 ++++++++++++++++++++++++++ scripts/gdb/kstd/vector.py | 22 ++++++++++++++++++++++ scripts/gdb/load_kstd.py | 14 ++++++++++++++ 7 files changed, 100 insertions(+) create mode 100644 scripts/gdb/kstd/__init__.py create mode 100644 scripts/gdb/kstd/std_types.py create mode 100644 scripts/gdb/kstd/string.py create mode 100644 scripts/gdb/kstd/vector.py create mode 100644 scripts/gdb/load_kstd.py diff --git a/.gitignore b/.gitignore index e96f481..4295aa6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ qemu-*-*.log /desktop.ini coverage.info + +__pycache__/ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 6478c96..4d15c6f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -25,6 +25,10 @@ { "description": "Load symbols", "text": "-file-exec-and-symbols ${command:cmake.buildDirectory}/bin/${command:cmake.buildType}/kernel.sym" + }, + { + "description": "Load custom Python helpers", + "text": "-interpreter-exec console \"source ${workspaceFolder}/scripts/gdb/load_kstd.py\"" } ] } diff --git a/scripts/gdb/kstd/__init__.py b/scripts/gdb/kstd/__init__.py new file mode 100644 index 0000000..7dc1596 --- /dev/null +++ b/scripts/gdb/kstd/__init__.py @@ -0,0 +1,17 @@ +import gdb.printing + +from .vector import KstdVectorPrinter +from .string import KstdStringPrinter +from .std_types import StdBytePrinter + + +def build_pretty_printers(): + pp = gdb.printing.RegexpCollectionPrettyPrinter("kstd") + pp.add_printer("vector", "^kstd::vector<.*>$", KstdVectorPrinter) + pp.add_printer("string", "^kstd::string$", KstdStringPrinter) + pp.add_printer("std_byte", "^std::byte$", StdBytePrinter) + return pp + + +def register_printers(objfile): + gdb.printing.register_pretty_printer(objfile, build_pretty_printers(), replace=True) diff --git a/scripts/gdb/kstd/std_types.py b/scripts/gdb/kstd/std_types.py new file mode 100644 index 0000000..78d094c --- /dev/null +++ b/scripts/gdb/kstd/std_types.py @@ -0,0 +1,15 @@ +import gdb + + +class StdBytePrinter: + + def __init__(self, val): + self.val = val + + def to_string(self): + try: + uint8_type = gdb.lookup_type("unsigned char") + numeric_value = int(self.val.cast(uint8_type)) + return f"{numeric_value:#04x}" + except gdb.error: + return f"" diff --git a/scripts/gdb/kstd/string.py b/scripts/gdb/kstd/string.py new file mode 100644 index 0000000..6fa9996 --- /dev/null +++ b/scripts/gdb/kstd/string.py @@ -0,0 +1,26 @@ +import gdb + + +class KstdStringPrinter: + + def __init__(self, val): + self.val = val + + def to_string(self): + storage = self.val["m_storage"] + storage_size = int(storage["m_size"]) + + if storage_size <= 0: + return '""' + + data_pointer = storage["m_data"] + string_length = storage_size - 1 + + try: + string_data = data_pointer.string(encoding="utf-8", length=string_length) + return f"{string_data}" + except gdb.error: + return "" + + def display_hint(self): + return "string" diff --git a/scripts/gdb/kstd/vector.py b/scripts/gdb/kstd/vector.py new file mode 100644 index 0000000..b42aeae --- /dev/null +++ b/scripts/gdb/kstd/vector.py @@ -0,0 +1,22 @@ +import gdb + + +class KstdVectorPrinter: + + def __init__(self, val): + self.val = val + self.type = val.type.template_argument(0) + + def to_string(self): + size = int(self.val(["m_size"])) + capacity = int(self.val(["m_capacity"])) + return f"kstd::vector<{self.type}> of length {size}, capacity {capacity}" + + def children(self): + size = int(self.val["m_size"]) + data_pointer = self.val["m_data"] + for i in range(size): + yield (f"[{i}]", (data_pointer + i).dereference()) + + def display_hint(self): + return "array" diff --git a/scripts/gdb/load_kstd.py b/scripts/gdb/load_kstd.py new file mode 100644 index 0000000..f3adce1 --- /dev/null +++ b/scripts/gdb/load_kstd.py @@ -0,0 +1,14 @@ +import sys +import os +import gdb + +script_dir = os.path.dirname(os.path.abspath(__file__)) + +if script_dir not in sys.path: + sys.path.insert(0, script_dir) + +import kstd + +kstd.register_printers(gdb.current_objfile()) + +gdb.write("Loaded kstd pretty printers.\n") -- cgit v1.2.3 From 128e4a2adca6721254e8ffd290b670cc58b7a898 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 1 May 2026 21:00:39 +0200 Subject: debug: add support for smart pointer pretty printing --- scripts/gdb/kstd/__init__.py | 3 +++ scripts/gdb/kstd/smart_pointers.py | 50 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 scripts/gdb/kstd/smart_pointers.py diff --git a/scripts/gdb/kstd/__init__.py b/scripts/gdb/kstd/__init__.py index 7dc1596..fc5e8fb 100644 --- a/scripts/gdb/kstd/__init__.py +++ b/scripts/gdb/kstd/__init__.py @@ -3,6 +3,7 @@ import gdb.printing from .vector import KstdVectorPrinter from .string import KstdStringPrinter from .std_types import StdBytePrinter +from .smart_pointers import KstdUniquePtrPrinter, KstdSharedPtrPrinter def build_pretty_printers(): @@ -10,6 +11,8 @@ def build_pretty_printers(): pp.add_printer("vector", "^kstd::vector<.*>$", KstdVectorPrinter) pp.add_printer("string", "^kstd::string$", KstdStringPrinter) pp.add_printer("std_byte", "^std::byte$", StdBytePrinter) + pp.add_printer("unique_ptr", "^kstd::unique_ptr<.*>$", KstdUniquePtrPrinter) + pp.add_printer("shared_ptr", "^kstd::shared_ptr<.*>$", KstdSharedPtrPrinter) return pp diff --git a/scripts/gdb/kstd/smart_pointers.py b/scripts/gdb/kstd/smart_pointers.py new file mode 100644 index 0000000..9af16fd --- /dev/null +++ b/scripts/gdb/kstd/smart_pointers.py @@ -0,0 +1,50 @@ +import gdb + + +class KstdUniquePtrPrinter: + + def __init__(self, val): + self.val = val + self.type = val.type.template_argument(0) + + def to_string(self): + pointer = self.val["pointer"] + if int(pointer) == 0: + return f"kstd::unique_ptr<{self.type}> (empty)" + return f"kstd::unique_ptr<{self.type}>" + + def children(self): + pointer = self.val["m_ptr"] + if int(pointer) != 0: + yield ("get()", pointer) + yield ("*get()", pointer.dereference()) + + +class KstdSharedPtrPrinter: + + def __init__(self, val): + self.val = val + self.type = val.type.template_argument(0) + + def to_string(self): + pointer = self.val["pointer"] + cb = self.val["control_block"] + + if int(pointer) == 0 or int(cb) == 0: + return f"kstd::shared_ptr<{self.type}> (empty)" + + strong_refs = int(cb["m_strong_refs"]) + weak_refs = int(cb["m_weak_refs"]) + + return f"kstd::shared_ptr<{self.type}> (use_count={strong_refs}, weak_count={weak_refs})" + + def children(self): + pointer = self.val["pointer"] + control_block = self.val["control"] + + if int(pointer) != 0: + yield ("get()", pointer) + yield ("*get()", pointer.dereference()) + + if int(control_block) != 0: + yield ("[control_block]", control_block.dereference()) -- cgit v1.2.3 From d55ebee48dbf06ed59759f6c78c75c264bf6b8c5 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 2 May 2026 13:29:38 +0200 Subject: debug: fix smart pointer pretty printers --- scripts/gdb/kstd/smart_pointers.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/gdb/kstd/smart_pointers.py b/scripts/gdb/kstd/smart_pointers.py index 9af16fd..6e4a4f9 100644 --- a/scripts/gdb/kstd/smart_pointers.py +++ b/scripts/gdb/kstd/smart_pointers.py @@ -14,7 +14,7 @@ class KstdUniquePtrPrinter: return f"kstd::unique_ptr<{self.type}>" def children(self): - pointer = self.val["m_ptr"] + pointer = self.val["pointer"] if int(pointer) != 0: yield ("get()", pointer) yield ("*get()", pointer.dereference()) @@ -28,13 +28,13 @@ class KstdSharedPtrPrinter: def to_string(self): pointer = self.val["pointer"] - cb = self.val["control_block"] + control_block = self.val["control"] - if int(pointer) == 0 or int(cb) == 0: + if int(pointer) == 0 or int(control_block) == 0: return f"kstd::shared_ptr<{self.type}> (empty)" - strong_refs = int(cb["m_strong_refs"]) - weak_refs = int(cb["m_weak_refs"]) + strong_refs = int(control_block["shared_count"]["_M_i"]) + weak_refs = int(control_block["weak_count"]["_M_i"]) return f"kstd::shared_ptr<{self.type}> (use_count={strong_refs}, weak_count={weak_refs})" -- cgit v1.2.3 From 596550c8002c2a1fa375ff7f39b6b9707b9f042d Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 2 May 2026 14:49:28 +0200 Subject: debug: add kapi formatter support --- .vscode/launch.json | 2 +- scripts/gdb/kapi/__init__.py | 15 +++++++++++++++ scripts/gdb/kapi/address.py | 33 +++++++++++++++++++++++++++++++++ scripts/gdb/load.py | 16 ++++++++++++++++ scripts/gdb/load_kstd.py | 14 -------------- 5 files changed, 65 insertions(+), 15 deletions(-) create mode 100644 scripts/gdb/kapi/__init__.py create mode 100644 scripts/gdb/kapi/address.py create mode 100644 scripts/gdb/load.py delete mode 100644 scripts/gdb/load_kstd.py diff --git a/.vscode/launch.json b/.vscode/launch.json index 4d15c6f..d5b2401 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -28,7 +28,7 @@ }, { "description": "Load custom Python helpers", - "text": "-interpreter-exec console \"source ${workspaceFolder}/scripts/gdb/load_kstd.py\"" + "text": "-interpreter-exec console \"source ${workspaceFolder}/scripts/gdb/load.py\"" } ] } diff --git a/scripts/gdb/kapi/__init__.py b/scripts/gdb/kapi/__init__.py new file mode 100644 index 0000000..c37c7b7 --- /dev/null +++ b/scripts/gdb/kapi/__init__.py @@ -0,0 +1,15 @@ +import gdb.printing + +from .address import KapiMemoryAddressPrinter + + +def build_pretty_printers(): + pp = gdb.printing.RegexpCollectionPrettyPrinter("kapi") + pp.add_printer( + "kapi_memory_address", "^kapi::memory::address<.*>$", KapiMemoryAddressPrinter + ) + return pp + + +def register_printers(objfile): + gdb.printing.register_pretty_printer(objfile, build_pretty_printers(), replace=True) diff --git a/scripts/gdb/kapi/address.py b/scripts/gdb/kapi/address.py new file mode 100644 index 0000000..677c9aa --- /dev/null +++ b/scripts/gdb/kapi/address.py @@ -0,0 +1,33 @@ +import gdb + + +class KapiMemoryAddressPrinter: + """Print kapi::MemoryAddress.""" + + def __init__(self, val): + self.val = val + self.address_type = val.type.template_argument(0) + + def to_string(self): + try: + raw_address = int(self.val["m_value"]) + type_string = str(self.address_type) + + if "linear" in type_string: + suffix = "%lin" + elif "physical" in type_string: + suffix = "%phy" + else: + suffix = "%???" + + return f"{raw_address:#018x}{suffix}" + except Exception as e: + return f"{self.val}: {e}" + + def children(self): + if "linear" in str(self.address_type): + yield ( + "std::byte *", + self.val["m_value"].cast(gdb.lookup_type("std::byte").pointer()), + ) + yield ("m_value", self.val["m_value"]) diff --git a/scripts/gdb/load.py b/scripts/gdb/load.py new file mode 100644 index 0000000..ec512b7 --- /dev/null +++ b/scripts/gdb/load.py @@ -0,0 +1,16 @@ +import sys +import os +import gdb + +script_dir = os.path.dirname(os.path.abspath(__file__)) + +if script_dir not in sys.path: + sys.path.insert(0, script_dir) + +import kstd +import kapi + +kstd.register_printers(gdb.current_objfile()) +kapi.register_printers(gdb.current_objfile()) + +gdb.write("Loaded TeachOS pretty printers.\n") diff --git a/scripts/gdb/load_kstd.py b/scripts/gdb/load_kstd.py deleted file mode 100644 index f3adce1..0000000 --- a/scripts/gdb/load_kstd.py +++ /dev/null @@ -1,14 +0,0 @@ -import sys -import os -import gdb - -script_dir = os.path.dirname(os.path.abspath(__file__)) - -if script_dir not in sys.path: - sys.path.insert(0, script_dir) - -import kstd - -kstd.register_printers(gdb.current_objfile()) - -gdb.write("Loaded kstd pretty printers.\n") -- cgit v1.2.3 From ff5d5458df2e69c7fca43d78f02ad1c5837a4a22 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 2 May 2026 15:17:01 +0200 Subject: debug: fix vector pretty printer --- scripts/gdb/kstd/vector.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/gdb/kstd/vector.py b/scripts/gdb/kstd/vector.py index b42aeae..597ffdc 100644 --- a/scripts/gdb/kstd/vector.py +++ b/scripts/gdb/kstd/vector.py @@ -8,9 +8,9 @@ class KstdVectorPrinter: self.type = val.type.template_argument(0) def to_string(self): - size = int(self.val(["m_size"])) - capacity = int(self.val(["m_capacity"])) - return f"kstd::vector<{self.type}> of length {size}, capacity {capacity}" + size = int(self.val["m_size"]) + capacity = int(self.val["m_capacity"]) + return f"kstd::vector of length {size}, capacity {capacity}" def children(self): size = int(self.val["m_size"]) -- cgit v1.2.3 From 44961491f7c3097d1182d68f9c34929275d8655c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 2 May 2026 15:17:21 +0200 Subject: debug: optimize string pretty printer --- scripts/gdb/kstd/string.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/gdb/kstd/string.py b/scripts/gdb/kstd/string.py index 6fa9996..8230b21 100644 --- a/scripts/gdb/kstd/string.py +++ b/scripts/gdb/kstd/string.py @@ -17,8 +17,9 @@ class KstdStringPrinter: string_length = storage_size - 1 try: - string_data = data_pointer.string(encoding="utf-8", length=string_length) - return f"{string_data}" + if hasattr(data_pointer, "lazy_string"): + return data_pointer.lazy_string(encoding="utf-8", length=string_length) + return data_pointer.string(encoding="utf-8", length=string_length) except gdb.error: return "" -- cgit v1.2.3 From e8904a65e49dea8ce2e1d58eaa3fb60c7cd3443e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 2 May 2026 15:17:33 +0200 Subject: debug: load pretty printers in tests --- .vscode/settings.json | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 79c4971..4a70d18 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -80,6 +80,17 @@ "cwd": "${cwd}", "env": "${envObject}", "environment": "${envObjArray}", - "sourceFileMap": "${sourceFileMapObj}" + "sourceFileMap": "${sourceFileMapObj}", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Load custom Python helpers", + "text": "-interpreter-exec console \"source ${workspaceFolder}/scripts/gdb/load.py\"" + } + ], }, } \ No newline at end of file -- cgit v1.2.3 From 3ab0a15fb6aba0ad9516da69589b9da8dbd63a8e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 2 May 2026 17:12:21 +0200 Subject: libs: adopt p1204 layout for kstd --- .clang-tidy | 2 +- .vscode/settings.json | 5 +- libs/kstd/CMakeLists.txt | 34 +- libs/kstd/include/kstd/allocator | 64 - libs/kstd/include/kstd/asm_ptr | 78 - libs/kstd/include/kstd/bits/concepts.hpp | 15 - libs/kstd/include/kstd/bits/flat_map.hpp | 186 -- libs/kstd/include/kstd/bits/format/arg.hpp | 75 - libs/kstd/include/kstd/bits/format/args.hpp | 160 -- libs/kstd/include/kstd/bits/format/context.hpp | 65 - libs/kstd/include/kstd/bits/format/error.hpp | 24 - libs/kstd/include/kstd/bits/format/formatter.hpp | 92 - .../include/kstd/bits/format/formatter/bool.hpp | 83 - .../include/kstd/bits/format/formatter/byte.hpp | 23 - .../include/kstd/bits/format/formatter/char.hpp | 94 - .../include/kstd/bits/format/formatter/cstring.hpp | 29 - .../kstd/bits/format/formatter/integral.hpp | 204 -- .../kstd/bits/format/formatter/ordering.hpp | 111 -- .../include/kstd/bits/format/formatter/pointer.hpp | 42 - .../include/kstd/bits/format/formatter/range.hpp | 32 - .../kstd/bits/format/formatter/string_view.hpp | 41 - libs/kstd/include/kstd/bits/format/fwd.hpp | 23 - .../include/kstd/bits/format/output_buffer.hpp | 32 - .../include/kstd/bits/format/parse_context.hpp | 109 -- libs/kstd/include/kstd/bits/format/specifiers.hpp | 205 -- libs/kstd/include/kstd/bits/format/string.hpp | 148 -- libs/kstd/include/kstd/bits/format/vformat.hpp | 99 - libs/kstd/include/kstd/bits/observer_ptr.hpp | 163 -- libs/kstd/include/kstd/bits/print_sink.hpp | 17 - libs/kstd/include/kstd/bits/shared_ptr.hpp | 648 ------- libs/kstd/include/kstd/bits/unique_ptr.hpp | 199 -- libs/kstd/include/kstd/cstring | 21 - libs/kstd/include/kstd/ext/bitfield_enum | 65 - libs/kstd/include/kstd/flat_map | 365 ---- libs/kstd/include/kstd/format | 22 - libs/kstd/include/kstd/memory | 8 - libs/kstd/include/kstd/mutex | 101 - libs/kstd/include/kstd/os/error.hpp | 34 - libs/kstd/include/kstd/os/print.hpp | 14 - libs/kstd/include/kstd/print | 69 - libs/kstd/include/kstd/ranges | 21 - libs/kstd/include/kstd/stack | 192 -- libs/kstd/include/kstd/string | 380 ---- libs/kstd/include/kstd/units | 149 -- libs/kstd/include/kstd/vector | 1154 ----------- libs/kstd/kstd/allocator | 64 + libs/kstd/kstd/asm_ptr | 78 + libs/kstd/kstd/bits/concepts.hpp | 15 + libs/kstd/kstd/bits/flat_map.hpp | 186 ++ libs/kstd/kstd/bits/format/arg.hpp | 75 + libs/kstd/kstd/bits/format/args.hpp | 160 ++ libs/kstd/kstd/bits/format/context.hpp | 65 + libs/kstd/kstd/bits/format/error.hpp | 24 + libs/kstd/kstd/bits/format/formatter.hpp | 92 + libs/kstd/kstd/bits/format/formatter/bool.hpp | 83 + libs/kstd/kstd/bits/format/formatter/byte.hpp | 23 + libs/kstd/kstd/bits/format/formatter/char.hpp | 94 + libs/kstd/kstd/bits/format/formatter/cstring.hpp | 29 + libs/kstd/kstd/bits/format/formatter/integral.hpp | 204 ++ libs/kstd/kstd/bits/format/formatter/ordering.hpp | 111 ++ libs/kstd/kstd/bits/format/formatter/pointer.hpp | 42 + libs/kstd/kstd/bits/format/formatter/range.hpp | 32 + .../kstd/bits/format/formatter/string_view.hpp | 41 + libs/kstd/kstd/bits/format/fwd.hpp | 23 + libs/kstd/kstd/bits/format/output_buffer.hpp | 32 + libs/kstd/kstd/bits/format/parse_context.hpp | 109 ++ libs/kstd/kstd/bits/format/specifiers.hpp | 205 ++ libs/kstd/kstd/bits/format/string.hpp | 148 ++ libs/kstd/kstd/bits/format/vformat.hpp | 99 + libs/kstd/kstd/bits/observer_ptr.hpp | 163 ++ libs/kstd/kstd/bits/observer_ptr.test.cpp | 360 ++++ libs/kstd/kstd/bits/print_sink.hpp | 17 + libs/kstd/kstd/bits/shared_ptr.hpp | 648 +++++++ libs/kstd/kstd/bits/unique_ptr.hpp | 199 ++ libs/kstd/kstd/cstring | 21 + libs/kstd/kstd/ext/bitfield_enum | 65 + libs/kstd/kstd/flat_map | 365 ++++ libs/kstd/kstd/flat_map.test.cpp | 314 +++ libs/kstd/kstd/format | 22 + libs/kstd/kstd/format.test.cpp | 114 ++ libs/kstd/kstd/libc/stdlib.cpp | 19 + libs/kstd/kstd/libc/string.cpp | 78 + libs/kstd/kstd/memory | 8 + libs/kstd/kstd/mutex | 101 + libs/kstd/kstd/mutex.cpp | 38 + libs/kstd/kstd/os/error.cpp | 12 + libs/kstd/kstd/os/error.hpp | 34 + libs/kstd/kstd/os/print.hpp | 14 + libs/kstd/kstd/print | 69 + libs/kstd/kstd/ranges | 21 + libs/kstd/kstd/stack | 192 ++ libs/kstd/kstd/string | 380 ++++ libs/kstd/kstd/string.test.cpp | 445 +++++ libs/kstd/kstd/test_support/os_panic.hpp | 23 + libs/kstd/kstd/test_support/os_panic.test.cpp | 15 + libs/kstd/kstd/test_support/test_types.hpp | 313 +++ libs/kstd/kstd/units | 149 ++ libs/kstd/kstd/vector | 1154 +++++++++++ libs/kstd/kstd/vector.test.cpp | 2003 ++++++++++++++++++++ libs/kstd/kstd/vformat.cpp | 209 ++ libs/kstd/src/libc/stdlib.cpp | 19 - libs/kstd/src/libc/string.cpp | 78 - libs/kstd/src/mutex.cpp | 38 - libs/kstd/src/os/error.cpp | 12 - libs/kstd/src/vformat.cpp | 209 -- libs/kstd/tests/include/kstd/tests/os_panic.hpp | 23 - libs/kstd/tests/include/kstd/tests/test_types.hpp | 313 --- libs/kstd/tests/src/flat_map.cpp | 314 --- libs/kstd/tests/src/format.cpp | 114 -- libs/kstd/tests/src/observer_ptr.cpp | 360 ---- libs/kstd/tests/src/os_panic.cpp | 15 - libs/kstd/tests/src/string.cpp | 445 ----- libs/kstd/tests/src/vector.cpp | 2003 -------------------- 113 files changed, 9619 insertions(+), 9620 deletions(-) delete mode 100644 libs/kstd/include/kstd/allocator delete mode 100644 libs/kstd/include/kstd/asm_ptr delete mode 100644 libs/kstd/include/kstd/bits/concepts.hpp delete mode 100644 libs/kstd/include/kstd/bits/flat_map.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/arg.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/args.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/context.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/error.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/formatter.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/formatter/bool.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/formatter/byte.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/formatter/char.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/formatter/cstring.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/formatter/integral.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/formatter/ordering.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/formatter/pointer.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/formatter/range.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/formatter/string_view.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/fwd.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/output_buffer.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/parse_context.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/specifiers.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/string.hpp delete mode 100644 libs/kstd/include/kstd/bits/format/vformat.hpp delete mode 100644 libs/kstd/include/kstd/bits/observer_ptr.hpp delete mode 100644 libs/kstd/include/kstd/bits/print_sink.hpp delete mode 100644 libs/kstd/include/kstd/bits/shared_ptr.hpp delete mode 100644 libs/kstd/include/kstd/bits/unique_ptr.hpp delete mode 100644 libs/kstd/include/kstd/cstring delete mode 100644 libs/kstd/include/kstd/ext/bitfield_enum delete mode 100644 libs/kstd/include/kstd/flat_map delete mode 100644 libs/kstd/include/kstd/format delete mode 100644 libs/kstd/include/kstd/memory delete mode 100644 libs/kstd/include/kstd/mutex delete mode 100644 libs/kstd/include/kstd/os/error.hpp delete mode 100644 libs/kstd/include/kstd/os/print.hpp delete mode 100644 libs/kstd/include/kstd/print delete mode 100644 libs/kstd/include/kstd/ranges delete mode 100644 libs/kstd/include/kstd/stack delete mode 100644 libs/kstd/include/kstd/string delete mode 100644 libs/kstd/include/kstd/units delete mode 100644 libs/kstd/include/kstd/vector create mode 100644 libs/kstd/kstd/allocator create mode 100644 libs/kstd/kstd/asm_ptr create mode 100644 libs/kstd/kstd/bits/concepts.hpp create mode 100644 libs/kstd/kstd/bits/flat_map.hpp create mode 100644 libs/kstd/kstd/bits/format/arg.hpp create mode 100644 libs/kstd/kstd/bits/format/args.hpp create mode 100644 libs/kstd/kstd/bits/format/context.hpp create mode 100644 libs/kstd/kstd/bits/format/error.hpp create mode 100644 libs/kstd/kstd/bits/format/formatter.hpp create mode 100644 libs/kstd/kstd/bits/format/formatter/bool.hpp create mode 100644 libs/kstd/kstd/bits/format/formatter/byte.hpp create mode 100644 libs/kstd/kstd/bits/format/formatter/char.hpp create mode 100644 libs/kstd/kstd/bits/format/formatter/cstring.hpp create mode 100644 libs/kstd/kstd/bits/format/formatter/integral.hpp create mode 100644 libs/kstd/kstd/bits/format/formatter/ordering.hpp create mode 100644 libs/kstd/kstd/bits/format/formatter/pointer.hpp create mode 100644 libs/kstd/kstd/bits/format/formatter/range.hpp create mode 100644 libs/kstd/kstd/bits/format/formatter/string_view.hpp create mode 100644 libs/kstd/kstd/bits/format/fwd.hpp create mode 100644 libs/kstd/kstd/bits/format/output_buffer.hpp create mode 100644 libs/kstd/kstd/bits/format/parse_context.hpp create mode 100644 libs/kstd/kstd/bits/format/specifiers.hpp create mode 100644 libs/kstd/kstd/bits/format/string.hpp create mode 100644 libs/kstd/kstd/bits/format/vformat.hpp create mode 100644 libs/kstd/kstd/bits/observer_ptr.hpp create mode 100644 libs/kstd/kstd/bits/observer_ptr.test.cpp create mode 100644 libs/kstd/kstd/bits/print_sink.hpp create mode 100644 libs/kstd/kstd/bits/shared_ptr.hpp create mode 100644 libs/kstd/kstd/bits/unique_ptr.hpp create mode 100644 libs/kstd/kstd/cstring create mode 100644 libs/kstd/kstd/ext/bitfield_enum create mode 100644 libs/kstd/kstd/flat_map create mode 100644 libs/kstd/kstd/flat_map.test.cpp create mode 100644 libs/kstd/kstd/format create mode 100644 libs/kstd/kstd/format.test.cpp create mode 100644 libs/kstd/kstd/libc/stdlib.cpp create mode 100644 libs/kstd/kstd/libc/string.cpp create mode 100644 libs/kstd/kstd/memory create mode 100644 libs/kstd/kstd/mutex create mode 100644 libs/kstd/kstd/mutex.cpp create mode 100644 libs/kstd/kstd/os/error.cpp create mode 100644 libs/kstd/kstd/os/error.hpp create mode 100644 libs/kstd/kstd/os/print.hpp create mode 100644 libs/kstd/kstd/print create mode 100644 libs/kstd/kstd/ranges create mode 100644 libs/kstd/kstd/stack create mode 100644 libs/kstd/kstd/string create mode 100644 libs/kstd/kstd/string.test.cpp create mode 100644 libs/kstd/kstd/test_support/os_panic.hpp create mode 100644 libs/kstd/kstd/test_support/os_panic.test.cpp create mode 100644 libs/kstd/kstd/test_support/test_types.hpp create mode 100644 libs/kstd/kstd/units create mode 100644 libs/kstd/kstd/vector create mode 100644 libs/kstd/kstd/vector.test.cpp create mode 100644 libs/kstd/kstd/vformat.cpp delete mode 100644 libs/kstd/src/libc/stdlib.cpp delete mode 100644 libs/kstd/src/libc/string.cpp delete mode 100644 libs/kstd/src/mutex.cpp delete mode 100644 libs/kstd/src/os/error.cpp delete mode 100644 libs/kstd/src/vformat.cpp delete mode 100644 libs/kstd/tests/include/kstd/tests/os_panic.hpp delete mode 100644 libs/kstd/tests/include/kstd/tests/test_types.hpp delete mode 100644 libs/kstd/tests/src/flat_map.cpp delete mode 100644 libs/kstd/tests/src/format.cpp delete mode 100644 libs/kstd/tests/src/observer_ptr.cpp delete mode 100644 libs/kstd/tests/src/os_panic.cpp delete mode 100644 libs/kstd/tests/src/string.cpp delete mode 100644 libs/kstd/tests/src/vector.cpp diff --git a/.clang-tidy b/.clang-tidy index 7f40ad1..dc76491 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -68,7 +68,7 @@ CheckOptions: readability-magic-numbers.IgnoreTypeAliases: true FormatStyle: file -HeaderFilterRegex: "(.*/kstd/include/.*)|(arch|kernel|kapi)/.*\\.hpp" +HeaderFilterRegex: "(.*/kstd/kstd/.*)|(arch|kernel|kapi)/.*\\.hpp" SystemHeaders: true RemovedArgs: - -fcondition-coverage diff --git a/.vscode/settings.json b/.vscode/settings.json index 4a70d18..c8f9013 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,10 +15,11 @@ "explorer.fileNesting.enabled": true, "explorer.fileNesting.expand": false, "explorer.fileNesting.patterns": { - "*.hpp": "${capture}.cpp, ${capture}.test.cpp, ${capture}.S" + "*.hpp": "${capture}.cpp, ${capture}.test.cpp, ${capture}.S", + "*": "${capture}.cpp, ${capture}.test.cpp, ${capture}.S" }, "files.associations": { - "**/kstd/include/kstd/**": "cpp", + "**/kstd/kstd/**": "cpp", }, "[cpp]": { "editor.formatOnSave": true, diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 2b5ee12..6902891 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -37,26 +37,28 @@ add_library("kstd" STATIC) add_library("kstd::lib" ALIAS "kstd") target_sources("kstd" PRIVATE - "src/os/error.cpp" - "src/mutex.cpp" - "src/vformat.cpp" + "kstd/os/error.cpp" + "kstd/mutex.cpp" + "kstd/vformat.cpp" ) file(GLOB_RECURSE KSTD_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} CONFIGURE_DEPENDS - "include/kstd/*" + "kstd/*" ) +list(FILTER KSTD_HEADERS EXCLUDE REGEX ".*\.cpp") + target_sources("kstd" PUBLIC FILE_SET HEADERS - BASE_DIRS "include" + BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${KSTD_HEADERS} ) target_include_directories("kstd" PUBLIC - "include" + "${CMAKE_CURRENT_SOURCE_DIR}" ) set_target_properties("kstd" PROPERTIES @@ -65,8 +67,8 @@ set_target_properties("kstd" PROPERTIES if(NOT BUILD_TESTING) target_sources("kstd" PRIVATE - "src/libc/stdlib.cpp" - "src/libc/string.cpp" + "kstd/libc/stdlib.cpp" + "kstd/libc/string.cpp" ) set(KSTD_LIBC_SYMBOLS @@ -90,16 +92,12 @@ if(BUILD_TESTING) add_executable("kstd::tests" ALIAS "kstd_tests") target_sources("kstd_tests" PRIVATE - "tests/src/flat_map.cpp" - "tests/src/format.cpp" - "tests/src/vector.cpp" - "tests/src/observer_ptr.cpp" - "tests/src/os_panic.cpp" - "tests/src/string.cpp" - ) - - target_include_directories("kstd_tests" PRIVATE - "tests/include" + "kstd/flat_map.test.cpp" + "kstd/format.test.cpp" + "kstd/vector.test.cpp" + "kstd/bits/observer_ptr.test.cpp" + "kstd/test_support/os_panic.test.cpp" + "kstd/string.test.cpp" ) target_link_libraries("kstd_tests" PRIVATE diff --git a/libs/kstd/include/kstd/allocator b/libs/kstd/include/kstd/allocator deleted file mode 100644 index 0de0e10..0000000 --- a/libs/kstd/include/kstd/allocator +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef KSTD_ALLOCATOR_HPP -#define KSTD_ALLOCATOR_HPP - -#include -#include -#include - -#if __has_builtin(__builtin_operator_new) >= 201'802L -#define KSTD_OPERATOR_NEW __builtin_operator_new -#define KSTD_OPERATOR_DELETE __builtin_operator_delete -#else -#define KSTD_OPERATOR_NEW ::operator new -#define KSTD_OPERATOR_DELETE ::operator delete -#endif - -namespace kstd -{ - - template - struct allocator - { - using value_type = T; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using propagate_on_container_move_assignment = std::true_type; - using is_always_equal = std::true_type; - - constexpr allocator() noexcept = default; - - template - constexpr allocator(allocator const &) noexcept - {} - - constexpr auto allocate(std::size_t n) -> T * - { - if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - { - return static_cast(KSTD_OPERATOR_NEW(n * sizeof(T), std::align_val_t{alignof(T)})); - } - return static_cast(KSTD_OPERATOR_NEW(n * sizeof(T))); - } - - constexpr void deallocate(T * p, std::size_t n) noexcept - { - if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) - { - KSTD_OPERATOR_DELETE(p, n * sizeof(T), std::align_val_t{alignof(T)}); - } - KSTD_OPERATOR_DELETE(p, n * sizeof(T)); - } - }; - - template - constexpr auto operator==(allocator const &, allocator const &) noexcept -> bool - { - return true; - } - -} // namespace kstd - -#undef KSTD_OPERATOR_NEW -#undef KSTD_OPERATOR_DELETE - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/asm_ptr b/libs/kstd/include/kstd/asm_ptr deleted file mode 100644 index c06a8b5..0000000 --- a/libs/kstd/include/kstd/asm_ptr +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef KSTD_ASM_POINTER_HPP -#define KSTD_ASM_POINTER_HPP - -#include -#include - -namespace kstd -{ - - /** - * @brief A pointer that is defined in some assembly source file. - * - * @tparam Type The type of the pointer - */ - template - struct asm_ptr - { - using value_type = Type; - using pointer = value_type *; - using const_pointer = value_type const *; - using reference = value_type &; - using const_reference = value_type const &; - - asm_ptr() = delete; - asm_ptr(asm_ptr const &) = delete; - asm_ptr(asm_ptr &&) = delete; - ~asm_ptr() = delete; - - constexpr auto operator=(asm_ptr const &) = delete; - constexpr auto operator=(asm_ptr &&) = delete; - - auto get() const noexcept -> pointer - { - return m_ptr; - } - - constexpr auto operator+(std::ptrdiff_t offset) const noexcept -> pointer - { - return std::bit_cast(m_ptr) + offset; - } - - constexpr auto operator*() noexcept -> reference - { - return *(std::bit_cast(m_ptr)); - } - - constexpr auto operator*() const noexcept -> const_reference - { - return *(std::bit_cast(m_ptr)); - } - - constexpr auto operator[](std::ptrdiff_t offset) noexcept -> reference - { - return *(*this + offset); - } - - constexpr auto operator[](std::ptrdiff_t offset) const noexcept -> const_reference - { - return *(*this + offset); - } - - constexpr auto operator->() noexcept -> pointer - { - return m_ptr; - } - - constexpr auto operator->() const noexcept -> const_pointer - { - return m_ptr; - } - - private: - pointer m_ptr; - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/concepts.hpp b/libs/kstd/include/kstd/bits/concepts.hpp deleted file mode 100644 index 74c25cb..0000000 --- a/libs/kstd/include/kstd/bits/concepts.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef KSTD_BITS_CONCEPTS_HPP -#define KSTD_BITS_CONCEPTS_HPP - -#include -#include -namespace kstd::bits -{ - - template - concept container_compatible_range = - std::ranges::input_range && std::convertible_to, ValueType>; - -} - -#endif diff --git a/libs/kstd/include/kstd/bits/flat_map.hpp b/libs/kstd/include/kstd/bits/flat_map.hpp deleted file mode 100644 index fe46203..0000000 --- a/libs/kstd/include/kstd/bits/flat_map.hpp +++ /dev/null @@ -1,186 +0,0 @@ -#ifndef KSTD_BITS_FLAT_MAP_HPP -#define KSTD_BITS_FLAT_MAP_HPP - -#include -#include -#include -#include -#include -#include -#include - -namespace kstd::bits -{ - - template - struct flat_map_reference - { - using key_type = KeyType; - using mapped_type = MappedType; - - constexpr flat_map_reference(key_type const & key, mapped_type & mapped) - : first{key} - , second{mapped} - {} - - constexpr auto operator=(flat_map_reference const & other) const -> flat_map_reference const & - { - second = other.second; - return *this; - } - - constexpr auto operator=(flat_map_reference && other) const -> flat_map_reference const & - { - second = std::move(other.second); - return *this; - } - - template - requires(std::tuple_size_v> == 2) - constexpr auto operator=(TupleLikeType && tuple) const -> flat_map_reference const & - { - second = std::forward(tuple).second; - return *this; - } - - template - requires(Index >= 0 && Index <= 1) - [[nodiscard]] constexpr auto get() const noexcept -> decltype(auto) - { - if constexpr (Index == 0) - { - return (first); - } - else - { - return (second); - } - } - - key_type const & first; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) - mapped_type & second; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) - }; - - template - struct flat_map_pointer - { - Reference reference; - - [[nodiscard]] constexpr auto operator->() noexcept -> Reference * - { - return std::addressof(reference); - } - - [[nodiscard]] constexpr auto operator->() const noexcept -> Reference const * - { - return std::addressof(reference); - } - }; - - template - struct flat_map_iterator - { - using iterator_category = std::random_access_iterator_tag; - using value_type = std::pair; - using difference_type = std::ptrdiff_t; - using reference = flat_map_reference; - using pointer = flat_map_pointer; - - constexpr flat_map_iterator() = default; - - constexpr flat_map_iterator(KeyIterator key_iterator, MappedIterator mapped_iterator) - : m_key_iterator{key_iterator} - , m_mapped_iterator{mapped_iterator} - {} - - template - requires(std::convertible_to && - std::convertible_to) - constexpr flat_map_iterator( - flat_map_iterator const & other) noexcept - : m_key_iterator{other.m_key_iterator} - , m_mapped_iterator{other.m_mapped_iterator} - {} - - [[nodiscard]] auto key_iterator() const noexcept -> KeyIterator - { - return m_key_iterator; - } - - [[nodiscard]] constexpr auto operator*() const noexcept -> reference - { - return {*m_key_iterator, *m_mapped_iterator}; - } - - [[nodiscard]] constexpr auto operator->() const noexcept -> pointer - { - return { - {*m_key_iterator, *m_mapped_iterator} - }; - } - - constexpr auto operator++() noexcept -> flat_map_iterator & - { - ++m_key_iterator; - ++m_mapped_iterator; - return *this; - } - - constexpr auto operator++(int) noexcept -> flat_map_iterator - { - auto copy = *this; - ++(*this); - return copy; - } - - constexpr auto operator--() noexcept -> flat_map_iterator & - { - --m_key_iterator; - --m_mapped_iterator; - return *this; - } - - constexpr auto operator--(int) noexcept -> flat_map_iterator - { - auto copy = *this; - --(*this); - return copy; - } - - [[nodiscard]] constexpr auto operator+(difference_type offset) const noexcept -> flat_map_iterator - { - return {m_key_iterator + offset, m_mapped_iterator + offset}; - } - - [[nodiscard]] constexpr auto operator-(flat_map_iterator const & other) const noexcept -> difference_type - { - return m_key_iterator - other.m_key_iterator; - } - - [[nodiscard]] constexpr auto operator<=>(flat_map_iterator const & other) const noexcept = default; - - private: - KeyIterator m_key_iterator{}; - MappedIterator m_mapped_iterator{}; - }; - -} // namespace kstd::bits - -template -struct std::tuple_size> : std::integral_constant -{ -}; - -template -struct std::tuple_element<0, kstd::bits::flat_map_reference> -{ - using type = K const &; -}; - -template -struct std::tuple_element<1, kstd::bits::flat_map_reference> -{ - using type = M &; -}; - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/arg.hpp b/libs/kstd/include/kstd/bits/format/arg.hpp deleted file mode 100644 index e65b26f..0000000 --- a/libs/kstd/include/kstd/bits/format/arg.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_ARG_HPP -#define KSTD_BITS_FORMAT_ARG_HPP - -// IWYU pragma: private, include - -#include -#include - -#include -#include -#include - -namespace kstd -{ - - namespace bits::format - { - enum struct arg_type : std::uint8_t - { - none, - boolean, - character, - integer, - unsigned_integer, - string_view, - c_string, - pointer, - user_defined, - }; - } // namespace bits::format - - struct format_arg - { - bits::format::arg_type type{}; - union - { - bool boolean; - char character; - std::int64_t integer; - std::uint64_t unsigned_integer; - std::string_view string_view; - char const * c_string; - void const * pointer; - struct - { - void const * pointer; - auto (*format)(void const * value, format_parse_context & parse_context, format_context & context) -> void; - } user_defined; - } value{}; - }; - - namespace bits::format - { - constexpr auto extrat_dynamic_width(format_arg const & arg) -> std::size_t - { - if (arg.type == arg_type::unsigned_integer) - { - return static_cast(arg.value.unsigned_integer); - } - else if (arg.type == arg_type::integer) - { - if (arg.value.integer < 0) - { - error("Dynamic width cannont be negative."); - } - return static_cast(arg.value.integer); - } - error("Dynamic width argument is not an integral value."); - return 0; - } - } // namespace bits::format - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/args.hpp b/libs/kstd/include/kstd/bits/format/args.hpp deleted file mode 100644 index e8e3114..0000000 --- a/libs/kstd/include/kstd/bits/format/args.hpp +++ /dev/null @@ -1,160 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_ARGS_HPP -#define KSTD_BITS_FORMAT_ARGS_HPP - -// IWYU pragma: private, include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace kstd -{ - - namespace bits::format - { - - template - auto format_trampoline(void const * value_pointer, format_parse_context & parse_context, format_context & context) - -> void - { - auto typed_value_pointer = static_cast(value_pointer); - auto fmt = formatter>{}; - auto const it = fmt.parse(parse_context); - parse_context.advance_to(it); - fmt.format(*typed_value_pointer, context); - } - - template - constexpr auto determine_arg_type() -> arg_type - { - using decay_type = std::remove_cvref_t; - if constexpr (std::same_as) - { - return arg_type::boolean; - } - else if constexpr (std::same_as) - { - return arg_type::character; - } - else if constexpr (std::integral && std::is_signed_v) - { - return arg_type::integer; - } - else if constexpr (std::integral && std::is_unsigned_v) - { - return arg_type::unsigned_integer; - } - else if constexpr (std::same_as) - { - return arg_type::string_view; - } - else if constexpr (std::same_as, char *> || - std::same_as, char const *>) - { - return arg_type::c_string; - } - else if constexpr (std::is_pointer_v || std::same_as) - { - if constexpr (std::same_as || std::same_as) - { - return arg_type::user_defined; - } - else - { - return arg_type::pointer; - } - } - else - { - return arg_type::user_defined; - } - } - - template - constexpr auto make_single_arg(ValueType const & value) -> format_arg - { - auto result = format_arg{}; - constexpr auto type = determine_arg_type(); - result.type = type; - - if constexpr (type == arg_type::boolean) - { - result.value.boolean = value; - } - else if constexpr (type == arg_type::character) - { - result.value.character = value; - } - else if constexpr (type == arg_type::integer) - { - result.value.integer = static_cast(value); - } - else if constexpr (type == arg_type::unsigned_integer) - { - result.value.unsigned_integer = static_cast(value); - } - else if constexpr (type == arg_type::string_view) - { - result.value.string_view = value; - } - else if constexpr (type == arg_type::c_string) - { - result.value.c_string = value; - } - else if constexpr (type == arg_type::pointer) - { - if constexpr (std::same_as, std::nullptr_t>) - { - result.value.pointer = nullptr; - } - else - { - result.value.pointer = static_cast(value); - } - } - else - { - result.value.user_defined.pointer = &value; - result.value.user_defined.format = format_trampoline; - } - return result; - } - - } // namespace bits::format - - using format_args = std::span; - - template - struct format_arg_store - { - std::array args{}; - }; - - template - [[nodiscard]] auto make_format_args(Arguments const &... args) noexcept -> format_arg_store - { - using namespace bits::format; - - if constexpr (sizeof...(Arguments) == 0) - { - return format_arg_store<0>{}; - } - else - { - return format_arg_store{ - std::array{make_single_arg(args)...}}; - } - } - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/context.hpp b/libs/kstd/include/kstd/bits/format/context.hpp deleted file mode 100644 index c166ba9..0000000 --- a/libs/kstd/include/kstd/bits/format/context.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_CONTEXT_HPP -#define KSTD_BITS_FORMAT_CONTEXT_HPP - -// IWYU pragma: private, include - -#include -#include -#include - -#include -#include -#include -#include - -namespace kstd -{ - - namespace bits::format - { - template - constexpr auto inline is_width_v = std::integral && // - !std::same_as && // - !std::same_as && // - !std::same_as && // - !std::same_as && // - !std::same_as && // - !std::same_as; - } - - struct format_context - { - using format_args = std::span; - format_args args{}; - - format_context(bits::format::output_buffer & buffer, format_args args) - : args{args} - , m_buffer{&buffer} - {} - - [[nodiscard]] auto arg(std::size_t const id) const -> format_arg const & - { - if (id >= args.size()) - { - kstd::os::panic("[kstd:format] argument index out of range!"); - } - return args[id]; - } - - constexpr auto push(std::string_view string) -> void - { - m_buffer->push(string); - } - - constexpr auto push(char character) -> void - { - m_buffer->push(character); - } - - private: - bits::format::output_buffer * m_buffer; - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/error.hpp b/libs/kstd/include/kstd/bits/format/error.hpp deleted file mode 100644 index c0cb53d..0000000 --- a/libs/kstd/include/kstd/bits/format/error.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_ERROR_HPP -#define KSTD_BITS_FORMAT_ERROR_HPP - -#include - -namespace kstd::bits::format -{ - - constexpr auto error(char const * message) -> void - { - if consteval - { - extern void compile_time_format_error_triggered(char const *); - compile_time_format_error_triggered(message); - } - else - { - kstd::os::panic("Error while formatting a string."); - } - } - -} // namespace kstd::bits::format - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter.hpp b/libs/kstd/include/kstd/bits/format/formatter.hpp deleted file mode 100644 index eb28829..0000000 --- a/libs/kstd/include/kstd/bits/format/formatter.hpp +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef KSTD_BITS_FORMATTER_HPP -#define KSTD_BITS_FORMATTER_HPP - -// IWYU pragma: private, include - -#include -#include -#include - -#include -#include -#include - -namespace kstd -{ - - template - struct formatter - { - formatter() = delete; - formatter(formatter const &) = delete; - auto operator=(formatter const &) -> formatter & = delete; - }; - - template - struct range_formatter - { - constexpr auto set_separator(std::string_view sep) -> void - { - m_separator = sep; - } - - constexpr auto set_brackets(std::string_view opening, std::string_view closing) - { - m_prefix = opening; - m_suffix = closing; - } - - constexpr auto parse(format_parse_context & context) - { - auto it = context.begin(); - auto const end = context.end(); - - if (it != end && *it == 'n') - { - set_brackets("", ""); - std::advance(it, 1); - } - - if (it != end && *it == ':') - { - std::advance(it, 1); - context.advance_to(it); - it = m_inner_formatter.parse(context); - } - - if (it != end && *it != '}') - { - bits::format::error("Invalid formate specifier for range"); - } - - return it; - } - - template - auto format(Range const & range, format_context & context) - { - context.push(m_prefix); - - auto is_first = true; - std::ranges::for_each(range, [&](auto const & element) { - if (!is_first) - { - context.push(m_separator); - } - m_inner_formatter.format(element, context); - is_first = false; - }); - - context.push(m_suffix); - } - - private: - kstd::formatter m_inner_formatter{}; - std::string_view m_separator{", "}; - std::string_view m_prefix{"["}; - std::string_view m_suffix{"]"}; - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp b/libs/kstd/include/kstd/bits/format/formatter/bool.hpp deleted file mode 100644 index cc8d190..0000000 --- a/libs/kstd/include/kstd/bits/format/formatter/bool.hpp +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_FORMATTER_BOOL_HPP -#define KSTD_BITS_FORMAT_FORMATTER_BOOL_HPP - -#include -#include -#include -#include -#include - -#include -#include - -namespace kstd -{ - - template<> - struct formatter - { - bits::format::specifiers specifiers{}; - - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - specifiers = bits::format::parse_format_specifiers(context); - - auto it = context.begin(); - auto const end = context.end(); - - if (it != end && *it != '}') - { - if (*it == 's') - { - specifiers.type = *it; - std::advance(it, 1); - } - else - { - bits::format::error("Invalid type specifier for bool."); - } - } - - if (it != end && *it != '}') - { - bits::format::error("Missing terminating '}' in format string."); - } - - return it; - } - - auto format(bool value, format_context & context) const -> void - { - auto const text = value ? std::string_view{"true"} : std::string_view{"false"}; - auto final_width = 0uz; - - if (specifiers.mode == bits::format::width_mode::static_value) - { - final_width = specifiers.width_value; - } - else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id) - { - auto const & arg = context.arg(specifiers.width_value); - final_width = bits::format::extrat_dynamic_width(arg); - } - - auto padding = bits::format::calculate_format_padding(final_width, text.size(), specifiers.align, - bits::format::alignment::left); - - for (auto i = 0uz; i < padding.left; ++i) - { - context.push(specifiers.fill); - } - - context.push(text); - - for (auto i = 0uz; i < padding.right; ++i) - { - context.push(specifiers.fill); - } - } - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/byte.hpp b/libs/kstd/include/kstd/bits/format/formatter/byte.hpp deleted file mode 100644 index cc8aece..0000000 --- a/libs/kstd/include/kstd/bits/format/formatter/byte.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_FORMATTER_BYTE_HPP -#define KSTD_BITS_FORMAT_FORMATTER_BYTE_HPP - -#include -#include -#include - -#include - -namespace kstd -{ - - template<> - struct formatter : formatter - { - auto format(std::byte value, format_context & context) const -> void - { - formatter::format(static_cast(value), context); - } - }; - -} // namespace kstd -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/char.hpp b/libs/kstd/include/kstd/bits/format/formatter/char.hpp deleted file mode 100644 index 92489a1..0000000 --- a/libs/kstd/include/kstd/bits/format/formatter/char.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_FORMATTER_CHAR_HPP -#define KSTD_BITS_FORMAT_FORMATTER_CHAR_HPP - -#include -#include -#include -#include -#include -#include - -#include - -namespace kstd -{ - - template<> - struct formatter : formatter - { - bits::format::specifiers specifiers{}; - - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - specifiers = bits::format::parse_format_specifiers(context); - - auto it = context.begin(); - auto const end = context.end(); - - if (specifiers.alternative_form || specifiers.zero_pad || specifiers.sign != bits::format::sign_mode::none) - { - bits::format::error("Invalid format specifiers for 'char'"); - } - - if (it != end && *it != '}') - { - if (*it == 'c' || *it == 'b' || *it == 'B' || *it == 'd' || *it == 'o' || *it == 'x' || *it == 'X') - { - specifiers.type = *it; - std::advance(it, 1); - } - else - { - bits::format::error("Invalid type specifier for char."); - } - } - - if (it != end && *it != '}') - { - bits::format::error("Missing terminating '}' in format string."); - } - - return it; - } - - auto format(char value, format_context & context) const -> void - { - if (specifiers.type == '\0' || specifiers.type == 'c') - { - auto final_width = 0uz; - - if (specifiers.mode == bits::format::width_mode::static_value) - { - final_width = specifiers.width_value; - } - else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id) - { - auto const & arg = context.arg(specifiers.width_value); - final_width = bits::format::extrat_dynamic_width(arg); - } - - auto padding = - bits::format::calculate_format_padding(final_width, 1, specifiers.align, bits::format::alignment::left); - - for (auto i = 0uz; i < padding.left; ++i) - { - context.push(specifiers.fill); - } - - context.push(value); - - for (auto i = 0uz; i < padding.right; ++i) - { - context.push(specifiers.fill); - } - } - else - { - formatter::format(static_cast(value), context); - } - } - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp b/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp deleted file mode 100644 index 553c8ca..0000000 --- a/libs/kstd/include/kstd/bits/format/formatter/cstring.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_FORMATTER_CSTRING_HPP -#define KSTD_BITS_FORMAT_FORMATTER_CSTRING_HPP - -#include -#include -#include - -#include - -namespace kstd -{ - - template<> - struct formatter : formatter - { - auto format(char const * string, format_context & context) const -> void - { - formatter::format(string ? std::string_view{string} : "(null)", context); - } - }; - - template<> - struct formatter : formatter - { - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp b/libs/kstd/include/kstd/bits/format/formatter/integral.hpp deleted file mode 100644 index d17dc95..0000000 --- a/libs/kstd/include/kstd/bits/format/formatter/integral.hpp +++ /dev/null @@ -1,204 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_FORMATTER_INTEGRAL_HPP -#define KSTD_BITS_FORMAT_FORMATTER_INTEGRAL_HPP - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace kstd -{ - - template - struct formatter - { - bits::format::specifiers specifiers{}; - - constexpr auto static maximum_digits = 80; - - enum struct base - { - bin = 2, - oct = 8, - dec = 10, - hex = 16, - }; - - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - specifiers = bits::format::parse_format_specifiers(context); - - auto it = context.begin(); - auto const end = context.end(); - - if (it != end && *it != '}') - { - if (*it == 'b' || *it == 'B' || *it == 'd' || *it == 'o' || *it == 'x' || *it == 'X' || *it == 'p') - { - specifiers.type = *it; - std::advance(it, 1); - } - else - { - bits::format::error("Invalid type specifier for integral type."); - } - } - - if (it != end && *it != '}') - { - bits::format::error("Missing terminating '}' in format string."); - } - - return it; - } - - auto format(T value, format_context & context) const -> void - { - auto final_width = 0uz; - if (specifiers.mode == bits::format::width_mode::static_value) - { - final_width = specifiers.width_value; - } - else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id) - { - auto const & arg = context.arg(specifiers.width_value); - final_width = bits::format::extrat_dynamic_width(arg); - } - - using unsigned_T = std::make_unsigned_t; - auto absolute_value = static_cast(value); - auto is_negative = false; - - if constexpr (std::is_signed_v) - { - if (value < 0) - { - is_negative = true; - absolute_value = 0 - static_cast(value); - } - } - - auto const base = [type = specifiers.type] -> auto { - switch (type) - { - case 'x': - case 'X': - case 'p': - return base::hex; - case 'b': - case 'B': - return base::bin; - case 'o': - return base::oct; - default: - return base::dec; - } - }(); - - auto buffer = std::array{}; - auto digits = (specifiers.type == 'X') ? "0123456789ABCDEF" : "0123456789abcdef"; - auto current = buffer.rbegin(); - - if (absolute_value == 0) - { - *current = '0'; - std::advance(current, 1); - } - else - { - while (absolute_value != 0) - { - *current = digits[absolute_value % std::to_underlying(base)]; - std::advance(current, 1); - absolute_value /= std::to_underlying(base); - } - } - - auto content_length = static_cast(std::distance(buffer.rbegin(), current)); - auto prefix = std::array{'0', '\0'}; - auto prefix_length = 0uz; - if (specifiers.alternative_form) - { - switch (base) - { - case base::bin: - prefix[1] = specifiers.type == 'B' ? 'B' : 'b'; - prefix_length = 2; - break; - case base::oct: - prefix_length = 1; - break; - case base::hex: - prefix[1] = specifiers.type == 'X' ? 'X' : 'x'; - prefix_length = 2; - break; - default: - break; - } - } - - auto sign_character = '\0'; - if (is_negative) - { - sign_character = '-'; - } - else if (specifiers.sign == bits::format::sign_mode::plus) - { - sign_character = '+'; - } - else if (specifiers.sign == bits::format::sign_mode::space) - { - sign_character = ' '; - } - - auto const total_length = content_length + prefix_length + (sign_character != '\0'); - auto const padding = bits::format::calculate_format_padding(final_width, total_length, specifiers.align, - bits::format::alignment::right); - auto const effective_zero_pad = specifiers.zero_pad && (specifiers.align == bits::format::alignment::none); - - if (!effective_zero_pad) - { - for (auto i = 0uz; i < padding.left; ++i) - { - context.push(specifiers.fill); - } - } - - if (sign_character != '\0') - { - context.push(sign_character); - } - if (prefix_length > 0) - { - context.push(std::string_view{prefix.data(), prefix_length}); - } - - if (effective_zero_pad) - { - for (auto i = 0uz; i < padding.left; ++i) - { - context.push('0'); - } - } - - context.push(std::string_view{current.base(), content_length}); - - for (auto i = 0uz; i < padding.right; ++i) - { - context.push(specifiers.fill); - } - } - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp b/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp deleted file mode 100644 index 7832226..0000000 --- a/libs/kstd/include/kstd/bits/format/formatter/ordering.hpp +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_FORMATTER_ORDERING_HPP -#define KSTD_BITS_FORMAT_FORMATTER_ORDERING_HPP - -#include -#include -#include -#include - -#include - -namespace kstd -{ - - template<> - struct formatter - { - bits::format::specifiers specifiers{}; - - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - specifiers = bits::format::parse_format_specifiers(context); - return context.begin(); - } - - auto format(std::strong_ordering value, format_context & context) const -> void - { - if (value == std::strong_ordering::equal) - { - return context.push(specifiers.alternative_form ? "==" : "equal"); - } - else if (value == std::strong_ordering::equivalent) - { - return context.push(specifiers.alternative_form ? "==" : "equivalent"); - } - else if (value == std::strong_ordering::greater) - { - return context.push(specifiers.alternative_form ? ">" : "greater"); - } - else if (value == std::strong_ordering::less) - { - return context.push(specifiers.alternative_form ? "<" : "less"); - } - kstd::os::panic("[kstd:format] Invalid strong ordering value!"); - } - }; - - template<> - struct formatter - { - bits::format::specifiers specifiers{}; - - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - specifiers = bits::format::parse_format_specifiers(context); - return context.begin(); - } - - auto format(std::weak_ordering value, format_context & context) const -> void - { - if (value == std::weak_ordering::equivalent) - { - return context.push(specifiers.alternative_form ? "==" : "equivalent"); - } - else if (value == std::weak_ordering::greater) - { - return context.push(specifiers.alternative_form ? ">" : "greater"); - } - else if (value == std::weak_ordering::less) - { - return context.push(specifiers.alternative_form ? "<" : "less"); - } - kstd::os::panic("[kstd:format] Invalid weak ordering value!"); - } - }; - - template<> - struct formatter - { - bits::format::specifiers specifiers{}; - - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - specifiers = bits::format::parse_format_specifiers(context); - return context.begin(); - } - - auto format(std::partial_ordering value, format_context & context) const -> void - { - if (value == std::partial_ordering::equivalent) - { - return context.push(specifiers.alternative_form ? "==" : "equivalent"); - } - else if (value == std::partial_ordering::greater) - { - return context.push(specifiers.alternative_form ? ">" : "greater"); - } - else if (value == std::partial_ordering::less) - { - return context.push(specifiers.alternative_form ? "<" : "less"); - } - else if (value == std::partial_ordering::unordered) - { - return context.push(specifiers.alternative_form ? "<=>" : "unordered"); - } - kstd::os::panic("[kstd:format] Invalid partial ordering value!"); - } - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp b/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp deleted file mode 100644 index 15f9a5b..0000000 --- a/libs/kstd/include/kstd/bits/format/formatter/pointer.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_FORMATTER_POINTER_HPP -#define KSTD_BITS_FORMAT_FORMATTER_POINTER_HPP - -#include -#include -#include -#include -#include - -#include -#include - -namespace kstd -{ - - template - struct formatter : formatter - { - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - auto result = formatter::parse(context); - if (!this->specifiers.type) - { - this->specifiers.type = 'p'; - this->specifiers.alternative_form = true; - } - return result; - } - - auto format(T const * pointer, format_context & context) const -> void - { - formatter::format(std::bit_cast(pointer), context); - } - }; - - template - struct formatter : formatter - { - }; - -} // namespace kstd -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/range.hpp b/libs/kstd/include/kstd/bits/format/formatter/range.hpp deleted file mode 100644 index 05af06f..0000000 --- a/libs/kstd/include/kstd/bits/format/formatter/range.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_FORMATTER_RANGE_HPP -#define KSTD_BITS_FORMAT_FORMATTER_RANGE_HPP - -#include - -#include -#include -#include -#include - -namespace kstd -{ - namespace bits::format - { - template - concept iterable = requires(T const & t) { - t.begin(); - t.end(); - }; - - template - concept formattable_range = iterable && !std::same_as, std::string_view> && - !std::same_as, char *> && !std::same_as, char const *>; - } // namespace bits::format - - template - struct formatter : range_formatter> - { - }; -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp b/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp deleted file mode 100644 index 7d74579..0000000 --- a/libs/kstd/include/kstd/bits/format/formatter/string_view.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_FORMATTER_STRING_VIEW_HPP -#define KSTD_BITS_FORMAT_FORMATTER_STRING_VIEW_HPP - -#include -#include -#include -#include - -#include - -namespace kstd -{ - - template<> - struct formatter - { - constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator - { - auto it = context.begin(); - - if (it != context.end() && *it == 's') - { - ++it; - } - - if (it != context.end() && *it != '}') - { - bits::format::error("Invalid specifier for string_view."); - } - return it; - } - - auto format(std::string_view const & string, format_context & context) const -> void - { - context.push(string); - } - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/fwd.hpp b/libs/kstd/include/kstd/bits/format/fwd.hpp deleted file mode 100644 index 6caedae..0000000 --- a/libs/kstd/include/kstd/bits/format/fwd.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_FWD_HPP -#define KSTD_BITS_FORMAT_FWD_HPP - -// IWYU pragma: private - -#include - -namespace kstd -{ - - struct format_parse_context; - struct format_context; - struct format_arg; - - template - struct formatter; - - template - struct format_arg_store; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/output_buffer.hpp b/libs/kstd/include/kstd/bits/format/output_buffer.hpp deleted file mode 100644 index fd7a2b4..0000000 --- a/libs/kstd/include/kstd/bits/format/output_buffer.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP -#define KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP - -// IWYU pragma: private, include - -#include - -namespace kstd::bits::format -{ - - //! An abstract interface for formatted output buffers. - //! - //! This interface is intended to be use for functions dealing with string formatting, like the print and the format - //! family. - struct output_buffer - { - virtual ~output_buffer() = default; - - //! Push a text segment into the buffer. - //! - //! @param text The text segment to push. - virtual auto push(std::string_view text) -> void = 0; - - //! Push a single character into the buffer. - //! - //! @param character The character to push into the buffer. - virtual auto push(char character) -> void = 0; - }; - -} // namespace kstd::bits::format - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/parse_context.hpp b/libs/kstd/include/kstd/bits/format/parse_context.hpp deleted file mode 100644 index cab8d72..0000000 --- a/libs/kstd/include/kstd/bits/format/parse_context.hpp +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_PARSE_CONTEXT_HPP -#define KSTD_BITS_FORMAT_PARSE_CONTEXT_HPP - -// IWYU pragma: private, include - -#include - -#include -#include - -namespace kstd -{ - - struct format_parse_context - { - using iterator = std::string_view::const_iterator; - - constexpr format_parse_context(std::string_view format, std::size_t argument_count, - bool const * is_integral = nullptr) - : m_current{format.begin()} - , m_end{format.end()} - , m_argument_count{argument_count} - , m_is_integral{is_integral} - {} - - [[nodiscard]] constexpr auto begin() const -> iterator - { - return m_current; - } - - [[nodiscard]] constexpr auto end() const -> iterator - { - return m_end; - } - - constexpr auto advance_to(iterator position) -> void - { - m_current = position; - } - - constexpr auto next_arg_id() -> std::size_t - { - if (m_mode == index_mode::manual) - { - bits::format::error("Cannot mix automatic and manual indexing."); - } - - m_mode = index_mode::automatic; - - if (m_next_argument_id >= m_argument_count) - { - bits::format::error("Argument index out of bounds."); - } - return m_next_argument_id++; - } - - constexpr auto check_arg_id(std::size_t index) -> void - { - if (m_mode == index_mode::automatic) - { - bits::format::error("Cannot mix automatic and manual indexing."); - } - - m_mode = index_mode::manual; - - if (index >= m_argument_count) - { - bits::format::error("Argument index out of bounds."); - } - } - - constexpr auto check_dynamic_width_id(std::size_t id) -> void - { - check_arg_id(id); - if (m_is_integral && !m_is_integral[id]) - { - bits::format::error("Dynamic width argument must be an integral object."); - } - } - - constexpr auto next_dynamic_width_id() -> std::size_t - { - auto const id = next_arg_id(); - if (m_is_integral && !m_is_integral[id]) - { - bits::format::error("Dynamic width argument must be an integral object."); - } - return id; - } - - private: - enum class index_mode - { - unknown, - automatic, - manual, - }; - - iterator m_current{}; - iterator m_end{}; - index_mode m_mode{}; - std::size_t m_next_argument_id{}; - std::size_t m_argument_count{}; - bool const * m_is_integral{}; - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/specifiers.hpp b/libs/kstd/include/kstd/bits/format/specifiers.hpp deleted file mode 100644 index 211c95d..0000000 --- a/libs/kstd/include/kstd/bits/format/specifiers.hpp +++ /dev/null @@ -1,205 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_SPECS_HPP -#define KSTD_BITS_FORMAT_SPECS_HPP - -// IWYU pragma: private - -#include -#include - -#include -#include -#include - -namespace kstd::bits::format -{ - enum struct alignment : std::uint8_t - { - none, - left, - right, - center, - }; - - enum struct sign_mode : std::uint8_t - { - none, - plus, - minus, - space, - }; - - enum struct width_mode : std::uint8_t - { - none, - static_value, - dynamic_argument_id - }; - - struct specifiers - { - char fill{' '}; - alignment align{}; - sign_mode sign{}; - bool alternative_form{}; - bool zero_pad{}; - - width_mode mode{}; - std::size_t width_value{}; - char type{}; - }; - - struct padding - { - std::size_t left{}; - std::size_t right{}; - }; - - constexpr auto parse_format_specifiers(format_parse_context & context) -> specifiers - { - auto specs = specifiers{}; - auto it = context.begin(); - auto const end = context.end(); - - if (it != end && *it == '}') - { - return specs; - } - - if (std::next(it) != end && ((*std::next(it)) == '<' || (*std::next(it)) == '>' || (*std::next(it)) == '^')) - { - specs.fill = *it; - switch (*std::next(it)) - { - case '<': - specs.align = alignment::left; - break; - case '>': - specs.align = alignment::right; - break; - case '^': - default: - specs.align = alignment::center; - break; - } - std::advance(it, 2); - } - else if (*it == '<' || *it == '>' || *it == '^') - { - switch (*it) - { - case '<': - specs.align = alignment::left; - break; - case '>': - specs.align = alignment::right; - break; - case '^': - default: - specs.align = alignment::center; - break; - } - std::advance(it, 1); - } - - if (it != end && (*it == '+' || *it == '-' || *it == ' ')) - { - switch (*it) - { - case '+': - specs.sign = sign_mode::plus; - break; - case '-': - specs.sign = sign_mode::minus; - break; - case ' ': - default: - specs.sign = sign_mode::space; - break; - } - std::advance(it, 1); - } - - if (it != end && *it == '#') - { - specs.alternative_form = true; - std::advance(it, 1); - } - - if (it != end && *it == '0') - { - specs.zero_pad = true; - std::advance(it, 1); - } - - if (it != end && *it == '{') - { - specs.mode = width_mode::dynamic_argument_id; - std::advance(it, 1); - auto argument_id = 0uz; - - if (it != end && *it >= '0' && *it <= '9') - { - while (it != end && *it >= '0' && *it <= '9') - { - argument_id = argument_id * 10 + static_cast(*it - '0'); - std::advance(it, 1); - } - context.check_dynamic_width_id(argument_id); - } - else - { - argument_id = context.next_dynamic_width_id(); - } - - if (it == end || *it != '}') - { - error("Expected '}' for dynamic width."); - } - std::advance(it, 1); - specs.width_value = argument_id; - } - else if (it != end && *it >= '0' && *it <= '9') - { - specs.mode = width_mode::static_value; - while (it != end && *it >= '0' && *it <= '9') - { - specs.width_value = specs.width_value * 10 + static_cast(*it - '0'); - std::advance(it, 1); - } - } - - context.advance_to(it); - return specs; - } - - constexpr auto calculate_format_padding(std::size_t target_width, std::size_t content_length, - alignment requested_alignment, alignment default_alignment) -> padding - { - if (target_width <= content_length) - { - return {}; - } - - auto total_padding = target_width - content_length; - auto effective_alignment = (requested_alignment == alignment::none) ? default_alignment : requested_alignment; - - switch (effective_alignment) - { - case alignment::center: - { - auto left = total_padding / 2; - auto right = total_padding - left; - return {left, right}; - } - case alignment::left: - return {0, total_padding}; - case alignment::right: - default: - return {total_padding, 0}; - break; - } - } - -} // namespace kstd::bits::format - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/string.hpp b/libs/kstd/include/kstd/bits/format/string.hpp deleted file mode 100644 index e7e4088..0000000 --- a/libs/kstd/include/kstd/bits/format/string.hpp +++ /dev/null @@ -1,148 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_STRING_HPP -#define KSTD_BITS_FORMAT_STRING_HPP - -// IWYU pragma: private, include - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace kstd -{ - - namespace bits::format - { - template - constexpr auto validate_argument(std::size_t target_index, format_parse_context & context) -> void - { - auto found = false; - auto current_index = 0uz; - - (..., [&] { - if (current_index == target_index && !found) - { - using decay_type = std::remove_cvref_t; - static_assert(std::is_default_constructible_v>, "Missing formatter specialization."); - auto fmt = formatter{}; - - auto it = fmt.parse(context); - context.advance_to(it); - found = true; - } - ++current_index; - }()); - - if (!found) - { - error("Argument index out of bounds."); - } - } - - template - constexpr auto validate_dynamic_width(std::size_t target_index) -> void - { - auto is_valid_integer = false; - auto current_index = 0uz; - - (..., [&] { - if (current_index == target_index) - { - using decay_type = std::remove_cvref_t; - if constexpr (std::is_integral_v) - { - is_valid_integer = true; - } - } - ++current_index; - }()); - - if (!is_valid_integer) - { - error("Dynamic width argument must be an integral object."); - } - } - } // namespace bits::format - - template - struct format_string - { - template - consteval format_string(char const (&str)[Size]) noexcept(false) // NOLINT - : str_view{str} - { - using namespace bits::format; - - auto const is_width_compatible = - std::array 0 ? sizeof...(Args) : 1)>{is_width_v>...}; - auto context = format_parse_context{str_view, sizeof...(Args), is_width_compatible.data()}; - auto it = context.begin(); - - while (it != context.end()) - { - if (*it == '{') - { - ++it; - if (it != context.end() && *it == '{') - { - ++it; - context.advance_to(it); - continue; - } - - context.advance_to(it); - auto argument_id = 0uz; - - if (it != context.end() && *it >= '0' && *it <= '9') - { - while (it != context.end() && *it >= '0' && *it <= '9') - { - argument_id = argument_id * 10 + static_cast(*it - '0'); - ++it; - } - context.check_arg_id(argument_id); - context.advance_to(it); - } - else - { - argument_id = context.next_arg_id(); - } - - if (it != context.end() && *it == ':') - { - ++it; - context.advance_to(it); - } - - validate_argument(argument_id, context); - - it = context.begin(); - if (it == context.end() || *it != '}') - { - bits::format::error("Missing closing '}' in format string."); - } - } - else if (*it == '}') - { - ++it; - if (it != context.end() && *it == '}') - { - bits::format::error("Unescaped '}' in format string."); - } - } - ++it; - context.advance_to(it); - } - } - - std::string_view str_view; - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/format/vformat.hpp b/libs/kstd/include/kstd/bits/format/vformat.hpp deleted file mode 100644 index 994fae5..0000000 --- a/libs/kstd/include/kstd/bits/format/vformat.hpp +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef KSTD_BITS_FORMAT_VFORMAT_HPP -#define KSTD_BITS_FORMAT_VFORMAT_HPP - -// IWYU pragma: private, include - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace kstd -{ - - namespace bits::format - { - //! Format a string with the given arguments into the given buffer. - //! - //! External implementations of `vprint` may call this function. - //! - //! @param buffer The buffer to format into. - //! @param format The format string to use. - //! @param args The arguments for the format string. - auto vformat_to(output_buffer & buffer, std::string_view format, format_args args) -> void; - - struct string_writer : output_buffer - { - auto push(std::string_view text) -> void override; - auto push(char character) -> void override; - - auto release() -> string &&; - - private: - string m_result{}; - }; - - template Output> - struct iterator_writer : output_buffer - { - explicit iterator_writer(Output iterator) - : m_output{iterator} - {} - - [[nodiscard]] auto iterator() const -> Output - { - return m_output; - } - - auto push(std::string_view text) -> void override - { - m_output = std::ranges::copy(text, m_output).out; - } - - auto push(char character) -> void override - { - *m_output++ = character; - } - - private: - Output m_output{}; - }; - - } // namespace bits::format - - //! Format a given string with the provided arguments. - //! - //! @param format The format string. - //! @param args The arguments for the format string. - //! @return A new string containing the result of the format operation. - template - auto format(format_string...> format, ArgumentTypes &&... args) -> string - { - auto buffer = bits::format::string_writer{}; - bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward(args)...).args); - return buffer.release(); - } - - //! Format a given string with the provided arguments. - //! - //! @param iterator The iterator to write to. - //! @param format The format string. - //! @param args The arguments for the format string. - //! @return An iterator past the last element written. - template Output, typename... ArgumentTypes> - auto format_to(Output iterator, format_string...> format, - ArgumentTypes &&... args) -> Output - { - auto buffer = bits::format::iterator_writer{iterator}; - bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward(args)...).args); - return buffer.iterator(); - } - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/observer_ptr.hpp b/libs/kstd/include/kstd/bits/observer_ptr.hpp deleted file mode 100644 index 2593d7a..0000000 --- a/libs/kstd/include/kstd/bits/observer_ptr.hpp +++ /dev/null @@ -1,163 +0,0 @@ -#ifndef KSTD_OBSERVER_PTR_HPP -#define KSTD_OBSERVER_PTR_HPP - -// IWYU pragma: private, include - -#include - -#include -#include -#include -#include -#include - -namespace kstd -{ - - template - struct observer_ptr - { - //! The type of the element being pointed to. - using element_type = ElementType; - - //! Construct an empty observer pointer. - constexpr observer_ptr() noexcept = default; - - //! Construct an empty observer pointer from a null pointer. - constexpr observer_ptr(std::nullptr_t) noexcept {} - - //! Construct an observer pointer from a raw pointer. - constexpr explicit observer_ptr(element_type * pointer) - : m_ptr{pointer} - {} - - //! Construct an observer pointer from another observer pointer. - template - requires std::convertible_to - constexpr observer_ptr(observer_ptr other) noexcept - : m_ptr{other.get()} - {} - - //! Copy construct an observer pointer. - constexpr observer_ptr(observer_ptr const & other) noexcept = default; - - //! Move construct an observer pointer. - constexpr observer_ptr(observer_ptr && other) noexcept = default; - - //! Copy assign an observer pointer. - constexpr auto operator=(observer_ptr const & other) noexcept -> observer_ptr & = default; - - //! Move assign an observer pointer. - constexpr auto operator=(observer_ptr && other) noexcept -> observer_ptr & = default; - - //! Stop watching the the watched object. - //! - //! @return The currently watched object, or nullptr if no object is being watched. - [[nodiscard]] constexpr auto release() noexcept -> element_type * - { - return std::exchange(m_ptr, nullptr); - } - - //! Reset the observer pointer. - //! - //! @param pointer The new object to watch. - constexpr auto reset(element_type * pointer = nullptr) noexcept -> void - { - m_ptr = pointer; - } - - //! Swap the observer pointer with another observer pointer. - //! - //! @param other The other observer pointer to swap with. - constexpr auto swap(observer_ptr & other) noexcept -> void - { - std::swap(m_ptr, other.m_ptr); - } - - //! Get the currently watched object. - //! - //! @return The currently watched object, or nullptr if no object is being watched. - [[nodiscard]] constexpr auto get() const noexcept -> element_type * - { - return m_ptr; - } - - //! Check if the observer pointer is watching an object. - //! - //! @return True if the observer pointer is watching an object, false otherwise. - [[nodiscard]] constexpr explicit operator bool() const noexcept - { - return m_ptr != nullptr; - } - - //! Get the currently watched object. - //! - //! @return A reference to the currently watched object. - [[nodiscard]] constexpr auto operator*() const -> std::add_lvalue_reference_t - { - throw_on_null(); - return *m_ptr; - } - - //! Get the currently watched object. - //! - //! @return A pointer to the currently watched object. - [[nodiscard]] constexpr auto operator->() const -> element_type * - { - throw_on_null(); - return m_ptr; - } - - //! Convert the observer pointer to a raw pointer. - //! - //! @return A pointer to the currently watched object. - constexpr explicit operator element_type *() const noexcept - { - return m_ptr; - } - - //! Compare the observer pointer with another observer pointer. - //!> - //! @param other The other observer pointer to compare with. - //! @return The result of the comparison. - constexpr auto operator<=>(observer_ptr const & other) const noexcept -> std::strong_ordering = default; - - private: - //! Throw an exception if the observer pointer is null. - //! - //! @throws std::runtime_error if the observer pointer is null. - constexpr auto throw_on_null() const -> void - { - if (m_ptr == nullptr) - { - os::panic("[kstd:observer_ptr] Dereferencing a null observer pointer"); - } - } - - //! The raw pointer to the watched object. - ElementType * m_ptr{}; - }; - - //! Swap two observer pointers. - //! - //! @param lhs The first observer pointer to swap. - //! @param rhs The second observer pointer to swap. - template - constexpr auto swap(observer_ptr & lhs, observer_ptr & rhs) noexcept -> void - { - lhs.swap(rhs); - } - - //! Create an observer pointer from a raw pointer. - //! - //! @param pointer The raw pointer to create an observer pointer from. - //! @return An observer pointer to the given raw pointer. - template - constexpr auto make_observer(ElementType * pointer) noexcept -> observer_ptr - { - return observer_ptr{pointer}; - } - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/print_sink.hpp b/libs/kstd/include/kstd/bits/print_sink.hpp deleted file mode 100644 index af765e0..0000000 --- a/libs/kstd/include/kstd/bits/print_sink.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef KSTD_BITS_PRINT_SINK_HPP -#define KSTD_BITS_PRINT_SINK_HPP - -// IWYU pragma: private, include - -namespace kstd -{ - - enum struct print_sink - { - stdout, - stderr, - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/shared_ptr.hpp b/libs/kstd/include/kstd/bits/shared_ptr.hpp deleted file mode 100644 index 8930095..0000000 --- a/libs/kstd/include/kstd/bits/shared_ptr.hpp +++ /dev/null @@ -1,648 +0,0 @@ -#ifndef KSTD_BITS_SHARED_PTR_HPP -#define KSTD_BITS_SHARED_PTR_HPP - -#include -#include -#include -#include - -// IWYU pragma: private, include - -namespace kstd -{ - /** - * @brief Control block for shared_ptr and weak_ptr. This control block contains the reference counts for shared - * ownership and weak ownership. The shared_count tracks the number of shared_ptr instances that own the managed - * object, while the weak_count tracks the number of weak_ptr instances that reference the managed object. - * The weak_count is needed to determine when it is safe to delete the shared_control_block itself - */ - struct shared_control_block - { - std::atomic shared_count; - std::atomic weak_count; - - explicit shared_control_block(std::size_t shared = 1, std::size_t weak = 0) - : shared_count(shared) - , weak_count(weak) - {} - }; - - template - struct shared_ptr; - - /** - * @brief weak_ptr is a smart pointer that holds a non-owning weak reference to an object that is managed by - * shared_ptr. It must be converted to shared_ptr in to be able to access the referenced object. A weak_ptr is created - * as a copy of a shared_ptr, or as a copy of another weak_ptr. A weak_ptr is typically used to break circular - * references between shared_ptr to avoid memory leaks. A weak_ptr does not contribute to the reference count of the - * object, and it does not manage the lifetime of the object. If the object managed by shared_ptr is destroyed, the - * weak_ptr becomes expired and cannot be used to access the object anymore. - */ - template - struct weak_ptr - { - template - friend struct shared_ptr; - - /** - * @brief Constructs a null weak_ptr. - */ - weak_ptr() noexcept - : pointer(nullptr) - , control(nullptr) - {} - - template - requires(std::is_convertible_v) - weak_ptr(shared_ptr const & other) - : pointer(other.pointer) - , control(other.control) - { - if (control != nullptr) - { - ++(control->weak_count); - } - } - - /** - * @brief Copy constructor. Constructs a weak_ptr which shares ownership of the object managed by other. - */ - weak_ptr(weak_ptr const & other) - : pointer(other.pointer) - , control(other.control) - { - if (control != nullptr) - { - ++(control->weak_count); - } - } - - /** - * @brief Move constructor. Constructs a weak_ptr which takes ownership of the object managed by other. - */ - weak_ptr(weak_ptr && other) noexcept - : pointer(other.pointer) - , control(other.control) - { - other.pointer = nullptr; - other.control = nullptr; - } - - /** - * @brief Assignment operator. Assigns the weak_ptr to another weak_ptr. - */ - auto operator=(weak_ptr const & other) -> weak_ptr & - { - if (this != &other) - { - cleanup(); - pointer = other.pointer; - control = other.control; - if (control != nullptr) - { - ++(control->weak_count); - } - } - - return *this; - } - - /** - * @brief Move assignment operator. Move-assigns a weak_ptr from other. - */ - auto operator=(weak_ptr && other) noexcept -> weak_ptr & - { - if (this != &other) - { - cleanup(); - pointer = other.pointer; - control = other.control; - other.pointer = nullptr; - other.control = nullptr; - } - - return *this; - } - - /** - * @brief Destructor. Cleans up resources if necessary. - */ - ~weak_ptr() - { - cleanup(); - } - - /** - * @brief Returns a shared_ptr that shares ownership of the managed object if the managed object still exists, or an - * empty shared_ptr otherwise. - */ - [[nodiscard]] auto lock() const -> shared_ptr - { - return shared_ptr(*this); - } - - private: - auto cleanup() -> void - { - if (control != nullptr) - { - if (--(control->weak_count) == 0 && control->shared_count == 0) - { - delete control; - } - } - } - - T * pointer; - shared_control_block * control; - }; - - /** - * @brief enable_shared_from_this is a base class that allows an object that is currently managed by a shared_ptr to - * create additional shared_ptr instances that share ownership of the same object. This is usefl when you want to - * create shared_ptr instances in a member function of the object. - * - * @tparam T The type of the managed object. - */ - template - struct enable_shared_from_this - { - template - friend struct shared_ptr; - - friend T; - - public: - /** - * @brief Returns a shared_ptr that shares ownership of *this. - */ - auto shared_from_this() -> shared_ptr - { - return shared_ptr(weak_this); - } - - /** - * @brief Returns a shared_ptr that shares ownership of *this. - */ - auto shared_from_this() const -> shared_ptr - { - return shared_ptr(weak_this); - } - - private: - enable_shared_from_this() = default; - enable_shared_from_this(enable_shared_from_this const &) = default; - auto operator=(enable_shared_from_this const &) -> enable_shared_from_this & = default; - ~enable_shared_from_this() = default; - - void internal_assign_ptr(shared_ptr const & ptr) const - { - weak_this = ptr; - } - - mutable weak_ptr weak_this{}; ///< Weak pointer to the object, used for shared_from_this functionality. - }; - - /** - * @brief Shared_pointer is a smart pointer that retains shared ownership of an object through a pointer. Several - * shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of - * the following happens: the last remaining shared_ptr owning the object is destroyed; the last remaining - * shared_ptr owning the object is assigned another pointer via operator= or reset(). A - * shared_ptr can share ownership of an object while storing a pointer to another object. This feature can be used - * to point to member objects while owning the object they belong to. The stored pointer is the one accessed by get(), - * the dereference and the comparison operators. The managed pointer is the one passed to the deleter when use count - * reaches zero. - * - * @tparam T The type of the managed object. - */ - template - struct shared_ptr - { - template - friend struct shared_ptr; - - template - friend struct weak_ptr; - - /** - * @brief Construct an empty shared_ptr. - */ - shared_ptr() noexcept - : pointer(nullptr) - , control(nullptr) - {} - - /** - * @brief Construct an empty shared_ptr from nullptr. - */ - shared_ptr(std::nullptr_t) noexcept - : pointer(nullptr) - , control(nullptr) - {} - - /** - * @brief Constructor. - * - * @param pointer A pointer to an object to manage (default is nullptr). - */ - template - requires(std::is_convertible_v) - explicit shared_ptr(U * pointer = nullptr) - : pointer(pointer) - , control(pointer != nullptr ? new shared_control_block() : nullptr) - { - assign_enable_shared_from_this(pointer); - } - - /** - * @brief Constructor a shared_ptr from a weak_ptr. If other is not expired, constructs a shared_ptr which shares - * ownership of the object managed by other. Otherwise, constructs an empty shared_ptr. - * - * @param other The weak_ptr to construct from. - */ - template - requires(std::is_convertible_v) - explicit shared_ptr(weak_ptr const & other) - : pointer(nullptr) - , control(nullptr) - { - if (other.control != nullptr && other.control->shared_count != 0) - { - pointer = other.pointer; - control = other.control; - ++(control->shared_count); - } - } - - /** - * @brief Copy constructor. - * - * @param other The shared_ptr to copy from. - */ - shared_ptr(shared_ptr const & other) - : pointer(other.pointer) - , control(other.control) - { - if (control != nullptr) - { - ++(control->shared_count); - } - } - - /** - * @brief Converting copy constructor for compatible shared_ptr types. - * - * @tparam U Source pointer element type. - * @param other The shared_ptr to copy from. - */ - template - requires(std::is_convertible_v) - shared_ptr(shared_ptr const & other) - : pointer(other.pointer) - , control(other.control) - { - if (control != nullptr) - { - ++(control->shared_count); - } - } - - /** - * @brief Move constructor. - * - * @param other The shared_ptr to move from. - */ - shared_ptr(shared_ptr && other) noexcept - : pointer(other.pointer) - , control(other.control) - { - other.pointer = nullptr; - other.control = nullptr; - } - - /** - * @brief Converting move constructor for compatible shared_ptr types. - * - * @tparam U Source pointer element type. - * @param other The shared_ptr to move from. - */ - template - requires(std::is_convertible_v) - shared_ptr(shared_ptr && other) noexcept - : pointer(other.pointer) - , control(other.control) - { - other.pointer = nullptr; - other.control = nullptr; - } - - /** - * @brief Copy assignment operator. Replaces the managed object with the one managed by r. Shares ownership of the - * object managed by r. If r manages no object, *this manages no object too. Equivalent to - * shared_ptr(r).swap(*this). - * - * @param other Another smart pointer to share the ownership with. - * @return Reference to this shared pointer. - */ - auto operator=(shared_ptr const & other) -> shared_ptr & - { - if (this != &other) - { - cleanup(); - pointer = other.pointer; - control = other.control; - - if (control != nullptr) - { - ++(control->shared_count); - } - } - - return *this; - } - - /** - * @brief Converting copy assignment for compatible shared_ptr types. - * - * @tparam U Source pointer element type. - * @param other Another smart pointer to share ownership with. - * @return Reference to this shared pointer. - */ - template - requires(std::is_convertible_v) - auto operator=(shared_ptr const & other) -> shared_ptr & - { - cleanup(); - pointer = other.pointer; - control = other.control; - - if (control != nullptr) - { - ++(control->shared_count); - } - - return *this; - } - - /** - * @brief Move assignment operator. Move-assigns a shared_ptr from r. After the assignment, *this contains a copy of - * the previous state of r, and r is empty. Equivalent to shared_ptr(std::move(r)).swap(*this). - * - * @param other Another smart pointer to acquire the ownership from. - * @return Reference to this shared pointer. - */ - auto operator=(shared_ptr && other) noexcept -> shared_ptr & - { - if (this != &other) - { - cleanup(); - pointer = other.pointer; - control = other.control; - other.pointer = nullptr; - other.control = nullptr; - } - - return *this; - } - - /** - * @brief Converting move assignment for compatible shared_ptr types. - * - * @tparam U Source pointer element type. - * @param other Another smart pointer to acquire ownership from. - * @return Reference to this shared pointer. - */ - template - requires(std::is_convertible_v) - auto operator=(shared_ptr && other) noexcept -> shared_ptr & - { - cleanup(); - pointer = other.pointer; - control = other.control; - other.pointer = nullptr; - other.control = nullptr; - - return *this; - } - - /** - * @brief Reset this shared_ptr to empty via nullptr assignment. - */ - auto operator=(std::nullptr_t) noexcept -> shared_ptr & - { - cleanup(); - pointer = nullptr; - control = nullptr; - return *this; - } - - /** - * @brief Destructor. Cleans up resources if necessary. - */ - ~shared_ptr() - { - cleanup(); - } - - /** - * @brief Replaces the managed object. - * - * @param ptr Pointer to a new object to manage (default = nullptr). - */ - void reset(T * ptr = nullptr) - { - cleanup(); - pointer = ptr; - control = ptr != nullptr ? new shared_control_block() : nullptr; - assign_enable_shared_from_this(ptr); - } - - /** - * @brief Exchanges the stored pointer values and the ownerships of *this and r. Reference counts, if any, are not - * adjusted. - * - * @param other The shared_ptr to swap with. - */ - void swap(shared_ptr & other) - { - std::swap(pointer, other.pointer); - std::swap(control, other.control); - } - - /** - * @brief Dereference operator. If get() is a null pointer, the behavior is undefined. - * - * @return Returns the object owned by *this, equivalent to *get(). - */ - [[nodiscard]] auto operator*() const -> T & - { - return *pointer; - } - - /** - * @brief Member access operator. - * - * @return Returns a pointer to the object owned by *this, i.e. get(). - */ - [[nodiscard]] auto operator->() const -> T * - { - return pointer; - } - - /** - * @brief Returns a pointer to the managed object or nullptr if no object is owned. - * - * @return Pointer to the managed object or nullptr if no object is owned. - */ - [[nodiscard]] auto get() const -> T * - { - return pointer; - } - - /** - * @brief Returns the number of different shared_ptr instances (*this included) managing the current object. If - * there is no managed object, ​0​ is returned. - * - * @note Common use cases include comparison with ​0​. If use_count returns zero, the shared pointer is empty - * and manages no objects (whether or not its stored pointer is nullptr). Comparison with 1. If use_count returns 1, - * there are no other owners. - * - * @return The number of Shared_pointer instances managing the current object or ​0​ if there is no managed - * object. - */ - [[nodiscard]] auto use_count() const -> std::size_t - { - if (control != nullptr) - { - return control->shared_count; - } - - return 0; - } - - /** - * @brief Checks whether *this owns an object, i.e. whether get() != nullptr. - * - * @return true if *this owns an object, false otherwise. - */ - [[nodiscard]] explicit operator bool() const - { - return pointer != nullptr; - } - - /** - * @brief Compare shared_ptr with nullptr. - */ - [[nodiscard]] auto operator==(std::nullptr_t) const -> bool - { - return pointer == nullptr; - } - - /** - * @brief Compare nullptr with shared_ptr. - */ - [[nodiscard]] friend auto operator==(std::nullptr_t, shared_ptr const & ptr) -> bool - { - return ptr.pointer == nullptr; - } - - private: - /** - * @brief If the candidate type inherits from enable_shared_from_this, assigns the internal weak pointer to this - * shared_ptr. This weak_ptr is used to implement shared_from_this functionality for the candidate type. If the - * candidate type does not inherit from enable_shared_from_this, this function does nothing. - * - * @tparam U The candidate type to check for enable_shared_from_this inheritance. - * @param candidate The candidate object to assign the internal weak pointer for. - */ - template - auto assign_enable_shared_from_this(U * candidate) -> void - { - if constexpr (requires(U * p, shared_ptr const & sp) { p->internal_assign_ptr(sp); }) - { - if (candidate != nullptr) - { - candidate->internal_assign_ptr(*this); - } - } - } - - /** - * @brief Releases ownership and deletes the object if this was the last reference to the owned managed object. - */ - auto cleanup() -> void - { - if (control != nullptr) - { - if (--(control->shared_count) == 0) - { - delete pointer; - pointer = nullptr; - - if (control->weak_count == 0) - { - delete control; - } - } - } - } - - T * pointer; ///< The managed object. - shared_control_block * control; ///< Shared control block. - }; - - /** - * @brief Specializes the std::swap algorithm for stl::unique_ptr. Swaps the contents of lhs and rhs. Calls - * lhs.swap(rhs). - * - * @tparam T Type of the managed object. - * @param lhs, rhs Smart pointers whose contents to swap. - */ - template - auto swap(shared_ptr & lhs, shared_ptr & rhs) -> void - { - lhs.swap(rhs); - } - - /** - * @brief Constructs an object of type T and wraps it in a shared_ptr. Constructs a non-array type T. The - * arguments args are passed to the constructor of T. This overload participates in overload resolution only if T is - * not an array type. The function is equivalent to: shared_ptr(new T(std::forward(args)...)). - * - * @tparam T Type of the managed object. - * @tparam Args Argument types for T's constructor. - * @param args List of arguments with which an instance of T will be constructed. - * @returns Shared_pointer of an instance of type T. - */ - template - auto make_shared(Args &&... args) -> shared_ptr - { - return shared_ptr(new T(std::forward(args)...)); - } - - /** - * @brief Equality operator for shared_ptr. Two shared_ptr instances are equal if they point to the same object - * @tparam T, U Types of the managed objects of the shared_ptr instances being compared. - * @param lhs, rhs The shared_ptr instances to compare. - * @return true if lhs and rhs point to the same object, false otherwise. - */ - template - [[nodiscard]] auto inline operator==(shared_ptr const & lhs, shared_ptr const & rhs) -> bool - { - return lhs.get() == rhs.get(); - } - - /** - * @brief Three-way comparison operator for shared_ptr. Compares the stored pointers of lhs and rhs using operator<=>. - * @tparam T, U Types of the managed objects of the shared_ptr instances being compared. - * @param lhs, rhs The shared_ptr instances to compare. - * @return The result of comparing the stored pointers of lhs and rhs using operator<=> - */ - template - [[nodiscard]] auto inline operator<=>(shared_ptr const & lhs, shared_ptr const & rhs) - { - return lhs.get() <=> rhs.get(); - } -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/bits/unique_ptr.hpp b/libs/kstd/include/kstd/bits/unique_ptr.hpp deleted file mode 100644 index 3d803b4..0000000 --- a/libs/kstd/include/kstd/bits/unique_ptr.hpp +++ /dev/null @@ -1,199 +0,0 @@ -#ifndef KSTD_BITS_UNIQUE_POINTER_HPP -#define KSTD_BITS_UNIQUE_POINTER_HPP - -#include - -// IWYU pragma: private, include - -namespace kstd -{ - /** - * @brief Unique_pointer is a smart pointer that owns (is responsible for) and manages another object via a pointer - * and subsequently disposes of that object when the unique_ptr goes out of scope. - * - * @tparam T Type of the managed object. - */ - template - struct unique_ptr - { - template - friend struct unique_ptr; - - /** - * @brief Constructor. - * - * @param ptr A pointer to an object to manage (default is nullptr). - */ - explicit unique_ptr(T * ptr = nullptr) - : pointer(ptr) - { - // Nothing to do. - } - - /** - * @brief Destructor that deletes the managed object. - */ - ~unique_ptr() - { - delete pointer; - } - - /** - * @brief Deleted copy constructor to enforce unique ownership. - */ - unique_ptr(unique_ptr const &) = delete; - - template - requires(std::is_convertible_v) - unique_ptr(unique_ptr && other) noexcept - : pointer{std::exchange(other.pointer, nullptr)} - {} - - /** - * @brief Deleted copy assignment operator to enforce unique ownership. - */ - auto operator=(unique_ptr const &) -> unique_ptr & = delete; - - /** - * @brief Move constructor. - * - * @param other Unique pointer to move from. - */ - unique_ptr(unique_ptr && other) noexcept - : pointer{std::exchange(other.pointer, nullptr)} - {} - - /** - * @brief Move assignment operator. Transfers ownership from other to *this as if by calling reset(r.release()). - * - * @param other Smart pointer from which ownership will be transferred. - * @return Reference to this unique pointer. - */ - auto operator=(unique_ptr && other) noexcept -> unique_ptr & - { - if (this != &other) - { - delete pointer; - pointer = std::exchange(other.pointer, nullptr); - } - return *this; - } - - /** - * @brief Dereference operator. If get() is a null pointer, the behavior is undefined. - * - * @return Returns the object owned by *this, equivalent to *get(). - */ - auto operator*() const -> T & - { - return *pointer; - } - - /** - * @brief Member access operator. - * - * @return Returns a pointer to the object owned by *this, i.e. get(). - */ - auto operator->() const -> T * - { - return pointer; - } - - /** - * @brief Returns a pointer to the managed object or nullptr if no object is owned. - * - * @return Pointer to the managed object or nullptr if no object is owned. - */ - [[nodiscard]] auto get() const -> T * - { - return pointer; - } - - /** - * @brief Checks whether *this owns an object, i.e. whether get() != nullptr. - * - * @return true if *this owns an object, false otherwise. - */ - explicit operator bool() const noexcept - { - return pointer != nullptr; - } - - /** - * @brief Releases the ownership of the managed object, if any. - * get() returns nullptr after the call. - * The caller is responsible for cleaning up the object (e.g. by use of get_deleter()). - * - * @return Pointer to the managed object or nullptr if there was no managed object, i.e. the value which would be - * returned by get() before the call. - */ - auto release() -> T * - { - return std::exchange(pointer, nullptr); - } - - /** - * @brief Replaces the managed object. - * - * @note A test for self-reset, i.e. whether ptr points to an object already managed by *this, is not performed, - * except where provided as a compiler extension or as a debugging assert. Note that code such as - * p.reset(p.release()) does not involve self-reset, only code like p.reset(p.get()) does. - * - * @param ptr Pointer to a new object to manage (default = nullptr). - */ - auto reset(T * ptr = nullptr) -> void - { - delete std::exchange(pointer, ptr); - } - - /** - * @brief Swaps the managed objects and associated deleters of *this and another unique_ptr object other. - * - * @param other Another unique_ptr object to swap the managed object and the deleter with. - */ - auto swap(unique_ptr & other) -> void - { - using std::swap; - swap(pointer, other.pointer); - } - - /** - * @brief Defaulted three-way comparator operator. - */ - auto operator<=>(unique_ptr const & other) const = default; - - private: - T * pointer; ///< The managed pointer. - }; - - /** - * @brief Specializes the std::swap algorithm for stl::unique_ptr. Swaps the contents of lhs and rhs. Calls - * lhs.swap(rhs). - * - * @tparam T Type of the managed object. - * @param lhs, rhs Smart pointers whose contents to swap. - */ - template - auto swap(unique_ptr & lhs, unique_ptr & rhs) -> void - { - lhs.swap(rhs); - } - - /** - * @brief Constructs an object of type T and wraps it in a unique_ptr. Constructs a non-array type T. The - * arguments args are passed to the constructor of T. This overload participates in overload resolution only if T is - * not an array type. The function is equivalent to: unique_ptr(new T(std::forward(args)...)). - * - * @tparam T Type of the managed object. - * @tparam Args Argument types for T's constructor. - * @param args List of arguments with which an instance of T will be constructed. - * @returns Unique_pointer of an instance of type T. - */ - template - auto make_unique(Args &&... args) -> unique_ptr - { - return unique_ptr(new T(std::forward(args)...)); - } -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/cstring b/libs/kstd/include/kstd/cstring deleted file mode 100644 index bd8b28d..0000000 --- a/libs/kstd/include/kstd/cstring +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef KSTD_CSTRING -#define KSTD_CSTRING - -#include - -namespace kstd::libc -{ - - extern "C" - { - 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) noexcept -> std::size_t; - } - -} // namespace kstd::libc - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/ext/bitfield_enum b/libs/kstd/include/kstd/ext/bitfield_enum deleted file mode 100644 index 80fe9d2..0000000 --- a/libs/kstd/include/kstd/ext/bitfield_enum +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef KSTD_EXT_BITFIELD_ENUM_HPP -#define KSTD_EXT_BITFIELD_ENUM_HPP - -#include -#include -#include - -namespace kstd::ext -{ - - template - requires std::is_enum_v - struct is_bitfield_enum : std::false_type - { - }; - - //! @concept Specifies that an enum is to be used to define bits in a bitfield. - template - concept bitfield_enum = is_bitfield_enum::value; - -}; // namespace kstd::ext - -template -constexpr auto operator|(EnumType lhs, EnumType rhs) -> EnumType -{ - return std::bit_cast(std::to_underlying(lhs) | std::to_underlying(rhs)); -} - -template -constexpr auto operator|=(EnumType & lhs, EnumType rhs) -> EnumType & -{ - return lhs = lhs | rhs; -} - -template -constexpr auto operator&(EnumType lhs, EnumType rhs) -> EnumType -{ - return std::bit_cast(std::to_underlying(lhs) & std::to_underlying(rhs)); -} - -template -constexpr auto operator&=(EnumType & lhs, EnumType rhs) -> EnumType & -{ - return lhs = lhs & rhs; -} - -template -constexpr auto operator^(EnumType lhs, EnumType rhs) -> EnumType -{ - return std::bit_cast(std::to_underlying(lhs) ^ std::to_underlying(rhs)); -} - -template -constexpr auto operator^=(EnumType & lhs, EnumType rhs) -> EnumType & -{ - return lhs = lhs ^ rhs; -} - -template -constexpr auto operator~(EnumType lhs) -> EnumType -{ - return std::bit_cast(~std::to_underlying(lhs)); -} - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/flat_map b/libs/kstd/include/kstd/flat_map deleted file mode 100644 index f12b1b5..0000000 --- a/libs/kstd/include/kstd/flat_map +++ /dev/null @@ -1,365 +0,0 @@ -#ifndef KSTD_FLAT_MAP_HPP -#define KSTD_FLAT_MAP_HPP - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace kstd -{ - - template, - typename KeyContainerType = kstd::vector, typename MappedContainerType = kstd::vector> - struct flat_map - { - using key_container_type = KeyContainerType; - using mapped_container_type = MappedContainerType; - using key_type = KeyType; - using mapped_type = MappedType; - using value_type = std::pair; - using key_compare = KeyCompare; - using reference = std::pair; - using const_reference = std::pair; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using iterator = bits::flat_map_iterator; - using const_iterator = - bits::flat_map_iterator; - using reverse_iterator = std::reverse_iterator; - using const_reverse_iterator = std::reverse_iterator; - using containers = struct - { - key_container_type keys; - mapped_container_type values; - }; - - //! Compare two object of type value_type. - struct value_compare - { - constexpr auto operator()(const_reference lhs, const_reference rhs) const -> bool - { - return lhs.first < rhs.first; - } - }; - - //! Construct an empty flat map. - constexpr flat_map() - : flat_map{key_compare{}} - {} - - //! Construct an empty flat map using the given custom comparator. - //! - //! @param comparator The comparator to use for comparing keys. - constexpr explicit flat_map(key_compare const & comparator) - : m_containers{} - , m_comparator{comparator} - {} - - //! Get a reference to the mapped value associated with the given key. - //! - //! @warning This function will panic if the key is not found. - //! @param key The key to look up. - //! @return A reference to the mapped value. - [[nodiscard]] constexpr auto at(key_type const & key) -> mapped_type & - { - auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); - if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) - { - auto offset = std::distance(m_containers.keys.begin(), found); - return *(m_containers.values.begin() + offset); - } - os::panic("[kstd::flat_map] Key not found"); - } - - //! Get a reference to the mapped value associated with the given key. - //! - //! @warning This function will panic if the key is not found. - //! @param key The key to look up. - //! @return A const reference to the mapped value. - [[nodiscard]] constexpr auto at(key_type const & key) const -> mapped_type const & - { - auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); - if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) - { - auto offset = std::distance(m_containers.keys.cbegin(), found); - return *(m_containers.values.cbegin() + offset); - } - os::panic("[kstd::flat_map] Key not found"); - } - - //! Get a reference to the mapped value associated with the given key. - //! - //! @note This overload only participates in overload resolution if the key compare type is transparent. - //! @warning This function will panic if the key is not found. - //! @param x The key to look up. - //! @return A reference to the mapped value. - template - requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } - [[nodiscard]] constexpr auto at(K const & x) -> mapped_type & - { - auto found = find(x); - if (found != end()) - { - auto offset = std::distance(m_containers.keys.begin(), found.key_iterator()); - return *(m_containers.values.begin() + offset); - } - os::panic("[kstd::flat_map] Key not found"); - } - - //! Get a reference to the mapped value associated with the given key. - //! @note This overload only participates in overload resolution if the key compare type is transparent. - //! @warning This function will panic if the key is not found. - //! @param x The key to look up. - //! @return A const reference to the mapped value. - template - requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } - [[nodiscard]] auto at(K const & x) const -> mapped_type const & - { - auto found = find(x); - if (found != end()) - { - auto offset = std::distance(m_containers.keys.cbegin(), found.key_iterator()); - return *(m_containers.values.cbegin() + offset); - } - os::panic("[kstd::flat_map] Key not found"); - } - - //! Get an iterator to the first element. - [[nodiscard]] auto begin() noexcept -> iterator - { - return iterator{m_containers.keys.begin(), m_containers.values.begin()}; - } - - //! Get an iterator to the first element. - [[nodiscard]] auto begin() const noexcept -> const_iterator - { - return const_iterator{m_containers.keys.cbegin(), m_containers.values.cbegin()}; - } - - //! Get an iterator to the first element. - [[nodiscard]] auto cbegin() const noexcept -> const_iterator - { - return const_iterator{m_containers.keys.cbegin(), m_containers.values.cbegin()}; - } - - //! Get an iterator to the element past the last element. - [[nodiscard]] auto end() noexcept -> iterator - { - return iterator{m_containers.keys.end(), m_containers.values.end()}; - } - - //! Get an iterator to the element past the last element. - [[nodiscard]] auto end() const noexcept -> const_iterator - { - return const_iterator{m_containers.keys.cend(), m_containers.values.cend()}; - } - - //! Get an iterator to the element past the last element. - [[nodiscard]] auto cend() const noexcept -> const_iterator - { - return const_iterator{m_containers.keys.cend(), m_containers.values.cend()}; - } - - //! Get an iterator to the first element. - [[nodiscard]] auto rbegin() noexcept -> reverse_iterator - { - return reverse_iterator{end()}; - } - - //! Get an iterator to the first element. - [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator - { - return const_reverse_iterator{cend()}; - } - - //! Get an iterator to the first element. - [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator - { - return const_reverse_iterator{cend()}; - } - - //! Get an iterator to the element past the last element. - [[nodiscard]] auto rend() noexcept -> reverse_iterator - { - return reverse_iterator{begin()}; - } - - //! Get an iterator to the element past the last element. - [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator - { - return const_reverse_iterator{cbegin()}; - } - - //! Get an iterator to the element past the last element. - [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator - { - return const_reverse_iterator{cbegin()}; - } - - //! Check whether this flat map is empty or not - [[nodiscard]] auto empty() const noexcept -> bool - { - return m_containers.keys.empty(); - } - - //! Get the number of elements in this flat map. - [[nodiscard]] auto size() const noexcept -> size_type - { - return m_containers.keys.size(); - } - - //! Get the maximum number of elements possible in this flat map - [[nodiscard]] auto max_size() const noexcept -> size_type - { - return std::min(m_containers.keys.max_size(), m_containers.values.max_size()); - } - - //! Try to insert a new key-value pair into the map. - //! - //! @param args Arguments to use for constructing the key-value pair. - //! @return A pair of an iterator to the inserted element and a boolean indicating whether the insertion took place. - template - auto emplace(Args &&... args) -> std::pair - requires std::constructible_from - { - auto value = value_type{std::forward(args)...}; - auto found = std::ranges::lower_bound(m_containers.keys, value.first, m_comparator); - - if (found != m_containers.keys.cend() && !m_comparator(value.first, *found) && !m_comparator(*found, value.first)) - { - auto offset = std::distance(m_containers.keys.begin(), found); - return { - iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}, - false - }; - } - - auto offset = std::distance(m_containers.keys.begin(), found); - auto key_iterator = m_containers.keys.begin() + offset; - auto mapped_iterator = m_containers.values.begin() + offset; - - auto inserted_key = m_containers.keys.insert(key_iterator, std::move(value.first)); - auto inserted_mapped = m_containers.values.insert(mapped_iterator, std::move(value.second)); - - return { - iterator{inserted_key, inserted_mapped}, - true - }; - } - - //! Try to insert a element for the given key into this map. - //! - //! This function does nothing if the key is already present. - //! - //! @param key The key to insert a value for. - //! @param args The arguments to use to construct the mapped value. - template - auto try_emplace(key_type const & key, Args &&... args) -> std::pair - { - auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); - if (found != m_containers.keys.cend() && !m_comparator(*found, key) && !m_comparator(key, *found)) - { - return {found, false}; - } - - auto offset = std::distance(m_containers.keys.cbegin(), found); - auto intersertion_point = m_containers.value.begin() + offset; - - auto inserted_key = m_containers.keys.emplace(key); - auto inserted_mapped = m_containers.values.emplace(std::forward(args)...); - - return { - iterator{inserted_key, inserted_mapped}, - true - }; - } - - //! Find an element with an equivalent key. - //! - //! @param key The key to look up. - //! @return An iterator to the element with the equivalent key, or end() if no such element is found. - [[nodiscard]] auto find(key_type const & key) noexcept -> iterator - { - auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); - if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) - { - auto offset = std::distance(m_containers.keys.begin(), found); - return iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}; - } - return end(); - } - - //! Find an element with an equivalent key. - //! - //! @param key The key to look up. - //! @return An iterator to the element with the equivalent key, or end() if no such element is found. - [[nodiscard]] auto find(key_type const & key) const noexcept -> const_iterator - { - auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); - if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) - { - auto offset = std::distance(m_containers.keys.cbegin(), found); - return const_iterator{m_containers.keys.cbegin() + offset, m_containers.values.cbegin() + offset}; - } - return cend(); - } - - //! Find an element with an equivalent key. - //! @note This overload only participates in overload resolution if the key compare type is transparent. - //! @param x The key to look up. - //! @return An iterator to the element with the equivalent key, or end() if no such element is found. - template - requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } - [[nodiscard]] auto find(K const & x) noexcept -> iterator - { - auto found = std::ranges::lower_bound(m_containers.keys, x, m_comparator); - if (found != m_containers.keys.cend() && !m_comparator(x, *found) && !m_comparator(*found, x)) - { - auto offset = std::distance(m_containers.keys.begin(), found); - return iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}; - } - return end(); - } - - //! Find an element with an equivalent key. - //! @note This overload only participates in overload resolution if the key compare type is transparent. - //! @param x The key to look up. - //! @return An iterator to the element with the equivalent key, or end() if no such element is found. - template - requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } - [[nodiscard]] auto find(K const & x) const noexcept -> const_iterator - { - auto found = std::ranges::lower_bound(m_containers.keys, x, m_comparator); - if (found != m_containers.keys.cend() && !m_comparator(x, *found) && !m_comparator(*found, x)) - { - auto offset = std::distance(m_containers.keys.cbegin(), found); - return const_iterator{m_containers.keys.cbegin() + offset, m_containers.values.cbegin() + offset}; - } - return cend(); - } - - //! Check if the map contains the given key. - //! - //! @param key The key to check. - //! @return true iff. the key is found, false otherwise. - [[nodiscard]] constexpr auto contains(key_type const & key) const noexcept -> bool - { - return find(key) != cend(); - } - - private: - containers m_containers; - key_compare m_comparator; - }; -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/format b/libs/kstd/include/kstd/format deleted file mode 100644 index e04b79a..0000000 --- a/libs/kstd/include/kstd/format +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef KSTD_FORMAT_HPP -#define KSTD_FORMAT_HPP - -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/memory b/libs/kstd/include/kstd/memory deleted file mode 100644 index f108c6d..0000000 --- a/libs/kstd/include/kstd/memory +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef KSTD_MEMORY_HPP -#define KSTD_MEMORY_HPP - -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/mutex b/libs/kstd/include/kstd/mutex deleted file mode 100644 index b2a31aa..0000000 --- a/libs/kstd/include/kstd/mutex +++ /dev/null @@ -1,101 +0,0 @@ -#ifndef KSTD_MUTEX_HPP -#define KSTD_MUTEX_HPP - -#include - -namespace kstd -{ - - //! A non-recursive mutex. - struct mutex - { - mutex(); - ~mutex(); - - mutex(mutex const &) = delete; - mutex(mutex &&) = delete; - - auto operator=(mutex const &) -> mutex & = delete; - auto operator=(mutex &&) -> mutex & = delete; - - //! Lock the mutex. - //! - //! @note This function blocks for as long as the mutex is not available. - auto lock() -> void; - - //! Try to lock the mutex. - //! - //! @note This function never blocks. - //! @return @p true iff. the mutex was successfully locked, @p false otherwise. - auto try_lock() -> bool; - - //! Unlock the mutex. - //! - //! @note The behavior is undefined if the mutex is not currently held by the thread unlocking it. - auto unlock() -> void; - - private: - std::atomic_flag m_locked{}; - }; - - //! A tag type to specify that a given @p lockable wrapper should adopt ownership of the @p lockable. - struct adopt_lock_t - { - explicit adopt_lock_t() = default; - } constexpr inline adopt_lock{}; - - //! A tag type to specify that a given @p lockable wrapper should defer locking the @p lockable. - struct defer_lock_t - { - explicit defer_lock_t() = default; - } constexpr inline defer_lock{}; - - //! A tag type to specify that a given @p lockable wrapper should attempt to lock the @p lockable. - struct try_to_lock_t - { - explicit try_to_lock_t() = default; - } constexpr inline try_to_lock{}; - - //! An RAII wrapper for a single @p lockable like a kstd::mutex. - template - struct lock_guard - { - using mutex_type = MutexType; - - //! Construct a new lock_guard and immediately lock the given mutex. - //! - //! @note This function will block until the mutex was successfully locked. - //! @param mutex The mutex to lock. - lock_guard(MutexType & mutex) noexcept - : m_mutex(mutex) - { - m_mutex.lock(); - } - - //! Construct a new lock_guard and take ownership of the given mutex. - //! - //! @note The behavior is undefined if the mutex is not owned by the current thread. - //! @param mutex The mutex to take ownership of. - lock_guard(MutexType & mutex, adopt_lock_t) noexcept - : m_mutex(mutex) - {} - - //! Destroy this lock_guard and release the owned mutex. - ~lock_guard() - { - m_mutex.unlock(); - } - - lock_guard(lock_guard const &) noexcept = delete; - lock_guard(lock_guard &&) noexcept = delete; - - auto operator=(lock_guard const &) noexcept -> lock_guard & = delete; - auto operator=(lock_guard &&) noexcept -> lock_guard & = delete; - - private: - mutex_type & m_mutex; - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/os/error.hpp b/libs/kstd/include/kstd/os/error.hpp deleted file mode 100644 index 9d43fb1..0000000 --- a/libs/kstd/include/kstd/os/error.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef KSTD_OS_ERROR_HPP -#define KSTD_OS_ERROR_HPP - -#include -#include - -namespace kstd::os -{ - /** - * @brief Handle an unrecoverable library error. - * - * The operating system kernel may choose to implement this function in order to try to perform additional cleanup - * before terminating execution. If the kernel does not implement this function, the default implementation will be - * chosen. This default implementation doest nothing. - */ - [[noreturn]] - auto abort() -> void; - - /** - * @brief Terminate execution of the operating system. - * - * The operating system must implement this function. This function must terminate the execution of the operating - * system kernel as is. It may choose to restart the kernel or to halt execution entirely. The implementation must - * guarantee that execution never return from this function. - * - * @param message A message describing the reason for termination of execution. - * @param where The source code location at which the panic was triggered. In general, no argument shall be provided - * for this parameter, thus implicitly capturing the location at which the call originates. - */ - [[noreturn]] - auto panic(std::string_view message, std::source_location where = std::source_location::current()) -> void; -} // namespace kstd::os - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/os/print.hpp b/libs/kstd/include/kstd/os/print.hpp deleted file mode 100644 index 36cb43d..0000000 --- a/libs/kstd/include/kstd/os/print.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef KSTD_OS_PRINT_HPP -#define KSTD_OS_PRINT_HPP - -#include -#include - -#include - -namespace kstd::os -{ - auto vprint(print_sink sink, std::string_view format, kstd::format_args args) -> void; -} // namespace kstd::os - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/print b/libs/kstd/include/kstd/print deleted file mode 100644 index 1033f72..0000000 --- a/libs/kstd/include/kstd/print +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef KSTD_PRINT -#define KSTD_PRINT - -#include // IWYU pragma: export -#include -#include - -#include - -namespace kstd -{ - - //! @qualifier kernel-defined - //! Format the given string using the given arguments and print it to the currently active output device. - //! - //! @param format The format string - //! @param args The arguments to use to place in the format string's placeholders. - template - // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) - auto print(kstd::format_string...> format, Args const &... args) -> void - { - auto const arg_store = kstd::make_format_args(args...); - os::vprint(print_sink::stdout, format.str_view, arg_store.args); - } - - //! @qualifier kernel-defined - //! Format the given error string using the given arguments and print it to the currently active output device. - //! - //! @param format The format string - //! @param args The arguments to use to place in the format string's placeholders. - template - // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) - auto print(print_sink sink, kstd::format_string...> format, Args &&... args) -> void - { - auto const arg_store = kstd::make_format_args(args...); - os::vprint(sink, format.str_view, arg_store.args); - } - - //! @qualifier kernel-defined - //! Format the given string using the given arguments and print it, including a newline, to the currently active - //! output device. - //! - //! @param format The format string - //! @param args The arguments to use to place in the format string's placeholders. - template - // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) - auto println(kstd::format_string...> format, Args &&... args) -> void - { - print(print_sink::stdout, format, std::forward(args)...); - print(print_sink::stdout, "\n"); - } - - //! @qualifier kernel-defined - //! Format the given error string using the given arguments and print it, including a newline, to the currently active - //! output device. - //! - //! @param format The format string - //! @param args The arguments - template - // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) - auto println(print_sink sink, kstd::format_string...> format, Args &&... args) -> void - { - print(sink, format, std::forward(args)...); - print(sink, "\n"); - } - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/ranges b/libs/kstd/include/kstd/ranges deleted file mode 100644 index 78c3adb..0000000 --- a/libs/kstd/include/kstd/ranges +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef KSTD_RANGES -#define KSTD_RANGES - -#include // IWYU pragma: export - -namespace kstd -{ -#if __glibcxx_ranges_to_container - using std::from_range; - using std::from_range_t; -#else - struct from_range_t - { - explicit from_range_t() = default; - }; - constexpr auto inline from_range = from_range_t{}; -#endif - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/stack b/libs/kstd/include/kstd/stack deleted file mode 100644 index 02e44ea..0000000 --- a/libs/kstd/include/kstd/stack +++ /dev/null @@ -1,192 +0,0 @@ -#ifndef KSTD_STACK_HPP -#define KSTD_STACK_HPP - -#include - -#include -#include - -namespace kstd -{ - /** - * @brief Custom stack implementation mirroring the std::stack to allow for the usage of STL functionality with our - * custom memory management. - * - * @tparam T Element the stack instance should contain. - * @tparam Container Actual underlying container that should be wrapped to provide stack functionality. Requires - * access to pop_back(), push_back(), back(), size(), empty() and emplace_back() - */ - template> - struct stack - { - using container_type = Container; ///< Type of the underlying container used to implement stack-like interface. - using value_type = Container::value_type; ///< Type of the elements contained in the underlying container. - using size_type = Container::size_type; ///< Type of the size in the underlying container. - using reference = Container::reference; ///< Type of reference to the elements. - using const_reference = Container::const_reference; ///< Type of constant reference to the elements. - - /** - * @brief Default Constructor. - */ - stack() = default; - - stack(stack const &) = delete; - stack(stack &&) = delete; - auto operator=(stack const &) -> stack & = delete; - auto operator=(stack &&) -> stack & = delete; - /** - * @brief Constructs data with the given amount of elements containing the given value or alternatively the default - * constructed value. - * - * @param n Amount of elements we want to create and set the given value for. - * @param initial Inital value of all elements in the underlying data array. - */ - explicit stack(size_type n, value_type initial = value_type{}) - : _container(n, initial) - { - // Nothing to do. - } - - /** - * @brief Constructs data by copying all element from the given exclusive range. - * - * @tparam InputIterator Template that should have atleast input iterator characteristics. - * @param first Input iterator to the first element in the range we want to copy from. - * @param last Input iterator to one past the last element in the range we want to copy from. - */ - template - explicit stack(InputIterator first, InputIterator last) - : _container(first, last) - { - // Nothing to do. - } - - /** - * @brief Construct data by copying all elements from the initializer list. - * - * @param elements List we want to copy all elements from. - */ - explicit stack(std::initializer_list elements) - : _container(elements) - { - // Nothing to do. - } - - /** - * @brief Copy constructor. - * - * @note Allocates underlying data container with the same capacity as stack we are copying from and copies all - * elements from it. - * - * @param other Other instance of stack we want to copy the data from. - */ - stack(stack const & other) - : _container(other) - { - // Nothing to do. - } - - /** - * @brief Destructor. - */ - ~stack() = default; - - /** - * @brief Amount of elements currently contained in this vector, will fill up until we have reached the capacity. If - * that is the case the capacity is increased automatically. - * - * @return Current amount of elements. - */ - auto size() const -> size_type - { - return _container.size(); - } - - /** - * @brief Returns a reference to the last element in the container. Calling back on an empty container causes - * undefined behavior. - * - * @return Reference to the last element. - */ - auto top() -> reference - { - return _container.back(); - } - - /** - * @brief Returns a reference to the last element in the container. Calling back on an empty container causes - * undefined behavior. - * - * @return Reference to the last element. - */ - auto top() const -> const_reference - { - return _container.back(); - } - - /** - * @brief Appends the given element value to the end of the container. The element is assigned through the - * assignment operator of the template type. The value is forwarded to the constructor as - * std::forward(value), meaning it is either moved (rvalue) or copied (lvalue). - * - * @note If after the operation the new size() is greater than old capacity() a reallocation takes place, - * in which case all iterators (including the end() iterator) and all references to the elements are invalidated. - * Otherwise only the end() iterator is invalidated. Uses a forward reference for the actual value passed, which - * allows the template method to be used by both lvalue and rvalues and compile a different implementation. - * - * @param value The value of the element to append. - */ - template - auto push(U && value) -> void - { - _container.push_back(std::forward(value)); - } - - /** - * @brief Appends a new element to the end of the container. The element is constructed through a constructor of the - * template type. The arguments args... are forwarded to the constructor as std::forward(args).... - * - * If after the operation the new size() is greater than old capacity() a reallocation takes place, in which case - * all iterators (including the end() iterator) and all references to the elements are invalidated. Otherwise only - * the end() iterator is invalidated. Uses a forward reference for the actual value passed, which - * allows the template method to be used by both lvalue and rvalues and compile a different implementation. - * - * @tparam Args - * @param args Arguments to forward to the constructor of the element - * @return value_type& - */ - template - auto emplace(Args &&... args) -> reference - { - _container.emplace_back(std::forward(args)...); - } - - /** - * @brief Removes the last element of the container. - * - * @note Calling pop_back on an empty container results in halting the - * further execution. Iterators and references to the last element are invalidated. The end() - * iterator is also invalidated. - */ - auto pop() -> void - { - _container.pop_back(); - } - - /** - * @brief Whether there are currently any items this container or not. - * - * @return True if there are no elements, false if there are. - */ - auto empty() const -> bool - { - return _container.empty(); - } - - private: - container_type _container = {}; ///< Underlying container used by the stack to actually save the data. - }; - -} // namespace kstd - -#endif diff --git a/libs/kstd/include/kstd/string b/libs/kstd/include/kstd/string deleted file mode 100644 index e228a04..0000000 --- a/libs/kstd/include/kstd/string +++ /dev/null @@ -1,380 +0,0 @@ -#ifndef KSTD_STRING_HPP -#define KSTD_STRING_HPP - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace kstd -{ - /** - * @brief A simple string implementation that owns its data and provides basic operations. - */ - struct string - { - //! The type of the characters contained in this string. - using value_type = char; - //! The type of the underlying storage used by this string. - using storage_type = kstd::vector; - //! The type of all sizes used in and with this string. - using size_type = std::size_t; - //! The type of the difference between two iterators. - using difference_type = std::ptrdiff_t; - //! The type of references to single values in this string. - using reference = value_type &; - //! The type of references to constant single values in this string. - using const_reference = value_type const &; - //! The type of pointers to single values in this string. - using pointer = value_type *; - //! The type of pointers to constant single values in this string. - using const_pointer = value_type const *; - //! The type of iterators into this string. - using iterator = pointer; - //! The type of constant iterators into this string. - using const_iterator = const_pointer; - - /** - * @brief Constructs an empty null-terminated string. - */ - string() - : m_storage{value_type{'\0'}} - {} - - /** - * @brief Constructs a string from a string view by copying the characters into owned storage. - * @param view The string view to copy the characters from. - */ - string(std::string_view view) - : string() - { - append(view); - } - - /** - * @brief Constructs a string from a null-terminated C-style string by copying the characters into owned storage. - * @param c_str The null-terminated C-style string to copy. - */ - string(char const * c_str) - : string() - { - if (c_str != nullptr) - { - append(std::string_view{c_str}); - } - } - - /** - * @brief Constructs a string containing a single character. - * @param c The character to copy. - */ - string(value_type c) - : string() - { - push_back(c); - } - - /** - * @brief Constructs a string by copying another string. - * @param other The string to copy. - */ - constexpr string(string const & other) - : m_storage{other.m_storage} - {} - - /** - * @brief Destructs the string. - */ - constexpr ~string() = default; - - /** - * @brief Assigns the value of another string to this string. - * @param other The string to assign from. - * @return A reference to this string. - */ - constexpr auto operator=(string const & other) -> string & = default; - - /** - * @brief Returns the number of characters in this string, not including the null terminator. - */ - [[nodiscard]] constexpr auto size() const noexcept -> size_type - { - return m_storage.empty() ? 0 : m_storage.size() - 1; - } - - /** - * @brief Checks if this string is empty, not including the null terminator. - */ - [[nodiscard]] constexpr auto empty() const noexcept -> bool - { - return size() == 0; - } - - /** - * @brief Clears the content of the string, resulting in an empty string. - * The string remains null-terminated after this operation. - */ - constexpr auto clear() -> void - { - m_storage.clear(); - m_storage.push_back(value_type{'\0'}); - } - - //! Get a pointer to the underlying storage of the string - [[nodiscard]] constexpr auto data() noexcept -> pointer - { - return m_storage.data(); - } - - //! Get a const pointer to the underlying storage of the string - [[nodiscard]] constexpr auto data() const noexcept -> const_pointer - { - return m_storage.data(); - } - - //! Get a const pointer to the underlying storage of the string - [[nodiscard]] constexpr auto c_str() const noexcept -> const_pointer - { - return data(); - } - - //! Get an iterator to the beginning of the string - [[nodiscard]] constexpr auto begin() noexcept -> iterator - { - return data(); - } - - //! Get an const iterator to the beginning of the string - [[nodiscard]] constexpr auto begin() const noexcept -> const_iterator - { - return data(); - } - - //! Get an const iterator to the beginning of the string - [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator - { - return begin(); - } - - //! Get an iterator to the end of the string - [[nodiscard]] constexpr auto end() noexcept -> iterator - { - return data() + size(); - } - - //! Get an const iterator to the end of the string - [[nodiscard]] constexpr auto end() const noexcept -> const_iterator - { - return data() + size(); - } - - //! Get an const iterator to the end of the string - [[nodiscard]] constexpr auto cend() const noexcept -> const_iterator - { - return end(); - } - - //! Get a reference to the first character of the string - [[nodiscard]] constexpr auto front() -> reference - { - return m_storage.front(); - } - - //! Get a const reference to the first character of the string - [[nodiscard]] constexpr auto front() const -> const_reference - { - return m_storage.front(); - } - - //! Get a reference to the last character of the string - [[nodiscard]] constexpr auto back() -> reference - { - return m_storage[size() - 1]; - } - - //! Get a const reference to the last character of the string - [[nodiscard]] constexpr auto back() const -> const_reference - { - return m_storage[size() - 1]; - } - - /** - * @brief Appends a character to the end of the string. - * @param ch The character to append. - */ - constexpr auto push_back(value_type ch) -> void - { - m_storage.back() = ch; - m_storage.push_back(value_type{'\0'}); - } - - /** - * @brief Appends a string view to the end of the string by copying the characters into owned storage. - * @param view The string view to append. - * @return A reference to this string. - */ - constexpr auto append(std::string_view view) -> string & - { - if (!view.empty()) - { - std::ranges::for_each(view, [this](auto const ch) { push_back(ch); }); - } - - return *this; - } - - /** - * @brief Appends another string to the end of this string by copying the characters into owned storage. - * @param other The string to append. - * @return A reference to this string. - */ - constexpr auto append(string const & other) -> string & - { - return append(other.view()); - } - - /** - * @brief Appends another string to the end of this string by copying the characters into owned storage. - * @param other The string to append. - * @return A reference to this string. - */ - constexpr auto operator+=(string const & other) -> string & - { - return append(other); - } - - /** - * @brief Appends a character to the end of the string. - * @param ch The character to append. - * @return A reference to this string. - */ - constexpr auto operator+=(value_type ch) -> string & - { - push_back(ch); - return *this; - } - - /** - * @brief Returns a string view of this string, which is a non-owning view into the characters of this string. - */ - [[nodiscard]] constexpr auto view() const noexcept -> std::string_view - { - return std::string_view{data(), size()}; - } - - private: - //! The underlying storage of the string, which owns the characters and ensures null-termination. - storage_type m_storage{}; - }; - - /** - * @brief Concatenates a strings and a character and returns the result as a new string. - * @param lhs The string to concatenate. - * @param rhs The string to concatenate. - * @return A new string that is the result of concatenating @p lhs and @p rhs. - */ - [[nodiscard]] constexpr auto inline operator+(string const & lhs, string const & rhs) -> string - { - string result{lhs}; - result += rhs; - return result; - } - - /** - * @brief Converts an unsigned integer to a string by converting each digit to the corresponding character and - * concatenating them. - * @tparam N The type of the unsigned integer to convert. - * @param value The unsigned integer to convert. - * @return A string representation of the given unsigned integer. - */ - template - requires std::unsigned_integral - [[nodiscard]] constexpr auto inline to_string(N value) -> string - { - if (value == 0) - { - return "0"; - } - - string result; - - while (value > 0) - { - char const digit = '0' + (value % 10); - result.push_back(digit); - value /= 10; - } - - std::reverse(result.begin(), result.end()); - return result; - } - - [[nodiscard]] constexpr auto inline operator==(string const & lhs, string const & rhs) -> bool - { - return lhs.view() == rhs.view(); - } - - [[nodiscard]] constexpr auto inline operator!=(string const & lhs, string const & rhs) -> bool - { - return !(lhs == rhs); - } - - [[nodiscard]] constexpr auto inline operator==(string const & lhs, std::string_view rhs) -> bool - { - return lhs.view() == rhs; - } - - [[nodiscard]] constexpr auto inline operator!=(string const & lhs, std::string_view rhs) -> bool - { - return !(lhs == rhs); - } - - [[nodiscard]] constexpr auto inline operator==(std::string_view lhs, string const & rhs) -> bool - { - return lhs == rhs.view(); - } - - [[nodiscard]] constexpr auto inline operator!=(std::string_view lhs, string const & rhs) -> bool - { - return !(lhs == rhs); - } - - [[nodiscard]] constexpr auto inline operator==(string const & lhs, char const * rhs) -> bool - { - return lhs.view() == std::string_view{rhs}; - } - - [[nodiscard]] constexpr auto inline operator!=(string const & lhs, char const * rhs) -> bool - { - return !(lhs == rhs); - } - - [[nodiscard]] constexpr auto inline operator==(char const * lhs, string const & rhs) -> bool - { - return std::string_view{lhs} == rhs.view(); - } - - [[nodiscard]] constexpr auto inline operator!=(char const * lhs, string const & rhs) -> bool - { - return !(lhs == rhs); - } - - template<> - struct formatter : formatter - { - auto format(string const & str, format_context & context) const -> void - { - formatter::format(str.view(), context); - } - }; - -} // namespace kstd - -#endif \ No newline at end of file diff --git a/libs/kstd/include/kstd/units b/libs/kstd/include/kstd/units deleted file mode 100644 index df5eb37..0000000 --- a/libs/kstd/include/kstd/units +++ /dev/null @@ -1,149 +0,0 @@ -#ifndef KSTD_UNITS_HPP -#define KSTD_UNITS_HPP - -#include -#include -#include - -namespace kstd -{ - - //! A basic template for strongly typed units. - template - struct basic_unit - { - using value_type = ValueType; - - constexpr basic_unit() noexcept - : value{} - {} - - explicit constexpr basic_unit(value_type value) noexcept - : value{value} - {} - - explicit constexpr operator value_type() const noexcept - { - return value; - } - - constexpr auto operator+(basic_unit const & other) const noexcept -> basic_unit - { - return basic_unit{value + other.value}; - } - - constexpr auto operator+=(basic_unit const & other) noexcept -> basic_unit & - { - return *this = *this + other; - } - - constexpr auto operator-(basic_unit const & other) const noexcept -> basic_unit - { - return basic_unit{value - other.value}; - } - - constexpr auto operator-=(basic_unit const & other) noexcept -> basic_unit & - { - return *this = *this - other; - } - - constexpr auto operator*(std::integral auto factor) noexcept -> basic_unit - { - return basic_unit{value * factor}; - } - - constexpr auto operator*=(std::integral auto factor) noexcept -> basic_unit - { - return *this = *this * factor; - } - - constexpr auto operator/(std::integral auto divisor) noexcept -> basic_unit - { - return basic_unit{value / divisor}; - } - - constexpr auto operator/=(std::integral auto divisor) noexcept -> basic_unit - { - return *this = *this / divisor; - } - - constexpr auto operator/(basic_unit const & other) const noexcept - { - return value / other.value; - } - - constexpr auto operator<=>(basic_unit const & other) const noexcept -> std::strong_ordering = default; - - value_type value; - }; - - template - constexpr auto operator*(Factor factor, basic_unit const & unit) noexcept - -> basic_unit - { - return basic_unit{unit.value * factor}; - } - - namespace units - { - using bytes = basic_unit; - - constexpr auto KiB(std::size_t value) noexcept -> bytes - { - return bytes{value * 1024}; - } - - constexpr auto MiB(std::size_t value) noexcept -> bytes - { - return bytes{value * 1024 * 1024}; - } - - constexpr auto GiB(std::size_t value) noexcept -> bytes - { - return bytes{value * 1024 * 1024 * 1024}; - } - - template - constexpr auto operator+(ValueType * pointer, bytes offset) -> ValueType * - { - return pointer + offset.value; - } - - } // namespace units - - namespace units_literals - { - constexpr auto operator""_B(unsigned long long value) noexcept -> units::bytes - { - return units::bytes{value}; - } - - constexpr auto operator""_KiB(unsigned long long value) noexcept -> units::bytes - { - return units::KiB(value); - } - - constexpr auto operator""_MiB(unsigned long long value) noexcept -> units::bytes - { - return units::MiB(value); - } - - constexpr auto operator""_GiB(unsigned long long value) noexcept -> units::bytes - { - return units::GiB(value); - } - - } // namespace units_literals - - template - constexpr auto object_size(ValueType const &) -> units::bytes - { - return units::bytes{sizeof(ValueType)}; - } - - template - constexpr auto type_size = units::bytes{sizeof(T)}; - -} // namespace kstd - -#endif diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector deleted file mode 100644 index c714957..0000000 --- a/libs/kstd/include/kstd/vector +++ /dev/null @@ -1,1154 +0,0 @@ -#ifndef KSTD_VECTOR_HPP -#define KSTD_VECTOR_HPP - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace kstd -{ - //! A resizable, contiguous container. - //! - //! @tparam ValueType The type of values contained in this vector. - //! @tparam Allocator The type of allocator used for memory management by this container. - template> - struct vector - { - //! The type of the elements contained in this vector. - using value_type = ValueType; - //! The allocator used by this vector for memory management. - using allocator_type = Allocator; - //! The type of all sizes used in and with this vector. - using size_type = std::size_t; - //! The type of the difference between two iterators. - using difference_type = std::ptrdiff_t; - //! The type of references to elements in this vector. - using reference = value_type &; - //! The type of references to constant elements in this vector. - using const_reference = value_type const &; - //! The type of pointers to elements in this vector. - using pointer = std::allocator_traits::pointer; - //! The type of pointers to constant elements in this vector. - using const_pointer = std::allocator_traits::const_pointer; - //! The type of iterators into this container. - using iterator = pointer; - //! The type of constant iterators into this container. - using const_iterator = const_pointer; - //! The type of reverse iterators into this container. - using reverse_iterator = std::reverse_iterator; - //! The type of constant reverse iterators into this container. - using const_reverse_iterator = std::reverse_iterator; - - //! Construct a new, empty vector. - constexpr vector() noexcept(std::is_nothrow_default_constructible_v) - : vector(allocator_type{}) - {} - - //! Construct a new, empty vector with a given allocator. - //! - //! @param allocator The allocator to use in the vector. - explicit constexpr vector(allocator_type const & allocator) noexcept( - std::is_nothrow_copy_constructible_v) - : m_allocator{allocator} - , m_size{0} - , m_capacity{0} - , m_data{allocate_n(m_capacity)} - {} - - //! Construct a new vector and fill it with the given number of default constructed elements. - //! - //! @param count The number of element to create the vector with. - //! @param allocator The allocator to use in the vector. - explicit constexpr vector(size_type count, allocator_type const & allocator = allocator_type{}) noexcept( - std::is_nothrow_copy_constructible_v) - : m_allocator{allocator} - , m_size{count} - , m_capacity{count} - , m_data{allocate_n(m_capacity)} - { - for (auto i = 0uz; i < count; ++i) - { - std::allocator_traits::construct(m_allocator, m_data + i); - } - } - - //! Construct a new vector and fill it with the given number of copy constructed elements. - //! - //! @param count The number of element to create the vector with. - //! @param value The value to copy for each element - //! @param allocator The allocator to use in the vector. - constexpr vector(size_type count, const_reference value, - allocator_type const & allocator = - allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) - : m_allocator{allocator} - , m_size{count} - , m_capacity{m_size} - , m_data{allocate_n(m_capacity)} - { - for (auto i = 0uz; i < count; ++i) - { - std::allocator_traits::construct(m_allocator, m_data + i, value); - } - } - - //! Construct a new vector and initialize it's content by copying all elements in the given range. - //! - //! @tparam ForwardIterator An iterator type used to describe the source range. - //! @param first The start of the source range. - //! @param last The end of the source range. - template - constexpr vector(ForwardIterator first, ForwardIterator last, - allocator_type const & allocator = - allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) - : m_allocator{allocator} - , m_size{static_cast(std::ranges::distance(first, last))} - , m_capacity{m_size} - , m_data{allocate_n(m_capacity)} - { - for (auto destination = m_data; first != last; ++first, ++destination) - { - std::allocator_traits::construct(m_allocator, destination, *first); - } - } - - //! Construct a new vector and initialize it's content by copying all elements in the given range. - //! - //! @tparam InputIterator An iterator type used to describe the source range. - //! @param first The start of the source range. - //! @param last The end of the source range. - template - constexpr vector(InputIterator first, InputIterator last, - allocator_type const & allocator = - allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) - : m_allocator{allocator} - , m_size{0} - , m_capacity{0} - , m_data{} - { - while (first != last) - { - emplace_back(*first); - ++first; - } - } - - //! Construct a new vector and initialize it's content by copying all elements in the given range. - //! - //! - template - requires((std::ranges::forward_range || std::ranges::sized_range) && - kstd::bits::container_compatible_range) - constexpr vector(kstd::from_range_t, Range && range, allocator_type const & allocator = allocator_type{}) - : m_allocator{allocator} - , m_size{std::ranges::size(range)} - , m_capacity{m_size} - , m_data{allocate_n(m_capacity)} - { - auto destination = m_data; - for (auto && element : std::forward(range)) - { - std::allocator_traits::construct(m_allocator, destination++, - std::forward>(element)); - } - } - - //! Construct a new vector and initialize it's content by copying all elements from a given vector. - //! - //! @param other The source vector. - constexpr vector(vector const & other) - : m_allocator{other.m_allocator} - , m_size{other.m_size} - , m_capacity{other.m_capacity} - , m_data(allocate_n(m_capacity)) - { - uninitialized_copy_with_allocator(other.begin(), begin(), other.size()); - } - - //! Construct a new vector and initialize it's content by moving from a given vector. - //! - //! @param other The source vector. - constexpr vector(vector && other) noexcept - : m_allocator{std::move(other.m_allocator)} - , m_size{std::exchange(other.m_size, size_type{})} - , m_capacity(std::exchange(other.m_capacity, size_type{})) - , m_data(std::exchange(other.m_data, nullptr)) - {} - - //! Construct a new vector and initialize it's content by copying from a given vector. - //! - //! @param other The source vector. - //! @param allocator The allocator to use in the vector. - constexpr vector(vector const & other, std::type_identity_t const & allocator) - : m_allocator{allocator} - , m_size{other.m_size} - , m_capacity{other.m_capacity} - , m_data{allocate_n(m_capacity)} - { - uninitialized_copy_with_allocator(other.begin(), begin(), other.size()); - } - - //! Construct a new vector and initialize it's content by copying from a given vector. - //! - //! @param other The source vector. - //! @param allocator The allocator to use in the vector. - constexpr vector(vector && other, std::type_identity_t const & allocator) - : m_allocator{allocator} - , m_size{} - , m_capacity{} - , m_data{} - { - if constexpr (!std::allocator_traits::is_always_equal::value) - { - if (m_allocator != other.m_allocator) - { - m_capacity = other.size(); - m_data = allocate_n(capacity()); - m_size = other.size(); - uninitialized_move_with_allocator(other.begin(), begin(), other.size()); - other.clear(); - return; - } - } - m_size = std::exchange(other.m_size, size_type{}); - m_capacity = std::exchange(other.m_capacity, size_type{}); - m_data = std::exchange(other.m_data, nullptr); - } - - //! Construct a new vector and initialize it's content by copying all elements in the given initializer list. - //! - //! @param list The initializer list containing the source objects. - vector(std::initializer_list list, allocator_type const & allocator = allocator_type{}) - : vector{std::ranges::begin(list), std::ranges::end(list), allocator} - {} - - //! Destroy this vector. - constexpr ~vector() - { - clear_and_deallocate(); - } - - //! Replace the contents of this vector by the copying from the given source vector. - //! - //! @param other The source vector. - constexpr auto operator=(vector const & other) -> vector & - { - if (this == std::addressof(other)) - { - return *this; - } - - if constexpr (std::allocator_traits::propagate_on_container_copy_assignment::value) - { - if (get_allocator() != other.get_allocator()) - { - clear_and_deallocate(); - } - m_allocator = other.get_allocator(); - } - - if (capacity() >= other.size()) - { - auto const overlap = std::min(m_size, other.m_size); - std::ranges::copy(other.begin(), other.begin() + overlap, begin()); - - if (m_size < other.m_size) - { - uninitialized_copy_with_allocator(other.begin() + size(), begin() + size(), other.m_size - size()); - } - else if (m_size > other.m_size) - { - destroy_n(begin() + other.size(), size() - other.size()); - } - } - else - { - auto new_data = allocate_n(other.size()); - uninitialized_copy_with_allocator(other.begin(), new_data, other.size()); - clear_and_deallocate(); - m_data = new_data; - m_capacity = other.size(); - } - - m_size = other.size(); - return *this; - } - - //! Replace the contents fo this vector by moving from the given source. - //! - //! @param other The source vector. - constexpr auto operator=(vector && other) noexcept( - std::allocator_traits::propagate_on_container_move_assignment::value || - std::allocator_traits::is_always_equal::value) -> vector & - { - using std::swap; - - if (this == std::addressof(other)) - { - return *this; - } - - if constexpr (std::allocator_traits::propagate_on_container_move_assignment::value) - { - clear_and_deallocate(); - swap(m_allocator, other.m_allocator); - swap(m_size, other.m_size); - swap(m_capacity, other.m_capacity); - swap(m_data, other.m_data); - } - else if (m_allocator == other.m_allocator) - { - clear_and_deallocate(); - swap(m_size, other.m_size); - swap(m_capacity, other.m_capacity); - swap(m_data, other.m_data); - } - else - { - if (capacity() >= other.size()) - { - auto const overlap = std::min(size(), other.size()); - std::ranges::move(other.begin(), other.begin() + overlap, begin()); - - if (size() < other.size()) - { - uninitialized_move_with_allocator(other.begin() + size(), begin() + size(), other.size() - size()); - } - else if (size() > other.size()) - { - destroy_n(begin() + other.size(), size() - other.size()); - } - } - else - { - auto new_data = allocate_n(other.size()); - uninitialized_move_with_allocator(other.begin(), new_data, other.size()); - clear_and_deallocate(); - m_data = new_data; - m_capacity = other.m_size; - } - - other.destroy_n(other.begin(), other.size()); - m_size = std::exchange(other.m_size, size_type{}); - } - - return *this; - } - - //! Get a copy of the allocator associated with this vector. - [[nodiscard]] constexpr auto get_allocator() const noexcept(std::is_nothrow_copy_constructible_v) - -> allocator_type - { - return m_allocator; - } - - //! Get a reference to the element at the given index. - //! - //! This function will panic if the index is out of bounds for this vector. - //! - //! @param index The index of the element to retrieve. - //! @return A reference to the element at the specified index. - [[nodiscard]] constexpr auto at(size_type index) -> reference - { - panic_if_out_of_bounds(index); - return (*this)[index]; - } - - //! Get a reference to the element at the given index. - //! - //! This function will panic if the index is out of bounds for this vector. - //! - //! @param index The index of the element to retrieve. - //! @return A reference to the element at the specified index. - [[nodiscard]] constexpr auto at(size_type index) const -> const_reference - { - panic_if_out_of_bounds(index); - return (*this)[index]; - } - - //! Get a reference to the element at the given index. - //! - //! The behavior is undefined if the index is out of bounds for this vector. - //! - //! @param index The index of the element to retrieve. - //! @return A reference to the element at the specified index. - [[nodiscard]] constexpr auto operator[](size_type index) -> reference - { - return data()[index]; - } - - //! Get a reference to the element at the given index. - //! - //! The behavior is undefined if the index is out of bounds for this vector. - //! - //! @param index The index of the element to retrieve. - //! @return A reference to the element at the specified index. - [[nodiscard]] constexpr auto operator[](size_type index) const -> const_reference - { - return data()[index]; - } - - //! Get a reference to the first element of this vector. - //! - //! The behavior is undefined if this vector is empty. - [[nodiscard]] constexpr auto front() -> reference - { - return *begin(); - } - - //! Get a reference to the first element of this vector. - //! - //! The behavior is undefined if this vector is empty. - [[nodiscard]] constexpr auto front() const -> const_reference - { - return *begin(); - } - - //! Get a reference to the last element of this vector. - //! - //! The behavior is undefined if this vector is empty. - [[nodiscard]] constexpr auto back() -> reference - { - return *rbegin(); - } - - //! Get a reference to the last element of this vector. - //! - //! The behavior is undefined if this vector is empty. - [[nodiscard]] constexpr auto back() const -> const_reference - { - return *rbegin(); - } - - //! Get a pointer to the beginning of the underlying contiguous storage. - [[nodiscard]] constexpr auto data() noexcept -> pointer - { - return m_data; - } - - //! Get a pointer to the beginning of the underlying contiguous storage. - [[nodiscard]] constexpr auto data() const noexcept -> const_pointer - { - return m_data; - } - - //! Get an iterator to the first element of this vector. - //! - //! @return An iterator to the first element of this container, or end() if the container is empty. - [[nodiscard]] constexpr auto begin() noexcept -> iterator - { - return empty() ? end() : data(); - } - - //! Get an iterator to the first element of this vector. - //! - //! @return An iterator to the first element of this container, or end() if the container is empty. - [[nodiscard]] constexpr auto begin() const noexcept -> const_iterator - { - return empty() ? end() : data(); - } - - //! Get an iterator to the first element of this vector. - //! - //! @return An iterator to the first element of this container, or end() if the container is empty. - [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator - { - return begin(); - } - - //! Get an iterator past the last element of this vector. - [[nodiscard]] constexpr auto end() noexcept -> pointer - { - return capacity() ? data() + size() : nullptr; - } - - //! Get an iterator past the last element of this vector. - [[nodiscard]] constexpr auto end() const noexcept -> const_pointer - { - return capacity() ? data() + size() : nullptr; - } - - //! Get an iterator past the last element of this vector. - [[nodiscard]] constexpr auto cend() const noexcept -> const_pointer - { - return end(); - } - - //! Get a reverse iterator to the reverse beginning. - [[nodiscard]] constexpr auto rbegin() noexcept -> reverse_iterator - { - return empty() ? rend() : reverse_iterator{end()}; - } - - //! Get a reverse iterator to the reverse beginning. - [[nodiscard]] constexpr auto rbegin() const noexcept -> const_reverse_iterator - { - return empty() ? rend() : const_reverse_iterator{end()}; - } - - //! Get a reverse iterator to the reverse beginning. - [[nodiscard]] constexpr auto crbegin() const noexcept -> const_reverse_iterator - { - return rbegin(); - } - - //! Get a reverse iterator to the reverse end. - [[nodiscard]] constexpr auto rend() noexcept -> reverse_iterator - { - return reverse_iterator{begin()}; - } - - //! Get a reverse iterator to the reverse end. - [[nodiscard]] constexpr auto rend() const noexcept -> const_reverse_iterator - { - return const_reverse_iterator{begin()}; - } - - //! Get a reverse iterator to the reverse end. - [[nodiscard]] constexpr auto crend() const noexcept -> const_reverse_iterator - { - return rend(); - } - - //! Check whether this vector is empty. - [[nodiscard]] constexpr auto empty() const noexcept -> bool - { - return !size(); - } - - //! Get the number of elements present in this vector. - [[nodiscard]] constexpr auto size() const noexcept -> size_type - { - return m_size; - } - - //! Get the maximum possible number of element for this vector. - [[nodiscard]] constexpr auto max_size() const noexcept -> size_type - { - return std::allocator_traits::max_size(m_allocator); - } - - //! Reserve storage for at list the given number of elements. - constexpr auto reserve(size_type new_capacity) -> void - { - if (new_capacity <= capacity()) - { - return; - } - - if (new_capacity > max_size()) - { - kstd::os::panic("[kstd:vector] Tried to reserve more space than theoretically possible."); - } - - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - uninitialized_move_with_allocator(begin(), new_data, size()); - clear_and_deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; - } - - //! Resize this vector to contain @p new_size elements. - constexpr auto resize(size_type new_size) -> void - { - resize(new_size, value_type{}); - } - - //! Resize this vector to contain @p new_size elements, filling new elements with @p value. - constexpr auto resize(size_type new_size, const_reference value) -> void - { - if (new_size < size()) - { - destroy_n(begin() + new_size, size() - new_size); - m_size = new_size; - return; - } - - if (new_size == size()) - { - return; - } - - if (new_size > max_size()) - { - kstd::os::panic("[kstd:vector] Tried to resize more space than theoretically possible."); - } - - if (new_size > capacity()) - { - reserve(new_size); - } - - for (auto i = size(); i < new_size; ++i) - { - std::allocator_traits::construct(m_allocator, m_data + i, value); - } - - m_size = new_size; - } - - //! Get the number of element this vector has currently space for, including elements currently in this vector. - [[nodiscard]] constexpr auto capacity() const noexcept -> size_type - { - return m_capacity; - } - - //! Try to release unused storage space. - constexpr auto shrink_to_fit() -> void - { - if (m_size == m_capacity) - { - return; - } - - auto new_data = allocate_n(m_size); - auto old_size = size(); - uninitialized_move_with_allocator(begin(), new_data, old_size); - clear_and_deallocate(); - std::exchange(m_data, new_data); - m_capacity = old_size; - m_size = old_size; - } - - //! Clear the contents of this vector. - constexpr auto clear() noexcept -> void - { - destroy_n(begin(), size()); - m_size = 0; - } - - //! Insert an element at a given position. - //! - //! @param position The position to insert the element at. - //! @param value The value to insert. - //! @return An iterator to the inserted element. - constexpr auto insert(const_iterator position, value_type const & value) -> iterator - { - auto prefix_size = std::ranges::distance(begin(), position); - auto suffix_size = std::ranges::distance(position, end()); - - if (position == end()) - { - push_back(value); - return begin() + prefix_size; - } - - if (m_capacity == m_size) - { - auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - std::allocator_traits::construct(m_allocator, new_data + prefix_size, value); - uninitialized_move_with_allocator(begin(), new_data, prefix_size); - uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); - destroy_n(begin(), old_size); - deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; - } - else if (&value >= begin() && &value < end()) - { - auto value_copy = value; - auto insert_position = begin() + prefix_size; - std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); - std::ranges::move_backward(insert_position, end() - 1, end()); - *insert_position = std::move(value_copy); - } - else - { - auto insert_position = begin() + prefix_size; - std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); - std::ranges::move_backward(insert_position, end() - 1, end()); - *insert_position = value; - } - - ++m_size; - return begin() + prefix_size; - } - - //! Insert an element at a given position. - //! - //! @param position The position to insert the element at. - //! @param value The value to insert. - //! @return An iterator to the inserted element. - constexpr auto insert(const_iterator position, value_type && value) -> iterator - { - auto prefix_size = std::ranges::distance(begin(), position); - auto suffix_size = std::ranges::distance(position, end()); - - if (position == end()) - { - push_back(std::move(value)); - return begin() + prefix_size; - } - - if (m_capacity == m_size) - { - auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - std::allocator_traits::construct(m_allocator, new_data + prefix_size, std::move(value)); - uninitialized_move_with_allocator(begin(), new_data, prefix_size); - uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); - destroy_n(begin(), old_size); - deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; - } - else if (&value >= begin() && &value < end()) - { - auto value_copy = std::move(value); - auto insert_position = begin() + prefix_size; - std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); - std::ranges::move_backward(insert_position, end() - 1, end()); - *insert_position = std::move(value_copy); - } - else - { - auto insert_position = begin() + prefix_size; - std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); - std::ranges::move_backward(insert_position, end() - 1, end()); - *insert_position = std::move(value); - } - - ++m_size; - return begin() + prefix_size; - } - - //! Insert the element of a given range into the vector at a given position. - //! - //! @param range The source range to insert elements from. - //! @tparam SourceRange A container compatible range type. - template - requires requires(allocator_type allocator, pointer destination, SourceRange range) { - requires kstd::bits::container_compatible_range; - requires std::move_constructible; - requires std::is_move_assignable_v; - requires std::swappable; - std::allocator_traits::construct(allocator, destination, *std::ranges::begin(range)); - } - // NOLINTNEXTLINE(misc-no-recursion) - constexpr auto insert_range(const_iterator position, SourceRange && range) -> iterator - { - auto prefix_size = std::ranges::distance(begin(), position); - - if (position == end()) - { - append_range(std::forward(range)); - return begin() + prefix_size; - } - - if constexpr (std::ranges::forward_range || std::ranges::sized_range) - { - auto number_of_elements = static_cast(std::ranges::distance(range)); - if (!number_of_elements) - { - return begin() + prefix_size; - } - - if (capacity() - size() < number_of_elements) - { - reserve(size() + std::max(size(), number_of_elements)); - } - - auto insert_position = begin() + prefix_size; - auto suffix_size = static_cast(std::ranges::distance(insert_position, end())); - - if (number_of_elements >= suffix_size) - { - uninitialized_move_with_allocator(insert_position, insert_position + number_of_elements, suffix_size); - auto result = std::ranges::copy_n(std::ranges::begin(range), suffix_size, insert_position); - uninitialized_copy_with_allocator(std::move(result.in), end(), number_of_elements - suffix_size); - } - else - { - uninitialized_move_with_allocator(end() - number_of_elements, end(), number_of_elements); - std::ranges::move_backward(insert_position, end() - number_of_elements, end()); - std::ranges::copy_n(std::ranges::begin(range), number_of_elements, insert_position); - } - - m_size += number_of_elements; - return insert_position; - } - - auto range_begin = std::ranges::begin(range); - auto range_end = std::ranges::end(range); - - auto remainder = vector{get_allocator()}; - for (; range_begin != range_end; ++range_begin) - { - remainder.emplace_back(*static_cast>(range_begin)); - } - reserve(size() + std::max(size(), remainder.size())); - - return insert_range(begin() + prefix_size, remainder); - } - - template - constexpr auto emplace(const_iterator position, Args &&... args) -> iterator - { - auto prefix_size = std::ranges::distance(begin(), position); - auto suffix_size = std::ranges::distance(position, end()); - - if (position == end()) - { - emplace_back(std::forward(args)...); - return begin() + prefix_size; - } - - if (m_capacity == m_size) - { - auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - std::allocator_traits::construct(m_allocator, new_data + prefix_size, - std::forward(args)...); - uninitialized_move_with_allocator(begin(), new_data, prefix_size); - uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); - destroy_n(begin(), old_size); - deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; - } - else - { - auto insert_position = begin() + prefix_size; - std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); - std::ranges::move_backward(insert_position, end() - 1, end()); - *insert_position = value_type{std::forward(args)...}; - } - - ++m_size; - return begin() + prefix_size; - } - - //! Erase an element at a given position. - //! - //! @note This function will panic if position == end() - //! - //! @param position An interator pointing to the element to delete - //! @return An iterator pointing to the element after the deleted element - constexpr auto erase(const_iterator position) -> iterator - { - if (position == end()) - { - os::panic("[kstd:vector] Attempted to erase end()!"); - } - - auto prefix_size = std::ranges::distance(cbegin(), position); - - std::ranges::move(begin() + prefix_size + 1, end(), begin() + prefix_size); - std::allocator_traits::destroy(m_allocator, end() - 1); - --m_size; - - return begin() + prefix_size; - } - - //! Erase a range of elements from this vector. - //! - //! @param first The start of the range to erase. - //! @param last The end of the range to erase. - //! @return An iterator pointing to the element after the last deleted element. - constexpr auto erase(const_iterator first, const_iterator last) -> iterator - { - if (first == last) - { - return begin() + std::ranges::distance(cbegin(), first); - } - - auto prefix_size = std::ranges::distance(cbegin(), first); - auto element_count = std::ranges::distance(first, last); - - std::ranges::move(begin() + prefix_size + element_count, end(), begin() + prefix_size); - destroy_n(end() - element_count, element_count); - m_size -= element_count; - - return begin() + prefix_size; - } - - //! Append a given element to this vector via copy construction. - constexpr auto push_back(value_type const & value) -> void - { - if (m_capacity == m_size) - { - auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - std::allocator_traits::construct(m_allocator, new_data + m_size, value); - uninitialized_move_with_allocator(begin(), new_data, size()); - destroy_n(begin(), size()); - deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; - } - else - { - std::allocator_traits::construct(m_allocator, data() + size(), value); - } - ++m_size; - } - - //! Append a given element to this vector via move construction. - constexpr auto push_back(value_type && value) -> void - { - if (m_capacity == m_size) - { - auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - std::allocator_traits::construct(m_allocator, new_data + m_size, std::move(value)); - uninitialized_move_with_allocator(begin(), new_data, size()); - destroy_n(begin(), size()); - deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; - } - else - { - std::allocator_traits::construct(m_allocator, data() + size(), std::move(value)); - } - ++m_size; - } - - //! Append a given element to this vector via direct construction. - template - constexpr auto emplace_back(Args &&... args) -> reference - { - if (m_capacity == m_size) - { - auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - std::allocator_traits::construct(m_allocator, new_data + m_size, std::forward(args)...); - uninitialized_move_with_allocator(begin(), new_data, size()); - destroy_n(begin(), size()); - deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; - } - else - { - std::allocator_traits::construct(m_allocator, data() + size(), std::forward(args)...); - } - ++m_size; - return this->back(); - } - - //! Append the elements of a given range to this vector. - //! - //! @param range The range of elements to be appended. - //! @tparam SourceRange A container compatible range type. - template SourceRange> - requires requires(Allocator allocator, pointer destination, SourceRange range) { - std::allocator_traits::construct(allocator, destination, *std::ranges::begin(range)); - } - // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward, misc-no-recursion) - constexpr auto append_range(SourceRange && range) -> void - { - if constexpr (std::ranges::forward_range || std::ranges::sized_range) - { - auto number_of_elements = static_cast(std::ranges::distance(range)); - - if (!capacity()) - { - reserve(number_of_elements); - } - - if (capacity() - size() >= number_of_elements) - { - uninitialized_copy_with_allocator(std::ranges::begin(range), end(), number_of_elements); - m_size += number_of_elements; - return; - } - - auto new_capacity = m_capacity + std::max(size(), number_of_elements); - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - - uninitialized_move_with_allocator(begin(), new_data, size()); - uninitialized_copy_with_allocator(std::ranges::begin(range), new_data + size(), number_of_elements); - clear_and_deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size + number_of_elements; - return; - } - - auto range_begin = std::ranges::begin(range); - auto range_end = std::ranges::end(range); - - for (auto i = capacity() - size(); i > 0; --i, ++range_begin) - { - emplace_back(*range_begin); - } - - if (range_begin == range_end) - { - return; - } - - auto remainder = vector{get_allocator()}; - for (; range_begin != range_end; ++range_begin) - { - remainder.emplace_back(*static_cast>(range_begin)); - } - reserve(size() + std::max(size(), remainder.size())); - append_range(remainder); - } - - //! Remove the last element of this vector. - //! - //! If this vector is empty, the behavior is undefined. - constexpr auto pop_back() -> void - { - --m_size; - std::allocator_traits::destroy(m_allocator, data() + size()); - } - - private: - //! Use the allocator of this vector to allocate enough space for the given number of elements. - //! - //! @param count The number of elements to allocate space for. - [[nodiscard]] constexpr auto allocate_n(std::size_t count) -> std::allocator_traits::pointer - { - if (count) - { - return std::allocator_traits::allocate(m_allocator, count); - } - return nullptr; - } - - //! Clear this vector and release it's memory. - constexpr auto clear_and_deallocate() -> void - { - clear(); - deallocate(); - } - - //! Release the memory of this vector. - constexpr auto deallocate() - { - if (m_data) - { - std::allocator_traits::deallocate(m_allocator, m_data, m_capacity); - m_capacity = 0; - m_size = 0; - m_data = nullptr; - } - } - - //! Destroy a number of elements in this vector. - //! - //! @param first The start of the range of the elements to be destroyed. - //! @param count The number of elements to destroy. - constexpr auto destroy_n(iterator first, std::size_t count) -> void - { - std::ranges::for_each(first, first + count, [&](auto & element) { - std::allocator_traits::destroy(m_allocator, std::addressof(element)); - }); - } - - //! Panic the kernel if the given index is out of bounds. - //! - //! @param index The index to check. - constexpr auto panic_if_out_of_bounds(size_type index) const -> void - { - if (index >= m_size) - { - os::panic("[kstd:vector] Attempted to read element at invalid index"); - } - } - - //! Copy a number of elements from a source range into the uninitialized destination range inside this vector. - //! - //! @param from The start of the source range. - //! @param to The start of the target range inside this vector. - //! @param count The number of elements to copy - template - constexpr auto uninitialized_copy_with_allocator(SourceIterator from, iterator to, size_type count) - { - for (auto i = 0uz; i < count; ++i) - { - std::allocator_traits::construct(m_allocator, to++, *from++); - } - } - - //! Move a number of elements from a source range into the uninitialized destination range inside this vector. - //! - //! @param from The start of the source range. - //! @param to The start of the target range inside this vector. - //! @param count The number of elements to copy - template - constexpr auto uninitialized_move_with_allocator(SourceIterator from, iterator to, size_type count) - { - for (auto i = 0uz; i < count; ++i) - { - std::allocator_traits::construct(m_allocator, to++, std::move(*from++)); - } - } - - //! The allocator used by this vector. - [[no_unique_address]] allocator_type m_allocator{}; - - //! The number of elements in this vector. - size_type m_size{}; - - //! The number of elements this vector has room for. - size_type m_capacity{}; - - //! The pointer to the start of the memory managed by this vector. - value_type * m_data{}; - }; - - //! Check if the content of two vectors is equal. - template - constexpr auto operator==(vector const & lhs, vector const & rhs) -> bool - { - return std::ranges::equal(lhs, rhs); - } - - //! Perform a lexicographical comparison of the content of two vectors. - template - constexpr auto operator<=>(vector const & lhs, vector const & rhs) - -> decltype(std::declval() <=> std::declval()) - { - return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); - } - - //! Deduction guide for vector construction from an interator pair. - template::value_type>> - vector(ForwardIterator, ForwardIterator, Allocator = Allocator()) - -> vector::value_type, Allocator>; - - //! Deduction guide for vector construction from an interator pair. - template::value_type>> - vector(InputIterator, InputIterator, Allocator = Allocator()) - -> vector::value_type, Allocator>; - - //! Deduction guide for vector construction from a range. - template>> - vector(kstd::from_range_t, Range &&, Allocator = Allocator()) -> vector, Allocator>; - -} // namespace kstd - -#endif diff --git a/libs/kstd/kstd/allocator b/libs/kstd/kstd/allocator new file mode 100644 index 0000000..0de0e10 --- /dev/null +++ b/libs/kstd/kstd/allocator @@ -0,0 +1,64 @@ +#ifndef KSTD_ALLOCATOR_HPP +#define KSTD_ALLOCATOR_HPP + +#include +#include +#include + +#if __has_builtin(__builtin_operator_new) >= 201'802L +#define KSTD_OPERATOR_NEW __builtin_operator_new +#define KSTD_OPERATOR_DELETE __builtin_operator_delete +#else +#define KSTD_OPERATOR_NEW ::operator new +#define KSTD_OPERATOR_DELETE ::operator delete +#endif + +namespace kstd +{ + + template + struct allocator + { + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using propagate_on_container_move_assignment = std::true_type; + using is_always_equal = std::true_type; + + constexpr allocator() noexcept = default; + + template + constexpr allocator(allocator const &) noexcept + {} + + constexpr auto allocate(std::size_t n) -> T * + { + if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + { + return static_cast(KSTD_OPERATOR_NEW(n * sizeof(T), std::align_val_t{alignof(T)})); + } + return static_cast(KSTD_OPERATOR_NEW(n * sizeof(T))); + } + + constexpr void deallocate(T * p, std::size_t n) noexcept + { + if (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + { + KSTD_OPERATOR_DELETE(p, n * sizeof(T), std::align_val_t{alignof(T)}); + } + KSTD_OPERATOR_DELETE(p, n * sizeof(T)); + } + }; + + template + constexpr auto operator==(allocator const &, allocator const &) noexcept -> bool + { + return true; + } + +} // namespace kstd + +#undef KSTD_OPERATOR_NEW +#undef KSTD_OPERATOR_DELETE + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/asm_ptr b/libs/kstd/kstd/asm_ptr new file mode 100644 index 0000000..c06a8b5 --- /dev/null +++ b/libs/kstd/kstd/asm_ptr @@ -0,0 +1,78 @@ +#ifndef KSTD_ASM_POINTER_HPP +#define KSTD_ASM_POINTER_HPP + +#include +#include + +namespace kstd +{ + + /** + * @brief A pointer that is defined in some assembly source file. + * + * @tparam Type The type of the pointer + */ + template + struct asm_ptr + { + using value_type = Type; + using pointer = value_type *; + using const_pointer = value_type const *; + using reference = value_type &; + using const_reference = value_type const &; + + asm_ptr() = delete; + asm_ptr(asm_ptr const &) = delete; + asm_ptr(asm_ptr &&) = delete; + ~asm_ptr() = delete; + + constexpr auto operator=(asm_ptr const &) = delete; + constexpr auto operator=(asm_ptr &&) = delete; + + auto get() const noexcept -> pointer + { + return m_ptr; + } + + constexpr auto operator+(std::ptrdiff_t offset) const noexcept -> pointer + { + return std::bit_cast(m_ptr) + offset; + } + + constexpr auto operator*() noexcept -> reference + { + return *(std::bit_cast(m_ptr)); + } + + constexpr auto operator*() const noexcept -> const_reference + { + return *(std::bit_cast(m_ptr)); + } + + constexpr auto operator[](std::ptrdiff_t offset) noexcept -> reference + { + return *(*this + offset); + } + + constexpr auto operator[](std::ptrdiff_t offset) const noexcept -> const_reference + { + return *(*this + offset); + } + + constexpr auto operator->() noexcept -> pointer + { + return m_ptr; + } + + constexpr auto operator->() const noexcept -> const_pointer + { + return m_ptr; + } + + private: + pointer m_ptr; + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/concepts.hpp b/libs/kstd/kstd/bits/concepts.hpp new file mode 100644 index 0000000..74c25cb --- /dev/null +++ b/libs/kstd/kstd/bits/concepts.hpp @@ -0,0 +1,15 @@ +#ifndef KSTD_BITS_CONCEPTS_HPP +#define KSTD_BITS_CONCEPTS_HPP + +#include +#include +namespace kstd::bits +{ + + template + concept container_compatible_range = + std::ranges::input_range && std::convertible_to, ValueType>; + +} + +#endif diff --git a/libs/kstd/kstd/bits/flat_map.hpp b/libs/kstd/kstd/bits/flat_map.hpp new file mode 100644 index 0000000..fe46203 --- /dev/null +++ b/libs/kstd/kstd/bits/flat_map.hpp @@ -0,0 +1,186 @@ +#ifndef KSTD_BITS_FLAT_MAP_HPP +#define KSTD_BITS_FLAT_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace kstd::bits +{ + + template + struct flat_map_reference + { + using key_type = KeyType; + using mapped_type = MappedType; + + constexpr flat_map_reference(key_type const & key, mapped_type & mapped) + : first{key} + , second{mapped} + {} + + constexpr auto operator=(flat_map_reference const & other) const -> flat_map_reference const & + { + second = other.second; + return *this; + } + + constexpr auto operator=(flat_map_reference && other) const -> flat_map_reference const & + { + second = std::move(other.second); + return *this; + } + + template + requires(std::tuple_size_v> == 2) + constexpr auto operator=(TupleLikeType && tuple) const -> flat_map_reference const & + { + second = std::forward(tuple).second; + return *this; + } + + template + requires(Index >= 0 && Index <= 1) + [[nodiscard]] constexpr auto get() const noexcept -> decltype(auto) + { + if constexpr (Index == 0) + { + return (first); + } + else + { + return (second); + } + } + + key_type const & first; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) + mapped_type & second; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) + }; + + template + struct flat_map_pointer + { + Reference reference; + + [[nodiscard]] constexpr auto operator->() noexcept -> Reference * + { + return std::addressof(reference); + } + + [[nodiscard]] constexpr auto operator->() const noexcept -> Reference const * + { + return std::addressof(reference); + } + }; + + template + struct flat_map_iterator + { + using iterator_category = std::random_access_iterator_tag; + using value_type = std::pair; + using difference_type = std::ptrdiff_t; + using reference = flat_map_reference; + using pointer = flat_map_pointer; + + constexpr flat_map_iterator() = default; + + constexpr flat_map_iterator(KeyIterator key_iterator, MappedIterator mapped_iterator) + : m_key_iterator{key_iterator} + , m_mapped_iterator{mapped_iterator} + {} + + template + requires(std::convertible_to && + std::convertible_to) + constexpr flat_map_iterator( + flat_map_iterator const & other) noexcept + : m_key_iterator{other.m_key_iterator} + , m_mapped_iterator{other.m_mapped_iterator} + {} + + [[nodiscard]] auto key_iterator() const noexcept -> KeyIterator + { + return m_key_iterator; + } + + [[nodiscard]] constexpr auto operator*() const noexcept -> reference + { + return {*m_key_iterator, *m_mapped_iterator}; + } + + [[nodiscard]] constexpr auto operator->() const noexcept -> pointer + { + return { + {*m_key_iterator, *m_mapped_iterator} + }; + } + + constexpr auto operator++() noexcept -> flat_map_iterator & + { + ++m_key_iterator; + ++m_mapped_iterator; + return *this; + } + + constexpr auto operator++(int) noexcept -> flat_map_iterator + { + auto copy = *this; + ++(*this); + return copy; + } + + constexpr auto operator--() noexcept -> flat_map_iterator & + { + --m_key_iterator; + --m_mapped_iterator; + return *this; + } + + constexpr auto operator--(int) noexcept -> flat_map_iterator + { + auto copy = *this; + --(*this); + return copy; + } + + [[nodiscard]] constexpr auto operator+(difference_type offset) const noexcept -> flat_map_iterator + { + return {m_key_iterator + offset, m_mapped_iterator + offset}; + } + + [[nodiscard]] constexpr auto operator-(flat_map_iterator const & other) const noexcept -> difference_type + { + return m_key_iterator - other.m_key_iterator; + } + + [[nodiscard]] constexpr auto operator<=>(flat_map_iterator const & other) const noexcept = default; + + private: + KeyIterator m_key_iterator{}; + MappedIterator m_mapped_iterator{}; + }; + +} // namespace kstd::bits + +template +struct std::tuple_size> : std::integral_constant +{ +}; + +template +struct std::tuple_element<0, kstd::bits::flat_map_reference> +{ + using type = K const &; +}; + +template +struct std::tuple_element<1, kstd::bits::flat_map_reference> +{ + using type = M &; +}; + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/arg.hpp b/libs/kstd/kstd/bits/format/arg.hpp new file mode 100644 index 0000000..e65b26f --- /dev/null +++ b/libs/kstd/kstd/bits/format/arg.hpp @@ -0,0 +1,75 @@ +#ifndef KSTD_BITS_FORMAT_ARG_HPP +#define KSTD_BITS_FORMAT_ARG_HPP + +// IWYU pragma: private, include + +#include +#include + +#include +#include +#include + +namespace kstd +{ + + namespace bits::format + { + enum struct arg_type : std::uint8_t + { + none, + boolean, + character, + integer, + unsigned_integer, + string_view, + c_string, + pointer, + user_defined, + }; + } // namespace bits::format + + struct format_arg + { + bits::format::arg_type type{}; + union + { + bool boolean; + char character; + std::int64_t integer; + std::uint64_t unsigned_integer; + std::string_view string_view; + char const * c_string; + void const * pointer; + struct + { + void const * pointer; + auto (*format)(void const * value, format_parse_context & parse_context, format_context & context) -> void; + } user_defined; + } value{}; + }; + + namespace bits::format + { + constexpr auto extrat_dynamic_width(format_arg const & arg) -> std::size_t + { + if (arg.type == arg_type::unsigned_integer) + { + return static_cast(arg.value.unsigned_integer); + } + else if (arg.type == arg_type::integer) + { + if (arg.value.integer < 0) + { + error("Dynamic width cannont be negative."); + } + return static_cast(arg.value.integer); + } + error("Dynamic width argument is not an integral value."); + return 0; + } + } // namespace bits::format + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/args.hpp b/libs/kstd/kstd/bits/format/args.hpp new file mode 100644 index 0000000..e8e3114 --- /dev/null +++ b/libs/kstd/kstd/bits/format/args.hpp @@ -0,0 +1,160 @@ +#ifndef KSTD_BITS_FORMAT_ARGS_HPP +#define KSTD_BITS_FORMAT_ARGS_HPP + +// IWYU pragma: private, include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace kstd +{ + + namespace bits::format + { + + template + auto format_trampoline(void const * value_pointer, format_parse_context & parse_context, format_context & context) + -> void + { + auto typed_value_pointer = static_cast(value_pointer); + auto fmt = formatter>{}; + auto const it = fmt.parse(parse_context); + parse_context.advance_to(it); + fmt.format(*typed_value_pointer, context); + } + + template + constexpr auto determine_arg_type() -> arg_type + { + using decay_type = std::remove_cvref_t; + if constexpr (std::same_as) + { + return arg_type::boolean; + } + else if constexpr (std::same_as) + { + return arg_type::character; + } + else if constexpr (std::integral && std::is_signed_v) + { + return arg_type::integer; + } + else if constexpr (std::integral && std::is_unsigned_v) + { + return arg_type::unsigned_integer; + } + else if constexpr (std::same_as) + { + return arg_type::string_view; + } + else if constexpr (std::same_as, char *> || + std::same_as, char const *>) + { + return arg_type::c_string; + } + else if constexpr (std::is_pointer_v || std::same_as) + { + if constexpr (std::same_as || std::same_as) + { + return arg_type::user_defined; + } + else + { + return arg_type::pointer; + } + } + else + { + return arg_type::user_defined; + } + } + + template + constexpr auto make_single_arg(ValueType const & value) -> format_arg + { + auto result = format_arg{}; + constexpr auto type = determine_arg_type(); + result.type = type; + + if constexpr (type == arg_type::boolean) + { + result.value.boolean = value; + } + else if constexpr (type == arg_type::character) + { + result.value.character = value; + } + else if constexpr (type == arg_type::integer) + { + result.value.integer = static_cast(value); + } + else if constexpr (type == arg_type::unsigned_integer) + { + result.value.unsigned_integer = static_cast(value); + } + else if constexpr (type == arg_type::string_view) + { + result.value.string_view = value; + } + else if constexpr (type == arg_type::c_string) + { + result.value.c_string = value; + } + else if constexpr (type == arg_type::pointer) + { + if constexpr (std::same_as, std::nullptr_t>) + { + result.value.pointer = nullptr; + } + else + { + result.value.pointer = static_cast(value); + } + } + else + { + result.value.user_defined.pointer = &value; + result.value.user_defined.format = format_trampoline; + } + return result; + } + + } // namespace bits::format + + using format_args = std::span; + + template + struct format_arg_store + { + std::array args{}; + }; + + template + [[nodiscard]] auto make_format_args(Arguments const &... args) noexcept -> format_arg_store + { + using namespace bits::format; + + if constexpr (sizeof...(Arguments) == 0) + { + return format_arg_store<0>{}; + } + else + { + return format_arg_store{ + std::array{make_single_arg(args)...}}; + } + } + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/context.hpp b/libs/kstd/kstd/bits/format/context.hpp new file mode 100644 index 0000000..c166ba9 --- /dev/null +++ b/libs/kstd/kstd/bits/format/context.hpp @@ -0,0 +1,65 @@ +#ifndef KSTD_BITS_FORMAT_CONTEXT_HPP +#define KSTD_BITS_FORMAT_CONTEXT_HPP + +// IWYU pragma: private, include + +#include +#include +#include + +#include +#include +#include +#include + +namespace kstd +{ + + namespace bits::format + { + template + constexpr auto inline is_width_v = std::integral && // + !std::same_as && // + !std::same_as && // + !std::same_as && // + !std::same_as && // + !std::same_as && // + !std::same_as; + } + + struct format_context + { + using format_args = std::span; + format_args args{}; + + format_context(bits::format::output_buffer & buffer, format_args args) + : args{args} + , m_buffer{&buffer} + {} + + [[nodiscard]] auto arg(std::size_t const id) const -> format_arg const & + { + if (id >= args.size()) + { + kstd::os::panic("[kstd:format] argument index out of range!"); + } + return args[id]; + } + + constexpr auto push(std::string_view string) -> void + { + m_buffer->push(string); + } + + constexpr auto push(char character) -> void + { + m_buffer->push(character); + } + + private: + bits::format::output_buffer * m_buffer; + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/error.hpp b/libs/kstd/kstd/bits/format/error.hpp new file mode 100644 index 0000000..c0cb53d --- /dev/null +++ b/libs/kstd/kstd/bits/format/error.hpp @@ -0,0 +1,24 @@ +#ifndef KSTD_BITS_FORMAT_ERROR_HPP +#define KSTD_BITS_FORMAT_ERROR_HPP + +#include + +namespace kstd::bits::format +{ + + constexpr auto error(char const * message) -> void + { + if consteval + { + extern void compile_time_format_error_triggered(char const *); + compile_time_format_error_triggered(message); + } + else + { + kstd::os::panic("Error while formatting a string."); + } + } + +} // namespace kstd::bits::format + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/formatter.hpp b/libs/kstd/kstd/bits/format/formatter.hpp new file mode 100644 index 0000000..eb28829 --- /dev/null +++ b/libs/kstd/kstd/bits/format/formatter.hpp @@ -0,0 +1,92 @@ +#ifndef KSTD_BITS_FORMATTER_HPP +#define KSTD_BITS_FORMATTER_HPP + +// IWYU pragma: private, include + +#include +#include +#include + +#include +#include +#include + +namespace kstd +{ + + template + struct formatter + { + formatter() = delete; + formatter(formatter const &) = delete; + auto operator=(formatter const &) -> formatter & = delete; + }; + + template + struct range_formatter + { + constexpr auto set_separator(std::string_view sep) -> void + { + m_separator = sep; + } + + constexpr auto set_brackets(std::string_view opening, std::string_view closing) + { + m_prefix = opening; + m_suffix = closing; + } + + constexpr auto parse(format_parse_context & context) + { + auto it = context.begin(); + auto const end = context.end(); + + if (it != end && *it == 'n') + { + set_brackets("", ""); + std::advance(it, 1); + } + + if (it != end && *it == ':') + { + std::advance(it, 1); + context.advance_to(it); + it = m_inner_formatter.parse(context); + } + + if (it != end && *it != '}') + { + bits::format::error("Invalid formate specifier for range"); + } + + return it; + } + + template + auto format(Range const & range, format_context & context) + { + context.push(m_prefix); + + auto is_first = true; + std::ranges::for_each(range, [&](auto const & element) { + if (!is_first) + { + context.push(m_separator); + } + m_inner_formatter.format(element, context); + is_first = false; + }); + + context.push(m_suffix); + } + + private: + kstd::formatter m_inner_formatter{}; + std::string_view m_separator{", "}; + std::string_view m_prefix{"["}; + std::string_view m_suffix{"]"}; + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/formatter/bool.hpp b/libs/kstd/kstd/bits/format/formatter/bool.hpp new file mode 100644 index 0000000..cc8d190 --- /dev/null +++ b/libs/kstd/kstd/bits/format/formatter/bool.hpp @@ -0,0 +1,83 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_BOOL_HPP +#define KSTD_BITS_FORMAT_FORMATTER_BOOL_HPP + +#include +#include +#include +#include +#include + +#include +#include + +namespace kstd +{ + + template<> + struct formatter + { + bits::format::specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + + auto it = context.begin(); + auto const end = context.end(); + + if (it != end && *it != '}') + { + if (*it == 's') + { + specifiers.type = *it; + std::advance(it, 1); + } + else + { + bits::format::error("Invalid type specifier for bool."); + } + } + + if (it != end && *it != '}') + { + bits::format::error("Missing terminating '}' in format string."); + } + + return it; + } + + auto format(bool value, format_context & context) const -> void + { + auto const text = value ? std::string_view{"true"} : std::string_view{"false"}; + auto final_width = 0uz; + + if (specifiers.mode == bits::format::width_mode::static_value) + { + final_width = specifiers.width_value; + } + else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id) + { + auto const & arg = context.arg(specifiers.width_value); + final_width = bits::format::extrat_dynamic_width(arg); + } + + auto padding = bits::format::calculate_format_padding(final_width, text.size(), specifiers.align, + bits::format::alignment::left); + + for (auto i = 0uz; i < padding.left; ++i) + { + context.push(specifiers.fill); + } + + context.push(text); + + for (auto i = 0uz; i < padding.right; ++i) + { + context.push(specifiers.fill); + } + } + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/formatter/byte.hpp b/libs/kstd/kstd/bits/format/formatter/byte.hpp new file mode 100644 index 0000000..cc8aece --- /dev/null +++ b/libs/kstd/kstd/bits/format/formatter/byte.hpp @@ -0,0 +1,23 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_BYTE_HPP +#define KSTD_BITS_FORMAT_FORMATTER_BYTE_HPP + +#include +#include +#include + +#include + +namespace kstd +{ + + template<> + struct formatter : formatter + { + auto format(std::byte value, format_context & context) const -> void + { + formatter::format(static_cast(value), context); + } + }; + +} // namespace kstd +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/formatter/char.hpp b/libs/kstd/kstd/bits/format/formatter/char.hpp new file mode 100644 index 0000000..92489a1 --- /dev/null +++ b/libs/kstd/kstd/bits/format/formatter/char.hpp @@ -0,0 +1,94 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_CHAR_HPP +#define KSTD_BITS_FORMAT_FORMATTER_CHAR_HPP + +#include +#include +#include +#include +#include +#include + +#include + +namespace kstd +{ + + template<> + struct formatter : formatter + { + bits::format::specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + + auto it = context.begin(); + auto const end = context.end(); + + if (specifiers.alternative_form || specifiers.zero_pad || specifiers.sign != bits::format::sign_mode::none) + { + bits::format::error("Invalid format specifiers for 'char'"); + } + + if (it != end && *it != '}') + { + if (*it == 'c' || *it == 'b' || *it == 'B' || *it == 'd' || *it == 'o' || *it == 'x' || *it == 'X') + { + specifiers.type = *it; + std::advance(it, 1); + } + else + { + bits::format::error("Invalid type specifier for char."); + } + } + + if (it != end && *it != '}') + { + bits::format::error("Missing terminating '}' in format string."); + } + + return it; + } + + auto format(char value, format_context & context) const -> void + { + if (specifiers.type == '\0' || specifiers.type == 'c') + { + auto final_width = 0uz; + + if (specifiers.mode == bits::format::width_mode::static_value) + { + final_width = specifiers.width_value; + } + else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id) + { + auto const & arg = context.arg(specifiers.width_value); + final_width = bits::format::extrat_dynamic_width(arg); + } + + auto padding = + bits::format::calculate_format_padding(final_width, 1, specifiers.align, bits::format::alignment::left); + + for (auto i = 0uz; i < padding.left; ++i) + { + context.push(specifiers.fill); + } + + context.push(value); + + for (auto i = 0uz; i < padding.right; ++i) + { + context.push(specifiers.fill); + } + } + else + { + formatter::format(static_cast(value), context); + } + } + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/formatter/cstring.hpp b/libs/kstd/kstd/bits/format/formatter/cstring.hpp new file mode 100644 index 0000000..553c8ca --- /dev/null +++ b/libs/kstd/kstd/bits/format/formatter/cstring.hpp @@ -0,0 +1,29 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_CSTRING_HPP +#define KSTD_BITS_FORMAT_FORMATTER_CSTRING_HPP + +#include +#include +#include + +#include + +namespace kstd +{ + + template<> + struct formatter : formatter + { + auto format(char const * string, format_context & context) const -> void + { + formatter::format(string ? std::string_view{string} : "(null)", context); + } + }; + + template<> + struct formatter : formatter + { + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/formatter/integral.hpp b/libs/kstd/kstd/bits/format/formatter/integral.hpp new file mode 100644 index 0000000..d17dc95 --- /dev/null +++ b/libs/kstd/kstd/bits/format/formatter/integral.hpp @@ -0,0 +1,204 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_INTEGRAL_HPP +#define KSTD_BITS_FORMAT_FORMATTER_INTEGRAL_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace kstd +{ + + template + struct formatter + { + bits::format::specifiers specifiers{}; + + constexpr auto static maximum_digits = 80; + + enum struct base + { + bin = 2, + oct = 8, + dec = 10, + hex = 16, + }; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + + auto it = context.begin(); + auto const end = context.end(); + + if (it != end && *it != '}') + { + if (*it == 'b' || *it == 'B' || *it == 'd' || *it == 'o' || *it == 'x' || *it == 'X' || *it == 'p') + { + specifiers.type = *it; + std::advance(it, 1); + } + else + { + bits::format::error("Invalid type specifier for integral type."); + } + } + + if (it != end && *it != '}') + { + bits::format::error("Missing terminating '}' in format string."); + } + + return it; + } + + auto format(T value, format_context & context) const -> void + { + auto final_width = 0uz; + if (specifiers.mode == bits::format::width_mode::static_value) + { + final_width = specifiers.width_value; + } + else if (specifiers.mode == bits::format::width_mode::dynamic_argument_id) + { + auto const & arg = context.arg(specifiers.width_value); + final_width = bits::format::extrat_dynamic_width(arg); + } + + using unsigned_T = std::make_unsigned_t; + auto absolute_value = static_cast(value); + auto is_negative = false; + + if constexpr (std::is_signed_v) + { + if (value < 0) + { + is_negative = true; + absolute_value = 0 - static_cast(value); + } + } + + auto const base = [type = specifiers.type] -> auto { + switch (type) + { + case 'x': + case 'X': + case 'p': + return base::hex; + case 'b': + case 'B': + return base::bin; + case 'o': + return base::oct; + default: + return base::dec; + } + }(); + + auto buffer = std::array{}; + auto digits = (specifiers.type == 'X') ? "0123456789ABCDEF" : "0123456789abcdef"; + auto current = buffer.rbegin(); + + if (absolute_value == 0) + { + *current = '0'; + std::advance(current, 1); + } + else + { + while (absolute_value != 0) + { + *current = digits[absolute_value % std::to_underlying(base)]; + std::advance(current, 1); + absolute_value /= std::to_underlying(base); + } + } + + auto content_length = static_cast(std::distance(buffer.rbegin(), current)); + auto prefix = std::array{'0', '\0'}; + auto prefix_length = 0uz; + if (specifiers.alternative_form) + { + switch (base) + { + case base::bin: + prefix[1] = specifiers.type == 'B' ? 'B' : 'b'; + prefix_length = 2; + break; + case base::oct: + prefix_length = 1; + break; + case base::hex: + prefix[1] = specifiers.type == 'X' ? 'X' : 'x'; + prefix_length = 2; + break; + default: + break; + } + } + + auto sign_character = '\0'; + if (is_negative) + { + sign_character = '-'; + } + else if (specifiers.sign == bits::format::sign_mode::plus) + { + sign_character = '+'; + } + else if (specifiers.sign == bits::format::sign_mode::space) + { + sign_character = ' '; + } + + auto const total_length = content_length + prefix_length + (sign_character != '\0'); + auto const padding = bits::format::calculate_format_padding(final_width, total_length, specifiers.align, + bits::format::alignment::right); + auto const effective_zero_pad = specifiers.zero_pad && (specifiers.align == bits::format::alignment::none); + + if (!effective_zero_pad) + { + for (auto i = 0uz; i < padding.left; ++i) + { + context.push(specifiers.fill); + } + } + + if (sign_character != '\0') + { + context.push(sign_character); + } + if (prefix_length > 0) + { + context.push(std::string_view{prefix.data(), prefix_length}); + } + + if (effective_zero_pad) + { + for (auto i = 0uz; i < padding.left; ++i) + { + context.push('0'); + } + } + + context.push(std::string_view{current.base(), content_length}); + + for (auto i = 0uz; i < padding.right; ++i) + { + context.push(specifiers.fill); + } + } + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/formatter/ordering.hpp b/libs/kstd/kstd/bits/format/formatter/ordering.hpp new file mode 100644 index 0000000..7832226 --- /dev/null +++ b/libs/kstd/kstd/bits/format/formatter/ordering.hpp @@ -0,0 +1,111 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_ORDERING_HPP +#define KSTD_BITS_FORMAT_FORMATTER_ORDERING_HPP + +#include +#include +#include +#include + +#include + +namespace kstd +{ + + template<> + struct formatter + { + bits::format::specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + return context.begin(); + } + + auto format(std::strong_ordering value, format_context & context) const -> void + { + if (value == std::strong_ordering::equal) + { + return context.push(specifiers.alternative_form ? "==" : "equal"); + } + else if (value == std::strong_ordering::equivalent) + { + return context.push(specifiers.alternative_form ? "==" : "equivalent"); + } + else if (value == std::strong_ordering::greater) + { + return context.push(specifiers.alternative_form ? ">" : "greater"); + } + else if (value == std::strong_ordering::less) + { + return context.push(specifiers.alternative_form ? "<" : "less"); + } + kstd::os::panic("[kstd:format] Invalid strong ordering value!"); + } + }; + + template<> + struct formatter + { + bits::format::specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + return context.begin(); + } + + auto format(std::weak_ordering value, format_context & context) const -> void + { + if (value == std::weak_ordering::equivalent) + { + return context.push(specifiers.alternative_form ? "==" : "equivalent"); + } + else if (value == std::weak_ordering::greater) + { + return context.push(specifiers.alternative_form ? ">" : "greater"); + } + else if (value == std::weak_ordering::less) + { + return context.push(specifiers.alternative_form ? "<" : "less"); + } + kstd::os::panic("[kstd:format] Invalid weak ordering value!"); + } + }; + + template<> + struct formatter + { + bits::format::specifiers specifiers{}; + + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + specifiers = bits::format::parse_format_specifiers(context); + return context.begin(); + } + + auto format(std::partial_ordering value, format_context & context) const -> void + { + if (value == std::partial_ordering::equivalent) + { + return context.push(specifiers.alternative_form ? "==" : "equivalent"); + } + else if (value == std::partial_ordering::greater) + { + return context.push(specifiers.alternative_form ? ">" : "greater"); + } + else if (value == std::partial_ordering::less) + { + return context.push(specifiers.alternative_form ? "<" : "less"); + } + else if (value == std::partial_ordering::unordered) + { + return context.push(specifiers.alternative_form ? "<=>" : "unordered"); + } + kstd::os::panic("[kstd:format] Invalid partial ordering value!"); + } + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/formatter/pointer.hpp b/libs/kstd/kstd/bits/format/formatter/pointer.hpp new file mode 100644 index 0000000..15f9a5b --- /dev/null +++ b/libs/kstd/kstd/bits/format/formatter/pointer.hpp @@ -0,0 +1,42 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_POINTER_HPP +#define KSTD_BITS_FORMAT_FORMATTER_POINTER_HPP + +#include +#include +#include +#include +#include + +#include +#include + +namespace kstd +{ + + template + struct formatter : formatter + { + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + auto result = formatter::parse(context); + if (!this->specifiers.type) + { + this->specifiers.type = 'p'; + this->specifiers.alternative_form = true; + } + return result; + } + + auto format(T const * pointer, format_context & context) const -> void + { + formatter::format(std::bit_cast(pointer), context); + } + }; + + template + struct formatter : formatter + { + }; + +} // namespace kstd +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/formatter/range.hpp b/libs/kstd/kstd/bits/format/formatter/range.hpp new file mode 100644 index 0000000..05af06f --- /dev/null +++ b/libs/kstd/kstd/bits/format/formatter/range.hpp @@ -0,0 +1,32 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_RANGE_HPP +#define KSTD_BITS_FORMAT_FORMATTER_RANGE_HPP + +#include + +#include +#include +#include +#include + +namespace kstd +{ + namespace bits::format + { + template + concept iterable = requires(T const & t) { + t.begin(); + t.end(); + }; + + template + concept formattable_range = iterable && !std::same_as, std::string_view> && + !std::same_as, char *> && !std::same_as, char const *>; + } // namespace bits::format + + template + struct formatter : range_formatter> + { + }; +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/formatter/string_view.hpp b/libs/kstd/kstd/bits/format/formatter/string_view.hpp new file mode 100644 index 0000000..7d74579 --- /dev/null +++ b/libs/kstd/kstd/bits/format/formatter/string_view.hpp @@ -0,0 +1,41 @@ +#ifndef KSTD_BITS_FORMAT_FORMATTER_STRING_VIEW_HPP +#define KSTD_BITS_FORMAT_FORMATTER_STRING_VIEW_HPP + +#include +#include +#include +#include + +#include + +namespace kstd +{ + + template<> + struct formatter + { + constexpr auto parse(format_parse_context & context) -> format_parse_context::iterator + { + auto it = context.begin(); + + if (it != context.end() && *it == 's') + { + ++it; + } + + if (it != context.end() && *it != '}') + { + bits::format::error("Invalid specifier for string_view."); + } + return it; + } + + auto format(std::string_view const & string, format_context & context) const -> void + { + context.push(string); + } + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/fwd.hpp b/libs/kstd/kstd/bits/format/fwd.hpp new file mode 100644 index 0000000..6caedae --- /dev/null +++ b/libs/kstd/kstd/bits/format/fwd.hpp @@ -0,0 +1,23 @@ +#ifndef KSTD_BITS_FORMAT_FWD_HPP +#define KSTD_BITS_FORMAT_FWD_HPP + +// IWYU pragma: private + +#include + +namespace kstd +{ + + struct format_parse_context; + struct format_context; + struct format_arg; + + template + struct formatter; + + template + struct format_arg_store; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/output_buffer.hpp b/libs/kstd/kstd/bits/format/output_buffer.hpp new file mode 100644 index 0000000..fd7a2b4 --- /dev/null +++ b/libs/kstd/kstd/bits/format/output_buffer.hpp @@ -0,0 +1,32 @@ +#ifndef KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP +#define KSTD_BITS_FORMAT_OUTPUT_BUFFER_HPP + +// IWYU pragma: private, include + +#include + +namespace kstd::bits::format +{ + + //! An abstract interface for formatted output buffers. + //! + //! This interface is intended to be use for functions dealing with string formatting, like the print and the format + //! family. + struct output_buffer + { + virtual ~output_buffer() = default; + + //! Push a text segment into the buffer. + //! + //! @param text The text segment to push. + virtual auto push(std::string_view text) -> void = 0; + + //! Push a single character into the buffer. + //! + //! @param character The character to push into the buffer. + virtual auto push(char character) -> void = 0; + }; + +} // namespace kstd::bits::format + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/parse_context.hpp b/libs/kstd/kstd/bits/format/parse_context.hpp new file mode 100644 index 0000000..cab8d72 --- /dev/null +++ b/libs/kstd/kstd/bits/format/parse_context.hpp @@ -0,0 +1,109 @@ +#ifndef KSTD_BITS_FORMAT_PARSE_CONTEXT_HPP +#define KSTD_BITS_FORMAT_PARSE_CONTEXT_HPP + +// IWYU pragma: private, include + +#include + +#include +#include + +namespace kstd +{ + + struct format_parse_context + { + using iterator = std::string_view::const_iterator; + + constexpr format_parse_context(std::string_view format, std::size_t argument_count, + bool const * is_integral = nullptr) + : m_current{format.begin()} + , m_end{format.end()} + , m_argument_count{argument_count} + , m_is_integral{is_integral} + {} + + [[nodiscard]] constexpr auto begin() const -> iterator + { + return m_current; + } + + [[nodiscard]] constexpr auto end() const -> iterator + { + return m_end; + } + + constexpr auto advance_to(iterator position) -> void + { + m_current = position; + } + + constexpr auto next_arg_id() -> std::size_t + { + if (m_mode == index_mode::manual) + { + bits::format::error("Cannot mix automatic and manual indexing."); + } + + m_mode = index_mode::automatic; + + if (m_next_argument_id >= m_argument_count) + { + bits::format::error("Argument index out of bounds."); + } + return m_next_argument_id++; + } + + constexpr auto check_arg_id(std::size_t index) -> void + { + if (m_mode == index_mode::automatic) + { + bits::format::error("Cannot mix automatic and manual indexing."); + } + + m_mode = index_mode::manual; + + if (index >= m_argument_count) + { + bits::format::error("Argument index out of bounds."); + } + } + + constexpr auto check_dynamic_width_id(std::size_t id) -> void + { + check_arg_id(id); + if (m_is_integral && !m_is_integral[id]) + { + bits::format::error("Dynamic width argument must be an integral object."); + } + } + + constexpr auto next_dynamic_width_id() -> std::size_t + { + auto const id = next_arg_id(); + if (m_is_integral && !m_is_integral[id]) + { + bits::format::error("Dynamic width argument must be an integral object."); + } + return id; + } + + private: + enum class index_mode + { + unknown, + automatic, + manual, + }; + + iterator m_current{}; + iterator m_end{}; + index_mode m_mode{}; + std::size_t m_next_argument_id{}; + std::size_t m_argument_count{}; + bool const * m_is_integral{}; + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/specifiers.hpp b/libs/kstd/kstd/bits/format/specifiers.hpp new file mode 100644 index 0000000..211c95d --- /dev/null +++ b/libs/kstd/kstd/bits/format/specifiers.hpp @@ -0,0 +1,205 @@ +#ifndef KSTD_BITS_FORMAT_SPECS_HPP +#define KSTD_BITS_FORMAT_SPECS_HPP + +// IWYU pragma: private + +#include +#include + +#include +#include +#include + +namespace kstd::bits::format +{ + enum struct alignment : std::uint8_t + { + none, + left, + right, + center, + }; + + enum struct sign_mode : std::uint8_t + { + none, + plus, + minus, + space, + }; + + enum struct width_mode : std::uint8_t + { + none, + static_value, + dynamic_argument_id + }; + + struct specifiers + { + char fill{' '}; + alignment align{}; + sign_mode sign{}; + bool alternative_form{}; + bool zero_pad{}; + + width_mode mode{}; + std::size_t width_value{}; + char type{}; + }; + + struct padding + { + std::size_t left{}; + std::size_t right{}; + }; + + constexpr auto parse_format_specifiers(format_parse_context & context) -> specifiers + { + auto specs = specifiers{}; + auto it = context.begin(); + auto const end = context.end(); + + if (it != end && *it == '}') + { + return specs; + } + + if (std::next(it) != end && ((*std::next(it)) == '<' || (*std::next(it)) == '>' || (*std::next(it)) == '^')) + { + specs.fill = *it; + switch (*std::next(it)) + { + case '<': + specs.align = alignment::left; + break; + case '>': + specs.align = alignment::right; + break; + case '^': + default: + specs.align = alignment::center; + break; + } + std::advance(it, 2); + } + else if (*it == '<' || *it == '>' || *it == '^') + { + switch (*it) + { + case '<': + specs.align = alignment::left; + break; + case '>': + specs.align = alignment::right; + break; + case '^': + default: + specs.align = alignment::center; + break; + } + std::advance(it, 1); + } + + if (it != end && (*it == '+' || *it == '-' || *it == ' ')) + { + switch (*it) + { + case '+': + specs.sign = sign_mode::plus; + break; + case '-': + specs.sign = sign_mode::minus; + break; + case ' ': + default: + specs.sign = sign_mode::space; + break; + } + std::advance(it, 1); + } + + if (it != end && *it == '#') + { + specs.alternative_form = true; + std::advance(it, 1); + } + + if (it != end && *it == '0') + { + specs.zero_pad = true; + std::advance(it, 1); + } + + if (it != end && *it == '{') + { + specs.mode = width_mode::dynamic_argument_id; + std::advance(it, 1); + auto argument_id = 0uz; + + if (it != end && *it >= '0' && *it <= '9') + { + while (it != end && *it >= '0' && *it <= '9') + { + argument_id = argument_id * 10 + static_cast(*it - '0'); + std::advance(it, 1); + } + context.check_dynamic_width_id(argument_id); + } + else + { + argument_id = context.next_dynamic_width_id(); + } + + if (it == end || *it != '}') + { + error("Expected '}' for dynamic width."); + } + std::advance(it, 1); + specs.width_value = argument_id; + } + else if (it != end && *it >= '0' && *it <= '9') + { + specs.mode = width_mode::static_value; + while (it != end && *it >= '0' && *it <= '9') + { + specs.width_value = specs.width_value * 10 + static_cast(*it - '0'); + std::advance(it, 1); + } + } + + context.advance_to(it); + return specs; + } + + constexpr auto calculate_format_padding(std::size_t target_width, std::size_t content_length, + alignment requested_alignment, alignment default_alignment) -> padding + { + if (target_width <= content_length) + { + return {}; + } + + auto total_padding = target_width - content_length; + auto effective_alignment = (requested_alignment == alignment::none) ? default_alignment : requested_alignment; + + switch (effective_alignment) + { + case alignment::center: + { + auto left = total_padding / 2; + auto right = total_padding - left; + return {left, right}; + } + case alignment::left: + return {0, total_padding}; + case alignment::right: + default: + return {total_padding, 0}; + break; + } + } + +} // namespace kstd::bits::format + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/string.hpp b/libs/kstd/kstd/bits/format/string.hpp new file mode 100644 index 0000000..e7e4088 --- /dev/null +++ b/libs/kstd/kstd/bits/format/string.hpp @@ -0,0 +1,148 @@ +#ifndef KSTD_BITS_FORMAT_STRING_HPP +#define KSTD_BITS_FORMAT_STRING_HPP + +// IWYU pragma: private, include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace kstd +{ + + namespace bits::format + { + template + constexpr auto validate_argument(std::size_t target_index, format_parse_context & context) -> void + { + auto found = false; + auto current_index = 0uz; + + (..., [&] { + if (current_index == target_index && !found) + { + using decay_type = std::remove_cvref_t; + static_assert(std::is_default_constructible_v>, "Missing formatter specialization."); + auto fmt = formatter{}; + + auto it = fmt.parse(context); + context.advance_to(it); + found = true; + } + ++current_index; + }()); + + if (!found) + { + error("Argument index out of bounds."); + } + } + + template + constexpr auto validate_dynamic_width(std::size_t target_index) -> void + { + auto is_valid_integer = false; + auto current_index = 0uz; + + (..., [&] { + if (current_index == target_index) + { + using decay_type = std::remove_cvref_t; + if constexpr (std::is_integral_v) + { + is_valid_integer = true; + } + } + ++current_index; + }()); + + if (!is_valid_integer) + { + error("Dynamic width argument must be an integral object."); + } + } + } // namespace bits::format + + template + struct format_string + { + template + consteval format_string(char const (&str)[Size]) noexcept(false) // NOLINT + : str_view{str} + { + using namespace bits::format; + + auto const is_width_compatible = + std::array 0 ? sizeof...(Args) : 1)>{is_width_v>...}; + auto context = format_parse_context{str_view, sizeof...(Args), is_width_compatible.data()}; + auto it = context.begin(); + + while (it != context.end()) + { + if (*it == '{') + { + ++it; + if (it != context.end() && *it == '{') + { + ++it; + context.advance_to(it); + continue; + } + + context.advance_to(it); + auto argument_id = 0uz; + + if (it != context.end() && *it >= '0' && *it <= '9') + { + while (it != context.end() && *it >= '0' && *it <= '9') + { + argument_id = argument_id * 10 + static_cast(*it - '0'); + ++it; + } + context.check_arg_id(argument_id); + context.advance_to(it); + } + else + { + argument_id = context.next_arg_id(); + } + + if (it != context.end() && *it == ':') + { + ++it; + context.advance_to(it); + } + + validate_argument(argument_id, context); + + it = context.begin(); + if (it == context.end() || *it != '}') + { + bits::format::error("Missing closing '}' in format string."); + } + } + else if (*it == '}') + { + ++it; + if (it != context.end() && *it == '}') + { + bits::format::error("Unescaped '}' in format string."); + } + } + ++it; + context.advance_to(it); + } + } + + std::string_view str_view; + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/format/vformat.hpp b/libs/kstd/kstd/bits/format/vformat.hpp new file mode 100644 index 0000000..994fae5 --- /dev/null +++ b/libs/kstd/kstd/bits/format/vformat.hpp @@ -0,0 +1,99 @@ +#ifndef KSTD_BITS_FORMAT_VFORMAT_HPP +#define KSTD_BITS_FORMAT_VFORMAT_HPP + +// IWYU pragma: private, include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace kstd +{ + + namespace bits::format + { + //! Format a string with the given arguments into the given buffer. + //! + //! External implementations of `vprint` may call this function. + //! + //! @param buffer The buffer to format into. + //! @param format The format string to use. + //! @param args The arguments for the format string. + auto vformat_to(output_buffer & buffer, std::string_view format, format_args args) -> void; + + struct string_writer : output_buffer + { + auto push(std::string_view text) -> void override; + auto push(char character) -> void override; + + auto release() -> string &&; + + private: + string m_result{}; + }; + + template Output> + struct iterator_writer : output_buffer + { + explicit iterator_writer(Output iterator) + : m_output{iterator} + {} + + [[nodiscard]] auto iterator() const -> Output + { + return m_output; + } + + auto push(std::string_view text) -> void override + { + m_output = std::ranges::copy(text, m_output).out; + } + + auto push(char character) -> void override + { + *m_output++ = character; + } + + private: + Output m_output{}; + }; + + } // namespace bits::format + + //! Format a given string with the provided arguments. + //! + //! @param format The format string. + //! @param args The arguments for the format string. + //! @return A new string containing the result of the format operation. + template + auto format(format_string...> format, ArgumentTypes &&... args) -> string + { + auto buffer = bits::format::string_writer{}; + bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward(args)...).args); + return buffer.release(); + } + + //! Format a given string with the provided arguments. + //! + //! @param iterator The iterator to write to. + //! @param format The format string. + //! @param args The arguments for the format string. + //! @return An iterator past the last element written. + template Output, typename... ArgumentTypes> + auto format_to(Output iterator, format_string...> format, + ArgumentTypes &&... args) -> Output + { + auto buffer = bits::format::iterator_writer{iterator}; + bits::format::vformat_to(buffer, format.str_view, make_format_args(std::forward(args)...).args); + return buffer.iterator(); + } + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/observer_ptr.hpp b/libs/kstd/kstd/bits/observer_ptr.hpp new file mode 100644 index 0000000..2593d7a --- /dev/null +++ b/libs/kstd/kstd/bits/observer_ptr.hpp @@ -0,0 +1,163 @@ +#ifndef KSTD_OBSERVER_PTR_HPP +#define KSTD_OBSERVER_PTR_HPP + +// IWYU pragma: private, include + +#include + +#include +#include +#include +#include +#include + +namespace kstd +{ + + template + struct observer_ptr + { + //! The type of the element being pointed to. + using element_type = ElementType; + + //! Construct an empty observer pointer. + constexpr observer_ptr() noexcept = default; + + //! Construct an empty observer pointer from a null pointer. + constexpr observer_ptr(std::nullptr_t) noexcept {} + + //! Construct an observer pointer from a raw pointer. + constexpr explicit observer_ptr(element_type * pointer) + : m_ptr{pointer} + {} + + //! Construct an observer pointer from another observer pointer. + template + requires std::convertible_to + constexpr observer_ptr(observer_ptr other) noexcept + : m_ptr{other.get()} + {} + + //! Copy construct an observer pointer. + constexpr observer_ptr(observer_ptr const & other) noexcept = default; + + //! Move construct an observer pointer. + constexpr observer_ptr(observer_ptr && other) noexcept = default; + + //! Copy assign an observer pointer. + constexpr auto operator=(observer_ptr const & other) noexcept -> observer_ptr & = default; + + //! Move assign an observer pointer. + constexpr auto operator=(observer_ptr && other) noexcept -> observer_ptr & = default; + + //! Stop watching the the watched object. + //! + //! @return The currently watched object, or nullptr if no object is being watched. + [[nodiscard]] constexpr auto release() noexcept -> element_type * + { + return std::exchange(m_ptr, nullptr); + } + + //! Reset the observer pointer. + //! + //! @param pointer The new object to watch. + constexpr auto reset(element_type * pointer = nullptr) noexcept -> void + { + m_ptr = pointer; + } + + //! Swap the observer pointer with another observer pointer. + //! + //! @param other The other observer pointer to swap with. + constexpr auto swap(observer_ptr & other) noexcept -> void + { + std::swap(m_ptr, other.m_ptr); + } + + //! Get the currently watched object. + //! + //! @return The currently watched object, or nullptr if no object is being watched. + [[nodiscard]] constexpr auto get() const noexcept -> element_type * + { + return m_ptr; + } + + //! Check if the observer pointer is watching an object. + //! + //! @return True if the observer pointer is watching an object, false otherwise. + [[nodiscard]] constexpr explicit operator bool() const noexcept + { + return m_ptr != nullptr; + } + + //! Get the currently watched object. + //! + //! @return A reference to the currently watched object. + [[nodiscard]] constexpr auto operator*() const -> std::add_lvalue_reference_t + { + throw_on_null(); + return *m_ptr; + } + + //! Get the currently watched object. + //! + //! @return A pointer to the currently watched object. + [[nodiscard]] constexpr auto operator->() const -> element_type * + { + throw_on_null(); + return m_ptr; + } + + //! Convert the observer pointer to a raw pointer. + //! + //! @return A pointer to the currently watched object. + constexpr explicit operator element_type *() const noexcept + { + return m_ptr; + } + + //! Compare the observer pointer with another observer pointer. + //!> + //! @param other The other observer pointer to compare with. + //! @return The result of the comparison. + constexpr auto operator<=>(observer_ptr const & other) const noexcept -> std::strong_ordering = default; + + private: + //! Throw an exception if the observer pointer is null. + //! + //! @throws std::runtime_error if the observer pointer is null. + constexpr auto throw_on_null() const -> void + { + if (m_ptr == nullptr) + { + os::panic("[kstd:observer_ptr] Dereferencing a null observer pointer"); + } + } + + //! The raw pointer to the watched object. + ElementType * m_ptr{}; + }; + + //! Swap two observer pointers. + //! + //! @param lhs The first observer pointer to swap. + //! @param rhs The second observer pointer to swap. + template + constexpr auto swap(observer_ptr & lhs, observer_ptr & rhs) noexcept -> void + { + lhs.swap(rhs); + } + + //! Create an observer pointer from a raw pointer. + //! + //! @param pointer The raw pointer to create an observer pointer from. + //! @return An observer pointer to the given raw pointer. + template + constexpr auto make_observer(ElementType * pointer) noexcept -> observer_ptr + { + return observer_ptr{pointer}; + } + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/observer_ptr.test.cpp b/libs/kstd/kstd/bits/observer_ptr.test.cpp new file mode 100644 index 0000000..1ba9c63 --- /dev/null +++ b/libs/kstd/kstd/bits/observer_ptr.test.cpp @@ -0,0 +1,360 @@ +#include +#include + +#include + +#include +#include +#include +#include + +namespace +{ + struct Base + { + }; + + struct Derived : Base + { + }; + + struct Element + { + int value{}; + + constexpr auto operator<=>(Element const &) const noexcept = default; + }; +} // namespace + +SCENARIO("Observer Pointer initialization and construction", "[observer_ptr]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto ptr = kstd::observer_ptr{}; + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + } + + WHEN("constructing from a nullptr") + { + auto ptr = kstd::observer_ptr{nullptr}; + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + } + + WHEN("constructing from a raw pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + + THEN("the observer pointer is not null") + { + REQUIRE(ptr); + } + + THEN("the observer pointer points to the correct object") + { + REQUIRE(&*ptr == &value); + } + } + + WHEN("copy constructing from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = ptr; + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("copy constructing from an existing observer pointer with a compatible type") + { + auto value = Derived{}; + auto ptr = kstd::observer_ptr(&value); + kstd::observer_ptr copy = ptr; + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("copy assigning from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = ptr; + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("move constructing from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = std::move(ptr); + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("move assigning from an existing observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + auto copy = std::move(ptr); + + THEN("the new observer pointer points to the same object as the other observer pointer") + { + REQUIRE(&*copy == &value); + } + } + + WHEN("constructing an observer pointer using make_observer") + { + auto value = 1; + auto ptr = kstd::make_observer(&value); + + THEN("the observer pointer points to the correct object") + { + REQUIRE(&*ptr == &value); + } + + THEN("the observe pointer has the correct element type") + { + STATIC_REQUIRE(std::is_same_v>); + } + } + } +} + +SCENARIO("Observer pointer modifiers", "[observer_ptr]") +{ + GIVEN("A non-null observer pointer") + { + auto value = 1; + auto ptr = kstd::observer_ptr{&value}; + + WHEN("releasing the observer pointer") + { + auto raw_ptr = ptr.release(); + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + + THEN("the returned pointer points to the correct object") + { + REQUIRE(raw_ptr == &value); + } + } + + WHEN("resetting the observer pointer to nullptr") + { + ptr.reset(); + + THEN("the observer pointer is null") + { + REQUIRE_FALSE(ptr); + } + } + + WHEN("resetting the observer pointer to a new object") + { + auto other_value = 2; + ptr.reset(&other_value); + + THEN("the observer pointer points to the new object") + { + REQUIRE(&*ptr == &other_value); + } + } + + WHEN("swapping it with another observer pointer") + { + auto other_value = 2; + auto other_ptr = kstd::observer_ptr{&other_value}; + ptr.swap(other_ptr); + + THEN("the observer pointer points to the other object") + { + REQUIRE(&*ptr == &other_value); + } + + THEN("the other observer pointer points to the original object") + { + REQUIRE(&*other_ptr == &value); + } + } + + WHEN("using namespace-level swap to swap it with another observer pointer") + { + using std::swap; + auto other_value = 2; + auto other_ptr = kstd::observer_ptr{&other_value}; + swap(ptr, other_ptr); + + THEN("the observer pointer points to the other object") + { + REQUIRE(&*ptr == &other_value); + } + + THEN("the other observer pointer points to the original object") + { + REQUIRE(&*other_ptr == &value); + } + } + } +} + +SCENARIO("Observer pointer observers", "[observer_ptr]") +{ + GIVEN("A non-null observer pointer") + { + auto value = Element{1}; + auto ptr = kstd::observer_ptr{&value}; + + WHEN("getting the raw pointer") + { + auto raw_ptr = ptr.get(); + + THEN("the raw pointer points to the correct object") + { + REQUIRE(raw_ptr == &value); + } + } + + WHEN("dereferencing the observer pointer") + { + auto dereferenced = *ptr; + + THEN("the dereferenced value is the correct value") + { + REQUIRE(dereferenced == value); + } + } + + WHEN("writing through the observer pointer with the arrow operator") + { + ptr->value = 2; + + THEN("the value is updated") + { + REQUIRE(value.value == 2); + } + } + + WHEN("converting the observer pointer to a raw pointer") + { + auto raw_ptr = static_cast(ptr); + + THEN("the raw pointer points to the correct object") + { + REQUIRE(raw_ptr == &value); + } + } + + WHEN("checking the observer pointer as a boolean") + { + THEN("it returns true") + { + REQUIRE(static_cast(ptr)); + } + } + } + + GIVEN("A null observer pointer") + { + auto ptr = kstd::observer_ptr{}; + + WHEN("checking the observer pointer as a boolean") + { + THEN("it returns false") + { + REQUIRE_FALSE(static_cast(ptr)); + } + } + + WHEN("dereferencing the observer pointer") + { + THEN("the observer pointer panics") + { + REQUIRE_THROWS_AS(*ptr, kstd::tests::os_panic); + } + } + + WHEN("writing through the observer pointer with the arrow operator") + { + THEN("the observer pointer panics") + { + REQUIRE_THROWS_AS(ptr->value = 2, kstd::tests::os_panic); + } + } + } +} + +SCENARIO("Observer pointer comparisons", "[observer_ptr]") +{ + GIVEN("Observer pointers to elements of an array") + { + auto arr = std::array{1, 2}; + auto ptr1 = kstd::observer_ptr{&arr[0]}; + auto ptr2 = kstd::observer_ptr{&arr[1]}; + + WHEN("comparing the same observer pointer") + { + THEN("they are equal") + { + REQUIRE(ptr1 == ptr1); + REQUIRE((ptr1 <=> ptr1) == std::strong_ordering::equal); + } + } + + WHEN("comparing different observer pointers") + { + THEN("they are ordered correctly") + { + REQUIRE(ptr1 != ptr2); + REQUIRE(ptr1 < ptr2); + REQUIRE(ptr1 <= ptr2); + REQUIRE(ptr2 > ptr1); + REQUIRE(ptr2 >= ptr1); + REQUIRE((ptr1 <=> ptr2) == std::strong_ordering::less); + REQUIRE((ptr2 <=> ptr1) == std::strong_ordering::greater); + } + } + } + + GIVEN("A null observer pointer") + { + auto ptr = kstd::observer_ptr{}; + + WHEN("comparing with another null observer pointer") + { + auto other_ptr = kstd::observer_ptr{}; + + THEN("they are equal") + { + REQUIRE(ptr == other_ptr); + REQUIRE((ptr <=> other_ptr) == std::strong_ordering::equal); + } + } + } +} \ No newline at end of file diff --git a/libs/kstd/kstd/bits/print_sink.hpp b/libs/kstd/kstd/bits/print_sink.hpp new file mode 100644 index 0000000..af765e0 --- /dev/null +++ b/libs/kstd/kstd/bits/print_sink.hpp @@ -0,0 +1,17 @@ +#ifndef KSTD_BITS_PRINT_SINK_HPP +#define KSTD_BITS_PRINT_SINK_HPP + +// IWYU pragma: private, include + +namespace kstd +{ + + enum struct print_sink + { + stdout, + stderr, + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/shared_ptr.hpp b/libs/kstd/kstd/bits/shared_ptr.hpp new file mode 100644 index 0000000..8930095 --- /dev/null +++ b/libs/kstd/kstd/bits/shared_ptr.hpp @@ -0,0 +1,648 @@ +#ifndef KSTD_BITS_SHARED_PTR_HPP +#define KSTD_BITS_SHARED_PTR_HPP + +#include +#include +#include +#include + +// IWYU pragma: private, include + +namespace kstd +{ + /** + * @brief Control block for shared_ptr and weak_ptr. This control block contains the reference counts for shared + * ownership and weak ownership. The shared_count tracks the number of shared_ptr instances that own the managed + * object, while the weak_count tracks the number of weak_ptr instances that reference the managed object. + * The weak_count is needed to determine when it is safe to delete the shared_control_block itself + */ + struct shared_control_block + { + std::atomic shared_count; + std::atomic weak_count; + + explicit shared_control_block(std::size_t shared = 1, std::size_t weak = 0) + : shared_count(shared) + , weak_count(weak) + {} + }; + + template + struct shared_ptr; + + /** + * @brief weak_ptr is a smart pointer that holds a non-owning weak reference to an object that is managed by + * shared_ptr. It must be converted to shared_ptr in to be able to access the referenced object. A weak_ptr is created + * as a copy of a shared_ptr, or as a copy of another weak_ptr. A weak_ptr is typically used to break circular + * references between shared_ptr to avoid memory leaks. A weak_ptr does not contribute to the reference count of the + * object, and it does not manage the lifetime of the object. If the object managed by shared_ptr is destroyed, the + * weak_ptr becomes expired and cannot be used to access the object anymore. + */ + template + struct weak_ptr + { + template + friend struct shared_ptr; + + /** + * @brief Constructs a null weak_ptr. + */ + weak_ptr() noexcept + : pointer(nullptr) + , control(nullptr) + {} + + template + requires(std::is_convertible_v) + weak_ptr(shared_ptr const & other) + : pointer(other.pointer) + , control(other.control) + { + if (control != nullptr) + { + ++(control->weak_count); + } + } + + /** + * @brief Copy constructor. Constructs a weak_ptr which shares ownership of the object managed by other. + */ + weak_ptr(weak_ptr const & other) + : pointer(other.pointer) + , control(other.control) + { + if (control != nullptr) + { + ++(control->weak_count); + } + } + + /** + * @brief Move constructor. Constructs a weak_ptr which takes ownership of the object managed by other. + */ + weak_ptr(weak_ptr && other) noexcept + : pointer(other.pointer) + , control(other.control) + { + other.pointer = nullptr; + other.control = nullptr; + } + + /** + * @brief Assignment operator. Assigns the weak_ptr to another weak_ptr. + */ + auto operator=(weak_ptr const & other) -> weak_ptr & + { + if (this != &other) + { + cleanup(); + pointer = other.pointer; + control = other.control; + if (control != nullptr) + { + ++(control->weak_count); + } + } + + return *this; + } + + /** + * @brief Move assignment operator. Move-assigns a weak_ptr from other. + */ + auto operator=(weak_ptr && other) noexcept -> weak_ptr & + { + if (this != &other) + { + cleanup(); + pointer = other.pointer; + control = other.control; + other.pointer = nullptr; + other.control = nullptr; + } + + return *this; + } + + /** + * @brief Destructor. Cleans up resources if necessary. + */ + ~weak_ptr() + { + cleanup(); + } + + /** + * @brief Returns a shared_ptr that shares ownership of the managed object if the managed object still exists, or an + * empty shared_ptr otherwise. + */ + [[nodiscard]] auto lock() const -> shared_ptr + { + return shared_ptr(*this); + } + + private: + auto cleanup() -> void + { + if (control != nullptr) + { + if (--(control->weak_count) == 0 && control->shared_count == 0) + { + delete control; + } + } + } + + T * pointer; + shared_control_block * control; + }; + + /** + * @brief enable_shared_from_this is a base class that allows an object that is currently managed by a shared_ptr to + * create additional shared_ptr instances that share ownership of the same object. This is usefl when you want to + * create shared_ptr instances in a member function of the object. + * + * @tparam T The type of the managed object. + */ + template + struct enable_shared_from_this + { + template + friend struct shared_ptr; + + friend T; + + public: + /** + * @brief Returns a shared_ptr that shares ownership of *this. + */ + auto shared_from_this() -> shared_ptr + { + return shared_ptr(weak_this); + } + + /** + * @brief Returns a shared_ptr that shares ownership of *this. + */ + auto shared_from_this() const -> shared_ptr + { + return shared_ptr(weak_this); + } + + private: + enable_shared_from_this() = default; + enable_shared_from_this(enable_shared_from_this const &) = default; + auto operator=(enable_shared_from_this const &) -> enable_shared_from_this & = default; + ~enable_shared_from_this() = default; + + void internal_assign_ptr(shared_ptr const & ptr) const + { + weak_this = ptr; + } + + mutable weak_ptr weak_this{}; ///< Weak pointer to the object, used for shared_from_this functionality. + }; + + /** + * @brief Shared_pointer is a smart pointer that retains shared ownership of an object through a pointer. Several + * shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when either of + * the following happens: the last remaining shared_ptr owning the object is destroyed; the last remaining + * shared_ptr owning the object is assigned another pointer via operator= or reset(). A + * shared_ptr can share ownership of an object while storing a pointer to another object. This feature can be used + * to point to member objects while owning the object they belong to. The stored pointer is the one accessed by get(), + * the dereference and the comparison operators. The managed pointer is the one passed to the deleter when use count + * reaches zero. + * + * @tparam T The type of the managed object. + */ + template + struct shared_ptr + { + template + friend struct shared_ptr; + + template + friend struct weak_ptr; + + /** + * @brief Construct an empty shared_ptr. + */ + shared_ptr() noexcept + : pointer(nullptr) + , control(nullptr) + {} + + /** + * @brief Construct an empty shared_ptr from nullptr. + */ + shared_ptr(std::nullptr_t) noexcept + : pointer(nullptr) + , control(nullptr) + {} + + /** + * @brief Constructor. + * + * @param pointer A pointer to an object to manage (default is nullptr). + */ + template + requires(std::is_convertible_v) + explicit shared_ptr(U * pointer = nullptr) + : pointer(pointer) + , control(pointer != nullptr ? new shared_control_block() : nullptr) + { + assign_enable_shared_from_this(pointer); + } + + /** + * @brief Constructor a shared_ptr from a weak_ptr. If other is not expired, constructs a shared_ptr which shares + * ownership of the object managed by other. Otherwise, constructs an empty shared_ptr. + * + * @param other The weak_ptr to construct from. + */ + template + requires(std::is_convertible_v) + explicit shared_ptr(weak_ptr const & other) + : pointer(nullptr) + , control(nullptr) + { + if (other.control != nullptr && other.control->shared_count != 0) + { + pointer = other.pointer; + control = other.control; + ++(control->shared_count); + } + } + + /** + * @brief Copy constructor. + * + * @param other The shared_ptr to copy from. + */ + shared_ptr(shared_ptr const & other) + : pointer(other.pointer) + , control(other.control) + { + if (control != nullptr) + { + ++(control->shared_count); + } + } + + /** + * @brief Converting copy constructor for compatible shared_ptr types. + * + * @tparam U Source pointer element type. + * @param other The shared_ptr to copy from. + */ + template + requires(std::is_convertible_v) + shared_ptr(shared_ptr const & other) + : pointer(other.pointer) + , control(other.control) + { + if (control != nullptr) + { + ++(control->shared_count); + } + } + + /** + * @brief Move constructor. + * + * @param other The shared_ptr to move from. + */ + shared_ptr(shared_ptr && other) noexcept + : pointer(other.pointer) + , control(other.control) + { + other.pointer = nullptr; + other.control = nullptr; + } + + /** + * @brief Converting move constructor for compatible shared_ptr types. + * + * @tparam U Source pointer element type. + * @param other The shared_ptr to move from. + */ + template + requires(std::is_convertible_v) + shared_ptr(shared_ptr && other) noexcept + : pointer(other.pointer) + , control(other.control) + { + other.pointer = nullptr; + other.control = nullptr; + } + + /** + * @brief Copy assignment operator. Replaces the managed object with the one managed by r. Shares ownership of the + * object managed by r. If r manages no object, *this manages no object too. Equivalent to + * shared_ptr(r).swap(*this). + * + * @param other Another smart pointer to share the ownership with. + * @return Reference to this shared pointer. + */ + auto operator=(shared_ptr const & other) -> shared_ptr & + { + if (this != &other) + { + cleanup(); + pointer = other.pointer; + control = other.control; + + if (control != nullptr) + { + ++(control->shared_count); + } + } + + return *this; + } + + /** + * @brief Converting copy assignment for compatible shared_ptr types. + * + * @tparam U Source pointer element type. + * @param other Another smart pointer to share ownership with. + * @return Reference to this shared pointer. + */ + template + requires(std::is_convertible_v) + auto operator=(shared_ptr const & other) -> shared_ptr & + { + cleanup(); + pointer = other.pointer; + control = other.control; + + if (control != nullptr) + { + ++(control->shared_count); + } + + return *this; + } + + /** + * @brief Move assignment operator. Move-assigns a shared_ptr from r. After the assignment, *this contains a copy of + * the previous state of r, and r is empty. Equivalent to shared_ptr(std::move(r)).swap(*this). + * + * @param other Another smart pointer to acquire the ownership from. + * @return Reference to this shared pointer. + */ + auto operator=(shared_ptr && other) noexcept -> shared_ptr & + { + if (this != &other) + { + cleanup(); + pointer = other.pointer; + control = other.control; + other.pointer = nullptr; + other.control = nullptr; + } + + return *this; + } + + /** + * @brief Converting move assignment for compatible shared_ptr types. + * + * @tparam U Source pointer element type. + * @param other Another smart pointer to acquire ownership from. + * @return Reference to this shared pointer. + */ + template + requires(std::is_convertible_v) + auto operator=(shared_ptr && other) noexcept -> shared_ptr & + { + cleanup(); + pointer = other.pointer; + control = other.control; + other.pointer = nullptr; + other.control = nullptr; + + return *this; + } + + /** + * @brief Reset this shared_ptr to empty via nullptr assignment. + */ + auto operator=(std::nullptr_t) noexcept -> shared_ptr & + { + cleanup(); + pointer = nullptr; + control = nullptr; + return *this; + } + + /** + * @brief Destructor. Cleans up resources if necessary. + */ + ~shared_ptr() + { + cleanup(); + } + + /** + * @brief Replaces the managed object. + * + * @param ptr Pointer to a new object to manage (default = nullptr). + */ + void reset(T * ptr = nullptr) + { + cleanup(); + pointer = ptr; + control = ptr != nullptr ? new shared_control_block() : nullptr; + assign_enable_shared_from_this(ptr); + } + + /** + * @brief Exchanges the stored pointer values and the ownerships of *this and r. Reference counts, if any, are not + * adjusted. + * + * @param other The shared_ptr to swap with. + */ + void swap(shared_ptr & other) + { + std::swap(pointer, other.pointer); + std::swap(control, other.control); + } + + /** + * @brief Dereference operator. If get() is a null pointer, the behavior is undefined. + * + * @return Returns the object owned by *this, equivalent to *get(). + */ + [[nodiscard]] auto operator*() const -> T & + { + return *pointer; + } + + /** + * @brief Member access operator. + * + * @return Returns a pointer to the object owned by *this, i.e. get(). + */ + [[nodiscard]] auto operator->() const -> T * + { + return pointer; + } + + /** + * @brief Returns a pointer to the managed object or nullptr if no object is owned. + * + * @return Pointer to the managed object or nullptr if no object is owned. + */ + [[nodiscard]] auto get() const -> T * + { + return pointer; + } + + /** + * @brief Returns the number of different shared_ptr instances (*this included) managing the current object. If + * there is no managed object, ​0​ is returned. + * + * @note Common use cases include comparison with ​0​. If use_count returns zero, the shared pointer is empty + * and manages no objects (whether or not its stored pointer is nullptr). Comparison with 1. If use_count returns 1, + * there are no other owners. + * + * @return The number of Shared_pointer instances managing the current object or ​0​ if there is no managed + * object. + */ + [[nodiscard]] auto use_count() const -> std::size_t + { + if (control != nullptr) + { + return control->shared_count; + } + + return 0; + } + + /** + * @brief Checks whether *this owns an object, i.e. whether get() != nullptr. + * + * @return true if *this owns an object, false otherwise. + */ + [[nodiscard]] explicit operator bool() const + { + return pointer != nullptr; + } + + /** + * @brief Compare shared_ptr with nullptr. + */ + [[nodiscard]] auto operator==(std::nullptr_t) const -> bool + { + return pointer == nullptr; + } + + /** + * @brief Compare nullptr with shared_ptr. + */ + [[nodiscard]] friend auto operator==(std::nullptr_t, shared_ptr const & ptr) -> bool + { + return ptr.pointer == nullptr; + } + + private: + /** + * @brief If the candidate type inherits from enable_shared_from_this, assigns the internal weak pointer to this + * shared_ptr. This weak_ptr is used to implement shared_from_this functionality for the candidate type. If the + * candidate type does not inherit from enable_shared_from_this, this function does nothing. + * + * @tparam U The candidate type to check for enable_shared_from_this inheritance. + * @param candidate The candidate object to assign the internal weak pointer for. + */ + template + auto assign_enable_shared_from_this(U * candidate) -> void + { + if constexpr (requires(U * p, shared_ptr const & sp) { p->internal_assign_ptr(sp); }) + { + if (candidate != nullptr) + { + candidate->internal_assign_ptr(*this); + } + } + } + + /** + * @brief Releases ownership and deletes the object if this was the last reference to the owned managed object. + */ + auto cleanup() -> void + { + if (control != nullptr) + { + if (--(control->shared_count) == 0) + { + delete pointer; + pointer = nullptr; + + if (control->weak_count == 0) + { + delete control; + } + } + } + } + + T * pointer; ///< The managed object. + shared_control_block * control; ///< Shared control block. + }; + + /** + * @brief Specializes the std::swap algorithm for stl::unique_ptr. Swaps the contents of lhs and rhs. Calls + * lhs.swap(rhs). + * + * @tparam T Type of the managed object. + * @param lhs, rhs Smart pointers whose contents to swap. + */ + template + auto swap(shared_ptr & lhs, shared_ptr & rhs) -> void + { + lhs.swap(rhs); + } + + /** + * @brief Constructs an object of type T and wraps it in a shared_ptr. Constructs a non-array type T. The + * arguments args are passed to the constructor of T. This overload participates in overload resolution only if T is + * not an array type. The function is equivalent to: shared_ptr(new T(std::forward(args)...)). + * + * @tparam T Type of the managed object. + * @tparam Args Argument types for T's constructor. + * @param args List of arguments with which an instance of T will be constructed. + * @returns Shared_pointer of an instance of type T. + */ + template + auto make_shared(Args &&... args) -> shared_ptr + { + return shared_ptr(new T(std::forward(args)...)); + } + + /** + * @brief Equality operator for shared_ptr. Two shared_ptr instances are equal if they point to the same object + * @tparam T, U Types of the managed objects of the shared_ptr instances being compared. + * @param lhs, rhs The shared_ptr instances to compare. + * @return true if lhs and rhs point to the same object, false otherwise. + */ + template + [[nodiscard]] auto inline operator==(shared_ptr const & lhs, shared_ptr const & rhs) -> bool + { + return lhs.get() == rhs.get(); + } + + /** + * @brief Three-way comparison operator for shared_ptr. Compares the stored pointers of lhs and rhs using operator<=>. + * @tparam T, U Types of the managed objects of the shared_ptr instances being compared. + * @param lhs, rhs The shared_ptr instances to compare. + * @return The result of comparing the stored pointers of lhs and rhs using operator<=> + */ + template + [[nodiscard]] auto inline operator<=>(shared_ptr const & lhs, shared_ptr const & rhs) + { + return lhs.get() <=> rhs.get(); + } +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/bits/unique_ptr.hpp b/libs/kstd/kstd/bits/unique_ptr.hpp new file mode 100644 index 0000000..3d803b4 --- /dev/null +++ b/libs/kstd/kstd/bits/unique_ptr.hpp @@ -0,0 +1,199 @@ +#ifndef KSTD_BITS_UNIQUE_POINTER_HPP +#define KSTD_BITS_UNIQUE_POINTER_HPP + +#include + +// IWYU pragma: private, include + +namespace kstd +{ + /** + * @brief Unique_pointer is a smart pointer that owns (is responsible for) and manages another object via a pointer + * and subsequently disposes of that object when the unique_ptr goes out of scope. + * + * @tparam T Type of the managed object. + */ + template + struct unique_ptr + { + template + friend struct unique_ptr; + + /** + * @brief Constructor. + * + * @param ptr A pointer to an object to manage (default is nullptr). + */ + explicit unique_ptr(T * ptr = nullptr) + : pointer(ptr) + { + // Nothing to do. + } + + /** + * @brief Destructor that deletes the managed object. + */ + ~unique_ptr() + { + delete pointer; + } + + /** + * @brief Deleted copy constructor to enforce unique ownership. + */ + unique_ptr(unique_ptr const &) = delete; + + template + requires(std::is_convertible_v) + unique_ptr(unique_ptr && other) noexcept + : pointer{std::exchange(other.pointer, nullptr)} + {} + + /** + * @brief Deleted copy assignment operator to enforce unique ownership. + */ + auto operator=(unique_ptr const &) -> unique_ptr & = delete; + + /** + * @brief Move constructor. + * + * @param other Unique pointer to move from. + */ + unique_ptr(unique_ptr && other) noexcept + : pointer{std::exchange(other.pointer, nullptr)} + {} + + /** + * @brief Move assignment operator. Transfers ownership from other to *this as if by calling reset(r.release()). + * + * @param other Smart pointer from which ownership will be transferred. + * @return Reference to this unique pointer. + */ + auto operator=(unique_ptr && other) noexcept -> unique_ptr & + { + if (this != &other) + { + delete pointer; + pointer = std::exchange(other.pointer, nullptr); + } + return *this; + } + + /** + * @brief Dereference operator. If get() is a null pointer, the behavior is undefined. + * + * @return Returns the object owned by *this, equivalent to *get(). + */ + auto operator*() const -> T & + { + return *pointer; + } + + /** + * @brief Member access operator. + * + * @return Returns a pointer to the object owned by *this, i.e. get(). + */ + auto operator->() const -> T * + { + return pointer; + } + + /** + * @brief Returns a pointer to the managed object or nullptr if no object is owned. + * + * @return Pointer to the managed object or nullptr if no object is owned. + */ + [[nodiscard]] auto get() const -> T * + { + return pointer; + } + + /** + * @brief Checks whether *this owns an object, i.e. whether get() != nullptr. + * + * @return true if *this owns an object, false otherwise. + */ + explicit operator bool() const noexcept + { + return pointer != nullptr; + } + + /** + * @brief Releases the ownership of the managed object, if any. + * get() returns nullptr after the call. + * The caller is responsible for cleaning up the object (e.g. by use of get_deleter()). + * + * @return Pointer to the managed object or nullptr if there was no managed object, i.e. the value which would be + * returned by get() before the call. + */ + auto release() -> T * + { + return std::exchange(pointer, nullptr); + } + + /** + * @brief Replaces the managed object. + * + * @note A test for self-reset, i.e. whether ptr points to an object already managed by *this, is not performed, + * except where provided as a compiler extension or as a debugging assert. Note that code such as + * p.reset(p.release()) does not involve self-reset, only code like p.reset(p.get()) does. + * + * @param ptr Pointer to a new object to manage (default = nullptr). + */ + auto reset(T * ptr = nullptr) -> void + { + delete std::exchange(pointer, ptr); + } + + /** + * @brief Swaps the managed objects and associated deleters of *this and another unique_ptr object other. + * + * @param other Another unique_ptr object to swap the managed object and the deleter with. + */ + auto swap(unique_ptr & other) -> void + { + using std::swap; + swap(pointer, other.pointer); + } + + /** + * @brief Defaulted three-way comparator operator. + */ + auto operator<=>(unique_ptr const & other) const = default; + + private: + T * pointer; ///< The managed pointer. + }; + + /** + * @brief Specializes the std::swap algorithm for stl::unique_ptr. Swaps the contents of lhs and rhs. Calls + * lhs.swap(rhs). + * + * @tparam T Type of the managed object. + * @param lhs, rhs Smart pointers whose contents to swap. + */ + template + auto swap(unique_ptr & lhs, unique_ptr & rhs) -> void + { + lhs.swap(rhs); + } + + /** + * @brief Constructs an object of type T and wraps it in a unique_ptr. Constructs a non-array type T. The + * arguments args are passed to the constructor of T. This overload participates in overload resolution only if T is + * not an array type. The function is equivalent to: unique_ptr(new T(std::forward(args)...)). + * + * @tparam T Type of the managed object. + * @tparam Args Argument types for T's constructor. + * @param args List of arguments with which an instance of T will be constructed. + * @returns Unique_pointer of an instance of type T. + */ + template + auto make_unique(Args &&... args) -> unique_ptr + { + return unique_ptr(new T(std::forward(args)...)); + } +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/cstring b/libs/kstd/kstd/cstring new file mode 100644 index 0000000..bd8b28d --- /dev/null +++ b/libs/kstd/kstd/cstring @@ -0,0 +1,21 @@ +#ifndef KSTD_CSTRING +#define KSTD_CSTRING + +#include + +namespace kstd::libc +{ + + extern "C" + { + 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) noexcept -> std::size_t; + } + +} // namespace kstd::libc + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/ext/bitfield_enum b/libs/kstd/kstd/ext/bitfield_enum new file mode 100644 index 0000000..80fe9d2 --- /dev/null +++ b/libs/kstd/kstd/ext/bitfield_enum @@ -0,0 +1,65 @@ +#ifndef KSTD_EXT_BITFIELD_ENUM_HPP +#define KSTD_EXT_BITFIELD_ENUM_HPP + +#include +#include +#include + +namespace kstd::ext +{ + + template + requires std::is_enum_v + struct is_bitfield_enum : std::false_type + { + }; + + //! @concept Specifies that an enum is to be used to define bits in a bitfield. + template + concept bitfield_enum = is_bitfield_enum::value; + +}; // namespace kstd::ext + +template +constexpr auto operator|(EnumType lhs, EnumType rhs) -> EnumType +{ + return std::bit_cast(std::to_underlying(lhs) | std::to_underlying(rhs)); +} + +template +constexpr auto operator|=(EnumType & lhs, EnumType rhs) -> EnumType & +{ + return lhs = lhs | rhs; +} + +template +constexpr auto operator&(EnumType lhs, EnumType rhs) -> EnumType +{ + return std::bit_cast(std::to_underlying(lhs) & std::to_underlying(rhs)); +} + +template +constexpr auto operator&=(EnumType & lhs, EnumType rhs) -> EnumType & +{ + return lhs = lhs & rhs; +} + +template +constexpr auto operator^(EnumType lhs, EnumType rhs) -> EnumType +{ + return std::bit_cast(std::to_underlying(lhs) ^ std::to_underlying(rhs)); +} + +template +constexpr auto operator^=(EnumType & lhs, EnumType rhs) -> EnumType & +{ + return lhs = lhs ^ rhs; +} + +template +constexpr auto operator~(EnumType lhs) -> EnumType +{ + return std::bit_cast(~std::to_underlying(lhs)); +} + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/flat_map b/libs/kstd/kstd/flat_map new file mode 100644 index 0000000..f12b1b5 --- /dev/null +++ b/libs/kstd/kstd/flat_map @@ -0,0 +1,365 @@ +#ifndef KSTD_FLAT_MAP_HPP +#define KSTD_FLAT_MAP_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace kstd +{ + + template, + typename KeyContainerType = kstd::vector, typename MappedContainerType = kstd::vector> + struct flat_map + { + using key_container_type = KeyContainerType; + using mapped_container_type = MappedContainerType; + using key_type = KeyType; + using mapped_type = MappedType; + using value_type = std::pair; + using key_compare = KeyCompare; + using reference = std::pair; + using const_reference = std::pair; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using iterator = bits::flat_map_iterator; + using const_iterator = + bits::flat_map_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using containers = struct + { + key_container_type keys; + mapped_container_type values; + }; + + //! Compare two object of type value_type. + struct value_compare + { + constexpr auto operator()(const_reference lhs, const_reference rhs) const -> bool + { + return lhs.first < rhs.first; + } + }; + + //! Construct an empty flat map. + constexpr flat_map() + : flat_map{key_compare{}} + {} + + //! Construct an empty flat map using the given custom comparator. + //! + //! @param comparator The comparator to use for comparing keys. + constexpr explicit flat_map(key_compare const & comparator) + : m_containers{} + , m_comparator{comparator} + {} + + //! Get a reference to the mapped value associated with the given key. + //! + //! @warning This function will panic if the key is not found. + //! @param key The key to look up. + //! @return A reference to the mapped value. + [[nodiscard]] constexpr auto at(key_type const & key) -> mapped_type & + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return *(m_containers.values.begin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get a reference to the mapped value associated with the given key. + //! + //! @warning This function will panic if the key is not found. + //! @param key The key to look up. + //! @return A const reference to the mapped value. + [[nodiscard]] constexpr auto at(key_type const & key) const -> mapped_type const & + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.cbegin(), found); + return *(m_containers.values.cbegin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get a reference to the mapped value associated with the given key. + //! + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @warning This function will panic if the key is not found. + //! @param x The key to look up. + //! @return A reference to the mapped value. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] constexpr auto at(K const & x) -> mapped_type & + { + auto found = find(x); + if (found != end()) + { + auto offset = std::distance(m_containers.keys.begin(), found.key_iterator()); + return *(m_containers.values.begin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get a reference to the mapped value associated with the given key. + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @warning This function will panic if the key is not found. + //! @param x The key to look up. + //! @return A const reference to the mapped value. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] auto at(K const & x) const -> mapped_type const & + { + auto found = find(x); + if (found != end()) + { + auto offset = std::distance(m_containers.keys.cbegin(), found.key_iterator()); + return *(m_containers.values.cbegin() + offset); + } + os::panic("[kstd::flat_map] Key not found"); + } + + //! Get an iterator to the first element. + [[nodiscard]] auto begin() noexcept -> iterator + { + return iterator{m_containers.keys.begin(), m_containers.values.begin()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto begin() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cbegin(), m_containers.values.cbegin()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto cbegin() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cbegin(), m_containers.values.cbegin()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto end() noexcept -> iterator + { + return iterator{m_containers.keys.end(), m_containers.values.end()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto end() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cend(), m_containers.values.cend()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto cend() const noexcept -> const_iterator + { + return const_iterator{m_containers.keys.cend(), m_containers.values.cend()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto rbegin() noexcept -> reverse_iterator + { + return reverse_iterator{end()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto rbegin() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cend()}; + } + + //! Get an iterator to the first element. + [[nodiscard]] auto crbegin() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cend()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto rend() noexcept -> reverse_iterator + { + return reverse_iterator{begin()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto rend() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cbegin()}; + } + + //! Get an iterator to the element past the last element. + [[nodiscard]] auto crend() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{cbegin()}; + } + + //! Check whether this flat map is empty or not + [[nodiscard]] auto empty() const noexcept -> bool + { + return m_containers.keys.empty(); + } + + //! Get the number of elements in this flat map. + [[nodiscard]] auto size() const noexcept -> size_type + { + return m_containers.keys.size(); + } + + //! Get the maximum number of elements possible in this flat map + [[nodiscard]] auto max_size() const noexcept -> size_type + { + return std::min(m_containers.keys.max_size(), m_containers.values.max_size()); + } + + //! Try to insert a new key-value pair into the map. + //! + //! @param args Arguments to use for constructing the key-value pair. + //! @return A pair of an iterator to the inserted element and a boolean indicating whether the insertion took place. + template + auto emplace(Args &&... args) -> std::pair + requires std::constructible_from + { + auto value = value_type{std::forward(args)...}; + auto found = std::ranges::lower_bound(m_containers.keys, value.first, m_comparator); + + if (found != m_containers.keys.cend() && !m_comparator(value.first, *found) && !m_comparator(*found, value.first)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return { + iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}, + false + }; + } + + auto offset = std::distance(m_containers.keys.begin(), found); + auto key_iterator = m_containers.keys.begin() + offset; + auto mapped_iterator = m_containers.values.begin() + offset; + + auto inserted_key = m_containers.keys.insert(key_iterator, std::move(value.first)); + auto inserted_mapped = m_containers.values.insert(mapped_iterator, std::move(value.second)); + + return { + iterator{inserted_key, inserted_mapped}, + true + }; + } + + //! Try to insert a element for the given key into this map. + //! + //! This function does nothing if the key is already present. + //! + //! @param key The key to insert a value for. + //! @param args The arguments to use to construct the mapped value. + template + auto try_emplace(key_type const & key, Args &&... args) -> std::pair + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(*found, key) && !m_comparator(key, *found)) + { + return {found, false}; + } + + auto offset = std::distance(m_containers.keys.cbegin(), found); + auto intersertion_point = m_containers.value.begin() + offset; + + auto inserted_key = m_containers.keys.emplace(key); + auto inserted_mapped = m_containers.values.emplace(std::forward(args)...); + + return { + iterator{inserted_key, inserted_mapped}, + true + }; + } + + //! Find an element with an equivalent key. + //! + //! @param key The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + [[nodiscard]] auto find(key_type const & key) noexcept -> iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}; + } + return end(); + } + + //! Find an element with an equivalent key. + //! + //! @param key The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + [[nodiscard]] auto find(key_type const & key) const noexcept -> const_iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, key, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(key, *found) && !m_comparator(*found, key)) + { + auto offset = std::distance(m_containers.keys.cbegin(), found); + return const_iterator{m_containers.keys.cbegin() + offset, m_containers.values.cbegin() + offset}; + } + return cend(); + } + + //! Find an element with an equivalent key. + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @param x The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] auto find(K const & x) noexcept -> iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, x, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(x, *found) && !m_comparator(*found, x)) + { + auto offset = std::distance(m_containers.keys.begin(), found); + return iterator{m_containers.keys.begin() + offset, m_containers.values.begin() + offset}; + } + return end(); + } + + //! Find an element with an equivalent key. + //! @note This overload only participates in overload resolution if the key compare type is transparent. + //! @param x The key to look up. + //! @return An iterator to the element with the equivalent key, or end() if no such element is found. + template + requires requires(K const & k, key_type const & l) { typename key_compare::is_transparent; } + [[nodiscard]] auto find(K const & x) const noexcept -> const_iterator + { + auto found = std::ranges::lower_bound(m_containers.keys, x, m_comparator); + if (found != m_containers.keys.cend() && !m_comparator(x, *found) && !m_comparator(*found, x)) + { + auto offset = std::distance(m_containers.keys.cbegin(), found); + return const_iterator{m_containers.keys.cbegin() + offset, m_containers.values.cbegin() + offset}; + } + return cend(); + } + + //! Check if the map contains the given key. + //! + //! @param key The key to check. + //! @return true iff. the key is found, false otherwise. + [[nodiscard]] constexpr auto contains(key_type const & key) const noexcept -> bool + { + return find(key) != cend(); + } + + private: + containers m_containers; + key_compare m_comparator; + }; +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/flat_map.test.cpp b/libs/kstd/kstd/flat_map.test.cpp new file mode 100644 index 0000000..2e5a47c --- /dev/null +++ b/libs/kstd/kstd/flat_map.test.cpp @@ -0,0 +1,314 @@ +#include + +#include + +#include + +#include +#include +#include + +SCENARIO("Flat Map initialization and construction", "[flat_map]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto map = kstd::flat_map{}; + + THEN("the Flat Map does not contain elements") + { + REQUIRE_FALSE(map.contains(1)); + } + } + } +} + +SCENARIO("Flat Map modifiers", "[flat_map]") +{ + GIVEN("An empty Flat Map") + { + auto map = kstd::flat_map{}; + + WHEN("emplacing a new element") + { + auto [it, inserted] = map.emplace(1, 100); + + THEN("the map contains the new element") + { + REQUIRE(inserted); + REQUIRE(map.contains(1)); + } + } + + WHEN("emplacing an existing element") + { + map.emplace(1, 100); + auto [it, inserted] = map.emplace(1, 200); + + THEN("the map does not insert the duplicate") + { + REQUIRE_FALSE(inserted); + REQUIRE(map.contains(1)); + } + } + } +} + +SCENARIO("Flat Map element access", "[flat_map]") +{ + GIVEN("A populated Flat Map") + { + auto map = kstd::flat_map{}; + map.emplace(1, 10); + map.emplace(2, 20); + map.emplace(3, 30); + + WHEN("accessing an existing element with at()") + { + auto & val = map.at(2); + + THEN("it returns a reference to the mapped value") + { + REQUIRE(val == 20); + } + + THEN("the mapped value can be modified") + { + val = 200; + REQUIRE(map.at(2) == 200); + } + } + + WHEN("accessing a non-existent element with at()") + { + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(4), kstd::tests::os_panic); + } + } + } + + GIVEN("A const populated Flat Map") + { + auto map_builder = kstd::flat_map{}; + map_builder.emplace(1, 10); + map_builder.emplace(2, 20); + auto const map = map_builder; + + WHEN("accessing an existing element with const at()") + { + auto const & val = map.at(2); + + THEN("it returns a const reference to the mapped value") + { + REQUIRE(val == 20); + } + } + + WHEN("accessing a non-existent element with const at()") + { + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(4), kstd::tests::os_panic); + } + } + } +} + +SCENARIO("Flat Map iterators", "[flat_map]") +{ + GIVEN("A populated Flat Map") + { + auto map = kstd::flat_map{}; + map.emplace(1, 10); + map.emplace(2, 20); + map.emplace(3, 30); + + WHEN("using forward iterators") + { + THEN("they navigate the elements in the correct forward order") + { + auto it = map.begin(); + REQUIRE(it != map.end()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it != map.end()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.end()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it == map.end()); + } + + THEN("const forward iterators provide correct access") + { + auto it = map.cbegin(); + REQUIRE(it != map.cend()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it != map.cend()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.cend()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it == map.cend()); + } + + THEN("assignment through the proxy modifies the mapped value") + { + auto it = map.begin(); + + *it = std::pair{1, 100}; + + REQUIRE(it->second == 100); + REQUIRE(map.at(1) == 100); + } + + THEN("structured bindings evaluate correctly") + { + auto it = map.cbegin(); + + auto [key, value] = *it; + + REQUIRE(key == 1); + REQUIRE(value == 10); + + STATIC_REQUIRE(std::is_same_v); + STATIC_REQUIRE(std::is_same_v); + } + } + + WHEN("using reverse iterators") + { + THEN("they navigate the elements in the correct reverse order") + { + auto it = map.rbegin(); + REQUIRE(it != map.rend()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it != map.rend()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.rend()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it == map.rend()); + } + + THEN("const reverse iterators provide correct access") + { + auto it = map.crbegin(); + REQUIRE(it != map.crend()); + REQUIRE((*it).first == 3); + + ++it; + REQUIRE(it != map.crend()); + REQUIRE((*it).first == 2); + + ++it; + REQUIRE(it != map.crend()); + REQUIRE((*it).first == 1); + + ++it; + REQUIRE(it == map.crend()); + } + } + } + + GIVEN("an empty Flat Map") + { + auto map = kstd::flat_map{}; + + WHEN("getting iterators") + { + THEN("begin() equals end() and cbegin() equals cend()") + { + REQUIRE(map.begin() == map.end()); + REQUIRE(map.cbegin() == map.cend()); + } + + THEN("rbegin() equals rend() and crbegin() equals crend()") + { + REQUIRE(map.rbegin() == map.rend()); + REQUIRE(map.crbegin() == map.crend()); + } + } + } +} + +SCENARIO("Flat Map heterogeneous element access", "[flat_map]") +{ + GIVEN("A populated Flat Map with a transparent comparator") + { + auto map = kstd::flat_map>{}; + map.emplace(1, 10); + map.emplace(2, 20); + map.emplace(3, 30); + + WHEN("accessing an existing element with a different key type via at()") + { + long const key = 2L; + auto & val = map.at(key); + + THEN("it returns a reference to the mapped value") + { + REQUIRE(val == 20); + } + + THEN("the mapped value can be modified") + { + val = 200; + REQUIRE(map.at(2L) == 200); + } + } + + WHEN("accessing a non-existent element with a different key type via at()") + { + long const key = 4L; + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(key), kstd::tests::os_panic); + } + } + } + + GIVEN("A const populated Flat Map with a transparent comparator") + { + auto map_builder = kstd::flat_map>{}; + map_builder.emplace(1, 10); + map_builder.emplace(2, 20); + auto const map = map_builder; + + WHEN("accessing an existing element with a different key type via const at()") + { + long const key = 2L; + auto const & val = map.at(key); + + THEN("it returns a const reference to the mapped value") + { + REQUIRE(val == 20); + } + } + + WHEN("accessing a non-existent element with a different key type via const at()") + { + long const key = 4L; + THEN("it panics") + { + REQUIRE_THROWS_AS(map.at(key), kstd::tests::os_panic); + } + } + } +} diff --git a/libs/kstd/kstd/format b/libs/kstd/kstd/format new file mode 100644 index 0000000..e04b79a --- /dev/null +++ b/libs/kstd/kstd/format @@ -0,0 +1,22 @@ +#ifndef KSTD_FORMAT_HPP +#define KSTD_FORMAT_HPP + +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/format.test.cpp b/libs/kstd/kstd/format.test.cpp new file mode 100644 index 0000000..624779a --- /dev/null +++ b/libs/kstd/kstd/format.test.cpp @@ -0,0 +1,114 @@ +#include + +#include + +#include +#include + +SCENARIO("Formatting to a new string", "[format]") +{ + GIVEN("a format string without any placeholders") + { + auto const & fmt = "This is a test"; + + WHEN("calling format with without any arguments.") + { + auto result = kstd::format(fmt); + + THEN("the result is the unmodified string") + { + REQUIRE(result == "This is a test"); + } + } + + WHEN("calling format with additional arguments") + { + auto result = kstd::format(fmt, 1, 2, 3); + + THEN("the result is the unmodified string") + { + REQUIRE(result == "This is a test"); + } + } + } + + GIVEN("a format string with placeholders") + { + auto const & fmt = "Here are some placeholders: {} {} {}"; + + WHEN("calling format with the same number of arguments as there are placeholders") + { + auto result = kstd::format(fmt, 1, true, 'a'); + + THEN("the result is the formatted string") + { + REQUIRE(result == "Here are some placeholders: 1 true a"); + } + } + + WHEN("calling format with too many arguments") + { + auto result = kstd::format(fmt, 2, false, 'b', 4, 5, 6); + + THEN("the result is the formatted string") + { + REQUIRE(result == "Here are some placeholders: 2 false b"); + } + } + } +} + +SCENARIO("Formatting to an output iterator", "[format]") +{ + auto buffer = std::ostringstream{}; + + GIVEN("a format string without any placeholders") + { + auto const & fmt = "This is a test"; + + WHEN("calling format with without any arguments.") + { + kstd::format_to(std::ostream_iterator{buffer}, fmt); + + THEN("the unmodified string is written to the iterator") + { + REQUIRE(buffer.str() == "This is a test"); + } + } + + WHEN("calling format with additional arguments") + { + kstd::format_to(std::ostream_iterator{buffer}, fmt, 1, 2, 3); + + THEN("the unmodified string is written to the iterator") + { + REQUIRE(buffer.str() == "This is a test"); + } + } + } + + GIVEN("a format string with placeholders") + { + auto const & fmt = "Here are some placeholders: {} {} {}"; + + WHEN("calling format with the same number of arguments as there are placeholders") + { + kstd::format_to(std::ostream_iterator{buffer}, fmt, 1, true, -100); + + THEN("the formatted string is written to the iterator") + { + REQUIRE(buffer.str() == "Here are some placeholders: 1 true -100"); + } + } + + WHEN("calling format with too many arguments") + { + kstd::format_to(std::ostream_iterator{buffer}, fmt, 2, false, -200, 4, 5, 6); + + THEN("the formatted string is written to the iterator") + { + REQUIRE(buffer.str() == "Here are some placeholders: 2 false -200"); + } + } + } +} \ No newline at end of file diff --git a/libs/kstd/kstd/libc/stdlib.cpp b/libs/kstd/kstd/libc/stdlib.cpp new file mode 100644 index 0000000..7ed051f --- /dev/null +++ b/libs/kstd/kstd/libc/stdlib.cpp @@ -0,0 +1,19 @@ +#include + +namespace kstd::libc +{ + + extern "C" + { + [[noreturn]] auto abort() -> void + { + kstd::os::abort(); + } + + [[noreturn, gnu::weak]] auto free(void *) -> void + { + kstd::os::panic("Tried to call free."); + } + } + +} // namespace kstd::libc \ No newline at end of file diff --git a/libs/kstd/kstd/libc/string.cpp b/libs/kstd/kstd/libc/string.cpp new file mode 100644 index 0000000..b7cdb82 --- /dev/null +++ b/libs/kstd/kstd/libc/string.cpp @@ -0,0 +1,78 @@ +#include + +#include +#include +#include +#include +#include + +namespace kstd::libc +{ + + 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}; + + for (std::size_t i = 0; i < size; ++i) + { + dest_span[i] = src_span[i]; + } + + return dest; + } + + 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}; + + for (std::size_t i = 0; i < size; ++i) + { + dest_span[i] = byte_value; + } + + return dest; + } + + 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}; + auto mismatched = std::ranges::mismatch(left_span, right_span); + + if (mismatched.in1 == left_span.end()) + { + return 0; + } + + return std::bit_cast(*mismatched.in1) - std::bit_cast(*mismatched.in2); + } + + 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}; + if (dest < src) + { + for (std::size_t i = 0; i < size; ++i) + { + dest_span[i] = src_span[i]; + } + } + else + { + for (std::size_t i = size; i > 0; --i) + { + dest_span[i - 1] = src_span[i - 1]; + } + } + return dest; + } + + auto strlen(char const * string) noexcept -> std::size_t + { + return std::distance(string, std::ranges::find(string, nullptr, '\0')); + } + +} // namespace kstd::libc \ No newline at end of file diff --git a/libs/kstd/kstd/memory b/libs/kstd/kstd/memory new file mode 100644 index 0000000..f108c6d --- /dev/null +++ b/libs/kstd/kstd/memory @@ -0,0 +1,8 @@ +#ifndef KSTD_MEMORY_HPP +#define KSTD_MEMORY_HPP + +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/mutex b/libs/kstd/kstd/mutex new file mode 100644 index 0000000..b2a31aa --- /dev/null +++ b/libs/kstd/kstd/mutex @@ -0,0 +1,101 @@ +#ifndef KSTD_MUTEX_HPP +#define KSTD_MUTEX_HPP + +#include + +namespace kstd +{ + + //! A non-recursive mutex. + struct mutex + { + mutex(); + ~mutex(); + + mutex(mutex const &) = delete; + mutex(mutex &&) = delete; + + auto operator=(mutex const &) -> mutex & = delete; + auto operator=(mutex &&) -> mutex & = delete; + + //! Lock the mutex. + //! + //! @note This function blocks for as long as the mutex is not available. + auto lock() -> void; + + //! Try to lock the mutex. + //! + //! @note This function never blocks. + //! @return @p true iff. the mutex was successfully locked, @p false otherwise. + auto try_lock() -> bool; + + //! Unlock the mutex. + //! + //! @note The behavior is undefined if the mutex is not currently held by the thread unlocking it. + auto unlock() -> void; + + private: + std::atomic_flag m_locked{}; + }; + + //! A tag type to specify that a given @p lockable wrapper should adopt ownership of the @p lockable. + struct adopt_lock_t + { + explicit adopt_lock_t() = default; + } constexpr inline adopt_lock{}; + + //! A tag type to specify that a given @p lockable wrapper should defer locking the @p lockable. + struct defer_lock_t + { + explicit defer_lock_t() = default; + } constexpr inline defer_lock{}; + + //! A tag type to specify that a given @p lockable wrapper should attempt to lock the @p lockable. + struct try_to_lock_t + { + explicit try_to_lock_t() = default; + } constexpr inline try_to_lock{}; + + //! An RAII wrapper for a single @p lockable like a kstd::mutex. + template + struct lock_guard + { + using mutex_type = MutexType; + + //! Construct a new lock_guard and immediately lock the given mutex. + //! + //! @note This function will block until the mutex was successfully locked. + //! @param mutex The mutex to lock. + lock_guard(MutexType & mutex) noexcept + : m_mutex(mutex) + { + m_mutex.lock(); + } + + //! Construct a new lock_guard and take ownership of the given mutex. + //! + //! @note The behavior is undefined if the mutex is not owned by the current thread. + //! @param mutex The mutex to take ownership of. + lock_guard(MutexType & mutex, adopt_lock_t) noexcept + : m_mutex(mutex) + {} + + //! Destroy this lock_guard and release the owned mutex. + ~lock_guard() + { + m_mutex.unlock(); + } + + lock_guard(lock_guard const &) noexcept = delete; + lock_guard(lock_guard &&) noexcept = delete; + + auto operator=(lock_guard const &) noexcept -> lock_guard & = delete; + auto operator=(lock_guard &&) noexcept -> lock_guard & = delete; + + private: + mutex_type & m_mutex; + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/mutex.cpp b/libs/kstd/kstd/mutex.cpp new file mode 100644 index 0000000..7387657 --- /dev/null +++ b/libs/kstd/kstd/mutex.cpp @@ -0,0 +1,38 @@ +#include + +#include + +#include + +namespace kstd +{ + + mutex::mutex() = default; + + mutex::~mutex() + { + if (m_locked.test(std::memory_order_relaxed)) + { + os::panic("[KSTD] Tried to destroy a locked mutex."); + } + } + + auto mutex::lock() -> void + { + while (!try_lock()) + { + asm volatile("nop"); + } + } + + auto mutex::try_lock() -> bool + { + return !m_locked.test_and_set(std::memory_order_acquire); + } + + auto mutex::unlock() -> void + { + m_locked.clear(std::memory_order_release); + } + +} // namespace kstd diff --git a/libs/kstd/kstd/os/error.cpp b/libs/kstd/kstd/os/error.cpp new file mode 100644 index 0000000..f969cb5 --- /dev/null +++ b/libs/kstd/kstd/os/error.cpp @@ -0,0 +1,12 @@ +#include + +namespace kstd::os +{ + + [[gnu::weak, noreturn]] + auto abort() -> void + { + os::panic("Abort called."); + } + +} // namespace kstd::os \ No newline at end of file diff --git a/libs/kstd/kstd/os/error.hpp b/libs/kstd/kstd/os/error.hpp new file mode 100644 index 0000000..9d43fb1 --- /dev/null +++ b/libs/kstd/kstd/os/error.hpp @@ -0,0 +1,34 @@ +#ifndef KSTD_OS_ERROR_HPP +#define KSTD_OS_ERROR_HPP + +#include +#include + +namespace kstd::os +{ + /** + * @brief Handle an unrecoverable library error. + * + * The operating system kernel may choose to implement this function in order to try to perform additional cleanup + * before terminating execution. If the kernel does not implement this function, the default implementation will be + * chosen. This default implementation doest nothing. + */ + [[noreturn]] + auto abort() -> void; + + /** + * @brief Terminate execution of the operating system. + * + * The operating system must implement this function. This function must terminate the execution of the operating + * system kernel as is. It may choose to restart the kernel or to halt execution entirely. The implementation must + * guarantee that execution never return from this function. + * + * @param message A message describing the reason for termination of execution. + * @param where The source code location at which the panic was triggered. In general, no argument shall be provided + * for this parameter, thus implicitly capturing the location at which the call originates. + */ + [[noreturn]] + auto panic(std::string_view message, std::source_location where = std::source_location::current()) -> void; +} // namespace kstd::os + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/os/print.hpp b/libs/kstd/kstd/os/print.hpp new file mode 100644 index 0000000..36cb43d --- /dev/null +++ b/libs/kstd/kstd/os/print.hpp @@ -0,0 +1,14 @@ +#ifndef KSTD_OS_PRINT_HPP +#define KSTD_OS_PRINT_HPP + +#include +#include + +#include + +namespace kstd::os +{ + auto vprint(print_sink sink, std::string_view format, kstd::format_args args) -> void; +} // namespace kstd::os + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/print b/libs/kstd/kstd/print new file mode 100644 index 0000000..1033f72 --- /dev/null +++ b/libs/kstd/kstd/print @@ -0,0 +1,69 @@ +#ifndef KSTD_PRINT +#define KSTD_PRINT + +#include // IWYU pragma: export +#include +#include + +#include + +namespace kstd +{ + + //! @qualifier kernel-defined + //! Format the given string using the given arguments and print it to the currently active output device. + //! + //! @param format The format string + //! @param args The arguments to use to place in the format string's placeholders. + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + auto print(kstd::format_string...> format, Args const &... args) -> void + { + auto const arg_store = kstd::make_format_args(args...); + os::vprint(print_sink::stdout, format.str_view, arg_store.args); + } + + //! @qualifier kernel-defined + //! Format the given error string using the given arguments and print it to the currently active output device. + //! + //! @param format The format string + //! @param args The arguments to use to place in the format string's placeholders. + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + auto print(print_sink sink, kstd::format_string...> format, Args &&... args) -> void + { + auto const arg_store = kstd::make_format_args(args...); + os::vprint(sink, format.str_view, arg_store.args); + } + + //! @qualifier kernel-defined + //! Format the given string using the given arguments and print it, including a newline, to the currently active + //! output device. + //! + //! @param format The format string + //! @param args The arguments to use to place in the format string's placeholders. + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + auto println(kstd::format_string...> format, Args &&... args) -> void + { + print(print_sink::stdout, format, std::forward(args)...); + print(print_sink::stdout, "\n"); + } + + //! @qualifier kernel-defined + //! Format the given error string using the given arguments and print it, including a newline, to the currently active + //! output device. + //! + //! @param format The format string + //! @param args The arguments + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + auto println(print_sink sink, kstd::format_string...> format, Args &&... args) -> void + { + print(sink, format, std::forward(args)...); + print(sink, "\n"); + } + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/ranges b/libs/kstd/kstd/ranges new file mode 100644 index 0000000..78c3adb --- /dev/null +++ b/libs/kstd/kstd/ranges @@ -0,0 +1,21 @@ +#ifndef KSTD_RANGES +#define KSTD_RANGES + +#include // IWYU pragma: export + +namespace kstd +{ +#if __glibcxx_ranges_to_container + using std::from_range; + using std::from_range_t; +#else + struct from_range_t + { + explicit from_range_t() = default; + }; + constexpr auto inline from_range = from_range_t{}; +#endif + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/stack b/libs/kstd/kstd/stack new file mode 100644 index 0000000..02e44ea --- /dev/null +++ b/libs/kstd/kstd/stack @@ -0,0 +1,192 @@ +#ifndef KSTD_STACK_HPP +#define KSTD_STACK_HPP + +#include + +#include +#include + +namespace kstd +{ + /** + * @brief Custom stack implementation mirroring the std::stack to allow for the usage of STL functionality with our + * custom memory management. + * + * @tparam T Element the stack instance should contain. + * @tparam Container Actual underlying container that should be wrapped to provide stack functionality. Requires + * access to pop_back(), push_back(), back(), size(), empty() and emplace_back() + */ + template> + struct stack + { + using container_type = Container; ///< Type of the underlying container used to implement stack-like interface. + using value_type = Container::value_type; ///< Type of the elements contained in the underlying container. + using size_type = Container::size_type; ///< Type of the size in the underlying container. + using reference = Container::reference; ///< Type of reference to the elements. + using const_reference = Container::const_reference; ///< Type of constant reference to the elements. + + /** + * @brief Default Constructor. + */ + stack() = default; + + stack(stack const &) = delete; + stack(stack &&) = delete; + auto operator=(stack const &) -> stack & = delete; + auto operator=(stack &&) -> stack & = delete; + /** + * @brief Constructs data with the given amount of elements containing the given value or alternatively the default + * constructed value. + * + * @param n Amount of elements we want to create and set the given value for. + * @param initial Inital value of all elements in the underlying data array. + */ + explicit stack(size_type n, value_type initial = value_type{}) + : _container(n, initial) + { + // Nothing to do. + } + + /** + * @brief Constructs data by copying all element from the given exclusive range. + * + * @tparam InputIterator Template that should have atleast input iterator characteristics. + * @param first Input iterator to the first element in the range we want to copy from. + * @param last Input iterator to one past the last element in the range we want to copy from. + */ + template + explicit stack(InputIterator first, InputIterator last) + : _container(first, last) + { + // Nothing to do. + } + + /** + * @brief Construct data by copying all elements from the initializer list. + * + * @param elements List we want to copy all elements from. + */ + explicit stack(std::initializer_list elements) + : _container(elements) + { + // Nothing to do. + } + + /** + * @brief Copy constructor. + * + * @note Allocates underlying data container with the same capacity as stack we are copying from and copies all + * elements from it. + * + * @param other Other instance of stack we want to copy the data from. + */ + stack(stack const & other) + : _container(other) + { + // Nothing to do. + } + + /** + * @brief Destructor. + */ + ~stack() = default; + + /** + * @brief Amount of elements currently contained in this vector, will fill up until we have reached the capacity. If + * that is the case the capacity is increased automatically. + * + * @return Current amount of elements. + */ + auto size() const -> size_type + { + return _container.size(); + } + + /** + * @brief Returns a reference to the last element in the container. Calling back on an empty container causes + * undefined behavior. + * + * @return Reference to the last element. + */ + auto top() -> reference + { + return _container.back(); + } + + /** + * @brief Returns a reference to the last element in the container. Calling back on an empty container causes + * undefined behavior. + * + * @return Reference to the last element. + */ + auto top() const -> const_reference + { + return _container.back(); + } + + /** + * @brief Appends the given element value to the end of the container. The element is assigned through the + * assignment operator of the template type. The value is forwarded to the constructor as + * std::forward(value), meaning it is either moved (rvalue) or copied (lvalue). + * + * @note If after the operation the new size() is greater than old capacity() a reallocation takes place, + * in which case all iterators (including the end() iterator) and all references to the elements are invalidated. + * Otherwise only the end() iterator is invalidated. Uses a forward reference for the actual value passed, which + * allows the template method to be used by both lvalue and rvalues and compile a different implementation. + * + * @param value The value of the element to append. + */ + template + auto push(U && value) -> void + { + _container.push_back(std::forward(value)); + } + + /** + * @brief Appends a new element to the end of the container. The element is constructed through a constructor of the + * template type. The arguments args... are forwarded to the constructor as std::forward(args).... + * + * If after the operation the new size() is greater than old capacity() a reallocation takes place, in which case + * all iterators (including the end() iterator) and all references to the elements are invalidated. Otherwise only + * the end() iterator is invalidated. Uses a forward reference for the actual value passed, which + * allows the template method to be used by both lvalue and rvalues and compile a different implementation. + * + * @tparam Args + * @param args Arguments to forward to the constructor of the element + * @return value_type& + */ + template + auto emplace(Args &&... args) -> reference + { + _container.emplace_back(std::forward(args)...); + } + + /** + * @brief Removes the last element of the container. + * + * @note Calling pop_back on an empty container results in halting the + * further execution. Iterators and references to the last element are invalidated. The end() + * iterator is also invalidated. + */ + auto pop() -> void + { + _container.pop_back(); + } + + /** + * @brief Whether there are currently any items this container or not. + * + * @return True if there are no elements, false if there are. + */ + auto empty() const -> bool + { + return _container.empty(); + } + + private: + container_type _container = {}; ///< Underlying container used by the stack to actually save the data. + }; + +} // namespace kstd + +#endif diff --git a/libs/kstd/kstd/string b/libs/kstd/kstd/string new file mode 100644 index 0000000..e228a04 --- /dev/null +++ b/libs/kstd/kstd/string @@ -0,0 +1,380 @@ +#ifndef KSTD_STRING_HPP +#define KSTD_STRING_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace kstd +{ + /** + * @brief A simple string implementation that owns its data and provides basic operations. + */ + struct string + { + //! The type of the characters contained in this string. + using value_type = char; + //! The type of the underlying storage used by this string. + using storage_type = kstd::vector; + //! The type of all sizes used in and with this string. + using size_type = std::size_t; + //! The type of the difference between two iterators. + using difference_type = std::ptrdiff_t; + //! The type of references to single values in this string. + using reference = value_type &; + //! The type of references to constant single values in this string. + using const_reference = value_type const &; + //! The type of pointers to single values in this string. + using pointer = value_type *; + //! The type of pointers to constant single values in this string. + using const_pointer = value_type const *; + //! The type of iterators into this string. + using iterator = pointer; + //! The type of constant iterators into this string. + using const_iterator = const_pointer; + + /** + * @brief Constructs an empty null-terminated string. + */ + string() + : m_storage{value_type{'\0'}} + {} + + /** + * @brief Constructs a string from a string view by copying the characters into owned storage. + * @param view The string view to copy the characters from. + */ + string(std::string_view view) + : string() + { + append(view); + } + + /** + * @brief Constructs a string from a null-terminated C-style string by copying the characters into owned storage. + * @param c_str The null-terminated C-style string to copy. + */ + string(char const * c_str) + : string() + { + if (c_str != nullptr) + { + append(std::string_view{c_str}); + } + } + + /** + * @brief Constructs a string containing a single character. + * @param c The character to copy. + */ + string(value_type c) + : string() + { + push_back(c); + } + + /** + * @brief Constructs a string by copying another string. + * @param other The string to copy. + */ + constexpr string(string const & other) + : m_storage{other.m_storage} + {} + + /** + * @brief Destructs the string. + */ + constexpr ~string() = default; + + /** + * @brief Assigns the value of another string to this string. + * @param other The string to assign from. + * @return A reference to this string. + */ + constexpr auto operator=(string const & other) -> string & = default; + + /** + * @brief Returns the number of characters in this string, not including the null terminator. + */ + [[nodiscard]] constexpr auto size() const noexcept -> size_type + { + return m_storage.empty() ? 0 : m_storage.size() - 1; + } + + /** + * @brief Checks if this string is empty, not including the null terminator. + */ + [[nodiscard]] constexpr auto empty() const noexcept -> bool + { + return size() == 0; + } + + /** + * @brief Clears the content of the string, resulting in an empty string. + * The string remains null-terminated after this operation. + */ + constexpr auto clear() -> void + { + m_storage.clear(); + m_storage.push_back(value_type{'\0'}); + } + + //! Get a pointer to the underlying storage of the string + [[nodiscard]] constexpr auto data() noexcept -> pointer + { + return m_storage.data(); + } + + //! Get a const pointer to the underlying storage of the string + [[nodiscard]] constexpr auto data() const noexcept -> const_pointer + { + return m_storage.data(); + } + + //! Get a const pointer to the underlying storage of the string + [[nodiscard]] constexpr auto c_str() const noexcept -> const_pointer + { + return data(); + } + + //! Get an iterator to the beginning of the string + [[nodiscard]] constexpr auto begin() noexcept -> iterator + { + return data(); + } + + //! Get an const iterator to the beginning of the string + [[nodiscard]] constexpr auto begin() const noexcept -> const_iterator + { + return data(); + } + + //! Get an const iterator to the beginning of the string + [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator + { + return begin(); + } + + //! Get an iterator to the end of the string + [[nodiscard]] constexpr auto end() noexcept -> iterator + { + return data() + size(); + } + + //! Get an const iterator to the end of the string + [[nodiscard]] constexpr auto end() const noexcept -> const_iterator + { + return data() + size(); + } + + //! Get an const iterator to the end of the string + [[nodiscard]] constexpr auto cend() const noexcept -> const_iterator + { + return end(); + } + + //! Get a reference to the first character of the string + [[nodiscard]] constexpr auto front() -> reference + { + return m_storage.front(); + } + + //! Get a const reference to the first character of the string + [[nodiscard]] constexpr auto front() const -> const_reference + { + return m_storage.front(); + } + + //! Get a reference to the last character of the string + [[nodiscard]] constexpr auto back() -> reference + { + return m_storage[size() - 1]; + } + + //! Get a const reference to the last character of the string + [[nodiscard]] constexpr auto back() const -> const_reference + { + return m_storage[size() - 1]; + } + + /** + * @brief Appends a character to the end of the string. + * @param ch The character to append. + */ + constexpr auto push_back(value_type ch) -> void + { + m_storage.back() = ch; + m_storage.push_back(value_type{'\0'}); + } + + /** + * @brief Appends a string view to the end of the string by copying the characters into owned storage. + * @param view The string view to append. + * @return A reference to this string. + */ + constexpr auto append(std::string_view view) -> string & + { + if (!view.empty()) + { + std::ranges::for_each(view, [this](auto const ch) { push_back(ch); }); + } + + return *this; + } + + /** + * @brief Appends another string to the end of this string by copying the characters into owned storage. + * @param other The string to append. + * @return A reference to this string. + */ + constexpr auto append(string const & other) -> string & + { + return append(other.view()); + } + + /** + * @brief Appends another string to the end of this string by copying the characters into owned storage. + * @param other The string to append. + * @return A reference to this string. + */ + constexpr auto operator+=(string const & other) -> string & + { + return append(other); + } + + /** + * @brief Appends a character to the end of the string. + * @param ch The character to append. + * @return A reference to this string. + */ + constexpr auto operator+=(value_type ch) -> string & + { + push_back(ch); + return *this; + } + + /** + * @brief Returns a string view of this string, which is a non-owning view into the characters of this string. + */ + [[nodiscard]] constexpr auto view() const noexcept -> std::string_view + { + return std::string_view{data(), size()}; + } + + private: + //! The underlying storage of the string, which owns the characters and ensures null-termination. + storage_type m_storage{}; + }; + + /** + * @brief Concatenates a strings and a character and returns the result as a new string. + * @param lhs The string to concatenate. + * @param rhs The string to concatenate. + * @return A new string that is the result of concatenating @p lhs and @p rhs. + */ + [[nodiscard]] constexpr auto inline operator+(string const & lhs, string const & rhs) -> string + { + string result{lhs}; + result += rhs; + return result; + } + + /** + * @brief Converts an unsigned integer to a string by converting each digit to the corresponding character and + * concatenating them. + * @tparam N The type of the unsigned integer to convert. + * @param value The unsigned integer to convert. + * @return A string representation of the given unsigned integer. + */ + template + requires std::unsigned_integral + [[nodiscard]] constexpr auto inline to_string(N value) -> string + { + if (value == 0) + { + return "0"; + } + + string result; + + while (value > 0) + { + char const digit = '0' + (value % 10); + result.push_back(digit); + value /= 10; + } + + std::reverse(result.begin(), result.end()); + return result; + } + + [[nodiscard]] constexpr auto inline operator==(string const & lhs, string const & rhs) -> bool + { + return lhs.view() == rhs.view(); + } + + [[nodiscard]] constexpr auto inline operator!=(string const & lhs, string const & rhs) -> bool + { + return !(lhs == rhs); + } + + [[nodiscard]] constexpr auto inline operator==(string const & lhs, std::string_view rhs) -> bool + { + return lhs.view() == rhs; + } + + [[nodiscard]] constexpr auto inline operator!=(string const & lhs, std::string_view rhs) -> bool + { + return !(lhs == rhs); + } + + [[nodiscard]] constexpr auto inline operator==(std::string_view lhs, string const & rhs) -> bool + { + return lhs == rhs.view(); + } + + [[nodiscard]] constexpr auto inline operator!=(std::string_view lhs, string const & rhs) -> bool + { + return !(lhs == rhs); + } + + [[nodiscard]] constexpr auto inline operator==(string const & lhs, char const * rhs) -> bool + { + return lhs.view() == std::string_view{rhs}; + } + + [[nodiscard]] constexpr auto inline operator!=(string const & lhs, char const * rhs) -> bool + { + return !(lhs == rhs); + } + + [[nodiscard]] constexpr auto inline operator==(char const * lhs, string const & rhs) -> bool + { + return std::string_view{lhs} == rhs.view(); + } + + [[nodiscard]] constexpr auto inline operator!=(char const * lhs, string const & rhs) -> bool + { + return !(lhs == rhs); + } + + template<> + struct formatter : formatter + { + auto format(string const & str, format_context & context) const -> void + { + formatter::format(str.view(), context); + } + }; + +} // namespace kstd + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/string.test.cpp b/libs/kstd/kstd/string.test.cpp new file mode 100644 index 0000000..53d7c9a --- /dev/null +++ b/libs/kstd/kstd/string.test.cpp @@ -0,0 +1,445 @@ +#include + +#include + +#include +#include +#include + +SCENARIO("String initialization and construction", "[string]") +{ + GIVEN("Nothing") + { + WHEN("constructing an empty string") + { + auto str = kstd::string{}; + + THEN("the size is zero and therefore the string is empty") + { + REQUIRE(str.empty()); + REQUIRE(str.size() == 0); + } + + THEN("the string is empty") + { + REQUIRE(str.view() == std::string_view{}); + } + } + } + + GIVEN("A string view") + { + auto view = std::string_view{"Blub Blub"}; + + WHEN("constructing a string from string_view") + { + auto str = kstd::string{view}; + + THEN("the string is not empty and has the same size as the view") + { + REQUIRE(!str.empty()); + REQUIRE(str.size() == view.size()); + } + + THEN("the string contains the same characters as the view") + { + REQUIRE(str.view() == view); + } + } + } + + GIVEN("A C-style string") + { + auto c_str = "Blub Blub"; + + WHEN("constructing a string from the C-style string") + { + auto str = kstd::string{c_str}; + + THEN("the string is not empty and has the same size as the C-style string") + { + REQUIRE(!str.empty()); + REQUIRE(str.size() == std::strlen(c_str)); + } + + THEN("the string contains the same characters as the C-style string") + { + REQUIRE(str.view() == c_str); + } + } + } + + GIVEN("A character") + { + auto ch = 'x'; + + WHEN("constructing a string from the character") + { + auto str = kstd::string{ch}; + + THEN("the string is not empty and has size 1") + { + REQUIRE(!str.empty()); + REQUIRE(str.size() == 1); + } + + THEN("the string contains the same character as the given character") + { + REQUIRE(str.view() == std::string_view{&ch, 1}); + } + } + } + + GIVEN("Another string") + { + auto other = kstd::string{"Blub Blub"}; + + WHEN("copy constructing a new string") + { + auto str = kstd::string{other}; + + THEN("the new string contains the same characters as the original") + { + REQUIRE(str.view() == other.view()); + } + } + + auto str = kstd::string{"Blub"}; + + WHEN("copy assigning another string") + { + auto other = kstd::string{"Blub Blub"}; + str = other; + + THEN("the string contains the same characters as the assigned string") + { + REQUIRE(str.view() == other.view()); + } + } + } + + GIVEN("A string") + { + auto str = kstd::string{"Hello"}; + + WHEN("copy assigning a string view") + { + auto view = std::string_view{"Hello, world!"}; + str = view; + + THEN("the string contains the same characters as the assigned view") + { + REQUIRE(str.view() == view); + } + } + } + + GIVEN("A string") + { + auto str = kstd::string{"Hello"}; + + WHEN("copy assigning a C-style string") + { + auto c_str = "Hello, world!"; + str = c_str; + + THEN("the string contains the same characters as the assigned C-style string") + { + REQUIRE(str.view() == c_str); + } + } + } +} + +SCENARIO("String concatenation", "[string]") +{ + GIVEN("Two strings") + { + auto str1 = kstd::string{"Blub"}; + auto str2 = kstd::string{" Blub"}; + + WHEN("appending the second string to the first string") + { + str1.append(str2); + + THEN("the first string contains the characters of both strings concatenated") + { + REQUIRE(str1.view() == "Blub Blub"); + } + + THEN("the size of the first string is the sum of the sizes of both strings") + { + REQUIRE(str1.size() == str2.size() + 4); + } + } + + WHEN("using operator+= to append the second string to the first string") + { + str1 += str2; + + THEN("the first string contains the characters of both strings concatenated") + { + REQUIRE(str1.view() == "Blub Blub"); + } + + THEN("the size of the first string is the sum of the sizes of both strings") + { + REQUIRE(str1.size() == str2.size() + 4); + } + } + + WHEN("using operator+ to concatenate the two strings into a new string") + { + auto str3 = str1 + str2; + + THEN("the new string contains the characters of both strings concatenated") + { + REQUIRE(str3.view() == "Blub Blub"); + } + + THEN("the size of the new string is the sum of the sizes of both strings") + { + REQUIRE(str3.size() == str1.size() + str2.size()); + } + } + } + + GIVEN("A string and a string view") + { + auto str = kstd::string{"Blub"}; + auto view = std::string_view{" Blub"}; + + WHEN("appending the string view to the string") + { + str.append(view); + + THEN("the string contains the characters of both the original string and the appended view concatenated") + { + REQUIRE(str.view() == "Blub Blub"); + } + + THEN("the size of the string is the sum of the sizes of the original string and the appended view") + { + REQUIRE(str.size() == view.size() + 4); + } + } + } + + GIVEN("A string and a character") + { + auto str = kstd::string{"Blub"}; + auto ch = '!'; + + WHEN("appending the character to the string") + { + str.push_back(ch); + + THEN("the string contains the original characters followed by the appended character") + { + REQUIRE(str.view() == "Blub!"); + } + + THEN("the size of the string is one more than the original size") + { + REQUIRE(str.size() == 5); + } + } + + WHEN("using operator+= to append the character to the string") + { + str += ch; + + THEN("the string contains the original characters followed by the appended character") + { + REQUIRE(str.view() == "Blub!"); + } + + THEN("the size of the string is one more than the original size") + { + REQUIRE(str.size() == 5); + } + } + } +} + +SCENARIO("String conversion and comparison", "[string]") +{ + GIVEN("An unsigned integer") + { + constexpr auto value1 = 12345u; + constexpr auto value2 = 0u; + + WHEN("converting the unsigned integer to a string") + { + auto str1 = kstd::to_string(value1); + auto str2 = kstd::to_string(value2); + + THEN("the string contains the decimal representation of the unsigned integer") + { + REQUIRE(str1.view() == "12345"); + REQUIRE(str2.view() == "0"); + } + } + } + + GIVEN("Two strings with the same characters") + { + auto str1 = kstd::string{"Blub Blub"}; + auto str2 = kstd::string{"Blub Blub"}; + + THEN("the strings are equal") + { + REQUIRE(str1 == str2); + } + + THEN("the strings are not unequal") + { + REQUIRE(!(str1 != str2)); + } + } + + GIVEN("A string and a string view with the same characters") + { + auto str = kstd::string{"Blub Blub"}; + auto view = std::string_view{"Blub Blub"}; + + THEN("the string and the string view are equal") + { + REQUIRE(str == view); + REQUIRE(view == str); + } + + THEN("the string and the string view are not unequal") + { + REQUIRE(!(str != view)); + REQUIRE(!(view != str)); + } + } +} + +SCENARIO("String clearing", "[string]") +{ + GIVEN("A non-empty string") + { + auto str = kstd::string{"Blub Blub"}; + + WHEN("clearing the string") + { + str.clear(); + + THEN("the string is empty and has size zero") + { + REQUIRE(str.empty()); + REQUIRE(str.size() == 0); + } + + THEN("the string contains no characters") + { + REQUIRE(str.view() == std::string_view{}); + } + } + } +} + +SCENARIO("String iteration", "[string]") +{ + GIVEN("A string") + { + auto str = kstd::string{"Blub"}; + + WHEN("iterating over the characters of the string as string_view using a range-based for loop") + { + kstd::string result; + + for (auto ch : str.view()) + { + result.push_back(ch); + } + + THEN("the iterated characters are the same as the characters in the string") + { + REQUIRE(result == str); + } + } + + WHEN("using std::ranges::for_each to iterate over the characters of the string") + { + kstd::string result; + + std::ranges::for_each(str, [&result](auto ch) { result.push_back(ch); }); + + THEN("the iterated characters are the same as the characters in the string") + { + REQUIRE(result == str); + } + } + + WHEN("using front and back to access the first and last characters of the string") + { + THEN("front returns the first character of the string") + { + REQUIRE(str.front() == 'B'); + } + + THEN("back returns the last character of the string") + { + REQUIRE(str.back() == 'b'); + } + } + } + + GIVEN("A const string") + { + auto const str = kstd::string{"Blub"}; + + WHEN("iterating over the characters of the string as string_view using a range-based for loop") + { + kstd::string result; + + for (auto ch : str.view()) + { + result.push_back(ch); + } + + THEN("the iterated characters are the same as the characters in the string") + { + REQUIRE(result == str.view()); + } + } + + WHEN("using front and back to access the first and last characters of the string") + { + THEN("front returns the first character of the string") + { + REQUIRE(str.front() == 'B'); + } + + THEN("back returns the last character of the string") + { + REQUIRE(str.back() == 'b'); + } + } + } + + GIVEN("An empty string") + { + auto str = kstd::string{}; + + WHEN("iterating over the characters of an empty string") + { + kstd::string result; + + for (auto ch : str.view()) + { + result.push_back(ch); + } + + THEN("no characters are iterated and the result is an empty string") + { + REQUIRE(result.empty()); + REQUIRE(result.size() == 0); + REQUIRE(result.view() == std::string_view{}); + } + } + } +} diff --git a/libs/kstd/kstd/test_support/os_panic.hpp b/libs/kstd/kstd/test_support/os_panic.hpp new file mode 100644 index 0000000..4396a9f --- /dev/null +++ b/libs/kstd/kstd/test_support/os_panic.hpp @@ -0,0 +1,23 @@ +#ifndef KSTD_TESTS_OS_PANIC_HPP +#define KSTD_TESTS_OS_PANIC_HPP + +#include +#include +#include + +namespace kstd::tests +{ + + struct os_panic : std::runtime_error + { + os_panic(std::string message, std::source_location location) + : std::runtime_error{message} + , location(location) + {} + + std::source_location location; + }; + +} // namespace kstd::tests + +#endif \ No newline at end of file diff --git a/libs/kstd/kstd/test_support/os_panic.test.cpp b/libs/kstd/kstd/test_support/os_panic.test.cpp new file mode 100644 index 0000000..c30411a --- /dev/null +++ b/libs/kstd/kstd/test_support/os_panic.test.cpp @@ -0,0 +1,15 @@ +#include + +#include +#include +#include + +namespace kstd::os +{ + + auto panic(std::string_view message, std::source_location location) + { + throw kstd::tests::os_panic{std::string{message}, location}; + } + +} // namespace kstd::os \ No newline at end of file diff --git a/libs/kstd/kstd/test_support/test_types.hpp b/libs/kstd/kstd/test_support/test_types.hpp new file mode 100644 index 0000000..8231fd3 --- /dev/null +++ b/libs/kstd/kstd/test_support/test_types.hpp @@ -0,0 +1,313 @@ +#ifndef KSTD_TESTS_TEST_TYPES_HPP +#define KSTD_TESTS_TEST_TYPES_HPP + +#include +#include +#include + +namespace kstd::tests +{ + + //! A type tracking copy and move operations + //! + //! This type is designed to test move and copy semantics of standard library containers implemented in kstd. + struct special_member_tracker + { + //! A value indicating that the object was moved from. + constexpr auto static moved_from_v = -1; + + constexpr special_member_tracker() + : default_constructed_count{1} + {} + + //! Construct a new move tracker with the given value, if any. + constexpr special_member_tracker(int v = 0) + : value{v} + , value_constructed_count{1} + {} + + //! Construct a new move tracker by copying an existing one. + constexpr special_member_tracker(special_member_tracker const & other) + : value{other.value} + , copy_constructed_count{1} + {} + + //! Construct a new move tracker by moving from an existing one. + constexpr special_member_tracker(special_member_tracker && other) noexcept + : value{other.value} + , move_constructed_count{1} + { + other.value = moved_from_v; + } + + //! Copy assign a new move tracker from an existing one. + constexpr auto operator=(special_member_tracker const & other) -> special_member_tracker & + { + if (this != &other) + { + value = other.value; + ++copy_assigned_count; + ++other.copied_from_count; + } + return *this; + } + + //! Move assign a new move tracker from an existing one. + //! + //! This function ensures that the moved-from state is marked. + constexpr auto operator=(special_member_tracker && other) noexcept -> special_member_tracker & + { + if (this != &other) + { + value = other.value; + ++move_assigned_count; + other.value = moved_from_v; + ++other.moved_from_count; + } + return *this; + } + + ~special_member_tracker() + { + ++destroyed_count; + } + + auto reset_counts() -> void + { + default_constructed_count = 0; + copy_constructed_count = 0; + move_constructed_count = 0; + value_constructed_count = 0; + copy_assigned_count = 0; + move_assigned_count = 0; + destroyed_count = 0; + copied_from_count = 0; + moved_from_count = 0; + } + + //! A simple value to be able to track the move-from state. + int value{}; + //! A counter to track how many times an instance of this type was default constructed. + std::size_t default_constructed_count{0}; + //! A counter to track how many times an instance of this type was copy constructed. + std::size_t copy_constructed_count{0}; + //! A counter to track how many times an instance of this type was move constructed. + std::size_t move_constructed_count{0}; + //! A counter to track how many times an instance of this type was value constructed. + std::size_t value_constructed_count{0}; + //! A counter to track how many times an instance of this type was copy assigned. + std::size_t copy_assigned_count{0}; + //! A counter to track how many times an instance of this type was move assigned. + std::size_t move_assigned_count{0}; + //! A counter to track how many times an instance of this type was destroyed. + std::size_t destroyed_count{0}; + //! A counter to track how many times an instance of this type was copied from another instance. + mutable std::size_t copied_from_count{0}; + //! A counter to track how many times an instance of this type was moved from another instance. + std::size_t moved_from_count{0}; + }; + + //! A type that is not default constructible. + //! + //! This type is designed to test default construction semantics of standard library containers implemented in kstd. + struct non_default_constructible + { + //! A simple placeholder value. + int value{}; + + //! Construct a new non-default-constructible object with the given value. + constexpr explicit non_default_constructible(int v) + : value{v} + {} + + //! Compare two non-default-constructible objects for equality. + [[nodiscard]] constexpr auto operator==(non_default_constructible const & other) const -> bool + { + return value == other.value; + } + }; + + //! An allocator that tracks the number of allocations. + //! + //! This allocator is designed to test allocation semantics of standard library containers implemented in kstd. + //! + //! @tparam T The type of the elements to be allocated. + template + struct tracking_allocator + { + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + //! A pointer to a counter that is incremented on allocation. + //! + //! A pointer is used so that multiple allocators can share the same counter. + int * allocation_count{}; + + //! Construct a new tracking allocator referencing the given counter. + tracking_allocator(int * counter) + : allocation_count{counter} + {} + + //! Construct a new tracking allocator by copying from another tracking allocator. + //! + //! This constructor is templated to allow for conversion from allocators of different types. + //! + //! @tparam U The type of the elements to be allocated by the other allocator. + template + tracking_allocator(tracking_allocator const & other) noexcept + : allocation_count{other.allocation_count} + {} + + //! Allocate memory for n elements of type T. + //! + //! @param n The number of elements to allocate. + //! @return A pointer to the allocated memory. + [[nodiscard]] auto allocate(std::size_t n) -> T * + { + if (allocation_count != nullptr) + { + ++(*allocation_count); + } + return static_cast(::operator new(n * sizeof(T))); + } + + //! Deallocate memory for n elements of type T. + //! + //! @param p A pointer to the memory to deallocate. + //! @param n The number of elements to deallocate. + auto deallocate(T * p, std::size_t n) noexcept -> void + { + ::operator delete(p, n * sizeof(T)); + } + + //! Compare two tracking allocators for equality. + //! + //! Two allocators are considered equal if they reference the same allocation counter. + //! + //! @param other The other tracking allocator to compare to. + //! @return True if the two tracking allocators are equal, false otherwise. + [[nodiscard]] auto operator==(tracking_allocator const & other) const -> bool + { + return allocation_count == other.allocation_count; + } + + //! Compare two tracking_allocators for inequality. + //! + //! @param other The other tracking_allocator to compare to. + //! @return True if the two tracking_allocators are not equal, false otherwise. + [[nodiscard]] auto operator!=(tracking_allocator const & other) const -> bool + { + return allocation_count != other.allocation_count; + } + }; + + //! An allocator that propagates copy assignment. + //! + //! This allocator is designed to test copy assignment semantics of standard library containers implemented in kstd. + //! + //! @tparam T The type of the elements to be allocated. + template + struct propagating_allocator : tracking_allocator + { + //! A flag to indicate that the allocator propagates copy assignment. + using propagate_on_container_copy_assignment = std::true_type; + + //! Construct a new propagating allocator referencing the given counter. + //! + //! @param counter A pointer to a counter that is incremented on allocation. + //! @see tracking_allocator::tracking_allocator(int*) + propagating_allocator(int * counter) + : tracking_allocator{counter} + {} + + //! Construct a new propagating allocator by copying from another propagating allocator. + //! + //! This constructor is templated to allow for conversion from allocators of different types. + //! + //! @tparam U The type of the elements to be allocated by the other allocator. + //! @see tracking_allocator::tracking_allocator(tracking_allocator const&) + template + propagating_allocator(propagating_allocator const & other) noexcept + : tracking_allocator{other.allocation_count} + {} + }; + + //! A test input iterator. + //! + //! This iterator is designed to test input iterator semantics of standard library containers implemented in kstd. + struct test_input_iterator + { + using iterator_concept = std::input_iterator_tag; + using iterator_category = std::input_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = int; + using reference = int const &; + using pointer = int const *; + + //! The current element pointed to by the iterator. + int const * current; + //! The number of elements in the range. + std::size_t count; + + explicit test_input_iterator() + : current{nullptr} + , count{0} + {} + + //! Construct a new test input iterator. + //! + //! @param current The current element pointed to by the iterator. + //! @param count The number of elements in the range. + explicit test_input_iterator(int const * current, std::size_t count) + : current{current} + , count{count} + {} + + //! Dereference the iterator to get the current element. + //! + //! @return The current element pointed to by the iterator. + [[nodiscard]] auto operator*() const -> int + { + return *current; + } + + //! Increment the iterator to point to the next element. + //! + //! @return A reference to the incremented iterator. + auto operator++() -> test_input_iterator & + { + ++current; + --count; + return *this; + } + + //! Increment the iterator to point to the next element. + auto operator++(int) -> void + { + ++*this; + } + + //! Compare two test input iterators for equality. + //! + //! @param other The other test input iterator to compare to. + //! @return True if the two test input iterators are equal, false otherwise. + [[nodiscard]] auto operator==(test_input_iterator const & other) const -> bool + { + if (current == nullptr && other.current == nullptr) + { + return true; + } + + if (current == nullptr || other.current == nullptr) + { + return count == other.count; + } + + return current == other.current && count == other.count; + } + }; + +} // namespace kstd::tests + +#endif diff --git a/libs/kstd/kstd/units b/libs/kstd/kstd/units new file mode 100644 index 0000000..df5eb37 --- /dev/null +++ b/libs/kstd/kstd/units @@ -0,0 +1,149 @@ +#ifndef KSTD_UNITS_HPP +#define KSTD_UNITS_HPP + +#include +#include +#include + +namespace kstd +{ + + //! A basic template for strongly typed units. + template + struct basic_unit + { + using value_type = ValueType; + + constexpr basic_unit() noexcept + : value{} + {} + + explicit constexpr basic_unit(value_type value) noexcept + : value{value} + {} + + explicit constexpr operator value_type() const noexcept + { + return value; + } + + constexpr auto operator+(basic_unit const & other) const noexcept -> basic_unit + { + return basic_unit{value + other.value}; + } + + constexpr auto operator+=(basic_unit const & other) noexcept -> basic_unit & + { + return *this = *this + other; + } + + constexpr auto operator-(basic_unit const & other) const noexcept -> basic_unit + { + return basic_unit{value - other.value}; + } + + constexpr auto operator-=(basic_unit const & other) noexcept -> basic_unit & + { + return *this = *this - other; + } + + constexpr auto operator*(std::integral auto factor) noexcept -> basic_unit + { + return basic_unit{value * factor}; + } + + constexpr auto operator*=(std::integral auto factor) noexcept -> basic_unit + { + return *this = *this * factor; + } + + constexpr auto operator/(std::integral auto divisor) noexcept -> basic_unit + { + return basic_unit{value / divisor}; + } + + constexpr auto operator/=(std::integral auto divisor) noexcept -> basic_unit + { + return *this = *this / divisor; + } + + constexpr auto operator/(basic_unit const & other) const noexcept + { + return value / other.value; + } + + constexpr auto operator<=>(basic_unit const & other) const noexcept -> std::strong_ordering = default; + + value_type value; + }; + + template + constexpr auto operator*(Factor factor, basic_unit const & unit) noexcept + -> basic_unit + { + return basic_unit{unit.value * factor}; + } + + namespace units + { + using bytes = basic_unit; + + constexpr auto KiB(std::size_t value) noexcept -> bytes + { + return bytes{value * 1024}; + } + + constexpr auto MiB(std::size_t value) noexcept -> bytes + { + return bytes{value * 1024 * 1024}; + } + + constexpr auto GiB(std::size_t value) noexcept -> bytes + { + return bytes{value * 1024 * 1024 * 1024}; + } + + template + constexpr auto operator+(ValueType * pointer, bytes offset) -> ValueType * + { + return pointer + offset.value; + } + + } // namespace units + + namespace units_literals + { + constexpr auto operator""_B(unsigned long long value) noexcept -> units::bytes + { + return units::bytes{value}; + } + + constexpr auto operator""_KiB(unsigned long long value) noexcept -> units::bytes + { + return units::KiB(value); + } + + constexpr auto operator""_MiB(unsigned long long value) noexcept -> units::bytes + { + return units::MiB(value); + } + + constexpr auto operator""_GiB(unsigned long long value) noexcept -> units::bytes + { + return units::GiB(value); + } + + } // namespace units_literals + + template + constexpr auto object_size(ValueType const &) -> units::bytes + { + return units::bytes{sizeof(ValueType)}; + } + + template + constexpr auto type_size = units::bytes{sizeof(T)}; + +} // namespace kstd + +#endif diff --git a/libs/kstd/kstd/vector b/libs/kstd/kstd/vector new file mode 100644 index 0000000..c714957 --- /dev/null +++ b/libs/kstd/kstd/vector @@ -0,0 +1,1154 @@ +#ifndef KSTD_VECTOR_HPP +#define KSTD_VECTOR_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace kstd +{ + //! A resizable, contiguous container. + //! + //! @tparam ValueType The type of values contained in this vector. + //! @tparam Allocator The type of allocator used for memory management by this container. + template> + struct vector + { + //! The type of the elements contained in this vector. + using value_type = ValueType; + //! The allocator used by this vector for memory management. + using allocator_type = Allocator; + //! The type of all sizes used in and with this vector. + using size_type = std::size_t; + //! The type of the difference between two iterators. + using difference_type = std::ptrdiff_t; + //! The type of references to elements in this vector. + using reference = value_type &; + //! The type of references to constant elements in this vector. + using const_reference = value_type const &; + //! The type of pointers to elements in this vector. + using pointer = std::allocator_traits::pointer; + //! The type of pointers to constant elements in this vector. + using const_pointer = std::allocator_traits::const_pointer; + //! The type of iterators into this container. + using iterator = pointer; + //! The type of constant iterators into this container. + using const_iterator = const_pointer; + //! The type of reverse iterators into this container. + using reverse_iterator = std::reverse_iterator; + //! The type of constant reverse iterators into this container. + using const_reverse_iterator = std::reverse_iterator; + + //! Construct a new, empty vector. + constexpr vector() noexcept(std::is_nothrow_default_constructible_v) + : vector(allocator_type{}) + {} + + //! Construct a new, empty vector with a given allocator. + //! + //! @param allocator The allocator to use in the vector. + explicit constexpr vector(allocator_type const & allocator) noexcept( + std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} + , m_size{0} + , m_capacity{0} + , m_data{allocate_n(m_capacity)} + {} + + //! Construct a new vector and fill it with the given number of default constructed elements. + //! + //! @param count The number of element to create the vector with. + //! @param allocator The allocator to use in the vector. + explicit constexpr vector(size_type count, allocator_type const & allocator = allocator_type{}) noexcept( + std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} + , m_size{count} + , m_capacity{count} + , m_data{allocate_n(m_capacity)} + { + for (auto i = 0uz; i < count; ++i) + { + std::allocator_traits::construct(m_allocator, m_data + i); + } + } + + //! Construct a new vector and fill it with the given number of copy constructed elements. + //! + //! @param count The number of element to create the vector with. + //! @param value The value to copy for each element + //! @param allocator The allocator to use in the vector. + constexpr vector(size_type count, const_reference value, + allocator_type const & allocator = + allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} + , m_size{count} + , m_capacity{m_size} + , m_data{allocate_n(m_capacity)} + { + for (auto i = 0uz; i < count; ++i) + { + std::allocator_traits::construct(m_allocator, m_data + i, value); + } + } + + //! Construct a new vector and initialize it's content by copying all elements in the given range. + //! + //! @tparam ForwardIterator An iterator type used to describe the source range. + //! @param first The start of the source range. + //! @param last The end of the source range. + template + constexpr vector(ForwardIterator first, ForwardIterator last, + allocator_type const & allocator = + allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} + , m_size{static_cast(std::ranges::distance(first, last))} + , m_capacity{m_size} + , m_data{allocate_n(m_capacity)} + { + for (auto destination = m_data; first != last; ++first, ++destination) + { + std::allocator_traits::construct(m_allocator, destination, *first); + } + } + + //! Construct a new vector and initialize it's content by copying all elements in the given range. + //! + //! @tparam InputIterator An iterator type used to describe the source range. + //! @param first The start of the source range. + //! @param last The end of the source range. + template + constexpr vector(InputIterator first, InputIterator last, + allocator_type const & allocator = + allocator_type{}) noexcept(std::is_nothrow_copy_constructible_v) + : m_allocator{allocator} + , m_size{0} + , m_capacity{0} + , m_data{} + { + while (first != last) + { + emplace_back(*first); + ++first; + } + } + + //! Construct a new vector and initialize it's content by copying all elements in the given range. + //! + //! + template + requires((std::ranges::forward_range || std::ranges::sized_range) && + kstd::bits::container_compatible_range) + constexpr vector(kstd::from_range_t, Range && range, allocator_type const & allocator = allocator_type{}) + : m_allocator{allocator} + , m_size{std::ranges::size(range)} + , m_capacity{m_size} + , m_data{allocate_n(m_capacity)} + { + auto destination = m_data; + for (auto && element : std::forward(range)) + { + std::allocator_traits::construct(m_allocator, destination++, + std::forward>(element)); + } + } + + //! Construct a new vector and initialize it's content by copying all elements from a given vector. + //! + //! @param other The source vector. + constexpr vector(vector const & other) + : m_allocator{other.m_allocator} + , m_size{other.m_size} + , m_capacity{other.m_capacity} + , m_data(allocate_n(m_capacity)) + { + uninitialized_copy_with_allocator(other.begin(), begin(), other.size()); + } + + //! Construct a new vector and initialize it's content by moving from a given vector. + //! + //! @param other The source vector. + constexpr vector(vector && other) noexcept + : m_allocator{std::move(other.m_allocator)} + , m_size{std::exchange(other.m_size, size_type{})} + , m_capacity(std::exchange(other.m_capacity, size_type{})) + , m_data(std::exchange(other.m_data, nullptr)) + {} + + //! Construct a new vector and initialize it's content by copying from a given vector. + //! + //! @param other The source vector. + //! @param allocator The allocator to use in the vector. + constexpr vector(vector const & other, std::type_identity_t const & allocator) + : m_allocator{allocator} + , m_size{other.m_size} + , m_capacity{other.m_capacity} + , m_data{allocate_n(m_capacity)} + { + uninitialized_copy_with_allocator(other.begin(), begin(), other.size()); + } + + //! Construct a new vector and initialize it's content by copying from a given vector. + //! + //! @param other The source vector. + //! @param allocator The allocator to use in the vector. + constexpr vector(vector && other, std::type_identity_t const & allocator) + : m_allocator{allocator} + , m_size{} + , m_capacity{} + , m_data{} + { + if constexpr (!std::allocator_traits::is_always_equal::value) + { + if (m_allocator != other.m_allocator) + { + m_capacity = other.size(); + m_data = allocate_n(capacity()); + m_size = other.size(); + uninitialized_move_with_allocator(other.begin(), begin(), other.size()); + other.clear(); + return; + } + } + m_size = std::exchange(other.m_size, size_type{}); + m_capacity = std::exchange(other.m_capacity, size_type{}); + m_data = std::exchange(other.m_data, nullptr); + } + + //! Construct a new vector and initialize it's content by copying all elements in the given initializer list. + //! + //! @param list The initializer list containing the source objects. + vector(std::initializer_list list, allocator_type const & allocator = allocator_type{}) + : vector{std::ranges::begin(list), std::ranges::end(list), allocator} + {} + + //! Destroy this vector. + constexpr ~vector() + { + clear_and_deallocate(); + } + + //! Replace the contents of this vector by the copying from the given source vector. + //! + //! @param other The source vector. + constexpr auto operator=(vector const & other) -> vector & + { + if (this == std::addressof(other)) + { + return *this; + } + + if constexpr (std::allocator_traits::propagate_on_container_copy_assignment::value) + { + if (get_allocator() != other.get_allocator()) + { + clear_and_deallocate(); + } + m_allocator = other.get_allocator(); + } + + if (capacity() >= other.size()) + { + auto const overlap = std::min(m_size, other.m_size); + std::ranges::copy(other.begin(), other.begin() + overlap, begin()); + + if (m_size < other.m_size) + { + uninitialized_copy_with_allocator(other.begin() + size(), begin() + size(), other.m_size - size()); + } + else if (m_size > other.m_size) + { + destroy_n(begin() + other.size(), size() - other.size()); + } + } + else + { + auto new_data = allocate_n(other.size()); + uninitialized_copy_with_allocator(other.begin(), new_data, other.size()); + clear_and_deallocate(); + m_data = new_data; + m_capacity = other.size(); + } + + m_size = other.size(); + return *this; + } + + //! Replace the contents fo this vector by moving from the given source. + //! + //! @param other The source vector. + constexpr auto operator=(vector && other) noexcept( + std::allocator_traits::propagate_on_container_move_assignment::value || + std::allocator_traits::is_always_equal::value) -> vector & + { + using std::swap; + + if (this == std::addressof(other)) + { + return *this; + } + + if constexpr (std::allocator_traits::propagate_on_container_move_assignment::value) + { + clear_and_deallocate(); + swap(m_allocator, other.m_allocator); + swap(m_size, other.m_size); + swap(m_capacity, other.m_capacity); + swap(m_data, other.m_data); + } + else if (m_allocator == other.m_allocator) + { + clear_and_deallocate(); + swap(m_size, other.m_size); + swap(m_capacity, other.m_capacity); + swap(m_data, other.m_data); + } + else + { + if (capacity() >= other.size()) + { + auto const overlap = std::min(size(), other.size()); + std::ranges::move(other.begin(), other.begin() + overlap, begin()); + + if (size() < other.size()) + { + uninitialized_move_with_allocator(other.begin() + size(), begin() + size(), other.size() - size()); + } + else if (size() > other.size()) + { + destroy_n(begin() + other.size(), size() - other.size()); + } + } + else + { + auto new_data = allocate_n(other.size()); + uninitialized_move_with_allocator(other.begin(), new_data, other.size()); + clear_and_deallocate(); + m_data = new_data; + m_capacity = other.m_size; + } + + other.destroy_n(other.begin(), other.size()); + m_size = std::exchange(other.m_size, size_type{}); + } + + return *this; + } + + //! Get a copy of the allocator associated with this vector. + [[nodiscard]] constexpr auto get_allocator() const noexcept(std::is_nothrow_copy_constructible_v) + -> allocator_type + { + return m_allocator; + } + + //! Get a reference to the element at the given index. + //! + //! This function will panic if the index is out of bounds for this vector. + //! + //! @param index The index of the element to retrieve. + //! @return A reference to the element at the specified index. + [[nodiscard]] constexpr auto at(size_type index) -> reference + { + panic_if_out_of_bounds(index); + return (*this)[index]; + } + + //! Get a reference to the element at the given index. + //! + //! This function will panic if the index is out of bounds for this vector. + //! + //! @param index The index of the element to retrieve. + //! @return A reference to the element at the specified index. + [[nodiscard]] constexpr auto at(size_type index) const -> const_reference + { + panic_if_out_of_bounds(index); + return (*this)[index]; + } + + //! Get a reference to the element at the given index. + //! + //! The behavior is undefined if the index is out of bounds for this vector. + //! + //! @param index The index of the element to retrieve. + //! @return A reference to the element at the specified index. + [[nodiscard]] constexpr auto operator[](size_type index) -> reference + { + return data()[index]; + } + + //! Get a reference to the element at the given index. + //! + //! The behavior is undefined if the index is out of bounds for this vector. + //! + //! @param index The index of the element to retrieve. + //! @return A reference to the element at the specified index. + [[nodiscard]] constexpr auto operator[](size_type index) const -> const_reference + { + return data()[index]; + } + + //! Get a reference to the first element of this vector. + //! + //! The behavior is undefined if this vector is empty. + [[nodiscard]] constexpr auto front() -> reference + { + return *begin(); + } + + //! Get a reference to the first element of this vector. + //! + //! The behavior is undefined if this vector is empty. + [[nodiscard]] constexpr auto front() const -> const_reference + { + return *begin(); + } + + //! Get a reference to the last element of this vector. + //! + //! The behavior is undefined if this vector is empty. + [[nodiscard]] constexpr auto back() -> reference + { + return *rbegin(); + } + + //! Get a reference to the last element of this vector. + //! + //! The behavior is undefined if this vector is empty. + [[nodiscard]] constexpr auto back() const -> const_reference + { + return *rbegin(); + } + + //! Get a pointer to the beginning of the underlying contiguous storage. + [[nodiscard]] constexpr auto data() noexcept -> pointer + { + return m_data; + } + + //! Get a pointer to the beginning of the underlying contiguous storage. + [[nodiscard]] constexpr auto data() const noexcept -> const_pointer + { + return m_data; + } + + //! Get an iterator to the first element of this vector. + //! + //! @return An iterator to the first element of this container, or end() if the container is empty. + [[nodiscard]] constexpr auto begin() noexcept -> iterator + { + return empty() ? end() : data(); + } + + //! Get an iterator to the first element of this vector. + //! + //! @return An iterator to the first element of this container, or end() if the container is empty. + [[nodiscard]] constexpr auto begin() const noexcept -> const_iterator + { + return empty() ? end() : data(); + } + + //! Get an iterator to the first element of this vector. + //! + //! @return An iterator to the first element of this container, or end() if the container is empty. + [[nodiscard]] constexpr auto cbegin() const noexcept -> const_iterator + { + return begin(); + } + + //! Get an iterator past the last element of this vector. + [[nodiscard]] constexpr auto end() noexcept -> pointer + { + return capacity() ? data() + size() : nullptr; + } + + //! Get an iterator past the last element of this vector. + [[nodiscard]] constexpr auto end() const noexcept -> const_pointer + { + return capacity() ? data() + size() : nullptr; + } + + //! Get an iterator past the last element of this vector. + [[nodiscard]] constexpr auto cend() const noexcept -> const_pointer + { + return end(); + } + + //! Get a reverse iterator to the reverse beginning. + [[nodiscard]] constexpr auto rbegin() noexcept -> reverse_iterator + { + return empty() ? rend() : reverse_iterator{end()}; + } + + //! Get a reverse iterator to the reverse beginning. + [[nodiscard]] constexpr auto rbegin() const noexcept -> const_reverse_iterator + { + return empty() ? rend() : const_reverse_iterator{end()}; + } + + //! Get a reverse iterator to the reverse beginning. + [[nodiscard]] constexpr auto crbegin() const noexcept -> const_reverse_iterator + { + return rbegin(); + } + + //! Get a reverse iterator to the reverse end. + [[nodiscard]] constexpr auto rend() noexcept -> reverse_iterator + { + return reverse_iterator{begin()}; + } + + //! Get a reverse iterator to the reverse end. + [[nodiscard]] constexpr auto rend() const noexcept -> const_reverse_iterator + { + return const_reverse_iterator{begin()}; + } + + //! Get a reverse iterator to the reverse end. + [[nodiscard]] constexpr auto crend() const noexcept -> const_reverse_iterator + { + return rend(); + } + + //! Check whether this vector is empty. + [[nodiscard]] constexpr auto empty() const noexcept -> bool + { + return !size(); + } + + //! Get the number of elements present in this vector. + [[nodiscard]] constexpr auto size() const noexcept -> size_type + { + return m_size; + } + + //! Get the maximum possible number of element for this vector. + [[nodiscard]] constexpr auto max_size() const noexcept -> size_type + { + return std::allocator_traits::max_size(m_allocator); + } + + //! Reserve storage for at list the given number of elements. + constexpr auto reserve(size_type new_capacity) -> void + { + if (new_capacity <= capacity()) + { + return; + } + + if (new_capacity > max_size()) + { + kstd::os::panic("[kstd:vector] Tried to reserve more space than theoretically possible."); + } + + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + uninitialized_move_with_allocator(begin(), new_data, size()); + clear_and_deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size; + } + + //! Resize this vector to contain @p new_size elements. + constexpr auto resize(size_type new_size) -> void + { + resize(new_size, value_type{}); + } + + //! Resize this vector to contain @p new_size elements, filling new elements with @p value. + constexpr auto resize(size_type new_size, const_reference value) -> void + { + if (new_size < size()) + { + destroy_n(begin() + new_size, size() - new_size); + m_size = new_size; + return; + } + + if (new_size == size()) + { + return; + } + + if (new_size > max_size()) + { + kstd::os::panic("[kstd:vector] Tried to resize more space than theoretically possible."); + } + + if (new_size > capacity()) + { + reserve(new_size); + } + + for (auto i = size(); i < new_size; ++i) + { + std::allocator_traits::construct(m_allocator, m_data + i, value); + } + + m_size = new_size; + } + + //! Get the number of element this vector has currently space for, including elements currently in this vector. + [[nodiscard]] constexpr auto capacity() const noexcept -> size_type + { + return m_capacity; + } + + //! Try to release unused storage space. + constexpr auto shrink_to_fit() -> void + { + if (m_size == m_capacity) + { + return; + } + + auto new_data = allocate_n(m_size); + auto old_size = size(); + uninitialized_move_with_allocator(begin(), new_data, old_size); + clear_and_deallocate(); + std::exchange(m_data, new_data); + m_capacity = old_size; + m_size = old_size; + } + + //! Clear the contents of this vector. + constexpr auto clear() noexcept -> void + { + destroy_n(begin(), size()); + m_size = 0; + } + + //! Insert an element at a given position. + //! + //! @param position The position to insert the element at. + //! @param value The value to insert. + //! @return An iterator to the inserted element. + constexpr auto insert(const_iterator position, value_type const & value) -> iterator + { + auto prefix_size = std::ranges::distance(begin(), position); + auto suffix_size = std::ranges::distance(position, end()); + + if (position == end()) + { + push_back(value); + return begin() + prefix_size; + } + + if (m_capacity == m_size) + { + auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + std::allocator_traits::construct(m_allocator, new_data + prefix_size, value); + uninitialized_move_with_allocator(begin(), new_data, prefix_size); + uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); + destroy_n(begin(), old_size); + deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size; + } + else if (&value >= begin() && &value < end()) + { + auto value_copy = value; + auto insert_position = begin() + prefix_size; + std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); + std::ranges::move_backward(insert_position, end() - 1, end()); + *insert_position = std::move(value_copy); + } + else + { + auto insert_position = begin() + prefix_size; + std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); + std::ranges::move_backward(insert_position, end() - 1, end()); + *insert_position = value; + } + + ++m_size; + return begin() + prefix_size; + } + + //! Insert an element at a given position. + //! + //! @param position The position to insert the element at. + //! @param value The value to insert. + //! @return An iterator to the inserted element. + constexpr auto insert(const_iterator position, value_type && value) -> iterator + { + auto prefix_size = std::ranges::distance(begin(), position); + auto suffix_size = std::ranges::distance(position, end()); + + if (position == end()) + { + push_back(std::move(value)); + return begin() + prefix_size; + } + + if (m_capacity == m_size) + { + auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + std::allocator_traits::construct(m_allocator, new_data + prefix_size, std::move(value)); + uninitialized_move_with_allocator(begin(), new_data, prefix_size); + uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); + destroy_n(begin(), old_size); + deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size; + } + else if (&value >= begin() && &value < end()) + { + auto value_copy = std::move(value); + auto insert_position = begin() + prefix_size; + std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); + std::ranges::move_backward(insert_position, end() - 1, end()); + *insert_position = std::move(value_copy); + } + else + { + auto insert_position = begin() + prefix_size; + std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); + std::ranges::move_backward(insert_position, end() - 1, end()); + *insert_position = std::move(value); + } + + ++m_size; + return begin() + prefix_size; + } + + //! Insert the element of a given range into the vector at a given position. + //! + //! @param range The source range to insert elements from. + //! @tparam SourceRange A container compatible range type. + template + requires requires(allocator_type allocator, pointer destination, SourceRange range) { + requires kstd::bits::container_compatible_range; + requires std::move_constructible; + requires std::is_move_assignable_v; + requires std::swappable; + std::allocator_traits::construct(allocator, destination, *std::ranges::begin(range)); + } + // NOLINTNEXTLINE(misc-no-recursion) + constexpr auto insert_range(const_iterator position, SourceRange && range) -> iterator + { + auto prefix_size = std::ranges::distance(begin(), position); + + if (position == end()) + { + append_range(std::forward(range)); + return begin() + prefix_size; + } + + if constexpr (std::ranges::forward_range || std::ranges::sized_range) + { + auto number_of_elements = static_cast(std::ranges::distance(range)); + if (!number_of_elements) + { + return begin() + prefix_size; + } + + if (capacity() - size() < number_of_elements) + { + reserve(size() + std::max(size(), number_of_elements)); + } + + auto insert_position = begin() + prefix_size; + auto suffix_size = static_cast(std::ranges::distance(insert_position, end())); + + if (number_of_elements >= suffix_size) + { + uninitialized_move_with_allocator(insert_position, insert_position + number_of_elements, suffix_size); + auto result = std::ranges::copy_n(std::ranges::begin(range), suffix_size, insert_position); + uninitialized_copy_with_allocator(std::move(result.in), end(), number_of_elements - suffix_size); + } + else + { + uninitialized_move_with_allocator(end() - number_of_elements, end(), number_of_elements); + std::ranges::move_backward(insert_position, end() - number_of_elements, end()); + std::ranges::copy_n(std::ranges::begin(range), number_of_elements, insert_position); + } + + m_size += number_of_elements; + return insert_position; + } + + auto range_begin = std::ranges::begin(range); + auto range_end = std::ranges::end(range); + + auto remainder = vector{get_allocator()}; + for (; range_begin != range_end; ++range_begin) + { + remainder.emplace_back(*static_cast>(range_begin)); + } + reserve(size() + std::max(size(), remainder.size())); + + return insert_range(begin() + prefix_size, remainder); + } + + template + constexpr auto emplace(const_iterator position, Args &&... args) -> iterator + { + auto prefix_size = std::ranges::distance(begin(), position); + auto suffix_size = std::ranges::distance(position, end()); + + if (position == end()) + { + emplace_back(std::forward(args)...); + return begin() + prefix_size; + } + + if (m_capacity == m_size) + { + auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + std::allocator_traits::construct(m_allocator, new_data + prefix_size, + std::forward(args)...); + uninitialized_move_with_allocator(begin(), new_data, prefix_size); + uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); + destroy_n(begin(), old_size); + deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size; + } + else + { + auto insert_position = begin() + prefix_size; + std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); + std::ranges::move_backward(insert_position, end() - 1, end()); + *insert_position = value_type{std::forward(args)...}; + } + + ++m_size; + return begin() + prefix_size; + } + + //! Erase an element at a given position. + //! + //! @note This function will panic if position == end() + //! + //! @param position An interator pointing to the element to delete + //! @return An iterator pointing to the element after the deleted element + constexpr auto erase(const_iterator position) -> iterator + { + if (position == end()) + { + os::panic("[kstd:vector] Attempted to erase end()!"); + } + + auto prefix_size = std::ranges::distance(cbegin(), position); + + std::ranges::move(begin() + prefix_size + 1, end(), begin() + prefix_size); + std::allocator_traits::destroy(m_allocator, end() - 1); + --m_size; + + return begin() + prefix_size; + } + + //! Erase a range of elements from this vector. + //! + //! @param first The start of the range to erase. + //! @param last The end of the range to erase. + //! @return An iterator pointing to the element after the last deleted element. + constexpr auto erase(const_iterator first, const_iterator last) -> iterator + { + if (first == last) + { + return begin() + std::ranges::distance(cbegin(), first); + } + + auto prefix_size = std::ranges::distance(cbegin(), first); + auto element_count = std::ranges::distance(first, last); + + std::ranges::move(begin() + prefix_size + element_count, end(), begin() + prefix_size); + destroy_n(end() - element_count, element_count); + m_size -= element_count; + + return begin() + prefix_size; + } + + //! Append a given element to this vector via copy construction. + constexpr auto push_back(value_type const & value) -> void + { + if (m_capacity == m_size) + { + auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + std::allocator_traits::construct(m_allocator, new_data + m_size, value); + uninitialized_move_with_allocator(begin(), new_data, size()); + destroy_n(begin(), size()); + deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size; + } + else + { + std::allocator_traits::construct(m_allocator, data() + size(), value); + } + ++m_size; + } + + //! Append a given element to this vector via move construction. + constexpr auto push_back(value_type && value) -> void + { + if (m_capacity == m_size) + { + auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + std::allocator_traits::construct(m_allocator, new_data + m_size, std::move(value)); + uninitialized_move_with_allocator(begin(), new_data, size()); + destroy_n(begin(), size()); + deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size; + } + else + { + std::allocator_traits::construct(m_allocator, data() + size(), std::move(value)); + } + ++m_size; + } + + //! Append a given element to this vector via direct construction. + template + constexpr auto emplace_back(Args &&... args) -> reference + { + if (m_capacity == m_size) + { + auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + std::allocator_traits::construct(m_allocator, new_data + m_size, std::forward(args)...); + uninitialized_move_with_allocator(begin(), new_data, size()); + destroy_n(begin(), size()); + deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size; + } + else + { + std::allocator_traits::construct(m_allocator, data() + size(), std::forward(args)...); + } + ++m_size; + return this->back(); + } + + //! Append the elements of a given range to this vector. + //! + //! @param range The range of elements to be appended. + //! @tparam SourceRange A container compatible range type. + template SourceRange> + requires requires(Allocator allocator, pointer destination, SourceRange range) { + std::allocator_traits::construct(allocator, destination, *std::ranges::begin(range)); + } + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward, misc-no-recursion) + constexpr auto append_range(SourceRange && range) -> void + { + if constexpr (std::ranges::forward_range || std::ranges::sized_range) + { + auto number_of_elements = static_cast(std::ranges::distance(range)); + + if (!capacity()) + { + reserve(number_of_elements); + } + + if (capacity() - size() >= number_of_elements) + { + uninitialized_copy_with_allocator(std::ranges::begin(range), end(), number_of_elements); + m_size += number_of_elements; + return; + } + + auto new_capacity = m_capacity + std::max(size(), number_of_elements); + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + + uninitialized_move_with_allocator(begin(), new_data, size()); + uninitialized_copy_with_allocator(std::ranges::begin(range), new_data + size(), number_of_elements); + clear_and_deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size + number_of_elements; + return; + } + + auto range_begin = std::ranges::begin(range); + auto range_end = std::ranges::end(range); + + for (auto i = capacity() - size(); i > 0; --i, ++range_begin) + { + emplace_back(*range_begin); + } + + if (range_begin == range_end) + { + return; + } + + auto remainder = vector{get_allocator()}; + for (; range_begin != range_end; ++range_begin) + { + remainder.emplace_back(*static_cast>(range_begin)); + } + reserve(size() + std::max(size(), remainder.size())); + append_range(remainder); + } + + //! Remove the last element of this vector. + //! + //! If this vector is empty, the behavior is undefined. + constexpr auto pop_back() -> void + { + --m_size; + std::allocator_traits::destroy(m_allocator, data() + size()); + } + + private: + //! Use the allocator of this vector to allocate enough space for the given number of elements. + //! + //! @param count The number of elements to allocate space for. + [[nodiscard]] constexpr auto allocate_n(std::size_t count) -> std::allocator_traits::pointer + { + if (count) + { + return std::allocator_traits::allocate(m_allocator, count); + } + return nullptr; + } + + //! Clear this vector and release it's memory. + constexpr auto clear_and_deallocate() -> void + { + clear(); + deallocate(); + } + + //! Release the memory of this vector. + constexpr auto deallocate() + { + if (m_data) + { + std::allocator_traits::deallocate(m_allocator, m_data, m_capacity); + m_capacity = 0; + m_size = 0; + m_data = nullptr; + } + } + + //! Destroy a number of elements in this vector. + //! + //! @param first The start of the range of the elements to be destroyed. + //! @param count The number of elements to destroy. + constexpr auto destroy_n(iterator first, std::size_t count) -> void + { + std::ranges::for_each(first, first + count, [&](auto & element) { + std::allocator_traits::destroy(m_allocator, std::addressof(element)); + }); + } + + //! Panic the kernel if the given index is out of bounds. + //! + //! @param index The index to check. + constexpr auto panic_if_out_of_bounds(size_type index) const -> void + { + if (index >= m_size) + { + os::panic("[kstd:vector] Attempted to read element at invalid index"); + } + } + + //! Copy a number of elements from a source range into the uninitialized destination range inside this vector. + //! + //! @param from The start of the source range. + //! @param to The start of the target range inside this vector. + //! @param count The number of elements to copy + template + constexpr auto uninitialized_copy_with_allocator(SourceIterator from, iterator to, size_type count) + { + for (auto i = 0uz; i < count; ++i) + { + std::allocator_traits::construct(m_allocator, to++, *from++); + } + } + + //! Move a number of elements from a source range into the uninitialized destination range inside this vector. + //! + //! @param from The start of the source range. + //! @param to The start of the target range inside this vector. + //! @param count The number of elements to copy + template + constexpr auto uninitialized_move_with_allocator(SourceIterator from, iterator to, size_type count) + { + for (auto i = 0uz; i < count; ++i) + { + std::allocator_traits::construct(m_allocator, to++, std::move(*from++)); + } + } + + //! The allocator used by this vector. + [[no_unique_address]] allocator_type m_allocator{}; + + //! The number of elements in this vector. + size_type m_size{}; + + //! The number of elements this vector has room for. + size_type m_capacity{}; + + //! The pointer to the start of the memory managed by this vector. + value_type * m_data{}; + }; + + //! Check if the content of two vectors is equal. + template + constexpr auto operator==(vector const & lhs, vector const & rhs) -> bool + { + return std::ranges::equal(lhs, rhs); + } + + //! Perform a lexicographical comparison of the content of two vectors. + template + constexpr auto operator<=>(vector const & lhs, vector const & rhs) + -> decltype(std::declval() <=> std::declval()) + { + return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); + } + + //! Deduction guide for vector construction from an interator pair. + template::value_type>> + vector(ForwardIterator, ForwardIterator, Allocator = Allocator()) + -> vector::value_type, Allocator>; + + //! Deduction guide for vector construction from an interator pair. + template::value_type>> + vector(InputIterator, InputIterator, Allocator = Allocator()) + -> vector::value_type, Allocator>; + + //! Deduction guide for vector construction from a range. + template>> + vector(kstd::from_range_t, Range &&, Allocator = Allocator()) -> vector, Allocator>; + +} // namespace kstd + +#endif diff --git a/libs/kstd/kstd/vector.test.cpp b/libs/kstd/kstd/vector.test.cpp new file mode 100644 index 0000000..8bf8f79 --- /dev/null +++ b/libs/kstd/kstd/vector.test.cpp @@ -0,0 +1,2003 @@ +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +SCENARIO("Vector initialization and construction", "[vector]") +{ + GIVEN("An empty context") + { + WHEN("constructing by default") + { + auto v = kstd::vector{}; + + THEN("the vector is empty") + { + REQUIRE(v.empty()); + } + + THEN("the size and capacity are zero") + { + REQUIRE(v.size() == 0); + REQUIRE(v.capacity() == 0); + } + } + + WHEN("constructing with a specific size") + { + auto v = kstd::vector(10); + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size is and capacity match the specified value") + { + REQUIRE(v.size() == 10); + REQUIRE(v.capacity() == 10); + } + } + + WHEN("constructing from an initializer list") + { + auto v = kstd::vector{1, 2, 3, 4, 5}; + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size is and capacity match the specified value") + { + REQUIRE(v.size() == 5); + REQUIRE(v.capacity() == 5); + } + + THEN("the elements are correctly initialized") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + REQUIRE(v[3] == 4); + REQUIRE(v[4] == 5); + } + } + } + + GIVEN("A non-empty range") + { + auto range = std::array{1, 2, 3}; + + WHEN("constructing from a random-access iterator range") + { + auto v = kstd::vector{std::begin(range), std::end(range)}; + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size and capacity match the range size") + { + REQUIRE(v.size() == std::size(range)); + REQUIRE(v.capacity() == std::size(range)); + } + + THEN("the elements are correctly initialized") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + + WHEN("constructing from a range") + { + auto v = kstd::vector{kstd::from_range, range}; + + THEN("the vector is not empty") + { + REQUIRE_FALSE(v.empty()); + } + + THEN("the size and capacity match the range size") + { + REQUIRE(v.size() == std::ranges::size(range)); + REQUIRE(v.capacity() == std::ranges::size(range)); + } + + THEN("the elements are correctly initialized") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + } + + GIVEN("A populated vector") + { + auto source = kstd::vector{1, 2, 3, 4, 5}; + + WHEN("copy constructing a new vector") + { + auto copy = kstd::vector(source); + + THEN("the copy matches the original") + { + REQUIRE(copy.size() == source.size()); + REQUIRE(copy.capacity() == source.capacity()); + REQUIRE(copy[0] == 1); + REQUIRE(copy[1] == 2); + REQUIRE(copy[2] == 3); + REQUIRE(copy[3] == 4); + REQUIRE(copy[4] == 5); + } + + THEN("the original is left unchanged") + { + REQUIRE(source.size() == 5); + REQUIRE(source.capacity() == 5); + REQUIRE(source[0] == 1); + REQUIRE(source[1] == 2); + REQUIRE(source[2] == 3); + REQUIRE(source[3] == 4); + REQUIRE(source[4] == 5); + } + } + + WHEN("move constructing a new vector") + { + auto moved = kstd::vector(std::move(source)); + + THEN("The new vector has the original elements") + { + REQUIRE(moved.size() == 5); + REQUIRE(moved.capacity() == 5); + REQUIRE(moved[0] == 1); + REQUIRE(moved[1] == 2); + REQUIRE(moved[2] == 3); + REQUIRE(moved[3] == 4); + REQUIRE(moved[4] == 5); + } + + THEN("The original vector is left in a valid but unspecified state") + { + REQUIRE(source.empty()); + REQUIRE(source.size() == 0); + REQUIRE(source.capacity() == 0); + } + } + } +} + +SCENARIO("Vector element access", "[vector]") +{ + GIVEN("A populated vector") + { + auto v = kstd::vector{10, 20, 30}; + + WHEN("accessing elements for reading") + { + THEN("operator[] and at() return the correct elements") + { + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + + REQUIRE(v.at(0) == 10); + REQUIRE(v.at(1) == 20); + REQUIRE(v.at(2) == 30); + } + + THEN("front() and back() return the first and last elements") + { + REQUIRE(v.front() == 10); + REQUIRE(v.back() == 30); + } + + THEN("data() return a pointer to the contiguous storage") + { + auto ptr = v.data(); + REQUIRE(ptr); + REQUIRE(ptr[0] == 10); + REQUIRE(ptr[1] == 20); + REQUIRE(ptr[2] == 30); + } + + THEN("accessing out of bounds elements panics") + { + REQUIRE_THROWS_AS(v.at(3), kstd::tests::os_panic); + } + } + + WHEN("accessing elements for writing") + { + v[0] = 100; + v.at(1) = 200; + v.back() = 300; + + THEN("the elements are correctly modified") + { + REQUIRE(v[0] == 100); + REQUIRE(v[1] == 200); + REQUIRE(v[2] == 300); + } + } + } +} + +SCENARIO("Vector iterators", "[vector]") +{ + GIVEN("A populated vector") + { + auto v = kstd::vector{1, 2, 3}; + + WHEN("using forward iterators") + { + THEN("they navigate the elements in the correct forward order") + { + auto it = v.begin(); + REQUIRE(it != v.end()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it != v.end()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.end()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it == v.end()); + } + + THEN("const forward iterators provide correct access") + { + auto it = v.cbegin(); + REQUIRE(it != v.cend()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it != v.cend()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.cend()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it == v.cend()); + } + } + + WHEN("using reverse iterators") + { + THEN("they navigate the elements in the correct reverse order") + { + auto it = v.rbegin(); + REQUIRE(it != v.rend()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it != v.rend()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.rend()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it == v.rend()); + } + + THEN("const reverse iterators provide correct access") + { + auto it = v.crbegin(); + REQUIRE(it != v.crend()); + REQUIRE(*it == 3); + + ++it; + REQUIRE(it != v.crend()); + REQUIRE(*it == 2); + + ++it; + REQUIRE(it != v.crend()); + REQUIRE(*it == 1); + + ++it; + REQUIRE(it == v.crend()); + } + } + } + + GIVEN("an empty vector") + { + auto v = kstd::vector{}; + + WHEN("getting iterators") + { + THEN("begin() equals end() and cbegin() equals cend()") + { + REQUIRE(v.begin() == v.end()); + REQUIRE(v.cbegin() == v.cend()); + } + + THEN("rbegin() equals rend() and crbegin() equals crend()") + { + REQUIRE(v.rbegin() == v.rend()); + REQUIRE(v.crbegin() == v.crend()); + } + } + } +} + +SCENARIO("Vector capacity management", "[vector]") +{ + GIVEN("An empty vector") + { + auto v = kstd::vector{}; + + WHEN("reserving space") + { + v.reserve(10); + + THEN("the capacity is at least the reserved amount") + { + REQUIRE(v.capacity() >= 10); + } + + THEN("the size is still zero") + { + REQUIRE(v.size() == 0); + } + + THEN("the vector is still empty") + { + REQUIRE(v.empty()); + } + } + + WHEN("reserving space less than or equal to current capacity") + { + v.reserve(10); + auto const current_capacity = v.capacity(); + v.reserve(5); + + THEN("the capacity remains unchanged") + { + REQUIRE(v.capacity() == current_capacity); + } + } + + WHEN("reserving space greater than max_size") + { + THEN("a panic is triggered") + { + REQUIRE_THROWS_AS(v.reserve(v.max_size() + 1), kstd::tests::os_panic); + } + } + } + + GIVEN("A populated vector with excess capacity") + { + auto v = kstd::vector{1, 2, 3}; + v.reserve(10); + + REQUIRE(v.capacity() == 10); + + WHEN("calling shrink_to_fit") + { + v.shrink_to_fit(); + + THEN("the capacity is reduced to match the size") + { + REQUIRE(v.capacity() == 3); + REQUIRE(v.size() == 3); + } + + THEN("the elements remain unchanged") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + } +} + +SCENARIO("Vector modifiers", "[vector]") +{ + GIVEN("An empty vector") + { + auto v = kstd::vector{}; + + WHEN("push_back is called with a value") + { + v.push_back(10); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v.back() == 10); + } + } + + WHEN("emplace_back is called with constructor arguments") + { + v.emplace_back(20); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v.back() == 20); + } + } + + WHEN("elements are added while capacity is sufficient") + { + v.reserve(10); + auto const capacity = v.capacity(); + + v.push_back(10); + v.emplace_back(20); + + THEN("the elements are added without reallocation") + { + REQUIRE(v.size() == 2); + REQUIRE(v.capacity() == capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + } + } + + WHEN("emplace is called with the end iterator and constructor arguments") + { + v.emplace(v.end(), 20); + + THEN("the element is appended and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v.back() == 20); + } + } + + WHEN("emplace is called while capacity is sufficient") + { + v.reserve(10); + auto const capacity = v.capacity(); + + v.emplace(v.end(), 20); + + THEN("the element is appended without reallocation") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() == capacity); + REQUIRE(v.back() == 20); + } + } + + WHEN("inserting an element") + { + auto it = v.insert(v.cbegin(), 40); + + THEN("the size and capacity increase and the element is inserted") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v[0] == 40); + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting an lvalue element") + { + auto const value = 40; + auto it = v.insert(v.cbegin(), value); + + THEN("the size and capacity increase and the element is inserted") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() >= 1); + REQUIRE(v[0] == 40); + REQUIRE(it == v.begin()); + } + } + + WHEN("appending a range") + { + auto const range = std::views::iota(0, 3); + v.append_range(range); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are appended") + { + REQUIRE(v[0] == 0); + REQUIRE(v[1] == 1); + REQUIRE(v[2] == 2); + } + } + + WHEN("appending from an input range") + { + auto const arr = std::array{1, 2, 3}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; + + v.append_range(std::ranges::subrange{first, last}); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are appended") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + + WHEN("appending from an input range with sufficient capacity") + { + v.reserve(3); + auto const arr = std::array{1, 2, 3}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; + + v.append_range(std::ranges::subrange{first, last}); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity stays the same") + { + REQUIRE(v.capacity() == 3); + } + + THEN("the elements are appended") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + } + + WHEN("resizing the vector to a greater size") + { + v.resize(3); + + THEN("the size and capacity increase and the elements are value initialized") + { + REQUIRE(v.size() == 3); + REQUIRE(v.capacity() >= 3); + REQUIRE(v[0] == 0); + REQUIRE(v[1] == 0); + REQUIRE(v[2] == 0); + } + } + + WHEN("resizing the vector to a greater size with initial value") + { + v.resize(3, 2); + + THEN("the size and capacity increase and the elements are initialized to the given value") + { + REQUIRE(v.size() == 3); + REQUIRE(v.capacity() >= 3); + REQUIRE(v[0] == 2); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 2); + } + } + + WHEN("inserting a range at the beginning") + { + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.begin(), arr); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting a range at the end") + { + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.end(), arr); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting from an input range without sufficient capacity") + { + auto const arr = std::array{1, 2, 3}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; + auto it = v.insert_range(v.begin(), std::ranges::subrange{first, last}); + + THEN("the size increases") + { + REQUIRE(v.size() == 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } + } + + GIVEN("A populated vector") + { + auto v = kstd::vector{10, 20, 30}; + auto initial_capacity = v.capacity(); + + WHEN("push_back is called") + { + v.push_back(40); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() >= initial_capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 40); + } + } + + WHEN("emplace_back is called with constructor arguments") + { + v.emplace_back(40); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() >= initial_capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 40); + } + } + + WHEN("emplace is called with an iterator and constructor arguments") + { + auto it = v.emplace(v.begin() + 2, 25); + + THEN("the element is inserted and the size and capacity increase") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() >= initial_capacity); + REQUIRE(v.at(2) == 25); + } + + THEN("the returned iterator points to the inserted element") + { + REQUIRE(it == v.cbegin() + 2); + REQUIRE(*it == 25); + } + } + + WHEN("emplace is called with an iterator and sufficient capacity") + { + v.reserve(v.size() + 1); + + auto it = v.emplace(v.begin() + 2, 25); + + THEN("the element is inserted and the size and capacity increase") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() >= initial_capacity); + REQUIRE(v.at(2) == 25); + } + + THEN("the returned iterator points to the inserted element") + { + REQUIRE(it == v.cbegin() + 2); + REQUIRE(*it == 25); + } + } + + WHEN("push_back is called with a reference to an internal element") + { + v.shrink_to_fit(); + auto const original_value = v[0]; + + v.push_back(v[0]); + + THEN("reallocation handles the internal reference safely without dangling") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == original_value); + REQUIRE(v.back() == original_value); + } + } + + WHEN("pop_back is called") + { + v.pop_back(); + + THEN("the last element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v.capacity() == initial_capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + } + } + + WHEN("clear is called") + { + v.clear(); + + THEN("the vector is empty") + { + REQUIRE(v.empty()); + REQUIRE(v.size() == 0); + REQUIRE(v.capacity() == initial_capacity); + } + } + + WHEN("inserting at the beginning") + { + auto it = v.insert(v.cbegin(), 5); + + THEN("the element is inserted at the front") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 5); + REQUIRE(v[1] == 10); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting in the middle") + { + auto it = v.insert(v.cbegin() + 1, 15); + + THEN("the element is inserted in the middle") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 15); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting at the end") + { + auto it = v.insert(v.cend(), 40); + + THEN("the element is inserted at the back") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 40); + REQUIRE(it == v.begin() + 3); + } + } + + WHEN("inserting an lvalue at the end") + { + auto const value = 40; + auto it = v.insert(v.cend(), value); + + THEN("the element is inserted at the back") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 40); + REQUIRE(it == v.begin() + 3); + } + } + + WHEN("inserting when capacity is sufficient") + { + v.reserve(10); + auto const capacity = v.capacity(); + + auto it = v.insert(v.cbegin() + 1, 15); + + THEN("the element is added without reallocation") + { + REQUIRE(v.size() == 4); + REQUIRE(v.capacity() == capacity); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 15); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting a reference to an existing element with reallocation") + { + v.shrink_to_fit(); + REQUIRE(v.capacity() == v.size()); + auto it = v.insert(v.cbegin() + 1, v[2]); + + THEN("the element is correctly copied and inserted") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting a reference to an existing element without reallocation") + { + v.reserve(10); + REQUIRE(v.capacity() > v.size()); + auto it = v.insert(v.cbegin() + 1, v[2]); + + THEN("the element is correctly copied and inserted") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting an rvalue reference to an existing element with reallocation") + { + v.shrink_to_fit(); + REQUIRE(v.capacity() == v.size()); + auto it = v.insert(v.cbegin() + 1, std::move(v[2])); + + THEN("the element is correctly moved and inserted") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting an rvalue reference to an existing element without reallocation") + { + v.reserve(10); + REQUIRE(v.capacity() > v.size()); + auto it = v.insert(v.cbegin() + 1, std::move(v[2])); + + THEN("the element is correctly moved and inserted") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("erasing the first element") + { + auto it = v.erase(v.cbegin()); + + THEN("the first element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 20); + REQUIRE(v[1] == 30); + REQUIRE(it == v.begin()); + } + } + + WHEN("erasing a middle element") + { + auto it = v.erase(v.cbegin() + 1); + + THEN("the middle element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("erasing the last element") + { + auto it = v.erase(v.cend() - 1); + + THEN("the last element is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(it == v.end()); + } + } + + WHEN("erasing the end() iterator") + { + THEN("a panic is triggered") + { + REQUIRE_THROWS_AS(v.erase(v.end()), kstd::tests::os_panic); + } + } + + WHEN("erasing a range of elements") + { + auto it = v.erase(v.cbegin() + 1, v.cend() - 1); + + THEN("the specified range is removed and the size decreases") + { + REQUIRE(v.size() == 2); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("erasing an empty range") + { + auto it = v.erase(v.cbegin() + 1, v.cbegin() + 1); + + THEN("the vector is unchanged") + { + REQUIRE(v.size() == 3); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("appending a range") + { + auto initial_size = v.size(); + v.append_range(std::views::iota(0, 3)); + + THEN("capacity is increased") + { + REQUIRE(v.capacity() >= initial_capacity); + } + + THEN("size is increased") + { + REQUIRE(v.size() == initial_size + 3); + } + + THEN("the elements are appended") + { + REQUIRE(v[initial_size + 0] == 0); + REQUIRE(v[initial_size + 1] == 1); + REQUIRE(v[initial_size + 2] == 2); + } + } + + WHEN("resizing the vector to a greater size") + { + auto initial_size = v.size(); + v.resize(initial_size + 3); + + THEN("the size and capacity increase and the elements are value initialized") + { + REQUIRE(v.size() == initial_size + 3); + REQUIRE(v.capacity() >= initial_size + 3); + REQUIRE(v[initial_size + 0] == 0); + REQUIRE(v[initial_size + 1] == 0); + REQUIRE(v[initial_size + 2] == 0); + } + } + + WHEN("resizing the vector to a greater size with initial value") + { + auto initial_size = v.size(); + v.resize(initial_size + 3, 2); + + THEN("the size and capacity increase and the elements are initialized to the given value") + { + REQUIRE(v.size() == initial_size + 3); + REQUIRE(v.capacity() >= initial_size + 3); + REQUIRE(v[initial_size + 0] == 2); + REQUIRE(v[initial_size + 1] == 2); + REQUIRE(v[initial_size + 2] == 2); + } + } + + WHEN("resizing the vector to a smaller size") + { + v.resize(1); + + THEN("the size decreases and the elements are destroyed") + { + REQUIRE(v.size() == 1); + REQUIRE(v[0] == 10); + } + } + + WHEN("inserting an empty range") + { + auto initial_size = v.size(); + auto it = v.insert_range(v.begin(), std::views::empty); + + THEN("the size does not change") + { + REQUIRE(v.size() == initial_size); + } + + THEN("the capacity does not change") + { + REQUIRE(v.capacity() == initial_capacity); + } + + THEN("the content is unchanged") + { + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + } + + THEN("the returned iterator points to the position of insertion") + { + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting a range at the beginning") + { + auto initial_size = v.size(); + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.begin(), arr); + + THEN("the size increases") + { + REQUIRE(v.size() == initial_size + 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= initial_size + 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + REQUIRE(v[3] == 10); + REQUIRE(v[4] == 20); + REQUIRE(v[5] == 30); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } + + WHEN("inserting a range at the end") + { + auto initial_size = v.size(); + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.end(), arr); + + THEN("the size increases") + { + REQUIRE(v.size() == initial_size + 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= initial_size + 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 20); + REQUIRE(v[2] == 30); + REQUIRE(v[3] == 1); + REQUIRE(v[4] == 2); + REQUIRE(v[5] == 3); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin() + initial_size); + } + } + + WHEN("inserting a range that causes reallocation") + { + auto initial_size = v.size(); + auto const arr = std::array{1, 2, 3}; + auto it = v.insert_range(v.begin() + 1, arr); + + THEN("the size increases") + { + REQUIRE(v.size() == initial_size + 3); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= initial_size + 3); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 1); + REQUIRE(v[2] == 2); + REQUIRE(v[3] == 3); + REQUIRE(v[4] == 20); + REQUIRE(v[5] == 30); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting fewer elements than the suffix without reallocation") + { + v.reserve(10); + auto const capacity = v.capacity(); + auto const arr = std::array{1}; + auto it = v.insert_range(v.begin() + 1, arr); + + THEN("the elements are correctly placed") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 1); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + } + + THEN("no reallocation occurs") + { + REQUIRE(v.capacity() == capacity); + } + + THEN("the returned iterator points to the inserted element") + { + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting fewer elements than the suffix without sufficient capacity") + { + v.shrink_to_fit(); + auto const arr = std::array{1}; + auto it = v.insert_range(v.begin() + 1, arr); + + THEN("the elements are correctly placed") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0] == 10); + REQUIRE(v[1] == 1); + REQUIRE(v[2] == 20); + REQUIRE(v[3] == 30); + } + + THEN("the returned iterator points to the inserted element") + { + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("inserting from an input range without sufficient capacity") + { + auto const arr = std::array{1, 2, 3}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; + auto it = v.insert_range(v.begin(), std::ranges::subrange{first, last}); + + THEN("the size increases") + { + REQUIRE(v.size() == 6); + } + + THEN("the capacity increases") + { + REQUIRE(v.capacity() >= 6); + } + + THEN("the elements are inserted") + { + REQUIRE(v[0] == 1); + REQUIRE(v[1] == 2); + REQUIRE(v[2] == 3); + REQUIRE(v[3] == 10); + REQUIRE(v[4] == 20); + REQUIRE(v[5] == 30); + } + + THEN("the returned iterator points to the beginning of the inserted range") + { + REQUIRE(it == v.begin()); + } + } + } +} + +SCENARIO("Vector comparison", "[vector]") +{ + GIVEN("Two identical vectors") + { + auto v1 = kstd::vector{1, 2, 3}; + auto v2 = kstd::vector{1, 2, 3}; + + WHEN("comparing for equality") + { + THEN("the vectors are equal") + { + REQUIRE(v1 == v2); + } + + THEN("the vectors and not not-equal") + { + REQUIRE_FALSE(v1 != v2); + } + } + + WHEN("comparing using the spaceship operator") + { + THEN("the vectors are equivalent") + { + REQUIRE((v1 <=> v2) == 0); + REQUIRE(v1 <= v2); + REQUIRE(v1 >= v2); + } + } + } + + GIVEN("Two vectors of different sizes") + { + auto v1 = kstd::vector{1, 2, 3}; + auto v2 = kstd::vector{1, 2, 3, 4}; + + WHEN("comparing for equality") + { + THEN("the vectors are not equal") + { + REQUIRE_FALSE(v1 == v2); + } + + THEN("the vectors are not-equal") + { + REQUIRE(v1 != v2); + } + } + + WHEN("comparing for ordering") + { + THEN("the shorter vector evaluates as less than the longer vector") + { + REQUIRE(v1 < v2); + REQUIRE(v2 > v1); + } + } + } + + GIVEN("Two vectors of the same size but different elements") + { + auto v1 = kstd::vector{1, 2, 3}; + auto v2 = kstd::vector{1, 2, 4}; + + WHEN("comparing for ordering") + { + THEN("they are ordered lexicographically") + { + REQUIRE(v1 < v2); + REQUIRE(v2 > v1); + } + } + } +} + +SCENARIO("Vector with non-default-constructible types", "[vector]") +{ + GIVEN("A type without a default constructor") + { + WHEN("constructing an empty vector") + { + auto v = kstd::vector{}; + + THEN("the vector is empty") + { + REQUIRE(v.empty()); + } + } + + WHEN("using emplace_back") + { + auto v = kstd::vector{}; + v.emplace_back(40); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.back() == kstd::tests::non_default_constructible{40}); + } + } + } +} + +SCENARIO("Vector with custom allocator", "[vector]") +{ + GIVEN("a tracking allocator acting as the vector's memory manager") + { + auto allocations = 0; + auto allocator = kstd::tests::tracking_allocator{&allocations}; + + WHEN("a vector uses this allocator to allocate memory") + { + auto v = kstd::vector>(allocator); + REQUIRE(allocations == 0); + + v.reserve(10); + + THEN("the allocator was used to allocate memory") + { + REQUIRE(allocations > 0); + } + } + } +} + +SCENARIO("Vector modifier move semantics", "[vector]") +{ + GIVEN("An empty vector and a move tracker element") + { + auto v = kstd::vector{}; + auto tracker = kstd::tests::special_member_tracker{40}; + + WHEN("push_back is called with the move tracker") + { + v.push_back(std::move(tracker)); + + THEN("the element is added and the size and capacity increase") + { + REQUIRE(v.size() == 1); + REQUIRE(v.back().move_constructed_count == 1); + REQUIRE(v.back().copy_constructed_count == 0); + REQUIRE(v.back().value == 40); + } + + THEN("the original tracker is left in a valid but unspecified state") + { + REQUIRE(tracker.value == -1); + } + } + } + + GIVEN("An empty vector") + { + auto v = kstd::vector{}; + + WHEN("emplace_back is called with constructor arguments") + { + v.emplace_back(40); + + THEN("the element is constructed directly, without moves or copies") + { + REQUIRE(v.size() == 1); + REQUIRE(v.back().move_constructed_count == 0); + REQUIRE(v.back().copy_constructed_count == 0); + REQUIRE(v.back().value == 40); + } + } + } + + GIVEN("A populated vector of move trackers") + { + auto v = kstd::vector{}; + v.reserve(10); + v.emplace_back(10); + v.emplace_back(20); + v.emplace_back(30); + + WHEN("inserting an element in the middle with sufficient capacity") + { + auto const tracker = kstd::tests::special_member_tracker{15}; + v.insert(v.cbegin() + 1, tracker); + + THEN("the shifted elements are move-assigned and the new element is copy-assigned") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0].value == 10); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); + REQUIRE(v[1].value == 15); + REQUIRE(v[1].move_assigned_count == 0); + REQUIRE(v[1].copy_assigned_count == 1); + REQUIRE(tracker.copied_from_count == 1); + REQUIRE(v[2].value == 20); + REQUIRE(v[2].move_assigned_count == 1); + REQUIRE(v[2].copy_assigned_count == 0); + REQUIRE(v[3].value == 30); + REQUIRE(v[3].move_constructed_count == 1); + } + } + + WHEN("inserting an rvalue element in the middle with sufficient capacity") + { + auto tracker = kstd::tests::special_member_tracker{15}; + v.insert(v.cbegin() + 1, std::move(tracker)); + + THEN("the shifted elements are move-assigned and the new element is move-assigned") + { + REQUIRE(v.size() == 4); + REQUIRE(v[0].value == 10); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); + REQUIRE(v[1].value == 15); + REQUIRE(v[1].move_assigned_count == 1); + REQUIRE(v[1].copy_assigned_count == 0); + REQUIRE(tracker.moved_from_count == 1); + REQUIRE(v[2].value == 20); + REQUIRE(v[2].move_assigned_count == 1); + REQUIRE(v[3].value == 30); + REQUIRE(v[3].move_constructed_count == 1); + } + } + + WHEN("erasing an element in the middle") + { + for (auto & elem : v) + { + elem.reset_counts(); + } + + auto it = v.erase(v.cbegin() + 1); + + THEN("the subsequent elements are move-assigned leftwards") + { + REQUIRE(v.size() == 2); + + REQUIRE(v[0].value == 10); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); + + REQUIRE(v[1].value == 30); + REQUIRE(v[1].move_assigned_count == 1); + REQUIRE(v[1].copy_assigned_count == 0); + + REQUIRE(v.data()[2].destroyed_count == 1); + REQUIRE(v.data()[2].moved_from_count == 1); + + REQUIRE(it == v.begin() + 1); + } + } + + WHEN("erasing the last element") + { + for (auto & elem : v) + { + elem.reset_counts(); + } + + auto it = v.erase(v.cend() - 1); + + THEN("no elements are moved, just the last element destroyed") + { + REQUIRE(v.size() == 2); + + REQUIRE(v[0].value == 10); + REQUIRE(v[0].move_constructed_count == 0); + REQUIRE(v[0].copy_constructed_count == 0); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); + + REQUIRE(v[1].value == 20); + REQUIRE(v[1].move_constructed_count == 0); + REQUIRE(v[1].copy_constructed_count == 0); + REQUIRE(v[1].move_assigned_count == 0); + REQUIRE(v[1].copy_assigned_count == 0); + + REQUIRE(v.data()[2].destroyed_count == 1); + REQUIRE(v.data()[2].moved_from_count == 0); + REQUIRE(v.data()[2].copied_from_count == 0); + + REQUIRE(it == v.end()); + } + } + + WHEN("erasing a range of elements in the middle") + { + v.emplace_back(40); + v.emplace_back(50); + + for (auto & elem : v) + { + elem.reset_counts(); + } + + auto it = v.erase(v.cbegin() + 1, v.cbegin() + 3); + + THEN("the specified elements are destroyed and subsequent elements are move-assigned leftwards") + { + REQUIRE(v.size() == 3); + + REQUIRE(v[0].value == 10); + REQUIRE(v[0].move_constructed_count == 0); + REQUIRE(v[0].copy_constructed_count == 0); + REQUIRE(v[0].move_assigned_count == 0); + REQUIRE(v[0].copy_assigned_count == 0); + + REQUIRE(v[1].value == 40); + REQUIRE(v[1].move_constructed_count == 0); + REQUIRE(v[1].copy_constructed_count == 0); + REQUIRE(v[1].move_assigned_count == 1); + REQUIRE(v[1].copy_assigned_count == 0); + + REQUIRE(v[2].value == 50); + REQUIRE(v[2].move_constructed_count == 0); + REQUIRE(v[2].copy_constructed_count == 0); + REQUIRE(v[2].move_assigned_count == 1); + REQUIRE(v[2].copy_assigned_count == 0); + + REQUIRE(v.data()[3].destroyed_count == 1); + REQUIRE(v.data()[3].moved_from_count == 1); + REQUIRE(v.data()[3].copied_from_count == 0); + + REQUIRE(v.data()[4].destroyed_count == 1); + REQUIRE(v.data()[4].moved_from_count == 1); + REQUIRE(v.data()[4].copied_from_count == 0); + + REQUIRE(it == v.begin() + 1); + } + } + } +} + +SCENARIO("Vector advanced construction", "[vector]") +{ + GIVEN("A count and a default value") + { + WHEN("constructing with count and default argument") + { + auto const count = 5uz; + auto const value = 42; + auto v = kstd::vector(count, value); + + THEN("the vector is initialized with count copies of value") + { + REQUIRE(v.size() == 5); + REQUIRE(v.capacity() == 5); + REQUIRE(v.front() == 42); + REQUIRE(v.back() == 42); + } + } + } + + GIVEN("A pure input iterator range") + { + WHEN("constructing from input iterators") + { + auto const arr = std::array{1, 2, 3}; + auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; + auto const last = kstd::tests::test_input_iterator{}; + + auto v = kstd::vector(first, last); + + THEN("the vector is generated dynamically and initialized correctly") + { + REQUIRE(v.size() == 3); + REQUIRE(v[0] == 1); + REQUIRE(v[2] == 3); + } + } + } + + GIVEN("A tracking allocator and a source vector") + { + auto allocs = 0; + auto allocator = kstd::tests::tracking_allocator{&allocs}; + auto source = kstd::vector>{allocator}; + source.push_back(1); + source.push_back(2); + source.push_back(3); + + allocs = 0; + + WHEN("copy constructing with an allocator") + { + auto copy = kstd::vector>(source, allocator); + + THEN("the copy succeeds and the allocator is used") + { + REQUIRE(copy.size() == 3); + REQUIRE(allocs > 0); + REQUIRE(copy[0] == 1); + REQUIRE(copy[2] == 3); + } + } + + WHEN("move constructing with an identically comparing allocator") + { + auto moved = kstd::vector>(std::move(source), allocator); + + THEN("the move succeeds and no new allocations are made (memory is stolen)") + { + REQUIRE(moved.size() == 3); + REQUIRE(allocs == 0); + REQUIRE(moved[0] == 1); + REQUIRE(moved[2] == 3); + } + } + + WHEN("move constructing with a non-equal allocator") + { + auto allocs2 = 0; + auto allocator2 = kstd::tests::tracking_allocator{&allocs2}; + + auto moved = kstd::vector>(std::move(source), allocator2); + + THEN("the move allocates new memory and moves elements") + { + REQUIRE(allocs2 > 0); + REQUIRE(moved.size() == 3); + REQUIRE(source.empty()); + } + } + } +} + +SCENARIO("Vector assignment operators", "[vector]") +{ + GIVEN("A source vector and an empty target vector") + { + auto source = kstd::vector{1, 2, 3}; + auto target = kstd::vector{}; + + WHEN("copy assigning") + { + target = source; + + THEN("the target matches the source") + { + REQUIRE(target.size() == 3); + REQUIRE(target[0] == 1); + REQUIRE(target[2] == 3); + REQUIRE(source.size() == 3); + } + } + + WHEN("move assigning") + { + target = std::move(source); + + THEN("the target assumes the source's data") + { + REQUIRE(target.size() == 3); + REQUIRE(target[0] == 1); + REQUIRE(target[2] == 3); + REQUIRE(source.empty()); + } + } + } + + GIVEN("Vectors with propagating copy allocator") + { + auto allocs1 = 0; + auto allocs2 = 0; + auto alloc1 = kstd::tests::propagating_allocator{&allocs1}; + auto alloc2 = kstd::tests::propagating_allocator{&allocs2}; + + auto v1 = kstd::vector>{alloc1}; + v1.push_back(1); + v1.push_back(2); + auto v2 = kstd::vector>{alloc2}; + + WHEN("copy assigning") + { + v2 = v1; + THEN("the allocator propagates") + { + REQUIRE(v2.get_allocator() == v1.get_allocator()); + } + } + } + + GIVEN("Vectors for copy assignment overlap") + { + auto v1 = kstd::vector{1, 2, 3}; + auto v2 = kstd::vector{4, 5}; + v2.reserve(10); + + WHEN("copy assigning a larger vector to a smaller one with enough capacity") + { + v2 = v1; + THEN("elements are copied and size is updated") + { + REQUIRE(v2.size() == 3); + REQUIRE(v2.capacity() >= 10); + REQUIRE(v2[2] == 3); + } + } + + auto v3 = kstd::vector{1, 2, 3, 4}; + v3.reserve(10); + WHEN("copy assigning a smaller vector to a larger one") + { + v3 = v1; + THEN("excess elements are destroyed") + { + REQUIRE(v3.size() == 3); + REQUIRE(v3[0] == 1); + } + } + } + + GIVEN("Vectors with the same tracking allocator") + { + auto allocs = 0; + auto alloc = kstd::tests::tracking_allocator{&allocs}; + auto v1 = kstd::vector>{alloc}; + v1.push_back(1); + auto v2 = kstd::vector>{alloc}; + + WHEN("move assigning") + { + v2 = std::move(v1); + THEN("memory is stolen without allocation") + { + REQUIRE(v2.size() == 1); + REQUIRE(allocs == 1); + } + } + } + + GIVEN("Vectors with different non-propagating tracking allocators") + { + auto allocs1 = 0; + auto allocs2 = 0; + auto alloc1 = kstd::tests::tracking_allocator{&allocs1}; + auto alloc2 = kstd::tests::tracking_allocator{&allocs2}; + + auto v1 = kstd::vector>{alloc1}; + v1.push_back(1); + v1.push_back(2); + v1.push_back(3); + + auto v2 = kstd::vector>{alloc2}; + v2.push_back(4); + v2.push_back(5); + + WHEN("move assigning a larger vector to a smaller one without enough capacity") + { + v2.shrink_to_fit(); + v2 = std::move(v1); + THEN("memory is reallocated and elements are moved") + { + REQUIRE(v2.size() == 3); + REQUIRE(allocs2 > 2); + } + } + + auto v3 = kstd::vector>{alloc2}; + v3.reserve(10); + v3.push_back(4); + v3.push_back(5); + WHEN("move assigning a larger vector to a smaller one with enough capacity") + { + v3 = std::move(v1); + THEN("elements are move-assigned over overlap and move-constructed over remainder") + { + REQUIRE(v3.size() == 3); + } + } + + auto v4 = kstd::vector>{alloc2}; + v4.reserve(10); + v4.push_back(4); + v4.push_back(5); + v4.push_back(6); + v4.push_back(7); + WHEN("move assigning a smaller vector to a larger one with enough capacity") + { + v4 = std::move(v1); + THEN("overlap is moved and excess is destroyed") + { + REQUIRE(v4.size() == 3); + } + } + } +} + +SCENARIO("Vector self-assignment operators", "[vector]") +{ + GIVEN("A populated vector") + { + auto v = kstd::vector{1, 2, 3}; + auto const initial_capacity = v.capacity(); + auto const * initial_data = v.data(); + + WHEN("copy assigning to itself") + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-warning-option" +#pragma GCC diagnostic ignored "-Wself-assign-overloaded" + v = v; +#pragma GCC diagnostic pop + + THEN("the vector remains unchanged") + { + REQUIRE(v.size() == 3); + REQUIRE(v.capacity() == initial_capacity); + REQUIRE(v.data() == initial_data); + REQUIRE(v[0] == 1); + REQUIRE(v[2] == 3); + } + } + + WHEN("move assigning to itself") + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wunknown-warning-option" +#pragma GCC diagnostic ignored "-Wself-move" + v = std::move(v); +#pragma GCC diagnostic pop + + THEN("the vector remains unchanged") + { + REQUIRE(v.size() == 3); + REQUIRE(v.capacity() == initial_capacity); + REQUIRE(v.data() == initial_data); + REQUIRE(v[0] == 1); + REQUIRE(v[2] == 3); + } + } + } +} + +SCENARIO("Vector const accessors and copy insertion", "[vector]") +{ + GIVEN("A const populated vector") + { + auto const v = kstd::vector{10, 20, 30}; + + WHEN("calling const accessors") + { + THEN("elements are read correctly as const references") + { + REQUIRE(v.front() == 10); + REQUIRE(v.back() == 30); + REQUIRE(v[1] == 20); + REQUIRE(v.at(1) == 20); + } + } + } + + GIVEN("An empty vector and a const lvalue tracker") + { + auto v = kstd::vector{}; + auto const tracker = kstd::tests::special_member_tracker{42}; + + WHEN("push_back is called with the const lvalue") + { + v.push_back(tracker); + + THEN("the element is gracefully copy-constructed") + { + REQUIRE(v.size() == 1); + REQUIRE(v.back().value == 42); + REQUIRE(v.back().copy_constructed_count == 1); + REQUIRE(v.back().move_constructed_count == 0); + } + } + + WHEN("push_back is called with a const lvalue when capacity is sufficient") + { + v.reserve(10); + auto const current_capacity = v.capacity(); + v.push_back(tracker); + + THEN("the element is copy-constructed without reallocation") + { + REQUIRE(v.size() == 1); + REQUIRE(v.capacity() == current_capacity); + REQUIRE(v.back().value == 42); + REQUIRE(v.back().copy_constructed_count == 1); + REQUIRE(v.back().move_constructed_count == 0); + } + } + } +} diff --git a/libs/kstd/kstd/vformat.cpp b/libs/kstd/kstd/vformat.cpp new file mode 100644 index 0000000..b7c5121 --- /dev/null +++ b/libs/kstd/kstd/vformat.cpp @@ -0,0 +1,209 @@ +#include +#include + +#include +#include +#include +#include + +namespace kstd::bits::format +{ + + auto vformat_to(output_buffer & buffer, std::string_view format, format_args args) -> void + { + auto context = kstd::format_context{buffer, args}; + auto parse_context = kstd::format_parse_context{format, args.size()}; + + auto it = parse_context.begin(); + auto end = parse_context.end(); + + while (it != end) + { + if (*it != '{' && *it != '}') + { + auto start = it; + while (it != end && *it != '{' && *it != '}') + { + std::advance(it, 1); + } + parse_context.advance_to(it); + context.push(std::string_view(start, it - start)); + continue; + } + + if (*it == '{') + { + std::advance(it, 1); + if (it != end && *it == '{') + { + context.push('{'); + std::advance(it, 1); + parse_context.advance_to(it); + continue; + } + + parse_context.advance_to(it); + auto index = 0uz; + + if (it != end && *it >= '0' && *it <= '9') + { + while (it != end && *it >= '0' && *it <= '9') + { + index = index * 10 + static_cast(*it - '0'); + std::advance(it, 1); + } + parse_context.check_arg_id(index); + } + else + { + index = parse_context.next_arg_id(); + } + + if (it != end && *it == ':') + { + std::advance(it, 1); + } + + parse_context.advance_to(it); + + if (index < args.size()) + { + auto const & arg = args[index]; + switch (arg.type) + { + case kstd::bits::format::arg_type::boolean: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.boolean, context); + break; + } + case kstd::bits::format::arg_type::character: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.character, context); + break; + } + case kstd::bits::format::arg_type::integer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.integer, context); + break; + } + case kstd::bits::format::arg_type::unsigned_integer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.unsigned_integer, context); + break; + } + case kstd::bits::format::arg_type::string_view: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.string_view, context); + break; + } + case kstd::bits::format::arg_type::c_string: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.c_string, context); + break; + } + case kstd::bits::format::arg_type::pointer: + { + auto fmt = kstd::formatter{}; + auto const parsed = fmt.parse(parse_context); + parse_context.advance_to(parsed); + fmt.format(arg.value.pointer, context); + break; + } + case kstd::bits::format::arg_type::user_defined: + { + if (arg.value.user_defined.format) + { + arg.value.user_defined.format(arg.value.user_defined.pointer, parse_context, context); + } + else + { + context.push("{?}"); + } + break; + } + default: + { + context.push("{fmt-err: unknown-type}"); + break; + } + } + } + else + { + context.push("{fmt-err: bound}"); + } + + it = parse_context.begin(); + + if (it != end && *it == '}') + { + std::advance(it, 1); + parse_context.advance_to(it); + } + else + { + context.push("{fmt-err: unconsumed}"); + while (it != end && *it != '}') + { + std::advance(it, 1); + } + + if (it != end) + { + std::advance(it, 1); + parse_context.advance_to(it); + } + } + } + else if (*it == '}') + { + std::advance(it, 1); + if (it != end && *it == '}') + { + context.push('}'); + std::advance(it, 1); + parse_context.advance_to(it); + } + else + { + context.push("{fmt-err: unescaped}"); + parse_context.advance_to(it); + } + } + } + } + + auto string_writer::push(std::string_view text) -> void + { + m_result.append(text); + } + + auto string_writer::push(char character) -> void + { + m_result.push_back(character); + } + + auto string_writer::release() -> string && + { + return std::move(m_result); + } + +} // namespace kstd::bits::format \ No newline at end of file diff --git a/libs/kstd/src/libc/stdlib.cpp b/libs/kstd/src/libc/stdlib.cpp deleted file mode 100644 index a18fed0..0000000 --- a/libs/kstd/src/libc/stdlib.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include - -namespace kstd::libc -{ - - extern "C" - { - [[noreturn]] auto abort() -> void - { - kstd::os::abort(); - } - - [[noreturn, gnu::weak]] auto free(void *) -> void - { - kstd::os::panic("Tried to call free."); - } - } - -} // namespace kstd::libc \ No newline at end of file diff --git a/libs/kstd/src/libc/string.cpp b/libs/kstd/src/libc/string.cpp deleted file mode 100644 index c9fada1..0000000 --- a/libs/kstd/src/libc/string.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include - -#include -#include -#include -#include -#include - -namespace kstd::libc -{ - - 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}; - - for (std::size_t i = 0; i < size; ++i) - { - dest_span[i] = src_span[i]; - } - - return dest; - } - - 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}; - - for (std::size_t i = 0; i < size; ++i) - { - dest_span[i] = byte_value; - } - - return dest; - } - - 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}; - auto mismatched = std::ranges::mismatch(left_span, right_span); - - if (mismatched.in1 == left_span.end()) - { - return 0; - } - - return std::bit_cast(*mismatched.in1) - std::bit_cast(*mismatched.in2); - } - - 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}; - if (dest < src) - { - for (std::size_t i = 0; i < size; ++i) - { - dest_span[i] = src_span[i]; - } - } - else - { - for (std::size_t i = size; i > 0; --i) - { - dest_span[i - 1] = src_span[i - 1]; - } - } - return dest; - } - - auto strlen(char const * string) noexcept -> std::size_t - { - return std::distance(string, std::ranges::find(string, nullptr, '\0')); - } - -} // namespace kstd::libc \ No newline at end of file diff --git a/libs/kstd/src/mutex.cpp b/libs/kstd/src/mutex.cpp deleted file mode 100644 index 7387657..0000000 --- a/libs/kstd/src/mutex.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include - -#include - -#include - -namespace kstd -{ - - mutex::mutex() = default; - - mutex::~mutex() - { - if (m_locked.test(std::memory_order_relaxed)) - { - os::panic("[KSTD] Tried to destroy a locked mutex."); - } - } - - auto mutex::lock() -> void - { - while (!try_lock()) - { - asm volatile("nop"); - } - } - - auto mutex::try_lock() -> bool - { - return !m_locked.test_and_set(std::memory_order_acquire); - } - - auto mutex::unlock() -> void - { - m_locked.clear(std::memory_order_release); - } - -} // namespace kstd diff --git a/libs/kstd/src/os/error.cpp b/libs/kstd/src/os/error.cpp deleted file mode 100644 index f969cb5..0000000 --- a/libs/kstd/src/os/error.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include - -namespace kstd::os -{ - - [[gnu::weak, noreturn]] - auto abort() -> void - { - os::panic("Abort called."); - } - -} // namespace kstd::os \ No newline at end of file diff --git a/libs/kstd/src/vformat.cpp b/libs/kstd/src/vformat.cpp deleted file mode 100644 index b7c5121..0000000 --- a/libs/kstd/src/vformat.cpp +++ /dev/null @@ -1,209 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -namespace kstd::bits::format -{ - - auto vformat_to(output_buffer & buffer, std::string_view format, format_args args) -> void - { - auto context = kstd::format_context{buffer, args}; - auto parse_context = kstd::format_parse_context{format, args.size()}; - - auto it = parse_context.begin(); - auto end = parse_context.end(); - - while (it != end) - { - if (*it != '{' && *it != '}') - { - auto start = it; - while (it != end && *it != '{' && *it != '}') - { - std::advance(it, 1); - } - parse_context.advance_to(it); - context.push(std::string_view(start, it - start)); - continue; - } - - if (*it == '{') - { - std::advance(it, 1); - if (it != end && *it == '{') - { - context.push('{'); - std::advance(it, 1); - parse_context.advance_to(it); - continue; - } - - parse_context.advance_to(it); - auto index = 0uz; - - if (it != end && *it >= '0' && *it <= '9') - { - while (it != end && *it >= '0' && *it <= '9') - { - index = index * 10 + static_cast(*it - '0'); - std::advance(it, 1); - } - parse_context.check_arg_id(index); - } - else - { - index = parse_context.next_arg_id(); - } - - if (it != end && *it == ':') - { - std::advance(it, 1); - } - - parse_context.advance_to(it); - - if (index < args.size()) - { - auto const & arg = args[index]; - switch (arg.type) - { - case kstd::bits::format::arg_type::boolean: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.boolean, context); - break; - } - case kstd::bits::format::arg_type::character: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.character, context); - break; - } - case kstd::bits::format::arg_type::integer: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.integer, context); - break; - } - case kstd::bits::format::arg_type::unsigned_integer: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.unsigned_integer, context); - break; - } - case kstd::bits::format::arg_type::string_view: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.string_view, context); - break; - } - case kstd::bits::format::arg_type::c_string: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.c_string, context); - break; - } - case kstd::bits::format::arg_type::pointer: - { - auto fmt = kstd::formatter{}; - auto const parsed = fmt.parse(parse_context); - parse_context.advance_to(parsed); - fmt.format(arg.value.pointer, context); - break; - } - case kstd::bits::format::arg_type::user_defined: - { - if (arg.value.user_defined.format) - { - arg.value.user_defined.format(arg.value.user_defined.pointer, parse_context, context); - } - else - { - context.push("{?}"); - } - break; - } - default: - { - context.push("{fmt-err: unknown-type}"); - break; - } - } - } - else - { - context.push("{fmt-err: bound}"); - } - - it = parse_context.begin(); - - if (it != end && *it == '}') - { - std::advance(it, 1); - parse_context.advance_to(it); - } - else - { - context.push("{fmt-err: unconsumed}"); - while (it != end && *it != '}') - { - std::advance(it, 1); - } - - if (it != end) - { - std::advance(it, 1); - parse_context.advance_to(it); - } - } - } - else if (*it == '}') - { - std::advance(it, 1); - if (it != end && *it == '}') - { - context.push('}'); - std::advance(it, 1); - parse_context.advance_to(it); - } - else - { - context.push("{fmt-err: unescaped}"); - parse_context.advance_to(it); - } - } - } - } - - auto string_writer::push(std::string_view text) -> void - { - m_result.append(text); - } - - auto string_writer::push(char character) -> void - { - m_result.push_back(character); - } - - auto string_writer::release() -> string && - { - return std::move(m_result); - } - -} // namespace kstd::bits::format \ No newline at end of file diff --git a/libs/kstd/tests/include/kstd/tests/os_panic.hpp b/libs/kstd/tests/include/kstd/tests/os_panic.hpp deleted file mode 100644 index 4396a9f..0000000 --- a/libs/kstd/tests/include/kstd/tests/os_panic.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef KSTD_TESTS_OS_PANIC_HPP -#define KSTD_TESTS_OS_PANIC_HPP - -#include -#include -#include - -namespace kstd::tests -{ - - struct os_panic : std::runtime_error - { - os_panic(std::string message, std::source_location location) - : std::runtime_error{message} - , location(location) - {} - - std::source_location location; - }; - -} // namespace kstd::tests - -#endif \ No newline at end of file diff --git a/libs/kstd/tests/include/kstd/tests/test_types.hpp b/libs/kstd/tests/include/kstd/tests/test_types.hpp deleted file mode 100644 index 8231fd3..0000000 --- a/libs/kstd/tests/include/kstd/tests/test_types.hpp +++ /dev/null @@ -1,313 +0,0 @@ -#ifndef KSTD_TESTS_TEST_TYPES_HPP -#define KSTD_TESTS_TEST_TYPES_HPP - -#include -#include -#include - -namespace kstd::tests -{ - - //! A type tracking copy and move operations - //! - //! This type is designed to test move and copy semantics of standard library containers implemented in kstd. - struct special_member_tracker - { - //! A value indicating that the object was moved from. - constexpr auto static moved_from_v = -1; - - constexpr special_member_tracker() - : default_constructed_count{1} - {} - - //! Construct a new move tracker with the given value, if any. - constexpr special_member_tracker(int v = 0) - : value{v} - , value_constructed_count{1} - {} - - //! Construct a new move tracker by copying an existing one. - constexpr special_member_tracker(special_member_tracker const & other) - : value{other.value} - , copy_constructed_count{1} - {} - - //! Construct a new move tracker by moving from an existing one. - constexpr special_member_tracker(special_member_tracker && other) noexcept - : value{other.value} - , move_constructed_count{1} - { - other.value = moved_from_v; - } - - //! Copy assign a new move tracker from an existing one. - constexpr auto operator=(special_member_tracker const & other) -> special_member_tracker & - { - if (this != &other) - { - value = other.value; - ++copy_assigned_count; - ++other.copied_from_count; - } - return *this; - } - - //! Move assign a new move tracker from an existing one. - //! - //! This function ensures that the moved-from state is marked. - constexpr auto operator=(special_member_tracker && other) noexcept -> special_member_tracker & - { - if (this != &other) - { - value = other.value; - ++move_assigned_count; - other.value = moved_from_v; - ++other.moved_from_count; - } - return *this; - } - - ~special_member_tracker() - { - ++destroyed_count; - } - - auto reset_counts() -> void - { - default_constructed_count = 0; - copy_constructed_count = 0; - move_constructed_count = 0; - value_constructed_count = 0; - copy_assigned_count = 0; - move_assigned_count = 0; - destroyed_count = 0; - copied_from_count = 0; - moved_from_count = 0; - } - - //! A simple value to be able to track the move-from state. - int value{}; - //! A counter to track how many times an instance of this type was default constructed. - std::size_t default_constructed_count{0}; - //! A counter to track how many times an instance of this type was copy constructed. - std::size_t copy_constructed_count{0}; - //! A counter to track how many times an instance of this type was move constructed. - std::size_t move_constructed_count{0}; - //! A counter to track how many times an instance of this type was value constructed. - std::size_t value_constructed_count{0}; - //! A counter to track how many times an instance of this type was copy assigned. - std::size_t copy_assigned_count{0}; - //! A counter to track how many times an instance of this type was move assigned. - std::size_t move_assigned_count{0}; - //! A counter to track how many times an instance of this type was destroyed. - std::size_t destroyed_count{0}; - //! A counter to track how many times an instance of this type was copied from another instance. - mutable std::size_t copied_from_count{0}; - //! A counter to track how many times an instance of this type was moved from another instance. - std::size_t moved_from_count{0}; - }; - - //! A type that is not default constructible. - //! - //! This type is designed to test default construction semantics of standard library containers implemented in kstd. - struct non_default_constructible - { - //! A simple placeholder value. - int value{}; - - //! Construct a new non-default-constructible object with the given value. - constexpr explicit non_default_constructible(int v) - : value{v} - {} - - //! Compare two non-default-constructible objects for equality. - [[nodiscard]] constexpr auto operator==(non_default_constructible const & other) const -> bool - { - return value == other.value; - } - }; - - //! An allocator that tracks the number of allocations. - //! - //! This allocator is designed to test allocation semantics of standard library containers implemented in kstd. - //! - //! @tparam T The type of the elements to be allocated. - template - struct tracking_allocator - { - using value_type = T; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - - //! A pointer to a counter that is incremented on allocation. - //! - //! A pointer is used so that multiple allocators can share the same counter. - int * allocation_count{}; - - //! Construct a new tracking allocator referencing the given counter. - tracking_allocator(int * counter) - : allocation_count{counter} - {} - - //! Construct a new tracking allocator by copying from another tracking allocator. - //! - //! This constructor is templated to allow for conversion from allocators of different types. - //! - //! @tparam U The type of the elements to be allocated by the other allocator. - template - tracking_allocator(tracking_allocator const & other) noexcept - : allocation_count{other.allocation_count} - {} - - //! Allocate memory for n elements of type T. - //! - //! @param n The number of elements to allocate. - //! @return A pointer to the allocated memory. - [[nodiscard]] auto allocate(std::size_t n) -> T * - { - if (allocation_count != nullptr) - { - ++(*allocation_count); - } - return static_cast(::operator new(n * sizeof(T))); - } - - //! Deallocate memory for n elements of type T. - //! - //! @param p A pointer to the memory to deallocate. - //! @param n The number of elements to deallocate. - auto deallocate(T * p, std::size_t n) noexcept -> void - { - ::operator delete(p, n * sizeof(T)); - } - - //! Compare two tracking allocators for equality. - //! - //! Two allocators are considered equal if they reference the same allocation counter. - //! - //! @param other The other tracking allocator to compare to. - //! @return True if the two tracking allocators are equal, false otherwise. - [[nodiscard]] auto operator==(tracking_allocator const & other) const -> bool - { - return allocation_count == other.allocation_count; - } - - //! Compare two tracking_allocators for inequality. - //! - //! @param other The other tracking_allocator to compare to. - //! @return True if the two tracking_allocators are not equal, false otherwise. - [[nodiscard]] auto operator!=(tracking_allocator const & other) const -> bool - { - return allocation_count != other.allocation_count; - } - }; - - //! An allocator that propagates copy assignment. - //! - //! This allocator is designed to test copy assignment semantics of standard library containers implemented in kstd. - //! - //! @tparam T The type of the elements to be allocated. - template - struct propagating_allocator : tracking_allocator - { - //! A flag to indicate that the allocator propagates copy assignment. - using propagate_on_container_copy_assignment = std::true_type; - - //! Construct a new propagating allocator referencing the given counter. - //! - //! @param counter A pointer to a counter that is incremented on allocation. - //! @see tracking_allocator::tracking_allocator(int*) - propagating_allocator(int * counter) - : tracking_allocator{counter} - {} - - //! Construct a new propagating allocator by copying from another propagating allocator. - //! - //! This constructor is templated to allow for conversion from allocators of different types. - //! - //! @tparam U The type of the elements to be allocated by the other allocator. - //! @see tracking_allocator::tracking_allocator(tracking_allocator const&) - template - propagating_allocator(propagating_allocator const & other) noexcept - : tracking_allocator{other.allocation_count} - {} - }; - - //! A test input iterator. - //! - //! This iterator is designed to test input iterator semantics of standard library containers implemented in kstd. - struct test_input_iterator - { - using iterator_concept = std::input_iterator_tag; - using iterator_category = std::input_iterator_tag; - using difference_type = std::ptrdiff_t; - using value_type = int; - using reference = int const &; - using pointer = int const *; - - //! The current element pointed to by the iterator. - int const * current; - //! The number of elements in the range. - std::size_t count; - - explicit test_input_iterator() - : current{nullptr} - , count{0} - {} - - //! Construct a new test input iterator. - //! - //! @param current The current element pointed to by the iterator. - //! @param count The number of elements in the range. - explicit test_input_iterator(int const * current, std::size_t count) - : current{current} - , count{count} - {} - - //! Dereference the iterator to get the current element. - //! - //! @return The current element pointed to by the iterator. - [[nodiscard]] auto operator*() const -> int - { - return *current; - } - - //! Increment the iterator to point to the next element. - //! - //! @return A reference to the incremented iterator. - auto operator++() -> test_input_iterator & - { - ++current; - --count; - return *this; - } - - //! Increment the iterator to point to the next element. - auto operator++(int) -> void - { - ++*this; - } - - //! Compare two test input iterators for equality. - //! - //! @param other The other test input iterator to compare to. - //! @return True if the two test input iterators are equal, false otherwise. - [[nodiscard]] auto operator==(test_input_iterator const & other) const -> bool - { - if (current == nullptr && other.current == nullptr) - { - return true; - } - - if (current == nullptr || other.current == nullptr) - { - return count == other.count; - } - - return current == other.current && count == other.count; - } - }; - -} // namespace kstd::tests - -#endif diff --git a/libs/kstd/tests/src/flat_map.cpp b/libs/kstd/tests/src/flat_map.cpp deleted file mode 100644 index 2b793d9..0000000 --- a/libs/kstd/tests/src/flat_map.cpp +++ /dev/null @@ -1,314 +0,0 @@ -#include - -#include - -#include - -#include -#include -#include - -SCENARIO("Flat Map initialization and construction", "[flat_map]") -{ - GIVEN("An empty context") - { - WHEN("constructing by default") - { - auto map = kstd::flat_map{}; - - THEN("the Flat Map does not contain elements") - { - REQUIRE_FALSE(map.contains(1)); - } - } - } -} - -SCENARIO("Flat Map modifiers", "[flat_map]") -{ - GIVEN("An empty Flat Map") - { - auto map = kstd::flat_map{}; - - WHEN("emplacing a new element") - { - auto [it, inserted] = map.emplace(1, 100); - - THEN("the map contains the new element") - { - REQUIRE(inserted); - REQUIRE(map.contains(1)); - } - } - - WHEN("emplacing an existing element") - { - map.emplace(1, 100); - auto [it, inserted] = map.emplace(1, 200); - - THEN("the map does not insert the duplicate") - { - REQUIRE_FALSE(inserted); - REQUIRE(map.contains(1)); - } - } - } -} - -SCENARIO("Flat Map element access", "[flat_map]") -{ - GIVEN("A populated Flat Map") - { - auto map = kstd::flat_map{}; - map.emplace(1, 10); - map.emplace(2, 20); - map.emplace(3, 30); - - WHEN("accessing an existing element with at()") - { - auto & val = map.at(2); - - THEN("it returns a reference to the mapped value") - { - REQUIRE(val == 20); - } - - THEN("the mapped value can be modified") - { - val = 200; - REQUIRE(map.at(2) == 200); - } - } - - WHEN("accessing a non-existent element with at()") - { - THEN("it panics") - { - REQUIRE_THROWS_AS(map.at(4), kstd::tests::os_panic); - } - } - } - - GIVEN("A const populated Flat Map") - { - auto map_builder = kstd::flat_map{}; - map_builder.emplace(1, 10); - map_builder.emplace(2, 20); - auto const map = map_builder; - - WHEN("accessing an existing element with const at()") - { - auto const & val = map.at(2); - - THEN("it returns a const reference to the mapped value") - { - REQUIRE(val == 20); - } - } - - WHEN("accessing a non-existent element with const at()") - { - THEN("it panics") - { - REQUIRE_THROWS_AS(map.at(4), kstd::tests::os_panic); - } - } - } -} - -SCENARIO("Flat Map iterators", "[flat_map]") -{ - GIVEN("A populated Flat Map") - { - auto map = kstd::flat_map{}; - map.emplace(1, 10); - map.emplace(2, 20); - map.emplace(3, 30); - - WHEN("using forward iterators") - { - THEN("they navigate the elements in the correct forward order") - { - auto it = map.begin(); - REQUIRE(it != map.end()); - REQUIRE((*it).first == 1); - - ++it; - REQUIRE(it != map.end()); - REQUIRE((*it).first == 2); - - ++it; - REQUIRE(it != map.end()); - REQUIRE((*it).first == 3); - - ++it; - REQUIRE(it == map.end()); - } - - THEN("const forward iterators provide correct access") - { - auto it = map.cbegin(); - REQUIRE(it != map.cend()); - REQUIRE((*it).first == 1); - - ++it; - REQUIRE(it != map.cend()); - REQUIRE((*it).first == 2); - - ++it; - REQUIRE(it != map.cend()); - REQUIRE((*it).first == 3); - - ++it; - REQUIRE(it == map.cend()); - } - - THEN("assignment through the proxy modifies the mapped value") - { - auto it = map.begin(); - - *it = std::pair{1, 100}; - - REQUIRE(it->second == 100); - REQUIRE(map.at(1) == 100); - } - - THEN("structured bindings evaluate correctly") - { - auto it = map.cbegin(); - - auto [key, value] = *it; - - REQUIRE(key == 1); - REQUIRE(value == 10); - - STATIC_REQUIRE(std::is_same_v); - STATIC_REQUIRE(std::is_same_v); - } - } - - WHEN("using reverse iterators") - { - THEN("they navigate the elements in the correct reverse order") - { - auto it = map.rbegin(); - REQUIRE(it != map.rend()); - REQUIRE((*it).first == 3); - - ++it; - REQUIRE(it != map.rend()); - REQUIRE((*it).first == 2); - - ++it; - REQUIRE(it != map.rend()); - REQUIRE((*it).first == 1); - - ++it; - REQUIRE(it == map.rend()); - } - - THEN("const reverse iterators provide correct access") - { - auto it = map.crbegin(); - REQUIRE(it != map.crend()); - REQUIRE((*it).first == 3); - - ++it; - REQUIRE(it != map.crend()); - REQUIRE((*it).first == 2); - - ++it; - REQUIRE(it != map.crend()); - REQUIRE((*it).first == 1); - - ++it; - REQUIRE(it == map.crend()); - } - } - } - - GIVEN("an empty Flat Map") - { - auto map = kstd::flat_map{}; - - WHEN("getting iterators") - { - THEN("begin() equals end() and cbegin() equals cend()") - { - REQUIRE(map.begin() == map.end()); - REQUIRE(map.cbegin() == map.cend()); - } - - THEN("rbegin() equals rend() and crbegin() equals crend()") - { - REQUIRE(map.rbegin() == map.rend()); - REQUIRE(map.crbegin() == map.crend()); - } - } - } -} - -SCENARIO("Flat Map heterogeneous element access", "[flat_map]") -{ - GIVEN("A populated Flat Map with a transparent comparator") - { - auto map = kstd::flat_map>{}; - map.emplace(1, 10); - map.emplace(2, 20); - map.emplace(3, 30); - - WHEN("accessing an existing element with a different key type via at()") - { - long const key = 2L; - auto & val = map.at(key); - - THEN("it returns a reference to the mapped value") - { - REQUIRE(val == 20); - } - - THEN("the mapped value can be modified") - { - val = 200; - REQUIRE(map.at(2L) == 200); - } - } - - WHEN("accessing a non-existent element with a different key type via at()") - { - long const key = 4L; - THEN("it panics") - { - REQUIRE_THROWS_AS(map.at(key), kstd::tests::os_panic); - } - } - } - - GIVEN("A const populated Flat Map with a transparent comparator") - { - auto map_builder = kstd::flat_map>{}; - map_builder.emplace(1, 10); - map_builder.emplace(2, 20); - auto const map = map_builder; - - WHEN("accessing an existing element with a different key type via const at()") - { - long const key = 2L; - auto const & val = map.at(key); - - THEN("it returns a const reference to the mapped value") - { - REQUIRE(val == 20); - } - } - - WHEN("accessing a non-existent element with a different key type via const at()") - { - long const key = 4L; - THEN("it panics") - { - REQUIRE_THROWS_AS(map.at(key), kstd::tests::os_panic); - } - } - } -} diff --git a/libs/kstd/tests/src/format.cpp b/libs/kstd/tests/src/format.cpp deleted file mode 100644 index 624779a..0000000 --- a/libs/kstd/tests/src/format.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include - -#include - -#include -#include - -SCENARIO("Formatting to a new string", "[format]") -{ - GIVEN("a format string without any placeholders") - { - auto const & fmt = "This is a test"; - - WHEN("calling format with without any arguments.") - { - auto result = kstd::format(fmt); - - THEN("the result is the unmodified string") - { - REQUIRE(result == "This is a test"); - } - } - - WHEN("calling format with additional arguments") - { - auto result = kstd::format(fmt, 1, 2, 3); - - THEN("the result is the unmodified string") - { - REQUIRE(result == "This is a test"); - } - } - } - - GIVEN("a format string with placeholders") - { - auto const & fmt = "Here are some placeholders: {} {} {}"; - - WHEN("calling format with the same number of arguments as there are placeholders") - { - auto result = kstd::format(fmt, 1, true, 'a'); - - THEN("the result is the formatted string") - { - REQUIRE(result == "Here are some placeholders: 1 true a"); - } - } - - WHEN("calling format with too many arguments") - { - auto result = kstd::format(fmt, 2, false, 'b', 4, 5, 6); - - THEN("the result is the formatted string") - { - REQUIRE(result == "Here are some placeholders: 2 false b"); - } - } - } -} - -SCENARIO("Formatting to an output iterator", "[format]") -{ - auto buffer = std::ostringstream{}; - - GIVEN("a format string without any placeholders") - { - auto const & fmt = "This is a test"; - - WHEN("calling format with without any arguments.") - { - kstd::format_to(std::ostream_iterator{buffer}, fmt); - - THEN("the unmodified string is written to the iterator") - { - REQUIRE(buffer.str() == "This is a test"); - } - } - - WHEN("calling format with additional arguments") - { - kstd::format_to(std::ostream_iterator{buffer}, fmt, 1, 2, 3); - - THEN("the unmodified string is written to the iterator") - { - REQUIRE(buffer.str() == "This is a test"); - } - } - } - - GIVEN("a format string with placeholders") - { - auto const & fmt = "Here are some placeholders: {} {} {}"; - - WHEN("calling format with the same number of arguments as there are placeholders") - { - kstd::format_to(std::ostream_iterator{buffer}, fmt, 1, true, -100); - - THEN("the formatted string is written to the iterator") - { - REQUIRE(buffer.str() == "Here are some placeholders: 1 true -100"); - } - } - - WHEN("calling format with too many arguments") - { - kstd::format_to(std::ostream_iterator{buffer}, fmt, 2, false, -200, 4, 5, 6); - - THEN("the formatted string is written to the iterator") - { - REQUIRE(buffer.str() == "Here are some placeholders: 2 false -200"); - } - } - } -} \ No newline at end of file diff --git a/libs/kstd/tests/src/observer_ptr.cpp b/libs/kstd/tests/src/observer_ptr.cpp deleted file mode 100644 index 0c94892..0000000 --- a/libs/kstd/tests/src/observer_ptr.cpp +++ /dev/null @@ -1,360 +0,0 @@ -#include -#include - -#include - -#include -#include -#include -#include - -namespace -{ - struct Base - { - }; - - struct Derived : Base - { - }; - - struct Element - { - int value{}; - - constexpr auto operator<=>(Element const &) const noexcept = default; - }; -} // namespace - -SCENARIO("Observer Pointer initialization and construction", "[observer_ptr]") -{ - GIVEN("An empty context") - { - WHEN("constructing by default") - { - auto ptr = kstd::observer_ptr{}; - - THEN("the observer pointer is null") - { - REQUIRE_FALSE(ptr); - } - } - - WHEN("constructing from a nullptr") - { - auto ptr = kstd::observer_ptr{nullptr}; - - THEN("the observer pointer is null") - { - REQUIRE_FALSE(ptr); - } - } - - WHEN("constructing from a raw pointer") - { - auto value = 1; - auto ptr = kstd::observer_ptr{&value}; - - THEN("the observer pointer is not null") - { - REQUIRE(ptr); - } - - THEN("the observer pointer points to the correct object") - { - REQUIRE(&*ptr == &value); - } - } - - WHEN("copy constructing from an existing observer pointer") - { - auto value = 1; - auto ptr = kstd::observer_ptr{&value}; - auto copy = ptr; - - THEN("the new observer pointer points to the same object as the other observer pointer") - { - REQUIRE(&*copy == &value); - } - } - - WHEN("copy constructing from an existing observer pointer with a compatible type") - { - auto value = Derived{}; - auto ptr = kstd::observer_ptr(&value); - kstd::observer_ptr copy = ptr; - - THEN("the new observer pointer points to the same object as the other observer pointer") - { - REQUIRE(&*copy == &value); - } - } - - WHEN("copy assigning from an existing observer pointer") - { - auto value = 1; - auto ptr = kstd::observer_ptr{&value}; - auto copy = ptr; - - THEN("the new observer pointer points to the same object as the other observer pointer") - { - REQUIRE(&*copy == &value); - } - } - - WHEN("move constructing from an existing observer pointer") - { - auto value = 1; - auto ptr = kstd::observer_ptr{&value}; - auto copy = std::move(ptr); - - THEN("the new observer pointer points to the same object as the other observer pointer") - { - REQUIRE(&*copy == &value); - } - } - - WHEN("move assigning from an existing observer pointer") - { - auto value = 1; - auto ptr = kstd::observer_ptr{&value}; - auto copy = std::move(ptr); - - THEN("the new observer pointer points to the same object as the other observer pointer") - { - REQUIRE(&*copy == &value); - } - } - - WHEN("constructing an observer pointer using make_observer") - { - auto value = 1; - auto ptr = kstd::make_observer(&value); - - THEN("the observer pointer points to the correct object") - { - REQUIRE(&*ptr == &value); - } - - THEN("the observe pointer has the correct element type") - { - STATIC_REQUIRE(std::is_same_v>); - } - } - } -} - -SCENARIO("Observer pointer modifiers", "[observer_ptr]") -{ - GIVEN("A non-null observer pointer") - { - auto value = 1; - auto ptr = kstd::observer_ptr{&value}; - - WHEN("releasing the observer pointer") - { - auto raw_ptr = ptr.release(); - - THEN("the observer pointer is null") - { - REQUIRE_FALSE(ptr); - } - - THEN("the returned pointer points to the correct object") - { - REQUIRE(raw_ptr == &value); - } - } - - WHEN("resetting the observer pointer to nullptr") - { - ptr.reset(); - - THEN("the observer pointer is null") - { - REQUIRE_FALSE(ptr); - } - } - - WHEN("resetting the observer pointer to a new object") - { - auto other_value = 2; - ptr.reset(&other_value); - - THEN("the observer pointer points to the new object") - { - REQUIRE(&*ptr == &other_value); - } - } - - WHEN("swapping it with another observer pointer") - { - auto other_value = 2; - auto other_ptr = kstd::observer_ptr{&other_value}; - ptr.swap(other_ptr); - - THEN("the observer pointer points to the other object") - { - REQUIRE(&*ptr == &other_value); - } - - THEN("the other observer pointer points to the original object") - { - REQUIRE(&*other_ptr == &value); - } - } - - WHEN("using namespace-level swap to swap it with another observer pointer") - { - using std::swap; - auto other_value = 2; - auto other_ptr = kstd::observer_ptr{&other_value}; - swap(ptr, other_ptr); - - THEN("the observer pointer points to the other object") - { - REQUIRE(&*ptr == &other_value); - } - - THEN("the other observer pointer points to the original object") - { - REQUIRE(&*other_ptr == &value); - } - } - } -} - -SCENARIO("Observer pointer observers", "[observer_ptr]") -{ - GIVEN("A non-null observer pointer") - { - auto value = Element{1}; - auto ptr = kstd::observer_ptr{&value}; - - WHEN("getting the raw pointer") - { - auto raw_ptr = ptr.get(); - - THEN("the raw pointer points to the correct object") - { - REQUIRE(raw_ptr == &value); - } - } - - WHEN("dereferencing the observer pointer") - { - auto dereferenced = *ptr; - - THEN("the dereferenced value is the correct value") - { - REQUIRE(dereferenced == value); - } - } - - WHEN("writing through the observer pointer with the arrow operator") - { - ptr->value = 2; - - THEN("the value is updated") - { - REQUIRE(value.value == 2); - } - } - - WHEN("converting the observer pointer to a raw pointer") - { - auto raw_ptr = static_cast(ptr); - - THEN("the raw pointer points to the correct object") - { - REQUIRE(raw_ptr == &value); - } - } - - WHEN("checking the observer pointer as a boolean") - { - THEN("it returns true") - { - REQUIRE(static_cast(ptr)); - } - } - } - - GIVEN("A null observer pointer") - { - auto ptr = kstd::observer_ptr{}; - - WHEN("checking the observer pointer as a boolean") - { - THEN("it returns false") - { - REQUIRE_FALSE(static_cast(ptr)); - } - } - - WHEN("dereferencing the observer pointer") - { - THEN("the observer pointer panics") - { - REQUIRE_THROWS_AS(*ptr, kstd::tests::os_panic); - } - } - - WHEN("writing through the observer pointer with the arrow operator") - { - THEN("the observer pointer panics") - { - REQUIRE_THROWS_AS(ptr->value = 2, kstd::tests::os_panic); - } - } - } -} - -SCENARIO("Observer pointer comparisons", "[observer_ptr]") -{ - GIVEN("Observer pointers to elements of an array") - { - auto arr = std::array{1, 2}; - auto ptr1 = kstd::observer_ptr{&arr[0]}; - auto ptr2 = kstd::observer_ptr{&arr[1]}; - - WHEN("comparing the same observer pointer") - { - THEN("they are equal") - { - REQUIRE(ptr1 == ptr1); - REQUIRE((ptr1 <=> ptr1) == std::strong_ordering::equal); - } - } - - WHEN("comparing different observer pointers") - { - THEN("they are ordered correctly") - { - REQUIRE(ptr1 != ptr2); - REQUIRE(ptr1 < ptr2); - REQUIRE(ptr1 <= ptr2); - REQUIRE(ptr2 > ptr1); - REQUIRE(ptr2 >= ptr1); - REQUIRE((ptr1 <=> ptr2) == std::strong_ordering::less); - REQUIRE((ptr2 <=> ptr1) == std::strong_ordering::greater); - } - } - } - - GIVEN("A null observer pointer") - { - auto ptr = kstd::observer_ptr{}; - - WHEN("comparing with another null observer pointer") - { - auto other_ptr = kstd::observer_ptr{}; - - THEN("they are equal") - { - REQUIRE(ptr == other_ptr); - REQUIRE((ptr <=> other_ptr) == std::strong_ordering::equal); - } - } - } -} \ No newline at end of file diff --git a/libs/kstd/tests/src/os_panic.cpp b/libs/kstd/tests/src/os_panic.cpp deleted file mode 100644 index 0759763..0000000 --- a/libs/kstd/tests/src/os_panic.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#include -#include -#include - -namespace kstd::os -{ - - auto panic(std::string_view message, std::source_location location) - { - throw kstd::tests::os_panic{std::string{message}, location}; - } - -} // namespace kstd::os \ No newline at end of file diff --git a/libs/kstd/tests/src/string.cpp b/libs/kstd/tests/src/string.cpp deleted file mode 100644 index 53d7c9a..0000000 --- a/libs/kstd/tests/src/string.cpp +++ /dev/null @@ -1,445 +0,0 @@ -#include - -#include - -#include -#include -#include - -SCENARIO("String initialization and construction", "[string]") -{ - GIVEN("Nothing") - { - WHEN("constructing an empty string") - { - auto str = kstd::string{}; - - THEN("the size is zero and therefore the string is empty") - { - REQUIRE(str.empty()); - REQUIRE(str.size() == 0); - } - - THEN("the string is empty") - { - REQUIRE(str.view() == std::string_view{}); - } - } - } - - GIVEN("A string view") - { - auto view = std::string_view{"Blub Blub"}; - - WHEN("constructing a string from string_view") - { - auto str = kstd::string{view}; - - THEN("the string is not empty and has the same size as the view") - { - REQUIRE(!str.empty()); - REQUIRE(str.size() == view.size()); - } - - THEN("the string contains the same characters as the view") - { - REQUIRE(str.view() == view); - } - } - } - - GIVEN("A C-style string") - { - auto c_str = "Blub Blub"; - - WHEN("constructing a string from the C-style string") - { - auto str = kstd::string{c_str}; - - THEN("the string is not empty and has the same size as the C-style string") - { - REQUIRE(!str.empty()); - REQUIRE(str.size() == std::strlen(c_str)); - } - - THEN("the string contains the same characters as the C-style string") - { - REQUIRE(str.view() == c_str); - } - } - } - - GIVEN("A character") - { - auto ch = 'x'; - - WHEN("constructing a string from the character") - { - auto str = kstd::string{ch}; - - THEN("the string is not empty and has size 1") - { - REQUIRE(!str.empty()); - REQUIRE(str.size() == 1); - } - - THEN("the string contains the same character as the given character") - { - REQUIRE(str.view() == std::string_view{&ch, 1}); - } - } - } - - GIVEN("Another string") - { - auto other = kstd::string{"Blub Blub"}; - - WHEN("copy constructing a new string") - { - auto str = kstd::string{other}; - - THEN("the new string contains the same characters as the original") - { - REQUIRE(str.view() == other.view()); - } - } - - auto str = kstd::string{"Blub"}; - - WHEN("copy assigning another string") - { - auto other = kstd::string{"Blub Blub"}; - str = other; - - THEN("the string contains the same characters as the assigned string") - { - REQUIRE(str.view() == other.view()); - } - } - } - - GIVEN("A string") - { - auto str = kstd::string{"Hello"}; - - WHEN("copy assigning a string view") - { - auto view = std::string_view{"Hello, world!"}; - str = view; - - THEN("the string contains the same characters as the assigned view") - { - REQUIRE(str.view() == view); - } - } - } - - GIVEN("A string") - { - auto str = kstd::string{"Hello"}; - - WHEN("copy assigning a C-style string") - { - auto c_str = "Hello, world!"; - str = c_str; - - THEN("the string contains the same characters as the assigned C-style string") - { - REQUIRE(str.view() == c_str); - } - } - } -} - -SCENARIO("String concatenation", "[string]") -{ - GIVEN("Two strings") - { - auto str1 = kstd::string{"Blub"}; - auto str2 = kstd::string{" Blub"}; - - WHEN("appending the second string to the first string") - { - str1.append(str2); - - THEN("the first string contains the characters of both strings concatenated") - { - REQUIRE(str1.view() == "Blub Blub"); - } - - THEN("the size of the first string is the sum of the sizes of both strings") - { - REQUIRE(str1.size() == str2.size() + 4); - } - } - - WHEN("using operator+= to append the second string to the first string") - { - str1 += str2; - - THEN("the first string contains the characters of both strings concatenated") - { - REQUIRE(str1.view() == "Blub Blub"); - } - - THEN("the size of the first string is the sum of the sizes of both strings") - { - REQUIRE(str1.size() == str2.size() + 4); - } - } - - WHEN("using operator+ to concatenate the two strings into a new string") - { - auto str3 = str1 + str2; - - THEN("the new string contains the characters of both strings concatenated") - { - REQUIRE(str3.view() == "Blub Blub"); - } - - THEN("the size of the new string is the sum of the sizes of both strings") - { - REQUIRE(str3.size() == str1.size() + str2.size()); - } - } - } - - GIVEN("A string and a string view") - { - auto str = kstd::string{"Blub"}; - auto view = std::string_view{" Blub"}; - - WHEN("appending the string view to the string") - { - str.append(view); - - THEN("the string contains the characters of both the original string and the appended view concatenated") - { - REQUIRE(str.view() == "Blub Blub"); - } - - THEN("the size of the string is the sum of the sizes of the original string and the appended view") - { - REQUIRE(str.size() == view.size() + 4); - } - } - } - - GIVEN("A string and a character") - { - auto str = kstd::string{"Blub"}; - auto ch = '!'; - - WHEN("appending the character to the string") - { - str.push_back(ch); - - THEN("the string contains the original characters followed by the appended character") - { - REQUIRE(str.view() == "Blub!"); - } - - THEN("the size of the string is one more than the original size") - { - REQUIRE(str.size() == 5); - } - } - - WHEN("using operator+= to append the character to the string") - { - str += ch; - - THEN("the string contains the original characters followed by the appended character") - { - REQUIRE(str.view() == "Blub!"); - } - - THEN("the size of the string is one more than the original size") - { - REQUIRE(str.size() == 5); - } - } - } -} - -SCENARIO("String conversion and comparison", "[string]") -{ - GIVEN("An unsigned integer") - { - constexpr auto value1 = 12345u; - constexpr auto value2 = 0u; - - WHEN("converting the unsigned integer to a string") - { - auto str1 = kstd::to_string(value1); - auto str2 = kstd::to_string(value2); - - THEN("the string contains the decimal representation of the unsigned integer") - { - REQUIRE(str1.view() == "12345"); - REQUIRE(str2.view() == "0"); - } - } - } - - GIVEN("Two strings with the same characters") - { - auto str1 = kstd::string{"Blub Blub"}; - auto str2 = kstd::string{"Blub Blub"}; - - THEN("the strings are equal") - { - REQUIRE(str1 == str2); - } - - THEN("the strings are not unequal") - { - REQUIRE(!(str1 != str2)); - } - } - - GIVEN("A string and a string view with the same characters") - { - auto str = kstd::string{"Blub Blub"}; - auto view = std::string_view{"Blub Blub"}; - - THEN("the string and the string view are equal") - { - REQUIRE(str == view); - REQUIRE(view == str); - } - - THEN("the string and the string view are not unequal") - { - REQUIRE(!(str != view)); - REQUIRE(!(view != str)); - } - } -} - -SCENARIO("String clearing", "[string]") -{ - GIVEN("A non-empty string") - { - auto str = kstd::string{"Blub Blub"}; - - WHEN("clearing the string") - { - str.clear(); - - THEN("the string is empty and has size zero") - { - REQUIRE(str.empty()); - REQUIRE(str.size() == 0); - } - - THEN("the string contains no characters") - { - REQUIRE(str.view() == std::string_view{}); - } - } - } -} - -SCENARIO("String iteration", "[string]") -{ - GIVEN("A string") - { - auto str = kstd::string{"Blub"}; - - WHEN("iterating over the characters of the string as string_view using a range-based for loop") - { - kstd::string result; - - for (auto ch : str.view()) - { - result.push_back(ch); - } - - THEN("the iterated characters are the same as the characters in the string") - { - REQUIRE(result == str); - } - } - - WHEN("using std::ranges::for_each to iterate over the characters of the string") - { - kstd::string result; - - std::ranges::for_each(str, [&result](auto ch) { result.push_back(ch); }); - - THEN("the iterated characters are the same as the characters in the string") - { - REQUIRE(result == str); - } - } - - WHEN("using front and back to access the first and last characters of the string") - { - THEN("front returns the first character of the string") - { - REQUIRE(str.front() == 'B'); - } - - THEN("back returns the last character of the string") - { - REQUIRE(str.back() == 'b'); - } - } - } - - GIVEN("A const string") - { - auto const str = kstd::string{"Blub"}; - - WHEN("iterating over the characters of the string as string_view using a range-based for loop") - { - kstd::string result; - - for (auto ch : str.view()) - { - result.push_back(ch); - } - - THEN("the iterated characters are the same as the characters in the string") - { - REQUIRE(result == str.view()); - } - } - - WHEN("using front and back to access the first and last characters of the string") - { - THEN("front returns the first character of the string") - { - REQUIRE(str.front() == 'B'); - } - - THEN("back returns the last character of the string") - { - REQUIRE(str.back() == 'b'); - } - } - } - - GIVEN("An empty string") - { - auto str = kstd::string{}; - - WHEN("iterating over the characters of an empty string") - { - kstd::string result; - - for (auto ch : str.view()) - { - result.push_back(ch); - } - - THEN("no characters are iterated and the result is an empty string") - { - REQUIRE(result.empty()); - REQUIRE(result.size() == 0); - REQUIRE(result.view() == std::string_view{}); - } - } - } -} diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp deleted file mode 100644 index 4fb3559..0000000 --- a/libs/kstd/tests/src/vector.cpp +++ /dev/null @@ -1,2003 +0,0 @@ -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include - -SCENARIO("Vector initialization and construction", "[vector]") -{ - GIVEN("An empty context") - { - WHEN("constructing by default") - { - auto v = kstd::vector{}; - - THEN("the vector is empty") - { - REQUIRE(v.empty()); - } - - THEN("the size and capacity are zero") - { - REQUIRE(v.size() == 0); - REQUIRE(v.capacity() == 0); - } - } - - WHEN("constructing with a specific size") - { - auto v = kstd::vector(10); - - THEN("the vector is not empty") - { - REQUIRE_FALSE(v.empty()); - } - - THEN("the size is and capacity match the specified value") - { - REQUIRE(v.size() == 10); - REQUIRE(v.capacity() == 10); - } - } - - WHEN("constructing from an initializer list") - { - auto v = kstd::vector{1, 2, 3, 4, 5}; - - THEN("the vector is not empty") - { - REQUIRE_FALSE(v.empty()); - } - - THEN("the size is and capacity match the specified value") - { - REQUIRE(v.size() == 5); - REQUIRE(v.capacity() == 5); - } - - THEN("the elements are correctly initialized") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - REQUIRE(v[3] == 4); - REQUIRE(v[4] == 5); - } - } - } - - GIVEN("A non-empty range") - { - auto range = std::array{1, 2, 3}; - - WHEN("constructing from a random-access iterator range") - { - auto v = kstd::vector{std::begin(range), std::end(range)}; - - THEN("the vector is not empty") - { - REQUIRE_FALSE(v.empty()); - } - - THEN("the size and capacity match the range size") - { - REQUIRE(v.size() == std::size(range)); - REQUIRE(v.capacity() == std::size(range)); - } - - THEN("the elements are correctly initialized") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - } - - WHEN("constructing from a range") - { - auto v = kstd::vector{kstd::from_range, range}; - - THEN("the vector is not empty") - { - REQUIRE_FALSE(v.empty()); - } - - THEN("the size and capacity match the range size") - { - REQUIRE(v.size() == std::ranges::size(range)); - REQUIRE(v.capacity() == std::ranges::size(range)); - } - - THEN("the elements are correctly initialized") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - } - } - - GIVEN("A populated vector") - { - auto source = kstd::vector{1, 2, 3, 4, 5}; - - WHEN("copy constructing a new vector") - { - auto copy = kstd::vector(source); - - THEN("the copy matches the original") - { - REQUIRE(copy.size() == source.size()); - REQUIRE(copy.capacity() == source.capacity()); - REQUIRE(copy[0] == 1); - REQUIRE(copy[1] == 2); - REQUIRE(copy[2] == 3); - REQUIRE(copy[3] == 4); - REQUIRE(copy[4] == 5); - } - - THEN("the original is left unchanged") - { - REQUIRE(source.size() == 5); - REQUIRE(source.capacity() == 5); - REQUIRE(source[0] == 1); - REQUIRE(source[1] == 2); - REQUIRE(source[2] == 3); - REQUIRE(source[3] == 4); - REQUIRE(source[4] == 5); - } - } - - WHEN("move constructing a new vector") - { - auto moved = kstd::vector(std::move(source)); - - THEN("The new vector has the original elements") - { - REQUIRE(moved.size() == 5); - REQUIRE(moved.capacity() == 5); - REQUIRE(moved[0] == 1); - REQUIRE(moved[1] == 2); - REQUIRE(moved[2] == 3); - REQUIRE(moved[3] == 4); - REQUIRE(moved[4] == 5); - } - - THEN("The original vector is left in a valid but unspecified state") - { - REQUIRE(source.empty()); - REQUIRE(source.size() == 0); - REQUIRE(source.capacity() == 0); - } - } - } -} - -SCENARIO("Vector element access", "[vector]") -{ - GIVEN("A populated vector") - { - auto v = kstd::vector{10, 20, 30}; - - WHEN("accessing elements for reading") - { - THEN("operator[] and at() return the correct elements") - { - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - - REQUIRE(v.at(0) == 10); - REQUIRE(v.at(1) == 20); - REQUIRE(v.at(2) == 30); - } - - THEN("front() and back() return the first and last elements") - { - REQUIRE(v.front() == 10); - REQUIRE(v.back() == 30); - } - - THEN("data() return a pointer to the contiguous storage") - { - auto ptr = v.data(); - REQUIRE(ptr); - REQUIRE(ptr[0] == 10); - REQUIRE(ptr[1] == 20); - REQUIRE(ptr[2] == 30); - } - - THEN("accessing out of bounds elements panics") - { - REQUIRE_THROWS_AS(v.at(3), kstd::tests::os_panic); - } - } - - WHEN("accessing elements for writing") - { - v[0] = 100; - v.at(1) = 200; - v.back() = 300; - - THEN("the elements are correctly modified") - { - REQUIRE(v[0] == 100); - REQUIRE(v[1] == 200); - REQUIRE(v[2] == 300); - } - } - } -} - -SCENARIO("Vector iterators", "[vector]") -{ - GIVEN("A populated vector") - { - auto v = kstd::vector{1, 2, 3}; - - WHEN("using forward iterators") - { - THEN("they navigate the elements in the correct forward order") - { - auto it = v.begin(); - REQUIRE(it != v.end()); - REQUIRE(*it == 1); - - ++it; - REQUIRE(it != v.end()); - REQUIRE(*it == 2); - - ++it; - REQUIRE(it != v.end()); - REQUIRE(*it == 3); - - ++it; - REQUIRE(it == v.end()); - } - - THEN("const forward iterators provide correct access") - { - auto it = v.cbegin(); - REQUIRE(it != v.cend()); - REQUIRE(*it == 1); - - ++it; - REQUIRE(it != v.cend()); - REQUIRE(*it == 2); - - ++it; - REQUIRE(it != v.cend()); - REQUIRE(*it == 3); - - ++it; - REQUIRE(it == v.cend()); - } - } - - WHEN("using reverse iterators") - { - THEN("they navigate the elements in the correct reverse order") - { - auto it = v.rbegin(); - REQUIRE(it != v.rend()); - REQUIRE(*it == 3); - - ++it; - REQUIRE(it != v.rend()); - REQUIRE(*it == 2); - - ++it; - REQUIRE(it != v.rend()); - REQUIRE(*it == 1); - - ++it; - REQUIRE(it == v.rend()); - } - - THEN("const reverse iterators provide correct access") - { - auto it = v.crbegin(); - REQUIRE(it != v.crend()); - REQUIRE(*it == 3); - - ++it; - REQUIRE(it != v.crend()); - REQUIRE(*it == 2); - - ++it; - REQUIRE(it != v.crend()); - REQUIRE(*it == 1); - - ++it; - REQUIRE(it == v.crend()); - } - } - } - - GIVEN("an empty vector") - { - auto v = kstd::vector{}; - - WHEN("getting iterators") - { - THEN("begin() equals end() and cbegin() equals cend()") - { - REQUIRE(v.begin() == v.end()); - REQUIRE(v.cbegin() == v.cend()); - } - - THEN("rbegin() equals rend() and crbegin() equals crend()") - { - REQUIRE(v.rbegin() == v.rend()); - REQUIRE(v.crbegin() == v.crend()); - } - } - } -} - -SCENARIO("Vector capacity management", "[vector]") -{ - GIVEN("An empty vector") - { - auto v = kstd::vector{}; - - WHEN("reserving space") - { - v.reserve(10); - - THEN("the capacity is at least the reserved amount") - { - REQUIRE(v.capacity() >= 10); - } - - THEN("the size is still zero") - { - REQUIRE(v.size() == 0); - } - - THEN("the vector is still empty") - { - REQUIRE(v.empty()); - } - } - - WHEN("reserving space less than or equal to current capacity") - { - v.reserve(10); - auto const current_capacity = v.capacity(); - v.reserve(5); - - THEN("the capacity remains unchanged") - { - REQUIRE(v.capacity() == current_capacity); - } - } - - WHEN("reserving space greater than max_size") - { - THEN("a panic is triggered") - { - REQUIRE_THROWS_AS(v.reserve(v.max_size() + 1), kstd::tests::os_panic); - } - } - } - - GIVEN("A populated vector with excess capacity") - { - auto v = kstd::vector{1, 2, 3}; - v.reserve(10); - - REQUIRE(v.capacity() == 10); - - WHEN("calling shrink_to_fit") - { - v.shrink_to_fit(); - - THEN("the capacity is reduced to match the size") - { - REQUIRE(v.capacity() == 3); - REQUIRE(v.size() == 3); - } - - THEN("the elements remain unchanged") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - } - } -} - -SCENARIO("Vector modifiers", "[vector]") -{ - GIVEN("An empty vector") - { - auto v = kstd::vector{}; - - WHEN("push_back is called with a value") - { - v.push_back(10); - - THEN("the element is added and the size and capacity increase") - { - REQUIRE(v.size() == 1); - REQUIRE(v.capacity() >= 1); - REQUIRE(v.back() == 10); - } - } - - WHEN("emplace_back is called with constructor arguments") - { - v.emplace_back(20); - - THEN("the element is added and the size and capacity increase") - { - REQUIRE(v.size() == 1); - REQUIRE(v.capacity() >= 1); - REQUIRE(v.back() == 20); - } - } - - WHEN("elements are added while capacity is sufficient") - { - v.reserve(10); - auto const capacity = v.capacity(); - - v.push_back(10); - v.emplace_back(20); - - THEN("the elements are added without reallocation") - { - REQUIRE(v.size() == 2); - REQUIRE(v.capacity() == capacity); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - } - } - - WHEN("emplace is called with the end iterator and constructor arguments") - { - v.emplace(v.end(), 20); - - THEN("the element is appended and the size and capacity increase") - { - REQUIRE(v.size() == 1); - REQUIRE(v.capacity() >= 1); - REQUIRE(v.back() == 20); - } - } - - WHEN("emplace is called while capacity is sufficient") - { - v.reserve(10); - auto const capacity = v.capacity(); - - v.emplace(v.end(), 20); - - THEN("the element is appended without reallocation") - { - REQUIRE(v.size() == 1); - REQUIRE(v.capacity() == capacity); - REQUIRE(v.back() == 20); - } - } - - WHEN("inserting an element") - { - auto it = v.insert(v.cbegin(), 40); - - THEN("the size and capacity increase and the element is inserted") - { - REQUIRE(v.size() == 1); - REQUIRE(v.capacity() >= 1); - REQUIRE(v[0] == 40); - REQUIRE(it == v.begin()); - } - } - - WHEN("inserting an lvalue element") - { - auto const value = 40; - auto it = v.insert(v.cbegin(), value); - - THEN("the size and capacity increase and the element is inserted") - { - REQUIRE(v.size() == 1); - REQUIRE(v.capacity() >= 1); - REQUIRE(v[0] == 40); - REQUIRE(it == v.begin()); - } - } - - WHEN("appending a range") - { - auto const range = std::views::iota(0, 3); - v.append_range(range); - - THEN("the size increases") - { - REQUIRE(v.size() == 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= 3); - } - - THEN("the elements are appended") - { - REQUIRE(v[0] == 0); - REQUIRE(v[1] == 1); - REQUIRE(v[2] == 2); - } - } - - WHEN("appending from an input range") - { - auto const arr = std::array{1, 2, 3}; - auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; - auto const last = kstd::tests::test_input_iterator{}; - - v.append_range(std::ranges::subrange{first, last}); - - THEN("the size increases") - { - REQUIRE(v.size() == 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= 3); - } - - THEN("the elements are appended") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - } - - WHEN("appending from an input range with sufficient capacity") - { - v.reserve(3); - auto const arr = std::array{1, 2, 3}; - auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; - auto const last = kstd::tests::test_input_iterator{}; - - v.append_range(std::ranges::subrange{first, last}); - - THEN("the size increases") - { - REQUIRE(v.size() == 3); - } - - THEN("the capacity stays the same") - { - REQUIRE(v.capacity() == 3); - } - - THEN("the elements are appended") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - } - - WHEN("resizing the vector to a greater size") - { - v.resize(3); - - THEN("the size and capacity increase and the elements are value initialized") - { - REQUIRE(v.size() == 3); - REQUIRE(v.capacity() >= 3); - REQUIRE(v[0] == 0); - REQUIRE(v[1] == 0); - REQUIRE(v[2] == 0); - } - } - - WHEN("resizing the vector to a greater size with initial value") - { - v.resize(3, 2); - - THEN("the size and capacity increase and the elements are initialized to the given value") - { - REQUIRE(v.size() == 3); - REQUIRE(v.capacity() >= 3); - REQUIRE(v[0] == 2); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 2); - } - } - - WHEN("inserting a range at the beginning") - { - auto const arr = std::array{1, 2, 3}; - auto it = v.insert_range(v.begin(), arr); - - THEN("the size increases") - { - REQUIRE(v.size() == 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= 3); - } - - THEN("the elements are inserted") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - - THEN("the returned iterator points to the beginning of the inserted range") - { - REQUIRE(it == v.begin()); - } - } - - WHEN("inserting a range at the end") - { - auto const arr = std::array{1, 2, 3}; - auto it = v.insert_range(v.end(), arr); - - THEN("the size increases") - { - REQUIRE(v.size() == 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= 3); - } - - THEN("the elements are inserted") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - - THEN("the returned iterator points to the beginning of the inserted range") - { - REQUIRE(it == v.begin()); - } - } - - WHEN("inserting from an input range without sufficient capacity") - { - auto const arr = std::array{1, 2, 3}; - auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; - auto const last = kstd::tests::test_input_iterator{}; - auto it = v.insert_range(v.begin(), std::ranges::subrange{first, last}); - - THEN("the size increases") - { - REQUIRE(v.size() == 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= 3); - } - - THEN("the elements are inserted") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - } - - THEN("the returned iterator points to the beginning of the inserted range") - { - REQUIRE(it == v.begin()); - } - } - } - - GIVEN("A populated vector") - { - auto v = kstd::vector{10, 20, 30}; - auto initial_capacity = v.capacity(); - - WHEN("push_back is called") - { - v.push_back(40); - - THEN("the element is added and the size and capacity increase") - { - REQUIRE(v.size() == 4); - REQUIRE(v.capacity() >= initial_capacity); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - REQUIRE(v[3] == 40); - } - } - - WHEN("emplace_back is called with constructor arguments") - { - v.emplace_back(40); - - THEN("the element is added and the size and capacity increase") - { - REQUIRE(v.size() == 4); - REQUIRE(v.capacity() >= initial_capacity); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - REQUIRE(v[3] == 40); - } - } - - WHEN("emplace is called with an iterator and constructor arguments") - { - auto it = v.emplace(v.begin() + 2, 25); - - THEN("the element is inserted and the size and capacity increase") - { - REQUIRE(v.size() == 4); - REQUIRE(v.capacity() >= initial_capacity); - REQUIRE(v.at(2) == 25); - } - - THEN("the returned iterator points to the inserted element") - { - REQUIRE(it == v.cbegin() + 2); - REQUIRE(*it == 25); - } - } - - WHEN("emplace is called with an iterator and sufficient capacity") - { - v.reserve(v.size() + 1); - - auto it = v.emplace(v.begin() + 2, 25); - - THEN("the element is inserted and the size and capacity increase") - { - REQUIRE(v.size() == 4); - REQUIRE(v.capacity() >= initial_capacity); - REQUIRE(v.at(2) == 25); - } - - THEN("the returned iterator points to the inserted element") - { - REQUIRE(it == v.cbegin() + 2); - REQUIRE(*it == 25); - } - } - - WHEN("push_back is called with a reference to an internal element") - { - v.shrink_to_fit(); - auto const original_value = v[0]; - - v.push_back(v[0]); - - THEN("reallocation handles the internal reference safely without dangling") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == original_value); - REQUIRE(v.back() == original_value); - } - } - - WHEN("pop_back is called") - { - v.pop_back(); - - THEN("the last element is removed and the size decreases") - { - REQUIRE(v.size() == 2); - REQUIRE(v.capacity() == initial_capacity); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - } - } - - WHEN("clear is called") - { - v.clear(); - - THEN("the vector is empty") - { - REQUIRE(v.empty()); - REQUIRE(v.size() == 0); - REQUIRE(v.capacity() == initial_capacity); - } - } - - WHEN("inserting at the beginning") - { - auto it = v.insert(v.cbegin(), 5); - - THEN("the element is inserted at the front") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 5); - REQUIRE(v[1] == 10); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - REQUIRE(it == v.begin()); - } - } - - WHEN("inserting in the middle") - { - auto it = v.insert(v.cbegin() + 1, 15); - - THEN("the element is inserted in the middle") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 15); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting at the end") - { - auto it = v.insert(v.cend(), 40); - - THEN("the element is inserted at the back") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - REQUIRE(v[3] == 40); - REQUIRE(it == v.begin() + 3); - } - } - - WHEN("inserting an lvalue at the end") - { - auto const value = 40; - auto it = v.insert(v.cend(), value); - - THEN("the element is inserted at the back") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - REQUIRE(v[3] == 40); - REQUIRE(it == v.begin() + 3); - } - } - - WHEN("inserting when capacity is sufficient") - { - v.reserve(10); - auto const capacity = v.capacity(); - - auto it = v.insert(v.cbegin() + 1, 15); - - THEN("the element is added without reallocation") - { - REQUIRE(v.size() == 4); - REQUIRE(v.capacity() == capacity); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 15); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting a reference to an existing element with reallocation") - { - v.shrink_to_fit(); - REQUIRE(v.capacity() == v.size()); - auto it = v.insert(v.cbegin() + 1, v[2]); - - THEN("the element is correctly copied and inserted") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 30); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting a reference to an existing element without reallocation") - { - v.reserve(10); - REQUIRE(v.capacity() > v.size()); - auto it = v.insert(v.cbegin() + 1, v[2]); - - THEN("the element is correctly copied and inserted") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 30); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting an rvalue reference to an existing element with reallocation") - { - v.shrink_to_fit(); - REQUIRE(v.capacity() == v.size()); - auto it = v.insert(v.cbegin() + 1, std::move(v[2])); - - THEN("the element is correctly moved and inserted") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 30); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting an rvalue reference to an existing element without reallocation") - { - v.reserve(10); - REQUIRE(v.capacity() > v.size()); - auto it = v.insert(v.cbegin() + 1, std::move(v[2])); - - THEN("the element is correctly moved and inserted") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 30); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("erasing the first element") - { - auto it = v.erase(v.cbegin()); - - THEN("the first element is removed and the size decreases") - { - REQUIRE(v.size() == 2); - REQUIRE(v[0] == 20); - REQUIRE(v[1] == 30); - REQUIRE(it == v.begin()); - } - } - - WHEN("erasing a middle element") - { - auto it = v.erase(v.cbegin() + 1); - - THEN("the middle element is removed and the size decreases") - { - REQUIRE(v.size() == 2); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("erasing the last element") - { - auto it = v.erase(v.cend() - 1); - - THEN("the last element is removed and the size decreases") - { - REQUIRE(v.size() == 2); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(it == v.end()); - } - } - - WHEN("erasing the end() iterator") - { - THEN("a panic is triggered") - { - REQUIRE_THROWS_AS(v.erase(v.end()), kstd::tests::os_panic); - } - } - - WHEN("erasing a range of elements") - { - auto it = v.erase(v.cbegin() + 1, v.cend() - 1); - - THEN("the specified range is removed and the size decreases") - { - REQUIRE(v.size() == 2); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("erasing an empty range") - { - auto it = v.erase(v.cbegin() + 1, v.cbegin() + 1); - - THEN("the vector is unchanged") - { - REQUIRE(v.size() == 3); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("appending a range") - { - auto initial_size = v.size(); - v.append_range(std::views::iota(0, 3)); - - THEN("capacity is increased") - { - REQUIRE(v.capacity() >= initial_capacity); - } - - THEN("size is increased") - { - REQUIRE(v.size() == initial_size + 3); - } - - THEN("the elements are appended") - { - REQUIRE(v[initial_size + 0] == 0); - REQUIRE(v[initial_size + 1] == 1); - REQUIRE(v[initial_size + 2] == 2); - } - } - - WHEN("resizing the vector to a greater size") - { - auto initial_size = v.size(); - v.resize(initial_size + 3); - - THEN("the size and capacity increase and the elements are value initialized") - { - REQUIRE(v.size() == initial_size + 3); - REQUIRE(v.capacity() >= initial_size + 3); - REQUIRE(v[initial_size + 0] == 0); - REQUIRE(v[initial_size + 1] == 0); - REQUIRE(v[initial_size + 2] == 0); - } - } - - WHEN("resizing the vector to a greater size with initial value") - { - auto initial_size = v.size(); - v.resize(initial_size + 3, 2); - - THEN("the size and capacity increase and the elements are initialized to the given value") - { - REQUIRE(v.size() == initial_size + 3); - REQUIRE(v.capacity() >= initial_size + 3); - REQUIRE(v[initial_size + 0] == 2); - REQUIRE(v[initial_size + 1] == 2); - REQUIRE(v[initial_size + 2] == 2); - } - } - - WHEN("resizing the vector to a smaller size") - { - v.resize(1); - - THEN("the size decreases and the elements are destroyed") - { - REQUIRE(v.size() == 1); - REQUIRE(v[0] == 10); - } - } - - WHEN("inserting an empty range") - { - auto initial_size = v.size(); - auto it = v.insert_range(v.begin(), std::views::empty); - - THEN("the size does not change") - { - REQUIRE(v.size() == initial_size); - } - - THEN("the capacity does not change") - { - REQUIRE(v.capacity() == initial_capacity); - } - - THEN("the content is unchanged") - { - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - } - - THEN("the returned iterator points to the position of insertion") - { - REQUIRE(it == v.begin()); - } - } - - WHEN("inserting a range at the beginning") - { - auto initial_size = v.size(); - auto const arr = std::array{1, 2, 3}; - auto it = v.insert_range(v.begin(), arr); - - THEN("the size increases") - { - REQUIRE(v.size() == initial_size + 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= initial_size + 3); - } - - THEN("the elements are inserted") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - REQUIRE(v[3] == 10); - REQUIRE(v[4] == 20); - REQUIRE(v[5] == 30); - } - - THEN("the returned iterator points to the beginning of the inserted range") - { - REQUIRE(it == v.begin()); - } - } - - WHEN("inserting a range at the end") - { - auto initial_size = v.size(); - auto const arr = std::array{1, 2, 3}; - auto it = v.insert_range(v.end(), arr); - - THEN("the size increases") - { - REQUIRE(v.size() == initial_size + 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= initial_size + 3); - } - - THEN("the elements are inserted") - { - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 20); - REQUIRE(v[2] == 30); - REQUIRE(v[3] == 1); - REQUIRE(v[4] == 2); - REQUIRE(v[5] == 3); - } - - THEN("the returned iterator points to the beginning of the inserted range") - { - REQUIRE(it == v.begin() + initial_size); - } - } - - WHEN("inserting a range that causes reallocation") - { - auto initial_size = v.size(); - auto const arr = std::array{1, 2, 3}; - auto it = v.insert_range(v.begin() + 1, arr); - - THEN("the size increases") - { - REQUIRE(v.size() == initial_size + 3); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= initial_size + 3); - } - - THEN("the elements are inserted") - { - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 1); - REQUIRE(v[2] == 2); - REQUIRE(v[3] == 3); - REQUIRE(v[4] == 20); - REQUIRE(v[5] == 30); - } - - THEN("the returned iterator points to the beginning of the inserted range") - { - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting fewer elements than the suffix without reallocation") - { - v.reserve(10); - auto const capacity = v.capacity(); - auto const arr = std::array{1}; - auto it = v.insert_range(v.begin() + 1, arr); - - THEN("the elements are correctly placed") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 1); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - } - - THEN("no reallocation occurs") - { - REQUIRE(v.capacity() == capacity); - } - - THEN("the returned iterator points to the inserted element") - { - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting fewer elements than the suffix without sufficient capacity") - { - v.shrink_to_fit(); - auto const arr = std::array{1}; - auto it = v.insert_range(v.begin() + 1, arr); - - THEN("the elements are correctly placed") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0] == 10); - REQUIRE(v[1] == 1); - REQUIRE(v[2] == 20); - REQUIRE(v[3] == 30); - } - - THEN("the returned iterator points to the inserted element") - { - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("inserting from an input range without sufficient capacity") - { - auto const arr = std::array{1, 2, 3}; - auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; - auto const last = kstd::tests::test_input_iterator{}; - auto it = v.insert_range(v.begin(), std::ranges::subrange{first, last}); - - THEN("the size increases") - { - REQUIRE(v.size() == 6); - } - - THEN("the capacity increases") - { - REQUIRE(v.capacity() >= 6); - } - - THEN("the elements are inserted") - { - REQUIRE(v[0] == 1); - REQUIRE(v[1] == 2); - REQUIRE(v[2] == 3); - REQUIRE(v[3] == 10); - REQUIRE(v[4] == 20); - REQUIRE(v[5] == 30); - } - - THEN("the returned iterator points to the beginning of the inserted range") - { - REQUIRE(it == v.begin()); - } - } - } -} - -SCENARIO("Vector comparison", "[vector]") -{ - GIVEN("Two identical vectors") - { - auto v1 = kstd::vector{1, 2, 3}; - auto v2 = kstd::vector{1, 2, 3}; - - WHEN("comparing for equality") - { - THEN("the vectors are equal") - { - REQUIRE(v1 == v2); - } - - THEN("the vectors and not not-equal") - { - REQUIRE_FALSE(v1 != v2); - } - } - - WHEN("comparing using the spaceship operator") - { - THEN("the vectors are equivalent") - { - REQUIRE((v1 <=> v2) == 0); - REQUIRE(v1 <= v2); - REQUIRE(v1 >= v2); - } - } - } - - GIVEN("Two vectors of different sizes") - { - auto v1 = kstd::vector{1, 2, 3}; - auto v2 = kstd::vector{1, 2, 3, 4}; - - WHEN("comparing for equality") - { - THEN("the vectors are not equal") - { - REQUIRE_FALSE(v1 == v2); - } - - THEN("the vectors are not-equal") - { - REQUIRE(v1 != v2); - } - } - - WHEN("comparing for ordering") - { - THEN("the shorter vector evaluates as less than the longer vector") - { - REQUIRE(v1 < v2); - REQUIRE(v2 > v1); - } - } - } - - GIVEN("Two vectors of the same size but different elements") - { - auto v1 = kstd::vector{1, 2, 3}; - auto v2 = kstd::vector{1, 2, 4}; - - WHEN("comparing for ordering") - { - THEN("they are ordered lexicographically") - { - REQUIRE(v1 < v2); - REQUIRE(v2 > v1); - } - } - } -} - -SCENARIO("Vector with non-default-constructible types", "[vector]") -{ - GIVEN("A type without a default constructor") - { - WHEN("constructing an empty vector") - { - auto v = kstd::vector{}; - - THEN("the vector is empty") - { - REQUIRE(v.empty()); - } - } - - WHEN("using emplace_back") - { - auto v = kstd::vector{}; - v.emplace_back(40); - - THEN("the element is added and the size and capacity increase") - { - REQUIRE(v.size() == 1); - REQUIRE(v.back() == kstd::tests::non_default_constructible{40}); - } - } - } -} - -SCENARIO("Vector with custom allocator", "[vector]") -{ - GIVEN("a tracking allocator acting as the vector's memory manager") - { - auto allocations = 0; - auto allocator = kstd::tests::tracking_allocator{&allocations}; - - WHEN("a vector uses this allocator to allocate memory") - { - auto v = kstd::vector>(allocator); - REQUIRE(allocations == 0); - - v.reserve(10); - - THEN("the allocator was used to allocate memory") - { - REQUIRE(allocations > 0); - } - } - } -} - -SCENARIO("Vector modifier move semantics", "[vector]") -{ - GIVEN("An empty vector and a move tracker element") - { - auto v = kstd::vector{}; - auto tracker = kstd::tests::special_member_tracker{40}; - - WHEN("push_back is called with the move tracker") - { - v.push_back(std::move(tracker)); - - THEN("the element is added and the size and capacity increase") - { - REQUIRE(v.size() == 1); - REQUIRE(v.back().move_constructed_count == 1); - REQUIRE(v.back().copy_constructed_count == 0); - REQUIRE(v.back().value == 40); - } - - THEN("the original tracker is left in a valid but unspecified state") - { - REQUIRE(tracker.value == -1); - } - } - } - - GIVEN("An empty vector") - { - auto v = kstd::vector{}; - - WHEN("emplace_back is called with constructor arguments") - { - v.emplace_back(40); - - THEN("the element is constructed directly, without moves or copies") - { - REQUIRE(v.size() == 1); - REQUIRE(v.back().move_constructed_count == 0); - REQUIRE(v.back().copy_constructed_count == 0); - REQUIRE(v.back().value == 40); - } - } - } - - GIVEN("A populated vector of move trackers") - { - auto v = kstd::vector{}; - v.reserve(10); - v.emplace_back(10); - v.emplace_back(20); - v.emplace_back(30); - - WHEN("inserting an element in the middle with sufficient capacity") - { - auto const tracker = kstd::tests::special_member_tracker{15}; - v.insert(v.cbegin() + 1, tracker); - - THEN("the shifted elements are move-assigned and the new element is copy-assigned") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0].value == 10); - REQUIRE(v[0].move_assigned_count == 0); - REQUIRE(v[0].copy_assigned_count == 0); - REQUIRE(v[1].value == 15); - REQUIRE(v[1].move_assigned_count == 0); - REQUIRE(v[1].copy_assigned_count == 1); - REQUIRE(tracker.copied_from_count == 1); - REQUIRE(v[2].value == 20); - REQUIRE(v[2].move_assigned_count == 1); - REQUIRE(v[2].copy_assigned_count == 0); - REQUIRE(v[3].value == 30); - REQUIRE(v[3].move_constructed_count == 1); - } - } - - WHEN("inserting an rvalue element in the middle with sufficient capacity") - { - auto tracker = kstd::tests::special_member_tracker{15}; - v.insert(v.cbegin() + 1, std::move(tracker)); - - THEN("the shifted elements are move-assigned and the new element is move-assigned") - { - REQUIRE(v.size() == 4); - REQUIRE(v[0].value == 10); - REQUIRE(v[0].move_assigned_count == 0); - REQUIRE(v[0].copy_assigned_count == 0); - REQUIRE(v[1].value == 15); - REQUIRE(v[1].move_assigned_count == 1); - REQUIRE(v[1].copy_assigned_count == 0); - REQUIRE(tracker.moved_from_count == 1); - REQUIRE(v[2].value == 20); - REQUIRE(v[2].move_assigned_count == 1); - REQUIRE(v[3].value == 30); - REQUIRE(v[3].move_constructed_count == 1); - } - } - - WHEN("erasing an element in the middle") - { - for (auto & elem : v) - { - elem.reset_counts(); - } - - auto it = v.erase(v.cbegin() + 1); - - THEN("the subsequent elements are move-assigned leftwards") - { - REQUIRE(v.size() == 2); - - REQUIRE(v[0].value == 10); - REQUIRE(v[0].move_assigned_count == 0); - REQUIRE(v[0].copy_assigned_count == 0); - - REQUIRE(v[1].value == 30); - REQUIRE(v[1].move_assigned_count == 1); - REQUIRE(v[1].copy_assigned_count == 0); - - REQUIRE(v.data()[2].destroyed_count == 1); - REQUIRE(v.data()[2].moved_from_count == 1); - - REQUIRE(it == v.begin() + 1); - } - } - - WHEN("erasing the last element") - { - for (auto & elem : v) - { - elem.reset_counts(); - } - - auto it = v.erase(v.cend() - 1); - - THEN("no elements are moved, just the last element destroyed") - { - REQUIRE(v.size() == 2); - - REQUIRE(v[0].value == 10); - REQUIRE(v[0].move_constructed_count == 0); - REQUIRE(v[0].copy_constructed_count == 0); - REQUIRE(v[0].move_assigned_count == 0); - REQUIRE(v[0].copy_assigned_count == 0); - - REQUIRE(v[1].value == 20); - REQUIRE(v[1].move_constructed_count == 0); - REQUIRE(v[1].copy_constructed_count == 0); - REQUIRE(v[1].move_assigned_count == 0); - REQUIRE(v[1].copy_assigned_count == 0); - - REQUIRE(v.data()[2].destroyed_count == 1); - REQUIRE(v.data()[2].moved_from_count == 0); - REQUIRE(v.data()[2].copied_from_count == 0); - - REQUIRE(it == v.end()); - } - } - - WHEN("erasing a range of elements in the middle") - { - v.emplace_back(40); - v.emplace_back(50); - - for (auto & elem : v) - { - elem.reset_counts(); - } - - auto it = v.erase(v.cbegin() + 1, v.cbegin() + 3); - - THEN("the specified elements are destroyed and subsequent elements are move-assigned leftwards") - { - REQUIRE(v.size() == 3); - - REQUIRE(v[0].value == 10); - REQUIRE(v[0].move_constructed_count == 0); - REQUIRE(v[0].copy_constructed_count == 0); - REQUIRE(v[0].move_assigned_count == 0); - REQUIRE(v[0].copy_assigned_count == 0); - - REQUIRE(v[1].value == 40); - REQUIRE(v[1].move_constructed_count == 0); - REQUIRE(v[1].copy_constructed_count == 0); - REQUIRE(v[1].move_assigned_count == 1); - REQUIRE(v[1].copy_assigned_count == 0); - - REQUIRE(v[2].value == 50); - REQUIRE(v[2].move_constructed_count == 0); - REQUIRE(v[2].copy_constructed_count == 0); - REQUIRE(v[2].move_assigned_count == 1); - REQUIRE(v[2].copy_assigned_count == 0); - - REQUIRE(v.data()[3].destroyed_count == 1); - REQUIRE(v.data()[3].moved_from_count == 1); - REQUIRE(v.data()[3].copied_from_count == 0); - - REQUIRE(v.data()[4].destroyed_count == 1); - REQUIRE(v.data()[4].moved_from_count == 1); - REQUIRE(v.data()[4].copied_from_count == 0); - - REQUIRE(it == v.begin() + 1); - } - } - } -} - -SCENARIO("Vector advanced construction", "[vector]") -{ - GIVEN("A count and a default value") - { - WHEN("constructing with count and default argument") - { - auto const count = 5uz; - auto const value = 42; - auto v = kstd::vector(count, value); - - THEN("the vector is initialized with count copies of value") - { - REQUIRE(v.size() == 5); - REQUIRE(v.capacity() == 5); - REQUIRE(v.front() == 42); - REQUIRE(v.back() == 42); - } - } - } - - GIVEN("A pure input iterator range") - { - WHEN("constructing from input iterators") - { - auto const arr = std::array{1, 2, 3}; - auto const first = kstd::tests::test_input_iterator{arr.data(), arr.size()}; - auto const last = kstd::tests::test_input_iterator{}; - - auto v = kstd::vector(first, last); - - THEN("the vector is generated dynamically and initialized correctly") - { - REQUIRE(v.size() == 3); - REQUIRE(v[0] == 1); - REQUIRE(v[2] == 3); - } - } - } - - GIVEN("A tracking allocator and a source vector") - { - auto allocs = 0; - auto allocator = kstd::tests::tracking_allocator{&allocs}; - auto source = kstd::vector>{allocator}; - source.push_back(1); - source.push_back(2); - source.push_back(3); - - allocs = 0; - - WHEN("copy constructing with an allocator") - { - auto copy = kstd::vector>(source, allocator); - - THEN("the copy succeeds and the allocator is used") - { - REQUIRE(copy.size() == 3); - REQUIRE(allocs > 0); - REQUIRE(copy[0] == 1); - REQUIRE(copy[2] == 3); - } - } - - WHEN("move constructing with an identically comparing allocator") - { - auto moved = kstd::vector>(std::move(source), allocator); - - THEN("the move succeeds and no new allocations are made (memory is stolen)") - { - REQUIRE(moved.size() == 3); - REQUIRE(allocs == 0); - REQUIRE(moved[0] == 1); - REQUIRE(moved[2] == 3); - } - } - - WHEN("move constructing with a non-equal allocator") - { - auto allocs2 = 0; - auto allocator2 = kstd::tests::tracking_allocator{&allocs2}; - - auto moved = kstd::vector>(std::move(source), allocator2); - - THEN("the move allocates new memory and moves elements") - { - REQUIRE(allocs2 > 0); - REQUIRE(moved.size() == 3); - REQUIRE(source.empty()); - } - } - } -} - -SCENARIO("Vector assignment operators", "[vector]") -{ - GIVEN("A source vector and an empty target vector") - { - auto source = kstd::vector{1, 2, 3}; - auto target = kstd::vector{}; - - WHEN("copy assigning") - { - target = source; - - THEN("the target matches the source") - { - REQUIRE(target.size() == 3); - REQUIRE(target[0] == 1); - REQUIRE(target[2] == 3); - REQUIRE(source.size() == 3); - } - } - - WHEN("move assigning") - { - target = std::move(source); - - THEN("the target assumes the source's data") - { - REQUIRE(target.size() == 3); - REQUIRE(target[0] == 1); - REQUIRE(target[2] == 3); - REQUIRE(source.empty()); - } - } - } - - GIVEN("Vectors with propagating copy allocator") - { - auto allocs1 = 0; - auto allocs2 = 0; - auto alloc1 = kstd::tests::propagating_allocator{&allocs1}; - auto alloc2 = kstd::tests::propagating_allocator{&allocs2}; - - auto v1 = kstd::vector>{alloc1}; - v1.push_back(1); - v1.push_back(2); - auto v2 = kstd::vector>{alloc2}; - - WHEN("copy assigning") - { - v2 = v1; - THEN("the allocator propagates") - { - REQUIRE(v2.get_allocator() == v1.get_allocator()); - } - } - } - - GIVEN("Vectors for copy assignment overlap") - { - auto v1 = kstd::vector{1, 2, 3}; - auto v2 = kstd::vector{4, 5}; - v2.reserve(10); - - WHEN("copy assigning a larger vector to a smaller one with enough capacity") - { - v2 = v1; - THEN("elements are copied and size is updated") - { - REQUIRE(v2.size() == 3); - REQUIRE(v2.capacity() >= 10); - REQUIRE(v2[2] == 3); - } - } - - auto v3 = kstd::vector{1, 2, 3, 4}; - v3.reserve(10); - WHEN("copy assigning a smaller vector to a larger one") - { - v3 = v1; - THEN("excess elements are destroyed") - { - REQUIRE(v3.size() == 3); - REQUIRE(v3[0] == 1); - } - } - } - - GIVEN("Vectors with the same tracking allocator") - { - auto allocs = 0; - auto alloc = kstd::tests::tracking_allocator{&allocs}; - auto v1 = kstd::vector>{alloc}; - v1.push_back(1); - auto v2 = kstd::vector>{alloc}; - - WHEN("move assigning") - { - v2 = std::move(v1); - THEN("memory is stolen without allocation") - { - REQUIRE(v2.size() == 1); - REQUIRE(allocs == 1); - } - } - } - - GIVEN("Vectors with different non-propagating tracking allocators") - { - auto allocs1 = 0; - auto allocs2 = 0; - auto alloc1 = kstd::tests::tracking_allocator{&allocs1}; - auto alloc2 = kstd::tests::tracking_allocator{&allocs2}; - - auto v1 = kstd::vector>{alloc1}; - v1.push_back(1); - v1.push_back(2); - v1.push_back(3); - - auto v2 = kstd::vector>{alloc2}; - v2.push_back(4); - v2.push_back(5); - - WHEN("move assigning a larger vector to a smaller one without enough capacity") - { - v2.shrink_to_fit(); - v2 = std::move(v1); - THEN("memory is reallocated and elements are moved") - { - REQUIRE(v2.size() == 3); - REQUIRE(allocs2 > 2); - } - } - - auto v3 = kstd::vector>{alloc2}; - v3.reserve(10); - v3.push_back(4); - v3.push_back(5); - WHEN("move assigning a larger vector to a smaller one with enough capacity") - { - v3 = std::move(v1); - THEN("elements are move-assigned over overlap and move-constructed over remainder") - { - REQUIRE(v3.size() == 3); - } - } - - auto v4 = kstd::vector>{alloc2}; - v4.reserve(10); - v4.push_back(4); - v4.push_back(5); - v4.push_back(6); - v4.push_back(7); - WHEN("move assigning a smaller vector to a larger one with enough capacity") - { - v4 = std::move(v1); - THEN("overlap is moved and excess is destroyed") - { - REQUIRE(v4.size() == 3); - } - } - } -} - -SCENARIO("Vector self-assignment operators", "[vector]") -{ - GIVEN("A populated vector") - { - auto v = kstd::vector{1, 2, 3}; - auto const initial_capacity = v.capacity(); - auto const * initial_data = v.data(); - - WHEN("copy assigning to itself") - { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpragmas" -#pragma GCC diagnostic ignored "-Wunknown-warning-option" -#pragma GCC diagnostic ignored "-Wself-assign-overloaded" - v = v; -#pragma GCC diagnostic pop - - THEN("the vector remains unchanged") - { - REQUIRE(v.size() == 3); - REQUIRE(v.capacity() == initial_capacity); - REQUIRE(v.data() == initial_data); - REQUIRE(v[0] == 1); - REQUIRE(v[2] == 3); - } - } - - WHEN("move assigning to itself") - { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpragmas" -#pragma GCC diagnostic ignored "-Wunknown-warning-option" -#pragma GCC diagnostic ignored "-Wself-move" - v = std::move(v); -#pragma GCC diagnostic pop - - THEN("the vector remains unchanged") - { - REQUIRE(v.size() == 3); - REQUIRE(v.capacity() == initial_capacity); - REQUIRE(v.data() == initial_data); - REQUIRE(v[0] == 1); - REQUIRE(v[2] == 3); - } - } - } -} - -SCENARIO("Vector const accessors and copy insertion", "[vector]") -{ - GIVEN("A const populated vector") - { - auto const v = kstd::vector{10, 20, 30}; - - WHEN("calling const accessors") - { - THEN("elements are read correctly as const references") - { - REQUIRE(v.front() == 10); - REQUIRE(v.back() == 30); - REQUIRE(v[1] == 20); - REQUIRE(v.at(1) == 20); - } - } - } - - GIVEN("An empty vector and a const lvalue tracker") - { - auto v = kstd::vector{}; - auto const tracker = kstd::tests::special_member_tracker{42}; - - WHEN("push_back is called with the const lvalue") - { - v.push_back(tracker); - - THEN("the element is gracefully copy-constructed") - { - REQUIRE(v.size() == 1); - REQUIRE(v.back().value == 42); - REQUIRE(v.back().copy_constructed_count == 1); - REQUIRE(v.back().move_constructed_count == 0); - } - } - - WHEN("push_back is called with a const lvalue when capacity is sufficient") - { - v.reserve(10); - auto const current_capacity = v.capacity(); - v.push_back(tracker); - - THEN("the element is copy-constructed without reallocation") - { - REQUIRE(v.size() == 1); - REQUIRE(v.capacity() == current_capacity); - REQUIRE(v.back().value == 42); - REQUIRE(v.back().copy_constructed_count == 1); - REQUIRE(v.back().move_constructed_count == 0); - } - } - } -} -- cgit v1.2.3 From 1246e00478fb5ab2a357de17066fd8738395d9f1 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 4 May 2026 08:20:42 +0200 Subject: debug: split gdb modules --- .vscode/launch.json | 2 +- .vscode/settings.json | 2 +- kapi/gdb/__init__.py | 15 ++++++++++++ kapi/gdb/address.py | 33 +++++++++++++++++++++++++ libs/kstd/gdb/__init__.py | 20 +++++++++++++++ libs/kstd/gdb/smart_pointers.py | 50 ++++++++++++++++++++++++++++++++++++++ libs/kstd/gdb/std_types.py | 15 ++++++++++++ libs/kstd/gdb/string.py | 27 ++++++++++++++++++++ libs/kstd/gdb/vector.py | 22 +++++++++++++++++ scripts/gdb/kapi/__init__.py | 15 ------------ scripts/gdb/kapi/address.py | 33 ------------------------- scripts/gdb/kstd/__init__.py | 20 --------------- scripts/gdb/kstd/smart_pointers.py | 50 -------------------------------------- scripts/gdb/kstd/std_types.py | 15 ------------ scripts/gdb/kstd/string.py | 27 -------------------- scripts/gdb/kstd/vector.py | 22 ----------------- scripts/gdb/load.py | 16 ------------ scripts/gdb/pretty_printers.py | 37 ++++++++++++++++++++++++++++ 18 files changed, 221 insertions(+), 200 deletions(-) create mode 100644 kapi/gdb/__init__.py create mode 100644 kapi/gdb/address.py create mode 100644 libs/kstd/gdb/__init__.py create mode 100644 libs/kstd/gdb/smart_pointers.py create mode 100644 libs/kstd/gdb/std_types.py create mode 100644 libs/kstd/gdb/string.py create mode 100644 libs/kstd/gdb/vector.py delete mode 100644 scripts/gdb/kapi/__init__.py delete mode 100644 scripts/gdb/kapi/address.py delete mode 100644 scripts/gdb/kstd/__init__.py delete mode 100644 scripts/gdb/kstd/smart_pointers.py delete mode 100644 scripts/gdb/kstd/std_types.py delete mode 100644 scripts/gdb/kstd/string.py delete mode 100644 scripts/gdb/kstd/vector.py delete mode 100644 scripts/gdb/load.py create mode 100644 scripts/gdb/pretty_printers.py diff --git a/.vscode/launch.json b/.vscode/launch.json index d5b2401..15f5d3a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -28,7 +28,7 @@ }, { "description": "Load custom Python helpers", - "text": "-interpreter-exec console \"source ${workspaceFolder}/scripts/gdb/load.py\"" + "text": "-interpreter-exec console \"source ${workspaceFolder}/scripts/gdb/pretty_printers.py\"" } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index c8f9013..efe3bc7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -90,7 +90,7 @@ }, { "description": "Load custom Python helpers", - "text": "-interpreter-exec console \"source ${workspaceFolder}/scripts/gdb/load.py\"" + "text": "-interpreter-exec console \"source ${workspaceFolder}/scripts/gdb/pretty_printers.py\"" } ], }, diff --git a/kapi/gdb/__init__.py b/kapi/gdb/__init__.py new file mode 100644 index 0000000..c37c7b7 --- /dev/null +++ b/kapi/gdb/__init__.py @@ -0,0 +1,15 @@ +import gdb.printing + +from .address import KapiMemoryAddressPrinter + + +def build_pretty_printers(): + pp = gdb.printing.RegexpCollectionPrettyPrinter("kapi") + pp.add_printer( + "kapi_memory_address", "^kapi::memory::address<.*>$", KapiMemoryAddressPrinter + ) + return pp + + +def register_printers(objfile): + gdb.printing.register_pretty_printer(objfile, build_pretty_printers(), replace=True) diff --git a/kapi/gdb/address.py b/kapi/gdb/address.py new file mode 100644 index 0000000..677c9aa --- /dev/null +++ b/kapi/gdb/address.py @@ -0,0 +1,33 @@ +import gdb + + +class KapiMemoryAddressPrinter: + """Print kapi::MemoryAddress.""" + + def __init__(self, val): + self.val = val + self.address_type = val.type.template_argument(0) + + def to_string(self): + try: + raw_address = int(self.val["m_value"]) + type_string = str(self.address_type) + + if "linear" in type_string: + suffix = "%lin" + elif "physical" in type_string: + suffix = "%phy" + else: + suffix = "%???" + + return f"{raw_address:#018x}{suffix}" + except Exception as e: + return f"{self.val}: {e}" + + def children(self): + if "linear" in str(self.address_type): + yield ( + "std::byte *", + self.val["m_value"].cast(gdb.lookup_type("std::byte").pointer()), + ) + yield ("m_value", self.val["m_value"]) diff --git a/libs/kstd/gdb/__init__.py b/libs/kstd/gdb/__init__.py new file mode 100644 index 0000000..fc5e8fb --- /dev/null +++ b/libs/kstd/gdb/__init__.py @@ -0,0 +1,20 @@ +import gdb.printing + +from .vector import KstdVectorPrinter +from .string import KstdStringPrinter +from .std_types import StdBytePrinter +from .smart_pointers import KstdUniquePtrPrinter, KstdSharedPtrPrinter + + +def build_pretty_printers(): + pp = gdb.printing.RegexpCollectionPrettyPrinter("kstd") + pp.add_printer("vector", "^kstd::vector<.*>$", KstdVectorPrinter) + pp.add_printer("string", "^kstd::string$", KstdStringPrinter) + pp.add_printer("std_byte", "^std::byte$", StdBytePrinter) + pp.add_printer("unique_ptr", "^kstd::unique_ptr<.*>$", KstdUniquePtrPrinter) + pp.add_printer("shared_ptr", "^kstd::shared_ptr<.*>$", KstdSharedPtrPrinter) + return pp + + +def register_printers(objfile): + gdb.printing.register_pretty_printer(objfile, build_pretty_printers(), replace=True) diff --git a/libs/kstd/gdb/smart_pointers.py b/libs/kstd/gdb/smart_pointers.py new file mode 100644 index 0000000..6e4a4f9 --- /dev/null +++ b/libs/kstd/gdb/smart_pointers.py @@ -0,0 +1,50 @@ +import gdb + + +class KstdUniquePtrPrinter: + + def __init__(self, val): + self.val = val + self.type = val.type.template_argument(0) + + def to_string(self): + pointer = self.val["pointer"] + if int(pointer) == 0: + return f"kstd::unique_ptr<{self.type}> (empty)" + return f"kstd::unique_ptr<{self.type}>" + + def children(self): + pointer = self.val["pointer"] + if int(pointer) != 0: + yield ("get()", pointer) + yield ("*get()", pointer.dereference()) + + +class KstdSharedPtrPrinter: + + def __init__(self, val): + self.val = val + self.type = val.type.template_argument(0) + + def to_string(self): + pointer = self.val["pointer"] + control_block = self.val["control"] + + if int(pointer) == 0 or int(control_block) == 0: + return f"kstd::shared_ptr<{self.type}> (empty)" + + strong_refs = int(control_block["shared_count"]["_M_i"]) + weak_refs = int(control_block["weak_count"]["_M_i"]) + + return f"kstd::shared_ptr<{self.type}> (use_count={strong_refs}, weak_count={weak_refs})" + + def children(self): + pointer = self.val["pointer"] + control_block = self.val["control"] + + if int(pointer) != 0: + yield ("get()", pointer) + yield ("*get()", pointer.dereference()) + + if int(control_block) != 0: + yield ("[control_block]", control_block.dereference()) diff --git a/libs/kstd/gdb/std_types.py b/libs/kstd/gdb/std_types.py new file mode 100644 index 0000000..78d094c --- /dev/null +++ b/libs/kstd/gdb/std_types.py @@ -0,0 +1,15 @@ +import gdb + + +class StdBytePrinter: + + def __init__(self, val): + self.val = val + + def to_string(self): + try: + uint8_type = gdb.lookup_type("unsigned char") + numeric_value = int(self.val.cast(uint8_type)) + return f"{numeric_value:#04x}" + except gdb.error: + return f"" diff --git a/libs/kstd/gdb/string.py b/libs/kstd/gdb/string.py new file mode 100644 index 0000000..8230b21 --- /dev/null +++ b/libs/kstd/gdb/string.py @@ -0,0 +1,27 @@ +import gdb + + +class KstdStringPrinter: + + def __init__(self, val): + self.val = val + + def to_string(self): + storage = self.val["m_storage"] + storage_size = int(storage["m_size"]) + + if storage_size <= 0: + return '""' + + data_pointer = storage["m_data"] + string_length = storage_size - 1 + + try: + if hasattr(data_pointer, "lazy_string"): + return data_pointer.lazy_string(encoding="utf-8", length=string_length) + return data_pointer.string(encoding="utf-8", length=string_length) + except gdb.error: + return "" + + def display_hint(self): + return "string" diff --git a/libs/kstd/gdb/vector.py b/libs/kstd/gdb/vector.py new file mode 100644 index 0000000..597ffdc --- /dev/null +++ b/libs/kstd/gdb/vector.py @@ -0,0 +1,22 @@ +import gdb + + +class KstdVectorPrinter: + + def __init__(self, val): + self.val = val + self.type = val.type.template_argument(0) + + def to_string(self): + size = int(self.val["m_size"]) + capacity = int(self.val["m_capacity"]) + return f"kstd::vector of length {size}, capacity {capacity}" + + def children(self): + size = int(self.val["m_size"]) + data_pointer = self.val["m_data"] + for i in range(size): + yield (f"[{i}]", (data_pointer + i).dereference()) + + def display_hint(self): + return "array" diff --git a/scripts/gdb/kapi/__init__.py b/scripts/gdb/kapi/__init__.py deleted file mode 100644 index c37c7b7..0000000 --- a/scripts/gdb/kapi/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -import gdb.printing - -from .address import KapiMemoryAddressPrinter - - -def build_pretty_printers(): - pp = gdb.printing.RegexpCollectionPrettyPrinter("kapi") - pp.add_printer( - "kapi_memory_address", "^kapi::memory::address<.*>$", KapiMemoryAddressPrinter - ) - return pp - - -def register_printers(objfile): - gdb.printing.register_pretty_printer(objfile, build_pretty_printers(), replace=True) diff --git a/scripts/gdb/kapi/address.py b/scripts/gdb/kapi/address.py deleted file mode 100644 index 677c9aa..0000000 --- a/scripts/gdb/kapi/address.py +++ /dev/null @@ -1,33 +0,0 @@ -import gdb - - -class KapiMemoryAddressPrinter: - """Print kapi::MemoryAddress.""" - - def __init__(self, val): - self.val = val - self.address_type = val.type.template_argument(0) - - def to_string(self): - try: - raw_address = int(self.val["m_value"]) - type_string = str(self.address_type) - - if "linear" in type_string: - suffix = "%lin" - elif "physical" in type_string: - suffix = "%phy" - else: - suffix = "%???" - - return f"{raw_address:#018x}{suffix}" - except Exception as e: - return f"{self.val}: {e}" - - def children(self): - if "linear" in str(self.address_type): - yield ( - "std::byte *", - self.val["m_value"].cast(gdb.lookup_type("std::byte").pointer()), - ) - yield ("m_value", self.val["m_value"]) diff --git a/scripts/gdb/kstd/__init__.py b/scripts/gdb/kstd/__init__.py deleted file mode 100644 index fc5e8fb..0000000 --- a/scripts/gdb/kstd/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -import gdb.printing - -from .vector import KstdVectorPrinter -from .string import KstdStringPrinter -from .std_types import StdBytePrinter -from .smart_pointers import KstdUniquePtrPrinter, KstdSharedPtrPrinter - - -def build_pretty_printers(): - pp = gdb.printing.RegexpCollectionPrettyPrinter("kstd") - pp.add_printer("vector", "^kstd::vector<.*>$", KstdVectorPrinter) - pp.add_printer("string", "^kstd::string$", KstdStringPrinter) - pp.add_printer("std_byte", "^std::byte$", StdBytePrinter) - pp.add_printer("unique_ptr", "^kstd::unique_ptr<.*>$", KstdUniquePtrPrinter) - pp.add_printer("shared_ptr", "^kstd::shared_ptr<.*>$", KstdSharedPtrPrinter) - return pp - - -def register_printers(objfile): - gdb.printing.register_pretty_printer(objfile, build_pretty_printers(), replace=True) diff --git a/scripts/gdb/kstd/smart_pointers.py b/scripts/gdb/kstd/smart_pointers.py deleted file mode 100644 index 6e4a4f9..0000000 --- a/scripts/gdb/kstd/smart_pointers.py +++ /dev/null @@ -1,50 +0,0 @@ -import gdb - - -class KstdUniquePtrPrinter: - - def __init__(self, val): - self.val = val - self.type = val.type.template_argument(0) - - def to_string(self): - pointer = self.val["pointer"] - if int(pointer) == 0: - return f"kstd::unique_ptr<{self.type}> (empty)" - return f"kstd::unique_ptr<{self.type}>" - - def children(self): - pointer = self.val["pointer"] - if int(pointer) != 0: - yield ("get()", pointer) - yield ("*get()", pointer.dereference()) - - -class KstdSharedPtrPrinter: - - def __init__(self, val): - self.val = val - self.type = val.type.template_argument(0) - - def to_string(self): - pointer = self.val["pointer"] - control_block = self.val["control"] - - if int(pointer) == 0 or int(control_block) == 0: - return f"kstd::shared_ptr<{self.type}> (empty)" - - strong_refs = int(control_block["shared_count"]["_M_i"]) - weak_refs = int(control_block["weak_count"]["_M_i"]) - - return f"kstd::shared_ptr<{self.type}> (use_count={strong_refs}, weak_count={weak_refs})" - - def children(self): - pointer = self.val["pointer"] - control_block = self.val["control"] - - if int(pointer) != 0: - yield ("get()", pointer) - yield ("*get()", pointer.dereference()) - - if int(control_block) != 0: - yield ("[control_block]", control_block.dereference()) diff --git a/scripts/gdb/kstd/std_types.py b/scripts/gdb/kstd/std_types.py deleted file mode 100644 index 78d094c..0000000 --- a/scripts/gdb/kstd/std_types.py +++ /dev/null @@ -1,15 +0,0 @@ -import gdb - - -class StdBytePrinter: - - def __init__(self, val): - self.val = val - - def to_string(self): - try: - uint8_type = gdb.lookup_type("unsigned char") - numeric_value = int(self.val.cast(uint8_type)) - return f"{numeric_value:#04x}" - except gdb.error: - return f"" diff --git a/scripts/gdb/kstd/string.py b/scripts/gdb/kstd/string.py deleted file mode 100644 index 8230b21..0000000 --- a/scripts/gdb/kstd/string.py +++ /dev/null @@ -1,27 +0,0 @@ -import gdb - - -class KstdStringPrinter: - - def __init__(self, val): - self.val = val - - def to_string(self): - storage = self.val["m_storage"] - storage_size = int(storage["m_size"]) - - if storage_size <= 0: - return '""' - - data_pointer = storage["m_data"] - string_length = storage_size - 1 - - try: - if hasattr(data_pointer, "lazy_string"): - return data_pointer.lazy_string(encoding="utf-8", length=string_length) - return data_pointer.string(encoding="utf-8", length=string_length) - except gdb.error: - return "" - - def display_hint(self): - return "string" diff --git a/scripts/gdb/kstd/vector.py b/scripts/gdb/kstd/vector.py deleted file mode 100644 index 597ffdc..0000000 --- a/scripts/gdb/kstd/vector.py +++ /dev/null @@ -1,22 +0,0 @@ -import gdb - - -class KstdVectorPrinter: - - def __init__(self, val): - self.val = val - self.type = val.type.template_argument(0) - - def to_string(self): - size = int(self.val["m_size"]) - capacity = int(self.val["m_capacity"]) - return f"kstd::vector of length {size}, capacity {capacity}" - - def children(self): - size = int(self.val["m_size"]) - data_pointer = self.val["m_data"] - for i in range(size): - yield (f"[{i}]", (data_pointer + i).dereference()) - - def display_hint(self): - return "array" diff --git a/scripts/gdb/load.py b/scripts/gdb/load.py deleted file mode 100644 index ec512b7..0000000 --- a/scripts/gdb/load.py +++ /dev/null @@ -1,16 +0,0 @@ -import sys -import os -import gdb - -script_dir = os.path.dirname(os.path.abspath(__file__)) - -if script_dir not in sys.path: - sys.path.insert(0, script_dir) - -import kstd -import kapi - -kstd.register_printers(gdb.current_objfile()) -kapi.register_printers(gdb.current_objfile()) - -gdb.write("Loaded TeachOS pretty printers.\n") diff --git a/scripts/gdb/pretty_printers.py b/scripts/gdb/pretty_printers.py new file mode 100644 index 0000000..83db491 --- /dev/null +++ b/scripts/gdb/pretty_printers.py @@ -0,0 +1,37 @@ +import sys +import os +import gdb +import importlib.util + +script_path = os.path.abspath(__file__) +repo_root = os.path.dirname(os.path.dirname(os.path.dirname(script_path))) + +components = { + "kstd": "libs/kstd/gdb", + "kapi": "kapi/gdb", +} + +for component, path in components.items(): + full_path = os.path.join(repo_root, *path.split("/")) + init_file = os.path.join(full_path, "__init__.py") + + if os.path.isfile(init_file): + try: + spec = importlib.util.spec_from_file_location(component, init_file) + module = importlib.util.module_from_spec(spec) + sys.modules[component] = module + spec.loader.exec_module(module) + + if hasattr(module, "register_printers"): + module.register_printers(gdb.current_objfile()) + gdb.write(f"Info: Registered pretty printers for '{component}'.\n") + else: + gdb.write( + f"Warning: '{component}' does not have 'register_printers' function\n" + ) + except Exception as e: + gdb.write(f"Warning: Failed to load '{component}' pretty printers: {e}\n") + else: + gdb.write(f"Warning: GDB extension init not found: '{init_file}'\n") + +gdb.write("Info: Loaded TeachOS pretty printers.\n") -- cgit v1.2.3 From 78e42a1b6e0a857865be1e60f82871ac13c91bb1 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 4 May 2026 12:01:01 +0200 Subject: debug: improve pretty printer implementations --- kapi/gdb/__init__.py | 4 ++++ kapi/gdb/address.py | 25 +++++++++++------------- kapi/gdb/device.py | 9 +++++++++ libs/kstd/gdb/smart_pointers.py | 43 +++++++++++++++++++++-------------------- libs/kstd/gdb/std_types.py | 8 +++----- libs/kstd/gdb/string.py | 4 ++-- libs/kstd/gdb/vector.py | 19 +++++++++--------- scripts/gdb/pretty_printers.py | 6 +++++- scripts/gdb/teachos/__init__.py | 30 ++++++++++++++++++++++++++++ 9 files changed, 96 insertions(+), 52 deletions(-) create mode 100644 kapi/gdb/device.py create mode 100644 scripts/gdb/teachos/__init__.py diff --git a/kapi/gdb/__init__.py b/kapi/gdb/__init__.py index c37c7b7..ce95628 100644 --- a/kapi/gdb/__init__.py +++ b/kapi/gdb/__init__.py @@ -1,6 +1,7 @@ import gdb.printing from .address import KapiMemoryAddressPrinter +from .device import KapiDevicesDevicePrinter def build_pretty_printers(): @@ -8,6 +9,9 @@ def build_pretty_printers(): pp.add_printer( "kapi_memory_address", "^kapi::memory::address<.*>$", KapiMemoryAddressPrinter ) + pp.add_printer( + "kapi_devices_device", "^kapi::devices::device$", KapiDevicesDevicePrinter + ) return pp diff --git a/kapi/gdb/address.py b/kapi/gdb/address.py index 677c9aa..24fe681 100644 --- a/kapi/gdb/address.py +++ b/kapi/gdb/address.py @@ -1,17 +1,16 @@ import gdb +from teachos import TeachOSBasePrinter -class KapiMemoryAddressPrinter: - """Print kapi::MemoryAddress.""" - +class KapiMemoryAddressPrinter(TeachOSBasePrinter): def __init__(self, val): - self.val = val - self.address_type = val.type.template_argument(0) + super().__init__(val) + self.__type = val.type.template_argument(0) def to_string(self): try: - raw_address = int(self.val["m_value"]) - type_string = str(self.address_type) + raw_address = int(self.value["m_value"]) + type_string = str(self.__type) if "linear" in type_string: suffix = "%lin" @@ -22,12 +21,10 @@ class KapiMemoryAddressPrinter: return f"{raw_address:#018x}{suffix}" except Exception as e: - return f"{self.val}: {e}" + return f"{self.value}: {e}" def children(self): - if "linear" in str(self.address_type): - yield ( - "std::byte *", - self.val["m_value"].cast(gdb.lookup_type("std::byte").pointer()), - ) - yield ("m_value", self.val["m_value"]) + if "linear" in str(self.__type): + pointer_type = gdb.lookup_type("std::byte").pointer() + yield ("[bytes]", self.value["m_value"].cast(pointer_type)) + yield from super().children() diff --git a/kapi/gdb/device.py b/kapi/gdb/device.py new file mode 100644 index 0000000..b655972 --- /dev/null +++ b/kapi/gdb/device.py @@ -0,0 +1,9 @@ +import gdb +from teachos import TeachOSBasePrinter + + +class KapiDevicesDevicePrinter(TeachOSBasePrinter): + def to_string(self): + return ( + f"{self.value['m_name']} @ {self.value['m_major']}:{self.value['m_minor']}" + ) diff --git a/libs/kstd/gdb/smart_pointers.py b/libs/kstd/gdb/smart_pointers.py index 6e4a4f9..3e5da02 100644 --- a/libs/kstd/gdb/smart_pointers.py +++ b/libs/kstd/gdb/smart_pointers.py @@ -1,50 +1,51 @@ import gdb +from teachos import TeachOSBasePrinter -class KstdUniquePtrPrinter: - +class KstdUniquePtrPrinter(TeachOSBasePrinter): def __init__(self, val): - self.val = val - self.type = val.type.template_argument(0) + super().__init__(val) + self.__type = val.type.template_argument(0) def to_string(self): - pointer = self.val["pointer"] + pointer = self.value["pointer"] if int(pointer) == 0: - return f"kstd::unique_ptr<{self.type}> (empty)" - return f"kstd::unique_ptr<{self.type}>" + return f"kstd::unique_ptr<{self.__type}> (empty)" + return f"kstd::unique_ptr<{self.__type}>" def children(self): - pointer = self.val["pointer"] + pointer = self.value["pointer"] if int(pointer) != 0: - yield ("get()", pointer) - yield ("*get()", pointer.dereference()) + yield ("[object]", pointer.dereference()) + yield from super().children() -class KstdSharedPtrPrinter: +class KstdSharedPtrPrinter(TeachOSBasePrinter): def __init__(self, val): - self.val = val - self.type = val.type.template_argument(0) + super().__init__(val) + self.__type = val.type.template_argument(0) def to_string(self): - pointer = self.val["pointer"] - control_block = self.val["control"] + pointer = self.value["pointer"] + control_block = self.value["control"] if int(pointer) == 0 or int(control_block) == 0: - return f"kstd::shared_ptr<{self.type}> (empty)" + return f"kstd::shared_ptr<{self.__type}> (empty)" strong_refs = int(control_block["shared_count"]["_M_i"]) weak_refs = int(control_block["weak_count"]["_M_i"]) - return f"kstd::shared_ptr<{self.type}> (use_count={strong_refs}, weak_count={weak_refs})" + return f"kstd::shared_ptr<{self.__type}> (use_count={strong_refs}, weak_count={weak_refs})" def children(self): - pointer = self.val["pointer"] - control_block = self.val["control"] + pointer = self.value["pointer"] + control_block = self.value["control"] if int(pointer) != 0: - yield ("get()", pointer) - yield ("*get()", pointer.dereference()) + yield ("[object]", pointer.dereference()) if int(control_block) != 0: yield ("[control_block]", control_block.dereference()) + + yield from super().children() diff --git a/libs/kstd/gdb/std_types.py b/libs/kstd/gdb/std_types.py index 78d094c..deb5c58 100644 --- a/libs/kstd/gdb/std_types.py +++ b/libs/kstd/gdb/std_types.py @@ -1,15 +1,13 @@ import gdb +from teachos import TeachOSBasePrinter -class StdBytePrinter: - - def __init__(self, val): - self.val = val +class StdBytePrinter(TeachOSBasePrinter): def to_string(self): try: uint8_type = gdb.lookup_type("unsigned char") - numeric_value = int(self.val.cast(uint8_type)) + numeric_value = int(self.value.cast(uint8_type)) return f"{numeric_value:#04x}" except gdb.error: return f"" diff --git a/libs/kstd/gdb/string.py b/libs/kstd/gdb/string.py index 8230b21..2688061 100644 --- a/libs/kstd/gdb/string.py +++ b/libs/kstd/gdb/string.py @@ -4,10 +4,10 @@ import gdb class KstdStringPrinter: def __init__(self, val): - self.val = val + self.__val = val def to_string(self): - storage = self.val["m_storage"] + storage = self.__val["m_storage"] storage_size = int(storage["m_size"]) if storage_size <= 0: diff --git a/libs/kstd/gdb/vector.py b/libs/kstd/gdb/vector.py index 597ffdc..4340ef4 100644 --- a/libs/kstd/gdb/vector.py +++ b/libs/kstd/gdb/vector.py @@ -1,20 +1,21 @@ import gdb +from teachos import TeachOSBasePrinter -class KstdVectorPrinter: - +class KstdVectorPrinter(TeachOSBasePrinter): def __init__(self, val): - self.val = val - self.type = val.type.template_argument(0) + super().__init__(val) + self.__type = val.type.template_argument(0) def to_string(self): - size = int(self.val["m_size"]) - capacity = int(self.val["m_capacity"]) - return f"kstd::vector of length {size}, capacity {capacity}" + size = int(self.value["m_size"]) + capacity = int(self.value["m_capacity"]) + return f"kstd::vector<{self.__type}> (size={size}, capacity={capacity})" def children(self): - size = int(self.val["m_size"]) - data_pointer = self.val["m_data"] + yield from super().children() + size = int(self.value["m_size"]) + data_pointer = self.value["m_data"] for i in range(size): yield (f"[{i}]", (data_pointer + i).dereference()) diff --git a/scripts/gdb/pretty_printers.py b/scripts/gdb/pretty_printers.py index 83db491..c209328 100644 --- a/scripts/gdb/pretty_printers.py +++ b/scripts/gdb/pretty_printers.py @@ -4,7 +4,11 @@ import gdb import importlib.util script_path = os.path.abspath(__file__) -repo_root = os.path.dirname(os.path.dirname(os.path.dirname(script_path))) +script_root = os.path.dirname(script_path) +repo_root = os.path.dirname(os.path.dirname(script_root)) + +if script_root not in sys.path: + sys.path.insert(0, script_root) components = { "kstd": "libs/kstd/gdb", diff --git a/scripts/gdb/teachos/__init__.py b/scripts/gdb/teachos/__init__.py new file mode 100644 index 0000000..d90d6ad --- /dev/null +++ b/scripts/gdb/teachos/__init__.py @@ -0,0 +1,30 @@ +import gdb + + +class TeachOSBasePrinter: + def __init__(self, val): + self.__val = val + + @property + def value(self): + return self.__val + + def children(self): + real_type = self.value.type.strip_typedefs() + + if real_type.code not in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION): + return + + for field in real_type.fields(): + if field.artificial: + continue + + try: + if field.is_base_class: + yield (field.name, self.value.cast(field.type)) + elif field.name is not None: + yield (field.name, self.value[field.name]) + else: + yield ("", self.value[field]) + except gdb.error: + yield (str(field.name), "") -- cgit v1.2.3 From d8670d8eeb55bc0ea347cfe4a9a27640fe4e4e7e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 4 May 2026 19:40:01 +0200 Subject: debug: add multiboot2 information dump tool This patch introduces a new GDB tool `dump_mb2i` that dump the multiboot2 information provided by the bootloader. This tool can be invoked in the GDB console. For example in vscode: -exec dump_mb2i ((kapi::boot::information)bootstrap_information).mbi The tool expects the address of the loader provided MB2 information as its only argument. --- .vscode/launch.json | 2 +- .vscode/settings.json | 2 +- scripts/gdb/load.py | 47 +++++++++++++ scripts/gdb/pretty_printers.py | 41 ----------- scripts/gdb/teachos/dump_mb2i.py | 147 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 196 insertions(+), 43 deletions(-) create mode 100644 scripts/gdb/load.py delete mode 100644 scripts/gdb/pretty_printers.py create mode 100644 scripts/gdb/teachos/dump_mb2i.py diff --git a/.vscode/launch.json b/.vscode/launch.json index 15f5d3a..d5b2401 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -28,7 +28,7 @@ }, { "description": "Load custom Python helpers", - "text": "-interpreter-exec console \"source ${workspaceFolder}/scripts/gdb/pretty_printers.py\"" + "text": "-interpreter-exec console \"source ${workspaceFolder}/scripts/gdb/load.py\"" } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index efe3bc7..c8f9013 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -90,7 +90,7 @@ }, { "description": "Load custom Python helpers", - "text": "-interpreter-exec console \"source ${workspaceFolder}/scripts/gdb/pretty_printers.py\"" + "text": "-interpreter-exec console \"source ${workspaceFolder}/scripts/gdb/load.py\"" } ], }, diff --git a/scripts/gdb/load.py b/scripts/gdb/load.py new file mode 100644 index 0000000..355f6b9 --- /dev/null +++ b/scripts/gdb/load.py @@ -0,0 +1,47 @@ +import sys +import os +import gdb +import importlib.util + +script_path = os.path.abspath(__file__) +script_root = os.path.dirname(script_path) +repo_root = os.path.dirname(os.path.dirname(script_root)) + +if script_root not in sys.path: + sys.path.insert(0, script_root) + +from teachos.dump_mb2i import DumpMB2I + +components = { + "kstd": "libs/kstd/gdb", + "kapi": "kapi/gdb", +} + +for component, path in components.items(): + full_path = os.path.join(repo_root, *path.split("/")) + init_file = os.path.join(full_path, "__init__.py") + + if os.path.isfile(init_file): + try: + spec = importlib.util.spec_from_file_location(component, init_file) + module = importlib.util.module_from_spec(spec) + sys.modules[component] = module + spec.loader.exec_module(module) + + if hasattr(module, "register_printers"): + module.register_printers(gdb.current_objfile()) + gdb.write(f"Info: Registered pretty printers for '{component}'.\n") + else: + gdb.write( + f"Warning: '{component}' does not have 'register_printers' function\n" + ) + except Exception as e: + gdb.write(f"Warning: Failed to load '{component}' pretty printers: {e}\n") + else: + gdb.write(f"Warning: GDB extension init not found: '{init_file}'\n") + +gdb.write("Info: Loaded TeachOS pretty printers.\n") + +DumpMB2I() + +gdb.write("Info: Loaded TeachOS tools.\n") \ No newline at end of file diff --git a/scripts/gdb/pretty_printers.py b/scripts/gdb/pretty_printers.py deleted file mode 100644 index c209328..0000000 --- a/scripts/gdb/pretty_printers.py +++ /dev/null @@ -1,41 +0,0 @@ -import sys -import os -import gdb -import importlib.util - -script_path = os.path.abspath(__file__) -script_root = os.path.dirname(script_path) -repo_root = os.path.dirname(os.path.dirname(script_root)) - -if script_root not in sys.path: - sys.path.insert(0, script_root) - -components = { - "kstd": "libs/kstd/gdb", - "kapi": "kapi/gdb", -} - -for component, path in components.items(): - full_path = os.path.join(repo_root, *path.split("/")) - init_file = os.path.join(full_path, "__init__.py") - - if os.path.isfile(init_file): - try: - spec = importlib.util.spec_from_file_location(component, init_file) - module = importlib.util.module_from_spec(spec) - sys.modules[component] = module - spec.loader.exec_module(module) - - if hasattr(module, "register_printers"): - module.register_printers(gdb.current_objfile()) - gdb.write(f"Info: Registered pretty printers for '{component}'.\n") - else: - gdb.write( - f"Warning: '{component}' does not have 'register_printers' function\n" - ) - except Exception as e: - gdb.write(f"Warning: Failed to load '{component}' pretty printers: {e}\n") - else: - gdb.write(f"Warning: GDB extension init not found: '{init_file}'\n") - -gdb.write("Info: Loaded TeachOS pretty printers.\n") diff --git a/scripts/gdb/teachos/dump_mb2i.py b/scripts/gdb/teachos/dump_mb2i.py new file mode 100644 index 0000000..ff15fde --- /dev/null +++ b/scripts/gdb/teachos/dump_mb2i.py @@ -0,0 +1,147 @@ +import gdb +import struct + + +TAG_NAMES = { + 0: "End of Tags", + 1: "Boot Command Line", + 2: "Boot Loader Name", + 3: "Boot Module", + 4: "Basic Memory Information", + 5: "BIOS Boot Device", + 6: "Memory Map", + 7: "VBE Info", + 8: "Framebuffer Info", + 9: "ELF Symbols", + 10: "APM Table", + 14: "ACPI old RSDP (1.0)", + 15: "ACPI new RSDP (2.0+)", + 21: "Image Load Base Physical Address", +} + +MEMORY_TYPES = { + 1: "Available", + 2: "Reserved", + 3: "ACPI Reclaim", + 4: "Non-volatile storage", + 5: "Bad Memory", +} + +INDENT = f"{' '*11}" + + +class DumpMB2I(gdb.Command): + """ + Dump the information provided by the Multiboot2 loader. + Usage: dump_mb2i + Example: dump_mb2i $rbx + dump_mb2i 0x8000 + """ + + def __init__(self): + super(DumpMB2I, self).__init__("dump_mb2i", gdb.COMMAND_USER) + + def invoke(self, arg, from_tty): + if not arg: + gdb.write( + "Error: please provide the address of the Multiboot2 information structure.\n" + ) + return + + try: + address = int(gdb.parse_and_eval(arg)) + except gdb.error as e: + gdb.write(f"Error: Invalid address expression: {e}\n") + return + + inferior = gdb.selected_inferior() + + try: + header_data = inferior.read_memory(address, 8).tobytes() + total_size, reserved = struct.unpack(" 1024 * 1024: + gdb.write("Warning: suspicious total size ({total_size} bytes). Aborting.") + return + + gdb.write(f"+{'-'*70}+\n") + gdb.write(f"| Multiboot2 Boot Information Payload{' ':>34}|\n") + gdb.write(f"+{'-'*70}+\n") + gdb.write(f"Base Address: {address:#018x}\nTotal Size: {total_size} Bytes\n\n") + + offset = 8 + + while offset < total_size: + tag_address = address + offset + + try: + tag_header = inferior.read_memory(tag_address, 8).tobytes() + tag_type, tag_size = struct.unpack("\n") -- cgit v1.2.3 From e0c43cf62221d859b52d8a7cd3c78559de3bac8a Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 5 May 2026 11:03:17 +0000 Subject: ide: fix container configuration --- .devcontainer/x86-64/devcontainer.json | 2 +- CMakeLists.txt | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.devcontainer/x86-64/devcontainer.json b/.devcontainer/x86-64/devcontainer.json index 6a5da88..18b9308 100644 --- a/.devcontainer/x86-64/devcontainer.json +++ b/.devcontainer/x86-64/devcontainer.json @@ -5,7 +5,7 @@ "ghcr.io/devcontainers/features/git:1": {}, "ghcr.io/devcontainers/features/git-lfs:1": {}, "ghcr.io/devcontainers-extra/features/apt-packages:1": { - "packages": "acpica-tools,build-essential,clang-tidy,clangd,cmake,gdb,grub2-common,grub-pc,mtools,ninja-build,qemu-system-x86,ssh,wget,xorriso" + "packages": "acpica-tools,build-essential,clang-tidy-22,clangd-22,cmake,gdb,grub2-common,grub-pc,mtools,ninja-build,qemu-system-x86,ssh,wget,xorriso" } }, "customizations": { diff --git a/CMakeLists.txt b/CMakeLists.txt index 972422c..8d48a13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,8 @@ add_compile_options( #]============================================================================] find_program(CLANG_TIDY_EXE NAMES + "clang-tidy-23" + "clang-tidy-22" "clang-tidy-21" "clang-tidy" ) -- cgit v1.2.3 From 3082340fa8ab3c7c0da5d2f9d321d2367d399b20 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 5 May 2026 12:35:53 +0000 Subject: ide: unset clangd path --- .vscode/settings.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index c8f9013..e71d64f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,6 @@ "cmake.options.statusBarVisibility": "visible", "cmake.ctest.testExplorerIntegrationEnabled": false, "cmake.copyCompileCommands": "${workspaceFolder}/build/compile_commands.json", - "clangd.path": "clangd", "clangd.arguments": [ "--compile-commands-dir=${workspaceFolder}/build", "--query-driver=**/x86_64-pc-elf-g++", -- cgit v1.2.3 From b3209ac2564f21f3b78ecf5e0c05ca346a4a4276 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 28 Apr 2026 10:49:34 +0200 Subject: refactor inode kind --- kernel/include/kernel/filesystem/devfs/inode.hpp | 10 +++--- kernel/include/kernel/filesystem/device_inode.hpp | 5 +++ kernel/include/kernel/filesystem/ext2/inode.hpp | 21 +++++++++--- kernel/include/kernel/filesystem/inode.hpp | 25 +++------------ .../include/kernel/filesystem/open_file_table.hpp | 4 +-- kernel/include/kernel/filesystem/rootfs/inode.hpp | 10 +++--- .../kernel/test_support/filesystem/inode.hpp | 4 +-- kernel/src/filesystem/devfs/inode.cpp | 11 +++---- kernel/src/filesystem/device_inode.cpp | 9 ++++-- kernel/src/filesystem/ext2/filesystem.cpp | 36 +++------------------ kernel/src/filesystem/ext2/inode.cpp | 21 ++++++++++-- kernel/src/filesystem/ext2/inode.tests.cpp | 37 +++++++++++++++------- kernel/src/filesystem/inode.cpp | 10 ++---- kernel/src/filesystem/rootfs/inode.cpp | 9 +++--- kernel/src/test_support/filesystem/inode.cpp | 9 +++--- 15 files changed, 113 insertions(+), 108 deletions(-) diff --git a/kernel/include/kernel/filesystem/devfs/inode.hpp b/kernel/include/kernel/filesystem/devfs/inode.hpp index 0fab280..5589730 100644 --- a/kernel/include/kernel/filesystem/devfs/inode.hpp +++ b/kernel/include/kernel/filesystem/devfs/inode.hpp @@ -13,11 +13,6 @@ namespace kernel::filesystem::devfs */ struct inode : kernel::filesystem::inode { - /** - @brief Create a devfs inode. The inode is initialized with the appropriate kind (directory). - */ - inode(); - /** @brief Reads from the devfs directory inode. @param buffer Destination buffer. @@ -35,6 +30,11 @@ namespace kernel::filesystem::devfs @return Number of bytes written (always 0 because writes are not supported for this inode). */ auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + /** + // % TODO BA-FS26 + */ + [[nodiscard]] auto is_directory() const -> bool override; }; } // namespace kernel::filesystem::devfs diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index 2f79fca..fb60524 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -50,6 +50,11 @@ namespace kernel::filesystem */ [[nodiscard]] auto device() const -> kstd::shared_ptr const &; + /** + // TODO BA-FS26 + */ + [[nodiscard]] auto is_device() const -> bool override; + private: kstd::shared_ptr m_device; }; diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index 9291eea..64cdb06 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -42,8 +42,10 @@ namespace kernel::filesystem::ext2 { /** @brief Create an ext2 inode associated with the given filesystem. + @param fs The ext2 filesystem that this inode belongs to. + @param data The data associated with this inode, read from the disk. */ - explicit inode(filesystem * fs); + explicit inode(filesystem * fs, inode_data const & data); /** @brief Reads from the ext2 inode into a @p buffer, starting at the specified @p offset and for a given @p size. @@ -65,12 +67,23 @@ namespace kernel::filesystem::ext2 auto write(void const * buffer, size_t offset, size_t size) -> size_t override; /** - @brief The raw inode data as read from the disk. - */ - inode_data m_data{}; + // TODO BA-FS26 + */ + [[nodiscard]] auto data() const -> inode_data const &; + + /** + // TODO BA-FS26 + */ + [[nodiscard]] auto is_directory() const -> bool override; + + /** + // TODO BA-FS26 + */ + [[nodiscard]] auto is_regular() const -> bool override; private: filesystem * m_filesystem; + inode_data m_data{}; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index 5208be2..d071a6b 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -11,20 +11,9 @@ namespace kernel::filesystem struct inode { /** - @brief Represents the kind of an inode. + @brief Create an inode. */ - enum class inode_kind - { - regular, - directory, - device - }; - - /** - @brief Create an inode with the given @p kind. - @param kind The kind of the inode. - */ - explicit inode(inode_kind kind); + inode() = default; /** @brief Virtual destructor for the inode. @@ -55,23 +44,19 @@ namespace kernel::filesystem @brief Returns whether the inode is a directory. @return true if the inode is a directory, false otherwise. */ - [[nodiscard]] auto is_directory() const -> bool; + [[nodiscard]] virtual auto is_directory() const -> bool; /** @brief Returns whether the inode is a regular file. @return true if the inode is a regular file, false otherwise. */ - [[nodiscard]] auto is_regular() const -> bool; + [[nodiscard]] virtual auto is_regular() const -> bool; /** @brief Returns whether the inode is a device. @return true if the inode is a device, false otherwise. */ - [[nodiscard]] auto is_device() const -> bool; - - // TODO BA-FS26 avoid public member - public: - inode_kind m_kind{inode_kind::regular}; + [[nodiscard]] virtual auto is_device() const -> bool; }; } // namespace kernel::filesystem diff --git a/kernel/include/kernel/filesystem/open_file_table.hpp b/kernel/include/kernel/filesystem/open_file_table.hpp index 2f9a421..694f3b6 100644 --- a/kernel/include/kernel/filesystem/open_file_table.hpp +++ b/kernel/include/kernel/filesystem/open_file_table.hpp @@ -34,10 +34,10 @@ namespace kernel::filesystem /** @brief Add a file to the open file table. - @param f The file descriptor to add. + @param fd The file descriptor to add. @return The file descriptor index assigned to the file, or -1 on failure. */ - auto add_file(kstd::shared_ptr const & f) -> int; + auto add_file(kstd::shared_ptr const & fd) -> int; /** @brief Get a file from the open file table. diff --git a/kernel/include/kernel/filesystem/rootfs/inode.hpp b/kernel/include/kernel/filesystem/rootfs/inode.hpp index 37d0a30..e7c7eff 100644 --- a/kernel/include/kernel/filesystem/rootfs/inode.hpp +++ b/kernel/include/kernel/filesystem/rootfs/inode.hpp @@ -21,11 +21,6 @@ namespace kernel::filesystem::rootfs */ struct inode : kernel::filesystem::inode { - /** - @brief Create a rootfs inode. The inode is initialized with the appropriate kind (directory). - */ - inode(); - /** @brief Reads from the rootfs directory inode. @param buffer Destination buffer. @@ -57,6 +52,11 @@ namespace kernel::filesystem::rootfs */ auto lookup_child(std::string_view name) -> kstd::shared_ptr; + /** + // TODO BA-FS26 + */ + [[nodiscard]] auto is_directory() const -> bool override; + private: kstd::vector>> m_children; }; diff --git a/kernel/include/kernel/test_support/filesystem/inode.hpp b/kernel/include/kernel/test_support/filesystem/inode.hpp index 9d17917..8a76437 100644 --- a/kernel/include/kernel/test_support/filesystem/inode.hpp +++ b/kernel/include/kernel/test_support/filesystem/inode.hpp @@ -9,10 +9,10 @@ namespace kernel::tests::filesystem { struct inode : kernel::filesystem::inode { - inode(); - auto read(void * buffer, size_t offset, size_t size) const -> size_t override; auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + [[nodiscard]] auto is_regular() const -> bool override; }; } // namespace kernel::tests::filesystem diff --git a/kernel/src/filesystem/devfs/inode.cpp b/kernel/src/filesystem/devfs/inode.cpp index 0ed66ad..2029a7f 100644 --- a/kernel/src/filesystem/devfs/inode.cpp +++ b/kernel/src/filesystem/devfs/inode.cpp @@ -1,15 +1,9 @@ #include -#include - #include namespace kernel::filesystem::devfs { - inode::inode() - : kernel::filesystem::inode(inode_kind::directory) - {} - auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t { return 0; @@ -19,4 +13,9 @@ namespace kernel::filesystem::devfs { return 0; } + + auto inode::is_directory() const -> bool + { + return true; + } } // namespace kernel::filesystem::devfs \ No newline at end of file diff --git a/kernel/src/filesystem/device_inode.cpp b/kernel/src/filesystem/device_inode.cpp index 3bafe06..81a784c 100644 --- a/kernel/src/filesystem/device_inode.cpp +++ b/kernel/src/filesystem/device_inode.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include @@ -13,8 +12,7 @@ namespace kernel::filesystem { device_inode::device_inode(kstd::shared_ptr const & device) - : inode(inode_kind::device) - , m_device(device) + : m_device(device) { if (!device) { @@ -51,4 +49,9 @@ namespace kernel::filesystem return m_device; } + auto device_inode::is_device() const -> bool + { + return true; + } + } // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 41572ee..47e54fe 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -16,19 +16,6 @@ namespace kernel::filesystem::ext2 { - namespace - { - auto S_ISREG(uint16_t mode) -> bool - { - return (mode & constants::mode_mask) == constants::mode_regular; - } - - auto S_ISDIR(uint16_t mode) -> bool - { - return (mode & constants::mode_mask) == constants::mode_directory; - } - } // namespace - auto filesystem::mount(kstd::shared_ptr const & backing_inode) -> operation_result { kernel::filesystem::filesystem::mount(backing_inode); @@ -74,7 +61,7 @@ namespace kernel::filesystem::ext2 } auto const block_size = get_block_size(); - auto const & inode_data = ext2_parent->m_data; + auto const & inode_data = ext2_parent->data(); kstd::vector buffer(block_size); for (uint32_t i = 0; i < get_inode_block_count(inode_data); ++i) @@ -119,25 +106,10 @@ namespace kernel::filesystem::ext2 auto const inode_table_offset = static_cast(inode_table_start_block) * block_size; auto const inode_offset = inode_table_offset + inode_index_within_group * get_inode_size(); - auto new_inode = kstd::make_shared(this); - m_backing_inode->read(&new_inode->m_data, inode_offset, sizeof(inode_data)); - - // TODO BA-FS26 improve inode_kind really needed? or just map it to the mode bits? - if (S_ISREG(new_inode->m_data.mode)) - { - new_inode->m_kind = inode::inode_kind::regular; - } - else if (S_ISDIR(new_inode->m_data.mode)) - { - new_inode->m_kind = inode::inode_kind::directory; - } - else - { - // TODO BA-FS26 really correct?? - return nullptr; - } + auto new_inode_data = inode_data{}; + m_backing_inode->read(&new_inode_data, inode_offset, sizeof(inode_data)); - return new_inode; + return kstd::make_shared(this, new_inode_data); } auto filesystem::map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index c45c41e..279c84f 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -11,9 +11,9 @@ namespace kernel::filesystem::ext2 { - inode::inode(filesystem * fs) - : kernel::filesystem::inode(inode_kind::regular) - , m_filesystem(fs) + inode::inode(filesystem * fs, inode_data const & data) + : m_filesystem(fs) + , m_data(data) { if (!m_filesystem) { @@ -57,4 +57,19 @@ namespace kernel::filesystem::ext2 kapi::system::panic("[EXT2] inode::write is not implemented yet"); return 0; } + + [[nodiscard]] auto inode::data() const -> inode_data const & + { + return m_data; + } + + auto inode::is_regular() const -> bool + { + return (m_data.mode & constants::mode_mask) == constants::mode_regular; + } + + auto inode::is_directory() const -> bool + { + return (m_data.mode & constants::mode_mask) == constants::mode_directory; + } } // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp index 4d61790..e68352f 100644 --- a/kernel/src/filesystem/ext2/inode.tests.cpp +++ b/kernel/src/filesystem/ext2/inode.tests.cpp @@ -23,21 +23,34 @@ SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]" GIVEN("an ext2 filesystem") { auto fs = kernel::filesystem::ext2::filesystem{}; + auto data = kernel::filesystem::ext2::inode_data{}; - THEN("the inode is initialized and has the kind regular") + THEN("the inode is initialized with regular file mode in data and has the kind regular") { - auto inode = kernel::filesystem::ext2::inode{&fs}; + data.mode = kernel::filesystem::ext2::constants::mode_regular; + auto inode = kernel::filesystem::ext2::inode(&fs, data); + REQUIRE(inode.is_regular()); REQUIRE(!inode.is_directory()); REQUIRE(!inode.is_device()); } + + THEN("the inode is initialized with directory mode in data and has the kind directory") + { + data.mode = kernel::filesystem::ext2::constants::mode_directory; + auto inode = kernel::filesystem::ext2::inode(&fs, data); + + REQUIRE(!inode.is_regular()); + REQUIRE(inode.is_directory()); + REQUIRE(!inode.is_device()); + } } GIVEN("no filesystem (null pointer)") { THEN("constructing an inode with a null filesystem pointer panics") { - REQUIRE_THROWS_AS(kernel::filesystem::ext2::inode{nullptr}, kernel::tests::cpu::halt); + REQUIRE_THROWS_AS(kernel::filesystem::ext2::inode(nullptr, {}), kernel::tests::cpu::halt); } } } @@ -102,9 +115,10 @@ SCENARIO("Ext2 inode read stops when block mapping resolves to zero", "[filesyst auto fs = kernel::filesystem::ext2::filesystem{}; REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); - auto inode = kernel::filesystem::ext2::inode{&fs}; - inode.m_data.blocks = 2; - inode.m_data.block[0] = 0; + auto data = kernel::filesystem::ext2::inode_data{}; + data.blocks = 2; + data.block[0] = 0; + auto inode = kernel::filesystem::ext2::inode{&fs, data}; auto buffer = kstd::vector(32, std::byte{0xAB}); @@ -130,12 +144,13 @@ SCENARIO("Ext2 inode read across block boundaries", "[filesystem][ext2][inode]") auto fs = kernel::filesystem::ext2::filesystem{}; REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); - auto inode = kernel::filesystem::ext2::inode{&fs}; - inode.m_data.blocks = 2; - inode.m_data.block[0] = 20; + auto inode_data = kernel::filesystem::ext2::inode_data{}; + inode_data.blocks = 2; + inode_data.block[0] = 20; kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size - 6, "Hello ", 6); - inode.m_data.block[1] = 21; + inode_data.block[1] = 21; kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size, "World!", 6); + auto inode = kernel::filesystem::ext2::inode{&fs, inode_data}; auto buffer = kstd::vector(12, std::byte{0x00}); @@ -155,7 +170,7 @@ SCENARIO("Ext2 inode write is not implemented", "[filesystem][ext2][inode]") GIVEN("an ext2 inode") { auto fs = kernel::filesystem::ext2::filesystem{}; - auto inode = kernel::filesystem::ext2::inode{&fs}; + auto inode = kernel::filesystem::ext2::inode{&fs, kernel::filesystem::ext2::inode_data{}}; THEN("writing to the inode panics") { diff --git a/kernel/src/filesystem/inode.cpp b/kernel/src/filesystem/inode.cpp index 2f0764c..f2a7741 100644 --- a/kernel/src/filesystem/inode.cpp +++ b/kernel/src/filesystem/inode.cpp @@ -2,22 +2,18 @@ namespace kernel::filesystem { - inode::inode(inode_kind kind) - : m_kind(kind) - {} - auto inode::is_directory() const -> bool { - return m_kind == inode_kind::directory; + return false; } auto inode::is_regular() const -> bool { - return m_kind == inode_kind::regular; + return false; } auto inode::is_device() const -> bool { - return m_kind == inode_kind::device; + return false; } } // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/rootfs/inode.cpp b/kernel/src/filesystem/rootfs/inode.cpp index eeea3fe..d099676 100644 --- a/kernel/src/filesystem/rootfs/inode.cpp +++ b/kernel/src/filesystem/rootfs/inode.cpp @@ -12,10 +12,6 @@ namespace kernel::filesystem::rootfs { - inode::inode() - : kernel::filesystem::inode(inode_kind::directory) - {} - auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t { return 0; @@ -36,4 +32,9 @@ namespace kernel::filesystem::rootfs auto it = std::ranges::find_if(m_children, [&](auto const & pair) { return pair.first == name; }); return (it != m_children.end()) ? it->second : nullptr; } + + auto inode::is_directory() const -> bool + { + return true; + } } // namespace kernel::filesystem::rootfs diff --git a/kernel/src/test_support/filesystem/inode.cpp b/kernel/src/test_support/filesystem/inode.cpp index 54bd7e0..0c8d956 100644 --- a/kernel/src/test_support/filesystem/inode.cpp +++ b/kernel/src/test_support/filesystem/inode.cpp @@ -6,10 +6,6 @@ namespace kernel::tests::filesystem { - inode::inode() - : kernel::filesystem::inode(inode_kind::regular) - {} - auto inode::read(void *, size_t, size_t size) const -> size_t { return size; @@ -19,4 +15,9 @@ namespace kernel::tests::filesystem { return size; } + + auto inode::is_regular() const -> bool + { + return true; + } } // namespace kernel::tests::filesystem \ No newline at end of file -- cgit v1.2.3 From 6790ab170578594f26e8e84a3e57b80cb6094e21 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 28 Apr 2026 10:55:37 +0200 Subject: add is_symbolic_link method in ext2 inode --- .../include/kernel/filesystem/ext2/filesystem.hpp | 1 + kernel/include/kernel/filesystem/ext2/inode.hpp | 5 +++++ kernel/include/kernel/filesystem/inode.hpp | 6 ++++++ kernel/src/filesystem/ext2/inode.cpp | 5 +++++ kernel/src/filesystem/ext2/inode.tests.cpp | 24 ++++++++++++++++++++++ kernel/src/filesystem/inode.cpp | 5 +++++ 6 files changed, 46 insertions(+) diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 516e71d..d22433f 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -35,6 +35,7 @@ namespace kernel::filesystem::ext2 constexpr uint16_t inline mode_mask = 0xF000; constexpr uint16_t inline mode_regular = 0x8000; constexpr uint16_t inline mode_directory = 0x4000; + constexpr uint16_t inline mode_symbolic_link = 0xA000; } // namespace constants /** diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index 64cdb06..688a1d8 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -81,6 +81,11 @@ namespace kernel::filesystem::ext2 */ [[nodiscard]] auto is_regular() const -> bool override; + /** + // TODO BA-FS26 + */ + [[nodiscard]] auto is_symbolic_link() const -> bool override; + private: filesystem * m_filesystem; inode_data m_data{}; diff --git a/kernel/include/kernel/filesystem/inode.hpp b/kernel/include/kernel/filesystem/inode.hpp index d071a6b..b34b921 100644 --- a/kernel/include/kernel/filesystem/inode.hpp +++ b/kernel/include/kernel/filesystem/inode.hpp @@ -57,6 +57,12 @@ namespace kernel::filesystem @return true if the inode is a device, false otherwise. */ [[nodiscard]] virtual auto is_device() const -> bool; + + /** + @brief Returns whether the inode is a symbolic link. + @return true if the inode is a symbolic link, false otherwise. + */ + [[nodiscard]] virtual auto is_symbolic_link() const -> bool; }; } // namespace kernel::filesystem diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index 279c84f..5b6bcb3 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -72,4 +72,9 @@ namespace kernel::filesystem::ext2 { return (m_data.mode & constants::mode_mask) == constants::mode_directory; } + + auto inode::is_symbolic_link() const -> bool + { + return (m_data.mode & constants::mode_mask) == constants::mode_symbolic_link; + } } // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp index e68352f..f0f4aaf 100644 --- a/kernel/src/filesystem/ext2/inode.tests.cpp +++ b/kernel/src/filesystem/ext2/inode.tests.cpp @@ -33,6 +33,7 @@ SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]" REQUIRE(inode.is_regular()); REQUIRE(!inode.is_directory()); REQUIRE(!inode.is_device()); + REQUIRE(!inode.is_symbolic_link()); } THEN("the inode is initialized with directory mode in data and has the kind directory") @@ -43,6 +44,29 @@ SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]" REQUIRE(!inode.is_regular()); REQUIRE(inode.is_directory()); REQUIRE(!inode.is_device()); + REQUIRE(!inode.is_symbolic_link()); + } + + THEN("the inode is initialized with symbolic link mode in data and has the kind symbolic link") + { + data.mode = kernel::filesystem::ext2::constants::mode_symbolic_link; + auto inode = kernel::filesystem::ext2::inode(&fs, data); + + REQUIRE(!inode.is_regular()); + REQUIRE(!inode.is_directory()); + REQUIRE(!inode.is_device()); + REQUIRE(inode.is_symbolic_link()); + } + + THEN("the inode is initialized with zero mode in data and has no specific kind") + { + data.mode = 0; + auto inode = kernel::filesystem::ext2::inode(&fs, data); + + REQUIRE(!inode.is_regular()); + REQUIRE(!inode.is_directory()); + REQUIRE(!inode.is_device()); + REQUIRE(!inode.is_symbolic_link()); } } diff --git a/kernel/src/filesystem/inode.cpp b/kernel/src/filesystem/inode.cpp index f2a7741..c188917 100644 --- a/kernel/src/filesystem/inode.cpp +++ b/kernel/src/filesystem/inode.cpp @@ -16,4 +16,9 @@ namespace kernel::filesystem { return false; } + + auto inode::is_symbolic_link() const -> bool + { + return false; + } } // namespace kernel::filesystem \ No newline at end of file -- cgit v1.2.3 From 62da1b8c8d1c59abc7ca33c144591839f126937e Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 30 Apr 2026 21:32:49 +0200 Subject: resolve todos --- kernel/include/kernel/filesystem/devfs/inode.hpp | 3 ++- kernel/include/kernel/filesystem/device_inode.hpp | 3 ++- kernel/include/kernel/filesystem/ext2/inode.hpp | 12 ++++++++---- kernel/include/kernel/filesystem/rootfs/inode.hpp | 3 ++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/kernel/include/kernel/filesystem/devfs/inode.hpp b/kernel/include/kernel/filesystem/devfs/inode.hpp index 5589730..e428891 100644 --- a/kernel/include/kernel/filesystem/devfs/inode.hpp +++ b/kernel/include/kernel/filesystem/devfs/inode.hpp @@ -32,7 +32,8 @@ namespace kernel::filesystem::devfs auto write(void const * buffer, size_t offset, size_t size) -> size_t override; /** - // % TODO BA-FS26 + @brief Check if this inode represents a directory. + @return returns true, since this inode represents the /dev directory in the devfs filesystem. */ [[nodiscard]] auto is_directory() const -> bool override; }; diff --git a/kernel/include/kernel/filesystem/device_inode.hpp b/kernel/include/kernel/filesystem/device_inode.hpp index fb60524..f4aa2d1 100644 --- a/kernel/include/kernel/filesystem/device_inode.hpp +++ b/kernel/include/kernel/filesystem/device_inode.hpp @@ -51,7 +51,8 @@ namespace kernel::filesystem [[nodiscard]] auto device() const -> kstd::shared_ptr const &; /** - // TODO BA-FS26 + @brief Check if this inode represents a device. + @return returns true, since this indoe is a device inode and represents a device. */ [[nodiscard]] auto is_device() const -> bool override; diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index 688a1d8..b4a3cc4 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -67,22 +67,26 @@ namespace kernel::filesystem::ext2 auto write(void const * buffer, size_t offset, size_t size) -> size_t override; /** - // TODO BA-FS26 + @brief Get the data associated with this inode. + @return A reference to the inode data. */ [[nodiscard]] auto data() const -> inode_data const &; /** - // TODO BA-FS26 + @brief Check if this inode represents a directory. + @return returns true if this inode represents a directory, false otherwise. */ [[nodiscard]] auto is_directory() const -> bool override; /** - // TODO BA-FS26 + @brief Check if this inode represents a regular file. + @return returns true if this inode represents a regular file, false otherwise. */ [[nodiscard]] auto is_regular() const -> bool override; /** - // TODO BA-FS26 + @brief Check if this inode represents a symbolic link. + @return returns true if this inode represents a symbolic link, false otherwise. */ [[nodiscard]] auto is_symbolic_link() const -> bool override; diff --git a/kernel/include/kernel/filesystem/rootfs/inode.hpp b/kernel/include/kernel/filesystem/rootfs/inode.hpp index e7c7eff..58035ea 100644 --- a/kernel/include/kernel/filesystem/rootfs/inode.hpp +++ b/kernel/include/kernel/filesystem/rootfs/inode.hpp @@ -53,7 +53,8 @@ namespace kernel::filesystem::rootfs auto lookup_child(std::string_view name) -> kstd::shared_ptr; /** - // TODO BA-FS26 + @brief Check if this inode represents a directory. + @return returns true, since this inode represents the root directory in the rootfs filesystem. */ [[nodiscard]] auto is_directory() const -> bool override; -- cgit v1.2.3 From 8550b6a1aacc2bfce733dcac7a44065b7e9116a1 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 2 May 2026 14:14:24 +0200 Subject: relative path support --- kernel/include/kernel/filesystem/dentry.hpp | 2 +- kernel/src/filesystem/mount_table.cpp | 4 +- kernel/src/filesystem/vfs.cpp | 115 ++++++++++++++++++++++++---- 3 files changed, 103 insertions(+), 18 deletions(-) diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index 94fa39a..45d2ca2 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -24,7 +24,7 @@ namespace kernel::filesystem */ enum class dentry_flags : uint32_t { - dcache_mounted = 1 << 15 + mounted = 1 << 15 }; /** diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index da3c451..e21e497 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -54,7 +54,7 @@ namespace kernel::filesystem m_mounts.push_back(mount); if (auto mount_dentry = mount->get_mount_dentry()) { - mount_dentry->set_flag(dentry::dentry_flags::dcache_mounted); + mount_dentry->set_flag(dentry::dentry_flags::mounted); } } @@ -75,7 +75,7 @@ namespace kernel::filesystem return operation_result::has_child_mounts; } - mount->get_mount_dentry()->unset_flag(dentry::dentry_flags::dcache_mounted); + mount->get_mount_dentry()->unset_flag(dentry::dentry_flags::mounted); m_mounts.erase(std::ranges::find(m_mounts, mount)); return operation_result::removed; } diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 5b454f6..41afad3 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -10,6 +10,8 @@ #include #include +#include +#include #include #include @@ -35,21 +37,38 @@ namespace kernel::filesystem auto vfs::init_internal() -> void { + // Mount rootfs at / as the temporary base auto root_fs = kstd::make_shared(); root_fs->mount(nullptr); - auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode()); - m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, "", nullptr)); + auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode(), "/"); + m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, "/", nullptr)); + // Mount devfs at /dev in rootfs (temporary, will be shadowed) auto device_fs = kstd::make_shared(); device_fs->mount(nullptr); - do_mount_internal("/dev", root_fs_root_dentry, device_fs); + auto dev_mount_point_dentry = resolve_path("/dev"); + if (!dev_mount_point_dentry) + { + kapi::system::panic("[FILESYSTEM] failed to resolve /dev for initial devfs mount."); + } + do_mount_internal("/dev", dev_mount_point_dentry, device_fs); - if (auto boot_device_dentry = resolve_path("/dev/ram0")) // TODO BA-FS26 better boot device detection + // Mount boot filesystem at / (will shadow rootfs) + if (auto boot_device_dentry = resolve_path("/dev/ram0")) { if (auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device_dentry->get_inode())) { do_mount_internal("/", root_fs_root_dentry, boot_root_fs); + + // Resolve / to get the boot root dentry + if (auto boot_root_dentry = resolve_path("/")) + { + auto dev_dentry = kstd::make_shared(boot_root_dentry, device_fs->root_inode(), "dev"); + boot_root_dentry->add_child(dev_dentry); + + do_mount_internal("/dev", dev_dentry, device_fs); + } } } } @@ -117,33 +136,46 @@ namespace kernel::filesystem kstd::shared_ptr const & fs) -> void { auto parent_mount = m_mount_table.find_longest_prefix_mount(path); - auto new_fs_root = kstd::make_shared(mount_point_dentry, fs->root_inode()); + auto new_fs_root = + kstd::make_shared(mount_point_dentry->get_parent(), fs->root_inode(), mount_point_dentry->get_name()); auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, path, parent_mount); m_mount_table.add_mount(new_mount); } auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr { - // TODO BA-FS26 implement full path resolution semantics. // TODO BA-FS26 better path validation // TODO BA-FS26 implement a path parser (maybe in libs?) and use it here and in do_mount - if (path.empty() || path.front() != '/') return nullptr; - // TODO BA-FS26 longest match in mount_table -> jump into final fs directly - // TODO BA-FS26 performance optimization first check mounted_flag O(1) then check mount_table O(n) - - auto best_mount = m_mount_table.find_longest_prefix_mount(path); - if (!best_mount) + auto current_mount = m_mount_table.find_longest_prefix_mount("/"); + if (!current_mount) { kapi::system::panic("[FILESYSTEM] no root mount found."); } - auto current_dentry = best_mount->root_dentry(); - auto current_fs = best_mount->get_filesystem(); + auto current_dentry = current_mount->root_dentry(); + kstd::vector resolved_parts{}; + + // TODO BA-FS26 remove again an get path out of the dentires instead of building it up again here + auto build_resolved_path = [&resolved_parts]() { + kstd::string resolved_path{"/"}; + + for (auto const & resolved_part : resolved_parts) + { + if (resolved_path.size() > 1) + { + resolved_path += '/'; + } + + resolved_path += resolved_part; + } - std::string_view remaining = path.substr(best_mount->get_mount_path().size()); + return resolved_path; + }; + + std::string_view remaining = path.substr(current_mount->get_mount_path().size()); auto path_parts = std::views::split(remaining, '/') | std::views::filter([](auto const & part) { return !part.empty(); }); @@ -152,9 +184,50 @@ namespace kernel::filesystem { std::string_view part_view{part}; + if (part_view == ".") + { + continue; + } + + if (part_view == "..") + { + if (!resolved_parts.empty()) + { + resolved_parts.pop_back(); + } + + auto parent_dentry = current_dentry->get_parent(); + + if (current_dentry == current_mount->root_dentry()) + { + // change the mount point + if (auto parent_mount = current_mount->get_parent_mount()) + { + current_mount = parent_mount; + current_dentry = current_dentry->get_parent(); + + if (!parent_dentry) + { + parent_dentry = current_mount->root_dentry(); + } + } + } + + if (!parent_dentry) + { + return nullptr; + } + + current_dentry = parent_dentry; + continue; + } + + resolved_parts.push_back(part_view); + auto next_dentry = current_dentry->find_child(part_view); if (!next_dentry) { + auto current_fs = current_mount->get_filesystem(); auto found_inode = current_fs->lookup(current_dentry->get_inode(), part_view); if (!found_inode) return nullptr; @@ -162,6 +235,18 @@ namespace kernel::filesystem next_dentry = kstd::make_shared(current_dentry, found_inode, part_view); current_dentry->add_child(next_dentry); } + else if (next_dentry->has_flag(dentry::dentry_flags::mounted)) + { + // change the mount point + // TODO BA-FS26 get resolved path out of the dentry... + current_mount = m_mount_table.find_longest_prefix_mount(build_resolved_path().view()); + if (!current_mount) + { + kapi::system::panic("[FILESYSTEM] mount for dentry with mounted flag not found."); + } + + next_dentry = current_mount->root_dentry(); + } current_dentry = next_dentry; } -- cgit v1.2.3 From a096d84920aa078b5775149a95e5d3f010ae995e Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 2 May 2026 14:14:55 +0200 Subject: fix bht build --- kernel/src/filesystem/dentry.tests.cpp | 16 ++++++++-------- kernel/src/filesystem/mount_table.tests.cpp | 10 +++++----- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp index f81c260..c42c405 100644 --- a/kernel/src/filesystem/dentry.tests.cpp +++ b/kernel/src/filesystem/dentry.tests.cpp @@ -28,7 +28,7 @@ SCENARIO("Dentry construction", "[filesystem][dentry]") THEN("no flag is set") { - REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } } @@ -45,7 +45,7 @@ SCENARIO("Dentry construction", "[filesystem][dentry]") THEN("no flag is set") { - REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } } @@ -62,7 +62,7 @@ SCENARIO("Dentry construction", "[filesystem][dentry]") THEN("no flag is set") { - REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } } @@ -113,22 +113,22 @@ SCENARIO("Dentry Flag logic", "[filesystem][dentry]") WHEN("setting a flag") { - dentry.set_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted); + dentry.set_flag(kernel::filesystem::dentry::dentry_flags::mounted); THEN("the flag is set") { - REQUIRE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } } WHEN("unsetting a flag") { - dentry.set_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted); - dentry.unset_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted); + dentry.set_flag(kernel::filesystem::dentry::dentry_flags::mounted); + dentry.unset_flag(kernel::filesystem::dentry::dentry_flags::mounted); THEN("the flag is unset") { - REQUIRE_FALSE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE_FALSE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } } } diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp index 747ffdf..60b1755 100644 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -54,8 +54,8 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("dentry flags are set correctly for mounted dentries") { - REQUIRE(root_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); - REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(root_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } THEN("finding mounts by path returns the correct mount") @@ -70,7 +70,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("removing a mount that has no child mounts succeeds") { REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::removed); - REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); REQUIRE(table.find_longest_prefix_mount("/mnt") == mount1); } @@ -109,7 +109,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("removing the topmost mount with the same path succeeds") { REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::removed); - REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); REQUIRE(table.find_longest_prefix_mount("/") == mount1); } } @@ -156,7 +156,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("removing a leaf mount succeeds") { REQUIRE(table.remove_mount("/mnt/submnt") == kernel::filesystem::mount_table::operation_result::removed); - REQUIRE(!root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::dcache_mounted)); + REQUIRE(!root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == mount2); } } -- cgit v1.2.3 From 46f3992c10e960d33dbb314dad597650902686da Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 2 May 2026 14:15:26 +0200 Subject: add relative path tests --- kernel/src/filesystem/vfs.tests.cpp | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 979ea42..109ee89 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -168,6 +168,52 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.unmount("/information/nonexistent") == kernel::filesystem::vfs::operation_result::mount_point_not_found); } + + THEN("a file can be access if . in the path") + { + auto info_1 = vfs.open("/information/./info_1.txt"); + REQUIRE(info_1 != nullptr); + } + + THEN("a file can be accessed within the same mount if path contains .. ") + { + auto info_1 = vfs.open("/archiv/../information/info_1.txt"); + REQUIRE(info_1 != nullptr); + + auto img = vfs.open("/archiv/../information/../archiv/2024.img"); + REQUIRE(img != nullptr); + } + + THEN("a file can be accessed over multiple mounts if path contains .. or . ") + { + REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); + + auto img = vfs.open("/information/monkey_house/caretaker/../../../archiv/2024.img"); + REQUIRE(img != nullptr); + + auto dev_32 = vfs.open("/information/monkey_house/caretaker/../../../dev/ram32"); + REQUIRE(dev_32 != nullptr); + + auto water = vfs.open("/information/./monkey_house/infrastructure/water.txt"); + REQUIRE(water != nullptr); + } + + THEN("a file can be accessed over multiple mounts (device and file) if path contains .. ") + { + REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/archiv/2024.img", "/information/monkey_house/infrastructure") == + kernel::filesystem::vfs::operation_result::success); + + auto pig_1 = vfs.open("/information/monkey_house/infrastructure/stable/pig_1.txt"); + REQUIRE(pig_1 != nullptr); + + auto isabelle = + vfs.open("/information/monkey_house/infrastructure/stable/../../../monkey_house/caretaker/isabelle.txt"); + REQUIRE(isabelle != nullptr); + + auto closed = vfs.open("/information/monkey_house/infrastructure/stable/../../../../closed.txt"); + REQUIRE(closed != nullptr); + } } GIVEN("A real image file containing as filesystem formatted files") -- cgit v1.2.3 From d3c8b74020bfeee554394b7e41c58d5ddda6f396 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 2 May 2026 14:23:19 +0200 Subject: refactoring --- kernel/include/kernel/filesystem/dentry.hpp | 6 ++++++ kernel/src/filesystem/dentry.cpp | 17 +++++++++++++++ kernel/src/filesystem/vfs.cpp | 32 ++--------------------------- 3 files changed, 25 insertions(+), 30 deletions(-) diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index 45d2ca2..bd62735 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -54,6 +54,12 @@ namespace kernel::filesystem */ [[nodiscard]] auto get_name() const -> std::string_view; + /** + @brief Get the full path of the dentry by traversing up to the root. + @return The full path of the dentry. + */ + [[nodiscard]] auto get_full_path() const -> kstd::string; + /** @brief Add a @p child dentry. @param child The child dentry to add. diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index 572dd82..72500fd 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -38,6 +39,22 @@ namespace kernel::filesystem return m_name.view(); } + // TODO BA-FS26 fix warning (do not use recursion) + auto dentry::get_full_path() const -> kstd::string + { + if (m_parent) + { + auto parent_path = m_parent->get_full_path(); + if (parent_path != "/") + { + parent_path += '/'; + } + return parent_path + m_name.view(); + } + + return m_name.empty() ? "/" : m_name.view(); + } + auto dentry::add_child(kstd::shared_ptr const & child) -> void { m_children.push_back(child); diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 41afad3..9a6625d 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -156,25 +155,6 @@ namespace kernel::filesystem } auto current_dentry = current_mount->root_dentry(); - kstd::vector resolved_parts{}; - - // TODO BA-FS26 remove again an get path out of the dentires instead of building it up again here - auto build_resolved_path = [&resolved_parts]() { - kstd::string resolved_path{"/"}; - - for (auto const & resolved_part : resolved_parts) - { - if (resolved_path.size() > 1) - { - resolved_path += '/'; - } - - resolved_path += resolved_part; - } - - return resolved_path; - }; - std::string_view remaining = path.substr(current_mount->get_mount_path().size()); auto path_parts = @@ -191,11 +171,6 @@ namespace kernel::filesystem if (part_view == "..") { - if (!resolved_parts.empty()) - { - resolved_parts.pop_back(); - } - auto parent_dentry = current_dentry->get_parent(); if (current_dentry == current_mount->root_dentry()) @@ -222,8 +197,6 @@ namespace kernel::filesystem continue; } - resolved_parts.push_back(part_view); - auto next_dentry = current_dentry->find_child(part_view); if (!next_dentry) { @@ -237,9 +210,8 @@ namespace kernel::filesystem } else if (next_dentry->has_flag(dentry::dentry_flags::mounted)) { - // change the mount point - // TODO BA-FS26 get resolved path out of the dentry... - current_mount = m_mount_table.find_longest_prefix_mount(build_resolved_path().view()); + // TODO BA-FS26 really do it like this? or just call "get" without longes_prefix stuff + current_mount = m_mount_table.find_longest_prefix_mount(next_dentry->get_full_path().view()); if (!current_mount) { kapi::system::panic("[FILESYSTEM] mount for dentry with mounted flag not found."); -- cgit v1.2.3 From e74e56559ae99fd14715371ebceb2545f68008de Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 2 May 2026 14:24:46 +0200 Subject: add todos --- kernel/src/filesystem/vfs.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 9a6625d..a3da258 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -89,6 +89,7 @@ namespace kernel::filesystem auto vfs::do_mount(std::string_view source, std::string_view target) -> operation_result { + // TODO BA-FS26 better path validation if (target.empty() || target.front() != '/' || (target.size() > 1 && target.back() == '/')) { return operation_result::invalid_path; @@ -112,6 +113,7 @@ namespace kernel::filesystem auto vfs::unmount(std::string_view path) -> operation_result { + // TODO BA-FS26 better path validation if (path.empty() || path.front() != '/' || (path.size() > 1 && path.back() == '/')) { return operation_result::invalid_path; -- cgit v1.2.3 From 40fbefab704695b905e3de3e80668447cc64b20e Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 2 May 2026 15:36:19 +0200 Subject: refactoring and extend tests --- kernel/src/filesystem/devfs/filesystem.tests.cpp | 2 +- kernel/src/filesystem/devfs/inode.tests.cpp | 1 + kernel/src/filesystem/device_inode.tests.cpp | 1 + kernel/src/filesystem/ext2/inode.tests.cpp | 26 ++++++++++++------------ kernel/src/filesystem/mount_table.tests.cpp | 8 ++++---- kernel/src/filesystem/rootfs/inode.tests.cpp | 1 + libs/kstd/kstd/string.test.cpp | 12 +++++------ 7 files changed, 27 insertions(+), 24 deletions(-) diff --git a/kernel/src/filesystem/devfs/filesystem.tests.cpp b/kernel/src/filesystem/devfs/filesystem.tests.cpp index 2b6c09b..36cb411 100644 --- a/kernel/src/filesystem/devfs/filesystem.tests.cpp +++ b/kernel/src/filesystem/devfs/filesystem.tests.cpp @@ -47,7 +47,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, { auto non_directory_inode = fs.lookup(fs.root_inode(), "ram0"); REQUIRE(non_directory_inode != nullptr); - REQUIRE(!non_directory_inode->is_directory()); + REQUIRE_FALSE(non_directory_inode->is_directory()); auto result = fs.lookup(non_directory_inode, "anything"); REQUIRE(result == nullptr); diff --git a/kernel/src/filesystem/devfs/inode.tests.cpp b/kernel/src/filesystem/devfs/inode.tests.cpp index 030d709..ae26e74 100644 --- a/kernel/src/filesystem/devfs/inode.tests.cpp +++ b/kernel/src/filesystem/devfs/inode.tests.cpp @@ -19,6 +19,7 @@ SCENARIO("Devfs inode creation", "[filesystem][devfs][inode]") REQUIRE(inode.is_directory()); REQUIRE_FALSE(inode.is_device()); REQUIRE_FALSE(inode.is_regular()); + REQUIRE_FALSE(inode.is_symbolic_link()); } } } diff --git a/kernel/src/filesystem/device_inode.tests.cpp b/kernel/src/filesystem/device_inode.tests.cpp index 8ac4eff..025a22a 100644 --- a/kernel/src/filesystem/device_inode.tests.cpp +++ b/kernel/src/filesystem/device_inode.tests.cpp @@ -33,6 +33,7 @@ SCENARIO("Device inode construction", "[filesystem][device_inode]") REQUIRE(inode.is_device()); REQUIRE_FALSE(inode.is_directory()); REQUIRE_FALSE(inode.is_regular()); + REQUIRE_FALSE(inode.is_symbolic_link()); } } diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp index f0f4aaf..49ba21b 100644 --- a/kernel/src/filesystem/ext2/inode.tests.cpp +++ b/kernel/src/filesystem/ext2/inode.tests.cpp @@ -31,9 +31,9 @@ SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]" auto inode = kernel::filesystem::ext2::inode(&fs, data); REQUIRE(inode.is_regular()); - REQUIRE(!inode.is_directory()); - REQUIRE(!inode.is_device()); - REQUIRE(!inode.is_symbolic_link()); + REQUIRE_FALSE(inode.is_directory()); + REQUIRE_FALSE(inode.is_device()); + REQUIRE_FALSE(inode.is_symbolic_link()); } THEN("the inode is initialized with directory mode in data and has the kind directory") @@ -41,10 +41,10 @@ SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]" data.mode = kernel::filesystem::ext2::constants::mode_directory; auto inode = kernel::filesystem::ext2::inode(&fs, data); - REQUIRE(!inode.is_regular()); + REQUIRE_FALSE(inode.is_regular()); REQUIRE(inode.is_directory()); - REQUIRE(!inode.is_device()); - REQUIRE(!inode.is_symbolic_link()); + REQUIRE_FALSE(inode.is_device()); + REQUIRE_FALSE(inode.is_symbolic_link()); } THEN("the inode is initialized with symbolic link mode in data and has the kind symbolic link") @@ -52,9 +52,9 @@ SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]" data.mode = kernel::filesystem::ext2::constants::mode_symbolic_link; auto inode = kernel::filesystem::ext2::inode(&fs, data); - REQUIRE(!inode.is_regular()); - REQUIRE(!inode.is_directory()); - REQUIRE(!inode.is_device()); + REQUIRE_FALSE(inode.is_regular()); + REQUIRE_FALSE(inode.is_directory()); + REQUIRE_FALSE(inode.is_device()); REQUIRE(inode.is_symbolic_link()); } @@ -63,10 +63,10 @@ SCENARIO("Ext2 inode initialization and properties", "[filesystem][ext2][inode]" data.mode = 0; auto inode = kernel::filesystem::ext2::inode(&fs, data); - REQUIRE(!inode.is_regular()); - REQUIRE(!inode.is_directory()); - REQUIRE(!inode.is_device()); - REQUIRE(!inode.is_symbolic_link()); + REQUIRE_FALSE(inode.is_regular()); + REQUIRE_FALSE(inode.is_directory()); + REQUIRE_FALSE(inode.is_device()); + REQUIRE_FALSE(inode.is_symbolic_link()); } } diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp index 60b1755..e028ac8 100644 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -55,7 +55,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("dentry flags are set correctly for mounted dentries") { REQUIRE(root_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); - REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } THEN("finding mounts by path returns the correct mount") @@ -70,7 +70,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("removing a mount that has no child mounts succeeds") { REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::removed); - REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); REQUIRE(table.find_longest_prefix_mount("/mnt") == mount1); } @@ -109,7 +109,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("removing the topmost mount with the same path succeeds") { REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::removed); - REQUIRE(!root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); REQUIRE(table.find_longest_prefix_mount("/") == mount1); } } @@ -156,7 +156,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("removing a leaf mount succeeds") { REQUIRE(table.remove_mount("/mnt/submnt") == kernel::filesystem::mount_table::operation_result::removed); - REQUIRE(!root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE_FALSE(root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == mount2); } } diff --git a/kernel/src/filesystem/rootfs/inode.tests.cpp b/kernel/src/filesystem/rootfs/inode.tests.cpp index 879818c..7cc217f 100644 --- a/kernel/src/filesystem/rootfs/inode.tests.cpp +++ b/kernel/src/filesystem/rootfs/inode.tests.cpp @@ -17,6 +17,7 @@ SCENARIO("Rootfs inode creation", "[filesystem][rootfs][inode]") REQUIRE(inode.is_directory()); REQUIRE_FALSE(inode.is_device()); REQUIRE_FALSE(inode.is_regular()); + REQUIRE_FALSE(inode.is_symbolic_link()); } THEN("the inode has no children") diff --git a/libs/kstd/kstd/string.test.cpp b/libs/kstd/kstd/string.test.cpp index 53d7c9a..9755676 100644 --- a/libs/kstd/kstd/string.test.cpp +++ b/libs/kstd/kstd/string.test.cpp @@ -37,7 +37,7 @@ SCENARIO("String initialization and construction", "[string]") THEN("the string is not empty and has the same size as the view") { - REQUIRE(!str.empty()); + REQUIRE_FALSE(str.empty()); REQUIRE(str.size() == view.size()); } @@ -58,7 +58,7 @@ SCENARIO("String initialization and construction", "[string]") THEN("the string is not empty and has the same size as the C-style string") { - REQUIRE(!str.empty()); + REQUIRE_FALSE(str.empty()); REQUIRE(str.size() == std::strlen(c_str)); } @@ -79,7 +79,7 @@ SCENARIO("String initialization and construction", "[string]") THEN("the string is not empty and has size 1") { - REQUIRE(!str.empty()); + REQUIRE_FALSE(str.empty()); REQUIRE(str.size() == 1); } @@ -294,7 +294,7 @@ SCENARIO("String conversion and comparison", "[string]") THEN("the strings are not unequal") { - REQUIRE(!(str1 != str2)); + REQUIRE_FALSE(str1 != str2); } } @@ -311,8 +311,8 @@ SCENARIO("String conversion and comparison", "[string]") THEN("the string and the string view are not unequal") { - REQUIRE(!(str != view)); - REQUIRE(!(view != str)); + REQUIRE_FALSE(str != view); + REQUIRE_FALSE(view != str); } } } -- cgit v1.2.3 From 1269410c4c733ed38859b3e70713728afb273443 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Sun, 3 May 2026 15:01:49 +0200 Subject: Use iterator in path resolution, start with symlink implementation --- kernel/src/filesystem/vfs.cpp | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index a3da258..ba4f404 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -94,7 +94,7 @@ namespace kernel::filesystem { return operation_result::invalid_path; } - + // TODO BA-FS26 check if target is directory? if (auto mount_point_dentry = resolve_path(target)) { if (auto source_dentry = resolve_path(source)) @@ -162,9 +162,9 @@ namespace kernel::filesystem auto path_parts = std::views::split(remaining, '/') | std::views::filter([](auto const & part) { return !part.empty(); }); - for (auto const & part : path_parts) + for (auto it = path_parts.begin(); it != path_parts.end(); ++it) { - std::string_view part_view{part}; + std::string_view part_view{*it}; if (part_view == ".") { @@ -205,7 +205,9 @@ namespace kernel::filesystem auto current_fs = current_mount->get_filesystem(); auto found_inode = current_fs->lookup(current_dentry->get_inode(), part_view); if (!found_inode) + { return nullptr; + } next_dentry = kstd::make_shared(current_dentry, found_inode, part_view); current_dentry->add_child(next_dentry); @@ -222,6 +224,38 @@ namespace kernel::filesystem next_dentry = current_mount->root_dentry(); } + if (next_dentry->get_inode()->is_symbolic_link()) + { + // TODO BA-FS26 this for loop is ugly --> fix + kstd::string symlink_path = "halo"; + if (symlink_path.size() > 0 && symlink_path.front() == '/') + { + for (auto it2 = std::next(it); it2 != path_parts.end(); ++it2) + { + symlink_path += "/"; + symlink_path.append(std::string_view{*it2}); + } + return resolve_path(symlink_path.view()); + } + else + { + kstd::string combined; + for (auto it3 = path_parts.begin(); it3 != it; ++it3) + { + combined += "/"; + combined.append(std::string_view{*it3}); + } + combined += "/"; + combined += symlink_path; + for (auto it4 = std::next(it); it4 != path_parts.end(); ++it4) + { + combined += "/"; + combined.append(std::string_view{*it4}); + } + return resolve_path(combined.view()); + } + } + current_dentry = next_dentry; } -- cgit v1.2.3 From 72d686961a26789b7659f17fb090511ee28604ec Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 4 May 2026 19:47:15 +0200 Subject: Add helper functions for path validation and splitting --- kernel/include/kernel/filesystem/path.hpp | 56 +++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 kernel/include/kernel/filesystem/path.hpp diff --git a/kernel/include/kernel/filesystem/path.hpp b/kernel/include/kernel/filesystem/path.hpp new file mode 100644 index 0000000..976926f --- /dev/null +++ b/kernel/include/kernel/filesystem/path.hpp @@ -0,0 +1,56 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_PATH_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_PATH_HPP + +#include +#include + +namespace kernel::filesystem::path +{ + /** + @brief Provides utilities for handling filesystem paths, including validation and splitting into components. + */ + + /** + @brief Checks if the given path is a valid absolute path (starts with '/'). + @param path The path to check. + @return true if the path is a valid absolute path, false otherwise. + */ + auto inline is_valid_absolute_path(std::string_view path) -> bool + { + return !path.empty() && path.front() == '/'; + } + + /** + @brief Checks if the given path is a valid relative path (doesn't start with '/'). + @param path The path to check. + @return true if the path is a valid relative path, false otherwise. + */ + auto inline is_valid_relative_path(std::string_view path) -> bool + { + return !path.empty() && path.front() != '/'; + } + + /** + @brief Checks if the given path is a valid path (either absolute or relative). + @param path The path to check. + @return true if the path is a valid path, false otherwise. + */ + auto inline is_valid_path(std::string_view path) -> bool + { + return is_valid_absolute_path(path) || is_valid_relative_path(path); + } + + /** + @brief Splits the given path into its components. + @param path The path to split. + @return A range of string views representing the components of the path. + */ + auto inline split(std::string_view path) + { + return std::views::split(path, '/') | std::views::filter([](auto const & part) { return !part.empty(); }) | + std::views::transform([](auto const & part) { return std::string_view(part.begin(), part.end()); }); + } + +} // namespace kernel::filesystem::path + +#endif // TEACH_OS_KERNEL_FILESYSTEM_PATH_HPP \ No newline at end of file -- cgit v1.2.3 From 7ff97f0fe861bb7382f41ecd4bab26cbb62b1f7d Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 4 May 2026 19:47:57 +0200 Subject: Resolve TODOs concerning path validation --- kernel/src/filesystem/vfs.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index ba4f404..8f48820 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -89,12 +90,11 @@ namespace kernel::filesystem auto vfs::do_mount(std::string_view source, std::string_view target) -> operation_result { - // TODO BA-FS26 better path validation - if (target.empty() || target.front() != '/' || (target.size() > 1 && target.back() == '/')) + if (!kernel::filesystem::path::is_valid_path(source) || !kernel::filesystem::path::is_valid_path(target)) { return operation_result::invalid_path; } - // TODO BA-FS26 check if target is directory? + if (auto mount_point_dentry = resolve_path(target)) { if (auto source_dentry = resolve_path(source)) @@ -113,8 +113,7 @@ namespace kernel::filesystem auto vfs::unmount(std::string_view path) -> operation_result { - // TODO BA-FS26 better path validation - if (path.empty() || path.front() != '/' || (path.size() > 1 && path.back() == '/')) + if (!kernel::filesystem::path::is_valid_path(path)) { return operation_result::invalid_path; } @@ -145,11 +144,12 @@ namespace kernel::filesystem auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr { - // TODO BA-FS26 better path validation - // TODO BA-FS26 implement a path parser (maybe in libs?) and use it here and in do_mount - if (path.empty() || path.front() != '/') + if (!kernel::filesystem::path::is_valid_absolute_path(path)) + { return nullptr; + } + // TODO BA-FS26 refactor (get mount by path, no more prefix matching) auto current_mount = m_mount_table.find_longest_prefix_mount("/"); if (!current_mount) { @@ -157,10 +157,8 @@ namespace kernel::filesystem } auto current_dentry = current_mount->root_dentry(); - std::string_view remaining = path.substr(current_mount->get_mount_path().size()); - auto path_parts = - std::views::split(remaining, '/') | std::views::filter([](auto const & part) { return !part.empty(); }); + auto path_parts = kernel::filesystem::path::split(path); for (auto it = path_parts.begin(); it != path_parts.end(); ++it) { -- cgit v1.2.3 From d90d6bb4b4820df6cb4b0747439293bb85b8cbec Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 4 May 2026 20:17:43 +0200 Subject: Implement symlink read in inode, fix max amount of bytes to read --- kernel/include/kernel/filesystem/ext2/inode.hpp | 2 +- kernel/src/filesystem/ext2/inode.cpp | 16 +++++++++++++++- kernel/src/filesystem/open_file_descriptor.tests.cpp | 13 ++++--------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index b4a3cc4..b8f892a 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -20,7 +20,7 @@ namespace kernel::filesystem::ext2 { uint16_t mode; uint16_t uid; - uint32_t size; + uint32_t size; // TODO BA-FS26 signed? uint32_t atime; uint32_t ctime; uint32_t mtime; diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index 5b6bcb3..1914c70 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -5,6 +5,8 @@ #include +#include + #include #include #include @@ -23,6 +25,17 @@ namespace kernel::filesystem::ext2 auto inode::read(void * buffer, size_t offset, size_t size) const -> size_t { + // TODO BA-FS26 use revision 1 size + auto const max_readable = static_cast(m_data.size) - offset; + auto const requested_size = std::min(size, max_readable); + + if (is_symbolic_link() && m_data.size <= sizeof(m_data.block)) + { + auto inline_target = reinterpret_cast(m_data.block.data()); + kstd::libc::memcpy(static_cast(buffer), inline_target + offset, requested_size); + return requested_size; + } + auto block_index = offset / m_filesystem->get_block_size(); auto in_block_offset = offset % m_filesystem->get_block_size(); @@ -40,7 +53,8 @@ namespace kernel::filesystem::ext2 auto const block_start_offset = block_number * m_filesystem->get_block_size(); auto const read_offset = block_start_offset + in_block_offset; - auto const bytes_to_read = std::min(size - bytes_read, m_filesystem->get_block_size() - in_block_offset); + auto const bytes_to_read = + std::min(requested_size - bytes_read, m_filesystem->get_block_size() - in_block_offset); bytes_read += m_filesystem->backing_inode()->read(static_cast(buffer) + bytes_read, read_offset, bytes_to_read); diff --git a/kernel/src/filesystem/open_file_descriptor.tests.cpp b/kernel/src/filesystem/open_file_descriptor.tests.cpp index 095e203..53835ba 100644 --- a/kernel/src/filesystem/open_file_descriptor.tests.cpp +++ b/kernel/src/filesystem/open_file_descriptor.tests.cpp @@ -1,4 +1,5 @@ #include + #include #include #include @@ -83,17 +84,11 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Ope { kstd::vector buffer(32); auto bytes_read = ofd->read(buffer.data(), buffer.size()); - REQUIRE(bytes_read == 32); - REQUIRE(ofd->offset() == 32); + REQUIRE(bytes_read == 7); + REQUIRE(ofd->offset() == 7); std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; - auto const content_end = buffer_as_str.find('\0'); - REQUIRE(buffer_as_str.substr(0, content_end) == "info_1\n"); - - for (auto i = content_end; i < buffer_as_str.size(); ++i) - { - REQUIRE(buffer_as_str[i] == '\0'); - } + REQUIRE(buffer_as_str == "info_1\n"); } THEN("the file can be read multiple times") -- cgit v1.2.3 From d1e64340abb960c579651635d580660c12f94e6e Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 4 May 2026 20:31:58 +0200 Subject: Refactor path resolution, use vector and while instead of iterator and for-loop --- kernel/src/filesystem/vfs.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 8f48820..c9939fa 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -12,7 +12,9 @@ #include #include +#include +#include #include #include #include @@ -159,10 +161,13 @@ namespace kernel::filesystem auto current_dentry = current_mount->root_dentry(); auto path_parts = kernel::filesystem::path::split(path); + kstd::vector path_parts_vector(path_parts.begin(), path_parts.end()); + std::ranges::reverse(path_parts_vector); - for (auto it = path_parts.begin(); it != path_parts.end(); ++it) + while (!path_parts_vector.empty()) { - std::string_view part_view{*it}; + std::string_view part_view{path_parts_vector.back()}; + path_parts_vector.pop_back(); if (part_view == ".") { -- cgit v1.2.3 From bddee0f0cb77eb247eceea18005dc575b433bb69 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 4 May 2026 20:51:49 +0200 Subject: Implement symlink non-recursive --- kernel/src/filesystem/vfs.cpp | 41 +++++++++++++++-------------------------- 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index c9939fa..06d36ce 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -229,33 +230,21 @@ namespace kernel::filesystem if (next_dentry->get_inode()->is_symbolic_link()) { - // TODO BA-FS26 this for loop is ugly --> fix - kstd::string symlink_path = "halo"; - if (symlink_path.size() > 0 && symlink_path.front() == '/') - { - for (auto it2 = std::next(it); it2 != path_parts.end(); ++it2) - { - symlink_path += "/"; - symlink_path.append(std::string_view{*it2}); - } - return resolve_path(symlink_path.view()); - } - else + // TODO BA-FS26 max symbolic link depth to prevent infinite loops + kstd::vector buffer(4096); // TODO BA-FS26 max symlink size? + auto const bytes_read = next_dentry->get_inode()->read(buffer.data(), 0, buffer.size()); + auto const symbolic_link_path = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; + + auto symbolic_link_parts = kernel::filesystem::path::split(symbolic_link_path); + kstd::vector symbolic_link_parts_vector(symbolic_link_parts.begin(), symbolic_link_parts.end()); + std::ranges::reverse(symbolic_link_parts_vector); + + path_parts_vector.insert_range(path_parts_vector.end(), symbolic_link_parts_vector); + + if (kernel::filesystem::path::is_valid_absolute_path(symbolic_link_path)) { - kstd::string combined; - for (auto it3 = path_parts.begin(); it3 != it; ++it3) - { - combined += "/"; - combined.append(std::string_view{*it3}); - } - combined += "/"; - combined += symlink_path; - for (auto it4 = std::next(it); it4 != path_parts.end(); ++it4) - { - combined += "/"; - combined.append(std::string_view{*it4}); - } - return resolve_path(combined.view()); + current_mount = m_mount_table.find_longest_prefix_mount("/"); + next_dentry = current_mount->root_dentry(); } } -- cgit v1.2.3 From 9aaabb2cf73f20cc4d4c68588e7bf4592837626f Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 4 May 2026 23:25:16 +0200 Subject: Add simple symlink tests --- arch/x86_64/support/modules/README.md | 5 ++++- arch/x86_64/support/modules/ext2_1KB_fs.img | 2 +- kernel/src/filesystem/vfs.tests.cpp | 20 ++++++++++++++++++++ .../filesystem/test_assets/ext2_1KB_fs.img | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/arch/x86_64/support/modules/README.md b/arch/x86_64/support/modules/README.md index a04b9af..fac5daf 100644 --- a/arch/x86_64/support/modules/README.md +++ b/arch/x86_64/support/modules/README.md @@ -8,10 +8,13 @@ The ext2_4KB_fs image is intentionally fragmented, as some files were created an ./archiv ./archiv/2024.img ./archiv/2025.img +./closed.txt ./information ./information/info_1.txt ./information/info_2.txt -./closed.txt +./symlinks +./symlinks/info_1_absolute -> /information/info_1.txt +./symlinks/info_1_relative -> ../information/info_1.txt ### 2024.img (ext2 filesystem with 2KB Block size) diff --git a/arch/x86_64/support/modules/ext2_1KB_fs.img b/arch/x86_64/support/modules/ext2_1KB_fs.img index 5bbb76d..0312727 100644 --- a/arch/x86_64/support/modules/ext2_1KB_fs.img +++ b/arch/x86_64/support/modules/ext2_1KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2ef9536a439825520d9e230eedaa9ae327f9763350eddbc0f24bf5b9b5d2bf2 +oid sha256:7cf40c3cf3d8e3be614cadf2da1c76138c492734c3730eadbca645f50ed99a50 size 10485760 diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 109ee89..829c9d2 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -283,4 +283,24 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(unmounted_sheep_1 == nullptr); } } + + GIVEN("one real image files, containing symbolic links") + { + REQUIRE(std::filesystem::exists(image_path_1)); + REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1"}, {image_path_1})); + + THEN("file can be opened through absolute symbolic link") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto info_1 = vfs.open("/symlinks/info_1_absolute"); + REQUIRE(info_1 != nullptr); + } + + THEN("file can be opened through relative symbolic link") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto info_1 = vfs.open("/symlinks/info_1_relative"); + REQUIRE(info_1 != nullptr); + } + } } diff --git a/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img b/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img index 5bbb76d..0312727 100644 --- a/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img +++ b/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c2ef9536a439825520d9e230eedaa9ae327f9763350eddbc0f24bf5b9b5d2bf2 +oid sha256:7cf40c3cf3d8e3be614cadf2da1c76138c492734c3730eadbca645f50ed99a50 size 10485760 -- cgit v1.2.3 From 2ab9d6ffbc4aad8ab3a393bd32191e3c07103c0a Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 5 May 2026 14:37:47 +0200 Subject: fix problem with string_view lifetime --- kernel/include/kernel/filesystem/path.hpp | 5 +++-- kernel/src/filesystem/vfs.cpp | 15 ++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/kernel/include/kernel/filesystem/path.hpp b/kernel/include/kernel/filesystem/path.hpp index 976926f..298ac5f 100644 --- a/kernel/include/kernel/filesystem/path.hpp +++ b/kernel/include/kernel/filesystem/path.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace kernel::filesystem::path { @@ -43,12 +44,12 @@ namespace kernel::filesystem::path /** @brief Splits the given path into its components. @param path The path to split. - @return A range of string views representing the components of the path. + @return A range of strings representing the components of the path. */ auto inline split(std::string_view path) { return std::views::split(path, '/') | std::views::filter([](auto const & part) { return !part.empty(); }) | - std::views::transform([](auto const & part) { return std::string_view(part.begin(), part.end()); }); + std::views::transform([](auto const & part) { return kstd::string(std::string_view(part.begin(), part.end())); }); } } // namespace kernel::filesystem::path diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 06d36ce..03921df 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -167,15 +167,15 @@ namespace kernel::filesystem while (!path_parts_vector.empty()) { - std::string_view part_view{path_parts_vector.back()}; + auto part = path_parts_vector.back(); path_parts_vector.pop_back(); - if (part_view == ".") + if (part == ".") { continue; } - if (part_view == "..") + if (part == "..") { auto parent_dentry = current_dentry->get_parent(); @@ -203,17 +203,17 @@ namespace kernel::filesystem continue; } - auto next_dentry = current_dentry->find_child(part_view); + auto next_dentry = current_dentry->find_child(part.view()); if (!next_dentry) { auto current_fs = current_mount->get_filesystem(); - auto found_inode = current_fs->lookup(current_dentry->get_inode(), part_view); + auto found_inode = current_fs->lookup(current_dentry->get_inode(), part.view()); if (!found_inode) { return nullptr; } - next_dentry = kstd::make_shared(current_dentry, found_inode, part_view); + next_dentry = kstd::make_shared(current_dentry, found_inode, part.view()); current_dentry->add_child(next_dentry); } else if (next_dentry->has_flag(dentry::dentry_flags::mounted)) @@ -244,8 +244,9 @@ namespace kernel::filesystem if (kernel::filesystem::path::is_valid_absolute_path(symbolic_link_path)) { current_mount = m_mount_table.find_longest_prefix_mount("/"); - next_dentry = current_mount->root_dentry(); + current_dentry = current_mount->root_dentry(); } + continue; } current_dentry = next_dentry; -- cgit v1.2.3 From 88ebc124ed0e703e02dfe96006d0d490e83de4fe Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Tue, 5 May 2026 14:46:21 +0200 Subject: Fix vfs tests --- kernel/src/filesystem/vfs.tests.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 829c9d2..cbd67be 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -140,8 +140,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("mount with invalid path fails") { REQUIRE(vfs.do_mount("/dev/ram16", "") == kernel::filesystem::vfs::operation_result::invalid_path); - REQUIRE(vfs.do_mount("/dev/ram16", "information") == kernel::filesystem::vfs::operation_result::invalid_path); - REQUIRE(vfs.do_mount("/dev/ram16", "/information/") == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(vfs.do_mount("/dev/ram16", "information") == + kernel::filesystem::vfs::operation_result::mount_point_not_found); } THEN("mount with non-existent source path fails") @@ -159,8 +159,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("unmount with invalid path fails") { REQUIRE(vfs.unmount("") == kernel::filesystem::vfs::operation_result::invalid_path); - REQUIRE(vfs.unmount("information") == kernel::filesystem::vfs::operation_result::invalid_path); - REQUIRE(vfs.unmount("/information/") == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(vfs.unmount("information") == kernel::filesystem::vfs::operation_result::mount_point_not_found); } THEN("unmounting non-existent mount point returns expected error code") -- cgit v1.2.3 From 156925495d7bc8bd684a346e2ab9eea12f8187cc Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Tue, 5 May 2026 15:59:08 +0200 Subject: Add constants --- kernel/include/kernel/filesystem/constants.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 kernel/include/kernel/filesystem/constants.hpp diff --git a/kernel/include/kernel/filesystem/constants.hpp b/kernel/include/kernel/filesystem/constants.hpp new file mode 100644 index 0000000..aff512a --- /dev/null +++ b/kernel/include/kernel/filesystem/constants.hpp @@ -0,0 +1,13 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_CONSTANTS_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_CONSTANTS_HPP + +#include +namespace kernel::filesystem::constants +{ + constexpr size_t inline max_path_length = 4096; + + constexpr size_t inline symlink_max_path_length = 4096; + constexpr size_t inline symloop_max = 40; +} // namespace kernel::filesystem::constants + +#endif \ No newline at end of file -- cgit v1.2.3 From 9f6353679f4052b2f685ad74994bfb6d54678d44 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Tue, 5 May 2026 16:00:11 +0200 Subject: Add check for valid path length --- kernel/include/kernel/filesystem/path.hpp | 32 ++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/kernel/include/kernel/filesystem/path.hpp b/kernel/include/kernel/filesystem/path.hpp index 298ac5f..4845bf1 100644 --- a/kernel/include/kernel/filesystem/path.hpp +++ b/kernel/include/kernel/filesystem/path.hpp @@ -1,41 +1,54 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_PATH_HPP #define TEACH_OS_KERNEL_FILESYSTEM_PATH_HPP +#include + +#include + #include #include -#include namespace kernel::filesystem::path { /** @brief Provides utilities for handling filesystem paths, including validation and splitting into components. - */ + */ + + /** + @brief Checks if the given path is within the maximum allowed length. + @param path The path to check. + @return true if the path length is valid, false otherwise. + */ + auto inline is_valid_path_length(std::string_view path) -> bool + { + return path.length() < kernel::filesystem::constants::max_path_length; + } /** @brief Checks if the given path is a valid absolute path (starts with '/'). @param path The path to check. @return true if the path is a valid absolute path, false otherwise. - */ + */ auto inline is_valid_absolute_path(std::string_view path) -> bool { - return !path.empty() && path.front() == '/'; + return !path.empty() && path.front() == '/' && is_valid_path_length(path); } /** @brief Checks if the given path is a valid relative path (doesn't start with '/'). @param path The path to check. @return true if the path is a valid relative path, false otherwise. - */ + */ auto inline is_valid_relative_path(std::string_view path) -> bool { - return !path.empty() && path.front() != '/'; + return !path.empty() && path.front() != '/' && is_valid_path_length(path); } /** @brief Checks if the given path is a valid path (either absolute or relative). @param path The path to check. @return true if the path is a valid path, false otherwise. - */ + */ auto inline is_valid_path(std::string_view path) -> bool { return is_valid_absolute_path(path) || is_valid_relative_path(path); @@ -45,11 +58,12 @@ namespace kernel::filesystem::path @brief Splits the given path into its components. @param path The path to split. @return A range of strings representing the components of the path. - */ + */ auto inline split(std::string_view path) { return std::views::split(path, '/') | std::views::filter([](auto const & part) { return !part.empty(); }) | - std::views::transform([](auto const & part) { return kstd::string(std::string_view(part.begin(), part.end())); }); + std::views::transform( + [](auto const & part) { return kstd::string(std::string_view(part.begin(), part.end())); }); } } // namespace kernel::filesystem::path -- cgit v1.2.3 From b6cc02c3bac002ebac8e6ba734b00f0e60b24778 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Tue, 5 May 2026 16:05:48 +0200 Subject: Add check for max symlink count --- kernel/src/filesystem/vfs.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 03921df..e6d9241 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -93,7 +94,7 @@ namespace kernel::filesystem auto vfs::do_mount(std::string_view source, std::string_view target) -> operation_result { - if (!kernel::filesystem::path::is_valid_path(source) || !kernel::filesystem::path::is_valid_path(target)) + if (!path::is_valid_path(source) || !path::is_valid_path(target)) { return operation_result::invalid_path; } @@ -116,7 +117,7 @@ namespace kernel::filesystem auto vfs::unmount(std::string_view path) -> operation_result { - if (!kernel::filesystem::path::is_valid_path(path)) + if (!path::is_valid_path(path)) { return operation_result::invalid_path; } @@ -147,7 +148,7 @@ namespace kernel::filesystem auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr { - if (!kernel::filesystem::path::is_valid_absolute_path(path)) + if (!path::is_valid_absolute_path(path)) { return nullptr; } @@ -161,10 +162,12 @@ namespace kernel::filesystem auto current_dentry = current_mount->root_dentry(); - auto path_parts = kernel::filesystem::path::split(path); + auto path_parts = path::split(path); kstd::vector path_parts_vector(path_parts.begin(), path_parts.end()); std::ranges::reverse(path_parts_vector); + auto symlink_counter = 0uz; + while (!path_parts_vector.empty()) { auto part = path_parts_vector.back(); @@ -230,18 +233,22 @@ namespace kernel::filesystem if (next_dentry->get_inode()->is_symbolic_link()) { - // TODO BA-FS26 max symbolic link depth to prevent infinite loops - kstd::vector buffer(4096); // TODO BA-FS26 max symlink size? + if (symlink_counter++ > constants::symloop_max) + { + return nullptr; + } + + kstd::vector buffer(constants::symlink_max_path_length); auto const bytes_read = next_dentry->get_inode()->read(buffer.data(), 0, buffer.size()); auto const symbolic_link_path = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; - auto symbolic_link_parts = kernel::filesystem::path::split(symbolic_link_path); + auto symbolic_link_parts = path::split(symbolic_link_path); kstd::vector symbolic_link_parts_vector(symbolic_link_parts.begin(), symbolic_link_parts.end()); std::ranges::reverse(symbolic_link_parts_vector); path_parts_vector.insert_range(path_parts_vector.end(), symbolic_link_parts_vector); - if (kernel::filesystem::path::is_valid_absolute_path(symbolic_link_path)) + if (path::is_valid_absolute_path(symbolic_link_path)) { current_mount = m_mount_table.find_longest_prefix_mount("/"); current_dentry = current_mount->root_dentry(); -- cgit v1.2.3 From 94232ac15ce987cf6792be0c4d5b0141d7cac0b1 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Tue, 5 May 2026 16:18:54 +0200 Subject: Add tests for path utility --- kernel/CMakeLists.txt | 1 + kernel/src/filesystem/path.tests.cpp | 69 ++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 kernel/src/filesystem/path.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 909ccf7..860e28b 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -188,6 +188,7 @@ if(BUILD_TESTING) "src/filesystem/devfs/inode.tests.cpp" "src/filesystem/ext2/filesystem.tests.cpp" "src/filesystem/ext2/inode.tests.cpp" + "src/filesystem/path.tests.cpp" "src/filesystem/rootfs/filesystem.tests.cpp" "src/filesystem/rootfs/inode.tests.cpp" "src/filesystem/dentry.tests.cpp" diff --git a/kernel/src/filesystem/path.tests.cpp b/kernel/src/filesystem/path.tests.cpp new file mode 100644 index 0000000..3c18b5c --- /dev/null +++ b/kernel/src/filesystem/path.tests.cpp @@ -0,0 +1,69 @@ +#include + +#include + +#include +#include +#include +#include + +SCENARIO("path utilities", "[filesystem][path]") +{ + GIVEN("valid and invalid paths") + { + THEN("valid absolute paths are recognized as valid") + { + REQUIRE(kernel::filesystem::path::is_valid_path("/valid/absolute/path")); + REQUIRE(kernel::filesystem::path::is_valid_path("/")); + } + + THEN("valid relative paths are recognized as valid") + { + REQUIRE(kernel::filesystem::path::is_valid_path("valid/../relative/.././path")); + REQUIRE(kernel::filesystem::path::is_valid_path("valid/relative/path")); + REQUIRE(kernel::filesystem::path::is_valid_path("file.txt")); + } + + THEN("invalid paths are recognized as invalid") + { + REQUIRE_FALSE(kernel::filesystem::path::is_valid_path("")); + REQUIRE_FALSE(kernel::filesystem::path::is_valid_path(std::string(4096, 'a'))); + } + + THEN("valid absolute paths are recognized as absolute") + { + REQUIRE(kernel::filesystem::path::is_valid_absolute_path("/valid/absolute/path")); + REQUIRE_FALSE(kernel::filesystem::path::is_valid_absolute_path("valid/relative/path")); + } + + THEN("invalid paths are not recognized as absolute") + { + REQUIRE_FALSE(kernel::filesystem::path::is_valid_absolute_path("")); + REQUIRE_FALSE(kernel::filesystem::path::is_valid_absolute_path(std::string(4096, 'a'))); + REQUIRE_FALSE(kernel::filesystem::path::is_valid_absolute_path("invalid/absolute/path")); + } + + THEN("valid relative paths are recognized as relative") + { + REQUIRE(kernel::filesystem::path::is_valid_relative_path("valid/relative/path")); + REQUIRE_FALSE(kernel::filesystem::path::is_valid_relative_path("/valid/absolute/path")); + } + + THEN("invalid paths are not recognized as relative") + { + REQUIRE_FALSE(kernel::filesystem::path::is_valid_relative_path("")); + REQUIRE_FALSE(kernel::filesystem::path::is_valid_relative_path(std::string(4096, 'a'))); + REQUIRE_FALSE(kernel::filesystem::path::is_valid_relative_path("/invalid/absolute/path")); + } + } + + GIVEN("a valid path") + { + THEN("it can be split into components") + { + auto components = kernel::filesystem::path::split("/a/b///c/d.txt"); + std::vector expected = {"a", "b", "c", "d.txt"}; + REQUIRE(std::ranges::equal(components, expected)); + } + } +} \ No newline at end of file -- cgit v1.2.3 From c60f2e345231e03176e0882a88f8675c1814e456 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Tue, 5 May 2026 16:22:23 +0200 Subject: Remove manual tests --- kernel/src/main.cpp | 168 +--------------------------------------------------- 1 file changed, 1 insertion(+), 167 deletions(-) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index ffea979..bfb731a 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -21,173 +21,11 @@ #include #include -#include #include #include using namespace kstd::units_literals; -auto test_device_names() -> void -{ - auto storage_mgmt = kernel::devices::storage::management::get(); - std::ranges::for_each(storage_mgmt.all_controllers(), [](auto const & controller) { - std::ranges::for_each(controller->all_devices(), - [](auto const & device) { kstd::println("{}", device->name().view()); }); - }); -} - -auto test_file_descriptor_manually() -> void -{ - // setup - auto fd_table = kernel::filesystem::open_file_table::get(); - auto storage_mgmt = kernel::devices::storage::management::get(); - auto device = storage_mgmt.device_by_major_minor(1, 0); - - auto dev_node = kstd::make_shared(device); - - auto ofd = kstd::make_shared(dev_node); - auto fd_index = fd_table.add_file(ofd); - - // use: read two bytes and write two again - auto fd = fd_table.get_file(fd_index); - if (!fd) - { - kstd::os::panic("test code failed"); - } - - kstd::vector buffer{2}; - auto number_of_read_bytes = fd->read(buffer.data(), buffer.size()); - kstd::println("read bytes: {}", number_of_read_bytes); - kstd::println("buffer: {::#04x}", buffer); - - // write half of the file new - auto const value1 = std::byte{0xAA}; - auto const value2 = std::byte{0xBB}; - kstd::vector write_buffer{value1, value2}; - auto written_bytes = fd->write(write_buffer.data(), write_buffer.size()); - - kstd::println("written bytes: {}", written_bytes); - - fd_table.remove_file(fd_index); - - // use: read four bytes again -> two old bytes two new bytes - auto ofd1 = kstd::make_shared(dev_node); - fd_index = fd_table.add_file(ofd1); - auto fd1 = fd_table.get_file(fd_index); - - if (!fd1) - { - kstd::os::panic("test code failed"); - } - - kstd::vector buffer1{4}; - number_of_read_bytes = fd1->read(buffer1.data(), buffer1.size()); - kstd::println("read bytes: {}", number_of_read_bytes); - kstd::println("buffer: {::#04x}", buffer1); -} - -auto test_device_with_vfs() -> void -{ - auto vfs = kernel::filesystem::vfs::get(); - auto dentry = vfs.open("/dev/ram0"); - if (!dentry) - { - kstd::os::panic("test code failed"); - } - - auto fd_table = kernel::filesystem::open_file_table::get(); - auto ofd = kstd::make_shared(dentry->get_inode()); - auto fd = fd_table.add_file(ofd); - kstd::vector buffer{2}; - auto file = fd_table.get_file(fd); - if (!file) - { - kstd::os::panic("test code failed"); - } - - auto number_of_read_bytes = file->read(buffer.data(), buffer.size()); - kstd::println("read bytes: {}", number_of_read_bytes); - kstd::println("buffer: {::#04x}", buffer); -} - -auto test_file_lookup() -> void -{ - auto vfs = kernel::filesystem::vfs::get(); - auto read_and_write_file = [&vfs](std::string_view path) { - kstd::println("[TEST] Reading and writing file at path: {}", path); - auto dentry = vfs.open(path); - if (!dentry) - { - kstd::os::panic("test code failed"); - } - - kstd::vector buffer{32}; - auto ofd = kstd::make_shared(dentry->get_inode()); - auto number_of_read_bytes = ofd->read(buffer.data(), buffer.size()); - kstd::println("read bytes: {}", number_of_read_bytes); - kstd::println("buffer: {::#04x}", buffer); - - std::string_view buffer_as_str{reinterpret_cast(buffer.data()), number_of_read_bytes}; - kstd::println("buffer_as_str: {}", buffer_as_str); - }; - - read_and_write_file("/info.txt"); - read_and_write_file("/enclosures/info.txt"); - read_and_write_file("/enclosures/aquarium/tank_1/fish_4.txt"); - read_and_write_file("/enclosures/elephant_house/elephant_1.txt"); - read_and_write_file( - "/enclosures/aquarium/tank_2/" - "this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_" - "limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_30.txt"); - - vfs.do_mount("/dev/ram16", "/enclosures/aquarium"); - read_and_write_file("/enclosures/aquarium/closed.txt"); - read_and_write_file("/enclosures/aquarium/information/info_2.txt"); - - vfs.unmount("/enclosures/aquarium"); - read_and_write_file("/enclosures/aquarium/tank_1/fish_4.txt"); - - vfs.do_mount("/dev/ram32", "/enclosures/elephant_house"); - read_and_write_file("/enclosures/elephant_house/monkey_house/infrastructure/info.txt"); - - vfs.do_mount("/dev/ram16", "/enclosures/elephant_house/monkey_house"); - read_and_write_file("/enclosures/elephant_house/monkey_house/information/info_2.txt"); - - auto result = vfs.unmount("/enclosures/elephant_house"); - if (result == kernel::filesystem::vfs::operation_result::unmount_failed) - { - kstd::println("[TEST] Unmount failed as expected due to active child mount."); - } - - vfs.unmount("/enclosures/elephant_house/monkey_house"); - result = vfs.unmount("/enclosures/elephant_house"); - if (result == kernel::filesystem::vfs::operation_result::success) - { - kstd::println("[TEST] Unmount succeeded after unmounting child mount."); - } -} - -auto run_test_code() -> void -{ - kstd::println("[TEST] Running test code..."); - - kstd::println("[TEST] device names"); - test_device_names(); - kstd::println("---------------------------------"); - - kstd::println("[TEST] file descriptor manually"); - test_file_descriptor_manually(); - kstd::println("---------------------------------"); - - kstd::println("[TEST] device with VFS"); - test_device_with_vfs(); - kstd::println("---------------------------------"); - - kstd::println("[TEST] file lookup"); - test_file_lookup(); - kstd::println("---------------------------------"); -} - auto run_demo() -> void { // 1) open a file @@ -280,12 +118,8 @@ auto main() -> int kernel::filesystem::vfs::init(); kstd::println("[OS] Virtual filesystem initialized."); - // TODO BA-FS26 remove demo and test code again? + // TODO BA-FS26 remove demo code? // run_demo(); - // TODO BA-FS26 remove demo and test code again? - run_test_code(); - kstd::println("[TEST] All tests completed."); - kapi::system::panic("Returning from kernel main!"); } -- cgit v1.2.3 From 9f729ef76de79c8dabace2b4a6255f006385f6df Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Tue, 5 May 2026 16:29:50 +0200 Subject: Disable recursion-warning --- kernel/src/filesystem/dentry.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index 72500fd..1cf8730 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -39,7 +39,7 @@ namespace kernel::filesystem return m_name.view(); } - // TODO BA-FS26 fix warning (do not use recursion) + // NOLINTNEXTLINE(misc-no-recursion) auto dentry::get_full_path() const -> kstd::string { if (m_parent) @@ -52,7 +52,7 @@ namespace kernel::filesystem return parent_path + m_name.view(); } - return m_name.empty() ? "/" : m_name.view(); + return m_name.view(); } auto dentry::add_child(kstd::shared_ptr const & child) -> void -- cgit v1.2.3 From 2c24321681974e1aa8b1240155420f49a16f3c2e Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Tue, 5 May 2026 17:15:09 +0200 Subject: Add testing symlinks to filesystem images --- arch/x86_64/support/modules/ext2_1KB_fs.img | 2 +- arch/x86_64/support/modules/ext2_2KB_fs.img | 2 +- arch/x86_64/support/modules/ext2_4KB_fs.img | 2 +- kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img | 2 +- kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img | 2 +- kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/x86_64/support/modules/ext2_1KB_fs.img b/arch/x86_64/support/modules/ext2_1KB_fs.img index 0312727..a5202ca 100644 --- a/arch/x86_64/support/modules/ext2_1KB_fs.img +++ b/arch/x86_64/support/modules/ext2_1KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7cf40c3cf3d8e3be614cadf2da1c76138c492734c3730eadbca645f50ed99a50 +oid sha256:98ac3c1be872806e25fb14eea168ca79a91959f4e6a5ac3d00c5d8224c1f73a3 size 10485760 diff --git a/arch/x86_64/support/modules/ext2_2KB_fs.img b/arch/x86_64/support/modules/ext2_2KB_fs.img index 1880911..8327022 100644 --- a/arch/x86_64/support/modules/ext2_2KB_fs.img +++ b/arch/x86_64/support/modules/ext2_2KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a13da5abb9c65c737105b1da0d4344c7cd7604c7952c762c4f4e3d3f96fd42d +oid sha256:a1d102f2e40083613060d43b2b32d31031137bbef99761a2d1bf4d38e155adb7 size 5242880 diff --git a/arch/x86_64/support/modules/ext2_4KB_fs.img b/arch/x86_64/support/modules/ext2_4KB_fs.img index 3aaceb8..2cd452f 100644 --- a/arch/x86_64/support/modules/ext2_4KB_fs.img +++ b/arch/x86_64/support/modules/ext2_4KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ce6a1aea277906e1af6de223c017ff900b96569f076b4d99fc04eaa1ee986f4 +oid sha256:62e1fa40f95e0cb037c43e3f55d0094ab6cb4d68e00914f555a90faf7cc00c31 size 10485760 diff --git a/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img b/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img index 0312727..a5202ca 100644 --- a/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img +++ b/kernel/src/test_support/filesystem/test_assets/ext2_1KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7cf40c3cf3d8e3be614cadf2da1c76138c492734c3730eadbca645f50ed99a50 +oid sha256:98ac3c1be872806e25fb14eea168ca79a91959f4e6a5ac3d00c5d8224c1f73a3 size 10485760 diff --git a/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img b/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img index 1880911..8327022 100644 --- a/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img +++ b/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a13da5abb9c65c737105b1da0d4344c7cd7604c7952c762c4f4e3d3f96fd42d +oid sha256:a1d102f2e40083613060d43b2b32d31031137bbef99761a2d1bf4d38e155adb7 size 5242880 diff --git a/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img b/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img index 3aaceb8..2cd452f 100644 --- a/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img +++ b/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ce6a1aea277906e1af6de223c017ff900b96569f076b4d99fc04eaa1ee986f4 +oid sha256:62e1fa40f95e0cb037c43e3f55d0094ab6cb4d68e00914f555a90faf7cc00c31 size 10485760 -- cgit v1.2.3 From 491d7cb23f48b995e954b7cbb836ab3efb47ea88 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Tue, 5 May 2026 17:22:27 +0200 Subject: Update readme for images, add symlink to readme into test_assets folder --- arch/x86_64/support/modules/README.md | 18 +++++++++++++++--- .../src/test_support/filesystem/test_assets/README.md | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) create mode 120000 kernel/src/test_support/filesystem/test_assets/README.md diff --git a/arch/x86_64/support/modules/README.md b/arch/x86_64/support/modules/README.md index fac5daf..67b1217 100644 --- a/arch/x86_64/support/modules/README.md +++ b/arch/x86_64/support/modules/README.md @@ -8,13 +8,21 @@ The ext2_4KB_fs image is intentionally fragmented, as some files were created an ./archiv ./archiv/2024.img ./archiv/2025.img +./archiv/mnt ./closed.txt ./information ./information/info_1.txt ./information/info_2.txt -./symlinks -./symlinks/info_1_absolute -> /information/info_1.txt -./symlinks/info_1_relative -> ../information/info_1.txt +./symlinks +./symlinks/info_1_absolute -> /information/info_1.txt +./symlinks/info_1_relative -> ../information/info_1.txt +./symlinks/information_directory_absolute -> /information +./symlinks/information_directory_relative -> ../information +./symlinks/invalid_absolute -> ../invalid/non_existant.txt +./symlinks/invalid_relative -> ../invalid/non_existant.txt +./symlinks/symloop_a -> ./symloop_b +./symlinks/symloop_b -> /symlinks/symloop_a +./symlinks/traverse_back_5_times -> ../../../../../ ### 2024.img (ext2 filesystem with 2KB Block size) @@ -25,6 +33,8 @@ The ext2_4KB_fs image is intentionally fragmented, as some files were created an ./stable/pig_1.txt ./stable/pig_2.txt ./stable/pig_3.txt +./symlinks +./symlinks/traverse_back_twice -> ../../ ### 2025.img (ext2 filesystem 4KB Block size) @@ -52,6 +62,7 @@ The ext2_4KB_fs image is intentionally fragmented, as some files were created an ./monkey_house/caretaker ./monkey_house/caretaker/isabelle.txt ./monkey_house/caretaker/peter.txt +./symlinks/leave_and_reenter_mount -> ../../archiv/../information/monkey_house ## ext2_4KB_fs . @@ -109,3 +120,4 @@ The ext2_4KB_fs image is intentionally fragmented, as some files were created an ./enclosures/aquarium/spawn_fish.sh ./enclosures/info.txt ./info.txt +./symlinks/very_long_symlink -> ../enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_30.txt diff --git a/kernel/src/test_support/filesystem/test_assets/README.md b/kernel/src/test_support/filesystem/test_assets/README.md new file mode 120000 index 0000000..718a227 --- /dev/null +++ b/kernel/src/test_support/filesystem/test_assets/README.md @@ -0,0 +1 @@ +/arch/x86_64/support/modules/README.md \ No newline at end of file -- cgit v1.2.3 From c7e3528e9d40f62e6acf72b206a31105f252b529 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 5 May 2026 19:40:55 +0200 Subject: fix readme --- arch/x86_64/support/modules/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86_64/support/modules/README.md b/arch/x86_64/support/modules/README.md index 67b1217..6893176 100644 --- a/arch/x86_64/support/modules/README.md +++ b/arch/x86_64/support/modules/README.md @@ -18,7 +18,7 @@ The ext2_4KB_fs image is intentionally fragmented, as some files were created an ./symlinks/info_1_relative -> ../information/info_1.txt ./symlinks/information_directory_absolute -> /information ./symlinks/information_directory_relative -> ../information -./symlinks/invalid_absolute -> ../invalid/non_existant.txt +./symlinks/invalid_absolute -> /invalid/non_existant.txt ./symlinks/invalid_relative -> ../invalid/non_existant.txt ./symlinks/symloop_a -> ./symloop_b ./symlinks/symloop_b -> /symlinks/symloop_a -- cgit v1.2.3 From 265ac82029665921bd95d74411682968ee0d4ada Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 5 May 2026 19:41:50 +0200 Subject: refactoring do_mount_internal (retrieve path from dentry), handle .. correctly in relative path --- kernel/include/kernel/filesystem/vfs.hpp | 4 ++-- kernel/src/filesystem/vfs.cpp | 29 +++++++++++++++-------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 881f458..7e66fb7 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -77,8 +77,8 @@ namespace kernel::filesystem auto init_internal() -> void; [[nodiscard]] auto resolve_path(std::string_view path) -> kstd::shared_ptr; - auto do_mount_internal(std::string_view path, kstd::shared_ptr const & mount_point_dentry, - kstd::shared_ptr const & fs) -> void; + auto do_mount_internal(kstd::shared_ptr const & mount_point_dentry, kstd::shared_ptr const & fs) + -> void; mount_table m_mount_table; }; diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index e6d9241..4defc81 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -56,14 +56,14 @@ namespace kernel::filesystem { kapi::system::panic("[FILESYSTEM] failed to resolve /dev for initial devfs mount."); } - do_mount_internal("/dev", dev_mount_point_dentry, device_fs); + do_mount_internal(dev_mount_point_dentry, device_fs); // Mount boot filesystem at / (will shadow rootfs) if (auto boot_device_dentry = resolve_path("/dev/ram0")) { if (auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device_dentry->get_inode())) { - do_mount_internal("/", root_fs_root_dentry, boot_root_fs); + do_mount_internal(root_fs_root_dentry, boot_root_fs); // Resolve / to get the boot root dentry if (auto boot_root_dentry = resolve_path("/")) @@ -71,7 +71,7 @@ namespace kernel::filesystem auto dev_dentry = kstd::make_shared(boot_root_dentry, device_fs->root_inode(), "dev"); boot_root_dentry->add_child(dev_dentry); - do_mount_internal("/dev", dev_dentry, device_fs); + do_mount_internal(dev_dentry, device_fs); } } } @@ -105,7 +105,7 @@ namespace kernel::filesystem { if (auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode())) { - do_mount_internal(target, mount_point_dentry, fs); + do_mount_internal(mount_point_dentry, fs); return operation_result::success; } return operation_result::invalid_filesystem; @@ -136,13 +136,15 @@ namespace kernel::filesystem return operation_result::mount_point_not_found; } - auto vfs::do_mount_internal(std::string_view path, kstd::shared_ptr const & mount_point_dentry, + auto vfs::do_mount_internal(kstd::shared_ptr const & mount_point_dentry, kstd::shared_ptr const & fs) -> void { - auto parent_mount = m_mount_table.find_longest_prefix_mount(path); + auto mount_path = mount_point_dentry->get_full_path(); + + auto parent_mount = m_mount_table.find_longest_prefix_mount(mount_path.view()); auto new_fs_root = kstd::make_shared(mount_point_dentry->get_parent(), fs->root_inode(), mount_point_dentry->get_name()); - auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, path, parent_mount); + auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, mount_path.view(), parent_mount); m_mount_table.add_mount(new_mount); } @@ -188,16 +190,15 @@ namespace kernel::filesystem if (auto parent_mount = current_mount->get_parent_mount()) { current_mount = parent_mount; - current_dentry = current_dentry->get_parent(); + current_dentry = parent_dentry; + } - if (!parent_dentry) - { - parent_dentry = current_mount->root_dentry(); - } + if (!parent_dentry) + { + parent_dentry = current_mount->root_dentry(); } } - - if (!parent_dentry) + else if (!parent_dentry) { return nullptr; } -- cgit v1.2.3 From b7fba373341dd44b53642d7233396efbef4526b2 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 5 May 2026 20:07:37 +0200 Subject: avoid to traverse back over the root --- kernel/src/filesystem/vfs.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 4defc81..7f21a5c 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -186,22 +186,17 @@ namespace kernel::filesystem if (current_dentry == current_mount->root_dentry()) { - // change the mount point - if (auto parent_mount = current_mount->get_parent_mount()) + if (current_mount->get_mount_path() == "/") { - current_mount = parent_mount; - current_dentry = parent_dentry; + continue; } - if (!parent_dentry) + if (auto parent_mount = current_mount->get_parent_mount()) { - parent_dentry = current_mount->root_dentry(); + current_mount = parent_mount; + current_dentry = parent_dentry; } } - else if (!parent_dentry) - { - return nullptr; - } current_dentry = parent_dentry; continue; -- cgit v1.2.3 From a0f1c6f2199f55d2faaa5d9eafa9f763b2b299e4 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 5 May 2026 20:08:00 +0200 Subject: add symbolic link tests --- kernel/src/filesystem/vfs.tests.cpp | 42 +++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index cbd67be..5160d15 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -301,5 +301,47 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/symlinks/info_1_relative"); REQUIRE(info_1 != nullptr); } + + THEN("file can be opened through symbolic link pointing absolute to the directory containing the file") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto info_1 = vfs.open("/symlinks/information_directory_absolute/info_1.txt"); + REQUIRE(info_1 != nullptr); + } + + THEN("file can be opened through symbolic link pointing relative to the directory containing the file") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto info_1 = vfs.open("/symlinks/information_directory_relative/info_1.txt"); + REQUIRE(info_1 != nullptr); + } + + THEN("symbolic link with path traversing back to the root") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto info_1 = vfs.open("/symlinks/traverse_back_5_times/information/info_1.txt"); + REQUIRE(info_1 != nullptr); + } + + THEN("symbolic link containing an invalid absolute path is handled correctly") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto invalid_symlink = vfs.open("/symlinks/invalid_absolute"); + REQUIRE(invalid_symlink == nullptr); + } + + THEN("symbolic link containing an invalid relative path is handled correctly") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto invalid_symlink = vfs.open("/symlinks/invalid_relative"); + REQUIRE(invalid_symlink == nullptr); + } + + THEN("circular symbolic links are detected and handled correctly") + { + auto & vfs = kernel::filesystem::vfs::get(); + auto circular_symlink = vfs.open("/symlinks/symloop_a"); + REQUIRE(circular_symlink == nullptr); + } } } -- cgit v1.2.3 From 7414148b662a33cf6c69f89b7b0c3162f6880d6c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 5 May 2026 20:33:28 +0200 Subject: refactoring mount_table lookup --- kernel/include/kernel/filesystem/mount_table.hpp | 10 ++++++++-- kernel/src/filesystem/mount_table.cpp | 8 ++++++++ kernel/src/filesystem/vfs.cpp | 10 ++++------ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp index 00277ea..8e57d9e 100644 --- a/kernel/include/kernel/filesystem/mount_table.hpp +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -39,13 +39,19 @@ namespace kernel::filesystem [[nodiscard]] auto remove_mount(std::string_view path) -> operation_result; /** - @brief Finds the mount with the longest prefix matching the given @p path. This method is used to determine which - mounted filesystem should handle a given path lookup. + @brief Finds the mount with the longest prefix matching the given @p path. @param path The path to match against the mount paths in the table. @return A pointer to the mount with the longest matching prefix, or a null pointer if no mount matches the path. */ [[nodiscard]] auto find_longest_prefix_mount(std::string_view path) const -> kstd::shared_ptr; + /** + @brief Finds the mount with the exact mount path matching the given @p path. + @param path The path to match against the mount paths in the table. + @return A pointer to the mount with the exact matching path, or a null pointer if no mount matches the path. + */ + [[nodiscard]] auto find_exact_mount(std::string_view path) const -> kstd::shared_ptr; + private: [[nodiscard]] auto has_child_mounts(kstd::shared_ptr const & parent_mount) const -> bool; diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index e21e497..965e83b 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -102,4 +102,12 @@ namespace kernel::filesystem return mount_with_longest_prefix; } + + auto mount_table::find_exact_mount(std::string_view path) const -> kstd::shared_ptr + { + auto reversed_mounts = std::ranges::reverse_view(m_mounts); + auto mount_it = + std::ranges::find_if(reversed_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }); + return (mount_it != reversed_mounts.end()) ? *mount_it : nullptr; + } } // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 7f21a5c..19f5f48 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -41,7 +41,6 @@ namespace kernel::filesystem auto vfs::init_internal() -> void { - // Mount rootfs at / as the temporary base auto root_fs = kstd::make_shared(); root_fs->mount(nullptr); @@ -140,8 +139,8 @@ namespace kernel::filesystem kstd::shared_ptr const & fs) -> void { auto mount_path = mount_point_dentry->get_full_path(); - auto parent_mount = m_mount_table.find_longest_prefix_mount(mount_path.view()); + auto new_fs_root = kstd::make_shared(mount_point_dentry->get_parent(), fs->root_inode(), mount_point_dentry->get_name()); auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, mount_path.view(), parent_mount); @@ -156,7 +155,7 @@ namespace kernel::filesystem } // TODO BA-FS26 refactor (get mount by path, no more prefix matching) - auto current_mount = m_mount_table.find_longest_prefix_mount("/"); + auto current_mount = m_mount_table.find_exact_mount("/"); if (!current_mount) { kapi::system::panic("[FILESYSTEM] no root mount found."); @@ -217,8 +216,7 @@ namespace kernel::filesystem } else if (next_dentry->has_flag(dentry::dentry_flags::mounted)) { - // TODO BA-FS26 really do it like this? or just call "get" without longes_prefix stuff - current_mount = m_mount_table.find_longest_prefix_mount(next_dentry->get_full_path().view()); + current_mount = m_mount_table.find_exact_mount(next_dentry->get_full_path().view()); if (!current_mount) { kapi::system::panic("[FILESYSTEM] mount for dentry with mounted flag not found."); @@ -246,7 +244,7 @@ namespace kernel::filesystem if (path::is_valid_absolute_path(symbolic_link_path)) { - current_mount = m_mount_table.find_longest_prefix_mount("/"); + current_mount = m_mount_table.find_exact_mount("/"); current_dentry = current_mount->root_dentry(); } continue; -- cgit v1.2.3 From 8739d65ea50f13dbbb5bd1a27f461c367deb5b99 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 5 May 2026 20:59:52 +0200 Subject: add symlink chain to image --- arch/x86_64/support/modules/README.md | 6 +++++- arch/x86_64/support/modules/ext2_4KB_fs.img | 2 +- kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/x86_64/support/modules/README.md b/arch/x86_64/support/modules/README.md index 6893176..1a29ce5 100644 --- a/arch/x86_64/support/modules/README.md +++ b/arch/x86_64/support/modules/README.md @@ -78,9 +78,12 @@ The ext2_4KB_fs image is intentionally fragmented, as some files were created an ./enclosures/lion_house/cage_b ./enclosures/lion_house/cage_b/animals.txt ./enclosures/lion_house/cage_b/history.txt +./enclosures/lion_house/symlink_chain_2 -> /entrance/../enclosures/aquarium/symlink_chain_3 ./enclosures/elephant_house ./enclosures/elephant_house/elephant_1.txt ./enclosures/aquarium +./enclosures/aquarium/spawn_fish.sh +./enclosures/aquarium/symlink_chain_3 -> ../../entrance ./enclosures/aquarium/tank_1 ./enclosures/aquarium/tank_1/fish_1.txt ./enclosures/aquarium/tank_1/fish_2.txt @@ -117,7 +120,8 @@ The ext2_4KB_fs image is intentionally fragmented, as some files were created an ./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_28.txt ./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_29.txt ./enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_30.txt -./enclosures/aquarium/spawn_fish.sh ./enclosures/info.txt ./info.txt +./symlinks +./symlinks/symlink_chain_1 -> ../enclosures/lion_house/symlink_chain_2 ./symlinks/very_long_symlink -> ../enclosures/aquarium/tank_2/this_is_a_very_very_long_fish_filename_that_keeps_going_and_going_until_it_almost_breaks_linux_filesystem_limits_for_testing_purposes_and_we_add_more_characters_to_make_it_even_longer_30.txt diff --git a/arch/x86_64/support/modules/ext2_4KB_fs.img b/arch/x86_64/support/modules/ext2_4KB_fs.img index 2cd452f..c3f6daf 100644 --- a/arch/x86_64/support/modules/ext2_4KB_fs.img +++ b/arch/x86_64/support/modules/ext2_4KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62e1fa40f95e0cb037c43e3f55d0094ab6cb4d68e00914f555a90faf7cc00c31 +oid sha256:026ca30269dbd80beb2dd74677c94676d1d4a7f6b5fe406c4ddb82836ba2dc00 size 10485760 diff --git a/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img b/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img index 2cd452f..c3f6daf 100644 --- a/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img +++ b/kernel/src/test_support/filesystem/test_assets/ext2_4KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62e1fa40f95e0cb037c43e3f55d0094ab6cb4d68e00914f555a90faf7cc00c31 +oid sha256:026ca30269dbd80beb2dd74677c94676d1d4a7f6b5fe406c4ddb82836ba2dc00 size 10485760 -- cgit v1.2.3 From 9d1fc7cbe7d3851541f1986dc4bc66a2bc944c89 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 5 May 2026 21:00:17 +0200 Subject: add vfs symlink tests --- kernel/src/filesystem/vfs.tests.cpp | 63 ++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 5160d15..2eec572 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -283,7 +283,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS } } - GIVEN("one real image files, containing symbolic links") + GIVEN("A real image files, containing symbolic links") { REQUIRE(std::filesystem::exists(image_path_1)); REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1"}, {image_path_1})); @@ -344,4 +344,65 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(circular_symlink == nullptr); } } + + GIVEN("A real image file containing as filesystem formatted files and this filesystem contains a symbolic link") + { + REQUIRE(std::filesystem::exists(image_path_1)); + REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_1"}, {image_path_1})); + + auto & vfs = kernel::filesystem::vfs::get(); + vfs.do_mount("/archiv/2024.img", "/information"); + + THEN("file can be opened through symbolic link pointing to the parent filesystem") + { + auto closed_file = vfs.open("/information/symlinks/traverse_back_twice/closed.txt"); + REQUIRE(closed_file != nullptr); + } + } + + GIVEN("Two real images, one containing a symbolic link leaving and reentering filesystem again") + { + REQUIRE(std::filesystem::exists(image_path_1)); + REQUIRE(std::filesystem::exists(image_path_2)); + REQUIRE_NOTHROW( + setup_modules_from_img_and_init_vfs({"test_img_module_1", "test_img_module_2"}, {image_path_1, image_path_2})); + + auto & vfs = kernel::filesystem::vfs::get(); + vfs.do_mount("/dev/ram16", "/information"); + + THEN("file can be opened through symbolic link pointing to the parent filesystem and back into the mounted " + "filesystem again") + { + auto monkey_1 = vfs.open("/information/symlinks/leave_and_reenter_mount/monkey_1.txt"); + REQUIRE(monkey_1 != nullptr); + } + } + + GIVEN("One real image containing a very long symbolic link") + { + REQUIRE(std::filesystem::exists(image_path_3)); + REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_3"}, {image_path_3})); + + auto & vfs = kernel::filesystem::vfs::get(); + + THEN("file can be opened through symbolic link with a long path") + { + auto fish_30 = vfs.open("/symlinks/very_long_symlink"); + REQUIRE(fish_30 != nullptr); + } + } + + GIVEN("One real image containing a valid symbolic link chain") + { + REQUIRE(std::filesystem::exists(image_path_3)); + REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_3"}, {image_path_3})); + + auto & vfs = kernel::filesystem::vfs::get(); + + THEN("file can be opened through symbolic link chain") + { + auto map = vfs.open("/symlinks/symlink_chain_1/map.txt"); + REQUIRE(map != nullptr); + } + } } -- cgit v1.2.3 From a2dff6812af354f282b731bc138e98a18bf7f35c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 5 May 2026 21:13:45 +0200 Subject: add filesystem interface tests --- kernel/kapi/filesystem.tests.cpp | 49 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/kernel/kapi/filesystem.tests.cpp b/kernel/kapi/filesystem.tests.cpp index baa8613..8a532bb 100644 --- a/kernel/kapi/filesystem.tests.cpp +++ b/kernel/kapi/filesystem.tests.cpp @@ -16,7 +16,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap auto const image_path_1 = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_1KB_fs.img"; auto const image_path_2 = std::filesystem::path{KERNEL_TEST_ASSETS_DIR} / "ext2_2KB_fs.img"; - GIVEN("a real image file") + GIVEN("Two real image files") { REQUIRE(std::filesystem::exists(image_path_1)); REQUIRE(std::filesystem::exists(image_path_2)); @@ -38,6 +38,53 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap REQUIRE(kapi::filesystem::close(fd) == 0); } + THEN("files can be opened through absolute symbolic link, read and closed again") + { + auto fd = kapi::filesystem::open("/symlinks/information_directory_absolute/info_1.txt"); + REQUIRE(fd >= 0); + + auto buffer = std::vector(6); + auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); + REQUIRE(bytes_read >= 0); + + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; + REQUIRE(buffer_as_str == "info_1"); + + REQUIRE(kapi::filesystem::close(fd) == 0); + } + + THEN("files can be opened through relative symbolic link, read and closed again") + { + auto fd = kapi::filesystem::open("/symlinks/information_directory_relative/info_1.txt"); + REQUIRE(fd >= 0); + + auto buffer = std::vector(6); + auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); + REQUIRE(bytes_read >= 0); + + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; + REQUIRE(buffer_as_str == "info_1"); + + REQUIRE(kapi::filesystem::close(fd) == 0); + } + + THEN("files can be opened through relative symbolic link over multiple mount points, read and closed again") + { + kapi::filesystem::mount("/archiv/2024.img", "/information"); + + auto fd = kapi::filesystem::open("/information/symlinks/traverse_back_twice/information/sheep_1.txt"); + REQUIRE(fd >= 0); + + auto buffer = std::vector(7); + auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size()); + REQUIRE(bytes_read >= 0); + + std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; + REQUIRE(buffer_as_str == "sheep_1"); + + REQUIRE(kapi::filesystem::close(fd) == 0); + } + THEN("a filesystem can be mounted, files can be opened, read and closed again and unmounted") { REQUIRE(kapi::filesystem::mount("/dev/ram16", "/information") == 0); -- cgit v1.2.3 From 7ccfa26a3dde4d4266f8c59f4e3de8bd6f760059 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 5 May 2026 21:17:23 +0200 Subject: small refactoring, add todo --- kernel/kapi/filesystem.tests.cpp | 6 ++++-- kernel/src/filesystem/vfs.cpp | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/kernel/kapi/filesystem.tests.cpp b/kernel/kapi/filesystem.tests.cpp index 8a532bb..1d1f8ee 100644 --- a/kernel/kapi/filesystem.tests.cpp +++ b/kernel/kapi/filesystem.tests.cpp @@ -161,14 +161,16 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap THEN("not opened files cannot be read from") { std::vector buffer(10); - auto bytes_read = kapi::filesystem::read(999, buffer.data(), buffer.size()); + auto const invalid_fd = 999uz; + auto bytes_read = kapi::filesystem::read(invalid_fd, buffer.data(), buffer.size()); REQUIRE(bytes_read < 0); } THEN("not opened files cannot be written to") { std::vector buffer(10); - auto bytes_written = kapi::filesystem::write(999, buffer.data(), buffer.size()); + auto const invalid_fd = 999uz; + auto bytes_written = kapi::filesystem::write(invalid_fd, buffer.data(), buffer.size()); REQUIRE(bytes_written < 0); } } diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 19f5f48..b84f690 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -139,6 +139,7 @@ namespace kernel::filesystem kstd::shared_ptr const & fs) -> void { auto mount_path = mount_point_dentry->get_full_path(); + // TODO BA-FS26 refactoring, implement dentry lookup to get the parent mount... auto parent_mount = m_mount_table.find_longest_prefix_mount(mount_path.view()); auto new_fs_root = -- cgit v1.2.3 From 870039d48d28f479eea88c7548b8d2b2a28c09bc Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 5 May 2026 21:18:48 +0200 Subject: add mount table find_exact_mount tests, remove todo --- kernel/src/filesystem/mount_table.tests.cpp | 21 +++++++++++++++++++-- kernel/src/filesystem/vfs.cpp | 1 - 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp index e028ac8..efacdfe 100644 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -58,7 +58,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } - THEN("finding mounts by path returns the correct mount") + THEN("finding mounts by longest prefix returns the correct mount") { REQUIRE(table.find_longest_prefix_mount("/") == mount1); REQUIRE(table.find_longest_prefix_mount("/file") == mount1); @@ -67,6 +67,18 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] REQUIRE(table.find_longest_prefix_mount("/other") == mount1); } + THEN("finding mounts by exact valid path returns the correct mount") + { + REQUIRE(table.find_exact_mount("/") == mount1); + REQUIRE(table.find_exact_mount("/mnt") == mount2); + } + + THEN("finding mounts by exact invalid path returns null") + { + REQUIRE(table.find_exact_mount("/nonexistent") == nullptr); + REQUIRE(table.find_exact_mount("/mnt/file") == nullptr); + } + THEN("removing a mount that has no child mounts succeeds") { REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::removed); @@ -97,7 +109,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] table.add_mount(mount1); table.add_mount(mount2); - THEN("finding mounts by path returns the correct mount based on longest prefix") + THEN("finding mounts by longest prefix returns the correct mount") { REQUIRE(table.find_longest_prefix_mount("/") == mount2); REQUIRE(table.find_longest_prefix_mount("/file") == mount2); @@ -106,6 +118,11 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] REQUIRE(table.find_longest_prefix_mount("/other") == mount2); } + THEN("finding mounts by exact valid path returns the correct mount") + { + REQUIRE(table.find_exact_mount("/") == mount2); + } + THEN("removing the topmost mount with the same path succeeds") { REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::removed); diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index b84f690..519550b 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -155,7 +155,6 @@ namespace kernel::filesystem return nullptr; } - // TODO BA-FS26 refactor (get mount by path, no more prefix matching) auto current_mount = m_mount_table.find_exact_mount("/"); if (!current_mount) { -- cgit v1.2.3 From 4522374b902ee9a30c83c2ec23880522e80febea Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 5 May 2026 21:37:12 +0200 Subject: .. int the root directory remains in the root --- kernel/src/filesystem/vfs.tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 2eec572..8e4cb70 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -187,7 +187,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS { REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); - auto img = vfs.open("/information/monkey_house/caretaker/../../../archiv/2024.img"); + auto img = vfs.open("/information/monkey_house/caretaker/../../../../../../archiv/2024.img"); REQUIRE(img != nullptr); auto dev_32 = vfs.open("/information/monkey_house/caretaker/../../../dev/ram32"); -- cgit v1.2.3 From 1e9daa3ccda0a39a8b260649ddc3c75f95a88bdf Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 6 May 2026 05:20:48 +0000 Subject: dump_mb2i: add support for bios boot device tag --- scripts/gdb/teachos/dump_mb2i.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/gdb/teachos/dump_mb2i.py b/scripts/gdb/teachos/dump_mb2i.py index ff15fde..41a2932 100644 --- a/scripts/gdb/teachos/dump_mb2i.py +++ b/scripts/gdb/teachos/dump_mb2i.py @@ -123,6 +123,12 @@ class DumpMB2I(gdb.Command): ) if string: gdb.write(f'{INDENT}string: "{string}"\n') + elif tag_type == 5: + data = inferior.read_memory(tag_address + 8, tag_size - 8).tobytes() + device, partition, sub_partition = struct.unpack_from(" Date: Wed, 6 May 2026 06:34:29 +0000 Subject: debug: refactor dump_mb2i tool --- poetry.lock | 884 +++++++++++++++++++++++++++++++++++++++ pyproject.toml | 23 + scripts/gdb/teachos/dump_mb2i.py | 199 ++++++--- 3 files changed, 1042 insertions(+), 64 deletions(-) create mode 100644 poetry.lock create mode 100644 pyproject.toml diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..fbccc60 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,884 @@ +# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. + +[[package]] +name = "accessible-pygments" +version = "0.0.5" +description = "A collection of accessible pygments styles" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7"}, + {file = "accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872"}, +] + +[package.dependencies] +pygments = ">=1.5" + +[package.extras] +dev = ["pillow", "pkginfo (>=1.10)", "playwright", "pre-commit", "setuptools", "twine (>=5.0)"] +tests = ["hypothesis", "pytest"] + +[[package]] +name = "alabaster" +version = "1.0.0" +description = "A light, configurable Sphinx theme" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b"}, + {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, +] + +[[package]] +name = "babel" +version = "2.18.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35"}, + {file = "babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d"}, +] + +[package.extras] +dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] + +[[package]] +name = "beautifulsoup4" +version = "4.14.3" +description = "Screen-scraping library" +optional = false +python-versions = ">=3.7.0" +groups = ["main"] +files = [ + {file = "beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb"}, + {file = "beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86"}, +] + +[package.dependencies] +soupsieve = ">=1.6.1" +typing-extensions = ">=4.0.0" + +[package.extras] +cchardet = ["cchardet"] +chardet = ["chardet"] +charset-normalizer = ["charset-normalizer"] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "black" +version = "26.3.1" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "black-26.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:86a8b5035fce64f5dcd1b794cf8ec4d31fe458cf6ce3986a30deb434df82a1d2"}, + {file = "black-26.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5602bdb96d52d2d0672f24f6ffe5218795736dd34807fd0fd55ccd6bf206168b"}, + {file = "black-26.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c54a4a82e291a1fee5137371ab488866b7c86a3305af4026bdd4dc78642e1ac"}, + {file = "black-26.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:6e131579c243c98f35bce64a7e08e87fb2d610544754675d4a0e73a070a5aa3a"}, + {file = "black-26.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:5ed0ca58586c8d9a487352a96b15272b7fa55d139fc8496b519e78023a8dab0a"}, + {file = "black-26.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff"}, + {file = "black-26.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bf162ed91a26f1adba8efda0b573bc6924ec1408a52cc6f82cb73ec2b142c"}, + {file = "black-26.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:474c27574d6d7037c1bc875a81d9be0a9a4f9ee95e62800dab3cfaadbf75acd5"}, + {file = "black-26.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e9d0d86df21f2e1677cc4bd090cd0e446278bcbbe49bf3659c308c3e402843e"}, + {file = "black-26.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9a5e9f45e5d5e1c5b5c29b3bd4265dcc90e8b92cf4534520896ed77f791f4da5"}, + {file = "black-26.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1"}, + {file = "black-26.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f"}, + {file = "black-26.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7"}, + {file = "black-26.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983"}, + {file = "black-26.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb"}, + {file = "black-26.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54"}, + {file = "black-26.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f"}, + {file = "black-26.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56"}, + {file = "black-26.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839"}, + {file = "black-26.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2"}, + {file = "black-26.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78"}, + {file = "black-26.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568"}, + {file = "black-26.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f"}, + {file = "black-26.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c"}, + {file = "black-26.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1"}, + {file = "black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b"}, + {file = "black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=1.0.0" +platformdirs = ">=2" +pytokens = ">=0.4.0,<0.5.0" + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.10)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2) ; sys_platform != \"win32\"", "winloop (>=0.5.0) ; sys_platform == \"win32\""] + +[[package]] +name = "breathe" +version = "4.36.0" +description = "Sphinx Doxygen renderer" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "breathe-4.36.0-py3-none-any.whl", hash = "sha256:af85436f1f09e842bd1fd95617281211c635f8768d245ff830c59b979888d1d5"}, + {file = "breathe-4.36.0.tar.gz", hash = "sha256:14860b73118ac140b7a3f55446890c777d1b67149cb024279fe3710dad7f535c"}, +] + +[package.dependencies] +Sphinx = ">=7.2" + +[package.extras] +docs = ["furo", "sphinx-copybutton", "sphinxcontrib-spelling"] +lint = ["mypy (>=1)", "pytest (>=8.0)", "ruff (==0.9.2)", "sphinx-csharp", "sphinxcontrib-phpdomain", "types-Pygments", "types-docutils"] +test = ["pytest (>=8.0)"] + +[[package]] +name = "certifi" +version = "2026.4.22" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a"}, + {file = "certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.7" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "charset_normalizer-3.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cdd68a1fb318e290a2077696b7eb7a21a49163c455979c639bf5a5dcdc46617d"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e17b8d5d6a8c47c85e68ca8379def1303fd360c3e22093a807cd34a71cd082b8"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:511ef87c8aec0783e08ac18565a16d435372bc1ac25a91e6ac7f5ef2b0bff790"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:007d05ec7321d12a40227aae9e2bc6dca73f3cb21058999a1df9e193555a9dcc"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf29836da5119f3c8a8a70667b0ef5fdca3bb12f80fd06487cfa575b3909b393"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:12d8baf840cc7889b37c7c770f478adea7adce3dcb3944d02ec87508e2dcf153"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d560742f3c0d62afaccf9f41fe485ed69bd7661a241f86a3ef0f0fb8b1a397af"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b14b2d9dac08e28bb8046a1a0434b1750eb221c8f5b87a68f4fa11a6f97b5e34"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bc17a677b21b3502a21f66a8cc64f5bfad4df8a0b8434d661666f8ce90ac3af1"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:750e02e074872a3fad7f233b47734166440af3cdea0add3e95163110816d6752"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:4e5163c14bffd570ef2affbfdd77bba66383890797df43dc8b4cc7d6f500bf53"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6ed74185b2db44f41ef35fd1617c5888e59792da9bbc9190d6c7300617182616"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:94e1885b270625a9a828c9793b4d52a64445299baa1fea5a173bf1d3dd9a1a5a"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-win32.whl", hash = "sha256:6785f414ae0f3c733c437e0f3929197934f526d19dfaa75e18fdb4f94c6fb374"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:6696b7688f54f5af4462118f0bfa7c1621eeb87154f77fa04b9295ce7a8f2943"}, + {file = "charset_normalizer-3.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:66671f93accb62ed07da56613636f3641f1a12c13046ce91ffc923721f23c008"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7641bb8895e77f921102f72833904dcd9901df5d6d72a2ab8f31d04b7e51e4e7"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:202389074300232baeb53ae2569a60901f7efadd4245cf3a3bf0617d60b439d7"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:30b8d1d8c52a48c2c5690e152c169b673487a2a58de1ec7393196753063fcd5e"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:532bc9bf33a68613fd7d65e4b1c71a6a38d7d42604ecf239c77392e9b4e8998c"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe249cb4651fd12605b7288b24751d8bfd46d35f12a20b1ba33dea122e690df"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:65bcd23054beab4d166035cabbc868a09c1a49d1efe458fe8e4361215df40265"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:08e721811161356f97b4059a9ba7bafb23ea5ee2255402c42881c214e173c6b4"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e060d01aec0a910bdccb8be71faf34e7799ce36950f8294c8bf612cba65a2c9e"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:38c0109396c4cfc574d502df99742a45c72c08eff0a36158b6f04000043dbf38"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:1c2a768fdd44ee4a9339a9b0b130049139b8ce3c01d2ce09f67f5a68048d477c"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:1a87ca9d5df6fe460483d9a5bbf2b18f620cbed41b432e2bddb686228282d10b"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d635aab80466bc95771bb78d5370e74d36d1fe31467b6b29b8b57b2a3cd7d22c"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ae196f021b5e7c78e918242d217db021ed2a6ace2bc6ae94c0fc596221c7f58d"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-win32.whl", hash = "sha256:adb2597b428735679446b46c8badf467b4ca5f5056aae4d51a19f9570301b1ad"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:8e385e4267ab76874ae30db04c627faaaf0b509e1ccc11a95b3fc3e83f855c00"}, + {file = "charset_normalizer-3.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:d4a48e5b3c2a489fae013b7589308a40146ee081f6f509e047e0e096084ceca1"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6"}, + {file = "charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110"}, + {file = "charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f"}, + {file = "charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c"}, + {file = "charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e5f4d355f0a2b1a31bc3edec6795b46324349c9cb25eed068049e4f472fb4259"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16d971e29578a5e97d7117866d15889a4a07befe0e87e703ed63cd90cb348c01"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dca4bbc466a95ba9c0234ef56d7dd9509f63da22274589ebd4ed7f1f4d4c54e3"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e80c8378d8f3d83cd3164da1ad2df9e37a666cdde7b1cb2298ed0b558064be30"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36836d6ff945a00b88ba1e4572d721e60b5b8c98c155d465f56ad19d68f23734"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux_2_31_armv7l.whl", hash = "sha256:bd9b23791fe793e4968dba0c447e12f78e425c59fc0e3b97f6450f4781f3ee60"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aef65cd602a6d0e0ff6f9930fcb1c8fec60dd2cfcb6facaf4bdb0e5873042db0"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:82b271f5137d07749f7bf32f70b17ab6eaabedd297e75dce75081a24f76eb545"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:1efde3cae86c8c273f1eb3b287be7d8499420cf2fe7585c41d370d3e790054a5"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:c593052c465475e64bbfe5dbd81680f64a67fdc752c56d7a0ae205dc8aeefe0f"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:af21eb4409a119e365397b2adbaca4c9ccab56543a65d5dbd9f920d6ac29f686"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:84c018e49c3bf790f9c2771c45e9313a08c2c2a6342b162cd650258b57817706"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dd915403e231e6b1809fe9b6d9fc55cf8fb5e02765ac625d9cd623342a7905d7"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-win32.whl", hash = "sha256:320ade88cfb846b8cd6b4ddf5ee9e80ee0c1f52401f2456b84ae1ae6a1a5f207"}, + {file = "charset_normalizer-3.4.7-cp38-cp38-win_amd64.whl", hash = "sha256:1dc8b0ea451d6e69735094606991f32867807881400f808a106ee1d963c46a83"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e0d51f618228538a3e8f46bd246f87a6cd030565e015803691603f55e12afb5"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:14265bfe1f09498b9d8ec91e9ec9fa52775edf90fcbde092b25f4a33d444fea9"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:87fad7d9ba98c86bcb41b2dc8dbb326619be2562af1f8ff50776a39e55721c5a"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f22dec1690b584cea26fade98b2435c132c1b5f68e39f5a0b7627cd7ae31f1dc"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:d61f00a0869d77422d9b2aba989e2d24afa6ffd552af442e0e58de4f35ea6d00"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6370e8686f662e6a3941ee48ed4742317cafbe5707e36406e9df792cdb535776"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a6c5863edfbe888d9eff9c8b8087354e27618d9da76425c119293f11712a6319"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ed065083d0898c9d5b4bbec7b026fd755ff7454e6e8b73a67f8c744b13986e24"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2cd4a60d0e2fb04537162c62bbbb4182f53541fe0ede35cdf270a1c1e723cc42"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:813c0e0132266c08eb87469a642cb30aaff57c5f426255419572aaeceeaa7bf4"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:07d9e39b01743c3717745f4c530a6349eadbfa043c7577eef86c502c15df2c67"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c0f081d69a6e58272819b70288d3221a6ee64b98df852631c80f293514d3b274"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-win32.whl", hash = "sha256:8751d2787c9131302398b11e6c8068053dcb55d5a8964e114b6e196cf16cb366"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:12a6fff75f6bc66711b73a2f0addfc4c8c15a20e805146a02d147a318962c444"}, + {file = "charset_normalizer-3.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:bb8cc7534f51d9a017b93e3e85b260924f909601c3df002bcdb58ddb4dc41a5c"}, + {file = "charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d"}, + {file = "charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5"}, +] + +[[package]] +name = "click" +version = "8.3.3" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613"}, + {file = "click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +markers = "sys_platform == \"win32\" or platform_system == \"Windows\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "docutils" +version = "0.22.4" +description = "Docutils -- Python Documentation Utilities" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de"}, + {file = "docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968"}, +] + +[[package]] +name = "idna" +version = "3.13" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "idna-3.13-py3-none-any.whl", hash = "sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3"}, + {file = "idna-3.13.tar.gz", hash = "sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242"}, +] + +[package.extras] +all = ["mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "imagesize" +version = "1.5.0" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +groups = ["main"] +files = [ + {file = "imagesize-1.5.0-py2.py3-none-any.whl", hash = "sha256:32677681b3f434c2cb496f00e89c5a291247b35b1f527589909e008057da5899"}, + {file = "imagesize-1.5.0.tar.gz", hash = "sha256:8bfc5363a7f2133a89f0098451e0bcb1cd71aba4dc02bbcecb39d99d40e1b94f"}, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markupsafe" +version = "3.0.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, + {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, +] + +[[package]] +name = "packaging" +version = "26.2" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e"}, + {file = "packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661"}, +] + +[[package]] +name = "pathspec" +version = "1.1.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189"}, + {file = "pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a"}, +] + +[package.extras] +hyperscan = ["hyperscan (>=0.7)"] +optional = ["typing-extensions (>=4)"] +re2 = ["google-re2 (>=1.1)"] + +[[package]] +name = "platformdirs" +version = "4.9.6" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917"}, + {file = "platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a"}, +] + +[[package]] +name = "pydata-sphinx-theme" +version = "0.16.1" +description = "Bootstrap-based Sphinx theme from the PyData community" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydata_sphinx_theme-0.16.1-py3-none-any.whl", hash = "sha256:225331e8ac4b32682c18fcac5a57a6f717c4e632cea5dd0e247b55155faeccde"}, + {file = "pydata_sphinx_theme-0.16.1.tar.gz", hash = "sha256:a08b7f0b7f70387219dc659bff0893a7554d5eb39b59d3b8ef37b8401b7642d7"}, +] + +[package.dependencies] +accessible-pygments = "*" +Babel = "*" +beautifulsoup4 = "*" +docutils = "!=0.17.0" +pygments = ">=2.7" +sphinx = ">=6.1" +typing-extensions = "*" + +[package.extras] +a11y = ["pytest-playwright"] +dev = ["pandoc", "pre-commit", "pydata-sphinx-theme[doc,test]", "pyyaml", "sphinx-theme-builder[cli]", "tox"] +doc = ["ablog (>=0.11.8)", "colorama", "graphviz", "ipykernel", "ipyleaflet", "ipywidgets", "jupyter_sphinx", "jupyterlite-sphinx", "linkify-it-py", "matplotlib", "myst-parser", "nbsphinx", "numpy", "numpydoc", "pandas", "plotly", "rich", "sphinx-autoapi (>=3.0.0)", "sphinx-copybutton", "sphinx-design", "sphinx-favicon (>=1.0.1)", "sphinx-sitemap", "sphinx-togglebutton", "sphinxcontrib-youtube (>=1.4.1)", "sphinxext-rediraffe", "xarray"] +i18n = ["Babel", "jinja2"] +test = ["pytest", "pytest-cov", "pytest-regressions", "sphinx[test]"] + +[[package]] +name = "pygments" +version = "2.20.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176"}, + {file = "pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pytokens" +version = "0.4.1" +description = "A Fast, spec compliant Python 3.14+ tokenizer that runs on older Pythons." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pytokens-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a44ed93ea23415c54f3face3b65ef2b844d96aeb3455b8a69b3df6beab6acc5"}, + {file = "pytokens-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:add8bf86b71a5d9fb5b89f023a80b791e04fba57960aa790cc6125f7f1d39dfe"}, + {file = "pytokens-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:670d286910b531c7b7e3c0b453fd8156f250adb140146d234a82219459b9640c"}, + {file = "pytokens-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4e691d7f5186bd2842c14813f79f8884bb03f5995f0575272009982c5ac6c0f7"}, + {file = "pytokens-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:27b83ad28825978742beef057bfe406ad6ed524b2d28c252c5de7b4a6dd48fa2"}, + {file = "pytokens-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d70e77c55ae8380c91c0c18dea05951482e263982911fc7410b1ffd1dadd3440"}, + {file = "pytokens-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a58d057208cb9075c144950d789511220b07636dd2e4708d5645d24de666bdc"}, + {file = "pytokens-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b49750419d300e2b5a3813cf229d4e5a4c728dae470bcc89867a9ad6f25a722d"}, + {file = "pytokens-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9907d61f15bf7261d7e775bd5d7ee4d2930e04424bab1972591918497623a16"}, + {file = "pytokens-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:ee44d0f85b803321710f9239f335aafe16553b39106384cef8e6de40cb4ef2f6"}, + {file = "pytokens-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:140709331e846b728475786df8aeb27d24f48cbcf7bcd449f8de75cae7a45083"}, + {file = "pytokens-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d6c4268598f762bc8e91f5dbf2ab2f61f7b95bdc07953b602db879b3c8c18e1"}, + {file = "pytokens-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24afde1f53d95348b5a0eb19488661147285ca4dd7ed752bbc3e1c6242a304d1"}, + {file = "pytokens-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5ad948d085ed6c16413eb5fec6b3e02fa00dc29a2534f088d3302c47eb59adf9"}, + {file = "pytokens-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:3f901fe783e06e48e8cbdc82d631fca8f118333798193e026a50ce1b3757ea68"}, + {file = "pytokens-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8bdb9d0ce90cbf99c525e75a2fa415144fd570a1ba987380190e8b786bc6ef9b"}, + {file = "pytokens-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5502408cab1cb18e128570f8d598981c68a50d0cbd7c61312a90507cd3a1276f"}, + {file = "pytokens-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29d1d8fb1030af4d231789959f21821ab6325e463f0503a61d204343c9b355d1"}, + {file = "pytokens-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b08dd6b86058b6dc07efe9e98414f5102974716232d10f32ff39701e841c4"}, + {file = "pytokens-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:9bd7d7f544d362576be74f9d5901a22f317efc20046efe2034dced238cbbfe78"}, + {file = "pytokens-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4a14d5f5fc78ce85e426aa159489e2d5961acf0e47575e08f35584009178e321"}, + {file = "pytokens-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f50fd18543be72da51dd505e2ed20d2228c74e0464e4262e4899797803d7fa"}, + {file = "pytokens-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc74c035f9bfca0255c1af77ddd2d6ae8419012805453e4b0e7513e17904545d"}, + {file = "pytokens-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f66a6bbe741bd431f6d741e617e0f39ec7257ca1f89089593479347cc4d13324"}, + {file = "pytokens-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:b35d7e5ad269804f6697727702da3c517bb8a5228afa450ab0fa787732055fc9"}, + {file = "pytokens-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8fcb9ba3709ff77e77f1c7022ff11d13553f3c30299a9fe246a166903e9091eb"}, + {file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79fc6b8699564e1f9b521582c35435f1bd32dd06822322ec44afdeba666d8cb3"}, + {file = "pytokens-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d31b97b3de0f61571a124a00ffe9a81fb9939146c122c11060725bd5aea79975"}, + {file = "pytokens-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:967cf6e3fd4adf7de8fc73cd3043754ae79c36475c1c11d514fc72cf5490094a"}, + {file = "pytokens-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:584c80c24b078eec1e227079d56dc22ff755e0ba8654d8383b2c549107528918"}, + {file = "pytokens-0.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:da5baeaf7116dced9c6bb76dc31ba04a2dc3695f3d9f74741d7910122b456edc"}, + {file = "pytokens-0.4.1-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11edda0942da80ff58c4408407616a310adecae1ddd22eef8c692fe266fa5009"}, + {file = "pytokens-0.4.1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0fc71786e629cef478cbf29d7ea1923299181d0699dbe7c3c0f4a583811d9fc1"}, + {file = "pytokens-0.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dcafc12c30dbaf1e2af0490978352e0c4041a7cde31f4f81435c2a5e8b9cabb6"}, + {file = "pytokens-0.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:42f144f3aafa5d92bad964d471a581651e28b24434d184871bd02e3a0d956037"}, + {file = "pytokens-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:34bcc734bd2f2d5fe3b34e7b3c0116bfb2397f2d9666139988e7a3eb5f7400e3"}, + {file = "pytokens-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:941d4343bf27b605e9213b26bfa1c4bf197c9c599a9627eb7305b0defcfe40c1"}, + {file = "pytokens-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ad72b851e781478366288743198101e5eb34a414f1d5627cdd585ca3b25f1db"}, + {file = "pytokens-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:682fa37ff4d8e95f7df6fe6fe6a431e8ed8e788023c6bcc0f0880a12eab80ad1"}, + {file = "pytokens-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:30f51edd9bb7f85c748979384165601d028b84f7bd13fe14d3e065304093916a"}, + {file = "pytokens-0.4.1-py3-none-any.whl", hash = "sha256:26cef14744a8385f35d0e095dc8b3a7583f6c953c2e3d269c7f82484bf5ad2de"}, + {file = "pytokens-0.4.1.tar.gz", hash = "sha256:292052fe80923aae2260c073f822ceba21f3872ced9a68bb7953b348e561179a"}, +] + +[package.extras] +dev = ["black", "build", "mypy", "pytest", "pytest-cov", "setuptools", "tox", "twine", "wheel"] + +[[package]] +name = "requests" +version = "2.33.1" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a"}, + {file = "requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517"}, +] + +[package.dependencies] +certifi = ">=2023.5.7" +charset_normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.26,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] + +[[package]] +name = "roman-numerals" +version = "4.1.0" +description = "Manipulate well-formed Roman numerals" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7"}, + {file = "roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2"}, +] + +[[package]] +name = "snowballstemmer" +version = "3.0.1" +description = "This package provides 32 stemmers for 30 languages generated from Snowball algorithms." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*" +groups = ["main"] +files = [ + {file = "snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064"}, + {file = "snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895"}, +] + +[[package]] +name = "soupsieve" +version = "2.8.3" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95"}, + {file = "soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349"}, +] + +[[package]] +name = "sphinx" +version = "9.1.0" +description = "Python documentation generator" +optional = false +python-versions = ">=3.12" +groups = ["main"] +files = [ + {file = "sphinx-9.1.0-py3-none-any.whl", hash = "sha256:c84fdd4e782504495fe4f2c0b3413d6c2bf388589bb352d439b2a3bb99991978"}, + {file = "sphinx-9.1.0.tar.gz", hash = "sha256:7741722357dd75f8190766926071fed3bdc211c74dd2d7d4df5404da95930ddb"}, +] + +[package.dependencies] +alabaster = ">=0.7.14" +babel = ">=2.13" +colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\""} +docutils = ">=0.21,<0.23" +imagesize = ">=1.3" +Jinja2 = ">=3.1" +packaging = ">=23.0" +Pygments = ">=2.17" +requests = ">=2.30.0" +roman-numerals = ">=1.0.0" +snowballstemmer = ">=2.2" +sphinxcontrib-applehelp = ">=1.0.7" +sphinxcontrib-devhelp = ">=1.0.6" +sphinxcontrib-htmlhelp = ">=2.0.6" +sphinxcontrib-jsmath = ">=1.0.1" +sphinxcontrib-qthelp = ">=1.0.6" +sphinxcontrib-serializinghtml = ">=1.1.9" + +[[package]] +name = "sphinx-book-theme" +version = "1.2.0" +description = "A clean book theme for scientific explanations and documentation with Sphinx" +optional = false +python-versions = ">=3.11" +groups = ["main"] +files = [ + {file = "sphinx_book_theme-1.2.0-py3-none-any.whl", hash = "sha256:709605d308e1991c5ef0cf19c481dbe9084b62852e317fafab74382a0ee7ccfa"}, + {file = "sphinx_book_theme-1.2.0.tar.gz", hash = "sha256:4a7ebfc7da4395309ac942ddfc38fbec5c5254c3be22195e99ad12586fbda9e3"}, +] + +[package.dependencies] +pydata-sphinx-theme = "0.16.1" +sphinx = ">=7.0" + +[package.extras] +code-style = ["pre-commit"] +doc = ["ablog (>=0.11.13)", "folium", "ipywidgets", "matplotlib", "myst-nb", "nbclient", "numpy", "numpydoc", "pandas", "plotly", "sphinx-copybutton", "sphinx-design", "sphinx-examples", "sphinx-tabs", "sphinx-thebe", "sphinx-togglebutton", "sphinxcontrib-bibtex", "sphinxcontrib-youtube", "sphinxext-opengraph"] +test = ["beautifulsoup4", "coverage", "defusedxml", "myst-nb", "pytest", "pytest-cov", "pytest-regressions", "sphinx_thebe"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5"}, + {file = "sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2"}, + {file = "sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8"}, + {file = "sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +optional = false +python-versions = ">=3.5" +groups = ["main"] +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb"}, + {file = "sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["defusedxml (>=0.7.1)", "pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331"}, + {file = "sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d"}, +] + +[package.extras] +lint = ["mypy", "ruff (==0.5.5)", "types-docutils"] +standalone = ["Sphinx (>=5)"] +test = ["pytest"] + +[[package]] +name = "types-gdb" +version = "16.3.0.20260408" +description = "Typing stubs for gdb" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "types_gdb-16.3.0.20260408-py3-none-any.whl", hash = "sha256:531a0b4c3053c8a2f7b796ecde9d05e3ab48e6b9cda77b2fcc8ff3c9b703f999"}, + {file = "types_gdb-16.3.0.20260408.tar.gz", hash = "sha256:bdfa7cf5dd499f9030f9b8bb13680f9a5d7942524094277916bbae565e1fe458"}, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, + {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, +] + +[package.extras] +brotli = ["brotli (>=1.2.0) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=1.2.0.0) ; platform_python_implementation != \"CPython\""] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.14" +content-hash = "71741ff27f8f063e3fadb2287ca9bfc8e50c1c750e3a075f9cbd7810be82c41d" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..9584bd2 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,23 @@ +[project] +name = "teachos" +version = "0.1.0" +description = "" +authors = [ + {name = "Your Name",email = "you@example.com"} +] +license = {text = "BSD-3-clause"} +requires-python = ">=3.14" +dependencies = [ + "sphinx (>=9.1.0,<10.0.0)", + "sphinx-book-theme (>=1.2.0,<2.0.0)", + "breathe (>=4.36.0,<5.0.0)", + "types-gdb (>=16.3.0.20260408,<17.0.0.0)", + "black (>=26.3.1,<27.0.0)" +] + +[tool.poetry] +package-mode = false + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/scripts/gdb/teachos/dump_mb2i.py b/scripts/gdb/teachos/dump_mb2i.py index 41a2932..06f4531 100644 --- a/scripts/gdb/teachos/dump_mb2i.py +++ b/scripts/gdb/teachos/dump_mb2i.py @@ -1,30 +1,72 @@ import gdb import struct +from enum import IntEnum + + +class TagType(IntEnum): + END = 0 + CMDLINE = 1 + BOOT_LOADER_NAME = 2 + MODULE = 3 + BASIC_MEMINFO = 4 + BOOTDEV = 5 + MMAP = 6 + VBE = 7 + FRAMEBUFFER = 8 + ELF_SECTIONS = 9 + APM = 10 + EFI32_SYSTEM_TABLE_POINTER = 11 + EFI64_SYSTEM_TABLE_POINTER = 12 + SMBIOS_TABLES = 13 + ACPI_OLD = 14 + ACPI_NEW = 15 + NETWORKING = 16 + EFI_MEMORY_MAP = 17 + EFI_BOOT_SERVICES_NOT_TERMINATED = 18 + EFI32_IMAGE_HANDLE_POINTER = 19 + EFI64_IMAGE_HANDLE_POINTER = 20 + LOAD_BASE_ADDR = 21 + + +class MemoryType(IntEnum): + AVAILABLE = 1 + RESERVED = 2 + ACPI_RECLAIM = 3 + NVS = 4 + BAD = 5 TAG_NAMES = { - 0: "End of Tags", - 1: "Boot Command Line", - 2: "Boot Loader Name", - 3: "Boot Module", - 4: "Basic Memory Information", - 5: "BIOS Boot Device", - 6: "Memory Map", - 7: "VBE Info", - 8: "Framebuffer Info", - 9: "ELF Symbols", - 10: "APM Table", - 14: "ACPI old RSDP (1.0)", - 15: "ACPI new RSDP (2.0+)", - 21: "Image Load Base Physical Address", + TagType.END: "End of Tags", + TagType.CMDLINE: "Boot Command Line", + TagType.BOOT_LOADER_NAME: "Boot Loader Name", + TagType.MODULE: "Boot Module", + TagType.BASIC_MEMINFO: "Basic Memory Information", + TagType.BOOTDEV: "BIOS Boot Device", + TagType.MMAP: "Memory Map", + TagType.VBE: "VBE Info", + TagType.FRAMEBUFFER: "Framebuffer Info", + TagType.ELF_SECTIONS: "ELF Symbols", + TagType.APM: "APM Table", + TagType.EFI32_SYSTEM_TABLE_POINTER: "EFI 32-bit System Table Pointer", + TagType.EFI64_SYSTEM_TABLE_POINTER: "EFI 64-bit System Table Pointer", + TagType.SMBIOS_TABLES: "SMBIOS Tables", + TagType.ACPI_OLD: "ACPI old RSDP (1.0)", + TagType.ACPI_NEW: "ACPI new RSDP (2.0+)", + TagType.NETWORKING: "Networking Information", + TagType.EFI_MEMORY_MAP: "EFI Memory Map", + TagType.EFI_BOOT_SERVICES_NOT_TERMINATED: "EFI Boot Services Not Terminated", + TagType.EFI32_IMAGE_HANDLE_POINTER: "EFI 32-bit Image Handle Pointer", + TagType.EFI64_IMAGE_HANDLE_POINTER: "EFI 64-bit Image Handle Pointer", + TagType.LOAD_BASE_ADDR: "Image Load Base Physical Address", } MEMORY_TYPES = { - 1: "Available", - 2: "Reserved", - 3: "ACPI Reclaim", - 4: "Non-volatile storage", - 5: "Bad Memory", + MemoryType.AVAILABLE: "Available", + MemoryType.RESERVED: "Reserved", + MemoryType.ACPI_RECLAIM: "ACPI Reclaim", + MemoryType.NVS: "Non-volatile storage", + MemoryType.BAD: "Bad Memory", } INDENT = f"{' '*11}" @@ -64,7 +106,9 @@ class DumpMB2I(gdb.Command): return if total_size < 8 or total_size > 1024 * 1024: - gdb.write("Warning: suspicious total size ({total_size} bytes). Aborting.") + gdb.write( + f"Warning: suspicious total size ({total_size} bytes). Aborting.\n" + ) return gdb.write(f"+{'-'*70}+\n") @@ -86,11 +130,11 @@ class DumpMB2I(gdb.Command): ) break - self._print_tag(inferior, tag_address, tag_type, tag_size) - - if tag_type == 0 and tag_size == 0: + if tag_type == TagType.END and tag_size == 8: break + self._print_tag(inferior, tag_address, tag_type, tag_size) + offset += (tag_size + 7) & ~7 def _format_size(self, size): @@ -104,50 +148,77 @@ class DumpMB2I(gdb.Command): def _print_tag(self, inferior, tag_address, tag_type, tag_size): name = TAG_NAMES.get(tag_type, f"Unknown Tag") - gdb.write(f"[Tag {tag_type:#04x}] {name} (size: {self._format_size(tag_size)})\n") + gdb.write( + f"[Tag {tag_type:#04x}] {name} (size: {self._format_size(tag_size)})\n" + ) - if tag_size <= 8 and tag_type != 0: + if tag_size <= 8 and tag_type != TagType.END: return try: - if tag_type in (1, 2): - data = inferior.read_memory(tag_address + 8, tag_size - 8).tobytes() - text = data.split(b"\x00")[0].decode("ascii", "replace") - gdb.write(f'{INDENT}"{text}"\n') - elif tag_type == 3: - data = inferior.read_memory(tag_address + 8, tag_size - 8).tobytes() - start, end = struct.unpack_from("\n") + gdb.write( + f"{INDENT}\n" + ) + + def _write_string_tag(self, data): + text = data.split(b"\x00")[0].decode("ascii", "replace") + gdb.write(f'{INDENT}"{text}"\n') + + def _write_module_tag(self, data): + start, end = struct.unpack_from(" Date: Wed, 6 May 2026 06:49:02 +0000 Subject: dump_mb2i: add support for framebuffer tag --- scripts/gdb/teachos/dump_mb2i.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/scripts/gdb/teachos/dump_mb2i.py b/scripts/gdb/teachos/dump_mb2i.py index 06f4531..ca0c378 100644 --- a/scripts/gdb/teachos/dump_mb2i.py +++ b/scripts/gdb/teachos/dump_mb2i.py @@ -36,6 +36,12 @@ class MemoryType(IntEnum): BAD = 5 +class FramebufferType(IntEnum): + INDEXED = 0 + RGB = 1 + TEXT = 2 + + TAG_NAMES = { TagType.END: "End of Tags", TagType.CMDLINE: "Boot Command Line", @@ -69,6 +75,13 @@ MEMORY_TYPES = { MemoryType.BAD: "Bad Memory", } +FRAMEBUFFER_TYPES = { + FramebufferType.INDEXED: "Indexed Color", + FramebufferType.RGB: "RGB", + FramebufferType.TEXT: "Text Mode", +} + + INDENT = f"{' '*11}" @@ -221,4 +234,11 @@ class DumpMB2I(gdb.Command): gdb.write(f"{INDENT}address: {base:#010x}\n") def _write_framebuffer_info_tag(self, data): - pass + address, pitch, width, height, bpp, type = struct.unpack_from(" Date: Wed, 6 May 2026 06:56:47 +0000 Subject: dump_mb2i: dump unsupported tags as hex --- scripts/gdb/teachos/dump_mb2i.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/scripts/gdb/teachos/dump_mb2i.py b/scripts/gdb/teachos/dump_mb2i.py index ca0c378..3a9ee4a 100644 --- a/scripts/gdb/teachos/dump_mb2i.py +++ b/scripts/gdb/teachos/dump_mb2i.py @@ -161,9 +161,9 @@ class DumpMB2I(gdb.Command): def _print_tag(self, inferior, tag_address, tag_type, tag_size): name = TAG_NAMES.get(tag_type, f"Unknown Tag") - gdb.write( - f"[Tag {tag_type:#04x}] {name} (size: {self._format_size(tag_size)})\n" - ) + size = self._format_size(tag_size) + payload = self._format_size(tag_size - 8) + gdb.write(f"[Tag {tag_type:#04x}] {name} (size: {size} | payload: {payload})\n") if tag_size <= 8 and tag_type != TagType.END: return @@ -182,9 +182,8 @@ class DumpMB2I(gdb.Command): TagType.FRAMEBUFFER: self._write_framebuffer_info_tag, } - handler = handlers.get(tag_type) - if handler: - handler(data) + handler = handlers.get(tag_type, self._write_unknown_tag) + handler(data) except Exception as e: gdb.write( @@ -242,3 +241,9 @@ class DumpMB2I(gdb.Command): gdb.write(f"{INDENT}width: {width} chars | height: {height} chars\n") else: gdb.write(f"{INDENT}width: {width} px | height: {height} px\n") + + def _write_unknown_tag(self, data): + for i in range(0, len(data), 16): + chunk = data[i : i + 16] + hex_chunk = " ".join([f"{b:02x}" for b in chunk]) + gdb.write(f"{INDENT}{i:#010x}: {hex_chunk}\n") -- cgit v1.2.3 From 299737fae3aff1c7de39b0876106d0c492c3ed9f Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 6 May 2026 14:31:33 +0200 Subject: ide: add poetry to the devcontainer --- .devcontainer/x86-64/devcontainer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/x86-64/devcontainer.json b/.devcontainer/x86-64/devcontainer.json index 18b9308..76ddaed 100644 --- a/.devcontainer/x86-64/devcontainer.json +++ b/.devcontainer/x86-64/devcontainer.json @@ -5,7 +5,7 @@ "ghcr.io/devcontainers/features/git:1": {}, "ghcr.io/devcontainers/features/git-lfs:1": {}, "ghcr.io/devcontainers-extra/features/apt-packages:1": { - "packages": "acpica-tools,build-essential,clang-tidy-22,clangd-22,cmake,gdb,grub2-common,grub-pc,mtools,ninja-build,qemu-system-x86,ssh,wget,xorriso" + "packages": "acpica-tools,build-essential,clang-tidy-22,clangd-22,cmake,gdb,grub2-common,grub-pc,mtools,ninja-build,python3-poetry,qemu-system-x86,ssh,wget,xorriso" } }, "customizations": { -- cgit v1.2.3 From 22d0be58316125da668302370a1bebc6655880ea Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 7 May 2026 09:11:36 +0000 Subject: ide: update devcontainer --- .devcontainer/x86-64/devcontainer.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.devcontainer/x86-64/devcontainer.json b/.devcontainer/x86-64/devcontainer.json index 76ddaed..80abece 100644 --- a/.devcontainer/x86-64/devcontainer.json +++ b/.devcontainer/x86-64/devcontainer.json @@ -1,6 +1,6 @@ { "name": "TeachOS on x86-64", - "image": "registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:16.1.0-2-py3.14", + "image": "registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:16.1.0-3-py3.14", "features": { "ghcr.io/devcontainers/features/git:1": {}, "ghcr.io/devcontainers/features/git-lfs:1": {}, @@ -21,7 +21,5 @@ "ms-vscode.hexeditor" ] } - }, - "remoteUser": "ubuntu", - "updateRemoteUserUID": true + } } \ No newline at end of file -- cgit v1.2.3 From 0ea43527332b7e5f1cfec6007506aa54e8f628cb Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 7 May 2026 09:12:05 +0000 Subject: debug: enable libstdc++ helpers --- .vscode/launch.json | 8 ++++++-- libs/kstd/gdb/__init__.py | 2 -- libs/kstd/gdb/std_types.py | 13 ------------- scripts/gdb/load.py | 47 ---------------------------------------------- scripts/gdb/teachos.py | 47 ++++++++++++++++++++++++++++++++++++++++++++++ scripts/gdb/toolchain.py | 35 ++++++++++++++++++++++++++++++++++ 6 files changed, 88 insertions(+), 64 deletions(-) delete mode 100644 libs/kstd/gdb/std_types.py delete mode 100644 scripts/gdb/load.py create mode 100644 scripts/gdb/teachos.py create mode 100644 scripts/gdb/toolchain.py diff --git a/.vscode/launch.json b/.vscode/launch.json index d5b2401..90c7520 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -27,8 +27,12 @@ "text": "-file-exec-and-symbols ${command:cmake.buildDirectory}/bin/${command:cmake.buildType}/kernel.sym" }, { - "description": "Load custom Python helpers", - "text": "-interpreter-exec console \"source ${workspaceFolder}/scripts/gdb/load.py\"" + "description": "Load teachos python helpers", + "text": "source ${workspaceFolder}/scripts/gdb/teachos.py" + }, + { + "description": "Load toolchain python helpers", + "text": "source ${workspaceFolder}/scripts/gdb/toolchain.py" } ] } diff --git a/libs/kstd/gdb/__init__.py b/libs/kstd/gdb/__init__.py index fc5e8fb..2d61539 100644 --- a/libs/kstd/gdb/__init__.py +++ b/libs/kstd/gdb/__init__.py @@ -2,7 +2,6 @@ import gdb.printing from .vector import KstdVectorPrinter from .string import KstdStringPrinter -from .std_types import StdBytePrinter from .smart_pointers import KstdUniquePtrPrinter, KstdSharedPtrPrinter @@ -10,7 +9,6 @@ def build_pretty_printers(): pp = gdb.printing.RegexpCollectionPrettyPrinter("kstd") pp.add_printer("vector", "^kstd::vector<.*>$", KstdVectorPrinter) pp.add_printer("string", "^kstd::string$", KstdStringPrinter) - pp.add_printer("std_byte", "^std::byte$", StdBytePrinter) pp.add_printer("unique_ptr", "^kstd::unique_ptr<.*>$", KstdUniquePtrPrinter) pp.add_printer("shared_ptr", "^kstd::shared_ptr<.*>$", KstdSharedPtrPrinter) return pp diff --git a/libs/kstd/gdb/std_types.py b/libs/kstd/gdb/std_types.py deleted file mode 100644 index deb5c58..0000000 --- a/libs/kstd/gdb/std_types.py +++ /dev/null @@ -1,13 +0,0 @@ -import gdb -from teachos import TeachOSBasePrinter - - -class StdBytePrinter(TeachOSBasePrinter): - - def to_string(self): - try: - uint8_type = gdb.lookup_type("unsigned char") - numeric_value = int(self.value.cast(uint8_type)) - return f"{numeric_value:#04x}" - except gdb.error: - return f"" diff --git a/scripts/gdb/load.py b/scripts/gdb/load.py deleted file mode 100644 index 355f6b9..0000000 --- a/scripts/gdb/load.py +++ /dev/null @@ -1,47 +0,0 @@ -import sys -import os -import gdb -import importlib.util - -script_path = os.path.abspath(__file__) -script_root = os.path.dirname(script_path) -repo_root = os.path.dirname(os.path.dirname(script_root)) - -if script_root not in sys.path: - sys.path.insert(0, script_root) - -from teachos.dump_mb2i import DumpMB2I - -components = { - "kstd": "libs/kstd/gdb", - "kapi": "kapi/gdb", -} - -for component, path in components.items(): - full_path = os.path.join(repo_root, *path.split("/")) - init_file = os.path.join(full_path, "__init__.py") - - if os.path.isfile(init_file): - try: - spec = importlib.util.spec_from_file_location(component, init_file) - module = importlib.util.module_from_spec(spec) - sys.modules[component] = module - spec.loader.exec_module(module) - - if hasattr(module, "register_printers"): - module.register_printers(gdb.current_objfile()) - gdb.write(f"Info: Registered pretty printers for '{component}'.\n") - else: - gdb.write( - f"Warning: '{component}' does not have 'register_printers' function\n" - ) - except Exception as e: - gdb.write(f"Warning: Failed to load '{component}' pretty printers: {e}\n") - else: - gdb.write(f"Warning: GDB extension init not found: '{init_file}'\n") - -gdb.write("Info: Loaded TeachOS pretty printers.\n") - -DumpMB2I() - -gdb.write("Info: Loaded TeachOS tools.\n") \ No newline at end of file diff --git a/scripts/gdb/teachos.py b/scripts/gdb/teachos.py new file mode 100644 index 0000000..355f6b9 --- /dev/null +++ b/scripts/gdb/teachos.py @@ -0,0 +1,47 @@ +import sys +import os +import gdb +import importlib.util + +script_path = os.path.abspath(__file__) +script_root = os.path.dirname(script_path) +repo_root = os.path.dirname(os.path.dirname(script_root)) + +if script_root not in sys.path: + sys.path.insert(0, script_root) + +from teachos.dump_mb2i import DumpMB2I + +components = { + "kstd": "libs/kstd/gdb", + "kapi": "kapi/gdb", +} + +for component, path in components.items(): + full_path = os.path.join(repo_root, *path.split("/")) + init_file = os.path.join(full_path, "__init__.py") + + if os.path.isfile(init_file): + try: + spec = importlib.util.spec_from_file_location(component, init_file) + module = importlib.util.module_from_spec(spec) + sys.modules[component] = module + spec.loader.exec_module(module) + + if hasattr(module, "register_printers"): + module.register_printers(gdb.current_objfile()) + gdb.write(f"Info: Registered pretty printers for '{component}'.\n") + else: + gdb.write( + f"Warning: '{component}' does not have 'register_printers' function\n" + ) + except Exception as e: + gdb.write(f"Warning: Failed to load '{component}' pretty printers: {e}\n") + else: + gdb.write(f"Warning: GDB extension init not found: '{init_file}'\n") + +gdb.write("Info: Loaded TeachOS pretty printers.\n") + +DumpMB2I() + +gdb.write("Info: Loaded TeachOS tools.\n") \ No newline at end of file diff --git a/scripts/gdb/toolchain.py b/scripts/gdb/toolchain.py new file mode 100644 index 0000000..bbb7810 --- /dev/null +++ b/scripts/gdb/toolchain.py @@ -0,0 +1,35 @@ +import os +import subprocess +import sys + + +def setup_toolchain_debugging(): + try: + gcc_path = ( + subprocess.check_output(["which", "x86_64-pc-elf-g++"]) + .decode("utf-8") + .strip() + ) + python_dir = None + + share_path = os.path.join(os.path.dirname(os.path.dirname(gcc_path)), "share") + for root, dirs, _ in os.walk(share_path): + if "libstdcxx" in dirs: + python_dir = root + break + + if python_dir: + sys.path.insert(0, python_dir) + + from libstdcxx.v6.printers import register_libstdcxx_printers + from libstdcxx.v6.xmethods import register_libstdcxx_xmethods + + register_libstdcxx_printers(None) + register_libstdcxx_xmethods(None) + + print(f"Loaded Printers & Xmethods from: {python_dir}") + except Exception as e: + print(f"Debug setup failed: {e}") + + +setup_toolchain_debugging() -- cgit v1.2.3 From 35829497bdc0e00aa8f32b1855079fa5e2e0b084 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 7 May 2026 11:41:31 +0200 Subject: debug: align kstd printers with std ones --- libs/kstd/gdb/smart_pointers.py | 13 ++++++------- libs/kstd/gdb/vector.py | 10 ++++------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/libs/kstd/gdb/smart_pointers.py b/libs/kstd/gdb/smart_pointers.py index 3e5da02..0c14c77 100644 --- a/libs/kstd/gdb/smart_pointers.py +++ b/libs/kstd/gdb/smart_pointers.py @@ -16,7 +16,7 @@ class KstdUniquePtrPrinter(TeachOSBasePrinter): def children(self): pointer = self.value["pointer"] if int(pointer) != 0: - yield ("[object]", pointer.dereference()) + yield ("get()", pointer.dereference()) yield from super().children() @@ -31,21 +31,20 @@ class KstdSharedPtrPrinter(TeachOSBasePrinter): control_block = self.value["control"] if int(pointer) == 0 or int(control_block) == 0: - return f"kstd::shared_ptr<{self.__type}> (empty)" + return f"shared_ptr<{self.__type}> (empty)" strong_refs = int(control_block["shared_count"]["_M_i"]) weak_refs = int(control_block["weak_count"]["_M_i"]) - return f"kstd::shared_ptr<{self.__type}> (use_count={strong_refs}, weak_count={weak_refs})" + return ( + f"shared_ptr<{self.__type}> use count {strong_refs}, weak count {weak_refs}" + ) def children(self): pointer = self.value["pointer"] control_block = self.value["control"] if int(pointer) != 0: - yield ("[object]", pointer.dereference()) - - if int(control_block) != 0: - yield ("[control_block]", control_block.dereference()) + yield ("get()", pointer.dereference()) yield from super().children() diff --git a/libs/kstd/gdb/vector.py b/libs/kstd/gdb/vector.py index 4340ef4..b3604de 100644 --- a/libs/kstd/gdb/vector.py +++ b/libs/kstd/gdb/vector.py @@ -3,17 +3,12 @@ from teachos import TeachOSBasePrinter class KstdVectorPrinter(TeachOSBasePrinter): - def __init__(self, val): - super().__init__(val) - self.__type = val.type.template_argument(0) - def to_string(self): size = int(self.value["m_size"]) capacity = int(self.value["m_capacity"]) - return f"kstd::vector<{self.__type}> (size={size}, capacity={capacity})" + return f"vector of length {size}, capacity {capacity}" def children(self): - yield from super().children() size = int(self.value["m_size"]) data_pointer = self.value["m_data"] for i in range(size): @@ -21,3 +16,6 @@ class KstdVectorPrinter(TeachOSBasePrinter): def display_hint(self): return "array" + + def num_children(self): + return int(self.value["m_size"]) -- cgit v1.2.3 From 6ac1537d07dffa3482bbccf710a77a7316191c2e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 7 May 2026 12:09:27 +0200 Subject: debug: use gdb.ValuePrinter base class --- kapi/gdb/address.py | 15 ++++++++------- kapi/gdb/device.py | 17 ++++++++++++++--- libs/kstd/gdb/smart_pointers.py | 27 ++++++++++++++------------- libs/kstd/gdb/string.py | 2 +- libs/kstd/gdb/units.py | 0 libs/kstd/gdb/vector.py | 25 ++++++++++++++++--------- scripts/gdb/teachos/__init__.py | 30 ------------------------------ 7 files changed, 53 insertions(+), 63 deletions(-) create mode 100644 libs/kstd/gdb/units.py diff --git a/kapi/gdb/address.py b/kapi/gdb/address.py index 24fe681..429db1d 100644 --- a/kapi/gdb/address.py +++ b/kapi/gdb/address.py @@ -1,15 +1,14 @@ import gdb -from teachos import TeachOSBasePrinter -class KapiMemoryAddressPrinter(TeachOSBasePrinter): +class KapiMemoryAddressPrinter(gdb.ValuePrinter): def __init__(self, val): - super().__init__(val) + self.__val = val self.__type = val.type.template_argument(0) def to_string(self): try: - raw_address = int(self.value["m_value"]) + raw_address = int(self.__val["m_value"]) type_string = str(self.__type) if "linear" in type_string: @@ -21,10 +20,12 @@ class KapiMemoryAddressPrinter(TeachOSBasePrinter): return f"{raw_address:#018x}{suffix}" except Exception as e: - return f"{self.value}: {e}" + return f"{self.__val}: {e}" def children(self): if "linear" in str(self.__type): pointer_type = gdb.lookup_type("std::byte").pointer() - yield ("[bytes]", self.value["m_value"].cast(pointer_type)) - yield from super().children() + yield ("[bytes]", self.__val["m_value"].cast(pointer_type)) + + def display_hint(self): + return None diff --git a/kapi/gdb/device.py b/kapi/gdb/device.py index b655972..8e515ef 100644 --- a/kapi/gdb/device.py +++ b/kapi/gdb/device.py @@ -1,9 +1,20 @@ import gdb -from teachos import TeachOSBasePrinter -class KapiDevicesDevicePrinter(TeachOSBasePrinter): +class KapiDevicesDevicePrinter(gdb.ValuePrinter): + def __init__(self, val): + self.__val = val + def to_string(self): return ( - f"{self.value['m_name']} @ {self.value['m_major']}:{self.value['m_minor']}" + f"{self.__val['m_name']} @ {self.__val['m_major']}:{self.__val['m_minor']}" ) + + def children(self): + yield ("major", self.__val["m_major"]) + yield ("minor", self.__val["m_minor"]) + yield ("name", self.__val["m_name"]) + yield ("parent", self.__val["m_parent"]) + + def display_hint(self): + return None diff --git a/libs/kstd/gdb/smart_pointers.py b/libs/kstd/gdb/smart_pointers.py index 0c14c77..b94d466 100644 --- a/libs/kstd/gdb/smart_pointers.py +++ b/libs/kstd/gdb/smart_pointers.py @@ -1,34 +1,34 @@ import gdb -from teachos import TeachOSBasePrinter -class KstdUniquePtrPrinter(TeachOSBasePrinter): +class KstdUniquePtrPrinter(gdb.ValuePrinter): def __init__(self, val): - super().__init__(val) + self.__val = val self.__type = val.type.template_argument(0) def to_string(self): - pointer = self.value["pointer"] + pointer = self.__val["pointer"] if int(pointer) == 0: return f"kstd::unique_ptr<{self.__type}> (empty)" return f"kstd::unique_ptr<{self.__type}>" def children(self): - pointer = self.value["pointer"] + pointer = self.__val["pointer"] if int(pointer) != 0: yield ("get()", pointer.dereference()) - yield from super().children() + def display_hint(self): + return None -class KstdSharedPtrPrinter(TeachOSBasePrinter): +class KstdSharedPtrPrinter(gdb.ValuePrinter): def __init__(self, val): - super().__init__(val) + self.__val = val self.__type = val.type.template_argument(0) def to_string(self): - pointer = self.value["pointer"] - control_block = self.value["control"] + pointer = self.__val["pointer"] + control_block = self.__val["control"] if int(pointer) == 0 or int(control_block) == 0: return f"shared_ptr<{self.__type}> (empty)" @@ -41,10 +41,11 @@ class KstdSharedPtrPrinter(TeachOSBasePrinter): ) def children(self): - pointer = self.value["pointer"] - control_block = self.value["control"] + pointer = self.__val["pointer"] + control_block = self.__val["control"] if int(pointer) != 0: yield ("get()", pointer.dereference()) - yield from super().children() + def display_hint(self): + return None diff --git a/libs/kstd/gdb/string.py b/libs/kstd/gdb/string.py index 2688061..73c22d6 100644 --- a/libs/kstd/gdb/string.py +++ b/libs/kstd/gdb/string.py @@ -1,7 +1,7 @@ import gdb -class KstdStringPrinter: +class KstdStringPrinter(gdb.ValuePrinter): def __init__(self, val): self.__val = val diff --git a/libs/kstd/gdb/units.py b/libs/kstd/gdb/units.py new file mode 100644 index 0000000..e69de29 diff --git a/libs/kstd/gdb/vector.py b/libs/kstd/gdb/vector.py index b3604de..69f8ca2 100644 --- a/libs/kstd/gdb/vector.py +++ b/libs/kstd/gdb/vector.py @@ -1,21 +1,28 @@ import gdb -from teachos import TeachOSBasePrinter -class KstdVectorPrinter(TeachOSBasePrinter): +class KstdVectorPrinter(gdb.ValuePrinter): + def __init__(self, val): + self.__val = val + self.__size = int(val["m_size"]) + self.__capacity = int(val["m_capacity"]) + def to_string(self): - size = int(self.value["m_size"]) - capacity = int(self.value["m_capacity"]) - return f"vector of length {size}, capacity {capacity}" + return f"vector of length {self.__size}, capacity {self.__capacity}" def children(self): - size = int(self.value["m_size"]) - data_pointer = self.value["m_data"] - for i in range(size): + data_pointer = self.__val["m_data"] + for i in range(self.__size): yield (f"[{i}]", (data_pointer + i).dereference()) + def child(self, n): + if n < self.__size: + return (f"[{n}]", (self.__val["m_data"] + n).dereference()) + else: + raise gdb.MemoryError("Index out of range") + def display_hint(self): return "array" def num_children(self): - return int(self.value["m_size"]) + return self.__size diff --git a/scripts/gdb/teachos/__init__.py b/scripts/gdb/teachos/__init__.py index d90d6ad..e69de29 100644 --- a/scripts/gdb/teachos/__init__.py +++ b/scripts/gdb/teachos/__init__.py @@ -1,30 +0,0 @@ -import gdb - - -class TeachOSBasePrinter: - def __init__(self, val): - self.__val = val - - @property - def value(self): - return self.__val - - def children(self): - real_type = self.value.type.strip_typedefs() - - if real_type.code not in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_UNION): - return - - for field in real_type.fields(): - if field.artificial: - continue - - try: - if field.is_base_class: - yield (field.name, self.value.cast(field.type)) - elif field.name is not None: - yield (field.name, self.value[field.name]) - else: - yield ("", self.value[field]) - except gdb.error: - yield (str(field.name), "") -- cgit v1.2.3 From fb09cd6633b26ef2cfb4f21b8cd852611cfe59d8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 7 May 2026 12:17:01 +0200 Subject: debug: add support for kstd::observer_ptr --- libs/kstd/gdb/__init__.py | 7 ++++++- libs/kstd/gdb/smart_pointers.py | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/libs/kstd/gdb/__init__.py b/libs/kstd/gdb/__init__.py index 2d61539..c5d1e53 100644 --- a/libs/kstd/gdb/__init__.py +++ b/libs/kstd/gdb/__init__.py @@ -2,7 +2,11 @@ import gdb.printing from .vector import KstdVectorPrinter from .string import KstdStringPrinter -from .smart_pointers import KstdUniquePtrPrinter, KstdSharedPtrPrinter +from .smart_pointers import ( + KstdUniquePtrPrinter, + KstdSharedPtrPrinter, + KstdObserverPtrPrinter, +) def build_pretty_printers(): @@ -11,6 +15,7 @@ def build_pretty_printers(): pp.add_printer("string", "^kstd::string$", KstdStringPrinter) pp.add_printer("unique_ptr", "^kstd::unique_ptr<.*>$", KstdUniquePtrPrinter) pp.add_printer("shared_ptr", "^kstd::shared_ptr<.*>$", KstdSharedPtrPrinter) + pp.add_printer("observer_ptr", "^kstd::observer_ptr<.*>$", KstdObserverPtrPrinter) return pp diff --git a/libs/kstd/gdb/smart_pointers.py b/libs/kstd/gdb/smart_pointers.py index b94d466..f6e8a45 100644 --- a/libs/kstd/gdb/smart_pointers.py +++ b/libs/kstd/gdb/smart_pointers.py @@ -49,3 +49,20 @@ class KstdSharedPtrPrinter(gdb.ValuePrinter): def display_hint(self): return None + + +class KstdObserverPtrPrinter(gdb.ValuePrinter): + def __init__(self, val): + self.__val = val + self.__type = val.type.template_argument(0) + self.__pointer = val["m_ptr"] + + def to_string(self): + return f"{(self.__pointer)}" + + def children(self): + if int(self.__pointer) != 0: + yield ("get()", self.__pointer.dereference()) + + def display_hint(self): + return None -- cgit v1.2.3 From 07fb219869099c719b0fbfeae81b95512487639e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 8 May 2026 17:12:24 +0200 Subject: debug: add page and frame formatters --- kapi/gdb/__init__.py | 9 +++++++-- kapi/gdb/address.py | 31 ------------------------------ kapi/gdb/device.py | 20 -------------------- kapi/gdb/devices/__init__.py | 1 + kapi/gdb/devices/device.py | 20 ++++++++++++++++++++ kapi/gdb/memory/__init__.py | 4 ++++ kapi/gdb/memory/address.py | 31 ++++++++++++++++++++++++++++++ kapi/gdb/memory/chunk.py | 41 ++++++++++++++++++++++++++++++++++++++++ scripts/gdb/teachos/__init__.py | 8 ++++++++ scripts/gdb/teachos/dump_mb2i.py | 22 +++++++-------------- 10 files changed, 119 insertions(+), 68 deletions(-) delete mode 100644 kapi/gdb/address.py delete mode 100644 kapi/gdb/device.py create mode 100644 kapi/gdb/devices/__init__.py create mode 100644 kapi/gdb/devices/device.py create mode 100644 kapi/gdb/memory/__init__.py create mode 100644 kapi/gdb/memory/address.py create mode 100644 kapi/gdb/memory/chunk.py diff --git a/kapi/gdb/__init__.py b/kapi/gdb/__init__.py index ce95628..afb68f8 100644 --- a/kapi/gdb/__init__.py +++ b/kapi/gdb/__init__.py @@ -1,7 +1,7 @@ import gdb.printing -from .address import KapiMemoryAddressPrinter -from .device import KapiDevicesDevicePrinter +from .memory import * +from .devices import * def build_pretty_printers(): @@ -9,6 +9,11 @@ def build_pretty_printers(): pp.add_printer( "kapi_memory_address", "^kapi::memory::address<.*>$", KapiMemoryAddressPrinter ) + pp.add_printer( + "kapi_memory_chunk", "^kapi::memory::chunk<.*>$", KapiMemoryChunkPrinter + ) + pp.add_printer("kapi_memory_frame", "^kapi::memory::frame$", KapiMemoryFramePrinter) + pp.add_printer("kapi_memory_page", "^kapi::memory::page$", KapiMemoryPagePrinter) pp.add_printer( "kapi_devices_device", "^kapi::devices::device$", KapiDevicesDevicePrinter ) diff --git a/kapi/gdb/address.py b/kapi/gdb/address.py deleted file mode 100644 index 429db1d..0000000 --- a/kapi/gdb/address.py +++ /dev/null @@ -1,31 +0,0 @@ -import gdb - - -class KapiMemoryAddressPrinter(gdb.ValuePrinter): - def __init__(self, val): - self.__val = val - self.__type = val.type.template_argument(0) - - def to_string(self): - try: - raw_address = int(self.__val["m_value"]) - type_string = str(self.__type) - - if "linear" in type_string: - suffix = "%lin" - elif "physical" in type_string: - suffix = "%phy" - else: - suffix = "%???" - - return f"{raw_address:#018x}{suffix}" - except Exception as e: - return f"{self.__val}: {e}" - - def children(self): - if "linear" in str(self.__type): - pointer_type = gdb.lookup_type("std::byte").pointer() - yield ("[bytes]", self.__val["m_value"].cast(pointer_type)) - - def display_hint(self): - return None diff --git a/kapi/gdb/device.py b/kapi/gdb/device.py deleted file mode 100644 index 8e515ef..0000000 --- a/kapi/gdb/device.py +++ /dev/null @@ -1,20 +0,0 @@ -import gdb - - -class KapiDevicesDevicePrinter(gdb.ValuePrinter): - def __init__(self, val): - self.__val = val - - def to_string(self): - return ( - f"{self.__val['m_name']} @ {self.__val['m_major']}:{self.__val['m_minor']}" - ) - - def children(self): - yield ("major", self.__val["m_major"]) - yield ("minor", self.__val["m_minor"]) - yield ("name", self.__val["m_name"]) - yield ("parent", self.__val["m_parent"]) - - def display_hint(self): - return None diff --git a/kapi/gdb/devices/__init__.py b/kapi/gdb/devices/__init__.py new file mode 100644 index 0000000..3bab1ea --- /dev/null +++ b/kapi/gdb/devices/__init__.py @@ -0,0 +1 @@ +from .device import KapiDevicesDevicePrinter diff --git a/kapi/gdb/devices/device.py b/kapi/gdb/devices/device.py new file mode 100644 index 0000000..8e515ef --- /dev/null +++ b/kapi/gdb/devices/device.py @@ -0,0 +1,20 @@ +import gdb + + +class KapiDevicesDevicePrinter(gdb.ValuePrinter): + def __init__(self, val): + self.__val = val + + def to_string(self): + return ( + f"{self.__val['m_name']} @ {self.__val['m_major']}:{self.__val['m_minor']}" + ) + + def children(self): + yield ("major", self.__val["m_major"]) + yield ("minor", self.__val["m_minor"]) + yield ("name", self.__val["m_name"]) + yield ("parent", self.__val["m_parent"]) + + def display_hint(self): + return None diff --git a/kapi/gdb/memory/__init__.py b/kapi/gdb/memory/__init__.py new file mode 100644 index 0000000..2aa6564 --- /dev/null +++ b/kapi/gdb/memory/__init__.py @@ -0,0 +1,4 @@ +from .address import KapiMemoryAddressPrinter +from .chunk import KapiMemoryFramePrinter +from .chunk import KapiMemoryPagePrinter +from .chunk import KapiMemoryChunkPrinter diff --git a/kapi/gdb/memory/address.py b/kapi/gdb/memory/address.py new file mode 100644 index 0000000..429db1d --- /dev/null +++ b/kapi/gdb/memory/address.py @@ -0,0 +1,31 @@ +import gdb + + +class KapiMemoryAddressPrinter(gdb.ValuePrinter): + def __init__(self, val): + self.__val = val + self.__type = val.type.template_argument(0) + + def to_string(self): + try: + raw_address = int(self.__val["m_value"]) + type_string = str(self.__type) + + if "linear" in type_string: + suffix = "%lin" + elif "physical" in type_string: + suffix = "%phy" + else: + suffix = "%???" + + return f"{raw_address:#018x}{suffix}" + except Exception as e: + return f"{self.__val}: {e}" + + def children(self): + if "linear" in str(self.__type): + pointer_type = gdb.lookup_type("std::byte").pointer() + yield ("[bytes]", self.__val["m_value"].cast(pointer_type)) + + def display_hint(self): + return None diff --git a/kapi/gdb/memory/chunk.py b/kapi/gdb/memory/chunk.py new file mode 100644 index 0000000..74b1407 --- /dev/null +++ b/kapi/gdb/memory/chunk.py @@ -0,0 +1,41 @@ +import gdb +from teachos import format_size + + +class KapiMemoryChunkPrinter(gdb.ValuePrinter): + + def __init__(self, val: gdb.Value, typename="chunk"): + self.__val = val + try: + self.__number = int(val["m_number"]) + except gdb.error: + self.__number = "" + + try: + self.__size = int(gdb.parse_and_eval(f"{val.type.name}::size")["value"]) + except gdb.error: + self.__size = "" + + self.__typename = typename + + def to_string(self): + return f"{self.__typename} {self.__number} of size {format_size(self.__size)}" + + def children(self): + yield ("number", self.__number) + yield ("size", self.__size) + + def display_hint(self): + return None + + +class KapiMemoryFramePrinter(KapiMemoryChunkPrinter): + + def __init__(self, val): + super().__init__(val, "frame") + + +class KapiMemoryPagePrinter(KapiMemoryChunkPrinter): + + def __init__(self, val): + super().__init__(val, "page") diff --git a/scripts/gdb/teachos/__init__.py b/scripts/gdb/teachos/__init__.py index e69de29..a5eca92 100644 --- a/scripts/gdb/teachos/__init__.py +++ b/scripts/gdb/teachos/__init__.py @@ -0,0 +1,8 @@ +def format_size(size): + for unit in ["Bytes", "KiB", "MiB", "GiB", "TiB", "PiB"]: + if size < 1024.0: + if unit == "Bytes": + return f"{int(size)} {unit}" + return f"{size:.2f} {unit}" + size /= 1024.0 + return f"{size:.2f} PiB" diff --git a/scripts/gdb/teachos/dump_mb2i.py b/scripts/gdb/teachos/dump_mb2i.py index 3a9ee4a..0657ebd 100644 --- a/scripts/gdb/teachos/dump_mb2i.py +++ b/scripts/gdb/teachos/dump_mb2i.py @@ -1,6 +1,7 @@ import gdb import struct from enum import IntEnum +from teachos import format_size class TagType(IntEnum): @@ -150,19 +151,10 @@ class DumpMB2I(gdb.Command): offset += (tag_size + 7) & ~7 - def _format_size(self, size): - for unit in ["Bytes", "KiB", "MiB", "GiB", "TiB", "PiB"]: - if size < 1024.0: - if unit == "Bytes": - return f"{int(size)} {unit}" - return f"{size:.2f} {unit}" - size /= 1024.0 - return f"{size:.2f} PiB" - def _print_tag(self, inferior, tag_address, tag_type, tag_size): name = TAG_NAMES.get(tag_type, f"Unknown Tag") - size = self._format_size(tag_size) - payload = self._format_size(tag_size - 8) + size = format_size(tag_size) + payload = format_size(tag_size - 8) gdb.write(f"[Tag {tag_type:#04x}] {name} (size: {size} | payload: {payload})\n") if tag_size <= 8 and tag_type != TagType.END: @@ -198,7 +190,7 @@ class DumpMB2I(gdb.Command): start, end = struct.unpack_from(" Date: Fri, 8 May 2026 18:14:08 +0200 Subject: debug: add pretty printer for boot modules --- kapi/gdb/__init__.py | 8 +++++++- kapi/gdb/boot_modules/__init__.py | 1 + kapi/gdb/boot_modules/boot_module.py | 23 +++++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 kapi/gdb/boot_modules/__init__.py create mode 100644 kapi/gdb/boot_modules/boot_module.py diff --git a/kapi/gdb/__init__.py b/kapi/gdb/__init__.py index afb68f8..88b1d05 100644 --- a/kapi/gdb/__init__.py +++ b/kapi/gdb/__init__.py @@ -1,7 +1,8 @@ import gdb.printing -from .memory import * +from .boot_modules import * from .devices import * +from .memory import * def build_pretty_printers(): @@ -17,6 +18,11 @@ def build_pretty_printers(): pp.add_printer( "kapi_devices_device", "^kapi::devices::device$", KapiDevicesDevicePrinter ) + pp.add_printer( + "kapi_boot_modules_boot_module", + "^kapi::boot_modules::boot_module$", + KapiBootModulesBootModulePrinter, + ) return pp diff --git a/kapi/gdb/boot_modules/__init__.py b/kapi/gdb/boot_modules/__init__.py new file mode 100644 index 0000000..fedab65 --- /dev/null +++ b/kapi/gdb/boot_modules/__init__.py @@ -0,0 +1 @@ +from .boot_module import KapiBootModulesBootModulePrinter diff --git a/kapi/gdb/boot_modules/boot_module.py b/kapi/gdb/boot_modules/boot_module.py new file mode 100644 index 0000000..f0d558b --- /dev/null +++ b/kapi/gdb/boot_modules/boot_module.py @@ -0,0 +1,23 @@ +import gdb +from teachos import format_size + + +class KapiBootModulesBootModulePrinter(gdb.ValuePrinter): + def __init__(self, val): + self.__val = val + self.__name = val["name"] + self.__start = val["start_address"] + self.__size = val["size"] + self.__pointer_type = gdb.lookup_type("std::byte").pointer() + self.__pretty_name = self.__name if str(self.__name) != '""' else "" + + def to_string(self): + return f"boot module {self.__pretty_name} of size {format_size(int(self.__size))}, at {self.__start.cast(self.__pointer_type)}" + + def children(self): + yield ("name", self.__name) + yield ("start_address", self.__start) + yield ("size", self.__size) + + def display_hint(self): + return None -- cgit v1.2.3 From 2cb7a2575e8eb46df36dae108fae661b91801540 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sun, 10 May 2026 12:18:01 +0200 Subject: debug: add pretty printer for boot modules registry --- kapi/gdb/__init__.py | 5 ++++ kapi/gdb/boot_modules/__init__.py | 1 + kapi/gdb/boot_modules/boot_module.py | 4 ++-- kapi/gdb/boot_modules/boot_module_registry.py | 20 ++++++++++++++++ libs/kstd/gdb/vector.py | 33 +++++++++++++++++++-------- 5 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 kapi/gdb/boot_modules/boot_module_registry.py diff --git a/kapi/gdb/__init__.py b/kapi/gdb/__init__.py index 88b1d05..1b36753 100644 --- a/kapi/gdb/__init__.py +++ b/kapi/gdb/__init__.py @@ -23,6 +23,11 @@ def build_pretty_printers(): "^kapi::boot_modules::boot_module$", KapiBootModulesBootModulePrinter, ) + pp.add_printer( + "kapi_boot_modules_boot_module_registry", + "^kapi::boot_modules::boot_module_registry$", + KapiBootModulesBootModuleRegistryPrinter, + ) return pp diff --git a/kapi/gdb/boot_modules/__init__.py b/kapi/gdb/boot_modules/__init__.py index fedab65..2a54136 100644 --- a/kapi/gdb/boot_modules/__init__.py +++ b/kapi/gdb/boot_modules/__init__.py @@ -1 +1,2 @@ from .boot_module import KapiBootModulesBootModulePrinter +from .boot_module_registry import KapiBootModulesBootModuleRegistryPrinter diff --git a/kapi/gdb/boot_modules/boot_module.py b/kapi/gdb/boot_modules/boot_module.py index f0d558b..b26ecf1 100644 --- a/kapi/gdb/boot_modules/boot_module.py +++ b/kapi/gdb/boot_modules/boot_module.py @@ -9,10 +9,10 @@ class KapiBootModulesBootModulePrinter(gdb.ValuePrinter): self.__start = val["start_address"] self.__size = val["size"] self.__pointer_type = gdb.lookup_type("std::byte").pointer() - self.__pretty_name = self.__name if str(self.__name) != '""' else "" + self.__pretty_name = " " + str(self.__name) if str(self.__name) != '""' else "" def to_string(self): - return f"boot module {self.__pretty_name} of size {format_size(int(self.__size))}, at {self.__start.cast(self.__pointer_type)}" + return f"boot module{self.__pretty_name} of size {format_size(int(self.__size))}, at {self.__start.cast(self.__pointer_type)}" def children(self): yield ("name", self.__name) diff --git a/kapi/gdb/boot_modules/boot_module_registry.py b/kapi/gdb/boot_modules/boot_module_registry.py new file mode 100644 index 0000000..599a823 --- /dev/null +++ b/kapi/gdb/boot_modules/boot_module_registry.py @@ -0,0 +1,20 @@ +import gdb +from teachos import format_size + + +class KapiBootModulesBootModuleRegistryPrinter(gdb.ValuePrinter): + def __init__(self, val: gdb.Value): + self.__val = val + self.__modules = val["m_modules"] + self.__size = int(self.__modules["m_size"]) + self.__element_type = gdb.lookup_type("kapi::boot_modules::boot_module") + + def to_string(self): + return f"boot module registry of size {self.__size}" + + def children(self): + yield ("[size]", self.__size) + yield ("m_modules", self.__modules) + + def display_hint(self): + return None diff --git a/libs/kstd/gdb/vector.py b/libs/kstd/gdb/vector.py index 69f8ca2..f11e064 100644 --- a/libs/kstd/gdb/vector.py +++ b/libs/kstd/gdb/vector.py @@ -2,24 +2,37 @@ import gdb class KstdVectorPrinter(gdb.ValuePrinter): - def __init__(self, val): + class Iterator: + def __init__(self, begin: gdb.Value, end: gdb.Value): + self._item = begin + self._end = end + self._count = 0 + + def __iter__(self): + return self + + def __next__(self): + count = self._count + self._count = count + 1 + + if self._item == self._end: + raise StopIteration + + element = self._item.dereference() + self._item = self._item + 1 + return (f"[{count}]", element) + + def __init__(self, val: gdb.Value): self.__val = val self.__size = int(val["m_size"]) self.__capacity = int(val["m_capacity"]) + self.__data = val["m_data"] def to_string(self): return f"vector of length {self.__size}, capacity {self.__capacity}" def children(self): - data_pointer = self.__val["m_data"] - for i in range(self.__size): - yield (f"[{i}]", (data_pointer + i).dereference()) - - def child(self, n): - if n < self.__size: - return (f"[{n}]", (self.__val["m_data"] + n).dereference()) - else: - raise gdb.MemoryError("Index out of range") + return self.Iterator(self.__data, self.__data + self.__size) def display_hint(self): return "array" -- cgit v1.2.3 From f3b75be5725a5a975cf73926e73aa63efe928026 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sun, 10 May 2026 12:27:16 +0200 Subject: debug: show data in boot modules --- kapi/gdb/boot_modules/boot_module.py | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/kapi/gdb/boot_modules/boot_module.py b/kapi/gdb/boot_modules/boot_module.py index b26ecf1..97e6584 100644 --- a/kapi/gdb/boot_modules/boot_module.py +++ b/kapi/gdb/boot_modules/boot_module.py @@ -3,21 +3,42 @@ from teachos import format_size class KapiBootModulesBootModulePrinter(gdb.ValuePrinter): + class Iterator: + def __init__(self, begin: gdb.Value, end: gdb.Value): + self._item = begin + self._end = end + self._count = 0 + + def __iter__(self): + return self + + def __next__(self): + count = self._count + self._count = count + 1 + + if self._item == self._end: + raise StopIteration + + element = self._item.dereference() + self._item += 1 + return (f"[{count}]", element) + def __init__(self, val): + self.__pointer_type = gdb.lookup_type("std::byte").pointer() + self.__val = val self.__name = val["name"] self.__start = val["start_address"] - self.__size = val["size"] - self.__pointer_type = gdb.lookup_type("std::byte").pointer() + self.__size = int(val["size"]) + self.__begin = val["start_address"]["m_value"].cast(self.__pointer_type) + self.__end = self.__begin + self.__size self.__pretty_name = " " + str(self.__name) if str(self.__name) != '""' else "" def to_string(self): - return f"boot module{self.__pretty_name} of size {format_size(int(self.__size))}, at {self.__start.cast(self.__pointer_type)}" + return f"boot module{self.__pretty_name} of size {format_size(self.__size)}, at {self.__start.cast(self.__pointer_type)}" def children(self): - yield ("name", self.__name) - yield ("start_address", self.__start) - yield ("size", self.__size) + return self.Iterator(self.__begin, self.__end) def display_hint(self): - return None + return "array" -- cgit v1.2.3 From 0ffee4e5dbc20dd7f1f7991d1f8dab698fc9b7a0 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sun, 10 May 2026 17:12:27 +0200 Subject: kstd/vector: reduce code duplication --- libs/kstd/kstd/vector | 257 ++++++++++++++++++-------------------------------- 1 file changed, 91 insertions(+), 166 deletions(-) diff --git a/libs/kstd/kstd/vector b/libs/kstd/kstd/vector index c714957..736b854 100644 --- a/libs/kstd/kstd/vector +++ b/libs/kstd/kstd/vector @@ -551,13 +551,7 @@ namespace kstd kstd::os::panic("[kstd:vector] Tried to reserve more space than theoretically possible."); } - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - uninitialized_move_with_allocator(begin(), new_data, size()); - clear_and_deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; + reallocate_exactly(new_capacity); } //! Resize this vector to contain @p new_size elements. @@ -613,13 +607,7 @@ namespace kstd return; } - auto new_data = allocate_n(m_size); - auto old_size = size(); - uninitialized_move_with_allocator(begin(), new_data, old_size); - clear_and_deallocate(); - std::exchange(m_data, new_data); - m_capacity = old_size; - m_size = old_size; + reallocate_exactly(m_size); } //! Clear the contents of this vector. @@ -636,47 +624,7 @@ namespace kstd //! @return An iterator to the inserted element. constexpr auto insert(const_iterator position, value_type const & value) -> iterator { - auto prefix_size = std::ranges::distance(begin(), position); - auto suffix_size = std::ranges::distance(position, end()); - - if (position == end()) - { - push_back(value); - return begin() + prefix_size; - } - - if (m_capacity == m_size) - { - auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - std::allocator_traits::construct(m_allocator, new_data + prefix_size, value); - uninitialized_move_with_allocator(begin(), new_data, prefix_size); - uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); - destroy_n(begin(), old_size); - deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; - } - else if (&value >= begin() && &value < end()) - { - auto value_copy = value; - auto insert_position = begin() + prefix_size; - std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); - std::ranges::move_backward(insert_position, end() - 1, end()); - *insert_position = std::move(value_copy); - } - else - { - auto insert_position = begin() + prefix_size; - std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); - std::ranges::move_backward(insert_position, end() - 1, end()); - *insert_position = value; - } - - ++m_size; - return begin() + prefix_size; + return do_insert(position, value); } //! Insert an element at a given position. @@ -686,47 +634,7 @@ namespace kstd //! @return An iterator to the inserted element. constexpr auto insert(const_iterator position, value_type && value) -> iterator { - auto prefix_size = std::ranges::distance(begin(), position); - auto suffix_size = std::ranges::distance(position, end()); - - if (position == end()) - { - push_back(std::move(value)); - return begin() + prefix_size; - } - - if (m_capacity == m_size) - { - auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - std::allocator_traits::construct(m_allocator, new_data + prefix_size, std::move(value)); - uninitialized_move_with_allocator(begin(), new_data, prefix_size); - uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); - destroy_n(begin(), old_size); - deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; - } - else if (&value >= begin() && &value < end()) - { - auto value_copy = std::move(value); - auto insert_position = begin() + prefix_size; - std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); - std::ranges::move_backward(insert_position, end() - 1, end()); - *insert_position = std::move(value_copy); - } - else - { - auto insert_position = begin() + prefix_size; - std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); - std::ranges::move_backward(insert_position, end() - 1, end()); - *insert_position = std::move(value); - } - - ++m_size; - return begin() + prefix_size; + return do_insert(position, std::move(value)); } //! Insert the element of a given range into the vector at a given position. @@ -801,39 +709,24 @@ namespace kstd template constexpr auto emplace(const_iterator position, Args &&... args) -> iterator { - auto prefix_size = std::ranges::distance(begin(), position); - auto suffix_size = std::ranges::distance(position, end()); - - if (position == end()) + auto prefix_size = std::ranges::distance(cbegin(), position); + if (position == cend()) { emplace_back(std::forward(args)...); - return begin() + prefix_size; } - - if (m_capacity == m_size) + else if (m_capacity == m_size) { - auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - std::allocator_traits::construct(m_allocator, new_data + prefix_size, - std::forward(args)...); - uninitialized_move_with_allocator(begin(), new_data, prefix_size); - uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); - destroy_n(begin(), old_size); - deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; + reallocate_and_insert(begin() + prefix_size, std::forward(args)...); } else { + auto to_insert = value_type{std::forward(args)...}; auto insert_position = begin() + prefix_size; - std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); - std::ranges::move_backward(insert_position, end() - 1, end()); - *insert_position = value_type{std::forward(args)...}; + shift_back(insert_position); + *insert_position = std::move(to_insert); + ++m_size; } - ++m_size; return begin() + prefix_size; } @@ -884,47 +777,13 @@ namespace kstd //! Append a given element to this vector via copy construction. constexpr auto push_back(value_type const & value) -> void { - if (m_capacity == m_size) - { - auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - std::allocator_traits::construct(m_allocator, new_data + m_size, value); - uninitialized_move_with_allocator(begin(), new_data, size()); - destroy_n(begin(), size()); - deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; - } - else - { - std::allocator_traits::construct(m_allocator, data() + size(), value); - } - ++m_size; + emplace_back(value); } //! Append a given element to this vector via move construction. constexpr auto push_back(value_type && value) -> void { - if (m_capacity == m_size) - { - auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - std::allocator_traits::construct(m_allocator, new_data + m_size, std::move(value)); - uninitialized_move_with_allocator(begin(), new_data, size()); - destroy_n(begin(), size()); - deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; - } - else - { - std::allocator_traits::construct(m_allocator, data() + size(), std::move(value)); - } - ++m_size; + emplace_back(std::move(value)); } //! Append a given element to this vector via direct construction. @@ -933,22 +792,13 @@ namespace kstd { if (m_capacity == m_size) { - auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; - auto new_data = allocate_n(new_capacity); - auto old_size = size(); - std::allocator_traits::construct(m_allocator, new_data + m_size, std::forward(args)...); - uninitialized_move_with_allocator(begin(), new_data, size()); - destroy_n(begin(), size()); - deallocate(); - m_data = new_data; - m_capacity = new_capacity; - m_size = old_size; + reallocate_and_insert(end(), std::forward(args)...); } else { std::allocator_traits::construct(m_allocator, data() + size(), std::forward(args)...); + ++m_size; } - ++m_size; return this->back(); } @@ -1055,6 +905,38 @@ namespace kstd } } + //! Insert an element into this vector at the given position. + //! + //! @param position The position to insert the element at. + //! @param value The value to insert. + template + constexpr auto do_insert(const_iterator position, U && value) + { + auto prefix_size = std::ranges::distance(cbegin(), position); + if (position == cend()) + { + push_back(std::forward(value)); + } + else if (m_capacity == m_size) + { + reallocate_and_insert(begin() + prefix_size, std::forward(value)); + } + else if (&value >= cbegin() && &value < cend()) + { + auto temporary = std::forward(value); + shift_back(begin() + prefix_size); + *(begin() + prefix_size) = std::move(temporary); + ++m_size; + } + else + { + shift_back(begin() + prefix_size); + *(begin() + prefix_size) = std::forward(value); + ++m_size; + } + return begin() + prefix_size; + } + //! Destroy a number of elements in this vector. //! //! @param first The start of the range of the elements to be destroyed. @@ -1105,6 +987,49 @@ namespace kstd } } + //! Reallocate the storage space to be exactly as large as the given size. + constexpr auto reallocate_exactly(size_type new_capacity) -> void + { + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + uninitialized_move_with_allocator(begin(), new_data, old_size); + clear_and_deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size; + } + + //! Shift all elements, starting the given position, one position back inside the vector. + constexpr auto shift_back(iterator starting_at) + { + std::allocator_traits::construct(m_allocator, end(), std::move(*(end() - 1))); + std::ranges::move_backward(starting_at, end() - 1, end()); + } + + //! Reallocate the storage of this vector and insert an element at the given position. + //! + //! @param position The position to insert the element at. + //! @param args The constructor arguments for the inserted element. + template + constexpr auto reallocate_and_insert(iterator position, Args &&... args) + { + auto prefix_size = std::ranges::distance(begin(), position); + auto suffix_size = std::ranges::distance(position, end()); + auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2; + auto new_data = allocate_n(new_capacity); + auto old_size = size(); + + std::allocator_traits::construct(m_allocator, new_data + prefix_size, + std::forward(args)...); + uninitialized_move_with_allocator(begin(), new_data, prefix_size); + uninitialized_move_with_allocator(begin() + prefix_size, new_data + prefix_size + 1, suffix_size); + destroy_n(begin(), old_size); + deallocate(); + m_data = new_data; + m_capacity = new_capacity; + m_size = old_size + 1; + } + //! The allocator used by this vector. [[no_unique_address]] allocator_type m_allocator{}; -- cgit v1.2.3 From 8a11512eb8030de09ed806d8b40b7f54c2f204ab Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 11 May 2026 14:48:46 +0200 Subject: Refactor get_full_path remove recursion --- kernel/src/filesystem/dentry.cpp | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index 1cf8730..a3bd7f4 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -39,20 +40,35 @@ namespace kernel::filesystem return m_name.view(); } - // NOLINTNEXTLINE(misc-no-recursion) auto dentry::get_full_path() const -> kstd::string { - if (m_parent) + if (m_name == "/") { - auto parent_path = m_parent->get_full_path(); - if (parent_path != "/") - { - parent_path += '/'; - } - return parent_path + m_name.view(); + return "/"; } - return m_name.view(); + kstd::vector components; + components.push_back(m_name.view()); + components.push_back("/"); + + auto parent = m_parent; + + while (parent && parent->m_name != "/") + { + components.push_back(parent->m_name.view()); + components.push_back("/"); + parent = parent->get_parent(); + } + + std::ranges::reverse(components); + + kstd::string full_path = ""; + for (auto const & view : components) + { + full_path += view; + } + + return full_path; } auto dentry::add_child(kstd::shared_ptr const & child) -> void -- cgit v1.2.3 From 7ea0aa798e3062dea20a317e2b19d7cf879611ca Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 11 May 2026 18:46:10 +0200 Subject: Refactor dentry --- kernel/include/kernel/filesystem/dentry.hpp | 2 +- kernel/src/filesystem/dentry.cpp | 40 +++++++++++++---------------- kernel/src/filesystem/dentry.tests.cpp | 15 +++-------- 3 files changed, 22 insertions(+), 35 deletions(-) diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index bd62735..1366206 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -34,7 +34,7 @@ namespace kernel::filesystem @param inode The associated inode for this dentry. @param name The name of the dentry (optional). */ - dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & inode, std::string_view name = {}); + dentry(kstd::shared_ptr const & parent, kstd::shared_ptr const & inode, std::string_view name); /** @brief Get the associated inode. diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index a3bd7f4..7617b28 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -23,6 +23,10 @@ namespace kernel::filesystem { kapi::system::panic("[FILESYSTEM] dentry constructed with null inode."); } + if (m_name.empty()) + { + kapi::system::panic("[FILESYSTEM] dentry constructed with empty name."); + } } auto dentry::get_inode() const -> kstd::shared_ptr const & @@ -42,33 +46,25 @@ namespace kernel::filesystem auto dentry::get_full_path() const -> kstd::string { - if (m_name == "/") - { - return "/"; - } - - kstd::vector components; - components.push_back(m_name.view()); - components.push_back("/"); + kstd::string path = m_name; auto parent = m_parent; - - while (parent && parent->m_name != "/") - { - components.push_back(parent->m_name.view()); - components.push_back("/"); - parent = parent->get_parent(); - } - - std::ranges::reverse(components); - - kstd::string full_path = ""; - for (auto const & view : components) + while (parent) { - full_path += view; + auto parent_name = parent->m_name; + if (parent_name == "/") + { + path = "/" + path; + } + else + { + path = parent_name + "/" + path; + } + + parent = parent->m_parent; } - return full_path; + return path; } auto dentry::add_child(kstd::shared_ptr const & child) -> void diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp index c42c405..200262a 100644 --- a/kernel/src/filesystem/dentry.tests.cpp +++ b/kernel/src/filesystem/dentry.tests.cpp @@ -13,7 +13,7 @@ SCENARIO("Dentry construction", "[filesystem][dentry]") GIVEN("A parent dentry and inode") { auto inode = kstd::make_shared(); - auto parent_dentry = kstd::make_shared(nullptr, inode); + auto parent_dentry = kstd::make_shared(nullptr, inode, "parent"); WHEN("constructing a dentry") { @@ -34,18 +34,9 @@ SCENARIO("Dentry construction", "[filesystem][dentry]") WHEN("constructing a dentry with an empty name") { - auto child_dentry = kernel::filesystem::dentry{parent_dentry, inode}; - THEN("the dentry has the correct parent and inode, and an empty name") { - REQUIRE(child_dentry.get_parent() == parent_dentry); - REQUIRE(child_dentry.get_inode() == inode); - REQUIRE(child_dentry.get_name().empty()); - } - - THEN("no flag is set") - { - REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE_THROWS_AS((kernel::filesystem::dentry{parent_dentry, inode, ""}), kernel::tests::cpu::halt); } } @@ -81,7 +72,7 @@ SCENARIO("Dentry child logic", "[filesystem][dentry]") GIVEN("A parent dentry and inode") { auto inode = kstd::make_shared(); - auto parent_dentry = kstd::make_shared(nullptr, inode); + auto parent_dentry = kstd::make_shared(nullptr, inode, "parent"); WHEN("adding child dentries") { -- cgit v1.2.3 From 7683d52861f05566a3b82e24484e367fcdc63ea8 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 11 May 2026 18:52:59 +0200 Subject: Add test for dentry get_full_path --- kernel/src/filesystem/dentry.tests.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp index 200262a..1a5a784 100644 --- a/kernel/src/filesystem/dentry.tests.cpp +++ b/kernel/src/filesystem/dentry.tests.cpp @@ -124,3 +124,27 @@ SCENARIO("Dentry Flag logic", "[filesystem][dentry]") } } } + +SCENARIO("Dentry path resolution", "[filesystem][dentry]") +{ + GIVEN("A dentry with a parent hierarchy") + { + auto root_inode = kstd::make_shared(); + auto root_dentry = kstd::make_shared(nullptr, root_inode, "/"); + + auto home_inode = kstd::make_shared(); + auto home_dentry = kstd::make_shared(root_dentry, home_inode, "home"); + root_dentry->add_child(home_dentry); + + auto user_inode = kstd::make_shared(); + auto user_dentry = kstd::make_shared(home_dentry, user_inode, "user"); + home_dentry->add_child(user_dentry); + + THEN("the full path is constructed correctly") + { + REQUIRE(root_dentry->get_full_path() == "/"); + REQUIRE(home_dentry->get_full_path() == "/home"); + REQUIRE(user_dentry->get_full_path() == "/home/user"); + } + } +} \ No newline at end of file -- cgit v1.2.3 From a4c9b9bdb768ce20c9f9eac353a34598e4a422a9 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 11 May 2026 18:54:27 +0200 Subject: Rename get_full_path to get_absolute_path --- kernel/include/kernel/filesystem/dentry.hpp | 2 +- kernel/src/filesystem/dentry.cpp | 2 +- kernel/src/filesystem/dentry.tests.cpp | 6 +++--- kernel/src/filesystem/vfs.cpp | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index 1366206..226f2b9 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -58,7 +58,7 @@ namespace kernel::filesystem @brief Get the full path of the dentry by traversing up to the root. @return The full path of the dentry. */ - [[nodiscard]] auto get_full_path() const -> kstd::string; + [[nodiscard]] auto get_absolute_path() const -> kstd::string; /** @brief Add a @p child dentry. diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index 7617b28..59fd89e 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -44,7 +44,7 @@ namespace kernel::filesystem return m_name.view(); } - auto dentry::get_full_path() const -> kstd::string + auto dentry::get_absolute_path() const -> kstd::string { kstd::string path = m_name; diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp index 1a5a784..dd01394 100644 --- a/kernel/src/filesystem/dentry.tests.cpp +++ b/kernel/src/filesystem/dentry.tests.cpp @@ -142,9 +142,9 @@ SCENARIO("Dentry path resolution", "[filesystem][dentry]") THEN("the full path is constructed correctly") { - REQUIRE(root_dentry->get_full_path() == "/"); - REQUIRE(home_dentry->get_full_path() == "/home"); - REQUIRE(user_dentry->get_full_path() == "/home/user"); + REQUIRE(root_dentry->get_absolute_path() == "/"); + REQUIRE(home_dentry->get_absolute_path() == "/home"); + REQUIRE(user_dentry->get_absolute_path() == "/home/user"); } } } \ No newline at end of file diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 519550b..1410b2a 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -138,7 +138,7 @@ namespace kernel::filesystem auto vfs::do_mount_internal(kstd::shared_ptr const & mount_point_dentry, kstd::shared_ptr const & fs) -> void { - auto mount_path = mount_point_dentry->get_full_path(); + auto mount_path = mount_point_dentry->get_absolute_path(); // TODO BA-FS26 refactoring, implement dentry lookup to get the parent mount... auto parent_mount = m_mount_table.find_longest_prefix_mount(mount_path.view()); @@ -216,7 +216,7 @@ namespace kernel::filesystem } else if (next_dentry->has_flag(dentry::dentry_flags::mounted)) { - current_mount = m_mount_table.find_exact_mount(next_dentry->get_full_path().view()); + current_mount = m_mount_table.find_exact_mount(next_dentry->get_absolute_path().view()); if (!current_mount) { kapi::system::panic("[FILESYSTEM] mount for dentry with mounted flag not found."); -- cgit v1.2.3 From c958b72922b89fff35c0b8e0bbf21ad42a667022 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 11 May 2026 20:27:01 +0200 Subject: Remove unused include --- kernel/src/filesystem/dentry.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index 59fd89e..a77ce23 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include -- cgit v1.2.3 From df549fb7a3ed3ff9d675da6a90595c78bed7a1f8 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 10 May 2026 13:20:47 +0200 Subject: return const boot_module_registry reference --- kapi/kapi/boot_modules.hpp | 2 +- kernel/kapi/boot_modules.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kapi/kapi/boot_modules.hpp b/kapi/kapi/boot_modules.hpp index 026479d..2a88f74 100644 --- a/kapi/kapi/boot_modules.hpp +++ b/kapi/kapi/boot_modules.hpp @@ -25,7 +25,7 @@ namespace kapi::boot_modules //! Get the boot module registry. //! //! @returns The boot module registry. - auto get_boot_module_registry() -> boot_module_registry &; + auto get_boot_module_registry() -> boot_module_registry const &; } // namespace kapi::boot_modules #endif \ No newline at end of file diff --git a/kernel/kapi/boot_modules.cpp b/kernel/kapi/boot_modules.cpp index 1ad541b..5629b77 100644 --- a/kernel/kapi/boot_modules.cpp +++ b/kernel/kapi/boot_modules.cpp @@ -21,7 +21,7 @@ namespace kapi::boot_modules registry = new_registry; } - auto get_boot_module_registry() -> boot_module_registry & + auto get_boot_module_registry() -> boot_module_registry const & { if (!registry) { -- cgit v1.2.3 From e05b52111d952c626c29d92a862ee0d1dce180f3 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 10 May 2026 13:21:25 +0200 Subject: renaming --- kernel/src/devices/storage/management.cpp | 31 +++++++++++++------------- kernel/src/devices/storage/ram_disk/device.cpp | 4 ++-- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/kernel/src/devices/storage/management.cpp b/kernel/src/devices/storage/management.cpp index 1f2acba..06efc27 100644 --- a/kernel/src/devices/storage/management.cpp +++ b/kernel/src/devices/storage/management.cpp @@ -17,43 +17,42 @@ namespace { - constexpr size_t static MINORS_PER_DEVICE = 16; - constexpr size_t static START_MAJOR = 1; - constinit size_t static next_free_major = START_MAJOR; + constexpr size_t static minors_per_device = 16; + constexpr size_t static start_major = 1; + constinit size_t static next_free_major = start_major; - constinit auto static active_storage_management = std::optional{}; + constinit auto static storage_management = std::optional{}; } // namespace namespace kernel::devices::storage { auto management::init() -> void { - if (active_storage_management) + if (storage_management) { kapi::system::panic("[DEVICES] Storage management has already been initialized."); } - active_storage_management.emplace(management{}); + storage_management.emplace(management{}); - auto current_ram_disk_controller = - kstd::make_shared(&kapi::boot_modules::get_boot_module_registry()); - active_storage_management->add_controller(current_ram_disk_controller); + auto ram_disk_controller = kstd::make_shared(&kapi::boot_modules::get_boot_module_registry()); + storage_management->add_controller(ram_disk_controller); - std::ranges::for_each(active_storage_management->m_controllers, [](auto controller) { controller->probe(); }); + std::ranges::for_each(storage_management->m_controllers, [](auto controller) { controller->probe(); }); } auto management::get() -> management & { - if (!active_storage_management) + if (!storage_management) { kapi::system::panic("[DEVICES] Storage management has not been initialized."); } - return *active_storage_management; + return *storage_management; } auto management::add_controller(kstd::shared_ptr const & controller) -> void { - controller->set_ids(next_free_major++, MINORS_PER_DEVICE); + controller->set_ids(next_free_major++, minors_per_device); m_controllers.push_back(controller); } @@ -82,7 +81,7 @@ namespace kernel::devices::storage auto management::determine_boot_device() -> kstd::shared_ptr { - return device_by_major_minor(START_MAJOR, 0); + return device_by_major_minor(start_major, 0); } } // namespace kernel::devices::storage @@ -90,7 +89,7 @@ namespace kernel::tests::devices::storage::management { auto deinit() -> void { - active_storage_management.reset(); - next_free_major = START_MAJOR; + storage_management.reset(); + next_free_major = start_major; } } // namespace kernel::tests::devices::storage::management diff --git a/kernel/src/devices/storage/ram_disk/device.cpp b/kernel/src/devices/storage/ram_disk/device.cpp index 21b0000..1557204 100644 --- a/kernel/src/devices/storage/ram_disk/device.cpp +++ b/kernel/src/devices/storage/ram_disk/device.cpp @@ -14,11 +14,11 @@ namespace kernel::devices::storage::ram_disk { namespace { - constexpr size_t RAM_DISK_BLOCK_SIZE = 512uz; + constexpr size_t ram_disk_block_size = 512uz; } // namespace device::device(kapi::boot_modules::boot_module const & module, size_t major, size_t minor) - : block_device(major, minor, "ram" + kstd::to_string(minor), RAM_DISK_BLOCK_SIZE) + : block_device(major, minor, "ram" + kstd::to_string(minor), ram_disk_block_size) , m_boot_module(module) {} -- cgit v1.2.3 From 00aa2c8695b81944798010d81d600038e1f1ef3d Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 10 May 2026 19:08:07 +0200 Subject: remove mount_path from mount struct (retrieve path from m_mount_dentry) --- kernel/include/kernel/filesystem/mount.hpp | 8 +--- kernel/src/filesystem/mount.cpp | 14 ++++--- kernel/src/filesystem/mount.tests.cpp | 4 +- kernel/src/filesystem/mount_table.cpp | 7 +++- kernel/src/filesystem/mount_table.tests.cpp | 60 ++++++++++++++++++----------- kernel/src/filesystem/vfs.cpp | 7 +++- 6 files changed, 59 insertions(+), 41 deletions(-) diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index 72855a0..102f660 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -7,8 +7,6 @@ #include #include -#include - namespace kernel::filesystem { /** @@ -27,8 +25,7 @@ namespace kernel::filesystem @param parent_mount The parent mount that this mount is attached beneath. */ mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, - kstd::shared_ptr const & fs, std::string_view mount_path, - kstd::shared_ptr const & parent_mount); + kstd::shared_ptr const & fs, kstd::shared_ptr const & parent_mount); /** @brief Get the dentry where the filesystem is mounted. @@ -48,7 +45,7 @@ namespace kernel::filesystem /** @brief Get the path at which the filesystem is mounted. */ - [[nodiscard]] auto get_mount_path() const -> std::string_view; + [[nodiscard]] auto get_mount_path() const -> kstd::string; /** @brief Get the parent mount that this mount was attached beneath. @@ -56,7 +53,6 @@ namespace kernel::filesystem [[nodiscard]] auto get_parent_mount() const -> kstd::shared_ptr const &; private: - kstd::string m_mount_path; kstd::shared_ptr m_mount_dentry; kstd::shared_ptr m_root_dentry; kstd::shared_ptr m_filesystem{}; diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index 67450b7..c474c8e 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -13,10 +13,8 @@ namespace kernel::filesystem { mount::mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, - kstd::shared_ptr const & fs, std::string_view mount_path, - kstd::shared_ptr const & parent_mount) - : m_mount_path(mount_path) - , m_mount_dentry(mount_dentry) + kstd::shared_ptr const & fs, kstd::shared_ptr const & parent_mount) + : m_mount_dentry(mount_dentry) , m_root_dentry(root_dentry) , m_filesystem(fs) , m_parent_mount(parent_mount) @@ -42,9 +40,13 @@ namespace kernel::filesystem return m_root_dentry; } - auto mount::get_mount_path() const -> std::string_view + auto mount::get_mount_path() const -> kstd::string { - return m_mount_path.view(); + if (m_mount_dentry) + { + return m_mount_dentry->get_full_path(); + } + return "/"; } auto mount::get_parent_mount() const -> kstd::shared_ptr const & diff --git a/kernel/src/filesystem/mount.tests.cpp b/kernel/src/filesystem/mount.tests.cpp index d630464..4b44983 100644 --- a/kernel/src/filesystem/mount.tests.cpp +++ b/kernel/src/filesystem/mount.tests.cpp @@ -21,7 +21,7 @@ SCENARIO("Mount construction", "[filesystem][mount]") WHEN("constructing a mount with the filesystem and root dentry") { - auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, "/", nullptr}; + auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr}; THEN("the mount has the correct filesystem, root dentry, mount dentry, and mount path") { @@ -41,7 +41,7 @@ SCENARIO("Mount construction", "[filesystem][mount]") { THEN("the constructor panics") { - REQUIRE_THROWS_AS((kernel::filesystem::mount{root_dentry, root_dentry, nullptr, "/", nullptr}), + REQUIRE_THROWS_AS((kernel::filesystem::mount{root_dentry, root_dentry, nullptr, nullptr}), kernel::tests::cpu::halt); } } diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 965e83b..b67e05c 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -37,7 +37,9 @@ namespace kernel::filesystem kstd::vector> const & mounts) -> bool { return std::ranges::none_of(mounts, [&](auto const & other) { - return other != candidate && is_strict_prefix(other->get_mount_path(), candidate->get_mount_path()) && + // TODO BA-FS26 really correct? + return other != candidate && + is_strict_prefix(other->get_mount_path().view(), candidate->get_mount_path().view()) && !is_descendant_of(candidate, other); }); } @@ -80,6 +82,7 @@ namespace kernel::filesystem return operation_result::removed; } + // TODO BA-FS26 remove? auto mount_table::find_longest_prefix_mount(std::string_view path) const -> kstd::shared_ptr { kstd::shared_ptr mount_with_longest_prefix = nullptr; @@ -90,7 +93,7 @@ namespace kernel::filesystem auto mp = mount->get_mount_path(); // /a/b/c should match /a/b but not /a/bb or /a/b/c/d, / should match everything - bool is_prefix = path.starts_with(mp) && (mp == "/" || path.size() == mp.size() || path[mp.size()] == '/'); + bool is_prefix = path.starts_with(mp.view()) && (mp == "/" || path.size() == mp.size() || path[mp.size()] == '/'); bool visible = is_visible_mount(mount, m_mounts); if (is_prefix && visible && mp.size() >= best_len) diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp index efacdfe..732d32f 100644 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -40,22 +40,26 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] kernel::filesystem::mount_table table; auto fs1 = kstd::make_shared(); - auto root_inode1 = kstd::make_shared(); - auto root_dentry1 = kstd::make_shared(nullptr, root_inode1, "/"); - auto mount1 = kstd::make_shared(root_dentry1, root_dentry1, fs1, "/", nullptr); + auto root_dentry1 = kstd::make_shared( + nullptr, kstd::make_shared(), "/"); + auto mount_dentry1 = kstd::make_shared( + nullptr, kstd::make_shared(), "/"); + auto mount1 = kstd::make_shared(mount_dentry1, root_dentry1, fs1, nullptr); auto fs2 = kstd::make_shared(); - auto root_inode2 = kstd::make_shared(); - auto root_dentry2 = kstd::make_shared(nullptr, root_inode2, "/"); - auto mount2 = kstd::make_shared(root_dentry1, root_dentry2, fs2, "/mnt", nullptr); + auto root_dentry2 = kstd::make_shared( + nullptr, kstd::make_shared(), "/"); + auto mount_dentry2 = kstd::make_shared( + nullptr, kstd::make_shared(), "/mnt"); + auto mount2 = kstd::make_shared(mount_dentry2, root_dentry2, fs2, nullptr); table.add_mount(mount1); table.add_mount(mount2); THEN("dentry flags are set correctly for mounted dentries") { - REQUIRE(root_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); - REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE(mount_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE(mount_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } THEN("finding mounts by longest prefix returns the correct mount") @@ -97,14 +101,18 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] kernel::filesystem::mount_table table; auto fs1 = kstd::make_shared(); - auto root_inode1 = kstd::make_shared(); - auto root_dentry1 = kstd::make_shared(nullptr, root_inode1, "/"); - auto mount1 = kstd::make_shared(root_dentry1, root_dentry1, fs1, "/", nullptr); + auto root_dentry1 = kstd::make_shared( + nullptr, kstd::make_shared(), "/"); + auto mount_dentry1 = kstd::make_shared( + nullptr, kstd::make_shared(), "/"); + auto mount1 = kstd::make_shared(mount_dentry1, root_dentry1, fs1, nullptr); auto fs2 = kstd::make_shared(); - auto root_inode2 = kstd::make_shared(); - auto root_dentry2 = kstd::make_shared(nullptr, root_inode2, "/"); - auto mount2 = kstd::make_shared(root_dentry1, root_dentry2, fs2, "/", mount1); + auto root_dentry2 = kstd::make_shared( + nullptr, kstd::make_shared(), "/"); + auto mount_dentry2 = kstd::make_shared( + nullptr, kstd::make_shared(), "/"); + auto mount2 = kstd::make_shared(mount_dentry2, root_dentry2, fs2, nullptr); table.add_mount(mount1); table.add_mount(mount2); @@ -136,19 +144,25 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] kernel::filesystem::mount_table table; auto fs1 = kstd::make_shared(); - auto root_inode1 = kstd::make_shared(); - auto root_dentry1 = kstd::make_shared(nullptr, root_inode1, "/"); - auto mount1 = kstd::make_shared(root_dentry1, root_dentry1, fs1, "/", nullptr); + auto root_dentry1 = kstd::make_shared( + nullptr, kstd::make_shared(), "/"); + auto mount_dentry1 = kstd::make_shared( + nullptr, kstd::make_shared(), "/"); + auto mount1 = kstd::make_shared(mount_dentry1, root_dentry1, fs1, nullptr); auto fs2 = kstd::make_shared(); - auto root_inode2 = kstd::make_shared(); - auto root_dentry2 = kstd::make_shared(nullptr, root_inode2, "/"); - auto mount2 = kstd::make_shared(root_dentry1, root_dentry2, fs2, "/mnt", mount1); + auto root_dentry2 = kstd::make_shared( + nullptr, kstd::make_shared(), "/"); + auto mount_dentry2 = kstd::make_shared( + mount_dentry1, kstd::make_shared(), "mnt"); + auto mount2 = kstd::make_shared(mount_dentry2, root_dentry2, fs2, mount1); auto fs3 = kstd::make_shared(); - auto root_inode3 = kstd::make_shared(); - auto root_dentry3 = kstd::make_shared(nullptr, root_inode3, "/"); - auto mount3 = kstd::make_shared(root_dentry2, root_dentry3, fs3, "/mnt/submnt", mount2); + auto root_dentry3 = kstd::make_shared( + nullptr, kstd::make_shared(), "/"); + auto mount_dentry3 = kstd::make_shared( + mount_dentry2, kstd::make_shared(), "submnt"); + auto mount3 = kstd::make_shared(mount_dentry3, root_dentry3, fs3, mount2); table.add_mount(mount1); table.add_mount(mount2); diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 1410b2a..ea060a9 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -45,7 +45,7 @@ namespace kernel::filesystem root_fs->mount(nullptr); auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode(), "/"); - m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, "/", nullptr)); + m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, nullptr)); // Mount devfs at /dev in rootfs (temporary, will be shadowed) auto device_fs = kstd::make_shared(); @@ -142,9 +142,12 @@ namespace kernel::filesystem // TODO BA-FS26 refactoring, implement dentry lookup to get the parent mount... auto parent_mount = m_mount_table.find_longest_prefix_mount(mount_path.view()); + // auto parent_mount_dentry = mount_point_dentry->get_parent_mounted_dentry(); + // auto parent_mount = m_mount_table.find_exact_mount(parent_mount_dentry->get_full_path()); + auto new_fs_root = kstd::make_shared(mount_point_dentry->get_parent(), fs->root_inode(), mount_point_dentry->get_name()); - auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, mount_path.view(), parent_mount); + auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, parent_mount); m_mount_table.add_mount(new_mount); } -- cgit v1.2.3 From feab4cd81f2bbc89e55353a54df2575b9c21b514 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Sun, 10 May 2026 20:07:51 +0200 Subject: Add method that returns the next ancestor with mount flag set --- kernel/include/kernel/filesystem/dentry.hpp | 6 ++++++ kernel/src/filesystem/dentry.cpp | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index 226f2b9..1d99a25 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -60,6 +60,12 @@ namespace kernel::filesystem */ [[nodiscard]] auto get_absolute_path() const -> kstd::string; + /** + @brief traverse parent dentries until dentry with mount flag is found. + @return The found dentry. + */ + [[nodiscard]] auto get_ancestor_with_mount_flag() const -> kstd::shared_ptr; + /** @brief Add a @p child dentry. @param child The child dentry to add. diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index a77ce23..6945a27 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -66,6 +66,16 @@ namespace kernel::filesystem return path; } + auto dentry::get_ancestor_with_mount_flag() const -> kstd::shared_ptr + { + auto parent = m_parent; + while (parent && !parent->has_flag(dentry_flags::mounted)) + { + parent = parent->get_parent(); + } + return parent; + } + auto dentry::add_child(kstd::shared_ptr const & child) -> void { m_children.push_back(child); -- cgit v1.2.3 From 85fd2e3b4da7d84a9357b35bde740b780fe0cb72 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Sun, 10 May 2026 21:49:20 +0200 Subject: Rename root_dentry() to get_root_dentry() --- kernel/include/kernel/filesystem/mount.hpp | 2 +- kernel/src/filesystem/mount.cpp | 2 +- kernel/src/filesystem/mount.tests.cpp | 2 +- kernel/src/filesystem/vfs.cpp | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index 102f660..af5d08b 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -35,7 +35,7 @@ namespace kernel::filesystem /** @brief Get the root dentry of the mounted filesystem. */ - [[nodiscard]] auto root_dentry() const -> kstd::shared_ptr const &; + [[nodiscard]] auto get_root_dentry() const -> kstd::shared_ptr const &; /** @brief Get the filesystem instance being mounted. diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index c474c8e..ebacf32 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -35,7 +35,7 @@ namespace kernel::filesystem return m_filesystem; } - auto mount::root_dentry() const -> kstd::shared_ptr const & + auto mount::get_root_dentry() const -> kstd::shared_ptr const & { return m_root_dentry; } diff --git a/kernel/src/filesystem/mount.tests.cpp b/kernel/src/filesystem/mount.tests.cpp index 4b44983..58e9bab 100644 --- a/kernel/src/filesystem/mount.tests.cpp +++ b/kernel/src/filesystem/mount.tests.cpp @@ -26,7 +26,7 @@ SCENARIO("Mount construction", "[filesystem][mount]") THEN("the mount has the correct filesystem, root dentry, mount dentry, and mount path") { REQUIRE(mount.get_filesystem() == fs); - REQUIRE(mount.root_dentry() == root_dentry); + REQUIRE(mount.get_root_dentry() == root_dentry); REQUIRE(mount.get_mount_dentry() == root_dentry); REQUIRE(mount.get_mount_path() == "/"); } diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index ea060a9..2b8c3bd 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -164,7 +164,7 @@ namespace kernel::filesystem kapi::system::panic("[FILESYSTEM] no root mount found."); } - auto current_dentry = current_mount->root_dentry(); + auto current_dentry = current_mount->get_root_dentry(); auto path_parts = path::split(path); kstd::vector path_parts_vector(path_parts.begin(), path_parts.end()); @@ -186,7 +186,7 @@ namespace kernel::filesystem { auto parent_dentry = current_dentry->get_parent(); - if (current_dentry == current_mount->root_dentry()) + if (current_dentry == current_mount->get_root_dentry()) { if (current_mount->get_mount_path() == "/") { @@ -225,7 +225,7 @@ namespace kernel::filesystem kapi::system::panic("[FILESYSTEM] mount for dentry with mounted flag not found."); } - next_dentry = current_mount->root_dentry(); + next_dentry = current_mount->get_root_dentry(); } if (next_dentry->get_inode()->is_symbolic_link()) @@ -248,7 +248,7 @@ namespace kernel::filesystem if (path::is_valid_absolute_path(symbolic_link_path)) { current_mount = m_mount_table.find_exact_mount("/"); - current_dentry = current_mount->root_dentry(); + current_dentry = current_mount->get_root_dentry(); } continue; } -- cgit v1.2.3 From 636551d03ac725e015dd88631d531c02067ed3de Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Sun, 10 May 2026 21:51:11 +0200 Subject: Create first draft of get_parent_mount without find_longest_prefix_mount --- kernel/src/filesystem/vfs.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 2b8c3bd..e7f9015 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -138,13 +138,13 @@ namespace kernel::filesystem auto vfs::do_mount_internal(kstd::shared_ptr const & mount_point_dentry, kstd::shared_ptr const & fs) -> void { - auto mount_path = mount_point_dentry->get_absolute_path(); - // TODO BA-FS26 refactoring, implement dentry lookup to get the parent mount... - auto parent_mount = m_mount_table.find_longest_prefix_mount(mount_path.view()); - - // auto parent_mount_dentry = mount_point_dentry->get_parent_mounted_dentry(); - // auto parent_mount = m_mount_table.find_exact_mount(parent_mount_dentry->get_full_path()); - + auto parent_mount_dentry = mount_point_dentry->get_ancestor_with_mount_flag(); + kstd::shared_ptr parent_mount = nullptr; + if (parent_mount_dentry) + { + parent_mount = m_mount_table.find_exact_mount(parent_mount_dentry->get_absolute_path().view()); + } + auto new_fs_root = kstd::make_shared(mount_point_dentry->get_parent(), fs->root_inode(), mount_point_dentry->get_name()); auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, parent_mount); -- cgit v1.2.3 From 763227e31adf924a5dfe3139db158e26162294a0 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Sun, 10 May 2026 21:58:54 +0200 Subject: Remove find_longest_prefix_mount --- kernel/include/kernel/filesystem/mount_table.hpp | 7 ----- kernel/src/filesystem/mount_table.cpp | 24 --------------- kernel/src/filesystem/mount_table.tests.cpp | 37 ------------------------ 3 files changed, 68 deletions(-) diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp index 8e57d9e..59b9503 100644 --- a/kernel/include/kernel/filesystem/mount_table.hpp +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -38,13 +38,6 @@ namespace kernel::filesystem */ [[nodiscard]] auto remove_mount(std::string_view path) -> operation_result; - /** - @brief Finds the mount with the longest prefix matching the given @p path. - @param path The path to match against the mount paths in the table. - @return A pointer to the mount with the longest matching prefix, or a null pointer if no mount matches the path. - */ - [[nodiscard]] auto find_longest_prefix_mount(std::string_view path) const -> kstd::shared_ptr; - /** @brief Finds the mount with the exact mount path matching the given @p path. @param path The path to match against the mount paths in the table. diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index b67e05c..30a94f4 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -82,30 +82,6 @@ namespace kernel::filesystem return operation_result::removed; } - // TODO BA-FS26 remove? - auto mount_table::find_longest_prefix_mount(std::string_view path) const -> kstd::shared_ptr - { - kstd::shared_ptr mount_with_longest_prefix = nullptr; - std::size_t best_len = 0; - - for (auto const & mount : m_mounts) - { - auto mp = mount->get_mount_path(); - - // /a/b/c should match /a/b but not /a/bb or /a/b/c/d, / should match everything - bool is_prefix = path.starts_with(mp.view()) && (mp == "/" || path.size() == mp.size() || path[mp.size()] == '/'); - bool visible = is_visible_mount(mount, m_mounts); - - if (is_prefix && visible && mp.size() >= best_len) - { - mount_with_longest_prefix = mount; - best_len = mp.size(); - } - } - - return mount_with_longest_prefix; - } - auto mount_table::find_exact_mount(std::string_view path) const -> kstd::shared_ptr { auto reversed_mounts = std::ranges::reverse_view(m_mounts); diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp index 732d32f..80772ca 100644 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -19,12 +19,6 @@ SCENARIO("Mount table construction", "[filesystem][mount_table]") { kernel::filesystem::mount_table table; - THEN("finding any mount returns null") - { - REQUIRE(table.find_longest_prefix_mount("/") == nullptr); - REQUIRE(table.find_longest_prefix_mount("/any/path") == nullptr); - } - THEN("removing any mount returns mount_not_found") { REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::mount_not_found); @@ -62,15 +56,6 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] REQUIRE(mount_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); } - THEN("finding mounts by longest prefix returns the correct mount") - { - REQUIRE(table.find_longest_prefix_mount("/") == mount1); - REQUIRE(table.find_longest_prefix_mount("/file") == mount1); - REQUIRE(table.find_longest_prefix_mount("/mnt") == mount2); - REQUIRE(table.find_longest_prefix_mount("/mnt/file") == mount2); - REQUIRE(table.find_longest_prefix_mount("/other") == mount1); - } - THEN("finding mounts by exact valid path returns the correct mount") { REQUIRE(table.find_exact_mount("/") == mount1); @@ -87,7 +72,6 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] { REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::removed); REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); - REQUIRE(table.find_longest_prefix_mount("/mnt") == mount1); } THEN("removing a mount that does not exist returns mount_not_found") @@ -117,15 +101,6 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] table.add_mount(mount1); table.add_mount(mount2); - THEN("finding mounts by longest prefix returns the correct mount") - { - REQUIRE(table.find_longest_prefix_mount("/") == mount2); - REQUIRE(table.find_longest_prefix_mount("/file") == mount2); - REQUIRE(table.find_longest_prefix_mount("/mnt") == mount2); - REQUIRE(table.find_longest_prefix_mount("/mnt/file") == mount2); - REQUIRE(table.find_longest_prefix_mount("/other") == mount2); - } - THEN("finding mounts by exact valid path returns the correct mount") { REQUIRE(table.find_exact_mount("/") == mount2); @@ -135,7 +110,6 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] { REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::removed); REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); - REQUIRE(table.find_longest_prefix_mount("/") == mount1); } } @@ -168,16 +142,6 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] table.add_mount(mount2); table.add_mount(mount3); - THEN("finding mounts by path returns the correct mount based on longest prefix") - { - REQUIRE(table.find_longest_prefix_mount("/") == mount1); - REQUIRE(table.find_longest_prefix_mount("/file") == mount1); - REQUIRE(table.find_longest_prefix_mount("/mnt") == mount2); - REQUIRE(table.find_longest_prefix_mount("/mnt/file") == mount2); - REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == mount3); - REQUIRE(table.find_longest_prefix_mount("/other") == mount1); - } - THEN("removing a mount with child mounts returns has_child_mounts") { REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::has_child_mounts); @@ -188,7 +152,6 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] { REQUIRE(table.remove_mount("/mnt/submnt") == kernel::filesystem::mount_table::operation_result::removed); REQUIRE_FALSE(root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); - REQUIRE(table.find_longest_prefix_mount("/mnt/submnt") == mount2); } } } -- cgit v1.2.3 From 5d72c256d4e2b8a9d2fd70e5a27e883a0f733e50 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 11 May 2026 20:48:03 +0200 Subject: Add is_mount_root flag to dentry and use in find_mount_root_dentry --- kernel/include/kernel/filesystem/dentry.hpp | 7 ++++--- kernel/src/filesystem/dentry.cpp | 4 ++-- kernel/src/filesystem/dentry.tests.cpp | 14 +++++++------- kernel/src/filesystem/mount_table.cpp | 9 ++++++--- kernel/src/filesystem/mount_table.tests.cpp | 10 +++++----- kernel/src/filesystem/vfs.cpp | 4 ++-- 6 files changed, 26 insertions(+), 22 deletions(-) diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index 1d99a25..7eef693 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -24,7 +24,8 @@ namespace kernel::filesystem */ enum class dentry_flags : uint32_t { - mounted = 1 << 15 + is_mount_point = 1 << 0, + is_mount_root = 1 << 1, }; /** @@ -61,10 +62,10 @@ namespace kernel::filesystem [[nodiscard]] auto get_absolute_path() const -> kstd::string; /** - @brief traverse parent dentries until dentry with mount flag is found. + @brief traverse parent dentries until dentry with is_mount_root flag is found. @return The found dentry. */ - [[nodiscard]] auto get_ancestor_with_mount_flag() const -> kstd::shared_ptr; + [[nodiscard]] auto find_mount_root_dentry() const -> kstd::shared_ptr; /** @brief Add a @p child dentry. diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index 6945a27..c21771b 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -66,10 +66,10 @@ namespace kernel::filesystem return path; } - auto dentry::get_ancestor_with_mount_flag() const -> kstd::shared_ptr + auto dentry::find_mount_root_dentry() const -> kstd::shared_ptr { auto parent = m_parent; - while (parent && !parent->has_flag(dentry_flags::mounted)) + while (parent && !parent->has_flag(dentry_flags::is_mount_root)) { parent = parent->get_parent(); } diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp index dd01394..e9ecbc8 100644 --- a/kernel/src/filesystem/dentry.tests.cpp +++ b/kernel/src/filesystem/dentry.tests.cpp @@ -28,7 +28,7 @@ SCENARIO("Dentry construction", "[filesystem][dentry]") THEN("no flag is set") { - REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); } } @@ -53,7 +53,7 @@ SCENARIO("Dentry construction", "[filesystem][dentry]") THEN("no flag is set") { - REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE_FALSE(child_dentry.has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); } } @@ -104,22 +104,22 @@ SCENARIO("Dentry Flag logic", "[filesystem][dentry]") WHEN("setting a flag") { - dentry.set_flag(kernel::filesystem::dentry::dentry_flags::mounted); + dentry.set_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point); THEN("the flag is set") { - REQUIRE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); } } WHEN("unsetting a flag") { - dentry.set_flag(kernel::filesystem::dentry::dentry_flags::mounted); - dentry.unset_flag(kernel::filesystem::dentry::dentry_flags::mounted); + dentry.set_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point); + dentry.unset_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point); THEN("the flag is unset") { - REQUIRE_FALSE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE_FALSE(dentry.has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); } } } diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 30a94f4..74c18ca 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -7,7 +7,6 @@ #include #include -#include #include #include @@ -56,7 +55,11 @@ namespace kernel::filesystem m_mounts.push_back(mount); if (auto mount_dentry = mount->get_mount_dentry()) { - mount_dentry->set_flag(dentry::dentry_flags::mounted); + mount_dentry->set_flag(dentry::dentry_flags::is_mount_point); + } + if (auto root_dentry = mount->get_root_dentry()) + { + root_dentry->set_flag(dentry::dentry_flags::is_mount_root); } } @@ -77,7 +80,7 @@ namespace kernel::filesystem return operation_result::has_child_mounts; } - mount->get_mount_dentry()->unset_flag(dentry::dentry_flags::mounted); + mount->get_mount_dentry()->unset_flag(dentry::dentry_flags::is_mount_point); m_mounts.erase(std::ranges::find(m_mounts, mount)); return operation_result::removed; } diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp index 80772ca..4ae8711 100644 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -52,8 +52,8 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("dentry flags are set correctly for mounted dentries") { - REQUIRE(mount_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); - REQUIRE(mount_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE(mount_dentry1->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); + REQUIRE(mount_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); } THEN("finding mounts by exact valid path returns the correct mount") @@ -71,7 +71,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("removing a mount that has no child mounts succeeds") { REQUIRE(table.remove_mount("/mnt") == kernel::filesystem::mount_table::operation_result::removed); - REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); } THEN("removing a mount that does not exist returns mount_not_found") @@ -109,7 +109,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("removing the topmost mount with the same path succeeds") { REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::removed); - REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE_FALSE(root_dentry2->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); } } @@ -151,7 +151,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("removing a leaf mount succeeds") { REQUIRE(table.remove_mount("/mnt/submnt") == kernel::filesystem::mount_table::operation_result::removed); - REQUIRE_FALSE(root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::mounted)); + REQUIRE_FALSE(root_dentry3->has_flag(kernel::filesystem::dentry::dentry_flags::is_mount_point)); } } } diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index e7f9015..ee7c262 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -138,7 +138,7 @@ namespace kernel::filesystem auto vfs::do_mount_internal(kstd::shared_ptr const & mount_point_dentry, kstd::shared_ptr const & fs) -> void { - auto parent_mount_dentry = mount_point_dentry->get_ancestor_with_mount_flag(); + auto parent_mount_dentry = mount_point_dentry->find_mount_root_dentry(); kstd::shared_ptr parent_mount = nullptr; if (parent_mount_dentry) { @@ -217,7 +217,7 @@ namespace kernel::filesystem next_dentry = kstd::make_shared(current_dentry, found_inode, part.view()); current_dentry->add_child(next_dentry); } - else if (next_dentry->has_flag(dentry::dentry_flags::mounted)) + else if (next_dentry->has_flag(dentry::dentry_flags::is_mount_point)) { current_mount = m_mount_table.find_exact_mount(next_dentry->get_absolute_path().view()); if (!current_mount) -- cgit v1.2.3 From 5bac7cc7cc37d137b633afd3910f251a22818b80 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 11 May 2026 22:49:22 +0200 Subject: fix build after rebase --- kernel/src/filesystem/mount.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index ebacf32..749c86a 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -44,7 +44,7 @@ namespace kernel::filesystem { if (m_mount_dentry) { - return m_mount_dentry->get_full_path(); + return m_mount_dentry->get_absolute_path(); } return "/"; } -- cgit v1.2.3 From cb86b1fdb4656d37937a27343a4971ee5896bd3a Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 12 May 2026 08:51:17 +0200 Subject: remove unused include --- kernel/src/main.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index bfb731a..22d2b1e 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,6 +1,4 @@ #include -#include -#include #include #include #include @@ -15,7 +13,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From ac5213633721fcf0e72da814d7ef70c51090c3f9 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 12 May 2026 08:51:49 +0200 Subject: refactoring, simplify code --- kernel/src/filesystem/mount_table.cpp | 51 +++++++---------------------------- 1 file changed, 10 insertions(+), 41 deletions(-) diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 74c18ca..daef93e 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -12,38 +12,6 @@ namespace kernel::filesystem { - namespace - { - auto is_descendant_of(kstd::shared_ptr const & candidate, kstd::shared_ptr const & ancestor) -> bool - { - for (auto current = candidate; current; current = current->get_parent_mount()) - { - if (current == ancestor) - { - return true; - } - } - - return false; - } - - auto is_strict_prefix(std::string_view prefix, std::string_view path) -> bool - { - return prefix != "/" && path.starts_with(prefix) && path.size() > prefix.size() && path[prefix.size()] == '/'; - } - - auto is_visible_mount(kstd::shared_ptr const & candidate, - kstd::vector> const & mounts) -> bool - { - return std::ranges::none_of(mounts, [&](auto const & other) { - // TODO BA-FS26 really correct? - return other != candidate && - is_strict_prefix(other->get_mount_path().view(), candidate->get_mount_path().view()) && - !is_descendant_of(candidate, other); - }); - } - } // namespace - auto mount_table::has_child_mounts(kstd::shared_ptr const & parent_mount) const -> bool { return std::ranges::any_of( @@ -53,6 +21,7 @@ namespace kernel::filesystem void mount_table::add_mount(kstd::shared_ptr const & mount) { m_mounts.push_back(mount); + if (auto mount_dentry = mount->get_mount_dentry()) { mount_dentry->set_flag(dentry::dentry_flags::is_mount_point); @@ -65,11 +34,11 @@ namespace kernel::filesystem auto mount_table::remove_mount(std::string_view path) -> operation_result { - auto mount_it = std::ranges::find_if(std::ranges::reverse_view(m_mounts), [&](auto const & mount) { - return mount->get_mount_path() == path && is_visible_mount(mount, m_mounts); - }); + auto mount_range = + std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }); + auto mount_it = mount_range.begin(); - if (mount_it == std::ranges::reverse_view(m_mounts).end()) + if (mount_it == m_mounts.end()) { return operation_result::mount_not_found; } @@ -81,15 +50,15 @@ namespace kernel::filesystem } mount->get_mount_dentry()->unset_flag(dentry::dentry_flags::is_mount_point); - m_mounts.erase(std::ranges::find(m_mounts, mount)); + m_mounts.erase(mount_it); return operation_result::removed; } auto mount_table::find_exact_mount(std::string_view path) const -> kstd::shared_ptr { - auto reversed_mounts = std::ranges::reverse_view(m_mounts); - auto mount_it = - std::ranges::find_if(reversed_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }); - return (mount_it != reversed_mounts.end()) ? *mount_it : nullptr; + auto mount_range = + std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }); + auto mount_it = mount_range.begin(); + return (mount_it != m_mounts.end()) ? *mount_it : nullptr; } } // namespace kernel::filesystem \ No newline at end of file -- cgit v1.2.3 From 91821da0110e05724640903434c3d85fc3d02466 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Tue, 12 May 2026 12:00:40 +0200 Subject: if the boot_root_fs contains a /dev directory, vfs mounts the devfs onto the existing directory --- arch/x86_64/support/modules/README.md | 3 ++ arch/x86_64/support/modules/ext2_2KB_fs.img | 2 +- kernel/include/kernel/filesystem/vfs.hpp | 3 ++ kernel/src/filesystem/vfs.cpp | 40 +++++++++++++++------- kernel/src/filesystem/vfs.tests.cpp | 17 +++++++++ .../filesystem/test_assets/ext2_2KB_fs.img | 2 +- 6 files changed, 52 insertions(+), 15 deletions(-) diff --git a/arch/x86_64/support/modules/README.md b/arch/x86_64/support/modules/README.md index 1a29ce5..6d235a7 100644 --- a/arch/x86_64/support/modules/README.md +++ b/arch/x86_64/support/modules/README.md @@ -40,6 +40,9 @@ The ext2_4KB_fs image is intentionally fragmented, as some files were created an (ext2 filesystem 4KB Block size) . ./lost+found +./dev +./dev/image_1.txt +./dev/image_2.txt ./snake_1.txt ./snake_2.txt ./petting_zoo/goat_1.txt diff --git a/arch/x86_64/support/modules/ext2_2KB_fs.img b/arch/x86_64/support/modules/ext2_2KB_fs.img index 8327022..7f297f0 100644 --- a/arch/x86_64/support/modules/ext2_2KB_fs.img +++ b/arch/x86_64/support/modules/ext2_2KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1d102f2e40083613060d43b2b32d31031137bbef99761a2d1bf4d38e155adb7 +oid sha256:6d9e872916e7d9107b321cc007e151899d5f19400a694666c0b24d482aef61ca size 5242880 diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 7e66fb7..b5053a2 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -1,6 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP #define TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP +#include "kernel/filesystem/devfs/filesystem.hpp" #include #include #include @@ -80,6 +81,8 @@ namespace kernel::filesystem auto do_mount_internal(kstd::shared_ptr const & mount_point_dentry, kstd::shared_ptr const & fs) -> void; + auto graft_persistent_device_fs(kstd::shared_ptr const & device_fs) -> void; + mount_table m_mount_table; }; } // namespace kernel::filesystem diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index ee7c262..535f898 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -41,36 +41,35 @@ namespace kernel::filesystem auto vfs::init_internal() -> void { + // mount rootfs at / auto root_fs = kstd::make_shared(); root_fs->mount(nullptr); auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode(), "/"); m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, nullptr)); - // Mount devfs at /dev in rootfs (temporary, will be shadowed) + // mount devfs at /dev (inside rootfs, temporary, will be shadowed) auto device_fs = kstd::make_shared(); device_fs->mount(nullptr); - auto dev_mount_point_dentry = resolve_path("/dev"); - if (!dev_mount_point_dentry) + + if (auto dev_mount_point_dentry = resolve_path("/dev")) + { + do_mount_internal(dev_mount_point_dentry, device_fs); + } + else { kapi::system::panic("[FILESYSTEM] failed to resolve /dev for initial devfs mount."); } - do_mount_internal(dev_mount_point_dentry, device_fs); - // Mount boot filesystem at / (will shadow rootfs) + // mount boot fs at / (shadows rootfs), re-graft devfs if (auto boot_device_dentry = resolve_path("/dev/ram0")) { if (auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device_dentry->get_inode())) { - do_mount_internal(root_fs_root_dentry, boot_root_fs); - - // Resolve / to get the boot root dentry - if (auto boot_root_dentry = resolve_path("/")) + if (auto root_dentry = resolve_path("/")) { - auto dev_dentry = kstd::make_shared(boot_root_dentry, device_fs->root_inode(), "dev"); - boot_root_dentry->add_child(dev_dentry); - - do_mount_internal(dev_dentry, device_fs); + do_mount_internal(root_dentry, boot_root_fs); + graft_persistent_device_fs(device_fs); } } } @@ -151,6 +150,21 @@ namespace kernel::filesystem m_mount_table.add_mount(new_mount); } + auto vfs::graft_persistent_device_fs(kstd::shared_ptr const & device_fs) -> void + { + if (auto new_root_dentry = resolve_path("/")) + { + auto dev_dentry = new_root_dentry->find_child("dev"); + if (!dev_dentry) + { + dev_dentry = kstd::make_shared(new_root_dentry, device_fs->root_inode(), "dev"); + new_root_dentry->add_child(dev_dentry); + } + + do_mount_internal(dev_dentry, device_fs); + } + } + auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr { if (!path::is_valid_absolute_path(path)) diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 8e4cb70..add96aa 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -62,6 +62,23 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS } } + GIVEN("a real image file containing a /dev directory") + { + REQUIRE(std::filesystem::exists(image_path_2)); + REQUIRE_NOTHROW(setup_modules_from_img_and_init_vfs({"test_img_module_2"}, {image_path_2})); + + THEN("vfs hides the image's /dev behind the devfs mount") + { + auto & vfs = kernel::filesystem::vfs::get(); + + auto image_1 = vfs.open("/dev/image_1.txt"); + REQUIRE(image_1 == nullptr); + + auto dev = vfs.open("/dev/ram0"); + REQUIRE(dev != nullptr); + } + } + GIVEN("three real image files") { REQUIRE(std::filesystem::exists(image_path_1)); diff --git a/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img b/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img index 8327022..7f297f0 100644 --- a/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img +++ b/kernel/src/test_support/filesystem/test_assets/ext2_2KB_fs.img @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a1d102f2e40083613060d43b2b32d31031137bbef99761a2d1bf4d38e155adb7 +oid sha256:6d9e872916e7d9107b321cc007e151899d5f19400a694666c0b24d482aef61ca size 5242880 -- cgit v1.2.3 From 988977b80cd118749b6b813e0909f4607a4f27fe Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Tue, 12 May 2026 13:50:56 +0200 Subject: Determine ext2 inode size depending on revision level, add const to several methods --- .../include/kernel/filesystem/ext2/filesystem.hpp | 22 ++++++++++++++++------ kernel/include/kernel/filesystem/ext2/inode.hpp | 12 +++++++++--- kernel/src/filesystem/ext2/filesystem.cpp | 20 +++++++++++++------- kernel/src/filesystem/ext2/inode.cpp | 20 ++++++++++++++++---- 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index d22433f..18ef372 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -25,6 +25,9 @@ namespace kernel::filesystem::ext2 constexpr size_t inline superblock_offset = base_block_size; constexpr uint16_t inline magic_number = 0xEF53; + constexpr uint32_t inline good_old_revision = 0; + constexpr uint32_t inline dynamic_revision = 1; + constexpr uint32_t inline root_inode_number = 2; constexpr size_t inline direct_block_count = 12; @@ -64,7 +67,13 @@ namespace kernel::filesystem::ext2 @brief Gets the size of a block in the filesystem. @return The size of a block in bytes. */ - auto get_block_size() -> size_t; + [[nodiscard]] auto get_block_size() const -> size_t; + + /** + @brief Gets the revision level of the filesystem. + @return The revision level. + */ + [[nodiscard]] auto get_revision_level() const -> size_t; /** @brief Maps an inode block index to a global block number. @@ -72,14 +81,15 @@ namespace kernel::filesystem::ext2 @param data The inode data. @return The global block number. */ - auto map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t; + [[nodiscard]] auto map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) const + -> uint32_t; private: - auto read_inode(uint32_t inode_number) -> kstd::shared_ptr; - auto read_block_number_at_index(uint32_t block_number, uint32_t index) -> uint32_t; + [[nodiscard]] auto read_inode(uint32_t inode_number) const -> kstd::shared_ptr; + [[nodiscard]] auto read_block_number_at_index(uint32_t block_number, uint32_t index) const -> uint32_t; - auto get_inode_size() -> size_t; - auto get_inode_block_count(inode_data const & data) -> uint32_t; + [[nodiscard]] auto get_inode_size() const -> size_t; + [[nodiscard]] auto get_inode_block_count(inode_data const & data) const -> uint32_t; superblock m_superblock{}; kstd::vector m_block_group_descriptors; diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index b8f892a..000a5d8 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -20,7 +20,7 @@ namespace kernel::filesystem::ext2 { uint16_t mode; uint16_t uid; - uint32_t size; // TODO BA-FS26 signed? + uint32_t size; uint32_t atime; uint32_t ctime; uint32_t mtime; @@ -45,7 +45,7 @@ namespace kernel::filesystem::ext2 @param fs The ext2 filesystem that this inode belongs to. @param data The data associated with this inode, read from the disk. */ - explicit inode(filesystem * fs, inode_data const & data); + explicit inode(filesystem const * fs, inode_data const & data); /** @brief Reads from the ext2 inode into a @p buffer, starting at the specified @p offset and for a given @p size. @@ -90,8 +90,14 @@ namespace kernel::filesystem::ext2 */ [[nodiscard]] auto is_symbolic_link() const -> bool override; + /** + @brief Get the size of the file represented by this inode. + @return The size of the file in bytes. + */ + [[nodiscard]] auto get_size() const -> size_t; + private: - filesystem * m_filesystem; + filesystem const * m_filesystem; inode_data m_data{}; }; } // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 47e54fe..aaa50c7 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -89,7 +89,7 @@ namespace kernel::filesystem::ext2 return nullptr; } - auto filesystem::read_inode(uint32_t inode_number) -> kstd::shared_ptr + auto filesystem::read_inode(uint32_t inode_number) const -> kstd::shared_ptr { auto const block_size = get_block_size(); auto const inodes_per_group = m_superblock.inodes_per_group; @@ -112,7 +112,8 @@ namespace kernel::filesystem::ext2 return kstd::make_shared(this, new_inode_data); } - auto filesystem::map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) -> uint32_t + auto filesystem::map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) const + -> uint32_t { if (inode_block_index < constants::direct_block_count) { @@ -170,7 +171,7 @@ namespace kernel::filesystem::ext2 return 0; // TODO BA-FS26 really correct?? } - auto filesystem::read_block_number_at_index(uint32_t block_number, uint32_t index) -> uint32_t + auto filesystem::read_block_number_at_index(uint32_t block_number, uint32_t index) const -> uint32_t { uint32_t block_number_buffer = 0; @@ -181,17 +182,22 @@ namespace kernel::filesystem::ext2 return block_number_buffer; } - auto filesystem::get_block_size() -> size_t + auto filesystem::get_block_size() const -> size_t { return constants::base_block_size << m_superblock.log_block_size; } - auto filesystem::get_inode_size() -> size_t + auto filesystem::get_revision_level() const -> size_t { - return m_superblock.rev_level == 0 ? 128 : m_superblock.inode_size; + return m_superblock.rev_level; } - auto filesystem::get_inode_block_count(inode_data const & data) -> uint32_t + auto filesystem::get_inode_size() const -> size_t + { + return get_revision_level() == constants::good_old_revision ? 128 : m_superblock.inode_size; + } + + auto filesystem::get_inode_block_count(inode_data const & data) const -> uint32_t { return data.blocks / (2 << m_superblock.log_block_size); } diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index 1914c70..303838e 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -13,7 +13,7 @@ namespace kernel::filesystem::ext2 { - inode::inode(filesystem * fs, inode_data const & data) + inode::inode(filesystem const * fs, inode_data const & data) : m_filesystem(fs) , m_data(data) { @@ -25,11 +25,10 @@ namespace kernel::filesystem::ext2 auto inode::read(void * buffer, size_t offset, size_t size) const -> size_t { - // TODO BA-FS26 use revision 1 size - auto const max_readable = static_cast(m_data.size) - offset; + auto const max_readable = get_size() - offset; auto const requested_size = std::min(size, max_readable); - if (is_symbolic_link() && m_data.size <= sizeof(m_data.block)) + if (is_symbolic_link() && get_size() <= sizeof(m_data.block)) { auto inline_target = reinterpret_cast(m_data.block.data()); kstd::libc::memcpy(static_cast(buffer), inline_target + offset, requested_size); @@ -91,4 +90,17 @@ namespace kernel::filesystem::ext2 { return (m_data.mode & constants::mode_mask) == constants::mode_symbolic_link; } + + auto inode::get_size() const -> size_t + { + uint64_t size = m_data.size; + + if (m_filesystem->get_revision_level() > constants::good_old_revision && is_regular()) + { + size |= static_cast(m_data.dir_acl) << 32; + } + + return size; + } + } // namespace kernel::filesystem::ext2 -- cgit v1.2.3 From 7b1e578480f2f522fe39a742e688012a7f5ea4ed Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Tue, 12 May 2026 14:46:02 +0200 Subject: Add tests for ext2 inode get_size() --- .../kernel/test_support/filesystem/ext2.hpp | 4 + kernel/src/filesystem/ext2/inode.tests.cpp | 90 +++++++++++++++++++++- kernel/src/test_support/filesystem/ext2.cpp | 6 ++ 3 files changed, 98 insertions(+), 2 deletions(-) diff --git a/kernel/include/kernel/test_support/filesystem/ext2.hpp b/kernel/include/kernel/test_support/filesystem/ext2.hpp index 107e5a4..18cef1c 100644 --- a/kernel/include/kernel/test_support/filesystem/ext2.hpp +++ b/kernel/include/kernel/test_support/filesystem/ext2.hpp @@ -1,6 +1,7 @@ #ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_EXT2_HPP #define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_EXT2_HPP +#include #include #include @@ -12,6 +13,9 @@ namespace kernel::tests::filesystem::ext2 -> void; auto write_u32(kernel::tests::devices::block_device & device, size_t offset, uint32_t value) -> void; auto setup_mock_ext2_layout(kernel::tests::devices::block_device & device) -> void; + auto setup_mock_ext2_layout(kernel::tests::devices::block_device & device, + kernel::filesystem::ext2::superblock const & superblock) -> void; + } // namespace kernel::tests::filesystem::ext2 #endif \ No newline at end of file diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp index 49ba21b..783d930 100644 --- a/kernel/src/filesystem/ext2/inode.tests.cpp +++ b/kernel/src/filesystem/ext2/inode.tests.cpp @@ -1,5 +1,6 @@ #include +#include "kernel/filesystem/ext2/superblock.hpp" #include #include #include @@ -127,7 +128,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 in SCENARIO("Ext2 inode read stops when block mapping resolves to zero", "[filesystem][ext2][inode]") { - auto const block_size = 1024; + auto const block_size = 1024uz; GIVEN("an ext2 inode without mapped data blocks") { auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); @@ -156,7 +157,7 @@ SCENARIO("Ext2 inode read stops when block mapping resolves to zero", "[filesyst SCENARIO("Ext2 inode read across block boundaries", "[filesystem][ext2][inode]") { - auto const block_size = 1024; + auto const block_size = 1024uz; GIVEN("an ext2 inode with two direct blocks and a block size of 1024 bytes") { auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); @@ -203,3 +204,88 @@ SCENARIO("Ext2 inode write is not implemented", "[filesystem][ext2][inode]") } } } + +SCENARIO("Ext2 inode get_size() correctly returns size depending on revision level", "[filesystem][ext2][inode]") +{ + auto const block_size = 1024uz; + + auto superblock = kernel::filesystem::ext2::superblock{}; + superblock.magic = kernel::filesystem::ext2::constants::magic_number; + superblock.log_block_size = 0; + superblock.blocks_count = 64; + superblock.blocks_per_group = 64; + superblock.inodes_per_group = 32; + superblock.inode_size = 128; + + GIVEN("an ext2 inode with good old revision and inode_data.size = 256, inode_data.dir_acl = 32") + { + superblock.rev_level = 0; + + auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); + REQUIRE(device != nullptr); + kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device, superblock); + + auto dev_inode = kstd::make_shared(device); + + auto fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); + + auto data = kernel::filesystem::ext2::inode_data{}; + data.size = 256; + data.dir_acl = 32; + + THEN("the inode size is 256 if mode = regular") + { + data.mode = kernel::filesystem::ext2::constants::mode_regular; + + auto inode = kernel::filesystem::ext2::inode{&fs, data}; + + REQUIRE(inode.get_size() == 256); + } + + THEN("the inode size is 256 if mode = directory") + { + data.mode = kernel::filesystem::ext2::constants::mode_directory; + + auto inode = kernel::filesystem::ext2::inode{&fs, data}; + + REQUIRE(inode.get_size() == 256); + } + } + + GIVEN("an ext2 inode with good dynamic revision and inode_data.size = 256, inode_data.dir_acl = 32") + { + superblock.rev_level = 1; + + auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); + REQUIRE(device != nullptr); + kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device, superblock); + + auto dev_inode = kstd::make_shared(device); + + auto fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); + + auto data = kernel::filesystem::ext2::inode_data{}; + data.size = 256; + data.dir_acl = 32; + + THEN("the inode size is 256 if mode = regular") + { + data.mode = kernel::filesystem::ext2::constants::mode_regular; + + auto inode = kernel::filesystem::ext2::inode{&fs, data}; + + REQUIRE(inode.get_size() == 0x0000'0020'0000'0100); + } + + THEN("the inode size is 256 if mode = directory") + { + data.mode = kernel::filesystem::ext2::constants::mode_directory; + + auto inode = kernel::filesystem::ext2::inode{&fs, data}; + + REQUIRE(inode.get_size() == 256); + } + } +} \ No newline at end of file diff --git a/kernel/src/test_support/filesystem/ext2.cpp b/kernel/src/test_support/filesystem/ext2.cpp index 5a27b63..52b6efe 100644 --- a/kernel/src/test_support/filesystem/ext2.cpp +++ b/kernel/src/test_support/filesystem/ext2.cpp @@ -43,6 +43,12 @@ namespace kernel::tests::filesystem::ext2 superblock.inodes_per_group = 32; superblock.rev_level = 1; superblock.inode_size = 128; + setup_mock_ext2_layout(device, superblock); + } + + auto setup_mock_ext2_layout(kernel::tests::devices::block_device & device, + kernel::filesystem::ext2::superblock const & superblock) -> void + { write_bytes(device, kernel::filesystem::ext2::constants::superblock_offset, &superblock, sizeof(superblock)); auto group_descriptor = kernel::filesystem::ext2::block_group_descriptor{}; -- cgit v1.2.3 From d061f31f33feccb4203979c6e8d9bbaeabecb453 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 13 May 2026 10:13:13 +0200 Subject: refactor ext2 map_inode_block_index_to_global_block_number --- .../include/kernel/filesystem/ext2/filesystem.hpp | 14 +++- kernel/src/filesystem/ext2/filesystem.cpp | 86 +++++++++++++++------- kernel/src/filesystem/ext2/filesystem.tests.cpp | 2 +- kernel/src/filesystem/vfs.cpp | 2 +- 4 files changed, 76 insertions(+), 28 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 18ef372..32d9baf 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace kernel::filesystem::ext2 { @@ -82,14 +83,25 @@ namespace kernel::filesystem::ext2 @return The global block number. */ [[nodiscard]] auto map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) const - -> uint32_t; + -> ssize_t; private: [[nodiscard]] auto read_inode(uint32_t inode_number) const -> kstd::shared_ptr; [[nodiscard]] auto read_block_number_at_index(uint32_t block_number, uint32_t index) const -> uint32_t; + [[nodiscard]] auto read_singly_indirect_block_number(uint32_t singly_indirect_block_number, + uint32_t block_index_in_singly_indirect_block) const + -> uint32_t; + [[nodiscard]] auto read_doubly_indirect_block_number(uint32_t doubly_indirect_block_number, + uint32_t block_index_in_doubly_indirect_block) const + -> uint32_t; + [[nodiscard]] auto read_triply_indirect_block_number(uint32_t triply_indirect_block_number, + uint32_t block_index_in_triply_indirect_block) const + -> uint32_t; + [[nodiscard]] auto get_inode_size() const -> size_t; [[nodiscard]] auto get_inode_block_count(inode_data const & data) const -> uint32_t; + [[nodiscard]] auto block_numbers_per_block() const -> uint32_t; superblock m_superblock{}; kstd::vector m_block_group_descriptors; diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index aaa50c7..0f34cfe 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace kernel::filesystem::ext2 { @@ -113,7 +114,7 @@ namespace kernel::filesystem::ext2 } auto filesystem::map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) const - -> uint32_t + -> ssize_t { if (inode_block_index < constants::direct_block_count) { @@ -121,9 +122,7 @@ namespace kernel::filesystem::ext2 } inode_block_index -= constants::direct_block_count; - auto const block_size = get_block_size(); - auto const numbers_per_block = block_size / sizeof(uint32_t); - + auto const numbers_per_block = block_numbers_per_block(); auto const block_numbers_per_singly_indirect_block = numbers_per_block; auto const block_numbers_per_doubly_indirect_block = numbers_per_block * block_numbers_per_singly_indirect_block; auto const block_numbers_per_triply_indirect_block = numbers_per_block * block_numbers_per_doubly_indirect_block; @@ -131,44 +130,76 @@ namespace kernel::filesystem::ext2 if (inode_block_index < block_numbers_per_singly_indirect_block) { auto const singly_indirect_block_number = data.block.at(constants::singly_indirect_block_index); - return read_block_number_at_index(singly_indirect_block_number, inode_block_index); + return read_singly_indirect_block_number(singly_indirect_block_number, inode_block_index); } inode_block_index -= block_numbers_per_singly_indirect_block; if (inode_block_index < block_numbers_per_doubly_indirect_block) { auto const doubly_indirect_block_number = data.block.at(constants::doubly_indirect_block_index); - auto const singly_indirect_block_index_in_doubly_indirect_block = - inode_block_index / block_numbers_per_singly_indirect_block; - auto const singly_indirect_block_number = read_block_number_at_index( - doubly_indirect_block_number, singly_indirect_block_index_in_doubly_indirect_block); - - auto const block_index_in_singly_indirect_block = inode_block_index % block_numbers_per_singly_indirect_block; - return read_block_number_at_index(singly_indirect_block_number, block_index_in_singly_indirect_block); + return read_doubly_indirect_block_number(doubly_indirect_block_number, inode_block_index); } inode_block_index -= block_numbers_per_doubly_indirect_block; if (inode_block_index < block_numbers_per_triply_indirect_block) { auto const triply_indirect_block_number = data.block.at(constants::triply_indirect_block_index); - auto const doubly_indirect_block_index_in_triply_indirect_block = - inode_block_index / block_numbers_per_doubly_indirect_block; - auto const doubly_indirect_block_number = read_block_number_at_index( - triply_indirect_block_number, doubly_indirect_block_index_in_triply_indirect_block); + return read_triply_indirect_block_number(triply_indirect_block_number, inode_block_index); + } + + return -1; + } + + auto filesystem::read_singly_indirect_block_number(uint32_t singly_indirect_block_number, + uint32_t block_index_in_singly_indirect_block) const -> uint32_t + { + if (singly_indirect_block_number == 0) + { + return 0; + } + return read_block_number_at_index(singly_indirect_block_number, block_index_in_singly_indirect_block); + } + + auto filesystem::read_doubly_indirect_block_number(uint32_t doubly_indirect_block_number, + uint32_t block_index_in_doubly_indirect_block) const -> uint32_t + { + auto const block_numbers_per_singly_indirect_block = block_numbers_per_block(); - auto const remaining_block_numbers = inode_block_index % block_numbers_per_doubly_indirect_block; + if (doubly_indirect_block_number == 0) + { + return 0; + } + + auto const singly_indirect_block_index_in_doubly_indirect_block = + block_index_in_doubly_indirect_block / block_numbers_per_singly_indirect_block; + auto const block_index_in_singly_indirect_block = + block_index_in_doubly_indirect_block % block_numbers_per_singly_indirect_block; + + auto const singly_indirect_block_number = + read_block_number_at_index(doubly_indirect_block_number, singly_indirect_block_index_in_doubly_indirect_block); - auto const singly_indirect_block_index_in_doubly_indirect_block = - remaining_block_numbers / block_numbers_per_singly_indirect_block; - auto const singly_indirect_block_number = read_block_number_at_index( - doubly_indirect_block_number, singly_indirect_block_index_in_doubly_indirect_block); + return read_singly_indirect_block_number(singly_indirect_block_number, block_index_in_singly_indirect_block); + } + + auto filesystem::read_triply_indirect_block_number(uint32_t triply_indirect_block_number, + uint32_t block_index_in_triply_indirect_block) const -> uint32_t + { + auto const block_numbers_per_doubly_indirect_block = block_numbers_per_block() * block_numbers_per_block(); - auto const block_index_in_singly_indirect_block = - remaining_block_numbers % block_numbers_per_singly_indirect_block; - return read_block_number_at_index(singly_indirect_block_number, block_index_in_singly_indirect_block); + if (triply_indirect_block_number == 0) + { + return 0; } - return 0; // TODO BA-FS26 really correct?? + auto const doubly_indirect_block_index_in_triply_indirect_block = + block_index_in_triply_indirect_block / block_numbers_per_doubly_indirect_block; + auto const block_index_in_doubly_indirect_block = + block_index_in_triply_indirect_block % block_numbers_per_doubly_indirect_block; + + auto const doubly_indirect_block_number = + read_block_number_at_index(triply_indirect_block_number, doubly_indirect_block_index_in_triply_indirect_block); + + return read_doubly_indirect_block_number(doubly_indirect_block_number, block_index_in_doubly_indirect_block); } auto filesystem::read_block_number_at_index(uint32_t block_number, uint32_t index) const -> uint32_t @@ -182,6 +213,11 @@ namespace kernel::filesystem::ext2 return block_number_buffer; } + auto filesystem::block_numbers_per_block() const -> uint32_t + { + return get_block_size() / sizeof(uint32_t); + } + auto filesystem::get_block_size() const -> size_t { return constants::base_block_size << m_superblock.log_block_size; diff --git a/kernel/src/filesystem/ext2/filesystem.tests.cpp b/kernel/src/filesystem/ext2/filesystem.tests.cpp index 31c4c29..8341070 100644 --- a/kernel/src/filesystem/ext2/filesystem.tests.cpp +++ b/kernel/src/filesystem/ext2/filesystem.tests.cpp @@ -133,7 +133,7 @@ SCENARIO("Ext2 block mapping includes direct and all indirect levels", "[filesys THEN("mapping returns zero for out-of-range indexes") { auto const beyond_triply = triply_start + numbers_per_block * numbers_per_block * numbers_per_block; - REQUIRE(fs.map_inode_block_index_to_global_block_number(beyond_triply, inode_data) == 0); + REQUIRE(fs.map_inode_block_index_to_global_block_number(beyond_triply, inode_data) == -1); } } } diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 535f898..f5d57be 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -143,7 +143,7 @@ namespace kernel::filesystem { parent_mount = m_mount_table.find_exact_mount(parent_mount_dentry->get_absolute_path().view()); } - + auto new_fs_root = kstd::make_shared(mount_point_dentry->get_parent(), fs->root_inode(), mount_point_dentry->get_name()); auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, parent_mount); -- cgit v1.2.3 From de0ef46e7bab75d0ab94f02d569df62e2b4281f2 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 13 May 2026 10:14:21 +0200 Subject: implement sparse files, fix bug with reading more than inode size --- kernel/src/filesystem/ext2/inode.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index 303838e..cfe0a35 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -40,23 +40,29 @@ namespace kernel::filesystem::ext2 auto bytes_read = 0uz; - while (bytes_read < size) + while (bytes_read < requested_size) { auto const block_number = m_filesystem->map_inode_block_index_to_global_block_number(block_index, m_data); - // TODO BA-FS26 really correct? sparse files -> 0 means a full block with zeros --> function - // map_inode_block_index_to_global_block_number should return 0 if not possible to find an block - if (block_number == 0) + if (block_number == -1) { break; } - auto const block_start_offset = block_number * m_filesystem->get_block_size(); - auto const read_offset = block_start_offset + in_block_offset; auto const bytes_to_read = std::min(requested_size - bytes_read, m_filesystem->get_block_size() - in_block_offset); + if (block_number == 0) + { + kstd::libc::memset(static_cast(buffer) + bytes_read, 0, bytes_to_read); + bytes_read += bytes_to_read; + } + else + { + auto const block_start_offset = block_number * m_filesystem->get_block_size(); + auto const read_offset = block_start_offset + in_block_offset; - bytes_read += - m_filesystem->backing_inode()->read(static_cast(buffer) + bytes_read, read_offset, bytes_to_read); + bytes_read += m_filesystem->backing_inode()->read(static_cast(buffer) + bytes_read, read_offset, + bytes_to_read); + } block_index++; in_block_offset = 0; // After the first block, we always start at the beginning of the block -- cgit v1.2.3 From 06b4c8bebbcd8aa845a845817cdeceeb86fbfc13 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 13 May 2026 10:15:09 +0200 Subject: add todos --- kernel/include/kernel/filesystem/mount.hpp | 3 +++ kernel/src/filesystem/mount_table.cpp | 1 + 2 files changed, 4 insertions(+) diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index af5d08b..f920891 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -7,6 +7,8 @@ #include #include +#include + namespace kernel::filesystem { /** @@ -57,6 +59,7 @@ namespace kernel::filesystem kstd::shared_ptr m_root_dentry; kstd::shared_ptr m_filesystem{}; kstd::shared_ptr m_parent_mount{}; + std::atomic_uint32_t m_ref_count{0}; // TODO BA-FS26 }; } // namespace kernel::filesystem diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index daef93e..5a49e7a 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -34,6 +34,7 @@ namespace kernel::filesystem auto mount_table::remove_mount(std::string_view path) -> operation_result { + // TODO BA-FS26 check wheter something is open in this mount auto mount_range = std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }); auto mount_it = mount_range.begin(); -- cgit v1.2.3 From f0715177763e1154668a656fbd7abfb8bb2c3261 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 13 May 2026 11:04:47 +0200 Subject: add inode sparse files tests --- kernel/src/filesystem/ext2/inode.tests.cpp | 101 ++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 8 deletions(-) diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp index 783d930..8381ee0 100644 --- a/kernel/src/filesystem/ext2/inode.tests.cpp +++ b/kernel/src/filesystem/ext2/inode.tests.cpp @@ -15,6 +15,7 @@ #include +#include #include #include #include @@ -126,10 +127,10 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_fixture, "Ext2 in } } -SCENARIO("Ext2 inode read stops when block mapping resolves to zero", "[filesystem][ext2][inode]") +SCENARIO("Ext2 inode handles zeros in block mappings as file holes", "[filesystem][ext2][inode]") { auto const block_size = 1024uz; - GIVEN("an ext2 inode without mapped data blocks") + GIVEN("an ext2 inode with only direct mapped data blocks") { auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); REQUIRE(device != nullptr); @@ -141,16 +142,100 @@ SCENARIO("Ext2 inode read stops when block mapping resolves to zero", "[filesyst REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); auto data = kernel::filesystem::ext2::inode_data{}; - data.blocks = 2; - data.block[0] = 0; + data.block[0] = 30; + data.block[1] = 0; + data.block[2] = 31; + data.size = block_size * 3; + + kernel::tests::filesystem::ext2::write_bytes(*device, 30 * block_size, "Hello", 5); + kernel::tests::filesystem::ext2::write_bytes(*device, 31 * block_size, "World!", 6); + + auto inode = kernel::filesystem::ext2::inode{&fs, data}; + + auto buffer = kstd::vector(data.size, std::byte{0xAB}); + + THEN("correct number of bytes are read and holes are returned as zeros") + { + auto const bytes_read = inode.read(buffer.data(), 0, buffer.size()); + REQUIRE(bytes_read == data.size); + + auto const text = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; + REQUIRE(text.substr(0, 5) == "Hello"); + REQUIRE(std::ranges::all_of(text.substr(5, block_size - 5), [](char c) { return c == '\0'; })); + REQUIRE(text.substr(2 * block_size, 6) == "World!"); + REQUIRE(std::ranges::all_of(text.substr(2 * block_size + 6, 3 * block_size), [](char c) { return c == '\0'; })); + } + } + + GIVEN("an ext2 indode with file holes in singly indirect blocks") + { + auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); + REQUIRE(device != nullptr); + kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); + + auto dev_inode = kstd::make_shared(device); + + auto fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); + + auto data = kernel::filesystem::ext2::inode_data{}; + data.block[0] = 30; + data.block[12] = 31; + data.size = block_size * 15; + + kernel::tests::filesystem::ext2::write_u32(*device, 31 * block_size, 50); + kernel::tests::filesystem::ext2::write_u32(*device, 31 * block_size + 4, 0); + kernel::tests::filesystem::ext2::write_u32(*device, 31 * block_size + 8, 51); + + kernel::tests::filesystem::ext2::write_bytes(*device, 30 * block_size, "Hello", 5); + kernel::tests::filesystem::ext2::write_bytes(*device, 50 * block_size, "Blub", 4); + kernel::tests::filesystem::ext2::write_bytes(*device, 51 * block_size, "World!", 6); + + auto inode = kernel::filesystem::ext2::inode{&fs, data}; + + auto buffer = kstd::vector(data.size, std::byte{0xAB}); + + THEN("correct number of bytes are read and holes are returned as zeros") + { + auto const bytes_read = inode.read(buffer.data(), 0, buffer.size()); + REQUIRE(bytes_read == data.size); + + auto const text = std::string_view{reinterpret_cast(buffer.data()), bytes_read}; + REQUIRE(text.substr(0, 5) == "Hello"); + REQUIRE(std::ranges::all_of(text.substr(5, 12 * block_size - 5), [](char c) { return c == '\0'; })); + REQUIRE(text.substr(12 * block_size, 4) == "Blub"); + REQUIRE( + std::ranges::all_of(text.substr(12 * block_size + 4, 2 * block_size - 4), [](char c) { return c == '\0'; })); + REQUIRE(text.substr(14 * block_size, 6) == "World!"); + REQUIRE( + std::ranges::all_of(text.substr(14 * block_size + 6, 1 * block_size - 6), [](char c) { return c == '\0'; })); + } + } + + GIVEN("an ext2 inode with zero singly indirect block pointer") + { + auto device = kstd::make_shared(0, 0, "mock", block_size, 64 * block_size); + REQUIRE(device != nullptr); + kernel::tests::filesystem::ext2::setup_mock_ext2_layout(*device); + + auto dev_inode = kstd::make_shared(device); + + auto fs = kernel::filesystem::ext2::filesystem{}; + REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); + + auto data = kernel::filesystem::ext2::inode_data{}; + data.block[12] = 0; + data.size = block_size * 15; + auto inode = kernel::filesystem::ext2::inode{&fs, data}; - auto buffer = kstd::vector(32, std::byte{0xAB}); + auto buffer = kstd::vector(block_size * 3, std::byte{0xAB}); - THEN("no bytes are read") + THEN("all direct blocks are zero when singly indirect block pointer is zero") { auto const bytes_read = inode.read(buffer.data(), 0, buffer.size()); - REQUIRE(bytes_read == 0); + REQUIRE(bytes_read == buffer.size()); + REQUIRE(std::ranges::all_of(buffer, [](std::byte c) { return c == std::byte{0x00}; })); } } } @@ -170,7 +255,7 @@ SCENARIO("Ext2 inode read across block boundaries", "[filesystem][ext2][inode]") REQUIRE(fs.mount(dev_inode) == kernel::filesystem::filesystem::operation_result::success); auto inode_data = kernel::filesystem::ext2::inode_data{}; - inode_data.blocks = 2; + inode_data.size = block_size * 2; inode_data.block[0] = 20; kernel::tests::filesystem::ext2::write_bytes(*device, 21 * block_size - 6, "Hello ", 6); inode_data.block[1] = 21; -- cgit v1.2.3 From 117b16cc5e3506da637a806a25bb22b82a02ef9e Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 13 May 2026 11:11:22 +0200 Subject: refactoring --- .../include/kernel/filesystem/ext2/filesystem.hpp | 4 +++ kernel/src/filesystem/ext2/filesystem.cpp | 42 ++++++++++++---------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 32d9baf..46be32f 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -101,7 +101,11 @@ namespace kernel::filesystem::ext2 [[nodiscard]] auto get_inode_size() const -> size_t; [[nodiscard]] auto get_inode_block_count(inode_data const & data) const -> uint32_t; + [[nodiscard]] auto block_numbers_per_block() const -> uint32_t; + [[nodiscard]] auto block_numbers_per_singly_indirect_block() const -> uint32_t; + [[nodiscard]] auto block_numbers_per_doubly_indirect_block() const -> uint32_t; + [[nodiscard]] auto block_numbers_per_triply_indirect_block() const -> uint32_t; superblock m_superblock{}; kstd::vector m_block_group_descriptors; diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 0f34cfe..893cc38 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -122,26 +122,21 @@ namespace kernel::filesystem::ext2 } inode_block_index -= constants::direct_block_count; - auto const numbers_per_block = block_numbers_per_block(); - auto const block_numbers_per_singly_indirect_block = numbers_per_block; - auto const block_numbers_per_doubly_indirect_block = numbers_per_block * block_numbers_per_singly_indirect_block; - auto const block_numbers_per_triply_indirect_block = numbers_per_block * block_numbers_per_doubly_indirect_block; - - if (inode_block_index < block_numbers_per_singly_indirect_block) + if (inode_block_index < block_numbers_per_singly_indirect_block()) { auto const singly_indirect_block_number = data.block.at(constants::singly_indirect_block_index); return read_singly_indirect_block_number(singly_indirect_block_number, inode_block_index); } - inode_block_index -= block_numbers_per_singly_indirect_block; + inode_block_index -= block_numbers_per_singly_indirect_block(); - if (inode_block_index < block_numbers_per_doubly_indirect_block) + if (inode_block_index < block_numbers_per_doubly_indirect_block()) { auto const doubly_indirect_block_number = data.block.at(constants::doubly_indirect_block_index); return read_doubly_indirect_block_number(doubly_indirect_block_number, inode_block_index); } - inode_block_index -= block_numbers_per_doubly_indirect_block; + inode_block_index -= block_numbers_per_doubly_indirect_block(); - if (inode_block_index < block_numbers_per_triply_indirect_block) + if (inode_block_index < block_numbers_per_triply_indirect_block()) { auto const triply_indirect_block_number = data.block.at(constants::triply_indirect_block_index); return read_triply_indirect_block_number(triply_indirect_block_number, inode_block_index); @@ -163,17 +158,15 @@ namespace kernel::filesystem::ext2 auto filesystem::read_doubly_indirect_block_number(uint32_t doubly_indirect_block_number, uint32_t block_index_in_doubly_indirect_block) const -> uint32_t { - auto const block_numbers_per_singly_indirect_block = block_numbers_per_block(); - if (doubly_indirect_block_number == 0) { return 0; } auto const singly_indirect_block_index_in_doubly_indirect_block = - block_index_in_doubly_indirect_block / block_numbers_per_singly_indirect_block; + block_index_in_doubly_indirect_block / block_numbers_per_singly_indirect_block(); auto const block_index_in_singly_indirect_block = - block_index_in_doubly_indirect_block % block_numbers_per_singly_indirect_block; + block_index_in_doubly_indirect_block % block_numbers_per_singly_indirect_block(); auto const singly_indirect_block_number = read_block_number_at_index(doubly_indirect_block_number, singly_indirect_block_index_in_doubly_indirect_block); @@ -184,17 +177,15 @@ namespace kernel::filesystem::ext2 auto filesystem::read_triply_indirect_block_number(uint32_t triply_indirect_block_number, uint32_t block_index_in_triply_indirect_block) const -> uint32_t { - auto const block_numbers_per_doubly_indirect_block = block_numbers_per_block() * block_numbers_per_block(); - if (triply_indirect_block_number == 0) { return 0; } auto const doubly_indirect_block_index_in_triply_indirect_block = - block_index_in_triply_indirect_block / block_numbers_per_doubly_indirect_block; + block_index_in_triply_indirect_block / block_numbers_per_doubly_indirect_block(); auto const block_index_in_doubly_indirect_block = - block_index_in_triply_indirect_block % block_numbers_per_doubly_indirect_block; + block_index_in_triply_indirect_block % block_numbers_per_doubly_indirect_block(); auto const doubly_indirect_block_number = read_block_number_at_index(triply_indirect_block_number, doubly_indirect_block_index_in_triply_indirect_block); @@ -218,6 +209,21 @@ namespace kernel::filesystem::ext2 return get_block_size() / sizeof(uint32_t); } + auto filesystem::block_numbers_per_singly_indirect_block() const -> uint32_t + { + return block_numbers_per_block(); + } + + auto filesystem::block_numbers_per_doubly_indirect_block() const -> uint32_t + { + return block_numbers_per_singly_indirect_block() * block_numbers_per_block(); + } + + auto filesystem::block_numbers_per_triply_indirect_block() const -> uint32_t + { + return block_numbers_per_doubly_indirect_block() * block_numbers_per_block(); + } + auto filesystem::get_block_size() const -> size_t { return constants::base_block_size << m_superblock.log_block_size; -- cgit v1.2.3 From 15afa6a030ee6e1fc6c255f9567b54d78c530d25 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 13 May 2026 11:23:33 +0200 Subject: increase buffer size to really test the singly indirect block pointer --- kernel/src/filesystem/ext2/inode.tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp index 8381ee0..45bea51 100644 --- a/kernel/src/filesystem/ext2/inode.tests.cpp +++ b/kernel/src/filesystem/ext2/inode.tests.cpp @@ -229,7 +229,7 @@ SCENARIO("Ext2 inode handles zeros in block mappings as file holes", "[filesyste auto inode = kernel::filesystem::ext2::inode{&fs, data}; - auto buffer = kstd::vector(block_size * 3, std::byte{0xAB}); + auto buffer = kstd::vector(block_size * 15, std::byte{0xAB}); THEN("all direct blocks are zero when singly indirect block pointer is zero") { -- cgit v1.2.3 From a5e65a09667cbaad0259ebf7315d09401651bbc2 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Wed, 13 May 2026 13:37:15 +0200 Subject: add tests for mount and unmount boot root filesystem --- kernel/src/filesystem/vfs.tests.cpp | 63 +++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index add96aa..0f1d6d5 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -148,6 +148,69 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(mounted_monkey != nullptr); } + THEN("image can be mounted on / file opened and unmounted again") + { + auto info_1 = vfs.open("/information/info_1.txt"); + REQUIRE(info_1 != nullptr); + + REQUIRE(vfs.do_mount("/dev/ram16", "/") == kernel::filesystem::vfs::operation_result::success); + + info_1 = vfs.open("/information/info_1.txt"); + REQUIRE(info_1 == nullptr); + + auto water = vfs.open("/monkey_house/infrastructure/water.txt"); + REQUIRE(water != nullptr); + + REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); + + info_1 = vfs.open("/information/info_1.txt"); + REQUIRE(info_1 != nullptr); + } + + THEN("image can be mounted on / just the boot root has /dev") + { + auto info_1 = vfs.open("/information/info_1.txt"); + REQUIRE(info_1 != nullptr); + + REQUIRE(vfs.do_mount("/dev/ram16", "/") == kernel::filesystem::vfs::operation_result::success); + + info_1 = vfs.open("/information/info_1.txt"); + REQUIRE(info_1 == nullptr); + + auto water = vfs.open("/monkey_house/infrastructure/water.txt"); + REQUIRE(water != nullptr); + + auto dev_ram_16 = vfs.open("/dev/ram16"); + REQUIRE(dev_ram_16 == nullptr); + + REQUIRE(vfs.do_mount("/dev/ram32", "/") == kernel::filesystem::vfs::operation_result::non_existent_path); + + REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); + + auto dev_ram_32 = vfs.open("/dev/ram32"); + REQUIRE(dev_ram_32 != nullptr); + } + + THEN("boot root can be unmounted and remounted again but /dev is not re-grafted") + { + auto info_1 = vfs.open("/information/info_1.txt"); + REQUIRE(info_1 != nullptr); + + REQUIRE(vfs.unmount("/dev") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); + + info_1 = vfs.open("/information/info_1.txt"); + REQUIRE(info_1 == nullptr); + + REQUIRE(vfs.do_mount("/dev/ram0", "/") == kernel::filesystem::vfs::operation_result::success); + + info_1 = vfs.open("/information/info_1.txt"); + REQUIRE(info_1 != nullptr); + + auto dev_ram_0 = vfs.open("/dev/ram0"); + REQUIRE(dev_ram_0 == nullptr); + } + THEN("mount with null file system fails") { REQUIRE(vfs.do_mount("/closed.txt", "/information") == -- cgit v1.2.3 From 192843f9afd67ed7dbe5133e5bf0330384d7cdff Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 10 May 2026 14:38:07 +0200 Subject: fix bht debugging --- .vscode/settings.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e71d64f..cdc8176 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -88,8 +88,12 @@ "ignoreFailures": true }, { - "description": "Load custom Python helpers", - "text": "-interpreter-exec console \"source ${workspaceFolder}/scripts/gdb/load.py\"" + "description": "Load teachos python helpers", + "text": "source ${workspaceFolder}/scripts/gdb/teachos.py" + }, + { + "description": "Load toolchain python helpers", + "text": "source ${workspaceFolder}/scripts/gdb/toolchain.py" } ], }, -- cgit v1.2.3 From cd2b6a5297f7fc0428dca46ab56e3cc46a31d5d0 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 16 May 2026 09:38:08 +0000 Subject: ide: fix debugging --- .devcontainer/x86-64/devcontainer.json | 3 +++ .vscode/settings.json | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.devcontainer/x86-64/devcontainer.json b/.devcontainer/x86-64/devcontainer.json index 80abece..54bfe08 100644 --- a/.devcontainer/x86-64/devcontainer.json +++ b/.devcontainer/x86-64/devcontainer.json @@ -21,5 +21,8 @@ "ms-vscode.hexeditor" ] } + }, + "containerEnv": { + "DEBUGINFOD_URLS": "NOSUCHURL" } } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index cdc8176..d62742c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -90,10 +90,6 @@ { "description": "Load teachos python helpers", "text": "source ${workspaceFolder}/scripts/gdb/teachos.py" - }, - { - "description": "Load toolchain python helpers", - "text": "source ${workspaceFolder}/scripts/gdb/toolchain.py" } ], }, -- cgit v1.2.3 From 2833fa2a2d2bf1f98f627503e52531615d1c1496 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 14 May 2026 15:41:21 +0200 Subject: small refactoring --- kernel/include/kernel/filesystem/dentry.hpp | 2 +- kernel/src/filesystem/dentry.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index 7eef693..3813f61 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -104,7 +104,7 @@ namespace kernel::filesystem kstd::shared_ptr m_parent; kstd::vector> m_children; kstd::shared_ptr m_inode; - uint32_t m_flags{0}; + uint32_t m_flags; }; } // namespace kernel::filesystem diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index c21771b..7603e11 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -17,6 +17,7 @@ namespace kernel::filesystem : m_name(name) , m_parent(parent) , m_inode(inode) + , m_flags(0) { if (!m_inode) { -- cgit v1.2.3 From 13f41e3816bd0be96c9bf728b534a58e6d4e5c28 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Thu, 14 May 2026 15:41:54 +0200 Subject: add todo --- kernel/src/filesystem/mount_table.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 5a49e7a..e0cf140 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -35,6 +35,7 @@ namespace kernel::filesystem auto mount_table::remove_mount(std::string_view path) -> operation_result { // TODO BA-FS26 check wheter something is open in this mount + // TODO BA-FS26 nearly the same code is in find_exact_mount -> refactor to avoid code duplication auto mount_range = std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }); auto mount_it = mount_range.begin(); -- cgit v1.2.3 From 245f47af9362e83235a28f993c89f844886e65c3 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Thu, 14 May 2026 16:25:15 +0200 Subject: Unify header inclusion syntax --- kernel/include/kernel/filesystem/vfs.hpp | 2 +- kernel/src/filesystem/ext2/inode.tests.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index b5053a2..4b6de53 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -1,8 +1,8 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP #define TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP -#include "kernel/filesystem/devfs/filesystem.hpp" #include +#include #include #include diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp index 45bea51..efc0660 100644 --- a/kernel/src/filesystem/ext2/inode.tests.cpp +++ b/kernel/src/filesystem/ext2/inode.tests.cpp @@ -1,9 +1,9 @@ #include -#include "kernel/filesystem/ext2/superblock.hpp" #include #include #include +#include #include #include #include -- cgit v1.2.3 From c6953852b9e10823830688bdfb269650b080f1bb Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Thu, 14 May 2026 16:29:29 +0200 Subject: Track dentry instead of inode in open_file_descriptor --- kernel/include/kernel/filesystem/open_file_descriptor.hpp | 12 ++++++------ kernel/kapi/filesystem.cpp | 2 +- kernel/src/filesystem/open_file_descriptor.cpp | 15 ++++++++------- kernel/src/filesystem/open_file_descriptor.tests.cpp | 14 +++++++++----- kernel/src/filesystem/open_file_table.tests.cpp | 10 +++++++--- kernel/src/filesystem/vfs.tests.cpp | 6 +++--- 6 files changed, 34 insertions(+), 25 deletions(-) diff --git a/kernel/include/kernel/filesystem/open_file_descriptor.hpp b/kernel/include/kernel/filesystem/open_file_descriptor.hpp index 036dcf0..823fe13 100644 --- a/kernel/include/kernel/filesystem/open_file_descriptor.hpp +++ b/kernel/include/kernel/filesystem/open_file_descriptor.hpp @@ -1,7 +1,7 @@ #ifndef TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTOR_HPP #define TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTOR_HPP -#include +#include #include @@ -11,15 +11,15 @@ namespace kernel::filesystem { /** @brief Represents an open file descriptor in the filesystem. This class encapsulates the state of an open file, - including a reference to the associated inode and the current file offset. + including a reference to the associated dentry and the current file offset. */ struct open_file_descriptor { /** - @brief Constructs an open file descriptor for the given @p inode. - @param inode The inode to associate with the open file descriptor. + @brief Constructs an open file descriptor for the given @p dentry. + @param dentry The dentry to associate with the open file descriptor. */ - explicit open_file_descriptor(kstd::shared_ptr const & inode); + explicit open_file_descriptor(kstd::shared_ptr const & dentry); /** @brief Destructor for the open file descriptor. @@ -53,7 +53,7 @@ namespace kernel::filesystem [[nodiscard]] auto offset() const -> size_t; private: - kstd::shared_ptr m_inode; + kstd::shared_ptr m_dentry; size_t m_offset; }; diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index 4c68f28..77d7eb0 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -35,7 +35,7 @@ namespace kapi::filesystem { if (auto dentry = kernel::filesystem::vfs::get().open(path)) { - auto open_file_descriptor = kstd::make_shared(dentry->get_inode()); + auto open_file_descriptor = kstd::make_shared(dentry); return kernel::filesystem::open_file_table::get().add_file(open_file_descriptor); } diff --git a/kernel/src/filesystem/open_file_descriptor.cpp b/kernel/src/filesystem/open_file_descriptor.cpp index 25bffbd..27e6449 100644 --- a/kernel/src/filesystem/open_file_descriptor.cpp +++ b/kernel/src/filesystem/open_file_descriptor.cpp @@ -1,6 +1,7 @@ -#include #include +#include + #include #include @@ -8,26 +9,26 @@ namespace kernel::filesystem { - open_file_descriptor::open_file_descriptor(kstd::shared_ptr const & inode) - : m_inode(inode) + open_file_descriptor::open_file_descriptor(kstd::shared_ptr const & dentry) + : m_dentry(dentry) , m_offset(0) { - if (!inode) + if (!dentry) { - kstd::os::panic("[FILESYSTEM] open_file_descriptor constructed with null inode."); + kstd::os::panic("[FILESYSTEM] open_file_descriptor constructed with null dentry."); } } auto open_file_descriptor::read(void * buffer, size_t size) -> size_t { - auto read_bytes = m_inode->read(buffer, m_offset, size); + auto read_bytes = m_dentry->get_inode()->read(buffer, m_offset, size); m_offset += read_bytes; return read_bytes; } auto open_file_descriptor::write(void const * buffer, size_t size) -> size_t { - auto written_bytes = m_inode->write(buffer, m_offset, size); + auto written_bytes = m_dentry->get_inode()->write(buffer, m_offset, size); m_offset += written_bytes; return written_bytes; } diff --git a/kernel/src/filesystem/open_file_descriptor.tests.cpp b/kernel/src/filesystem/open_file_descriptor.tests.cpp index 53835ba..8c24cf0 100644 --- a/kernel/src/filesystem/open_file_descriptor.tests.cpp +++ b/kernel/src/filesystem/open_file_descriptor.tests.cpp @@ -1,5 +1,7 @@ #include +#include +#include #include #include #include @@ -16,10 +18,11 @@ SCENARIO("Open file descriptor construction", "[filesystem][open_file_descriptor]") { - GIVEN("an inode and an open file descriptor for that inode") + GIVEN("a dentry and an open file descriptor for that dentry") { auto inode = kstd::make_shared(); - auto file_descriptor = kernel::filesystem::open_file_descriptor{inode}; + auto dentry = kstd::make_shared(nullptr, inode, "test_dentry"); + auto file_descriptor = kernel::filesystem::open_file_descriptor{dentry}; THEN("the initial offset is zero") { @@ -30,10 +33,11 @@ SCENARIO("Open file descriptor construction", "[filesystem][open_file_descriptor SCENARIO("Open file descriptor read/write offset management", "[filesystem][open_file_descriptor]") { - GIVEN("an inode that tracks read/write calls and an open file descriptor for that inode") + GIVEN("a dentry that tracks read/write calls and an open file descriptor for that dentry") { auto inode = kstd::make_shared(); - auto file_descriptor = kernel::filesystem::open_file_descriptor{inode}; + auto dentry = kstd::make_shared(nullptr, inode, "test_dentry"); + auto file_descriptor = kernel::filesystem::open_file_descriptor{dentry}; THEN("the offset is updated correctly after reads") { @@ -78,7 +82,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Ope auto & vfs = kernel::filesystem::vfs::get(); auto dentry = vfs.open("/information/info_1.txt"); REQUIRE(dentry != nullptr); - auto ofd = kstd::make_shared(dentry->get_inode()); + auto ofd = kstd::make_shared(dentry); THEN("the file can be read and the offset is updated") { diff --git a/kernel/src/filesystem/open_file_table.tests.cpp b/kernel/src/filesystem/open_file_table.tests.cpp index a5c791d..456d6b7 100644 --- a/kernel/src/filesystem/open_file_table.tests.cpp +++ b/kernel/src/filesystem/open_file_table.tests.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -15,8 +16,10 @@ SCENARIO("Open file table add/get file", "[filesystem][open_file_table]") { auto & table = kernel::filesystem::open_file_table::get(); auto inode = kstd::make_shared(); - auto file_descriptor_1 = kstd::make_shared(inode); - auto file_descriptor_2 = kstd::make_shared(inode); + auto dentry = kstd::make_shared(nullptr, inode, "test_dentry"); + + auto file_descriptor_1 = kstd::make_shared(dentry); + auto file_descriptor_2 = kstd::make_shared(dentry); WHEN("adding the open file descriptor to the open file table") { @@ -69,7 +72,8 @@ SCENARIO("Open file table remove file", "[filesystem][open_file_table]") { auto & table = kernel::filesystem::open_file_table::get(); auto inode = kstd::make_shared(); - auto file_descriptor = kstd::make_shared(inode); + auto dentry = kstd::make_shared(nullptr, inode, "test_dentry"); + auto file_descriptor = kstd::make_shared(dentry); auto fd = table.add_file(file_descriptor); WHEN("removing the file descriptor using the file descriptor") diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 0f1d6d5..eaffbc9 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -310,7 +310,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto dentry = vfs.open("/information/sheep_1.txt"); REQUIRE(dentry != nullptr); - auto sheep_1_ofd = kstd::make_shared(dentry->get_inode()); + auto sheep_1_ofd = kstd::make_shared(dentry); kstd::vector buffer(7); auto bytes_read = sheep_1_ofd->read(buffer.data(), buffer.size()); @@ -335,8 +335,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(sheep_1 != nullptr); REQUIRE(goat_1 != nullptr); - auto sheep_1_ofd = kstd::make_shared(sheep_1->get_inode()); - auto goat_1_ofd = kstd::make_shared(goat_1->get_inode()); + auto sheep_1_ofd = kstd::make_shared(sheep_1); + auto goat_1_ofd = kstd::make_shared(goat_1); kstd::vector sheep_buffer(7); auto bytes_read = sheep_1_ofd->read(sheep_buffer.data(), sheep_buffer.size()); -- cgit v1.2.3 From 216ec44cf2fdc914ce38e3ab56eb3a8d82b54c77 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Thu, 14 May 2026 16:59:56 +0200 Subject: Refactor resolve_path --- kernel/include/kernel/filesystem/vfs.hpp | 6 ++++++ kernel/src/filesystem/vfs.cpp | 21 ++++++++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 4b6de53..2aa1dd7 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -4,11 +4,13 @@ #include #include #include +#include #include #include #include +#include namespace kernel::filesystem { @@ -77,7 +79,11 @@ namespace kernel::filesystem vfs() = default; auto init_internal() -> void; + [[nodiscard]] auto resolve_path_internal(std::string_view path) + -> std::pair, kstd::shared_ptr>; [[nodiscard]] auto resolve_path(std::string_view path) -> kstd::shared_ptr; + [[nodiscard]] auto find_mount(std::string_view path) -> kstd::shared_ptr; + auto do_mount_internal(kstd::shared_ptr const & mount_point_dentry, kstd::shared_ptr const & fs) -> void; diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index f5d57be..52ffcc8 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace { @@ -165,11 +166,11 @@ namespace kernel::filesystem } } - auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr + auto vfs::resolve_path_internal(std::string_view path) -> std::pair, kstd::shared_ptr> { if (!path::is_valid_absolute_path(path)) { - return nullptr; + return {nullptr, nullptr}; } auto current_mount = m_mount_table.find_exact_mount("/"); @@ -225,7 +226,7 @@ namespace kernel::filesystem auto found_inode = current_fs->lookup(current_dentry->get_inode(), part.view()); if (!found_inode) { - return nullptr; + return {nullptr, nullptr}; } next_dentry = kstd::make_shared(current_dentry, found_inode, part.view()); @@ -246,7 +247,7 @@ namespace kernel::filesystem { if (symlink_counter++ > constants::symloop_max) { - return nullptr; + return {nullptr, nullptr}; } kstd::vector buffer(constants::symlink_max_path_length); @@ -269,9 +270,19 @@ namespace kernel::filesystem current_dentry = next_dentry; } + return {current_dentry, current_mount}; + } + + auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr + { + return resolve_path_internal(path).first; + } - return current_dentry; + auto vfs::find_mount(std::string_view path) -> kstd::shared_ptr + { + return resolve_path_internal(path).second; } + } // namespace kernel::filesystem namespace kernel::tests::filesystem::vfs -- cgit v1.2.3 From f2b46c2d9cd9b1bf4b5ec5f35593ae60b3740d0c Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Thu, 14 May 2026 17:24:49 +0200 Subject: Document design rationale for resolve_path return type --- kernel/include/kernel/filesystem/vfs.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 2aa1dd7..7a6ebf9 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -79,6 +79,16 @@ namespace kernel::filesystem vfs() = default; auto init_internal() -> void; + /** + * Note: Resolving a dentry requires traversing mount points; since the + * associated 'mount' object is discovered as a byproduct of this + * traversal, we return it alongside the dentry to avoid redundant + * lookups in callers that require mount context. + * + * If only one component is needed, the convenience wrappers can be used: + * - resolve_path() for the dentry only. + * - find_mount() for the mount context only. + */ [[nodiscard]] auto resolve_path_internal(std::string_view path) -> std::pair, kstd::shared_ptr>; [[nodiscard]] auto resolve_path(std::string_view path) -> kstd::shared_ptr; -- cgit v1.2.3 From 146c40b22b834e4bf8d5e1d7256d3071f11d4bf9 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Thu, 14 May 2026 17:33:14 +0200 Subject: Rename mount_point_dentry to target_dentry --- kernel/include/kernel/filesystem/vfs.hpp | 2 +- kernel/src/filesystem/vfs.cpp | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 7a6ebf9..dfb8f0e 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -94,7 +94,7 @@ namespace kernel::filesystem [[nodiscard]] auto resolve_path(std::string_view path) -> kstd::shared_ptr; [[nodiscard]] auto find_mount(std::string_view path) -> kstd::shared_ptr; - auto do_mount_internal(kstd::shared_ptr const & mount_point_dentry, kstd::shared_ptr const & fs) + auto do_mount_internal(kstd::shared_ptr const & target_dentry, kstd::shared_ptr const & fs) -> void; auto graft_persistent_device_fs(kstd::shared_ptr const & device_fs) -> void; diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 52ffcc8..31ffa42 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -53,9 +53,9 @@ namespace kernel::filesystem auto device_fs = kstd::make_shared(); device_fs->mount(nullptr); - if (auto dev_mount_point_dentry = resolve_path("/dev")) + if (auto dev_target_dentry = resolve_path("/dev")) { - do_mount_internal(dev_mount_point_dentry, device_fs); + do_mount_internal(dev_target_dentry, device_fs); } else { @@ -98,13 +98,13 @@ namespace kernel::filesystem return operation_result::invalid_path; } - if (auto mount_point_dentry = resolve_path(target)) + if (auto target_dentry = resolve_path(target)) { if (auto source_dentry = resolve_path(source)) { if (auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode())) { - do_mount_internal(mount_point_dentry, fs); + do_mount_internal(target_dentry, fs); return operation_result::success; } return operation_result::invalid_filesystem; @@ -135,10 +135,10 @@ namespace kernel::filesystem return operation_result::mount_point_not_found; } - auto vfs::do_mount_internal(kstd::shared_ptr const & mount_point_dentry, - kstd::shared_ptr const & fs) -> void + auto vfs::do_mount_internal(kstd::shared_ptr const & target_dentry, kstd::shared_ptr const & fs) + -> void { - auto parent_mount_dentry = mount_point_dentry->find_mount_root_dentry(); + auto parent_mount_dentry = target_dentry->find_mount_root_dentry(); kstd::shared_ptr parent_mount = nullptr; if (parent_mount_dentry) { @@ -146,8 +146,8 @@ namespace kernel::filesystem } auto new_fs_root = - kstd::make_shared(mount_point_dentry->get_parent(), fs->root_inode(), mount_point_dentry->get_name()); - auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, parent_mount); + kstd::make_shared(target_dentry->get_parent(), fs->root_inode(), target_dentry->get_name()); + auto new_mount = kstd::make_shared(target_dentry, new_fs_root, fs, parent_mount); m_mount_table.add_mount(new_mount); } -- cgit v1.2.3 From 4cc120e7dba5c858a3a0f68b63e91e8d7b831701 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Thu, 14 May 2026 18:03:44 +0200 Subject: Refactor do_mount_internal to use target_mount as parameter --- kernel/include/kernel/filesystem/vfs.hpp | 4 ++-- kernel/src/filesystem/vfs.cpp | 30 ++++++++++++++---------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index dfb8f0e..8515af0 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -94,8 +94,8 @@ namespace kernel::filesystem [[nodiscard]] auto resolve_path(std::string_view path) -> kstd::shared_ptr; [[nodiscard]] auto find_mount(std::string_view path) -> kstd::shared_ptr; - auto do_mount_internal(kstd::shared_ptr const & target_dentry, kstd::shared_ptr const & fs) - -> void; + auto do_mount_internal(kstd::shared_ptr const & target_dentry, kstd::shared_ptr const & target_mount, + kstd::shared_ptr const & fs) -> void; auto graft_persistent_device_fs(kstd::shared_ptr const & device_fs) -> void; diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 31ffa42..c395e74 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -47,7 +47,8 @@ namespace kernel::filesystem root_fs->mount(nullptr); auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode(), "/"); - m_mount_table.add_mount(kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, nullptr)); + auto root_mount = kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, nullptr); + m_mount_table.add_mount(root_mount); // mount devfs at /dev (inside rootfs, temporary, will be shadowed) auto device_fs = kstd::make_shared(); @@ -55,7 +56,7 @@ namespace kernel::filesystem if (auto dev_target_dentry = resolve_path("/dev")) { - do_mount_internal(dev_target_dentry, device_fs); + do_mount_internal(dev_target_dentry, root_mount, device_fs); } else { @@ -69,7 +70,7 @@ namespace kernel::filesystem { if (auto root_dentry = resolve_path("/")) { - do_mount_internal(root_dentry, boot_root_fs); + do_mount_internal(root_dentry, root_mount, boot_root_fs); graft_persistent_device_fs(device_fs); } } @@ -98,13 +99,15 @@ namespace kernel::filesystem return operation_result::invalid_path; } - if (auto target_dentry = resolve_path(target)) + auto [target_dentry, target_mount] = resolve_path_internal(target); + + if (target_dentry && target_mount) { if (auto source_dentry = resolve_path(source)) { if (auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode())) { - do_mount_internal(target_dentry, fs); + do_mount_internal(target_dentry, target_mount, fs); return operation_result::success; } return operation_result::invalid_filesystem; @@ -135,25 +138,20 @@ namespace kernel::filesystem return operation_result::mount_point_not_found; } - auto vfs::do_mount_internal(kstd::shared_ptr const & target_dentry, kstd::shared_ptr const & fs) + auto vfs::do_mount_internal(kstd::shared_ptr const & target_dentry, + kstd::shared_ptr const & target_mount, kstd::shared_ptr const & fs) -> void { - auto parent_mount_dentry = target_dentry->find_mount_root_dentry(); - kstd::shared_ptr parent_mount = nullptr; - if (parent_mount_dentry) - { - parent_mount = m_mount_table.find_exact_mount(parent_mount_dentry->get_absolute_path().view()); - } - auto new_fs_root = kstd::make_shared(target_dentry->get_parent(), fs->root_inode(), target_dentry->get_name()); - auto new_mount = kstd::make_shared(target_dentry, new_fs_root, fs, parent_mount); + auto new_mount = kstd::make_shared(target_dentry, new_fs_root, fs, target_mount); m_mount_table.add_mount(new_mount); } auto vfs::graft_persistent_device_fs(kstd::shared_ptr const & device_fs) -> void { - if (auto new_root_dentry = resolve_path("/")) + auto [new_root_dentry, root_mount] = resolve_path_internal("/"); + if (new_root_dentry && root_mount) { auto dev_dentry = new_root_dentry->find_child("dev"); if (!dev_dentry) @@ -162,7 +160,7 @@ namespace kernel::filesystem new_root_dentry->add_child(dev_dentry); } - do_mount_internal(dev_dentry, device_fs); + do_mount_internal(dev_dentry, root_mount, device_fs); } } -- cgit v1.2.3 From 5b97abfc9ce1032a0e42be213906b1abd51355dd Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Thu, 14 May 2026 18:09:17 +0200 Subject: Remove unneeded functionality --- kernel/include/kernel/filesystem/dentry.hpp | 9 +-------- kernel/src/filesystem/dentry.cpp | 10 ---------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index 3813f61..925768a 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -24,8 +24,7 @@ namespace kernel::filesystem */ enum class dentry_flags : uint32_t { - is_mount_point = 1 << 0, - is_mount_root = 1 << 1, + is_mount_point = 1 << 0 }; /** @@ -61,12 +60,6 @@ namespace kernel::filesystem */ [[nodiscard]] auto get_absolute_path() const -> kstd::string; - /** - @brief traverse parent dentries until dentry with is_mount_root flag is found. - @return The found dentry. - */ - [[nodiscard]] auto find_mount_root_dentry() const -> kstd::shared_ptr; - /** @brief Add a @p child dentry. @param child The child dentry to add. diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index 7603e11..d963ed7 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -67,16 +67,6 @@ namespace kernel::filesystem return path; } - auto dentry::find_mount_root_dentry() const -> kstd::shared_ptr - { - auto parent = m_parent; - while (parent && !parent->has_flag(dentry_flags::is_mount_root)) - { - parent = parent->get_parent(); - } - return parent; - } - auto dentry::add_child(kstd::shared_ptr const & child) -> void { m_children.push_back(child); -- cgit v1.2.3 From fe0b38db0f8848da8cf28bb883e01bbbb889dd0a Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Thu, 14 May 2026 21:02:25 +0200 Subject: Remove unneeded functionality part 2 --- kernel/src/filesystem/mount_table.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index e0cf140..4b61fb0 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -26,10 +26,6 @@ namespace kernel::filesystem { mount_dentry->set_flag(dentry::dentry_flags::is_mount_point); } - if (auto root_dentry = mount->get_root_dentry()) - { - root_dentry->set_flag(dentry::dentry_flags::is_mount_root); - } } auto mount_table::remove_mount(std::string_view path) -> operation_result -- cgit v1.2.3 From 963c926c68aac4606d80743aca8e7b052eee7efe Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Fri, 15 May 2026 11:42:33 +0200 Subject: Rename mount_table method from find_exact_mount to find_mount --- kernel/include/kernel/filesystem/mount_table.hpp | 2 +- kernel/src/filesystem/mount_table.cpp | 4 ++-- kernel/src/filesystem/mount_table.tests.cpp | 10 +++++----- kernel/src/filesystem/vfs.cpp | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp index 59b9503..8bebfe2 100644 --- a/kernel/include/kernel/filesystem/mount_table.hpp +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -43,7 +43,7 @@ namespace kernel::filesystem @param path The path to match against the mount paths in the table. @return A pointer to the mount with the exact matching path, or a null pointer if no mount matches the path. */ - [[nodiscard]] auto find_exact_mount(std::string_view path) const -> kstd::shared_ptr; + [[nodiscard]] auto find_mount(std::string_view path) const -> kstd::shared_ptr; private: [[nodiscard]] auto has_child_mounts(kstd::shared_ptr const & parent_mount) const -> bool; diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 4b61fb0..78ac727 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -31,7 +31,7 @@ namespace kernel::filesystem auto mount_table::remove_mount(std::string_view path) -> operation_result { // TODO BA-FS26 check wheter something is open in this mount - // TODO BA-FS26 nearly the same code is in find_exact_mount -> refactor to avoid code duplication + // TODO BA-FS26 nearly the same code is in find_mount -> refactor to avoid code duplication auto mount_range = std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }); auto mount_it = mount_range.begin(); @@ -52,7 +52,7 @@ namespace kernel::filesystem return operation_result::removed; } - auto mount_table::find_exact_mount(std::string_view path) const -> kstd::shared_ptr + auto mount_table::find_mount(std::string_view path) const -> kstd::shared_ptr { auto mount_range = std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }); diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp index 4ae8711..f22b25e 100644 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -58,14 +58,14 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("finding mounts by exact valid path returns the correct mount") { - REQUIRE(table.find_exact_mount("/") == mount1); - REQUIRE(table.find_exact_mount("/mnt") == mount2); + REQUIRE(table.find_mount("/") == mount1); + REQUIRE(table.find_mount("/mnt") == mount2); } THEN("finding mounts by exact invalid path returns null") { - REQUIRE(table.find_exact_mount("/nonexistent") == nullptr); - REQUIRE(table.find_exact_mount("/mnt/file") == nullptr); + REQUIRE(table.find_mount("/nonexistent") == nullptr); + REQUIRE(table.find_mount("/mnt/file") == nullptr); } THEN("removing a mount that has no child mounts succeeds") @@ -103,7 +103,7 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] THEN("finding mounts by exact valid path returns the correct mount") { - REQUIRE(table.find_exact_mount("/") == mount2); + REQUIRE(table.find_mount("/") == mount2); } THEN("removing the topmost mount with the same path succeeds") diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index c395e74..9b0440d 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -171,7 +171,7 @@ namespace kernel::filesystem return {nullptr, nullptr}; } - auto current_mount = m_mount_table.find_exact_mount("/"); + auto current_mount = m_mount_table.find_mount("/"); if (!current_mount) { kapi::system::panic("[FILESYSTEM] no root mount found."); @@ -232,7 +232,7 @@ namespace kernel::filesystem } else if (next_dentry->has_flag(dentry::dentry_flags::is_mount_point)) { - current_mount = m_mount_table.find_exact_mount(next_dentry->get_absolute_path().view()); + current_mount = m_mount_table.find_mount(next_dentry->get_absolute_path().view()); if (!current_mount) { kapi::system::panic("[FILESYSTEM] mount for dentry with mounted flag not found."); @@ -260,7 +260,7 @@ namespace kernel::filesystem if (path::is_valid_absolute_path(symbolic_link_path)) { - current_mount = m_mount_table.find_exact_mount("/"); + current_mount = m_mount_table.find_mount("/"); current_dentry = current_mount->get_root_dentry(); } continue; -- cgit v1.2.3 From 66d0e68376c9ad3e2b13f6ff8d999a0c85bda1a4 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 15 May 2026 15:47:06 +0200 Subject: renaming --- kernel/include/kernel/filesystem/vfs.hpp | 5 +++-- kernel/src/filesystem/vfs.cpp | 28 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 8515af0..7e2eae7 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -94,8 +94,9 @@ namespace kernel::filesystem [[nodiscard]] auto resolve_path(std::string_view path) -> kstd::shared_ptr; [[nodiscard]] auto find_mount(std::string_view path) -> kstd::shared_ptr; - auto do_mount_internal(kstd::shared_ptr const & target_dentry, kstd::shared_ptr const & target_mount, - kstd::shared_ptr const & fs) -> void; + auto do_mount_internal(kstd::shared_ptr const & mount_point_dentry, + kstd::shared_ptr const & parent_mount, kstd::shared_ptr const & fs) + -> void; auto graft_persistent_device_fs(kstd::shared_ptr const & device_fs) -> void; diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 9b0440d..77ae015 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -54,9 +54,9 @@ namespace kernel::filesystem auto device_fs = kstd::make_shared(); device_fs->mount(nullptr); - if (auto dev_target_dentry = resolve_path("/dev")) + if (auto dev_mount_point_dentry = resolve_path("/dev")) { - do_mount_internal(dev_target_dentry, root_mount, device_fs); + do_mount_internal(dev_mount_point_dentry, root_mount, device_fs); } else { @@ -99,15 +99,15 @@ namespace kernel::filesystem return operation_result::invalid_path; } - auto [target_dentry, target_mount] = resolve_path_internal(target); + auto [mount_point_dentry, mount_context] = resolve_path_internal(target); - if (target_dentry && target_mount) + if (mount_point_dentry && mount_context) { if (auto source_dentry = resolve_path(source)) { if (auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode())) { - do_mount_internal(target_dentry, target_mount, fs); + do_mount_internal(mount_point_dentry, mount_context, fs); return operation_result::success; } return operation_result::invalid_filesystem; @@ -138,26 +138,26 @@ namespace kernel::filesystem return operation_result::mount_point_not_found; } - auto vfs::do_mount_internal(kstd::shared_ptr const & target_dentry, - kstd::shared_ptr const & target_mount, kstd::shared_ptr const & fs) + auto vfs::do_mount_internal(kstd::shared_ptr const & mount_point_dentry, + kstd::shared_ptr const & parent_mount, kstd::shared_ptr const & fs) -> void { auto new_fs_root = - kstd::make_shared(target_dentry->get_parent(), fs->root_inode(), target_dentry->get_name()); - auto new_mount = kstd::make_shared(target_dentry, new_fs_root, fs, target_mount); + kstd::make_shared(mount_point_dentry->get_parent(), fs->root_inode(), mount_point_dentry->get_name()); + auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, parent_mount); m_mount_table.add_mount(new_mount); } auto vfs::graft_persistent_device_fs(kstd::shared_ptr const & device_fs) -> void { - auto [new_root_dentry, root_mount] = resolve_path_internal("/"); - if (new_root_dentry && root_mount) + auto [root_mount_point_dentry, root_mount] = resolve_path_internal("/"); + if (root_mount_point_dentry && root_mount) { - auto dev_dentry = new_root_dentry->find_child("dev"); + auto dev_dentry = root_mount_point_dentry->find_child("dev"); if (!dev_dentry) { - dev_dentry = kstd::make_shared(new_root_dentry, device_fs->root_inode(), "dev"); - new_root_dentry->add_child(dev_dentry); + dev_dentry = kstd::make_shared(root_mount_point_dentry, device_fs->root_inode(), "dev"); + root_mount_point_dentry->add_child(dev_dentry); } do_mount_internal(dev_dentry, root_mount, device_fs); -- cgit v1.2.3 From 95ff59017db74a6988f791ca9f122254dd743541 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 15 May 2026 16:17:50 +0200 Subject: refactor find_mount_iterator to avoid code duplication --- kernel/include/kernel/filesystem/mount_table.hpp | 2 ++ kernel/src/filesystem/mount_table.cpp | 17 +++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp index 8bebfe2..742c928 100644 --- a/kernel/include/kernel/filesystem/mount_table.hpp +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -47,6 +47,8 @@ namespace kernel::filesystem private: [[nodiscard]] auto has_child_mounts(kstd::shared_ptr const & parent_mount) const -> bool; + [[nodiscard]] auto find_mount_iterator(std::string_view path) const + -> kstd::vector>::const_iterator; kstd::vector> m_mounts; }; diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 78ac727..b582fd9 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -31,11 +31,7 @@ namespace kernel::filesystem auto mount_table::remove_mount(std::string_view path) -> operation_result { // TODO BA-FS26 check wheter something is open in this mount - // TODO BA-FS26 nearly the same code is in find_mount -> refactor to avoid code duplication - auto mount_range = - std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }); - auto mount_it = mount_range.begin(); - + auto mount_it = find_mount_iterator(path); if (mount_it == m_mounts.end()) { return operation_result::mount_not_found; @@ -54,9 +50,14 @@ namespace kernel::filesystem auto mount_table::find_mount(std::string_view path) const -> kstd::shared_ptr { - auto mount_range = - std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }); - auto mount_it = mount_range.begin(); + auto mount_it = find_mount_iterator(path); return (mount_it != m_mounts.end()) ? *mount_it : nullptr; } + + auto mount_table::find_mount_iterator(std::string_view path) const + -> kstd::vector>::const_iterator + { + return std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }) + .begin(); + } } // namespace kernel::filesystem \ No newline at end of file -- cgit v1.2.3 From 1d647adb1ba20121eeb5c8e4470f48b2e972b3d4 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 15 May 2026 16:50:55 +0200 Subject: Mount can only be unmounted if no references are present, increment references on open file and decrement on close file --- kernel/include/kernel/filesystem/mount.hpp | 19 ++++++++++++++++- kernel/include/kernel/filesystem/mount_table.hpp | 3 ++- .../kernel/filesystem/open_file_descriptor.hpp | 8 +++++++- kernel/include/kernel/filesystem/vfs.hpp | 8 ++++++++ kernel/kapi/filesystem.cpp | 10 ++++++++- kernel/src/filesystem/mount.cpp | 22 ++++++++++++++++++++ kernel/src/filesystem/mount_table.cpp | 5 ++++- kernel/src/filesystem/open_file_descriptor.cpp | 7 ++++++- .../src/filesystem/open_file_descriptor.tests.cpp | 24 +++++++++++----------- kernel/src/filesystem/vfs.cpp | 17 ++++++++++++++- 10 files changed, 104 insertions(+), 19 deletions(-) diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index f920891..fb5a627 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -54,12 +54,29 @@ namespace kernel::filesystem */ [[nodiscard]] auto get_parent_mount() const -> kstd::shared_ptr const &; + /** + @brief Increment the reference count for this mount. + */ + auto increment_ref_count() -> void; + + /** + @brief Decrement the reference count for this mount. + @return True if the reference count reached zero, false otherwise. + */ + auto decrement_ref_count() -> bool; + + /** + @brief Check if the mount is ready to be unmounted. + @return True if the mount is ready to be unmounted, false otherwise. + */ + [[nodiscard]] auto is_ready_to_unmount() const -> bool; + private: kstd::shared_ptr m_mount_dentry; kstd::shared_ptr m_root_dentry; kstd::shared_ptr m_filesystem{}; kstd::shared_ptr m_parent_mount{}; - std::atomic_uint32_t m_ref_count{0}; // TODO BA-FS26 + std::atomic_size_t m_ref_count; }; } // namespace kernel::filesystem diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp index 742c928..4f2d1b7 100644 --- a/kernel/include/kernel/filesystem/mount_table.hpp +++ b/kernel/include/kernel/filesystem/mount_table.hpp @@ -22,7 +22,8 @@ namespace kernel::filesystem { removed = 0, has_child_mounts = -1, - mount_not_found = -2 + mount_not_found = -2, + cannot_be_unmounted = -3 }; /** diff --git a/kernel/include/kernel/filesystem/open_file_descriptor.hpp b/kernel/include/kernel/filesystem/open_file_descriptor.hpp index 823fe13..7ca7350 100644 --- a/kernel/include/kernel/filesystem/open_file_descriptor.hpp +++ b/kernel/include/kernel/filesystem/open_file_descriptor.hpp @@ -50,7 +50,13 @@ namespace kernel::filesystem @brief Returns the current file offset for this open file descriptor. @return The current file offset in bytes. */ - [[nodiscard]] auto offset() const -> size_t; + [[nodiscard]] auto get_offset() const -> size_t; + + /** + @brief Return a reference to the dentry associated with this open file descriptor. + @return A reference to the associated dentry. + */ + [[nodiscard]] auto get_dentry() const -> kstd::shared_ptr const &; private: kstd::shared_ptr m_dentry; diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 7e2eae7..48b99b2 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -33,6 +33,7 @@ namespace kernel::filesystem mount_point_not_found = -3, unmount_failed = -4, invalid_filesystem = -5, + close_failed = -6 }; /** @@ -60,6 +61,13 @@ namespace kernel::filesystem */ auto open(std::string_view path) -> kstd::shared_ptr; + /** + @brief Close a file by its associated @p dentry. + @param dentry The dentry of the file to close. + @return The result of the close operation. + */ + auto close(kstd::shared_ptr const & dentry) -> operation_result; + /** @brief Mount a @p source path to a specific @p target path. @param source The source of the filesystem to mount. diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index 77d7eb0..1782da5 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -44,7 +44,15 @@ namespace kapi::filesystem auto close(int file_descriptor) -> int { - return kernel::filesystem::open_file_table::get().remove_file(file_descriptor); + if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().get_file(file_descriptor)) + { + if (kernel::filesystem::vfs::get().close(open_file_descriptor->get_dentry()) == + kernel::filesystem::vfs::operation_result::success) + { + return kernel::filesystem::open_file_table::get().remove_file(file_descriptor); + } + } + return -1; } auto read(int file_descriptor, void * buffer, size_t size) -> ssize_t diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index 749c86a..3016509 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -18,6 +18,7 @@ namespace kernel::filesystem , m_root_dentry(root_dentry) , m_filesystem(fs) , m_parent_mount(parent_mount) + , m_ref_count(0) { if (!m_filesystem) { @@ -53,4 +54,25 @@ namespace kernel::filesystem { return m_parent_mount; } + + auto mount::increment_ref_count() -> void + { + m_ref_count += 1; + } + + auto mount::decrement_ref_count() -> bool + { + if (m_ref_count == 0) + { + return false; + } + + m_ref_count -= 1; + return true; + } + + auto mount::is_ready_to_unmount() const -> bool + { + return m_ref_count == 0; + } } // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index b582fd9..9951590 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -30,7 +30,6 @@ namespace kernel::filesystem auto mount_table::remove_mount(std::string_view path) -> operation_result { - // TODO BA-FS26 check wheter something is open in this mount auto mount_it = find_mount_iterator(path); if (mount_it == m_mounts.end()) { @@ -38,6 +37,10 @@ namespace kernel::filesystem } auto const & mount = *mount_it; + if (!mount->is_ready_to_unmount()) + { + return operation_result::cannot_be_unmounted; + } if (has_child_mounts(mount)) { return operation_result::has_child_mounts; diff --git a/kernel/src/filesystem/open_file_descriptor.cpp b/kernel/src/filesystem/open_file_descriptor.cpp index 27e6449..ebaabef 100644 --- a/kernel/src/filesystem/open_file_descriptor.cpp +++ b/kernel/src/filesystem/open_file_descriptor.cpp @@ -33,9 +33,14 @@ namespace kernel::filesystem return written_bytes; } - auto open_file_descriptor::offset() const -> size_t + auto open_file_descriptor::get_offset() const -> size_t { return m_offset; } + auto open_file_descriptor::get_dentry() const -> kstd::shared_ptr const & + { + return m_dentry; + } + } // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/open_file_descriptor.tests.cpp b/kernel/src/filesystem/open_file_descriptor.tests.cpp index 8c24cf0..1910b8b 100644 --- a/kernel/src/filesystem/open_file_descriptor.tests.cpp +++ b/kernel/src/filesystem/open_file_descriptor.tests.cpp @@ -26,7 +26,7 @@ SCENARIO("Open file descriptor construction", "[filesystem][open_file_descriptor THEN("the initial offset is zero") { - REQUIRE(file_descriptor.offset() == 0); + REQUIRE(file_descriptor.get_offset() == 0); } } } @@ -42,29 +42,29 @@ SCENARIO("Open file descriptor read/write offset management", "[filesystem][open THEN("the offset is updated correctly after reads") { REQUIRE(file_descriptor.read(nullptr, 100) == 100); - REQUIRE(file_descriptor.offset() == 100); + REQUIRE(file_descriptor.get_offset() == 100); REQUIRE(file_descriptor.read(nullptr, 50) == 50); - REQUIRE(file_descriptor.offset() == 150); + REQUIRE(file_descriptor.get_offset() == 150); } THEN("the offset is updated correctly after writes") { REQUIRE(file_descriptor.write(nullptr, 200) == 200); - REQUIRE(file_descriptor.offset() == 200); + REQUIRE(file_descriptor.get_offset() == 200); REQUIRE(file_descriptor.write(nullptr, 25) == 25); - REQUIRE(file_descriptor.offset() == 225); + REQUIRE(file_descriptor.get_offset() == 225); } THEN("reads and writes both update the same offset") { REQUIRE(file_descriptor.read(nullptr, 10) == 10); - REQUIRE(file_descriptor.offset() == 10); + REQUIRE(file_descriptor.get_offset() == 10); REQUIRE(file_descriptor.write(nullptr, 20) == 20); - REQUIRE(file_descriptor.offset() == 30); + REQUIRE(file_descriptor.get_offset() == 30); REQUIRE(file_descriptor.read(nullptr, 5) == 5); - REQUIRE(file_descriptor.offset() == 35); + REQUIRE(file_descriptor.get_offset() == 35); REQUIRE(file_descriptor.write(nullptr, 15) == 15); - REQUIRE(file_descriptor.offset() == 50); + REQUIRE(file_descriptor.get_offset() == 50); } } } @@ -89,7 +89,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Ope kstd::vector buffer(32); auto bytes_read = ofd->read(buffer.data(), buffer.size()); REQUIRE(bytes_read == 7); - REQUIRE(ofd->offset() == 7); + REQUIRE(ofd->get_offset() == 7); std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; REQUIRE(buffer_as_str == "info_1\n"); @@ -100,11 +100,11 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Ope kstd::vector buffer(4); auto bytes_read_1 = ofd->read(buffer.data(), buffer.size() / 2); REQUIRE(bytes_read_1 == buffer.size() / 2); - REQUIRE(ofd->offset() == buffer.size() / 2); + REQUIRE(ofd->get_offset() == buffer.size() / 2); auto bytes_read_2 = ofd->read(buffer.data() + buffer.size() / 2, buffer.size() / 2); REQUIRE(bytes_read_2 == buffer.size() / 2); - REQUIRE(ofd->offset() == buffer.size()); + REQUIRE(ofd->get_offset() == buffer.size()); std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read_1 + bytes_read_2}; REQUIRE(buffer_as_str == "info"); diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 77ae015..3b3d6ff 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -89,7 +89,22 @@ namespace kernel::filesystem auto vfs::open(std::string_view path) -> kstd::shared_ptr { - return resolve_path(path); + auto [dentry, mount] = resolve_path_internal(path); + mount->increment_ref_count(); + return dentry; + } + + auto vfs::close(kstd::shared_ptr const & dentry) -> operation_result + { + if (auto mount = find_mount(dentry->get_absolute_path().view())) + { + if (mount->decrement_ref_count()) + { + return operation_result::success; + } + return operation_result::close_failed; + } + return operation_result::invalid_path; } auto vfs::do_mount(std::string_view source, std::string_view target) -> operation_result -- cgit v1.2.3 From 0e279db4e1b799c4db0cc7c714d57686e3de7089 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 15 May 2026 22:36:05 +0200 Subject: refactoring rootfs, no separate child management needed --- .../kernel/filesystem/rootfs/filesystem.hpp | 2 +- kernel/include/kernel/filesystem/rootfs/inode.hpp | 30 +------------- kernel/src/filesystem/rootfs/filesystem.cpp | 9 +---- kernel/src/filesystem/rootfs/filesystem.tests.cpp | 7 ---- kernel/src/filesystem/rootfs/inode.cpp | 19 --------- kernel/src/filesystem/rootfs/inode.tests.cpp | 47 ---------------------- kernel/src/filesystem/vfs.cpp | 10 +---- 7 files changed, 5 insertions(+), 119 deletions(-) diff --git a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp index cc778d8..f99440b 100644 --- a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp @@ -31,7 +31,7 @@ namespace kernel::filesystem::rootfs @brief Looks up an inode by @p name within a @p parent directory. @param parent The parent directory inode. @param name The name of the inode to look up. - @return A pointer to the found inode, or a null pointer if not found. + @return Always returns nullptr. */ auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr override; diff --git a/kernel/include/kernel/filesystem/rootfs/inode.hpp b/kernel/include/kernel/filesystem/rootfs/inode.hpp index 58035ea..442dc8a 100644 --- a/kernel/include/kernel/filesystem/rootfs/inode.hpp +++ b/kernel/include/kernel/filesystem/rootfs/inode.hpp @@ -8,16 +8,10 @@ #include #include -#include -#include - namespace kernel::filesystem::rootfs { /** - @brief Represents an inode in the rootfs filesystem. This inode represents a directory in the root filesystem and - maintains a list of child inodes corresponding to files and subdirectories within the root directory. The rootfs inode - provides methods for reading and writing data (which are no-ops for the root directory), as well as adding and looking - up child inodes by name. + @brief Represents an inode in the rootfs filesystem. */ struct inode : kernel::filesystem::inode { @@ -38,28 +32,6 @@ namespace kernel::filesystem::rootfs @return Number of bytes written (always 0 because writes are not supported for this inode). */ auto write(void const * buffer, size_t offset, size_t size) -> size_t override; - - /** - @brief Adds a child inode to the rootfs directory inode with the specified @p name. - @param name The name of the child inode. - */ - auto add_child(std::string_view name) -> void; - - /** - @brief Looks up a child inode by @p name. - @param name The name of the child inode to look up. - @return A pointer to the found child inode, or a null pointer if not found. - */ - auto lookup_child(std::string_view name) -> kstd::shared_ptr; - - /** - @brief Check if this inode represents a directory. - @return returns true, since this inode represents the root directory in the rootfs filesystem. - */ - [[nodiscard]] auto is_directory() const -> bool override; - - private: - kstd::vector>> m_children; }; } // namespace kernel::filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/filesystem.cpp b/kernel/src/filesystem/rootfs/filesystem.cpp index 6187c3c..d49e237 100644 --- a/kernel/src/filesystem/rootfs/filesystem.cpp +++ b/kernel/src/filesystem/rootfs/filesystem.cpp @@ -11,18 +11,13 @@ namespace kernel::filesystem::rootfs { auto filesystem::mount(kstd::shared_ptr const &) -> operation_result { - auto rfs_inode = kstd::make_shared(); - rfs_inode->add_child("dev"); - m_root_inode = rfs_inode; - + m_root_inode = kstd::make_shared(); return operation_result::success; } - auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) + auto filesystem::lookup(kstd::shared_ptr const &, std::string_view) -> kstd::shared_ptr { - if (auto * rfs_inode = static_cast(parent.get())) - return rfs_inode->lookup_child(name); return nullptr; } } // namespace kernel::filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/filesystem.tests.cpp b/kernel/src/filesystem/rootfs/filesystem.tests.cpp index 81ac9e4..ae320e9 100644 --- a/kernel/src/filesystem/rootfs/filesystem.tests.cpp +++ b/kernel/src/filesystem/rootfs/filesystem.tests.cpp @@ -21,13 +21,6 @@ SCENARIO("Rootfs filesystem mount and lookup", "[filesystem][rootfs][filesystem] REQUIRE(fs.root_inode() != nullptr); } - THEN("looking up the 'dev' directory returns a valid inode") - { - auto dev_inode = fs.lookup(fs.root_inode(), "dev"); - REQUIRE(dev_inode != nullptr); - REQUIRE(dev_inode->is_directory()); - } - THEN("looking up a non-existent directory returns null") { auto non_existent_inode_1 = fs.lookup(fs.root_inode(), ""); diff --git a/kernel/src/filesystem/rootfs/inode.cpp b/kernel/src/filesystem/rootfs/inode.cpp index d099676..2ad4815 100644 --- a/kernel/src/filesystem/rootfs/inode.cpp +++ b/kernel/src/filesystem/rootfs/inode.cpp @@ -5,10 +5,7 @@ #include #include -#include #include -#include -#include namespace kernel::filesystem::rootfs { @@ -21,20 +18,4 @@ namespace kernel::filesystem::rootfs { return 0; } - - auto inode::add_child(std::string_view name) -> void - { - m_children.push_back(std::make_pair(kstd::string{name}, kstd::make_shared())); - } - - auto inode::lookup_child(std::string_view name) -> kstd::shared_ptr - { - auto it = std::ranges::find_if(m_children, [&](auto const & pair) { return pair.first == name; }); - return (it != m_children.end()) ? it->second : nullptr; - } - - auto inode::is_directory() const -> bool - { - return true; - } } // namespace kernel::filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/inode.tests.cpp b/kernel/src/filesystem/rootfs/inode.tests.cpp index 7cc217f..f4b634f 100644 --- a/kernel/src/filesystem/rootfs/inode.tests.cpp +++ b/kernel/src/filesystem/rootfs/inode.tests.cpp @@ -6,53 +6,6 @@ #include -SCENARIO("Rootfs inode creation", "[filesystem][rootfs][inode]") -{ - GIVEN("a rootfs inode") - { - auto inode = kernel::filesystem::rootfs::inode{}; - - THEN("the inode has the correct kind") - { - REQUIRE(inode.is_directory()); - REQUIRE_FALSE(inode.is_device()); - REQUIRE_FALSE(inode.is_regular()); - REQUIRE_FALSE(inode.is_symbolic_link()); - } - - THEN("the inode has no children") - { - REQUIRE(inode.lookup_child("child") == nullptr); - } - } -} - -SCENARIO("Rootfs inode child management", "[filesystem][rootfs][inode]") -{ - GIVEN("a rootfs inode") - { - auto inode = kernel::filesystem::rootfs::inode{}; - - WHEN("adding a child inode") - { - inode.add_child("child"); - inode.add_child("another child"); - - THEN("the child can be looked up by name") - { - auto child_inode = inode.lookup_child("child"); - REQUIRE(child_inode != nullptr); - REQUIRE(child_inode->is_directory()); - } - - THEN("looking up a non-existent child returns null") - { - REQUIRE(inode.lookup_child("nonexistent") == nullptr); - } - } - } -} - SCENARIO("Rootfs inode read/write", "[filesystem][rootfs][inode]") { GIVEN("a rootfs inode") diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 3b3d6ff..67671e2 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -53,15 +53,7 @@ namespace kernel::filesystem // mount devfs at /dev (inside rootfs, temporary, will be shadowed) auto device_fs = kstd::make_shared(); device_fs->mount(nullptr); - - if (auto dev_mount_point_dentry = resolve_path("/dev")) - { - do_mount_internal(dev_mount_point_dentry, root_mount, device_fs); - } - else - { - kapi::system::panic("[FILESYSTEM] failed to resolve /dev for initial devfs mount."); - } + graft_persistent_device_fs(device_fs); // mount boot fs at / (shadows rootfs), re-graft devfs if (auto boot_device_dentry = resolve_path("/dev/ram0")) -- cgit v1.2.3 From 1f9fe3cf18b561749cfbdb2db8ab7572ddc40c03 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 15 May 2026 22:38:24 +0200 Subject: uniform interface for open and close --- kernel/include/kernel/filesystem/vfs.hpp | 6 +++--- kernel/kapi/filesystem.cpp | 2 +- kernel/src/filesystem/vfs.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 48b99b2..0058d04 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -62,11 +62,11 @@ namespace kernel::filesystem auto open(std::string_view path) -> kstd::shared_ptr; /** - @brief Close a file by its associated @p dentry. - @param dentry The dentry of the file to close. + @brief Close a file by its associated @p path. + @param path The path to the file to close. @return The result of the close operation. */ - auto close(kstd::shared_ptr const & dentry) -> operation_result; + auto close(std::string_view path) -> operation_result; /** @brief Mount a @p source path to a specific @p target path. diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index 1782da5..53a71be 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -46,7 +46,7 @@ namespace kapi::filesystem { if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().get_file(file_descriptor)) { - if (kernel::filesystem::vfs::get().close(open_file_descriptor->get_dentry()) == + if (kernel::filesystem::vfs::get().close(open_file_descriptor->get_dentry()->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success) { return kernel::filesystem::open_file_table::get().remove_file(file_descriptor); diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 67671e2..de19d38 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -86,9 +86,9 @@ namespace kernel::filesystem return dentry; } - auto vfs::close(kstd::shared_ptr const & dentry) -> operation_result + auto vfs::close(std::string_view path) -> operation_result { - if (auto mount = find_mount(dentry->get_absolute_path().view())) + if (auto mount = find_mount(path)) { if (mount->decrement_ref_count()) { -- cgit v1.2.3 From 2396fe6174ec875ba12dc135ab18f84550c07e9a Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 15 May 2026 22:40:52 +0200 Subject: avoid nullptr access --- kernel/src/filesystem/vfs.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index de19d38..f6eae25 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -82,6 +82,10 @@ namespace kernel::filesystem auto vfs::open(std::string_view path) -> kstd::shared_ptr { auto [dentry, mount] = resolve_path_internal(path); + if (!dentry || !mount) + { + return nullptr; + } mount->increment_ref_count(); return dentry; } -- cgit v1.2.3 From 16ccdee935d3b14edf93eea5a135e413b2fd47b5 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Fri, 15 May 2026 22:59:23 +0200 Subject: rootfs inode is a directory inode --- kernel/include/kernel/filesystem/rootfs/inode.hpp | 6 ++++++ kernel/src/filesystem/rootfs/inode.cpp | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/kernel/include/kernel/filesystem/rootfs/inode.hpp b/kernel/include/kernel/filesystem/rootfs/inode.hpp index 442dc8a..2671207 100644 --- a/kernel/include/kernel/filesystem/rootfs/inode.hpp +++ b/kernel/include/kernel/filesystem/rootfs/inode.hpp @@ -32,6 +32,12 @@ namespace kernel::filesystem::rootfs @return Number of bytes written (always 0 because writes are not supported for this inode). */ auto write(void const * buffer, size_t offset, size_t size) -> size_t override; + + /** + @brief Check if this inode represents a directory. + @return returns true, since this inode represents the / directory in the rootfs filesystem. + */ + [[nodiscard]] auto is_directory() const -> bool override; }; } // namespace kernel::filesystem::rootfs diff --git a/kernel/src/filesystem/rootfs/inode.cpp b/kernel/src/filesystem/rootfs/inode.cpp index 2ad4815..dbe7948 100644 --- a/kernel/src/filesystem/rootfs/inode.cpp +++ b/kernel/src/filesystem/rootfs/inode.cpp @@ -18,4 +18,9 @@ namespace kernel::filesystem::rootfs { return 0; } + + auto inode::is_directory() const -> bool + { + return true; + } } // namespace kernel::filesystem::rootfs -- cgit v1.2.3 From ba2f62972823df320e05dea7080adf658c2977b3 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 16 May 2026 13:43:38 +0200 Subject: fix tests, all files must be closed before unmounting --- kernel/src/filesystem/vfs.tests.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index eaffbc9..28782ec 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -105,6 +105,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); REQUIRE(mounted_monkey_1 != nullptr); + REQUIRE(vfs.close(mounted_monkey_1->get_absolute_path().view()) == + kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); @@ -126,6 +128,11 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(mounted_monkey_1 != nullptr); REQUIRE(mounted_fish1 != nullptr); + REQUIRE(vfs.close(mounted_monkey_1->get_absolute_path().view()) == + kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_fish1->get_absolute_path().view()) == + kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); REQUIRE(vfs.unmount("/information/monkey_house/infrastructure") == kernel::filesystem::vfs::operation_result::success); @@ -140,6 +147,9 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto mounted_tickets = vfs.open("/information/entrance/tickets.txt"); REQUIRE(mounted_tickets != nullptr); + REQUIRE(vfs.close(mounted_tickets->get_absolute_path().view()) == + kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); mounted_tickets = vfs.open("/information/entrance/tickets.txt"); REQUIRE(mounted_tickets == nullptr); @@ -161,6 +171,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto water = vfs.open("/monkey_house/infrastructure/water.txt"); REQUIRE(water != nullptr); + REQUIRE(vfs.close(water->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); info_1 = vfs.open("/information/info_1.txt"); @@ -180,6 +192,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto water = vfs.open("/monkey_house/infrastructure/water.txt"); REQUIRE(water != nullptr); + REQUIRE(vfs.close(water->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + auto dev_ram_16 = vfs.open("/dev/ram16"); REQUIRE(dev_ram_16 == nullptr); @@ -196,6 +210,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); + REQUIRE(vfs.close(info_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/dev") == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); @@ -317,6 +333,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "sheep_1"); + REQUIRE(vfs.close(dentry->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(unmounted_sheep_1 == nullptr); @@ -348,6 +366,9 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS buffer_as_str = std::string_view{reinterpret_cast(goat_buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "goat_1"); + REQUIRE(vfs.close(sheep_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(goat_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); REQUIRE(vfs.unmount("/information/stable") == kernel::filesystem::vfs::operation_result::success); @@ -357,6 +378,9 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto still_mounted_sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(still_mounted_sheep_1 != nullptr); + REQUIRE(vfs.close(still_mounted_sheep_1->get_absolute_path().view()) == + kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(unmounted_sheep_1 == nullptr); -- cgit v1.2.3 From 7ecf092ca7ff91dd59e81eda7ef2b05fe837844d Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 16 May 2026 13:53:34 +0200 Subject: add mount tests --- kernel/include/kernel/filesystem/mount.hpp | 7 +++++ kernel/src/filesystem/mount.cpp | 6 +++++ kernel/src/filesystem/mount.tests.cpp | 42 ++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index fb5a627..5d8ea69 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -8,6 +8,7 @@ #include #include +#include namespace kernel::filesystem { @@ -71,6 +72,12 @@ namespace kernel::filesystem */ [[nodiscard]] auto is_ready_to_unmount() const -> bool; + /** + @brief Get the current reference count for this mount. + @return The current reference count. + */ + [[nodiscard]] auto get_ref_count() const -> size_t; + private: kstd::shared_ptr m_mount_dentry; kstd::shared_ptr m_root_dentry; diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index 3016509..1e04083 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -8,6 +8,7 @@ #include #include +#include #include namespace kernel::filesystem @@ -75,4 +76,9 @@ namespace kernel::filesystem { return m_ref_count == 0; } + + auto mount::get_ref_count() const -> size_t + { + return m_ref_count; + } } // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/mount.tests.cpp b/kernel/src/filesystem/mount.tests.cpp index 58e9bab..e7dd709 100644 --- a/kernel/src/filesystem/mount.tests.cpp +++ b/kernel/src/filesystem/mount.tests.cpp @@ -29,6 +29,7 @@ SCENARIO("Mount construction", "[filesystem][mount]") REQUIRE(mount.get_root_dentry() == root_dentry); REQUIRE(mount.get_mount_dentry() == root_dentry); REQUIRE(mount.get_mount_path() == "/"); + REQUIRE(mount.is_ready_to_unmount()); } THEN("the mount has no parent mount") @@ -47,3 +48,44 @@ SCENARIO("Mount construction", "[filesystem][mount]") } } } + +SCENARIO("Mount reference counting", "[filesystem][mount]") +{ + GIVEN("a filesystem and a root dentry") + { + auto fs = kstd::make_shared(); + auto root_inode = kstd::make_shared(); + auto root_dentry = kstd::make_shared(nullptr, root_inode, "/"); + + THEN("reference count can be incremented and decremented, the mount is ready to unmount when the reference " + "count == 0") + { + auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr}; + + mount.increment_ref_count(); + REQUIRE(mount.get_ref_count() == 1); + REQUIRE_FALSE(mount.is_ready_to_unmount()); + + mount.increment_ref_count(); + REQUIRE(mount.get_ref_count() == 2); + REQUIRE_FALSE(mount.is_ready_to_unmount()); + + REQUIRE(mount.decrement_ref_count()); + REQUIRE(mount.get_ref_count() == 1); + REQUIRE_FALSE(mount.is_ready_to_unmount()); + + REQUIRE(mount.decrement_ref_count()); + REQUIRE(mount.get_ref_count() == 0); + REQUIRE(mount.is_ready_to_unmount()); + } + + THEN("decrementing reference count when it is already zero does not decrement it below zero") + { + auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr}; + + REQUIRE_FALSE(mount.decrement_ref_count()); + REQUIRE(mount.get_ref_count() == 0); + REQUIRE(mount.is_ready_to_unmount()); + } + } +} \ No newline at end of file -- cgit v1.2.3 From efc7ba748b977a792188724c461852f01c111957 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 16 May 2026 14:05:49 +0200 Subject: add vfs tests --- kernel/src/filesystem/vfs.cpp | 7 +++---- kernel/src/filesystem/vfs.tests.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index f6eae25..8636d0f 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -140,13 +140,12 @@ namespace kernel::filesystem { return operation_result::success; } - - if (remove_result == mount_table::operation_result::has_child_mounts) + else if (remove_result == mount_table::operation_result::mount_not_found) { - return operation_result::unmount_failed; + return operation_result::mount_point_not_found; } - return operation_result::mount_point_not_found; + return operation_result::unmount_failed; } auto vfs::do_mount_internal(kstd::shared_ptr const & mount_point_dentry, diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 28782ec..648ebb8 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -139,6 +139,36 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); } + THEN("image can be mounted, unmount only if no files are open") + { + REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); + + auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); + REQUIRE(mounted_monkey_1 != nullptr); + + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); + + REQUIRE(vfs.close(mounted_monkey_1->get_absolute_path().view()) == + kernel::filesystem::vfs::operation_result::success); + + REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); + } + + THEN("file with invalid path or not opened file cannot be closed") + { + REQUIRE(vfs.close("invalid_path") == kernel::filesystem::vfs::operation_result::invalid_path); + REQUIRE(vfs.close("/information/info_1.txt") == kernel::filesystem::vfs::operation_result::close_failed); + } + + THEN("file cannot be closed twice") + { + auto info_1 = vfs.open("/information/info_1.txt"); + REQUIRE(info_1 != nullptr); + + REQUIRE(vfs.close(info_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(info_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::close_failed); + } + THEN("images can be stacked mounted and correct file system is unmounted again") { REQUIRE(vfs.do_mount("/dev/ram16", "/information") == kernel::filesystem::vfs::operation_result::success); -- cgit v1.2.3 From 5b40e4a28307eed814adb46188c3f6783651d286 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 16 May 2026 14:11:49 +0200 Subject: add kapi::filesystem tests --- kernel/kapi/filesystem.tests.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/kernel/kapi/filesystem.tests.cpp b/kernel/kapi/filesystem.tests.cpp index 1d1f8ee..d241afa 100644 --- a/kernel/kapi/filesystem.tests.cpp +++ b/kernel/kapi/filesystem.tests.cpp @@ -103,6 +103,19 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap REQUIRE(kapi::filesystem::umount("/information") == 0); } + THEN("a filesystem cannot be unmounted if files are still open and can be unmounted after files are closed") + { + REQUIRE(kapi::filesystem::mount("/dev/ram16", "/information") == 0); + + auto fd = kapi::filesystem::open("/information/monkey_house/monkey_1.txt"); + REQUIRE(fd >= 0); + + REQUIRE(kapi::filesystem::umount("/information") < 0); + + REQUIRE(kapi::filesystem::close(fd) == 0); + REQUIRE(kapi::filesystem::umount("/information") == 0); + } + THEN("device can be opened as file and read from") { auto fd = kapi::filesystem::open("/dev/ram0"); @@ -158,6 +171,15 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Kap REQUIRE(kapi::filesystem::close(999) < 0); } + THEN("same file cannot be closed twice") + { + auto fd = kapi::filesystem::open("/information/info_1.txt"); + REQUIRE(fd >= 0); + + REQUIRE(kapi::filesystem::close(fd) == 0); + REQUIRE(kapi::filesystem::close(fd) < 0); + } + THEN("not opened files cannot be read from") { std::vector buffer(10); -- cgit v1.2.3 From 3b2f36d242eb895fd893ec7a674ff608f44f69ac Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sat, 16 May 2026 16:12:36 +0200 Subject: refactoring --- kapi/kapi/filesystem.hpp | 12 ++-- kernel/include/kernel/filesystem/constants.hpp | 1 + kernel/include/kernel/filesystem/dentry.hpp | 6 +- .../include/kernel/filesystem/devfs/filesystem.hpp | 2 +- .../include/kernel/filesystem/ext2/filesystem.hpp | 48 +++++++-------- kernel/include/kernel/filesystem/ext2/inode.hpp | 2 +- kernel/include/kernel/filesystem/filesystem.hpp | 3 +- kernel/include/kernel/filesystem/mount.hpp | 12 ++-- .../kernel/filesystem/open_file_descriptor.hpp | 2 +- .../include/kernel/filesystem/open_file_table.hpp | 9 ++- .../kernel/filesystem/rootfs/filesystem.hpp | 2 +- kernel/include/kernel/filesystem/rootfs/inode.hpp | 1 + kernel/include/kernel/filesystem/vfs.hpp | 6 +- .../kernel/test_support/filesystem/filesystem.hpp | 2 +- kernel/kapi/filesystem.cpp | 20 +++---- kernel/src/devices/block_device_utils.cpp | 2 + kernel/src/filesystem/dentry.cpp | 6 +- kernel/src/filesystem/dentry.tests.cpp | 14 ++--- kernel/src/filesystem/devfs/filesystem.cpp | 10 ++-- kernel/src/filesystem/devfs/inode.cpp | 4 +- kernel/src/filesystem/ext2/filesystem.cpp | 70 +++++++++++----------- kernel/src/filesystem/ext2/inode.cpp | 19 +++--- kernel/src/filesystem/ext2/inode.tests.cpp | 8 +-- kernel/src/filesystem/mount.cpp | 12 ++-- kernel/src/filesystem/mount.tests.cpp | 18 +++--- kernel/src/filesystem/mount_table.cpp | 11 ++-- kernel/src/filesystem/open_file_descriptor.cpp | 2 +- .../src/filesystem/open_file_descriptor.tests.cpp | 24 ++++---- kernel/src/filesystem/open_file_table.cpp | 31 ++++------ kernel/src/filesystem/open_file_table.tests.cpp | 19 ++---- kernel/src/filesystem/rootfs/filesystem.cpp | 3 +- kernel/src/filesystem/rootfs/inode.cpp | 7 +-- kernel/src/filesystem/vfs.cpp | 25 ++++---- kernel/src/filesystem/vfs.tests.cpp | 30 +++++----- kernel/src/test_support/filesystem/filesystem.cpp | 2 +- 35 files changed, 214 insertions(+), 231 deletions(-) diff --git a/kapi/kapi/filesystem.hpp b/kapi/kapi/filesystem.hpp index 94d42ce..fdaed73 100644 --- a/kapi/kapi/filesystem.hpp +++ b/kapi/kapi/filesystem.hpp @@ -23,7 +23,7 @@ namespace kapi::filesystem @return 0 on success, -1 on failure. @qualifier kernel-defined */ - auto mount(std::string_view source, std::string_view target) -> int; + auto mount(std::string_view source, std::string_view target) -> ssize_t; /** @brief Unmounts a filesystem from the specified @p target path. @@ -31,7 +31,7 @@ namespace kapi::filesystem @return 0 on success, -1 on failure. @qualifier kernel-defined */ - auto umount(std::string_view target) -> int; + auto umount(std::string_view target) -> ssize_t; /** @brief Opens a file at the specified @p path. @@ -39,7 +39,7 @@ namespace kapi::filesystem @return A file descriptor on success, -1 on failure. @qualifier kernel-defined */ - auto open(std::string_view path) -> int; + auto open(std::string_view path) -> ssize_t; /** @brief Closes a @p file_descriptor. @@ -47,7 +47,7 @@ namespace kapi::filesystem @return 0 on success, -1 on failure. @qualifier kernel-defined */ - auto close(int file_descriptor) -> int; + auto close(size_t file_descriptor) -> ssize_t; /** @brief Reads @p size bytes into @p buffer from a @p file_descriptor. @@ -57,7 +57,7 @@ namespace kapi::filesystem @return The number of bytes read on success, -1 on failure. @qualifier kernel-defined */ - auto read(int file_descriptor, void * buffer, size_t size) -> ssize_t; + auto read(size_t file_descriptor, void * buffer, size_t size) -> ssize_t; /** @brief Writes @p size bytes from @p buffer to a @p file_descriptor. @@ -67,7 +67,7 @@ namespace kapi::filesystem @return The number of bytes written on success, -1 on failure. @qualifier kernel-defined */ - auto write(int file_descriptor, void const * buffer, size_t size) -> ssize_t; + auto write(size_t file_descriptor, void const * buffer, size_t size) -> ssize_t; } // namespace kapi::filesystem #endif // TEACHOS_KAPI_FILESYSTEM_HPP \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/constants.hpp b/kernel/include/kernel/filesystem/constants.hpp index aff512a..8388d05 100644 --- a/kernel/include/kernel/filesystem/constants.hpp +++ b/kernel/include/kernel/filesystem/constants.hpp @@ -2,6 +2,7 @@ #define TEACH_OS_KERNEL_FILESYSTEM_CONSTANTS_HPP #include + namespace kernel::filesystem::constants { constexpr size_t inline max_path_length = 4096; diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp index 925768a..478596a 100644 --- a/kernel/include/kernel/filesystem/dentry.hpp +++ b/kernel/include/kernel/filesystem/dentry.hpp @@ -46,19 +46,19 @@ namespace kernel::filesystem @brief Get the parent dentry. @return A reference to the parent dentry. */ - [[nodiscard]] auto get_parent() const -> kstd::shared_ptr const &; + [[nodiscard]] auto parent() const -> kstd::shared_ptr const &; /** @brief Get the name of the dentry. @return The name of the dentry. */ - [[nodiscard]] auto get_name() const -> std::string_view; + [[nodiscard]] auto name() const -> std::string_view; /** @brief Get the full path of the dentry by traversing up to the root. @return The full path of the dentry. */ - [[nodiscard]] auto get_absolute_path() const -> kstd::string; + [[nodiscard]] auto absolute_path() const -> kstd::string; /** @brief Add a @p child dentry. diff --git a/kernel/include/kernel/filesystem/devfs/filesystem.hpp b/kernel/include/kernel/filesystem/devfs/filesystem.hpp index 8d96555..dbaa387 100644 --- a/kernel/include/kernel/filesystem/devfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/devfs/filesystem.hpp @@ -33,7 +33,7 @@ namespace kernel::filesystem::devfs @param name The name of the inode to look up. @return A pointer to the found inode, or a null pointer if not found. */ - auto lookup(kstd::shared_ptr const & parent, std::string_view name) + [[nodiscard]] auto lookup(kstd::shared_ptr const & parent, std::string_view name) const -> kstd::shared_ptr override; private: diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 46be32f..a408c64 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -15,6 +15,8 @@ #include #include +#include + namespace kernel::filesystem::ext2 { /** @@ -61,20 +63,20 @@ namespace kernel::filesystem::ext2 @param name The name of the inode to look up. @return A pointer to the found inode, or a null pointer if not found. */ - auto lookup(kstd::shared_ptr const & parent, std::string_view name) + [[nodiscard]] auto lookup(kstd::shared_ptr const & parent, std::string_view name) const -> kstd::shared_ptr override; /** @brief Gets the size of a block in the filesystem. @return The size of a block in bytes. */ - [[nodiscard]] auto get_block_size() const -> size_t; + [[nodiscard]] auto block_size() const -> size_t; /** @brief Gets the revision level of the filesystem. @return The revision level. */ - [[nodiscard]] auto get_revision_level() const -> size_t; + [[nodiscard]] auto revision_level() const -> size_t; /** @brief Maps an inode block index to a global block number. @@ -82,30 +84,28 @@ namespace kernel::filesystem::ext2 @param data The inode data. @return The global block number. */ - [[nodiscard]] auto map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) const + [[nodiscard]] auto map_inode_block_index_to_global_block_number(size_t inode_block_index, inode_data data) const -> ssize_t; private: - [[nodiscard]] auto read_inode(uint32_t inode_number) const -> kstd::shared_ptr; - [[nodiscard]] auto read_block_number_at_index(uint32_t block_number, uint32_t index) const -> uint32_t; - - [[nodiscard]] auto read_singly_indirect_block_number(uint32_t singly_indirect_block_number, - uint32_t block_index_in_singly_indirect_block) const - -> uint32_t; - [[nodiscard]] auto read_doubly_indirect_block_number(uint32_t doubly_indirect_block_number, - uint32_t block_index_in_doubly_indirect_block) const - -> uint32_t; - [[nodiscard]] auto read_triply_indirect_block_number(uint32_t triply_indirect_block_number, - uint32_t block_index_in_triply_indirect_block) const - -> uint32_t; - - [[nodiscard]] auto get_inode_size() const -> size_t; - [[nodiscard]] auto get_inode_block_count(inode_data const & data) const -> uint32_t; - - [[nodiscard]] auto block_numbers_per_block() const -> uint32_t; - [[nodiscard]] auto block_numbers_per_singly_indirect_block() const -> uint32_t; - [[nodiscard]] auto block_numbers_per_doubly_indirect_block() const -> uint32_t; - [[nodiscard]] auto block_numbers_per_triply_indirect_block() const -> uint32_t; + [[nodiscard]] auto read_inode(size_t inode_number) const -> kstd::shared_ptr; + [[nodiscard]] auto read_block_number_at_index(size_t block_number, size_t index) const -> size_t; + + [[nodiscard]] auto read_singly_indirect_block_number(size_t singly_indirect_block_number, + size_t block_index_in_singly_indirect_block) const -> size_t; + [[nodiscard]] auto read_doubly_indirect_block_number(size_t doubly_indirect_block_number, + size_t block_index_in_doubly_indirect_block) const -> size_t; + [[nodiscard]] auto read_triply_indirect_block_number(size_t triply_indirect_block_number, + size_t block_index_in_triply_indirect_block) const -> size_t; + + [[nodiscard]] auto inode_size() const -> size_t; + [[nodiscard]] auto inode_block_count(inode_data const & data) const -> size_t; + [[nodiscard]] auto block_group_descriptor_table_offset() const -> size_t; + + [[nodiscard]] auto block_numbers_per_block() const -> size_t; + [[nodiscard]] auto block_numbers_per_singly_indirect_block() const -> size_t; + [[nodiscard]] auto block_numbers_per_doubly_indirect_block() const -> size_t; + [[nodiscard]] auto block_numbers_per_triply_indirect_block() const -> size_t; superblock m_superblock{}; kstd::vector m_block_group_descriptors; diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index 000a5d8..5609319 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -94,7 +94,7 @@ namespace kernel::filesystem::ext2 @brief Get the size of the file represented by this inode. @return The size of the file in bytes. */ - [[nodiscard]] auto get_size() const -> size_t; + [[nodiscard]] auto size() const -> size_t; private: filesystem const * m_filesystem; diff --git a/kernel/include/kernel/filesystem/filesystem.hpp b/kernel/include/kernel/filesystem/filesystem.hpp index 2fdc0ed..bec1b16 100644 --- a/kernel/include/kernel/filesystem/filesystem.hpp +++ b/kernel/include/kernel/filesystem/filesystem.hpp @@ -56,7 +56,8 @@ namespace kernel::filesystem @param name The name of the child inode to look up. @return A pointer to the requested child inode, or a null pointer if not found. */ - virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) -> kstd::shared_ptr = 0; + [[nodiscard]] virtual auto lookup(kstd::shared_ptr const & parent, std::string_view name) const + -> kstd::shared_ptr = 0; /** @brief Returns a reference to the root inode of the filesystem. diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index 5d8ea69..4ce374f 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -33,12 +33,12 @@ namespace kernel::filesystem /** @brief Get the dentry where the filesystem is mounted. */ - [[nodiscard]] auto get_mount_dentry() const -> kstd::shared_ptr const &; + [[nodiscard]] auto mount_dentry() const -> kstd::shared_ptr const &; /** @brief Get the root dentry of the mounted filesystem. */ - [[nodiscard]] auto get_root_dentry() const -> kstd::shared_ptr const &; + [[nodiscard]] auto root_dentry() const -> kstd::shared_ptr const &; /** @brief Get the filesystem instance being mounted. @@ -48,12 +48,12 @@ namespace kernel::filesystem /** @brief Get the path at which the filesystem is mounted. */ - [[nodiscard]] auto get_mount_path() const -> kstd::string; + [[nodiscard]] auto mount_path() const -> kstd::string; /** @brief Get the parent mount that this mount was attached beneath. */ - [[nodiscard]] auto get_parent_mount() const -> kstd::shared_ptr const &; + [[nodiscard]] auto parent_mount() const -> kstd::shared_ptr const &; /** @brief Increment the reference count for this mount. @@ -64,7 +64,7 @@ namespace kernel::filesystem @brief Decrement the reference count for this mount. @return True if the reference count reached zero, false otherwise. */ - auto decrement_ref_count() -> bool; + [[nodiscard]] auto decrement_ref_count() -> bool; /** @brief Check if the mount is ready to be unmounted. @@ -76,7 +76,7 @@ namespace kernel::filesystem @brief Get the current reference count for this mount. @return The current reference count. */ - [[nodiscard]] auto get_ref_count() const -> size_t; + [[nodiscard]] auto ref_count() const -> size_t; private: kstd::shared_ptr m_mount_dentry; diff --git a/kernel/include/kernel/filesystem/open_file_descriptor.hpp b/kernel/include/kernel/filesystem/open_file_descriptor.hpp index 7ca7350..fd10e64 100644 --- a/kernel/include/kernel/filesystem/open_file_descriptor.hpp +++ b/kernel/include/kernel/filesystem/open_file_descriptor.hpp @@ -50,7 +50,7 @@ namespace kernel::filesystem @brief Returns the current file offset for this open file descriptor. @return The current file offset in bytes. */ - [[nodiscard]] auto get_offset() const -> size_t; + [[nodiscard]] auto offset() const -> size_t; /** @brief Return a reference to the dentry associated with this open file descriptor. diff --git a/kernel/include/kernel/filesystem/open_file_table.hpp b/kernel/include/kernel/filesystem/open_file_table.hpp index 694f3b6..5794e4c 100644 --- a/kernel/include/kernel/filesystem/open_file_table.hpp +++ b/kernel/include/kernel/filesystem/open_file_table.hpp @@ -6,6 +6,9 @@ #include #include +#include +#include + namespace kernel::filesystem { /** @@ -37,21 +40,21 @@ namespace kernel::filesystem @param fd The file descriptor to add. @return The file descriptor index assigned to the file, or -1 on failure. */ - auto add_file(kstd::shared_ptr const & fd) -> int; + auto add_file(kstd::shared_ptr const & fd) -> ssize_t; /** @brief Get a file from the open file table. @param fd The file descriptor index to retrieve. @return A pointer to the requested file descriptor, or a null pointer if not found. */ - [[nodiscard]] auto get_file(int fd) const -> kstd::shared_ptr; + [[nodiscard]] auto file(size_t fd) const -> kstd::shared_ptr; /** @brief Remove a file from the open file table. @param fd The file descriptor index to remove. @return 0 on success, or -1 on failure. */ - auto remove_file(int fd) -> int; + auto remove_file(size_t fd) -> ssize_t; private: open_file_table() = default; diff --git a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp index f99440b..3c2dcb1 100644 --- a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp +++ b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp @@ -33,7 +33,7 @@ namespace kernel::filesystem::rootfs @param name The name of the inode to look up. @return Always returns nullptr. */ - auto lookup(kstd::shared_ptr const & parent, std::string_view name) + [[nodiscard]] auto lookup(kstd::shared_ptr const & parent, std::string_view name) const -> kstd::shared_ptr override; }; } // namespace kernel::filesystem::rootfs diff --git a/kernel/include/kernel/filesystem/rootfs/inode.hpp b/kernel/include/kernel/filesystem/rootfs/inode.hpp index 2671207..0f21eaa 100644 --- a/kernel/include/kernel/filesystem/rootfs/inode.hpp +++ b/kernel/include/kernel/filesystem/rootfs/inode.hpp @@ -8,6 +8,7 @@ #include #include + namespace kernel::filesystem::rootfs { /** diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index 0058d04..aec8bfe 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -97,10 +97,10 @@ namespace kernel::filesystem * - resolve_path() for the dentry only. * - find_mount() for the mount context only. */ - [[nodiscard]] auto resolve_path_internal(std::string_view path) + [[nodiscard]] auto resolve_path_internal(std::string_view path) const -> std::pair, kstd::shared_ptr>; - [[nodiscard]] auto resolve_path(std::string_view path) -> kstd::shared_ptr; - [[nodiscard]] auto find_mount(std::string_view path) -> kstd::shared_ptr; + [[nodiscard]] auto resolve_path(std::string_view path) const -> kstd::shared_ptr; + [[nodiscard]] auto find_mount(std::string_view path) const -> kstd::shared_ptr; auto do_mount_internal(kstd::shared_ptr const & mount_point_dentry, kstd::shared_ptr const & parent_mount, kstd::shared_ptr const & fs) diff --git a/kernel/include/kernel/test_support/filesystem/filesystem.hpp b/kernel/include/kernel/test_support/filesystem/filesystem.hpp index dab0892..5f26022 100644 --- a/kernel/include/kernel/test_support/filesystem/filesystem.hpp +++ b/kernel/include/kernel/test_support/filesystem/filesystem.hpp @@ -14,7 +14,7 @@ namespace kernel::tests::filesystem { filesystem() = default; - auto lookup(kstd::shared_ptr const & parent, std::string_view name) + [[nodiscard]] auto lookup(kstd::shared_ptr const & parent, std::string_view name) const -> kstd::shared_ptr override; }; } // namespace kernel::tests::filesystem diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index 53a71be..db61c34 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -13,7 +13,7 @@ namespace kapi::filesystem { - auto mount(std::string_view source, std::string_view target) -> int + auto mount(std::string_view source, std::string_view target) -> ssize_t { if (kernel::filesystem::vfs::get().do_mount(source, target) == kernel::filesystem::vfs::operation_result::success) { @@ -22,7 +22,7 @@ namespace kapi::filesystem return -1; } - auto umount(std::string_view target) -> int + auto umount(std::string_view target) -> ssize_t { if (kernel::filesystem::vfs::get().unmount(target) == kernel::filesystem::vfs::operation_result::success) { @@ -31,7 +31,7 @@ namespace kapi::filesystem return -1; } - auto open(std::string_view path) -> int + auto open(std::string_view path) -> ssize_t { if (auto dentry = kernel::filesystem::vfs::get().open(path)) { @@ -42,11 +42,11 @@ namespace kapi::filesystem return -1; } - auto close(int file_descriptor) -> int + auto close(size_t file_descriptor) -> ssize_t { - if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().get_file(file_descriptor)) + if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().file(file_descriptor)) { - if (kernel::filesystem::vfs::get().close(open_file_descriptor->get_dentry()->get_absolute_path().view()) == + if (kernel::filesystem::vfs::get().close(open_file_descriptor->get_dentry()->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success) { return kernel::filesystem::open_file_table::get().remove_file(file_descriptor); @@ -55,9 +55,9 @@ namespace kapi::filesystem return -1; } - auto read(int file_descriptor, void * buffer, size_t size) -> ssize_t + auto read(size_t file_descriptor, void * buffer, size_t size) -> ssize_t { - if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().get_file(file_descriptor)) + if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().file(file_descriptor)) { return open_file_descriptor->read(buffer, size); } @@ -65,9 +65,9 @@ namespace kapi::filesystem return -1; } - auto write(int file_descriptor, void const * buffer, size_t size) -> ssize_t + auto write(size_t file_descriptor, void const * buffer, size_t size) -> ssize_t { - if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().get_file(file_descriptor)) + if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().file(file_descriptor)) { return open_file_descriptor->write(buffer, size); } diff --git a/kernel/src/devices/block_device_utils.cpp b/kernel/src/devices/block_device_utils.cpp index cb8ecba..18d1e9d 100644 --- a/kernel/src/devices/block_device_utils.cpp +++ b/kernel/src/devices/block_device_utils.cpp @@ -42,7 +42,9 @@ namespace kernel::devices::block_device_utils size_t const capacity = block_dev->capacity(); if (offset >= capacity) + { return 0; + } size_t const total_to_process = std::min(size, capacity - offset); kstd::vector scratch_buffer{block_size}; diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index d963ed7..14de875 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -34,17 +34,17 @@ namespace kernel::filesystem return m_inode; } - auto dentry::get_parent() const -> kstd::shared_ptr const & + auto dentry::parent() const -> kstd::shared_ptr const & { return m_parent; } - auto dentry::get_name() const -> std::string_view + auto dentry::name() const -> std::string_view { return m_name.view(); } - auto dentry::get_absolute_path() const -> kstd::string + auto dentry::absolute_path() const -> kstd::string { kstd::string path = m_name; diff --git a/kernel/src/filesystem/dentry.tests.cpp b/kernel/src/filesystem/dentry.tests.cpp index e9ecbc8..b7690f5 100644 --- a/kernel/src/filesystem/dentry.tests.cpp +++ b/kernel/src/filesystem/dentry.tests.cpp @@ -21,9 +21,9 @@ SCENARIO("Dentry construction", "[filesystem][dentry]") THEN("the dentry has the correct parent, inode, and name") { - REQUIRE(child_dentry.get_parent() == parent_dentry); + REQUIRE(child_dentry.parent() == parent_dentry); REQUIRE(child_dentry.get_inode() == inode); - REQUIRE(child_dentry.get_name() == "child"); + REQUIRE(child_dentry.name() == "child"); } THEN("no flag is set") @@ -46,9 +46,9 @@ SCENARIO("Dentry construction", "[filesystem][dentry]") THEN("the dentry has a null parent, the correct inode, and the correct name") { - REQUIRE(child_dentry.get_parent() == nullptr); + REQUIRE(child_dentry.parent() == nullptr); REQUIRE(child_dentry.get_inode() == inode); - REQUIRE(child_dentry.get_name() == "child"); + REQUIRE(child_dentry.name() == "child"); } THEN("no flag is set") @@ -142,9 +142,9 @@ SCENARIO("Dentry path resolution", "[filesystem][dentry]") THEN("the full path is constructed correctly") { - REQUIRE(root_dentry->get_absolute_path() == "/"); - REQUIRE(home_dentry->get_absolute_path() == "/home"); - REQUIRE(user_dentry->get_absolute_path() == "/home/user"); + REQUIRE(root_dentry->absolute_path() == "/"); + REQUIRE(home_dentry->absolute_path() == "/home"); + REQUIRE(user_dentry->absolute_path() == "/home/user"); } } } \ No newline at end of file diff --git a/kernel/src/filesystem/devfs/filesystem.cpp b/kernel/src/filesystem/devfs/filesystem.cpp index 96e40a8..f0d8bf7 100644 --- a/kernel/src/filesystem/devfs/filesystem.cpp +++ b/kernel/src/filesystem/devfs/filesystem.cpp @@ -1,5 +1,6 @@ #include +#include "kernel/filesystem/filesystem.hpp" #include #include #include @@ -22,7 +23,7 @@ namespace kernel::filesystem::devfs return operation_result::success; } - auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) + auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) const -> kstd::shared_ptr { if (!parent || !parent->is_directory()) @@ -36,12 +37,11 @@ namespace kernel::filesystem::devfs } auto it = std::ranges::find_if(m_inodes, [&](auto const & dev_node) { - auto device_inode_ptr = static_cast(dev_node.get()); - if (!device_inode_ptr) + if (auto device_inode_ptr = static_cast(dev_node.get())) { - return false; + return device_inode_ptr->device()->name() == name; } - return device_inode_ptr->device()->name() == name; + return false; }); return (it != m_inodes.end()) ? *it : nullptr; } diff --git a/kernel/src/filesystem/devfs/inode.cpp b/kernel/src/filesystem/devfs/inode.cpp index 2029a7f..7bbfbbe 100644 --- a/kernel/src/filesystem/devfs/inode.cpp +++ b/kernel/src/filesystem/devfs/inode.cpp @@ -4,12 +4,12 @@ namespace kernel::filesystem::devfs { - auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t + auto inode::read(void *, size_t, size_t) const -> size_t { return 0; } - auto inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t + auto inode::write(void const *, size_t, size_t) -> size_t { return 0; } diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 893cc38..7633972 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -28,14 +28,12 @@ namespace kernel::filesystem::ext2 return operation_result::invalid_magic_number; } - auto const block_size = get_block_size(); auto const blocks_per_group = m_superblock.blocks_per_group; auto const num_block_groups = (m_superblock.blocks_count + blocks_per_group - 1) / blocks_per_group; m_block_group_descriptors = kstd::vector(num_block_groups); - auto const block_group_descriptor_table_offset = block_size == 1024 ? 2 * block_size : block_size; - m_backing_inode->read(m_block_group_descriptors.data(), block_group_descriptor_table_offset, + m_backing_inode->read(m_block_group_descriptors.data(), block_group_descriptor_table_offset(), num_block_groups * sizeof(block_group_descriptor)); m_root_inode = read_inode(constants::root_inode_number); @@ -47,7 +45,7 @@ namespace kernel::filesystem::ext2 return operation_result::success; } - auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) + auto filesystem::lookup(kstd::shared_ptr const & parent, std::string_view name) const -> kstd::shared_ptr { if (!parent || !parent->is_directory()) @@ -61,20 +59,19 @@ namespace kernel::filesystem::ext2 return nullptr; } - auto const block_size = get_block_size(); auto const & inode_data = ext2_parent->data(); - kstd::vector buffer(block_size); + kstd::vector buffer(block_size()); - for (uint32_t i = 0; i < get_inode_block_count(inode_data); ++i) + for (auto i = 0uz; i < inode_block_count(inode_data); ++i) { auto const global_block_number = map_inode_block_index_to_global_block_number(i, inode_data); - auto const block_offset = global_block_number * block_size; - m_backing_inode->read(buffer.data(), block_offset, block_size); + auto const block_offset = global_block_number * block_size(); + m_backing_inode->read(buffer.data(), block_offset, block_size()); auto const * entry = reinterpret_cast(buffer.data()); auto bytes_read = 0uz; - while (bytes_read < block_size && entry->inode != 0) + while (bytes_read < block_size() && entry->inode != 0) { auto const entry_name = std::string_view{entry->name.data(), entry->name_len}; if (entry_name == name) @@ -90,9 +87,8 @@ namespace kernel::filesystem::ext2 return nullptr; } - auto filesystem::read_inode(uint32_t inode_number) const -> kstd::shared_ptr + auto filesystem::read_inode(size_t inode_number) const -> kstd::shared_ptr { - auto const block_size = get_block_size(); auto const inodes_per_group = m_superblock.inodes_per_group; auto const block_group_index = (inode_number - 1) / inodes_per_group; auto const inode_index_within_group = (inode_number - 1) % inodes_per_group; @@ -104,8 +100,8 @@ namespace kernel::filesystem::ext2 auto const & block_group_descriptor = m_block_group_descriptors.at(block_group_index); auto const inode_table_start_block = block_group_descriptor.inode_table; - auto const inode_table_offset = static_cast(inode_table_start_block) * block_size; - auto const inode_offset = inode_table_offset + inode_index_within_group * get_inode_size(); + auto const inode_table_offset = static_cast(inode_table_start_block) * block_size(); + auto const inode_offset = inode_table_offset + inode_index_within_group * inode_size(); auto new_inode_data = inode_data{}; m_backing_inode->read(&new_inode_data, inode_offset, sizeof(inode_data)); @@ -113,7 +109,7 @@ namespace kernel::filesystem::ext2 return kstd::make_shared(this, new_inode_data); } - auto filesystem::map_inode_block_index_to_global_block_number(uint32_t inode_block_index, inode_data data) const + auto filesystem::map_inode_block_index_to_global_block_number(size_t inode_block_index, inode_data data) const -> ssize_t { if (inode_block_index < constants::direct_block_count) @@ -145,18 +141,19 @@ namespace kernel::filesystem::ext2 return -1; } - auto filesystem::read_singly_indirect_block_number(uint32_t singly_indirect_block_number, - uint32_t block_index_in_singly_indirect_block) const -> uint32_t + auto filesystem::read_singly_indirect_block_number(size_t singly_indirect_block_number, + size_t block_index_in_singly_indirect_block) const -> size_t { if (singly_indirect_block_number == 0) { return 0; } + return read_block_number_at_index(singly_indirect_block_number, block_index_in_singly_indirect_block); } - auto filesystem::read_doubly_indirect_block_number(uint32_t doubly_indirect_block_number, - uint32_t block_index_in_doubly_indirect_block) const -> uint32_t + auto filesystem::read_doubly_indirect_block_number(size_t doubly_indirect_block_number, + size_t block_index_in_doubly_indirect_block) const -> size_t { if (doubly_indirect_block_number == 0) { @@ -174,8 +171,8 @@ namespace kernel::filesystem::ext2 return read_singly_indirect_block_number(singly_indirect_block_number, block_index_in_singly_indirect_block); } - auto filesystem::read_triply_indirect_block_number(uint32_t triply_indirect_block_number, - uint32_t block_index_in_triply_indirect_block) const -> uint32_t + auto filesystem::read_triply_indirect_block_number(size_t triply_indirect_block_number, + size_t block_index_in_triply_indirect_block) const -> size_t { if (triply_indirect_block_number == 0) { @@ -193,54 +190,59 @@ namespace kernel::filesystem::ext2 return read_doubly_indirect_block_number(doubly_indirect_block_number, block_index_in_doubly_indirect_block); } - auto filesystem::read_block_number_at_index(uint32_t block_number, uint32_t index) const -> uint32_t + auto filesystem::read_block_number_at_index(size_t block_number, size_t index) const -> size_t { - uint32_t block_number_buffer = 0; + auto block_number_buffer = 0uz; - auto const block_start_offset = block_number * get_block_size(); + auto const block_start_offset = block_number * block_size(); auto const number_start_address = block_start_offset + index * sizeof(uint32_t); m_backing_inode->read(&block_number_buffer, number_start_address, sizeof(uint32_t)); return block_number_buffer; } - auto filesystem::block_numbers_per_block() const -> uint32_t + auto filesystem::block_numbers_per_block() const -> size_t { - return get_block_size() / sizeof(uint32_t); + return block_size() / sizeof(uint32_t); } - auto filesystem::block_numbers_per_singly_indirect_block() const -> uint32_t + auto filesystem::block_numbers_per_singly_indirect_block() const -> size_t { return block_numbers_per_block(); } - auto filesystem::block_numbers_per_doubly_indirect_block() const -> uint32_t + auto filesystem::block_numbers_per_doubly_indirect_block() const -> size_t { return block_numbers_per_singly_indirect_block() * block_numbers_per_block(); } - auto filesystem::block_numbers_per_triply_indirect_block() const -> uint32_t + auto filesystem::block_numbers_per_triply_indirect_block() const -> size_t { return block_numbers_per_doubly_indirect_block() * block_numbers_per_block(); } - auto filesystem::get_block_size() const -> size_t + auto filesystem::block_size() const -> size_t { return constants::base_block_size << m_superblock.log_block_size; } - auto filesystem::get_revision_level() const -> size_t + auto filesystem::revision_level() const -> size_t { return m_superblock.rev_level; } - auto filesystem::get_inode_size() const -> size_t + auto filesystem::inode_size() const -> size_t { - return get_revision_level() == constants::good_old_revision ? 128 : m_superblock.inode_size; + return revision_level() == constants::good_old_revision ? 128 : m_superblock.inode_size; } - auto filesystem::get_inode_block_count(inode_data const & data) const -> uint32_t + auto filesystem::inode_block_count(inode_data const & data) const -> size_t { return data.blocks / (2 << m_superblock.log_block_size); } + + auto filesystem::block_group_descriptor_table_offset() const -> size_t + { + return block_size() == 1024 ? 2 * block_size() : block_size(); + } } // namespace kernel::filesystem::ext2 diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index cfe0a35..f8c818c 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -25,18 +25,18 @@ namespace kernel::filesystem::ext2 auto inode::read(void * buffer, size_t offset, size_t size) const -> size_t { - auto const max_readable = get_size() - offset; + auto const max_readable = this->size() - offset; auto const requested_size = std::min(size, max_readable); - if (is_symbolic_link() && get_size() <= sizeof(m_data.block)) + if (is_symbolic_link() && this->size() <= sizeof(m_data.block)) { auto inline_target = reinterpret_cast(m_data.block.data()); kstd::libc::memcpy(static_cast(buffer), inline_target + offset, requested_size); return requested_size; } - auto block_index = offset / m_filesystem->get_block_size(); - auto in_block_offset = offset % m_filesystem->get_block_size(); + auto block_index = offset / m_filesystem->block_size(); + auto in_block_offset = offset % m_filesystem->block_size(); auto bytes_read = 0uz; @@ -48,8 +48,7 @@ namespace kernel::filesystem::ext2 break; } - auto const bytes_to_read = - std::min(requested_size - bytes_read, m_filesystem->get_block_size() - in_block_offset); + auto const bytes_to_read = std::min(requested_size - bytes_read, m_filesystem->block_size() - in_block_offset); if (block_number == 0) { kstd::libc::memset(static_cast(buffer) + bytes_read, 0, bytes_to_read); @@ -57,7 +56,7 @@ namespace kernel::filesystem::ext2 } else { - auto const block_start_offset = block_number * m_filesystem->get_block_size(); + auto const block_start_offset = block_number * m_filesystem->block_size(); auto const read_offset = block_start_offset + in_block_offset; bytes_read += m_filesystem->backing_inode()->read(static_cast(buffer) + bytes_read, read_offset, @@ -71,7 +70,7 @@ namespace kernel::filesystem::ext2 return bytes_read; } - auto inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t + auto inode::write(void const *, size_t, size_t) -> size_t { kapi::system::panic("[EXT2] inode::write is not implemented yet"); return 0; @@ -97,11 +96,11 @@ namespace kernel::filesystem::ext2 return (m_data.mode & constants::mode_mask) == constants::mode_symbolic_link; } - auto inode::get_size() const -> size_t + auto inode::size() const -> size_t { uint64_t size = m_data.size; - if (m_filesystem->get_revision_level() > constants::good_old_revision && is_regular()) + if (m_filesystem->revision_level() > constants::good_old_revision && is_regular()) { size |= static_cast(m_data.dir_acl) << 32; } diff --git a/kernel/src/filesystem/ext2/inode.tests.cpp b/kernel/src/filesystem/ext2/inode.tests.cpp index efc0660..4aecc04 100644 --- a/kernel/src/filesystem/ext2/inode.tests.cpp +++ b/kernel/src/filesystem/ext2/inode.tests.cpp @@ -325,7 +325,7 @@ SCENARIO("Ext2 inode get_size() correctly returns size depending on revision lev auto inode = kernel::filesystem::ext2::inode{&fs, data}; - REQUIRE(inode.get_size() == 256); + REQUIRE(inode.size() == 256); } THEN("the inode size is 256 if mode = directory") @@ -334,7 +334,7 @@ SCENARIO("Ext2 inode get_size() correctly returns size depending on revision lev auto inode = kernel::filesystem::ext2::inode{&fs, data}; - REQUIRE(inode.get_size() == 256); + REQUIRE(inode.size() == 256); } } @@ -361,7 +361,7 @@ SCENARIO("Ext2 inode get_size() correctly returns size depending on revision lev auto inode = kernel::filesystem::ext2::inode{&fs, data}; - REQUIRE(inode.get_size() == 0x0000'0020'0000'0100); + REQUIRE(inode.size() == 0x0000'0020'0000'0100); } THEN("the inode size is 256 if mode = directory") @@ -370,7 +370,7 @@ SCENARIO("Ext2 inode get_size() correctly returns size depending on revision lev auto inode = kernel::filesystem::ext2::inode{&fs, data}; - REQUIRE(inode.get_size() == 256); + REQUIRE(inode.size() == 256); } } } \ No newline at end of file diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index 1e04083..b64c370 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -27,7 +27,7 @@ namespace kernel::filesystem } } - auto mount::get_mount_dentry() const -> kstd::shared_ptr const & + auto mount::mount_dentry() const -> kstd::shared_ptr const & { return m_mount_dentry; } @@ -37,21 +37,21 @@ namespace kernel::filesystem return m_filesystem; } - auto mount::get_root_dentry() const -> kstd::shared_ptr const & + auto mount::root_dentry() const -> kstd::shared_ptr const & { return m_root_dentry; } - auto mount::get_mount_path() const -> kstd::string + auto mount::mount_path() const -> kstd::string { if (m_mount_dentry) { - return m_mount_dentry->get_absolute_path(); + return m_mount_dentry->absolute_path(); } return "/"; } - auto mount::get_parent_mount() const -> kstd::shared_ptr const & + auto mount::parent_mount() const -> kstd::shared_ptr const & { return m_parent_mount; } @@ -77,7 +77,7 @@ namespace kernel::filesystem return m_ref_count == 0; } - auto mount::get_ref_count() const -> size_t + auto mount::ref_count() const -> size_t { return m_ref_count; } diff --git a/kernel/src/filesystem/mount.tests.cpp b/kernel/src/filesystem/mount.tests.cpp index e7dd709..6b66571 100644 --- a/kernel/src/filesystem/mount.tests.cpp +++ b/kernel/src/filesystem/mount.tests.cpp @@ -26,15 +26,15 @@ SCENARIO("Mount construction", "[filesystem][mount]") THEN("the mount has the correct filesystem, root dentry, mount dentry, and mount path") { REQUIRE(mount.get_filesystem() == fs); - REQUIRE(mount.get_root_dentry() == root_dentry); - REQUIRE(mount.get_mount_dentry() == root_dentry); - REQUIRE(mount.get_mount_path() == "/"); + REQUIRE(mount.root_dentry() == root_dentry); + REQUIRE(mount.mount_dentry() == root_dentry); + REQUIRE(mount.mount_path() == "/"); REQUIRE(mount.is_ready_to_unmount()); } THEN("the mount has no parent mount") { - REQUIRE(mount.get_parent_mount() == nullptr); + REQUIRE(mount.parent_mount() == nullptr); } } @@ -63,19 +63,19 @@ SCENARIO("Mount reference counting", "[filesystem][mount]") auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr}; mount.increment_ref_count(); - REQUIRE(mount.get_ref_count() == 1); + REQUIRE(mount.ref_count() == 1); REQUIRE_FALSE(mount.is_ready_to_unmount()); mount.increment_ref_count(); - REQUIRE(mount.get_ref_count() == 2); + REQUIRE(mount.ref_count() == 2); REQUIRE_FALSE(mount.is_ready_to_unmount()); REQUIRE(mount.decrement_ref_count()); - REQUIRE(mount.get_ref_count() == 1); + REQUIRE(mount.ref_count() == 1); REQUIRE_FALSE(mount.is_ready_to_unmount()); REQUIRE(mount.decrement_ref_count()); - REQUIRE(mount.get_ref_count() == 0); + REQUIRE(mount.ref_count() == 0); REQUIRE(mount.is_ready_to_unmount()); } @@ -84,7 +84,7 @@ SCENARIO("Mount reference counting", "[filesystem][mount]") auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr}; REQUIRE_FALSE(mount.decrement_ref_count()); - REQUIRE(mount.get_ref_count() == 0); + REQUIRE(mount.ref_count() == 0); REQUIRE(mount.is_ready_to_unmount()); } } diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 9951590..26828b4 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -14,15 +14,15 @@ namespace kernel::filesystem { auto mount_table::has_child_mounts(kstd::shared_ptr const & parent_mount) const -> bool { - return std::ranges::any_of( - m_mounts, [&parent_mount](auto const & mount) { return mount->get_parent_mount() == parent_mount; }); + return std::ranges::any_of(m_mounts, + [&parent_mount](auto const & mount) { return mount->parent_mount() == parent_mount; }); } void mount_table::add_mount(kstd::shared_ptr const & mount) { m_mounts.push_back(mount); - if (auto mount_dentry = mount->get_mount_dentry()) + if (auto mount_dentry = mount->mount_dentry()) { mount_dentry->set_flag(dentry::dentry_flags::is_mount_point); } @@ -46,7 +46,7 @@ namespace kernel::filesystem return operation_result::has_child_mounts; } - mount->get_mount_dentry()->unset_flag(dentry::dentry_flags::is_mount_point); + mount->mount_dentry()->unset_flag(dentry::dentry_flags::is_mount_point); m_mounts.erase(mount_it); return operation_result::removed; } @@ -60,7 +60,6 @@ namespace kernel::filesystem auto mount_table::find_mount_iterator(std::string_view path) const -> kstd::vector>::const_iterator { - return std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->get_mount_path() == path; }) - .begin(); + return std::ranges::find_last_if(m_mounts, [&](auto const & mount) { return mount->mount_path() == path; }).begin(); } } // namespace kernel::filesystem \ No newline at end of file diff --git a/kernel/src/filesystem/open_file_descriptor.cpp b/kernel/src/filesystem/open_file_descriptor.cpp index ebaabef..a5567bf 100644 --- a/kernel/src/filesystem/open_file_descriptor.cpp +++ b/kernel/src/filesystem/open_file_descriptor.cpp @@ -33,7 +33,7 @@ namespace kernel::filesystem return written_bytes; } - auto open_file_descriptor::get_offset() const -> size_t + auto open_file_descriptor::offset() const -> size_t { return m_offset; } diff --git a/kernel/src/filesystem/open_file_descriptor.tests.cpp b/kernel/src/filesystem/open_file_descriptor.tests.cpp index 1910b8b..8c24cf0 100644 --- a/kernel/src/filesystem/open_file_descriptor.tests.cpp +++ b/kernel/src/filesystem/open_file_descriptor.tests.cpp @@ -26,7 +26,7 @@ SCENARIO("Open file descriptor construction", "[filesystem][open_file_descriptor THEN("the initial offset is zero") { - REQUIRE(file_descriptor.get_offset() == 0); + REQUIRE(file_descriptor.offset() == 0); } } } @@ -42,29 +42,29 @@ SCENARIO("Open file descriptor read/write offset management", "[filesystem][open THEN("the offset is updated correctly after reads") { REQUIRE(file_descriptor.read(nullptr, 100) == 100); - REQUIRE(file_descriptor.get_offset() == 100); + REQUIRE(file_descriptor.offset() == 100); REQUIRE(file_descriptor.read(nullptr, 50) == 50); - REQUIRE(file_descriptor.get_offset() == 150); + REQUIRE(file_descriptor.offset() == 150); } THEN("the offset is updated correctly after writes") { REQUIRE(file_descriptor.write(nullptr, 200) == 200); - REQUIRE(file_descriptor.get_offset() == 200); + REQUIRE(file_descriptor.offset() == 200); REQUIRE(file_descriptor.write(nullptr, 25) == 25); - REQUIRE(file_descriptor.get_offset() == 225); + REQUIRE(file_descriptor.offset() == 225); } THEN("reads and writes both update the same offset") { REQUIRE(file_descriptor.read(nullptr, 10) == 10); - REQUIRE(file_descriptor.get_offset() == 10); + REQUIRE(file_descriptor.offset() == 10); REQUIRE(file_descriptor.write(nullptr, 20) == 20); - REQUIRE(file_descriptor.get_offset() == 30); + REQUIRE(file_descriptor.offset() == 30); REQUIRE(file_descriptor.read(nullptr, 5) == 5); - REQUIRE(file_descriptor.get_offset() == 35); + REQUIRE(file_descriptor.offset() == 35); REQUIRE(file_descriptor.write(nullptr, 15) == 15); - REQUIRE(file_descriptor.get_offset() == 50); + REQUIRE(file_descriptor.offset() == 50); } } } @@ -89,7 +89,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Ope kstd::vector buffer(32); auto bytes_read = ofd->read(buffer.data(), buffer.size()); REQUIRE(bytes_read == 7); - REQUIRE(ofd->get_offset() == 7); + REQUIRE(ofd->offset() == 7); std::string_view buffer_as_str{reinterpret_cast(buffer.data()), static_cast(bytes_read)}; REQUIRE(buffer_as_str == "info_1\n"); @@ -100,11 +100,11 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "Ope kstd::vector buffer(4); auto bytes_read_1 = ofd->read(buffer.data(), buffer.size() / 2); REQUIRE(bytes_read_1 == buffer.size() / 2); - REQUIRE(ofd->get_offset() == buffer.size() / 2); + REQUIRE(ofd->offset() == buffer.size() / 2); auto bytes_read_2 = ofd->read(buffer.data() + buffer.size() / 2, buffer.size() / 2); REQUIRE(bytes_read_2 == buffer.size() / 2); - REQUIRE(ofd->get_offset() == buffer.size()); + REQUIRE(ofd->offset() == buffer.size()); std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read_1 + bytes_read_2}; REQUIRE(buffer_as_str == "info"); diff --git a/kernel/src/filesystem/open_file_table.cpp b/kernel/src/filesystem/open_file_table.cpp index e47d229..4d52d36 100644 --- a/kernel/src/filesystem/open_file_table.cpp +++ b/kernel/src/filesystem/open_file_table.cpp @@ -9,6 +9,7 @@ #include #include #include +#include namespace { @@ -37,7 +38,7 @@ namespace kernel::filesystem return *global_open_file_table; } - auto open_file_table::add_file(kstd::shared_ptr const & file_descriptor) -> int + auto open_file_table::add_file(kstd::shared_ptr const & file_descriptor) -> ssize_t { if (!file_descriptor) { @@ -48,43 +49,31 @@ namespace kernel::filesystem if (it != m_open_files.end()) { *it = file_descriptor; - return static_cast(it - m_open_files.begin()); + return it - m_open_files.begin(); } m_open_files.push_back(file_descriptor); - return static_cast(m_open_files.size() - 1); + return m_open_files.size() - 1; } - auto open_file_table::get_file(int fd) const -> kstd::shared_ptr + auto open_file_table::file(size_t fd) const -> kstd::shared_ptr { - if (fd < 0) + if (fd >= m_open_files.size()) { return nullptr; } - auto const index = static_cast(fd); - if (index >= m_open_files.size()) - { - return nullptr; - } - - return m_open_files.at(index); + return m_open_files.at(fd); } - auto open_file_table::remove_file(int fd) -> int + auto open_file_table::remove_file(size_t fd) -> ssize_t { - if (fd < 0) - { - return -1; - } - - auto const index = static_cast(fd); - if (index >= m_open_files.size()) + if (fd >= m_open_files.size()) { return -1; } - m_open_files.at(index) = nullptr; + m_open_files.at(fd) = nullptr; return 0; } } // namespace kernel::filesystem diff --git a/kernel/src/filesystem/open_file_table.tests.cpp b/kernel/src/filesystem/open_file_table.tests.cpp index 456d6b7..3e91111 100644 --- a/kernel/src/filesystem/open_file_table.tests.cpp +++ b/kernel/src/filesystem/open_file_table.tests.cpp @@ -36,7 +36,7 @@ SCENARIO("Open file table add/get file", "[filesystem][open_file_table]") THEN("the file descriptor can be retrieved using the returned file descriptor") { - auto retrieved_descriptor = table.get_file(fd_1); + auto retrieved_descriptor = table.file(fd_1); REQUIRE(retrieved_descriptor == file_descriptor_1); } } @@ -52,15 +52,9 @@ SCENARIO("Open file table add/get file", "[filesystem][open_file_table]") REQUIRE(fd == -1); } - THEN("retrieving a file descriptor with a negative file descriptor returns a null pointer") - { - auto retrieved_descriptor = table.get_file(-1); - REQUIRE(retrieved_descriptor == nullptr); - } - THEN("retrieving a file descriptor with an out-of-bounds file descriptor returns a null pointer") { - auto retrieved_descriptor = table.get_file(1000); + auto retrieved_descriptor = table.file(1000); REQUIRE(retrieved_descriptor == nullptr); } } @@ -82,7 +76,7 @@ SCENARIO("Open file table remove file", "[filesystem][open_file_table]") THEN("the file descriptor can no longer be retrieved using the file descriptor") { - auto retrieved_descriptor = table.get_file(fd); + auto retrieved_descriptor = table.file(fd); REQUIRE(retrieved_descriptor == nullptr); } } @@ -94,7 +88,7 @@ SCENARIO("Open file table remove file", "[filesystem][open_file_table]") THEN("the second file descriptor can still be retrieved using its file descriptor") { - auto retrieved_descriptor = table.get_file(fd2); + auto retrieved_descriptor = table.file(fd2); REQUIRE(retrieved_descriptor == file_descriptor); } } @@ -104,11 +98,6 @@ SCENARIO("Open file table remove file", "[filesystem][open_file_table]") { auto & table = kernel::filesystem::open_file_table::get(); - THEN("removing a file with a negative file descriptor does nothing") - { - REQUIRE_NOTHROW(table.remove_file(-1)); - } - THEN("removing a file with an out-of-bounds file descriptor does nothing") { REQUIRE_NOTHROW(table.remove_file(1000)); diff --git a/kernel/src/filesystem/rootfs/filesystem.cpp b/kernel/src/filesystem/rootfs/filesystem.cpp index d49e237..0ba2936 100644 --- a/kernel/src/filesystem/rootfs/filesystem.cpp +++ b/kernel/src/filesystem/rootfs/filesystem.cpp @@ -1,5 +1,6 @@ #include +#include "kernel/filesystem/filesystem.hpp" #include #include @@ -15,7 +16,7 @@ namespace kernel::filesystem::rootfs return operation_result::success; } - auto filesystem::lookup(kstd::shared_ptr const &, std::string_view) + auto filesystem::lookup(kstd::shared_ptr const &, std::string_view) const -> kstd::shared_ptr { return nullptr; diff --git a/kernel/src/filesystem/rootfs/inode.cpp b/kernel/src/filesystem/rootfs/inode.cpp index dbe7948..f64fb87 100644 --- a/kernel/src/filesystem/rootfs/inode.cpp +++ b/kernel/src/filesystem/rootfs/inode.cpp @@ -2,19 +2,16 @@ #include -#include -#include - #include namespace kernel::filesystem::rootfs { - auto inode::read(void * /*buffer*/, size_t /*offset*/, size_t /*size*/) const -> size_t + auto inode::read(void *, size_t, size_t) const -> size_t { return 0; } - auto inode::write(void const * /*buffer*/, size_t /*offset*/, size_t /*size*/) -> size_t + auto inode::write(void const *, size_t, size_t) -> size_t { return 0; } diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 8636d0f..bf9a77d 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -153,7 +153,7 @@ namespace kernel::filesystem -> void { auto new_fs_root = - kstd::make_shared(mount_point_dentry->get_parent(), fs->root_inode(), mount_point_dentry->get_name()); + kstd::make_shared(mount_point_dentry->parent(), fs->root_inode(), mount_point_dentry->name()); auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, parent_mount); m_mount_table.add_mount(new_mount); } @@ -174,7 +174,8 @@ namespace kernel::filesystem } } - auto vfs::resolve_path_internal(std::string_view path) -> std::pair, kstd::shared_ptr> + auto vfs::resolve_path_internal(std::string_view path) const + -> std::pair, kstd::shared_ptr> { if (!path::is_valid_absolute_path(path)) { @@ -187,7 +188,7 @@ namespace kernel::filesystem kapi::system::panic("[FILESYSTEM] no root mount found."); } - auto current_dentry = current_mount->get_root_dentry(); + auto current_dentry = current_mount->root_dentry(); auto path_parts = path::split(path); kstd::vector path_parts_vector(path_parts.begin(), path_parts.end()); @@ -207,16 +208,16 @@ namespace kernel::filesystem if (part == "..") { - auto parent_dentry = current_dentry->get_parent(); + auto parent_dentry = current_dentry->parent(); - if (current_dentry == current_mount->get_root_dentry()) + if (current_dentry == current_mount->root_dentry()) { - if (current_mount->get_mount_path() == "/") + if (current_mount->mount_path() == "/") { continue; } - if (auto parent_mount = current_mount->get_parent_mount()) + if (auto parent_mount = current_mount->parent_mount()) { current_mount = parent_mount; current_dentry = parent_dentry; @@ -242,13 +243,13 @@ namespace kernel::filesystem } else if (next_dentry->has_flag(dentry::dentry_flags::is_mount_point)) { - current_mount = m_mount_table.find_mount(next_dentry->get_absolute_path().view()); + current_mount = m_mount_table.find_mount(next_dentry->absolute_path().view()); if (!current_mount) { kapi::system::panic("[FILESYSTEM] mount for dentry with mounted flag not found."); } - next_dentry = current_mount->get_root_dentry(); + next_dentry = current_mount->root_dentry(); } if (next_dentry->get_inode()->is_symbolic_link()) @@ -271,7 +272,7 @@ namespace kernel::filesystem if (path::is_valid_absolute_path(symbolic_link_path)) { current_mount = m_mount_table.find_mount("/"); - current_dentry = current_mount->get_root_dentry(); + current_dentry = current_mount->root_dentry(); } continue; } @@ -281,12 +282,12 @@ namespace kernel::filesystem return {current_dentry, current_mount}; } - auto vfs::resolve_path(std::string_view path) -> kstd::shared_ptr + auto vfs::resolve_path(std::string_view path) const -> kstd::shared_ptr { return resolve_path_internal(path).first; } - auto vfs::find_mount(std::string_view path) -> kstd::shared_ptr + auto vfs::find_mount(std::string_view path) const -> kstd::shared_ptr { return resolve_path_internal(path).second; } diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 648ebb8..6fcc84e 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -105,7 +105,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); REQUIRE(mounted_monkey_1 != nullptr); - REQUIRE(vfs.close(mounted_monkey_1->get_absolute_path().view()) == + REQUIRE(vfs.close(mounted_monkey_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); @@ -128,10 +128,9 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(mounted_monkey_1 != nullptr); REQUIRE(mounted_fish1 != nullptr); - REQUIRE(vfs.close(mounted_monkey_1->get_absolute_path().view()) == - kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.close(mounted_fish1->get_absolute_path().view()) == + REQUIRE(vfs.close(mounted_monkey_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_fish1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); REQUIRE(vfs.unmount("/information/monkey_house/infrastructure") == @@ -148,7 +147,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); - REQUIRE(vfs.close(mounted_monkey_1->get_absolute_path().view()) == + REQUIRE(vfs.close(mounted_monkey_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); @@ -165,8 +164,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); - REQUIRE(vfs.close(info_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.close(info_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::close_failed); + REQUIRE(vfs.close(info_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(info_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::close_failed); } THEN("images can be stacked mounted and correct file system is unmounted again") @@ -177,8 +176,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto mounted_tickets = vfs.open("/information/entrance/tickets.txt"); REQUIRE(mounted_tickets != nullptr); - REQUIRE(vfs.close(mounted_tickets->get_absolute_path().view()) == - kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_tickets->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); mounted_tickets = vfs.open("/information/entrance/tickets.txt"); @@ -201,7 +199,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto water = vfs.open("/monkey_house/infrastructure/water.txt"); REQUIRE(water != nullptr); - REQUIRE(vfs.close(water->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(water->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); @@ -222,7 +220,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto water = vfs.open("/monkey_house/infrastructure/water.txt"); REQUIRE(water != nullptr); - REQUIRE(vfs.close(water->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(water->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); auto dev_ram_16 = vfs.open("/dev/ram16"); REQUIRE(dev_ram_16 == nullptr); @@ -240,7 +238,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); - REQUIRE(vfs.close(info_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(info_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/dev") == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); @@ -363,7 +361,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "sheep_1"); - REQUIRE(vfs.close(dentry->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(dentry->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); @@ -396,8 +394,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS buffer_as_str = std::string_view{reinterpret_cast(goat_buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "goat_1"); - REQUIRE(vfs.close(sheep_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.close(goat_1->get_absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(sheep_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(goat_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); @@ -408,7 +406,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto still_mounted_sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(still_mounted_sheep_1 != nullptr); - REQUIRE(vfs.close(still_mounted_sheep_1->get_absolute_path().view()) == + REQUIRE(vfs.close(still_mounted_sheep_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); diff --git a/kernel/src/test_support/filesystem/filesystem.cpp b/kernel/src/test_support/filesystem/filesystem.cpp index 12d43e0..ec70607 100644 --- a/kernel/src/test_support/filesystem/filesystem.cpp +++ b/kernel/src/test_support/filesystem/filesystem.cpp @@ -9,7 +9,7 @@ namespace kernel::tests::filesystem { - auto filesystem::lookup(kstd::shared_ptr const &, std::string_view) + auto filesystem::lookup(kstd::shared_ptr const &, std::string_view) const -> kstd::shared_ptr { return kstd::make_shared(); -- cgit v1.2.3 From 3d8ea5b1b833f39b77f0591fb2a301842ed5eb1c Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Sat, 16 May 2026 17:00:10 +0200 Subject: Refactor data types in ext2 --- kernel/include/kernel/filesystem/ext2/filesystem.hpp | 16 ++++++++-------- kernel/include/kernel/filesystem/ext2/inode.hpp | 2 +- kernel/src/filesystem/ext2/filesystem.cpp | 20 ++++++++++---------- kernel/src/filesystem/ext2/inode.cpp | 2 +- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index a408c64..45cd6a1 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -76,7 +76,7 @@ namespace kernel::filesystem::ext2 @brief Gets the revision level of the filesystem. @return The revision level. */ - [[nodiscard]] auto revision_level() const -> size_t; + [[nodiscard]] auto revision_level() const -> uint32_t; /** @brief Maps an inode block index to a global block number. @@ -88,18 +88,18 @@ namespace kernel::filesystem::ext2 -> ssize_t; private: - [[nodiscard]] auto read_inode(size_t inode_number) const -> kstd::shared_ptr; - [[nodiscard]] auto read_block_number_at_index(size_t block_number, size_t index) const -> size_t; + [[nodiscard]] auto read_inode(uint32_t inode_number) const -> kstd::shared_ptr; + [[nodiscard]] auto read_block_number_at_index(uint32_t block_number, size_t index) const -> uint32_t; - [[nodiscard]] auto read_singly_indirect_block_number(size_t singly_indirect_block_number, + [[nodiscard]] auto read_singly_indirect_block_number(uint32_t singly_indirect_block_number, size_t block_index_in_singly_indirect_block) const -> size_t; - [[nodiscard]] auto read_doubly_indirect_block_number(size_t doubly_indirect_block_number, + [[nodiscard]] auto read_doubly_indirect_block_number(uint32_t doubly_indirect_block_number, size_t block_index_in_doubly_indirect_block) const -> size_t; - [[nodiscard]] auto read_triply_indirect_block_number(size_t triply_indirect_block_number, + [[nodiscard]] auto read_triply_indirect_block_number(uint32_t triply_indirect_block_number, size_t block_index_in_triply_indirect_block) const -> size_t; - [[nodiscard]] auto inode_size() const -> size_t; - [[nodiscard]] auto inode_block_count(inode_data const & data) const -> size_t; + [[nodiscard]] auto inode_size() const -> uint16_t; + [[nodiscard]] auto inode_block_count(inode_data const & data) const -> uint32_t; [[nodiscard]] auto block_group_descriptor_table_offset() const -> size_t; [[nodiscard]] auto block_numbers_per_block() const -> size_t; diff --git a/kernel/include/kernel/filesystem/ext2/inode.hpp b/kernel/include/kernel/filesystem/ext2/inode.hpp index 5609319..f2496f0 100644 --- a/kernel/include/kernel/filesystem/ext2/inode.hpp +++ b/kernel/include/kernel/filesystem/ext2/inode.hpp @@ -94,7 +94,7 @@ namespace kernel::filesystem::ext2 @brief Get the size of the file represented by this inode. @return The size of the file in bytes. */ - [[nodiscard]] auto size() const -> size_t; + [[nodiscard]] auto size() const -> uint64_t; private: filesystem const * m_filesystem; diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 7633972..df5b4c4 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -62,7 +62,7 @@ namespace kernel::filesystem::ext2 auto const & inode_data = ext2_parent->data(); kstd::vector buffer(block_size()); - for (auto i = 0uz; i < inode_block_count(inode_data); ++i) + for (uint32_t i = 0; i < inode_block_count(inode_data); ++i) { auto const global_block_number = map_inode_block_index_to_global_block_number(i, inode_data); auto const block_offset = global_block_number * block_size(); @@ -87,7 +87,7 @@ namespace kernel::filesystem::ext2 return nullptr; } - auto filesystem::read_inode(size_t inode_number) const -> kstd::shared_ptr + auto filesystem::read_inode(uint32_t inode_number) const -> kstd::shared_ptr { auto const inodes_per_group = m_superblock.inodes_per_group; auto const block_group_index = (inode_number - 1) / inodes_per_group; @@ -141,7 +141,7 @@ namespace kernel::filesystem::ext2 return -1; } - auto filesystem::read_singly_indirect_block_number(size_t singly_indirect_block_number, + auto filesystem::read_singly_indirect_block_number(uint32_t singly_indirect_block_number, size_t block_index_in_singly_indirect_block) const -> size_t { if (singly_indirect_block_number == 0) @@ -152,7 +152,7 @@ namespace kernel::filesystem::ext2 return read_block_number_at_index(singly_indirect_block_number, block_index_in_singly_indirect_block); } - auto filesystem::read_doubly_indirect_block_number(size_t doubly_indirect_block_number, + auto filesystem::read_doubly_indirect_block_number(uint32_t doubly_indirect_block_number, size_t block_index_in_doubly_indirect_block) const -> size_t { if (doubly_indirect_block_number == 0) @@ -171,7 +171,7 @@ namespace kernel::filesystem::ext2 return read_singly_indirect_block_number(singly_indirect_block_number, block_index_in_singly_indirect_block); } - auto filesystem::read_triply_indirect_block_number(size_t triply_indirect_block_number, + auto filesystem::read_triply_indirect_block_number(uint32_t triply_indirect_block_number, size_t block_index_in_triply_indirect_block) const -> size_t { if (triply_indirect_block_number == 0) @@ -190,9 +190,9 @@ namespace kernel::filesystem::ext2 return read_doubly_indirect_block_number(doubly_indirect_block_number, block_index_in_doubly_indirect_block); } - auto filesystem::read_block_number_at_index(size_t block_number, size_t index) const -> size_t + auto filesystem::read_block_number_at_index(uint32_t block_number, size_t index) const -> uint32_t { - auto block_number_buffer = 0uz; + uint32_t block_number_buffer = 0; auto const block_start_offset = block_number * block_size(); auto const number_start_address = block_start_offset + index * sizeof(uint32_t); @@ -226,17 +226,17 @@ namespace kernel::filesystem::ext2 return constants::base_block_size << m_superblock.log_block_size; } - auto filesystem::revision_level() const -> size_t + auto filesystem::revision_level() const -> uint32_t { return m_superblock.rev_level; } - auto filesystem::inode_size() const -> size_t + auto filesystem::inode_size() const -> uint16_t { return revision_level() == constants::good_old_revision ? 128 : m_superblock.inode_size; } - auto filesystem::inode_block_count(inode_data const & data) const -> size_t + auto filesystem::inode_block_count(inode_data const & data) const -> uint32_t { return data.blocks / (2 << m_superblock.log_block_size); } diff --git a/kernel/src/filesystem/ext2/inode.cpp b/kernel/src/filesystem/ext2/inode.cpp index f8c818c..35a32ee 100644 --- a/kernel/src/filesystem/ext2/inode.cpp +++ b/kernel/src/filesystem/ext2/inode.cpp @@ -96,7 +96,7 @@ namespace kernel::filesystem::ext2 return (m_data.mode & constants::mode_mask) == constants::mode_symbolic_link; } - auto inode::size() const -> size_t + auto inode::size() const -> uint64_t { uint64_t size = m_data.size; -- cgit v1.2.3 From 918b270c61383f9f2220e55226d385be49c82326 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sun, 17 May 2026 15:19:54 +0000 Subject: chore: update ignore files --- .aiignore | 1 + .gitignore | 19 +++++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) create mode 100644 .aiignore diff --git a/.aiignore b/.aiignore new file mode 100644 index 0000000..60193e2 --- /dev/null +++ b/.aiignore @@ -0,0 +1 @@ +*.img \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4295aa6..36f5e77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,16 @@ -.venv/ +# Directories .cache/ - -.gdb_history - .devpod-internal/ - +.venv/ /build /docs/_build -qemu-*-*.log - -/desktop.ini - +# Generated files +.gdb_history +*.pyc coverage.info +desktop.ini +devcontainer-lock.json -__pycache__/ \ No newline at end of file +# QEMU log files +qemu-*-*.log -- cgit v1.2.3 From 504a0946f5813e65c2e40bcd08c525e65308ca79 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sun, 17 May 2026 15:22:39 +0000 Subject: ide: simplify devcontainer setup --- .devcontainer/x86-64/Containerfile | 31 +++++++++++++++++++++++++++++++ .devcontainer/x86-64/devcontainer.json | 10 +++------- 2 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 .devcontainer/x86-64/Containerfile diff --git a/.devcontainer/x86-64/Containerfile b/.devcontainer/x86-64/Containerfile new file mode 100644 index 0000000..25f98cd --- /dev/null +++ b/.devcontainer/x86-64/Containerfile @@ -0,0 +1,31 @@ +FROM registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:16.1.0-3-py3.14 + +RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install --yes --no-install-recommends \ + acpica-tools \ + clangd-22 \ + clang-tidy-22 \ + cmake \ + g++-16 \ + gcc-16 \ + gdb \ + git \ + git-lfs \ + grub2-common \ + grub-pc \ + mtools \ + ninja-build \ + python3-poetry \ + qemu-system-x86 \ + ssh \ + wget \ + xorriso + +RUN update-alternatives --install /usr/bin/cpp cpp /usr/bin/cpp-16 100 && \ + update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-16 100 && \ + update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-16 100 && \ + update-alternatives --install /usr/bin/gcc-ar gcc-ar /usr/bin/gcc-ar-16 100 && \ + update-alternatives --install /usr/bin/gcc-nm gcc-nm /usr/bin/gcc-nm-16 100 && \ + update-alternatives --install /usr/bin/gcc-ranlib gcc-ranlib /usr/bin/gcc-ranlib-16 100 && \ + update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-16 100 && \ + update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-22 100 && \ + update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-22 100 diff --git a/.devcontainer/x86-64/devcontainer.json b/.devcontainer/x86-64/devcontainer.json index 54bfe08..ebfe64d 100644 --- a/.devcontainer/x86-64/devcontainer.json +++ b/.devcontainer/x86-64/devcontainer.json @@ -1,13 +1,9 @@ { "name": "TeachOS on x86-64", - "image": "registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64:16.1.0-3-py3.14", - "features": { - "ghcr.io/devcontainers/features/git:1": {}, - "ghcr.io/devcontainers/features/git-lfs:1": {}, - "ghcr.io/devcontainers-extra/features/apt-packages:1": { - "packages": "acpica-tools,build-essential,clang-tidy-22,clangd-22,cmake,gdb,grub2-common,grub-pc,mtools,ninja-build,python3-poetry,qemu-system-x86,ssh,wget,xorriso" - } + "build": { + "dockerfile": "Containerfile" }, + "remoteUser": "ubuntu", "customizations": { "vscode": { "extensions": [ -- cgit v1.2.3 From cb61eb0c7a2b259ebedeca78ce604742d4bbc0e8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 18 May 2026 13:14:49 +0200 Subject: ide: fix box character rendering in devcontainer --- .devcontainer/x86-64/Containerfile | 8 +++++++- .devcontainer/x86-64/devcontainer.json | 5 +++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.devcontainer/x86-64/Containerfile b/.devcontainer/x86-64/Containerfile index 25f98cd..87a33b8 100644 --- a/.devcontainer/x86-64/Containerfile +++ b/.devcontainer/x86-64/Containerfile @@ -12,13 +12,19 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install --yes --no- git-lfs \ grub2-common \ grub-pc \ + locales \ mtools \ ninja-build \ python3-poetry \ qemu-system-x86 \ ssh \ wget \ - xorriso + xorriso \ + && rm -rf /var/lib/apt/lists/* + +RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \ + dpkg-reconfigure --frontend=noninteractive locales && \ + update-locale LANG=en_US.UTF-8 RUN update-alternatives --install /usr/bin/cpp cpp /usr/bin/cpp-16 100 && \ update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-16 100 && \ diff --git a/.devcontainer/x86-64/devcontainer.json b/.devcontainer/x86-64/devcontainer.json index ebfe64d..dc70596 100644 --- a/.devcontainer/x86-64/devcontainer.json +++ b/.devcontainer/x86-64/devcontainer.json @@ -20,5 +20,10 @@ }, "containerEnv": { "DEBUGINFOD_URLS": "NOSUCHURL" + }, + "remoteEnv": { + "LANG": "en_US.UTF-8", + "LC_ALL": "en_US.UTF-8", + "TERM": "xterm-256color" } } \ No newline at end of file -- cgit v1.2.3 From 845d4d0522f063dde7e84b281c0b191de1a43dee Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 18 May 2026 13:50:49 +0200 Subject: ci: enable code quality reporting --- .gitlab-ci.yml | 10 ++++++++- scripts/ci/parse_clang_tidy.py | 47 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 scripts/ci/parse_clang_tidy.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 26e474e..01424bd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,9 +4,13 @@ build:bht: script: - cmake --preset bht - cmake --build --preset bht-dbg + - set -o pipefail + - python3 scripts/ci/parse_clang_tidy.py build_output.txt > code-quality.json artifacts: paths: - build/bht/ + reports: + codequality: code-quality.json expire_in: 5 min build:bootable: @@ -14,7 +18,9 @@ build:bootable: image: registry.gitlab.ost.ch:45023/teachos/devcontainers/x86-64.ci:latest script: - cmake --preset $PLATFORM - - cmake --build --preset $PLATFORM-$TYPE + - cmake --build --preset $PLATFORM-$TYPE 2>&1 | tee build_output.txt + - set -o pipefail + - python3 scripts/ci/parse_clang_tidy.py build_output.txt > code-quality.json - cp build/${PLATFORM}/bin/**/kernel.{dis,elf,sym,iso} . artifacts: paths: @@ -22,6 +28,8 @@ build:bootable: - kernel.elf - kernel.sym - kernel.iso + reports: + codequality: code-quality.json expire_in: 1 week parallel: diff --git a/scripts/ci/parse_clang_tidy.py b/scripts/ci/parse_clang_tidy.py new file mode 100644 index 0000000..48596de --- /dev/null +++ b/scripts/ci/parse_clang_tidy.py @@ -0,0 +1,47 @@ +import hashlib +import json +import re +import sys + + +def parse_clang_tidy(log_file_path: str) -> list[dict]: + issues = [] + pattern = re.compile( + r"^(.*?):(\d+):(\d+):\s+(warning|error|note):\s+(.*?)\s+\[(.*?)\]$" + ) + + with open(log_file_path, "r") as f: + for line in f: + match = pattern.match(line) + if not match: + continue + + path, line, column, severity, message, name = match.groups() + severity = "minor" if severity == "warning" else "major" + + fingerprint_data = f"{path}:{line}:{name}" + fingerprint = hashlib.sha256(fingerprint_data.encode()).hexdigest() + + issues.append( + { + "description": f"{message} ({name})", + "fingerprint": fingerprint, + "severity": severity, + "location": { + "path": path, + "lines": { + "begin": int(line), + }, + }, + } + ) + + return issues + + +if __name__ == "__main__": + if len(sys.argv) < 2: + sys.exit("Usage: python3 parse_clang_tidy.py ") + + issues = parse_clang_tidy(sys.argv[1]) + print(json.dumps(issues, indent=2)) -- cgit v1.2.3 From 24232fa7db160b4e7b702345e0892906dcccd8cd Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 18 May 2026 14:08:13 +0200 Subject: bht: improve catch2 configuration --- CMakeLists.txt | 2 ++ CMakePresets.json | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d48a13..fb5b101 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,8 @@ if (BUILD_TESTING) find_package("Catch2") include("Catch") + + add_compile_definitions("CATCH_CONFIG_NO_COUNTER") endif() #[============================================================================[ diff --git a/CMakePresets.json b/CMakePresets.json index d5123a2..0e5dd88 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -24,10 +24,7 @@ { "name": "bht", "inherits": "base", - "description": "Build-host Testing", - "cacheVariables": { - "CATCH_CONFIG_NO_COUNTER": true - } + "description": "Build-host Testing" } ], "buildPresets": [ -- cgit v1.2.3 From b92418534cb0018dec43349f6d72f64a5b6e18db Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 18 May 2026 14:11:44 +0200 Subject: ci: add missing redirection --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 01424bd..ebb2b8a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,7 +3,7 @@ build:bht: image: registry.gitlab.ost.ch:45023/teachos/devcontainers/bht.ci:latest script: - cmake --preset bht - - cmake --build --preset bht-dbg + - cmake --build --preset bht-dbg 2>&1 | tee build_output.txt - set -o pipefail - python3 scripts/ci/parse_clang_tidy.py build_output.txt > code-quality.json artifacts: -- cgit v1.2.3 From de8efd2af75a8d8d5dd4b0b5c125d006bf7f1ba9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 18 May 2026 14:26:52 +0200 Subject: ci: fix code quality file paths --- scripts/ci/parse_clang_tidy.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/ci/parse_clang_tidy.py b/scripts/ci/parse_clang_tidy.py index 48596de..6167c99 100644 --- a/scripts/ci/parse_clang_tidy.py +++ b/scripts/ci/parse_clang_tidy.py @@ -1,5 +1,6 @@ import hashlib import json +import os import re import sys @@ -9,6 +10,7 @@ def parse_clang_tidy(log_file_path: str) -> list[dict]: pattern = re.compile( r"^(.*?):(\d+):(\d+):\s+(warning|error|note):\s+(.*?)\s+\[(.*?)\]$" ) + repo_root = os.environ.get("CI_PROJECT_DIR", os.getcwd()) with open(log_file_path, "r") as f: for line in f: @@ -19,7 +21,12 @@ def parse_clang_tidy(log_file_path: str) -> list[dict]: path, line, column, severity, message, name = match.groups() severity = "minor" if severity == "warning" else "major" - fingerprint_data = f"{path}:{line}:{name}" + try: + path = os.path.relpath(path, repo_root) + except ValueError: + pass + + fingerprint_data = f"{path}:{line}:{name}:{message}" fingerprint = hashlib.sha256(fingerprint_data.encode()).hexdigest() issues.append( -- cgit v1.2.3 From a7ba17a11c17c9975e7dc2233264913aa2e79538 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 18 May 2026 14:31:17 +0200 Subject: ci: improve code quality fingerprints --- scripts/ci/parse_clang_tidy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ci/parse_clang_tidy.py b/scripts/ci/parse_clang_tidy.py index 6167c99..ec51d77 100644 --- a/scripts/ci/parse_clang_tidy.py +++ b/scripts/ci/parse_clang_tidy.py @@ -26,7 +26,7 @@ def parse_clang_tidy(log_file_path: str) -> list[dict]: except ValueError: pass - fingerprint_data = f"{path}:{line}:{name}:{message}" + fingerprint_data = f"{path}:{line}:{column}:{message}:{name}" fingerprint = hashlib.sha256(fingerprint_data.encode()).hexdigest() issues.append( -- cgit v1.2.3 From e381b49caf9d29f405cb8c9d7c2b81640135d3ba Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 18 May 2026 14:39:27 +0200 Subject: ci: generate unique quality report names --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ebb2b8a..774708b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,12 +5,12 @@ build:bht: - cmake --preset bht - cmake --build --preset bht-dbg 2>&1 | tee build_output.txt - set -o pipefail - - python3 scripts/ci/parse_clang_tidy.py build_output.txt > code-quality.json + - python3 scripts/ci/parse_clang_tidy.py build_output.txt > code-quality-bht.json artifacts: paths: - build/bht/ reports: - codequality: code-quality.json + codequality: code-quality-bht.json expire_in: 5 min build:bootable: @@ -20,7 +20,7 @@ build:bootable: - cmake --preset $PLATFORM - cmake --build --preset $PLATFORM-$TYPE 2>&1 | tee build_output.txt - set -o pipefail - - python3 scripts/ci/parse_clang_tidy.py build_output.txt > code-quality.json + - python3 scripts/ci/parse_clang_tidy.py build_output.txt > code-quality-$PLATFORM-$TYPE.json - cp build/${PLATFORM}/bin/**/kernel.{dis,elf,sym,iso} . artifacts: paths: @@ -29,7 +29,7 @@ build:bootable: - kernel.sym - kernel.iso reports: - codequality: code-quality.json + codequality: code-quality-$PLATFORM-$TYPE.json expire_in: 1 week parallel: -- cgit v1.2.3 From a50d6cfcea67b11f6689ec825afc2e2b33252714 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 18 May 2026 15:21:39 +0200 Subject: ci: enable jUnit style test reports --- .gitlab-ci.yml | 1 + CMakeLists.txt | 1 + kernel/CMakeLists.txt | 2 +- libs/acpi/CMakeLists.txt | 2 +- libs/kstd/CMakeLists.txt | 2 +- 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 774708b..7551708 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -58,6 +58,7 @@ test:bht: coverage_report: coverage_format: cobertura path: coverage/cobertura-coverage.xml + junit: build/bht/**/junit.xml license_check: stage: .pre diff --git a/CMakeLists.txt b/CMakeLists.txt index fb5b101..8118e0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,7 @@ if (BUILD_TESTING) include("Catch") add_compile_definitions("CATCH_CONFIG_NO_COUNTER") + set(CATCH_TEST_ARGS "EXTRA_ARGS" "--reporter" "junit::out=junit.xml" "--reporter" "console::out=-::colour-mode=ansi") endif() #[============================================================================[ diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 860e28b..2ce9621 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -220,5 +220,5 @@ if(BUILD_TESTING) ) enable_coverage("kernel_tests") - catch_discover_tests("kernel_tests") + catch_discover_tests("kernel_tests" ${CATCH_TEST_ARGS}) endif() diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index 2c4d76d..135ce6a 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -132,5 +132,5 @@ if(BUILD_TESTING) EXCLUDE_FROM_ALL NO ) - catch_discover_tests("acpi_tests") + catch_discover_tests("acpi_tests" ${CATCH_TEST_ARGS}) endif() diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 6902891..1cc75b7 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -114,5 +114,5 @@ if(BUILD_TESTING) enable_coverage("kstd_tests") endif() - catch_discover_tests("kstd::tests") + catch_discover_tests("kstd::tests" ${CATCH_TEST_ARGS}) endif() \ No newline at end of file -- cgit v1.2.3 From fd55ecdd2d3b96d667a2f5fb13625d363a7a9f93 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 18 May 2026 18:30:46 +0200 Subject: python: add black --- poetry.lock | 504 +++++++++++++++++++++++++++++++++++++++++++++++++++------ pyproject.toml | 9 +- 2 files changed, 462 insertions(+), 51 deletions(-) diff --git a/poetry.lock b/poetry.lock index fbccc60..94630e9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.3.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.4.1 and should not be changed by hand. [[package]] name = "accessible-pygments" @@ -31,6 +31,18 @@ files = [ {file = "alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e"}, ] +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + [[package]] name = "babel" version = "2.18.0" @@ -71,39 +83,39 @@ lxml = ["lxml"] [[package]] name = "black" -version = "26.3.1" +version = "26.5.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "black-26.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:86a8b5035fce64f5dcd1b794cf8ec4d31fe458cf6ce3986a30deb434df82a1d2"}, - {file = "black-26.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5602bdb96d52d2d0672f24f6ffe5218795736dd34807fd0fd55ccd6bf206168b"}, - {file = "black-26.3.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c54a4a82e291a1fee5137371ab488866b7c86a3305af4026bdd4dc78642e1ac"}, - {file = "black-26.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:6e131579c243c98f35bce64a7e08e87fb2d610544754675d4a0e73a070a5aa3a"}, - {file = "black-26.3.1-cp310-cp310-win_arm64.whl", hash = "sha256:5ed0ca58586c8d9a487352a96b15272b7fa55d139fc8496b519e78023a8dab0a"}, - {file = "black-26.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:28ef38aee69e4b12fda8dba75e21f9b4f979b490c8ac0baa7cb505369ac9e1ff"}, - {file = "black-26.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bf9bf162ed91a26f1adba8efda0b573bc6924ec1408a52cc6f82cb73ec2b142c"}, - {file = "black-26.3.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:474c27574d6d7037c1bc875a81d9be0a9a4f9ee95e62800dab3cfaadbf75acd5"}, - {file = "black-26.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:5e9d0d86df21f2e1677cc4bd090cd0e446278bcbbe49bf3659c308c3e402843e"}, - {file = "black-26.3.1-cp311-cp311-win_arm64.whl", hash = "sha256:9a5e9f45e5d5e1c5b5c29b3bd4265dcc90e8b92cf4534520896ed77f791f4da5"}, - {file = "black-26.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e6f89631eb88a7302d416594a32faeee9fb8fb848290da9d0a5f2903519fc1"}, - {file = "black-26.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cd2012d35b47d589cb8a16faf8a32ef7a336f56356babd9fcf70939ad1897f"}, - {file = "black-26.3.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f76ff19ec5297dd8e66eb64deda23631e642c9393ab592826fd4bdc97a4bce7"}, - {file = "black-26.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:ddb113db38838eb9f043623ba274cfaf7d51d5b0c22ecb30afe58b1bb8322983"}, - {file = "black-26.3.1-cp312-cp312-win_arm64.whl", hash = "sha256:dfdd51fc3e64ea4f35873d1b3fb25326773d55d2329ff8449139ebaad7357efb"}, - {file = "black-26.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:855822d90f884905362f602880ed8b5df1b7e3ee7d0db2502d4388a954cc8c54"}, - {file = "black-26.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8a33d657f3276328ce00e4d37fe70361e1ec7614da5d7b6e78de5426cb56332f"}, - {file = "black-26.3.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f1cd08e99d2f9317292a311dfe578fd2a24b15dbce97792f9c4d752275c1fa56"}, - {file = "black-26.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:c7e72339f841b5a237ff14f7d3880ddd0fc7f98a1199e8c4327f9a4f478c1839"}, - {file = "black-26.3.1-cp313-cp313-win_arm64.whl", hash = "sha256:afc622538b430aa4c8c853f7f63bc582b3b8030fd8c80b70fb5fa5b834e575c2"}, - {file = "black-26.3.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2d6bfaf7fd0993b420bed691f20f9492d53ce9a2bcccea4b797d34e947318a78"}, - {file = "black-26.3.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f89f2ab047c76a9c03f78d0d66ca519e389519902fa27e7a91117ef7611c0568"}, - {file = "black-26.3.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b07fc0dab849d24a80a29cfab8d8a19187d1c4685d8a5e6385a5ce323c1f015f"}, - {file = "black-26.3.1-cp314-cp314-win_amd64.whl", hash = "sha256:0126ae5b7c09957da2bdbd91a9ba1207453feada9e9fe51992848658c6c8e01c"}, - {file = "black-26.3.1-cp314-cp314-win_arm64.whl", hash = "sha256:92c0ec1f2cc149551a2b7b47efc32c866406b6891b0ee4625e95967c8f4acfb1"}, - {file = "black-26.3.1-py3-none-any.whl", hash = "sha256:2bd5aa94fc267d38bb21a70d7410a89f1a1d318841855f698746f8e7f51acd1b"}, - {file = "black-26.3.1.tar.gz", hash = "sha256:2c50f5063a9641c7eed7795014ba37b0f5fa227f3d408b968936e24bc0566b07"}, + {file = "black-26.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:402454bfdd7a940be00455e87309438a24b328b7ba7d80b7207e8a87b32ffc29"}, + {file = "black-26.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4863b2a2c382661a018bf2213f2b957fa34511df131259ffaa8d54859620ac31"}, + {file = "black-26.5.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:490b623006a75c0ea59c1ecf91cc76ecb9d66df1482c3a53f4f7de95a7c85e10"}, + {file = "black-26.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:6f53deb3d1108a523212da5c79e5c0cd76abcc548948f2d8415e62929c81a569"}, + {file = "black-26.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:a62f9d069ac27de20c6fa3dbf60d7c951141c4025bb9755274802d05b1aa418b"}, + {file = "black-26.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:862945b2a08193cdff9f632f51bdadbb11e6852da1d31c306a3508449dc81b84"}, + {file = "black-26.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:03102aa97c279e5f62e1e1ab828cfe8aa72c3af4cf86f9448e5537b2519cbfea"}, + {file = "black-26.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:990ee0e1d96dd8ca623f19dd3f339c138bdc02f74e4fea01cc64aee38944ea2b"}, + {file = "black-26.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:209fabb250681900502b3b6a03e31d8cac606c9ef9629fd0fbd5d33235647c00"}, + {file = "black-26.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:dbb6fc70f8bd9821981fd47efb68a5be0eee9055f400eb3bf2dbebf49f9ec4fe"}, + {file = "black-26.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b92983a6674c133ca61d6b4fea17f76cbbaac582ea583002792ee1094dbece49"}, + {file = "black-26.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1f80998e73fcfc67fc1d222060cf34ab213f1ae7e131b5c8199d93405890c13a"}, + {file = "black-26.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:081df4dc908702e2becd66d714f125a954cbf1c6dbe2ad83a6be313368c7c2db"}, + {file = "black-26.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf015b38829ca32a699312fdcfb8c15bd0b156192f5400bd0b559c6bfef25236"}, + {file = "black-26.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:828db2292848cf427592fcd162f02d770849d20ea4bdda2806e9494b3a15d481"}, + {file = "black-26.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c2b64ce9841e8b8254c3d702ebccdaf5c520607df8aa4176f5732b7f9af1e6f6"}, + {file = "black-26.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0a789a41b386f0f83711785f182f2977138ba9cc1f41ad0f6fbc8faac4d2639e"}, + {file = "black-26.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f69837f7e26d67b1d1e9d0ed49231a14a0469f266e44cd142873e0552f325395"}, + {file = "black-26.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:c5b08371561dae9c90391fe7f2138fe7fa495437d3bb134eb865839036e65784"}, + {file = "black-26.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:3968ce82ca0bd4914769518490d91a9b0ef2ff2fc68e2122d22b5915a0342eaa"}, + {file = "black-26.5.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:ea8a0c4505486c132c6640e4e108d25f41360a06d844db5a76477c3dbae1b616"}, + {file = "black-26.5.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2178a70e7c45fb85999b687d8326abceef1e7227463d5d7e07ef125c9fbb9c5c"}, + {file = "black-26.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3ad14d7c24c40eafecf4fb212d9c01e7c7b2ab05c8646b351c93728f499c555"}, + {file = "black-26.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:8ea767bae9c4f331ea9ad2e08895c951e600dffd550a42624d5210a908720b39"}, + {file = "black-26.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:d658f4ee6167797b08be07ee4bbf6045753ddabfc676c3cb0eec23752ca83eff"}, + {file = "black-26.5.0-py3-none-any.whl", hash = "sha256:241f25bf59f5ca17f5121031e310e089b84cd22bb4eca47360099ea825544f17"}, + {file = "black-26.5.0.tar.gz", hash = "sha256:5cbe4cc4037ffca34cdb0a6a9a046f104b262d0bd63c30fd4a88c7adc2049b1d"}, ] [package.dependencies] @@ -293,14 +305,14 @@ files = [ [[package]] name = "click" -version = "8.3.3" +version = "8.4.0" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613"}, - {file = "click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2"}, + {file = "click-8.4.0-py3-none-any.whl", hash = "sha256:40c50b7c6c6adac2823d411041ec84f3f103f1b280d5e9ce0d7f998995832f81"}, + {file = "click-8.4.0.tar.gz", hash = "sha256:638f1338fe1235c8f4e008e4a8a254fb5c5fbdcbb40ece3c9142ebb78e792973"}, ] [package.dependencies] @@ -333,14 +345,14 @@ files = [ [[package]] name = "idna" -version = "3.13" +version = "3.15" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "idna-3.13-py3-none-any.whl", hash = "sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3"}, - {file = "idna-3.13.tar.gz", hash = "sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242"}, + {file = "idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8"}, + {file = "idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc"}, ] [package.extras] @@ -376,6 +388,39 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "lance-namespace" +version = "0.6.1" +description = "Lance Namespace interface and plugin registry" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "lance_namespace-0.6.1-py3-none-any.whl", hash = "sha256:9699c9e3f12236e5e08ea979cc4e036a8e3c67ed2f37ae6f25c5353ab908e1be"}, + {file = "lance_namespace-0.6.1.tar.gz", hash = "sha256:f0deea442bd3f1056a8e2fed056ae2778e3356517ec2e680db049058b824d131"}, +] + +[package.dependencies] +lance-namespace-urllib3-client = "0.6.1" + +[[package]] +name = "lance-namespace-urllib3-client" +version = "0.6.1" +description = "Lance Namespace Specification" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "lance_namespace_urllib3_client-0.6.1-py3-none-any.whl", hash = "sha256:b9c103e1377ad46d2bd70eec894bfec0b1e2133dae0964d7e4de543c6e16293b"}, + {file = "lance_namespace_urllib3_client-0.6.1.tar.gz", hash = "sha256:31fbd058ce1ea0bf49045cdeaa756360ece0bc61e9e10276f41af6d217debe87"}, +] + +[package.dependencies] +pydantic = ">=2" +python-dateutil = ">=2.8.2" +typing-extensions = ">=4.7.1" +urllib3 = ">=1.25.3,<3.0.0" + [[package]] name = "markupsafe" version = "3.0.3" @@ -487,6 +532,88 @@ files = [ {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, ] +[[package]] +name = "numpy" +version = "2.4.5" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.11" +groups = ["main"] +files = [ + {file = "numpy-2.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3176dc8ff71dbb593606f91a69ad0c3cd3303c7eb546af477370ab9edf760288"}, + {file = "numpy-2.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1811150e5148f5a01a7cc282cb2f489b4a3050a773e173adb480e507bad3a3d7"}, + {file = "numpy-2.4.5-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0d63a780070871210853ba01e90b88f9b85cf2abf63a7f143d5127189265ddf6"}, + {file = "numpy-2.4.5-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:0c6919cefafb3b76cd46a89dbb203bf1dd95529d2a6d09fef2d325d95d6a79d8"}, + {file = "numpy-2.4.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d51efede1e58e8b11877536a5518f60e318d8ff69b89ad7b38ee5e431b24d772"}, + {file = "numpy-2.4.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:07ce7e74da92d7c71b5df157b9758bcdd53d7fea10602154de3afd2b3ddc34dd"}, + {file = "numpy-2.4.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d7828234a13185effb34979e146f9921f2a65dfbbe215e6dbb57d6478fc8e059"}, + {file = "numpy-2.4.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f96083adc3dfc1bbf778f2c79654d88115fa07074c97cb724fe9508f12d91c55"}, + {file = "numpy-2.4.5-cp311-cp311-win32.whl", hash = "sha256:4ed78c904a638b6e5d7cd4db90c06fca5fc6ec2f28d258305368f454a50e79cf"}, + {file = "numpy-2.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:079b0fad6f2899b23c5da89792b5409d2d83fc83e8bd5c2299cc9c397a264864"}, + {file = "numpy-2.4.5-cp311-cp311-win_arm64.whl", hash = "sha256:d6c78e260b53affe9b395a9d54fc61f101f9521c4d9452c7e9e3718b19e2215b"}, + {file = "numpy-2.4.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:654fb8674b61b1c4bd568f944d13a908566fdcb0d797303521d4149d16da05ef"}, + {file = "numpy-2.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4cd9f6fa7ce10dc4627f2bb81dd9075dab67e94632e04c2b638e12575ddaa862"}, + {file = "numpy-2.4.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:4f5bc96d35d94e4ceab8b38a92241b4611e95dc44e63b9f1fa2a331858ee3507"}, + {file = "numpy-2.4.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:4bb33e900ee81730ad77a258965134aa8ceac805124f7e5229347beda4b8d0aa"}, + {file = "numpy-2.4.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32f8f852273ef32b291201ac2a2c97629c4a1ee8632bb670e3443eaa09fc2e72"}, + {file = "numpy-2.4.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:685681e956fc8dcb75adc6ff26694e1dfd738b24bd8d4696c51ca0110157f912"}, + {file = "numpy-2.4.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6f64dd84b277a737eb59513f6b9bb6195bf41ab11941ef15b2562dbab43fa8ef"}, + {file = "numpy-2.4.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b42d9496f79e3a728192f05a42d86e36163217b7cdecb3813d0028a0aa6b72d7"}, + {file = "numpy-2.4.5-cp312-cp312-win32.whl", hash = "sha256:86d980970f5110595ca14855768073b08585fc1acc36895de303e039e7dee4a5"}, + {file = "numpy-2.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:3333dba6a4e611d666f69e177ba8fe4140366ff681a5feb2374d3fd4fff3acb6"}, + {file = "numpy-2.4.5-cp312-cp312-win_arm64.whl", hash = "sha256:4593d197270b894efeb538dcbe227e4bcf1c77f88c4c6bf933ead812cfaa4453"}, + {file = "numpy-2.4.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1ef248460b645c102026b82337cc4e88231909c66dd77b59ec6d6cac7e44f277"}, + {file = "numpy-2.4.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4603622bdcdbf8dccb1d9d5b21d16a7aa4e473ae6c8e14048d846fd4ca2907a0"}, + {file = "numpy-2.4.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:6c18d49c67689c562854b53fdc433b93e47c12952aa6fa6d59f185e1a5992419"}, + {file = "numpy-2.4.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:b1c663ddc641f4192e90511bec61a09bc231e3bbdb996cdc6edbcaa0e528d685"}, + {file = "numpy-2.4.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93793222b524f692f12b2f8752ce8b1d9d9125b2bfd5dbf0fb69c92c5e1ce86c"}, + {file = "numpy-2.4.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1616bde34b2bcba2fa9bde06217ce00da4f3d1bdfb264d54525a99e8fe170d83"}, + {file = "numpy-2.4.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:09d7d97da1c2c62f4818b3e150a57572ff8dcf1cf5ac501aac832ffd4ebd9566"}, + {file = "numpy-2.4.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d68d0b355ab2e39fe0de59001d7151dfdbbb880ef67baeed806661e03df5097"}, + {file = "numpy-2.4.5-cp313-cp313-win32.whl", hash = "sha256:fe28b64777ddfa0eca9b5f51474034ebe3dcb8324f48f27b28f479085673ae33"}, + {file = "numpy-2.4.5-cp313-cp313-win_amd64.whl", hash = "sha256:fb4a6c9c537d6ccec9cc4aeae4261bd3cc79b070c67ddc0646f5b1c07fddde42"}, + {file = "numpy-2.4.5-cp313-cp313-win_arm64.whl", hash = "sha256:6d7df2da2e7ea0624a43aa368104b3a3ce14aae98ad4bb2c9a93fecef76f1c97"}, + {file = "numpy-2.4.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:2a235607a18df941760a695927051af4b1cd5d3ee85840d0e2af816785771feb"}, + {file = "numpy-2.4.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:58dcf64969d870f36bc7fbd557d2617e997db7dc06261b6e3327148ea460d0a4"}, + {file = "numpy-2.4.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:235f54b0156274d8fa3155db3ed6d2f401c7e8f3367c90db0a12f02a58fde6ed"}, + {file = "numpy-2.4.5-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3b5bb65437a3555c648e706475db01c645559ca80dc8b03e4f202ea757e0d6"}, + {file = "numpy-2.4.5-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7f09a7e5f017d7098c66522097c96257411c9620c0926212200d66bc8cee3976"}, + {file = "numpy-2.4.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:993a88d8fdd8554466a8765cd8bacd97ba56b70ca6b0a04bcdca77f5afed4222"}, + {file = "numpy-2.4.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:84f58bed609b5669f5ad3d597901a4f1f86ee5b3c3708aaa55f05b4fe6e0f656"}, + {file = "numpy-2.4.5-cp313-cp313t-win32.whl", hash = "sha256:7200c58f3f933ca61e66346667dcc8510bb111995e9ce15398a731e6a4afa4bb"}, + {file = "numpy-2.4.5-cp313-cp313t-win_amd64.whl", hash = "sha256:c26c71080d35db5002102f5d9ff614d45de02aa1f7802943e691e063e5ee93bc"}, + {file = "numpy-2.4.5-cp313-cp313t-win_arm64.whl", hash = "sha256:2caa576d1707b275cba1aeb60a5c50daa6fa2a3f28ecb08123bc05fd439005db"}, + {file = "numpy-2.4.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:889ca2c072315de638a5194a772aa1fa2df92bdd6175f6a222d4784040424b61"}, + {file = "numpy-2.4.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:89e89304fb1f8c3f0ecfa4a7d48f311dd79771336a940e920159d643d1307e77"}, + {file = "numpy-2.4.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:144fcc5a3a17679b2b82543b4a2d8dd29937230a7af13232b5f753872feb6361"}, + {file = "numpy-2.4.5-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:398bb16772b265b9fa5c07b07072646ea97137c10ffb62a9a087b277fc825c29"}, + {file = "numpy-2.4.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb352e7b8876da1249e72254736d6c58c505fa4e58a3d7e30efca241ca9ca9ce"}, + {file = "numpy-2.4.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7341b08ff8124d7353939778e2707b8732d03c78c1c30e0815aba2dacbe1245a"}, + {file = "numpy-2.4.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:deb01226f012539f3945261ffe1c10aec081a0fa0a5c925419933c70f3ae2d23"}, + {file = "numpy-2.4.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d888bdf7335f76878c3c7b264ac1ff089863e211ec81249f9fb5795c2183dc25"}, + {file = "numpy-2.4.5-cp314-cp314-win32.whl", hash = "sha256:15f90d1256e9b2320aff24fde44815b787ab6d7c49a1a11bfd8138b321c5f080"}, + {file = "numpy-2.4.5-cp314-cp314-win_amd64.whl", hash = "sha256:4bd2cd4ef9c0afa87de73723c0a33c0edff62143e1432917458e26d3d195d87f"}, + {file = "numpy-2.4.5-cp314-cp314-win_arm64.whl", hash = "sha256:db304568c650e9d7039744d3575d0d287754debb2057d7c7b8cdfdc2c487a957"}, + {file = "numpy-2.4.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6de2883e0d2c63eae1bab1a84b390dca74aabb3d20ea1f5d58f360853c83abf3"}, + {file = "numpy-2.4.5-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:06760fe73ae5005008748d182de612c733542af3cde063d532cd2127561b27be"}, + {file = "numpy-2.4.5-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:4b51a01745cb04cc19278482207444b4d30728ce91c28d27a3bfae5fc6ff24c7"}, + {file = "numpy-2.4.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a05636d7937d0936f271e5ba957fa8d746b5be3c2025caa1a2508f4fe521d40"}, + {file = "numpy-2.4.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b86f56048ed09c3bbe48962a7dff077c2fd3274f8cf981800f3b38eac49cc3"}, + {file = "numpy-2.4.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:130d58151c4db23e9fa860b84784e219a3aa3e030acc88a493ea37006c4dfd4c"}, + {file = "numpy-2.4.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d475afc8cbe935ff5944f753d863bba774d7f4e1feaaa4102901e3e053ca5963"}, + {file = "numpy-2.4.5-cp314-cp314t-win32.whl", hash = "sha256:27f4a6dc26353a860b348961b9aa9e009835688b435cfa105e873b8dc2c726f5"}, + {file = "numpy-2.4.5-cp314-cp314t-win_amd64.whl", hash = "sha256:76ac6e90f5e226011c88f9b7040a4bcae612518bc7e9adc127e697a13b28ad1a"}, + {file = "numpy-2.4.5-cp314-cp314t-win_arm64.whl", hash = "sha256:7c392e2c1bf596701d3c6832be7567eab5d5b0a13865036c33365ee097d37f8b"}, + {file = "numpy-2.4.5-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6bf0bfc1c2e1db972e30b6cd3d4861f477f3af908b27799b239dc3cbe3eb4b95"}, + {file = "numpy-2.4.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:73d664413fb97229149c4711ef56531a6fe8c15c1c2626b0bbe497b84c287e70"}, + {file = "numpy-2.4.5-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:b35bee5ef99e8d227a07829bee2e864fcb65f7c157646fcd8ec8b4b45dd8b88f"}, + {file = "numpy-2.4.5-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:02981d0fc9f9ce147643d552966d47f329a02f7ecb3b113e84207242f20dfa83"}, + {file = "numpy-2.4.5-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0e63caf31a1df06338ae63d999f7a33a675ced62eea9c9b02db4b1c1f45cff38"}, + {file = "numpy-2.4.5-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d8fc52b85a7b45e474be53eddf08e006d22e381a4e41bcde8e4aa08da0e7d198"}, + {file = "numpy-2.4.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:40c71d50a4da1a7c317af419461052d3911a5770bfc5fd55baf52cc45e7a2c20"}, + {file = "numpy-2.4.5.tar.gz", hash = "sha256:ca670567a5683b7c1670ec03e0ddd5862e10934e92a70751d68d7b7b74ca7f9f"}, +] + [[package]] name = "packaging" version = "26.2" @@ -528,6 +655,221 @@ files = [ {file = "platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a"}, ] +[[package]] +name = "pyarrow" +version = "24.0.0" +description = "Python library for Apache Arrow" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "pyarrow-24.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:7c2b98645d576a0b9616892ead22b64a83a5f043c5e2ca15ebcefcb5b70c80cb"}, + {file = "pyarrow-24.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:644a246325b8c69c595ad1dd4b463eba4b0cdb731370e4a86137d433208d6147"}, + {file = "pyarrow-24.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:3a577bd840ca83f646f0a625dbc571dba7044c43c2d1503afc378b570954345c"}, + {file = "pyarrow-24.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:e3268e43984d0b1a185c89b4cfff282a7ead12fc93f56cfd7088bdbcbe727041"}, + {file = "pyarrow-24.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2392d954fcb920f42d230284b677605e4e2fbb11f2821e823e642abd67fbb491"}, + {file = "pyarrow-24.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bec9373df11544592b0ba7ec2af0e35059e5f0e7647c6183a854dedd193298f1"}, + {file = "pyarrow-24.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:c42ab9439498270139cc63e18847a02afe5c8b3ed9c931266533cfe378bd3591"}, + {file = "pyarrow-24.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:b0e131f880cda8d04e076cee175a46fc0e8bc8b65c99c6c09dff6669335fde74"}, + {file = "pyarrow-24.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:1b2fe7f9a5566401a0ef2571f197eb92358925c1f0c8dba305d6e43ea0871bb3"}, + {file = "pyarrow-24.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:0b3537c00fb8d384f15ac1e79b6eb6db04a16514c8c1d22e59a9b95c8ba42868"}, + {file = "pyarrow-24.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:14e31a3c9e35f1ab6356c6378f6f72830e6d2d5f1791df3774a7b097d18a6a1e"}, + {file = "pyarrow-24.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7d9a514e73bc42711e6a35aaccf3587c520024fe0a25d830a1a8a27c15f4f57"}, + {file = "pyarrow-24.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b196eb3f931862af3fa84c2a253514d859c08e0d8fe020e07be12e75a5a9780c"}, + {file = "pyarrow-24.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:35405aecb474e683fb36af650618fd5340ee5471fc65a21b36076a18bbc6c981"}, + {file = "pyarrow-24.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:6233c9ed9ab9d1db47de57d9753256d9dcffbf42db341576099f0fd9f6bf4810"}, + {file = "pyarrow-24.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:f7616236ec1bc2b15bfdec22a71ab38851c86f8f05ff64f379e1278cf20c634a"}, + {file = "pyarrow-24.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:1617043b99bd33e5318ae18eb2919af09c71322ef1ca46566cdafc6e6712fb66"}, + {file = "pyarrow-24.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6165461f55ef6314f026de6638d661188e3455d3ec49834556a0ebbdbace18bb"}, + {file = "pyarrow-24.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b13dedfe76a0ad2d1d859b0811b53827a4e9d93a0bcb05cf59333ab4980cc7e"}, + {file = "pyarrow-24.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:25ea65d868eb04015cd18e6df2fbe98f07e5bda2abefabcb88fce39a947716f6"}, + {file = "pyarrow-24.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:295f0a7f2e242dabd513737cf076007dc5b2d59237e3eca37b05c0c6446f3826"}, + {file = "pyarrow-24.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:02b001b3ed4723caa44f6cd1af2d5c86aa2cf9971dacc2ffa55b21237713dfba"}, + {file = "pyarrow-24.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:04920d6a71aabd08a0417709efce97d45ea8e6fb733d9ca9ecffb13c67839f68"}, + {file = "pyarrow-24.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:a964266397740257f16f7bb2e4f08a0c81454004beab8ff59dd531b73610e9f2"}, + {file = "pyarrow-24.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6f066b179d68c413374294bc1735f68475457c933258df594443bb9d88ddc2a0"}, + {file = "pyarrow-24.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1183baeb14c5f587b1ec52831e665718ce632caab84b7cd6b85fd44f96114495"}, + {file = "pyarrow-24.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:806f24b4085453c197a5078218d1ee08783ebbba271badd153d1ae22a3ee804f"}, + {file = "pyarrow-24.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:e4505fc6583f7b05ab854934896bcac8253b04ac1171a77dfb73efef92076d91"}, + {file = "pyarrow-24.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:1a4e45017efbf115032e4475ee876d525e0e36c742214fbe405332480ecd6275"}, + {file = "pyarrow-24.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:7986f1fa71cee060ad00758bcc79d3a93bab8559bf978fab9e53472a2e25a17b"}, + {file = "pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:d3e0b61e8efb24ed38898e5cdc5fffa9124be480008d401a1f8071500494ae42"}, + {file = "pyarrow-24.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:55a3bc1e3df3b5567b7d27ef551b2283f0c68a5e86f1cd56abc569da4f31335b"}, + {file = "pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:641f795b361874ac9da5294f8f443dfdbee355cf2bd9e3b8d97aaac2306b9b37"}, + {file = "pyarrow-24.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8adc8e6ce5fccf5dc707046ae4914fd537def529709cc0d285d37a7f9cd442ca"}, + {file = "pyarrow-24.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:9b18371ad2f44044b81a8d23bc2d8a9b6a6226dca775e8e16cfee640473d6c5d"}, + {file = "pyarrow-24.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:1cc9057f0319e26333b357e17f3c2c022f1a83739b48a88b25bfd5fa2dc18838"}, + {file = "pyarrow-24.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e6f1278ee4785b6db21229374a1c9e54ec7c549de5d1efc9630b6207de7e170b"}, + {file = "pyarrow-24.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:adbbedc55506cbdabb830890444fb856bfb0060c46c6f8026c6c2f2cf86ae795"}, + {file = "pyarrow-24.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:ae8a1145af31d903fa9bb166824d7abe9b4681a000b0159c9fb99c11bc11ad26"}, + {file = "pyarrow-24.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d7027eba1df3b2069e2e8d80f644fa0918b68c46432af3d088ddd390d063ecde"}, + {file = "pyarrow-24.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e56a1ffe9bf7b727432b89104cc0849c21582949dd7bdcb34f17b2001a351a76"}, + {file = "pyarrow-24.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:38be1808cdd068605b787e6ca9119b27eb275a0234e50212c3492331680c3b1e"}, + {file = "pyarrow-24.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:418e48ce50a45a6a6c73c454677203a9c75c966cb1e92ca3370959185f197a05"}, + {file = "pyarrow-24.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:2f16197705a230a78270cdd4ea8a1d57e86b2fdcbc34a1f6aebc72e65c986f9a"}, + {file = "pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:fb24ac194bfc5e86839d7dcd52092ee31e5fe6733fe11f5e3b06ef0812b20072"}, + {file = "pyarrow-24.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:9700ebd9a51f5895ce75ff4ac4b3c47a7d4b42bc618be8e713e5d56bacf5f931"}, + {file = "pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d8ddd2768da81d3ee08cfea9b597f4abb4e8e1dc8ae7e204b608d23a0d3ab699"}, + {file = "pyarrow-24.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:61a3d7eaa97a14768b542f3d284dc6400dd2470d9f080708b13cd46b6ae18136"}, + {file = "pyarrow-24.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:c91d00057f23b8d353039520dc3a6c09d8608164c692e9f59a175a42b2ae0c19"}, + {file = "pyarrow-24.0.0.tar.gz", hash = "sha256:85fe721a14dd823aca09127acbb06c3ca723efbd436c004f16bca601b04dcc83"}, +] + +[[package]] +name = "pydantic" +version = "2.13.4" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba"}, + {file = "pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.46.4" +typing-extensions = ">=4.14.1" +typing-inspection = ">=0.4.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""] + +[[package]] +name = "pydantic-core" +version = "2.46.4" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_core-2.46.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a396dcc17e5a0b164dbe026896245a4fa9ff402edca1dff0be3d53a517f74de4"}, + {file = "pydantic_core-2.46.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:da4b951fe36dc7c3a1ccb4e3cd1747c3542b8c9ceede8fc86cae054e764485f5"}, + {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb63e0198ca18aad131c089b9204c23079c3afa95487e561f4c522d519e55aba"}, + {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f47286a97f0bc9b8859519809077b91b2cefe4ae47fcbf5e466a009c1c5d742b"}, + {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:905a0ed8ea6f2d61c1738835f99b699348d7857379083e5fc497fa0c967a407c"}, + {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea793e075b70290d89d8142074262885d3f7da19634845135751bd6344f73b50"}, + {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395aebd9183f9d112f569aeb5b2214d1a10a33bec8456447f7fbdfa51d38d4cd"}, + {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:b078afbc25f3a1436c7a1d2cd3e322497ee99615ba97c563566fdf46aff1ee01"}, + {file = "pydantic_core-2.46.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f747929cf940cddb5b3668a390056ddd5ba2e5010615ea2dcf4f9c4f3ab8791d"}, + {file = "pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:daa27d92c36f24388fe3ad306b174781c747627f134452e4f128ea00ce1fe8c4"}, + {file = "pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:19e51f073cd3df251856a8a4189fbdf1de4012c3ebacfb1884f94f1eb406079f"}, + {file = "pydantic_core-2.46.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1747f85cee84c26985853c6f3d9bd3e75da5212912443fa111c113b9c246f39"}, + {file = "pydantic_core-2.46.4-cp310-cp310-win32.whl", hash = "sha256:2f84c03c8607173d16b5a854ec68a2f9079ae03237a54fb506d13af47e1d018d"}, + {file = "pydantic_core-2.46.4-cp310-cp310-win_amd64.whl", hash = "sha256:8358a950c8909158e3df31538a7e4edc2d7265a7c54b47f0864d9e5bae9dcebf"}, + {file = "pydantic_core-2.46.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:0e96592440881c74a213e5ad528e2b24d3d4f940de2766bed9010ab1d9e51594"}, + {file = "pydantic_core-2.46.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e0d65b8c354be7fb5f720c3caa8bc940bc2d20ce749c8e06135f07f8ed95dd7c"}, + {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bfb192b3f4b9e8a89b6277b6ce787564f62cfd272055f6e685726b111dc7826"}, + {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9037063db01f09b09e237c282b6792bd4da634b5402c4e7f0c61effed7701a04"}, + {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc010ab034c8c7452522748bf937df58020d256ccae0874463d1f4d01758af8e"}, + {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5dac79fa1614d1e06ca695109c6105923bd9c7d1d6c918d4e637b7e6b32fd3"}, + {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9fa868638bf362d3d138ea55829cefb3d5f4b0d7f142234382a15e2485dbec4"}, + {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:17299feefe090f2caa5b8e37222bb5f663e4935a8bfa6931d4102e5df1a9f398"}, + {file = "pydantic_core-2.46.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4c63ebc82684aa89d9a3bcbd13d515b3be44250dc68dd3bd81526c1cb31286c3"}, + {file = "pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaa2a54443eff1950ba5ddc6b6ccda0d9c84a364276a62f969bdf2a390650848"}, + {file = "pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:18e5ceec2ab67e6d5f1a9085e5a24c9c4e2ac4545730bfe668680bca05e555f3"}, + {file = "pydantic_core-2.46.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a0f62d0a58f4e7da165457e995725421e0064f2255d8eccebc49f41bbc23b109"}, + {file = "pydantic_core-2.46.4-cp311-cp311-win32.whl", hash = "sha256:041bde0a48fd37cf71cab1c9d56d3e8625a3793fef1f7dd232b3ff37e978ecda"}, + {file = "pydantic_core-2.46.4-cp311-cp311-win_amd64.whl", hash = "sha256:6f2eeda33a839975441c86a4119e1383c50b47faf0cbb5176985565c6bb02c33"}, + {file = "pydantic_core-2.46.4-cp311-cp311-win_arm64.whl", hash = "sha256:14f4c5d6db102bd796a627bbb3a17b4cf4574b9ae861d8b7c9a9661c6dd3362d"}, + {file = "pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2"}, + {file = "pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f"}, + {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7"}, + {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7"}, + {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712"}, + {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4"}, + {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce"}, + {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987"}, + {file = "pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b"}, + {file = "pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458"}, + {file = "pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b"}, + {file = "pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c"}, + {file = "pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894"}, + {file = "pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89"}, + {file = "pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a"}, + {file = "pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008"}, + {file = "pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4"}, + {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76"}, + {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3"}, + {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76"}, + {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4"}, + {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a"}, + {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262"}, + {file = "pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e"}, + {file = "pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd"}, + {file = "pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be"}, + {file = "pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d"}, + {file = "pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb"}, + {file = "pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292"}, + {file = "pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d"}, + {file = "pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb"}, + {file = "pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462"}, + {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9"}, + {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4"}, + {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914"}, + {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28"}, + {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b"}, + {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c"}, + {file = "pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb"}, + {file = "pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898"}, + {file = "pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e"}, + {file = "pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519"}, + {file = "pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4"}, + {file = "pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac"}, + {file = "pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5"}, + {file = "pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596"}, + {file = "pydantic_core-2.46.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:fd8b3d9fd264be37976686c7f65cd52a83f5e84f4bfd2adf9c1d469676bbb6ae"}, + {file = "pydantic_core-2.46.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9f444c499b3eefd3a92e348059471ea0c3a6e303d9c1cec09fa748fd9f895201"}, + {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3447661d99f75a3683a4cf5c87da72f2161964611864dbbeac7fbb118bb4bfc0"}, + {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b9bab013d1c7a79d3501ff86d0bc9c31bf587db4551677b96bec07df78c6b15"}, + {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d995260fdf4e1db774581b4900e0f832abe3c7c84996726bbc161b19c8f29e76"}, + {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f13a646d65d09fbf1bc6b3a9635d30095c8e7e5cc419ff35ecc563c5fd04cd49"}, + {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432c179df7874eeb73307aad2df0755e1ae0efa61ff0ea89b93e194411ae3928"}, + {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:e68b7a074f65a2fd746c52a7ce6142ab7006074ac269ace0c25cd8ba171f8066"}, + {file = "pydantic_core-2.46.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4a05d69cba51d852c5c3e92758653245a50c0b646ced0cf05bd793ed592839d6"}, + {file = "pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:228ee9bae8bef5b1e97ec58302f80357c37199e0d0a99174e138d28e6957b9d9"}, + {file = "pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:10e17cbb10a330363733efc4d7c4d0dd827ac0909b8f6a6542298fed1ea62f29"}, + {file = "pydantic_core-2.46.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:91a06d2e259ecfbd8c901d70c3c507900458498142b3026a296b7de4d1322cc9"}, + {file = "pydantic_core-2.46.4-cp39-cp39-win32.whl", hash = "sha256:d80ee3d731373b24cebbc10d689ca4ee1875caf0d5703a245db18efd4dd37fc1"}, + {file = "pydantic_core-2.46.4-cp39-cp39-win_amd64.whl", hash = "sha256:3be77f45df024d789a672ae34f8b06fb346c4f9f46ea714956660ea4862e89ac"}, + {file = "pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:14d4edf427bdcf950a8a02d7cb44a08614388dd6e1bdcbf4f67504fa7887da9c"}, + {file = "pydantic_core-2.46.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0ce40cd7b21210e99342afafbd4d0f76d784eb5b1d60f3bdc566be4983c6c73b"}, + {file = "pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90884113d8b48f760e9587002789ddd741e76ab9f89518cd1e43b1f1a52ec44b"}, + {file = "pydantic_core-2.46.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66ce7632c22d837c95301830e111ad0128a32b8207533b60896a96c4915192ea"}, + {file = "pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7"}, + {file = "pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df"}, + {file = "pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526"}, + {file = "pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0c563b08bca408dc7f65f700633d8442fffb2421fc47b8101377e9fd65051ff0"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:db06ffe51636ffe9ca531fe9023dd64bdd794be8754cb5df57c5498ae5b518a7"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:133878133d271ade3d41d1bfb2a45ec38dbdbda40bc065921c6b04e4630127e2"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9bc519fbf2b7578398853d815009ae5e4d4603d12f4e3f91da8c06852d3da3e9"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c7a7bd4e39e8e4c12c39cd480356842b6a8a06e41b23a55a5e3e191718838ddf"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:d396ec2b979760aaf3218e76c24e65bd0aca24983298653b3a9d7a45f9e47b30"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:86e1a4418c6cd97d60c95c71164158eaf7324fae7b0923264016baa993eba6fc"}, + {file = "pydantic_core-2.46.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:d51026d73fcfd93610abc7b27789c26b313920fcfb20e27462d74a7f8b06e983"}, + {file = "pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1"}, +] + +[package.dependencies] +typing-extensions = ">=4.14.1" + [[package]] name = "pydata-sphinx-theme" version = "0.16.1" @@ -571,6 +913,49 @@ files = [ [package.extras] windows-terminal = ["colorama (>=0.4.6)"] +[[package]] +name = "pylance" +version = "4.0.1" +description = "python wrapper for Lance columnar format" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pylance-4.0.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:9887930ff174851ef2b0471ad89675cb27b40f20ba048ceb4c09664eea778076"}, + {file = "pylance-4.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:400aa25ee82efd09ed8184f669f1150073c701330c614c63c4ed377b076a79e7"}, + {file = "pylance-4.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53b64cee1970d273642b70b0a5d337e2421402730dd1d15cbf488924fbcda0c7"}, + {file = "pylance-4.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c8459ffedae783941c73ae0e8cb2d453316b91b2b100c74c3825c44b6f7bd75c"}, + {file = "pylance-4.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b467f096079a805f568738548ed72f0951bc9c07a6786c76e1c3052b73e5ae69"}, + {file = "pylance-4.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:15c1b9d215d9a4cc15538faca192497f97c807128c759a37d28ea0a6780cce1c"}, +] + +[package.dependencies] +lance-namespace = ">=0.5.2,<0.7" +numpy = ">=1.22" +pyarrow = ">=14" + +[package.extras] +benchmarks = ["pytest-benchmark"] +dev = ["pyright", "ruff (==0.11.2)"] +geo = ["geoarrow-rust-core", "geoarrow-rust-io"] +tests = ["boto3", "datafusion (>=52,<53) ; python_full_version >= \"3.10.0\"", "datasets", "duckdb", "ml-dtypes", "pandas", "pillow", "polars[pandas,pyarrow]", "psutil", "pytest", "tensorflow ; sys_platform == \"linux\"", "tqdm"] +torch = ["torch (>=2.0)"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + [[package]] name = "pytokens" version = "0.4.1" @@ -628,14 +1013,14 @@ dev = ["black", "build", "mypy", "pytest", "pytest-cov", "setuptools", "tox", "t [[package]] name = "requests" -version = "2.33.1" +version = "2.34.2" description = "Python HTTP for Humans." optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a"}, - {file = "requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517"}, + {file = "requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0"}, + {file = "requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed"}, ] [package.dependencies] @@ -660,6 +1045,18 @@ files = [ {file = "roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2"}, ] +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + [[package]] name = "snowballstemmer" version = "3.0.1" @@ -838,14 +1235,14 @@ test = ["pytest"] [[package]] name = "types-gdb" -version = "16.3.0.20260408" +version = "16.3.0.20260518" description = "Typing stubs for gdb" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "types_gdb-16.3.0.20260408-py3-none-any.whl", hash = "sha256:531a0b4c3053c8a2f7b796ecde9d05e3ab48e6b9cda77b2fcc8ff3c9b703f999"}, - {file = "types_gdb-16.3.0.20260408.tar.gz", hash = "sha256:bdfa7cf5dd499f9030f9b8bb13680f9a5d7942524094277916bbae565e1fe458"}, + {file = "types_gdb-16.3.0.20260518-py3-none-any.whl", hash = "sha256:70a716562a123f9faaf40f9c4b42db39ab7674abadbfca58afe763fd4d925ea6"}, + {file = "types_gdb-16.3.0.20260518.tar.gz", hash = "sha256:7ca5d70b95636cbd2ea32fcba5fe823506ccbe61e4add410b9224d1a3ee1e192"}, ] [[package]] @@ -860,16 +1257,31 @@ files = [ {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] +[[package]] +name = "typing-inspection" +version = "0.4.2" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.0" + [[package]] name = "urllib3" -version = "2.6.3" +version = "2.7.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4"}, - {file = "urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed"}, + {file = "urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897"}, + {file = "urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c"}, ] [package.extras] @@ -881,4 +1293,4 @@ zstd = ["backports-zstd (>=1.0.0) ; python_version < \"3.14\""] [metadata] lock-version = "2.1" python-versions = ">=3.14" -content-hash = "71741ff27f8f063e3fadb2287ca9bfc8e50c1c750e3a075f9cbd7810be82c41d" +content-hash = "23bdc37737ba63ebb598ec2a34feb857a6317504a3af5b5def8f654d2118090e" diff --git a/pyproject.toml b/pyproject.toml index 9584bd2..087a76f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,17 +2,16 @@ name = "teachos" version = "0.1.0" description = "" -authors = [ - {name = "Your Name",email = "you@example.com"} -] -license = {text = "BSD-3-clause"} +authors = [{ name = "Your Name", email = "you@example.com" }] +license = { text = "BSD-3-clause" } requires-python = ">=3.14" dependencies = [ "sphinx (>=9.1.0,<10.0.0)", "sphinx-book-theme (>=1.2.0,<2.0.0)", "breathe (>=4.36.0,<5.0.0)", "types-gdb (>=16.3.0.20260408,<17.0.0.0)", - "black (>=26.3.1,<27.0.0)" + "black (>=26.3.1,<27.0.0)", + "pylance (>=4.0.1,<5.0.0)", ] [tool.poetry] -- cgit v1.2.3 From 033ecf6714089d2ce331152f5e120567f8d546cf Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 19 May 2026 12:48:08 +0200 Subject: build: clean up dependencies --- CMakeLists.txt | 3 --- kernel/CMakeLists.txt | 3 +++ libs/acpi/CMakeLists.txt | 26 ++++---------------------- libs/elf/CMakeLists.txt | 21 --------------------- libs/kstd/CMakeLists.txt | 24 +++--------------------- libs/multiboot2/CMakeLists.txt | 21 --------------------- 6 files changed, 10 insertions(+), 88 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8118e0f..f7c7fe8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,9 +31,6 @@ if (BUILD_TESTING) FetchContent_MakeAvailable("Catch2") - find_package("Catch2") - include("Catch") - add_compile_definitions("CATCH_CONFIG_NO_COUNTER") set(CATCH_TEST_ARGS "EXTRA_ARGS" "--reporter" "junit::out=junit.xml" "--reporter" "console::out=-::colour-mode=ansi") endif() diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 2ce9621..cbc7fa5 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -137,6 +137,9 @@ endif() #]============================================================================] if(BUILD_TESTING) + find_package("Catch2") + include("Catch") + enable_coverage("kernel_lib") add_executable("kernel_tests") diff --git a/libs/acpi/CMakeLists.txt b/libs/acpi/CMakeLists.txt index 135ce6a..1d03bb5 100644 --- a/libs/acpi/CMakeLists.txt +++ b/libs/acpi/CMakeLists.txt @@ -8,27 +8,6 @@ project("acpi" include("CTest") -#[============================================================================[ -# External Dependencies -#]============================================================================] - -include("FetchContent") - -if (BUILD_TESTING) - FetchContent_Declare( - "Catch2" - URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" - URL_HASH "SHA256=c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c" - EXCLUDE_FROM_ALL - FIND_PACKAGE_ARGS - ) - - FetchContent_MakeAvailable("Catch2") - - find_package("Catch2") - include("Catch") -endif() - #[============================================================================[ # Library #]============================================================================] @@ -75,6 +54,9 @@ set_target_properties("acpi" PROPERTIES #]============================================================================] if(BUILD_TESTING) + find_package("Catch2") + include("Catch") + find_program(IASL_EXE NAMES "iasl" REQUIRED) set(TEST_TABLES @@ -132,5 +114,5 @@ if(BUILD_TESTING) EXCLUDE_FROM_ALL NO ) - catch_discover_tests("acpi_tests" ${CATCH_TEST_ARGS}) + catch_discover_tests("acpi::tests" ${CATCH_TEST_ARGS}) endif() diff --git a/libs/elf/CMakeLists.txt b/libs/elf/CMakeLists.txt index 22ca200..1841132 100644 --- a/libs/elf/CMakeLists.txt +++ b/libs/elf/CMakeLists.txt @@ -8,27 +8,6 @@ project("elf" include("CTest") -#[============================================================================[ -# External Dependencies -#]============================================================================] - -include("FetchContent") - -if (BUILD_TESTING) - FetchContent_Declare( - "Catch2" - URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" - URL_HASH "SHA256=c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c" - EXCLUDE_FROM_ALL - FIND_PACKAGE_ARGS - ) - - FetchContent_MakeAvailable("Catch2") - - find_package("Catch2") - include("Catch") -endif() - #[============================================================================[ # Library #]============================================================================] diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt index 1cc75b7..0f64761 100644 --- a/libs/kstd/CMakeLists.txt +++ b/libs/kstd/CMakeLists.txt @@ -8,27 +8,6 @@ project("kstd" include("CTest") -#[============================================================================[ -# External Dependencies -#]============================================================================] - -include("FetchContent") - -if (BUILD_TESTING) - FetchContent_Declare( - "Catch2" - URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" - URL_HASH "SHA256=c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c" - EXCLUDE_FROM_ALL - FIND_PACKAGE_ARGS - ) - - FetchContent_MakeAvailable("Catch2") - - find_package("Catch2") - include("Catch") -endif() - #[============================================================================[ # Library #]============================================================================] @@ -88,6 +67,9 @@ endif() #]============================================================================] if(BUILD_TESTING) + find_package("Catch2") + include("Catch") + add_executable("kstd_tests") add_executable("kstd::tests" ALIAS "kstd_tests") diff --git a/libs/multiboot2/CMakeLists.txt b/libs/multiboot2/CMakeLists.txt index da5fb53..5ab56db 100644 --- a/libs/multiboot2/CMakeLists.txt +++ b/libs/multiboot2/CMakeLists.txt @@ -8,27 +8,6 @@ project("multiboot2" include("CTest") -#[============================================================================[ -# External Dependencies -#]============================================================================] - -include("FetchContent") - -if (BUILD_TESTING) - FetchContent_Declare( - "Catch2" - URL "https://github.com/catchorg/Catch2/archive/refs/tags/v3.7.1.tar.gz" - URL_HASH "SHA256=c991b247a1a0d7bb9c39aa35faf0fe9e19764213f28ffba3109388e62ee0269c" - EXCLUDE_FROM_ALL - FIND_PACKAGE_ARGS - ) - - FetchContent_MakeAvailable("Catch2") - - find_package("Catch2") - include("Catch") -endif() - #[============================================================================[ # Library #]============================================================================] -- cgit v1.2.3 From 1279dddbf400828cbadb1b3774fb060d18d8251c Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 19 May 2026 12:48:19 +0200 Subject: ide: preinstall catch2 --- .devcontainer/x86-64/Containerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/.devcontainer/x86-64/Containerfile b/.devcontainer/x86-64/Containerfile index 87a33b8..eb03057 100644 --- a/.devcontainer/x86-64/Containerfile +++ b/.devcontainer/x86-64/Containerfile @@ -12,6 +12,7 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install --yes --no- git-lfs \ grub2-common \ grub-pc \ + libcatch2-dev \ locales \ mtools \ ninja-build \ -- cgit v1.2.3 From 2063d3e165a1b92a46c73badf56927228ed4d5e8 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 25 May 2026 10:15:21 +0200 Subject: Refactor ssize_t --- kapi/kapi/filesystem.hpp | 16 ++++++++-------- kernel/include/kernel/filesystem/ext2/filesystem.hpp | 6 ++---- kernel/include/kernel/filesystem/open_file_table.hpp | 6 +++--- kernel/kapi/filesystem.cpp | 15 +++++++-------- kernel/src/filesystem/ext2/filesystem.cpp | 4 ++-- kernel/src/filesystem/open_file_table.cpp | 6 +++--- libs/kstd/kstd/unikstd.h | 12 ++++++++++++ 7 files changed, 37 insertions(+), 28 deletions(-) create mode 100644 libs/kstd/kstd/unikstd.h diff --git a/kapi/kapi/filesystem.hpp b/kapi/kapi/filesystem.hpp index fdaed73..3bd9aaf 100644 --- a/kapi/kapi/filesystem.hpp +++ b/kapi/kapi/filesystem.hpp @@ -1,11 +1,11 @@ #ifndef TEACHOS_KAPI_FILESYSTEM_HPP #define TEACHOS_KAPI_FILESYSTEM_HPP +#include + #include #include -#include - namespace kapi::filesystem { /** @@ -23,7 +23,7 @@ namespace kapi::filesystem @return 0 on success, -1 on failure. @qualifier kernel-defined */ - auto mount(std::string_view source, std::string_view target) -> ssize_t; + auto mount(std::string_view source, std::string_view target) -> kstd::ssize_t; /** @brief Unmounts a filesystem from the specified @p target path. @@ -31,7 +31,7 @@ namespace kapi::filesystem @return 0 on success, -1 on failure. @qualifier kernel-defined */ - auto umount(std::string_view target) -> ssize_t; + auto umount(std::string_view target) -> kstd::ssize_t; /** @brief Opens a file at the specified @p path. @@ -39,7 +39,7 @@ namespace kapi::filesystem @return A file descriptor on success, -1 on failure. @qualifier kernel-defined */ - auto open(std::string_view path) -> ssize_t; + auto open(std::string_view path) -> kstd::ssize_t; /** @brief Closes a @p file_descriptor. @@ -47,7 +47,7 @@ namespace kapi::filesystem @return 0 on success, -1 on failure. @qualifier kernel-defined */ - auto close(size_t file_descriptor) -> ssize_t; + auto close(size_t file_descriptor) -> kstd::ssize_t; /** @brief Reads @p size bytes into @p buffer from a @p file_descriptor. @@ -57,7 +57,7 @@ namespace kapi::filesystem @return The number of bytes read on success, -1 on failure. @qualifier kernel-defined */ - auto read(size_t file_descriptor, void * buffer, size_t size) -> ssize_t; + auto read(size_t file_descriptor, void * buffer, size_t size) -> kstd::ssize_t; /** @brief Writes @p size bytes from @p buffer to a @p file_descriptor. @@ -67,7 +67,7 @@ namespace kapi::filesystem @return The number of bytes written on success, -1 on failure. @qualifier kernel-defined */ - auto write(size_t file_descriptor, void const * buffer, size_t size) -> ssize_t; + auto write(size_t file_descriptor, void const * buffer, size_t size) -> kstd::ssize_t; } // namespace kapi::filesystem #endif // TEACHOS_KAPI_FILESYSTEM_HPP \ No newline at end of file diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index 45cd6a1..d5e5b8b 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -8,14 +8,12 @@ #include #include +#include #include #include #include #include -#include - -#include namespace kernel::filesystem::ext2 { @@ -85,7 +83,7 @@ namespace kernel::filesystem::ext2 @return The global block number. */ [[nodiscard]] auto map_inode_block_index_to_global_block_number(size_t inode_block_index, inode_data data) const - -> ssize_t; + -> kstd::ssize_t; private: [[nodiscard]] auto read_inode(uint32_t inode_number) const -> kstd::shared_ptr; diff --git a/kernel/include/kernel/filesystem/open_file_table.hpp b/kernel/include/kernel/filesystem/open_file_table.hpp index 5794e4c..7e754ac 100644 --- a/kernel/include/kernel/filesystem/open_file_table.hpp +++ b/kernel/include/kernel/filesystem/open_file_table.hpp @@ -4,10 +4,10 @@ #include #include +#include #include #include -#include namespace kernel::filesystem { @@ -40,7 +40,7 @@ namespace kernel::filesystem @param fd The file descriptor to add. @return The file descriptor index assigned to the file, or -1 on failure. */ - auto add_file(kstd::shared_ptr const & fd) -> ssize_t; + auto add_file(kstd::shared_ptr const & fd) -> kstd::ssize_t; /** @brief Get a file from the open file table. @@ -54,7 +54,7 @@ namespace kernel::filesystem @param fd The file descriptor index to remove. @return 0 on success, or -1 on failure. */ - auto remove_file(size_t fd) -> ssize_t; + auto remove_file(size_t fd) -> kstd::ssize_t; private: open_file_table() = default; diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index db61c34..838d2cb 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -5,15 +5,14 @@ #include #include +#include #include #include -#include - namespace kapi::filesystem { - auto mount(std::string_view source, std::string_view target) -> ssize_t + auto mount(std::string_view source, std::string_view target) -> kstd::ssize_t { if (kernel::filesystem::vfs::get().do_mount(source, target) == kernel::filesystem::vfs::operation_result::success) { @@ -22,7 +21,7 @@ namespace kapi::filesystem return -1; } - auto umount(std::string_view target) -> ssize_t + auto umount(std::string_view target) -> kstd::ssize_t { if (kernel::filesystem::vfs::get().unmount(target) == kernel::filesystem::vfs::operation_result::success) { @@ -31,7 +30,7 @@ namespace kapi::filesystem return -1; } - auto open(std::string_view path) -> ssize_t + auto open(std::string_view path) -> kstd::ssize_t { if (auto dentry = kernel::filesystem::vfs::get().open(path)) { @@ -42,7 +41,7 @@ namespace kapi::filesystem return -1; } - auto close(size_t file_descriptor) -> ssize_t + auto close(size_t file_descriptor) -> kstd::ssize_t { if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().file(file_descriptor)) { @@ -55,7 +54,7 @@ namespace kapi::filesystem return -1; } - auto read(size_t file_descriptor, void * buffer, size_t size) -> ssize_t + auto read(size_t file_descriptor, void * buffer, size_t size) -> kstd::ssize_t { if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().file(file_descriptor)) { @@ -65,7 +64,7 @@ namespace kapi::filesystem return -1; } - auto write(size_t file_descriptor, void const * buffer, size_t size) -> ssize_t + auto write(size_t file_descriptor, void const * buffer, size_t size) -> kstd::ssize_t { if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().file(file_descriptor)) { diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index df5b4c4..a30149e 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -8,12 +8,12 @@ #include #include +#include #include #include #include #include -#include namespace kernel::filesystem::ext2 { @@ -110,7 +110,7 @@ namespace kernel::filesystem::ext2 } auto filesystem::map_inode_block_index_to_global_block_number(size_t inode_block_index, inode_data data) const - -> ssize_t + -> kstd::ssize_t { if (inode_block_index < constants::direct_block_count) { diff --git a/kernel/src/filesystem/open_file_table.cpp b/kernel/src/filesystem/open_file_table.cpp index 4d52d36..2afe3aa 100644 --- a/kernel/src/filesystem/open_file_table.cpp +++ b/kernel/src/filesystem/open_file_table.cpp @@ -5,11 +5,11 @@ #include #include +#include #include #include #include -#include namespace { @@ -38,7 +38,7 @@ namespace kernel::filesystem return *global_open_file_table; } - auto open_file_table::add_file(kstd::shared_ptr const & file_descriptor) -> ssize_t + auto open_file_table::add_file(kstd::shared_ptr const & file_descriptor) -> kstd::ssize_t { if (!file_descriptor) { @@ -66,7 +66,7 @@ namespace kernel::filesystem return m_open_files.at(fd); } - auto open_file_table::remove_file(size_t fd) -> ssize_t + auto open_file_table::remove_file(size_t fd) -> kstd::ssize_t { if (fd >= m_open_files.size()) { diff --git a/libs/kstd/kstd/unikstd.h b/libs/kstd/kstd/unikstd.h new file mode 100644 index 0000000..aa60be6 --- /dev/null +++ b/libs/kstd/kstd/unikstd.h @@ -0,0 +1,12 @@ +#ifndef KSTD_UNIKSTD_HPP +#define KSTD_UNIKSTD_HPP + +#include +#include + +namespace kstd +{ + using ssize_t = std::make_signed_t; +} // namespace kstd + +#endif \ No newline at end of file -- cgit v1.2.3 From 61d29a288334960cd9f43df91e4fd632a7f6ad66 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 25 May 2026 11:13:18 +0200 Subject: Increase reference count of source_mount when one of its files is mounted somewhere --- kernel/include/kernel/filesystem/mount.hpp | 13 ++++++++++--- kernel/include/kernel/filesystem/vfs.hpp | 7 +++---- kernel/src/filesystem/mount.cpp | 14 ++++++++++---- kernel/src/filesystem/mount.tests.cpp | 16 +++++++++------- kernel/src/filesystem/mount_table.cpp | 16 +++++++++++++++- kernel/src/filesystem/mount_table.tests.cpp | 14 +++++++------- kernel/src/filesystem/vfs.cpp | 26 ++++++++++++-------------- kernel/src/filesystem/vfs.tests.cpp | 5 +++-- 8 files changed, 69 insertions(+), 42 deletions(-) diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index 4ce374f..6c0c5b9 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -28,7 +28,8 @@ namespace kernel::filesystem @param parent_mount The parent mount that this mount is attached beneath. */ mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, - kstd::shared_ptr const & fs, kstd::shared_ptr const & parent_mount); + kstd::shared_ptr const & fs, kstd::shared_ptr const & parent_mount, + kstd::shared_ptr const & source_mount); /** @brief Get the dentry where the filesystem is mounted. @@ -55,6 +56,11 @@ namespace kernel::filesystem */ [[nodiscard]] auto parent_mount() const -> kstd::shared_ptr const &; + /** + @brief Get the source mount where this mount originates from. + */ + [[nodiscard]] auto source_mount() const -> kstd::shared_ptr; + /** @brief Increment the reference count for this mount. */ @@ -62,9 +68,9 @@ namespace kernel::filesystem /** @brief Decrement the reference count for this mount. - @return True if the reference count reached zero, false otherwise. + @warning Throws if ref_count is zero. */ - [[nodiscard]] auto decrement_ref_count() -> bool; + auto decrement_ref_count() -> void; /** @brief Check if the mount is ready to be unmounted. @@ -83,6 +89,7 @@ namespace kernel::filesystem kstd::shared_ptr m_root_dentry; kstd::shared_ptr m_filesystem{}; kstd::shared_ptr m_parent_mount{}; + kstd::weak_ptr m_source_mount{}; std::atomic_size_t m_ref_count; }; } // namespace kernel::filesystem diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index aec8bfe..e6f2327 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -32,8 +32,7 @@ namespace kernel::filesystem non_existent_path = -2, mount_point_not_found = -3, unmount_failed = -4, - invalid_filesystem = -5, - close_failed = -6 + invalid_filesystem = -5 }; /** @@ -103,8 +102,8 @@ namespace kernel::filesystem [[nodiscard]] auto find_mount(std::string_view path) const -> kstd::shared_ptr; auto do_mount_internal(kstd::shared_ptr const & mount_point_dentry, - kstd::shared_ptr const & parent_mount, kstd::shared_ptr const & fs) - -> void; + kstd::shared_ptr const & parent_mount, kstd::shared_ptr const & fs, + kstd::shared_ptr const & source_mount = nullptr) -> void; auto graft_persistent_device_fs(kstd::shared_ptr const & device_fs) -> void; diff --git a/kernel/src/filesystem/mount.cpp b/kernel/src/filesystem/mount.cpp index b64c370..ead7479 100644 --- a/kernel/src/filesystem/mount.cpp +++ b/kernel/src/filesystem/mount.cpp @@ -14,11 +14,13 @@ namespace kernel::filesystem { mount::mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, - kstd::shared_ptr const & fs, kstd::shared_ptr const & parent_mount) + kstd::shared_ptr const & fs, kstd::shared_ptr const & parent_mount, + kstd::shared_ptr const & source_mount) : m_mount_dentry(mount_dentry) , m_root_dentry(root_dentry) , m_filesystem(fs) , m_parent_mount(parent_mount) + , m_source_mount(source_mount) , m_ref_count(0) { if (!m_filesystem) @@ -56,20 +58,24 @@ namespace kernel::filesystem return m_parent_mount; } + auto mount::source_mount() const -> kstd::shared_ptr + { + return m_source_mount.lock(); + } + auto mount::increment_ref_count() -> void { m_ref_count += 1; } - auto mount::decrement_ref_count() -> bool + auto mount::decrement_ref_count() -> void { if (m_ref_count == 0) { - return false; + kapi::system::panic("[FILESYSTEM] decrement_ref_count() was called but ref_count is 0"); } m_ref_count -= 1; - return true; } auto mount::is_ready_to_unmount() const -> bool diff --git a/kernel/src/filesystem/mount.tests.cpp b/kernel/src/filesystem/mount.tests.cpp index 6b66571..40b7cbb 100644 --- a/kernel/src/filesystem/mount.tests.cpp +++ b/kernel/src/filesystem/mount.tests.cpp @@ -11,6 +11,8 @@ #include +#include + SCENARIO("Mount construction", "[filesystem][mount]") { GIVEN("a filesystem and a root dentry") @@ -21,7 +23,7 @@ SCENARIO("Mount construction", "[filesystem][mount]") WHEN("constructing a mount with the filesystem and root dentry") { - auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr}; + auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr, nullptr}; THEN("the mount has the correct filesystem, root dentry, mount dentry, and mount path") { @@ -42,7 +44,7 @@ SCENARIO("Mount construction", "[filesystem][mount]") { THEN("the constructor panics") { - REQUIRE_THROWS_AS((kernel::filesystem::mount{root_dentry, root_dentry, nullptr, nullptr}), + REQUIRE_THROWS_AS((kernel::filesystem::mount{root_dentry, root_dentry, nullptr, nullptr, nullptr}), kernel::tests::cpu::halt); } } @@ -60,7 +62,7 @@ SCENARIO("Mount reference counting", "[filesystem][mount]") THEN("reference count can be incremented and decremented, the mount is ready to unmount when the reference " "count == 0") { - auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr}; + auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr, nullptr}; mount.increment_ref_count(); REQUIRE(mount.ref_count() == 1); @@ -70,20 +72,20 @@ SCENARIO("Mount reference counting", "[filesystem][mount]") REQUIRE(mount.ref_count() == 2); REQUIRE_FALSE(mount.is_ready_to_unmount()); - REQUIRE(mount.decrement_ref_count()); + mount.decrement_ref_count(); REQUIRE(mount.ref_count() == 1); REQUIRE_FALSE(mount.is_ready_to_unmount()); - REQUIRE(mount.decrement_ref_count()); + mount.decrement_ref_count(); REQUIRE(mount.ref_count() == 0); REQUIRE(mount.is_ready_to_unmount()); } THEN("decrementing reference count when it is already zero does not decrement it below zero") { - auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr}; + auto mount = kernel::filesystem::mount{root_dentry, root_dentry, fs, nullptr, nullptr}; - REQUIRE_FALSE(mount.decrement_ref_count()); + REQUIRE_THROWS_AS(mount.decrement_ref_count(), std::runtime_error); REQUIRE(mount.ref_count() == 0); REQUIRE(mount.is_ready_to_unmount()); } diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index 26828b4..af8434b 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -22,6 +22,11 @@ namespace kernel::filesystem { m_mounts.push_back(mount); + if (auto source_mount = mount->source_mount()) + { + source_mount->increment_ref_count(); + } + if (auto mount_dentry = mount->mount_dentry()) { mount_dentry->set_flag(dentry::dentry_flags::is_mount_point); @@ -46,7 +51,16 @@ namespace kernel::filesystem return operation_result::has_child_mounts; } - mount->mount_dentry()->unset_flag(dentry::dentry_flags::is_mount_point); + if (auto source_mount = mount->source_mount()) + { + source_mount->decrement_ref_count(); + } + + if (auto mount_dentry = mount->mount_dentry()) + { + mount_dentry->unset_flag(dentry::dentry_flags::is_mount_point); + } + m_mounts.erase(mount_it); return operation_result::removed; } diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp index f22b25e..8118e19 100644 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -38,14 +38,14 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] nullptr, kstd::make_shared(), "/"); auto mount_dentry1 = kstd::make_shared( nullptr, kstd::make_shared(), "/"); - auto mount1 = kstd::make_shared(mount_dentry1, root_dentry1, fs1, nullptr); + auto mount1 = kstd::make_shared(mount_dentry1, root_dentry1, fs1, nullptr, nullptr); auto fs2 = kstd::make_shared(); auto root_dentry2 = kstd::make_shared( nullptr, kstd::make_shared(), "/"); auto mount_dentry2 = kstd::make_shared( nullptr, kstd::make_shared(), "/mnt"); - auto mount2 = kstd::make_shared(mount_dentry2, root_dentry2, fs2, nullptr); + auto mount2 = kstd::make_shared(mount_dentry2, root_dentry2, fs2, nullptr, nullptr); table.add_mount(mount1); table.add_mount(mount2); @@ -89,14 +89,14 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] nullptr, kstd::make_shared(), "/"); auto mount_dentry1 = kstd::make_shared( nullptr, kstd::make_shared(), "/"); - auto mount1 = kstd::make_shared(mount_dentry1, root_dentry1, fs1, nullptr); + auto mount1 = kstd::make_shared(mount_dentry1, root_dentry1, fs1, nullptr, nullptr); auto fs2 = kstd::make_shared(); auto root_dentry2 = kstd::make_shared( nullptr, kstd::make_shared(), "/"); auto mount_dentry2 = kstd::make_shared( nullptr, kstd::make_shared(), "/"); - auto mount2 = kstd::make_shared(mount_dentry2, root_dentry2, fs2, nullptr); + auto mount2 = kstd::make_shared(mount_dentry2, root_dentry2, fs2, nullptr, nullptr); table.add_mount(mount1); table.add_mount(mount2); @@ -122,21 +122,21 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] nullptr, kstd::make_shared(), "/"); auto mount_dentry1 = kstd::make_shared( nullptr, kstd::make_shared(), "/"); - auto mount1 = kstd::make_shared(mount_dentry1, root_dentry1, fs1, nullptr); + auto mount1 = kstd::make_shared(mount_dentry1, root_dentry1, fs1, nullptr, nullptr); auto fs2 = kstd::make_shared(); auto root_dentry2 = kstd::make_shared( nullptr, kstd::make_shared(), "/"); auto mount_dentry2 = kstd::make_shared( mount_dentry1, kstd::make_shared(), "mnt"); - auto mount2 = kstd::make_shared(mount_dentry2, root_dentry2, fs2, mount1); + auto mount2 = kstd::make_shared(mount_dentry2, root_dentry2, fs2, mount1, nullptr); auto fs3 = kstd::make_shared(); auto root_dentry3 = kstd::make_shared( nullptr, kstd::make_shared(), "/"); auto mount_dentry3 = kstd::make_shared( mount_dentry2, kstd::make_shared(), "submnt"); - auto mount3 = kstd::make_shared(mount_dentry3, root_dentry3, fs3, mount2); + auto mount3 = kstd::make_shared(mount_dentry3, root_dentry3, fs3, mount2, nullptr); table.add_mount(mount1); table.add_mount(mount2); diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index bf9a77d..ae85291 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -47,7 +47,7 @@ namespace kernel::filesystem root_fs->mount(nullptr); auto root_fs_root_dentry = kstd::make_shared(nullptr, root_fs->root_inode(), "/"); - auto root_mount = kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, nullptr); + auto root_mount = kstd::make_shared(nullptr, root_fs_root_dentry, root_fs, nullptr, nullptr); m_mount_table.add_mount(root_mount); // mount devfs at /dev (inside rootfs, temporary, will be shadowed) @@ -56,13 +56,14 @@ namespace kernel::filesystem graft_persistent_device_fs(device_fs); // mount boot fs at / (shadows rootfs), re-graft devfs - if (auto boot_device_dentry = resolve_path("/dev/ram0")) + auto [boot_device_dentry, boot_device_mount_context] = resolve_path_internal("/dev/ram0"); + if (boot_device_dentry && boot_device_mount_context) { if (auto boot_root_fs = kernel::filesystem::filesystem::probe_and_mount(boot_device_dentry->get_inode())) { if (auto root_dentry = resolve_path("/")) { - do_mount_internal(root_dentry, root_mount, boot_root_fs); + do_mount_internal(root_dentry, root_mount, boot_root_fs, boot_device_mount_context); graft_persistent_device_fs(device_fs); } } @@ -94,11 +95,8 @@ namespace kernel::filesystem { if (auto mount = find_mount(path)) { - if (mount->decrement_ref_count()) - { - return operation_result::success; - } - return operation_result::close_failed; + mount->decrement_ref_count(); + return operation_result::success; } return operation_result::invalid_path; } @@ -111,14 +109,14 @@ namespace kernel::filesystem } auto [mount_point_dentry, mount_context] = resolve_path_internal(target); - if (mount_point_dentry && mount_context) { - if (auto source_dentry = resolve_path(source)) + auto [source_dentry, source_mount_context] = resolve_path_internal(source); + if (source_dentry && source_mount_context) { if (auto fs = kernel::filesystem::filesystem::probe_and_mount(source_dentry->get_inode())) { - do_mount_internal(mount_point_dentry, mount_context, fs); + do_mount_internal(mount_point_dentry, mount_context, fs, source_mount_context); return operation_result::success; } return operation_result::invalid_filesystem; @@ -149,12 +147,12 @@ namespace kernel::filesystem } auto vfs::do_mount_internal(kstd::shared_ptr const & mount_point_dentry, - kstd::shared_ptr const & parent_mount, kstd::shared_ptr const & fs) - -> void + kstd::shared_ptr const & parent_mount, kstd::shared_ptr const & fs, + kstd::shared_ptr const & source_mount) -> void { auto new_fs_root = kstd::make_shared(mount_point_dentry->parent(), fs->root_inode(), mount_point_dentry->name()); - auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, parent_mount); + auto new_mount = kstd::make_shared(mount_point_dentry, new_fs_root, fs, parent_mount, source_mount); m_mount_table.add_mount(new_mount); } diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 6fcc84e..8ae206a 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -10,6 +10,7 @@ #include #include +#include #include SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS with dummy modules", @@ -156,7 +157,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS THEN("file with invalid path or not opened file cannot be closed") { REQUIRE(vfs.close("invalid_path") == kernel::filesystem::vfs::operation_result::invalid_path); - REQUIRE(vfs.close("/information/info_1.txt") == kernel::filesystem::vfs::operation_result::close_failed); + REQUIRE_THROWS_AS(vfs.close("/information/info_1.txt"), std::runtime_error); } THEN("file cannot be closed twice") @@ -165,7 +166,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(info_1 != nullptr); REQUIRE(vfs.close(info_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.close(info_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::close_failed); + REQUIRE_THROWS_AS(vfs.close(info_1->absolute_path().view()), std::runtime_error); } THEN("images can be stacked mounted and correct file system is unmounted again") -- cgit v1.2.3 From 90d4163c336348cd21db8cbdb292994ddcfa3908 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 25 May 2026 11:43:00 +0200 Subject: improve demo code --- kernel/src/main.cpp | 50 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 22d2b1e..25b5c00 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -26,57 +26,83 @@ using namespace kstd::units_literals; auto run_demo() -> void { // 1) open a file + kstd::println("attempting to open /entrance/tickets.txt"); auto fd_1 = kapi::filesystem::open("/entrance/tickets.txt"); if (fd_1 == -1) { - kstd::os::panic("demo failed"); + kapi::system::panic("demo failed"); + } + else + { + kstd::println("--> successfully opened /entrance/tickets.txt with file descriptor {}", fd_1); } // 2) read from the file kstd::vector buffer_1{10}; auto bytes_read = kapi::filesystem::read(fd_1, buffer_1.data(), buffer_1.size()); auto buffer_as_str = std::string_view{reinterpret_cast(buffer_1.data()), static_cast(bytes_read)}; - kstd::println("Read {} bytes from /entrance/tickets.txt: {}", bytes_read, buffer_as_str); + kstd::println("--> read {} bytes from /entrance/tickets.txt: {}", bytes_read, buffer_as_str); + kstd::println(""); // 3) show that /entrance/information/info_1.txt is not accessible before mounting + kstd::println("attempting to open /entrance/information/info_1.txt before mounting"); auto fd_before_mount = kapi::filesystem::open("/entrance/information/info_1.txt"); if (fd_before_mount == -1) { - kstd::println("/entrance/information/info_1.txt is not accessible before mounting, as expected."); + kstd::println("--> as expected the file could not be opened before mounting"); } // 4) mount a new filesystem on top of /entrance - kapi::filesystem::mount("/dev/ram16", "/entrance"); + kstd::println("mount /dev/ram16 to /entrance"); + if (kapi::filesystem::mount("/dev/ram16", "/entrance") == 0) + { + kstd::println("--> successfully mounted /dev/ram16 to /entrance"); + } + else + { + kapi::system::panic("demo failed"); + } + kstd::println(""); // 5) open a file from the new filesystem + kstd::println("attempting to open /entrance/information/info_1.txt"); auto fd_2 = kapi::filesystem::open("/entrance/information/info_1.txt"); - if (fd_2 == -1) + if (fd_2 != -1) + { + kstd::println("--> successfully opened /entrance/information/info_1.txt with file descriptor {}", fd_2); + } + else { - kstd::os::panic("demo failed"); + kapi::system::panic("demo failed"); } // 6) read from the new file kstd::vector buffer_2{10}; bytes_read = kapi::filesystem::read(fd_2, buffer_2.data(), buffer_2.size()); buffer_as_str = std::string_view{reinterpret_cast(buffer_2.data()), static_cast(bytes_read)}; - kstd::println("Read {} bytes from /entrance/information/info_1.txt: {}", bytes_read, buffer_as_str); + kstd::println("--> read {} bytes from /entrance/information/info_1.txt: {} ", bytes_read, buffer_as_str); // 7) open device as file - auto fd_3 = kapi::filesystem::open("/dev/ram48"); - if (fd_3 == -1) + kstd::println("attempting to open /dev/ram32 as a file"); + auto fd_3 = kapi::filesystem::open("/dev/ram32"); + if (fd_3 != -1) + { + kstd::println("--> successfully opened /dev/ram32 as a file with file descriptor {}", fd_3); + } + else { - kstd::os::panic("demo failed"); + kapi::system::panic("demo failed"); } // 8) read from the device file kstd::vector buffer_3{2}; bytes_read = kapi::filesystem::read(fd_3, buffer_3.data(), buffer_3.size()); - kstd::println("Read {} bytes from /dev/ram48: {::#04x}", bytes_read, buffer_3); + kstd::println("--> read {} bytes from /dev/ram32: {::#04x} ", bytes_read, buffer_3); // 9) write to the device file kstd::vector write_buffer{std::byte{0xAA}, std::byte{0xAA}}; auto bytes_written = kapi::filesystem::write(fd_3, write_buffer.data(), write_buffer.size()); - kstd::println("Written {} bytes to /dev/ram48: {::#04x}", bytes_written, write_buffer); + kstd::println("--> written {} bytes to /dev/ram32: {::#04x}", bytes_written, write_buffer); // 10) do memory dump to show that the write to the device file had an effect } -- cgit v1.2.3 From d916ecfc94aab583d60bba94de257a675accab7a Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 25 May 2026 13:04:22 +0200 Subject: add vfs tests --- kernel/src/filesystem/vfs.tests.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 8ae206a..fc1ea0d 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -416,6 +416,37 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS } } + GIVEN("two real image files where the second contains as filesystem formatted files") + { + REQUIRE(std::filesystem::exists(image_path_1)); + REQUIRE(std::filesystem::exists(image_path_3)); + REQUIRE_NOTHROW( + setup_modules_from_img_and_init_vfs({"test_img_module_3", "test_img_module_1"}, {image_path_3, image_path_1})); + + auto & vfs = kernel::filesystem::vfs::get(); + + THEN("cannot unmount a filesystem if files are mounted") + { + REQUIRE(vfs.do_mount("/dev/ram16", "/entrance") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/entrance/archiv/2024.img", "/enclosures") == + kernel::filesystem::vfs::operation_result::success); + + REQUIRE(vfs.unmount("/entrance") == kernel::filesystem::vfs::operation_result::unmount_failed); + REQUIRE(vfs.unmount("/enclosures") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/entrance") == kernel::filesystem::vfs::operation_result::success); + } + + THEN("can mount filesystem ontop of the path where this is inside") + { + REQUIRE(vfs.do_mount("/dev/ram16", "/entrance") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.do_mount("/entrance/archiv/2024.img", "/entrance") == + kernel::filesystem::vfs::operation_result::success); + + REQUIRE(vfs.unmount("/entrance") == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.unmount("/entrance") == kernel::filesystem::vfs::operation_result::success); + } + } + GIVEN("A real image files, containing symbolic links") { REQUIRE(std::filesystem::exists(image_path_1)); -- cgit v1.2.3 From 756854104db95f247ad9bed5eb59fb90a9a1cb69 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 25 May 2026 13:04:36 +0200 Subject: add mount table and mount tests --- kernel/src/filesystem/mount.tests.cpp | 3 ++- kernel/src/filesystem/mount_table.tests.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/kernel/src/filesystem/mount.tests.cpp b/kernel/src/filesystem/mount.tests.cpp index 40b7cbb..c9ff82e 100644 --- a/kernel/src/filesystem/mount.tests.cpp +++ b/kernel/src/filesystem/mount.tests.cpp @@ -34,9 +34,10 @@ SCENARIO("Mount construction", "[filesystem][mount]") REQUIRE(mount.is_ready_to_unmount()); } - THEN("the mount has no parent mount") + THEN("the mount has no parent mount and no source mount") { REQUIRE(mount.parent_mount() == nullptr); + REQUIRE(mount.source_mount() == nullptr); } } diff --git a/kernel/src/filesystem/mount_table.tests.cpp b/kernel/src/filesystem/mount_table.tests.cpp index 8118e19..19b47b2 100644 --- a/kernel/src/filesystem/mount_table.tests.cpp +++ b/kernel/src/filesystem/mount_table.tests.cpp @@ -155,3 +155,30 @@ SCENARIO("Adding, finding and removing mounts in the mount table", "[filesystem] } } } + +SCENARIO("Mount reference counting", "[filesystem][mount_table]") +{ + kernel::filesystem::mount_table table; + + GIVEN("a filesystem and a root dentry") + { + auto fs = kstd::make_shared(); + auto root_inode = kstd::make_shared(); + auto root_dentry = kstd::make_shared(nullptr, root_inode, "/"); + + auto source_mount = kstd::make_shared(root_dentry, root_dentry, fs, nullptr, nullptr); + auto mount = kstd::make_shared(root_dentry, root_dentry, fs, nullptr, source_mount); + + THEN("reference count of source mount is incremented when a mount is added to the mount table and decremented when " + "the mount is removed") + { + REQUIRE(source_mount->ref_count() == 0); + + table.add_mount(mount); + REQUIRE(source_mount->ref_count() == 1); + + REQUIRE(table.remove_mount("/") == kernel::filesystem::mount_table::operation_result::removed); + REQUIRE(source_mount->ref_count() == 0); + } + } +} -- cgit v1.2.3 From 685990ae8c4fad6847e8f90cccb52bb34280298c Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 25 May 2026 13:14:07 +0200 Subject: Apply 1 suggestion(s) to 1 file(s) Co-authored-by: Marcel Braun --- kernel/src/filesystem/vfs.tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index fc1ea0d..962e067 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -436,7 +436,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.unmount("/entrance") == kernel::filesystem::vfs::operation_result::success); } - THEN("can mount filesystem ontop of the path where this is inside") + THEN("can mount filesystem onto the directory that contains it") { REQUIRE(vfs.do_mount("/dev/ram16", "/entrance") == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.do_mount("/entrance/archiv/2024.img", "/entrance") == -- cgit v1.2.3 From 5d3222599c2af9241ac44718ede4ec9f4955f490 Mon Sep 17 00:00:00 2001 From: Marcel Braun Date: Mon, 25 May 2026 13:35:50 +0200 Subject: Fix mount parameter description --- kernel/include/kernel/filesystem/mount.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index 6c0c5b9..53d7f6d 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -24,8 +24,8 @@ namespace kernel::filesystem @param mount_dentry The dentry where the filesystem is mounted. @param root_dentry The root dentry of the mounted filesystem. @param fs The filesystem instance being mounted. - @param mount_path The path at which the filesystem is mounted. - @param parent_mount The parent mount that this mount is attached beneath. + @param parent_mount The mount that contains the mount_dentry. + @param source_mount The mount that the filesystem originates from. */ mount(kstd::shared_ptr const & mount_dentry, kstd::shared_ptr const & root_dentry, kstd::shared_ptr const & fs, kstd::shared_ptr const & parent_mount, -- cgit v1.2.3 From 649bf64b4d4ec0742e56539348761f5957d64922 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 25 May 2026 14:03:56 +0200 Subject: fix linter warnings --- kernel/src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 25b5c00..a84ca64 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -13,7 +13,6 @@ #include #include -#include #include #include #include @@ -100,7 +99,8 @@ auto run_demo() -> void kstd::println("--> read {} bytes from /dev/ram32: {::#04x} ", bytes_read, buffer_3); // 9) write to the device file - kstd::vector write_buffer{std::byte{0xAA}, std::byte{0xAA}}; + auto const buffer_fill_byte = std::byte{0xAA}; + kstd::vector write_buffer{buffer_fill_byte, buffer_fill_byte}; auto bytes_written = kapi::filesystem::write(fd_3, write_buffer.data(), write_buffer.size()); kstd::println("--> written {} bytes to /dev/ram32: {::#04x}", bytes_written, write_buffer); -- cgit v1.2.3 From adb1b5f2d6858097227fa610e86d1152b79d0bfa Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Mon, 25 May 2026 14:05:38 +0200 Subject: renaming --- kernel/src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index a84ca64..6985e94 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -99,8 +99,8 @@ auto run_demo() -> void kstd::println("--> read {} bytes from /dev/ram32: {::#04x} ", bytes_read, buffer_3); // 9) write to the device file - auto const buffer_fill_byte = std::byte{0xAA}; - kstd::vector write_buffer{buffer_fill_byte, buffer_fill_byte}; + auto const default_buffer_value = std::byte{0xAA}; + kstd::vector write_buffer{default_buffer_value, default_buffer_value}; auto bytes_written = kapi::filesystem::write(fd_3, write_buffer.data(), write_buffer.size()); kstd::println("--> written {} bytes to /dev/ram32: {::#04x}", bytes_written, write_buffer); -- cgit v1.2.3 From e4dfaac871b9c0e38ed3e500f86ff1f74d8a3a52 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 31 May 2026 15:55:47 +0200 Subject: refactor and simplify mapping logic --- .../include/kernel/filesystem/ext2/filesystem.hpp | 16 ++-- kernel/src/filesystem/ext2/filesystem.cpp | 101 ++++++++------------- 2 files changed, 48 insertions(+), 69 deletions(-) diff --git a/kernel/include/kernel/filesystem/ext2/filesystem.hpp b/kernel/include/kernel/filesystem/ext2/filesystem.hpp index d5e5b8b..2284d7b 100644 --- a/kernel/include/kernel/filesystem/ext2/filesystem.hpp +++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -86,16 +87,17 @@ namespace kernel::filesystem::ext2 -> kstd::ssize_t; private: + struct indirect_level + { + uint32_t slot_index; + size_t capacity; + }; + + [[nodiscard]] auto indirect_levels() const -> std::array; + [[nodiscard]] auto read_inode(uint32_t inode_number) const -> kstd::shared_ptr; [[nodiscard]] auto read_block_number_at_index(uint32_t block_number, size_t index) const -> uint32_t; - [[nodiscard]] auto read_singly_indirect_block_number(uint32_t singly_indirect_block_number, - size_t block_index_in_singly_indirect_block) const -> size_t; - [[nodiscard]] auto read_doubly_indirect_block_number(uint32_t doubly_indirect_block_number, - size_t block_index_in_doubly_indirect_block) const -> size_t; - [[nodiscard]] auto read_triply_indirect_block_number(uint32_t triply_indirect_block_number, - size_t block_index_in_triply_indirect_block) const -> size_t; - [[nodiscard]] auto inode_size() const -> uint16_t; [[nodiscard]] auto inode_block_count(inode_data const & data) const -> uint32_t; [[nodiscard]] auto block_group_descriptor_table_offset() const -> size_t; diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index a30149e..720f0a8 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -109,6 +110,15 @@ namespace kernel::filesystem::ext2 return kstd::make_shared(this, new_inode_data); } + auto filesystem::indirect_levels() const -> std::array + { + return { + {{constants::singly_indirect_block_index, block_numbers_per_singly_indirect_block()}, + {constants::doubly_indirect_block_index, block_numbers_per_doubly_indirect_block()}, + {constants::triply_indirect_block_index, block_numbers_per_triply_indirect_block()}} + }; + } + auto filesystem::map_inode_block_index_to_global_block_number(size_t inode_block_index, inode_data data) const -> kstd::ssize_t { @@ -116,78 +126,45 @@ namespace kernel::filesystem::ext2 { return data.block.at(inode_block_index); } - inode_block_index -= constants::direct_block_count; - - if (inode_block_index < block_numbers_per_singly_indirect_block()) - { - auto const singly_indirect_block_number = data.block.at(constants::singly_indirect_block_index); - return read_singly_indirect_block_number(singly_indirect_block_number, inode_block_index); - } - inode_block_index -= block_numbers_per_singly_indirect_block(); - - if (inode_block_index < block_numbers_per_doubly_indirect_block()) - { - auto const doubly_indirect_block_number = data.block.at(constants::doubly_indirect_block_index); - return read_doubly_indirect_block_number(doubly_indirect_block_number, inode_block_index); - } - inode_block_index -= block_numbers_per_doubly_indirect_block(); - if (inode_block_index < block_numbers_per_triply_indirect_block()) - { - auto const triply_indirect_block_number = data.block.at(constants::triply_indirect_block_index); - return read_triply_indirect_block_number(triply_indirect_block_number, inode_block_index); - } - - return -1; - } + inode_block_index -= constants::direct_block_count; + auto const levels = indirect_levels(); - auto filesystem::read_singly_indirect_block_number(uint32_t singly_indirect_block_number, - size_t block_index_in_singly_indirect_block) const -> size_t - { - if (singly_indirect_block_number == 0) + for (auto const & level : levels) { - return 0; - } - - return read_block_number_at_index(singly_indirect_block_number, block_index_in_singly_indirect_block); - } + if (inode_block_index >= level.capacity) + { + inode_block_index -= level.capacity; + continue; + } - auto filesystem::read_doubly_indirect_block_number(uint32_t doubly_indirect_block_number, - size_t block_index_in_doubly_indirect_block) const -> size_t - { - if (doubly_indirect_block_number == 0) - { - return 0; - } + auto block_number = data.block[level.slot_index]; + if (block_number == 0) + { + return 0; + } - auto const singly_indirect_block_index_in_doubly_indirect_block = - block_index_in_doubly_indirect_block / block_numbers_per_singly_indirect_block(); - auto const block_index_in_singly_indirect_block = - block_index_in_doubly_indirect_block % block_numbers_per_singly_indirect_block(); + for (auto stride = level.capacity / block_numbers_per_block();; stride /= block_numbers_per_block()) + { + auto const idx = inode_block_index / stride; + inode_block_index %= stride; - auto const singly_indirect_block_number = - read_block_number_at_index(doubly_indirect_block_number, singly_indirect_block_index_in_doubly_indirect_block); + block_number = read_block_number_at_index(block_number, idx); + if (block_number == 0) + { + return 0; + } - return read_singly_indirect_block_number(singly_indirect_block_number, block_index_in_singly_indirect_block); - } + if (stride == 1) + { + break; + } + } - auto filesystem::read_triply_indirect_block_number(uint32_t triply_indirect_block_number, - size_t block_index_in_triply_indirect_block) const -> size_t - { - if (triply_indirect_block_number == 0) - { - return 0; + return block_number; } - auto const doubly_indirect_block_index_in_triply_indirect_block = - block_index_in_triply_indirect_block / block_numbers_per_doubly_indirect_block(); - auto const block_index_in_doubly_indirect_block = - block_index_in_triply_indirect_block % block_numbers_per_doubly_indirect_block(); - - auto const doubly_indirect_block_number = - read_block_number_at_index(triply_indirect_block_number, doubly_indirect_block_index_in_triply_indirect_block); - - return read_doubly_indirect_block_number(doubly_indirect_block_number, block_index_in_doubly_indirect_block); + return -1; } auto filesystem::read_block_number_at_index(uint32_t block_number, size_t index) const -> uint32_t -- cgit v1.2.3 From f26569c6a4e5f0ed61b7efa81ebdddd532d542c5 Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 31 May 2026 16:06:38 +0200 Subject: small refactoring --- kernel/src/filesystem/ext2/filesystem.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 720f0a8..12aeaaa 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -128,9 +128,8 @@ namespace kernel::filesystem::ext2 } inode_block_index -= constants::direct_block_count; - auto const levels = indirect_levels(); - for (auto const & level : levels) + for (auto const & level : indirect_levels()) { if (inode_block_index >= level.capacity) { -- cgit v1.2.3 From dc9bd3b44cbbd0235a176f05c27eb15ff31f5e09 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Fri, 22 May 2026 20:18:05 +0200 Subject: kernel/vfs: prepare fs type registration support --- arch/x86_64/scripts/kernel.ld | 5 +++++ kernel/include/kernel/filesystem/type.hpp | 33 ++++++++++++++++++++++++++++++ kernel/src/filesystem/devfs/filesystem.cpp | 22 ++++++++++++++++++++ kernel/src/filesystem/ext2/filesystem.cpp | 23 +++++++++++++++++++++ kernel/src/filesystem/vfs.cpp | 15 ++++++++++++++ 5 files changed, 98 insertions(+) create mode 100644 kernel/include/kernel/filesystem/type.hpp diff --git a/arch/x86_64/scripts/kernel.ld b/arch/x86_64/scripts/kernel.ld index a429570..388c46e 100644 --- a/arch/x86_64/scripts/kernel.ld +++ b/arch/x86_64/scripts/kernel.ld @@ -72,6 +72,11 @@ SECTIONS .kernel_rodata ALIGN(4K) : AT (ADDR (.kernel_rodata) - TEACHOS_VMA) { *(.rodata*) + + . = ALIGN(8); + PROVIDE(__vfs_type_descriptors_begin = .); + KEEP(*(.vfs_type_descriptors*)); + PROVIDE(__vfs_type_descriptors_end = .); } :kernel_rodata .kernel_data ALIGN(4K) : AT (ADDR (.kernel_data) - TEACHOS_VMA) diff --git a/kernel/include/kernel/filesystem/type.hpp b/kernel/include/kernel/filesystem/type.hpp new file mode 100644 index 0000000..87e466b --- /dev/null +++ b/kernel/include/kernel/filesystem/type.hpp @@ -0,0 +1,33 @@ +#ifndef TEACH_OS_KERNEL_FILESYSTEM_TYPE_HPP +#define TEACH_OS_KERNEL_FILESYSTEM_TYPE_HPP + +#include + +#include + +#include + +namespace kernel::filesystem +{ + + //! A type descriptor for a filesystem driver. + //! + //! Each filesystem must expose an instance of a class derived from this type in order to be registered with the vfs + //! filesystem registry. + struct type + { + virtual ~type() = default; + + //! Get the name of the filesystem represented by this descriptor. + [[nodiscard]] virtual auto name() const noexcept -> std::string_view = 0; + + //! Check if filesystems of this type require a device to back them. + [[nodiscard]] virtual auto requires_device() const noexcept -> bool = 0; + + //! Create a new instance of the filesytem represented by this descriptor. + [[nodiscard]] virtual auto make_instance() const -> kstd::shared_ptr = 0; + }; + +} // namespace kernel::filesystem + +#endif diff --git a/kernel/src/filesystem/devfs/filesystem.cpp b/kernel/src/filesystem/devfs/filesystem.cpp index f0d8bf7..c61a3d0 100644 --- a/kernel/src/filesystem/devfs/filesystem.cpp +++ b/kernel/src/filesystem/devfs/filesystem.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -15,6 +16,27 @@ namespace kernel::filesystem::devfs { + struct type final : kernel::filesystem::type + { + [[nodiscard]] auto name() const noexcept -> std::string_view override + { + return "devfs"; + } + + [[nodiscard]] auto requires_device() const noexcept -> bool override + { + return false; + } + + [[nodiscard]] auto make_instance() const -> kstd::shared_ptr override + { + return kstd::make_shared(); + } + } const constinit type_instance{}; + + [[gnu::section(".vfs_type_descriptors"), gnu::used]] + auto const * const type_descriptor_pointer = static_cast(&type_instance); + auto filesystem::mount(kstd::shared_ptr const &) -> operation_result { m_root_inode = kstd::make_shared(); diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 12aeaaa..6959653 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -18,6 +19,28 @@ namespace kernel::filesystem::ext2 { + + struct type final : kernel::filesystem::type + { + [[nodiscard]] auto name() const noexcept -> std::string_view override + { + return "ext2"; + } + + [[nodiscard]] auto requires_device() const noexcept -> bool override + { + return true; + } + + [[nodiscard]] auto make_instance() const -> kstd::shared_ptr override + { + return kstd::make_shared(); + } + } const constinit type_instance{}; + + [[gnu::section(".vfs_type_descriptors"), gnu::used]] + auto const * const type_descriptor_pointer = static_cast(&type_instance); + auto filesystem::mount(kstd::shared_ptr const & backing_inode) -> operation_result { kernel::filesystem::filesystem::mount(backing_inode); diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index ae85291..d22c74b 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -8,10 +8,12 @@ #include #include #include +#include #include #include +#include #include #include @@ -19,9 +21,16 @@ #include #include #include +#include #include #include +extern "C" +{ + extern kernel::filesystem::type const * const __vfs_type_descriptors_begin; + extern kernel::filesystem::type const * const __vfs_type_descriptors_end; +} + namespace { constinit auto static active_vfs = std::optional{}; @@ -42,6 +51,12 @@ namespace kernel::filesystem auto vfs::init_internal() -> void { + auto type_descriptors = std::span{&__vfs_type_descriptors_begin, &__vfs_type_descriptors_end} | + std::views::filter([](auto p) { return p != nullptr; }); + std::ranges::for_each(type_descriptors, [](auto descriptor) { + kstd::println("[OS] registering filesystem '{}'", descriptor->name()); + }); + // mount rootfs at / auto root_fs = kstd::make_shared(); root_fs->mount(nullptr); -- cgit v1.2.3 From 86170be29f6cb72b29865db0975de09bec89f854 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 1 Jun 2026 10:40:39 +0200 Subject: kernel/vfs: rename type descriptor section --- arch/x86_64/scripts/kernel.ld | 6 +++--- kernel/src/filesystem/devfs/filesystem.cpp | 2 +- kernel/src/filesystem/ext2/filesystem.cpp | 2 +- kernel/src/filesystem/vfs.cpp | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/x86_64/scripts/kernel.ld b/arch/x86_64/scripts/kernel.ld index 388c46e..a1d8a65 100644 --- a/arch/x86_64/scripts/kernel.ld +++ b/arch/x86_64/scripts/kernel.ld @@ -74,9 +74,9 @@ SECTIONS *(.rodata*) . = ALIGN(8); - PROVIDE(__vfs_type_descriptors_begin = .); - KEEP(*(.vfs_type_descriptors*)); - PROVIDE(__vfs_type_descriptors_end = .); + PROVIDE(__start_vfs_type_descriptors = .); + KEEP(*(vfs_type_descriptors)); + PROVIDE(__stop_vfs_type_descriptors = .); } :kernel_rodata .kernel_data ALIGN(4K) : AT (ADDR (.kernel_data) - TEACHOS_VMA) diff --git a/kernel/src/filesystem/devfs/filesystem.cpp b/kernel/src/filesystem/devfs/filesystem.cpp index c61a3d0..1aaa0d1 100644 --- a/kernel/src/filesystem/devfs/filesystem.cpp +++ b/kernel/src/filesystem/devfs/filesystem.cpp @@ -34,7 +34,7 @@ namespace kernel::filesystem::devfs } } const constinit type_instance{}; - [[gnu::section(".vfs_type_descriptors"), gnu::used]] + [[gnu::section("vfs_type_descriptors"), gnu::used]] auto const * const type_descriptor_pointer = static_cast(&type_instance); auto filesystem::mount(kstd::shared_ptr const &) -> operation_result diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 6959653..7b5d2d7 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -38,7 +38,7 @@ namespace kernel::filesystem::ext2 } } const constinit type_instance{}; - [[gnu::section(".vfs_type_descriptors"), gnu::used]] + [[gnu::section("vfs_type_descriptors"), gnu::used]] auto const * const type_descriptor_pointer = static_cast(&type_instance); auto filesystem::mount(kstd::shared_ptr const & backing_inode) -> operation_result diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index d22c74b..c297210 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -27,8 +27,8 @@ extern "C" { - extern kernel::filesystem::type const * const __vfs_type_descriptors_begin; - extern kernel::filesystem::type const * const __vfs_type_descriptors_end; + extern kernel::filesystem::type const * const __start_vfs_type_descriptors; + extern kernel::filesystem::type const * const __stop_vfs_type_descriptors; } namespace @@ -51,7 +51,7 @@ namespace kernel::filesystem auto vfs::init_internal() -> void { - auto type_descriptors = std::span{&__vfs_type_descriptors_begin, &__vfs_type_descriptors_end} | + auto type_descriptors = std::span{&__start_vfs_type_descriptors, &__stop_vfs_type_descriptors} | std::views::filter([](auto p) { return p != nullptr; }); std::ranges::for_each(type_descriptors, [](auto descriptor) { kstd::println("[OS] registering filesystem '{}'", descriptor->name()); -- cgit v1.2.3 From 568e9586c036fc319fc36b12495e5deacb167be9 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 1 Jun 2026 20:10:57 +0200 Subject: kernel/vfs: simplify initialization --- kernel/include/kernel/filesystem/vfs.hpp | 5 ++--- kernel/src/filesystem/vfs.cpp | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index e6f2327..bbafa4f 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -35,6 +35,8 @@ namespace kernel::filesystem invalid_filesystem = -5 }; + vfs(); + /** @brief Initialize the virtual filesystem. @warning Panics if the VFS has already been initialized. @@ -83,9 +85,6 @@ namespace kernel::filesystem auto unmount(std::string_view path) -> operation_result; private: - vfs() = default; - auto init_internal() -> void; - /** * Note: Resolving a dentry requires traversing mount points; since the * associated 'mount' object is discovered as a byproduct of this diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index c297210..91afc13 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -45,12 +45,12 @@ namespace kernel::filesystem kapi::system::panic("[FILESYSTEM] vfs has already been initialized."); } - active_vfs.emplace(vfs{}); - active_vfs->init_internal(); + active_vfs.emplace(); } - auto vfs::init_internal() -> void + vfs::vfs() { + // Register all compiled-in filesystems auto type_descriptors = std::span{&__start_vfs_type_descriptors, &__stop_vfs_type_descriptors} | std::views::filter([](auto p) { return p != nullptr; }); std::ranges::for_each(type_descriptors, [](auto descriptor) { -- cgit v1.2.3 From 6c8b068c15e28e91117f84cb8d5789f5fe6fcbd0 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 1 Jun 2026 20:31:57 +0200 Subject: kstd/string: simplify comparisons --- kernel/kapi/filesystem.cpp | 2 +- kernel/src/filesystem/dentry.cpp | 2 +- kernel/src/filesystem/vfs.cpp | 8 +-- kernel/src/filesystem/vfs.tests.cpp | 32 +++++------ libs/kstd/kstd/string | 105 ++++++++++++++++-------------------- libs/kstd/kstd/string.test.cpp | 44 +++++++-------- 6 files changed, 89 insertions(+), 104 deletions(-) diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp index 838d2cb..68b51c9 100644 --- a/kernel/kapi/filesystem.cpp +++ b/kernel/kapi/filesystem.cpp @@ -45,7 +45,7 @@ namespace kapi::filesystem { if (auto open_file_descriptor = kernel::filesystem::open_file_table::get().file(file_descriptor)) { - if (kernel::filesystem::vfs::get().close(open_file_descriptor->get_dentry()->absolute_path().view()) == + if (kernel::filesystem::vfs::get().close(open_file_descriptor->get_dentry()->absolute_path()) == kernel::filesystem::vfs::operation_result::success) { return kernel::filesystem::open_file_table::get().remove_file(file_descriptor); diff --git a/kernel/src/filesystem/dentry.cpp b/kernel/src/filesystem/dentry.cpp index 14de875..3d8e01a 100644 --- a/kernel/src/filesystem/dentry.cpp +++ b/kernel/src/filesystem/dentry.cpp @@ -41,7 +41,7 @@ namespace kernel::filesystem auto dentry::name() const -> std::string_view { - return m_name.view(); + return m_name; } auto dentry::absolute_path() const -> kstd::string diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 91afc13..b0d63a2 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -241,22 +241,22 @@ namespace kernel::filesystem continue; } - auto next_dentry = current_dentry->find_child(part.view()); + auto next_dentry = current_dentry->find_child(part); if (!next_dentry) { auto current_fs = current_mount->get_filesystem(); - auto found_inode = current_fs->lookup(current_dentry->get_inode(), part.view()); + auto found_inode = current_fs->lookup(current_dentry->get_inode(), part); if (!found_inode) { return {nullptr, nullptr}; } - next_dentry = kstd::make_shared(current_dentry, found_inode, part.view()); + next_dentry = kstd::make_shared(current_dentry, found_inode, part); current_dentry->add_child(next_dentry); } else if (next_dentry->has_flag(dentry::dentry_flags::is_mount_point)) { - current_mount = m_mount_table.find_mount(next_dentry->absolute_path().view()); + current_mount = m_mount_table.find_mount(next_dentry->absolute_path()); if (!current_mount) { kapi::system::panic("[FILESYSTEM] mount for dentry with mounted flag not found."); diff --git a/kernel/src/filesystem/vfs.tests.cpp b/kernel/src/filesystem/vfs.tests.cpp index 962e067..f1d0df0 100644 --- a/kernel/src/filesystem/vfs.tests.cpp +++ b/kernel/src/filesystem/vfs.tests.cpp @@ -106,8 +106,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto mounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); REQUIRE(mounted_monkey_1 != nullptr); - REQUIRE(vfs.close(mounted_monkey_1->absolute_path().view()) == - kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_monkey_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_monkey_1 = vfs.open("/information/monkey_house/monkey_1.txt"); @@ -129,9 +128,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(mounted_monkey_1 != nullptr); REQUIRE(mounted_fish1 != nullptr); - REQUIRE(vfs.close(mounted_monkey_1->absolute_path().view()) == - kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.close(mounted_fish1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_monkey_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_fish1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); REQUIRE(vfs.unmount("/information/monkey_house/infrastructure") == @@ -148,8 +146,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); - REQUIRE(vfs.close(mounted_monkey_1->absolute_path().view()) == - kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_monkey_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); } @@ -165,8 +162,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); - REQUIRE(vfs.close(info_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); - REQUIRE_THROWS_AS(vfs.close(info_1->absolute_path().view()), std::runtime_error); + REQUIRE(vfs.close(info_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE_THROWS_AS(vfs.close(info_1->absolute_path()), std::runtime_error); } THEN("images can be stacked mounted and correct file system is unmounted again") @@ -177,7 +174,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto mounted_tickets = vfs.open("/information/entrance/tickets.txt"); REQUIRE(mounted_tickets != nullptr); - REQUIRE(vfs.close(mounted_tickets->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(mounted_tickets->absolute_path()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); mounted_tickets = vfs.open("/information/entrance/tickets.txt"); @@ -200,7 +197,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto water = vfs.open("/monkey_house/infrastructure/water.txt"); REQUIRE(water != nullptr); - REQUIRE(vfs.close(water->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(water->absolute_path()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); @@ -221,7 +218,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto water = vfs.open("/monkey_house/infrastructure/water.txt"); REQUIRE(water != nullptr); - REQUIRE(vfs.close(water->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(water->absolute_path()) == kernel::filesystem::vfs::operation_result::success); auto dev_ram_16 = vfs.open("/dev/ram16"); REQUIRE(dev_ram_16 == nullptr); @@ -239,7 +236,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto info_1 = vfs.open("/information/info_1.txt"); REQUIRE(info_1 != nullptr); - REQUIRE(vfs.close(info_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(info_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/dev") == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/") == kernel::filesystem::vfs::operation_result::success); @@ -362,7 +359,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS std::string_view buffer_as_str{reinterpret_cast(buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "sheep_1"); - REQUIRE(vfs.close(dentry->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(dentry->absolute_path()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); @@ -395,8 +392,8 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS buffer_as_str = std::string_view{reinterpret_cast(goat_buffer.data()), bytes_read}; REQUIRE(buffer_as_str == "goat_1"); - REQUIRE(vfs.close(sheep_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); - REQUIRE(vfs.close(goat_1->absolute_path().view()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(sheep_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(goat_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::unmount_failed); @@ -407,8 +404,7 @@ SCENARIO_METHOD(kernel::tests::filesystem::storage_boot_module_vfs_fixture, "VFS auto still_mounted_sheep_1 = vfs.open("/information/sheep_1.txt"); REQUIRE(still_mounted_sheep_1 != nullptr); - REQUIRE(vfs.close(still_mounted_sheep_1->absolute_path().view()) == - kernel::filesystem::vfs::operation_result::success); + REQUIRE(vfs.close(still_mounted_sheep_1->absolute_path()) == kernel::filesystem::vfs::operation_result::success); REQUIRE(vfs.unmount("/information") == kernel::filesystem::vfs::operation_result::success); auto unmounted_sheep_1 = vfs.open("/information/sheep_1.txt"); diff --git a/libs/kstd/kstd/string b/libs/kstd/kstd/string index e228a04..9343b42 100644 --- a/libs/kstd/kstd/string +++ b/libs/kstd/kstd/string @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -52,7 +53,7 @@ namespace kstd * @brief Constructs a string from a string view by copying the characters into owned storage. * @param view The string view to copy the characters from. */ - string(std::string_view view) + explicit string(std::string_view view) : string() { append(view); @@ -101,6 +102,26 @@ namespace kstd */ constexpr auto operator=(string const & other) -> string & = default; + constexpr auto operator=(std::string_view const & other) -> string & + { + clear(); + append(other); + return *this; + } + + constexpr auto operator=(char const * other) -> string & + { + clear(); + append(std::string_view{other}); + return *this; + } + + //! Create a string view from this string. + constexpr operator std::string_view() const noexcept + { + return {data(), size()}; + } + /** * @brief Returns the number of characters in this string, not including the null terminator. */ @@ -224,6 +245,7 @@ namespace kstd { if (!view.empty()) { + m_storage.reserve(size() + view.size() + 1); std::ranges::for_each(view, [this](auto const ch) { push_back(ch); }); } @@ -237,7 +259,7 @@ namespace kstd */ constexpr auto append(string const & other) -> string & { - return append(other.view()); + return append(static_cast(other)); } /** @@ -261,12 +283,29 @@ namespace kstd return *this; } - /** - * @brief Returns a string view of this string, which is a non-owning view into the characters of this string. - */ - [[nodiscard]] constexpr auto view() const noexcept -> std::string_view + //! Compare this string lexicographically to another string. + //! + //! @param other The string to compare to this one. + [[nodiscard]] constexpr auto operator<=>(string const & other) const noexcept -> std::strong_ordering = default; + + [[nodiscard]] constexpr auto operator==(string const & other) const noexcept -> bool = default; + + //! Compare this string lexicographically to a C-style string. + //! + //! @param other The C-style string to compare to this one. + //! @return The result of the comparison. + [[nodiscard]] constexpr auto operator<=>(char const * other) const noexcept + { + return static_cast(*this) <=> other; + } + + //! Check if this string compares equal to a C-style string. + //! + //! @param other The C-style string to compare to this one. + //! @return @p true iff. this string compares equal to @p other, @p false otherwise. + [[nodiscard]] constexpr auto operator==(char const * other) const noexcept -> bool { - return std::string_view{data(), size()}; + return static_cast(*this) == other; } private: @@ -316,62 +355,12 @@ namespace kstd return result; } - [[nodiscard]] constexpr auto inline operator==(string const & lhs, string const & rhs) -> bool - { - return lhs.view() == rhs.view(); - } - - [[nodiscard]] constexpr auto inline operator!=(string const & lhs, string const & rhs) -> bool - { - return !(lhs == rhs); - } - - [[nodiscard]] constexpr auto inline operator==(string const & lhs, std::string_view rhs) -> bool - { - return lhs.view() == rhs; - } - - [[nodiscard]] constexpr auto inline operator!=(string const & lhs, std::string_view rhs) -> bool - { - return !(lhs == rhs); - } - - [[nodiscard]] constexpr auto inline operator==(std::string_view lhs, string const & rhs) -> bool - { - return lhs == rhs.view(); - } - - [[nodiscard]] constexpr auto inline operator!=(std::string_view lhs, string const & rhs) -> bool - { - return !(lhs == rhs); - } - - [[nodiscard]] constexpr auto inline operator==(string const & lhs, char const * rhs) -> bool - { - return lhs.view() == std::string_view{rhs}; - } - - [[nodiscard]] constexpr auto inline operator!=(string const & lhs, char const * rhs) -> bool - { - return !(lhs == rhs); - } - - [[nodiscard]] constexpr auto inline operator==(char const * lhs, string const & rhs) -> bool - { - return std::string_view{lhs} == rhs.view(); - } - - [[nodiscard]] constexpr auto inline operator!=(char const * lhs, string const & rhs) -> bool - { - return !(lhs == rhs); - } - template<> struct formatter : formatter { auto format(string const & str, format_context & context) const -> void { - formatter::format(str.view(), context); + formatter::format(static_cast(str), context); } }; diff --git a/libs/kstd/kstd/string.test.cpp b/libs/kstd/kstd/string.test.cpp index 9755676..b81cd3a 100644 --- a/libs/kstd/kstd/string.test.cpp +++ b/libs/kstd/kstd/string.test.cpp @@ -22,7 +22,7 @@ SCENARIO("String initialization and construction", "[string]") THEN("the string is empty") { - REQUIRE(str.view() == std::string_view{}); + REQUIRE(str == std::string_view{}); } } } @@ -43,7 +43,7 @@ SCENARIO("String initialization and construction", "[string]") THEN("the string contains the same characters as the view") { - REQUIRE(str.view() == view); + REQUIRE(str == view); } } } @@ -64,7 +64,7 @@ SCENARIO("String initialization and construction", "[string]") THEN("the string contains the same characters as the C-style string") { - REQUIRE(str.view() == c_str); + REQUIRE(str == c_str); } } } @@ -85,7 +85,7 @@ SCENARIO("String initialization and construction", "[string]") THEN("the string contains the same character as the given character") { - REQUIRE(str.view() == std::string_view{&ch, 1}); + REQUIRE(str == std::string_view{&ch, 1}); } } } @@ -100,7 +100,7 @@ SCENARIO("String initialization and construction", "[string]") THEN("the new string contains the same characters as the original") { - REQUIRE(str.view() == other.view()); + REQUIRE(static_cast(str) == static_cast(other)); } } @@ -113,7 +113,7 @@ SCENARIO("String initialization and construction", "[string]") THEN("the string contains the same characters as the assigned string") { - REQUIRE(str.view() == other.view()); + REQUIRE(static_cast(str) == static_cast(other)); } } } @@ -129,7 +129,7 @@ SCENARIO("String initialization and construction", "[string]") THEN("the string contains the same characters as the assigned view") { - REQUIRE(str.view() == view); + REQUIRE(str == view); } } } @@ -145,7 +145,7 @@ SCENARIO("String initialization and construction", "[string]") THEN("the string contains the same characters as the assigned C-style string") { - REQUIRE(str.view() == c_str); + REQUIRE(str == c_str); } } } @@ -164,7 +164,7 @@ SCENARIO("String concatenation", "[string]") THEN("the first string contains the characters of both strings concatenated") { - REQUIRE(str1.view() == "Blub Blub"); + REQUIRE(str1 == "Blub Blub"); } THEN("the size of the first string is the sum of the sizes of both strings") @@ -179,7 +179,7 @@ SCENARIO("String concatenation", "[string]") THEN("the first string contains the characters of both strings concatenated") { - REQUIRE(str1.view() == "Blub Blub"); + REQUIRE(str1 == "Blub Blub"); } THEN("the size of the first string is the sum of the sizes of both strings") @@ -194,7 +194,7 @@ SCENARIO("String concatenation", "[string]") THEN("the new string contains the characters of both strings concatenated") { - REQUIRE(str3.view() == "Blub Blub"); + REQUIRE(str3 == "Blub Blub"); } THEN("the size of the new string is the sum of the sizes of both strings") @@ -215,7 +215,7 @@ SCENARIO("String concatenation", "[string]") THEN("the string contains the characters of both the original string and the appended view concatenated") { - REQUIRE(str.view() == "Blub Blub"); + REQUIRE(str == "Blub Blub"); } THEN("the size of the string is the sum of the sizes of the original string and the appended view") @@ -236,7 +236,7 @@ SCENARIO("String concatenation", "[string]") THEN("the string contains the original characters followed by the appended character") { - REQUIRE(str.view() == "Blub!"); + REQUIRE(str == "Blub!"); } THEN("the size of the string is one more than the original size") @@ -251,7 +251,7 @@ SCENARIO("String concatenation", "[string]") THEN("the string contains the original characters followed by the appended character") { - REQUIRE(str.view() == "Blub!"); + REQUIRE(str == "Blub!"); } THEN("the size of the string is one more than the original size") @@ -276,8 +276,8 @@ SCENARIO("String conversion and comparison", "[string]") THEN("the string contains the decimal representation of the unsigned integer") { - REQUIRE(str1.view() == "12345"); - REQUIRE(str2.view() == "0"); + REQUIRE(str1 == "12345"); + REQUIRE(str2 == "0"); } } } @@ -335,7 +335,7 @@ SCENARIO("String clearing", "[string]") THEN("the string contains no characters") { - REQUIRE(str.view() == std::string_view{}); + REQUIRE(str == std::string_view{}); } } } @@ -351,7 +351,7 @@ SCENARIO("String iteration", "[string]") { kstd::string result; - for (auto ch : str.view()) + for (auto ch : static_cast(str)) { result.push_back(ch); } @@ -396,14 +396,14 @@ SCENARIO("String iteration", "[string]") { kstd::string result; - for (auto ch : str.view()) + for (auto ch : static_cast(str)) { result.push_back(ch); } THEN("the iterated characters are the same as the characters in the string") { - REQUIRE(result == str.view()); + REQUIRE(result == static_cast(str)); } } @@ -429,7 +429,7 @@ SCENARIO("String iteration", "[string]") { kstd::string result; - for (auto ch : str.view()) + for (auto ch : static_cast(str)) { result.push_back(ch); } @@ -438,7 +438,7 @@ SCENARIO("String iteration", "[string]") { REQUIRE(result.empty()); REQUIRE(result.size() == 0); - REQUIRE(result.view() == std::string_view{}); + REQUIRE(static_cast(result) == std::string_view{}); } } } -- cgit v1.2.3 From ad2319188269331f4873adbeb44380d63e8e83c5 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 1 Jun 2026 21:15:25 +0200 Subject: kernel/vfs: manage filesystem registrations --- kernel/include/kernel/filesystem/vfs.hpp | 7 ++++++- kernel/src/filesystem/vfs.cpp | 5 +++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index bbafa4f..c8aae5c 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -6,8 +6,11 @@ #include #include #include +#include +#include #include +#include #include #include @@ -106,7 +109,9 @@ namespace kernel::filesystem auto graft_persistent_device_fs(kstd::shared_ptr const & device_fs) -> void; - mount_table m_mount_table; + //! A map from filesystem names (identifiers) to filesystem type descriptors. + kstd::flat_map> m_filesystems{}; + mount_table m_mount_table{}; }; } // namespace kernel::filesystem diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index b0d63a2..8dcae2c 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -53,8 +53,9 @@ namespace kernel::filesystem // Register all compiled-in filesystems auto type_descriptors = std::span{&__start_vfs_type_descriptors, &__stop_vfs_type_descriptors} | std::views::filter([](auto p) { return p != nullptr; }); - std::ranges::for_each(type_descriptors, [](auto descriptor) { - kstd::println("[OS] registering filesystem '{}'", descriptor->name()); + std::ranges::for_each(type_descriptors, [this](auto descriptor) { + kstd::println("[FILESYSTEM] registering '{}'", descriptor->name()); + m_filesystems.emplace(descriptor->name(), descriptor); }); // mount rootfs at / -- cgit v1.2.3 From d2d4fa3330a09f421b8658c077166cc493532b9e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 2 Jun 2026 10:54:17 +0200 Subject: kernel/vfs: extract fs type registry --- arch/x86_64/scripts/kernel.ld | 7 ++- kernel/CMakeLists.txt | 1 + kernel/include/kernel/filesystem/type.hpp | 9 +++ kernel/include/kernel/filesystem/type_registry.hpp | 47 +++++++++++++++ kernel/include/kernel/filesystem/vfs.hpp | 5 -- kernel/src/filesystem/devfs/filesystem.cpp | 6 +- kernel/src/filesystem/ext2/filesystem.cpp | 6 +- kernel/src/filesystem/type_registry.cpp | 67 ++++++++++++++++++++++ kernel/src/filesystem/vfs.cpp | 17 ------ kernel/src/main.cpp | 2 + 10 files changed, 136 insertions(+), 31 deletions(-) create mode 100644 kernel/include/kernel/filesystem/type_registry.hpp create mode 100644 kernel/src/filesystem/type_registry.cpp diff --git a/arch/x86_64/scripts/kernel.ld b/arch/x86_64/scripts/kernel.ld index a1d8a65..dbb0f8f 100644 --- a/arch/x86_64/scripts/kernel.ld +++ b/arch/x86_64/scripts/kernel.ld @@ -74,9 +74,10 @@ SECTIONS *(.rodata*) . = ALIGN(8); - PROVIDE(__start_vfs_type_descriptors = .); - KEEP(*(vfs_type_descriptors)); - PROVIDE(__stop_vfs_type_descriptors = .); + + PROVIDE(__start_fs_types = .); + KEEP(*(fs_types)); + PROVIDE(__stop_fs_types = .); } :kernel_rodata .kernel_data ALIGN(4K) : AT (ADDR (.kernel_data) - TEACHOS_VMA) diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index cbc7fa5..3dd84ba 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -53,6 +53,7 @@ target_sources("kernel_lib" PRIVATE "src/filesystem/mount.cpp" "src/filesystem/open_file_descriptor.cpp" "src/filesystem/open_file_table.cpp" + "src/filesystem/type_registry.cpp" "src/filesystem/vfs.cpp" # DevFS Filesystem diff --git a/kernel/include/kernel/filesystem/type.hpp b/kernel/include/kernel/filesystem/type.hpp index 87e466b..0948e54 100644 --- a/kernel/include/kernel/filesystem/type.hpp +++ b/kernel/include/kernel/filesystem/type.hpp @@ -28,6 +28,15 @@ namespace kernel::filesystem [[nodiscard]] virtual auto make_instance() const -> kstd::shared_ptr = 0; }; + template + struct type_registration + { + constexpr auto static instance = Type{}; + [[using gnu: section("fs_types"), used, visibility("hidden")]] constexpr auto static pointer{ + kstd::make_observer(&instance), + }; + }; + } // namespace kernel::filesystem #endif diff --git a/kernel/include/kernel/filesystem/type_registry.hpp b/kernel/include/kernel/filesystem/type_registry.hpp new file mode 100644 index 0000000..2f5e708 --- /dev/null +++ b/kernel/include/kernel/filesystem/type_registry.hpp @@ -0,0 +1,47 @@ +#ifndef TEACH_OS_KERNEL_TYPE_REGISRY_HPP +#define TEACH_OS_KERNEL_TYPE_REGISRY_HPP + +#include + +#include +#include +#include + +#include +#include + +namespace kernel::filesystem +{ + + struct type_registry + { + using value_type = type; + using pointer = kstd::observer_ptr; + + auto static init() -> void; + auto static get() -> type_registry &; + + constexpr type_registry() noexcept = default; + + //! Add a type descriptor to this registry. + //! + //! This function will register the given descriptor with this registry, given that no descriptor for a filesystem + //! with the same name exists in this registry already. + //! + //! @param descriptor The filesystem type descriptor to add to the registry. + //! @return @p true iff. the descriptor was successfully added, @p false if not. + auto add(pointer descriptor) -> bool; + + //! Get all currently registered type descriptors. + //! + //! @return A span containing all currently registered filesystem type descriptors. + [[nodiscard]] auto all() const noexcept -> std::span; + + private: + //! A map from filesystem names (identifiers) to filesystem type descriptors. + kstd::flat_map m_descriptors{}; + }; + +} // namespace kernel::filesystem + +#endif diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp index c8aae5c..ddc9a9b 100644 --- a/kernel/include/kernel/filesystem/vfs.hpp +++ b/kernel/include/kernel/filesystem/vfs.hpp @@ -6,11 +6,8 @@ #include #include #include -#include -#include #include -#include #include #include @@ -109,8 +106,6 @@ namespace kernel::filesystem auto graft_persistent_device_fs(kstd::shared_ptr const & device_fs) -> void; - //! A map from filesystem names (identifiers) to filesystem type descriptors. - kstd::flat_map> m_filesystems{}; mount_table m_mount_table{}; }; } // namespace kernel::filesystem diff --git a/kernel/src/filesystem/devfs/filesystem.cpp b/kernel/src/filesystem/devfs/filesystem.cpp index 1aaa0d1..ce887ff 100644 --- a/kernel/src/filesystem/devfs/filesystem.cpp +++ b/kernel/src/filesystem/devfs/filesystem.cpp @@ -32,10 +32,10 @@ namespace kernel::filesystem::devfs { return kstd::make_shared(); } - } const constinit type_instance{}; + }; - [[gnu::section("vfs_type_descriptors"), gnu::used]] - auto const * const type_descriptor_pointer = static_cast(&type_instance); + [[gnu::used]] + constexpr auto registration = type_registration{}; auto filesystem::mount(kstd::shared_ptr const &) -> operation_result { diff --git a/kernel/src/filesystem/ext2/filesystem.cpp b/kernel/src/filesystem/ext2/filesystem.cpp index 7b5d2d7..3180a19 100644 --- a/kernel/src/filesystem/ext2/filesystem.cpp +++ b/kernel/src/filesystem/ext2/filesystem.cpp @@ -36,10 +36,10 @@ namespace kernel::filesystem::ext2 { return kstd::make_shared(); } - } const constinit type_instance{}; + }; - [[gnu::section("vfs_type_descriptors"), gnu::used]] - auto const * const type_descriptor_pointer = static_cast(&type_instance); + [[gnu::used]] + constexpr auto registration = type_registration{}; auto filesystem::mount(kstd::shared_ptr const & backing_inode) -> operation_result { diff --git a/kernel/src/filesystem/type_registry.cpp b/kernel/src/filesystem/type_registry.cpp new file mode 100644 index 0000000..033b879 --- /dev/null +++ b/kernel/src/filesystem/type_registry.cpp @@ -0,0 +1,67 @@ +#include + +#include + +#include +#include + +#include +#include +#include +#include + +namespace kernel::filesystem +{ + + extern "C" + { + extern type_registry::pointer const __start_fs_types; + extern type_registry::pointer const __stop_fs_types; + } + + namespace + { + auto constinit instance = std::optional{}; + } + + auto type_registry::init() -> void + { + if (instance) + { + kapi::system::panic("[FILESYSTEM] tried to initialize type registry more than once!"); + } + + instance.emplace(); + + auto type_descriptors = std::span{&__start_fs_types, &__stop_fs_types} | // + std::views::filter([](auto p) { return p != nullptr; }); + + std::ranges::for_each(type_descriptors, [](auto descriptor) { + kstd::println("[FILESYSTEM] registering '{}'", descriptor->name()); + instance->add(descriptor); + }); + } + + auto type_registry::get() -> type_registry & + { + if (!instance) + { + kapi::system::panic("[FILESYSTEM] type registry has not been initialized!"); + } + + return *instance; + } + + auto type_registry::add(pointer descriptor) -> bool + { + auto result = m_descriptors.emplace(descriptor->name(), descriptor); + return result.second; + } + + [[nodiscard]] auto type_registry::all() const noexcept -> std::span + { + // TODO: implement value accessor for flat_map + return {}; + } + +} // namespace kernel::filesystem diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 8dcae2c..8d1fd23 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -8,29 +8,20 @@ #include #include #include -#include #include #include #include -#include #include #include #include #include #include -#include #include #include -extern "C" -{ - extern kernel::filesystem::type const * const __start_vfs_type_descriptors; - extern kernel::filesystem::type const * const __stop_vfs_type_descriptors; -} - namespace { constinit auto static active_vfs = std::optional{}; @@ -50,14 +41,6 @@ namespace kernel::filesystem vfs::vfs() { - // Register all compiled-in filesystems - auto type_descriptors = std::span{&__start_vfs_type_descriptors, &__stop_vfs_type_descriptors} | - std::views::filter([](auto p) { return p != nullptr; }); - std::ranges::for_each(type_descriptors, [this](auto descriptor) { - kstd::println("[FILESYSTEM] registering '{}'", descriptor->name()); - m_filesystems.emplace(descriptor->name(), descriptor); - }); - // mount rootfs at / auto root_fs = kstd::make_shared(); root_fs->mount(nullptr); diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 6985e94..5063d18 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -1,3 +1,4 @@ +#include "kernel/filesystem/type_registry.hpp" #include #include #include @@ -138,6 +139,7 @@ auto main() -> int kernel::filesystem::open_file_table::init(); kstd::println("[OS] Global open file table initialized."); + kernel::filesystem::type_registry::init(); kernel::filesystem::vfs::init(); kstd::println("[OS] Virtual filesystem initialized."); -- cgit v1.2.3 From b34db5a8acd0639fde9a81b38e96776f7c2ef61e Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 2 Jun 2026 11:38:48 +0200 Subject: kernel/vfs: add type descriptor for rootfs --- kernel/src/filesystem/rootfs/filesystem.cpp | 25 ++++++++++++++++++++++++- kernel/src/main.cpp | 2 ++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/kernel/src/filesystem/rootfs/filesystem.cpp b/kernel/src/filesystem/rootfs/filesystem.cpp index 0ba2936..7fe5c1e 100644 --- a/kernel/src/filesystem/rootfs/filesystem.cpp +++ b/kernel/src/filesystem/rootfs/filesystem.cpp @@ -1,8 +1,9 @@ #include -#include "kernel/filesystem/filesystem.hpp" +#include #include #include +#include #include @@ -10,6 +11,28 @@ namespace kernel::filesystem::rootfs { + + struct type final : kernel::filesystem::type + { + [[nodiscard]] auto name() const noexcept -> std::string_view override + { + return "rootfs"; + } + + [[nodiscard]] auto requires_device() const noexcept -> bool override + { + return true; + } + + [[nodiscard]] auto make_instance() const -> kstd::shared_ptr override + { + return kstd::make_shared(); + } + }; + + [[gnu::used]] + constexpr auto registration = type_registration{}; + auto filesystem::mount(kstd::shared_ptr const &) -> operation_result { m_root_inode = kstd::make_shared(); diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 5063d18..8dc1349 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -140,6 +140,8 @@ auto main() -> int kstd::println("[OS] Global open file table initialized."); kernel::filesystem::type_registry::init(); + kstd::println("[OS] Builtin filesystems registered."); + kernel::filesystem::vfs::init(); kstd::println("[OS] Virtual filesystem initialized."); -- cgit v1.2.3 From 772861fc5fae1c126fcc63a8809b0a9c729bd152 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 2 Jun 2026 13:43:49 +0200 Subject: kernel/vfs: add type registry tests --- kernel/CMakeLists.txt | 1 + kernel/include/kernel/filesystem/type_registry.hpp | 8 ++- kernel/src/filesystem/type_registry.cpp | 11 +++- kernel/src/filesystem/type_registry.tests.cpp | 77 ++++++++++++++++++++++ kernel/src/filesystem/vfs.cpp | 1 - libs/kstd/kstd/flat_map | 16 +++++ libs/kstd/kstd/flat_map.test.cpp | 10 +++ 7 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 kernel/src/filesystem/type_registry.tests.cpp diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 3dd84ba..2388370 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -201,6 +201,7 @@ if(BUILD_TESTING) "src/filesystem/mount.tests.cpp" "src/filesystem/open_file_descriptor.tests.cpp" "src/filesystem/open_file_table.tests.cpp" + "src/filesystem/type_registry.tests.cpp" "src/filesystem/vfs.tests.cpp" # Storage Subsystem Tests diff --git a/kernel/include/kernel/filesystem/type_registry.hpp b/kernel/include/kernel/filesystem/type_registry.hpp index 2f5e708..3be7295 100644 --- a/kernel/include/kernel/filesystem/type_registry.hpp +++ b/kernel/include/kernel/filesystem/type_registry.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -35,7 +36,12 @@ namespace kernel::filesystem //! Get all currently registered type descriptors. //! //! @return A span containing all currently registered filesystem type descriptors. - [[nodiscard]] auto all() const noexcept -> std::span; + [[nodiscard]] auto all() const noexcept -> std::span; + + //! Get the number of registered filesystem types. + //! + //! @return The number of filesystem descriptors currently registered with this registry. + [[nodiscard]] auto size() const noexcept -> std::size_t; private: //! A map from filesystem names (identifiers) to filesystem type descriptors. diff --git a/kernel/src/filesystem/type_registry.cpp b/kernel/src/filesystem/type_registry.cpp index 033b879..d917c81 100644 --- a/kernel/src/filesystem/type_registry.cpp +++ b/kernel/src/filesystem/type_registry.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -58,10 +59,14 @@ namespace kernel::filesystem return result.second; } - [[nodiscard]] auto type_registry::all() const noexcept -> std::span + auto type_registry::all() const noexcept -> std::span { - // TODO: implement value accessor for flat_map - return {}; + return {m_descriptors.values().begin(), m_descriptors.values().end()}; + } + + auto type_registry::size() const noexcept -> std::size_t + { + return m_descriptors.size(); } } // namespace kernel::filesystem diff --git a/kernel/src/filesystem/type_registry.tests.cpp b/kernel/src/filesystem/type_registry.tests.cpp new file mode 100644 index 0000000..8382579 --- /dev/null +++ b/kernel/src/filesystem/type_registry.tests.cpp @@ -0,0 +1,77 @@ +#include + +#include +#include + +#include + +#include + +#include +#include + +struct test_type final : kernel::filesystem::type +{ + [[nodiscard]] auto name() const noexcept -> std::string_view override + { + return "bht_testfs"; + } + + [[nodiscard]] auto requires_device() const noexcept -> bool override + { + return false; + } + + [[nodiscard]] auto make_instance() const -> kstd::shared_ptr override + { + return nullptr; + } +}; + +SCENARIO("Filesystem type registry initialization and construction", "[filesystem]") +{ + GIVEN("A default constructed type_registry") + { + auto instance = kernel::filesystem::type_registry{}; + + WHEN("getting the span of filesystem descriptors") + { + auto descriptors = instance.all(); + + THEN("the span is empty") + { + REQUIRE(descriptors.empty()); + } + } + } +} + +SCENARIO("Filesystem type registry modifiers", "[filesystem]") +{ + GIVEN("A default constructed type_registry") + { + auto instance = kernel::filesystem::type_registry{}; + + WHEN("adding a type descriptor") + { + auto descriptor = test_type{}; + + instance.add(kstd::make_observer(&descriptor)); + + THEN("the size of the registry is one") + { + REQUIRE(instance.size() == 1); + } + + THEN("the span is not empty") + { + REQUIRE_FALSE(instance.all().empty()); + } + + THEN("the span's size is equal to the registry size") + { + REQUIRE(std::size(instance.all()) == std::size(instance)); + } + } + } +} diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp index 8d1fd23..e5dff8c 100644 --- a/kernel/src/filesystem/vfs.cpp +++ b/kernel/src/filesystem/vfs.cpp @@ -12,7 +12,6 @@ #include #include -#include #include #include diff --git a/libs/kstd/kstd/flat_map b/libs/kstd/kstd/flat_map index f12b1b5..943e9fc 100644 --- a/libs/kstd/kstd/flat_map +++ b/libs/kstd/kstd/flat_map @@ -356,6 +356,22 @@ namespace kstd return find(key) != cend(); } + //! Get a reference to the keys container. + //! + //! @return a reference to the keys container. + [[nodiscard]] constexpr auto keys() const noexcept -> key_container_type const & + { + return m_containers.keys; + } + + //! Get a reference to the values container. + //! + //! @return a reference to the values container. + [[nodiscard]] constexpr auto values() const noexcept -> mapped_container_type const & + { + return m_containers.values; + } + private: containers m_containers; key_compare m_comparator; diff --git a/libs/kstd/kstd/flat_map.test.cpp b/libs/kstd/kstd/flat_map.test.cpp index 2e5a47c..6c57173 100644 --- a/libs/kstd/kstd/flat_map.test.cpp +++ b/libs/kstd/kstd/flat_map.test.cpp @@ -20,6 +20,16 @@ SCENARIO("Flat Map initialization and construction", "[flat_map]") { REQUIRE_FALSE(map.contains(1)); } + + THEN("the keys container is empty") + { + REQUIRE(map.keys().empty()); + } + + THEN("the values container is empty") + { + REQUIRE(map.values().empty()); + } } } } -- cgit v1.2.3 From 46d3f8978e9f4235064daf5f19de5bf3054e7c24 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Tue, 2 Jun 2026 16:38:30 +0200 Subject: acpi: fix two lint issues --- libs/acpi/acpi/common/basic_table.hpp | 5 +++++ libs/acpi/acpi/common/vla_table.hpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/libs/acpi/acpi/common/basic_table.hpp b/libs/acpi/acpi/common/basic_table.hpp index 33f23d5..f5b5b27 100644 --- a/libs/acpi/acpi/common/basic_table.hpp +++ b/libs/acpi/acpi/common/basic_table.hpp @@ -28,6 +28,11 @@ namespace acpi { return signature() == table_signature_v && validate_checksum(); } + + private: + friend TableType; + + constexpr basic_table() noexcept = default; }; } // namespace acpi diff --git a/libs/acpi/acpi/common/vla_table.hpp b/libs/acpi/acpi/common/vla_table.hpp index a65a28e..d3f33a7 100644 --- a/libs/acpi/acpi/common/vla_table.hpp +++ b/libs/acpi/acpi/common/vla_table.hpp @@ -109,6 +109,11 @@ namespace acpi { return end(); } + + private: + friend TableType; + + constexpr vla_table() noexcept = default; }; } // namespace acpi -- cgit v1.2.3 From 0a772bdb97ea7eccc7bdf079e7bc09e5f27c1718 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Wed, 3 Jun 2026 05:51:34 +0200 Subject: kstd: implement flat_map copy assignment --- libs/kstd/kstd/flat_map | 25 +++++++++++++++++++++++++ libs/kstd/kstd/flat_map.test.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/libs/kstd/kstd/flat_map b/libs/kstd/kstd/flat_map index 943e9fc..e51eb91 100644 --- a/libs/kstd/kstd/flat_map +++ b/libs/kstd/kstd/flat_map @@ -19,12 +19,24 @@ namespace kstd typename KeyContainerType = kstd::vector, typename MappedContainerType = kstd::vector> struct flat_map { + //! The type of container used to store the map keys. using key_container_type = KeyContainerType; + + //! The type of container used to store the map values. using mapped_container_type = MappedContainerType; + + //! The type of the map keys. using key_type = KeyType; + + //! The type of the mappe values. using mapped_type = MappedType; + + //! The type of a single key-value value in the map. using value_type = std::pair; + + //! The comparator used to sort the keys. using key_compare = KeyCompare; + using reference = std::pair; using const_reference = std::pair; using size_type = std::size_t; @@ -64,6 +76,19 @@ namespace kstd , m_comparator{comparator} {} + //! Replace the contents of this flat map with the one of a different one. + //! + //! @param other the flat map to copy from. + //! @return A reference to this flat map. + auto operator=(flat_map const & other) -> flat_map & + { + if (this != &other) + { + std::tie(m_containers, m_comparator) = std::tie(other.m_containers, other.m_comparator); + } + return *this; + } + //! Get a reference to the mapped value associated with the given key. //! //! @warning This function will panic if the key is not found. diff --git a/libs/kstd/kstd/flat_map.test.cpp b/libs/kstd/kstd/flat_map.test.cpp index 6c57173..9df03bb 100644 --- a/libs/kstd/kstd/flat_map.test.cpp +++ b/libs/kstd/kstd/flat_map.test.cpp @@ -62,6 +62,33 @@ SCENARIO("Flat Map modifiers", "[flat_map]") REQUIRE(map.contains(1)); } } + + AND_GIVEN("a populated Flat Map") + { + auto other = kstd::flat_map{}; + other.emplace(1, 10); + other.emplace(2, 20); + other.emplace(3, 30); + + WHEN("assigning the populated Flat Map to the empty one") + { + map = other; + + THEN("the elements are copied") + { + REQUIRE(map.at(1) == 10); + REQUIRE(map.at(2) == 20); + REQUIRE(map.at(3) == 30); + } + + THEN("the elements are still in the populated Flat Map") + { + REQUIRE(other.at(1) == 10); + REQUIRE(other.at(2) == 20); + REQUIRE(other.at(3) == 30); + } + } + } } } -- cgit v1.2.3 From 66ea58d7c5b41475e0be61172966613fbd14d43b Mon Sep 17 00:00:00 2001 From: Lukas Oesch Date: Sun, 7 Jun 2026 18:54:49 +0200 Subject: small refactoring --- kernel/include/kernel/filesystem/mount.hpp | 6 +++--- kernel/src/filesystem/mount_table.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp index 53d7f6d..ced4f81 100644 --- a/kernel/include/kernel/filesystem/mount.hpp +++ b/kernel/include/kernel/filesystem/mount.hpp @@ -85,12 +85,12 @@ namespace kernel::filesystem [[nodiscard]] auto ref_count() const -> size_t; private: - kstd::shared_ptr m_mount_dentry; - kstd::shared_ptr m_root_dentry; + kstd::shared_ptr m_mount_dentry{}; + kstd::shared_ptr m_root_dentry{}; kstd::shared_ptr m_filesystem{}; kstd::shared_ptr m_parent_mount{}; kstd::weak_ptr m_source_mount{}; - std::atomic_size_t m_ref_count; + std::atomic_size_t m_ref_count{0}; }; } // namespace kernel::filesystem diff --git a/kernel/src/filesystem/mount_table.cpp b/kernel/src/filesystem/mount_table.cpp index af8434b..e4baac7 100644 --- a/kernel/src/filesystem/mount_table.cpp +++ b/kernel/src/filesystem/mount_table.cpp @@ -18,7 +18,7 @@ namespace kernel::filesystem [&parent_mount](auto const & mount) { return mount->parent_mount() == parent_mount; }); } - void mount_table::add_mount(kstd::shared_ptr const & mount) + auto mount_table::add_mount(kstd::shared_ptr const & mount) -> void { m_mounts.push_back(mount); -- cgit v1.2.3