os-workshop

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 0dd2907986e5f654b25e79ee621668d70a7b724f
parent b35076df2a9c5a79f1916f62f50db938589c9cfd
Author: Brian Swetland <swetland@frotz.net>
Date:   Wed, 11 May 2022 15:55:04 -0700

clean up a bunch of stuff

- move intrinsics from hw/riscv.h to hw/intrinsics.h
- add intrinsics for thread pointer access
- add intrinsics for interrupt state management
- add intrinsics for tlb management
- move xputc()/xputs()/xgetc() into hw/src/debug-io.c
- move xprintf() into hw/src/debug-printf.c
- move exception printing code into hw/src/print-exception.c
- remove dead code from boot.c
- adjust project files and sources as necessary
- add context-switch.h and context-switch.c

Diffstat:
Mboot/boot.c | 82++++++++++---------------------------------------------------------------------
Ahw/inc/hw/context-switch.h | 35+++++++++++++++++++++++++++++++++++
Mhw/inc/hw/debug.h | 10+++++++++-
Ahw/inc/hw/intrinsics.h | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mhw/inc/hw/platform.h | 1+
Mhw/inc/hw/riscv.h | 41++---------------------------------------
Ahw/src/context-switch.S | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahw/src/debug-io.c | 32++++++++++++++++++++++++++++++++
Ahw/src/debug-printf.c | 17+++++++++++++++++
Dhw/src/debug.c | 40----------------------------------------
Ahw/src/print-exception.c | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmake/app.mk | 2+-
Mmisc/info.c | 1+
Mproject/boot.app.mk | 3++-
Mproject/devicetree.app.mk | 2+-
Mproject/info.app.mk | 2+-
Mproject/mandelbrot-fb.app.mk | 2+-
Mproject/mandelbrot.app.mk | 2+-
Mproject/traps.app.mk | 2+-
19 files changed, 317 insertions(+), 159 deletions(-)

