Compare commits

...

6 commits

9 changed files with 70 additions and 19 deletions

View file

@ -27,7 +27,7 @@ CFLAGS += -fno-omit-frame-pointer # More reliable backtraces in GDB
all: kernel.elf 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 $@ @echo LD $@
@$(LD) $(LDFLAGS) -o $@ $^ @$(LD) $(LDFLAGS) -o $@ $^

View file

@ -6,3 +6,23 @@ For a quick reference on RISC-V assembly:
Toolchains: Toolchains:
- https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack - https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack
- https://github.com/xpack-dev-tools/qemu-riscv-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. |

8
lib/panic.c Normal file
View file

@ -0,0 +1,8 @@
#include <uart.h>
volatile int panicked;
void panic(char *s) {
panicked = 1;
uart_puts(s);
while (1);
}

6
lib/panic.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef KERNEL_PANIC_H
#define KERNEL_PANIC_H
void panic(char *s);
#endif

View file

@ -4,13 +4,11 @@
*/ */
// #include <lib/stdio.h> // #include <lib/stdio.h>
#include <panic.h>
#include <proc.h> #include <proc.h>
#include <riscv.h> #include <riscv.h>
#include <spinlock.h> #include <spinlock.h>
// void panic(char *s) { for (;;); }
void panic(char *s) {}
/** /**
* The aquire() and release() functions control ownership of the lock. * The aquire() and release() functions control ownership of the lock.
* To perform these operations, modern CPU's provide atomic instructions * To perform these operations, modern CPU's provide atomic instructions
@ -60,8 +58,8 @@ void acquire(struct spinlock *lk) {
panic("acquire"); panic("acquire");
// Spin until aquired. See file header for details // 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 __sync_synchronize(); // No loads/stores after this point
// Record info about lock acquisition for holding() and debugging. // Record info about lock acquisition for holding() and debugging.

9
lib/uart.c Normal file
View file

@ -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++);
}

10
lib/uart.h Normal file
View file

@ -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

View file

@ -40,6 +40,9 @@ static inline u64 r_tp() {
return x; 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. * Read the value of the sstatus register.
* (Supervisor Status Register) * (Supervisor Status Register)

23
start.c
View file

@ -1,18 +1,10 @@
#include <config.h> #include <config.h>
#include <proc.h>
#include <riscv.h> #include <riscv.h>
#include <spinlock.h> #include <spinlock.h>
#include <string.h>
#include <types.h> #include <types.h>
#include <uart.h>
/* 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++);
}
/** /**
* Allocate one stack per CPU (hart). * Allocate one stack per CPU (hart).
@ -28,8 +20,13 @@ volatile int greeted = 0;
/* This is where entry.S drops us of. All cores land here */ /* This is where entry.S drops us of. All cores land here */
void start() { 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); acquire(&sl);
@ -39,7 +36,7 @@ void start() {
} }
uart_puts("Hart number: "); uart_puts("Hart number: ");
uart_putc(a + '0'); uart_putc(id + '0');
uart_putc('\n'); uart_putc('\n');
release(&sl); release(&sl);