kernel: Add support for special device files and add a mknod() syscall

This commit is contained in:
apio 2023-03-18 09:10:33 +01:00
parent d86da05bea
commit 7173c05a22
Signed by: apio
GPG Key ID: B8A7D06E42258954
18 changed files with 357 additions and 9 deletions

View File

@ -1,6 +1,24 @@
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysmacros.h>
#include <unistd.h>
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);
}

View File

@ -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
)

View File

@ -7,7 +7,8 @@ namespace VFS
enum class InodeType
{
RegularFile,
Directory
Directory,
Device
};
class FileSystem;
@ -22,6 +23,8 @@ namespace VFS
virtual Result<SharedPtr<Inode>> create_subdirectory(const char* name) = 0;
virtual Result<void> add_entry(SharedPtr<Inode> inode, const char* name) = 0;
// File-specific methods
virtual Result<usize> read(u8* buf, usize offset, usize length) const = 0;
@ -65,6 +68,11 @@ namespace VFS
return err(ENOTDIR);
}
Result<void> add_entry(SharedPtr<Inode>, 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<SharedPtr<Inode>> find(const char*) const override
{
return err(ENOTDIR);
}
Result<SharedPtr<Inode>> create_file(const char*) override
{
return err(ENOTDIR);
}
Result<SharedPtr<Inode>> create_subdirectory(const char*) override
{
return err(ENOTDIR);
}
Result<void> add_entry(SharedPtr<Inode>, 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<SharedPtr<Inode>> create_dir_inode(SharedPtr<Inode> parent) = 0;
virtual Result<SharedPtr<Inode>> create_device_inode(u32 major, u32 minor) = 0;
virtual ~FileSystem() = default;
};

View File

@ -0,0 +1,19 @@
#include "fs/devices/ConsoleDevice.h"
#include "video/TextConsole.h"
Result<SharedPtr<Device>> ConsoleDevice::create()
{
return (SharedPtr<Device>)TRY(make_shared<ConsoleDevice>());
}
// FIXME: Read from the keyboard.
Result<usize> ConsoleDevice::read(u8*, usize, usize) const
{
return 0;
}
Result<usize> ConsoleDevice::write(const u8* buf, usize, usize length)
{
TextConsole::print((const char*)buf);
return length;
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "fs/devices/DeviceRegistry.h"
class ConsoleDevice : public Device
{
public:
// Initializer for DeviceRegistry.
static Result<SharedPtr<Device>> create();
Result<usize> read(u8*, usize, usize) const override;
Result<usize> write(const u8*, usize, usize) override;
virtual ~ConsoleDevice() = default;
};

View File

@ -0,0 +1,12 @@
#pragma once
#include <luna/Result.h>
class Device
{
public:
virtual Result<usize> read(u8* buf, usize offset, usize length) const = 0;
virtual Result<usize> write(const u8* buf, usize offset, usize length) = 0;
virtual ~Device() = default;
};

View File

@ -0,0 +1,49 @@
#include "fs/devices/DeviceRegistry.h"
#include "Log.h"
#include "fs/devices/ConsoleDevice.h"
#include "fs/devices/NullDevice.h"
#include <luna/Vector.h>
struct DeviceDescriptor
{
device_create_func_t initializer;
u32 major;
u32 minor;
};
Vector<DeviceDescriptor> g_available_devices = {};
namespace DeviceRegistry
{
Result<SharedPtr<Device>> 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<void> 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<void> init()
{
// Hardcode default devices.
TRY(register_special_device(DeviceMajorTypes::Memory, 0, NullDevice::create));
TRY(register_special_device(DeviceMajorTypes::Console, 0, ConsoleDevice::create));
return {};
}
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "fs/devices/Device.h"
#include <luna/SharedPtr.h>
typedef Result<SharedPtr<Device>> (*device_create_func_t)(void);
namespace DeviceRegistry
{
enum DeviceMajorTypes : u32
{
Null = 0,
Console = 1,
Memory = 2
};
Result<SharedPtr<Device>> create_special_device(u32 major, u32 minor);
Result<void> register_special_device(u32 major, u32 minor, device_create_func_t initializer);
Result<void> init();
}

View File

@ -0,0 +1,6 @@
#include "fs/devices/NullDevice.h"
Result<SharedPtr<Device>> NullDevice::create()
{
return (SharedPtr<Device>)TRY(make_shared<NullDevice>());
}

View File

@ -0,0 +1,21 @@
#pragma once
#include "fs/devices/DeviceRegistry.h"
class NullDevice : public Device
{
public:
// Initializer for DeviceRegistry.
static Result<SharedPtr<Device>> create();
Result<usize> read(u8*, usize, usize) const override
{
return 0;
}
Result<usize> write(const u8*, usize, usize) override
{
return 0;
}
virtual ~NullDevice() = default;
};

View File

@ -1,4 +1,5 @@
#include "fs/tmpfs/FileSystem.h"
#include "fs/devices/DeviceRegistry.h"
#include <luna/Alloc.h>
#include <luna/CString.h>
#include <luna/Ignore.h>
@ -17,9 +18,7 @@ namespace TmpFS
{
SharedPtr<FileInode> inode = TRY(make_shared<FileInode>());
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<VFS::Inode>)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<VFS::Inode>)inode;
}
Result<SharedPtr<VFS::Inode>> FileSystem::create_device_inode(u32 major, u32 minor)
{
SharedPtr<Device> device = TRY(DeviceRegistry::create_special_device(major, minor));
SharedPtr<DeviceInode> inode = TRY(make_shared<DeviceInode>());
inode->set_fs(*this, {});
inode->set_inode_number(m_next_inode_number++, {});
inode->set_device(device, {});
return (SharedPtr<VFS::Inode>)inode;
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "fs/VFS.h"
#include "fs/devices/DeviceRegistry.h"
#include <luna/Atomic.h>
#include <luna/Badge.h>
#include <luna/Buffer.h>
@ -18,6 +19,7 @@ namespace TmpFS
Result<SharedPtr<VFS::Inode>> create_file_inode() override;
Result<SharedPtr<VFS::Inode>> create_dir_inode(SharedPtr<VFS::Inode> parent) override;
Result<SharedPtr<VFS::Inode>> create_device_inode(u32 major, u32 minor) override;
static Result<SharedPtr<VFS::FileSystem>> create();
@ -29,7 +31,6 @@ namespace TmpFS
void set_root(SharedPtr<VFS::Inode> root);
SharedPtr<VFS::Inode> m_root_inode;
Vector<SharedPtr<VFS::Inode>> m_inodes;
Atomic<usize> 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<FileSystem>)
{
m_fs = &fs;
}
void set_inode_number(usize inum, Badge<FileSystem>)
{
m_inode_number = inum;
}
void set_device(SharedPtr<Device> device, Badge<FileSystem>)
{
m_device = device;
}
VFS::FileSystem& fs() const override
{
return *m_fs;
}
usize inode_number() const override
{
return m_inode_number;
}
Result<usize> read(u8* buf, usize offset, usize length) const override
{
return m_device->read(buf, offset, length);
}
Result<usize> write(const u8* buf, usize offset, usize length) override
{
return m_device->write(buf, offset, length);
}
Result<void> 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<void> chmod(mode_t mode) override
{
m_mode = mode;
return {};
}
virtual ~DeviceInode() = default;
private:
VFS::FileSystem* m_fs;
SharedPtr<Device> m_device;
usize m_inode_number;
mode_t m_mode;
};
class DirInode : public VFS::Inode
{
public:

View File

@ -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<void> try_init_vfs()
{
VFS::root_fs = TRY(TmpFS::FileSystem::create());
TRY(DeviceRegistry::init());
InitRD::populate_vfs();
return {};

35
kernel/src/sys/mknod.cpp Normal file
View File

@ -0,0 +1,35 @@
#include "Log.h"
#include "fs/VFS.h"
#include "memory/MemoryManager.h"
#include "sys/Syscall.h"
#include "thread/Scheduler.h"
#include <bits/makedev.h>
#include <luna/PathParser.h>
Result<u64> 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;
}

View File

@ -0,0 +1,10 @@
#ifndef _BITS_MAKEDEV_H
#define _BITS_MAKEDEV_H
#include <bits/fixed-size-types.h>
#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

View File

@ -0,0 +1,12 @@
/* sys/sysmacros.h: System macros. */
#ifndef _SYS_SYSMACROS_H
#define _SYS_SYSMACROS_H
#include <bits/makedev.h>
#define major luna_dev_major
#define minor luna_dev_minor
#define makedev luna_dev_makedev
#endif

View File

@ -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;

View File

@ -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
{