From 7ececbe2818510a8acaa2a2e513085c768b14d15 Mon Sep 17 00:00:00 2001 From: Richard Qin Date: Sat, 27 Dec 2025 21:56:42 +0800 Subject: feat: Implement the first user program of the lume OS --- src/kernel/main.cc | 6 +- src/kernel/mm/vm.cc | 299 +++++++++++++++++++++++++++++++++++++------ src/kernel/proc/proc.cc | 161 ++++++++++++++++++++--- src/kernel/trap/handler.cc | 50 ++++++++ src/kernel/trap/trampoline.S | 98 ++++++++++++++ src/kernel/trap/trap.S | 10 +- 6 files changed, 559 insertions(+), 65 deletions(-) create mode 100644 src/kernel/trap/trampoline.S (limited to 'src/kernel') diff --git a/src/kernel/main.cc b/src/kernel/main.cc index 6a7970c..c6d794d 100644 --- a/src/kernel/main.cc +++ b/src/kernel/main.cc @@ -67,9 +67,9 @@ extern "C" void kernel_main() void task_a(); void task_b(); - ProcManager::create_kernel_thread(task_a, "Task A"); - ProcManager::create_kernel_thread(task_b, "Task B"); + // ProcManager::create_kernel_thread(task_a, "Task A"); + // ProcManager::create_kernel_thread(task_b, "Task B"); + ProcManager::user_init(); - Drivers::uart_puts("[Kernel] Scheduler Starting...\n"); ProcManager::scheduler(); } \ No newline at end of file diff --git a/src/kernel/mm/vm.cc b/src/kernel/mm/vm.cc index 8a7068e..a38ee84 100644 --- a/src/kernel/mm/vm.cc +++ b/src/kernel/mm/vm.cc @@ -1,26 +1,62 @@ #include "kernel/mm.h" #include "kernel/pmm.h" +#include "kernel/riscv.h" #include "drivers/uart.h" #include "common/types.h" #include "kernel/config.h" -extern "C" char text_start[]; -extern "C" char text_end[]; -extern "C" char rodata_start[]; -extern "C" char rodata_end[]; -extern "C" char data_start[]; -extern "C" char kernel_end[]; +void *memset(void *dst, int c, uint n) +{ + char *cdst = (char *)dst; + for (uint i = 0; i < n; i++) + cdst[i] = c; + return dst; +} -namespace VM +void *memmove(void *dst, const void *src, uint n) { - pagetable_t kernel_pagetable; + const char *s = (const char *)src; + char *d = (char *)dst; + if (s < d && s + n > d) + { + s += n; + d += n; + while (n-- > 0) + *--d = *--s; + } + else + { + while (n-- > 0) + *d++ = *s++; + } + return dst; +} + +extern char text_start[]; +extern char text_end[]; +extern char rodata_start[]; +extern char rodata_end[]; +extern char data_start[]; +extern char data_end[]; +extern char bss_start[]; +extern char bss_end[]; +extern char kernel_end[]; - #define PX(level, va) ((((uint64)va) >> (12 + 9 * (level))) & 0x1FF) - #define PA2PTE(pa) ((((uint64)pa) >> 12) << 10) - #define PTE2PA(pte) (((pte) >> 10) << 12) +extern char uservec[]; +extern char userret[]; +extern char trampoline[]; + +VM::pagetable_t kernel_pagetable; + +namespace VM +{ + // pagetable_t kernel_pagetable; pte_t *walk(pagetable_t pagetable, uint64 va, int alloc) { + if (va >= MAXVA) + return 0; + for (int level = 2; level > 0; level--) { pte_t *pte = &pagetable[PX(level, va)]; @@ -31,13 +67,10 @@ namespace VM } else { - if (!alloc) + if (!alloc || (pagetable = (pagetable_t)PMM::alloc_page()) == 0) return 0; - + memset(pagetable, 0, PGSIZE); pagetable = (pagetable_t)PMM::alloc_page(); - if (!pagetable) - return 0; - *pte = PA2PTE(pagetable) | PTE_V; } } @@ -49,8 +82,8 @@ namespace VM uint64 a, last; pte_t *pte; - a = (va / PGSIZE) * PGSIZE; - last = ((va + size - 1) / PGSIZE) * PGSIZE; + a = PGROUNDDOWN(va); + last = PGROUNDDOWN(va + size - 1); for(;;) { @@ -59,7 +92,9 @@ namespace VM if (*pte & PTE_V) { - Drivers::uart_puts("Remap error\n"); + Drivers::uart_puts("Remap panic: "); + Drivers::print_hex(a); + Drivers::uart_puts("\n"); return -1; } @@ -73,42 +108,228 @@ namespace VM return 0; } + void kvmmap(uint64 va, uint64 pa, uint64 sz, int perm) + { + if (mappages(kernel_pagetable, va, sz, pa, perm) != 0) + { + Drivers::uart_puts("kvmmap: failed to map\n"); + } + } + void kvminit() { kernel_pagetable = (pagetable_t)PMM::alloc_page(); + memset(kernel_pagetable, 0, PGSIZE); + + kvmmap(g_uart_base, g_uart_base, PGSIZE, PTE_R | PTE_W); + + kvmmap(g_plic_base, g_plic_base, 0x400000, PTE_R | PTE_W); + + // kvmmap(g_clint_base, g_clint_base, 0x10000, PTE_R | PTE_W); + + kvmmap((uint64)text_start, (uint64)text_start, + (uint64)text_end - (uint64)text_start, PTE_R | PTE_X); + + kvmmap((uint64)rodata_start, (uint64)rodata_start, + (uint64)rodata_end - (uint64)rodata_start, PTE_R); + + kvmmap((uint64)data_start, (uint64)data_start, + (uint64)kernel_end - (uint64)data_start, PTE_R | PTE_W); - if (g_uart_base != 0) + kvmmap((uint64)kernel_end, (uint64)kernel_end, + (uint64)PHYSTOP - (uint64)kernel_end, PTE_R | PTE_W); + + kvmmap(TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X); + } + void kvminithart() + { + w_satp(MAKE_SATP(kernel_pagetable)); + sfence_vma(); + } + + + pagetable_t uvmcreate() + { + pagetable_t pagetable = (pagetable_t)PMM::alloc_page(); + if (pagetable == 0) + return 0; + memset(pagetable, 0, PGSIZE); + + if (mappages(pagetable, TRAMPOLINE, PGSIZE, (uint64)trampoline, PTE_R | PTE_X) < 0) { - mappages(kernel_pagetable, g_uart_base, PGSIZE, g_uart_base, PTE_R | PTE_W); + return 0; } - // if (g_clint_base != 0) - // { - // mappages(kernel_pagetable, g_clint_base, 0x10000, g_clint_base, PTE_R | PTE_W); - // } + return pagetable; + } + + void uvminit(pagetable_t pagetable, uchar *src, uint sz) + { + char *mem; - if (g_plic_base != 0) + if (sz >= PGSIZE) { - mappages(kernel_pagetable, g_plic_base, 0x400000, g_plic_base, PTE_R | PTE_W); + Drivers::uart_puts("uvminit: initcode too big\n"); + return; } - mappages(kernel_pagetable, (uint64)text_start, (uint64)text_end - (uint64)text_start, - (uint64)text_start, PTE_R | PTE_X); + mem = (char*)PMM::alloc_page(); + memset(mem, 0, PGSIZE); - mappages(kernel_pagetable, (uint64)data_start, PHYSTOP - (uint64)data_start, - (uint64)data_start, PTE_R | PTE_W); + mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U); - mappages(kernel_pagetable, (uint64)rodata_start, (uint64)rodata_end - (uint64)rodata_start, - (uint64)rodata_start, PTE_R); - + memmove(mem, src, sz); + + uint64 trap_pa = (uint64)uservec; + uint64 trap_page_start = PGROUNDDOWN(trap_pa); + + mappages(pagetable, trap_page_start, PGSIZE, trap_page_start, PTE_R | PTE_X); + + mappages(pagetable, trap_page_start + PGSIZE, PGSIZE, trap_page_start + PGSIZE, PTE_R | PTE_X); } - void kvminithart() + + void uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) + { + uint64 a; + pte_t *pte; + + if ((va % PGSIZE) != 0) + { + Drivers::uart_puts("uvmunmap: not aligned\n"); + return; + } + + for(a = va; a < va + npages * PGSIZE; a += PGSIZE) + { + if ((pte = walk(pagetable, a, 0)) == 0) + continue; + + if((*pte & PTE_V) == 0) + continue; + + if (PTE_FLAGS(*pte) == PTE_V) + { + Drivers::uart_puts("uvmunmap: not a leaf\n"); + } + + if (do_free) + { + uint64 pa = PTE2PA(*pte); + PMM::free_page((void *)pa); + } + *pte = 0; + } + } + + void uvmfree(pagetable_t pagetable, uint64 sz) + { + if(sz > 0) + uvmunmap(pagetable, 0, PGROUNDDOWN(sz)/PGSIZE, 1); + + PMM::free_page((void *)pagetable); + } + + int uvmcopy(pagetable_t oldpt, pagetable_t newpt, uint64 sz) + { + pte_t *pte; + uint64 pa, i; + uint flags; + char *mem; + + for(i = 0; i < sz; i += PGSIZE) + { + if((pte = walk(oldpt, i, 0)) == 0) + continue; + if ((*pte & PTE_V) == 0) + continue; + + pa = PTE2PA(*pte); + flags = PTE_FLAGS(*pte); + + if ((mem = (char *)PMM::alloc_page()) == 0) + goto err; + + memmove(mem, (char *)pa, PGSIZE); + + if (mappages(newpt, i, PGSIZE, (uint64)mem, flags) != 0) + { + PMM::free_page(mem); + goto err; + } + } + return 0; + err: + uvmunmap(newpt, 0, i / PGSIZE, 1); + return -1; + } + + uint64 uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) { - asm volatile("sfence.vma zero, zero"); + char *mem; + uint64 a; - uint64 satp_val = MAKE_SATP(kernel_pagetable); - asm volatile("csrw satp, %0" : : "r"(satp_val)); + if (newsz < oldsz) + return oldsz; - asm volatile("sfence.vma zero, zero"); + oldsz = PGROUNDUP(oldsz); + for (a = oldsz; a < newsz; a += PGSIZE) + { + mem = (char *)PMM::alloc_page(); + if (mem == 0) + { + uvmdealloc(pagetable, a, oldsz); + return 0; + } + memset(mem, 0, PGSIZE); + if (mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W | PTE_X | PTE_R | PTE_U) != 0) + { + PMM::free_page(mem); + uvmdealloc(pagetable, a, oldsz); + return 0; + } + } + return newsz; + } + + uint64 uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) + { + if (newsz >= oldsz) + return oldsz; + + if (PGROUNDUP(newsz) < PGROUNDUP(oldsz)) + { + int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE; + uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1); + } + + return newsz; + } + + int copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len) + { + uint64 n, va0, pa0; + pte_t *pte; + + while (len > 0) + { + va0 = PGROUNDDOWN(srcva); + pte = walk(pagetable, va0, 0); + if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0) + { + return -1; + } + pa0 = PTE2PA(*pte); + n = PGSIZE - (srcva - va0); + if(n > len) + n = len; + + memmove(dst, (void*)(pa0 + (srcva - va0)), n); + + len -= n; + dst += n; + srcva = va0 + PGSIZE; + } + return 0; + } } \ No newline at end of file diff --git a/src/kernel/proc/proc.cc b/src/kernel/proc/proc.cc index 469b7eb..1240bee 100644 --- a/src/kernel/proc/proc.cc +++ b/src/kernel/proc/proc.cc @@ -1,9 +1,24 @@ #include "kernel/proc.h" #include "kernel/pmm.h" +#include "kernel/mm.h" #include "drivers/uart.h" +#include "kernel/trap.h" +#include "common/types.h" +#include "kernel/riscv.h" +#include "kernel/config.h" #define NPROC 10 +extern "C" void swtch(struct Context *old_ctx, struct Context *new_ctx); +extern char uservec[]; +extern "C" void userret(uint64, uint64); +extern char trampoline[]; +extern void *memset(void *dst, int c, uint n); + +struct Proc procs[NPROC]; +struct Context main_context; +struct Proc *current_proc = nullptr; + void str_copy(char *dst, const char *src) { while (*src) @@ -11,10 +26,53 @@ void str_copy(char *dst, const char *src) *dst = 0; } -extern "C" void swtch(struct Context *old_ctx, struct Context *new_ctx); +uchar initcode[] = { + 0x13, 0x05, 0x50, 0x05, // li a0, 85 ('U') + 0x93, 0x08, 0x10, 0x00, // li a7, 1 (SYS_putc) + 0x73, 0x00, 0x00, 0x00, // ecall + 0x6f, 0xf0, 0x5f, 0xff // j loop (-12) +}; + +struct Proc *myproc() +{ + return current_proc; +} + +void usertrapret() +{ + struct Proc *p = myproc(); + + asm volatile("csrc sstatus, %0" : : "r"(SSTATUS_SIE)); + + uint64 trampoline_uservec = TRAMPOLINE + (uservec - trampoline); + asm volatile("csrw stvec, %0" : : "r"(trampoline_uservec)); + + 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(); + + unsigned long x = r_sstatus(); + x &= ~SSTATUS_SPP; + x |= SSTATUS_SPIE; + w_sstatus(x); + + w_sepc(p->tf->epc); + + uint64 satp = MAKE_SATP(p->pagetable); + + uint64 fn = TRAMPOLINE + ((uint64)userret - (uint64)trampoline); + ((void (*)(uint64, uint64))fn)(TRAPFRAME, satp); +} + +void forkret() +{ + //.... + -struct Proc procs[NPROC]; -struct Context main_context; + + usertrapret(); +} namespace ProcManager { @@ -23,6 +81,7 @@ namespace ProcManager for (int i = 0; i < NPROC; i++) { procs[i].state = UNUSED; + procs[i].kstack = 0; } } @@ -66,16 +125,8 @@ namespace ProcManager void yield() { - struct Proc *p = nullptr; - for(int i = 0; i < NPROC; i++) - { - if (procs[i].state == RUNNING) - { - p = &procs[i]; - break; - } - } - if(p) + struct Proc *p = myproc(); + if (p && p->state == RUNNING) { p->state = RUNNABLE; swtch(&p->context, &main_context); @@ -89,25 +140,99 @@ namespace ProcManager for (;;) { - // int nothing_to_run = 1; + int nothing_found = 1; - intr_off(); + intr_on(); for (p = procs; p < &procs[NPROC]; p++) { if (p->state == RUNNABLE) { - // nothing_to_run = 0; + nothing_found = 0; p->state = RUNNING; - intr_on(); + current_proc = p; swtch(&main_context, &p->context); - intr_off(); + current_proc = nullptr; } } + if (nothing_found) + { + asm volatile("wfi"); + } + } + } + + void user_init() + { + struct Proc *p = nullptr; + for(int i = 0; i < NPROC; ++i) + { + if(procs[i].state == UNUSED) + { + p = &procs[i]; + p->pid = i + 1; + break; + } + } + + if (!p) + { + Drivers::uart_puts("user_init: no free procs\n"); + return; + } + + p->kstack = PMM::alloc_page(); + if (p->kstack == 0) + { + Drivers::uart_puts("user_init: kstack alloc failed\n"); + return; + } + + p->tf = (struct Trapframe*)PMM::alloc_page(); + + if (p->tf == 0) + { + Drivers::uart_puts("user_init: tf alloc failed\n"); + return; } + memset(p->tf, 0, PGSIZE); + + p->pagetable = VM::uvmcreate(); + + // if (VM::mappages(p->pagetable, TRAMPOLINE, PGSIZE, + // (uint64)trampoline, PTE_R | PTE_X) < 0) + // { + // Drivers::uart_puts("user_init: map trampoline failed\n"); + // return; + // } + + if (VM::mappages(p->pagetable, TRAPFRAME, PGSIZE, + (uint64)p->tf, PTE_R | PTE_W) < 0) + { + Drivers::uart_puts("user_init: map trapframe failed\n"); + return; + } + + VM::uvminit(p->pagetable, initcode, sizeof(initcode)); + p->sz = PGSIZE; + + p->tf->epc = 0; + p->tf->sp = PGSIZE; + + memset(&p->context, 0, sizeof(p->context)); + p->context.ra = (uint64)forkret; + p->context.sp = (uint64)p->kstack + PGSIZE; + + p->state = RUNNABLE; + + const char *name = "initcode"; + for (int i = 0; name[i] && i < 15; i++) + p->name[i] = name[i]; + + Drivers::uart_puts("[Proc] user_init success. PID: 1\n"); } } \ No newline at end of file diff --git a/src/kernel/trap/handler.cc b/src/kernel/trap/handler.cc index df4f202..4bb8c06 100644 --- a/src/kernel/trap/handler.cc +++ b/src/kernel/trap/handler.cc @@ -4,6 +4,7 @@ #include "kernel/proc.h" #include "kernel/timer.h" +extern void usertrapret(); void panic(const char *s) { @@ -123,4 +124,53 @@ namespace Trap } } } +} + +extern "C" void usertrap() +{ + asm volatile("csrw stvec, %0" : : "r"(Trap::kernelvec)); + + uint64 scause; + asm volatile("csrr %0, scause" : "=r"(scause)); + + struct Proc *p = myproc(); + + extern struct Proc procs[]; + for(int i = 0; i < 10; ++i) + if(procs[i].state == RUNNING) + p = &procs[i]; + + uint64 sepc; + asm volatile("csrr %0, sepc" : "=r"(sepc)); + p->tf->epc = sepc; + + if(scause == 8) + { + p->tf->epc += 4; + + uint64 syscall_num = p->tf->a7; + + if (syscall_num == 1) + { + char c = (char)p->tf->a0; + Drivers::uart_putc(c); + } + else + { + Drivers::uart_puts("Unknown Syscall\n"); + } + } + else if ((scause & (1L << 63)) && (scause & 0xF) == 5) + { + Timer::set_next_trigger(); + ProcManager::yield(); + } + else + { + Drivers::uart_puts("Unexpected User Trap!\n"); + while (1) + ; + } + + forkret(); } \ No newline at end of file diff --git a/src/kernel/trap/trampoline.S b/src/kernel/trap/trampoline.S new file mode 100644 index 0000000..478d345 --- /dev/null +++ b/src/kernel/trap/trampoline.S @@ -0,0 +1,98 @@ +.section .text +.globl trampoline +.globl uservec +.globl userret + +.align 12 +trampoline: +.align 4 +uservec: + + csrrw a0, sscratch, a0 + + sd ra, 40(a0) + sd sp, 48(a0) + sd gp, 56(a0) + sd tp, 64(a0) + sd t0, 72(a0) + sd t1, 80(a0) + sd t2, 88(a0) + sd s0, 96(a0) + sd s1, 104(a0) + sd a1, 120(a0) + sd a2, 128(a0) + sd a3, 136(a0) + sd a4, 144(a0) + sd a5, 152(a0) + sd a6, 160(a0) + sd a7, 168(a0) + sd s2, 176(a0) + sd s3, 184(a0) + sd s4, 192(a0) + sd s5, 200(a0) + sd s6, 208(a0) + sd s7, 216(a0) + sd s8, 224(a0) + sd s9, 232(a0) + sd s10, 240(a0) + sd s11, 248(a0) + sd t3, 256(a0) + sd t4, 264(a0) + sd t5, 272(a0) + sd t6, 280(a0) + + csrr t0, sscratch + sd t0, 112(a0) + + ld sp, 8(a0) + ld tp, 32(a0) + ld t0, 16(a0) + ld t1, 0(a0) + + csrw satp, t1 + sfence.vma zero, zero + + jr t0 + +.align 4 +userret: + + csrw satp, a1 + sfence.vma zero, zero + + csrw sscratch, a0 + + ld ra, 40(a0) + ld sp, 48(a0) + ld gp, 56(a0) + ld tp, 64(a0) + ld t0, 72(a0) + ld t1, 80(a0) + ld t2, 88(a0) + ld s0, 96(a0) + ld s1, 104(a0) + ld a1, 120(a0) + ld a2, 128(a0) + ld a3, 136(a0) + ld a4, 144(a0) + ld a5, 152(a0) + ld a6, 160(a0) + ld a7, 168(a0) + ld s2, 176(a0) + ld s3, 184(a0) + ld s4, 192(a0) + ld s5, 200(a0) + ld s6, 208(a0) + ld s7, 216(a0) + ld s8, 224(a0) + ld s9, 232(a0) + ld s10, 240(a0) + ld s11, 248(a0) + ld t3, 256(a0) + ld t4, 264(a0) + ld t5, 272(a0) + ld t6, 280(a0) + + ld a0, 112(a0) + + sret \ No newline at end of file diff --git a/src/kernel/trap/trap.S b/src/kernel/trap/trap.S index ffce52a..573248e 100644 --- a/src/kernel/trap/trap.S +++ b/src/kernel/trap/trap.S @@ -1,10 +1,11 @@ .section .text .global kernelvec -.align 4 +.global uservec +.global userret +.align 12 kernelvec: - - addi sp, sp, -256 + addi sp, sp, -288 sd ra, 40(sp) sd sp, 48(sp) @@ -82,6 +83,5 @@ kernelvec: ld t5, 272(sp) ld t6, 280(sp) - addi sp, sp, 256 - + addi sp, sp, 288 sret \ No newline at end of file -- cgit v1.2.3