From f1f480251c1364a93d556f2803657bdf6187c828 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 06:19:43 +0200 Subject: [PATCH 1/6] Move panic into its own translation unit --- lib/panic.c | 8 ++++++++ lib/panic.h | 6 ++++++ lib/spinlock.c | 4 +--- 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 lib/panic.c create mode 100644 lib/panic.h diff --git a/lib/panic.c b/lib/panic.c new file mode 100644 index 0000000..f0df22e --- /dev/null +++ b/lib/panic.c @@ -0,0 +1,8 @@ +#include +volatile int panicked; + +void panic(char *s) { + panicked = 1; + uart_puts(s); + while (1); +} diff --git a/lib/panic.h b/lib/panic.h new file mode 100644 index 0000000..77cd640 --- /dev/null +++ b/lib/panic.h @@ -0,0 +1,6 @@ +#ifndef KERNEL_PANIC_H +#define KERNEL_PANIC_H + +void panic(char *s); + +#endif diff --git a/lib/spinlock.c b/lib/spinlock.c index 7e7418d..3f3e0d7 100644 --- a/lib/spinlock.c +++ b/lib/spinlock.c @@ -4,13 +4,11 @@ */ // #include +#include #include #include #include -// void panic(char *s) { for (;;); } -void panic(char *s) {} - /** * The aquire() and release() functions control ownership of the lock. * To perform these operations, modern CPU's provide atomic instructions From f48e74bebe7fe33b0a86952a08c59eba8f65273e Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 06:19:58 +0200 Subject: [PATCH 2/6] Cosmetic fixes in spinlock --- lib/spinlock.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/spinlock.c b/lib/spinlock.c index 3f3e0d7..595432e 100644 --- a/lib/spinlock.c +++ b/lib/spinlock.c @@ -58,8 +58,8 @@ void acquire(struct spinlock *lk) { panic("acquire"); // Spin until aquired. See file header for details - while (__sync_lock_test_and_set(&lk->locked, 1) != 0) { - } + 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. From 74fdd267590bb8854406d3d652ebad6628feb7a9 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 06:22:16 +0200 Subject: [PATCH 3/6] Move uart into its own translation unit --- lib/uart.c | 9 +++++++++ lib/uart.h | 10 ++++++++++ start.c | 12 +----------- 3 files changed, 20 insertions(+), 11 deletions(-) create mode 100644 lib/uart.c create mode 100644 lib/uart.h diff --git a/lib/uart.c b/lib/uart.c new file mode 100644 index 0000000..864840a --- /dev/null +++ b/lib/uart.c @@ -0,0 +1,9 @@ +/* QEMU memory maps a UART device here. */ +#define UART_BASE ((volatile char *)0x10000000) + +void uart_putc(char c) { *UART_BASE = c; } + +void uart_puts(const char *s) { + while (*s) uart_putc(*s++); +} + diff --git a/lib/uart.h b/lib/uart.h new file mode 100644 index 0000000..953c5f6 --- /dev/null +++ b/lib/uart.h @@ -0,0 +1,10 @@ +#ifndef UART_KERNEL_H +#define UART_KERNEL_H + +/** 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/start.c b/start.c index 0a9ca9b..425059a 100644 --- a/start.c +++ b/start.c @@ -2,17 +2,7 @@ #include #include #include - -/* QEMU memory maps a UART device here. */ -#define UART_BASE ((volatile char *)0x10000000) - -/** Send a single character to the UART device */ -void uart_putc(char c) { *UART_BASE = c; } - -/** Send a **NULL TERMINATED** string to the UART device */ -void uart_puts(const char *s) { - while (*s) uart_putc(*s++); -} +#include /** * Allocate one stack per CPU (hart). From 93295eaeb861af33829fb237008a62b89397a9bf Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 06:22:59 +0200 Subject: [PATCH 4/6] Add uart and panic to kernel dependency list --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8d0e4a2..9636a7f 100644 --- a/Makefile +++ b/Makefile @@ -27,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 +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 $@ $^ From 58bface3c9c5d27f0db1f087f0d8320396f29f4b Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 06:24:01 +0200 Subject: [PATCH 5/6] Fix nasty bug related to spinlock panic when lock is already held, because the tread pointer was never set on a per-hart basis. --- riscv.h | 3 +++ start.c | 11 +++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/riscv.h b/riscv.h index b614112..91b17b0 100644 --- a/riscv.h +++ b/riscv.h @@ -40,6 +40,9 @@ static inline u64 r_tp() { return x; } +/** Write thread pointer */ +static inline void w_tp(u64 x) { asm volatile("mv tp, %0" : : "r"(x)); } + /** * Read the value of the sstatus register. * (Supervisor Status Register) diff --git a/start.c b/start.c index 425059a..656ac65 100644 --- a/start.c +++ b/start.c @@ -1,6 +1,8 @@ #include +#include #include #include +#include #include #include @@ -18,8 +20,13 @@ volatile int greeted = 0; /* This is where entry.S drops us of. All cores land here */ void start() { + u64 id = r_mhartid(); - u64 a = 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). + w_tp(id); acquire(&sl); @@ -29,7 +36,7 @@ void start() { } uart_puts("Hart number: "); - uart_putc(a + '0'); + uart_putc(id + '0'); uart_putc('\n'); release(&sl); From 5dbcb337266595a63e745c03981d4b62f5608b2a Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Jun 2025 06:26:25 +0200 Subject: [PATCH 6/6] Some vocabulary in readme --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 3badd28..9c6acae 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,23 @@ For a quick reference on RISC-V assembly: Toolchains: - https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack - https://github.com/xpack-dev-tools/qemu-riscv-xpack/ + +--- + +| Register | Name | Privilege Level | Description | +|-------------|----------------------------|------------------|-----------------------------------------------------------------------------| +| `mstatus` | Machine Status Register | Machine | Holds global interrupt enable, previous privilege mode, etc. | +| `mtvec` | Machine Trap-Vector Base | Machine | Holds the base address of the trap handler (exception/interrupt entry). | +| `mepc` | Machine Exception PC | Machine | Stores the program counter at the time of the last trap. | +| `mcause` | Machine Cause Register | Machine | Indicates the cause of the last trap (interrupt or exception). | +| `satp` | Supervisor Address Translation and Protection | Supervisor | Controls page table base address and mode (e.g., Sv39, Sv48). | +| `sstatus` | Supervisor Status Register | Supervisor | Like `mstatus`, but accessible from supervisor mode. | +| `stvec` | Supervisor Trap-Vector Base| Supervisor | Like `mtvec`, but for supervisor mode traps. | +| `sepc` | Supervisor Exception PC | Supervisor | Like `mepc`, but for supervisor mode. | +| `scause` | Supervisor Cause Register | Supervisor | Like `mcause`, but for supervisor mode traps. | +| `sscratch` | Supervisor Scratch | Supervisor | Can be used to store temporary state across traps in supervisor mode. | +| `mscratch` | Machine Scratch | Machine | Like `sscratch`, but in machine mode. | +| `mcycle` | Machine Cycle Counter | Machine | Counts the number of cycles executed. | +| `mtime` | Machine Timer Register | Machine (via memory-mapped) | Used for timing and scheduling (not a CSR, but a memory-mapped register). | +| `mip` | Machine Interrupt Pending | Machine | Indicates pending interrupts. | +| `mie` | Machine Interrupt Enable | Machine | Controls which interrupts are enabled. |