From 7173c05a2226ff62a5118b31d3079cbb76726e0b Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 18 Mar 2023 09:10:33 +0100 Subject: [PATCH] kernel: Add support for special device files and add a mknod() syscall --- apps/hello.c | 18 ++++++ kernel/CMakeLists.txt | 4 ++ kernel/src/fs/VFS.h | 43 +++++++++++++- kernel/src/fs/devices/ConsoleDevice.cpp | 19 ++++++ kernel/src/fs/devices/ConsoleDevice.h | 15 +++++ kernel/src/fs/devices/Device.h | 12 ++++ kernel/src/fs/devices/DeviceRegistry.cpp | 49 ++++++++++++++++ kernel/src/fs/devices/DeviceRegistry.h | 22 +++++++ kernel/src/fs/devices/NullDevice.cpp | 6 ++ kernel/src/fs/devices/NullDevice.h | 21 +++++++ kernel/src/fs/tmpfs/FileSystem.cpp | 20 +++++-- kernel/src/fs/tmpfs/FileSystem.h | 74 +++++++++++++++++++++++- kernel/src/main.cpp | 3 + kernel/src/sys/mknod.cpp | 35 +++++++++++ libc/include/bits/makedev.h | 10 ++++ libc/include/sys/sysmacros.h | 12 ++++ libc/include/sys/types.h | 1 + libluna/include/luna/Syscall.h | 2 +- 18 files changed, 357 insertions(+), 9 deletions(-) create mode 100644 kernel/src/fs/devices/ConsoleDevice.cpp create mode 100644 kernel/src/fs/devices/ConsoleDevice.h create mode 100644 kernel/src/fs/devices/Device.h create mode 100644 kernel/src/fs/devices/DeviceRegistry.cpp create mode 100644 kernel/src/fs/devices/DeviceRegistry.h create mode 100644 kernel/src/fs/devices/NullDevice.cpp create mode 100644 kernel/src/fs/devices/NullDevice.h create mode 100644 kernel/src/sys/mknod.cpp create mode 100644 libc/include/bits/makedev.h create mode 100644 libc/include/sys/sysmacros.h diff --git a/apps/hello.c b/apps/hello.c index 2eb09b6e..192b335a 100644 --- a/apps/hello.c +++ b/apps/hello.c @@ -1,6 +1,24 @@ #include +#include +#include +#include +#include +#include int main() { printf("Hello world!\n"); + + mkdir("/dev", 0755); + if (syscall(SYS_mknod, "/dev/console", 0666, makedev(1, 0)) < 0) + { + perror("mknod"); + return 1; + } + + const char* str = "Hello from /dev!"; + + FILE* f = fopen("/dev/console", "r+"); + fwrite(str, 1, strlen(str), f); + if (f) fclose(f); } diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 082542e2..b2f6977b 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -32,8 +32,12 @@ set(SOURCES src/sys/file.cpp src/sys/id.cpp src/sys/mkdir.cpp + src/sys/mknod.cpp src/fs/VFS.cpp src/fs/tmpfs/FileSystem.cpp + src/fs/devices/DeviceRegistry.cpp + src/fs/devices/NullDevice.cpp + src/fs/devices/ConsoleDevice.cpp src/InitRD.cpp src/ELF.cpp ) diff --git a/kernel/src/fs/VFS.h b/kernel/src/fs/VFS.h index 37d0a769..ef4a1ed2 100644 --- a/kernel/src/fs/VFS.h +++ b/kernel/src/fs/VFS.h @@ -7,7 +7,8 @@ namespace VFS enum class InodeType { RegularFile, - Directory + Directory, + Device }; class FileSystem; @@ -22,6 +23,8 @@ namespace VFS virtual Result> create_subdirectory(const char* name) = 0; + virtual Result add_entry(SharedPtr inode, const char* name) = 0; + // File-specific methods virtual Result read(u8* buf, usize offset, usize length) const = 0; @@ -65,6 +68,11 @@ namespace VFS return err(ENOTDIR); } + Result add_entry(SharedPtr, const char*) override + { + return err(ENOTDIR); + } + InodeType type() const override { return InodeType::RegularFile; @@ -73,6 +81,37 @@ namespace VFS virtual ~FileInode() = default; }; + class DeviceInode : Inode + { + public: + Result> find(const char*) const override + { + return err(ENOTDIR); + } + + Result> create_file(const char*) override + { + return err(ENOTDIR); + } + + Result> create_subdirectory(const char*) override + { + return err(ENOTDIR); + } + + Result add_entry(SharedPtr, const char*) override + { + return err(ENOTDIR); + } + + InodeType type() const override + { + return InodeType::Device; + } + + virtual ~DeviceInode() = default; + }; + class FileSystem { public: @@ -82,6 +121,8 @@ namespace VFS virtual Result> create_dir_inode(SharedPtr parent) = 0; + virtual Result> create_device_inode(u32 major, u32 minor) = 0; + virtual ~FileSystem() = default; }; diff --git a/kernel/src/fs/devices/ConsoleDevice.cpp b/kernel/src/fs/devices/ConsoleDevice.cpp new file mode 100644 index 00000000..4d8910e4 --- /dev/null +++ b/kernel/src/fs/devices/ConsoleDevice.cpp @@ -0,0 +1,19 @@ +#include "fs/devices/ConsoleDevice.h" +#include "video/TextConsole.h" + +Result> ConsoleDevice::create() +{ + return (SharedPtr)TRY(make_shared()); +} + +// FIXME: Read from the keyboard. +Result ConsoleDevice::read(u8*, usize, usize) const +{ + return 0; +} + +Result ConsoleDevice::write(const u8* buf, usize, usize length) +{ + TextConsole::print((const char*)buf); + return length; +} diff --git a/kernel/src/fs/devices/ConsoleDevice.h b/kernel/src/fs/devices/ConsoleDevice.h new file mode 100644 index 00000000..1d1bb970 --- /dev/null +++ b/kernel/src/fs/devices/ConsoleDevice.h @@ -0,0 +1,15 @@ +#pragma once +#include "fs/devices/DeviceRegistry.h" + +class ConsoleDevice : public Device +{ + public: + // Initializer for DeviceRegistry. + static Result> create(); + + Result read(u8*, usize, usize) const override; + + Result write(const u8*, usize, usize) override; + + virtual ~ConsoleDevice() = default; +}; diff --git a/kernel/src/fs/devices/Device.h b/kernel/src/fs/devices/Device.h new file mode 100644 index 00000000..ea8d8c21 --- /dev/null +++ b/kernel/src/fs/devices/Device.h @@ -0,0 +1,12 @@ +#pragma once +#include + +class Device +{ + public: + virtual Result read(u8* buf, usize offset, usize length) const = 0; + + virtual Result write(const u8* buf, usize offset, usize length) = 0; + + virtual ~Device() = default; +}; diff --git a/kernel/src/fs/devices/DeviceRegistry.cpp b/kernel/src/fs/devices/DeviceRegistry.cpp new file mode 100644 index 00000000..a4eab963 --- /dev/null +++ b/kernel/src/fs/devices/DeviceRegistry.cpp @@ -0,0 +1,49 @@ +#include "fs/devices/DeviceRegistry.h" +#include "Log.h" +#include "fs/devices/ConsoleDevice.h" +#include "fs/devices/NullDevice.h" +#include + +struct DeviceDescriptor +{ + device_create_func_t initializer; + u32 major; + u32 minor; +}; + +Vector g_available_devices = {}; + +namespace DeviceRegistry +{ + Result> create_special_device(u32 major, u32 minor) + { + for (const auto& descriptor : g_available_devices) + { + if (descriptor.major == major && descriptor.minor == minor) return descriptor.initializer(); + } + + return err(ENODEV); + } + + Result register_special_device(u32 major, u32 minor, device_create_func_t initializer) + { + for (const auto& descriptor : g_available_devices) + { + if (descriptor.major == major && descriptor.minor == minor) return err(EEXIST); + } + + kdbgln("device manager: registered new device type %u:%u", major, minor); + + return g_available_devices.try_append( + DeviceDescriptor { .initializer = initializer, .major = major, .minor = minor }); + } + + Result init() + { + // Hardcode default devices. + TRY(register_special_device(DeviceMajorTypes::Memory, 0, NullDevice::create)); + TRY(register_special_device(DeviceMajorTypes::Console, 0, ConsoleDevice::create)); + + return {}; + } +} diff --git a/kernel/src/fs/devices/DeviceRegistry.h b/kernel/src/fs/devices/DeviceRegistry.h new file mode 100644 index 00000000..72db9c58 --- /dev/null +++ b/kernel/src/fs/devices/DeviceRegistry.h @@ -0,0 +1,22 @@ +#pragma once + +#include "fs/devices/Device.h" +#include + +typedef Result> (*device_create_func_t)(void); + +namespace DeviceRegistry +{ + enum DeviceMajorTypes : u32 + { + Null = 0, + Console = 1, + Memory = 2 + }; + + Result> create_special_device(u32 major, u32 minor); + + Result register_special_device(u32 major, u32 minor, device_create_func_t initializer); + + Result init(); +} diff --git a/kernel/src/fs/devices/NullDevice.cpp b/kernel/src/fs/devices/NullDevice.cpp new file mode 100644 index 00000000..9d3d0094 --- /dev/null +++ b/kernel/src/fs/devices/NullDevice.cpp @@ -0,0 +1,6 @@ +#include "fs/devices/NullDevice.h" + +Result> NullDevice::create() +{ + return (SharedPtr)TRY(make_shared()); +} diff --git a/kernel/src/fs/devices/NullDevice.h b/kernel/src/fs/devices/NullDevice.h new file mode 100644 index 00000000..8461dbe3 --- /dev/null +++ b/kernel/src/fs/devices/NullDevice.h @@ -0,0 +1,21 @@ +#pragma once +#include "fs/devices/DeviceRegistry.h" + +class NullDevice : public Device +{ + public: + // Initializer for DeviceRegistry. + static Result> create(); + + Result read(u8*, usize, usize) const override + { + return 0; + } + + Result write(const u8*, usize, usize) override + { + return 0; + } + + virtual ~NullDevice() = default; +}; diff --git a/kernel/src/fs/tmpfs/FileSystem.cpp b/kernel/src/fs/tmpfs/FileSystem.cpp index 8ba8735d..0282f061 100644 --- a/kernel/src/fs/tmpfs/FileSystem.cpp +++ b/kernel/src/fs/tmpfs/FileSystem.cpp @@ -1,4 +1,5 @@ #include "fs/tmpfs/FileSystem.h" +#include "fs/devices/DeviceRegistry.h" #include #include #include @@ -17,9 +18,7 @@ namespace TmpFS { SharedPtr inode = TRY(make_shared()); inode->set_fs(*this, {}); - inode->set_inode_number(m_next_inode_number, {}); - TRY(m_inodes.try_append(inode)); - m_next_inode_number++; + inode->set_inode_number(m_next_inode_number++, {}); return (SharedPtr)inode; } @@ -32,10 +31,19 @@ namespace TmpFS inode->set_self(inode, {}); inode->set_fs(*this, {}); - inode->set_inode_number(m_next_inode_number, {}); + inode->set_inode_number(m_next_inode_number++, {}); - TRY(m_inodes.try_append(inode)); - m_next_inode_number++; + return (SharedPtr)inode; + } + + Result> FileSystem::create_device_inode(u32 major, u32 minor) + { + SharedPtr device = TRY(DeviceRegistry::create_special_device(major, minor)); + + SharedPtr inode = TRY(make_shared()); + inode->set_fs(*this, {}); + inode->set_inode_number(m_next_inode_number++, {}); + inode->set_device(device, {}); return (SharedPtr)inode; } diff --git a/kernel/src/fs/tmpfs/FileSystem.h b/kernel/src/fs/tmpfs/FileSystem.h index f7ec31b7..94efe12d 100644 --- a/kernel/src/fs/tmpfs/FileSystem.h +++ b/kernel/src/fs/tmpfs/FileSystem.h @@ -1,5 +1,6 @@ #pragma once #include "fs/VFS.h" +#include "fs/devices/DeviceRegistry.h" #include #include #include @@ -18,6 +19,7 @@ namespace TmpFS Result> create_file_inode() override; Result> create_dir_inode(SharedPtr parent) override; + Result> create_device_inode(u32 major, u32 minor) override; static Result> create(); @@ -29,7 +31,6 @@ namespace TmpFS void set_root(SharedPtr root); SharedPtr m_root_inode; - Vector> m_inodes; Atomic m_next_inode_number { 2 }; }; @@ -87,6 +88,77 @@ namespace TmpFS mode_t m_mode; }; + class DeviceInode : public VFS::DeviceInode + { + public: + DeviceInode() = default; + + void set_fs(FileSystem& fs, Badge) + { + m_fs = &fs; + } + + void set_inode_number(usize inum, Badge) + { + m_inode_number = inum; + } + + void set_device(SharedPtr device, Badge) + { + m_device = device; + } + + VFS::FileSystem& fs() const override + { + return *m_fs; + } + + usize inode_number() const override + { + return m_inode_number; + } + + Result read(u8* buf, usize offset, usize length) const override + { + return m_device->read(buf, offset, length); + } + + Result write(const u8* buf, usize offset, usize length) override + { + return m_device->write(buf, offset, length); + } + + Result truncate(usize) override + { + // POSIX says truncate is for regular files, but doesn't tell us what error to return for non-regular files. + return err(EINVAL); + } + + usize size() override + { + return 0; + } + + mode_t mode() override + { + return m_mode; + } + + Result chmod(mode_t mode) override + { + m_mode = mode; + return {}; + } + + virtual ~DeviceInode() = default; + + private: + VFS::FileSystem* m_fs; + SharedPtr m_device; + usize m_inode_number; + mode_t m_mode; + }; + class DirInode : public VFS::Inode { public: diff --git a/kernel/src/main.cpp b/kernel/src/main.cpp index 6688076a..406307a9 100644 --- a/kernel/src/main.cpp +++ b/kernel/src/main.cpp @@ -7,6 +7,7 @@ #include "arch/Timer.h" #include "boot/Init.h" #include "config.h" +#include "fs/devices/DeviceRegistry.h" #include "fs/tmpfs/FileSystem.h" #include "memory/Heap.h" #include "memory/KernelVM.h" @@ -51,6 +52,8 @@ static Result try_init_vfs() { VFS::root_fs = TRY(TmpFS::FileSystem::create()); + TRY(DeviceRegistry::init()); + InitRD::populate_vfs(); return {}; diff --git a/kernel/src/sys/mknod.cpp b/kernel/src/sys/mknod.cpp new file mode 100644 index 00000000..3b669aa3 --- /dev/null +++ b/kernel/src/sys/mknod.cpp @@ -0,0 +1,35 @@ +#include "Log.h" +#include "fs/VFS.h" +#include "memory/MemoryManager.h" +#include "sys/Syscall.h" +#include "thread/Scheduler.h" +#include +#include + +Result sys_mknod(Registers*, SyscallArgs args) +{ + auto path = TRY(MemoryManager::strdup_from_user(args[0])); + mode_t mode = (mode_t)args[1]; + dev_t dev = (dev_t)args[2]; + + u32 maj = luna_dev_major(dev); + u32 min = luna_dev_minor(dev); + + kdbgln("mknod: attempting to create device %u:%u in path %s", maj, min, path.chars()); + + auto parser = TRY(PathParser::create(path.chars())); + + auto dirname = TRY(parser.dirname()); + auto basename = TRY(parser.basename()); + + auto parent = TRY(VFS::resolve_path(dirname.chars())); + + if (parent->find(basename.chars()).has_value()) return err(EEXIST); + + auto inode = TRY(parent->fs().create_device_inode(maj, min)); + TRY(parent->add_entry(inode, basename.chars())); + + TRY(inode->chmod(mode)); + + return 0; +} diff --git a/libc/include/bits/makedev.h b/libc/include/bits/makedev.h new file mode 100644 index 00000000..3446a013 --- /dev/null +++ b/libc/include/bits/makedev.h @@ -0,0 +1,10 @@ +#ifndef _BITS_MAKEDEV_H +#define _BITS_MAKEDEV_H + +#include + +#define luna_dev_major(dev) (__u32_t)((dev) >> 32) +#define luna_dev_minor(dev) (__u32_t)((dev)&0xffffffff) +#define luna_dev_makedev(maj, min) (((__u64_t)(maj) << 32) | (__u64_t)(min)) + +#endif diff --git a/libc/include/sys/sysmacros.h b/libc/include/sys/sysmacros.h new file mode 100644 index 00000000..9512d0f6 --- /dev/null +++ b/libc/include/sys/sysmacros.h @@ -0,0 +1,12 @@ +/* sys/sysmacros.h: System macros. */ + +#ifndef _SYS_SYSMACROS_H +#define _SYS_SYSMACROS_H + +#include + +#define major luna_dev_major +#define minor luna_dev_minor +#define makedev luna_dev_makedev + +#endif diff --git a/libc/include/sys/types.h b/libc/include/sys/types.h index 9a9a02f4..fbf33018 100644 --- a/libc/include/sys/types.h +++ b/libc/include/sys/types.h @@ -14,6 +14,7 @@ typedef __i64_t time_t; typedef __u16_t mode_t; typedef __u64_t useconds_t; typedef __i64_t off_t; +typedef __u64_t dev_t; typedef off_t fpos_t; diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index e7e0f05b..a806f3d0 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -2,7 +2,7 @@ #define enumerate_syscalls(_e) \ _e(exit) _e(console_write) _e(clock_gettime) _e(mmap) _e(munmap) _e(usleep) _e(open) _e(close) _e(read) _e(getpid) \ - _e(write) _e(lseek) _e(mkdir) _e(exec) + _e(write) _e(lseek) _e(mkdir) _e(exec) _e(mknod) enum Syscalls {