diff --git a/Makefile b/Makefile index 50ea96b..1496c7f 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,6 @@ CFLAGS += -ffreestanding CFLAGS += -fno-common CFLAGS += -nostdlib CFLAGS += -mno-relax -CFLAGS += -std=gnu99 CFLAGS += -fno-stack-protector # Prevents code that needs libc / runtime support CFLAGS += -MD # Generate header dependency files (.d) @@ -45,8 +44,7 @@ KERNEL_OBJ := \ kern/libkern/panic.o \ kern/libkern/memory.o \ kern/libkern/spinlock.o \ - kern/libkern/mini-printf.o \ - kern/libkern/stdio.o + kern/libkern/mini-printf.o kern/kernel.elf: $(KERNEL_OBJ) @echo LD $@ diff --git a/kern/kalloc.c b/kern/kalloc.c index 88238ee..17d65c7 100644 --- a/kern/kalloc.c +++ b/kern/kalloc.c @@ -44,7 +44,7 @@ void kfree(void *pa) { // Assert that page is a ligned to a page boundary and that its correctly // sized if (((u64)pa % PGSIZE) != 0 || (char *)pa < kernel_end || (u64)pa >= PHYSTOP) - PANIC("kfree"); + panic("kfree"); // Fill with junk to catch dangling refs. memset(pa, 1, PGSIZE); diff --git a/kern/libkern/panic.c b/kern/libkern/panic.c index 3671b04..f0df22e 100644 --- a/kern/libkern/panic.c +++ b/kern/libkern/panic.c @@ -1,18 +1,8 @@ -#include "stdbool.h" -#include -#include -#include -#include #include +volatile int panicked; -volatile int panicked = false; - -__attribute__((visibility("hidden"))) -void __panic(const char *restrict fmt, ...) { - va_list ap; - va_start(ap, fmt); - (void)mini_vpprintf(stdout_puts, NULL, fmt, ap); - va_end(ap); - panicked = true; - while (true) asm volatile("wfi"); +void panic(char *s) { + panicked = 1; + uart_puts(s); + while (1); } diff --git a/kern/libkern/panic.h b/kern/libkern/panic.h index 53dc283..77cd640 100644 --- a/kern/libkern/panic.h +++ b/kern/libkern/panic.h @@ -1,8 +1,6 @@ #ifndef KERNEL_PANIC_H #define KERNEL_PANIC_H -#define PANIC(fmt, ...) __panic("[%s:%d %s] \n" fmt, __FILE__, __LINE__, __func__) - -void __panic(const char *restrict fmt, ...); +void panic(char *s); #endif diff --git a/kern/libkern/proc.c b/kern/libkern/proc.c index bebf1a5..66bca67 100644 --- a/kern/libkern/proc.c +++ b/kern/libkern/proc.c @@ -1,6 +1,6 @@ #include -Cpu cpus[NCPU]; +struct Cpu cpus[NCPU]; /** * Must be called with interrupts disabled, to prevent race with process being @@ -14,8 +14,8 @@ int cpuid() { /** * Return this CPU's cpu struct. Interrupts must be disabled. */ -Cpu *mycpu(void) { - int id = cpuid(); - Cpu *c = &cpus[id]; +struct Cpu *mycpu(void) { + int id = cpuid(); + struct Cpu *c = &cpus[id]; return c; } diff --git a/kern/libkern/spinlock.c b/kern/libkern/spinlock.c index 7dc6658..f3b194a 100644 --- a/kern/libkern/spinlock.c +++ b/kern/libkern/spinlock.c @@ -3,6 +3,8 @@ * (Not mutexes as these are spinning locks). */ +// #include +#include "string.h" #include #include #include @@ -39,42 +41,86 @@ * On RISC-V, this emits a fence instruction. */ -/* - * These are from the original xv6 implementation, with only slight modifications on their return type. - * - * 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. - */ - -uint32_t push_off(void) { - int old = intr_get(); - Cpu *cpu = mycpu(); - - intr_off(); - - if (cpu->noff == 0) - cpu->intena = old; - - cpu->noff += 1; - return cpu->noff; +/** Initialize Spinlock */ +void initlock(struct Spinlock *lk, char *name) { + lk->name = name; + lk->locked = 0; + lk->cpu = 0; } -uint32_t pop_off(void) { - Cpu *cpu = mycpu(); +/** + * 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 (cpu->noff < 1) - PANIC("pop_off"); - - cpu->noff -= 1; - - if (cpu->noff == 0 && cpu->intena) + panic("pop_off - interruptible"); + if (c->noff < 1) { + { + // TODO: Remove this block when fixed + char amt[100]; + itoa(c->noff, amt, 10); + uart_puts(amt); + } + panic("pop_off"); + } + c->noff -= 1; + if (c->noff == 0 && c->intena) intr_on(); - - return cpu->noff; } void spinlock_init(spinlock_t *l) { @@ -89,47 +135,34 @@ __attribute__((warn_unused_result)) bool spin_trylock(spinlock_t *l) { } void spin_unlock(spinlock_t *l) { - // if (!spin_is_holding(l)) - // panic("spin_unlock"); - - l->cpu = 0; - // Release: store 0 with .rl ordering. uint32_t dummy; __asm__ volatile("amoswap.w.rl %0, %2, (%1)\n" : "=&r"(dummy) : "r"(&l->v), "r"(0u) : "memory"); - - // __sync_synchronize(); // No loads/stores after this point - // __sync_lock_release(&lk->locked); // Essentially lk->locked = 0 - - // pop_off(); } -/** - * Test-and-test-and-set acquire with polite spinning + exponential backoff. - */ +// Optional: tiny pause/backoff (works even if Zihintpause isn't present). +// See: https://github.com/riscv/riscv-isa-manual/blob/main/src/zihintpause.adoc +static inline void cpu_relax(void) { +#if defined(__riscv_zihintpause) + __asm__ volatile("pause"); +#else + __asm__ volatile("nop"); +#endif +} + +// Test-and-test-and-set acquire with polite spinning + exponential backoff. void spin_lock(spinlock_t *l) { - uint32_t backoff = 1; + unsigned backoff = 1; for (;;) { if (spin_trylock(l)) return; + // Contended: spin on plain loads (no AMO) until it looks free. while (__atomic_load_n(&l->v, __ATOMIC_RELAXED) != 0) { - for (uint32_t i = 0; i < backoff; ++i) - __asm__ volatile("nop"); /* NOTE: Pause can be used here if supported */ + for (unsigned i = 0; i < backoff; ++i) cpu_relax(); if (backoff < 1u << 12) backoff <<= 1; } + // Try again; loop. } - - l->cpu = mycpu(); -} - -/** - * Check whether this cpu is holding the lock. - * Interrupts must be off. - */ -bool spin_is_holding(spinlock_t *l) { - int r; - r = (l->v && l->cpu == mycpu()); - return r; } diff --git a/kern/libkern/spinlock.h b/kern/libkern/spinlock.h index de6091e..5561b98 100644 --- a/kern/libkern/spinlock.h +++ b/kern/libkern/spinlock.h @@ -1,22 +1,60 @@ #ifndef KERNEL_Spinlock_H #define KERNEL_Spinlock_H -#include -#include #include +/** 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. +}; + +/** + * 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); + typedef struct { volatile uint32_t v; // 0 = unlocked, 1 = locked - Cpu *cpu; } spinlock_t; -uint32_t push_off(void); -uint32_t pop_off(void); - void spinlock_init(spinlock_t *l); bool spin_trylock(spinlock_t *l); void spin_unlock(spinlock_t *l); -bool spin_is_holding(spinlock_t *l); void spin_lock(spinlock_t *l); #endif diff --git a/kern/libkern/stddef.h b/kern/libkern/stddef.h deleted file mode 100644 index ab8a2b5..0000000 --- a/kern/libkern/stddef.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef STDDEF_H -#define STDDEF_H - -#ifndef NULL -#define NULL ((void*)0) -#endif - -#endif // STDDEF_H - diff --git a/kern/libkern/stdio.c b/kern/libkern/stdio.c deleted file mode 100644 index d02e59b..0000000 --- a/kern/libkern/stdio.c +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include - -int stdout_puts(char *s, int len, void *unused) { - (void)unused; - // Example: UART write loop - for (int i = 0; i < len; i++) { - uart_putc(s[i]); // <-- your low-level "put char" routine - } - return len; -} - -int kprintf(const char *restrict fmt, ...) { - va_list ap; - va_start(ap, fmt); - int ret = mini_vpprintf(stdout_puts, NULL, fmt, ap); - va_end(ap); - return ret; -} diff --git a/kern/libkern/stdio.h b/kern/libkern/stdio.h deleted file mode 100644 index 2602c87..0000000 --- a/kern/libkern/stdio.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef STDIO_H -#define STDIO_H - -int stdout_puts(char *s, int len, void *unused); -int kprintf(const char *restrict format, ...); - -// int fprintf(FILE *restrict stream, const char *restrict format, ...); -// int dprintf(int fd, const char *restrict format, ...); -// int sprintf(char *restrict str, const char *restrict format, ...); -// int snprintf(char str[restrict.size], size_t size, const char *restrict format, ...); - -// int vprintf(const char *restrict format, va_list ap); -// int vfprintf(FILE *restrict stream, const char *restrict format, va_list ap); -// int vdprintf(int fd, const char *restrict format, va_list ap); -// int vsprintf(char *restrict str, const char *restrict format, va_list ap); -// int vsnprintf(char str[restrict.size], size_t size, const char *restrict format, va_list ap); - -#endif // STDIO_H diff --git a/kern/libkern/uart.c b/kern/libkern/uart.c index df0af5d..bb753f3 100644 --- a/kern/libkern/uart.c +++ b/kern/libkern/uart.c @@ -4,3 +4,7 @@ void uart_putc(char c) { *UART_BASE = c; } + +void uart_puts(const char *s) { + while (*s) uart_putc(*s++); +} diff --git a/kern/libkern/uart.h b/kern/libkern/uart.h index 8f08f1a..953c5f6 100644 --- a/kern/libkern/uart.h +++ b/kern/libkern/uart.h @@ -4,4 +4,7 @@ /** Send a single character to the UART device */ void uart_putc(char c); +/** Send a **NULL TERMINATED** string to the UART device */ +void uart_puts(const char *s); + #endif diff --git a/kern/proc.h b/kern/proc.h index ac5a46d..37ab333 100644 --- a/kern/proc.h +++ b/kern/proc.h @@ -1,8 +1,6 @@ -#ifndef PROC_H -#define PROC_H - #include #include +#include #include typedef enum { @@ -15,7 +13,7 @@ typedef enum { } ProcessState; /** Saved registers for kernel context switches. */ -typedef struct Context { +struct Context { uint64_t ra; uint64_t sp; @@ -32,18 +30,18 @@ typedef struct Context { uint64_t s9; uint64_t s10; uint64_t s11; -} Context; +}; /** Per-CPU state. */ -typedef struct cpu_t { +struct Cpu { struct Process *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()? -} Cpu; +}; /** Saved registers for kernel context switches. */ -typedef struct TrapFrame_t { +typedef struct { /* 0 */ uint64_t kernel_satp; // kernel page table /* 8 */ uint64_t kernel_sp; // top of process's kernel stack /* 16 */ uint64_t kernel_trap; // usertrap() @@ -80,13 +78,11 @@ typedef struct TrapFrame_t { /* 264 */ uint64_t t4; /* 272 */ uint64_t t5; /* 280 */ uint64_t t6; -} TrapFrame; +} TrapFrame_t; -Cpu *mycpu(void); +struct Cpu *mycpu(void); -extern Cpu cpus[NCPU]; +extern struct Cpu cpus[NCPU]; /** Per-process state */ struct Proc {}; - -#endif diff --git a/kern/start.c b/kern/start.c index 15a224f..e717064 100644 --- a/kern/start.c +++ b/kern/start.c @@ -1,12 +1,10 @@ #include #include #include -#include #include #include #include #include -#include #include /** @@ -39,24 +37,30 @@ void start() { if (id == 0) { /* Here we will do a bunch of initialization steps */ kalloc_init(); + uart_puts("Hello Neptune!\n"); spinlock_init(&sl); - kprintf("Hello Neptune!\n"); - - __sync_synchronize(); hold = 0; } else { while (hold); } + // spin_lock(&sl); + // + // uart_puts("Hart number: "); + // uart_putc(id + '0'); + // uart_putc('\n'); + // + // spin_unlock(&sl); + if (id == 0) { spin_lock(&sl); - kprintf("Core count: %d\n", max_hart); - - if (max_hart == NCPU) - kprintf("All cores up!\n"); - else - PANIC("Some cores seem to have been enumerated incorrectly!\n"); - + uart_puts("Core count: "); + uart_putc(max_hart + '0'); + uart_putc('\n'); + if (max_hart == NCPU) { + uart_puts("All cores up!"); + uart_putc('\n'); + } spin_unlock(&sl); }