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