aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-07-17 19:08:34 +0200
committerFelix Morgner <felix.morgner@ost.ch>2026-07-17 19:08:34 +0200
commitf04a10d70c6a3bd7b6b6e2b1760c30dd3f3754de (patch)
treeb641979dfdfd2f994365f9059514f67631474470 /kernel
parent14685093004d767669fcfecaad875f7ab2aae76d (diff)
downloadkernel-f04a10d70c6a3bd7b6b6e2b1760c30dd3f3754de.tar.xz
kernel-f04a10d70c6a3bd7b6b6e2b1760c30dd3f3754de.zip
kernel: introduce device_number abstraction
Diffstat (limited to 'kernel')
-rw-r--r--kernel/CMakeLists.txt1
-rw-r--r--kernel/kernel/filesystem/device_number.hpp23
-rw-r--r--kernel/kernel/filesystem/device_number_registry.cpp121
-rw-r--r--kernel/kernel/filesystem/device_number_registry.hpp64
-rw-r--r--kernel/kernel/filesystem/device_number_registry.tests.cpp90
-rw-r--r--kernel/kernel/filesystem/file_type.hpp18
-rw-r--r--kernel/kernel/filesystem/file_type_bindings.hpp32
-rw-r--r--kernel/kernel/filesystem/reserved_numbers.hpp21
-rw-r--r--kernel/kernel/test_support/filesystem/device_number_registry.hpp9
-rw-r--r--kernel/kernel/test_support/state_reset_listener.cpp2
10 files changed, 381 insertions, 0 deletions
diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt
index 7ada841d..47c06ea9 100644
--- a/kernel/CMakeLists.txt
+++ b/kernel/CMakeLists.txt
@@ -47,6 +47,7 @@ target_sources("kernel_lib" PRIVATE
# Filesystem Subsystem
"kernel/filesystem/dentry.cpp"
"kernel/filesystem/device_inode.cpp"
+ "kernel/filesystem/device_number_registry.cpp"
"kernel/filesystem/error.cpp"
"kernel/filesystem/filesystem.cpp"
"kernel/filesystem/inode.cpp"
diff --git a/kernel/kernel/filesystem/device_number.hpp b/kernel/kernel/filesystem/device_number.hpp
new file mode 100644
index 00000000..0c158eca
--- /dev/null
+++ b/kernel/kernel/filesystem/device_number.hpp
@@ -0,0 +1,23 @@
+#ifndef TEACHOS_KERNEL_FILESYSTEM_DEVICE_NUMBER_HPP
+#define TEACHOS_KERNEL_FILESYSTEM_DEVICE_NUMBER_HPP
+
+#include <compare>
+#include <cstdint>
+
+namespace kernel::filesystem
+{
+
+ //! A POSIX device number.
+ struct device_number
+ {
+ //! The major number, identifying the device class or driver.
+ std::uint8_t major;
+ //! The minor number, identifying the device instance.
+ std::uint8_t minor;
+
+ constexpr auto friend operator<=>(device_number, device_number) noexcept -> std::strong_ordering = default;
+ };
+
+} // namespace kernel::filesystem
+
+#endif
diff --git a/kernel/kernel/filesystem/device_number_registry.cpp b/kernel/kernel/filesystem/device_number_registry.cpp
new file mode 100644
index 00000000..41520b50
--- /dev/null
+++ b/kernel/kernel/filesystem/device_number_registry.cpp
@@ -0,0 +1,121 @@
+#include <kernel/filesystem/device_number_registry.hpp>
+
+#include <kernel/filesystem/device_number.hpp>
+#include <kernel/filesystem/error.hpp>
+#include <kernel/filesystem/file_type.hpp>
+#include <kernel/filesystem/file_type_bindings.hpp>
+
+#include <kapi/devices.hpp>
+
+#include <kstd/memory.hpp>
+#include <kstd/result.hpp>
+#include <kstd/vector.hpp>
+
+#include <algorithm>
+#include <optional>
+#include <ranges>
+
+namespace
+{
+ constinit auto static instance = std::optional<kernel::filesystem::device_number_registry>{};
+}
+
+namespace kernel::filesystem
+{
+
+ auto device_number_registry::get() -> device_number_registry &
+ {
+ if (!instance)
+ {
+ instance.emplace(kapi::devices::interface_registry::get());
+ }
+ return *instance;
+ }
+
+ device_number_registry::device_number_registry(kapi::devices::interface_registry & interface_registry)
+ : m_interface_registry{&interface_registry}
+ {}
+
+ auto device_number_registry::resolve(device_number number, file_type type) const
+ -> kstd::result<kstd::shared_ptr<kapi::devices::device>>
+ {
+ populate();
+
+ auto found =
+ std::ranges::find_if(m_entries, [&](auto entry) { return entry.number == number && entry.type == type; });
+
+ if (found == std::ranges::cend(m_entries))
+ {
+ return kstd::failure(vfs_errc::no_such_device);
+ }
+
+ if (auto device = found->device.lock())
+ {
+ return device;
+ }
+
+ return kstd::failure(vfs_errc::no_such_device);
+ }
+
+ auto device_number_registry::number_of(kapi::devices::device const & device) const -> kstd::result<device_number>
+ {
+ populate();
+
+ auto found = std::ranges::find_if(m_entries, [&](auto entry) {
+ auto locked = entry.device.lock();
+ return locked.get() == &device;
+ });
+
+ if (found == std::ranges::cend(m_entries))
+ {
+ return kstd::failure(vfs_errc::no_such_device);
+ }
+
+ return found->number;
+ }
+
+ auto device_number_registry::all() const -> kstd::vector<entry>
+ {
+ populate();
+
+ return m_entries;
+ }
+
+ auto device_number_registry::populate() const -> void
+ {
+ erase_if(m_entries, [](auto entry) { return entry.device.expired(); });
+
+ if (m_populated)
+ {
+ return;
+ }
+
+ for (auto const & binding : file_type_bindings)
+ {
+ auto next_minor = decltype(device_number::minor){};
+
+ for (auto const & published : m_interface_registry->all(binding.interface))
+ {
+ if (auto device = published.device().lock())
+ {
+ m_entries.emplace_back(device_number{.major = binding.major, .minor = next_minor}, //
+ binding.posix_type, //
+ published.name(), //
+ published.device());
+ ++next_minor;
+ }
+ }
+ }
+
+ m_populated = true;
+ }
+
+} // namespace kernel::filesystem
+
+namespace kernel::tests::filesystem::device_number_registry
+{
+ auto deinit() -> void
+ {
+ instance.reset();
+ }
+} // namespace kernel::tests::filesystem::device_number_registry
diff --git a/kernel/kernel/filesystem/device_number_registry.hpp b/kernel/kernel/filesystem/device_number_registry.hpp
new file mode 100644
index 00000000..02a7918f
--- /dev/null
+++ b/kernel/kernel/filesystem/device_number_registry.hpp
@@ -0,0 +1,64 @@
+#ifndef TEACHOS_KERNEL_FILESYSTEM_DEVICE_NUMBER_REGISTRY_HPP
+#define TEACHOS_KERNEL_FILESYSTEM_DEVICE_NUMBER_REGISTRY_HPP
+
+#include <kernel/filesystem/device_number.hpp>
+#include <kernel/filesystem/file_type.hpp>
+
+#include <kapi/devices.hpp>
+
+#include <kstd/memory.hpp>
+#include <kstd/result.hpp>
+#include <kstd/string.hpp>
+#include <kstd/vector.hpp>
+
+namespace kernel::filesystem
+{
+
+ struct device_number_registry
+ {
+ //! A single numbered device.
+ //!
+ //! Theses entries are used, for example, in devfs to provide access to devices.
+ struct entry
+ {
+ device_number number;
+ file_type type;
+ kstd::string name;
+ kstd::weak_ptr<kapi::devices::device> device;
+ };
+
+ explicit device_number_registry(kapi::devices::interface_registry & interface_registry);
+
+ //! Get the system global instance of the registry.
+ [[nodiscard]] auto static get() -> device_number_registry &;
+
+ //! Resolve a device number and file type to a device.
+ //!
+ //! @param number The device number.
+ //! @param type The expected file type of the device.
+ //! @return A device if a fitting entry is found, an error otherwise.
+ [[nodiscard]] auto resolve(device_number number, file_type type) const
+ -> kstd::result<kstd::shared_ptr<kapi::devices::device>>;
+
+ //! Resolve a device to it's device number.
+ //!
+ //! @param device The device to find the number for.
+ //! @return The device number associated with the given device, or an error otherwise.
+ [[nodiscard]] auto number_of(kapi::devices::device const & device) const -> kstd::result<device_number>;
+
+ //! Get all currently registered devices.
+ //!
+ //! @return A range containing all currently registered devices numbers mappings.
+ [[nodiscard]] auto all() const -> kstd::vector<entry>;
+
+ private:
+ auto populate() const -> void;
+
+ kstd::observer_ptr<kapi::devices::interface_registry> m_interface_registry;
+ mutable bool m_populated{};
+ mutable kstd::vector<entry> m_entries{};
+ };
+
+} // namespace kernel::filesystem
+
+#endif
diff --git a/kernel/kernel/filesystem/device_number_registry.tests.cpp b/kernel/kernel/filesystem/device_number_registry.tests.cpp
new file mode 100644
index 00000000..7090860e
--- /dev/null
+++ b/kernel/kernel/filesystem/device_number_registry.tests.cpp
@@ -0,0 +1,90 @@
+#include <kernel/filesystem/device_number_registry.hpp>
+
+#include <kernel/filesystem/file_type.hpp>
+#include <kernel/test_support/devices/block_device.hpp>
+
+#include <kapi/devices.hpp>
+
+#include <kstd/memory.hpp>
+#include <kstd/units.hpp>
+
+#include <catch2/catch_test_macros.hpp>
+
+using namespace kstd::units_literals;
+
+SCENARIO("Device number registry resolves and numbers published block devices",
+ "[kernel][filesystem][device_number_registry]")
+{
+ auto interfaces = kapi::devices::interface_registry{};
+ auto registry = kernel::filesystem::device_number_registry{interfaces};
+
+ GIVEN("No devices have been published")
+ {
+ THEN("all() returns an empty vector")
+ {
+ REQUIRE(registry.all().empty());
+ }
+ }
+
+ GIVEN("A single block device has been published")
+ {
+ auto device = kstd::make_shared<kernel::tests::devices::block_device>(0, 0, "ram0", 512_B);
+ CHECK(interfaces.publish<kapi::devices::block_device>(device, "ram0"));
+
+ THEN("all() returns a vector with one element")
+ {
+ auto all = registry.all();
+ REQUIRE(all.size() == 1);
+
+ AND_THEN("that entry has the published name")
+ {
+ REQUIRE(all[0].name == "ram0");
+ }
+
+ AND_THEN("resolve() finds the device by numer")
+ {
+ REQUIRE(registry.resolve(all[0].number, kernel::filesystem::file_type::block));
+ }
+
+ AND_THEN("number_of() finds the device number")
+ {
+ REQUIRE(registry.number_of(*device));
+ }
+
+ AND_THEN("number_of() is the inverse of resolve()")
+ {
+ auto number = registry.number_of(*device);
+ CHECK(number);
+
+ auto resolved = registry.resolve(*number, kernel::filesystem::file_type::block);
+ CHECK(resolved);
+
+ REQUIRE(resolved == device);
+ }
+ }
+
+ THEN("number_of() fails on a device that has never been published")
+ {
+ auto not_published = kstd::make_shared<kernel::tests::devices::block_device>(0, 0, "ram1", 512_B);
+
+ REQUIRE(!registry.number_of(*not_published));
+ }
+ }
+
+ GIVEN("A device was published")
+ {
+ auto device = kstd::make_shared<kernel::tests::devices::block_device>(0, 0, "ram0", 512_B);
+ CHECK(interfaces.publish<kapi::devices::block_device>(device, "ram0"));
+ auto number = registry.number_of(*device);
+
+ WHEN("destroying it with no further owner")
+ {
+ device.reset();
+
+ THEN("resolve() does not report it anymore")
+ {
+ REQUIRE(!registry.resolve(*number, kernel::filesystem::file_type::block));
+ }
+ }
+ }
+}
diff --git a/kernel/kernel/filesystem/file_type.hpp b/kernel/kernel/filesystem/file_type.hpp
new file mode 100644
index 00000000..b4c3f914
--- /dev/null
+++ b/kernel/kernel/filesystem/file_type.hpp
@@ -0,0 +1,18 @@
+#ifndef TEACHOS_KERNEL_FILESYSTEM_POSIX_FILE_TYPE_HPP
+#define TEACHOS_KERNEL_FILESYSTEM_POSIX_FILE_TYPE_HPP
+
+namespace kernel::filesystem
+{
+
+ //! POSIX file types.
+ enum struct file_type
+ {
+ block,
+ character,
+ pipe,
+ socket,
+ };
+
+} // namespace kernel::filesystem
+
+#endif
diff --git a/kernel/kernel/filesystem/file_type_bindings.hpp b/kernel/kernel/filesystem/file_type_bindings.hpp
new file mode 100644
index 00000000..e33d5086
--- /dev/null
+++ b/kernel/kernel/filesystem/file_type_bindings.hpp
@@ -0,0 +1,32 @@
+#ifndef TEACHOS_KERNEL_FILESYSTEM_FILE_TYPE_BINDINGS_HPP
+#define TEACHOS_KERNEL_FILESYSTEM_FILE_TYPE_BINDINGS_HPP
+
+#include <kernel/filesystem/file_type.hpp>
+#include <kernel/filesystem/reserved_numbers.hpp>
+
+#include <kapi/devices.hpp>
+
+#include <array>
+#include <cstdint>
+
+namespace kernel::filesystem
+{
+
+ //! A binding between an interface, a posix special file type, and a major number.
+ struct file_type_binding // NOLINT(cppcoreguidelines-pro-type-member-init)
+ {
+ kapi::devices::interface interface;
+ file_type posix_type;
+ std::uint8_t major;
+ };
+
+ //! Predefined bindings for interface + file type + major number.
+ //!
+ //! This array associates reserved major numbers with file types and interfaces.
+ constexpr auto inline file_type_bindings = std::array{
+ file_type_binding{kapi::devices::block_device::id, file_type::block, block_major_numbers::ram_disk},
+ };
+
+} // namespace kernel::filesystem
+
+#endif
diff --git a/kernel/kernel/filesystem/reserved_numbers.hpp b/kernel/kernel/filesystem/reserved_numbers.hpp
new file mode 100644
index 00000000..e1d0fdab
--- /dev/null
+++ b/kernel/kernel/filesystem/reserved_numbers.hpp
@@ -0,0 +1,21 @@
+#ifndef TEACHOS_KERNEL_FILESYSTEM_RESERVED_NUMBERS_HPP
+#define TEACHOS_KERNEL_FILESYSTEM_RESERVED_NUMBERS_HPP
+
+#include <cstdint>
+
+//! Constants defining major numbers used in the VFS layer.
+namespace kernel::filesystem
+{
+
+ namespace block_major_numbers
+ {
+ //! Reserved devices.
+ constexpr auto inline internal = std::uint8_t{0};
+
+ //! RAM disk devices.
+ constexpr auto inline ram_disk = std::uint8_t{1};
+ } // namespace block_major_numbers
+
+} // namespace kernel::filesystem
+
+#endif
diff --git a/kernel/kernel/test_support/filesystem/device_number_registry.hpp b/kernel/kernel/test_support/filesystem/device_number_registry.hpp
new file mode 100644
index 00000000..5722837f
--- /dev/null
+++ b/kernel/kernel/test_support/filesystem/device_number_registry.hpp
@@ -0,0 +1,9 @@
+#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_DEVICE_NUMBER_REGISTRY_HPP
+#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_DEVICE_NUMBER_REGISTRY_HPP
+
+namespace kernel::tests::filesystem::device_number_registry
+{
+ auto deinit() -> void;
+}
+
+#endif
diff --git a/kernel/kernel/test_support/state_reset_listener.cpp b/kernel/kernel/test_support/state_reset_listener.cpp
index cbb8ef7c..af886d5e 100644
--- a/kernel/kernel/test_support/state_reset_listener.cpp
+++ b/kernel/kernel/test_support/state_reset_listener.cpp
@@ -3,6 +3,7 @@
#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/device_number_registry.hpp>
#include <kernel/test_support/filesystem/open_file_table.hpp>
#include <kernel/test_support/filesystem/vfs.hpp>
#include <kernel/test_support/memory.hpp>
@@ -34,6 +35,7 @@ struct state_reset_listener : Catch::EventListenerBase
void testCaseEnded(Catch::TestCaseStats const &) override
{
+ kernel::tests::filesystem::device_number_registry::deinit();
kernel::tests::filesystem::open_file_table::deinit();
kernel::tests::filesystem::vfs::deinit();
kernel::tests::boot_modules::deinit();