diff --git a/.clang-format b/.clang-format index a4fbf68..5ff1461 100644 --- a/.clang-format +++ b/.clang-format @@ -13,4 +13,3 @@ AlignConsecutiveDeclarations: AlignCompound: false AlignFunctionPointers: false PadOperators: false -AlignConsecutiveMacros: true diff --git a/Makefile b/Makefile index 3d29dc2..9636a7f 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,6 @@ CFLAGS += -ffreestanding -fno-common -nostdlib -mno-relax CFLAGS += -I. CFLAGS += -Ilib -CFLAGS += -Ikern CFLAGS += -fno-stack-protector # Prevents code that needs libc / runtime support CFLAGS += -MD # Generate header dependency files (.d) @@ -28,7 +27,7 @@ CFLAGS += -fno-omit-frame-pointer # More reliable backtraces in GDB all: kernel.elf -kernel.elf: entry.o start.o lib/string.o lib/proc.o lib/spinlock.o lib/proc.o lib/uart.o lib/panic.o kern/kalloc.o lib/memory.o +kernel.elf: entry.o start.o lib/string.o lib/proc.o lib/spinlock.o lib/proc.o lib/uart.o lib/panic.o @echo LD $@ @$(LD) $(LDFLAGS) -o $@ $^ diff --git a/README.md b/README.md index ec8b689..9c6acae 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # Neptune Kernel -Inspired by xv6 - For a quick reference on RISC-V assembly: - https://risc-v.guru/instructions/ @@ -11,10 +9,6 @@ Toolchains: --- -> A word on terminology: Although the official x86 term is exception, xv6 uses the -> term trap, largely because it was the term used by the PDP11/40 and therefore is the -> conventional Unix term. - | Register | Name | Privilege Level | Description | |-------------|----------------------------|------------------|-----------------------------------------------------------------------------| | `mstatus` | Machine Status Register | Machine | Holds global interrupt enable, previous privilege mode, etc. | diff --git a/kern/kalloc.c b/kern/kalloc.c deleted file mode 100644 index 6cd329d..0000000 --- a/kern/kalloc.c +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -// Physical memory allocator, for user processes, -// kernel stacks, page-table pages, -// and pipe buffers. Allocates whole 4096-byte pages. - -/** Free list of physical pages. */ -void freerange(void *physaddr_start, void *physaddr_end); - -/** First address after kernel. Provided kernel.ld */ -extern char kernel_end[]; - -/** A run is a node in the free list. */ -struct Run { - struct Run *next; -}; - -/** Kernel memory allocator. */ -struct { - struct Spinlock lock; - struct Run *freelist; -} kmem; - -void kalloc_init() { - initlock(&kmem.lock, "kmem"); - freerange(kernel_end, (void *)PHYSTOP); -} - -void freerange(void *physaddr_start, void *physaddr_end) { - char *p; - p = (char *)PGROUNDUP((u64)physaddr_start); - for (; p + PGSIZE <= (char *)physaddr_end; p += PGSIZE) kfree(p); -} - -void kfree(void *pa) { - struct Run *r; - - // 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"); - - // 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); -} - -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/kern/kalloc.h b/kern/kalloc.h deleted file mode 100644 index 0f13e53..0000000 --- a/kern/kalloc.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef KALLOC_KERNEL_H -#define KALLOC_KERNEL_H - -/** - * 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 kalloc_init(void); - -#endif diff --git a/kernel.ld b/kernel.ld index af3fd7c..1836834 100644 --- a/kernel.ld +++ b/kernel.ld @@ -74,7 +74,7 @@ SECTIONS } /* Define symbol end as current location, note that this is not aligned, see vm.c */ - PROVIDE(kernel_end = .); + PROVIDE(end = .); } PHDRS { diff --git a/lib/endian.h b/lib/endian.h deleted file mode 100644 index e6ea172..0000000 --- a/lib/endian.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef ENDIAN_KERNEL_H -#define ENDIAN_KERNEL_H - -#include - -/** Swap byte order of 16-bit value */ -static inline u16 swap16(u16 x) { return (x >> 8) | (x << 8); } - -/** Swap byte order of 32-bit value */ -static inline u32 swap32(u32 x) { - return ((x >> 24) & 0x000000ff) | ((x >> 8) & 0x0000ff00) | - ((x << 8) & 0x00ff0000) | ((x << 24) & 0xff000000); -} - -/** Swap byte order of 64-bit value */ -static inline u64 swap64(u64 x) { - return ((x >> 56) & 0x00000000000000ffULL) | - ((x >> 40) & 0x000000000000ff00ULL) | - ((x >> 24) & 0x0000000000ff0000ULL) | - ((x >> 8) & 0x00000000ff000000ULL) | - ((x << 8) & 0x000000ff00000000ULL) | - ((x << 24) & 0x0000ff0000000000ULL) | - ((x << 40) & 0x00ff000000000000ULL) | - ((x << 56) & 0xff00000000000000ULL); -} - -#ifdef __LITTLE_ENDIAN__ -/** Convert 16-bit value to little-endian */ -static inline u16 to_le16(u16 x) { return x; } -/** Convert 16-bit little-endian value to host */ -static inline u16 from_le16(u16 x) { return x; } - -/** Convert 32-bit value to little-endian */ -static inline u32 to_le32(u32 x) { return x; } -/** Convert 32-bit little-endian value to host */ -static inline u32 from_le32(u32 x) { return x; } - -/** Convert 64-bit value to little-endian */ -static inline u64 to_le64(u64 x) { return x; } -/** Convert 64-bit little-endian value to host */ -static inline u64 from_le64(u64 x) { return x; } - -/** Convert 16-bit value to big-endian */ -static inline u16 to_be16(u16 x) { return swap16(x); } -/** Convert 16-bit big-endian value to host */ -static inline u16 from_be16(u16 x) { return swap16(x); } - -/** Convert 32-bit value to big-endian */ -static inline u32 to_be32(u32 x) { return swap32(x); } -/** Convert 32-bit big-endian value to host */ -static inline u32 from_be32(u32 x) { return swap32(x); } - -/** Convert 64-bit value to big-endian */ -static inline u64 to_be64(u64 x) { return swap64(x); } -/** Convert 64-bit big-endian value to host */ -static inline u64 from_be64(u64 x) { return swap64(x); } - -#else // Big-endian - -/** Convert 16-bit value to little-endian */ -static inline u16 to_le16(u16 x) { return swap16(x); } -/** Convert 16-bit little-endian value to host */ -static inline u16 from_le16(u16 x) { return swap16(x); } - -/** Convert 32-bit value to little-endian */ -static inline u32 to_le32(u32 x) { return swap32(x); } -/** Convert 32-bit little-endian value to host */ -static inline u32 from_le32(u32 x) { return swap32(x); } - -/** Convert 64-bit value to little-endian */ -static inline u64 to_le64(u64 x) { return swap64(x); } -/** Convert 64-bit little-endian value to host */ -static inline u64 from_le64(u64 x) { return swap64(x); } - -/** Convert 16-bit value to big-endian */ -static inline u16 to_be16(u16 x) { return x; } -/** Convert 16-bit big-endian value to host */ -static inline u16 from_be16(u16 x) { return x; } - -/** Convert 32-bit value to big-endian */ -static inline u32 to_be32(u32 x) { return x; } -/** Convert 32-bit big-endian value to host */ -static inline u32 from_be32(u32 x) { return x; } - -/** Convert 64-bit value to big-endian */ -static inline u64 to_be64(u64 x) { return x; } -/** Convert 64-bit big-endian value to host */ -static inline u64 from_be64(u64 x) { return x; } - -#endif // __LITTLE_ENDIAN__ - -#endif // ENDIAN_KERNEL_H diff --git a/lib/memory.c b/lib/memory.c deleted file mode 100644 index a1cb090..0000000 --- a/lib/memory.c +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include -#include - -#define MAX_PROBE_SIZE (256 * 1024 * 1024) // Probe up to 256 MiB max -#define PROBE_STEP 0x1000 // Probe every 4 KiB page - -size_t probe_memory(void) { - volatile u32 *addr; - u32 test_pattern = 0xA5A5A5A5; - size_t detected = 0; - - for (size_t offset = 4096 * 16; offset < MAX_PROBE_SIZE; - offset += PROBE_STEP) { - addr = (volatile u32 *)(KERNBASE + offset); - - u32 old = *addr; - *addr = test_pattern; - - if (*addr != test_pattern) { - break; // Memory not readable/writable here, stop probing - } - - *addr = old; // restore original data - detected = offset + PROBE_STEP; - } - - return detected; -} diff --git a/lib/memory.h b/lib/memory.h deleted file mode 100644 index 8a012a2..0000000 --- a/lib/memory.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef MEMORY_KERNEL_H -#define MEMORY_KERNEL_H - -#include - -/* These are hardcoded for now */ -#define KERNBASE 0x80000000L -#define PHYSTOP (KERNBASE + 128 * 1024 * 1024) - -/** - * Returns size in bytes of detected RAM In qemu, it requires a trap handler to - * handle the interrupt when accessing unavailable memory. - */ -size_t probe_memory(void); - -#endif diff --git a/lib/proc.c b/lib/proc.c index a55c8d6..aa28a41 100644 --- a/lib/proc.c +++ b/lib/proc.c @@ -2,7 +2,18 @@ struct Cpu cpus[NCPU]; -/** - * Return this CPU's cpu struct. Interrupts must be disabled. - */ -inline struct Cpu *mycpu(void) { return &cpus[read_tp()]; } +// 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; +} diff --git a/lib/proc.h b/lib/proc.h index 1208b2b..67749a2 100644 --- a/lib/proc.h +++ b/lib/proc.h @@ -3,6 +3,7 @@ #include #include +int cpuid(void); struct Cpu *mycpu(void); /** Saved registers for kernel context switches. */ diff --git a/lib/spinlock.c b/lib/spinlock.c index 5277a8b..595432e 100644 --- a/lib/spinlock.c +++ b/lib/spinlock.c @@ -39,8 +39,8 @@ * On RISC-V, this emits a fence instruction. */ -/** Initialize Spinlock */ -void initlock(struct Spinlock *lk, char *name) { +/** Initialize spinlock */ +void initlock(struct spinlock *lk, char *name) { lk->name = name; lk->locked = 0; lk->cpu = 0; @@ -51,7 +51,7 @@ void initlock(struct Spinlock *lk, char *name) { * Loops (spins) until the lock is acquired. * Panics if the lock is already held by this cpu. */ -void acquire(struct Spinlock *lk) { +void acquire(struct spinlock *lk) { push_off(); // disable interrupts to avoid deadlock. if (holding(lk)) // If the lock is already held, panic. @@ -70,7 +70,7 @@ void acquire(struct Spinlock *lk) { * Release the lock. * Panics if the lock is not held. */ -void release(struct Spinlock *lk) { +void release(struct spinlock *lk) { if (!holding(lk)) // If the lock is not held, panic. panic("release"); @@ -83,7 +83,7 @@ void release(struct Spinlock *lk) { // Check whether this cpu is holding the lock. // Interrupts must be off. -int holding(struct Spinlock *lk) { +int holding(struct spinlock *lk) { int r; r = (lk->locked && lk->cpu == mycpu()); return r; diff --git a/lib/spinlock.h b/lib/spinlock.h index eb4f4b3..48bcb27 100644 --- a/lib/spinlock.h +++ b/lib/spinlock.h @@ -1,10 +1,10 @@ -#ifndef KERNEL_Spinlock_H -#define KERNEL_Spinlock_H +#ifndef KERNEL_SPINLOCK_H +#define KERNEL_SPINLOCK_H #include "types.h" /** Mutual exclusion spin lock */ -struct Spinlock { +struct spinlock { u32 locked; // Is the lock held? // NOTE: Perhaps feature gate this? @@ -19,24 +19,24 @@ struct Spinlock { * Loops (spins) until the lock is acquired. * Panics if the lock is already held by this cpu. */ -void acquire(struct Spinlock *); +void acquire(struct spinlock *); /** * Check whether this cpu is holding the lock. * Interrupts must be off. */ -int holding(struct Spinlock *); +int holding(struct spinlock *); /** - * Initialize Spinlock + * Initialize spinlock */ -void initlock(struct Spinlock *, char *); +void initlock(struct spinlock *, char *); /** * Release the lock. * Panics if the lock is not held. */ -void release(struct Spinlock *); +void release(struct spinlock *); /** * @brief push_off/pop_off are like intr_off()/intr_on() except that they are diff --git a/lib/string.c b/lib/string.c index 69fb5c7..7647d1f 100644 --- a/lib/string.c +++ b/lib/string.c @@ -1,10 +1,8 @@ -#include - char *itoa(int value, char *str, int base) { - char *p = str; - char *p1, *p2; + char *p = str; + char *p1, *p2; unsigned int uvalue = value; - int negative = 0; + int negative = 0; if (base < 2 || base > 36) { *str = '\0'; @@ -39,61 +37,3 @@ char *itoa(int value, char *str, int base) { return str; } - -void *memset(void *dst, int c, size_t length) { - u8 *ptr = (u8 *)dst; - const u8 value = (u8)c; - - while (length--) *(ptr++) = value; - - return dst; -} - -void *memcpy(void *dst, const void *src, size_t len) { - u8 *d = (u8 *)dst; - const u8 *s = (const u8 *)src; - for (size_t i = 0; i < len; i++) { - d[i] = s[i]; - } - return dst; -} - -void *memmove(void *dst, const void *src, size_t len) { - u8 *d = (u8 *)dst; - const u8 *s = (const u8 *)src; - if (d < s) { - for (size_t i = 0; i < len; i++) { - d[i] = s[i]; - } - } else if (d > s) { - for (size_t i = len; i > 0; i--) { - d[i - 1] = s[i - 1]; - } - } - return dst; -} - -int memcmp(const void *s1, const void *s2, size_t len) { - const u8 *a = (const u8 *)s1; - const u8 *b = (const u8 *)s2; - for (size_t i = 0; i < len; i++) { - if (a[i] != b[i]) { - return (int)a[i] - (int)b[i]; - } - } - return 0; -} - -size_t strlen(const char *s) { - const char *p = s; - while (*p) ++p; - return (size_t)(p - s); -} - -size_t strnlen(const char *s, size_t maxlen) { - size_t len = 0; - while (len < maxlen && s[len] != '\0') { - len++; - } - return len; -} diff --git a/lib/string.h b/lib/string.h index 00805c7..ceeb2a8 100644 --- a/lib/string.h +++ b/lib/string.h @@ -1,40 +1,7 @@ #ifndef KERNEL_STRING_H #define KERNEL_STRING_H -#include - /** Integer to ascii */ char *itoa(int value, char *str, int base); -/** Fill memory with constant byte */ -void *memset(void *dst, int c, size_t len); - -/** Copy `len` bytes from `src` to `dst`. Undefined if regions overlap. */ -void *memcpy(void *dst, const void *src, size_t len); - -/** Copy `len` bytes from `src` to `dst`, safe for overlapping regions. */ -void *memmove(void *dst, const void *src, size_t len); - -/** Compare `len` bytes of `s1` and `s2`. - * Returns 0 if equal, <0 if s1 < s2, >0 if s1 > s2. */ -int memcmp(const void *s1, const void *s2, size_t len); - -/** Returns the length of a null-terminated string */ -size_t strlen(const char *s); - -/** Return length of string `s`, up to a max of `maxlen` bytes */ -size_t strnlen(const char *s, size_t maxlen); - -// TODO: These: -/* -int strcmp(const char *s1, const char *s2); -int strncmp(const char *s1, const char *s2, size_t n); - -char *strcpy(char *dst, const char *src); -char *strncpy(char *dst, const char *src, size_t n); - -char *strchr(const char *s, int c); -char *strrchr(const char *s, int c); -*/ - #endif diff --git a/lib/uart.c b/lib/uart.c index 83b2656..864840a 100644 --- a/lib/uart.c +++ b/lib/uart.c @@ -6,3 +6,4 @@ void uart_putc(char c) { *UART_BASE = c; } void uart_puts(const char *s) { while (*s) uart_putc(*s++); } + diff --git a/riscv.h b/riscv.h index 3114b79..91b17b0 100644 --- a/riscv.h +++ b/riscv.h @@ -3,20 +3,22 @@ #include -/** 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)) - // Supervisor Status Register, sstatus -#define SSTATUS_SPP (1L << 8) /** Supervisor Previous Privilege 1=S, 0=U */ -#define SSTATUS_SPIE (1L << 5) /** Supervisor Previous Interrupt Enable */ -#define SSTATUS_UPIE (1L << 4) /** User Previous Interrupt Enable */ -#define SSTATUS_SIE (1L << 1) /** Supervisor Interrupt Enable */ -#define SSTATUS_UIE (1L << 0) /** User Interrupt Enable */ + +/** 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) /** Page Table Entry Type */ typedef u64 pte_t; @@ -24,60 +26,22 @@ typedef u64 pte_t; /** Page Table Type */ typedef u64 *pagetable_t; // 512 PTEs -// CSR numeric addresses -#define CSR_MSTATUS 0x300 -#define CSR_MISA 0x301 -#define CSR_MIE 0x304 -#define CSR_MTVEC 0x305 -#define CSR_MCOUNTEREN 0x306 - -#define CSR_MSCRATCH 0x340 -#define CSR_MEPC 0x341 -#define CSR_MCAUSE 0x342 -#define CSR_MTVAL 0x343 -#define CSR_MIP 0x344 - -#define CSR_MHARTID 0xF14 - -static inline u64 read_csr(u32 csr) { - u64 value; - asm volatile("csrr %0, %1" : "=r"(value) : "i"(csr)); - return value; -} - -static inline void write_csr(u32 csr, u64 value) { - asm volatile("csrw %0, %1" ::"i"(csr), "r"(value)); -} - -static inline u64 read_mstatus(void) { return read_csr(CSR_MSTATUS); } -static inline void write_mstatus(u64 val) { write_csr(CSR_MSTATUS, val); } - -static inline u64 read_mcause(void) { return read_csr(CSR_MCAUSE); } -static inline u64 read_mtval(void) { return read_csr(CSR_MTVAL); } -static inline u64 read_mepc(void) { return read_csr(CSR_MEPC); } -static inline void write_mepc(u64 val) { write_csr(CSR_MEPC, val); } - -static inline void write_mtvec(u64 val) { write_csr(CSR_MTVEC, val); } -static inline u64 read_mtvec(void) { return read_csr(CSR_MTVEC); } - /** Returns the current hart id */ -static inline u64 read_mhartid(void) { return read_csr(CSR_MHARTID); } - -// static inline u64 r_mhartid() { -// u64 x; -// asm volatile("csrr %0, mhartid" : "=r"(x)); -// return x; -// } +static inline u64 r_mhartid() { + u64 x; + asm volatile("csrr %0, mhartid" : "=r"(x)); + return x; +} /** Read thread pointer */ -static inline u64 read_tp() { +static inline u64 r_tp() { u64 x; asm volatile("mv %0, tp" : "=r"(x)); return x; } /** Write thread pointer */ -static inline void write_tp(u64 x) { asm volatile("mv tp, %0" : : "r"(x)); } +static inline void w_tp(u64 x) { asm volatile("mv tp, %0" : : "r"(x)); } /** * Read the value of the sstatus register. diff --git a/start.c b/start.c index d9ca8ef..656ac65 100644 --- a/start.c +++ b/start.c @@ -1,9 +1,8 @@ #include -#include -#include #include #include #include +#include #include #include @@ -16,18 +15,18 @@ char stack0[4096 * NCPU] __attribute__((aligned(16))); /* Keep this here and sync on it until we have synchronized printf */ -struct Spinlock sl = {0}; +struct spinlock sl = {0}; volatile int greeted = 0; /* This is where entry.S drops us of. All cores land here */ void start() { - u64 id = read_mhartid(); + u64 id = r_mhartid(); // Keep each CPU's hartid in its tp (thread pointer) register, for cpuid(). // This can then be retrieved with r_wp or cpuid(). It is used to index the // cpus[] array in mycpu(), which in turn holds state for each individual // cpu (struct Cpu). - write_tp(id); + w_tp(id); acquire(&sl); @@ -42,10 +41,7 @@ void start() { release(&sl); - if (id == 0) { - /* Here we will do a bunch of initialization steps */ - kalloc_init(); - } + /* Here we will do a bunch of initialization steps */ // We should not arrive here, but if we do, hang in a while on wfi. while (1) __asm__ volatile("wfi"); // (Wait For Interrupt) diff --git a/types.h b/types.h index cd25ff8..f0780a4 100644 --- a/types.h +++ b/types.h @@ -4,4 +4,3 @@ typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long u64; -typedef u64 size_t;