]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
proc: add waitpid + exit status; add temporary spawn() test helper
authorTulio A M Mendes <[email protected]>
Sat, 7 Feb 2026 20:46:02 +0000 (17:46 -0300)
committerTulio A M Mendes <[email protected]>
Sat, 7 Feb 2026 20:46:02 +0000 (17:46 -0300)
include/process.h
include/syscall.h
src/kernel/scheduler.c
src/kernel/syscall.c
src/kernel/tty.c
user/init.c

index 7c496cf6dc4e42b4aed82b97561c8a9f8ee85c4a..09d6b2eb76625204508a839d81cb542cc6b474c3 100644 (file)
@@ -27,11 +27,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)
@@ -60,4 +67,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
index 40847cb610d2bc965c9a850da9060f7d9493035c..ecbf1664de87ef60ce284b868c001d08fdd36612 100644 (file)
@@ -22,6 +22,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
index 10faaedb3c9c52ba314609cfe9b1982a26ea5a69..930077939f1c593189ff98e0a2ea3d7cf08adfc5 100644 (file)
 #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 <stddef.h>
 
@@ -23,20 +25,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;
+        }
+    }
+}
+
+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;
+            }
         }
-        // Not safe to touch yet; put it back.
-        pmm_free_page(p);
     }
-    return 0;
+
+    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) {
@@ -45,18 +132,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;
@@ -66,6 +161,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();
@@ -77,48 +173,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);
@@ -155,12 +258,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;
     }
 
@@ -168,8 +269,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; 
     }
 
@@ -187,11 +287,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) {
index 9dac3cff6e1753bfc14eb33d4a3998866a748406..4d7cfe5d8d3cc49fafaef6ad1423a421b22db1b1 100644 (file)
@@ -16,6 +16,8 @@
 #include "uart_console.h"
 #include "uaccess.h"
 
+#include "hal/cpu.h"
+
 #include <stddef.h>
 
 struct file {
@@ -140,7 +142,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;
     }
 
@@ -166,12 +168,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;
 }
 
index fcae4d200658b0b1f7d001e3363dab66352a851c..f35a74d675b4f46515362008d09a6855bde2b725 100644 (file)
@@ -15,6 +15,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
@@ -195,6 +197,8 @@ int tty_read(void* user_buf, uint32_t len) {
         }
 
         spin_unlock_irqrestore(&tty_lock, flags);
+
+        hal_cpu_enable_interrupts();
         schedule();
     }
 }
index 9a1558914f0ca9433a7a6b5db8fbb9a7efc4bba8..bfff0adcf09e30ad0d7130e71f23f5820f6e0d2c 100644 (file)
@@ -15,6 +15,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) {
@@ -28,6 +30,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(
@@ -62,12 +86,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();
@@ -104,5 +127,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);
 }