vfs: Implement syscalls

This commit is contained in:
2026-10-25 19:47:43 +01:00
parent 244618dbeb
commit eb86ea3622
11 changed files with 1823 additions and 205 deletions
+51 -51
View File
@@ -8,10 +8,10 @@ SYSCALL_DECLARE(2, getuname, 1)
SYSCALL_DECLARE(3, setuname, 1)
SYSCALL_DECLARE(4, archctl, 2)
// SYSCALL_DECLARE(5, reboot, -1)
// SYSCALL_DECLARE(6, mmap, -1)
// SYSCALL_DECLARE(7, munmap, -1)
// SYSCALL_DECLARE(8, mprotect, -1)
// SYSCALL_DECLARE(9, madvise, -1)
SYSCALL_DECLARE(6, mmap, 6)
SYSCALL_DECLARE(7, munmap, 2)
SYSCALL_DECLARE(8, mprotect, 3)
SYSCALL_DECLARE(9, madvise, 3)
// SYSCALL_DECLARE(10, sigprocmask, -1)
// SYSCALL_DECLARE(11, sigsuspend, -1)
// SYSCALL_DECLARE(12, sigpending, -1)
@@ -29,47 +29,47 @@ SYSCALL_DECLARE(4, archctl, 2)
// SYSCALL_DECLARE(24, getppid, -1)
// SYSCALL_DECLARE(25, waitid, -1)
// SYSCALL_DECLARE(26, waitpid, -1)
// SYSCALL_DECLARE(27, pread, -1)
// SYSCALL_DECLARE(28, readv, -1)
// SYSCALL_DECLARE(29, pwrite, -1)
// SYSCALL_DECLARE(30, writev, -1)
// SYSCALL_DECLARE(31, seek, -1)
// SYSCALL_DECLARE(32, ioctl, -1)
// SYSCALL_DECLARE(33, openat, -1)
// SYSCALL_DECLARE(34, close, -1)
// SYSCALL_DECLARE(35, fstat, -1)
// SYSCALL_DECLARE(36, fstatat, -1)
// SYSCALL_DECLARE(37, statvfs, -1)
// SYSCALL_DECLARE(38, fstatvfs, -1)
// SYSCALL_DECLARE(39, faccessat, -1)
// SYSCALL_DECLARE(40, fcntl, -1)
// SYSCALL_DECLARE(41, ftruncate, -1)
// SYSCALL_DECLARE(42, truncate, -1)
// SYSCALL_DECLARE(43, fallocate, -1)
// SYSCALL_DECLARE(44, fadvise, -1)
// SYSCALL_DECLARE(45, utimensat, -1)
// SYSCALL_DECLARE(46, mknodat, -1)
// SYSCALL_DECLARE(47, getcwd, -1)
// SYSCALL_DECLARE(48, chdir, -1)
// SYSCALL_DECLARE(49, fchdir, -1)
// SYSCALL_DECLARE(50, mkdirat, -1)
// SYSCALL_DECLARE(51, rmdirat, -1)
// SYSCALL_DECLARE(52, getdents, -1)
// SYSCALL_DECLARE(53, renameat, -1)
// SYSCALL_DECLARE(54, fchmod, -1)
// SYSCALL_DECLARE(55, fchmodat, -1)
// SYSCALL_DECLARE(56, fchownat, -1)
// SYSCALL_DECLARE(57, linkat, -1)
// SYSCALL_DECLARE(58, symlinkat, -1)
// SYSCALL_DECLARE(59, unlinkat, -1)
// SYSCALL_DECLARE(60, readlinkat, -1)
// SYSCALL_DECLARE(61, flock, -1)
// SYSCALL_DECLARE(62, ppoll, -1)
// SYSCALL_DECLARE(63, dup, -1)
// SYSCALL_DECLARE(64, dup3, -1)
// SYSCALL_DECLARE(65, sync, -1)
// SYSCALL_DECLARE(66, fsync, -1)
// SYSCALL_DECLARE(67, fdatasync, -1)
SYSCALL_DECLARE(27, pread, 4)
SYSCALL_DECLARE(28, readv, 3)
SYSCALL_DECLARE(29, pwrite, 4)
SYSCALL_DECLARE(30, writev, 3)
SYSCALL_DECLARE(31, seek, 3)
SYSCALL_DECLARE(32, ioctl, 3)
SYSCALL_DECLARE(33, openat, 4)
SYSCALL_DECLARE(34, close, 1)
SYSCALL_DECLARE(35, fstat, 2)
SYSCALL_DECLARE(36, fstatat, 4)
SYSCALL_DECLARE(37, statvfs, 2)
SYSCALL_DECLARE(38, fstatvfs, 2)
SYSCALL_DECLARE(39, faccessat, 4)
SYSCALL_DECLARE(40, fcntl, 3)
SYSCALL_DECLARE(41, ftruncate, 2)
SYSCALL_DECLARE(42, truncate, 2)
SYSCALL_DECLARE(43, fallocate, 3)
SYSCALL_DECLARE(44, fadvise, 4)
SYSCALL_DECLARE(45, utimensat, 4)
SYSCALL_DECLARE(46, mknodat, 4)
SYSCALL_DECLARE(47, getcwd, 2)
SYSCALL_DECLARE(48, chdir, 1)
SYSCALL_DECLARE(49, fchdir, 1)
SYSCALL_DECLARE(50, mkdirat, 3)
SYSCALL_DECLARE(51, rmdirat, 2)
SYSCALL_DECLARE(52, getdents, 3)
SYSCALL_DECLARE(53, renameat, 4)
SYSCALL_DECLARE(54, fchmod, 2)
SYSCALL_DECLARE(55, fchmodat, 4)
SYSCALL_DECLARE(56, fchownat, 5)
SYSCALL_DECLARE(57, linkat, 5)
SYSCALL_DECLARE(58, symlinkat, 3)
SYSCALL_DECLARE(59, unlinkat, 3)
SYSCALL_DECLARE(60, readlinkat, 4)
SYSCALL_DECLARE(61, flock, 2)
SYSCALL_DECLARE(62, ppoll, 4)
SYSCALL_DECLARE(63, dup, 1)
SYSCALL_DECLARE(64, dup3, 3)
SYSCALL_DECLARE(65, sync, 0)
SYSCALL_DECLARE(66, fsync, 1)
SYSCALL_DECLARE(67, fdatasync, 1)
// SYSCALL_DECLARE(68, getgroups, -1)
// SYSCALL_DECLARE(69, setgroups, -1)
// SYSCALL_DECLARE(70, getsid, -1)
@@ -90,8 +90,8 @@ SYSCALL_DECLARE(4, archctl, 2)
// SYSCALL_DECLARE(85, setresgid, -1)
// SYSCALL_DECLARE(86, setreuid, -1)
// SYSCALL_DECLARE(87, setregid, -1)
// SYSCALL_DECLARE(88, umask, -1)
// SYSCALL_DECLARE(89, pipe, -1)
SYSCALL_DECLARE(88, umask, 1)
SYSCALL_DECLARE(89, pipe, 2)
// SYSCALL_DECLARE(90, futex_wait, -1)
// SYSCALL_DECLARE(91, futex_wake, -1)
// SYSCALL_DECLARE(92, thread_create, -1)
@@ -111,9 +111,9 @@ SYSCALL_DECLARE(4, archctl, 2)
// SYSCALL_DECLARE(106, clock_set, -1)
// SYSCALL_DECLARE(107, sleep, -1)
// SYSCALL_DECLARE(108, yield, -1)
// SYSCALL_DECLARE(109, chroot, -1)
// SYSCALL_DECLARE(110, mount, -1)
// SYSCALL_DECLARE(111, umount, -1)
SYSCALL_DECLARE(109, chroot, 1)
SYSCALL_DECLARE(110, mount, 4)
SYSCALL_DECLARE(111, umount, 2)
// SYSCALL_DECLARE(112, socket, -1)
// SYSCALL_DECLARE(113, socketpair, -1)
// SYSCALL_DECLARE(114, shutdown, -1)
@@ -145,5 +145,5 @@ SYSCALL_DECLARE(4, archctl, 2)
// SYSCALL_DECLARE(140, timerfd_settime, -1)
// SYSCALL_DECLARE(141, signalfd_create, -1)
// SYSCALL_DECLARE(142, eventfd_create, -1)
// SYSCALL_DECLARE(143, msync, -1)
SYSCALL_DECLARE(143, msync, 3)
// SYSCALL_DECLARE(144, sysconf, -1)
-6
View File
@@ -1,6 +0,0 @@
#include <zinnia/syscall.h>
#include <arch/archctl.h>
struct syscall_ret sys_archctl(uintptr_t op, uintptr_t arg) {
return arch_archctl(op, arg);
}
-12
View File
@@ -1,12 +0,0 @@
#include <zinnia/panic.h>
#include <zinnia/syscall.h>
struct syscall_ret sys_exit(int err) {
struct syscall_ret ret = {};
ret.error = ENOSYS;
panic("sys::exit() not implemented!\n");
return ret;
}
+12
View File
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: ISC
#pragma once
#include <zinnia/percpu.h>
#include <zinnia/process.h>
#include <zinnia/sched.h>
#include <zinnia/syscall.h>
static inline struct process* self() {
return percpu_get()->sched.current->process;
}
+159
View File
@@ -0,0 +1,159 @@
// SPDX-License-Identifier: ISC
#include <zinnia/atomic.h>
#include <zinnia/mman.h>
#include <zinnia/vfs.h>
#include <zinnia/vma.h>
#include <zinnia/vmo.h>
#include <arch/mem.h>
#include <stdckdint.h>
#include <uapi/fcntl.h>
#include "internal.h"
static struct vma* space() {
return percpu_get()->sched.current->space;
}
constexpr enum prot_flags PROT_MASK = (PROT_READ | PROT_WRITE | PROT_EXEC);
static bool is_user_range(uintptr_t addr, size_t len) {
uintptr_t last;
if (len == 0 || ckd_add(&last, addr, len - 1))
return false;
return arch_mem_addr_is_user(addr) && arch_mem_addr_is_user(last);
}
static errno_t map_anon(struct vma* vma, uintptr_t addr, size_t len, uint32_t prot, uint32_t flags) {
if (!(flags & MAP_SHARED))
return vma_map(vma, nullptr, addr, len, prot, 0, 0);
struct vmo* anon;
errno_t status = vmo_new_anon(&anon);
if (status)
return status;
status = vma_map(vma, anon, addr, len, prot, VMA_SHARED, 0);
vmo_unref(anon);
return status;
}
static errno_t get_map_file(int fd, uint32_t prot, uint32_t flags, struct vfs_file** out) {
struct vfs_file* file = process_fd_get(self(), fd);
if (!file)
return EBADF;
if (!(atomic_load(&file->flags, ATOMIC_RELAXED) & O_RDONLY) ||
((flags & MAP_SHARED) && (prot & PROT_WRITE) && !(atomic_load(&file->flags, ATOMIC_RELAXED) & O_WRONLY))) {
vfs_file_unref(file);
return EACCES;
}
*out = file;
return 0;
}
struct syscall_ret sys_mmap(uintptr_t addr, size_t len, uint32_t prot, uint32_t flags, int fd, off_t offset) {
struct syscall_ret ret = {};
const size_t page_size = arch_mem_page_size();
const uint32_t kind = flags & (MAP_SHARED | MAP_PRIVATE);
if (len == 0 || (prot & ~PROT_MASK) || (kind != MAP_SHARED && kind != MAP_PRIVATE)) {
ret.error = EINVAL;
return ret;
}
if (offset < 0 || (uint64_t)offset % page_size != 0) {
ret.error = EINVAL;
return ret;
}
if ((flags & MAP_FIXED) && (addr % page_size != 0 || !is_user_range(addr, len))) {
ret.error = EINVAL;
return ret;
}
struct vfs_file* file = nullptr;
if (!(flags & MAP_ANON)) {
ret.error = get_map_file(fd, prot, flags, &file);
if (ret.error)
return ret;
}
struct vma* vma = space();
mutex_lock(&vma->map_lock);
if (!(flags & MAP_FIXED))
ret.error = vma_find_free(vma, addr, len, &addr);
if (!ret.error) {
if (file)
ret.error = vfs_file_mmap(file, vma, addr, len, prot, flags, offset, &addr);
else
ret.error = map_anon(vma, addr, len, prot, flags);
}
mutex_unlock(&vma->map_lock);
if (file)
vfs_file_unref(file);
if (!ret.error)
ret.value = addr;
return ret;
}
struct syscall_ret sys_mprotect(uintptr_t addr, size_t len, uint32_t prot) {
struct syscall_ret ret = {};
if ((prot & ~PROT_MASK) || !is_user_range(addr, len)) {
ret.error = EINVAL;
return ret;
}
struct vma* vma = space();
mutex_lock(&vma->map_lock);
ret.error = vma_protect(vma, addr, len, prot);
mutex_unlock(&vma->map_lock);
return ret;
}
struct syscall_ret sys_munmap(uintptr_t addr, size_t len) {
struct syscall_ret ret = {};
if (!is_user_range(addr, len)) {
ret.error = EINVAL;
return ret;
}
struct vma* vma = space();
mutex_lock(&vma->map_lock);
ret.error = vma_unmap(vma, addr, len);
mutex_unlock(&vma->map_lock);
return ret;
}
struct syscall_ret sys_madvise(uintptr_t addr, size_t len, int advice) {
struct syscall_ret ret = {};
if (addr % arch_mem_page_size() != 0)
ret.error = EINVAL;
return ret;
}
struct syscall_ret sys_msync(uintptr_t addr, size_t len, int flags) {
struct syscall_ret ret = {};
if (flags & ~(MS_ASYNC | MS_INVALIDATE | MS_SYNC)) {
ret.error = EINVAL;
return ret;
}
if ((flags & MS_ASYNC) && (flags & MS_SYNC)) {
ret.error = EINVAL;
return ret;
}
if (addr % arch_mem_page_size() != 0 || !is_user_range(addr, len)) {
ret.error = EINVAL;
return ret;
}
if (flags & MS_ASYNC) {
if (!vma_range_mapped(space(), addr, len))
ret.error = ENOMEM;
return ret;
}
ret.error = vma_sync(space(), addr, len);
return ret;
}
+4 -4
View File
@@ -1,7 +1,7 @@
kernel_sources += files(
'archctl.c',
'dispatch.c',
'exit.c',
'syslog.c',
'uname.c',
'memory.c',
'process.c',
'system.c',
'vfs.c',
)
+26
View File
@@ -0,0 +1,26 @@
// SPDX-License-Identifier: ISC
#include <zinnia/panic.h>
#include <zinnia/syscall.h>
#include "internal.h"
struct syscall_ret sys_exit(int err) {
struct syscall_ret ret = {};
ret.error = ENOSYS;
panic("sys::exit() not implemented!\n");
return ret;
}
struct syscall_ret sys_umask(mode_t mask) {
struct syscall_ret ret = {};
struct process* proc = self();
mutex_lock(&proc->lock);
ret.value = proc->umask;
proc->umask = mask & 0777;
mutex_unlock(&proc->lock);
return ret;
}
-67
View File
@@ -1,67 +0,0 @@
#include <zinnia/alloc.h>
#include <zinnia/panic.h>
#include <zinnia/print.h>
#include <zinnia/syscall.h>
#include <zinnia/usercopy.h>
#include <stdint.h>
enum syslog_kind : uintptr_t {
SYSLOG_EMERG = 0,
SYSLOG_ALERT = 1,
SYSLOG_CRIT = 2,
SYSLOG_ERR = 3,
SYSLOG_WARNING = 4,
SYSLOG_NOTICE = 5,
SYSLOG_INFO = 6,
SYSLOG_DEBUG = 7,
};
struct syscall_ret sys_syslog(enum syslog_kind kind, __user char* message, size_t len) {
struct syscall_ret ret = {};
const size_t buf_len = MIN(len + 1, 256);
char* buf = mem_alloc(buf_len, ALLOC_NOZERO);
if (!usercopy_read(buf, message, buf_len - 1)) {
mem_free(buf);
ret.error = EFAULT;
return ret;
}
buf[buf_len - 1] = 0;
const char* log_level;
switch (kind) {
case SYSLOG_EMERG:
log_level = "emerg";
break;
case SYSLOG_ALERT:
log_level = "alert";
break;
case SYSLOG_CRIT:
log_level = "crit";
break;
case SYSLOG_ERR:
log_level = "err";
break;
case SYSLOG_WARNING:
log_level = "warning";
break;
case SYSLOG_NOTICE:
log_level = "notice";
break;
case SYSLOG_INFO:
log_level = "info";
break;
case SYSLOG_DEBUG:
log_level = "debug";
break;
default:
log_level = "invalid";
}
log("syslog(%s): %s\n", log_level, buf);
mem_free(buf);
return ret;
}
+141
View File
@@ -0,0 +1,141 @@
// SPDX-License-Identifier: ISC
#include <zinnia/alloc.h>
#include <zinnia/ident.h>
#include <zinnia/print.h>
#include <zinnia/spin.h>
#include <zinnia/syscall.h>
#include <zinnia/usercopy.h>
#include <arch/archctl.h>
#include <config.h>
#include <stdint.h>
#include <string.h>
#include <uapi/utsname.h>
#include "internal.h"
enum syslog_kind : uintptr_t {
SYSLOG_EMERG = 0,
SYSLOG_ALERT = 1,
SYSLOG_CRIT = 2,
SYSLOG_ERR = 3,
SYSLOG_WARNING = 4,
SYSLOG_NOTICE = 5,
SYSLOG_INFO = 6,
SYSLOG_DEBUG = 7,
};
static struct spinlock uname_lock = {};
static struct __utsname uname_buf = {
.sysname = "Zinnia",
.release = ZINNIA_VERSION,
.version = CONFIG_KERNEL_FLAVOR,
.nodename = "localhost",
.machine = ZINNIA_ARCH,
.domainname = "(none)",
};
struct syscall_ret sys_archctl(uintptr_t op, uintptr_t arg) {
return arch_archctl(op, arg);
}
struct syscall_ret sys_getuname(__user struct __utsname* ptr) {
struct syscall_ret ret = {};
if (!ptr) {
ret.error = EINVAL;
return ret;
}
spin_lock(&uname_lock);
struct __utsname write_buf = uname_buf;
spin_unlock(&uname_lock);
if (!usercopy_write(ptr, &write_buf, sizeof(write_buf)))
ret.error = EFAULT;
return ret;
}
struct syscall_ret sys_setuname(const __user struct __utsname* ptr) {
struct syscall_ret ret = {};
if (!ptr) {
ret.error = EINVAL;
return ret;
}
if (!ident_is_privileged(&self()->identity, false)) {
ret.error = EPERM;
return ret;
}
struct __utsname read_buf;
if (!usercopy_read(&read_buf, ptr, sizeof(read_buf))) {
ret.error = EFAULT;
return ret;
}
spin_lock(&uname_lock);
strncpy(uname_buf.sysname, read_buf.sysname, sizeof(uname_buf.sysname) - 1);
strncpy(uname_buf.release, read_buf.release, sizeof(uname_buf.release) - 1);
strncpy(uname_buf.version, read_buf.version, sizeof(uname_buf.version) - 1);
strncpy(uname_buf.nodename, read_buf.nodename, sizeof(uname_buf.nodename) - 1);
strncpy(uname_buf.machine, read_buf.machine, sizeof(uname_buf.machine) - 1);
strncpy(uname_buf.domainname, read_buf.domainname, sizeof(uname_buf.domainname) - 1);
spin_unlock(&uname_lock);
return ret;
}
struct syscall_ret sys_syslog(enum syslog_kind kind, __user char* message, size_t len) {
struct syscall_ret ret = {};
const size_t buf_len = MIN(len + 1, 256);
char* buf = mem_alloc(buf_len, ALLOC_NOZERO);
if (!usercopy_read(buf, message, buf_len - 1)) {
mem_free(buf);
ret.error = EFAULT;
return ret;
}
buf[buf_len - 1] = 0;
const char* log_level;
switch (kind) {
case SYSLOG_EMERG:
log_level = "emerg";
break;
case SYSLOG_ALERT:
log_level = "alert";
break;
case SYSLOG_CRIT:
log_level = "crit";
break;
case SYSLOG_ERR:
log_level = "err";
break;
case SYSLOG_WARNING:
log_level = "warning";
break;
case SYSLOG_NOTICE:
log_level = "notice";
break;
case SYSLOG_INFO:
log_level = "info";
break;
case SYSLOG_DEBUG:
log_level = "debug";
break;
default:
log_level = "invalid";
}
log("syslog(%s): %s\n", log_level, buf);
mem_free(buf);
return ret;
}
-65
View File
@@ -1,65 +0,0 @@
#include <zinnia/spin.h>
#include <zinnia/syscall.h>
#include <zinnia/usercopy.h>
#include <config.h>
#include <string.h>
#include <uapi/utsname.h>
static struct spinlock uname_lock = {};
static struct __utsname uname_buf = {
.sysname = "Zinnia",
.release = ZINNIA_VERSION,
.version = CONFIG_KERNEL_FLAVOR,
.nodename = "localhost",
.machine = ZINNIA_ARCH,
.domainname = "(none)",
};
struct syscall_ret sys_setuname(const __user struct __utsname* ptr) {
struct syscall_ret ret = {};
if (!ptr) {
ret.error = EINVAL;
return ret;
}
// TODO: User has to be root to do this.
struct __utsname read_buf;
if (!usercopy_read(&read_buf, ptr, sizeof(read_buf))) {
ret.error = EFAULT;
return ret;
}
spin_lock(&uname_lock);
strncpy(uname_buf.sysname, read_buf.sysname, sizeof(uname_buf.sysname) - 1);
strncpy(uname_buf.release, read_buf.release, sizeof(uname_buf.release) - 1);
strncpy(uname_buf.version, read_buf.version, sizeof(uname_buf.version) - 1);
strncpy(uname_buf.nodename, read_buf.nodename, sizeof(uname_buf.nodename) - 1);
strncpy(uname_buf.machine, read_buf.machine, sizeof(uname_buf.machine) - 1);
strncpy(uname_buf.domainname, read_buf.domainname, sizeof(uname_buf.domainname) - 1);
spin_unlock(&uname_lock);
return ret;
}
struct syscall_ret sys_getuname(__user struct __utsname* ptr) {
struct syscall_ret ret = {};
if (!ptr) {
ret.error = EINVAL;
return ret;
}
spin_lock(&uname_lock);
struct __utsname write_buf = uname_buf;
spin_unlock(&uname_lock);
if (!usercopy_write(ptr, &write_buf, sizeof(write_buf)))
ret.error = EFAULT;
return ret;
}
+1430
View File
File diff suppressed because it is too large Load Diff