diff --git a/include/ipc/common.h b/include/ipc/common.h new file mode 100644 index 0000000..c0348c5 --- /dev/null +++ b/include/ipc/common.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include + +#define ZN_IPC_NUM(group, num) ((uint32_t)(group) << 24u) | ((uint32_t)(num) & 0xffffffu) + +#define ZN_IPC(name, group, num, num_handles) \ + name##_inner; \ + constexpr uint32_t name##_num = ZN_IPC_NUM(group, num); \ + typedef struct { \ + uint32_t id; \ + zn_handle_t handles[num_handles]; \ + name##_inner inner; \ + } name + +#define zn_ipc_write(channel, obj) \ + zn_channel_write(channel, (obj)->handles, &(obj)->inner, ARRAY_SIZE((obj)->handles), sizeof((obj)->inner)) + +#define zn_ipc_read(channel, obj) \ + zn_channel_read( \ + channel, \ + (obj)->handles, \ + &(obj)->inner, \ + ARRAY_SIZE((obj)->handles), \ + sizeof((obj)->inner), \ + nullptr, \ + nullptr \ + ) + +#define zn_ipc_new(name) \ + (zn_ipc_fw_rsdp) { \ + .id = name##_num \ + } diff --git a/include/zinnia/channel.h b/include/zinnia/channel.h index 1d3012f..81ddbb8 100644 --- a/include/zinnia/channel.h +++ b/include/zinnia/channel.h @@ -13,6 +13,7 @@ extern "C" { enum zn_channel_flags { ZN_CHANNEL_NONE = 0, + ZN_CHANNEL_NONBLOCK = 1 << 0, }; // Creates a new channel. diff --git a/kernel/arch/x86_64/include/bits/sched.h b/kernel/arch/x86_64/include/bits/sched.h index d92c955..54ef8d6 100644 --- a/kernel/arch/x86_64/include/bits/sched.h +++ b/kernel/arch/x86_64/include/bits/sched.h @@ -40,7 +40,7 @@ struct arch_context { uint64_t ss; }; -static_assert(sizeof(struct arch_context) == 176); +static_assert(sizeof(struct arch_context) == 22 * sizeof(uint64_t)); #define ARCH_CTX_NUM rax #define ARCH_CTX_A0 rdi diff --git a/kernel/arch/x86_64/percpu.c b/kernel/arch/x86_64/percpu.c index 62b75e1..edca25a 100644 --- a/kernel/arch/x86_64/percpu.c +++ b/kernel/arch/x86_64/percpu.c @@ -30,6 +30,8 @@ union ia32_star { }; }; +static_assert(sizeof(union ia32_star) == 8); + static void setup_cpu(struct percpu* cpu) { asm_wrmsr(MSR_GS_BASE, (uint64_t)cpu); asm_wrmsr(MSR_FS_BASE, 0); diff --git a/kernel/arch/x86_64/usercopy.c b/kernel/arch/x86_64/usercopy.c index 6f2ffe1..f69e1cc 100644 --- a/kernel/arch/x86_64/usercopy.c +++ b/kernel/arch/x86_64/usercopy.c @@ -82,7 +82,7 @@ bool arch_usercopy_write(__user void* dst, const void* src, size_t len, struct u "clac\n" "ret\n" : - : "i"(&read_region) + : "i"(&write_region) : "memory" ); } diff --git a/kernel/core/channel.c b/kernel/core/channel.c index 45a8fd9..987bfbe 100644 --- a/kernel/core/channel.c +++ b/kernel/core/channel.c @@ -84,7 +84,7 @@ zn_status_t channel_read(struct channel* ch, struct channel_msg** msg_out) { return ZN_OK; } - // No message and peer is gone — nothing will ever arrive. + // No message and peer is gone. if (!ch->peer) { spin_unlock(&ch->lock); return ZN_ERR_BAD_HANDLE; diff --git a/kernel/include/assert.h b/kernel/include/assert.h index 0162604..6890c76 100644 --- a/kernel/include/assert.h +++ b/kernel/include/assert.h @@ -5,7 +5,7 @@ // Like the standard C assert, but also active in debug builds. #define assert(expr, msg, ...) \ - ({ \ + do { \ if (__unlikely(!(expr))) { \ panic( \ "In function \"%s\" (%s:%u):\n" \ @@ -17,7 +17,7 @@ ##__VA_ARGS__ \ ); \ } \ - }) + } while (0) #ifndef NDEBUG #define debug_assert(...) assert(__VA_ARGS__) diff --git a/kernel/include/kernel/channel.h b/kernel/include/kernel/channel.h index 70ad715..75c585d 100644 --- a/kernel/include/kernel/channel.h +++ b/kernel/include/kernel/channel.h @@ -1,5 +1,8 @@ +// SPDX-License-Identifier: MIT + #pragma once +#include #include #include #include @@ -19,6 +22,7 @@ struct channel_msg { struct channel { struct channel* peer; struct spinlock lock; + enum zn_channel_flags flags; tailq_head(struct channel_msg) messages; struct wait_queue waiters; }; diff --git a/kernel/include/kernel/syscalls.h b/kernel/include/kernel/syscalls.h index a59d4f7..c663524 100644 --- a/kernel/include/kernel/syscalls.h +++ b/kernel/include/kernel/syscalls.h @@ -6,32 +6,34 @@ typedef zn_status_t (*syscall_fn_t)(struct arch_context* ctx); void syscall_dispatch(struct arch_context* ctx); -zn_status_t syscall_log(struct arch_context* ctx); -zn_status_t syscall_archctl(struct arch_context* ctx); -zn_status_t syscall_page_size(struct arch_context* ctx); -zn_status_t syscall_time(struct arch_context* ctx); +#define DEFINE_SYSCALL(name) zn_status_t syscall_##name(struct arch_context* ctx) -zn_status_t syscall_handle_validate(struct arch_context* ctx); -zn_status_t syscall_handle_drop(struct arch_context* ctx); -zn_status_t syscall_handle_clone(struct arch_context* ctx); +DEFINE_SYSCALL(log); +DEFINE_SYSCALL(archctl); +DEFINE_SYSCALL(page_size); +DEFINE_SYSCALL(time); -zn_status_t syscall_vas_create(struct arch_context* ctx); +DEFINE_SYSCALL(handle_validate); +DEFINE_SYSCALL(handle_drop); +DEFINE_SYSCALL(handle_clone); -zn_status_t syscall_vmo_create(struct arch_context* ctx); -zn_status_t syscall_vmo_create_phys(struct arch_context* ctx); -zn_status_t syscall_vmo_map(struct arch_context* ctx); +DEFINE_SYSCALL(vas_create); -zn_status_t syscall_channel_create(struct arch_context* ctx); -zn_status_t syscall_channel_wait(struct arch_context* ctx); -zn_status_t syscall_channel_read(struct arch_context* ctx); -zn_status_t syscall_channel_write(struct arch_context* ctx); +DEFINE_SYSCALL(vmo_create); +DEFINE_SYSCALL(vmo_create_phys); +DEFINE_SYSCALL(vmo_map); -zn_status_t syscall_thread_create(struct arch_context* ctx); -zn_status_t syscall_thread_start(struct arch_context* ctx); -zn_status_t syscall_thread_exit(struct arch_context* ctx); +DEFINE_SYSCALL(channel_create); +DEFINE_SYSCALL(channel_wait); +DEFINE_SYSCALL(channel_read); +DEFINE_SYSCALL(channel_write); -zn_status_t syscall_random_get(struct arch_context* ctx); -zn_status_t syscall_random_entropy(struct arch_context* ctx); +DEFINE_SYSCALL(thread_create); +DEFINE_SYSCALL(thread_start); +DEFINE_SYSCALL(thread_exit); -zn_status_t syscall_futex_wait(struct arch_context* ctx); -zn_status_t syscall_futex_wake(struct arch_context* ctx); +DEFINE_SYSCALL(random_get); +DEFINE_SYSCALL(random_entropy); + +DEFINE_SYSCALL(futex_wait); +DEFINE_SYSCALL(futex_wake); diff --git a/kernel/syscalls/channel.c b/kernel/syscalls/channel.c index f3838b5..4f94aae 100644 --- a/kernel/syscalls/channel.c +++ b/kernel/syscalls/channel.c @@ -12,8 +12,14 @@ #include 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; + auto flags = (enum zn_channel_flags)ctx->a0; + auto end0 = (__user zn_handle_t*)ctx->a1; + auto end1 = (__user zn_handle_t*)ctx->a2; + + // Validate flags before doing anything. + if (flags & !(ZN_CHANNEL_NONE | ZN_CHANNEL_NONBLOCK)) { + return ZN_ERR_INVALID; + } struct channel* ep0; struct channel* ep1; @@ -61,11 +67,11 @@ zn_status_t syscall_channel_create(struct arch_context* ctx) { } 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; + auto ch_handle = (zn_handle_t)ctx->a0; + auto handles = (__user zn_handle_t*)ctx->a0; + auto bytes = (__user void*)ctx->a2; + size_t num_handles = ctx->a3; + size_t num_bytes = ctx->a4; struct task* current = percpu_get()->sched.current; @@ -95,7 +101,7 @@ zn_status_t syscall_channel_write(struct arch_context* ctx) { } } - // Copy and transfer handles (move semantics). + // Copy and transfer handles. struct namespace_desc* descs = nullptr; if (num_handles > 0) { if (!handles) { @@ -155,13 +161,13 @@ zn_status_t syscall_channel_write(struct arch_context* ctx) { } 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; + zn_handle_t ch_handle = ctx->a0; + __user zn_handle_t* handles = (__user zn_handle_t*)ctx->a1; + __user void* bytes = (__user void*)ctx->a2; + size_t num_handles = ctx->a3; + size_t num_bytes = ctx->a4; + __user size_t* read_handles = (__user size_t*)ctx->a5; + __user size_t* read_bytes = (__user size_t*)ctx->a6; struct task* current = percpu_get()->sched.current; struct sched_percpu* sched = &percpu_get()->sched; @@ -183,7 +189,7 @@ zn_status_t syscall_channel_read(struct arch_context* ctx) { if (status == ZN_OK) { // Copy byte data to userspace. - size_t bytes_to_copy = msg->num_bytes < num_bytes ? msg->num_bytes : num_bytes; + size_t bytes_to_copy = MIN(msg->num_bytes, num_bytes); if (bytes_to_copy > 0 && bytes) { if (!usercopy_write(bytes, msg->data, bytes_to_copy)) { channel_msg_free(msg); @@ -192,7 +198,7 @@ zn_status_t syscall_channel_read(struct arch_context* ctx) { } // Transfer handle descriptors into the reader's namespace. - size_t handles_to_copy = msg->num_handles < num_handles ? msg->num_handles : num_handles; + size_t handles_to_copy = MIN(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; @@ -231,13 +237,13 @@ zn_status_t syscall_channel_read(struct arch_context* ctx) { if (status == ZN_ERR_BAD_HANDLE) return status; - // Empty but peer alive — block until a message arrives or peer closes. + // 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; + zn_handle_t ch_handle = ctx->a0; struct task* current = percpu_get()->sched.current; struct sched_percpu* sched = &percpu_get()->sched; diff --git a/kernel/syscalls/vmo.c b/kernel/syscalls/vmo.c index d357dab..3b38e73 100644 --- a/kernel/syscalls/vmo.c +++ b/kernel/syscalls/vmo.c @@ -15,8 +15,8 @@ #include 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; + size_t length = ctx->a0; + __user zn_handle_t* out = (__user zn_handle_t*)ctx->a1; // Allocate new VMO. struct paged_vmo* vmo; @@ -43,9 +43,9 @@ zn_status_t syscall_vmo_create(struct arch_context* ctx) { } 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; + uintptr_t addr = ctx->a0; + size_t length = ctx->a1; + __user zn_handle_t* out = (__user zn_handle_t*)ctx->a2; struct vmo* vmo; zn_status_t status = vmo_new_phys_range(addr, length, &vmo); @@ -71,12 +71,12 @@ zn_status_t syscall_vmo_create_phys(struct arch_context* ctx) { } 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; + zn_handle_t vmo_handle = ctx->a0; + zn_handle_t vas_handle = ctx->a1; + uintptr_t vmo_offset = ctx->a2; + auto addr = (__user uintptr_t*)ctx->a3; + size_t bytes = ctx->a4; + enum zn_vm_flags flags = ctx->a5; struct task* current = percpu_get()->sched.current; @@ -112,8 +112,8 @@ zn_status_t syscall_vmo_map(struct arch_context* ctx) { // 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())); + static _Atomic uintptr_t map_offset = 0xA000'0000; + target_addr = atomic_fetch_add((volatile uintptr_t*)&map_offset, ALIGN_UP(bytes, arch_mem_page_size())); } if (addr != nullptr) {