diff --git a/boot/boot.c b/boot/boot.c @@ -2,55 +2,22 @@ // Licensed under the Apache License, Version 2.0 #include <hw/riscv.h> -#include <hw/riscv-clint.h> +#include <hw/intrinsics.h> #include <hw/debug.h> -#include "boot.h" #include <hw/platform.h> #include <hw/litex.h> +#include "boot.h" + // we expect the supervisor program to be in memory after the end of the bootloader #define SVC_ENTRY (DRAM_BASE + BOOTLOADER_SIZE) -static const char* cause_name(uint32_t n) { - if (n & 0x80000000U) { - return "Interrupt"; - } else { - switch(n & 0x7FFFFFFFU) { - case 0: return "Instruction Address Misaligned"; - case 1: return "Instruction Address Fault"; - case 2: return "Illegal Instruction"; - case 3: return "Breakpoint"; - case 4: return "Load Address Misaligned"; - case 5: return "Load Address Fault"; - case 6: return "Store Address Misaligned"; - case 7: return "Store Address Fault"; - case 8: return "User Mode ECALL"; - case 9: return "Supervisor Mode ECALL"; - case 10: return "Machine Mode ECALL"; - case 12: return "Instruction Page Fault"; - case 13: return "Load Page Fault"; - case 14: return "Store Page Fault"; - } - } - return "Unknown"; -} - -static const char* mode_name(uint32_t n) { - switch (n) { - case 0: return "User"; - case 1: return "Supervisor"; - case 3: return "Machine Mode"; - default: return "???"; - } -} - void mach_exception_handler(uint32_t regs[32]) { +#if EMULATE_MISSING_CSR_READS uint32_t mcause = csr_read(CSR_MCAUSE); - uint32_t mstatus = csr_read(CSR_MSTATUS); uint32_t mtval = csr_read(CSR_MTVAL); -#if EMULATE_MISSING_CSR_READS // fragile: no mtval on qemu if (mcause == 2) mtval = *((uint32_t*) regs[0]); @@ -64,24 +31,16 @@ void mach_exception_handler(uint32_t regs[32]) { #endif xprintf("\n** MACHINE EXCEPTION **\n"); - xprintf("** %s (in %s mode)\n\n", cause_name(mcause), - mode_name((mstatus >> MSTATUS_MPP_SHIFT) & 3)); - - xprintf("pc %08x ra %08x sp %08x gp %08x MSTATUS %08x\n", - regs[0], regs[1], regs[2], regs[3], mstatus); - xprintf("tp %08x t0 %08x t1 %08x t2 %08x MCAUSE %08x\n", - regs[4], regs[5], regs[6], regs[7], mcause); - xprintf("fp %08x s1 %08x a0 %08x a1 %08x MTVAL %08x\n", - regs[8], regs[9], regs[10], regs[11], mtval); - xprintf("a2 %08x a3 %08x a4 %08x a5 %08x\n", - regs[12], regs[13], regs[14], regs[15]); - xprintf("\n** HALT\n"); + xprint_exception(regs); + + xprintf("\nHALT\n"); for (;;) ; } // interrupts and exceptions to delegate to supervisor mode #define INT_LIST (INTb_SVC_SW|INTb_SVC_TIMER|INTb_SVC_EXTERN) -#define EXC_LIST (EXCb_ECALL_UMODE) +//#define EXC_LIST (EXCb_ECALL_UMODE) +#define EXC_LIST (0xFFFF) void start(uint32_t hartid, uint32_t fdt) { xprintf("\n** Frobozz Magic Bootloader v0.2 **\n\n"); @@ -94,28 +53,6 @@ void start(uint32_t hartid, uint32_t fdt) { // use the free ram below the supervisor entry as our exception stack csr_write(CSR_MSCRATCH, SVC_ENTRY); - -#if USE_CLINT_TIMER -#define CLINT_BASE 0x2000000 -#define TIME_TICK 10000000 - - // leaving room for the timer interrupt workspace above the stack - csr_write(CSR_MSCRATCH, SVC_ENTRY - IWS_SIZE); - - uint32_t* iws = (void*) (SVC_ENTRY - IWS_SIZE); - uint32_t mtimecmp = CLINT_BASE + CLINT_MTIMECMP(hartid); - uint32_t mtime = CLINT_BASE + CLINT_MTIME; - - iws[IWS_TIMECMP/4] = mtimecmp; - iws[IWS_TICKINC/4] = TIME_TICK; - - // set initial tick - uint64_t next = *((uint64_t*) mtime) + TIME_TICK; - *((uint64_t*) mtimecmp) = next; - - csr_set(CSR_MIE, INTb_MACH_TIMER); -#endif - // QEMU currently emulates memory protection, which we must appease if (qemu) { // U/S allow access to all memory @@ -131,6 +68,7 @@ void start(uint32_t hartid, uint32_t fdt) { csr_write(CSR_MSTATUS, (PRIV_S << MSTATUS_MPP_SHIFT) | MSTATUS_MPIE); xprintf("SVC ENTRY @0x%08x\n\n", SVC_ENTRY); + enter_mode_s(hartid, fdt, SVC_ENTRY, 0); } diff --git a/hw/inc/hw/context-switch.h b/hw/inc/hw/context-switch.h @@ -0,0 +1,35 @@ +// Copyright 2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0 + +#pragma once + +#include <stdint.h> + +// this layout must match the assembly in context-switch.S +typedef struct { + uint32_t pc; + uint32_t sp; + uint32_t gp; + uint32_t tp; + uint32_t s0; + uint32_t s1; + uint32_t s2; + uint32_t s3; + uint32_t s4; + uint32_t s5; + uint32_t s6; + uint32_t s7; + uint32_t s8; + uint32_t s9; + uint32_t s10; + uint32_t s11; +} rv32_ctxt_t; + +// store the current state into prev +// restore the new state from next +void context_switch(rv32_ctxt_t* prev, rv32_ctxt_t* next); + +// bootstrap for a new context +// will call s2(s0, s1) +// and call context_exit() should it return +void context_entry(void); diff --git a/hw/inc/hw/debug.h b/hw/inc/hw/debug.h @@ -3,8 +3,16 @@ #pragma once +#include <stdint.h> + +// debug-io.c -- can be replaced with alternate impls void xputc(unsigned c); void xputs(const char* s); -int xgetc(void); +int xgetc(void); +// debug-printf.c -- calls xputs() void xprintf(const char* fmt, ...); + +// print-exception.c -- calls xprintf() +void xprint_exception(uint32_t regs[32]); + diff --git a/hw/inc/hw/intrinsics.h b/hw/inc/hw/intrinsics.h @@ -0,0 +1,93 @@ +// Copyright 2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0 + +// inline assembly helpers for use from C + +#pragma once + +#include <stdint.h> +#include <stddef.h> + +#include <hw/riscv.h> + +typedef uint32_t irqstate_t; + +#define __S(x) #x + +// CSR Accessors + +#define csr_read(csr) ({ \ + uint32_t v; asm volatile ("csrr %0, " __S(csr) : "=r"(v)); v; }) + +#define csr_write(csr, val) ({ \ + asm volatile ("csrw " __S(csr) ", %0" :: "rK"(val)); }); + +#define csr_swap(csr, val) ({ \ + uint32_t v; asm volatile ("csrrw %0, " __S(csr) ", %1" : "=r"(v) : "rK"(val)); v; }) + +#define csr_set(csr, bit) ({ \ + asm volatile ("csrrs x0, " __S(csr) ", %0" :: "rK"(bit)); }) + +#define csr_clr(csr, bit) ({ \ + asm volatile ("csrrc x0, " __S(csr) ", %0" :: "rK"(bit)); }) + +// Disable Interrupts +static inline void irq_disable(void) { + asm volatile("csrrc x0, sstatus, %1" :: "i"(SSTATUS_SIE)); +} + +// Enable Interrupts +static inline void irq_enable(void) { + asm volatile("csrrs x0, sstatus, %1" :: "i"(SSTATUS_SIE)); +} + +// Disable Interrupts and return the previous interrupt state +static inline irqstate_t irq_disable_save(void) { + uint32_t v; asm volatile("csrrc %0, sstatus, %1" : "=r"(v) : "i"(SSTATUS_SIE)); + return v & SSTATUS_SIE; +} + +// Restore the previous interrupt state +static inline void irq_restore(irqstate_t state) { + if (state) { + asm volatile("csrrs x0, sstatus, %0" :: "i"(SSTATUS_SIE)); + } +} + +// Thread Pointer Accessors + +static inline void* threadptr_get(void) { + void *ptr; asm ("mv %0, tp" : "=r"(ptr)); return ptr; +} + +static inline void threadptr_set(void* ptr) { + asm ("mv tp, %0" :: "r"(ptr)); +} + +#define threadptr_getf(type, field) ({\ + type T; typeof(T.field) v; asm ("lw %0, %1(tp)" : "=r"(v) : "i"(offsetof(type,field))); v; }) + +#define threadptr_setf(type, field, val) ({\ + asm ("sw %0, %1(tp)" :: "r"(val), "i"(offsetof(type,field))); }) + + +// TLB Cache Managemant + +static inline void tlb_flush_all(void) { + asm volatile("sfence.vma zero, zero"); +} + +static inline void tlb_fush_addr(uint32_t a) { + asm volatile("sfence.vma %0, zero" :: "r"(a)); +} + +// sfence.vma rs2, rs1 (asid, addr) +// sfence.vma x0, x0 orders all r/w to any level page table +// invalidates all TLB entries +// sfence.vma x1, x0 orders all r/w to any level page table for ASID x1 +// invalidates all non-global ASID x1 TLB entries +// sfence.vma x0, x2 orders all r/w to leaf PTEs for vaddr in x2 +// invalidates TLB entries for vaddr x2 +// sfence.vma x1, x2 as last but for ASID x1 + + diff --git a/hw/inc/hw/platform.h b/hw/inc/hw/platform.h @@ -34,6 +34,7 @@ #ifndef __ASSEMBLY__ #include <stdint.h> +#include <hw/intrinsics.h> static inline void io_wr32(uint32_t a, uint32_t v) { *((volatile uint32_t*) a) = v; diff --git a/hw/inc/hw/riscv.h b/hw/inc/hw/riscv.h @@ -1,6 +1,8 @@ // Copyright 2022, Brian Swetland <swetland@frotz.net> // Licensed under the Apache License, Version 2.0 +// RISCV RV32 CSRs + #pragma once #define CSR_FFLAGS 0x001 // FP Exceptions @@ -159,42 +161,3 @@ #define PTE_RSW0 0x100 // Reserved For Software 0 #define PTE_RSW1 0x200 // Reserved For Software 1 -// inline assembly helpers for CSR access, etc -#ifndef __ASSEMBLER__ -#include <stdint.h> - -#define __S(x) #x - -#define csr_read(csr) ({ \ - uint32_t v; asm volatile ("csrr %0, " __S(csr) : "=r"(v)); v; }) - -#define csr_write(csr, val) ({ \ - asm volatile ("csrw " __S(csr) ", %0" :: "rK"(val)); }); - -#define csr_swap(csr, val) ({ \ - uint32_t v; asm volatile ("csrrw %0, " __S(csr) ", %1" : "=r"(v) : "rK"(val)); v; }) - -#define csr_set(csr, bit) ({ \ - uint32_t v; asm volatile ("csrrs %0, " __S(csr) ", %1" : "=r"(v) : "rK"(bit)); v; }) - -#define csr_clr(csr, bit) ({ \ - uint32_t v; asm volatile ("csrrc %0, " __S(csr) ", %1" : "=r"(v) : "rK"(bit)); v; }) - -static inline void tlb_flush_all(void) { - asm volatile("sfence.vma zero, zero"); -} - -static inline void tlb_fush_addr(uint32_t a) { - asm volatile("sfence.vma %0, zero" :: "r"(a)); -} - -// sfence.vma rs2, rs1 (asid, addr) -// sfence.vma x0, x0 orders all r/w to any level page table -// invalidates all TLB entries -// sfence.vma x1, x0 orders all r/w to any level page table for ASID x1 -// invalidates all non-global ASID x1 TLB entries -// sfence.vma x0, x2 orders all r/w to leaf PTEs for vaddr in x2 -// invalidates TLB entries for vaddr x2 -// sfence.vma x1, x2 as last but for ASID x1 - -#endif diff --git a/hw/src/context-switch.S b/hw/src/context-switch.S @@ -0,0 +1,52 @@ +// Copyright 2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0 + +// this code must match the structure rv32_ctxt_t in context-switch.h + +// context_switch(rv32_ctxt_t* prev, rv32_ctxt_t* next) +.globl context_switch +context_switch: + // save old context to prev + sw ra, 0x00(a0) + sw sp, 0x04(a0) + sw gp, 0x08(a0) + sw tp, 0x0C(a0) + sw s0, 0x10(a0) + sw s1, 0x14(a0) + sw s2, 0x18(a0) + sw s3, 0x1C(a0) + sw s4, 0x20(a0) + sw s5, 0x24(a0) + sw s6, 0x28(a0) + sw s7, 0x2C(a0) + sw s8, 0x30(a0) + sw s9, 0x34(a0) + sw s10, 0x38(a0) + sw s11, 0x3C(a0) + // load new context from next + lw ra, 0x00(a1) + lw sp, 0x04(a1) + lw gp, 0x08(a1) + lw tp, 0x0C(a1) + lw s0, 0x10(a1) + lw s1, 0x14(a1) + lw s2, 0x18(a1) + lw s3, 0x1C(a1) + lw s4, 0x20(a1) + lw s5, 0x24(a1) + lw s6, 0x28(a1) + lw s7, 0x2C(a1) + lw s8, 0x30(a1) + lw s9, 0x34(a1) + lw s10, 0x38(a1) + lw s11, 0x3C(a1) + // return to new context + ret + +.globl context_entry +context_entry: + mv a0, s0 + mv a1, s1 + jalr s2 + j context_exit + diff --git a/hw/src/debug-io.c b/hw/src/debug-io.c @@ -0,0 +1,32 @@ +// Copyright 2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0. + +#include <stdio.h> +#include <stdarg.h> + +#include <hw/platform.h> +#include <hw/litex.h> +#include <hw/debug.h> + +#define uart_rd(a) io_rd32(UART0_BASE + LX_UART_ ## a) +#define uart_wr(a,v) io_wr32(UART0_BASE + LX_UART_ ## a, v) + +void xputc(unsigned c) { + if (c == '\n') { + while (uart_rd(TXFULL)) ; + uart_wr(TX, '\r'); + } + while (uart_rd(TXFULL)) ; + uart_wr(TX, c); +} + +void xputs(const char* s) { + while (*s != 0) { + xputc(*s++); + } +} + +int xgetc(void) { + return -1; +} + diff --git a/hw/src/debug-printf.c b/hw/src/debug-printf.c @@ -0,0 +1,17 @@ +// Copyright 2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0. + +#include <stdio.h> +#include <stdarg.h> + +#include <hw/debug.h> + +void xprintf(const char* fmt, ...) { + char msg[128]; + va_list ap; + va_start(ap, fmt); + vsnprintf(msg, sizeof(msg), fmt, ap); + va_end(ap); + xputs(msg); +} + diff --git a/hw/src/debug.c b/hw/src/debug.c @@ -1,40 +0,0 @@ -// Copyright 2022, Brian Swetland <swetland@frotz.net> -// Licensed under the Apache License, Version 2.0. - -#include <stdio.h> -#include <stdarg.h> - -#include <hw/platform.h> -#include <hw/litex.h> - -#define uart_rd(a) io_rd32(UART0_BASE + LX_UART_ ## a) -#define uart_wr(a,v) io_wr32(UART0_BASE + LX_UART_ ## a, v) - -void xputc(unsigned c) { - if (c == '\n') { - while (uart_rd(TXFULL)) ; - uart_wr(TX, '\r'); - } - while (uart_rd(TXFULL)) ; - uart_wr(TX, c); -} - -void xputs(const char* s) { - while (*s != 0) { - xputc(*s++); - } -} - -void xprintf(const char* fmt, ...) { - char msg[128]; - va_list ap; - va_start(ap, fmt); - vsnprintf(msg, sizeof(msg), fmt, ap); - va_end(ap); - xputs(msg); -} - -int xgetc(void) { - return -1; -} - diff --git a/hw/src/print-exception.c b/hw/src/print-exception.c @@ -0,0 +1,57 @@ +// Copyright 2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0 + +#include <hw/debug.h> +#include <hw/riscv.h> +#include <hw/intrinsics.h> + +static const char* cause_name(uint32_t n) { + if (n & 0x80000000U) { + return "Interrupt"; + } else { + switch(n & 0x7FFFFFFFU) { + case 0: return "Instruction Address Misaligned"; + case 1: return "Instruction Address Fault"; + case 2: return "Illegal Instruction"; + case 3: return "Breakpoint"; + case 4: return "Load Address Misaligned"; + case 5: return "Load Address Fault"; + case 6: return "Store Address Misaligned"; + case 7: return "Store Address Fault"; + case 8: return "User Mode ECALL"; + case 9: return "Supervisor Mode ECALL"; + case 10: return "Machine Mode ECALL"; + case 12: return "Instruction Page Fault"; + case 13: return "Load Page Fault"; + case 14: return "Store Page Fault"; + } + } + return "Unknown"; +} + +static const char* mode_name(uint32_t n) { + switch (n) { + case 0: return "User"; + case 1: return "Supervisor"; + case 3: return "Machine Mode"; + default: return "???"; + } +} + +void xprint_exception(uint32_t regs[32]) { + uint32_t mcause = csr_read(CSR_MCAUSE); + uint32_t mstatus = csr_read(CSR_MSTATUS); + uint32_t mtval = csr_read(CSR_MTVAL); + + xprintf("** %s (in %s mode)\n\n", cause_name(mcause), + mode_name((mstatus >> MSTATUS_MPP_SHIFT) & 3)); + + xprintf("pc %08x ra %08x sp %08x gp %08x MSTATUS %08x\n", + regs[0], regs[1], regs[2], regs[3], mstatus); + xprintf("tp %08x t0 %08x t1 %08x t2 %08x MCAUSE %08x\n", + regs[4], regs[5], regs[6], regs[7], mcause); + xprintf("fp %08x s1 %08x a0 %08x a1 %08x MTVAL %08x\n", + regs[8], regs[9], regs[10], regs[11], mtval); + xprintf("a2 %08x a3 %08x a4 %08x a5 %08x\n", + regs[12], regs[13], regs[14], regs[15]); +} diff --git a/make/app.mk b/make/app.mk @@ -22,7 +22,7 @@ $(MOD_ELF): $(MOD_OBJ) $(MOD_LDSCRIPT) $(V)$(XGCC) $(_LDFLAGS) -o $@ $(_OBJ) $(_LIB) $(MOD_LST): $(MOD_ELF) - $(V)$(XOBJDUMP) -D $< > $@ + $(V)$(XOBJDUMP) -d -z $< > $@ $(MOD_BIN): $(MOD_ELF) $(V)$(XOBJCOPY) -O binary $< $@ diff --git a/misc/info.c b/misc/info.c @@ -3,6 +3,7 @@ #include <hw/debug.h> #include <hw/riscv.h> +#include <hw/intrinsics.h> void start(uint32_t hartid, uint32_t dtb) { xprintf("MSTATUS %08x\n", csr_read(CSR_MSTATUS)); diff --git a/project/boot.app.mk b/project/boot.app.mk @@ -1,7 +1,8 @@ MOD_NAME := boot MOD_SRC := hw/src/start.S boot/entry.S boot/boot.c -MOD_SRC += hw/src/debug.c +MOD_SRC += hw/src/print-exception.c +MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c MOD_LIB := c MOD_LDSCRIPT := hw/boot.ram.ld include make/app.mk diff --git a/project/devicetree.app.mk b/project/devicetree.app.mk @@ -1,6 +1,6 @@ MOD_NAME := devicetree MOD_SRC := hw/src/start.S misc/devicetree.c -MOD_SRC += hw/src/debug.c +MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c MOD_LIB := c include make/app.mk diff --git a/project/info.app.mk b/project/info.app.mk @@ -1,6 +1,6 @@ MOD_NAME := info MOD_SRC := hw/src/start.S misc/info.c -MOD_SRC += hw/src/debug.c +MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c MOD_LIB := c include make/app.mk diff --git a/project/mandelbrot-fb.app.mk b/project/mandelbrot-fb.app.mk @@ -1,7 +1,7 @@ MOD_NAME := mandelbrot-fb MOD_SRC := hw/src/start.S misc/mandelbrot-fb.c -MOD_SRC += hw/src/debug.c +MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c MOD_QEMU_FB := 1 MOD_LIB := c include make/app.mk diff --git a/project/mandelbrot.app.mk b/project/mandelbrot.app.mk @@ -1,6 +1,6 @@ MOD_NAME := mandelbrot MOD_SRC := hw/src/start.S misc/mandelbrot.c -MOD_SRC += hw/src/debug.c +MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c MOD_LIB := c include make/app.mk diff --git a/project/traps.app.mk b/project/traps.app.mk @@ -1,6 +1,6 @@ MOD_NAME := traps MOD_SRC := hw/src/start.S misc/traps-entry.S misc/traps.c -MOD_SRC += hw/src/debug.c +MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c MOD_LIB := c include make/app.mk