From 436b9ee6086554c4b889a7743491268b4efb88fb Mon Sep 17 00:00:00 2001 From: KN Date: Thu, 15 Jan 2026 08:07:55 +0100 Subject: [PATCH 1/8] fix(libstd:list): fix RCU list race conditions --- include/kernel/sync/rcu.h | 10 ++----- include/libstd/sys/list.h | 61 +++++++++++++++++++++++++++++++++++++-- src/kernel/proc/process.c | 6 ++-- src/kernel/sched/thread.c | 4 +-- src/kernel/sync/rcu.c | 42 ++++++++++++++++++++++----- 5 files changed, 101 insertions(+), 22 deletions(-) diff --git a/include/kernel/sync/rcu.h b/include/kernel/sync/rcu.h index c186665fa..323388821 100644 --- a/include/kernel/sync/rcu.h +++ b/include/kernel/sync/rcu.h @@ -40,9 +40,7 @@ typedef struct rcu_entry rcu_entry_t; * `rcu_entry_t` member and a callback function that will free the structure. * * To access RCU protected data, a read-side critical section must be created using `rcu_read_lock()` and - * `rcu_read_unlock()`, or the `RCU_READ_SCOPE()` macro. Within the critical section, RCU protected pointers should be - * accessed using the `RCU_DEREFERENCE()` macro, and any updates to RCU protected pointers should be done using the - * `RCU_ASSIGN_POINTER()` macro. + * `rcu_read_unlock()`, or the `RCU_READ_SCOPE()` macro. * * @see https://en.wikipedia.org/wiki/Read-copy-update for more information about RCU. * @see https://www.kernel.org/doc/Documentation/RCU/whatisRCU.txt for a explanation of RCU in the Linux kernel. @@ -76,7 +74,7 @@ typedef struct rcu_entry */ static inline void rcu_read_lock(void) { - cli_push(); + sched_disable(); } /** @@ -86,7 +84,7 @@ static inline void rcu_read_lock(void) */ static inline void rcu_read_unlock(void) { - cli_pop(); + sched_enable(); } /** @@ -114,8 +112,6 @@ void rcu_call(rcu_entry_t* entry, rcu_callback_t func, void* arg); /** * @brief Called during a context switch to report a quiescent state. - * - * Will also be invoked via an IPI when a grace period starts if the CPU is not in a quiescent state. */ void rcu_report_quiescent(void); diff --git a/include/libstd/sys/list.h b/include/libstd/sys/list.h index 7b8e26923..30a27d63c 100644 --- a/include/libstd/sys/list.h +++ b/include/libstd/sys/list.h @@ -7,17 +7,19 @@ #include #include #include +#include /** * @brief Doubly linked list header. * @ingroup libstd * @defgroup libstd_sys_list Doubly linked list * - * The `sys/list.h` header implements a intrusive doubly linked list where the linked list entry structure is stored - * within each entry instead of each entry having a pointer to each stucture. + * The `sys/list.h` header implements a intrusive doubly linked list. * * Given a entry within a structure, the `CONTAINER_OF()` macro can be used to get a pointer to the structure from the * list entry pointer. + * + * @warning If a list is protected with RCU, the `list_*_rcu()` functions must be used. * * @{ */ @@ -233,6 +235,30 @@ static inline void list_add(list_entry_t* prev, list_entry_t* next, list_entry_t prev->next = entry; } +/** + * @brief Adds a new element between two existing list entries in a RCU-safe manner. + * + * @param prev A pointer to the list entry that will precede the new element. + * @param next A pointer to the list entry that will follow the new element. + * @param elem A pointer to the `list_entry_t` to add. + */ +static inline void list_add_rcu(list_entry_t* prev, list_entry_t* next, list_entry_t* entry) +{ + assert(prev != NULL); + assert(next != NULL); + assert(entry != NULL); + // For RCU we allow adding an entry that is already in a list + // as we cant properly remove it until all readers are done. + //assert(entry->next == entry && entry->prev == entry); + //assert(prev->next == next && next->prev == prev); + + next->prev = entry; + entry->next = next; + entry->prev = prev; + atomic_thread_fence(memory_order_release); + prev->next = entry; +} + /** * @brief Appends an entry to the list. * @@ -270,6 +296,22 @@ static inline void list_remove(list_entry_t* entry) list_entry_init(entry); } +/** + * @brief Removes a list entry from its current list in a RCU-safe manner. + * + * @warning After calling this function the entry will still be connected to the list, but iteration over the list will not find it. + * + * @param entry A pointer to the `list_entry_t` to remove. + */ +static inline void list_remove_rcu(list_entry_t* entry) +{ + assert(entry != NULL); + + entry->prev->next = entry->next; + atomic_thread_fence(memory_order_release); + entry->next->prev = entry->prev; +} + /** * @brief Pushes an entry to the end of the list. * @@ -285,6 +327,21 @@ static inline void list_push_back(list_t* list, list_entry_t* entry) list_add(list->head.prev, &list->head, entry); } +/** + * @brief Pushes an entry to the end of the list in a RCU-safe manner. + * + * @param list A pointer to the `list_t` to push the entry to. + * @param entry A pointer to the `list_entry_t` to push. + */ +static inline void list_push_back_rcu(list_t* list, list_entry_t* entry) +{ + assert(list != NULL); + assert(entry != NULL); + assert(entry->next == entry && entry->prev == entry); + + list_add_rcu(list->head.prev, &list->head, entry); +} + /** * @brief Pushes an entry to the front of the list. * diff --git a/src/kernel/proc/process.c b/src/kernel/proc/process.c index 323256df0..58f5cbb77 100644 --- a/src/kernel/proc/process.c +++ b/src/kernel/proc/process.c @@ -150,7 +150,7 @@ process_t* process_new(priority_t priority, group_member_t* group, namespace_t* LOG_DEBUG("created process pid=%d\n", process->id); - list_push_back(&_processes, &process->entry); + list_push_back_rcu(&_processes, &process->entry); lock_release(&processesLock); return REF(process); } @@ -166,7 +166,7 @@ process_t* process_get(pid_t id) return NULL; } - return REF(CONTAINER_OF(entry, process_t, mapEntry)); + return REF_TRY(CONTAINER_OF(entry, process_t, mapEntry)); } namespace_t* process_get_ns(process_t* process) @@ -251,7 +251,7 @@ void process_remove(process_t* process) { lock_acquire(&processesLock); map_remove(&pidMap, &process->mapEntry); - list_remove(&process->entry); + list_remove_rcu(&process->entry); lock_release(&processesLock); UNREF(process); diff --git a/src/kernel/sched/thread.c b/src/kernel/sched/thread.c index 4fbcccfa1..9d0bef055 100644 --- a/src/kernel/sched/thread.c +++ b/src/kernel/sched/thread.c @@ -83,7 +83,7 @@ thread_t* thread_new(process_t* process) lock_acquire(&process->threads.lock); process->threads.count++; - list_push_back(&process->threads.list, &thread->processEntry); + list_push_back_rcu(&process->threads.list, &thread->processEntry); lock_release(&process->threads.lock); return thread; } @@ -92,7 +92,7 @@ void thread_free(thread_t* thread) { lock_acquire(&thread->process->threads.lock); thread->process->threads.count--; - list_remove(&thread->processEntry); + list_remove_rcu(&thread->processEntry); lock_release(&thread->process->threads.lock); UNREF(thread->process); diff --git a/src/kernel/sync/rcu.c b/src/kernel/sync/rcu.c index 13e667ebe..7f8ab9dc8 100644 --- a/src/kernel/sync/rcu.c +++ b/src/kernel/sync/rcu.c @@ -85,7 +85,7 @@ void rcu_call(rcu_entry_t* entry, rcu_callback_t func, void* arg) list_push_back(pcpu_rcu->batch, &entry->entry); } -void rcu_report_quiescent(void) +static bool rcu_check_quiescent(void) { lock_acquire(&lock); if (active && bitmap_is_set(&ack, SELF->id)) @@ -107,13 +107,11 @@ void rcu_report_quiescent(void) } } lock_release(&lock); + return wake; +} - while (!list_is_empty(pcpu_rcu->ready)) - { - rcu_entry_t* entry = CONTAINER_OF(list_pop_front(pcpu_rcu->ready), rcu_entry_t, entry); - entry->func(entry->arg); - } - +static void rcu_advance(bool wake) +{ if (wake) { list_t* temp = pcpu_rcu->ready; @@ -130,8 +128,12 @@ void rcu_report_quiescent(void) lock_acquire(&lock); pcpu_rcu->grace = grace + 1; lock_release(&lock); + pcpu_rcu->grace = active ? grace : grace + 1; } +} +static void rcu_start_grace(void) +{ if (list_is_empty(pcpu_rcu->waiting)) { return; @@ -155,10 +157,34 @@ void rcu_report_quiescent(void) continue; } - ipi_wake_up(cpu, IPI_SINGLE); + bitmap_clear(&ack, cpu->id); + } +} + +static void rcu_invoke_callbacks(void) +{ + while (!list_is_empty(pcpu_rcu->ready)) + { + rcu_entry_t* entry = CONTAINER_OF(list_pop_front(pcpu_rcu->ready), rcu_entry_t, entry); + entry->func(entry->arg); } } +void rcu_report_quiescent(void) +{ + assert(!(rflags_read() & RFLAGS_INTERRUPT_ENABLE)); + + bool wake = rcu_check_quiescent(); + + rcu_invoke_callbacks(); + + rcu_advance(wake); + + rcu_invoke_callbacks(); + + rcu_start_grace(); +} + void rcu_call_free(void* arg) { free(arg); From ffb49c7122dcbd05eaf84c6f67e62ce50517d60c Mon Sep 17 00:00:00 2001 From: KN Date: Thu, 15 Jan 2026 08:19:14 +0100 Subject: [PATCH 2/8] perf(kernel): add process and thread constructor --- src/kernel/proc/process.c | 57 ++++++++++++++++++++++++++++----------- src/kernel/sched/thread.c | 26 ++++++++++++++---- 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/src/kernel/proc/process.c b/src/kernel/proc/process.c index 58f5cbb77..9659d6dd1 100644 --- a/src/kernel/proc/process.c +++ b/src/kernel/proc/process.c @@ -45,7 +45,41 @@ static map_t pidMap = MAP_CREATE(); list_t _processes = LIST_CREATE(_processes); static lock_t processesLock = LOCK_CREATE(); -static cache_t cache = CACHE_CREATE(cache, "process", sizeof(process_t), CACHE_LINE, NULL, NULL); +static void process_ctor(void* ptr) +{ + process_t* process = (process_t*)ptr; + + process->ref = (ref_t){0}; + list_entry_init(&process->entry); + map_entry_init(&process->mapEntry); + list_entry_init(&process->zombieEntry); + process->id = 0; + atomic_init(&process->priority, 0); + memset_s(process->status.buffer, PROCESS_STATUS_MAX, 0, PROCESS_STATUS_MAX); + lock_init(&process->status.lock); + process->space = (space_t){0}; + process->nspace = NULL; + lock_init(&process->nspaceLock); + process->cwd = (cwd_t){0}; + process->fileTable = (file_table_t){0}; + process->futexCtx = (futex_ctx_t){0}; + process->perf = (perf_process_ctx_t){0}; + process->noteHandler = (note_handler_t){0}; + process->suspendQueue = (wait_queue_t){0}; + process->dyingQueue = (wait_queue_t){0}; + atomic_init(&process->flags, PROCESS_NONE); + atomic_init(&process->threads.newTid, 0); + list_init(&process->threads.list); + process->threads.count = 0; + lock_init(&process->threads.lock); + env_init(&process->env); + process->argv = NULL; + process->argc = 0; + process->group = (group_member_t){0}; + process->rcu = (rcu_entry_t){0}; +} + +static cache_t cache = CACHE_CREATE(cache, "process", sizeof(process_t), CACHE_LINE, process_ctor, NULL); static void process_free(process_t* process) { @@ -62,7 +96,7 @@ static void process_free(process_t* process) free(process->argv[i]); } } - free(process->argv); + free((void*)process->argv); process->argv = NULL; process->argc = 0; } @@ -99,13 +133,9 @@ process_t* process_new(priority_t priority, group_member_t* group, namespace_t* } ref_init(&process->ref, process_free); - list_entry_init(&process->entry); - map_entry_init(&process->mapEntry); - list_entry_init(&process->zombieEntry); - process->id = atomic_fetch_add(&newPid, 1); - atomic_init(&process->priority, priority); - memset_s(process->status.buffer, PROCESS_STATUS_MAX, 0, PROCESS_STATUS_MAX); - lock_init(&process->status.lock); + process->id = atomic_fetch_add_explicit(&newPid, 1, memory_order_relaxed); + atomic_store(&process->priority, priority); + process->status.buffer[0] = '\0'; if (space_init(&process->space, VMM_USER_SPACE_MIN, VMM_USER_SPACE_MAX, SPACE_MAP_KERNEL_BINARY | SPACE_MAP_KERNEL_HEAP | SPACE_MAP_IDENTITY) == ERR) @@ -115,7 +145,6 @@ process_t* process_new(priority_t priority, group_member_t* group, namespace_t* } process->nspace = REF(ns); - lock_init(&process->nspaceLock); cwd_init(&process->cwd); file_table_init(&process->fileTable); futex_ctx_init(&process->futexCtx); @@ -123,14 +152,10 @@ process_t* process_new(priority_t priority, group_member_t* group, namespace_t* note_handler_init(&process->noteHandler); wait_queue_init(&process->suspendQueue); wait_queue_init(&process->dyingQueue); - atomic_init(&process->flags, PROCESS_NONE); - atomic_init(&process->threads.newTid, 0); - list_init(&process->threads.list); - process->threads.count = 0; + atomic_store(&process->flags, PROCESS_NONE); + atomic_store(&process->threads.newTid, 0); lock_init(&process->threads.lock); env_init(&process->env); - process->argv = NULL; - process->argc = 0; if (group_member_init(&process->group, group) == ERR) { diff --git a/src/kernel/sched/thread.c b/src/kernel/sched/thread.c index 9d0bef055..e59327195 100644 --- a/src/kernel/sched/thread.c +++ b/src/kernel/sched/thread.c @@ -18,6 +18,26 @@ #include #include +static void thread_ctor(void* ptr) +{ + thread_t* thread = (thread_t*)ptr; + + thread->process = NULL; + thread->id = 0; + list_entry_init(&thread->processEntry); + atomic_init(&thread->state, THREAD_PARKED); + thread->error = 0; + thread->kernelStack = (stack_pointer_t){0}; + thread->userStack = (stack_pointer_t){0}; + thread->wait = (wait_client_t){0}; + thread->simd = (simd_ctx_t){0}; + thread->notes = (note_queue_t){0}; + thread->syscall = (syscall_ctx_t){0}; + thread->perf = (perf_thread_ctx_t){0}; + thread->rcu = (rcu_entry_t){0}; + memset_s(&thread->frame, sizeof(interrupt_frame_t), 0, sizeof(interrupt_frame_t)); +} + static cache_t cache = CACHE_CREATE(cache, "thread", sizeof(thread_t), CACHE_LINE, NULL, NULL); static uintptr_t thread_id_to_offset(tid_t tid, uint64_t maxPages) @@ -49,9 +69,8 @@ thread_t* thread_new(process_t* process) thread->process = process; thread->id = atomic_fetch_add_explicit(&process->threads.newTid, 1, memory_order_relaxed); - list_entry_init(&thread->processEntry); sched_client_init(&thread->sched); - atomic_init(&thread->state, THREAD_PARKED); + atomic_store(&thread->state, THREAD_PARKED); thread->error = 0; if (stack_pointer_init(&thread->kernelStack, VMM_KERNEL_STACKS_MAX - thread_id_to_offset(thread->id, CONFIG_MAX_KERNEL_STACK_PAGES), @@ -77,10 +96,7 @@ thread_t* thread_new(process_t* process) syscall_ctx_init(&thread->syscall, &thread->kernelStack); perf_thread_ctx_init(&thread->perf); - memset(&thread->frame, 0, sizeof(interrupt_frame_t)); - REF(process); - lock_acquire(&process->threads.lock); process->threads.count++; list_push_back_rcu(&process->threads.list, &thread->processEntry); From 16f01a3b4cb9ba5d3e159d834ff4a0d1cd22ee14 Mon Sep 17 00:00:00 2001 From: KN Date: Thu, 15 Jan 2026 08:24:48 +0100 Subject: [PATCH 3/8] refactor: replace s prefix with s suffix for string io --- README.md | 28 +++++++++++----------- include/kernel/cpu/syscall.h | 4 ++-- include/libstd/sys/io.h | 16 ++++++------- include/libstd/sys/proc.h | 2 +- src/boxes/apps/terminal/terminal.c | 8 +++---- src/boxes/core/dwm/dwm.c | 8 +++---- src/kernel/sched/clock.c | 2 +- src/kernel/sched/sched.c | 2 +- src/libpatchwork/display.c | 4 ++-- src/libstd/functions/stdlib/abort.c | 2 +- src/libstd/user/common/note.c | 6 ++--- src/libstd/user/common/syscalls.h | 6 ++--- src/libstd/user/functions/io/chdir.c | 2 +- src/libstd/user/functions/io/sread.c | 2 +- src/libstd/user/functions/io/sreadfile.c | 4 ++-- src/libstd/user/functions/io/swrite.c | 2 +- src/libstd/user/functions/io/swritefile.c | 4 ++-- src/libstd/user/functions/proc/kill.c | 2 +- src/libstd/user/functions/proc/proc_exit.c | 4 ++-- src/libstd/user/functions/stdlib/exit.c | 2 +- src/programs/core/boxd/main.c | 26 ++++++++++---------- src/programs/core/boxspawn/main.c | 8 +++---- src/programs/core/init/main.c | 2 +- src/programs/core/shell/interactive.c | 2 +- src/programs/core/shell/main.c | 4 ++-- src/programs/core/shell/pipeline.c | 2 +- src/programs/utils/cat/main.c | 2 +- src/programs/utils/echo/main.c | 4 ++-- 28 files changed, 80 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 8b589af15..73e315427 100644 --- a/README.md +++ b/README.md @@ -215,20 +215,20 @@ read(fd, id, 31); close(fd); ``` -Using the `sread()` helper which reads a null-terminated string from a file descriptor, we can simplify this to: +Using the `reads()` helper which reads a null-terminated string from a file descriptor, we can simplify this to: ```c fd_t fd = open("/net/local/seqpacket"); -char* id = sread(fd); +char* id = reads(fd); close(fd); // ... do stuff ... free(id); ``` -Finally, using use the `sreadfile()` helper which reads a null-terminated string from a file from its path, we can simplify this even further to: +Finally, using use the `readfiles()` helper which reads a null-terminated string from a file from its path, we can simplify this even further to: ```c -char* id = sreadfile("/net/local/seqpacket"); +char* id = readfiles("/net/local/seqpacket"); // ... do stuff ... free(id); ``` @@ -256,18 +256,18 @@ write(ctl, str, strlen(str)); close(ctl); ``` -Using the `F()` macro which allocates formatted strings on the stack and the `swrite()` helper that writes a null-terminated string to a file descriptor: +Using the `F()` macro which allocates formatted strings on the stack and the `writes()` helper that writes a null-terminated string to a file descriptor: ```c fd_t ctl = open(F("/net/local/%s/ctl", id)); -swrite(ctl, "bind myserver && listen") +writes(ctl, "bind myserver && listen") close(ctl); ``` -Finally, using the `swritefile()` helper which writes a null-terminated string to a file from its path: +Finally, using the `writefiles()` helper which writes a null-terminated string to a file from its path: ```c -swritefile(F("/net/local/%s/ctl", id), "bind myserver && listen"); +writefiles(F("/net/local/%s/ctl", id), "bind myserver && listen"); ``` If we wanted to accept a connection using our newly created server, we just open its accept file: @@ -283,8 +283,8 @@ The file descriptor returned when the accept file is opened can be used to send For the sake of completeness, to connect the server we just create a new socket and use the `connect` command: ```c -char* id = sreadfile("/net/local/seqpacket"); -swritefile(F("/net/local/%s/ctl", id), "connect myserver"); +char* id = readfiles("/net/local/seqpacket"); +writefiles(F("/net/local/%s/ctl", id), "connect myserver"); free(id); ``` @@ -361,7 +361,7 @@ At this point, the process exists but its stuck blocking before it is can load i Now we can redirect the stdio file descriptors in the child process using the `/proc/[pid]/ctl` file, which just like the socket ctl file, allows us to send commands to control the process. In this case, we want to use two commands, `dup2` to redirect the stdio file descriptors and `close` to close the unneeded file descriptors. ```c -swritefile(F("/proc/%d/ctl", child), F("dup2 %d 0 && dup2 %d 1 && dup2 %d 2 && close 3 -1", stdin, stdout, stderr)); +writefiles(F("/proc/%d/ctl", child), F("dup2 %d 0 && dup2 %d 1 && dup2 %d 2 && close 3 -1", stdin, stdout, stderr)); ``` > Note that `close` can either take one or two arguments. When two arguments are provided, it closes all file descriptors in the specified range. In our case `-1` causes a underflow to the maximum file descriptor value, closing all file descriptors higher than or equal to the first argument. @@ -369,13 +369,13 @@ swritefile(F("/proc/%d/ctl", child), F("dup2 %d 0 && dup2 %d 1 && dup2 %d 2 && c Next, we create the environment variable by creating a file in the child's `/proc/[pid]/env/` directory: ```c -swritefile(F("/proc/%d/env/MY_VAR:create", child), "my_value"); +writefiles(F("/proc/%d/env/MY_VAR:create", child), "my_value"); ``` Finally, we can start the child process using the `start` command: ```c -swritefile(F("/proc/%d/ctl", child), "start"); +writefiles(F("/proc/%d/ctl", child), "start"); ``` At this point the child process will begin executing with its stdio redirected to the specified file descriptors and the environment variable set as expected. @@ -426,7 +426,7 @@ For a basic example, say we have a process A which creates a child process B. Pr const char* argv[] = {"/base/bin/b", NULL}; pid_t child = spawn(argv, SPAWN_EMPTY_NS | SPAWN_SUSPENDED); // Mount/bind other needed directories but not /secret -swritefile(F("/proc/%d/ctl", child), "mount ... && bind ... && start"); +writefiles(F("/proc/%d/ctl", child), "mount ... && bind ... && start"); ``` Alternatively, process A could mount a new empty tmpfs instance in its own namespace over the `/secret` directory using the ":private" flag. This prevents a child namespace from inheriting the mountpoint and process A could store whatever it wanted there: diff --git a/include/kernel/cpu/syscall.h b/include/kernel/cpu/syscall.h index b1c87e3c3..2ae11600b 100644 --- a/include/kernel/cpu/syscall.h +++ b/include/kernel/cpu/syscall.h @@ -62,7 +62,7 @@ */ typedef enum { - SYS_PROCESS_EXIT, + SYS_EXITS, SYS_THREAD_EXIT, SYS_SPAWN, SYS_NANOSLEEP, @@ -70,7 +70,7 @@ typedef enum SYS_GETPID, SYS_GETTID, SYS_UPTIME, - SYS_UNIX_EPOCH, + SYS_EPOCH, SYS_OPEN, SYS_OPEN2, SYS_CLOSE, diff --git a/include/libstd/sys/io.h b/include/libstd/sys/io.h index a4236462d..c7a811dce 100644 --- a/include/libstd/sys/io.h +++ b/include/libstd/sys/io.h @@ -134,13 +134,13 @@ size_t read(fd_t fd, void* buffer, size_t count); /** * @brief Wrapper for reading a file directly into a null-terminated string. * - * The `sread()` function reads the entire contents of a file into a newly allocated null-terminated string. + * The `reads()` function reads the entire contents of a file into a newly allocated null-terminated string. * The caller is responsible for freeing the returned string. * * @param fd The file descriptor to read from. * @return On success, a pointer to the null-terminated string. On failure, `NULL` and `errno` is set. */ -char* sread(fd_t fd); +char* reads(fd_t fd); /** * @brief Wrapper for reading a file directly using a path. @@ -158,15 +158,15 @@ size_t readfile(const char* path, void* buffer, size_t count, size_t offset); /** * @brief Wrapper for reading an entire file directly into a null-terminated string. * - * The `sreadfile()` function reads the entire contents of a file into a newly allocated null-terminated string. + * The `readfiles()` function reads the entire contents of a file into a newly allocated null-terminated string. * The caller is responsible for freeing the returned string. * - * Equivalent to calling `open()`, `sread()`, and `close()` in sequence. + * Equivalent to calling `open()`, `reads()`, and `close()` in sequence. * * @param path The path to the file. * @return On success, a pointer to the null-terminated string. On failure, `NULL` and `errno` is set. */ -char* sreadfile(const char* path); +char* readfiles(const char* path); /** * @brief System call for writing to files. @@ -185,7 +185,7 @@ size_t write(fd_t fd, const void* buffer, size_t count); * @param string The null-terminated string to write. * @return On success, the number of bytes written. On failure, `ERR` and `errno` is set. */ -size_t swrite(fd_t fd, const char* string); +size_t writes(fd_t fd, const char* string); /** * @brief Wrapper for writing to a file directly using a path. @@ -203,13 +203,13 @@ size_t writefile(const char* path, const void* buffer, size_t count, size_t offs /** * @brief Wrapper for writing a null-terminated string directly to a file using a path. * - * Equivalent to calling `open()`, `swrite()`, and `close()` in sequence. + * Equivalent to calling `open()`, `writes()`, and `close()` in sequence. * * @param path The path to the file. * @param string The null-terminated string to write. * @return On success, the number of bytes written. On failure, `ERR` and `errno` is set. */ -size_t swritefile(const char* path, const char* string); +size_t writefiles(const char* path, const char* string); /** * @brief Wrapper for reading from a file descriptor using scan formatting. diff --git a/include/libstd/sys/proc.h b/include/libstd/sys/proc.h index 5c5276829..d2be8fd4c 100644 --- a/include/libstd/sys/proc.h +++ b/include/libstd/sys/proc.h @@ -329,7 +329,7 @@ uint64_t atnotify(atnotify_func_t handler, atnotify_t action); * @param frame The interrupt frame of the current interrupt. * @return On success, `true` if a note was handled, `false` otherwise. */ -_NORETURN void proc_exit(const char* status); +_NORETURN void exits(const char* status); /** * @brief Helper for sending the "kill" command to a process. diff --git a/src/boxes/apps/terminal/terminal.c b/src/boxes/apps/terminal/terminal.c index f5b007985..ea6ac836a 100644 --- a/src/boxes/apps/terminal/terminal.c +++ b/src/boxes/apps/terminal/terminal.c @@ -227,7 +227,7 @@ static void terminal_handle_input(terminal_t* term, element_t* elem, drawable_t* if (ansi.length == 1 && ansi.buffer[0] == '\003') { - swritefile(F("/proc/%llu/notegroup", term->shell), "interrupt due to ctrl+c"); + writefiles(F("/proc/%llu/notegroup", term->shell), "interrupt due to ctrl+c"); } } @@ -607,11 +607,11 @@ static uint64_t terminal_procedure(window_t* win, element_t* elem, const event_t return ERR; } - if (swritefile(F("/proc/%d/ctl", term->shell), + if (writefiles(F("/proc/%d/ctl", term->shell), F("dup2 %d 0 && dup2 %d 1 && dup2 %d 2 && close 3 -1 && start", term->stdin[0], term->stdout[1], term->stdout[1])) == ERR) { - swritefile(F("/proc/%d/ctl", term->shell), "kill"); + writefiles(F("/proc/%d/ctl", term->shell), "kill"); close(term->stdin[0]); close(term->stdin[1]); close(term->stdout[0]); @@ -638,7 +638,7 @@ static uint64_t terminal_procedure(window_t* win, element_t* elem, const event_t close(term->stdout[0]); close(term->stdout[1]); - swritefile(F("/proc/%d/notegroup", term->shell), "terminate due to terminal close"); + writefiles(F("/proc/%d/notegroup", term->shell), "terminate due to terminal close"); } break; case EVENT_LIB_QUIT: diff --git a/src/boxes/core/dwm/dwm.c b/src/boxes/core/dwm/dwm.c index f59b37fdf..4bb9c3083 100644 --- a/src/boxes/core/dwm/dwm.c +++ b/src/boxes/core/dwm/dwm.c @@ -103,7 +103,7 @@ void dwm_init(void) abort(); } - char* name = sreadfile("/dev/kbd/0/name"); + char* name = readfiles("/dev/kbd/0/name"); if (name != NULL) { printf("dwm: using keyboard '%s'\n", name); @@ -117,21 +117,21 @@ void dwm_init(void) abort(); } - name = sreadfile("/dev/mouse/0/name"); + name = readfiles("/dev/mouse/0/name"); if (name != NULL) { printf("dwm: using mouse '%s'\n", name); free(name); } - id = sreadfile("/net/local/seqpacket:nonblock"); + id = readfiles("/net/local/seqpacket:nonblock"); if (id == NULL) { printf("dwm: failed to read seqpacket id (%s)\n", strerror(errno)); abort(); } - if (swritefile(F("/net/local/%s/ctl", id), "bind dwm && listen") == ERR) + if (writefiles(F("/net/local/%s/ctl", id), "bind dwm && listen") == ERR) { printf("dwm: failed to bind socket (%s)\n", strerror(errno)); abort(); diff --git a/src/kernel/sched/clock.c b/src/kernel/sched/clock.c index 19e67463f..b7df64d08 100644 --- a/src/kernel/sched/clock.c +++ b/src/kernel/sched/clock.c @@ -142,7 +142,7 @@ SYSCALL_DEFINE(SYS_UPTIME, clock_t) return clock_uptime(); } -SYSCALL_DEFINE(SYS_UNIX_EPOCH, time_t, time_t* timePtr) +SYSCALL_DEFINE(SYS_EPOCH, time_t, time_t* timePtr) { time_t epoch = clock_epoch(); if (timePtr != NULL) diff --git a/src/kernel/sched/sched.c b/src/kernel/sched/sched.c index dd45cb1c8..c1ec66d8f 100644 --- a/src/kernel/sched/sched.c +++ b/src/kernel/sched/sched.c @@ -674,7 +674,7 @@ SYSCALL_DEFINE(SYS_NANOSLEEP, uint64_t, clock_t nanoseconds) return sched_nanosleep(nanoseconds); } -SYSCALL_DEFINE(SYS_PROCESS_EXIT, void, const char* status) +SYSCALL_DEFINE(SYS_EXITS, void, const char* status) { sched_process_exit(status); diff --git a/src/libpatchwork/display.c b/src/libpatchwork/display.c index 5fbab9e19..f08fe2d42 100644 --- a/src/libpatchwork/display.c +++ b/src/libpatchwork/display.c @@ -52,7 +52,7 @@ display_t* display_new(void) return NULL; } - disp->id = sreadfile("/net/local/seqpacket"); + disp->id = readfiles("/net/local/seqpacket"); if (disp->id == NULL) { free(disp); @@ -66,7 +66,7 @@ display_t* display_new(void) free(disp); return NULL; } - if (swrite(disp->ctl, "connect dwm") == ERR) + if (writes(disp->ctl, "connect dwm") == ERR) { close(disp->ctl); free(disp->id); diff --git a/src/libstd/functions/stdlib/abort.c b/src/libstd/functions/stdlib/abort.c index 78f6529d8..be2108ec4 100644 --- a/src/libstd/functions/stdlib/abort.c +++ b/src/libstd/functions/stdlib/abort.c @@ -12,6 +12,6 @@ void abort(void) panic(NULL, "abort() called"); #else raise(SIGABRT); - proc_exit("aborted"); + exits("aborted"); #endif } diff --git a/src/libstd/user/common/note.c b/src/libstd/user/common/note.c index ebc8291fc..7bd56080b 100644 --- a/src/libstd/user/common/note.c +++ b/src/libstd/user/common/note.c @@ -20,7 +20,7 @@ static void _signal_invoke(int sig, const char* note) if (handler == SIG_DFL) { - proc_exit(note); + exits(note); } handler(sig); @@ -36,7 +36,7 @@ _NORETURN static void _note_kernel_handler(char* note) uint64_t result = func(note); if (result == ERR) { - proc_exit(note); + exits(note); } } } @@ -69,7 +69,7 @@ void _note_init(void) { if (notify(_note_kernel_handler) == ERR) { - proc_exit("notify failed"); + exits("notify failed"); } } diff --git a/src/libstd/user/common/syscalls.h b/src/libstd/user/common/syscalls.h index 6e0db58e7..41a26ae75 100644 --- a/src/libstd/user/common/syscalls.h +++ b/src/libstd/user/common/syscalls.h @@ -79,9 +79,9 @@ ret; \ }) -_NORETURN static inline void _syscall_process_exit(const char* status) +_NORETURN static inline void _syscall_exits(const char* status) { - _SYSCALL1(uint64_t, SYS_PROCESS_EXIT, const char*, status); + _SYSCALL1(uint64_t, SYS_EXITS, const char*, status); ASM("ud2"); __builtin_unreachable(); } @@ -125,7 +125,7 @@ static inline clock_t _syscall_uptime(void) static inline time_t _syscall_unix_epoch(void) { - return _SYSCALL0(time_t, SYS_UNIX_EPOCH); + return _SYSCALL0(time_t, SYS_EPOCH); } static inline fd_t _syscall_open(const char* path) diff --git a/src/libstd/user/functions/io/chdir.c b/src/libstd/user/functions/io/chdir.c index eab81f2d3..84343232a 100644 --- a/src/libstd/user/functions/io/chdir.c +++ b/src/libstd/user/functions/io/chdir.c @@ -7,7 +7,7 @@ uint64_t chdir(const char* path) { - if (swritefile("/proc/self/cwd", path) == ERR) + if (writefiles("/proc/self/cwd", path) == ERR) { return ERR; } diff --git a/src/libstd/user/functions/io/sread.c b/src/libstd/user/functions/io/sread.c index cd3d505f6..df4d29027 100644 --- a/src/libstd/user/functions/io/sread.c +++ b/src/libstd/user/functions/io/sread.c @@ -2,7 +2,7 @@ #include #include -char* sread(fd_t fd) +char* reads(fd_t fd) { uint64_t size = 4096; char* buffer = malloc(size); diff --git a/src/libstd/user/functions/io/sreadfile.c b/src/libstd/user/functions/io/sreadfile.c index 080704f7a..ef17cffe4 100644 --- a/src/libstd/user/functions/io/sreadfile.c +++ b/src/libstd/user/functions/io/sreadfile.c @@ -1,13 +1,13 @@ #include -char* sreadfile(const char* path) +char* readfiles(const char* path) { fd_t fd = open(path); if (fd == ERR) { return NULL; } - char* str = sread(fd); + char* str = reads(fd); close(fd); return str; } \ No newline at end of file diff --git a/src/libstd/user/functions/io/swrite.c b/src/libstd/user/functions/io/swrite.c index dca4c17fd..b88c1ae51 100644 --- a/src/libstd/user/functions/io/swrite.c +++ b/src/libstd/user/functions/io/swrite.c @@ -1,7 +1,7 @@ #include #include -size_t swrite(fd_t fd, const char* string) +size_t writes(fd_t fd, const char* string) { size_t length = strlen(string); return write(fd, string, length); diff --git a/src/libstd/user/functions/io/swritefile.c b/src/libstd/user/functions/io/swritefile.c index 3ffa9dff8..874fdebad 100644 --- a/src/libstd/user/functions/io/swritefile.c +++ b/src/libstd/user/functions/io/swritefile.c @@ -1,14 +1,14 @@ #include #include -size_t swritefile(const char* path, const char* string) +size_t writefiles(const char* path, const char* string) { fd_t fd = open(path); if (fd == ERR) { return ERR; } - uint64_t totalWritten = swrite(fd, string); + uint64_t totalWritten = writes(fd, string); close(fd); return totalWritten; } \ No newline at end of file diff --git a/src/libstd/user/functions/proc/kill.c b/src/libstd/user/functions/proc/kill.c index 7a2c15e52..0e779cb93 100644 --- a/src/libstd/user/functions/proc/kill.c +++ b/src/libstd/user/functions/proc/kill.c @@ -3,5 +3,5 @@ uint64_t kill(pid_t pid) { - return swritefile(F("/proc/%llu/ctl", pid), "kill"); + return writefiles(F("/proc/%llu/ctl", pid), "kill"); } \ No newline at end of file diff --git a/src/libstd/user/functions/proc/proc_exit.c b/src/libstd/user/functions/proc/proc_exit.c index 53b34830b..97dc1763d 100644 --- a/src/libstd/user/functions/proc/proc_exit.c +++ b/src/libstd/user/functions/proc/proc_exit.c @@ -4,9 +4,9 @@ #include "user/common/file.h" #include "user/common/syscalls.h" -void proc_exit(const char* status) +void exits(const char* status) { _exit_stack_dispatch(); _files_close(); - _syscall_process_exit(status); + _syscall_exits(status); } \ No newline at end of file diff --git a/src/libstd/user/functions/stdlib/exit.c b/src/libstd/user/functions/stdlib/exit.c index e5b841efa..5342b1e46 100644 --- a/src/libstd/user/functions/stdlib/exit.c +++ b/src/libstd/user/functions/stdlib/exit.c @@ -6,5 +6,5 @@ void exit(int status) { - proc_exit(F("%d", status)); + exits(F("%d", status)); } diff --git a/src/programs/core/boxd/main.c b/src/programs/core/boxd/main.c index 6e06a06d9..58d6ed433 100644 --- a/src/programs/core/boxd/main.c +++ b/src/programs/core/boxd/main.c @@ -246,7 +246,7 @@ static void box_spawn(box_spawn_t* ctx) goto error; } - if (swritefile(F("/proc/%llu/prio", pid), F("%llu", priority)) == ERR) + if (writefiles(F("/proc/%llu/prio", pid), F("%llu", priority)) == ERR) { snprintf(ctx->result, sizeof(ctx->result), "error due to priority failure for '%s' (%s)", args.box, strerror(errno)); @@ -256,7 +256,7 @@ static void box_spawn(box_spawn_t* ctx) section_t* env = &manifest.sections[SECTION_ENV]; for (uint64_t i = 0; i < env->amount; i++) { - if (swritefile(F("/proc/%llu/env/%s:cw", pid, env->entries[i].key), env->entries[i].value) == ERR) + if (writefiles(F("/proc/%llu/env/%s:cw", pid, env->entries[i].key), env->entries[i].value) == ERR) { snprintf(ctx->result, sizeof(ctx->result), "error due to env var failure for '%s' (%s)", args.box, strerror(errno)); @@ -274,7 +274,7 @@ static void box_spawn(box_spawn_t* ctx) if (shouldInheritNamespace) { - if (swrite(ctl, F("setns %llu", args.namespace)) == ERR) + if (writes(ctl, F("setns %llu", args.namespace)) == ERR) { snprintf(ctx->result, sizeof(ctx->result), "error due to setns failure for '%s' (%s)", args.box, strerror(errno)); @@ -283,7 +283,7 @@ static void box_spawn(box_spawn_t* ctx) } else { - if (swrite(ctl, "mount /:Lrwx /sys/fs/tmpfs") == ERR) + if (writes(ctl, "mount /:Lrwx /sys/fs/tmpfs") == ERR) { snprintf(ctx->result, sizeof(ctx->result), "error due to root mount failure for '%s' (%s)", args.box, strerror(errno)); @@ -297,7 +297,7 @@ static void box_spawn(box_spawn_t* ctx) char* key = namespace->entries[i].key; char* value = namespace->entries[i].value; - if (swrite(ctl, F("touch %s:rwcp && bind %s %s", key, key, value)) == ERR) + if (writes(ctl, F("touch %s:rwcp && bind %s %s", key, key, value)) == ERR) { printf("boxd: failed to bind '%s' to '%s' (%s)\n", key, value, strerror(errno)); goto error; @@ -313,7 +313,7 @@ static void box_spawn(box_spawn_t* ctx) continue; } - if (swrite(ctl, F("dup2 %llu %llu", args.stdio[i], i)) == ERR) + if (writes(ctl, F("dup2 %llu %llu", args.stdio[i], i)) == ERR) { snprintf(ctx->result, sizeof(ctx->result), "error due to dup2 failure for '%s' (%s)", args.box, strerror(errno)); @@ -321,14 +321,14 @@ static void box_spawn(box_spawn_t* ctx) } } - if (swrite(ctl, F("setgroup %llu", args.group)) == ERR) + if (writes(ctl, F("setgroup %llu", args.group)) == ERR) { snprintf(ctx->result, sizeof(ctx->result), "error due to setns failure for '%s' (%s)", args.box, strerror(errno)); goto error; } - if (swrite(ctl, "close 3 -1") == ERR) + if (writes(ctl, "close 3 -1") == ERR) { snprintf(ctx->result, sizeof(ctx->result), "error due to close failure for '%s' (%s)", args.box, strerror(errno)); @@ -357,7 +357,7 @@ static void box_spawn(box_spawn_t* ctx) } else { - if (swrite(ctl, "close 0 -1") == ERR) + if (writes(ctl, "close 0 -1") == ERR) { snprintf(ctx->result, sizeof(ctx->result), "error due to close failure for '%s' (%s)", args.box, strerror(errno)); @@ -367,7 +367,7 @@ static void box_spawn(box_spawn_t* ctx) snprintf(ctx->result, sizeof(ctx->result), "background"); } - if (swrite(ctl, "start") == ERR) + if (writes(ctl, "start") == ERR) { snprintf(ctx->result, sizeof(ctx->result), "error due to start failure for '%s' (%s)", args.box, strerror(errno)); @@ -407,14 +407,14 @@ int main(void) /// @todo Use nonblocking sockets to avoid hanging on accept or read, or just wait until we have filesystem servers /// and do that instead. - char* id = sreadfile("/net/local/seqpacket"); + char* id = readfiles("/net/local/seqpacket"); if (id == NULL) { printf("boxd: failed to open local seqpacket socket (%s)\n", strerror(errno)); abort(); } - if (swritefile(F("/net/local/%s/ctl", id), "bind boxspawn && listen") == ERR) + if (writefiles(F("/net/local/%s/ctl", id), "bind boxspawn && listen") == ERR) { printf("boxd: failed to bind to box (%s)\n", strerror(errno)); goto error; @@ -440,7 +440,7 @@ int main(void) box_spawn(&ctx); - if (swrite(client, ctx.result) == ERR) + if (writes(client, ctx.result) == ERR) { printf("boxd: failed to write response (%s)\n", strerror(errno)); } diff --git a/src/programs/core/boxspawn/main.c b/src/programs/core/boxspawn/main.c index f8955eaa3..1cffc263c 100644 --- a/src/programs/core/boxspawn/main.c +++ b/src/programs/core/boxspawn/main.c @@ -29,14 +29,14 @@ int main(int argc, char** argv) return EXIT_FAILURE; } - char* id = sreadfile("/net/local/seqpacket"); + char* id = readfiles("/net/local/seqpacket"); if (id == NULL) { printf("boxspawn: failed to open local seqpacket socket (%s)\n", strerror(errno)); return EXIT_FAILURE; } - if (swritefile(F("/net/local/%s/ctl", id), "connect boxspawn") == ERR) + if (writefiles(F("/net/local/%s/ctl", id), "connect boxspawn") == ERR) { printf("boxspawn: failed to connect to boxspawn (%s)\n", strerror(errno)); free(id); @@ -116,7 +116,7 @@ int main(int argc, char** argv) return EXIT_FAILURE; } - if (swrite(data, buffer) == ERR) + if (writes(data, buffer) == ERR) { printf("boxspawn: failed to send request (%s)\n", strerror(errno)); free(id); @@ -174,5 +174,5 @@ int main(int argc, char** argv) } close(wait); - proc_exit(status); + exits(status); } \ No newline at end of file diff --git a/src/programs/core/init/main.c b/src/programs/core/init/main.c index d50412235..52a378f2b 100644 --- a/src/programs/core/init/main.c +++ b/src/programs/core/init/main.c @@ -59,7 +59,7 @@ static uint64_t init_socket_addr_wait(const char* family, const char* addr) { nanosleep(CLOCKS_PER_SEC / 10); - const char* data = sreadfile(F("/net/%s/addrs", family)); + const char* data = readfiles(F("/net/%s/addrs", family)); if (data == NULL) { close(addrs); diff --git a/src/programs/core/shell/interactive.c b/src/programs/core/shell/interactive.c index ca590c13b..036ec8a6c 100644 --- a/src/programs/core/shell/interactive.c +++ b/src/programs/core/shell/interactive.c @@ -231,7 +231,7 @@ void interactive_shell(void) { if (signal(SIGINT, interactive_sigint_handler) == SIG_ERR) { - proc_exit(F("shell: failed to set SIGINT handler (%s)\n", strerror(errno))); + exits(F("shell: failed to set SIGINT handler (%s)\n", strerror(errno))); } printf("Welcome to the PatchworkOS Shell!\n"); diff --git a/src/programs/core/shell/main.c b/src/programs/core/shell/main.c index 215353eaa..596f80e08 100644 --- a/src/programs/core/shell/main.c +++ b/src/programs/core/shell/main.c @@ -59,13 +59,13 @@ void execute_command(const char* cmdline) pipeline_t pipeline; if (pipeline_init(&pipeline, cmdline, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO) == ERR) { - proc_exit(F("shell: failed to initialize pipeline (%s)\n", strerror(errno))); + exits(F("shell: failed to initialize pipeline (%s)\n", strerror(errno))); } pipeline_execute(&pipeline); pipeline_wait(&pipeline); - proc_exit(pipeline.status); + exits(pipeline.status); } int main(int argc, char* argv[]) diff --git a/src/programs/core/shell/pipeline.c b/src/programs/core/shell/pipeline.c index e3f467e25..79ffd27e8 100644 --- a/src/programs/core/shell/pipeline.c +++ b/src/programs/core/shell/pipeline.c @@ -354,7 +354,7 @@ static pid_t pipeline_execute_cmd(cmd_t* cmd) else { bool isFound = false; - char* pathEnv = sreadfile("/proc/self/env/PATH"); + char* pathEnv = readfiles("/proc/self/env/PATH"); if (pathEnv == NULL) { pathEnv = strdup("/bin:/usr/bin"); diff --git a/src/programs/utils/cat/main.c b/src/programs/utils/cat/main.c index ff6be1496..b563ad057 100644 --- a/src/programs/utils/cat/main.c +++ b/src/programs/utils/cat/main.c @@ -29,7 +29,7 @@ static size_t read_fd(fd_t fd, const char* name, bool hexOutput) { for (uint64_t i = 0; i < count; i++) { - swrite(STDOUT_FILENO, F("%02x ", (unsigned char)buffer[i])); + writes(STDOUT_FILENO, F("%02x ", (unsigned char)buffer[i])); } continue; } diff --git a/src/programs/utils/echo/main.c b/src/programs/utils/echo/main.c index c27d0e9d5..e4e57ae80 100644 --- a/src/programs/utils/echo/main.c +++ b/src/programs/utils/echo/main.c @@ -10,7 +10,7 @@ int main(int argc, char** argv) { for (int i = 1; i < argc; i++) { - if (swrite(STDOUT_FILENO, argv[i]) == ERR) + if (writes(STDOUT_FILENO, argv[i]) == ERR) { fprintf(stderr, "echo: %s\n", strerror(errno)); return EXIT_FAILURE; @@ -18,7 +18,7 @@ int main(int argc, char** argv) if (i != argc - 1) { - if (swrite(STDOUT_FILENO, " ") == ERR) + if (writes(STDOUT_FILENO, " ") == ERR) { fprintf(stderr, "echo: %s\n", strerror(errno)); return EXIT_FAILURE; From 20f74a2021e14a935af7e2aa0c03e0fbb11b0bd5 Mon Sep 17 00:00:00 2001 From: KN Date: Thu, 15 Jan 2026 09:14:39 +0100 Subject: [PATCH 4/8] feat(kernel): add SYS_ARCH_PRCTL system call --- README.md | 2 +- include/kernel/cpu/syscall.h | 1 + include/kernel/sched/thread.h | 1 + include/libstd/sys/math.h | 4 +-- include/libstd/sys/proc.h | 19 +++++++++++++ src/kernel/sched/thread.c | 28 ++++++++++++++++++++ src/libstd/common/common.h | 13 +++++++++ src/libstd/common/heap.h | 4 +-- src/libstd/common/print.h | 4 +-- src/libstd/common/scan.h | 4 +-- src/libstd/functions/errno/errno.c | 7 +---- src/libstd/user/common/common.h | 16 +++++++++++ src/libstd/user/common/syscalls.h | 5 ++++ src/libstd/user/common/threading.c | 5 ++++ src/libstd/user/common/threading.h | 17 ++++++++++++ src/libstd/user/functions/proc/arch_prctl.c | 14 ++++++++++ src/libstd/user/functions/thread/thrd_exit.c | 2 +- src/libstd/user/user.h | 15 +---------- 18 files changed, 131 insertions(+), 30 deletions(-) create mode 100644 src/libstd/common/common.h create mode 100644 src/libstd/user/common/common.h create mode 100644 src/libstd/user/functions/proc/arch_prctl.c diff --git a/README.md b/README.md index 73e315427..5151168cd 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Will this project ever reach its goals? Probably not, but that's not the point. ### User Space -- Custom superset of the ANSI C standard library including threading, file I/O, math, PatchworkOS extensions, etc. +- Custom superset of the ANSI C standard library including threading, buffered I/O, math.h, PatchworkOS extensions, etc. - Highly modular shared memory based desktop environment. - Theming via [config files](https://github.com/KaiNorberg/PatchworkOS/blob/main/root/cfg). - Capability based containerization security model using per-process mountpoint namespaces. See [Security](#security) for more info. diff --git a/include/kernel/cpu/syscall.h b/include/kernel/cpu/syscall.h index 2ae11600b..633865020 100644 --- a/include/kernel/cpu/syscall.h +++ b/include/kernel/cpu/syscall.h @@ -101,6 +101,7 @@ typedef enum SYS_SYMLINK, SYS_MOUNT, SYS_UNMOUNT, + SYS_ARCH_PRCTL, SYS_TOTAL_AMOUNT } syscall_number_t; diff --git a/include/kernel/sched/thread.h b/include/kernel/sched/thread.h index e0317f558..a19d4b69f 100644 --- a/include/kernel/sched/thread.h +++ b/include/kernel/sched/thread.h @@ -79,6 +79,7 @@ typedef struct thread syscall_ctx_t syscall; perf_thread_ctx_t perf; rcu_entry_t rcu; + uintptr_t fsBase; ///< The FS base address for the thread. /** * The threads interrupt frame is used to save the values in the CPU registers such that the scheduler can continue * executing the thread later on. diff --git a/include/libstd/sys/math.h b/include/libstd/sys/math.h index 0f1142063..d995879c2 100644 --- a/include/libstd/sys/math.h +++ b/include/libstd/sys/math.h @@ -4,9 +4,9 @@ #include /** - * @brief Common math macros. + * @brief Helper Macros for math operations. * @ingroup libstd - * @defgroup libstd_sys_math Common math macros + * @defgroup libstd_sys_math Math Helpers * * The `sys/math.h` header provides common math macros for operations such as clamping, rounding, and linear * interpolation. diff --git a/include/libstd/sys/proc.h b/include/libstd/sys/proc.h index d2be8fd4c..de9dc0782 100644 --- a/include/libstd/sys/proc.h +++ b/include/libstd/sys/proc.h @@ -339,6 +339,25 @@ _NORETURN void exits(const char* status); */ uint64_t kill(pid_t pid); +/** + * @brief Architecture specific thread data codes. + * @typedef arch_prctl_t + */ +typedef enum +{ + ARCH_GET_FS = 0, ///< Get the FS base address. + ARCH_SET_FS = 1, ///< Set the FS base address. +} arch_prctl_t; + +/** + * @brief System call for setting architecture specific thread data. + * + * @param op The operation to perform. + * @param addr If getting data, a pointer to store the retrieved address. If setting data, the address to set. + * @return On success, `0`. On failure, `ERR` and `errno` is set. + */ +uint64_t arch_prctl(arch_prctl_t op, uintptr_t addr); + #if defined(__cplusplus) } #endif diff --git a/src/kernel/sched/thread.c b/src/kernel/sched/thread.c index e59327195..a6fc753f2 100644 --- a/src/kernel/sched/thread.c +++ b/src/kernel/sched/thread.c @@ -1,3 +1,4 @@ +#include #include #include @@ -35,6 +36,7 @@ static void thread_ctor(void* ptr) thread->syscall = (syscall_ctx_t){0}; thread->perf = (perf_thread_ctx_t){0}; thread->rcu = (rcu_entry_t){0}; + thread->fsBase = 0; memset_s(&thread->frame, sizeof(interrupt_frame_t), 0, sizeof(interrupt_frame_t)); } @@ -96,6 +98,8 @@ thread_t* thread_new(process_t* process) syscall_ctx_init(&thread->syscall, &thread->kernelStack); perf_thread_ctx_init(&thread->perf); + thread->fsBase = 0; + REF(process); lock_acquire(&process->threads.lock); process->threads.count++; @@ -160,6 +164,8 @@ void thread_load(thread_t* thread, interrupt_frame_t* frame) vmm_load(&thread->process->space); simd_ctx_load(&thread->simd); syscall_ctx_load(&thread->syscall); + + msr_write(MSR_FS_BASE, thread->fsBase); } bool thread_is_note_pending(thread_t* thread) @@ -381,3 +387,25 @@ uint64_t thread_load_atomic_from_user(thread_t* thread, atomic_uint64_t* userObj space_unpin(&thread->process->space, userObj, sizeof(atomic_uint64_t)); return 0; } + +SYSCALL_DEFINE(SYS_ARCH_PRCTL, uint64_t, arch_prctl_t op, uintptr_t addr) +{ + thread_t* thread = thread_current(); + + switch (op) + { + case ARCH_SET_FS: + thread->fsBase = addr; + msr_write(MSR_FS_BASE, addr); + return 0; + case ARCH_GET_FS: + if (thread_copy_to_user(thread, (void*)addr, &thread->fsBase, sizeof(uintptr_t)) == ERR) + { + return ERR; + } + return 0; + default: + errno = EINVAL; + return ERR; + } +} \ No newline at end of file diff --git a/src/libstd/common/common.h b/src/libstd/common/common.h new file mode 100644 index 000000000..6693571f7 --- /dev/null +++ b/src/libstd/common/common.h @@ -0,0 +1,13 @@ +#pragma once + +/** + * @brief Internal common functions. + * @defgroup libstd_common Common + * @ingroup libstd + * + * Common functions and definitions shared between kernel and user space. + * + * @{ + */ + +/** @} */ diff --git a/src/libstd/common/heap.h b/src/libstd/common/heap.h index 5eb96b069..b82867a2d 100644 --- a/src/libstd/common/heap.h +++ b/src/libstd/common/heap.h @@ -14,8 +14,8 @@ /** * @brief Internal Heap Implementation. - * @defgroup libstd_heap Heap - * @ingroup libstd + * @defgroup libstd_common_heap Heap + * @ingroup libstd_common * * We use a "segregated free list" allocator with a set of bins where each bin stores free blocks of size \f$n \cdot * 64\f$ bytes where \f$n\f$ is the index of the bin, up to `_HEAP_LARGE_ALLOC_THRESHOLD`. Above this size, blocks are diff --git a/src/libstd/common/print.h b/src/libstd/common/print.h index 0a2c44068..93f2e062c 100644 --- a/src/libstd/common/print.h +++ b/src/libstd/common/print.h @@ -22,8 +22,8 @@ /** * @brief Internal Print Implementation. - * @defgroup libstd_print Print - * @ingroup libstd + * @defgroup libstd_common_print Print + * @ingroup libstd_common * * Provides a common implementation for printing formatted output, any function that needs to print formatted output * should define the `_PRINT_WRITE()` and `_PRINT_FILL()` macros before including this file. diff --git a/src/libstd/common/scan.h b/src/libstd/common/scan.h index 2f44b23fd..fc3a66928 100644 --- a/src/libstd/common/scan.h +++ b/src/libstd/common/scan.h @@ -21,8 +21,8 @@ /** * @brief Internal Scan Implementation. - * @defgroup libstd_scan Scan - * @ingroup libstd + * @defgroup libstd_common_scan Scan + * @ingroup libstd_common * * Provides a common implementation for scanning formatted input, any function that needs to scan formatted input should * define the `_SCAN_GET()` and `_SCAN_UNGET()` macros before including this file. diff --git a/src/libstd/functions/errno/errno.c b/src/libstd/functions/errno/errno.c index 99b3e98f4..2bbdcd409 100644 --- a/src/libstd/functions/errno/errno.c +++ b/src/libstd/functions/errno/errno.c @@ -21,11 +21,6 @@ int* _errno_get(void) return &thread->error; #else - _thread_t* thread = _thread_get(gettid()); - if (thread == NULL) - { - return &garbage; - } - return &thread->err; + return &_THREAD_SELF->self->err; #endif } diff --git a/src/libstd/user/common/common.h b/src/libstd/user/common/common.h new file mode 100644 index 000000000..13d683cef --- /dev/null +++ b/src/libstd/user/common/common.h @@ -0,0 +1,16 @@ +#pragma once + +/** + * @brief Internal user space common functions. + * @defgroup libstd_common_user User Space + * @ingroup libstd_common + * + * While the rest of the standard library is shared between kernel and user space, this module contains code that will + * only be used in user space. + * + * @{ + */ + +void _user_init(void); + +/** @} */ diff --git a/src/libstd/user/common/syscalls.h b/src/libstd/user/common/syscalls.h index 41a26ae75..ea6e0fe8b 100644 --- a/src/libstd/user/common/syscalls.h +++ b/src/libstd/user/common/syscalls.h @@ -278,4 +278,9 @@ static inline uint64_t _syscall_mount(const char* mountpoint, const char* fs, co static inline uint64_t _syscall_umount(const char* mountpoint) { return _SYSCALL1(uint64_t, SYS_UNMOUNT, const char*, mountpoint); +} + +static inline uint64_t _syscall_arch_prctl(arch_prctl_t code, uintptr_t addr) +{ + return _SYSCALL2(uint64_t, SYS_ARCH_PRCTL, arch_prctl_t, code, uintptr_t, addr); } \ No newline at end of file diff --git a/src/libstd/user/common/threading.c b/src/libstd/user/common/threading.c index efc6fed3d..f193e2af2 100644 --- a/src/libstd/user/common/threading.c +++ b/src/libstd/user/common/threading.c @@ -51,6 +51,7 @@ static void _thread_remove(_thread_t* thread) static void _thread_init(_thread_t* thread) { + thread->self = thread; atomic_init(&thread->state, _THREAD_ATTACHED); thread->id = 0; thread->result = 0; @@ -71,10 +72,14 @@ void _threading_init(void) thread0.id = _syscall_gettid(); _thread_insert(&thread0); + + arch_prctl(ARCH_SET_FS, (uintptr_t)&thread0); } _NORETURN static void _thread_entry(_thread_t* thread) { + arch_prctl(ARCH_SET_FS, (uintptr_t)thread); + thrd_exit(thread->func(thread->arg)); } diff --git a/src/libstd/user/common/threading.h b/src/libstd/user/common/threading.h index 11db42f9a..228dbd4dc 100644 --- a/src/libstd/user/common/threading.h +++ b/src/libstd/user/common/threading.h @@ -6,6 +6,18 @@ #include #include +/** + * @brief Threading. + * @defgroup libstd_common_user_threading Threading + * @ingroup libstd_common_user + * + * @todo Write threading documentation. + * + * @todo Implement Thread Local Storage (TLS). + * + * @{ + */ + #define _MTX_SPIN_COUNT 100 #define _THREADS_MAX 2048 @@ -21,6 +33,7 @@ typedef void (*_thread_entry_t)(_thread_t*); typedef struct _thread { + _thread_t* self; atomic_uint64_t state; tid_t id; int result; @@ -36,3 +49,7 @@ _thread_t* _thread_new(thrd_start_t func, void* arg); void _thread_free(_thread_t* thread); _thread_t* _thread_get(tid_t id); + +#define _THREAD_SELF ((_thread_t __seg_fs*)0) + +/** @} */ \ No newline at end of file diff --git a/src/libstd/user/functions/proc/arch_prctl.c b/src/libstd/user/functions/proc/arch_prctl.c new file mode 100644 index 000000000..4f5a809a9 --- /dev/null +++ b/src/libstd/user/functions/proc/arch_prctl.c @@ -0,0 +1,14 @@ +#include +#include + +#include "user/common/syscalls.h" + +uint64_t arch_prctl(arch_prctl_t op, uintptr_t addr) +{ + uint64_t result = _syscall_arch_prctl(op, addr); + if (result == ERR) + { + errno = _syscall_errno(); + } + return result; +} diff --git a/src/libstd/user/functions/thread/thrd_exit.c b/src/libstd/user/functions/thread/thrd_exit.c index 4dd20bd0d..38335af64 100644 --- a/src/libstd/user/functions/thread/thrd_exit.c +++ b/src/libstd/user/functions/thread/thrd_exit.c @@ -10,7 +10,7 @@ void thrd_exit(int res) { - _thread_t* thread = _thread_get(gettid()); + _thread_t* thread = _THREAD_SELF->self; if (thread == NULL) { fprintf(stderr, "libstd: thrd_exit called from unknown thread\n"); diff --git a/src/libstd/user/user.h b/src/libstd/user/user.h index fee9d3b4d..45a6a9091 100644 --- a/src/libstd/user/user.h +++ b/src/libstd/user/user.h @@ -1,16 +1,3 @@ #pragma once -/** - * @brief User space only functions and definitions. - * @defgroup libstd_user User Space - * @ingroup libstd - * - * While the rest of the standard library is shared between kernel and user space, this module contains code that will - * only be used in user space. - * - * @{ - */ - -void _user_init(void); - -/** @} */ +void _user_init(void); \ No newline at end of file From 386fe1df694cda38c22de9b05722b14c133e3f68 Mon Sep 17 00:00:00 2001 From: KN Date: Thu, 15 Jan 2026 11:33:17 +0100 Subject: [PATCH 5/8] refactor(kernel:sched): rename sched_process_exit to sched_exits --- include/kernel/sched/sched.h | 2 +- src/kernel/sched/loader.c | 2 +- src/kernel/sched/sched.c | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/kernel/sched/sched.h b/include/kernel/sched/sched.h index 133ac6dc7..c524255b6 100644 --- a/include/kernel/sched/sched.h +++ b/include/kernel/sched/sched.h @@ -491,7 +491,7 @@ void sched_enable(void); * * @param status The exit status of the process. */ -_NORETURN void sched_process_exit(const char* status); +_NORETURN void sched_exits(const char* status); /** * @brief Terminates the currently executing thread. diff --git a/src/kernel/sched/loader.c b/src/kernel/sched/loader.c index 7563e9a37..748758660 100644 --- a/src/kernel/sched/loader.c +++ b/src/kernel/sched/loader.c @@ -160,7 +160,7 @@ void loader_exec(void) thread_jump(thread); } LOG_DEBUG("exec failed due to %s pid=%llu\n", strerror(errno), pid); - sched_process_exit("exec failed"); + sched_exits("exec failed"); } static void loader_entry(void) diff --git a/src/kernel/sched/sched.c b/src/kernel/sched/sched.c index c1ec66d8f..9099dd155 100644 --- a/src/kernel/sched/sched.c +++ b/src/kernel/sched/sched.c @@ -651,14 +651,14 @@ void sched_enable(void) atomic_fetch_sub(&sched->preemptCount, 1); } -void sched_process_exit(const char* status) +void sched_exits(const char* status) { thread_t* thread = thread_current(); process_kill(thread->process, status); atomic_store(&thread->state, THREAD_DYING); ipi_invoke(); - panic(NULL, "Return to sched_process_exit"); + panic(NULL, "Return to sched_exits"); } void sched_thread_exit(void) @@ -676,9 +676,9 @@ SYSCALL_DEFINE(SYS_NANOSLEEP, uint64_t, clock_t nanoseconds) SYSCALL_DEFINE(SYS_EXITS, void, const char* status) { - sched_process_exit(status); + sched_exits(status); - panic(NULL, "Return to syscall_process_exit"); + panic(NULL, "Return to syscall_exits"); } SYSCALL_DEFINE(SYS_THREAD_EXIT, void) From b9949eb0ea3821598289d0210706387f959bd361 Mon Sep 17 00:00:00 2001 From: KN Date: Thu, 15 Jan 2026 11:35:52 +0100 Subject: [PATCH 6/8] chore(README): update future plans --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5151168cd..6fb39f711 100644 --- a/README.md +++ b/README.md @@ -84,12 +84,12 @@ Will this project ever reach its goals? Probably not, but that's not the point. ## Notable Future Plans +- Fully Asynchronous I/O and syscalls (io_uring?). <-- Currently being worked on. - Implement 9P file servers. <-- Currently being worked on. - Implement user system in user-space using namespaces. - Improve `share()` and `claim()` security by specifying a target PID when sharing. - Overhaul Desktop Window Manager to use the new security system and file servers? - Port LUA and use it for dynamic system configuration. -- Fully Asynchronous I/O and syscalls (io_uring?). - USB support. ## Setup From 825558bd68a893c7645bdc03e23aceb9dbcdfac9 Mon Sep 17 00:00:00 2001 From: KN Date: Thu, 15 Jan 2026 11:39:20 +0100 Subject: [PATCH 7/8] fix(libstd:list): remove faulty assert --- include/libstd/sys/list.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/libstd/sys/list.h b/include/libstd/sys/list.h index 30a27d63c..f2745d75e 100644 --- a/include/libstd/sys/list.h +++ b/include/libstd/sys/list.h @@ -337,7 +337,6 @@ static inline void list_push_back_rcu(list_t* list, list_entry_t* entry) { assert(list != NULL); assert(entry != NULL); - assert(entry->next == entry && entry->prev == entry); list_add_rcu(list->head.prev, &list->head, entry); } From 79f948d8403365bfd2e48a2318b456d106b1d455 Mon Sep 17 00:00:00 2001 From: KN Date: Thu, 15 Jan 2026 11:43:12 +0100 Subject: [PATCH 8/8] docs(kernel:rcu): fix link formating --- include/kernel/sync/rcu.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/kernel/sync/rcu.h b/include/kernel/sync/rcu.h index 323388821..f8151227f 100644 --- a/include/kernel/sync/rcu.h +++ b/include/kernel/sync/rcu.h @@ -42,8 +42,8 @@ typedef struct rcu_entry rcu_entry_t; * To access RCU protected data, a read-side critical section must be created using `rcu_read_lock()` and * `rcu_read_unlock()`, or the `RCU_READ_SCOPE()` macro. * - * @see https://en.wikipedia.org/wiki/Read-copy-update for more information about RCU. - * @see https://www.kernel.org/doc/Documentation/RCU/whatisRCU.txt for a explanation of RCU in the Linux kernel. + * @see [https://en.wikipedia.org/wiki/Read-copy-update](Wikipedia) for more information about RCU. + * @see [https://www.kernel.org/doc/Documentation/RCU/whatisRCU.txt](kernel.org) for a explanation of RCU in the Linux kernel. * * @{ */