From: Tulio A M Mendes Date: Sat, 7 Feb 2026 20:46:02 +0000 (-0300) Subject: proc: add waitpid + exit status; add temporary spawn() test helper X-Git-Url: https://projects.tadryanom.me/?a=commitdiff_plain;h=e08591fd5e755e64a6e8488aba5426802c036679;p=AdrOS.git proc: add waitpid + exit status; add temporary spawn() test helper --- diff --git a/include/process.h b/include/process.h index eb02a7c..b9e8a83 100644 --- a/include/process.h +++ b/include/process.h @@ -18,11 +18,18 @@ struct file; struct process { uint32_t pid; + uint32_t parent_pid; uintptr_t sp; uintptr_t addr_space; uint32_t* kernel_stack; process_state_t state; uint32_t wake_at_tick; // New: When to wake up (global tick count) + int exit_status; + + int waiting; + int wait_pid; + int wait_result_pid; + int wait_result_status; struct file* files[PROCESS_MAX_FILES]; struct process* next; struct process* prev; // Doubly linked list helps here too! (Optional but good) @@ -51,4 +58,13 @@ extern void context_switch(uintptr_t* old_sp_ptr, uintptr_t new_sp); // Yield the CPU to the next process voluntarily void schedule(void); +// Wait for a child to exit. Returns child's pid on success, -1 on error. +int process_waitpid(int pid, int* status_out); + +// Mark current process as exiting and notify/wake a waiter (if any). +void process_exit_notify(int status); + +// Temporary: spawn a kernel-thread child that sleeps briefly and exits. +int process_spawn_test_child(void); + #endif diff --git a/include/syscall.h b/include/syscall.h index 280c1a2..d05ae3e 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -13,6 +13,11 @@ enum { SYSCALL_OPEN = 4, SYSCALL_READ = 5, SYSCALL_CLOSE = 6, + + SYSCALL_WAITPID = 7, + + // Temporary: spawn a kernel-thread child for waitpid testing. + SYSCALL_SPAWN = 8, }; #endif diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c index a153098..c406ece 100644 --- a/src/kernel/scheduler.c +++ b/src/kernel/scheduler.c @@ -1,9 +1,11 @@ #include "process.h" #include "pmm.h" #include "vmm.h" +#include "heap.h" #include "uart_console.h" #include "timer.h" // Need access to current tick usually, but we pass it in wake_check #include "spinlock.h" +#include "utils.h" #include "hal/cpu.h" #include @@ -14,20 +16,105 @@ static uint32_t next_pid = 1; static spinlock_t sched_lock = {0}; -static void* pmm_alloc_page_low(void) { - // Bring-up safety: ensure we allocate from the identity-mapped area (0-4MB) - // until we have a full kernel virtual mapping for arbitrary phys pages. - for (int tries = 0; tries < 1024; tries++) { - void* p = pmm_alloc_page(); - if (!p) return 0; - // boot.S currently identity-maps a large low window; keep allocations within it. - if ((uint32_t)p < 0x01000000) { - return p; +static struct process* process_find_locked(uint32_t pid) { + if (!ready_queue_head) return NULL; + + struct process* it = ready_queue_head; + struct process* start = it; + do { + if (it->pid == pid) return it; + it = it->next; + } while (it && it != start); + + return NULL; +} + +int process_waitpid(int pid, int* status_out) { + if (!current_process) return -1; + + while (1) { + uintptr_t flags = spin_lock_irqsave(&sched_lock); + + struct process* it = ready_queue_head; + struct process* start = it; + int found_child = 0; + + if (it) { + do { + if (it->parent_pid == current_process->pid) { + found_child = 1; + if (pid == -1 || (int)it->pid == pid) { + if (it->state == PROCESS_ZOMBIE) { + if (status_out) *status_out = it->exit_status; + int retpid = (int)it->pid; + spin_unlock_irqrestore(&sched_lock, flags); + return retpid; + } + } + } + it = it->next; + } while (it != start); + } + + if (!found_child) { + spin_unlock_irqrestore(&sched_lock, flags); + return -1; + } + + current_process->waiting = 1; + current_process->wait_pid = pid; + current_process->wait_result_pid = -1; + current_process->state = PROCESS_BLOCKED; + + spin_unlock_irqrestore(&sched_lock, flags); + + hal_cpu_enable_interrupts(); + schedule(); + + if (current_process->wait_result_pid != -1) { + if (status_out) *status_out = current_process->wait_result_status; + int rp = current_process->wait_result_pid; + current_process->waiting = 0; + current_process->wait_pid = -1; + current_process->wait_result_pid = -1; + return rp; } - // Not safe to touch yet; put it back. - pmm_free_page(p); } - return 0; +} + +void process_exit_notify(int status) { + if (!current_process) return; + + uintptr_t flags = spin_lock_irqsave(&sched_lock); + + current_process->exit_status = status; + current_process->state = PROCESS_ZOMBIE; + + if (current_process->pid != 0) { + struct process* parent = process_find_locked(current_process->parent_pid); + if (parent && parent->state == PROCESS_BLOCKED && parent->waiting) { + if (parent->wait_pid == -1 || parent->wait_pid == (int)current_process->pid) { + parent->wait_result_pid = (int)current_process->pid; + parent->wait_result_status = status; + parent->state = PROCESS_READY; + } + } + } + + spin_unlock_irqrestore(&sched_lock, flags); +} +static void spawn_test_child_entry(void) { + process_exit_notify(42); + schedule(); + for(;;) { + hal_cpu_idle(); + } +} + +int process_spawn_test_child(void) { + struct process* p = process_create_kernel(spawn_test_child_entry); + if (!p) return -1; + return (int)p->pid; } void process_init(void) { @@ -36,18 +123,26 @@ void process_init(void) { uintptr_t flags = spin_lock_irqsave(&sched_lock); // Initial Kernel Thread (PID 0) - IDLE TASK - struct process* kernel_proc = (struct process*)pmm_alloc_page_low(); + struct process* kernel_proc = (struct process*)kmalloc(sizeof(*kernel_proc)); if (!kernel_proc) { spin_unlock_irqrestore(&sched_lock, flags); uart_print("[SCHED] OOM allocating kernel process struct.\n"); for(;;) hal_cpu_idle(); __builtin_unreachable(); } + + memset(kernel_proc, 0, sizeof(*kernel_proc)); kernel_proc->pid = 0; + kernel_proc->parent_pid = 0; kernel_proc->state = PROCESS_RUNNING; kernel_proc->wake_at_tick = 0; kernel_proc->addr_space = hal_cpu_get_address_space(); + kernel_proc->exit_status = 0; + kernel_proc->waiting = 0; + kernel_proc->wait_pid = -1; + kernel_proc->wait_result_pid = -1; + kernel_proc->wait_result_status = 0; for (int i = 0; i < PROCESS_MAX_FILES; i++) { kernel_proc->files[i] = NULL; @@ -57,6 +152,7 @@ void process_init(void) { ready_queue_head = kernel_proc; ready_queue_tail = kernel_proc; kernel_proc->next = kernel_proc; + kernel_proc->prev = kernel_proc; // Best effort: set esp0 to current stack until we have a dedicated kernel stack for PID 0 uintptr_t cur_esp = hal_cpu_get_stack_pointer(); @@ -68,48 +164,55 @@ void process_init(void) { void thread_wrapper(void (*fn)(void)) { hal_cpu_enable_interrupts(); fn(); - uart_print("[SCHED] Thread exited.\n"); for(;;) hal_cpu_idle(); } struct process* process_create_kernel(void (*entry_point)(void)) { uintptr_t flags = spin_lock_irqsave(&sched_lock); - struct process* proc = (struct process*)pmm_alloc_page_low(); + struct process* proc = (struct process*)kmalloc(sizeof(*proc)); if (!proc) { spin_unlock_irqrestore(&sched_lock, flags); return NULL; } + memset(proc, 0, sizeof(*proc)); + proc->pid = next_pid++; + proc->parent_pid = current_process ? current_process->pid : 0; proc->state = PROCESS_READY; proc->addr_space = current_process->addr_space; proc->wake_at_tick = 0; + proc->exit_status = 0; + proc->waiting = 0; + proc->wait_pid = -1; + proc->wait_result_pid = -1; + proc->wait_result_status = 0; for (int i = 0; i < PROCESS_MAX_FILES; i++) { proc->files[i] = NULL; } - void* stack_phys = pmm_alloc_page_low(); - if (!stack_phys) { + void* stack = kmalloc(4096); + if (!stack) { spin_unlock_irqrestore(&sched_lock, flags); return NULL; } - // Until we guarantee a linear phys->virt mapping, use the identity-mapped address - // for kernel thread stacks during bring-up. - uint32_t stack_addr = (uint32_t)stack_phys; - proc->kernel_stack = (uint32_t*)stack_addr; + proc->kernel_stack = (uint32_t*)stack; - uint32_t* sp = (uint32_t*)((uint8_t*)stack_addr + 4096); + uint32_t* sp = (uint32_t*)((uint8_t*)stack + 4096); *--sp = (uint32_t)entry_point; + *--sp = 0; *--sp = (uint32_t)thread_wrapper; *--sp = 0; *--sp = 0; *--sp = 0; *--sp = 0; proc->sp = (uintptr_t)sp; proc->next = ready_queue_head; + proc->prev = ready_queue_tail; ready_queue_tail->next = proc; + ready_queue_head->prev = proc; ready_queue_tail = proc; spin_unlock_irqrestore(&sched_lock, flags); @@ -146,12 +249,10 @@ struct process* get_next_ready_process(void) { } void schedule(void) { - uintptr_t irq_flags = irq_save(); - spin_lock(&sched_lock); + uintptr_t irq_flags = spin_lock_irqsave(&sched_lock); if (!current_process) { - spin_unlock(&sched_lock); - irq_restore(irq_flags); + spin_unlock_irqrestore(&sched_lock, irq_flags); return; } @@ -159,8 +260,7 @@ void schedule(void) { struct process* next = get_next_ready_process(); if (prev == next) { - spin_unlock(&sched_lock); - irq_restore(irq_flags); + spin_unlock_irqrestore(&sched_lock, irq_flags); return; } @@ -178,11 +278,14 @@ void schedule(void) { hal_cpu_set_kernel_stack((uintptr_t)current_process->kernel_stack + 4096); } - spin_unlock(&sched_lock); - + spin_unlock_irqrestore(&sched_lock, irq_flags); + context_switch(&prev->sp, current_process->sp); - irq_restore(irq_flags); + // Do not restore the old IF state after switching stacks. + // The previous context may have entered schedule() with IF=0 (e.g. syscall/ISR), + // and propagating that would prevent timer/keyboard IRQs from firing. + hal_cpu_enable_interrupts(); } void process_sleep(uint32_t ticks) { diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index f283505..2e34c8b 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -7,6 +7,8 @@ #include "uart_console.h" #include "uaccess.h" +#include "hal/cpu.h" + #include struct file { @@ -131,7 +133,7 @@ static void syscall_handler(struct registers* regs) { } if (syscall_no == SYSCALL_GETPID) { - regs->eax = 0; + regs->eax = current_process ? current_process->pid : 0; return; } @@ -157,12 +159,58 @@ static void syscall_handler(struct registers* regs) { } if (syscall_no == SYSCALL_EXIT) { - uart_print("[USER] exit()\n"); + int status = (int)regs->ebx; + + for (int fd = 3; fd < PROCESS_MAX_FILES; fd++) { + if (current_process && current_process->files[fd]) { + (void)fd_close(fd); + } + } + + process_exit_notify(status); + + hal_cpu_enable_interrupts(); + schedule(); for(;;) { __asm__ volatile("cli; hlt"); } } + if (syscall_no == SYSCALL_WAITPID) { + int pid = (int)regs->ebx; + int* user_status = (int*)regs->ecx; + uint32_t options = regs->edx; + (void)options; + + if (user_status && user_range_ok(user_status, sizeof(int)) == 0) { + regs->eax = (uint32_t)-1; + return; + } + + int status = 0; + int retpid = process_waitpid(pid, &status); + if (retpid < 0) { + regs->eax = (uint32_t)-1; + return; + } + + if (user_status) { + if (copy_to_user(user_status, &status, sizeof(status)) < 0) { + regs->eax = (uint32_t)-1; + return; + } + } + + regs->eax = (uint32_t)retpid; + return; + } + + if (syscall_no == SYSCALL_SPAWN) { + int pid = process_spawn_test_child(); + regs->eax = (pid < 0) ? (uint32_t)-1 : (uint32_t)pid; + return; + } + regs->eax = (uint32_t)-1; } diff --git a/src/kernel/tty.c b/src/kernel/tty.c index 24b1b2d..95055a1 100644 --- a/src/kernel/tty.c +++ b/src/kernel/tty.c @@ -6,6 +6,8 @@ #include "uart_console.h" #include "uaccess.h" +#include "hal/cpu.h" + #define TTY_LINE_MAX 256 #define TTY_CANON_BUF 1024 #define TTY_WAITQ_MAX 16 @@ -186,6 +188,8 @@ int tty_read(void* user_buf, uint32_t len) { } spin_unlock_irqrestore(&tty_lock, flags); + + hal_cpu_enable_interrupts(); schedule(); } } diff --git a/user/init.c b/user/init.c index 34dd58b..52ef1fb 100644 --- a/user/init.c +++ b/user/init.c @@ -6,6 +6,8 @@ enum { SYSCALL_OPEN = 4, SYSCALL_READ = 5, SYSCALL_CLOSE = 6, + SYSCALL_WAITPID = 7, + SYSCALL_SPAWN = 8, }; static int sys_write(int fd, const void* buf, uint32_t len) { @@ -19,6 +21,28 @@ static int sys_write(int fd, const void* buf, uint32_t len) { return ret; } +static int sys_waitpid(int pid, int* status, uint32_t options) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_WAITPID), "b"(pid), "c"(status), "d"(options) + : "memory" + ); + return ret; +} + +static int sys_spawn(void) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_SPAWN) + : "memory" + ); + return ret; +} + static int sys_open(const char* path, uint32_t flags) { int ret; __asm__ volatile( @@ -53,12 +77,11 @@ static int sys_close(int fd) { } __attribute__((noreturn)) static void sys_exit(int code) { - (void)code; __asm__ volatile( "int $0x80\n" "1: jmp 1b\n" : - : "a"(SYSCALL_EXIT) + : "a"(SYSCALL_EXIT), "b"(code) : "memory" ); __builtin_unreachable(); @@ -95,5 +118,22 @@ void _start(void) { static const char bad[] = "[init] read failed or bad header\n"; (void)sys_write(1, bad, (uint32_t)(sizeof(bad) - 1)); } + + int child = sys_spawn(); + if (child < 0) { + static const char smsg[] = "[init] spawn failed\n"; + (void)sys_write(1, smsg, (uint32_t)(sizeof(smsg) - 1)); + sys_exit(2); + } + + int st = 0; + int wp = sys_waitpid(child, &st, 0); + if (wp == child && st == 42) { + static const char wmsg[] = "[init] waitpid OK\n"; + (void)sys_write(1, wmsg, (uint32_t)(sizeof(wmsg) - 1)); + } else { + static const char wbad[] = "[init] waitpid failed\n"; + (void)sys_write(1, wbad, (uint32_t)(sizeof(wbad) - 1)); + } sys_exit(0); }