diff options
Diffstat (limited to 'kernel/include')
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 |
