aboutsummaryrefslogtreecommitdiff
path: root/kernel/include
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/include')
-rw-r--r--kernel/include/kernel/acpi/manager.hpp32
-rw-r--r--kernel/include/kernel/devices/block_device.hpp97
-rw-r--r--kernel/include/kernel/devices/block_device_utils.hpp44
-rw-r--r--kernel/include/kernel/devices/root_bus.hpp16
-rw-r--r--kernel/include/kernel/devices/storage/controller.hpp71
-rw-r--r--kernel/include/kernel/devices/storage/management.hpp78
-rw-r--r--kernel/include/kernel/devices/storage/ram_disk/controller.hpp31
-rw-r--r--kernel/include/kernel/devices/storage/ram_disk/device.hpp58
-rw-r--r--kernel/include/kernel/filesystem/constants.hpp14
-rw-r--r--kernel/include/kernel/filesystem/dentry.hpp104
-rw-r--r--kernel/include/kernel/filesystem/devfs/filesystem.hpp46
-rw-r--r--kernel/include/kernel/filesystem/devfs/inode.hpp42
-rw-r--r--kernel/include/kernel/filesystem/device_inode.hpp64
-rw-r--r--kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp24
-rw-r--r--kernel/include/kernel/filesystem/ext2/filesystem.hpp115
-rw-r--r--kernel/include/kernel/filesystem/ext2/inode.hpp105
-rw-r--r--kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp22
-rw-r--r--kernel/include/kernel/filesystem/ext2/superblock.hpp74
-rw-r--r--kernel/include/kernel/filesystem/filesystem.hpp79
-rw-r--r--kernel/include/kernel/filesystem/inode.hpp69
-rw-r--r--kernel/include/kernel/filesystem/mount.hpp97
-rw-r--r--kernel/include/kernel/filesystem/mount_table.hpp58
-rw-r--r--kernel/include/kernel/filesystem/open_file_descriptor.hpp68
-rw-r--r--kernel/include/kernel/filesystem/open_file_table.hpp66
-rw-r--r--kernel/include/kernel/filesystem/path.hpp71
-rw-r--r--kernel/include/kernel/filesystem/rootfs/filesystem.hpp41
-rw-r--r--kernel/include/kernel/filesystem/rootfs/inode.hpp45
-rw-r--r--kernel/include/kernel/filesystem/type.hpp42
-rw-r--r--kernel/include/kernel/filesystem/type_registry.hpp53
-rw-r--r--kernel/include/kernel/filesystem/vfs.hpp113
-rw-r--r--kernel/include/kernel/memory.hpp4
-rw-r--r--kernel/include/kernel/memory/bitmap_allocator.hpp2
-rw-r--r--kernel/include/kernel/memory/block_list_allocator.hpp25
-rw-r--r--kernel/include/kernel/memory/heap_allocator.hpp7
-rw-r--r--kernel/include/kernel/memory/mmio_allocator.hpp41
-rw-r--r--kernel/include/kernel/test_support/boot_modules.hpp10
-rw-r--r--kernel/include/kernel/test_support/bump_frame_allocator.hpp54
-rw-r--r--kernel/include/kernel/test_support/cio.hpp39
-rw-r--r--kernel/include/kernel/test_support/cpu.hpp23
-rw-r--r--kernel/include/kernel/test_support/devices/block_device.hpp31
-rw-r--r--kernel/include/kernel/test_support/devices/character_device.hpp23
-rw-r--r--kernel/include/kernel/test_support/devices/storage/management.hpp10
-rw-r--r--kernel/include/kernel/test_support/filesystem/ext2.hpp21
-rw-r--r--kernel/include/kernel/test_support/filesystem/filesystem.hpp22
-rw-r--r--kernel/include/kernel/test_support/filesystem/inode.hpp19
-rw-r--r--kernel/include/kernel/test_support/filesystem/open_file_table.hpp10
-rw-r--r--kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp48
-rw-r--r--kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp23
-rw-r--r--kernel/include/kernel/test_support/filesystem/vfs.hpp10
-rw-r--r--kernel/include/kernel/test_support/log_buffer.hpp37
-rw-r--r--kernel/include/kernel/test_support/memory.hpp22
-rw-r--r--kernel/include/kernel/test_support/page_mapper.hpp49
-rw-r--r--kernel/include/kernel/test_support/simulated_memory.hpp67
53 files changed, 2416 insertions, 20 deletions
diff --git a/kernel/include/kernel/acpi/manager.hpp b/kernel/include/kernel/acpi/manager.hpp
new file mode 100644
index 0000000..1e8c1e8
--- /dev/null
+++ b/kernel/include/kernel/acpi/manager.hpp
@@ -0,0 +1,32 @@
+#ifndef TEACHOS_KERNEL_ACPI_MANAGER_HPP
+#define TEACHOS_KERNEL_ACPI_MANAGER_HPP
+
+#include <acpi/acpi.hpp>
+
+#include <kstd/flat_map>
+#include <kstd/memory>
+#include <kstd/vector>
+
+#include <string_view>
+
+namespace kernel::acpi
+{
+
+ struct manager
+ {
+ explicit manager(::acpi::rsdp const & sdp);
+
+ auto load_tables() -> bool;
+
+ auto get_table(std::string_view signature) -> kstd::observer_ptr<::acpi::table_header const>;
+
+ private:
+ ::acpi::rsdp const * m_sdp{};
+ ::acpi::table_header const * m_rsdt{};
+ kstd::flat_map<std::string_view, ::acpi::table_header const *> m_tables{};
+ bool m_extended{};
+ };
+
+} // namespace kernel::acpi
+
+#endif
diff --git a/kernel/include/kernel/devices/block_device.hpp b/kernel/include/kernel/devices/block_device.hpp
new file mode 100644
index 0000000..a6d68ee
--- /dev/null
+++ b/kernel/include/kernel/devices/block_device.hpp
@@ -0,0 +1,97 @@
+#ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP
+#define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_HPP
+
+#include <kapi/devices/device.hpp>
+
+#include <kstd/string>
+
+#include <cstddef>
+
+namespace kernel::devices
+{
+ /**
+ * @brief Base interface for block-addressable devices.
+ */
+ struct block_device : kapi::devices::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, kstd::string const & name, size_t block_size);
+
+ /**
+ * @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 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
new file mode 100644
index 0000000..8be75b6
--- /dev/null
+++ b/kernel/include/kernel/devices/block_device_utils.hpp
@@ -0,0 +1,44 @@
+#ifndef TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_UTILS_HPP
+#define TEACH_OS_KERNEL_DEVICES_BLOCK_DEVICE_UTILS_HPP
+
+#include <kapi/devices/device.hpp>
+
+#include <kstd/memory>
+
+#include <cstddef>
+
+namespace kernel::devices::block_device_utils
+{
+ /**
+ @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<kapi::devices::device> 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<kapi::devices::device> const & device, void const * buffer, size_t offset, size_t size)
+ -> size_t;
+} // namespace kernel::devices::block_device_utils
+
+#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
new file mode 100644
index 0000000..c8fee52
--- /dev/null
+++ b/kernel/include/kernel/devices/root_bus.hpp
@@ -0,0 +1,16 @@
+#ifndef TEACHOS_KERNEL_DEVICES_ROOT_BUS_HPP
+#define TEACHOS_KERNEL_DEVICES_ROOT_BUS_HPP
+
+#include <kapi/devices/bus.hpp>
+
+namespace kernel::devices
+{
+
+ struct root_bus final : kapi::devices::bus
+ {
+ root_bus();
+ };
+
+} // 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
new file mode 100644
index 0000000..bea18f3
--- /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 <kapi/devices/device.hpp>
+
+#include <kstd/memory>
+#include <kstd/vector>
+
+#include <cstddef>
+
+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<kstd::shared_ptr<kapi::devices::device>> 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<kapi::devices::device>;
+
+ protected:
+ size_t m_major{};
+ size_t m_minors_per_device{};
+ kstd::vector<kstd::shared_ptr<kapi::devices::device>> 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..9a84087
--- /dev/null
+++ b/kernel/include/kernel/devices/storage/management.hpp
@@ -0,0 +1,78 @@
+#ifndef TEACH_OS_KERNEL_DEVICES_STORAGE_MANAGEMENT_HPP
+#define TEACH_OS_KERNEL_DEVICES_STORAGE_MANAGEMENT_HPP
+
+#include <kernel/devices/storage/controller.hpp>
+
+#include <kapi/devices/device.hpp>
+
+#include <kstd/memory>
+#include <kstd/vector>
+
+#include <cstddef>
+
+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<controller> const & controller) -> void;
+
+ /**
+ * @brief Return all registered storage controllers.
+ * @return Vector of all registered storage controllers.
+ */
+ [[nodiscard]] auto all_controllers() const -> kstd::vector<kstd::shared_ptr<controller>> 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<kapi::devices::device>;
+
+ /**
+ * @brief Determine the boot device.
+ * @return Boot device, or nullptr if it cannot be determined.
+ */
+ auto determine_boot_device() -> kstd::shared_ptr<kapi::devices::device>;
+
+ private:
+ /**
+ * @brief Private default constructor for storage management singleton.
+ */
+ management() = default;
+
+ kstd::vector<kstd::shared_ptr<controller>> 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..93cf30d
--- /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 <kernel/devices/storage/controller.hpp>
+
+#include <kapi/boot_module/boot_module_registry.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..89789ea
--- /dev/null
+++ b/kernel/include/kernel/devices/storage/ram_disk/device.hpp
@@ -0,0 +1,58 @@
+#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 <kapi/boot_module/boot_module.hpp>
+
+#include <cstddef>
+
+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 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.
+ * @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/filesystem/constants.hpp b/kernel/include/kernel/filesystem/constants.hpp
new file mode 100644
index 0000000..8388d05
--- /dev/null
+++ b/kernel/include/kernel/filesystem/constants.hpp
@@ -0,0 +1,14 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_CONSTANTS_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_CONSTANTS_HPP
+
+#include <cstddef>
+
+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
diff --git a/kernel/include/kernel/filesystem/dentry.hpp b/kernel/include/kernel/filesystem/dentry.hpp
new file mode 100644
index 0000000..478596a
--- /dev/null
+++ b/kernel/include/kernel/filesystem/dentry.hpp
@@ -0,0 +1,104 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_DENTRY_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_DENTRY_HPP
+
+#include <kernel/filesystem/inode.hpp>
+
+#include <kstd/memory>
+#include <kstd/string>
+#include <kstd/vector>
+
+#include <cstdint>
+#include <string_view>
+
+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
+ {
+ is_mount_point = 1 << 0
+ };
+
+ /**
+ @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<dentry> const & parent, kstd::shared_ptr<inode> const & inode, std::string_view name);
+
+ /**
+ @brief Get the associated inode.
+ @return A reference to the associated inode.
+ */
+ [[nodiscard]] auto get_inode() const -> kstd::shared_ptr<inode> const &;
+
+ /**
+ @brief Get the parent dentry.
+ @return A reference to the parent dentry.
+ */
+ [[nodiscard]] auto parent() const -> kstd::shared_ptr<dentry> const &;
+
+ /**
+ @brief Get the name of the dentry.
+ @return The name of the dentry.
+ */
+ [[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 absolute_path() const -> kstd::string;
+
+ /**
+ @brief Add a @p child dentry.
+ @param child The child dentry to add.
+ */
+ auto add_child(kstd::shared_ptr<dentry> 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<dentry>;
+
+ /**
+ @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:
+ kstd::string m_name;
+ kstd::shared_ptr<dentry> m_parent;
+ kstd::vector<kstd::shared_ptr<dentry>> m_children;
+ kstd::shared_ptr<inode> m_inode;
+ uint32_t m_flags;
+ };
+} // namespace kernel::filesystem
+
+#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..dbaa387
--- /dev/null
+++ b/kernel/include/kernel/filesystem/devfs/filesystem.hpp
@@ -0,0 +1,46 @@
+#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 <kstd/memory>
+#include <kstd/vector>
+
+#include <string_view>
+
+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 backing_inode Backing inode passed by the vfs (not required by devfs).
+ @return The result of the mount operation.
+ */
+ auto mount(kstd::shared_ptr<kernel::filesystem::inode> const & backing_inode) -> operation_result 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.
+ */
+ [[nodiscard]] auto lookup(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name) const
+ -> kstd::shared_ptr<kernel::filesystem::inode> override;
+
+ private:
+ auto build_device_inode_table() -> void;
+
+ kstd::vector<kstd::shared_ptr<device_inode>> m_inodes{};
+ };
+} // 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..e428891
--- /dev/null
+++ b/kernel/include/kernel/filesystem/devfs/inode.hpp
@@ -0,0 +1,42 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVFS_INODE_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_DEVFS_INODE_HPP
+
+#include <kernel/filesystem/inode.hpp>
+
+#include <cstddef>
+
+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 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;
+
+ /**
+ @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;
+ };
+} // 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
new file mode 100644
index 0000000..f4aa2d1
--- /dev/null
+++ b/kernel/include/kernel/filesystem/device_inode.hpp
@@ -0,0 +1,64 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_DEVICE_INODE_HPP
+
+#include <kernel/filesystem/inode.hpp>
+
+#include <kapi/devices/device.hpp>
+
+#include <kstd/memory>
+
+#include <cstddef>
+
+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<kapi::devices::device> 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<kapi::devices::device> const &;
+
+ /**
+ @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;
+
+ private:
+ kstd::shared_ptr<kapi::devices::device> m_device;
+ };
+} // namespace kernel::filesystem
+
+#endif \ No newline at end of file
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..7fbba3f
--- /dev/null
+++ b/kernel/include/kernel/filesystem/ext2/block_group_descriptor.hpp
@@ -0,0 +1,24 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_BLOCK_GROUP_DESCRIPTOR_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_BLOCK_GROUP_DESCRIPTOR_HPP
+
+#include <array>
+#include <cstdint>
+
+namespace kernel::filesystem::ext2
+{
+ /**
+ @brief Represents a block group descriptor in the ext2 filesystem.
+ */
+ struct [[gnu::packed]] 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<uint8_t, 2> padding;
+ std::array<uint8_t, 12> reserved; // 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..2284d7b
--- /dev/null
+++ b/kernel/include/kernel/filesystem/ext2/filesystem.hpp
@@ -0,0 +1,115 @@
+#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 <kstd/memory>
+#include <kstd/unikstd.h>
+#include <kstd/vector>
+
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <string_view>
+
+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 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;
+ 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;
+ constexpr uint16_t inline mode_symbolic_link = 0xA000;
+ } // namespace constants
+
+ /**
+ @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 backing_inode.
+ @param backing_inode The backing inode to mount.
+ @return The result of the mount operation.
+ */
+ auto mount(kstd::shared_ptr<kernel::filesystem::inode> const & backing_inode) -> operation_result 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.
+ */
+ [[nodiscard]] auto lookup(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name) const
+ -> kstd::shared_ptr<kernel::filesystem::inode> override;
+
+ /**
+ @brief Gets the size of a block in the filesystem.
+ @return The size of a block in bytes.
+ */
+ [[nodiscard]] auto block_size() const -> size_t;
+
+ /**
+ @brief Gets the revision level of the filesystem.
+ @return The revision level.
+ */
+ [[nodiscard]] auto revision_level() const -> uint32_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.
+ */
+ [[nodiscard]] auto map_inode_block_index_to_global_block_number(size_t inode_block_index, inode_data data) const
+ -> kstd::ssize_t;
+
+ private:
+ struct indirect_level
+ {
+ uint32_t slot_index;
+ size_t capacity;
+ };
+
+ [[nodiscard]] auto indirect_levels() const -> std::array<indirect_level, 3>;
+
+ [[nodiscard]] auto read_inode(uint32_t inode_number) const -> kstd::shared_ptr<kernel::filesystem::ext2::inode>;
+ [[nodiscard]] auto read_block_number_at_index(uint32_t block_number, size_t index) const -> uint32_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;
+ [[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<block_group_descriptor> m_block_group_descriptors;
+ };
+} // 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..f2496f0
--- /dev/null
+++ b/kernel/include/kernel/filesystem/ext2/inode.hpp
@@ -0,0 +1,105 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_INODE_HPP
+
+#include <kernel/filesystem/inode.hpp>
+
+#include <kstd/memory>
+
+#include <array>
+#include <cstddef>
+#include <cstdint>
+
+namespace kernel::filesystem::ext2
+{
+ struct filesystem;
+
+ /**
+ @brief Represents the data associated with an ext2 inode.
+ */
+ struct [[gnu::packed]] inode_data
+ {
+ 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;
+ std::array<uint32_t, 15> block; // NOLINT(readability-magic-numbers)
+ uint32_t generation;
+ uint32_t file_acl;
+ uint32_t dir_acl;
+ uint32_t faddr;
+ std::array<uint8_t, 12> osd2; // NOLINT(readability-magic-numbers)
+ };
+
+ struct inode : kernel::filesystem::inode
+ {
+ /**
+ @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 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.
+ @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 Get the data associated with this inode.
+ @return A reference to the inode data.
+ */
+ [[nodiscard]] auto data() const -> inode_data const &;
+
+ /**
+ @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;
+
+ /**
+ @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;
+
+ /**
+ @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;
+
+ /**
+ @brief Get the size of the file represented by this inode.
+ @return The size of the file in bytes.
+ */
+ [[nodiscard]] auto size() const -> uint64_t;
+
+ private:
+ filesystem const * m_filesystem;
+ 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
new file mode 100644
index 0000000..4097cbb
--- /dev/null
+++ b/kernel/include/kernel/filesystem/ext2/linked_directory_entry.hpp
@@ -0,0 +1,22 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_LINKED_DIRECTORY_ENTRY_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_LINKED_DIRECTORY_ENTRY_HPP
+
+#include <array>
+#include <cstdint>
+
+namespace kernel::filesystem::ext2
+{
+ /**
+ @brief Represents a linked directory entry in the ext2 filesystem.
+ */
+ struct [[gnu::packed]] linked_directory_entry
+ {
+ uint32_t inode;
+ uint16_t rec_len;
+ uint8_t name_len;
+ uint8_t file_type;
+ std::array<char, 255> 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..41ad935
--- /dev/null
+++ b/kernel/include/kernel/filesystem/ext2/superblock.hpp
@@ -0,0 +1,74 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_EXT2_SUPERBLOCK_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_EXT2_SUPERBLOCK_HPP
+
+#include <array>
+#include <cstdint>
+
+namespace kernel::filesystem::ext2
+{
+ /**
+ @brief Represents the superblock in the ext2 filesystem.
+ */
+ struct [[gnu::packed]] 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;
+ std::array<uint8_t, 16> uuid;
+ std::array<uint8_t, 16> volume_name;
+ std::array<uint8_t, 64> last_mounted;
+ uint32_t algorithm_usage_bitmap;
+
+ // Performance Hints
+ uint8_t prealloc_blocks;
+ uint8_t prealloc_dir_blocks;
+ uint16_t padding1;
+
+ // Journaling Support
+ std::array<uint8_t, 16> journal_uuid;
+ uint32_t journal_inum;
+ uint32_t journal_dev;
+ uint32_t last_orphan;
+
+ // Directory Indexing Support
+ std::array<uint32_t, 4> hash_seed;
+ uint8_t def_hash_version;
+ std::array<uint8_t, 3> padding2;
+
+ // Other options
+ uint32_t default_mount_options;
+ uint32_t first_meta_bg;
+ std::array<uint8_t, 760> unused; // NOLINT(readability-magic-numbers)
+ };
+} // namespace kernel::filesystem::ext2
+#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..bec1b16
--- /dev/null
+++ b/kernel/include/kernel/filesystem/filesystem.hpp
@@ -0,0 +1,79 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_FILESYSTEM_HPP
+
+#include <kernel/filesystem/inode.hpp>
+
+#include <kstd/memory>
+#include <kstd/vector>
+
+#include <string_view>
+
+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
+ {
+ enum class operation_result : int
+ {
+ success = 0,
+ invalid_magic_number = -1,
+ invalid_root_inode = -2,
+ unmount_failed = -3
+ };
+
+ /**
+ @brief Virtual destructor for the filesystem.
+ */
+ virtual ~filesystem() = default;
+
+ /**
+ @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 backing inode.
+ @warning Panics if @p backing_inode is null.
+ */
+ auto static probe_and_mount(kstd::shared_ptr<inode> const & backing_inode) -> kstd::shared_ptr<filesystem>;
+
+ /**
+ @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<inode> 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
+ 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.
+ */
+ [[nodiscard]] virtual auto lookup(kstd::shared_ptr<inode> const & parent, std::string_view name) const
+ -> kstd::shared_ptr<inode> = 0;
+
+ /**
+ @brief Returns a reference to the root inode of the filesystem.
+ */
+ [[nodiscard]] auto root_inode() const -> kstd::shared_ptr<inode> const &;
+
+ /**
+ @brief Returns a reference to the backing inode of the filesystem.
+ */
+ [[nodiscard]] auto backing_inode() const -> kstd::shared_ptr<inode> const &;
+
+ protected:
+ kstd::shared_ptr<inode> m_root_inode{};
+ kstd::shared_ptr<inode> m_backing_inode{};
+ };
+
+} // 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
new file mode 100644
index 0000000..b34b921
--- /dev/null
+++ b/kernel/include/kernel/filesystem/inode.hpp
@@ -0,0 +1,69 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_INODE_HPP
+
+#include <cstddef>
+
+namespace kernel::filesystem
+{
+ /**
+ @brief Represents an inode in the filesystem.
+ */
+ struct inode
+ {
+ /**
+ @brief Create an inode.
+ */
+ inode() = default;
+
+ /**
+ @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]] 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]] 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]] 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
+
+#endif
diff --git a/kernel/include/kernel/filesystem/mount.hpp b/kernel/include/kernel/filesystem/mount.hpp
new file mode 100644
index 0000000..ced4f81
--- /dev/null
+++ b/kernel/include/kernel/filesystem/mount.hpp
@@ -0,0 +1,97 @@
+#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 <kstd/memory>
+#include <kstd/string>
+
+#include <atomic>
+#include <cstddef>
+
+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 parent_mount The mount that contains the mount_dentry.
+ @param source_mount The mount that the filesystem originates from.
+ */
+ mount(kstd::shared_ptr<dentry> const & mount_dentry, kstd::shared_ptr<dentry> const & root_dentry,
+ kstd::shared_ptr<filesystem> const & fs, kstd::shared_ptr<mount> const & parent_mount,
+ kstd::shared_ptr<mount> const & source_mount);
+
+ /**
+ @brief Get the dentry where the filesystem is mounted.
+ */
+ [[nodiscard]] auto mount_dentry() const -> kstd::shared_ptr<dentry> const &;
+
+ /**
+ @brief Get the root dentry of the mounted filesystem.
+ */
+ [[nodiscard]] auto root_dentry() const -> kstd::shared_ptr<dentry> const &;
+
+ /**
+ @brief Get the filesystem instance being mounted.
+ */
+ [[nodiscard]] auto get_filesystem() const -> kstd::shared_ptr<filesystem> const &;
+
+ /**
+ @brief Get the path at which the filesystem is mounted.
+ */
+ [[nodiscard]] auto mount_path() const -> kstd::string;
+
+ /**
+ @brief Get the parent mount that this mount was attached beneath.
+ */
+ [[nodiscard]] auto parent_mount() const -> kstd::shared_ptr<mount> const &;
+
+ /**
+ @brief Get the source mount where this mount originates from.
+ */
+ [[nodiscard]] auto source_mount() const -> kstd::shared_ptr<mount>;
+
+ /**
+ @brief Increment the reference count for this mount.
+ */
+ auto increment_ref_count() -> void;
+
+ /**
+ @brief Decrement the reference count for this mount.
+ @warning Throws if ref_count is zero.
+ */
+ auto decrement_ref_count() -> void;
+
+ /**
+ @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;
+
+ /**
+ @brief Get the current reference count for this mount.
+ @return The current reference count.
+ */
+ [[nodiscard]] auto ref_count() const -> size_t;
+
+ private:
+ kstd::shared_ptr<dentry> m_mount_dentry{};
+ kstd::shared_ptr<dentry> m_root_dentry{};
+ kstd::shared_ptr<filesystem> m_filesystem{};
+ kstd::shared_ptr<mount> m_parent_mount{};
+ kstd::weak_ptr<mount> m_source_mount{};
+ std::atomic_size_t m_ref_count{0};
+ };
+} // namespace kernel::filesystem
+
+#endif
diff --git a/kernel/include/kernel/filesystem/mount_table.hpp b/kernel/include/kernel/filesystem/mount_table.hpp
new file mode 100644
index 0000000..4f2d1b7
--- /dev/null
+++ b/kernel/include/kernel/filesystem/mount_table.hpp
@@ -0,0 +1,58 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_MOUNT_TABLE_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_MOUNT_TABLE_HPP
+
+#include <kernel/filesystem/mount.hpp>
+
+#include <kstd/memory>
+#include <kstd/vector>
+
+#include <string_view>
+
+namespace kernel::filesystem
+{
+ /**
+ @brief A table for managing mounted filesystems in the kernel.
+ */
+ struct mount_table
+ {
+ /**
+ @brief Results for mount table operations.
+ */
+ enum class operation_result : int
+ {
+ removed = 0,
+ has_child_mounts = -1,
+ mount_not_found = -2,
+ cannot_be_unmounted = -3
+ };
+
+ /**
+ @brief Adds a mount to the table.
+ @param mount The mount to add.
+ */
+ auto add_mount(kstd::shared_ptr<mount> 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 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_mount(std::string_view path) const -> kstd::shared_ptr<mount>;
+
+ private:
+ [[nodiscard]] auto has_child_mounts(kstd::shared_ptr<mount> const & parent_mount) const -> bool;
+ [[nodiscard]] auto find_mount_iterator(std::string_view path) const
+ -> kstd::vector<kstd::shared_ptr<mount>>::const_iterator;
+
+ kstd::vector<kstd::shared_ptr<mount>> m_mounts;
+ };
+} // namespace kernel::filesystem
+
+#endif
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..fd10e64
--- /dev/null
+++ b/kernel/include/kernel/filesystem/open_file_descriptor.hpp
@@ -0,0 +1,68 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTOR_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_DESCRIPTOR_HPP
+
+#include <kernel/filesystem/dentry.hpp>
+
+#include <kstd/memory>
+
+#include <cstddef>
+
+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 dentry and the current file offset.
+ */
+ struct 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<dentry> const & dentry);
+
+ /**
+ @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;
+
+ /**
+ @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<dentry> const &;
+
+ private:
+ kstd::shared_ptr<dentry> m_dentry;
+ size_t m_offset;
+ };
+
+} // 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..7e754ac
--- /dev/null
+++ b/kernel/include/kernel/filesystem/open_file_table.hpp
@@ -0,0 +1,66 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_TABLE_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_OPEN_FILE_TABLE_HPP
+
+#include <kernel/filesystem/open_file_descriptor.hpp>
+
+#include <kstd/memory>
+#include <kstd/unikstd.h>
+#include <kstd/vector>
+
+#include <cstddef>
+
+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 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<open_file_descriptor> const & fd) -> kstd::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 file(size_t fd) const -> kstd::shared_ptr<open_file_descriptor>;
+
+ /**
+ @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(size_t fd) -> kstd::ssize_t;
+
+ private:
+ open_file_table() = default;
+
+ kstd::vector<kstd::shared_ptr<open_file_descriptor>> m_open_files{};
+ };
+} // namespace kernel::filesystem
+
+#endif \ No newline at end of file
diff --git a/kernel/include/kernel/filesystem/path.hpp b/kernel/include/kernel/filesystem/path.hpp
new file mode 100644
index 0000000..4845bf1
--- /dev/null
+++ b/kernel/include/kernel/filesystem/path.hpp
@@ -0,0 +1,71 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_PATH_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_PATH_HPP
+
+#include <kernel/filesystem/constants.hpp>
+
+#include <kstd/string>
+
+#include <ranges>
+#include <string_view>
+
+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() == '/' && 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() != '/' && 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);
+ }
+
+ /**
+ @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())); });
+ }
+
+} // namespace kernel::filesystem::path
+
+#endif // TEACH_OS_KERNEL_FILESYSTEM_PATH_HPP \ No newline at end of file
diff --git a/kernel/include/kernel/filesystem/rootfs/filesystem.hpp b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp
new file mode 100644
index 0000000..3c2dcb1
--- /dev/null
+++ b/kernel/include/kernel/filesystem/rootfs/filesystem.hpp
@@ -0,0 +1,41 @@
+#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 <kstd/memory>
+#include <kstd/string>
+#include <kstd/vector>
+
+#include <string_view>
+
+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 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<kernel::filesystem::inode> const & backing_inode) -> operation_result 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 Always returns nullptr.
+ */
+ [[nodiscard]] auto lookup(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name) const
+ -> kstd::shared_ptr<kernel::filesystem::inode> 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..0f21eaa
--- /dev/null
+++ b/kernel/include/kernel/filesystem/rootfs/inode.hpp
@@ -0,0 +1,45 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_INODE_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_ROOTFS_INODE_HPP
+
+#include <kernel/filesystem/inode.hpp>
+
+#include <kstd/memory>
+#include <kstd/string>
+#include <kstd/vector>
+
+#include <cstddef>
+
+namespace kernel::filesystem::rootfs
+{
+ /**
+ @brief Represents an inode in the rootfs filesystem.
+ */
+ struct inode : kernel::filesystem::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 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
+
+#endif
diff --git a/kernel/include/kernel/filesystem/type.hpp b/kernel/include/kernel/filesystem/type.hpp
new file mode 100644
index 0000000..0948e54
--- /dev/null
+++ b/kernel/include/kernel/filesystem/type.hpp
@@ -0,0 +1,42 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_TYPE_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_TYPE_HPP
+
+#include <kernel/filesystem/filesystem.hpp>
+
+#include <kstd/memory>
+
+#include <string_view>
+
+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<filesystem> = 0;
+ };
+
+ template<typename Type>
+ struct type_registration
+ {
+ constexpr auto static instance = Type{};
+ [[using gnu: section("fs_types"), used, visibility("hidden")]] constexpr auto static pointer{
+ kstd::make_observer<type const>(&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..3be7295
--- /dev/null
+++ b/kernel/include/kernel/filesystem/type_registry.hpp
@@ -0,0 +1,53 @@
+#ifndef TEACH_OS_KERNEL_TYPE_REGISRY_HPP
+#define TEACH_OS_KERNEL_TYPE_REGISRY_HPP
+
+#include <kernel/filesystem/type.hpp>
+
+#include <kstd/flat_map>
+#include <kstd/memory>
+#include <kstd/string>
+
+#include <cstddef>
+#include <span>
+#include <string_view>
+
+namespace kernel::filesystem
+{
+
+ struct type_registry
+ {
+ using value_type = type;
+ using pointer = kstd::observer_ptr<value_type const>;
+
+ 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<pointer const>;
+
+ //! 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.
+ kstd::flat_map<std::string_view, pointer> m_descriptors{};
+ };
+
+} // namespace kernel::filesystem
+
+#endif
diff --git a/kernel/include/kernel/filesystem/vfs.hpp b/kernel/include/kernel/filesystem/vfs.hpp
new file mode 100644
index 0000000..ddc9a9b
--- /dev/null
+++ b/kernel/include/kernel/filesystem/vfs.hpp
@@ -0,0 +1,113 @@
+#ifndef TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP
+#define TEACH_OS_KERNEL_FILESYSTEM_VFS_HPP
+
+#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 <kstd/memory>
+
+#include <string_view>
+#include <utility>
+
+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 Results for VFS operations.
+ */
+ enum class operation_result : int
+ {
+ success = 0,
+ invalid_path = -1,
+ non_existent_path = -2,
+ mount_point_not_found = -3,
+ unmount_failed = -4,
+ invalid_filesystem = -5
+ };
+
+ 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 returns the corresponding dentry.
+ @param path The path to the file to open.
+ @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<dentry>;
+
+ /**
+ @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(std::string_view path) -> operation_result;
+
+ /**
+ @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 source, std::string_view target) -> 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 unmount(std::string_view path) -> operation_result;
+
+ private:
+ /**
+ * 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) const
+ -> std::pair<kstd::shared_ptr<dentry>, kstd::shared_ptr<mount>>;
+ [[nodiscard]] auto resolve_path(std::string_view path) const -> kstd::shared_ptr<dentry>;
+ [[nodiscard]] auto find_mount(std::string_view path) const -> kstd::shared_ptr<mount>;
+
+ auto do_mount_internal(kstd::shared_ptr<dentry> const & mount_point_dentry,
+ kstd::shared_ptr<mount> const & parent_mount, kstd::shared_ptr<filesystem> const & fs,
+ kstd::shared_ptr<mount> const & source_mount = nullptr) -> void;
+
+ auto graft_persistent_device_fs(kstd::shared_ptr<devfs::filesystem> const & device_fs) -> void;
+
+ mount_table m_mount_table{};
+ };
+} // namespace kernel::filesystem
+
+#endif \ No newline at end of file
diff --git a/kernel/include/kernel/memory.hpp b/kernel/include/kernel/memory.hpp
index f09c519..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 "kapi/memory.hpp"
+#include <kernel/memory/heap_allocator.hpp> // IWYU pragma: export
-#include "kernel/memory/heap_allocator.hpp" // IWYU pragma: export
+#include <kapi/memory.hpp>
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 <kapi/memory.hpp>
#include <cstddef>
#include <cstdint>
diff --git a/kernel/include/kernel/memory/block_list_allocator.hpp b/kernel/include/kernel/memory/block_list_allocator.hpp
index 5e81c44..51b226e 100644
--- a/kernel/include/kernel/memory/block_list_allocator.hpp
+++ b/kernel/include/kernel/memory/block_list_allocator.hpp
@@ -1,11 +1,12 @@
#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 "kernel/memory/heap_allocator.hpp"
+#include <kapi/memory.hpp>
#include <kstd/mutex>
+#include <kstd/units>
#include <cstddef>
#include <new>
@@ -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,26 +43,26 @@ namespace kernel::memory
private:
struct block_header final
{
- std::size_t 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.
//!
//! 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.
//!
@@ -86,4 +87,4 @@ namespace kernel::memory
} // namespace kernel::memory
-#endif \ No newline at end of file
+#endif
diff --git a/kernel/include/kernel/memory/heap_allocator.hpp b/kernel/include/kernel/memory/heap_allocator.hpp
index bc771e3..fd39bef 100644
--- a/kernel/include/kernel/memory/heap_allocator.hpp
+++ b/kernel/include/kernel/memory/heap_allocator.hpp
@@ -1,10 +1,7 @@
#ifndef TEACHOS_KERNEL_MEMORY_HEAP_ALLOCATOR_HPP
#define TEACHOS_KERNEL_MEMORY_HEAP_ALLOCATOR_HPP
-#include <kstd/mutex>
-
-#include <cstddef>
-#include <new>
+#include <kstd/units>
namespace kernel::memory
{
@@ -19,7 +16,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/include/kernel/memory/mmio_allocator.hpp b/kernel/include/kernel/memory/mmio_allocator.hpp
new file mode 100644
index 0000000..c7a8ed0
--- /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 <kstd/allocator>
+#include <kstd/memory>
+
+#include <cstddef>
+
+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<node> m_allocator{};
+ node * m_head{};
+ };
+
+} // namespace kernel::memory
+
+#endif
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/bump_frame_allocator.hpp b/kernel/include/kernel/test_support/bump_frame_allocator.hpp
new file mode 100644
index 0000000..a8ffd48
--- /dev/null
+++ b/kernel/include/kernel/test_support/bump_frame_allocator.hpp
@@ -0,0 +1,54 @@
+#ifndef TEACHOS_KERNEL_TEST_SUPPORT_BUMP_FRAME_ALLOCATOR_HPP
+#define TEACHOS_KERNEL_TEST_SUPPORT_BUMP_FRAME_ALLOCATOR_HPP
+
+#include <kapi/memory.hpp>
+
+#include <cstddef>
+#include <optional>
+#include <utility>
+
+namespace kernel::tests
+{
+
+ //! 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
+ {
+ //! @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<std::pair<kapi::memory::frame, std::size_t>> override
+ {
+ auto start = next_free_frame;
+ next_free_frame += count;
+ return std::pair{kapi::memory::frame{start}, count};
+ }
+
+ //! @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<kapi::memory::frame, std::size_t>) -> void override {}
+
+ //! The next free frame to be allocated.
+ std::size_t next_free_frame{};
+ };
+
+} // namespace kernel::tests
+
+#endif \ No newline at end of file
diff --git a/kernel/include/kernel/test_support/cio.hpp b/kernel/include/kernel/test_support/cio.hpp
new file mode 100644
index 0000000..afe27e0
--- /dev/null
+++ b/kernel/include/kernel/test_support/cio.hpp
@@ -0,0 +1,39 @@
+#ifndef TEACHOS_KERNEL_TEST_SUPPORT_CIO_HPP
+#define TEACHOS_KERNEL_TEST_SUPPORT_CIO_HPP
+
+#include <kernel/test_support/log_buffer.hpp>
+
+#include <kapi/cio.hpp>
+
+#include <string_view>
+
+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
+ {
+ //! @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
+
+#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..c99d1a7
--- /dev/null
+++ b/kernel/include/kernel/test_support/cpu.hpp
@@ -0,0 +1,23 @@
+#ifndef TEACHOS_KERNEL_TEST_SUPPORT_CPU_HPP
+#define TEACHOS_KERNEL_TEST_SUPPORT_CPU_HPP
+
+#include <stdexcept>
+
+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
+
+#endif \ No newline at end of file
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..89a2bf1
--- /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 <kstd/string>
+#include <kstd/vector>
+
+#include <cstddef>
+#include <cstdint>
+
+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, size_t initial_size = 0);
+
+ 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<uint8_t> data{};
+ };
+
+} // namespace kernel::tests::devices
+
+#endif \ No newline at end of file
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..aba183a
--- /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 <kstd/string>
+#include <kstd/vector>
+
+#include <cstddef>
+
+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/include/kernel/test_support/devices/storage/management.hpp b/kernel/include/kernel/test_support/devices/storage/management.hpp
new file mode 100644
index 0000000..581aa91
--- /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::management
+{
+ //! Deinitialize the storage management singleton.
+ auto deinit() -> void;
+} // namespace kernel::tests::devices::storage::management
+
+#endif \ No newline at end of file
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..18cef1c
--- /dev/null
+++ b/kernel/include/kernel/test_support/filesystem/ext2.hpp
@@ -0,0 +1,21 @@
+#ifndef TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_EXT2_HPP
+#define TEACHOS_KERNEL_TEST_SUPPORT_FILESYSTEM_EXT2_HPP
+
+#include <kernel/filesystem/ext2/superblock.hpp>
+#include <kernel/test_support/devices/block_device.hpp>
+
+#include <cstddef>
+#include <cstdint>
+
+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;
+ 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/include/kernel/test_support/filesystem/filesystem.hpp b/kernel/include/kernel/test_support/filesystem/filesystem.hpp
new file mode 100644
index 0000000..5f26022
--- /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 <kstd/memory>
+
+#include <string_view>
+
+namespace kernel::tests::filesystem
+{
+ struct filesystem : kernel::filesystem::filesystem
+ {
+ filesystem() = default;
+
+ [[nodiscard]] auto lookup(kstd::shared_ptr<kernel::filesystem::inode> const & parent, std::string_view name) const
+ -> kstd::shared_ptr<kernel::filesystem::inode> override;
+ };
+} // namespace kernel::tests::filesystem
+
+#endif
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..8a76437
--- /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 <cstddef>
+
+namespace kernel::tests::filesystem
+{
+ struct inode : kernel::filesystem::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
+
+#endif
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
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..94a6668
--- /dev/null
+++ b/kernel/include/kernel/test_support/filesystem/storage_boot_module_fixture.hpp
@@ -0,0 +1,48 @@
+#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 <cstddef>
+#include <filesystem>
+#include <string>
+#include <vector>
+
+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(std::vector<std::string> const & module_names,
+ std::vector<std::filesystem::path> 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{};
+ std::vector<std::string> m_module_names{};
+ std::vector<std::vector<std::byte>> m_module_data{};
+ std::vector<mapped_image> m_mapped_images{};
+
+ private:
+ auto setup_module_from_img(std::string const & module_name, std::filesystem::path const & img_path) -> void;
+ };
+} // namespace kernel::tests::filesystem
+
+#endif \ No newline at end of file
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..55f4b29
--- /dev/null
+++ b/kernel/include/kernel/test_support/filesystem/storage_boot_module_vfs_fixture.hpp
@@ -0,0 +1,23 @@
+#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 <cstddef>
+#include <filesystem>
+#include <string>
+#include <vector>
+
+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(std::vector<std::string> const & module_names,
+ std::vector<std::filesystem::path> const & img_paths) -> void;
+ };
+} // namespace kernel::tests::filesystem
+
+#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/include/kernel/test_support/log_buffer.hpp b/kernel/include/kernel/test_support/log_buffer.hpp
new file mode 100644
index 0000000..41d9a76
--- /dev/null
+++ b/kernel/include/kernel/test_support/log_buffer.hpp
@@ -0,0 +1,37 @@
+#ifndef KERNEL_TEST_SUPPORT_LOG_BUFFER_HPP
+#define KERNEL_TEST_SUPPORT_LOG_BUFFER_HPP
+
+#include <string>
+#include <vector>
+
+namespace kernel::tests
+{
+
+ 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<std::string> const &;
+
+ private:
+ std::vector<std::string> 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
new file mode 100644
index 0000000..21030a4
--- /dev/null
+++ b/kernel/include/kernel/test_support/memory.hpp
@@ -0,0 +1,22 @@
+#ifndef TEACHOS_KERNEL_TEST_SUPPORT_MEMORY_HPP
+#define TEACHOS_KERNEL_TEST_SUPPORT_MEMORY_HPP
+
+#include <kapi/memory.hpp>
+
+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
new file mode 100644
index 0000000..be4403b
--- /dev/null
+++ b/kernel/include/kernel/test_support/page_mapper.hpp
@@ -0,0 +1,49 @@
+#ifndef TEACHOS_KERNEL_TEST_SUPPORT_PAGE_MAPPER_HPP
+#define TEACHOS_KERNEL_TEST_SUPPORT_PAGE_MAPPER_HPP
+
+#include <kernel/test_support/simulated_memory.hpp>
+
+#include <kapi/memory.hpp>
+
+#include <kstd/units>
+
+#include <cstddef>
+#include <cstdint>
+#include <unordered_map>
+
+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<std::uint64_t, kapi::memory::frame> page_mappings;
+ };
+
+} // namespace kernel::tests
+
+#endif \ No newline at end of file
diff --git a/kernel/include/kernel/test_support/simulated_memory.hpp b/kernel/include/kernel/test_support/simulated_memory.hpp
new file mode 100644
index 0000000..a201c3d
--- /dev/null
+++ b/kernel/include/kernel/test_support/simulated_memory.hpp
@@ -0,0 +1,67 @@
+#ifndef TEACHOS_KERNEL_TEST_SUPPORT_SIMULATED_MEMORY_HPP
+#define TEACHOS_KERNEL_TEST_SUPPORT_SIMULATED_MEMORY_HPP
+
+#include <kapi/memory.hpp>
+
+#include <kstd/units>
+
+#include <cstddef>
+
+#include <sys/types.h>
+
+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;
+
+ //! 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:
+ 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};
+ };
+
+} // namespace kernel::tests
+
+#endif \ No newline at end of file