kernel: Add support for special device files and add a mknod() syscall
This commit is contained in:
parent
d86da05bea
commit
7173c05a22
18
apps/hello.c
18
apps/hello.c
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
19
kernel/src/fs/devices/ConsoleDevice.cpp
Normal file
19
kernel/src/fs/devices/ConsoleDevice.cpp
Normal 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;
|
||||
}
|
15
kernel/src/fs/devices/ConsoleDevice.h
Normal file
15
kernel/src/fs/devices/ConsoleDevice.h
Normal 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;
|
||||
};
|
12
kernel/src/fs/devices/Device.h
Normal file
12
kernel/src/fs/devices/Device.h
Normal 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;
|
||||
};
|
49
kernel/src/fs/devices/DeviceRegistry.cpp
Normal file
49
kernel/src/fs/devices/DeviceRegistry.cpp
Normal 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 {};
|
||||
}
|
||||
}
|
22
kernel/src/fs/devices/DeviceRegistry.h
Normal file
22
kernel/src/fs/devices/DeviceRegistry.h
Normal 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();
|
||||
}
|
6
kernel/src/fs/devices/NullDevice.cpp
Normal file
6
kernel/src/fs/devices/NullDevice.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "fs/devices/NullDevice.h"
|
||||
|
||||
Result<SharedPtr<Device>> NullDevice::create()
|
||||
{
|
||||
return (SharedPtr<Device>)TRY(make_shared<NullDevice>());
|
||||
}
|
21
kernel/src/fs/devices/NullDevice.h
Normal file
21
kernel/src/fs/devices/NullDevice.h
Normal 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;
|
||||
};
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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
35
kernel/src/sys/mknod.cpp
Normal 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;
|
||||
}
|
10
libc/include/bits/makedev.h
Normal file
10
libc/include/bits/makedev.h
Normal 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
|
12
libc/include/sys/sysmacros.h
Normal file
12
libc/include/sys/sysmacros.h
Normal 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
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user