From 5ab3b18865eb813d433a6c0687dfa1f62a1b6055 Mon Sep 17 00:00:00 2001 From: Jon Santmyer Date: Fri, 9 Oct 2026 16:50:13 -0400 Subject: add ipc send/recv mechanism for kernel objects. --- api/syscall.h | 10 ++-- arch/x86_64/proc/switch.s | 45 ++++++++++++++++++ doc/syscall.md | 6 +-- proc/process.c | 67 ++++++++++++++++++++++++++ proc/process.h | 11 ++++- proc/scheduler.c | 117 ++++++++++++++++++++++++++++++++++++++++++---- proc/scheduler.h | 5 ++ sys/objdir.c | 7 +++ sys/objdir.h | 1 + sys/syscall_pcb.c | 74 +++++++++++++++++++++++++++++ 10 files changed, 323 insertions(+), 20 deletions(-) diff --git a/api/syscall.h b/api/syscall.h index 88aad81..6b10569 100644 --- a/api/syscall.h +++ b/api/syscall.h @@ -182,19 +182,15 @@ enum SYSCALL_PCB_YIELD, SYSCALL_PCB_SEND, SYSCALL_PCB_RECV, - SYSCALL_PCB_SET_RDIR, - SYSCALL_PCB_SET_PDIR, - SYSCALL_PCB_SET_READY, + SYSCALL_PCB_READY, }; #define SYSCALL_PERM_PCB_YIELD (JOVE_PERM_WRITE) #define SYSCALL_PERM_PCB_SEND (JOVE_PERM_WRITE) #define SYSCALL_PERM_PCB_RECV (JOVE_PERM_WRITE) -#define SYSCALL_PERM_PCB_SET_RDIR (JOVE_PERM_WRITE | JOVE_PERM_MUTATE) -#define SYSCALL_PERM_PCB_SET_PDIR (JOVE_PERM_WRITE | JOVE_PERM_MUTATE) -#define SYSCALL_PERM_PCB_SET_READY (JOVE_PERM_WRITE | JOVE_PERM_MUTATE) +#define SYSCALL_PERM_PCB_READY (JOVE_PERM_WRITE | JOVE_PERM_MUTATE) -#define SYSCALL_PERM_PCB_SEND_TGT (JOVE_PERM_MUTATE) +#define SYSCALL_PERM_PCB_SEND_OBJDIR (JOVE_PERM_WRITE) #define SYSCALL_PERM_PCB_RECV_DST (JOVE_PERM_MUTATE) struct syscall_pcb_yield diff --git a/arch/x86_64/proc/switch.s b/arch/x86_64/proc/switch.s index d571c66..16b2198 100644 --- a/arch/x86_64/proc/switch.s +++ b/arch/x86_64/proc/switch.s @@ -7,3 +7,48 @@ scheduler_switch: retq .size scheduler_switch, . - scheduler_switch + +.global scheduler_savestate +.type scheduler_savestate @function + +scheduler_savestate: + pushq %r15 + pushq %r14 + pushq %r13 + pushq %r12 + pushq %r11 + pushq %r10 + pushq %r9 + pushq %r8 + pushq %rbp + pushq %rdi + pushq %rsi + pushq %rdx + pushq %rcx + pushq %rbx + pushq %rax + +.size scheduler_savestate, . - scheduler_savestate + +.global scheduler_restorestate +.type scheduler_restorestate @function + +scheduler_restorestate: + popq %rax + popq %rbx + popq %rcx + popq %rdx + popq %rsi + popq %rdi + popq %rbp + popq %r8 + popq %r9 + popq %r10 + popq %r11 + popq %r12 + popq %r13 + popq %r14 + popq %r15 + +.size scheduler_restorestate, . - scheduler_restorestate + diff --git a/doc/syscall.md b/doc/syscall.md index 1e03425..53626c8 100644 --- a/doc/syscall.md +++ b/doc/syscall.md @@ -305,9 +305,9 @@ If blocking timeout is zero, then the process will wait for the destination proc If the blocking timeout is non-zero, then the process will wait for the destination process to recieve the object for the specified number of scheduler ticks. -| Data | Destination Process | Blocking Timeout | Send Path Length | Send Path | -|--------------|---------------------|-------------------|------------------|----------------------| -| Size (bytes) | sizeof(pid_t) | sizeof(uintmax_t) | 2 | 2 * send_path_length | +| Data | Destination Process | Blocking Timeout | Send Path Length | Send Path | +|--------------|---------------------|-------------------|------------------------|-------------------------------------------| +| Size (bytes) | sizeof(pid_t) | sizeof(uintmax_t) | sizeof(objpath_part_t) | sizeof(objpath_part_t) * send_path_length | While the object is being sent, the kernel will lock the object and copy a pointer to it into the send mailbox. diff --git a/proc/process.c b/proc/process.c index 137e35c..62c0269 100644 --- a/proc/process.c +++ b/proc/process.c @@ -1,6 +1,8 @@ #include "process.h" +#include "api/error.h" #include "api/process.h" #include "hal/smp.h" +#include "proc/scheduler.h" #include "sys/objdir.h" #include @@ -26,6 +28,71 @@ pcb_new(jove_owned_obj_t *pcb_obj, size_t size, jove_owned_obj_t *rootdir_obj) return _pcb_new_arch((pcb_t*)pcb_header); } +int +pcb_send(pcb_t *pcb, pid_t target, jove_owned_obj_t *subject, size_t timeout) +{ + struct pcb_header *pcb_header = (struct pcb_header*)pcb; + struct pcb_header *target_pcb = (struct pcb_header*)rbtree_search(pcb_tree, target); + if(target_pcb == 0) { + return -EBADPROC; + } + + if(target_pcb->recv.obj == NULL && + (target_pcb->recv.pid == pcb_getpid(pcb) || target_pcb->recv.pid == 0)) + { + target_pcb->recv.obj = subject; + target_pcb->recv.pid = pcb_getpid(pcb); + return scheduler_yield(target); + } + + if(timeout == 0) { + return -EFULL; + } + + pcb_header->send.obj = subject; + pcb_header->send.pid = target; + + scheduler_block_for_mail(pcb_header, timeout, target); + + if(pcb_header->send.obj) return -ETIMEOUT; + return 0; +} + +int +pcb_recv(pcb_t *pcb, pid_t target, jove_owned_obj_t *dest_obj, size_t timeout) +{ + struct pcb_header *pcb_header = (struct pcb_header*)pcb; + struct pcb_header *target_pcb = (struct pcb_header*)rbtree_search(pcb_tree, target); + if(target_pcb == 0) { + return -EBADPROC; + } + + if(pcb_header->recv.obj) { + goto recv_success; + return 0; + } + + if(timeout == 0) { + return -EEMPTY; + } + + pcb_header->recv.pid = target; + scheduler_block_for_mail(pcb_header, timeout, target); + + if(!pcb_header->recv.obj) { + pcb_header->recv.pid = -1; + return -ETIMEOUT; + } + +recv_success: + objdir_swapobj(pcb_header->recv.obj, dest_obj); + obj_release(pcb_header->recv.obj); + pcb_header->recv.obj = 0; + pcb_header->recv.pid = -1; + + return 0; +} + pcb_t *pcb_frompid(pid_t pid) { return (pcb_t*)rbtree_search((struct rbtree_node*)pcb_tree, pid); diff --git a/proc/process.h b/proc/process.h index 950f63e..21dbf8d 100644 --- a/proc/process.h +++ b/proc/process.h @@ -7,14 +7,14 @@ struct pcb_mailbox { - jove_obj_t *obj; + jove_owned_obj_t *obj; pid_t pid; }; struct pcb_header { struct rbtree_node tree_node; - struct pcb_header *sched_next; + struct pcb_header *sched_prev, *sched_next; size_t size; pid_t pid; @@ -22,6 +22,7 @@ struct pcb_header enum pcb_state state; intmax_t state_code; + size_t block_until; struct pcb_mailbox send, recv; @@ -42,6 +43,9 @@ jove_obj_t *pcb_getrootdir(pcb_t *pcb); jove_owned_obj_t *pcb_getpagedir(jove_owned_obj_t *rootdir_obj); jove_owned_obj_t *pcb_getself(jove_owned_obj_t *rootdir_obj); +struct pcb_mailbox *pcb_getmail_send(pcb_t *pcb); +struct pcb_mailbox *pcb_getmail_recv(pcb_t *pcb); + enum pcb_state pcb_getstate(pcb_t *pcb); intmax_t pcb_getstatecode(pcb_t *pcb); @@ -50,6 +54,9 @@ void **pcb_getksp_ref(pcb_t *pcb); pcb_t *pcb_frompid(pid_t pid); +int pcb_send(pcb_t *pcb, pid_t target, jove_owned_obj_t *subject, size_t timeout); +int pcb_recv(pcb_t *pcb, pid_t target, jove_owned_obj_t *dest_obj, size_t timeout); + extern struct rbtree_node *pcb_tree; #endif diff --git a/proc/scheduler.c b/proc/scheduler.c index 875bcc4..29f45b5 100644 --- a/proc/scheduler.c +++ b/proc/scheduler.c @@ -5,24 +5,27 @@ #include "proc/init.h" #include "proc/process.h" +//TODO: Better scheduling algorithm than round robin. +static struct pcb_header *s_running_list; + static struct pcb_header *s_ready_queue; +static struct pcb_header *s_mail_queue; + +static uintmax_t s_this_tick; -static inline int -s_scheduler_yield_pcb(processor_t *proc, pcb_t *current_pcb, pcb_t *target_pcb) +static inline void +s_scheduler_swap_pcb(processor_t *proc, pcb_t *current_pcb, pcb_t *target_pcb) { jove_owned_obj_t *target_rootdir = obj_take(pcb_getrootdir(target_pcb)); jove_owned_obj_t *target_pcb_obj = pcb_getself(target_rootdir); + obj_release(target_rootdir); processor_setpcb(proc, target_pcb_obj); - obj_release(target_rootdir); _scheduler_switch_pcb_arch(target_pcb); scheduler_switch(pcb_getksp_ref(current_pcb), pcb_getksp(target_pcb)); - - return 0; } - int scheduler_yield(pid_t target_pid) { processor_t *proc = processor_current(); @@ -35,19 +38,117 @@ int scheduler_yield(pid_t target_pid) if(target_pcb == 0) { return -EBADPROC; } + + scheduler_savestate(); + s_scheduler_swap_pcb(proc, current_pcb, target_pcb); + scheduler_restorestate(); + return 0; +} - return s_scheduler_yield_pcb(proc, current_pcb, target_pcb); +void +s_enqueue_pcb(struct pcb_header *pcb, struct pcb_header *list) +{ + if(pcb->sched_prev) pcb->sched_prev->sched_next = pcb->sched_next; + if(pcb->sched_prev) pcb->sched_next->sched_prev = pcb->sched_prev; + + list->sched_prev = pcb; + pcb->sched_next = list; + pcb->sched_prev = NULL; +} + +void +scheduler_block_for_mail(struct pcb_header *pcb, uintmax_t timeout, pid_t yield_target) +{ + pcb->state = PCB_STATE_BLOCKED_MAIL; + + s_enqueue_pcb(pcb, s_mail_queue); + s_mail_queue = pcb; + + pcb->block_until = s_this_tick + timeout; + scheduler_yield(yield_target); +} + +void +s_schedule_ready_queue(void) +{ + for(struct pcb_header *pcb = s_ready_queue; pcb; pcb = s_ready_queue) { + s_ready_queue = pcb->sched_next; + + //Add this process to the ready list after the last ready process. + pcb->state = PCB_STATE_READY; + s_enqueue_pcb(pcb, s_running_list); + s_running_list = pcb; + } +} + +void +s_schedule_mail_queue(void) +{ + struct pcb_header *pcb = s_mail_queue; + while(pcb) { + struct pcb_header *next_pcb = pcb->sched_next; + + if(pcb->send.obj) { + struct pcb_header *send_target = (struct pcb_header*)pcb_frompid(pcb->send.pid); + if(send_target->recv.pid == 0 || send_target->recv.pid == pcb->pid) { + objdir_swapobj(send_target->recv.obj, pcb->send.obj); + send_target->recv.pid = pcb->pid; + + s_enqueue_pcb(pcb, s_running_list); + s_running_list = pcb; + goto loop_end; + } + } + if(pcb->recv.obj) { + s_enqueue_pcb(pcb, s_running_list); + s_running_list = pcb; + goto loop_end; + } + + if(s_this_tick >= pcb->block_until) { + s_enqueue_pcb(pcb, s_running_list); + s_running_list = pcb; + } +loop_end: + pcb = next_pcb; + } +} + +void +scheduler_tick() +{ + size_t last_tick = s_this_tick++; + + s_schedule_ready_queue(); + s_schedule_mail_queue(); + + processor_t *proc = processor_current(); + struct pcb_header *current_pcb = obj_deref(processor_getpcb(proc)); + struct pcb_header *next_pcb = s_running_list; + struct pcb_header *tail = next_pcb; + + while(tail->sched_next) { + tail = tail->sched_next; + if(tail == next_pcb) break; + } + + tail->sched_next = next_pcb; + next_pcb->sched_prev = tail; + s_running_list = next_pcb->sched_next; + + s_scheduler_swap_pcb(proc, (pcb_t*)current_pcb, (pcb_t*)next_pcb); } void scheduler_init(jove_owned_obj_t *pcb_obj, void *process_main) { struct pcb_header *pcb_header = obj_deref(pcb_obj); pcb_header->state = PCB_STATE_RUNNING; - pcb_header->sched_next = pcb_header; processor_t *proc = processor_current(); processor_setpcb(proc, pcb_obj); + s_running_list = pcb_header; + __asm__("movq %0, %%rsp; \ xorq %%rbp, %%rbp; \ jmp %1":: diff --git a/proc/scheduler.h b/proc/scheduler.h index 0371e86..2fc0c44 100644 --- a/proc/scheduler.h +++ b/proc/scheduler.h @@ -5,10 +5,15 @@ void scheduler_init(jove_owned_obj_t *pcb_obj, void *process_main); +void scheduler_savestate(void); +void scheduler_restorestate(void); + void scheduler_switch(void *this_sp, void *other_sp); void _scheduler_switch_pcb_arch(pcb_t *target); int scheduler_yield(pid_t target); +void scheduler_tick(void); +void scheduler_block_for_mail(struct pcb_header *pcb, size_t timeout, pid_t yield_target); #endif diff --git a/sys/objdir.c b/sys/objdir.c index c448e85..eab935f 100644 --- a/sys/objdir.c +++ b/sys/objdir.c @@ -112,3 +112,10 @@ objdir_trygetobj( } } +void +objdir_swapobj(jove_owned_obj_t *a, jove_owned_obj_t *b) +{ + jove_owned_obj_t buffer_obj = *a; + *a = *b; + *b = buffer_obj; +} diff --git a/sys/objdir.h b/sys/objdir.h index 54e9618..e2ae7ef 100644 --- a/sys/objdir.h +++ b/sys/objdir.h @@ -23,5 +23,6 @@ jove_owned_obj_t *objdir_getobj(jove_owned_obj_t *objdir_obj, const uint16_t *pa /*Same as objdir_getobj but does not block if child objects are locked.*/ jove_owned_obj_t *objdir_trygetobj(jove_owned_obj_t *objdir_obj, const uint16_t *path, size_t path_len); +void objdir_swapobj(jove_owned_obj_t *a, jove_owned_obj_t *b); #endif diff --git a/sys/syscall_pcb.c b/sys/syscall_pcb.c index a37e4bb..4fe557b 100644 --- a/sys/syscall_pcb.c +++ b/sys/syscall_pcb.c @@ -1,4 +1,6 @@ #include "api/error.h" +#include "api/obj.h" +#include "api/syscall.h" #include "jove.h" #include "lib/printf.h" #include "proc/process.h" @@ -18,6 +20,66 @@ s_syscall_pcb_yield(jove_owned_obj_t *pcb_obj, return scheduler_yield(subject_pid); } +int +s_syscall_pcb_send(jove_owned_obj_t *pcb_obj, + lockstack_t *lockstack, + jove_owned_obj_t *objdir_obj, + size_t bytes_left, + void *payload_data) +{ + size_t bytes_needed = sizeof(struct syscall_pcb_send); + CHECK_TOOSMALL(bytes_needed, bytes_left, "Message too small for payload"); + + struct syscall_pcb_send *payload = payload_data; + bytes_needed += payload->send_path_length; + CHECK_TOOSMALL(bytes_needed, bytes_left, "Message too small for subject path"); + + jove_perm_obj_t subject_objdir = link_try_traverse_objdir( + objdir_obj, lockstack, payload->send_path_length - 1, payload->send_path); + if(errno) { + klogf("Unable to get subject %P objdir : %s\n", + payload->send_path, payload->send_path_length, errstr(errno)); + return -errno; + } + CHECK_OBJPERM(subject_objdir, SYSCALL_PERM_PCB_SEND_OBJDIR, "Subject objdir does not permit send"); + + jove_owned_obj_t *subject = objdir_trygetlocal(subject_objdir.obj, + payload->send_path[payload->send_path_length-1]); + if(subject == 0) { + klogf("Unable to get subject %P object : %s\n", + payload->send_path, payload->send_path_length, errstr(errno)); + return -errno; + } + + return pcb_send(obj_deref(pcb_obj), payload->send_target, subject, payload->send_timeout); +} + +int +s_syscall_pcb_recv(jove_owned_obj_t *pcb_obj, + lockstack_t *lockstack, + jove_owned_obj_t *objdir_obj, + size_t bytes_left, + void *payload_data) +{ + size_t bytes_needed = sizeof(struct syscall_pcb_recv); + CHECK_TOOSMALL(bytes_needed, bytes_left, "Message too small for payload"); + + struct syscall_pcb_recv *payload = payload_data; + bytes_needed += payload->recv_path_length * sizeof(objpath_part_t); + CHECK_TOOSMALL(bytes_needed, bytes_left, "Message too small for destination path"); + + jove_perm_obj_t destination = link_try_traverse_objdir( + objdir_obj, lockstack, payload->recv_path_length, payload->recv_path); + if(errno) { + klogf("Unable to get destination %P : %s\n", + payload->recv_path, payload->recv_path_length, errstr(errno)); + return -errno; + } + CHECK_OBJPERM(destination, SYSCALL_PERM_PCB_RECV_DST, "Destination object does not permit recv."); + + return pcb_recv(obj_deref(pcb_obj), payload->recv_target, destination.obj, payload->recv_timeout); +} + int _syscall_handle_pcb(syscall_context_t *ctx) { @@ -27,6 +89,18 @@ _syscall_handle_pcb(syscall_context_t *ctx) return s_syscall_pcb_yield(ctx->target.obj, ctx->bytes_left, ctx->payload); return -EPERM; + case SYSCALL_PCB_SEND: + if(ctx->target.permissions & SYSCALL_PERM_PCB_SEND) + return s_syscall_pcb_send( + ctx->target.obj, &ctx->lockstack, ctx->objdir, ctx->bytes_left, ctx->payload); + return -EPERM; + + case SYSCALL_PCB_RECV: + if(ctx->target.permissions & SYSCALL_PERM_PCB_RECV) + return s_syscall_pcb_recv( + ctx->target.obj, &ctx->lockstack, ctx->objdir, ctx->bytes_left, ctx->payload); + return -EPERM; + default: return -EBADCALL; } -- cgit v1.2.3