summaryrefslogtreecommitdiff
path: root/src/kernel
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-01-02 10:09:37 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-01-02 10:09:37 +0800
commit1c8e37f63d413a559825020289178d442537449e (patch)
tree9779a712b5eec91035272e7646eb6811979d7eab /src/kernel
parentd8eaa13f2efc1eeca4e5f230d5a2d535aaf27338 (diff)
downloadlume-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.cc2
-rw-r--r--src/kernel/mm/pmm.cc12
-rw-r--r--src/kernel/mm/vm.cc29
-rw-r--r--src/kernel/proc/exec.cc210
-rw-r--r--src/kernel/syscall.cc115
-rw-r--r--src/kernel/trap/handler.cc6
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 ");