diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-01-02 10:09:37 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-01-02 10:09:37 +0800 |
| commit | 1c8e37f63d413a559825020289178d442537449e (patch) | |
| tree | 9779a712b5eec91035272e7646eb6811979d7eab /src/kernel | |
| parent | d8eaa13f2efc1eeca4e5f230d5a2d535aaf27338 (diff) | |
| download | lume-1c8e37f63d413a559825020289178d442537449e.tar.gz lume-1c8e37f63d413a559825020289178d442537449e.tar.xz | |
dev(sell): add simple libc and implement shell
please see the log for detail.
Diffstat (limited to 'src/kernel')
| -rw-r--r-- | src/kernel/main.cc | 2 | ||||
| -rw-r--r-- | src/kernel/mm/pmm.cc | 12 | ||||
| -rw-r--r-- | src/kernel/mm/vm.cc | 29 | ||||
| -rw-r--r-- | src/kernel/proc/exec.cc | 210 | ||||
| -rw-r--r-- | src/kernel/syscall.cc | 115 | ||||
| -rw-r--r-- | src/kernel/trap/handler.cc | 6 |
6 files changed, 368 insertions, 6 deletions
diff --git a/src/kernel/main.cc b/src/kernel/main.cc index 9654c18..9a24028 100644 --- a/src/kernel/main.cc +++ b/src/kernel/main.cc @@ -5,6 +5,7 @@ #include "drivers/virtio.h" #include "kernel/pmm.h" #include "kernel/mm.h" +#include "kernel/slab.h" #include "kernel/proc.h" #include "kernel/trap.h" #include "kernel/timer.h" @@ -34,6 +35,7 @@ extern "C" void kernel_main(uint64 hartid, uint64 dtb) PMM::init(); // Physical Memory Management VM::kvminit(); // Kernel Pagetable VM::kvminithart(); // Enable MMU + Slab::init(); Trap::init(); // Trap Management Trap::inithart(); PLIC::init(); diff --git a/src/kernel/mm/pmm.cc b/src/kernel/mm/pmm.cc index 3474a49..e871d8a 100644 --- a/src/kernel/mm/pmm.cc +++ b/src/kernel/mm/pmm.cc @@ -75,7 +75,7 @@ namespace PMM uint64 kern_end_pa = PGROUNDUP((uint64)kernel_end); total_pages = (PHYSTOP - KERNBASE) / PGSIZE; - Drivers::uart_puts("PMM: Total RAM size: 128MB\n"); + Drivers::uart_puts(ANSI_GREEN "[PMM] Total RAM size: 128MB\n" ANSI_RESET); // Place the mem_map array // mem_map is located immediately after kernel_end mem_map = (struct Page*)kern_end_pa; @@ -83,17 +83,17 @@ namespace PMM uint64 mem_map_size = total_pages * sizeof(struct Page); uint64 free_mem_start = PGROUNDUP(kern_end_pa + mem_map_size); - Drivers::uart_puts("PMM: MemMap placed at: "); + Drivers::uart_puts(ANSI_GREEN "[PMM] MemMap placed at: " ); Drivers::print_hex((uint64)mem_map); - Drivers::uart_putc('\n'); + Drivers::uart_puts("\n" ANSI_RESET); - Drivers::uart_puts("PMM: Free memory starts at: "); + Drivers::uart_puts(ANSI_GREEN "[PMM] Free memory starts at: "); Drivers::print_hex(free_mem_start); - Drivers::uart_puts("\n"); + Drivers::uart_puts("\n" ANSI_RESET); if (free_mem_start >= PHYSTOP) { - Drivers::uart_puts("PMM: Error - Kernel too large, no memory left!\n"); + Drivers::uart_puts(ANSI_RED "[PMM] Error - Kernel too large, no memory left!\n" ANSI_RESET); while (1) ; } diff --git a/src/kernel/mm/vm.cc b/src/kernel/mm/vm.cc index 29e9eb7..41d1aa1 100644 --- a/src/kernel/mm/vm.cc +++ b/src/kernel/mm/vm.cc @@ -261,6 +261,8 @@ namespace VM return 0; } + int handle_cow_fault(uint64 *pagetable, uint64 va); + int copyout(uint64 *pagetable, uint64 dstva, char *src, uint64 len) { uint64 n, va0, pa0; @@ -273,6 +275,17 @@ namespace VM if (pte == nullptr || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0) return -1; + // Note: Maybe a bug + if (*pte & PTE_COW) + { + if (handle_cow_fault(pagetable, va0) < 0) + return -1; + // Re-walk because handle_cow_fault replaced the page mapping + pte = walk(pagetable, va0, 0); + if (pte == nullptr || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0) + return -1; + } + pa0 = PTE2PA(*pte); n = PGSIZE - (dstva - va0); if (n > len) @@ -419,4 +432,20 @@ namespace VM sfence_vma(); Drivers::uart_puts("[Boot] MMU ENABLED! Using virtual addresses.\n"); } + + uint64 walkaddr(uint64 *pagetable, uint64 va) + { + uint64 *pte; + uint64 pa; + + if (va >= MAXVA) + return 0; + + pte = walk(pagetable, va, 0); + if (pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0) + return 0; + + pa = PTE2PA(*pte); + return pa; + } }
\ No newline at end of file diff --git a/src/kernel/proc/exec.cc b/src/kernel/proc/exec.cc new file mode 100644 index 0000000..d0e75ef --- /dev/null +++ b/src/kernel/proc/exec.cc @@ -0,0 +1,210 @@ +#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" +#include "drivers/uart.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) + 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) + return -1; + } + return 0; +} + +namespace Exec +{ + int exec(char *path, char **argv) + { + // Get Inode + Inode *raw_ip = VFS::namei(path); + if (!raw_ip) + return -1; + InodeGuard iguard(raw_ip); + + // Get ELF head + struct elfhdr elf; + if (raw_ip->read((char *)&elf, 0, sizeof(elf), 0) != sizeof(elf)) + return -1; + if (elf.magic != ELF_MAGIC) + return -1; + // Create new pagetable + VmGuard vm_guard; + if (!vm_guard.pagetable) + 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)) + 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) + 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); + uint64 sz1 = VM::uvmalloc(vm_guard.pagetable, sz, sz + 2 * PGSIZE, PTE_W | PTE_R | PTE_U); + if (sz1 == 0) + return -1; + vm_guard.sz = sz1; + + VM::uvmunmap(vm_guard.pagetable, sz1 - 2 * PGSIZE, 1, 0); // protected pagetable + + uint64 sp = sz1; + uint64 stackbase = sp - 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) + return -1; + + if (VM::copyout(vm_guard.pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0) + return -1; + ustack[argc] = sp; + } + ustack[argc] = 0; + + // Push into parameter array + sp -= (argc + 1) * sizeof(uint64); + sp -= sp % 16; + if (sp < stackbase) + return -1; + if (VM::copyout(vm_guard.pagetable, sp, (char *)ustack, (argc + 1) * sizeof(uint64)) < 0) + return -1; + + // Commit Changes + struct Proc *p = myproc(); + if (VM::mappages(vm_guard.pagetable, TRAPFRAME, PGSIZE, (uint64)p->tf, PTE_R | PTE_W) < 0) + return -1; + + uint64 oldsz = p->sz; + uint64 *oldpagetable = p->pagetable; + + // setup Trapframe + p->tf->a0 = argc; + p->tf->a1 = sp; + p->tf->sp = sp; + p->tf->epc = elf.entry; + + // update process name + char *s = path, *last = path; + while (*s) + { + if (*s == '/') + last = s + 1; + s++; + } + // memmove(p->name, last, sizeof(p->name)); + 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/src/kernel/syscall.cc b/src/kernel/syscall.cc index afa20a1..6af7730 100644 --- a/src/kernel/syscall.cc +++ b/src/kernel/syscall.cc @@ -8,10 +8,16 @@ #include "kernel/timer.h" #include "kernel/mm.h" #include "kernel/trap.h" +#include "kernel/slab.h" #include "fs/fat32.h" #include "fs/file.h" +#include "lib/string.h" extern "C++" int fork(); +namespace Exec +{ + int exec(char *path, char **argv); +} namespace ProcManager { @@ -54,6 +60,34 @@ static int argstr(int n, char *buf, int max) return VM::copyinstr(myproc()->pagetable, buf, addr, max); } +struct KernelArgv +{ + static const int MAX_ARGS = 32; + static const int MAX_ARG_LEN = 128; + + char *args[MAX_ARGS]; + + KernelArgv() + { + memset(args, 0, sizeof(args)); + } + + ~KernelArgv() + { + for (int i = 0; i < MAX_ARGS; i++) + { + if (args[i]) + { + Slab::kfree(args[i]); + args[i] = nullptr; + } + } + } + + KernelArgv(const KernelArgv &) = delete; + KernelArgv &operator=(const KernelArgv &) = delete; +}; + static int fdalloc(struct file *f) { struct Proc *p = myproc(); @@ -125,6 +159,53 @@ static uint64 sys_close() return 0; } +static uint64 sys_exec() +{ + char path[128]; + uint64 uargv_ptr; + + if (argstr(0, path, sizeof(path)) < 0) + { + return -1; + } + + if (argint(1, (int *)&uargv_ptr) < 0) + { + return -1; + } + + KernelArgv kargv; + + for (int i = 0; i < KernelArgv::MAX_ARGS; ++i) + { + uint64 uarg_str_ptr; + + if (VM::copyin(myproc()->pagetable, (char *)&uarg_str_ptr, uargv_ptr + i * sizeof(uint64), sizeof(uint64)) < 0) + { + return -1; + } + + if (uarg_str_ptr == 0) + { + kargv.args[i] = nullptr; + break; + } + + kargv.args[i] = (char *)Slab::kmalloc(KernelArgv::MAX_ARG_LEN); + if (kargv.args[i] == nullptr) + { + return -1; + } + + if (VM::copyinstr(myproc()->pagetable, kargv.args[i], uarg_str_ptr, KernelArgv::MAX_ARG_LEN) < 0) + { + return -1; + } + } + + return Exec::exec(path, kargv.args); +} + static uint64 sys_exit() { int n; @@ -255,6 +336,19 @@ static uint64 sys_disk_test() return 0; } +static uint64 sys_lseek() +{ + int fd, offset, whence; + if (argint(0, &fd) < 0 || argint(1, &offset) < 0 || argint(2, &whence) < 0) + return -1; + + struct Proc *p = myproc(); + if (fd < 0 || fd >= NOFILE || p->ofile[fd] == 0) + return -1; + + return FileTable::lseek(p->ofile[fd], offset, whence); +} + void syscall() { Proc *p = myproc(); @@ -286,6 +380,13 @@ void syscall() case SYS_fork: ret = sys_fork(); break; + case SYS_exec: + ret = sys_exec(); + if (ret != static_cast<uint64>(-1)) + { + return; + } + break; case SYS_exit: ret = sys_exit(); break; @@ -301,6 +402,20 @@ void syscall() case SYS_disk_test: ret = sys_disk_test(); break; + case SYS_lseek: + ret = sys_lseek(); + break; + case SYS_pipe: + case SYS_dup: + case SYS_chdir: + case SYS_fstat: + case SYS_mkdir: + case SYS_mknod: + case SYS_unlink: + case SYS_link: + Drivers::uart_puts("Unimplemented syscall\n"); + ret = static_cast<uint64>(-1); + break; default: Drivers::uart_puts("Unknown Syscall ID: "); Drivers::print_hex(num); diff --git a/src/kernel/trap/handler.cc b/src/kernel/trap/handler.cc index c1626cc..3e73213 100644 --- a/src/kernel/trap/handler.cc +++ b/src/kernel/trap/handler.cc @@ -157,6 +157,12 @@ extern "C" void usertrap() } else if (scause == 15) // Store Page Fault { + uint64 pte = VM::walkaddr(p->pagetable, stval); + + // Drivers::uart_puts("[Debug] Faulting PTE:"); + // Drivers::print_hex(pte); + // Drivers::uart_puts("\n"); + if (VM::handle_cow_fault(p->pagetable, PGROUNDDOWN(stval)) != 0) { Drivers::uart_puts("usertrap: cow failed at "); |
