From 29878e42e69d35bbf658a2a9b6c4eff327d118ec Mon Sep 17 00:00:00 2001 From: Garen Tyler Date: Mon, 16 Oct 2023 17:28:59 -0600 Subject: [PATCH] changes --- Makefile | 19 +- kernel/console.c | 121 +-------- kernel/defs.h | 18 +- kernel/fs.c | 4 +- kernel/kalloc.c | 82 ------ kernel/main.c | 48 ---- kernel/plic.c | 47 ---- kernel/printf.c | 139 ---------- kernel/proc.c | 220 ++-------------- kernel/proc.h | 1 + kernel/ramdisk.c | 5 +- kernel/riscv.c | 30 --- kernel/rustkernel/Cargo.lock | 14 + kernel/rustkernel/Cargo.toml | 2 + kernel/rustkernel/src/buf.rs | 16 ++ kernel/rustkernel/src/console.rs | 225 ++++++++++++++++ kernel/rustkernel/src/file.rs | 11 + kernel/rustkernel/src/fs.rs | 79 ++++++ kernel/rustkernel/src/kalloc.rs | 102 +++++++- kernel/rustkernel/src/lib.rs | 97 +++++-- kernel/rustkernel/src/printf.rs | 66 +++++ kernel/rustkernel/src/proc.rs | 311 ++++++++++++++++++++++- kernel/rustkernel/src/ramdisk.rs | 8 + kernel/rustkernel/src/riscv/asm.rs | 1 - kernel/rustkernel/src/riscv/memlayout.rs | 84 ++++++ kernel/rustkernel/src/riscv/mod.rs | 33 +++ kernel/rustkernel/src/riscv/plic.rs | 39 +++ kernel/rustkernel/src/sleeplock.rs | 53 ++++ kernel/rustkernel/src/spinlock.rs | 7 + kernel/rustkernel/src/start.rs | 83 ++++++ kernel/rustkernel/src/string.rs | 133 ++++++++++ kernel/rustkernel/src/syscall.rs | 231 +++++++++++++++++ kernel/rustkernel/src/sysproc.rs | 79 ++++++ kernel/rustkernel/src/trap.rs | 144 +++++++++++ kernel/rustkernel/src/uart.rs | 204 +++++++++++++++ kernel/sleeplock.c | 55 ---- kernel/start.c | 89 ------- kernel/string.c | 107 -------- kernel/syscall.c | 147 ----------- kernel/sysproc.c | 91 ------- kernel/trap.c | 99 ++------ kernel/uart.c | 190 -------------- 42 files changed, 2050 insertions(+), 1484 deletions(-) delete mode 100644 kernel/kalloc.c delete mode 100644 kernel/main.c delete mode 100644 kernel/plic.c delete mode 100644 kernel/printf.c delete mode 100644 kernel/riscv.c create mode 100644 kernel/rustkernel/src/buf.rs create mode 100644 kernel/rustkernel/src/console.rs create mode 100644 kernel/rustkernel/src/file.rs create mode 100644 kernel/rustkernel/src/fs.rs create mode 100644 kernel/rustkernel/src/printf.rs create mode 100644 kernel/rustkernel/src/ramdisk.rs create mode 100644 kernel/rustkernel/src/riscv/memlayout.rs create mode 100644 kernel/rustkernel/src/riscv/plic.rs create mode 100644 kernel/rustkernel/src/sleeplock.rs create mode 100644 kernel/rustkernel/src/start.rs create mode 100644 kernel/rustkernel/src/string.rs create mode 100644 kernel/rustkernel/src/syscall.rs create mode 100644 kernel/rustkernel/src/sysproc.rs create mode 100644 kernel/rustkernel/src/trap.rs create mode 100644 kernel/rustkernel/src/uart.rs delete mode 100644 kernel/sleeplock.c delete mode 100644 kernel/start.c delete mode 100644 kernel/string.c delete mode 100644 kernel/syscall.c delete mode 100644 kernel/sysproc.c delete mode 100644 kernel/uart.c diff --git a/Makefile b/Makefile index fe700e6..3724d41 100644 --- a/Makefile +++ b/Makefile @@ -5,32 +5,21 @@ P=programs OBJS = \ $K/entry.o \ - $K/start.o \ $K/console.o \ - $K/printf.o \ - $K/uart.o \ - $K/kalloc.o \ - $K/string.o \ - $K/main.o \ $K/vm.o \ $K/proc.o \ $K/swtch.o \ $K/trampoline.o \ $K/trap.o \ - $K/syscall.o \ - $K/sysproc.o \ $K/bio.o \ $K/fs.o \ $K/log.o \ - $K/sleeplock.o \ $K/file.o \ $K/pipe.o \ $K/exec.o \ $K/sysfile.o \ $K/kernelvec.o \ - $K/plic.o \ - $K/virtio_disk.o \ - $K/riscv.o + $K/virtio_disk.o # riscv64-unknown-elf- or riscv64-linux-gnu- # perhaps in /opt/riscv/bin @@ -77,9 +66,9 @@ endif LDFLAGS = -z max-page-size=4096 $K/kernel: $(OBJS) $K/kernel.ld $U/initcode $R/src - cargo +nightly -Z unstable-options -C $R build - $(OBJDUMP) -S $R/target/$(TARGET_TRIPLE)/debug/librustkernel.a > $R/target/$(TARGET_TRIPLE)/debug/librustkernel.asm - $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $R/target/$(TARGET_TRIPLE)/debug/librustkernel.a + cargo +nightly -Z unstable-options -C $R build --release + $(OBJDUMP) -S $R/target/$(TARGET_TRIPLE)/release/librustkernel.a > $R/target/$(TARGET_TRIPLE)/release/librustkernel.asm + $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $R/target/$(TARGET_TRIPLE)/release/librustkernel.a $(OBJDUMP) -S $K/kernel > $K/kernel.asm $(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym diff --git a/kernel/console.c b/kernel/console.c index 05dc526..8e185d5 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -23,117 +23,25 @@ #include "proc.h" #define BACKSPACE 0x100 +#define INPUT_BUF_SIZE 128 #define C(x) ((x)-'@') // Control-x -// -// send one character to the uart. -// called by printf(), and to echo input characters, -// but not from write(). -// -void -consputc(int c) -{ - if(c == BACKSPACE){ - // if the user typed backspace, overwrite with a space. - uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b'); - } else { - uartputc_sync(c); - } -} - -struct { +struct console { struct spinlock lock; // input -#define INPUT_BUF_SIZE 128 char buf[INPUT_BUF_SIZE]; uint r; // Read index uint w; // Write index uint e; // Edit index -} cons; +}; -// -// user write()s to the console go here. -// -int -consolewrite(int user_src, uint64 src, int n) -{ - int i; +extern struct console cons; +void consputc(int c); +int consolewrite(int user_src, uint64 src, int n); +int consoleread(int user_dst, uint64 dst, int n); - for(i = 0; i < n; i++){ - char c; - if(either_copyin(&c, user_src, src+i, 1) == -1) - break; - uartputc(c); - } - - return i; -} - -// -// user read()s from the console go here. -// copy (up to) a whole input line to dst. -// user_dist indicates whether dst is a user -// or kernel address. -// -int -consoleread(int user_dst, uint64 dst, int n) -{ - uint target; - int c; - char cbuf; - - target = n; - acquire(&cons.lock); - while(n > 0){ - // wait until interrupt handler has put some - // input into cons.buffer. - while(cons.r == cons.w){ - if(killed(myproc())){ - release(&cons.lock); - return -1; - } - sleep(&cons.r, &cons.lock); - } - - c = cons.buf[cons.r++ % INPUT_BUF_SIZE]; - - if(c == C('D')){ // end-of-file - if(n < target){ - // Save ^D for next time, to make sure - // caller gets a 0-byte result. - cons.r--; - } - break; - } - - // copy the input byte to the user-space buffer. - cbuf = c; - if(either_copyout(user_dst, dst, &cbuf, 1) == -1) - break; - - dst++; - --n; - - if(c == '\n'){ - // a whole line has arrived, return to - // the user-level read(). - break; - } - } - release(&cons.lock); - - return target - n; -} - -// -// the console input interrupt handler. -// uartintr() calls this for input character. -// do erase/kill processing, append to cons.buf, -// wake up consoleread() if a whole line has arrived. -// -void -consoleintr(int c) +void consoleintr(int c) { acquire(&cons.lock); @@ -177,16 +85,3 @@ consoleintr(int c) release(&cons.lock); } - -void -consoleinit(void) -{ - initlock(&cons.lock, "cons"); - - uartinit(); - - // connect read and write system calls - // to consoleread and consolewrite. - devsw[CONSOLE].read = consoleread; - devsw[CONSOLE].write = consolewrite; -} diff --git a/kernel/defs.h b/kernel/defs.h index 6323989..b0a6b1a 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -1,3 +1,5 @@ +#pragma once + struct buf; struct context; struct file; @@ -18,7 +20,6 @@ void bpin(struct buf*); void bunpin(struct buf*); // console.c -void consoleinit(void); void consoleintr(int); void consputc(int); @@ -77,9 +78,12 @@ int piperead(struct pipe*, uint64, int); int pipewrite(struct pipe*, uint64, int); // printf.c -void printf(char*, ...); -void panic(char*) __attribute__((noreturn)); -void printfinit(void); +__attribute__((noreturn)) void panic(char *s); +void printfinit(void); +void printstr(char *s); +void printint(int n); +void printhex(int n); +void printptr(uint64 p); // proc.c int cpuid(void); @@ -147,13 +151,7 @@ void trapinit(void); void trapinithart(void); extern struct spinlock tickslock; void usertrapret(void); - -// uart.c -void uartinit(void); void uartintr(void); -void uartputc(int); -void uartputc_sync(int); -int uartgetc(void); // vm.c void kvminit(void); diff --git a/kernel/fs.c b/kernel/fs.c index c6bab15..c81cb06 100644 --- a/kernel/fs.c +++ b/kernel/fs.c @@ -83,7 +83,7 @@ balloc(uint dev) } brelse(bp); } - printf("balloc: out of blocks\n"); + printstr("balloc: out of blocks\n"); return 0; } @@ -214,7 +214,7 @@ ialloc(uint dev, short type) } brelse(bp); } - printf("ialloc: no inodes\n"); + printstr("ialloc: no inodes\n"); return 0; } diff --git a/kernel/kalloc.c b/kernel/kalloc.c deleted file mode 100644 index 0699e7e..0000000 --- a/kernel/kalloc.c +++ /dev/null @@ -1,82 +0,0 @@ -// Physical memory allocator, for user processes, -// kernel stacks, page-table pages, -// and pipe buffers. Allocates whole 4096-byte pages. - -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "spinlock.h" -#include "riscv.h" -#include "defs.h" - -void freerange(void *pa_start, void *pa_end); - -extern char end[]; // first address after kernel. - // defined by kernel.ld. - -struct run { - struct run *next; -}; - -struct { - struct spinlock lock; - struct run *freelist; -} kmem; - -void -kinit() -{ - initlock(&kmem.lock, "kmem"); - freerange(end, (void*)PHYSTOP); -} - -void -freerange(void *pa_start, void *pa_end) -{ - char *p; - p = (char*)PGROUNDUP((uint64)pa_start); - for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE) - kfree(p); -} - -// Free the page of physical memory pointed at by pa, -// which normally should have been returned by a -// call to kalloc(). (The exception is when -// initializing the allocator; see kinit above.) -void -kfree(void *pa) -{ - struct run *r; - - if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP) - panic("kfree"); - - // Fill with junk to catch dangling refs. - memset(pa, 1, PGSIZE); - - r = (struct run*)pa; - - acquire(&kmem.lock); - r->next = kmem.freelist; - kmem.freelist = r; - release(&kmem.lock); -} - -// Allocate one 4096-byte page of physical memory. -// Returns a pointer that the kernel can use. -// Returns 0 if the memory cannot be allocated. -void * -kalloc(void) -{ - struct run *r; - - acquire(&kmem.lock); - r = kmem.freelist; - if(r) - kmem.freelist = r->next; - release(&kmem.lock); - - if(r) - memset((char*)r, 5, PGSIZE); // fill with junk - return (void*)r; -} diff --git a/kernel/main.c b/kernel/main.c deleted file mode 100644 index 9eaa96e..0000000 --- a/kernel/main.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "defs.h" - -volatile static int started = 0; - -void rust_main(); - -// start() jumps here in supervisor mode on all CPUs. -void -main() -{ - if(cpuid() == 0){ - consoleinit(); - printfinit(); - printf("\n"); - printf("xv6 kernel is booting\n"); - printf("\n"); - rust_main(); - kinit(); // physical page allocator - kvminit(); // create kernel page table - kvminithart(); // turn on paging - procinit(); // process table - trapinit(); // trap vectors - trapinithart(); // install kernel trap vector - plicinit(); // set up interrupt controller - plicinithart(); // ask PLIC for device interrupts - binit(); // buffer cache - iinit(); // inode table - fileinit(); // file table - virtio_disk_init(); // emulated hard disk - userinit(); // first user process - __sync_synchronize(); - started = 1; - } else { - while(started == 0) - ; - __sync_synchronize(); - printf("hart %d starting\n", cpuid()); - kvminithart(); // turn on paging - trapinithart(); // install kernel trap vector - plicinithart(); // ask PLIC for device interrupts - } - - scheduler(); -} diff --git a/kernel/plic.c b/kernel/plic.c deleted file mode 100644 index 4175db9..0000000 --- a/kernel/plic.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "defs.h" - -// -// the riscv Platform Level Interrupt Controller (PLIC). -// - -void -plicinit(void) -{ - // set desired IRQ priorities non-zero (otherwise disabled). - *(uint32*)(PLIC + UART0_IRQ*4) = 1; - *(uint32*)(PLIC + VIRTIO0_IRQ*4) = 1; -} - -void -plicinithart(void) -{ - int hart = cpuid(); - - // set enable bits for this hart's S-mode - // for the uart and virtio disk. - *(uint32*)PLIC_SENABLE(hart) = (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ); - - // set this hart's S-mode priority threshold to 0. - *(uint32*)PLIC_SPRIORITY(hart) = 0; -} - -// ask the PLIC what interrupt we should serve. -int -plic_claim(void) -{ - int hart = cpuid(); - int irq = *(uint32*)PLIC_SCLAIM(hart); - return irq; -} - -// tell the PLIC we've served this IRQ. -void -plic_complete(int irq) -{ - int hart = cpuid(); - *(uint32*)PLIC_SCLAIM(hart) = irq; -} diff --git a/kernel/printf.c b/kernel/printf.c deleted file mode 100644 index 6082602..0000000 --- a/kernel/printf.c +++ /dev/null @@ -1,139 +0,0 @@ -// -// formatted console output -- printf, panic. -// - -#include - -#include "types.h" -#include "param.h" -#include "spinlock.h" -#include "sleeplock.h" -#include "fs.h" -#include "file.h" -#include "memlayout.h" -#include "riscv.h" -#include "defs.h" -#include "proc.h" - -volatile int panicked = 0; - -// lock to avoid interleaving concurrent printf's. -static struct { - struct spinlock lock; - int locking; -} pr; - -static char digits[] = "0123456789abcdef"; - -static void -printint(int xx, int base, int sign) -{ - char buf[16]; - int i; - uint x; - - if(sign && (sign = xx < 0)) - x = -xx; - else - x = xx; - - i = 0; - do { - buf[i++] = digits[x % base]; - } while((x /= base) != 0); - - if(sign) - buf[i++] = '-'; - - while(--i >= 0) - consputc(buf[i]); -} - -static void -printptr(uint64 x) -{ - int i; - consputc('0'); - consputc('x'); - for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4) - consputc(digits[x >> (sizeof(uint64) * 8 - 4)]); -} - -void print(char *raw) { - printf("%s", raw); -} - -// Print to the console. only understands %d, %x, %p, %s. -void -printf(char *fmt, ...) -{ - va_list ap; - int i, c, locking; - char *s; - - locking = pr.locking; - if(locking) - acquire(&pr.lock); - - if (fmt == 0) - panic("null fmt"); - - va_start(ap, fmt); - for(i = 0; (c = fmt[i] & 0xff) != 0; i++){ - if(c != '%'){ - consputc(c); - continue; - } - c = fmt[++i] & 0xff; - if(c == 0) - break; - switch(c){ - case 'd': - printint(va_arg(ap, int), 10, 1); - break; - case 'x': - printint(va_arg(ap, int), 16, 1); - break; - case 'p': - printptr(va_arg(ap, uint64)); - break; - case 's': - if((s = va_arg(ap, char*)) == 0) - s = "(null)"; - for(; *s; s++) - consputc(*s); - break; - case '%': - consputc('%'); - break; - default: - // Print unknown % sequence to draw attention. - consputc('%'); - consputc(c); - break; - } - } - va_end(ap); - - if(locking) - release(&pr.lock); -} - -void -panic(char *s) -{ - pr.locking = 0; - printf("panic: "); - printf(s); - printf("\n"); - panicked = 1; // freeze uart output from other CPUs - for(;;) - ; -} - -void -printfinit(void) -{ - initlock(&pr.lock, "pr"); - pr.locking = 1; -} diff --git a/kernel/proc.c b/kernel/proc.c index 4d9f501..959d7db 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -16,7 +16,7 @@ int nextpid = 1; struct spinlock pid_lock; extern void forkret(void); -static void freeproc(struct proc *p); +void freeproc(struct proc *p); extern char trampoline[]; // trampoline.S @@ -58,56 +58,11 @@ procinit(void) } } -// Must be called with interrupts disabled, -// to prevent race with process being moved -// to a different CPU. -// int -// cpuid() -// { -// int id = r_tp(); -// return id; -// } - -// Return this CPU's cpu struct. -// Interrupts must be disabled. -struct cpu* -mycpu(void) -{ - int id = cpuid(); - struct cpu *c = &cpus[id]; - return c; -} - -// Return the current struct proc *, or zero if none. -struct proc* -myproc(void) -{ - push_off(); - struct cpu *c = mycpu(); - struct proc *p = c->proc; - pop_off(); - return p; -} - -int -allocpid() -{ - int pid; - - acquire(&pid_lock); - pid = nextpid; - nextpid = nextpid + 1; - release(&pid_lock); - - return pid; -} - // Look in the process table for an UNUSED proc. // If found, initialize state required to run in the kernel, // and return with p->lock held. // If there are no free procs, or a memory allocation fails, return 0. -static struct proc* -allocproc(void) +struct proc* allocproc(void) { struct proc *p; @@ -149,28 +104,6 @@ found: return p; } -// free a proc structure and the data hanging from it, -// including user pages. -// p->lock must be held. -static void -freeproc(struct proc *p) -{ - if(p->trapframe) - kfree((void*)p->trapframe); - p->trapframe = 0; - if(p->pagetable) - proc_freepagetable(p->pagetable, p->sz); - p->pagetable = 0; - p->sz = 0; - p->pid = 0; - p->parent = 0; - p->name[0] = 0; - p->chan = 0; - p->killed = 0; - p->xstate = 0; - p->state = UNUSED; -} - // Create a user page table for a given process, with no user memory, // but with trampoline and trapframe pages. pagetable_t @@ -256,23 +189,7 @@ userinit(void) // Grow or shrink user memory by n bytes. // Return 0 on success, -1 on failure. -int -growproc(int n) -{ - uint64 sz; - struct proc *p = myproc(); - - sz = p->sz; - if(n > 0){ - if((sz = uvmalloc(p->pagetable, sz, sz + n, PTE_W)) == 0) { - return -1; - } - } else if(n < 0){ - sz = uvmdealloc(p->pagetable, sz, sz + n); - } - p->sz = sz; - return 0; -} +int growproc(int n); // Create a new process, copying the parent. // Sets up child kernel stack to return as if from fork() system call. @@ -327,18 +244,7 @@ fork(void) // Pass p's abandoned children to init. // Caller must hold wait_lock. -void -reparent(struct proc *p) -{ - struct proc *pp; - - for(pp = proc; pp < &proc[NPROC]; pp++){ - if(pp->parent == p){ - pp->parent = initproc; - wakeup(initproc); - } - } -} +void reparent(struct proc *p); // Exit the current process. Does not return. // An exited process remains in the zombie state @@ -478,36 +384,10 @@ scheduler(void) // be proc->intena and proc->noff, but that would // break in the few places where a lock is held but // there's no process. -void -sched(void) -{ - int intena; - struct proc *p = myproc(); - - if(!holding(&p->lock)) - panic("sched p->lock"); - if(mycpu()->noff != 1) - panic("sched locks"); - if(p->state == RUNNING) - panic("sched running"); - if(intr_get()) - panic("sched interruptible"); - - intena = mycpu()->intena; - swtch(&p->context, &mycpu()->context); - mycpu()->intena = intena; -} +void sched(void); // Give up the CPU for one scheduling round. -void -yield(void) -{ - struct proc *p = myproc(); - acquire(&p->lock); - p->state = RUNNABLE; - sched(); - release(&p->lock); -} +void yield(void); // A fork child's very first scheduling by scheduler() // will swtch to forkret. @@ -532,39 +412,11 @@ forkret(void) // Atomically release lock and sleep on chan. // Reacquires lock when awakened. -void -sleep(void *chan, struct spinlock *lk) -{ - struct proc *p = myproc(); - - // Must acquire p->lock in order to - // change p->state and then call sched. - // Once we hold p->lock, we can be - // guaranteed that we won't miss any wakeup - // (wakeup locks p->lock), - // so it's okay to release lk. - - acquire(&p->lock); //DOC: sleeplock1 - release(lk); - - // Go to sleep. - p->chan = chan; - p->state = SLEEPING; - - sched(); - - // Tidy up. - p->chan = 0; - - // Reacquire original lock. - release(&p->lock); - acquire(lk); -} +void sleep(void *chan, struct spinlock *lk); // Wake up all processes sleeping on chan. // Must be called without any p->lock. -void -wakeup(void *chan) +void wakeup(void *chan) { struct proc *p; @@ -582,45 +434,9 @@ wakeup(void *chan) // Kill the process with the given pid. // The victim won't exit until it tries to return // to user space (see usertrap() in trap.c). -int -kill(int pid) -{ - struct proc *p; - - for(p = proc; p < &proc[NPROC]; p++){ - acquire(&p->lock); - if(p->pid == pid){ - p->killed = 1; - if(p->state == SLEEPING){ - // Wake process from sleep(). - p->state = RUNNABLE; - } - release(&p->lock); - return 0; - } - release(&p->lock); - } - return -1; -} - -void -setkilled(struct proc *p) -{ - acquire(&p->lock); - p->killed = 1; - release(&p->lock); -} - -int -killed(struct proc *p) -{ - int k; - - acquire(&p->lock); - k = p->killed; - release(&p->lock); - return k; -} +int kill(int pid); +void setkilled(struct proc *p); +int killed(struct proc *p); // Copy to either a user address, or kernel address, // depending on usr_dst. @@ -655,8 +471,7 @@ either_copyin(void *dst, int user_src, uint64 src, uint64 len) // Print a process listing to console. For debugging. // Runs when user types ^P on console. // No lock to avoid wedging a stuck machine further. -void -procdump(void) +void procdump(void) { static char *states[] = { [UNUSED] "unused", @@ -669,7 +484,7 @@ procdump(void) struct proc *p; char *state; - printf("\n"); + printstr("\n"); for(p = proc; p < &proc[NPROC]; p++){ if(p->state == UNUSED) continue; @@ -677,7 +492,12 @@ procdump(void) state = states[p->state]; else state = "???"; - printf("%d %s %s", p->pid, state, p->name); - printf("\n"); + + printint(p->pid); + printstr(" "); + printstr(state); + printstr(" "); + printstr(p->name); + printstr("\n"); } } diff --git a/kernel/proc.h b/kernel/proc.h index e022685..03fe4e1 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -107,3 +107,4 @@ struct proc { }; int cpuid(); +int allocpid(); diff --git a/kernel/ramdisk.c b/kernel/ramdisk.c index eb60ee7..8b215ee 100644 --- a/kernel/ramdisk.c +++ b/kernel/ramdisk.c @@ -12,10 +12,7 @@ #include "fs.h" #include "buf.h" -void -ramdiskinit(void) -{ -} +void ramdiskinit(void); // If B_DIRTY is set, write buf to disk, clear B_DIRTY, set B_VALID. // Else if B_VALID is not set, read buf from disk, set B_VALID. diff --git a/kernel/riscv.c b/kernel/riscv.c deleted file mode 100644 index af351d5..0000000 --- a/kernel/riscv.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "kernel/riscv.h" - -unsigned long rv_r_mhartid() { - return r_mhartid(); -} - -unsigned int rv_r_tp() { - return r_tp(); -} - -unsigned long rv_r_sstatus() { - return r_sstatus(); -} - -void rv_w_sstatus(unsigned long x) { - w_sstatus(x); -} - -void rv_intr_on() { - intr_on(); -} - -void rv_intr_off() { - intr_off(); -} - -int rv_intr_get() { - return intr_get(); -} - diff --git a/kernel/rustkernel/Cargo.lock b/kernel/rustkernel/Cargo.lock index 1a24024..9bafc96 100644 --- a/kernel/rustkernel/Cargo.lock +++ b/kernel/rustkernel/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "format_no_std" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f6ab8bf81c8fc5032d2d3e651eb5fda8e225e278f1bc7abeda0a258b7ab5eff" + [[package]] name = "libc" version = "0.2.149" @@ -12,5 +24,7 @@ checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" name = "rustkernel" version = "0.1.0" dependencies = [ + "arrayvec", + "format_no_std", "libc", ] diff --git a/kernel/rustkernel/Cargo.toml b/kernel/rustkernel/Cargo.toml index df72791..d7718bc 100644 --- a/kernel/rustkernel/Cargo.toml +++ b/kernel/rustkernel/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +arrayvec = { version = "0.7.4", default-features = false } +format_no_std = "1.0.0" libc = { version = "0.2.149", default-features = false } [lib] diff --git a/kernel/rustkernel/src/buf.rs b/kernel/rustkernel/src/buf.rs new file mode 100644 index 0000000..05e38e4 --- /dev/null +++ b/kernel/rustkernel/src/buf.rs @@ -0,0 +1,16 @@ +use crate::{fs::BSIZE, sleeplock::Sleeplock}; + +#[repr(C)] +pub struct Buf { + /// Has data been read from disk? + pub valid: i32, + /// Does disk "own" buf? + pub disk: i32, + pub dev: u32, + pub blockno: u32, + pub lock: Sleeplock, + pub refcnt: u32, + pub prev: *mut Buf, + pub next: *mut Buf, + pub data: [u8; BSIZE as usize], +} diff --git a/kernel/rustkernel/src/console.rs b/kernel/rustkernel/src/console.rs new file mode 100644 index 0000000..48cba15 --- /dev/null +++ b/kernel/rustkernel/src/console.rs @@ -0,0 +1,225 @@ +//! Console input and output, to the uart. +// +// Reads are a line at a time. +// Implements special input characters: +// - newline: end of line +// - ctrl-h: backspace +// - ctrl-u: kill line +// - ctrl-d: end of file +// - ctrl-p: print process list + +use crate::{ + file::{devsw, CONSOLE}, + proc::{killed, myproc, sleep}, + spinlock::{initlock, Spinlock}, + uart::{uartinit, uartputc, uartputc_sync}, +}; +use core::{ + ffi::{c_void, CStr}, + ptr::addr_of_mut, +}; + +extern "C" { + fn either_copyin(dst: *mut c_void, user_src: i32, src: u64, len: u64) -> i32; + fn either_copyout(user_dst: i32, dst: u64, src: *mut c_void, len: u64) -> i32; + + pub fn consoleintr(c: i32); + fn wakeup(chan: *mut c_void); + fn procdump(); +} + +pub const BACKSPACE: i32 = 0x100; +pub const INPUT_BUF_SIZE: u64 = 128; + +#[no_mangle] +pub static mut cons: Console = Console { + lock: unsafe { Spinlock::uninitialized() }, + buffer: [0u8; INPUT_BUF_SIZE as usize], + read_index: 0, + write_index: 0, + edit_index: 0, +}; + +/// ctrl-x +fn ctrl_x(x: char) -> char { + ((x as u8) - b'@') as char +} + +/// Send one character to the UART. +/// +/// Called by printf(), and to echo input +/// characters but not from write(). +#[no_mangle] +pub unsafe extern "C" fn consputc(c: i32) { + if c == BACKSPACE { + // If the user typed backspace, overwrite with a space. + uartputc_sync('\x08' as i32); + uartputc_sync(' ' as i32); + uartputc_sync('\x08' as i32); + } else { + uartputc_sync(c); + } +} + +#[repr(C)] +pub struct Console { + pub lock: Spinlock, + pub buffer: [u8; INPUT_BUF_SIZE as usize], + pub read_index: u32, + pub write_index: u32, + pub edit_index: u32, +} + +/// User write()s to the console go here. +#[no_mangle] +pub unsafe extern "C" fn consolewrite(user_src: i32, src: u64, n: i32) -> i32 { + for i in 0..n { + let mut c = 0i8; + + if either_copyin(addr_of_mut!(c).cast(), user_src, src + i as u64, 1) == -1 { + return i; + } else { + uartputc(c as i32); + } + } + 0 +} + +/// User read()s from the console go here. +/// +/// Copy (up to) a whole input line to dst. +/// user_dst indicates whether dst is a user +/// or kernel address. +#[no_mangle] +pub unsafe extern "C" fn consoleread(user_dst: i32, mut dst: u64, mut n: i32) -> i32 { + let target = n; + let mut c; + let mut cbuf; + + cons.lock.lock(); + + while n > 0 { + // Wait until interrupt handler has put + // some input into cons.buffer. + while cons.read_index == cons.write_index { + if killed(myproc()) != 0 { + cons.lock.unlock(); + return -1; + } + sleep( + addr_of_mut!(cons.read_index).cast(), + addr_of_mut!(cons.lock), + ); + } + + c = cons.buffer[(cons.read_index % INPUT_BUF_SIZE as u32) as usize]; + cons.read_index += 1; + + // ctrl-D or EOF + if c == ctrl_x('D') as u8 { + if n < target { + // Save ctrl-D for next time, to make + // sure caller gets a 0-byte result. + cons.read_index -= 1; + } + break; + } + + // Copy the input byte to the user-space buffer. + cbuf = c; + if either_copyout(user_dst, dst, addr_of_mut!(cbuf).cast(), 1) == -1 { + break; + } + + dst += 1; + n -= 1; + + if c == b'\n' { + // A whole line has arrived, + // return to the user-level read(). + break; + } + } + + cons.lock.unlock(); + + target - n +} + +// /// The console input interrupt handler. +// /// +// /// uartintr() calls this for input character. +// /// Do erase/kill processing, then append to cons.buf. +// /// Wake up consoleread() if a whole line has arrived. +// #[no_mangle] +// pub unsafe extern "C" fn consoleintr(c: i32) { +// cons.lock.lock(); +// +// let ctrl_p = ctrl_x('P') as u8 as i8 as i32; +// let ctrl_u = ctrl_x('P') as u8 as i8 as i32; +// let ctrl_h = ctrl_x('P') as u8 as i8 as i32; +// let ctrl_d = ctrl_x('D') as u8 as i8 as i32; +// let cr = '\r' as u8 as i8 as i32; +// let nl = '\n' as u8 as i8 as i32; +// +// match c { +// // Print process list. +// ctrl_p => procdump(), +// // Kill line +// ctrl_u => { +// while cons.edit_index != cons.write_index +// && cons.buffer[((cons.edit_index - 1) % INPUT_BUF_SIZE as u32) as usize] +// != '\n' as u8 +// { +// cons.edit_index -= 1; +// consputc(BACKSPACE); +// } +// } +// // Backspace +// ctrl_h => { +// if cons.edit_index != cons.write_index { +// cons.edit_index -= 1; +// consputc(BACKSPACE); +// } +// } +// c => { +// if cons.edit_index - cons.read_index < INPUT_BUF_SIZE as u32 { +// let c = if c == cr { nl } else { c }; +// +// // Echo back to the user. +// consputc(c); +// +// // Store for consumption by consoleread(). +// cons.buffer[(cons.edit_index % INPUT_BUF_SIZE as u32) as usize] = c as i8 as u8; +// cons.edit_index += 1; +// +// if c == nl +// || c == ctrl_d +// || cons.edit_index - cons.read_index == INPUT_BUF_SIZE as u32 +// { +// // Wake up consoleread() if a whole line (or EOF) has arrived. +// cons.write_index = cons.edit_index; +// wakeup(addr_of_mut!(cons.read_index).cast()); +// } +// } +// } +// } +// +// cons.lock.unlock(); +// } + +pub unsafe fn consoleinit() { + initlock( + addr_of_mut!(cons.lock), + CStr::from_bytes_with_nul(b"cons\0") + .unwrap() + .as_ptr() + .cast_mut(), + ); + uartinit(); + + // Connect read and write syscalls + // to consoleread and consolewrite. + devsw[CONSOLE].read = consoleread as usize as *const i32; + devsw[CONSOLE].write = consolewrite as usize as *const i32; +} diff --git a/kernel/rustkernel/src/file.rs b/kernel/rustkernel/src/file.rs new file mode 100644 index 0000000..874ea2a --- /dev/null +++ b/kernel/rustkernel/src/file.rs @@ -0,0 +1,11 @@ +#[repr(C)] +pub struct Devsw { + pub read: *const i32, + pub write: *const i32, +} + +extern "C" { + pub static mut devsw: [Devsw; crate::param::NDEV]; +} + +pub const CONSOLE: usize = 1; diff --git a/kernel/rustkernel/src/fs.rs b/kernel/rustkernel/src/fs.rs new file mode 100644 index 0000000..3f47f0a --- /dev/null +++ b/kernel/rustkernel/src/fs.rs @@ -0,0 +1,79 @@ +//! On-disk file system forma. +//! Both the kernel and user programs use this header file. + +// Root inode +pub const ROOTINO: u64 = 1; +/// Block size. +pub const BSIZE: u32 = 1024; + +// Disk layout: +// [ boot block | super block | log | inode blocks | free bit map | data blocks ] +// +// mkfs computes the super block and builds an initial file system. +// The super block describes the disk layout: +#[repr(C)] +pub struct Superblock { + /// Must be FSMAGIC. + pub magic: u32, + /// Size of file system image (blocks). + pub size: u32, + /// Number of data blocks. + pub nblocks: u32, + /// Number of inodes. + pub ninodes: u32, + /// Number of log blocks. + pub nlog: u32, + /// Block number of first log block. + pub logstart: u32, + /// Block number of first inode block. + pub inodestart: u32, + /// Block number of first free map block. + pub bmapstart: u32, +} + +pub const FSMAGIC: u32 = 0x10203040; +pub const NDIRECT: u32 = 12; +pub const NINDIRECT: u32 = BSIZE / core::mem::size_of::() as u32; +pub const MAXFILE: u32 = NDIRECT + NINDIRECT; + +// On-disk inode structure; +#[repr(C)] +pub struct DiskInode { + /// File type. + pub kind: i16, + /// Major device number (T_DEVICE only). + pub major: i16, + /// Minor device number (T_DEVICE only). + pub minor: i16, + /// Number of links to inode in file system. + pub nlink: i16, + /// Size of file (bytes). + pub size: u32, + /// Data block addresses. + pub addrs: [u32; NDIRECT as usize + 1], +} + +/// Inodes per block. +pub const IPB: u32 = BSIZE / core::mem::size_of::() as u32; + +/// Block containing inode i. +pub fn iblock(inode: u32, superblock: &Superblock) -> u32 { + inode / IPB + superblock.inodestart +} + +/// Bitmap bits per block. +pub const BPB: u32 = BSIZE * 8; + +/// Block of free map containing bit for block b. +pub fn bblock(block: u32, superblock: &Superblock) -> u32 { + block / BPB + superblock.bmapstart +} + +/// Directory is a file containing a sequence of DirectoryEntry structures. +pub const DIRSIZ: usize = 14; + +#[repr(C)] +pub struct DirectoryEntry { + pub inum: u16, + pub name: [u8; DIRSIZ], +} diff --git a/kernel/rustkernel/src/kalloc.rs b/kernel/rustkernel/src/kalloc.rs index a4b23bd..5efd7a4 100644 --- a/kernel/rustkernel/src/kalloc.rs +++ b/kernel/rustkernel/src/kalloc.rs @@ -1,6 +1,104 @@ +//! Physical memory allocator, for user processes, +//! kernel stacks, page-table pages, +//! and pipe buffers. Allocates whole 4096-byte pages. + +use crate::{ + riscv::{memlayout::PHYSTOP, pg_round_up, PGSIZE}, + spinlock::Spinlock, + string::memset, +}; +use core::{ + ffi::{c_char, CStr}, + ptr::{addr_of_mut, null_mut}, +}; + extern "C" { - fn kalloc() -> *mut u8; - fn kfree(ptr: *mut u8); + // oh my god this is so stupid why the fuck + // this took me so long to figure out it's 3am rn + // First address after kernel. Defined by kernel.ld. + pub static mut end: [c_char; 0]; +} + +#[no_mangle] +pub static mut kmem: KernelMemory = KernelMemory { + lock: unsafe { Spinlock::uninitialized() }, + freelist: null_mut(), +}; + +#[repr(C)] +pub struct Run { + next: *mut Run, +} +#[repr(C)] +pub struct KernelMemory { + pub lock: Spinlock, + pub freelist: *mut Run, +} + +#[no_mangle] +pub unsafe extern "C" fn kinit() { + kmem.lock = Spinlock::new( + CStr::from_bytes_with_nul(b"kmem\0") + .unwrap() + .as_ptr() + .cast_mut(), + ); + freerange(addr_of_mut!(end).cast(), PHYSTOP as *mut u8) +} + +#[no_mangle] +pub unsafe extern "C" fn freerange(pa_start: *mut u8, pa_end: *mut u8) { + let mut p = pg_round_up(pa_start as usize as u64) as *mut u8; + + while p.add(PGSIZE as usize) <= pa_end { + kfree(p.cast()); + p = p.add(PGSIZE as usize); + } +} + +/// Free the page of physical memory pointed at by pa, +/// which normally should have been returned by a call +/// to kalloc(). The exception is when initializing the +/// allocator - see kinit above. +#[no_mangle] +pub unsafe extern "C" fn kfree(pa: *mut u8) { + if (pa as usize as u64 % PGSIZE) != 0 + || pa <= addr_of_mut!(end) as *mut u8 + || pa >= PHYSTOP as *mut u8 + { + panic!("kfree"); + } + + memset(pa, 0, PGSIZE as u32); + + let run: *mut Run = pa.cast(); + + kmem.lock.lock(); + (*run).next = kmem.freelist; + kmem.freelist = run; + kmem.lock.unlock(); +} + +/// Allocate one 4096-byte page of physical memory. +/// +/// Returns a pointer that the kernel can use. +/// Returns 0 if the memory cannot be allocated. +#[no_mangle] +pub unsafe extern "C" fn kalloc() -> *mut u8 { + kmem.lock.lock(); + + let run = kmem.freelist; + if !run.is_null() { + kmem.freelist = (*run).next; + } + + kmem.lock.unlock(); + + if !run.is_null() { + memset(run.cast(), 0, PGSIZE as u32); + } + + run as *mut u8 } use core::alloc::{GlobalAlloc, Layout}; diff --git a/kernel/rustkernel/src/lib.rs b/kernel/rustkernel/src/lib.rs index 11e2490..cad269f 100644 --- a/kernel/rustkernel/src/lib.rs +++ b/kernel/rustkernel/src/lib.rs @@ -6,38 +6,93 @@ extern crate alloc; extern crate core; -extern "C" { - fn print(message: *const c_char); - fn panic(panic_message: *const c_char) -> !; -} - -mod kalloc; +pub mod buf; +pub mod console; +pub mod file; +pub mod fs; +pub(crate) mod kalloc; pub(crate) mod param; +pub mod printf; pub mod proc; pub(crate) mod riscv; +pub mod sleeplock; pub mod spinlock; +pub mod start; +pub mod string; +pub mod syscall; +pub mod sysproc; +pub mod trap; +pub mod uart; +extern "C" { + // pub fn printfinit(); + // pub fn kinit(); + pub fn kvminit(); + pub fn kvminithart(); + pub fn procinit(); + pub fn binit(); + pub fn iinit(); + pub fn fileinit(); + pub fn virtio_disk_init(); + pub fn userinit(); + pub fn scheduler(); +} + +use crate::{printf::print, proc::cpuid}; use core::ffi::{c_char, CStr}; -pub use proc::*; -pub use spinlock::*; +pub static mut STARTED: bool = false; +pub static mut PANICKED: bool = false; #[no_mangle] -pub extern "C" fn rust_main() { - unsafe { - print( - CStr::from_bytes_with_nul(b"Hello from Rust!\n\0") - .unwrap() - .as_ptr(), - ); +pub unsafe extern "C" fn main() { + if cpuid() == 0 { + console::consoleinit(); + printf::printfinit(); + kalloc::kinit(); + print!("\nxv6 kernel is booting\n"); + kvminit(); + kvminithart(); + procinit(); + trap::trapinit(); + trap::trapinithart(); + riscv::plic::plicinit(); + riscv::plic::plicinithart(); + binit(); + iinit(); + fileinit(); + virtio_disk_init(); + userinit(); + STARTED = true; + } else { + while !STARTED { + core::hint::spin_loop(); + } + kvminithart(); + trap::trapinithart(); + riscv::plic::plicinithart(); } + + scheduler(); } #[panic_handler] -unsafe fn panic_wrapper(_panic_info: &core::panic::PanicInfo) -> ! { - panic( - CStr::from_bytes_with_nul(b"panic from rust\0") - .unwrap_or_default() - .as_ptr(), - ) +fn panic_wrapper(panic_info: &core::panic::PanicInfo) -> ! { + if let Some(s) = panic_info.payload().downcast_ref::<&str>() { + crate::printf::print!("panic: {}\n", s); + } else { + crate::printf::print!("kernel panic\n"); + } + + unsafe { crate::PANICKED = true }; + + loop { + core::hint::spin_loop(); + } +} + +#[no_mangle] +pub unsafe extern "C" fn panic(s: *const c_char) -> ! { + let s = CStr::from_ptr(s).to_str().unwrap_or("panic from c"); + panic!("{}", s); } diff --git a/kernel/rustkernel/src/printf.rs b/kernel/rustkernel/src/printf.rs new file mode 100644 index 0000000..b8ba0d2 --- /dev/null +++ b/kernel/rustkernel/src/printf.rs @@ -0,0 +1,66 @@ +use crate::spinlock::Spinlock; +use core::ffi::{c_char, CStr}; + +pub use crate::panic; + +#[no_mangle] +pub static mut PRINT_LOCK: Spinlock = unsafe { Spinlock::uninitialized() }; + +#[repr(C)] +pub struct PrintLock { + pub lock: Spinlock, + pub locking: i32, +} + +macro_rules! print { + ($($arg:tt)*) => {{ + unsafe { $crate::printf::PRINT_LOCK.lock() }; + + // Allocate a page of memory as the buffer and release it when we're done. + let buf = unsafe { $crate::kalloc::kalloc() as *mut [u8; 4096] }; + + let s: &str = format_no_std::show( + unsafe { buf.as_mut() }.unwrap(), + format_args!($($arg)*), + ).unwrap(); + + for c in s.as_bytes() { + unsafe { $crate::console::consputc(*c as i8 as i32) }; + } + + unsafe { $crate::kalloc::kfree(buf.cast()) }; + unsafe { $crate::printf::PRINT_LOCK.unlock() }; + }}; +} +pub(crate) use print; + +#[no_mangle] +pub extern "C" fn printint(n: i32) { + print!("{}", n); +} + +#[no_mangle] +pub extern "C" fn printhex(n: i32) { + print!("{:0x}", n); +} + +#[no_mangle] +pub extern "C" fn printptr(p: u64) { + print!("{:#018x}", p); +} + +#[no_mangle] +pub unsafe extern "C" fn printstr(s: *const c_char) { + let s = CStr::from_ptr(s).to_str().unwrap_or_default(); + print!("{}", s); +} + +#[no_mangle] +pub unsafe extern "C" fn printfinit() { + PRINT_LOCK = Spinlock::new( + CStr::from_bytes_with_nul(b"pr\0") + .unwrap() + .as_ptr() + .cast_mut(), + ); +} diff --git a/kernel/rustkernel/src/proc.rs b/kernel/rustkernel/src/proc.rs index 08d0e06..d7dcb43 100644 --- a/kernel/rustkernel/src/proc.rs +++ b/kernel/rustkernel/src/proc.rs @@ -1,11 +1,48 @@ +#![allow(clippy::comparison_chain)] + use crate::{ - riscv::{self, Pagetable}, - spinlock::Spinlock, + kalloc::kfree, + param::*, + riscv::{self, Pagetable, PTE_W}, + spinlock::{pop_off, push_off, Spinlock}, }; -use core::ffi::c_char; +use core::{ + ffi::{c_char, c_void}, + ptr::{addr_of_mut, null_mut}, +}; + +extern "C" { + pub static mut cpus: [Cpu; NCPU]; + pub static mut proc: [Proc; NPROC]; + pub static mut initproc: *mut Proc; + pub static mut nextpid: i32; + pub static mut pid_lock: Spinlock; + /// Helps ensure that wakeups of wait()ing + /// parents are not lost. Helps obey the + /// memory model when using p->parent. + /// Must be acquired before any p->lock. + pub static mut wait_lock: Spinlock; + // trampoline.S + pub static mut trampoline: *mut c_char; + + pub fn forkret(); + pub fn fork() -> i32; + pub fn exit(status: i32) -> !; + pub fn wait(addr: u64) -> i32; + pub fn proc_pagetable(p: *mut Proc) -> Pagetable; + pub fn proc_freepagetable(pagetable: Pagetable, sz: u64); + pub fn wakeup(chan: *mut c_void); + pub fn allocproc() -> *mut Proc; + // pub fn freeproc(p: *mut Proc); + pub fn uvmalloc(pagetable: Pagetable, oldsz: u64, newsz: u64, xperm: i32) -> u64; + pub fn uvmdealloc(pagetable: Pagetable, oldsz: u64, newsz: u64) -> u64; + // pub fn sched(); + pub fn swtch(a: *mut Context, b: *mut Context); +} /// Saved registers for kernel context switches. #[repr(C)] +#[derive(Default)] pub struct Context { pub ra: u64, pub sp: u64, @@ -37,6 +74,16 @@ pub struct Cpu { /// Were interrupts enabled before push_off()? pub intena: i32, } +impl Default for Cpu { + fn default() -> Self { + Cpu { + proc: null_mut(), + context: Context::default(), + noff: 0, + intena: 0, + } + } +} /// Per-process data for the trap handling code in trampoline.S. /// @@ -52,6 +99,7 @@ pub struct Cpu { /// return-to-user path via usertrapret() doesn't return through /// the entire kernel call stack. #[repr(C)] +#[derive(Default)] pub struct TrapFrame { /// Kernel page table. pub kernel_satp: u64, @@ -97,7 +145,9 @@ pub struct TrapFrame { } #[repr(C)] +#[derive(PartialEq, Default)] pub enum ProcState { + #[default] Unused, Used, Sleeping, @@ -115,7 +165,7 @@ pub struct Proc { /// Process state pub state: ProcState, /// If non-zero, sleeping on chan - pub chan: *mut u8, + pub chan: *mut c_void, /// If non-zero, have been killed pub killed: i32, /// Exit status to be returned to parent's wait @@ -154,9 +204,252 @@ pub unsafe extern "C" fn cpuid() -> i32 { riscv::r_tp() as i32 } -extern "C" { - // pub fn cpuid() -> i32; - /// Return this CPU's cpu struct. - /// Interrupts must be disabled. - pub fn mycpu() -> *mut Cpu; +/// Return this CPU's cpu struct. +/// Interrupts must be disabled. +#[no_mangle] +pub unsafe extern "C" fn mycpu() -> *mut Cpu { + let id = cpuid(); + addr_of_mut!(cpus[id as usize]) +} + +/// Return the current struct proc *, or zero if none. +#[no_mangle] +pub unsafe extern "C" fn myproc() -> *mut Proc { + push_off(); + let c = mycpu(); + let p = (*c).proc; + pop_off(); + p +} + +#[no_mangle] +pub unsafe extern "C" fn allocpid() -> i32 { + let lock = addr_of_mut!(pid_lock); + (*lock).lock(); + let pid = nextpid; + nextpid += 1; + (*lock).unlock(); + pid +} + +/* + +/// Look in the process table for an UNUSED proc. +/// If found, initialize state required to run in the kernel, +/// and return with p->lock held. If there are no free procs, +/// or a memory allocation fails, return 0. +#[no_mangle] +pub unsafe extern "C" fn allocproc() -> *mut Proc { + for p in &mut proc { + let lock = addr_of_mut!(p.lock); + (*lock).lock(); + + if p.state != ProcState::Unused { + (*lock).unlock(); + continue; + } + + let p = addr_of_mut!(*p); + (*p).pid = allocpid(); + (*p).state = ProcState::Used; + + // Allocate a trapframe page and + // create an empty user page table. + (*p).trapframe = kalloc().cast(); + (*p).pagetable = proc_pagetable(p); + + if (*p).trapframe.is_null() || (*p).pagetable.is_null() { + freeproc(p); + (*p).lock.unlock(); + return null_mut(); + } + + // Set up new context to start executing + // at forkret which returns to user space. + memset(addr_of_mut!((*p).context).cast(), 0, size_of::() as u32); + // TODO: convert fn pointer to u64 + (*p).context.ra = forkret as usize as u64; + (*p).context.sp = (*p).kstack + PGSIZE; + + return p; + } + + null_mut() +} +*/ + +/// Free a proc structure and the data hanging from it, including user pages. +/// p->lock must be held. +#[no_mangle] +pub unsafe extern "C" fn freeproc(p: *mut Proc) { + if !(*p).trapframe.is_null() { + kfree((*p).trapframe.cast()); + } + (*p).trapframe = null_mut(); + if !(*p).pagetable.is_null() { + proc_freepagetable((*p).pagetable, (*p).sz); + } + (*p).pagetable = null_mut(); + (*p).sz = 0; + (*p).pid = 0; + (*p).parent = null_mut(); + (*p).name[0] = 0; + (*p).chan = null_mut(); + (*p).killed = 0; + (*p).xstate = 0; + (*p).state = ProcState::Unused; +} + +// /// Wake up all processes sleeping on chan. +// /// Must be called without any p->lock. +// #[no_mangle] +// pub unsafe extern "C" fn wakeup(chan: *mut c_void) { +// for p in &mut proc { +// let p: *mut Proc = addr_of_mut!(*p); +// +// if p != myproc() { +// (*p).lock.lock(); +// if (*p).state == ProcState::Sleeping && (*p).chan == chan { +// (*p).state = ProcState::Runnable; +// } +// (*p).lock.unlock(); +// } +// } +// } + +/// Pass p's abandoned children to init. +/// Caller must hold wait_lock. +#[no_mangle] +pub unsafe extern "C" fn reparent(p: *mut Proc) { + for pp in proc.iter_mut().map(|p: &mut Proc| addr_of_mut!(*p)) { + if (*pp).parent == p { + (*pp).parent = initproc; + wakeup(initproc.cast()); + } + } +} + +/// Grow or shrink user memory by n bytes. +/// Return 0 on success, -1 on failure. +#[no_mangle] +pub unsafe extern "C" fn growproc(n: i32) -> i32 { + let p = myproc(); + let mut sz = (*p).sz; + + if n > 0 { + sz = uvmalloc((*p).pagetable, sz, sz.wrapping_add(n as u64), PTE_W as i32); + if sz == 0 { + return -1; + } + } else if n < 0 { + sz = uvmdealloc((*p).pagetable, sz, sz.wrapping_add(n as u64)); + } + (*p).sz = sz; + 0 +} + +/// Give up the CPU for one scheduling round. +#[no_mangle] +pub unsafe extern "C" fn r#yield() { + let p = myproc(); + (*p).lock.lock(); + (*p).state = ProcState::Runnable; + sched(); + (*p).lock.unlock(); +} + +/// Switch to scheduler. Must hold only p->lock +/// and have changed proc->state. Saves and restores +/// intena because intena is a property of this +/// kernel thread, not this CPU. It should +/// be proc->intena and proc->noff, but that would +/// break in the few places where a lock is held but +/// there's no process. +#[no_mangle] +pub unsafe extern "C" fn sched() { + let p = myproc(); + let c = mycpu(); + + if !(*p).lock.held_by_current_cpu() { + panic!("sched p->lock"); + } else if (*c).noff != 1 { + panic!("sched locks"); + } else if (*p).state == ProcState::Running { + panic!("sched running"); + } else if riscv::intr_get() > 0 { + panic!("sched interruptible"); + } + + let intena = (*c).intena; + swtch(addr_of_mut!((*p).context), addr_of_mut!((*c).context)); + (*c).intena = intena; +} + +/// Atomically release lock and sleep on chan. +/// Reacquires lock when awakened. +#[no_mangle] +pub unsafe extern "C" fn sleep(chan: *mut c_void, lock: *mut Spinlock) { + let p = myproc(); + + // Must acquire p->lock in order to + // change p->state and then call sched. + // Once we hold p->lock, we can be + // guaranteed that we won't miss any wakeup + // (wakeup locks p->lock), + // so it's okay to release lk. + + (*p).lock.lock(); + (*lock).unlock(); + + // Go to sleep. + (*p).chan = chan; + (*p).state = ProcState::Sleeping; + + sched(); + + // Tidy up. + (*p).chan = null_mut(); + + // Reacquire original lock. + (*p).lock.unlock(); + (*lock).lock(); +} + +/// Kill the process with the given pid. +/// The victim won't exit until it tries to return +/// to user space (see usertrap() in trap.c). +#[no_mangle] +pub unsafe extern "C" fn kill(pid: i32) -> i32 { + for p in &mut proc { + p.lock.lock(); + + if p.pid == pid { + p.killed = 1; + + if p.state == ProcState::Sleeping { + // Wake process from sleep(). + p.state = ProcState::Runnable; + } + + p.lock.unlock(); + return 0; + } + p.lock.unlock(); + } + -1 +} + +#[no_mangle] +pub unsafe extern "C" fn setkilled(p: *mut Proc) { + (*p).lock.lock(); + (*p).killed = 1; + (*p).lock.unlock(); +} + +#[no_mangle] +pub unsafe extern "C" fn killed(p: *mut Proc) -> i32 { + (*p).lock.lock(); + let k = (*p).killed; + (*p).lock.unlock(); + k } diff --git a/kernel/rustkernel/src/ramdisk.rs b/kernel/rustkernel/src/ramdisk.rs new file mode 100644 index 0000000..91e01b9 --- /dev/null +++ b/kernel/rustkernel/src/ramdisk.rs @@ -0,0 +1,8 @@ +//! Ramdisk that uses the disk image loaded by qemu -initrd fs.img + +extern "C" { + pub fn ramdiskrw(buffer: *mut Buf); +} + +#[no_mangle] +pub extern "C" fn ramdiskinit() {} diff --git a/kernel/rustkernel/src/riscv/asm.rs b/kernel/rustkernel/src/riscv/asm.rs index de85023..a91ea95 100644 --- a/kernel/rustkernel/src/riscv/asm.rs +++ b/kernel/rustkernel/src/riscv/asm.rs @@ -159,7 +159,6 @@ pub unsafe fn w_mscratch(x: u64) { asm!("csrw mscratch, {}", in(reg) x); } - // Supervisor Trap Cause #[inline(always)] pub unsafe fn r_scause() -> u64 { diff --git a/kernel/rustkernel/src/riscv/memlayout.rs b/kernel/rustkernel/src/riscv/memlayout.rs new file mode 100644 index 0000000..d0ba291 --- /dev/null +++ b/kernel/rustkernel/src/riscv/memlayout.rs @@ -0,0 +1,84 @@ +// Physical memory layout + +// QEMU -machine virt is setup like this, +// based on QEMU's hw/riscv/virt.c +// +// 00001000 - boot ROM, provided by qemu +// 02000000 - CLINT +// 0C000000 - PLIC +// 10000000 - uart0 +// 10001000 - virtio disk +// 80000000 - boot ROM jumps here in machine mode (kernel loads the kernel here) +// unused after 8000000 + +// The kernel uses physical memory as so: +// 80000000 - entry.S, then kernel text and data +// end - start of kernel page allocation data +// PHYSTOP - end of RAM used by the kernel + +use super::{MAXVA, PGSIZE}; + +// QEMU puts UART registers here in physical memory. +pub const UART0: u64 = 0x10000000; +pub const UART0_IRQ: i32 = 10; + +// Virtio MMIO interface +pub const VIRTIO0: u64 = 0x10001000; +pub const VIRTIO0_IRQ: i32 = 1; + +// Core Local Interrupter (CLINT), which contains the timer. +pub const CLINT: u64 = 0x2000000; +pub const CLINT_MTIME: u64 = CLINT + 0xbff8; +pub fn clint_mtimecmp(hartid: u64) -> u64 { + CLINT + 0x4000 + (8 * hartid) +} + +// QEMU puts platform-level interrupt controller (PLIC) here. +pub const PLIC: u64 = 0x0c000000; +pub const PLIC_PRIORITY: u64 = PLIC; +pub const PLIC_PENDING: u64 = PLIC + 0x1000; +pub fn plic_menable(hartid: u64) -> u64 { + PLIC + 0x2000 + (0x100 * hartid) +} +pub fn plic_senable(hartid: u64) -> u64 { + PLIC + 0x2080 + (0x100 * hartid) +} +pub fn plic_mpriority(hartid: u64) -> u64 { + PLIC + 0x200000 + (0x2000 * hartid) +} +pub fn plic_spriority(hartid: u64) -> u64 { + PLIC + 0x201000 + (0x2000 * hartid) +} +pub fn plic_mclaim(hartid: u64) -> u64 { + PLIC + 0x200004 + (0x2000 * hartid) +} +pub fn plic_sclaim(hartid: u64) -> u64 { + PLIC + 0x201004 + (0x2000 * hartid) +} + +// The kernel expects there to be RAM +// for use by the kernel and user pages +// from physical address 0x80000000 to PHYSTOP. +pub const KERNBASE: u64 = 0x80000000; +pub const PHYSTOP: u64 = KERNBASE + 128 * 1024 * 1024; + +// Map the trampoline page to the highest address, +// in both user and kernel space. +pub const TRAMPOLINE: u64 = MAXVA - PGSIZE; + +// Map kernel stacks beneath the trampoline, +// each surrouned by invalid guard pages. +pub fn kstack(p: u64) -> u64 { + TRAMPOLINE - (p + 1) * 2 * PGSIZE +} + +// User memory layout. +// Address zero first: +// - text +// - original data and bss +// - fixed-size stack +// - expandable heap +// ... +// - TRAPFRAME (p->trapframe, used by the trampoline) +// - TRAMPOLINE (the same page as in the kernel) +pub const TRAPFRAME: u64 = TRAMPOLINE - PGSIZE; diff --git a/kernel/rustkernel/src/riscv/mod.rs b/kernel/rustkernel/src/riscv/mod.rs index b5ab51d..ceea72c 100644 --- a/kernel/rustkernel/src/riscv/mod.rs +++ b/kernel/rustkernel/src/riscv/mod.rs @@ -1,6 +1,9 @@ pub mod asm; +pub mod memlayout; +pub mod plic; pub use asm::*; +pub use memlayout::*; pub type Pte = u64; pub type Pagetable = *mut [Pte; 512]; @@ -45,8 +48,38 @@ pub const PGSIZE: u64 = 4096; /// Bits of offset within a page pub const PGSHIFT: u64 = 12; +pub fn pg_round_up(sz: u64) -> u64 { + (sz + PGSIZE - 1) & !(PGSIZE - 1) +} +pub fn pg_round_down(a: u64) -> u64 { + a & !(PGSIZE - 1) +} + +// Valid. pub const PTE_V: u64 = 1 << 0; pub const PTE_R: u64 = 1 << 1; pub const PTE_W: u64 = 1 << 2; pub const PTE_X: u64 = 1 << 3; +// User can access. pub const PTE_U: u64 = 1 << 4; + +/* +// shift a physical address to the right place for a PTE. +#define PA2PTE(pa) ((((uint64)pa) >> 12) << 10) + +#define PTE2PA(pte) (((pte) >> 10) << 12) + +#define PTE_FLAGS(pte) ((pte) & 0x3FF) + +// extract the three 9-bit page table indices from a virtual address. +#define PXMASK 0x1FF // 9 bits +#define PXSHIFT(level) (PGSHIFT+(9*(level))) +#define PX(level, va) ((((uint64) (va)) >> PXSHIFT(level)) & PXMASK) +*/ + +/// One beyond the highest possible virtual address. +/// +/// MAXVA is actually one bit less than the max allowed by +/// Sv39, to avoid having to sign-extend virtual addresses +/// that have the high bit set. +pub const MAXVA: u64 = 1u64 << (9 + 9 + 9 + 12 - 1); diff --git a/kernel/rustkernel/src/riscv/plic.rs b/kernel/rustkernel/src/riscv/plic.rs new file mode 100644 index 0000000..82419b0 --- /dev/null +++ b/kernel/rustkernel/src/riscv/plic.rs @@ -0,0 +1,39 @@ +//! The RISC-V Platform Level Interrupt Controller (PLIC) + +use crate::{ + proc::cpuid, + riscv::{plic_sclaim, plic_senable, plic_spriority, PLIC, UART0_IRQ, VIRTIO0_IRQ}, +}; + +#[no_mangle] +pub unsafe extern "C" fn plicinit() { + // Set desired IRQ priorities non-zero (otherwise disabled). + *((PLIC + UART0_IRQ as u64 * 4) as *mut u32) = 1; + *((PLIC + VIRTIO0_IRQ as u64 * 4) as *mut u32) = 1; +} + +#[no_mangle] +pub unsafe extern "C" fn plicinithart() { + let hart = cpuid() as u64; + + // Set enable bits for this hart's S-mode + // for the UART and VIRTIO disk. + *(plic_senable(hart) as *mut u32) = (1 << UART0_IRQ) | (1 << VIRTIO0_IRQ); + + // Set this hart's S-mode priority threshold to 0. + *(plic_spriority(hart) as *mut u32) = 0; +} + +/// Ask the PLIC what interrupt we should serve. +#[no_mangle] +pub unsafe extern "C" fn plic_claim() -> i32 { + let hart = cpuid() as u64; + *(plic_sclaim(hart) as *const i32) +} + +/// Tell the PLIC we've served this IRQ. +#[no_mangle] +pub unsafe extern "C" fn plic_complete(irq: i32) { + let hart = cpuid() as u64; + *(plic_sclaim(hart) as *mut i32) = irq; +} diff --git a/kernel/rustkernel/src/sleeplock.rs b/kernel/rustkernel/src/sleeplock.rs new file mode 100644 index 0000000..443e5a3 --- /dev/null +++ b/kernel/rustkernel/src/sleeplock.rs @@ -0,0 +1,53 @@ +use crate::{ + proc::{myproc, sleep, wakeup}, + spinlock::{self, Spinlock}, +}; +use core::{ffi::c_char, ptr::addr_of_mut}; + +#[repr(C)] +pub struct Sleeplock { + pub locked: u32, + pub inner: Spinlock, + pub name: *mut c_char, + pub pid: i32, +} + +#[no_mangle] +pub unsafe extern "C" fn initsleeplock(lock: *mut Sleeplock, name: *mut c_char) { + spinlock::initlock(addr_of_mut!((*lock).inner), name); + (*lock).name = name; + (*lock).locked = 0; + (*lock).pid = 0; +} + +#[no_mangle] +pub unsafe extern "C" fn acquiresleep(lock: *mut Sleeplock) { + (*lock).inner.lock(); + while (*lock).locked > 0 { + sleep(lock.cast(), addr_of_mut!((*lock).inner)); + } + (*lock).locked = 1; + (*lock).pid = (*myproc()).pid; + (*lock).inner.unlock() +} + +#[no_mangle] +pub unsafe extern "C" fn releasesleep(lock: *mut Sleeplock) { + (*lock).inner.lock(); + (*lock).locked = 0; + (*lock).pid = 0; + wakeup(lock.cast()); + (*lock).inner.unlock(); +} + +#[no_mangle] +pub unsafe extern "C" fn holdingsleep(lock: *mut Sleeplock) -> i32 { + (*lock).inner.lock(); + let holding = ((*lock).locked > 0) && ((*lock).pid == (*myproc()).pid); + (*lock).inner.unlock(); + if holding { + 1 + } else { + 0 + } +} diff --git a/kernel/rustkernel/src/spinlock.rs b/kernel/rustkernel/src/spinlock.rs index fc05a48..4c676bf 100644 --- a/kernel/rustkernel/src/spinlock.rs +++ b/kernel/rustkernel/src/spinlock.rs @@ -15,6 +15,13 @@ pub struct Spinlock { pub cpu: *mut Cpu, } impl Spinlock { + pub const unsafe fn uninitialized() -> Spinlock { + Spinlock { + locked: AtomicBool::new(false), + name: null_mut(), + cpu: null_mut(), + } + } /// Initializes a `Spinlock`. pub fn new(name: *mut c_char) -> Spinlock { Spinlock { diff --git a/kernel/rustkernel/src/start.rs b/kernel/rustkernel/src/start.rs new file mode 100644 index 0000000..7f398a4 --- /dev/null +++ b/kernel/rustkernel/src/start.rs @@ -0,0 +1,83 @@ +use crate::{main, param::NCPU, riscv::*}; +use core::{arch::asm, ptr::addr_of}; + +extern "C" { + pub fn timervec(); +} + +#[no_mangle] +pub static mut timer_scratch: [[u64; 5]; NCPU] = [[0u64; 5]; NCPU]; + +// The original C has this aligned to 16 - hopefully that's not a problem. +#[no_mangle] +pub static mut stack0: [u8; 4096 * NCPU] = [0u8; 4096 * NCPU]; + +// entry.S jumps here in machine mode on stack0 +#[no_mangle] +pub unsafe extern "C" fn start() { + // Set M Previous Privilege mode to Supervisor, for mret. + let mut x = r_mstatus(); + x &= !MSTATUS_MPP_MASK; + x |= MSTATUS_MPP_S; + w_mstatus(x); + + // Set M Exception Program Counter to main, for mret. + w_mepc(main as usize as u64); + + // Disable paging for now. + w_satp(0); + + // Delegate all interrupts and exceptions to supervisor mode. + w_medeleg(0xffffu64); + w_mideleg(0xffffu64); + w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE); + + // Configure Physical Memory Protection to give + // supervisor mode access to all of physical memory. + w_pmpaddr0(0x3fffffffffffffu64); + w_pmpcfg0(0xf); + + // Ask for clock interrupts. + timerinit(); + + // Keep each CPU's hartid in its tp register, for cpuid(). + w_tp(r_mhartid()); + + // Switch to supervisor mode and jump to main(). + asm!("mret"); +} + +/// Arrange to receive timer interrupts. +/// +/// They will arrive in machine mode at +/// at timervec in kernelvec.S, +/// which turns them into software interrupts for +/// devintr() in trap.c. +#[no_mangle] +pub unsafe extern "C" fn timerinit() { + // Each CPU has a separate source of timer interrupts. + let id = r_mhartid(); + + // Ask the CLINT for a timer interrupt. + // cycles, about 1/10th second in qemu + let interval = 1_000_000u64; + *(clint_mtimecmp(id) as *mut u64) = *(CLINT_MTIME as *const u64) + interval; + + // Prepare information in scratch[] for timervec. + // scratch[0..=2]: Space for timervec to save registers. + // scratch[3]: Address of CLINT MTIMECMP register. + // scratch[4]: Desired interval (in cycles) between timer interrupts. + let scratch: &mut [u64; 5] = &mut timer_scratch[id as usize]; + scratch[3] = clint_mtimecmp(id); + scratch[4] = interval; + w_mscratch(addr_of!(scratch[0]) as usize as u64); + + // Set the machine-mode trap handler. + w_mtvec(timervec as usize as u64); + + // Enable machine-mode interrupts. + w_mstatus(r_mstatus() | MSTATUS_MIE); + + // Enable machine-mode timer interrupts. + w_mie(r_mie() | MIE_MTIE); +} diff --git a/kernel/rustkernel/src/string.rs b/kernel/rustkernel/src/string.rs new file mode 100644 index 0000000..40c09e1 --- /dev/null +++ b/kernel/rustkernel/src/string.rs @@ -0,0 +1,133 @@ +use core::{ffi::c_char, option::Option}; + +#[no_mangle] +pub unsafe extern "C" fn memset(dst: *mut u8, data: i32, max_bytes: u32) -> *mut u8 { + for i in 0..max_bytes { + *dst.add(i as usize) = data as u8; + } + dst +} + +#[no_mangle] +pub unsafe extern "C" fn memcmp(mut a: *const u8, mut b: *const u8, max_bytes: u32) -> i32 { + for _ in 0..max_bytes { + if *a != *b { + return (*a - *b) as i32; + } else { + a = a.add(1); + b = b.add(1); + } + } + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn memmove(mut dst: *mut u8, mut src: *const u8, max_bytes: u32) -> *mut u8 { + if max_bytes == 0 { + return dst; + } + + // If src starts before dst and src + max_bytes + // is after d, the memory regions overlap. + if src < dst && src.add(max_bytes as usize) > dst { + dst = dst.add(max_bytes as usize); + src = src.add(max_bytes as usize); + + for _ in 0..max_bytes { + dst = dst.sub(1); + src = src.sub(1); + *dst = *src; + } + } else { + for _ in 0..max_bytes { + *dst = *src; + dst = dst.add(1); + src = src.add(1); + } + } + + dst +} + +#[no_mangle] +pub unsafe extern "C" fn memcpy(dst: *mut u8, src: *const u8, max_bytes: u32) -> *mut u8 { + memmove(dst, src, max_bytes) +} + +pub(crate) unsafe fn strlen_checked(s: *const c_char, max_chars: usize) -> Option { + for len in 0..max_chars { + if (*s.add(len)) == '\0' as i8 { + return Some(len.try_into().unwrap_or(i32::MAX)); + } + } + None +} + +#[no_mangle] +pub unsafe extern "C" fn strlen(s: *const c_char) -> i32 { + strlen_checked(s, usize::MAX).unwrap_or(i32::MAX) +} + +#[no_mangle] +pub unsafe extern "C" fn strncmp(mut a: *const u8, mut b: *const u8, mut max_chars: u32) -> i32 { + while max_chars > 0 && *a != 0 && *a == *b { + max_chars -= 1; + a = a.add(1); + b = b.add(1); + } + if max_chars == 0 { + 0 + } else { + (*a - *b) as i32 + } +} + +#[no_mangle] +pub unsafe extern "C" fn strncpy( + mut a: *mut u8, + mut b: *const u8, + mut max_chars: i32, +) -> *const u8 { + let original_a = a; + while max_chars > 0 && *b != 0 { + *a = *b; + max_chars -= 1; + a = a.add(1); + b = b.add(1); + } + + while max_chars > 0 { + *a = 0; + max_chars -= 1; + a = a.add(1); + } + + original_a +} + +/// Like strncpy but guaranteed to null-terminate. +#[no_mangle] +pub unsafe extern "C" fn safestrcpy( + mut a: *mut u8, + mut b: *const u8, + mut max_chars: i32, +) -> *const u8 { + let original_a = a; + + if max_chars <= 0 { + return a; + } else { + max_chars -= 1; + } + + while max_chars > 0 && *b != 0 { + *a = *b; + max_chars -= 1; + a = a.add(1); + b = b.add(1); + } + + *a = 0; + + original_a +} diff --git a/kernel/rustkernel/src/syscall.rs b/kernel/rustkernel/src/syscall.rs new file mode 100644 index 0000000..563aac5 --- /dev/null +++ b/kernel/rustkernel/src/syscall.rs @@ -0,0 +1,231 @@ +use crate::{printf::print, proc::myproc, riscv::Pagetable, string::strlen, sysproc}; +use core::{mem::size_of, ptr::addr_of_mut}; + +extern "C" { + fn copyin(pagetable: Pagetable, dst: *mut u8, srcva: u64, len: u64) -> i32; + fn copyinstr(pagetable: Pagetable, dst: *mut u8, srcva: u64, len: u64) -> i32; + // fn syscall(); + fn sys_pipe() -> u64; + fn sys_read() -> u64; + fn sys_exec() -> u64; + fn sys_fstat() -> u64; + fn sys_chdir() -> u64; + fn sys_dup() -> u64; + fn sys_open() -> u64; + fn sys_write() -> u64; + fn sys_mknod() -> u64; + fn sys_unlink() -> u64; + fn sys_link() -> u64; + fn sys_mkdir() -> u64; + fn sys_close() -> u64; +} + +pub enum Syscall { + Fork, + Exit, + Wait, + Pipe, + Read, + Kill, + Exec, + Fstat, + Chdir, + Dup, + Getpid, + Sbrk, + Sleep, + Uptime, + Open, + Write, + Mknod, + Unlink, + Link, + Mkdir, + Close, +} +impl Syscall { + pub unsafe fn call(&self) -> u64 { + match self { + Syscall::Fork => sysproc::sys_fork(), + Syscall::Exit => sysproc::sys_exit(), + Syscall::Wait => sysproc::sys_wait(), + Syscall::Pipe => sys_pipe(), + Syscall::Read => sys_read(), + Syscall::Kill => sysproc::sys_kill(), + Syscall::Exec => sys_exec(), + Syscall::Fstat => sys_fstat(), + Syscall::Chdir => sys_chdir(), + Syscall::Dup => sys_dup(), + Syscall::Getpid => sysproc::sys_getpid(), + Syscall::Sbrk => sysproc::sys_sbrk(), + Syscall::Sleep => sysproc::sys_sleep(), + Syscall::Uptime => sysproc::sys_uptime(), + Syscall::Open => sys_open(), + Syscall::Write => sys_write(), + Syscall::Mknod => sys_mknod(), + Syscall::Unlink => sys_unlink(), + Syscall::Link => sys_link(), + Syscall::Mkdir => sys_mkdir(), + Syscall::Close => sys_close(), + } + } +} +impl TryFrom for Syscall { + type Error = (); + + fn try_from(value: usize) -> core::result::Result { + match value { + 1 => Ok(Syscall::Fork), + 2 => Ok(Syscall::Exit), + 3 => Ok(Syscall::Wait), + 4 => Ok(Syscall::Pipe), + 5 => Ok(Syscall::Read), + 6 => Ok(Syscall::Kill), + 7 => Ok(Syscall::Exec), + 8 => Ok(Syscall::Fstat), + 9 => Ok(Syscall::Chdir), + 10 => Ok(Syscall::Dup), + 11 => Ok(Syscall::Getpid), + 12 => Ok(Syscall::Sbrk), + 13 => Ok(Syscall::Sleep), + 14 => Ok(Syscall::Uptime), + 15 => Ok(Syscall::Open), + 16 => Ok(Syscall::Write), + 17 => Ok(Syscall::Mknod), + 18 => Ok(Syscall::Unlink), + 19 => Ok(Syscall::Link), + 20 => Ok(Syscall::Mkdir), + 21 => Ok(Syscall::Close), + _ => Err(()), + } + } +} +impl From for usize { + fn from(syscall: Syscall) -> usize { + match syscall { + Syscall::Fork => 1, + Syscall::Exit => 2, + Syscall::Wait => 3, + Syscall::Pipe => 4, + Syscall::Read => 5, + Syscall::Kill => 6, + Syscall::Exec => 7, + Syscall::Fstat => 8, + Syscall::Chdir => 9, + Syscall::Dup => 10, + Syscall::Getpid => 11, + Syscall::Sbrk => 12, + Syscall::Sleep => 13, + Syscall::Uptime => 14, + Syscall::Open => 15, + Syscall::Write => 16, + Syscall::Mknod => 17, + Syscall::Unlink => 18, + Syscall::Link => 19, + Syscall::Mkdir => 20, + Syscall::Close => 21, + } + } +} + +/// Fetch the u64 at addr from the current process. +#[no_mangle] +pub unsafe extern "C" fn fetchaddr(addr: u64, ip: *mut u64) -> i32 { + let p = myproc(); + + // Both tests needed, in case of overflow. + if addr >= (*p).sz + || addr + size_of::() as u64 > (*p).sz + || copyin( + (*p).pagetable, + ip.cast(), + addr, + size_of::<*mut u64>() as u64, + ) != 0 + { + -1 + } else { + 0 + } +} + +/// Fetch the null-terminated string at addr from the current process. +/// +/// Returns length of string, not including null, or -1 for error. +#[no_mangle] +pub unsafe extern "C" fn fetchstr(addr: u64, buf: *mut u8, max: i32) -> i32 { + let p = myproc(); + if copyinstr((*p).pagetable, buf, addr, max as u64) < 0 { + -1 + } else { + strlen(buf.cast()) + } +} + +#[no_mangle] +pub unsafe extern "C" fn argraw(n: i32) -> u64 { + let p = myproc(); + match n { + 0 => (*(*p).trapframe).a0, + 1 => (*(*p).trapframe).a1, + 2 => (*(*p).trapframe).a2, + 3 => (*(*p).trapframe).a3, + 4 => (*(*p).trapframe).a4, + 5 => (*(*p).trapframe).a5, + _ => panic!("argraw"), + } +} + +/// Fetch the n-th 32-bit syscall argument. +#[no_mangle] +pub unsafe extern "C" fn argint(n: i32, ip: *mut i32) { + *ip = argraw(n) as i32; +} + +/// Retrieve an argument as a pointer. +/// +/// Doesn't check for legality, since +/// copyin/copyout will do that. +#[no_mangle] +pub unsafe extern "C" fn argaddr(n: i32, ip: *mut u64) { + *ip = argraw(n); +} + +/// Fetch the n-th word-sized syscall argument as a null-terminated string. +/// +/// Copies into buf, at most max. +/// Returns string length if ok (including null), -1 if error. +#[no_mangle] +pub unsafe extern "C" fn argstr(n: i32, buf: *mut u8, max: i32) -> i32 { + let mut addr = 0u64; + argaddr(n, addr_of_mut!(addr)); + fetchstr(addr, buf, max) +} + +#[no_mangle] +pub unsafe extern "C" fn syscall() { + let p = myproc(); + let num = (*(*p).trapframe).a7; + + // print!("syscall {}\n", num); + + (*(*p).trapframe).a0 = match TryInto::::try_into(num as usize) { + Ok(syscall) => syscall.call(), + Err(_) => { + print!("{} unknown syscall {}\n", (*p).pid, num); + -1i64 as u64 + } + }; +} + +// #[no_mangle] +// pub unsafe extern "C" fn rust_syscall(num: u64) -> u64 { +// match TryInto::::try_into(num as usize) { +// Ok(syscall) => syscall.call(), +// Err(_) => { +// print!("unknown syscall {}\n", num); +// -1i64 as u64 +// } +// } +// } +// diff --git a/kernel/rustkernel/src/sysproc.rs b/kernel/rustkernel/src/sysproc.rs new file mode 100644 index 0000000..68873bb --- /dev/null +++ b/kernel/rustkernel/src/sysproc.rs @@ -0,0 +1,79 @@ +use crate::{ + proc::{exit, fork, growproc, kill, killed, myproc, sleep, wait}, + syscall::{argaddr, argint}, +}; +use core::ptr::addr_of_mut; + +#[no_mangle] +pub unsafe extern "C" fn sys_exit() -> u64 { + let mut n = 0i32; + argint(0, addr_of_mut!(n)); + exit(n) +} + +#[no_mangle] +pub unsafe extern "C" fn sys_getpid() -> u64 { + (*myproc()).pid as u64 +} + +#[no_mangle] +pub unsafe extern "C" fn sys_fork() -> u64 { + fork() as u64 +} + +#[no_mangle] +pub unsafe extern "C" fn sys_wait() -> u64 { + let mut p = 0u64; + argaddr(0, addr_of_mut!(p)); + wait(p) as u64 +} + +#[no_mangle] +pub unsafe extern "C" fn sys_sbrk() -> u64 { + let mut n = 0i32; + argint(0, addr_of_mut!(n)); + let addr = (*myproc()).sz; + + if growproc(n) < 0 { + -1i64 as u64 + } else { + addr + } +} + +#[no_mangle] +pub unsafe extern "C" fn sys_sleep() -> u64 { + let mut n = 0i32; + argint(0, addr_of_mut!(n)); + + crate::trap::tickslock.lock(); + let ticks = crate::trap::ticks; + while crate::trap::ticks < ticks + n as u32 { + if killed(myproc()) > 0 { + crate::trap::tickslock.unlock(); + return -1i64 as u64; + } + sleep( + addr_of_mut!(crate::trap::ticks).cast(), + addr_of_mut!(crate::trap::tickslock).cast(), + ) + } + crate::trap::tickslock.unlock(); + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn sys_kill() -> u64 { + let mut pid = 0i32; + argint(0, addr_of_mut!(pid)); + kill(pid) as u64 +} + +/// Returns how many clock tick interrupts have occurred since start. +#[no_mangle] +pub unsafe extern "C" fn sys_uptime() -> u64 { + crate::trap::tickslock.lock(); + let ticks = crate::trap::ticks; + crate::trap::tickslock.unlock(); + ticks as u64 +} diff --git a/kernel/rustkernel/src/trap.rs b/kernel/rustkernel/src/trap.rs new file mode 100644 index 0000000..b217187 --- /dev/null +++ b/kernel/rustkernel/src/trap.rs @@ -0,0 +1,144 @@ +use crate::{ + printf::print, + proc::{cpuid, wakeup}, + riscv::*, + spinlock::Spinlock, +}; +use core::{ffi::CStr, ptr::addr_of_mut}; + +extern "C" { + pub fn kernelvec(); + pub fn usertrap(); + pub fn usertrapret(); + fn syscall(); + fn virtio_disk_intr(); +} + +#[no_mangle] +pub static mut tickslock: Spinlock = unsafe { Spinlock::uninitialized() }; +#[no_mangle] +pub static mut ticks: u32 = 0; + +#[no_mangle] +pub unsafe extern "C" fn trapinit() { + tickslock = Spinlock::new( + CStr::from_bytes_with_nul(b"time\0") + .unwrap() + .as_ptr() + .cast_mut(), + ); +} + +/// Set up to take exceptions and traps while in the kernel. +#[no_mangle] +pub unsafe extern "C" fn trapinithart() { + w_stvec(kernelvec as usize as u64); +} + +#[no_mangle] +pub unsafe extern "C" fn clockintr() { + tickslock.lock(); + ticks += 1; + wakeup(addr_of_mut!(ticks).cast()); + tickslock.unlock(); +} + +// /// Handle an interrupt, exception, or syscall from user space. +// /// Called from trampoline.S. +// #[no_mangle] +// pub unsafe extern "C" fn usertrap() { +// if r_sstatus() & SSTATUS_SPP != 0 { +// panic!("usertrap: not from user mode"); +// } +// +// // Send interrupts and exceptions to kerneltrap(), +// // since we're now in the kernel. +// w_stvec(kernelvec as usize as u64); +// +// let p = myproc(); +// +// // Save user program counter. +// (*(*p).trapframe).epc = r_sepc(); +// +// if r_scause() == 8 { +// // Syscall +// +// if killed(p) > 0 { +// exit(-1); +// } +// +// // sepc points to the ecall instruction, +// // but we want to return to the next instruction. +// (*(*p).trapframe).epc += 4; +// +// // An interrupt will change sepc, scause, and sstatus, +// // so enable only now that we're done with those registers. +// intr_on(); +// +// syscall(); +// } else { +// let which_dev = devintr(); +// if which_dev == 0 { +// print!("usertrap(): unexpected scause {:#018x} pid={}\n", r_scause(), (*p).pid); +// print!(" sepc={:#018x} stval={:#018x}\n", r_sepc(), r_stval()); +// setkilled(p); +// } +// if killed(p) > 0 { +// exit(-1); +// } +// +// // Give up the CPU if this is a timer interrupt. +// if which_dev == 2 { +// r#yield(); +// } +// +// usertrapret(); +// } +// } + +/// Check if it's an external interrupt or software interrupt and handle it. +/// +/// Returns 2 if timer interrupt, 1 if other device, 0 if not recognized. +#[no_mangle] +pub unsafe extern "C" fn devintr() -> i32 { + let scause = r_scause(); + + if (scause & 0x8000000000000000 > 0) && (scause & 0xff) == 9 { + // This is a supervisor external interrupt, via PLIC. + + // IRQ indicates which device interrupted. + let irq = plic::plic_claim(); + + if irq == UART0_IRQ { + crate::uart::uartintr(); + } else if irq == VIRTIO0_IRQ { + virtio_disk_intr(); + } else if irq > 0 { + print!("unexpected interrupt irq={}\n", irq); + } + + // The PLIC allows each device to raise at most one + // interrupt at a time; tell the PLIC the device is + // now allowed to interrupt again. + if irq > 0 { + plic::plic_complete(irq); + } + + 1 + } else if scause == 0x8000000000000001 { + // Software interrupt from a machine-mode timer interrupt, + // forwarded by timervec in kernelvec.S. + + if cpuid() == 0 { + clockintr(); + } + + // Acknowledge the software interrupt by + // clearing the SSIP bit in sip. + w_sip(r_sip() & !2); + + 2 + } else { + 0 + } +} diff --git a/kernel/rustkernel/src/uart.rs b/kernel/rustkernel/src/uart.rs new file mode 100644 index 0000000..735dfd8 --- /dev/null +++ b/kernel/rustkernel/src/uart.rs @@ -0,0 +1,204 @@ +//! Low-level driver routines for 16550a UART. +#![allow(non_upper_case_globals)] + +use crate::{ + console::consoleintr, + proc::{sleep, wakeup}, + riscv::memlayout::UART0, + spinlock::{pop_off, push_off, Spinlock}, +}; +use core::{ffi::CStr, ptr::addr_of_mut}; + +/// The UART control registers are memory-mapped +/// at address UART0. This function returns the +/// address of one of the registers. +#[inline(always)] +fn get_register_addr>(register: N) -> *mut u8 { + let register: u64 = register.into(); + (UART0 + register) as *mut u8 +} + +// The UART control registers. +// Some have different meanings for read vs write. +// See http://byterunner.com/16550.html + +/// Receive Holding Register (for input bytes) +const RHR: u8 = 0; +/// Transmit Holding Register (for output bytes) +const THR: u8 = 0; +/// Interrupt Enable Register +const IER: u8 = 1; +const IER_RX_ENABLE: u8 = 1 << 0; +const IER_TX_ENABLE: u8 = 1 << 1; +/// FIFO control register +const FCR: u8 = 2; +const FCR_FIFO_ENABLE: u8 = 1 << 0; +/// Clear the content of the two FIFOs. +const FCR_FIFO_CLEAR: u8 = 3 << 1; +/// Interrupt Status Register +const ISR: u8 = 2; +/// Line Control Register +const LCR: u8 = 2; +const LCR_EIGHT_BITS: u8 = 3; +/// Special mode to set baud rate +const LCR_BAUD_LATCH: u8 = 1 << 7; +/// Line Status Register +const LSR: u8 = 5; +/// Input is waiting to be read from RHR +const LSR_RX_READY: u8 = 1 << 0; +/// THR can accept another character to send +const LSR_TX_IDLE: u8 = 1 << 5; + +#[inline(always)] +unsafe fn read_register>(register: N) -> u8 { + *get_register_addr(register) +} +#[inline(always)] +unsafe fn write_register>(register: N, value: u8) { + *get_register_addr(register) = value; +} + +static mut uart_tx_lock: Spinlock = unsafe { Spinlock::uninitialized() }; +const UART_TX_BUF_SIZE: u64 = 32; +static mut uart_tx_buf: [u8; UART_TX_BUF_SIZE as usize] = [0u8; UART_TX_BUF_SIZE as usize]; +/// Write next to uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] +static mut uart_tx_w: u64 = 0; +/// Read next from uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE] +static mut uart_tx_r: u64 = 0; + +pub(crate) unsafe fn uartinit() { + // Disable interrupts. + write_register(IER, 0x00); + // Special mode to set baud rate. + write_register(LCR, LCR_BAUD_LATCH); + // LSB for baud rate of 38.4K. + write_register(0u8, 0x03); + // MSB for baud rate of 38.4K. + write_register(1u8, 0x00); + // Leave set-baud mode and set + // word length to 8 bits, no parity. + write_register(LCR, LCR_EIGHT_BITS); + // Reset and enable FIFOs. + write_register(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); + // Enable transmit and receive interrupts. + write_register(IER, IER_TX_ENABLE | IER_RX_ENABLE); + + uart_tx_lock = Spinlock::new( + CStr::from_bytes_with_nul(b"uart\0") + .unwrap() + .as_ptr() + .cast_mut(), + ); +} + +/// Add a character to the output buffer and tell the +/// UART to start sending if it isn't already. +/// Blocks if the output buffer is full. +/// Because it may block, it can't be called +/// from interrupts, it's only suitable for use +/// by write(). +pub(crate) unsafe fn uartputc(c: i32) { + uart_tx_lock.lock(); + + if crate::PANICKED { + loop { + core::hint::spin_loop(); + } + } + + while uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE { + // Buffer is full. + // Wait for uartstart() to open up space in the buffer. + sleep( + addr_of_mut!(uart_tx_r).cast(), + addr_of_mut!(uart_tx_lock).cast(), + ); + } + + uart_tx_buf[(uart_tx_w % UART_TX_BUF_SIZE) as usize] = c as i8 as u8; + uart_tx_w += 1; + uartstart(); + uart_tx_lock.unlock(); +} + +/// Alternate version of uartputc() that doesn't +/// use interrupts, for use by kernel printf() and +/// to echo characters. It spins waiting for the UART's +/// output register to be empty. +pub(crate) unsafe fn uartputc_sync(c: i32) { + push_off(); + + if crate::PANICKED { + loop { + core::hint::spin_loop(); + } + } + + // Wait for Transmit Holding Empty to be set in LSR. + while read_register(LSR) & LSR_TX_IDLE == 0 { + core::hint::spin_loop(); + } + + write_register(THR, c as i8 as u8); + + pop_off(); +} + +/// If the UART is idle, and a character is waiting +/// in the transmit buffer, send it. +/// Caller must hold uart_tx_lock. +/// Called from both the top and bottom halves. +unsafe fn uartstart() { + loop { + if uart_tx_w == uart_tx_r { + // Transmit buffer is ready. + return; + } + + if read_register(LSR) & LSR_TX_IDLE == 0 { + // The UART transmit holding register is full, + // so we cannot give it another byte. + // It will interrupt when it's ready for a new byte. + return; + } + + let c = uart_tx_buf[(uart_tx_r % UART_TX_BUF_SIZE) as usize]; + uart_tx_r += 1; + + // Maybe uartputc() is waiting for space in the buffer. + wakeup(addr_of_mut!(uart_tx_r).cast()); + + write_register(THR, c); + } +} + +/// Read one input character from the UART. +/// Return -1 if nothing is waiting. +unsafe fn uartgetc() -> i32 { + if read_register(LSR) & 0x01 != 0 { + // Input data is ready. + read_register(RHR) as i32 + } else { + -1 + } +} + +/// Handle a UART interrupt, raised because input has +/// arrived, or the uart is ready for more output, or +/// both. Called from devintr(). +#[no_mangle] +pub unsafe extern "C" fn uartintr() { + // Read and process incoming characters. + loop { + let c = uartgetc(); + if c == -1 { + break; + } + consoleintr(c); + } + + // Send buffered characters. + uart_tx_lock.lock(); + uartstart(); + uart_tx_lock.unlock(); +} diff --git a/kernel/sleeplock.c b/kernel/sleeplock.c deleted file mode 100644 index 81de585..0000000 --- a/kernel/sleeplock.c +++ /dev/null @@ -1,55 +0,0 @@ -// Sleeping locks - -#include "types.h" -#include "riscv.h" -#include "defs.h" -#include "param.h" -#include "memlayout.h" -#include "spinlock.h" -#include "proc.h" -#include "sleeplock.h" - -void -initsleeplock(struct sleeplock *lk, char *name) -{ - initlock(&lk->lk, "sleep lock"); - lk->name = name; - lk->locked = 0; - lk->pid = 0; -} - -void -acquiresleep(struct sleeplock *lk) -{ - acquire(&lk->lk); - while (lk->locked) { - sleep(lk, &lk->lk); - } - lk->locked = 1; - lk->pid = myproc()->pid; - release(&lk->lk); -} - -void -releasesleep(struct sleeplock *lk) -{ - acquire(&lk->lk); - lk->locked = 0; - lk->pid = 0; - wakeup(lk); - release(&lk->lk); -} - -int -holdingsleep(struct sleeplock *lk) -{ - int r; - - acquire(&lk->lk); - r = lk->locked && (lk->pid == myproc()->pid); - release(&lk->lk); - return r; -} - - - diff --git a/kernel/start.c b/kernel/start.c deleted file mode 100644 index e16f18a..0000000 --- a/kernel/start.c +++ /dev/null @@ -1,89 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "defs.h" - -void main(); -void timerinit(); - -// entry.S needs one stack per CPU. -__attribute__ ((aligned (16))) char stack0[4096 * NCPU]; - -// a scratch area per CPU for machine-mode timer interrupts. -uint64 timer_scratch[NCPU][5]; - -// assembly code in kernelvec.S for machine-mode timer interrupt. -extern void timervec(); - -// entry.S jumps here in machine mode on stack0. -void -start() -{ - // set M Previous Privilege mode to Supervisor, for mret. - unsigned long x = r_mstatus(); - x &= ~MSTATUS_MPP_MASK; - x |= MSTATUS_MPP_S; - w_mstatus(x); - - // set M Exception Program Counter to main, for mret. - // requires gcc -mcmodel=medany - w_mepc((uint64)main); - - // disable paging for now. - w_satp(0); - - // delegate all interrupts and exceptions to supervisor mode. - w_medeleg(0xffff); - w_mideleg(0xffff); - w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE); - - // configure Physical Memory Protection to give supervisor mode - // access to all of physical memory. - w_pmpaddr0(0x3fffffffffffffull); - w_pmpcfg0(0xf); - - // ask for clock interrupts. - timerinit(); - - // keep each CPU's hartid in its tp register, for cpuid(). - int id = r_mhartid(); - w_tp(id); - - // switch to supervisor mode and jump to main(). - asm volatile("mret"); -} - -// arrange to receive timer interrupts. -// they will arrive in machine mode at -// at timervec in kernelvec.S, -// which turns them into software interrupts for -// devintr() in trap.c. -void -timerinit() -{ - // each CPU has a separate source of timer interrupts. - int id = r_mhartid(); - - // ask the CLINT for a timer interrupt. - int interval = 1000000; // cycles; about 1/10th second in qemu. - *(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + interval; - - // prepare information in scratch[] for timervec. - // scratch[0..2] : space for timervec to save registers. - // scratch[3] : address of CLINT MTIMECMP register. - // scratch[4] : desired interval (in cycles) between timer interrupts. - uint64 *scratch = &timer_scratch[id][0]; - scratch[3] = CLINT_MTIMECMP(id); - scratch[4] = interval; - w_mscratch((uint64)scratch); - - // set the machine-mode trap handler. - w_mtvec((uint64)timervec); - - // enable machine-mode interrupts. - w_mstatus(r_mstatus() | MSTATUS_MIE); - - // enable machine-mode timer interrupts. - w_mie(r_mie() | MIE_MTIE); -} diff --git a/kernel/string.c b/kernel/string.c deleted file mode 100644 index 153536f..0000000 --- a/kernel/string.c +++ /dev/null @@ -1,107 +0,0 @@ -#include "types.h" - -void* -memset(void *dst, int c, uint n) -{ - char *cdst = (char *) dst; - int i; - for(i = 0; i < n; i++){ - cdst[i] = c; - } - return dst; -} - -int -memcmp(const void *v1, const void *v2, uint n) -{ - const uchar *s1, *s2; - - s1 = v1; - s2 = v2; - while(n-- > 0){ - if(*s1 != *s2) - return *s1 - *s2; - s1++, s2++; - } - - return 0; -} - -void* -memmove(void *dst, const void *src, uint n) -{ - const char *s; - char *d; - - if(n == 0) - return dst; - - s = src; - d = dst; - if(s < d && s + n > d){ - s += n; - d += n; - while(n-- > 0) - *--d = *--s; - } else - while(n-- > 0) - *d++ = *s++; - - return dst; -} - -// memcpy exists to placate GCC. Use memmove. -void* -memcpy(void *dst, const void *src, uint n) -{ - return memmove(dst, src, n); -} - -int -strncmp(const char *p, const char *q, uint n) -{ - while(n > 0 && *p && *p == *q) - n--, p++, q++; - if(n == 0) - return 0; - return (uchar)*p - (uchar)*q; -} - -char* -strncpy(char *s, const char *t, int n) -{ - char *os; - - os = s; - while(n-- > 0 && (*s++ = *t++) != 0) - ; - while(n-- > 0) - *s++ = 0; - return os; -} - -// Like strncpy but guaranteed to NUL-terminate. -char* -safestrcpy(char *s, const char *t, int n) -{ - char *os; - - os = s; - if(n <= 0) - return os; - while(--n > 0 && (*s++ = *t++) != 0) - ; - *s = 0; - return os; -} - -int -strlen(const char *s) -{ - int n; - - for(n = 0; s[n]; n++) - ; - return n; -} - diff --git a/kernel/syscall.c b/kernel/syscall.c deleted file mode 100644 index ed65409..0000000 --- a/kernel/syscall.c +++ /dev/null @@ -1,147 +0,0 @@ -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "spinlock.h" -#include "proc.h" -#include "syscall.h" -#include "defs.h" - -// Fetch the uint64 at addr from the current process. -int -fetchaddr(uint64 addr, uint64 *ip) -{ - struct proc *p = myproc(); - if(addr >= p->sz || addr+sizeof(uint64) > p->sz) // both tests needed, in case of overflow - return -1; - if(copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0) - return -1; - return 0; -} - -// Fetch the nul-terminated string at addr from the current process. -// Returns length of string, not including nul, or -1 for error. -int -fetchstr(uint64 addr, char *buf, int max) -{ - struct proc *p = myproc(); - if(copyinstr(p->pagetable, buf, addr, max) < 0) - return -1; - return strlen(buf); -} - -static uint64 -argraw(int n) -{ - struct proc *p = myproc(); - switch (n) { - case 0: - return p->trapframe->a0; - case 1: - return p->trapframe->a1; - case 2: - return p->trapframe->a2; - case 3: - return p->trapframe->a3; - case 4: - return p->trapframe->a4; - case 5: - return p->trapframe->a5; - } - panic("argraw"); - return -1; -} - -// Fetch the nth 32-bit system call argument. -void -argint(int n, int *ip) -{ - *ip = argraw(n); -} - -// Retrieve an argument as a pointer. -// Doesn't check for legality, since -// copyin/copyout will do that. -void -argaddr(int n, uint64 *ip) -{ - *ip = argraw(n); -} - -// Fetch the nth word-sized system call argument as a null-terminated string. -// Copies into buf, at most max. -// Returns string length if OK (including nul), -1 if error. -int -argstr(int n, char *buf, int max) -{ - uint64 addr; - argaddr(n, &addr); - return fetchstr(addr, buf, max); -} - -// Prototypes for the functions that handle system calls. -extern uint64 sys_fork(void); -extern uint64 sys_exit(void); -extern uint64 sys_wait(void); -extern uint64 sys_pipe(void); -extern uint64 sys_read(void); -extern uint64 sys_kill(void); -extern uint64 sys_exec(void); -extern uint64 sys_fstat(void); -extern uint64 sys_chdir(void); -extern uint64 sys_dup(void); -extern uint64 sys_getpid(void); -extern uint64 sys_sbrk(void); -extern uint64 sys_sleep(void); -extern uint64 sys_uptime(void); -extern uint64 sys_open(void); -extern uint64 sys_write(void); -extern uint64 sys_mknod(void); -extern uint64 sys_unlink(void); -extern uint64 sys_link(void); -extern uint64 sys_mkdir(void); -extern uint64 sys_close(void); - -// An array mapping syscall numbers from syscall.h -// to the function that handles the system call. -static uint64 (*syscalls[])(void) = { -[SYS_fork] sys_fork, -[SYS_exit] sys_exit, -[SYS_wait] sys_wait, -[SYS_pipe] sys_pipe, -[SYS_read] sys_read, -[SYS_kill] sys_kill, -[SYS_exec] sys_exec, -[SYS_fstat] sys_fstat, -[SYS_chdir] sys_chdir, -[SYS_dup] sys_dup, -[SYS_getpid] sys_getpid, -[SYS_sbrk] sys_sbrk, -[SYS_sleep] sys_sleep, -[SYS_uptime] sys_uptime, -[SYS_open] sys_open, -[SYS_write] sys_write, -[SYS_mknod] sys_mknod, -[SYS_unlink] sys_unlink, -[SYS_link] sys_link, -[SYS_mkdir] sys_mkdir, -[SYS_close] sys_close, -}; - -void -syscall(void) -{ - int num; - struct proc *p = myproc(); - - num = p->trapframe->a7; - if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { - // Use num to lookup the system call function for num, call it, - // and store its return value in p->trapframe->a0 - p->trapframe->a0 = syscalls[num](); - } else { - printf("%d %s: unknown sys call %d\n", - p->pid, p->name, num); - p->trapframe->a0 = -1; - } -} diff --git a/kernel/sysproc.c b/kernel/sysproc.c deleted file mode 100644 index 1de184e..0000000 --- a/kernel/sysproc.c +++ /dev/null @@ -1,91 +0,0 @@ -#include "types.h" -#include "riscv.h" -#include "defs.h" -#include "param.h" -#include "memlayout.h" -#include "spinlock.h" -#include "proc.h" - -uint64 -sys_exit(void) -{ - int n; - argint(0, &n); - exit(n); - return 0; // not reached -} - -uint64 -sys_getpid(void) -{ - return myproc()->pid; -} - -uint64 -sys_fork(void) -{ - return fork(); -} - -uint64 -sys_wait(void) -{ - uint64 p; - argaddr(0, &p); - return wait(p); -} - -uint64 -sys_sbrk(void) -{ - uint64 addr; - int n; - - argint(0, &n); - addr = myproc()->sz; - if(growproc(n) < 0) - return -1; - return addr; -} - -uint64 -sys_sleep(void) -{ - int n; - uint ticks0; - - argint(0, &n); - acquire(&tickslock); - ticks0 = ticks; - while(ticks - ticks0 < n){ - if(killed(myproc())){ - release(&tickslock); - return -1; - } - sleep(&ticks, &tickslock); - } - release(&tickslock); - return 0; -} - -uint64 -sys_kill(void) -{ - int pid; - - argint(0, &pid); - return kill(pid); -} - -// return how many clock tick interrupts have occurred -// since start. -uint64 -sys_uptime(void) -{ - uint xticks; - - acquire(&tickslock); - xticks = ticks; - release(&tickslock); - return xticks; -} diff --git a/kernel/trap.c b/kernel/trap.c index 512c850..8bae29c 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -6,9 +6,6 @@ #include "proc.h" #include "defs.h" -struct spinlock tickslock; -uint ticks; - extern char trampoline[], uservec[], userret[]; // in kernelvec.S, calls kerneltrap(). @@ -16,25 +13,11 @@ void kernelvec(); extern int devintr(); -void -trapinit(void) -{ - initlock(&tickslock, "time"); -} - -// set up to take exceptions and traps while in the kernel. -void -trapinithart(void) -{ - w_stvec((uint64)kernelvec); -} - // // handle an interrupt, exception, or system call from user space. // called from trampoline.S // -void -usertrap(void) +void usertrap(void) { int which_dev = 0; @@ -68,8 +51,15 @@ usertrap(void) } else if((which_dev = devintr()) != 0){ // ok } else { - printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid); - printf(" sepc=%p stval=%p\n", r_sepc(), r_stval()); + printstr("usertrap(): unexepected scause "); + printptr(r_scause()); + printstr(" pid="); + printint(p->pid); + printstr(" sepc="); + printptr(r_sepc()); + printstr(" stval="); + printptr(r_stval()); + printstr("\n"); setkilled(p); } @@ -145,8 +135,13 @@ kerneltrap() panic("kerneltrap: interrupts enabled"); if((which_dev = devintr()) == 0){ - printf("scause %p\n", scause); - printf("sepc=%p stval=%p\n", r_sepc(), r_stval()); + printstr("scause "); + printptr(scause); + printstr("\nsepc="); + printptr(r_sepc()); + printstr(" stval="); + printptr(r_stval()); + printstr("\n"); panic("kerneltrap"); } @@ -159,63 +154,3 @@ kerneltrap() w_sepc(sepc); w_sstatus(sstatus); } - -void -clockintr() -{ - acquire(&tickslock); - ticks++; - wakeup(&ticks); - release(&tickslock); -} - -// check if it's an external interrupt or software interrupt, -// and handle it. -// returns 2 if timer interrupt, -// 1 if other device, -// 0 if not recognized. -int -devintr() -{ - uint64 scause = r_scause(); - - if((scause & 0x8000000000000000L) && - (scause & 0xff) == 9){ - // this is a supervisor external interrupt, via PLIC. - - // irq indicates which device interrupted. - int irq = plic_claim(); - - if(irq == UART0_IRQ){ - uartintr(); - } else if(irq == VIRTIO0_IRQ){ - virtio_disk_intr(); - } else if(irq){ - printf("unexpected interrupt irq=%d\n", irq); - } - - // the PLIC allows each device to raise at most one - // interrupt at a time; tell the PLIC the device is - // now allowed to interrupt again. - if(irq) - plic_complete(irq); - - return 1; - } else if(scause == 0x8000000000000001L){ - // software interrupt from a machine-mode timer interrupt, - // forwarded by timervec in kernelvec.S. - - if(cpuid() == 0){ - clockintr(); - } - - // acknowledge the software interrupt by clearing - // the SSIP bit in sip. - w_sip(r_sip() & ~2); - - return 2; - } else { - return 0; - } -} - diff --git a/kernel/uart.c b/kernel/uart.c deleted file mode 100644 index e3b3b8a..0000000 --- a/kernel/uart.c +++ /dev/null @@ -1,190 +0,0 @@ -// -// low-level driver routines for 16550a UART. -// - -#include "types.h" -#include "param.h" -#include "memlayout.h" -#include "riscv.h" -#include "spinlock.h" -#include "proc.h" -#include "defs.h" - -// the UART control registers are memory-mapped -// at address UART0. this macro returns the -// address of one of the registers. -#define Reg(reg) ((volatile unsigned char *)(UART0 + reg)) - -// the UART control registers. -// some have different meanings for -// read vs write. -// see http://byterunner.com/16550.html -#define RHR 0 // receive holding register (for input bytes) -#define THR 0 // transmit holding register (for output bytes) -#define IER 1 // interrupt enable register -#define IER_RX_ENABLE (1<<0) -#define IER_TX_ENABLE (1<<1) -#define FCR 2 // FIFO control register -#define FCR_FIFO_ENABLE (1<<0) -#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs -#define ISR 2 // interrupt status register -#define LCR 3 // line control register -#define LCR_EIGHT_BITS (3<<0) -#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate -#define LSR 5 // line status register -#define LSR_RX_READY (1<<0) // input is waiting to be read from RHR -#define LSR_TX_IDLE (1<<5) // THR can accept another character to send - -#define ReadReg(reg) (*(Reg(reg))) -#define WriteReg(reg, v) (*(Reg(reg)) = (v)) - -// the transmit output buffer. -struct spinlock uart_tx_lock; -#define UART_TX_BUF_SIZE 32 -char uart_tx_buf[UART_TX_BUF_SIZE]; -uint64 uart_tx_w; // write next to uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] -uint64 uart_tx_r; // read next from uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE] - -extern volatile int panicked; // from printf.c - -void uartstart(); - -void -uartinit(void) -{ - // disable interrupts. - WriteReg(IER, 0x00); - - // special mode to set baud rate. - WriteReg(LCR, LCR_BAUD_LATCH); - - // LSB for baud rate of 38.4K. - WriteReg(0, 0x03); - - // MSB for baud rate of 38.4K. - WriteReg(1, 0x00); - - // leave set-baud mode, - // and set word length to 8 bits, no parity. - WriteReg(LCR, LCR_EIGHT_BITS); - - // reset and enable FIFOs. - WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); - - // enable transmit and receive interrupts. - WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE); - - initlock(&uart_tx_lock, "uart"); -} - -// add a character to the output buffer and tell the -// UART to start sending if it isn't already. -// blocks if the output buffer is full. -// because it may block, it can't be called -// from interrupts; it's only suitable for use -// by write(). -void -uartputc(int c) -{ - acquire(&uart_tx_lock); - - if(panicked){ - for(;;) - ; - } - while(uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE){ - // buffer is full. - // wait for uartstart() to open up space in the buffer. - sleep(&uart_tx_r, &uart_tx_lock); - } - uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c; - uart_tx_w += 1; - uartstart(); - release(&uart_tx_lock); -} - - -// alternate version of uartputc() that doesn't -// use interrupts, for use by kernel printf() and -// to echo characters. it spins waiting for the uart's -// output register to be empty. -void -uartputc_sync(int c) -{ - push_off(); - - if(panicked){ - for(;;) - ; - } - - // wait for Transmit Holding Empty to be set in LSR. - while((ReadReg(LSR) & LSR_TX_IDLE) == 0) - ; - WriteReg(THR, c); - - pop_off(); -} - -// if the UART is idle, and a character is waiting -// in the transmit buffer, send it. -// caller must hold uart_tx_lock. -// called from both the top- and bottom-half. -void -uartstart() -{ - while(1){ - if(uart_tx_w == uart_tx_r){ - // transmit buffer is empty. - return; - } - - if((ReadReg(LSR) & LSR_TX_IDLE) == 0){ - // the UART transmit holding register is full, - // so we cannot give it another byte. - // it will interrupt when it's ready for a new byte. - return; - } - - int c = uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE]; - uart_tx_r += 1; - - // maybe uartputc() is waiting for space in the buffer. - wakeup(&uart_tx_r); - - WriteReg(THR, c); - } -} - -// read one input character from the UART. -// return -1 if none is waiting. -int -uartgetc(void) -{ - if(ReadReg(LSR) & 0x01){ - // input data is ready. - return ReadReg(RHR); - } else { - return -1; - } -} - -// handle a uart interrupt, raised because input has -// arrived, or the uart is ready for more output, or -// both. called from devintr(). -void -uartintr(void) -{ - // read and process incoming characters. - while(1){ - int c = uartgetc(); - if(c == -1) - break; - consoleintr(c); - } - - // send buffered characters. - acquire(&uart_tx_lock); - uartstart(); - release(&uart_tx_lock); -}