aboutsummaryrefslogtreecommitdiff
path: root/kernel/kapi
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/kapi')
-rw-r--r--kernel/kapi/acpi.cpp40
-rw-r--r--kernel/kapi/boot_modules.cpp41
-rw-r--r--kernel/kapi/cio.cpp2
-rw-r--r--kernel/kapi/cpu.cpp35
-rw-r--r--kernel/kapi/cpu.tests.cpp19
-rw-r--r--kernel/kapi/devices.cpp86
-rw-r--r--kernel/kapi/devices/bus.cpp72
-rw-r--r--kernel/kapi/devices/cpu.cpp31
-rw-r--r--kernel/kapi/devices/device.cpp43
-rw-r--r--kernel/kapi/filesystem.cpp76
-rw-r--r--kernel/kapi/filesystem.tests.cpp199
-rw-r--r--kernel/kapi/interrupts.cpp65
-rw-r--r--kernel/kapi/memory.cpp60
-rw-r--r--kernel/kapi/system.cpp4
-rw-r--r--kernel/kapi/system.tests.cpp30
15 files changed, 792 insertions, 11 deletions
diff --git a/kernel/kapi/acpi.cpp b/kernel/kapi/acpi.cpp
new file mode 100644
index 0000000..b6d5cdf
--- /dev/null
+++ b/kernel/kapi/acpi.cpp
@@ -0,0 +1,40 @@
+#include <kapi/acpi.hpp>
+
+#include <kernel/acpi/manager.hpp>
+
+#include <kapi/system.hpp>
+
+#include <acpi/acpi.hpp>
+
+#include <kstd/memory>
+
+#include <atomic>
+#include <optional>
+#include <string_view>
+
+namespace kapi::acpi
+{
+
+ namespace
+ {
+ auto constinit manager = std::optional<kernel::acpi::manager>{};
+ } // namespace
+
+ auto init(::acpi::rsdp 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 get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::table_header const>
+ {
+ return manager->get_table(signature);
+ }
+
+}; // namespace kapi::acpi
diff --git a/kernel/kapi/boot_modules.cpp b/kernel/kapi/boot_modules.cpp
new file mode 100644
index 0000000..5629b77
--- /dev/null
+++ b/kernel/kapi/boot_modules.cpp
@@ -0,0 +1,41 @@
+#include <kapi/boot_modules.hpp>
+
+#include <kapi/system.hpp>
+
+#include <optional>
+
+namespace
+{
+ constinit auto static registry = std::optional<kapi::boot_modules::boot_module_registry>{};
+} // namespace
+
+namespace kapi::boot_modules
+{
+ auto set_boot_module_registry(boot_module_registry & new_registry) -> void
+ {
+ if (registry)
+ {
+ system::panic("[x86_64] Boot module registry has already been set.");
+ }
+
+ registry = new_registry;
+ }
+
+ auto get_boot_module_registry() -> boot_module_registry const &
+ {
+ if (!registry)
+ {
+ system::panic("[x86_64] Boot module registry has not been initialized.");
+ }
+
+ 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/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 <kapi/cio.hpp>
#include <optional>
#include <string_view>
diff --git a/kernel/kapi/cpu.cpp b/kernel/kapi/cpu.cpp
new file mode 100644
index 0000000..7b1a43b
--- /dev/null
+++ b/kernel/kapi/cpu.cpp
@@ -0,0 +1,35 @@
+#include <kapi/cpu.hpp>
+
+#include <kapi/system.hpp>
+
+#include <kstd/print>
+
+namespace kapi::cpu
+{
+
+ namespace
+ {
+ 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.");
+ }
+ } // namespace
+
+ auto dispatch(exception const & context) -> bool
+ {
+ 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/kapi/cpu.tests.cpp b/kernel/kapi/cpu.tests.cpp
new file mode 100644
index 0000000..9ce2917
--- /dev/null
+++ b/kernel/kapi/cpu.tests.cpp
@@ -0,0 +1,19 @@
+#include <kapi/cpu.hpp>
+
+#include <kernel/test_support/cpu.hpp>
+
+#include <catch2/catch_test_macros.hpp>
+
+SCENARIO("Kernel testing kapi::cpu shims", "[support]")
+{
+ GIVEN("the test support infrastructure is initialized")
+ {
+ WHEN("a CPU halt is requested")
+ {
+ THEN("the correct exception is thrown")
+ {
+ REQUIRE_THROWS_AS(kapi::cpu::halt(), kernel::tests::cpu::halt);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/kernel/kapi/devices.cpp b/kernel/kapi/devices.cpp
new file mode 100644
index 0000000..572227e
--- /dev/null
+++ b/kernel/kapi/devices.cpp
@@ -0,0 +1,86 @@
+#include <kapi/devices.hpp>
+
+#include <kernel/devices/root_bus.hpp>
+
+#include <kapi/system.hpp>
+
+#include <kstd/flat_map>
+#include <kstd/memory>
+#include <kstd/print>
+
+#include <atomic>
+#include <cstddef>
+#include <optional>
+#include <string_view>
+#include <utility>
+
+namespace kapi::devices
+{
+ namespace
+ {
+ auto constinit next_major_number = std::atomic_size_t{1};
+ auto constinit root_bus = std::optional<kernel::devices::root_bus>{};
+ auto constinit device_tree = kstd::flat_map<std::pair<std::size_t, std::size_t>, kstd::observer_ptr<device>>{};
+ } // namespace
+
+ auto init() -> void
+ {
+ auto static is_initialized = std::atomic_flag{};
+ if (is_initialized.test_and_set())
+ {
+ return;
+ }
+
+ auto & bus = root_bus.emplace();
+ register_device(bus);
+ bus.init();
+ }
+
+ auto get_root_bus() -> bus &
+ {
+ if (!root_bus.has_value())
+ {
+ kapi::system::panic("[OS:DEV] Root bus not initialized!");
+ }
+ return *root_bus;
+ }
+
+ auto allocate_major_number() -> std::size_t
+ {
+ return next_major_number++;
+ }
+
+ 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;
+ }
+
+ auto unregister_device(device &) -> bool
+ {
+ kstd::println("[OS:DEV] TODO: implement device deregistration");
+ return false;
+ }
+
+ auto find_device(std::size_t major, std::size_t minor) -> kstd::observer_ptr<device>
+ {
+ 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<device>
+ {
+ 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/kernel/kapi/devices/bus.cpp b/kernel/kapi/devices/bus.cpp
new file mode 100644
index 0000000..59753f7
--- /dev/null
+++ b/kernel/kapi/devices/bus.cpp
@@ -0,0 +1,72 @@
+#include <kapi/devices/bus.hpp>
+
+#include <kapi/devices.hpp>
+#include <kapi/system.hpp>
+
+#include <kstd/memory>
+#include <kstd/print>
+#include <kstd/string>
+#include <kstd/vector>
+
+#include <algorithm>
+#include <cstddef>
+#include <utility>
+
+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_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;
+ }
+
+ 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<device> 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);
+
+ 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<kstd::observer_ptr<device>> 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/cpu.cpp b/kernel/kapi/devices/cpu.cpp
new file mode 100644
index 0000000..f0e1d72
--- /dev/null
+++ b/kernel/kapi/devices/cpu.cpp
@@ -0,0 +1,31 @@
+#include <kapi/devices/cpu.hpp>
+
+#include <kapi/devices.hpp>
+
+#include <cstddef>
+#include <cstdint>
+
+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/kapi/devices/device.cpp b/kernel/kapi/devices/device.cpp
new file mode 100644
index 0000000..8b5d6b9
--- /dev/null
+++ b/kernel/kapi/devices/device.cpp
@@ -0,0 +1,43 @@
+#include <kapi/devices/device.hpp>
+
+#include <kapi/devices/bus.hpp>
+
+#include <kstd/memory>
+#include <kstd/string>
+
+#include <cstddef>
+
+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;
+ }
+
+ auto device::set_parent(kstd::observer_ptr<bus> parent) -> void
+ {
+ m_parent = parent;
+ }
+
+} // namespace kapi::devices \ No newline at end of file
diff --git a/kernel/kapi/filesystem.cpp b/kernel/kapi/filesystem.cpp
new file mode 100644
index 0000000..68b51c9
--- /dev/null
+++ b/kernel/kapi/filesystem.cpp
@@ -0,0 +1,76 @@
+#include <kapi/filesystem.hpp>
+
+#include <kernel/filesystem/open_file_descriptor.hpp>
+#include <kernel/filesystem/open_file_table.hpp>
+#include <kernel/filesystem/vfs.hpp>
+
+#include <kstd/memory>
+#include <kstd/unikstd.h>
+
+#include <cstddef>
+#include <string_view>
+
+namespace kapi::filesystem
+{
+ 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)
+ {
+ return 0;
+ }
+ return -1;
+ }
+
+ auto umount(std::string_view target) -> kstd::ssize_t
+ {
+ if (kernel::filesystem::vfs::get().unmount(target) == kernel::filesystem::vfs::operation_result::success)
+ {
+ return 0;
+ }
+ return -1;
+ }
+
+ auto open(std::string_view path) -> kstd::ssize_t
+ {
+ if (auto dentry = kernel::filesystem::vfs::get().open(path))
+ {
+ auto open_file_descriptor = kstd::make_shared<kernel::filesystem::open_file_descriptor>(dentry);
+ return kernel::filesystem::open_file_table::get().add_file(open_file_descriptor);
+ }
+
+ return -1;
+ }
+
+ auto close(size_t file_descriptor) -> kstd::ssize_t
+ {
+ 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()) ==
+ kernel::filesystem::vfs::operation_result::success)
+ {
+ return kernel::filesystem::open_file_table::get().remove_file(file_descriptor);
+ }
+ }
+ return -1;
+ }
+
+ 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))
+ {
+ return open_file_descriptor->read(buffer, size);
+ }
+
+ return -1;
+ }
+
+ 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))
+ {
+ return open_file_descriptor->write(buffer, size);
+ }
+
+ return -1;
+ }
+} // namespace kapi::filesystem \ No newline at end of file
diff --git a/kernel/kapi/filesystem.tests.cpp b/kernel/kapi/filesystem.tests.cpp
new file mode 100644
index 0000000..d241afa
--- /dev/null
+++ b/kernel/kapi/filesystem.tests.cpp
@@ -0,0 +1,199 @@
+#include <kapi/filesystem.hpp>
+
+#include <kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp>
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <algorithm>
+#include <cstddef>
+#include <filesystem>
+#include <string_view>
+#include <vector>
+
+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("Two real image files")
+ {
+ 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<std::byte>(6);
+ auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size());
+ REQUIRE(bytes_read >= 0);
+
+ std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), static_cast<size_t>(bytes_read)};
+ REQUIRE(buffer_as_str == "info_1");
+
+ 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<std::byte>(6);
+ auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size());
+ REQUIRE(bytes_read >= 0);
+
+ std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), static_cast<size_t>(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<std::byte>(6);
+ auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size());
+ REQUIRE(bytes_read >= 0);
+
+ std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), static_cast<size_t>(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<std::byte>(7);
+ auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size());
+ REQUIRE(bytes_read >= 0);
+
+ std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), static_cast<size_t>(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);
+
+ auto fd = kapi::filesystem::open("/information/monkey_house/monkey_1.txt");
+ REQUIRE(fd >= 0);
+
+ auto buffer = std::vector<std::byte>(8);
+ auto bytes_read = kapi::filesystem::read(fd, buffer.data(), buffer.size());
+ REQUIRE(bytes_read >= 0);
+
+ std::string_view buffer_as_str{reinterpret_cast<char *>(buffer.data()), static_cast<size_t>(bytes_read)};
+ REQUIRE(buffer_as_str == "monkey_1");
+
+ REQUIRE(kapi::filesystem::close(fd) == 0);
+ 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");
+ REQUIRE(fd >= 0);
+
+ auto buffer = std::vector<std::byte>(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<std::byte>(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<std::byte>(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("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<std::byte> buffer(10);
+ 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<std::byte> buffer(10);
+ 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/kapi/interrupts.cpp b/kernel/kapi/interrupts.cpp
new file mode 100644
index 0000000..4efcaa3
--- /dev/null
+++ b/kernel/kapi/interrupts.cpp
@@ -0,0 +1,65 @@
+#include <kapi/interrupts.hpp>
+
+#include <kstd/flat_map>
+#include <kstd/print>
+#include <kstd/vector>
+
+#include <algorithm>
+#include <cstdint>
+
+namespace kapi::interrupts
+{
+
+ namespace
+ {
+ auto constinit handlers = kstd::flat_map<std::uint32_t, kstd::vector<handler *>>{};
+ } // namespace
+
+ auto register_handler(std::uint32_t irq_number, handler & handler) -> void
+ {
+ 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
+ {
+ 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
+ {
+ 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())
+ {
+ 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)
+ {
+ return status::handled;
+ }
+ }
+
+ return status::unhandled;
+ }
+
+} // namespace kapi::interrupts \ No newline at end of file
diff --git a/kernel/kapi/memory.cpp b/kernel/kapi/memory.cpp
index d8065d4..5ea08b1 100644
--- a/kernel/kapi/memory.cpp
+++ b/kernel/kapi/memory.cpp
@@ -1,10 +1,12 @@
-#include "kapi/memory.hpp"
+#include <kapi/memory.hpp>
-#include "kapi/system.hpp"
+#include <kernel/memory/bitmap_allocator.hpp>
+#include <kernel/memory/mmio_allocator.hpp>
-#include "kernel/memory/bitmap_allocator.hpp"
+#include <kapi/system.hpp>
#include <kstd/print>
+#include <kstd/units>
#include <algorithm>
#include <cstddef>
@@ -62,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<kernel::memory::bitmap_frame_allocator>{};
+ auto constinit mmio_allocator = std::optional<kernel::memory::mmio_allocator>{};
} // namespace
constinit auto static active_frame_allocator = static_cast<frame_allocator *>(&bad_frame_allocator::instance);
@@ -112,8 +115,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)
@@ -122,15 +127,20 @@ namespace kapi::memory
}
auto const flags = page_mapper::flags::writable | page_mapper::flags::supervisor_only | page_mapper::flags::global;
+ auto bitmap_ptr = static_cast<std::uint64_t *>(nullptr);
std::ranges::for_each(std::views::iota(0uz, bitmap_pages), [&](auto index) {
auto page = page::containing(pmm_metadata_base + index * page::size);
auto frame = memory::frame(bitmap_frames->first.number() + index);
- active_page_mapper->map(page, frame, flags);
+ auto mapped = active_page_mapper->map(page, frame, flags);
+ if (!bitmap_ptr)
+ {
+ bitmap_ptr = reinterpret_cast<std::uint64_t *>(mapped);
+ }
});
- auto bitmap_ptr = static_cast<std::uint64_t *>(pmm_metadata_base);
- auto bitmap = std::span{bitmap_ptr, (bitmap_bytes + sizeof(std::uint64_t) - 1uz) / sizeof(std::uint64_t)};
+ auto bitmap =
+ std::span{bitmap_ptr, (bitmap_bytes + kstd::type_size<std::uint64_t> - 1_B) / kstd::type_size<std::uint64_t>};
allocator.emplace(bitmap, frame_count);
@@ -139,4 +149,38 @@ 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) -> mmio_region
+ {
+ auto region = mmio_allocator->allocate(page_count);
+ return {region, page_count};
+ }
+
+ auto map_mmio_region(mmio_region region, physical_address hw_base, page_mapper::flags flags) -> std::byte *
+ {
+ auto start_page = page::containing(region.first);
+ auto start_frame = frame::containing(hw_base);
+
+ 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(mmio_region region) -> void
+ {
+ mmio_allocator->release(region.first);
+ }
+
} // namespace kapi::memory
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 <kapi/system.hpp>
-#include "kapi/cpu.hpp"
+#include <kapi/cpu.hpp>
#include <kstd/print>
diff --git a/kernel/kapi/system.tests.cpp b/kernel/kapi/system.tests.cpp
new file mode 100644
index 0000000..1e30031
--- /dev/null
+++ b/kernel/kapi/system.tests.cpp
@@ -0,0 +1,30 @@
+#include <kapi/system.hpp>
+
+#include <kernel/test_support/cio.hpp>
+#include <kernel/test_support/cpu.hpp>
+
+#include <kstd/print>
+
+#include <catch2/catch_test_macros.hpp>
+
+SCENARIO("Kernel testing kapi::system shims", "[support]")
+{
+ GIVEN("the test support infrastructure is initialized")
+ {
+ WHEN("a the system panics")
+ {
+ kernel::tests::cio::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::cio::log_buffer().flat_messages().contains("[kernel:tests] Test Panic"));
+ }
+ }
+ }
+} \ No newline at end of file