diff --git a/.clang-format b/.clang-format deleted file mode 100644 index c59dfdc..0000000 --- a/.clang-format +++ /dev/null @@ -1,8 +0,0 @@ -BasedOnStyle: LLVM -IndentWidth: 4 -TabWidth: 4 -UseTab: Never -ColumnLimit: 80 -AllowShortLoopsOnASingleLine: true -AlwaysBreakTemplateDeclarations: true -BreakConstructorInitializers: BeforeComma diff --git a/Makefile b/Makefile deleted file mode 100644 index 21524e8..0000000 --- a/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -# Makefile for the Fusion operating system. -# Make things fixable, not autocorrecting - -# TODO: Toolchain installation should be documented in the README -# https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack -TOOLPREFIX = riscv-none-elf - -CC = $(TOOLPREFIX)-gcc -AS = $(TOOLPREFIX)-gas -LD = $(TOOLPREFIX)-ld -OBJCOPY = $(TOOLPREFIX)-objcopy -OBJDUMP = $(TOOLPREFIX)-objdump - -# TODO: Document these options with clear descriptions, it should be readable -# by people not intimately familiar with machine code and/or gcc tooling. -CFLAGS = -Wall -Werror -O -fno-omit-frame-pointer -ggdb -gdwarf-2 -CFLAGS += -MD -CFLAGS += -mcmodel=medany -CFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax -CFLAGS += -I. -CFLAGS += -fno-pie -no-pie -CFLAGS += -fno-stack-protector -CFLAGS += -march=rv64gc -mabi=lp64 - -LDFLAGS = -z max-page-size=4096 -LDFLAGS += -m elf64lriscv - -all: kernel.elf - -%.o: %.c - @echo CC $@ - @$(CC) $(CFLAGS) -nostdinc -I. -c $< -o $@ - -%.o: %.S - @echo CC-AS $@ - @$(CC) $(CFLAGS) -c $< -o $@ - -kernel.elf: trap.o kernelvec.o entry.o start.o kernel.o spinlock.o - @echo LD $@ - @$(LD) $(LDFLAGS) -T kernel.ld -o $@ $^ - -kernel.bin: kernel.elf - $(OBJCOPY) -O binary $< $@ - -clean: - rm -f *.o *.d *.elf *.bin - --include *.d - diff --git a/README.md b/README.md index cd2134f..b00b23e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,2 @@ # fusion -For a quick reference on RISC-V assembly: -- https://risc-v.guru/instructions/ - -Toolchains: -- https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack -- https://github.com/xpack-dev-tools/qemu-riscv-xpack/ diff --git a/defs.h b/defs.h deleted file mode 100644 index 6bf6f0c..0000000 --- a/defs.h +++ /dev/null @@ -1,210 +0,0 @@ -#pragma once - -#include "riscv.h" -#include "spinlock.h" -#include "types.h" - -struct buf; -struct context; -struct file; -struct inode; -struct pipe; -struct proc; -struct sleeplock; -struct stat; -struct superblock; - -// bio.c -void binit(void); -struct buf *bread(u32, u32); -void brelse(struct buf *); -void bwrite(struct buf *); -void bpin(struct buf *); -void bunpin(struct buf *); - -// console.c -void consoleinit(void); -void consoleintr(int); -void consputc(int); - -// exec.c -int exec(char *, char **); - -// file.c -struct file *filealloc(void); -void fileclose(struct file *); -struct file *filedup(struct file *); -void fileinit(void); -int fileread(struct file *, u64, int n); -int filestat(struct file *, u64 addr); -int filewrite(struct file *, u64, int n); - -// fs.c -void fsinit(int); -int dirlink(struct inode *, char *, u32); -struct inode *dirlookup(struct inode *, char *, u32 *); -struct inode *ialloc(u32, short); -struct inode *idup(struct inode *); -void iinit(); -void ilock(struct inode *); -void iput(struct inode *); -void iunlock(struct inode *); -void iunlockput(struct inode *); -void iupdate(struct inode *); -int namecmp(const char *, const char *); -struct inode *namei(char *); -struct inode *nameiparent(char *, char *); -int readi(struct inode *, int, u64, u32, u32); -void stati(struct inode *, struct stat *); -int writei(struct inode *, int, u64, u32, u32); -void itrunc(struct inode *); - -// ramdisk.c -void ramdiskinit(void); -void ramdiskintr(void); -void ramdiskrw(struct buf *); - -/** - * Kernel memory allocator - * - * Allocate one 4096-byte page of physical memory. - * Returns a pointer that the kernel can use. - * Returns 0 if the memory cannot be allocated. - * See: kalloc.c - */ -void *kalloc(void); - -/** - * Kernel memory allocator - * - * 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.) - * See: kalloc.c - */ -void kfree(void *); - -/** - * Initialize kernel memory allocator - * - * Called by main() on the way to the kernel's main loop. - * See: kalloc.c - */ -void kinit(void); - -// log.c -void initlog(int, struct superblock *); -void log_write(struct buf *); -void begin_op(void); -void end_op(void); - -// pipe.c -int pipealloc(struct file **, struct file **); -void pipeclose(struct pipe *, int); -int piperead(struct pipe *, u64, int); -int pipewrite(struct pipe *, u64, int); - -// printf.c -void printf(char *, ...); -void panic(char *) __attribute__((noreturn)); -void printfinit(void); - -// proc.c -int cpuid(void); -void exit(int); -int fork(void); -int growproc(int); -void proc_mapstacks(pagetable_t); -pagetable_t proc_pagetable(struct proc *); -void proc_freepagetable(pagetable_t, u64); -int kill(int); -int killed(struct proc *); -void setkilled(struct proc *); -struct cpu *mycpu(void); -struct cpu *getmycpu(void); -struct proc *myproc(); -void procinit(void); -void scheduler(void) __attribute__((noreturn)); -void sched(void); -void sleep(void *, struct spinlock *); -void userinit(void); -int wait(u64); -void wakeup(void *); -void yield(void); -int either_copyout(int user_dst, u64 dst, void *src, u64 len); -int either_copyin(void *dst, int user_src, u64 src, u64 len); -void procdump(void); - -// swtch.S -void swtch(struct context *, struct context *); - -// sleeplock.c -void acquiresleep(struct sleeplock *); -void releasesleep(struct sleeplock *); -int holdingsleep(struct sleeplock *); -void initsleeplock(struct sleeplock *, char *); - -// string.c -int memcmp(const void *, const void *, u32); -void *memmove(void *, const void *, u32); -void *memset(void *, int, u32); -char *safestrcpy(char *, const char *, int); -int strlen(const char *); -int strncmp(const char *, const char *, u32); -char *strncpy(char *, const char *, int); - -// syscall.c -void argint(int, int *); -int argstr(int, char *, int); -void argaddr(int, u64 *); -int fetchstr(u64, char *, int); -int fetchaddr(u64, u64 *); -void syscall(); - -// trap.c -extern u32 ticks; -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); -void kvminithart(void); -void kvmmap(pagetable_t, u64, u64, u64, int); -int mappages(pagetable_t, u64, u64, u64, int); -pagetable_t uvmcreate(void); -void uvmfirst(pagetable_t, u8 *, u32); -u64 uvmalloc(pagetable_t, u64, u64, int); -u64 uvmdealloc(pagetable_t, u64, u64); -int uvmcopy(pagetable_t, pagetable_t, u64); -void uvmfree(pagetable_t, u64); -void uvmunmap(pagetable_t, u64, u64, int); -void uvmclear(pagetable_t, u64); -pte_t *walk(pagetable_t, u64, int); -u64 walkaddr(pagetable_t, u64); -int copyout(pagetable_t, u64, char *, u64); -int copyin(pagetable_t, char *, u64, u64); -int copyinstr(pagetable_t, char *, u64, u64); - -// plic.c -void plicinit(void); -void plicinithart(void); -int plic_claim(void); -void plic_complete(int); - -// virtio_disk.c -void virtio_disk_init(void); -void virtio_disk_rw(struct buf *, int); -void virtio_disk_intr(void); - -// number of elements in fixed-size array -#define NELEM(x) (sizeof(x) / sizeof((x)[0])) diff --git a/entry.S b/entry.S deleted file mode 100644 index 0a2e5d9..0000000 --- a/entry.S +++ /dev/null @@ -1,65 +0,0 @@ -# For a quick reference on RISC-V assembly: -# https://risc-v.guru/instructions/ - -# Kernel entry point -# -# qemu -kernel loads the kernel at 0x80000000 -# and causes each hart (i.e. CPU) to jump there. -# kernel.ld causes the following code to -# be placed at 0x80000000. - -.section .text -.global _entry -_entry: - # Clear all the registers. - li x1, 0x0 - li x2, 0x0 - li x3, 0x0 - li x4, 0x0 - li x5, 0x0 - li x6, 0x0 - li x7, 0x0 - li x8, 0x0 - li x9, 0x0 - li x10, 0x0 - li x11, 0x0 - li x12, 0x0 - li x13, 0x0 - li x14, 0x0 - li x15, 0x0 - li x16, 0x0 - li x17, 0x0 - li x18, 0x0 - li x19, 0x0 - li x20, 0x0 - li x21, 0x0 - li x22, 0x0 - li x23, 0x0 - li x24, 0x0 - li x25, 0x0 - li x26, 0x0 - li x27, 0x0 - li x28, 0x0 - li x29, 0x0 - li x30, 0x0 - li x31, 0x0 - - # set up a stack for C. - # stack0 is declared in start.c, - # with a 4096-byte stack per CPU. - # sp = stack0 + (hartid * 4096) - la sp, stack0 - li a0, 1024*4 # a0 = 4096 - - # Control and Status Register Read - csrr a1, mhartid # a1 = unique hart (core) id - addi a1, a1, 1 # a1 += 1 - mul a0, a0, a1 # a0 *= a1, aka a0 = 4096 * (hartid + 1), the base of the stack for this hart - add sp, sp, a0 # sp += a0, stack pointer is now properly configured - - # jump to start() in start.c - call start - -# Infinite spin loop. -spin: - j spin diff --git a/kernel.c b/kernel.c deleted file mode 100644 index d712a30..0000000 --- a/kernel.c +++ /dev/null @@ -1,37 +0,0 @@ -// volatile unsigned int *uart = (unsigned int *)0x10000000; -// -// void _start() __attribute__((naked, section(".text"))); -// -// #define NCPU 1 -// __attribute__((aligned(16))) char stack0[4096 * NCPU]; -// -// void uart_putc(char c) { *uart = c; } -// -// void uart_print(char *str, int len) { -// for (int i = 0; i < len; i++) { -// uart_putc(str[i]); -// } -// } -// -// // void _start() { -// // void _entry() { -// // asm volatile("la sp, stack0"); -// // -// // *uart = 'H'; -// // -// // // *uart = 'i'; -// // // *uart = '\n'; -// // uart_putc('h'); -// // // uart_print("Hey", 3); -// // -// // while (1) {}; -// // } -// // -// // int main(void) { -// // -// // } -// int strlen(char *str) { -// int cnt = 0; -// while (*str != '\n') cnt++; -// return cnt; -// } diff --git a/kernel.ld b/kernel.ld deleted file mode 100644 index b355405..0000000 --- a/kernel.ld +++ /dev/null @@ -1,78 +0,0 @@ -OUTPUT_ARCH( "riscv" ) -ENTRY( _entry ) - -SECTIONS -{ - /* - * ensure that entry.S / _entry is at 0x80000000, - * where qemu's -kernel jumps. - */ - . = 0x80000000; - - /* - * This section contains the code. This is, the machine language instructions - * that will be executed by the processor. In here we will find symbols - * that reference the functions in your object file. - */ - .text : { - /* Match any section that starts with .text. */ - *(.text*) - - /* Align the next section to a 4KB (page) boundary. */ - . = ALIGN(0x1000); - - /* Put the trampoline code here. */ - _trampoline = .; - - /* Match any section that starts with .trampsec. */ - *(trampsec) - - /* Align the next section to a 4KB (page) boundary. */ - . = ALIGN(0x1000); - - /* Assert that the trampoline code is exactly 4KB (page) in size. */ - ASSERT(. - _trampoline == 0x1000, "error: trampoline larger than one page"); - - /* Define symbol etext to be the current location. */ - PROVIDE(etext = .); - } - - /* - * This contains any data that is marked as read only. - * It is not unusual to find this data interleaved with the text section. - */ - .rodata : { - /* Align on quadword boundary. */ - . = ALIGN(16); - *(.srodata*) /* do not need to distinguish this from .rodata */ - . = ALIGN(16); - *(.rodata*) - } - - /* - * This section contains initialized global and static variables. - * Any global object that has been explicitly initialized to a value different than zero. - */ - .data : { - . = ALIGN(16); - *(.sdata*) /* do not need to distinguish this from .data */ - . = ALIGN(16); - *(.data*) - } - - /* - * Contains all uninitialized global and static var iables. These are usually - * zeroed out by the startup code before we reach the main function. However, - * In an embedded system we usually provide our own startup code, which means - * we need to remember to do this ourselves. - */ - .bss : { - . = ALIGN(16); - *(.sbss*) /* do not need to distinguish this from .bss */ - . = ALIGN(16); - *(.bss*) - } - - /* Define symbol end as current location, note that this is not aligned, see vm.c */ - PROVIDE(end = .); -} diff --git a/kernelvec.S b/kernelvec.S deleted file mode 100644 index 008c831..0000000 --- a/kernelvec.S +++ /dev/null @@ -1,131 +0,0 @@ -# For a quick reference on RISC-V assembly: -# https://risc-v.guru/instructions/ - -# Kernel trap handling -# -# interrupts and exceptions while in supervisor -# mode come here. -# -# the current stack is a kernel stack. -# push all registers, call kerneltrap(). -# when kerneltrap() returns, restore registers, return. - -.globl kerneltrap -.globl kernelvec -.align 4 -kernelvec: - # make room to save registers. - addi sp, sp, -256 - - # save the registers. - sd ra, 0(sp) - sd sp, 8(sp) - sd gp, 16(sp) - sd tp, 24(sp) - sd t0, 32(sp) - sd t1, 40(sp) - sd t2, 48(sp) - sd s0, 56(sp) - sd s1, 64(sp) - sd a0, 72(sp) - sd a1, 80(sp) - sd a2, 88(sp) - sd a3, 96(sp) - sd a4, 104(sp) - sd a5, 112(sp) - sd a6, 120(sp) - sd a7, 128(sp) - sd s2, 136(sp) - sd s3, 144(sp) - sd s4, 152(sp) - sd s5, 160(sp) - sd s6, 168(sp) - sd s7, 176(sp) - sd s8, 184(sp) - sd s9, 192(sp) - sd s10, 200(sp) - sd s11, 208(sp) - sd t3, 216(sp) - sd t4, 224(sp) - sd t5, 232(sp) - sd t6, 240(sp) - - # call the C trap handler in trap.c - call kerneltrap - - # restore registers. - ld ra, 0(sp) - ld sp, 8(sp) - ld gp, 16(sp) - # Skip tp (thread pointer aka x4) (contains hartid) - # in case we moved CPUs - ld t0, 32(sp) - ld t1, 40(sp) - ld t2, 48(sp) - ld s0, 56(sp) - ld s1, 64(sp) - ld a0, 72(sp) - ld a1, 80(sp) - ld a2, 88(sp) - ld a3, 96(sp) - ld a4, 104(sp) - ld a5, 112(sp) - ld a6, 120(sp) - ld a7, 128(sp) - ld s2, 136(sp) - ld s3, 144(sp) - ld s4, 152(sp) - ld s5, 160(sp) - ld s6, 168(sp) - ld s7, 176(sp) - ld s8, 184(sp) - ld s9, 192(sp) - ld s10, 200(sp) - ld s11, 208(sp) - ld t3, 216(sp) - ld t4, 224(sp) - ld t5, 232(sp) - ld t6, 240(sp) - - addi sp, sp, 256 - - # return to whatever we were doing in the kernel. - sret - -# machine-mode timer interrupt -# -# See: start.c for timervec declaration -# extern void timervec(); - -.globl timervec -.align 4 -timervec: - # start.c has set up the memory that mscratch points to: - # scratch[0,8,16] : register save area. - # scratch[24] : address of CLINT's MTIMECMP register. - # scratch[32] : desired interval between interrupts. - - csrrw a0, mscratch, a0 - sd a1, 0(a0) - sd a2, 8(a0) - sd a3, 16(a0) - - # schedule the next timer interrupt - # by adding interval to mtimecmp. - ld a1, 24(a0) # CLINT_MTIMECMP(hart) - ld a2, 32(a0) # interval - ld a3, 0(a1) - add a3, a3, a2 - sd a3, 0(a1) - - # arrange for a supervisor software interrupt - # after this handler returns. - li a1, 2 - csrw sip, a1 - - ld a3, 16(a0) - ld a2, 8(a0) - ld a1, 0(a0) - csrrw a0, mscratch, a0 - - mret diff --git a/memlayout.h b/memlayout.h deleted file mode 100644 index 813407f..0000000 --- a/memlayout.h +++ /dev/null @@ -1,71 +0,0 @@ -#pragma once - -// Physical memory layout - -// qemu -machine virt is set up 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 RAM after 80000000. - -// the kernel uses physical memory thus: -// 80000000 -- entry.S, then kernel text and data -// end -- start of kernel page allocation area -// PHYSTOP -- end RAM used by the kernel - -// qemu puts UART registers here in physical memory. -#define UART0 0x10000000L -#define UART0_IRQ 10 - -#define QEMU_POWER 0x100000 - -// virtio mmio interface -#define VIRTIO0 0x10001000 -#define VIRTIO0_IRQ 1 - -// core local interruptor (CLINT), which contains the timer. -#define CLINT 0x2000000L -#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8 * (hartid)) -#define CLINT_MTIME (CLINT + 0xBFF8) // cycles since boot. - -// qemu puts platform-level interrupt controller (PLIC) here. -#define PLIC 0x0c000000L -#define PLIC_PRIORITY (PLIC + 0x0) -#define PLIC_PENDING (PLIC + 0x1000) -#define PLIC_MENABLE(hart) (PLIC + 0x2000 + (hart) * 0x100) -#define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart) * 0x100) -#define PLIC_MPRIORITY(hart) (PLIC + 0x200000 + (hart) * 0x2000) -#define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart) * 0x2000) -#define PLIC_MCLAIM(hart) (PLIC + 0x200004 + (hart) * 0x2000) -#define PLIC_SCLAIM(hart) (PLIC + 0x201004 + (hart) * 0x2000) - -// the kernel expects there to be RAM -// for use by the kernel and user pages -// from physical address 0x80000000 to PHYSTOP. -#define KERNBASE 0x80000000L -#define PHYSTOP (KERNBASE + 128 * 1024 * 1024) - -// map the trampoline page to the highest address, -// in both user and kernel space. -#define TRAMPOLINE (MAXVA - PGSIZE) - -// map kernel stacks beneath the trampoline, -// each surrounded by invalid guard pages. -#define KSTACK(p) (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) -#define TRAPFRAME (TRAMPOLINE - PGSIZE) diff --git a/param.h b/param.h deleted file mode 100644 index 741f02f..0000000 --- a/param.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#define NPROC 64 // maximum number of processes -#define NCPU 8 // maximum number of CPUs -#define NOFILE 16 // open files per process -#define NFILE 100 // open files per system -#define NINODE 50 // maximum number of active i-nodes -#define NDEV 10 // maximum major device number -#define ROOTDEV 1 // device number of file system root disk -#define MAXARG 32 // max exec arguments -#define MAXOPBLOCKS 10 // max # of blocks any FS op writes -#define LOGSIZE (MAXOPBLOCKS * 3) // max data blocks in on-disk log -#define NBUF (MAXOPBLOCKS * 3) // size of disk block cache -#define FSSIZE 2000 // size of file system in blocks -#define MAXPATH 128 // maximum file path name diff --git a/proc.c b/proc.c deleted file mode 100644 index cdcf836..0000000 --- a/proc.c +++ /dev/null @@ -1,622 +0,0 @@ -#include "proc.h" -#include "defs.h" -#include "memlayout.h" -#include "param.h" -#include "riscv.h" -#include "spinlock.h" -#include "types.h" - -struct cpu cpus[NCPU]; - -struct proc proc[NPROC]; - -struct proc *initproc; - -int nextpid = 1; -struct spinlock pid_lock; - -extern void forkret(void); -static void freeproc(struct proc *p); - -extern char trampoline[]; // trampoline.S - -// 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. -struct spinlock wait_lock; - -// Allocate a page for each process's kernel stack. -// Map it high in memory, followed by an invalid -// guard page. -void proc_mapstacks(pagetable_t kpgtbl) { - struct proc *p; - - for (p = proc; p < &proc[NPROC]; p++) { - char *pa = kalloc(); - if (pa == 0) - panic("kalloc"); - u64 va = KSTACK((int)(p - proc)); - kvmmap(kpgtbl, va, (u64)pa, PGSIZE, PTE_R | PTE_W); - } -} - -// initialize the proc table. -void procinit(void) { - struct proc *p; - - initlock(&pid_lock, "nextpid"); - initlock(&wait_lock, "wait_lock"); - for (p = proc; p < &proc[NPROC]; p++) { - initlock(&p->lock, "proc"); - p->state = UNUSED; - p->kstack = KSTACK((int)(p - proc)); - } -} - -// 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 *p; - - for (p = proc; p < &proc[NPROC]; p++) { - p->mask = 0; - acquire(&p->lock); - if (p->state == UNUSED) { - goto found; - } else { - release(&p->lock); - } - } - return 0; - -found: - p->pid = allocpid(); - p->state = USED; - - // Allocate a trapframe page. - if ((p->trapframe = (struct trapframe *)kalloc()) == 0) { - freeproc(p); - release(&p->lock); - return 0; - } - - // An empty user page table. - p->pagetable = proc_pagetable(p); - if (p->pagetable == 0) { - freeproc(p); - release(&p->lock); - return 0; - } - - // Set up new context to start executing at forkret, - // which returns to user space. - memset(&p->context, 0, sizeof(p->context)); - p->context.ra = (u64)forkret; - p->context.sp = p->kstack + PGSIZE; - - 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 proc_pagetable(struct proc *p) { - pagetable_t pagetable; - - // An empty page table. - pagetable = uvmcreate(); - if (pagetable == 0) - return 0; - - // map the trampoline code (for system call return) - // at the highest user virtual address. - // only the supervisor uses it, on the way - // to/from user space, so not PTE_U. - if (mappages(pagetable, TRAMPOLINE, PGSIZE, (u64)trampoline, - PTE_R | PTE_X) < 0) { - uvmfree(pagetable, 0); - return 0; - } - - // map the trapframe page just below the trampoline page, for - // trampoline.S. - if (mappages(pagetable, TRAPFRAME, PGSIZE, (u64)(p->trapframe), - PTE_R | PTE_W) < 0) { - uvmunmap(pagetable, TRAMPOLINE, 1, 0); - uvmfree(pagetable, 0); - return 0; - } - - return pagetable; -} - -// Free a process's page table, and free the -// physical memory it refers to. -void proc_freepagetable(pagetable_t pagetable, u64 sz) { - uvmunmap(pagetable, TRAMPOLINE, 1, 0); - uvmunmap(pagetable, TRAPFRAME, 1, 0); - uvmfree(pagetable, sz); -} - -// a user program that calls exec("/init") -// assembled from ../user/initcode.S -// od -t xC ../user/initcode -u8 initcode[] = {0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x45, 0x02, 0x97, - 0x05, 0x00, 0x00, 0x93, 0x85, 0x35, 0x02, 0x93, 0x08, - 0x70, 0x00, 0x73, 0x00, 0x00, 0x00, 0x93, 0x08, 0x20, - 0x00, 0x73, 0x00, 0x00, 0x00, 0xef, 0xf0, 0x9f, 0xff, - 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x00, 0x24, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -// Set up first user process. -void userinit(void) { - struct proc *p; - - p = allocproc(); - initproc = p; - - // allocate one user page and copy initcode's instructions - // and data into it. - uvmfirst(p->pagetable, initcode, sizeof(initcode)); - p->sz = PGSIZE; - - // prepare for the very first "return" from kernel to user. - p->trapframe->epc = 0; // user program counter - p->trapframe->sp = PGSIZE; // user stack pointer - - safestrcpy(p->name, "initcode", sizeof(p->name)); - p->cwd = namei("/"); - - p->state = RUNNABLE; - - release(&p->lock); -} - -// Grow or shrink user memory by n bytes. -// Return 0 on success, -1 on failure. -int growproc(int n) { - u64 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; -} - -// Create a new process, copying the parent. -// Sets up child kernel stack to return as if from fork() system call. -int fork(void) { - int i, pid; - struct proc *np; - struct proc *p = myproc(); - - // Allocate process. - if ((np = allocproc()) == 0) { - return -1; - } - - // Copy user memory from parent to child. - if (uvmcopy(p->pagetable, np->pagetable, p->sz) < 0) { - freeproc(np); - release(&np->lock); - return -1; - } - np->sz = p->sz; - - // copy saved user registers. - *(np->trapframe) = *(p->trapframe); - - // Cause fork to return 0 in the child. - np->trapframe->a0 = 0; - - // increment reference counts on open file descriptors. - for (i = 0; i < NOFILE; i++) - if (p->ofile[i]) - np->ofile[i] = filedup(p->ofile[i]); - np->cwd = idup(p->cwd); - - safestrcpy(np->name, p->name, sizeof(p->name)); - - pid = np->pid; - np->mask = 0; - - release(&np->lock); - - acquire(&wait_lock); - np->parent = p; - release(&wait_lock); - - acquire(&np->lock); - np->state = RUNNABLE; - release(&np->lock); - - return pid; -} - -// 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); - } - } -} - -// Exit the current process. Does not return. -// An exited process remains in the zombie state -// until its parent calls wait(). -void exit(int status) { - struct proc *p = myproc(); - - if (p == initproc) - panic("init exiting"); - - // Close all open files. - for (int fd = 0; fd < NOFILE; fd++) { - if (p->ofile[fd]) { - struct file *f = p->ofile[fd]; - fileclose(f); - p->ofile[fd] = 0; - } - } - - begin_op(); - iput(p->cwd); - end_op(); - p->cwd = 0; - - acquire(&wait_lock); - - // Give any children to init. - reparent(p); - - // Parent might be sleeping in wait(). - wakeup(p->parent); - - acquire(&p->lock); - - p->xstate = status; - p->state = ZOMBIE; - - release(&wait_lock); - - // Jump into the scheduler, never to return. - sched(); - panic("zombie exit"); -} - -// Wait for a child process to exit and return its pid. -// Return -1 if this process has no children. -int wait(u64 addr) { - struct proc *pp; - int havekids, pid; - struct proc *p = myproc(); - - acquire(&wait_lock); - - for (;;) { - // Scan through table looking for exited children. - havekids = 0; - for (pp = proc; pp < &proc[NPROC]; pp++) { - if (pp->parent == p) { - // make sure the child isn't still in exit() or swtch(). - acquire(&pp->lock); - - havekids = 1; - if (pp->state == ZOMBIE) { - // Found one. - pid = pp->pid; - if (addr != 0 && - copyout(p->pagetable, addr, (char *)&pp->xstate, - sizeof(pp->xstate)) < 0) { - release(&pp->lock); - release(&wait_lock); - return -1; - } - freeproc(pp); - release(&pp->lock); - release(&wait_lock); - return pid; - } - release(&pp->lock); - } - } - - // No point waiting if we don't have any children. - if (!havekids || killed(p)) { - release(&wait_lock); - return -1; - } - - // Wait for a child to exit. - sleep(p, &wait_lock); // DOC: wait-sleep - } -} - -// Per-CPU process scheduler. -// Each CPU calls scheduler() after setting itself up. -// Scheduler never returns. It loops, doing: -// - choose a process to run. -// - swtch to start running that process. -// - eventually that process transfers control -// via swtch back to the scheduler. -void scheduler(void) { - struct proc *p; - struct cpu *c = mycpu(); - - c->proc = 0; - for (;;) { - // Avoid deadlock by ensuring that devices can interrupt. - intr_on(); - - for (p = proc; p < &proc[NPROC]; p++) { - acquire(&p->lock); - if (p->state == RUNNABLE) { - // Switch to chosen process. It is the process's job - // to release its lock and then reacquire it - // before jumping back to us. - p->state = RUNNING; - c->proc = p; - swtch(&c->context, &p->context); - - // Process is done running for now. - // It should have changed its p->state before coming back. - c->proc = 0; - } - release(&p->lock); - } - } -} - -// 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. -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; -} - -// 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); -} - -// A fork child's very first scheduling by scheduler() -// will swtch to forkret. -void forkret(void) { - static int first = 1; - - // Still holding p->lock from scheduler. - release(&myproc()->lock); - - if (first) { - // File system initialization must be run in the context of a - // regular process (e.g., because it calls sleep), and thus cannot - // be run from main(). - first = 0; - fsinit(ROOTDEV); - } - - usertrapret(); -} - -// 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); -} - -// Wake up all processes sleeping on chan. -// Must be called without any p->lock. -void wakeup(void *chan) { - struct proc *p; - - for (p = proc; p < &proc[NPROC]; p++) { - if (p != myproc()) { - acquire(&p->lock); - if (p->state == SLEEPING && p->chan == chan) { - p->state = RUNNABLE; - } - release(&p->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). -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; -} - -// Copy to either a user address, or kernel address, -// depending on usr_dst. -// Returns 0 on success, -1 on error. -int either_copyout(int user_dst, u64 dst, void *src, u64 len) { - struct proc *p = myproc(); - if (user_dst) { - return copyout(p->pagetable, dst, src, len); - } else { - memmove((char *)dst, src, len); - return 0; - } -} - -// Copy from either a user address, or kernel address, -// depending on usr_src. -// Returns 0 on success, -1 on error. -int either_copyin(void *dst, int user_src, u64 src, u64 len) { - struct proc *p = myproc(); - if (user_src) { - return copyin(p->pagetable, dst, src, len); - } else { - memmove(dst, (char *)src, len); - return 0; - } -} - -// 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) { - static char *states[] = { - [UNUSED] = "unused", [USED] = "used", [SLEEPING] = "sleep ", - [RUNNABLE] = "runble", [RUNNING] = "run ", [ZOMBIE] = "zombie"}; - struct proc *p; - char *state; - - printf("\n"); - for (p = proc; p < &proc[NPROC]; p++) { - if (p->state == UNUSED) - continue; - if (p->state >= 0 && p->state < NELEM(states) && states[p->state]) - state = states[p->state]; - else - state = "???"; - printf("%d %s %s", p->pid, state, p->name); - printf("\n"); - } -} diff --git a/proc.h b/proc.h deleted file mode 100644 index 7803143..0000000 --- a/proc.h +++ /dev/null @@ -1,115 +0,0 @@ -#pragma once - -#include "param.h" -#include "riscv.h" -#include "spinlock.h" -#include "types.h" - -/** Saved registers for kernel context switches. */ -struct context { - u64 ra; - u64 sp; - - // callee-saved - u64 s0; - u64 s1; - u64 s2; - u64 s3; - u64 s4; - u64 s5; - u64 s6; - u64 s7; - u64 s8; - u64 s9; - u64 s10; - u64 s11; -}; - -/** Per-CPU state. */ -struct cpu { - struct proc *proc; // The process running on this cpu, or null. - struct context context; // swtch() here to enter scheduler(). - int noff; // Depth of push_off() nesting. - int intena; // Were interrupts enabled before push_off()? -}; - -extern struct cpu cpus[NCPU]; - -// per-process data for the trap handling code in trampoline.S. -// sits in a page by itself just under the trampoline page in the -// user page table. not specially mapped in the kernel page table. -// uservec in trampoline.S saves user registers in the trapframe, -// then initializes registers from the trapframe's -// kernel_sp, kernel_hartid, kernel_satp, and jumps to kernel_trap. -// usertrapret() and userret in trampoline.S set up -// the trapframe's kernel_*, restore user registers from the -// trapframe, switch to the user page table, and enter user space. -// the trapframe includes callee-saved user registers like s0-s11 because the -// return-to-user path via usertrapret() doesn't return through -// the entire kernel call stack. -struct trapframe { - /* 0 */ u64 kernel_satp; // kernel page table - /* 8 */ u64 kernel_sp; // top of process's kernel stack - /* 16 */ u64 kernel_trap; // usertrap() - /* 24 */ u64 epc; // saved user program counter - /* 32 */ u64 kernel_hartid; // saved kernel tp - /* 40 */ u64 ra; - /* 48 */ u64 sp; - /* 56 */ u64 gp; - /* 64 */ u64 tp; - /* 72 */ u64 t0; - /* 80 */ u64 t1; - /* 88 */ u64 t2; - /* 96 */ u64 s0; - /* 104 */ u64 s1; - /* 112 */ u64 a0; - /* 120 */ u64 a1; - /* 128 */ u64 a2; - /* 136 */ u64 a3; - /* 144 */ u64 a4; - /* 152 */ u64 a5; - /* 160 */ u64 a6; - /* 168 */ u64 a7; - /* 176 */ u64 s2; - /* 184 */ u64 s3; - /* 192 */ u64 s4; - /* 200 */ u64 s5; - /* 208 */ u64 s6; - /* 216 */ u64 s7; - /* 224 */ u64 s8; - /* 232 */ u64 s9; - /* 240 */ u64 s10; - /* 248 */ u64 s11; - /* 256 */ u64 t3; - /* 264 */ u64 t4; - /* 272 */ u64 t5; - /* 280 */ u64 t6; -}; - -enum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; - -/** Per-process state */ -struct proc { - struct spinlock lock; - - // p->lock must be held when using these: - enum procstate state; // Process state - void *chan; // If non-zero, sleeping on chan - int killed; // If non-zero, have been killed - int xstate; // Exit status to be returned to parent's wait - int pid; // Process ID - - // wait_lock must be held when using this: - struct proc *parent; // Parent process - - // these are private to the process, so p->lock need not be held. - u64 kstack; // Virtual address of kernel stack - u64 sz; // Size of process memory (bytes) - pagetable_t pagetable; // User page table - struct trapframe *trapframe; // data page for trampoline.S - struct context context; // swtch() here to run process - struct file *ofile[NOFILE]; // Open files - struct inode *cwd; // Current directory - char name[16]; // Process name (debugging) - int mask; // Process mask -}; diff --git a/riscv.h b/riscv.h deleted file mode 100644 index ed263da..0000000 --- a/riscv.h +++ /dev/null @@ -1,383 +0,0 @@ -#pragma once - -#ifndef __ASSEMBLER__ - -#include "types.h" - -// which hart (core) is this? -static inline u64 r_mhartid() { - u64 x; - asm volatile("csrr %0, mhartid" : "=r"(x)); - return x; -} - -// Machine Status Register, mstatus - -#define MSTATUS_MPP_MASK (3L << 11) // previous mode. -#define MSTATUS_MPP_M (3L << 11) -#define MSTATUS_MPP_S (1L << 11) -#define MSTATUS_MPP_U (0L << 11) -#define MSTATUS_MIE (1L << 3) // machine-mode interrupt enable. - -static inline u64 r_mstatus() { - u64 x; - asm volatile("csrr %0, mstatus" : "=r"(x)); - return x; -} - -static inline void w_mstatus(u64 x) { - asm volatile("csrw mstatus, %0" : : "r"(x)); -} - -// machine exception program counter, holds the -// instruction address to which a return from -// exception will go. -static inline void w_mepc(u64 x) { asm volatile("csrw mepc, %0" : : "r"(x)); } - -// Supervisor Status Register, sstatus - -/** Supervisor Previous Privilege */ -#define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User - -/** Supervisor Previous Interrupt Enable */ -#define SSTATUS_SPIE (1L << 5) - -/** User Previous Interrupt Enable */ -#define SSTATUS_UPIE (1L << 4) - -/** Supervisor Interrupt Enable */ -#define SSTATUS_SIE (1L << 1) - -/** User Interrupt Enable */ -#define SSTATUS_UIE (1L << 0) - -/** - * Read the value of the sstatus register. - * (Supervisor Status Register) - */ -static inline u64 r_sstatus() { - u64 x; - asm volatile("csrr %0, sstatus" : "=r"(x)); - return x; -} - -/** - * Write a value to the sstatus register. - * (Supervisor Status Register) - */ -static inline void w_sstatus(u64 x) { - asm volatile("csrw sstatus, %0" : : "r"(x)); -} - -/** Read Suporvisor Interrupt Pending */ -static inline u64 r_sip() { - u64 x; - asm volatile("csrr %0, sip" : "=r"(x)); - return x; -} - -/** Write Suporvisor Interrupt Pending */ -static inline void w_sip(u64 x) { asm volatile("csrw sip, %0" : : "r"(x)); } - -/** Supervisor External Interrup Enable */ -#define SIE_SEIE (1L << 9) - -/** Supervisor Timer Interrupt Enable */ -#define SIE_STIE (1L << 5) - -/** Supervisor Software Interrupt Enable */ -#define SIE_SSIE (1L << 1) - -/** - * Read the value of the sie register. - * (Supervisor Interrupt Enable) - */ -static inline u64 r_sie() { - u64 x; - asm volatile("csrr %0, sie" : "=r"(x)); - return x; -} - -/** - * Write the valie to the sie rgister - * (Supervisor Interrupt Enable) - */ -static inline void w_sie(u64 x) { asm volatile("csrw sie, %0" : : "r"(x)); } - -/** Machine External Interrupt Enable */ -#define MIE_MEIE (1L << 11) - -/** Machine Timer Interrupt Enable */ -#define MIE_MTIE (1L << 7) - -/** Machine Software Interrupt Enable */ -#define MIE_MSIE (1L << 3) - -/** - * Read the value of the mie register. - * (Machine Interrupt Enable) - */ -static inline u64 r_mie() { - u64 x; - asm volatile("csrr %0, mie" : "=r"(x)); - return x; -} - -/** - * Write the value to the mie register. - * (Machine Interrupt Enable) - */ -static inline void w_mie(u64 x) { asm volatile("csrw mie, %0" : : "r"(x)); } - -// The supervisor exception program counter holds the -// instruction address to which a return from -// exception will go. -// -// It is automatically set when an exception occurs. -// It can also be set with the w_sepc() function. -// (A wrapper for csrw sepc, x) -// -// Information related to the cause of the exception -// will be in the scause register. Readable with the -// r_scause() defined below. - -/** Write Supervisor Exception Program Counter */ -static inline void w_sepc(u64 x) { asm volatile("csrw sepc, %0" : : "r"(x)); } - -/** Read Supervisor Exception Program Counter */ -static inline u64 r_sepc() { - u64 x; - asm volatile("csrr %0, sepc" : "=r"(x)); - return x; -} - -/** Read Machine Exception Delegation */ -static inline u64 r_medeleg() { - u64 x; - asm volatile("csrr %0, medeleg" : "=r"(x)); - return x; -} - -/** Write Machine Exception Delegation */ -static inline void w_medeleg(u64 x) { - asm volatile("csrw medeleg, %0" : : "r"(x)); -} - -/** Read Machine Interrupt Delegation */ -static inline u64 r_mideleg() { - u64 x; - asm volatile("csrr %0, mideleg" : "=r"(x)); - return x; -} - -/** Write Machine Interrupt Delegation */ -static inline void w_mideleg(u64 x) { - asm volatile("csrw mideleg, %0" : : "r"(x)); -} - -/** Write Supervisor Trap-Vector Base Address */ -static inline void w_stvec(u64 x) { asm volatile("csrw stvec, %0" : : "r"(x)); } - -/** Read Supervisor Trap-Vector Base Address */ -static inline u64 r_stvec() { - u64 x; - asm volatile("csrr %0, stvec" : "=r"(x)); - return x; -} - -/** Write Machine Trap-Vector Base Address */ -static inline void w_mtvec(u64 x) { asm volatile("csrw mtvec, %0" : : "r"(x)); } - -/** Read Physical Memory Protection Configuration */ -static inline void w_pmpcfg0(u64 x) { - asm volatile("csrw pmpcfg0, %0" : : "r"(x)); -} - -/** Write Physical Memory Protection Configuration */ -static inline void w_pmpaddr0(u64 x) { - asm volatile("csrw pmpaddr0, %0" : : "r"(x)); -} - -/** Risc-v's sv39 page table scheme. */ -#define SATP_SV39 (8L << 60) - -/** Make Supervisor Address Translation and Protection */ -#define MAKE_SATP(pagetable) (SATP_SV39 | (((u64)pagetable) >> 12)) - -/** - * Write the value to the satp register. - * (Supervisor Address Translation and Protection) - * - * This register holds the address of the page table. - */ -static inline void w_satp(u64 x) { asm volatile("csrw satp, %0" : : "r"(x)); } - -/** - * Read the value of the satp register. - * (Supervisor Address Translation and Protection) - * Returns the address of the page table. - */ -static inline u64 r_satp() { - u64 x; - asm volatile("csrr %0, satp" : "=r"(x)); - return x; -} - -/** Read Supervisor Scratch Register */ -static inline void w_mscratch(u64 x) { - asm volatile("csrw mscratch, %0" : : "r"(x)); -} - -/** Supervisor Trap Cause */ -static inline u64 r_scause() { - u64 x; - asm volatile("csrr %0, scause" : "=r"(x)); - return x; -} - -/** Supervisor Trap Value */ -static inline u64 r_stval() { - u64 x; - asm volatile("csrr %0, stval" : "=r"(x)); - return x; -} - -/** Write Machine-mode Counter-Enable Register */ -static inline void w_mcounteren(u64 x) { - asm volatile("csrw mcounteren, %0" : : "r"(x)); -} - -/** Read Machine-mode Counter-Enable Register */ -static inline u64 r_mcounteren() { - u64 x; - asm volatile("csrr %0, mcounteren" : "=r"(x)); - return x; -} - -/** - * Machine-mode cycle counter - * Reports the current wall-clock time from the timer device. - */ -static inline u64 r_time() { - u64 x; - asm volatile("csrr %0, time" : "=r"(x)); - return x; -} - -/** Enable device interrupts */ -static inline void intr_on() { w_sstatus(r_sstatus() | SSTATUS_SIE); } - -/** Disable device interrupts */ -static inline void intr_off() { w_sstatus(r_sstatus() & ~SSTATUS_SIE); } - -/** Are device interrupts enabled? */ -static inline int intr_get() { - u64 x = r_sstatus(); - return (x & SSTATUS_SIE) != 0; -} - -/** Read stack pointer */ -static inline u64 r_sp() { - u64 x; - asm volatile("mv %0, sp" : "=r"(x)); - return x; -} - -// read and write tp, the thread pointer, which xv6 uses to hold -// this core's hartid (core number), the index into cpus[]. - -/** Read thread pointer */ -static inline u64 r_tp() { - u64 x; - asm volatile("mv %0, tp" : "=r"(x)); - return x; -} - -/** Write thread pointer */ -static inline void w_tp(u64 x) { asm volatile("mv tp, %0" : : "r"(x)); } - -/** Read the return address */ -static inline u64 r_ra() { - u64 x; - asm volatile("mv %0, ra" : "=r"(x)); - return x; -} - -/** Flush the TLB (Translation Lookaside Buffer) */ -static inline void sfence_vma() { - // the zero, zero means flush all TLB entries. - asm volatile("sfence.vma zero, zero"); -} - -/** Page Table Entry Type */ -typedef u64 pte_t; - -/** Page Table Type */ -typedef u64 *pagetable_t; // 512 PTEs - -#endif // __ASSEMBLER__ - -/** Page Size */ -#define PGSIZE 4096 // bytes per page - -/** Page Shift, bits of offset within a page */ -#define PGSHIFT 12 - -#define PGROUNDUP(sz) (((sz) + PGSIZE - 1) & ~(PGSIZE - 1)) -#define PGROUNDDOWN(a) (((a)) & ~(PGSIZE - 1)) - -/** - * Page Table Entry Flags - */ -/** Valid */ -#define PTE_V (1L << 0) -/** Readable */ -#define PTE_R (1L << 1) -/** Writable */ -#define PTE_W (1L << 2) -/** Executable */ -#define PTE_X (1L << 3) -/** User-accessible */ -#define PTE_U (1L << 4) - -#define PTE_V (1L << 0) /** PTE Valid */ -#define PTE_R (1L << 1) /** PTE Readable */ -#define PTE_W (1L << 2) /** PTE Writeable */ -#define PTE_X (1L << 3) /** PTE Executable */ -#define PTE_U (1L << 4) /** PTE User Accessible */ - -/** - * Helper macros to shift a physical address - * to the right place for a PTE. - */ - -/** Physical Address to Page Table Entry */ -#define PA2PTE(pa) ((((u64)pa) >> 12) << 10) - -/** Page Table Entry to Physical Address */ -#define PTE2PA(pte) (((pte) >> 10) << 12) - -/** Page Table Entry Flags */ -#define PTE_FLAGS(pte) ((pte) & 0x3FF) - -/** - * Helper macros to extract the three 9-bit - * page table indices from a virtual address. - */ - -/** Page Extract Mask */ -#define PXMASK 0x1FF // 9 bits, 0b111111111 - -/** Page Extract Shift */ -#define PXSHIFT(level) (PGSHIFT + (9 * (level))) - -/** Page Extract */ -#define PX(level, va) ((((u64)(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. - */ -#define MAXVA (1L << (9 + 9 + 9 + 12 - 1)) diff --git a/spinlock.c b/spinlock.c deleted file mode 100644 index b40e4ad..0000000 --- a/spinlock.c +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Mutual exclusion spin locks. - * (Not mutexes as these are spinning locks). - */ - -#include "spinlock.h" -#include "defs.h" -#include "proc.h" -#include "riscv.h" - -/** - * The aquire() and release() functions control ownership of the lock. - * To perform these operations, modern CPU's provide atomic instructions - * that prevent the cores from stepping on each other's toes, otherwise known - * as a deadlock. - * - * GCC provides a set of built-in functions that allow you to use atomic - * instructions in an architecture-independent way. These functions are - * defined in the GCC manual: - * - * See: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html - * See: https://en.wikipedia.org/wiki/Memory_barrier - * - * On RISC-V, sync_lock_test_and_set turns into an atomic swap: - * a5 = 1 - * s1 = &lk->locked - * amoswap.w.aq a5, a5, (s1) - * - * On RISC-V, sync_lock_release turns into an atomic swap: - * s1 = &lk->locked - * amoswap.w zero, zero, (s1) - * - * __sync_synchronize(); - * - * This function tells the C compiler and the processor to not move loads or - * stores past this point, to ensure that the critical section's memory - * references happen strictly after the lock is acquired/locked. - * On RISC-V, this emits a fence instruction. - */ - -/** Initialize spinlock */ -void initlock(struct spinlock *lk, char *name) { - lk->name = name; - lk->locked = 0; - lk->cpu = 0; -} - -/** - * Acquire the lock. - * Loops (spins) until the lock is acquired. - * Panics if the lock is already held by this cpu. - */ -void acquire(struct spinlock *lk) { - push_off(); // disable interrupts to avoid deadlock. - - if (holding(lk)) // If the lock is already held, panic. - panic("acquire"); - - // Spin until aquired. See file header for details - while (__sync_lock_test_and_set(&lk->locked, 1) != 0) { - } - __sync_synchronize(); // No loads/stores after this point - - // Record info about lock acquisition for holding() and debugging. - lk->cpu = mycpu(); -} - -/** - * Release the lock. - * Panics if the lock is not held. - */ -void release(struct spinlock *lk) { - if (!holding(lk)) // If the lock is not held, panic. - panic("release"); - - lk->cpu = 0; // 0 means unheld - __sync_synchronize(); // No loads/stores after this point - __sync_lock_release(&lk->locked); // Essentially lk->locked = 0 - - pop_off(); -} - -// Check whether this cpu is holding the lock. -// Interrupts must be off. -int holding(struct spinlock *lk) { - int r; - r = (lk->locked && lk->cpu == mycpu()); - return r; -} - -// push_off/pop_off are like intr_off()/intr_on() except that they are matched: -// it takes two pop_off()s to undo two push_off()s. Also, if interrupts -// are initially off, then push_off, pop_off leaves them off. - -void push_off(void) { - int old = intr_get(); - - intr_off(); - if (mycpu()->noff == 0) - mycpu()->intena = old; - mycpu()->noff += 1; -} - -void pop_off(void) { - struct cpu *c = mycpu(); - if (intr_get()) - panic("pop_off - interruptible"); - if (c->noff < 1) - panic("pop_off"); - c->noff -= 1; - if (c->noff == 0 && c->intena) - intr_on(); -} diff --git a/spinlock.h b/spinlock.h deleted file mode 100644 index 43de426..0000000 --- a/spinlock.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "types.h" - -struct spinlock; - -// spinlock.c - -/** - * Acquire the lock. - * Loops (spins) until the lock is acquired. - * Panics if the lock is already held by this cpu. - */ -void acquire(struct spinlock *); - -/** - * Check whether this cpu is holding the lock. - * Interrupts must be off. - */ -int holding(struct spinlock *); - -/** - * Initialize spinlock - */ -void initlock(struct spinlock *, char *); - -/** - * Release the lock. - * Panics if the lock is not held. - */ -void release(struct spinlock *); - -/** - * @brief push_off/pop_off are like intr_off()/intr_on() except that they are - * matched: it takes two pop_off()s to undo two push_off()s. Also, if - * interrupts are initially off, then push_off, pop_off leaves them off. - */ -void push_off(void); - -/** @copydoc pop_off */ -void pop_off(void); - -/** Mutual exclusion spin lock */ -struct spinlock { - u32 locked; // Is the lock held? - - // NOTE: Perhaps feature gate this? - - // For debugging: - char *name; // Name of lock. - struct cpu *cpu; // The cpu holding the lock. -}; diff --git a/start.c b/start.c deleted file mode 100644 index 5ee81bc..0000000 --- a/start.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "memlayout.h" -#include "param.h" -#include "riscv.h" -#include "types.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. -u64 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((u64)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. - *(u64 *)CLINT_MTIMECMP(id) = *(u64 *)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. - u64 *scratch = &timer_scratch[id][0]; - scratch[3] = CLINT_MTIMECMP(id); - scratch[4] = interval; - w_mscratch((u64)scratch); - - // Set the machine-mode trap handler. - w_mtvec((u64)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/trap.c b/trap.c deleted file mode 100644 index a658516..0000000 --- a/trap.c +++ /dev/null @@ -1,200 +0,0 @@ -#include "defs.h" -#include "memlayout.h" -#include "proc.h" -#include "riscv.h" -#include "spinlock.h" -#include "types.h" - -struct spinlock tickslock; -u32 ticks; - -extern char trampoline[], uservec[], userret[]; - -// in kernelvec.S, calls kerneltrap(). -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((u64)kernelvec); } - -// -// handle an interrupt, exception, or system call from user space. -// called from trampoline.S -// -void usertrap(void) { - int which_dev = 0; - - 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((u64)kernelvec); - - struct proc *p = myproc(); - - // save user program counter. - p->trapframe->epc = r_sepc(); - - if (r_scause() == 8) { - // system call - - if (killed(p)) - 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 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()); - setkilled(p); - } - - if (killed(p)) - exit(-1); - - // give up the CPU if this is a timer interrupt. - if (which_dev == 2) - yield(); - - usertrapret(); -} - -// -// return to user space -// -void usertrapret(void) { - struct proc *p = myproc(); - - // we're about to switch the destination of traps from - // kerneltrap() to usertrap(), so turn off interrupts until - // we're back in user space, where usertrap() is correct. - intr_off(); - - // send syscalls, interrupts, and exceptions to uservec in trampoline.S - u64 trampoline_uservec = TRAMPOLINE + (uservec - trampoline); - w_stvec(trampoline_uservec); - - // set up trapframe values that uservec will need when - // the process next traps into the kernel. - p->trapframe->kernel_satp = r_satp(); // kernel page table - p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack - p->trapframe->kernel_trap = (u64)usertrap; - p->trapframe->kernel_hartid = r_tp(); // hartid for cpuid() - - // set up the registers that trampoline.S's sret will use - // to get to user space. - - // set S Previous Privilege mode to User. - unsigned long x = r_sstatus(); - x &= ~SSTATUS_SPP; // clear SPP to 0 for user mode - x |= SSTATUS_SPIE; // enable interrupts in user mode - w_sstatus(x); - - // set S Exception Program Counter to the saved user pc. - w_sepc(p->trapframe->epc); - - // tell trampoline.S the user page table to switch to. - u64 satp = MAKE_SATP(p->pagetable); - - // jump to userret in trampoline.S at the top of memory, which - // switches to the user page table, restores user registers, - // and switches to user mode with sret. - u64 trampoline_userret = TRAMPOLINE + (userret - trampoline); - ((void (*)(u64))trampoline_userret)(satp); -} - -// interrupts and exceptions from kernel code go here via kernelvec, -// on whatever the current kernel stack is. -void kerneltrap() { - int which_dev = 0; - u64 sepc = r_sepc(); - u64 sstatus = r_sstatus(); - u64 scause = r_scause(); - - if ((sstatus & SSTATUS_SPP) == 0) - panic("kerneltrap: not from supervisor mode"); - if (intr_get() != 0) - panic("kerneltrap: interrupts enabled"); - - if ((which_dev = devintr()) == 0) { - printf("scause %p\n", scause); - printf("sepc=%p stval=%p\n", r_sepc(), r_stval()); - panic("kerneltrap"); - } - - // give up the CPU if this is a timer interrupt. - if (which_dev == 2 && myproc() != 0 && myproc()->state == RUNNING) - yield(); - - // the yield() may have caused some traps to occur, - // so restore trap registers for use by kernelvec.S's sepc instruction. - 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() { - u64 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/types.h b/types.h deleted file mode 100644 index acc0095..0000000 --- a/types.h +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -typedef unsigned long u64; - -typedef u64 pde_t;