This commit is contained in:
2026-07-15 22:09:13 +02:00
commit cf33867e68
152 changed files with 9663 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
#include <zinnia/archctl.h>
#include <kernel/syscalls.h>
zn_status_t arch_archctl(zn_archctl_t op, uintptr_t value);
zn_status_t syscall_archctl(struct arch_context* ctx) {
return arch_archctl((zn_archctl_t)ctx->ARCH_CTX_A0, (uintptr_t)ctx->ARCH_CTX_A1);
}
+259
View File
@@ -0,0 +1,259 @@
#include <zinnia/channel.h>
#include <zinnia/handle.h>
#include <zinnia/status.h>
#include <kernel/alloc.h>
#include <kernel/channel.h>
#include <kernel/namespace.h>
#include <kernel/percpu.h>
#include <kernel/sched.h>
#include <kernel/syscalls.h>
#include <kernel/usercopy.h>
zn_status_t syscall_channel_create(struct arch_context* ctx) {
__user zn_handle_t* end0 = (__user zn_handle_t*)ctx->ARCH_CTX_A1;
__user zn_handle_t* end1 = (__user zn_handle_t*)ctx->ARCH_CTX_A2;
struct channel* ep0;
struct channel* ep1;
zn_status_t status = channel_create(&ep0, &ep1);
if (status != ZN_OK)
return status;
struct task* current = percpu_get()->sched.current;
struct namespace_desc desc0 = {
.type = NAMESPACE_DESC_CHANNEL,
.rights = ZN_RIGHTS_ALL,
.value.channel = ep0,
};
struct namespace_desc desc1 = {
.type = NAMESPACE_DESC_CHANNEL,
.rights = ZN_RIGHTS_ALL,
.value.channel = ep1,
};
zn_handle_t h0, h1;
status = namespace_add_desc(current->namespace, desc0, &h0);
if (status != ZN_OK)
return status;
status = namespace_add_desc(current->namespace, desc1, &h1);
if (status != ZN_OK) {
namespace_del_desc(current->namespace, h0);
return status;
}
if (!usercopy_write(end0, &h0, sizeof(h0))) {
namespace_del_desc(current->namespace, h0);
namespace_del_desc(current->namespace, h1);
return ZN_ERR_BAD_BUFFER;
}
if (!usercopy_write(end1, &h1, sizeof(h1))) {
namespace_del_desc(current->namespace, h0);
namespace_del_desc(current->namespace, h1);
return ZN_ERR_BAD_BUFFER;
}
return ZN_OK;
}
zn_status_t syscall_channel_write(struct arch_context* ctx) {
zn_handle_t ch_handle = ctx->ARCH_CTX_A0;
__user zn_handle_t* handles = (__user zn_handle_t*)ctx->ARCH_CTX_A1;
__user void* bytes = (__user void*)ctx->ARCH_CTX_A2;
size_t num_handles = ctx->ARCH_CTX_A3;
size_t num_bytes = ctx->ARCH_CTX_A4;
struct task* current = percpu_get()->sched.current;
// Look up the channel.
struct namespace_desc desc;
zn_status_t status = namespace_get(current->namespace, ch_handle, &desc);
if (status != ZN_OK)
return status;
if (desc.type != NAMESPACE_DESC_CHANNEL)
return ZN_ERR_BAD_HANDLE;
struct channel* ch = desc.value.channel;
// Copy byte data from userspace.
void* msg_data = nullptr;
if (num_bytes > 0) {
if (!bytes)
return ZN_ERR_INVALID;
msg_data = mem_alloc(num_bytes, ALLOC_NOZERO);
if (!msg_data)
return ZN_ERR_NO_MEMORY;
if (!usercopy_read(msg_data, bytes, num_bytes)) {
mem_free(msg_data);
return ZN_ERR_BAD_BUFFER;
}
}
// Copy and transfer handles (move semantics).
struct namespace_desc* descs = nullptr;
if (num_handles > 0) {
if (!handles) {
mem_free(msg_data);
return ZN_ERR_INVALID;
}
// Read handle values from userspace.
size_t handles_size = num_handles * sizeof(zn_handle_t);
zn_handle_t* src_handles = mem_alloc(handles_size, ALLOC_NOZERO);
if (!src_handles) {
mem_free(msg_data);
return ZN_ERR_NO_MEMORY;
}
if (!usercopy_read(src_handles, handles, handles_size)) {
mem_free(src_handles);
mem_free(msg_data);
return ZN_ERR_BAD_BUFFER;
}
// Resolve all handles to descriptors before removing any.
descs = mem_alloc(num_handles * sizeof(struct namespace_desc), ALLOC_NOZERO);
if (!descs) {
mem_free(src_handles);
mem_free(msg_data);
return ZN_ERR_NO_MEMORY;
}
for (size_t i = 0; i < num_handles; i++) {
status = namespace_get(current->namespace, src_handles[i], &descs[i]);
if (status != ZN_OK) {
mem_free(descs);
mem_free(src_handles);
mem_free(msg_data);
return ZN_ERR_BAD_HANDLE;
}
}
// Remove handles from sender's namespace.
for (size_t i = 0; i < num_handles; i++) {
namespace_del_desc(current->namespace, src_handles[i]);
}
mem_free(src_handles);
}
// Send the message through the channel.
status = channel_write(ch, msg_data, num_bytes, descs, num_handles);
if (status != ZN_OK) {
mem_free(msg_data);
mem_free(descs);
return status;
}
return ZN_OK;
}
zn_status_t syscall_channel_read(struct arch_context* ctx) {
zn_handle_t ch_handle = ctx->ARCH_CTX_A0;
__user zn_handle_t* handles = (__user zn_handle_t*)ctx->ARCH_CTX_A1;
__user void* bytes = (__user void*)ctx->ARCH_CTX_A2;
size_t num_handles = ctx->ARCH_CTX_A3;
size_t num_bytes = ctx->ARCH_CTX_A4;
__user size_t* read_handles = (__user size_t*)ctx->ARCH_CTX_A5;
__user size_t* read_bytes = (__user size_t*)ctx->ARCH_CTX_A6;
struct task* current = percpu_get()->sched.current;
struct sched_percpu* sched = &percpu_get()->sched;
// Look up the channel.
struct namespace_desc ch_desc;
zn_status_t status = namespace_get(current->namespace, ch_handle, &ch_desc);
if (status != ZN_OK)
return status;
if (ch_desc.type != NAMESPACE_DESC_CHANNEL)
return ZN_ERR_BAD_HANDLE;
struct channel* ch = ch_desc.value.channel;
// Block until a message is available.
while (true) {
struct channel_msg* msg;
status = channel_read(ch, &msg);
if (status == ZN_OK) {
// Copy byte data to userspace.
size_t bytes_to_copy = msg->num_bytes < num_bytes ? msg->num_bytes : num_bytes;
if (bytes_to_copy > 0 && bytes) {
if (!usercopy_write(bytes, msg->data, bytes_to_copy)) {
channel_msg_free(msg);
return ZN_ERR_BAD_BUFFER;
}
}
// Transfer handle descriptors into the reader's namespace.
size_t handles_to_copy = msg->num_handles < num_handles ? msg->num_handles : num_handles;
if (handles_to_copy > 0 && handles) {
for (size_t i = 0; i < handles_to_copy; i++) {
zn_handle_t new_handle;
status = namespace_add_desc(current->namespace, msg->descs[i], &new_handle);
if (status != ZN_OK) {
channel_msg_free(msg);
return status;
}
if (!usercopy_write(&handles[i], &new_handle, sizeof(new_handle))) {
channel_msg_free(msg);
return ZN_ERR_BAD_BUFFER;
}
}
}
// Write actual counts to userspace.
if (read_bytes) {
if (!usercopy_write(read_bytes, &msg->num_bytes, sizeof(msg->num_bytes))) {
channel_msg_free(msg);
return ZN_ERR_BAD_BUFFER;
}
}
if (read_handles) {
if (!usercopy_write(read_handles, &msg->num_handles, sizeof(msg->num_handles))) {
channel_msg_free(msg);
return ZN_ERR_BAD_BUFFER;
}
}
channel_msg_free(msg);
return ZN_OK;
}
if (status == ZN_ERR_BAD_HANDLE)
return status;
// Empty but peer alive — block until a message arrives or peer closes.
sched_block(sched, &ch->waiters);
}
}
zn_status_t syscall_channel_wait(struct arch_context* ctx) {
zn_handle_t ch_handle = ctx->ARCH_CTX_A0;
struct task* current = percpu_get()->sched.current;
struct sched_percpu* sched = &percpu_get()->sched;
struct namespace_desc desc;
zn_status_t status = namespace_get(current->namespace, ch_handle, &desc);
if (status != ZN_OK)
return status;
if (desc.type != NAMESPACE_DESC_CHANNEL)
return ZN_ERR_BAD_HANDLE;
struct channel* ch = desc.value.channel;
status = channel_poll(ch);
if (status == ZN_ERR_INVALID) {
sched_block(sched, &ch->waiters);
return ZN_OK;
}
return status;
}
+44
View File
@@ -0,0 +1,44 @@
#include <zinnia/status.h>
#include <zinnia/syscall_numbers.h>
#include <common/compiler.h>
#include <common/utils.h>
#include <kernel/print.h>
#include <kernel/sched.h>
#include <kernel/syscalls.h>
#include <kernel/types.h>
static const syscall_fn_t dispatch_table[] = {
[ZN_SYSCALL_LOG] = syscall_log,
[ZN_SYSCALL_ARCHCTL] = syscall_archctl,
[ZN_SYSCALL_PAGE_SIZE] = syscall_page_size,
[ZN_SYSCALL_TIME] = syscall_time,
[ZN_SYSCALL_HANDLE_VALIDATE] = syscall_handle_validate,
[ZN_SYSCALL_HANDLE_DROP] = syscall_handle_drop,
[ZN_SYSCALL_HANDLE_CLONE] = syscall_handle_clone,
[ZN_SYSCALL_VAS_CREATE] = syscall_vas_create,
[ZN_SYSCALL_VMO_CREATE] = syscall_vmo_create,
[ZN_SYSCALL_VMO_CREATE_PHYS] = syscall_vmo_create_phys,
[ZN_SYSCALL_VMO_MAP] = syscall_vmo_map,
[ZN_SYSCALL_CHANNEL_CREATE] = syscall_channel_create,
[ZN_SYSCALL_CHANNEL_WAIT] = syscall_channel_wait,
[ZN_SYSCALL_CHANNEL_READ] = syscall_channel_read,
[ZN_SYSCALL_CHANNEL_WRITE] = syscall_channel_write,
[ZN_SYSCALL_THREAD_CREATE] = syscall_thread_create,
[ZN_SYSCALL_THREAD_START] = syscall_thread_start,
[ZN_SYSCALL_THREAD_EXIT] = syscall_thread_exit,
[ZN_SYSCALL_RANDOM_GET] = syscall_random_get,
[ZN_SYSCALL_RANDOM_ENTROPY] = syscall_random_entropy,
[ZN_SYSCALL_FUTEX_WAIT] = syscall_futex_wait,
[ZN_SYSCALL_FUTEX_WAKE] = syscall_futex_wake,
};
void syscall_dispatch(struct arch_context* ctx) {
const size_t num = ctx->ARCH_CTX_NUM;
if (num >= ARRAY_SIZE(dispatch_table)) {
ctx->ARCH_CTX_RET0 = ZN_ERR_BAD_SYSCALL;
return;
}
ctx->ARCH_CTX_RET0 = dispatch_table[num](ctx);
}
+17
View File
@@ -0,0 +1,17 @@
#include <zinnia/status.h>
#include <kernel/futex.h>
#include <kernel/syscalls.h>
zn_status_t syscall_futex_wait(struct arch_context* ctx) {
__user int* addr = (__user int*)ctx->ARCH_CTX_A0;
int expected = (int)ctx->ARCH_CTX_A1;
return futex_wait(addr, expected);
}
zn_status_t syscall_futex_wake(struct arch_context* ctx) {
__user int* addr = (__user int*)ctx->ARCH_CTX_A0;
int count = (int)ctx->ARCH_CTX_A1;
return futex_wake(addr, count);
}
+48
View File
@@ -0,0 +1,48 @@
#include <zinnia/handle.h>
#include <zinnia/rights.h>
#include <zinnia/status.h>
#include <kernel/namespace.h>
#include <kernel/percpu.h>
#include <kernel/syscalls.h>
#include <kernel/usercopy.h>
zn_status_t syscall_handle_clone(struct arch_context* ctx) {
zn_handle_t handle = ctx->ARCH_CTX_A0;
zn_rights_t cloned_rights = ctx->ARCH_CTX_A1;
__user zn_handle_t* cloned = (__user zn_handle_t*)ctx->ARCH_CTX_A2;
struct task* current = percpu_get()->sched.current;
struct namespace* ns = current->namespace;
struct namespace_desc desc;
zn_status_t status = namespace_get(ns, handle, &desc);
if (status != ZN_OK)
return status;
zn_handle_t new_handle;
status = namespace_clone_desc(ns, &desc, cloned_rights, &new_handle);
if (status != ZN_OK)
return status;
if (!usercopy_write(cloned, &new_handle, sizeof(new_handle))) {
namespace_del_desc(ns, new_handle);
return ZN_ERR_BAD_BUFFER;
}
return ZN_OK;
}
zn_status_t syscall_handle_drop(struct arch_context* ctx) {
zn_handle_t handle = ctx->ARCH_CTX_A0;
struct task* current = percpu_get()->sched.current;
return namespace_del_desc(current->namespace, handle);
}
zn_status_t syscall_handle_validate(struct arch_context* ctx) {
zn_handle_t handle = ctx->ARCH_CTX_A0;
struct task* current = percpu_get()->sched.current;
struct namespace_desc desc;
return namespace_get(current->namespace, handle, &desc);
}
+12
View File
@@ -0,0 +1,12 @@
kernel_sources += files(
'archctl.c',
'channel.c',
'dispatch.c',
'futex.c',
'handle.c',
'random.c',
'system.c',
'thread.c',
'vas.c',
'vmo.c',
)
+10
View File
@@ -0,0 +1,10 @@
#include <zinnia/status.h>
#include <kernel/syscalls.h>
zn_status_t syscall_random_entropy(struct arch_context* ctx) {
return ZN_ERR_UNSUPPORTED;
}
zn_status_t syscall_random_get(struct arch_context* ctx) {
return ZN_ERR_UNSUPPORTED;
}
+46
View File
@@ -0,0 +1,46 @@
#include <zinnia/status.h>
#include <kernel/alloc.h>
#include <kernel/clock.h>
#include <kernel/console.h>
#include <kernel/syscalls.h>
#include <kernel/usercopy.h>
zn_status_t syscall_log(struct arch_context* ctx) {
__user const char* msg = (__user const char*)ctx->ARCH_CTX_A0;
size_t len = ctx->ARCH_CTX_A1;
char* buf = mem_alloc(len, ALLOC_NOZERO);
if (!buf) {
return ZN_ERR_NO_MEMORY;
}
if (usercopy_read(buf, msg, len)) {
console_write(buf, len);
mem_free(buf);
return ZN_OK;
}
mem_free(buf);
return ZN_ERR_BAD_BUFFER;
}
zn_status_t syscall_page_size(struct arch_context* ctx) {
__user size_t* out = (__user size_t*)ctx->ARCH_CTX_A0;
size_t size = arch_mem_page_size();
if (!usercopy_write(out, &size, sizeof(size)))
return ZN_ERR_BAD_BUFFER;
return ZN_OK;
}
zn_status_t syscall_time(struct arch_context* ctx) {
__user uint64_t* out = (__user uint64_t*)ctx->ARCH_CTX_A0;
uint64_t time = clock_get_elapsed_ns();
if (!usercopy_write(out, &time, sizeof(time)))
return ZN_ERR_BAD_BUFFER;
return ZN_OK;
}
+118
View File
@@ -0,0 +1,118 @@
#include <zinnia/handle.h>
#include <zinnia/rights.h>
#include <zinnia/status.h>
#include <zinnia/thread.h>
#include <common/compiler.h>
#include <kernel/alloc.h>
#include <kernel/namespace.h>
#include <kernel/percpu.h>
#include <kernel/print.h>
#include <kernel/sched.h>
#include <kernel/syscalls.h>
#include <kernel/usercopy.h>
#include <string.h>
zn_status_t syscall_thread_create(struct arch_context* ctx) {
zn_handle_t ns_handle = ctx->ARCH_CTX_A0;
__user const char* name = (__user const char*)ctx->ARCH_CTX_A1;
size_t name_len = ctx->ARCH_CTX_A2;
enum zn_thread_flags flags = ctx->ARCH_CTX_A3;
__user zn_handle_t* out = (__user zn_handle_t*)ctx->ARCH_CTX_A4;
struct task* current = percpu_get()->sched.current;
// Resolve namespace.
struct namespace* ns;
if (ns_handle == ZN_HANDLE_THIS_NS) {
ns = current->namespace;
} else {
struct namespace_desc ns_desc;
zn_status_t status = namespace_get(current->namespace, ns_handle, &ns_desc);
if (status != ZN_OK)
return status;
if (ns_desc.type != NAMESPACE_DESC_NS)
return ZN_ERR_BAD_HANDLE;
ns = ns_desc.value.namespace;
}
// Copy name from userspace.
if (name_len == 0 || name_len >= TASK_NAME_MAX)
return ZN_ERR_INVALID;
char task_name[TASK_NAME_MAX] = {};
if (!usercopy_read(task_name, name, name_len))
return ZN_ERR_BAD_BUFFER;
// Create the task sharing the caller's address space.
// Uses sched_to_user as a placeholder entry — the real IP/SP come from thread_start.
struct task* new_task;
zn_status_t status = task_create(task_name, current->space, ns, sched_to_user, 0, &new_task);
if (status != ZN_OK)
return status;
// Register the task as a handle in the caller's namespace.
struct namespace_desc desc = {
.type = NAMESPACE_DESC_TASK,
.rights = ZN_RIGHTS_ALL,
.value.task = new_task,
};
zn_handle_t handle;
status = namespace_add_desc(current->namespace, desc, &handle);
if (status != ZN_OK)
return status;
if (!usercopy_write(out, &handle, sizeof(handle))) {
namespace_del_desc(current->namespace, handle);
return ZN_ERR_BAD_BUFFER;
}
return ZN_OK;
}
zn_status_t syscall_thread_start(struct arch_context* ctx) {
zn_handle_t thread_handle = ctx->ARCH_CTX_A0;
uintptr_t ip = ctx->ARCH_CTX_A1;
uintptr_t sp = ctx->ARCH_CTX_A2;
uintptr_t arg = ctx->ARCH_CTX_A3;
struct task* current = percpu_get()->sched.current;
struct sched_percpu* sched = &percpu_get()->sched;
// Look up the task.
struct namespace_desc desc;
zn_status_t status = namespace_get(current->namespace, thread_handle, &desc);
if (status != ZN_OK)
return status;
if (desc.type != NAMESPACE_DESC_TASK)
return ZN_ERR_BAD_HANDLE;
struct task* task = desc.value.task;
// Allocate a start info block for the thread entry.
struct task_start_info* info = mem_alloc(sizeof(struct task_start_info), 0);
if (!info)
return ZN_ERR_NO_MEMORY;
info->ip = ip;
info->sp = sp;
info->arg = arg;
// Re-initialize the task's arch context to enter user mode via sched_to_user_arg.
status =
arch_task_init(&task->context, sched_to_user, (uintptr_t)info, task->kernel_stack + KERNEL_STACK_SIZE, true);
if (status != ZN_OK) {
mem_free(info);
return status;
}
// Add to run queue.
sched_add_task(sched, task);
return ZN_OK;
}
zn_status_t syscall_thread_exit(struct arch_context* ctx) {
struct sched_percpu* sched = &percpu_get()->sched;
sched->current->state = TASK_STATE_DEAD;
sched_yield(sched);
__unreachable();
}
+6
View File
@@ -0,0 +1,6 @@
#include <zinnia/status.h>
#include <kernel/syscalls.h>
zn_status_t syscall_vas_create(struct arch_context* ctx) {
return ZN_ERR_UNSUPPORTED;
}
+124
View File
@@ -0,0 +1,124 @@
#include <zinnia/handle.h>
#include <zinnia/status.h>
#include <common/utils.h>
#include <kernel/alloc.h>
#include <kernel/namespace.h>
#include <kernel/percpu.h>
#include <kernel/print.h>
#include <kernel/sched.h>
#include <kernel/syscalls.h>
#include <kernel/usercopy.h>
#include <kernel/vas.h>
#include <kernel/vmo.h>
#include <stdatomic.h>
zn_status_t syscall_vmo_create(struct arch_context* ctx) {
size_t length = ctx->ARCH_CTX_A0;
__user zn_handle_t* out = (__user zn_handle_t*)ctx->ARCH_CTX_A1;
// Allocate new VMO.
struct paged_vmo* vmo;
zn_status_t status = vmo_new_phys(&vmo);
if (status != ZN_OK)
return status;
struct namespace_desc desc = {
.type = NAMESPACE_DESC_VMO,
.rights = ZN_RIGHTS_COMMON | ZN_RIGHT_READ | ZN_RIGHT_WRITE | ZN_RIGHT_MAP,
.value.vmo = &vmo->object,
};
struct task* current = percpu_get()->sched.current;
zn_handle_t handle;
status = namespace_add_desc(current->namespace, desc, &handle);
if (status != ZN_OK)
return status;
if (!usercopy_write(out, &handle, sizeof(handle)))
return ZN_ERR_BAD_BUFFER;
return ZN_OK;
}
zn_status_t syscall_vmo_create_phys(struct arch_context* ctx) {
uintptr_t addr = ctx->ARCH_CTX_A0;
size_t length = ctx->ARCH_CTX_A1;
__user zn_handle_t* out = (__user zn_handle_t*)ctx->ARCH_CTX_A2;
struct vmo* vmo;
zn_status_t status = vmo_new_phys_range(addr, length, &vmo);
if (status != ZN_OK)
return status;
struct namespace_desc desc = {
.type = NAMESPACE_DESC_VMO,
.rights = ZN_RIGHTS_COMMON | ZN_RIGHT_READ | ZN_RIGHT_WRITE | ZN_RIGHT_MAP,
.value.vmo = vmo,
};
struct task* current = percpu_get()->sched.current;
zn_handle_t handle;
status = namespace_add_desc(current->namespace, desc, &handle);
if (status != ZN_OK)
return status;
if (!usercopy_write(out, &handle, sizeof(handle)))
return ZN_ERR_BAD_BUFFER;
return ZN_OK;
}
zn_status_t syscall_vmo_map(struct arch_context* ctx) {
zn_handle_t vmo_handle = ctx->ARCH_CTX_A0;
zn_handle_t vas_handle = ctx->ARCH_CTX_A1;
uintptr_t vmo_offset = ctx->ARCH_CTX_A2;
__user uintptr_t* addr = (__user uintptr_t*)ctx->ARCH_CTX_A3;
size_t bytes = ctx->ARCH_CTX_A4;
enum zn_vm_flags flags = ctx->ARCH_CTX_A5;
struct task* current = percpu_get()->sched.current;
// Get VAS.
struct vas* vas;
if (vas_handle == ZN_HANDLE_THIS_VAS) {
vas = current->space;
} else {
struct namespace_desc vas_desc;
zn_status_t status = namespace_get(current->namespace, vmo_handle, &vas_desc);
if (status != ZN_OK)
return status;
if (vas_desc.type != NAMESPACE_DESC_VAS)
return ZN_ERR_BAD_HANDLE;
vas = vas_desc.value.vas;
}
// Get VMO.
struct namespace_desc vmo_desc;
zn_status_t status = namespace_get(current->namespace, vmo_handle, &vmo_desc);
if (status != ZN_OK)
return status;
if (vmo_desc.type != NAMESPACE_DESC_VMO)
return ZN_ERR_BAD_HANDLE;
struct vmo* vmo = vmo_desc.value.vmo;
// Determine mapping address.
uintptr_t target_addr = 0;
if (addr != nullptr) {
if (!usercopy_read(&target_addr, addr, sizeof(target_addr)))
return ZN_ERR_BAD_BUFFER;
}
// TODO: Use actual allocator for user task.
if (target_addr == 0) {
static uintptr_t map_offset = 0xA000'0000;
target_addr = atomic_fetch_add(&map_offset, ALIGN_UP(bytes, arch_mem_page_size()));
}
if (addr != nullptr) {
if (!usercopy_write(addr, &target_addr, sizeof(target_addr)))
return ZN_ERR_BAD_BUFFER;
}
vma_map_vmo(vas, vmo, target_addr, bytes, flags, vmo_offset);
return ZN_OK;
}