os-workshop

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

commit acc8ff2d29e72e99f0b2c8797797986030d984c0
parent 8cd4c8d007f49b64126702a632699af3cc9ecf41
Author: Brian Swetland <swetland@frotz.net>
Date:   Tue, 17 May 2022 23:55:22 -0700

boot: add a simple serial debug console

- dump memory with dw or db
- dump registers with regs
- stop / resume supervisor or user execution with stop/cont
- help

Diffstat:
Mboot/boot.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mboot/boot.h | 11+++++++++++
Aboot/console.c | 216+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aboot/helpers.S | 40++++++++++++++++++++++++++++++++++++++++
Mproject/boot.app.mk | 1+
5 files changed, 327 insertions(+), 0 deletions(-)

diff --git a/boot/boot.c b/boot/boot.c @@ -9,12 +9,66 @@ #include <hw/platform.h> #include <hw/litex.h> +#include <string.h> + #include "boot.h" +eframe_t *EF; +volatile int STOP; + // we expect the supervisor program to be in memory after the end of the bootloader #define SVC_ENTRY (DRAM_BASE + BOOTLOADER_SIZE) + +#define uart_rd(a) io_rd32(UART0_BASE + LX_UART_ ## a) +#define uart_wr(a,v) io_wr32(UART0_BASE + LX_UART_ ## a, v) + +static char cmdbuf[128]; +static unsigned cmdlen = 0; + +void console_char(uint32_t ch) { + if ((ch == '\r')) { + xputs("\r\n"); + cmdbuf[cmdlen] = 0; + console_line(cmdbuf); + cmdlen = 0; + return; + } + if ((ch == 8) || (ch == 127)) { + if (cmdlen > 0) { + xputs("\x08 \x08"); + cmdlen--; + } else { + xputc(7); + } + return; + } + if ((ch < ' ') || (ch > 127)) { + return; + } + if (cmdlen == (sizeof(cmdbuf) - 1)) { + xputc(7); + return; + } + cmdbuf[cmdlen++] = ch; + xputc(ch); +} + void mach_exception_handler(eframe_t *ef) { + uint32_t cause = csr_read(CSR_MCAUSE); + + if (cause == 0x8000000b) { + EF = ef; + do { + while (uart_rd(RXEMPTY) == 0) { + unsigned ch = uart_rd(RX); + uart_wr(EV_PENDING, LX_UART_EVb_RX); + console_char(ch); + } + } while (STOP); + return; + } + xprintf("\n** MACHINE EXCEPTION **\n"); xprint_m_exception(ef); xprintf("\nHALT\n"); @@ -53,6 +107,11 @@ void start(uint32_t hartid, uint32_t fdt) { xprintf("SVC ENTRY @0x%08x\n\n", SVC_ENTRY); + uart_wr(EV_ENABLE, LX_UART_EVb_RX); + uart_wr(EV_PENDING, LX_UART_EVb_RX); + csr_set(CSR_M_INTC_ENABLE, UART0_IRQb); + csr_set(CSR_MIE, INTb_MACH_EXTERN); + exit_mode_m(hartid, fdt, SVC_ENTRY, 0); } diff --git a/boot/boot.h b/boot/boot.h @@ -2,9 +2,20 @@ #ifndef __ASSEMBLY__ +#include <stdint.h> + // entry.S void mach_exception_entry(void); void exit_mode_m(uint32_t a0, uint32_t a1, uint32_t pc, uint32_t sp); +// console.c +void console_line(char *line); + +// helpers.S +uint32_t rd8safe(uint32_t addr); +uint32_t rd16safe(uint32_t addr); +uint32_t rd32safe(uint32_t addr); +void console_call_cmd(void *cmdfn, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uint32_t argc); + #endif diff --git a/boot/console.c b/boot/console.c @@ -0,0 +1,216 @@ +// Copyright 2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0 + +#include <hw/riscv.h> +#include <hw/intrinsics.h> +#include <hw/context.h> +#include <hw/debug.h> +#include "boot.h" +#include <string.h> + +int atoi(const char *s) { + unsigned c; + int n = 0; + while ((c = *s++)) { + if ((c >= '0') && (c <= '9')) { + n = n * 10 + (c - '0'); + } else { + break; + } + } + return n; +} + +uint32_t atox(const char *s) { + uint32_t n = 0; + uint32_t c; + while ((c = *s++)) { + switch (c) { + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + c = c - 'a' + 10; + break; + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + c = c - 'A' + 10; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + c = c - '0'; + break; + default: + return n; + } + n = (n << 4) | c; + } + return n; +} + +extern eframe_t* EF; +extern volatile int STOP; + + +// args: x hex number +// i integer number +// s string +typedef struct { + const char* name; + const char* args; + const char* help; + void *fn; +} console_cmd_t; + +void do_regs(void) { + eframe_t* ef = EF; + xprintf("pc %08x ra %08x sp %08x gp %08x sstatus %08x\n", + ef->pc, ef->ra, ef->sp, ef->gp, csr_read(CSR_SSTATUS)); + xprintf("tp %08x t0 %08x t1 %08x t2 %08x scause %08x\n", + ef->tp, ef->t0, ef->t1, ef->t2, csr_read(CSR_SCAUSE)); + xprintf("fp %08x s1 %08x a0 %08x a1 %08x stval %08x\n", + ef->s0, ef->s1, ef->a0, ef->a1, csr_read(CSR_STVAL)); + xprintf("a2 %08x a3 %08x a4 %08x a5 %08x stvec %08x\n", + ef->a2, ef->a3, ef->a4, ef->a5, csr_read(CSR_STVEC)); + xprintf("a6 %08x a7 %08x s2 %08x s3 %08x sepc %08x\n", + ef->a6, ef->a7, ef->s2, ef->s3, csr_read(CSR_SEPC)); + xprintf("s4 %08x s5 %08x s6 %08x s7 %08x sscratch %08x\n", + ef->s4, ef->s5, ef->s6, ef->s7, csr_read(CSR_SSCRATCH)); + xprintf("s8 %08x s9 %08x 10 %08x 11 %08x satp %08x\n", + ef->s8, ef->s9, ef->s10, ef->s11, csr_read(CSR_SATP)); + xprintf("t3 %08x t4 %08x t5 %08x t6 %08x sip %08x\n", + ef->t3, ef->t4, ef->t5, ef->t6, csr_read(CSR_SIP)); +} + +void do_help(void); + +void do_dw(unsigned n, unsigned addr, unsigned count) { + if (n < 2) count = 1; + if (count > 1024) count = 1024; + + n = 0; + while (n < count) { + if ((n & 3) == 0) { + xprintf("\n%08x:", addr); + } + xprintf(" %08x", rd32safe(addr)); + addr += 4; + n++; + } + if ((n & 3) == 0) { + xprintf("\n"); + } +} + +void do_db(unsigned n, unsigned addr, int count) { + char txt[17]; + if (count < 0) { + return; + } + txt[16] = 0; + while (count > 0) { + xprintf("%08x:", addr); + for (int i = 0; i < 16; i++) { + uint32_t c; + if (i < count) { + c = rd8safe(addr + i) & 0xFF; + xprintf(" %02x", c); + if ((c < ' ') || (c > 126)) c = '.'; + } else { + xprintf(" "); + c = ' '; + } + txt[i] = c; + } + xprintf(" %s\n", txt); + addr += 16; + count -= 16; + } +} + +void do_stop(void) { STOP = 1; } +void do_cont(void) { STOP = 0; }; + +console_cmd_t CMDS[] = { + { "help", "", "help", do_help }, + { "regs", "", "dump registers", do_regs }, + { "dw", "xx", "dump words <addr> <count>", do_dw }, + { "db", "xx", "dump bytes <addr> <count>", do_db }, + { "stop", "", "stop execution", do_stop }, + { "cont", "", "continue execution", do_cont }, +}; + +void do_help(void) { + for (unsigned n = 0; n < (sizeof(CMDS)/sizeof(CMDS[0])); n++) { + xprintf("%-16s %s\n", CMDS[n].name, CMDS[n].help); + } +} + +void console_cmd(console_cmd_t *cmd, char *args[], unsigned argc) { + uintptr_t argx[4]; + unsigned max = strlen(cmd->args); + + if ((argc > max) || (argc > 4)) { + xprintf("error: too many arguments\n"); + return; + } + + memset(argx, 0, sizeof(argx)); + for (unsigned n = 0; n < max; n++) { + switch (cmd->args[n]) { + case 'x': + argx[n] = (n < argc) ? atox(args[n]) : 0; + break; + case 'i': + argx[n] = (n < argc) ? atoi(args[n]) : 0; + break; + case 's': + argx[n] = (n < argc) ? (uintptr_t) args[n] : (uintptr_t) ""; + break; + default: + xprintf("error: internal: bad args '%s'\n", cmd->args); + return; + } + } + + console_call_cmd(cmd->fn, argx[0], argx[1], argx[2], argx[3], argc); +} + +#define MAXTOK 16 + +void console_line(char* line) { + char *token[MAXTOK]; + unsigned toklen = 0; + + while (toklen < MAXTOK) { + while(*line == ' ') { + if (*line == 0) { + goto done; + } + *line++ = 0; + } + token[toklen] = line; + while (*line != ' ') { + if (*line == 0) { + if (token[toklen] != line) { + *line = 0; + toklen++; + } + goto done; + } + line++; + } + toklen++; + } +done: + if (toklen) { + for (unsigned n = 0; n < (sizeof(CMDS)/sizeof(CMDS[0])); n++) { + if (!strcmp(CMDS[n].name, token[0])) { + console_cmd(CMDS + n, token + 1, toklen - 1); + goto prompt; + } + } + xprintf("unknown command '%s'\n", token[0]); + } +prompt: + xputs("monitor> "); +} + + + diff --git a/boot/helpers.S b/boot/helpers.S @@ -0,0 +1,40 @@ +// Copyright 2022, Brian Swetland <swetland@frotz.net> +// Licensed under the Apache License, Version 2.0 + + +// trap-safe memory accesses + +.globl rd32safe // (addr) +rd32safe: + csrr t0, mtvec + csrr t1, mstatus + la t2, rd32fail + mv t3, a0 + li a0, 0 + csrw mtvec, t2 + lw a0, 0(t3) +rd32fail: + csrw mtvec, t0 + csrw mstatus, t1 + ret + +.globl rd8safe // (addr) +rd8safe: + csrr t0, mtvec + csrr t1, mstatus + la t2, rd8fail + mv t3, a0 + li a0, 0 + csrw mtvec, t2 + lbu a0, 0(t3) +rd8fail: + csrw mtvec, t0 + csrw mstatus, t1 + ret + + +.globl console_call_cmd // (fn, a1, a2, a3, a4, a0) +console_call_cmd: + mv t0, a0 + mv a0, a5 + jr t0 diff --git a/project/boot.app.mk b/project/boot.app.mk @@ -1,6 +1,7 @@ MOD_NAME := boot MOD_SRC := hw/src/start.S boot/entry.S boot/boot.c +MOD_SRC += boot/console.c boot/helpers.S MOD_SRC += hw/src/print-exception.c MOD_SRC += hw/src/debug-printf.c hw/src/debug-io.c MOD_LIB := c