diff options
| -rw-r--r-- | kernel/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/device_number.hpp | 23 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/device_number_registry.cpp | 121 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/device_number_registry.hpp | 64 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/device_number_registry.tests.cpp | 90 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/file_type.hpp | 18 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/file_type_bindings.hpp | 32 | ||||
| -rw-r--r-- | kernel/kernel/filesystem/reserved_numbers.hpp | 21 | ||||
| -rw-r--r-- | kernel/kernel/test_support/filesystem/device_number_registry.hpp | 9 | ||||
| -rw-r--r-- | kernel/kernel/test_support/state_reset_listener.cpp | 2 |
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(); |
