From 952eeebb6dfe12cd831b8ba0a53ceab62a603f48 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 10:06:51 +0200 Subject: [PATCH 01/13] Remove trailing extra line --- lib/uart.c | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/uart.c b/lib/uart.c index 864840a..83b2656 100644 --- a/lib/uart.c +++ b/lib/uart.c @@ -6,4 +6,3 @@ void uart_putc(char c) { *UART_BASE = c; } void uart_puts(const char *s) { while (*s) uart_putc(*s++); } - From 4e73eee53edb71cd6f1f5aec8824d8809fbdfb5f Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 10:07:31 +0200 Subject: [PATCH 02/13] Formatting: string.c --- lib/string.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/string.c b/lib/string.c index 7647d1f..8057811 100644 --- a/lib/string.c +++ b/lib/string.c @@ -1,8 +1,8 @@ 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'; From 697a84b370158def03636e66bed5080cdcd303bb Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 10:28:31 +0200 Subject: [PATCH 03/13] README: Mention xv6, notes on terminology from xv6 book --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 9c6acae..ec8b689 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Neptune Kernel +Inspired by xv6 + For a quick reference on RISC-V assembly: - https://risc-v.guru/instructions/ @@ -9,6 +11,10 @@ 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. | From be50854251ddd73d0830431ffac5407faa99d6a0 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 10:29:40 +0200 Subject: [PATCH 04/13] proc.c: Simplify mycpu(), remove cpuid() for now as it is only a front for r_tp() --- lib/proc.c | 19 ++++--------------- lib/proc.h | 1 - 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/lib/proc.c b/lib/proc.c index aa28a41..73aec31 100644 --- a/lib/proc.c +++ b/lib/proc.c @@ -2,18 +2,7 @@ struct Cpu cpus[NCPU]; -// 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 this CPU's cpu struct. Interrupts must be disabled. + */ +inline struct Cpu *mycpu(void) { return &cpus[r_tp()]; } diff --git a/lib/proc.h b/lib/proc.h index 67749a2..1208b2b 100644 --- a/lib/proc.h +++ b/lib/proc.h @@ -3,7 +3,6 @@ #include #include -int cpuid(void); struct Cpu *mycpu(void); /** Saved registers for kernel context switches. */ From 521217f2b59e408e564aab87bb224423d7ea38f8 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 12:01:53 +0200 Subject: [PATCH 05/13] String.[ch]: memset, memcpy, memmove, memcmph and strlen, strnlen --- lib/string.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/string.h | 33 +++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/lib/string.c b/lib/string.c index 8057811..69fb5c7 100644 --- a/lib/string.c +++ b/lib/string.c @@ -1,3 +1,5 @@ +#include + char *itoa(int value, char *str, int base) { char *p = str; char *p1, *p2; @@ -37,3 +39,61 @@ 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 ceeb2a8..00805c7 100644 --- a/lib/string.h +++ b/lib/string.h @@ -1,7 +1,40 @@ #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 From 71ff1371927bd95d2f5367aba9a180214fa0aac8 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 12:02:41 +0200 Subject: [PATCH 06/13] Rename struct: spinlock -> Spinlock --- lib/spinlock.c | 10 +++++----- lib/spinlock.h | 16 ++++++++-------- start.c | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/spinlock.c b/lib/spinlock.c index 595432e..5277a8b 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 48bcb27..eb4f4b3 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/start.c b/start.c index 656ac65..8d369f9 100644 --- a/start.c +++ b/start.c @@ -15,7 +15,7 @@ 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 */ From e82b75f8f06e3fdf40b32e24d385e77b22f96709 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 12:03:02 +0200 Subject: [PATCH 07/13] Header: endian.h with endianness conversion functions --- lib/endian.h | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 lib/endian.h diff --git a/lib/endian.h b/lib/endian.h new file mode 100644 index 0000000..e6ea172 --- /dev/null +++ b/lib/endian.h @@ -0,0 +1,92 @@ +#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 From f2685ce7dfee73e1d1f057130f3f320f8af46d9b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 12:04:11 +0200 Subject: [PATCH 08/13] Symbol rename in linker.ld: end -> kernel_end --- kernel.ld | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel.ld b/kernel.ld index 1836834..af3fd7c 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(end = .); + PROVIDE(kernel_end = .); } PHDRS { From 58ed87340168df1d2153b39aef966f1acf070f92 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 12:04:40 +0200 Subject: [PATCH 09/13] New type in types.h: size_t (u64) --- types.h | 1 + 1 file changed, 1 insertion(+) diff --git a/types.h b/types.h index f0780a4..cd25ff8 100644 --- a/types.h +++ b/types.h @@ -4,3 +4,4 @@ typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long u64; +typedef u64 size_t; From bd7959cc6ca265b7d848786982ac8ee944197911 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 13:27:35 +0200 Subject: [PATCH 10/13] kalloc.[ch] from xv6, memory.[ch] containing memory probing algorithm and constants --- Makefile | 3 ++- kern/kalloc.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++ kern/kalloc.h | 33 +++++++++++++++++++++++ lib/memory.c | 29 ++++++++++++++++++++ lib/memory.h | 16 +++++++++++ 5 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 kern/kalloc.c create mode 100644 kern/kalloc.h create mode 100644 lib/memory.c create mode 100644 lib/memory.h diff --git a/Makefile b/Makefile index 9636a7f..3d29dc2 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ 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) @@ -27,7 +28,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 +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 @echo LD $@ @$(LD) $(LDFLAGS) -o $@ $^ diff --git a/kern/kalloc.c b/kern/kalloc.c new file mode 100644 index 0000000..6cd329d --- /dev/null +++ b/kern/kalloc.c @@ -0,0 +1,75 @@ +#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 new file mode 100644 index 0000000..0f13e53 --- /dev/null +++ b/kern/kalloc.h @@ -0,0 +1,33 @@ +#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/lib/memory.c b/lib/memory.c new file mode 100644 index 0000000..a1cb090 --- /dev/null +++ b/lib/memory.c @@ -0,0 +1,29 @@ +#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 new file mode 100644 index 0000000..8a012a2 --- /dev/null +++ b/lib/memory.h @@ -0,0 +1,16 @@ +#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 From 00bc2aa300846876b251aad4a7cfba57b6399140 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 13:28:08 +0200 Subject: [PATCH 11/13] Use new API 'read_tp()' in proc.c mycpu() --- lib/proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/proc.c b/lib/proc.c index 73aec31..a55c8d6 100644 --- a/lib/proc.c +++ b/lib/proc.c @@ -5,4 +5,4 @@ struct Cpu cpus[NCPU]; /** * Return this CPU's cpu struct. Interrupts must be disabled. */ -inline struct Cpu *mycpu(void) { return &cpus[r_tp()]; } +inline struct Cpu *mycpu(void) { return &cpus[read_tp()]; } From b8474a12fcbf26684f2831b4f2fc45400455eb24 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 13:28:43 +0200 Subject: [PATCH 12/13] .clang-format: AlignConsecutiveMacros: true --- .clang-format | 1 + 1 file changed, 1 insertion(+) diff --git a/.clang-format b/.clang-format index 5ff1461..a4fbf68 100644 --- a/.clang-format +++ b/.clang-format @@ -13,3 +13,4 @@ AlignConsecutiveDeclarations: AlignCompound: false AlignFunctionPointers: false PadOperators: false +AlignConsecutiveMacros: true From 2aa06778b382cda2a6a764edd60348776509ddc2 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 13:30:45 +0200 Subject: [PATCH 13/13] riscv.h, start.c: Some changed and some new machine specific routines --- riscv.h | 80 +++++++++++++++++++++++++++++++++++++++++---------------- start.c | 12 ++++++--- 2 files changed, 66 insertions(+), 26 deletions(-) diff --git a/riscv.h b/riscv.h index 91b17b0..3114b79 100644 --- a/riscv.h +++ b/riscv.h @@ -3,22 +3,20 @@ #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 - -/** 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) +#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 */ /** Page Table Entry Type */ typedef u64 pte_t; @@ -26,22 +24,60 @@ typedef u64 pte_t; /** Page Table Type */ typedef u64 *pagetable_t; // 512 PTEs -/** Returns the current hart id */ -static inline u64 r_mhartid() { - u64 x; - asm volatile("csrr %0, mhartid" : "=r"(x)); - return x; +// 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; +// } + /** Read thread pointer */ -static inline u64 r_tp() { +static inline u64 read_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)); } +static inline void write_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 8d369f9..d9ca8ef 100644 --- a/start.c +++ b/start.c @@ -1,8 +1,9 @@ #include +#include +#include #include #include #include -#include #include #include @@ -20,13 +21,13 @@ volatile int greeted = 0; /* This is where entry.S drops us of. All cores land here */ void start() { - u64 id = r_mhartid(); + u64 id = read_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). - w_tp(id); + write_tp(id); acquire(&sl); @@ -41,7 +42,10 @@ void start() { release(&sl); - /* Here we will do a bunch of initialization steps */ + if (id == 0) { + /* Here we will do a bunch of initialization steps */ + kalloc_init(); + } // We should not arrive here, but if we do, hang in a while on wfi. while (1) __asm__ volatile("wfi"); // (Wait For Interrupt)