summaryrefslogtreecommitdiff
path: root/lume-legacy/src/kernel/proc
diff options
context:
space:
mode:
Diffstat (limited to 'lume-legacy/src/kernel/proc')
-rw-r--r--lume-legacy/src/kernel/proc/exec.cc260
-rw-r--r--lume-legacy/src/kernel/proc/proc.cc654
-rw-r--r--lume-legacy/src/kernel/proc/swtch.S37
3 files changed, 951 insertions, 0 deletions
diff --git a/lume-legacy/src/kernel/proc/exec.cc b/lume-legacy/src/kernel/proc/exec.cc
new file mode 100644
index 0000000..8d8c467
--- /dev/null
+++ b/lume-legacy/src/kernel/proc/exec.cc
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 Richard QIn
+ */
+#include "common/types.h"
+#include "kernel/riscv.h"
+#include "kernel/proc.h"
+#include "kernel/mm.h"
+#include "kernel/elf.h"
+#include "fs/fs.h"
+#include "lib/string.h"
+#include "drivers/uart.h"
+#include "kernel/trap.h"
+
+extern char trampoline[];
+
+// RAII: Page Table Resource Management
+class VmGuard
+{
+public:
+ uint64 *pagetable = nullptr;
+ uint64 sz = 0;
+
+ VmGuard() : pagetable(VM::uvmcreate()) {}
+
+ ~VmGuard()
+ {
+ if (pagetable)
+ {
+ VM::uvmfree(pagetable, sz);
+ }
+ }
+
+ // Copying is prohibited
+ VmGuard(const VmGuard &) = delete;
+ VmGuard &operator=(const VmGuard &) = delete;
+
+ // Submit resources: Ownership transfer, destructor no longer releases
+ uint64 *commit()
+ {
+ uint64 *pt = pagetable;
+ pagetable = nullptr;
+ return pt;
+ }
+};
+
+// RAII: Inode Lock Management
+class InodeGuard
+{
+public:
+ Inode *ip;
+ InodeGuard(Inode *i) : ip(i)
+ {
+ if (ip)
+ VFS::ilock(ip);
+ }
+ ~InodeGuard()
+ {
+ if (ip)
+ {
+ VFS::iunlockput(ip); // Automatically unlock and release references
+ }
+ }
+ // Copying is prohibited
+ InodeGuard(const InodeGuard &) = delete;
+ InodeGuard &operator=(const InodeGuard &) = delete;
+};
+
+// laod segment
+static int loadseg(uint64 *pagetable, uint64 va, Inode *ip, uint64 offset, uint64 sz)
+{
+ for (uint64 i = 0; i < sz; i += PGSIZE)
+ {
+ uint64 pa = VM::walkaddr(pagetable, va + i);
+ if (pa == 0)
+ {
+ Drivers::uart_puts("[Exec Debug] loadseg: walkaddr failed\n");
+ return -1;
+ }
+
+ uint64 n = PGSIZE - (va + i) % PGSIZE;
+ if (i + n > sz)
+ n = sz - i;
+
+ // Read physical address (isUser=0)
+ if (ip->read((char *)(pa + (va + i) % PGSIZE), offset + i, n, 0) != (int)n)
+ {
+ Drivers::uart_puts("[Exec Debug] loadseg: read failed\n");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+namespace Exec
+{
+ int exec(char *path, char **argv)
+ {
+ // 1. Get Inode
+ Inode *raw_ip = VFS::namei(path);
+ if (!raw_ip)
+ {
+ Drivers::uart_puts("[Exec Debug] namei failed for path: ");
+ Drivers::uart_puts(path);
+ Drivers::uart_puts("\n");
+ return -1;
+ }
+ InodeGuard iguard(raw_ip);
+
+ // Get ELF head
+ struct elfhdr elf;
+ if (raw_ip->read((char *)&elf, 0, sizeof(elf), 0) != sizeof(elf))
+ {
+ Drivers::uart_puts("[Exec Debug] read elf header failed\n");
+ return -1;
+ }
+ if (elf.magic != ELF_MAGIC)
+ {
+ Drivers::uart_puts("[Exec Debug] invalid elf magic\n");
+ return -1;
+ }
+
+ // Create new pagetable
+ VmGuard vm_guard;
+ if (!vm_guard.pagetable)
+ {
+ Drivers::uart_puts("[Exec Debug] uvmcreate failed\n");
+ return -1;
+ }
+
+ // Load Program Segment
+ struct proghdr ph;
+ for (int i = 0; i < elf.phnum; i++)
+ {
+ uint64 off = elf.phoff + i * sizeof(ph);
+ if (raw_ip->read((char *)&ph, off, sizeof(ph), 0) != sizeof(ph))
+ {
+ Drivers::uart_puts("[Exec Debug] read proghdr failed\n");
+ return -1;
+ }
+
+ if (ph.type != ELF_PROG_LOAD)
+ continue;
+ if (ph.memsz < ph.filesz)
+ return -1;
+ if (ph.vaddr + ph.memsz < ph.vaddr)
+ return -1;
+
+ uint64 sz1 = VM::uvmalloc(vm_guard.pagetable, vm_guard.sz, ph.vaddr + ph.memsz, PTE_W | PTE_X | PTE_R | PTE_U);
+ if (sz1 == 0)
+ {
+ Drivers::uart_puts("[Exec Debug] uvmalloc failed (segment)\n");
+ return -1;
+ }
+ vm_guard.sz = sz1;
+
+ if (loadseg(vm_guard.pagetable, ph.vaddr, raw_ip, ph.off, ph.filesz) < 0)
+ return -1;
+ }
+
+ // Prepare user stack
+ uint64 sz = PGROUNDUP(vm_guard.sz);
+
+ const int stack_data_pages = 16;
+ const int stack_total_pages = stack_data_pages + 1;
+
+ uint64 sz1 = VM::uvmalloc(vm_guard.pagetable, sz, sz + stack_total_pages * PGSIZE, PTE_W | PTE_R | PTE_U);
+ if (sz1 == 0)
+ {
+ Drivers::uart_puts("[Exec Debug] uvmalloc failed (stack)\n");
+ return -1;
+ }
+ vm_guard.sz = sz1;
+
+ VM::uvmunmap(vm_guard.pagetable, sz, 1, 0);
+
+ uint64 sp = sz1;
+ uint64 stackbase = sp - stack_data_pages * PGSIZE;
+ uint64 ustack[32];
+ uint64 argc = 0;
+
+ // Push parameter string
+ for (; argv[argc]; argc++)
+ {
+ if (argc >= 31)
+ return -1;
+ sp -= strlen(argv[argc]) + 1;
+ sp -= sp % 16;
+ if (sp < stackbase)
+ {
+ Drivers::uart_puts("[Exec Debug] stack overflow (args)\n");
+ return -1;
+ }
+
+ if (VM::copyout(vm_guard.pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
+ {
+ Drivers::uart_puts("[Exec Debug] copyout arg string failed\n");
+ return -1;
+ }
+ ustack[argc] = sp;
+ }
+ ustack[argc] = 0;
+
+ // Push into parameter array
+ sp -= (argc + 1) * sizeof(uint64);
+ sp -= sp % 16;
+ if (sp < stackbase)
+ {
+ Drivers::uart_puts("[Exec Debug] stack overflow (argv array)\n");
+ return -1;
+ }
+ if (VM::copyout(vm_guard.pagetable, sp, (char *)ustack, (argc + 1) * sizeof(uint64)) < 0)
+ {
+ Drivers::uart_puts("[Exec Debug] copyout argv array failed\n");
+ return -1;
+ }
+
+ // Commit Changes
+ struct Proc *p = myproc();
+ if (VM::mappages(vm_guard.pagetable, TRAPFRAME, PGSIZE, (uint64)p->tf, PTE_R | PTE_W) < 0)
+ {
+ Drivers::uart_puts("[Exec Debug] mappages TRAPFRAME failed\n");
+ return -1;
+ }
+
+ uint64 oldsz = p->sz;
+ uint64 *oldpagetable = p->pagetable;
+
+ p->tf->a0 = argc;
+ p->tf->a1 = sp;
+ p->tf->sp = sp;
+ p->tf->epc = elf.entry;
+
+ char *s = path, *last = path;
+ while (*s)
+ {
+ if (*s == '/')
+ last = s + 1;
+ s++;
+ }
+
+ size_t len = 0;
+ while (last[len] && len < sizeof(p->name) - 1)
+ {
+ p->name[len] = last[len];
+ len++;
+ }
+ p->name[len] = '\0';
+
+ // change pagetable
+ p->pagetable = vm_guard.commit();
+ p->sz = vm_guard.sz;
+
+ // release old pagetable
+ VM::uvmfree(oldpagetable, oldsz);
+ asm volatile("fence.i");
+
+ return 0; // iguard unlocks and releases IP upon destruction
+ }
+} \ No newline at end of file
diff --git a/lume-legacy/src/kernel/proc/proc.cc b/lume-legacy/src/kernel/proc/proc.cc
new file mode 100644
index 0000000..3923fe8
--- /dev/null
+++ b/lume-legacy/src/kernel/proc/proc.cc
@@ -0,0 +1,654 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 Richard QIn
+ */
+#include "kernel/proc.h"
+#include "kernel/proc_def.h"
+#include "kernel/runqueue.h"
+#include "kernel/cpu.h"
+#include "kernel/spinlock.h"
+#include "kernel/pmm.h"
+#include "kernel/mm.h"
+#include "kernel/trap.h"
+#include "kernel/riscv.h"
+#include "kernel/slab.h"
+#include "drivers/uart.h"
+#include "fs/fs.h"
+#include "fs/file.h"
+#include "lib/string.h"
+
+// #define NPROC 64
+#define NCPU 8
+
+extern "C" void swtch(struct Context *old_ctx, struct Context *new_ctx);
+extern char uservec[], trampoline[];
+extern "C" void usertrap();
+extern "C" void userret(uint64, uint64);
+extern char initcode_start[], initcode_end[];
+
+// struct Proc procs[NPROC];
+struct Proc *proc_list_head = nullptr;
+struct Proc *initproc = nullptr;
+struct Spinlock proc_mem_lock;
+struct cpu cpus[NCPU];
+struct RunQueue runqueues[NCPU];
+
+
+static int nextpid = 1;
+
+
+struct cpu *mycpu()
+{
+ int id = r_tp();
+ return &cpus[id];
+}
+
+
+void RunQueue::init(const char *name)
+{
+ lock.init(name);
+ count = 0;
+ for (int i = 0; i < N_PRIO; i++)
+ {
+ heads[i] = nullptr;
+ tails[i] = nullptr;
+ }
+}
+
+void RunQueue::enqueue(struct Proc *p)
+{
+ if (p->priority < 0 || p->priority >= N_PRIO)
+ p->priority = PRIO_MIN;
+ p->next = nullptr;
+ if (heads[p->priority] == nullptr)
+ {
+ heads[p->priority] = p;
+ tails[p->priority] = p;
+ }
+ else
+ {
+ tails[p->priority]->next = p;
+ tails[p->priority] = p;
+ }
+ count++;
+}
+
+struct Proc *RunQueue::dequeue()
+{
+ for (int i = 0; i < N_PRIO; i++)
+ {
+ if (heads[i])
+ {
+ struct Proc *p = heads[i];
+ heads[i] = p->next;
+ if (!heads[i])
+ tails[i] = nullptr;
+ p->next = nullptr;
+ count--;
+ return p;
+ }
+ }
+ return nullptr;
+}
+
+struct Proc *RunQueue::steal()
+{
+ for (int i = N_PRIO - 1; i >= 0; i--)
+ {
+ if (heads[i])
+ {
+ struct Proc *p = heads[i];
+ heads[i] = p->next;
+ if (!heads[i])
+ tails[i] = nullptr;
+ p->next = nullptr;
+ count--;
+ return p;
+ }
+ }
+ return nullptr;
+}
+
+// Trap Return Functions
+
+void usertrapret()
+{
+ struct Proc *p = myproc();
+
+ intr_off();
+
+ // Set stvec to point to uservec in the trampoline
+ // When an interrupt occurs in user mode, the CPU will jump here
+ uint64 trampoline_uservec = TRAMPOLINE + (uservec - trampoline);
+ w_stvec(trampoline_uservec);
+
+ // Fill in the Trapframe, used when uservec saves the kernel state
+ p->tf->kernel_satp = r_satp();
+ p->tf->kernel_sp = (uint64)p->kstack + PGSIZE;
+ p->tf->kernel_trap = (uint64)usertrap;
+ p->tf->kernel_hartid = r_tp();
+
+ // Set SSTATUS
+ // SPP = 0 (return to User Mode)
+ // SPIE = 1 (enable interrupts)
+ unsigned long x = r_sstatus();
+ x &= ~SSTATUS_SPP;
+ x |= SSTATUS_SPIE;
+ w_sstatus(x);
+
+ // Set SEPC to the user program counter
+ w_sepc(p->tf->epc);
+
+ // Prepare user page table SATP
+ uint64 satp = MAKE_SATP(p->pagetable);
+
+ // Calculate the virtual address of userret in the trampoline
+ uint64 fn = TRAMPOLINE + ((uint64)userret - (uint64)trampoline);
+
+ // Jump to userret, with parameters being the TRAPFRAME address and the user page table SATP
+ ((void (*)(uint64, uint64))fn)(TRAPFRAME, satp);
+}
+
+
+void forkret()
+{
+
+ struct cpu *c = mycpu();
+ c->runqueue->lock.release();
+
+ // Debug
+ // Drivers::uart_puts("[Proc] forkret: jumping to user.\n");
+
+ // Enter user mode
+ usertrapret();
+}
+
+// ProcManager Implementation
+
+static Proc *allocproc()
+{
+ struct Proc *p;
+ proc_mem_lock.acquire();
+
+ p = (struct Proc *)Slab::kmalloc(sizeof(struct Proc));
+
+ if (!p)
+ {
+ proc_mem_lock.release();
+ return 0;
+ }
+
+ memset(p, 0, sizeof(struct Proc));
+
+ p->all_next = proc_list_head;
+ p->all_prev = nullptr;
+ if (proc_list_head)
+ proc_list_head->all_prev = p;
+ proc_list_head = p;
+
+
+ p->state = USED;
+ p->pid = nextpid++;
+ p->priority = 0;
+ p->killed = 0;
+ p->cpu = mycpu();
+ p->cwd = nullptr;
+ for (int i = 0; i < NOFILE; i++)
+ p->ofile[i] = nullptr;
+
+ if ((p->tf = (struct Trapframe *)PMM::alloc_page()) == 0)
+ {
+ p->state = UNUSED;
+ if (p->all_next)
+ p->all_next->all_prev = p->all_prev;
+ if (p->all_prev)
+ p->all_prev->all_next = p->all_next;
+ if (p == proc_list_head)
+ proc_list_head = p->all_next;
+ Slab::kfree(p);
+ proc_mem_lock.release();
+ return 0;
+ }
+
+ if ((p->kstack = PMM::alloc_page()) == 0)
+ {
+ PMM::free_page(p->tf);
+ p->state = UNUSED;
+ if (p->all_next)
+ p->all_next->all_prev = p->all_prev;
+ if (p->all_prev)
+ p->all_prev->all_next = p->all_next;
+ if (p == proc_list_head)
+ proc_list_head = p->all_next;
+ Slab::kfree(p);
+
+ proc_mem_lock.release();
+ return 0;
+ }
+ memset(p->kstack, 0, PGSIZE);
+
+ memset(&p->context, 0, sizeof(p->context));
+
+ // Set the context so that the next time this process is scheduled, it starts executing directly from forkret
+ p->context.ra = (uint64)forkret;
+ p->context.sp = (uint64)p->kstack + PGSIZE;
+ return p;
+}
+
+static void freeproc(struct Proc *p)
+{
+ if (p->tf)
+ PMM::free_page(p->tf);
+ p->tf = 0;
+ if (p->kstack)
+ PMM::free_page(p->kstack);
+ p->kstack = 0;
+ if (p->pagetable)
+ VM::uvmfree(p->pagetable, p->sz);
+ for (int i = 0; i < NOFILE; i++)
+ {
+ if (p->ofile[i])
+ {
+ FileTable::close(p->ofile[i]);
+ p->ofile[i] = nullptr;
+ }
+ }
+ if (p->cwd)
+ {
+ VFS::iput(p->cwd);
+ p->cwd = nullptr;
+ }
+
+ if (p->all_prev)
+ p->all_prev->all_next = p->all_next;
+ else
+ proc_list_head = p->all_next;
+
+ if (p->all_next)
+ p->all_next->all_prev = p->all_prev;
+
+ Slab::kfree(p);
+}
+
+int fork()
+{
+ struct Proc *np;
+ struct Proc *p = myproc();
+
+ if ((np = allocproc()) == 0)
+ return -1;
+
+ np->pagetable = VM::uvmcreate();
+ np->sz = p->sz;
+ if (VM::uvmcopy(p->pagetable, np->pagetable, p->sz) < 0)
+ {
+ freeproc(np);
+ proc_mem_lock.release();
+ return -1;
+ }
+
+
+ if (VM::mappages(np->pagetable, TRAPFRAME, PGSIZE, (uint64)np->tf, PTE_R | PTE_W) < 0)
+ {
+ freeproc(np);
+ proc_mem_lock.release();
+ return -1;
+ }
+
+ *(np->tf) = *(p->tf);
+ np->tf->a0 = 0;
+ strcpy(np->name, p->name);
+ np->parent = p;
+ np->priority = p->priority;
+
+ for (int i = 0; i < NOFILE; i++)
+ {
+ if (p->ofile[i])
+ {
+ np->ofile[i] = FileTable::dup(p->ofile[i]);
+ }
+ }
+
+ if (p->cwd)
+ {
+ np->cwd = VFS::idup(p->cwd);
+ }
+
+ proc_mem_lock.release();
+
+ struct cpu *c = mycpu();
+ c->runqueue->lock.acquire();
+ np->state = RUNNABLE;
+ c->runqueue->enqueue(np);
+ c->runqueue->lock.release();
+
+ return np->pid;
+}
+
+namespace ProcManager
+{
+
+ void init()
+ {
+ proc_mem_lock.init("proc_mem");
+
+ proc_list_head = nullptr;
+
+ for (int i = 0; i < NCPU; i++)
+ {
+ cpus[i].runqueue = &runqueues[i];
+ cpus[i].runqueue->init("runqueue");
+ }
+ }
+
+ void scheduler()
+ {
+ struct Proc *p;
+ struct cpu *c = mycpu();
+ c->proc = 0;
+ Drivers::uart_puts("[Proc] Scheduler started (Per-CPU).\n");
+
+ for (;;)
+ {
+ intr_on();
+
+ // Get from the local queue
+ c->runqueue->lock.acquire();
+ p = c->runqueue->dequeue();
+ c->runqueue->lock.release();
+
+ // Work Stealing
+ if (!p)
+ {
+ for (int i = 0; i < NCPU; i++)
+ {
+ if ((uint64)i == r_tp())
+ continue;
+
+ struct cpu *victim = &cpus[i];
+ victim->runqueue->lock.acquire();
+ if (victim->runqueue->count > 0)
+ p = victim->runqueue->steal();
+ victim->runqueue->lock.release();
+ if (p)
+ break;
+ }
+ }
+
+ if (p)
+ {
+ p->state = RUNNING;
+ c->proc = p;
+
+ c->runqueue->lock.acquire();
+ swtch(&c->context, &p->context);
+ c->runqueue->lock.release();
+
+ c->proc = 0;
+ }
+ }
+ }
+
+ void yield()
+ {
+ struct Proc *p = myproc();
+ struct cpu *c = mycpu();
+ c->runqueue->lock.acquire();
+ if (p && p->state == RUNNING)
+ {
+ p->state = RUNNABLE;
+ if (p->priority < PRIO_MIN)
+ p->priority++;
+ c->runqueue->enqueue(p);
+ swtch(&p->context, &c->context);
+ }
+ c->runqueue->lock.release();
+ }
+
+ void user_init()
+ {
+ struct Proc *p = allocproc();
+ if (!p)
+ return;
+
+ initproc = p;
+
+ p->pagetable = VM::uvmcreate();
+
+ uint64 initcode_sz = initcode_end - initcode_start;
+ Drivers::uart_puts("[Proc] initcode size: ");
+ Drivers::uart_put_int(initcode_sz);
+ Drivers::uart_puts(" bytes\n");
+
+ uint64 code_pages = PGROUNDUP(initcode_sz) / PGSIZE;
+
+ for (uint64 i = 0; i < code_pages; i++)
+ {
+ void *mem = PMM::alloc_page();
+ if (!mem)
+ {
+ Drivers::uart_puts("user_init: out of memory for code\n");
+ return;
+ }
+ memset(mem, 0, PGSIZE);
+
+ uint64 src_offset = i * PGSIZE;
+ uint64 copy_len = PGSIZE;
+ if (src_offset + copy_len > initcode_sz)
+ {
+ copy_len = initcode_sz - src_offset;
+ }
+ memmove(mem, (void *)(initcode_start + src_offset), copy_len);
+
+ if (VM::mappages(p->pagetable, i * PGSIZE, PGSIZE, (uint64)mem, PTE_R | PTE_W | PTE_X | PTE_U) < 0)
+ {
+ Drivers::uart_puts("user_init: mappages code failed\n");
+ return;
+ }
+ }
+
+ uint64 stack_va = (code_pages + 1) * PGSIZE;
+
+ void *stack = PMM::alloc_page();
+ if (!stack)
+ return;
+ memset(stack, 0, PGSIZE);
+
+ if (VM::mappages(p->pagetable, stack_va, PGSIZE, (uint64)stack, PTE_R | PTE_W | PTE_U) < 0)
+ return;
+
+ if (VM::mappages(p->pagetable, TRAPFRAME, PGSIZE, (uint64)p->tf, PTE_R | PTE_W) < 0)
+ return;
+
+ p->tf->epc = 0;
+ p->tf->sp = stack_va + PGSIZE;
+ p->sz = stack_va + PGSIZE;
+ strcpy(p->name, "initcode");
+
+ p->cwd = nullptr;
+
+ proc_mem_lock.release();
+
+ struct cpu *c = &cpus[0];
+ c->runqueue->lock.acquire();
+ p->state = RUNNABLE;
+ c->runqueue->enqueue(p);
+ c->runqueue->lock.release();
+
+ Drivers::uart_puts("[Proc] user_init success. PID: 1\n");
+ asm volatile("fence.i");
+ }
+
+ void sleep(void *chan, Spinlock *lk)
+ {
+ struct Proc *p = myproc();
+ struct cpu *c = mycpu();
+
+ if (lk && lk != &proc_mem_lock)
+ lk->release();
+
+ c->runqueue->lock.acquire();
+ p->chan = chan;
+ p->state = SLEEPING;
+ swtch(&p->context, &c->context);
+ p->chan = 0;
+ c->runqueue->lock.release();
+
+ if (lk && lk != &proc_mem_lock)
+ lk->acquire();
+ }
+
+ void wakeup(void *chan)
+ {
+ for (struct Proc *p = proc_list_head; p != nullptr; p = p->all_next)
+ {
+ if (p->state == SLEEPING && p->chan == chan)
+ {
+ struct cpu *c = mycpu();
+ c->runqueue->lock.acquire();
+ p->state = RUNNABLE;
+ c->runqueue->enqueue(p);
+ c->runqueue->lock.release();
+ }
+ }
+ }
+
+ void exit(int status)
+ {
+ struct Proc *p = myproc();
+ if (p == initproc)
+ while (1)
+ ;
+
+ wakeup(p->parent);
+
+ proc_mem_lock.acquire();
+
+ for (struct Proc *pp = proc_list_head; pp != nullptr; pp = pp->all_next)
+ {
+ if (pp->parent == p)
+ {
+ pp->parent = initproc;
+ wakeup(initproc);
+ }
+ }
+
+ proc_mem_lock.release();
+
+ p->xstate = status;
+
+ struct cpu *c = mycpu();
+ c->runqueue->lock.acquire();
+ p->state = ZOMBIE;
+ swtch(&p->context, &c->context);
+ while (1)
+ ;
+ }
+
+ int wait(uint64 addr)
+ {
+
+ struct Proc *np;
+ int havekids, pid;
+ struct Proc *p = myproc();
+ for (;;)
+ {
+ havekids = 0;
+ proc_mem_lock.acquire();
+
+ for (np = proc_list_head; np != nullptr; np = np->all_next)
+ {
+ if (np->parent == p)
+ {
+ havekids = 1;
+ if (np->state == ZOMBIE)
+ {
+
+ struct cpu *child_cpu = np->cpu;
+ if (child_cpu)
+ {
+ proc_mem_lock.release();
+ child_cpu->runqueue->lock.acquire();
+ child_cpu->runqueue->lock.release();
+ proc_mem_lock.acquire();
+ }
+
+ pid = np->pid;
+ if (addr != 0 && VM::copyout(p->pagetable, addr, (char *)&np->xstate, sizeof(np->xstate)) < 0)
+ {
+ proc_mem_lock.release();
+ return -1;
+ }
+ freeproc(np);
+ proc_mem_lock.release();
+ return pid;
+ }
+ }
+ }
+ proc_mem_lock.release();
+
+ if (!havekids || p->state == ZOMBIE)
+ return -1;
+ sleep(p, &proc_mem_lock);
+ }
+ }
+
+ int kill(int pid)
+ {
+ Proc* p;
+ proc_mem_lock.acquire();
+ for (p = proc_list_head; p != nullptr; p = p->all_next)
+ {
+ if (p->pid == pid)
+ {
+ p->killed = 1;
+
+ if (p->state == SLEEPING)
+ {
+ p->state = RUNNABLE;
+
+ struct cpu *c = mycpu();
+ c->runqueue->lock.acquire();
+ c->runqueue->enqueue(p);
+ c->runqueue->lock.release();
+ }
+
+ proc_mem_lock.release();
+ return 0;
+ }
+ }
+ proc_mem_lock.release();
+ return -1;
+ }
+
+ int growproc(int n)
+ {
+ uint64 sz;
+ struct Proc *p = myproc();
+
+ sz = p->sz;
+
+ if (n > 0)
+ {
+ // Call VM::uvmalloc to allocate memory
+ // Parameters: page table, old size, new size, permissions (PTE_W)
+ if ((sz = VM::uvmalloc(p->pagetable, sz, sz + n, PTE_W | PTE_R | PTE_U)) == 0)
+ {
+ return -1;
+ }
+ }
+ else if (n < 0)
+ {
+ sz = VM::uvmdealloc(p->pagetable, sz, sz + n);
+ }
+
+ p->sz = sz;
+ return 0;
+ }
+
+ void create_kernel_thread(void (*func)(), const char *name)
+ {
+ (void)func;
+ (void)name;
+ }
+} \ No newline at end of file
diff --git a/lume-legacy/src/kernel/proc/swtch.S b/lume-legacy/src/kernel/proc/swtch.S
new file mode 100644
index 0000000..d55c03e
--- /dev/null
+++ b/lume-legacy/src/kernel/proc/swtch.S
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2026 Richard Qin
+
+.global swtch
+
+swtch:
+ sd ra, 0(a0)
+ sd sp, 8(a0)
+ sd s0, 16(a0)
+ sd s1, 24(a0)
+ sd s2, 32(a0)
+ sd s3, 40(a0)
+ sd s4, 48(a0)
+ sd s5, 56(a0)
+ sd s6, 64(a0)
+ sd s7, 72(a0)
+ sd s8, 80(a0)
+ sd s9, 88(a0)
+ sd s10, 96(a0)
+ sd s11, 104(a0)
+
+ ld ra, 0(a1)
+ ld sp, 8(a1)
+ ld s0, 16(a1)
+ ld s1, 24(a1)
+ ld s2, 32(a1)
+ ld s3, 40(a1)
+ ld s4, 48(a1)
+ ld s5, 56(a1)
+ ld s6, 64(a1)
+ ld s7, 72(a1)
+ ld s8, 80(a1)
+ ld s9, 88(a1)
+ ld s10, 96(a1)
+ ld s11, 104(a1)
+
+ ret