We add RISC-V semihosting based serial console for JTAG based early debugging.
The RISC-V semihosting specification is available at: https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc Signed-off-by: Anup Patel <apa...@ventanamicro.com> Signed-off-by: Kautuk Consul <kcon...@ventanamicro.com> --- arch/riscv/Kconfig | 45 ++++++++ arch/riscv/include/asm/semihosting.h | 11 ++ arch/riscv/include/asm/spl.h | 1 + arch/riscv/lib/Makefile | 7 ++ arch/riscv/lib/semihosting.c | 166 +++++++++++++++++++++++++++ arch/riscv/lib/semihosting_mmode.c | 77 +++++++++++++ arch/riscv/lib/semihosting_smode.c | 77 +++++++++++++ 7 files changed, 384 insertions(+) create mode 100644 arch/riscv/include/asm/semihosting.h create mode 100644 arch/riscv/lib/semihosting.c create mode 100644 arch/riscv/lib/semihosting_mmode.c create mode 100644 arch/riscv/lib/semihosting_smode.c diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 78e964db12..1b23d1c6c1 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -371,4 +371,49 @@ config TPL_USE_ARCH_MEMSET endmenu +config SEMIHOSTING + bool "Support RISCV semihosting" + help + Semihosting is a method for a target to communicate with a host + debugger. It uses special instructions which the debugger will trap + on and interpret. This allows U-Boot to read/write files, print to + the console, and execute arbitrary commands on the host system. + + Enabling this option will add support for reading and writing files + on the host system. If you don't have a debugger attached then trying + to do this will likely cause U-Boot to hang. Say 'n' if you are unsure. + +config SEMIHOSTING_FALLBACK + bool "Recover gracefully when semihosting fails" + depends on SEMIHOSTING && RISCV + default y + help + Normally, if U-Boot makes a semihosting call and no debugger is + attached, then it will panic due to a synchronous abort + exception. This config adds an exception handler which will allow + U-Boot to recover. Say 'y' if unsure. + +config SPL_SEMIHOSTING + bool "Support RISCV semihosting in SPL" + depends on SPL + help + Semihosting is a method for a target to communicate with a host + debugger. It uses special instructions which the debugger will trap + on and interpret. This allows U-Boot to read/write files, print to + the console, and execute arbitrary commands on the host system. + + Enabling this option will add support for reading and writing files + on the host system. If you don't have a debugger attached then trying + to do this will likely cause U-Boot to hang. Say 'n' if you are unsure. + +config SPL_SEMIHOSTING_FALLBACK + bool "Recover gracefully when semihosting fails in SPL" + depends on SPL_SEMIHOSTING && RISCV + default y + help + Normally, if U-Boot makes a semihosting call and no debugger is + attached, then it will panic due to a synchronous abort + exception. This config adds an exception handler which will allow + U-Boot to recover. Say 'y' if unsure. + endmenu diff --git a/arch/riscv/include/asm/semihosting.h b/arch/riscv/include/asm/semihosting.h new file mode 100644 index 0000000000..7042821e00 --- /dev/null +++ b/arch/riscv/include/asm/semihosting.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2022 Ventana Micro Systems Inc. + */ + +#ifndef __ASM_RISCV_SEMIHOSTING_H +#define __ASM_RISCV_SEMIHOSTING_H + +long smh_trap(int sysnum, void *addr); + +#endif /* __ASM_RISCV_SEMIHOSTING_H */ diff --git a/arch/riscv/include/asm/spl.h b/arch/riscv/include/asm/spl.h index e8a94fcb1f..2898a770ee 100644 --- a/arch/riscv/include/asm/spl.h +++ b/arch/riscv/include/asm/spl.h @@ -25,6 +25,7 @@ enum { BOOT_DEVICE_DFU, BOOT_DEVICE_XIP, BOOT_DEVICE_BOOTROM, + BOOT_DEVICE_SMH, BOOT_DEVICE_NONE }; diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index 06020fcc2a..2c89c3a2fa 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -42,3 +42,10 @@ extra-$(CONFIG_EFI) += $(EFI_CRT0) $(EFI_RELOC) obj-$(CONFIG_$(SPL_TPL_)USE_ARCH_MEMSET) += memset.o obj-$(CONFIG_$(SPL_TPL_)USE_ARCH_MEMMOVE) += memmove.o obj-$(CONFIG_$(SPL_TPL_)USE_ARCH_MEMCPY) += memcpy.o + +obj-$(CONFIG_$(SPL_TPL_)SEMIHOSTING) += semihosting.o +ifeq ($(CONFIG_$(SPL_)RISCV_MMODE),y) +obj-$(CONFIG_$(SPL_TPL_)SEMIHOSTING) += semihosting_mmode.o +else +obj-$(CONFIG_$(SPL_TPL_)SEMIHOSTING) += semihosting_smode.o +endif diff --git a/arch/riscv/lib/semihosting.c b/arch/riscv/lib/semihosting.c new file mode 100644 index 0000000000..504c9a1ddc --- /dev/null +++ b/arch/riscv/lib/semihosting.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Ventana Micro Systems Inc. + */ + +#include <common.h> +#include <log.h> +#include <semihosting.h> +#include <asm/semihosting.h> + +#define SYSOPEN 0x01 +#define SYSCLOSE 0x02 +#define SYSWRITEC 0x03 +#define SYSWRITE0 0x04 +#define SYSWRITE 0x05 +#define SYSREAD 0x06 +#define SYSREADC 0x07 +#define SYSISERROR 0x08 +#define SYSSEEK 0x0A +#define SYSFLEN 0x0C +#define SYSERRNO 0x13 + +/** + * smh_errno() - Read the host's errno + * + * This gets the value of the host's errno and negates it. The host's errno may + * or may not be set, so only call this function if a previous semihosting call + * has failed. + * + * Return: a negative error value + */ +static int smh_errno(void) +{ + long ret = smh_trap(SYSERRNO, NULL); + + if (ret > 0 && ret < INT_MAX) + return -ret; + return -EIO; +} + +long smh_open(const char *fname, enum smh_open_mode mode) +{ + long fd; + struct smh_open_s { + const char *fname; + unsigned long mode; + size_t len; + } open; + + debug("%s: file \'%s\', mode \'%u\'\n", __func__, fname, mode); + + open.fname = fname; + open.len = strlen(fname); + open.mode = mode; + + /* Open the file on the host */ + fd = smh_trap(SYSOPEN, &open); + if (fd == -1) + return smh_errno(); + return fd; +} + +/** + * struct smg_rdwr_s - Arguments for read and write + * @fd: A file descriptor returned from smh_open() + * @memp: Pointer to a buffer of memory of at least @len bytes + * @len: The number of bytes to read or write + */ +struct smh_rdwr_s { + long fd; + void *memp; + size_t len; +}; + +long smh_read(long fd, void *memp, size_t len) +{ + long ret; + struct smh_rdwr_s read; + + debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len); + + read.fd = fd; + read.memp = memp; + read.len = len; + + ret = smh_trap(SYSREAD, &read); + if (ret < 0) + return smh_errno(); + return len - ret; +} + +long smh_write(long fd, const void *memp, size_t len, ulong *written) +{ + long ret; + struct smh_rdwr_s write; + + debug("%s: fd %ld, memp %p, len %zu\n", __func__, fd, memp, len); + + write.fd = fd; + write.memp = (void *)memp; + write.len = len; + + ret = smh_trap(SYSWRITE, &write); + *written = len - ret; + if (ret) + return smh_errno(); + return 0; +} + +long smh_close(long fd) +{ + long ret; + + debug("%s: fd %ld\n", __func__, fd); + + ret = smh_trap(SYSCLOSE, &fd); + if (ret == -1) + return smh_errno(); + return 0; +} + +long smh_flen(long fd) +{ + long ret; + + debug("%s: fd %ld\n", __func__, fd); + + ret = smh_trap(SYSFLEN, &fd); + if (ret == -1) + return smh_errno(); + return ret; +} + +long smh_seek(long fd, long pos) +{ + long ret; + struct smh_seek_s { + long fd; + long pos; + } seek; + + debug("%s: fd %ld pos %ld\n", __func__, fd, pos); + + seek.fd = fd; + seek.pos = pos; + + ret = smh_trap(SYSSEEK, &seek); + if (ret) + return smh_errno(); + return 0; +} + +int smh_getc(void) +{ + return smh_trap(SYSREADC, NULL); +} + +void smh_putc(char ch) +{ + smh_trap(SYSWRITEC, &ch); +} + +void smh_puts(const char *s) +{ + smh_trap(SYSWRITE0, (char *)s); +} diff --git a/arch/riscv/lib/semihosting_mmode.c b/arch/riscv/lib/semihosting_mmode.c new file mode 100644 index 0000000000..7bc97a1a66 --- /dev/null +++ b/arch/riscv/lib/semihosting_mmode.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Ventana Micro Systems Inc. + */ + +#include <common.h> + +#define SYSERRNO 0x13 + +long smh_trap(int sysnum, void *addr) +{ + register int ret asm ("a0") = sysnum; + register void *param0 asm ("a1") = addr; + + asm volatile ("\t.option push\n" + "\t.option norvc\n" + "\tj 1f\n" + "\t.align 4\n" + "\t1: slli zero, zero, 0x1f\n" + "\tebreak\n" + "\tsrai zero, zero, 7\n" + "\t.option pop\n" + : "+r" (ret) : "r" (param0) : "memory"); + + return ret; +} + +#if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK) +static bool _semihosting_enabled = true; +static bool try_semihosting = true; + +bool semihosting_enabled(void) +{ + unsigned long tmp = 0; + + register int ret asm ("a0") = SYSERRNO; + register void *param0 asm ("a1") = NULL; + + if (!try_semihosting) + return _semihosting_enabled; + + asm volatile ("\t.option push\n" + "\t.option norvc\n" + + "\tj _semihost_test_vector_next\n" + "\t.align 4\n" + "\t_semihost_test_vector:\n" + "\t\tcsrr %[en], mepc\n" + "\t\taddi %[en], %[en], 4\n" + "\t\tcsrw mepc, %[en]\n" + "\t\tadd %[en], zero, zero\n" + "\t\tmret\n" + "\t_semihost_test_vector_next:\n" + + "\tla %[tmp], _semihost_test_vector\n" + "\tcsrrw %[tmp], mtvec, %[tmp]\n" + "\tj 1f\n" + "\t.align 4\n" + "\t1: slli zero, zero, 0x1f\n" + "\tebreak\n" + "\tsrai zero, zero, 7\n" + "\tcsrw mtvec, %[tmp]\n" + + "\t.option pop\n" + : [tmp] "+r" (tmp), [en] "+r" (_semihosting_enabled), + [ret] "+r" (ret) + : "r" (param0) : "memory"); + + try_semihosting = false; + return _semihosting_enabled; +} + +void disable_semihosting(void) +{ + _semihosting_enabled = false; +} +#endif diff --git a/arch/riscv/lib/semihosting_smode.c b/arch/riscv/lib/semihosting_smode.c new file mode 100644 index 0000000000..d7ce07985b --- /dev/null +++ b/arch/riscv/lib/semihosting_smode.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2022 Ventana Micro Systems Inc. + */ + +#include <common.h> + +#define SYSERRNO 0x13 + +long smh_trap(int sysnum, void *addr) +{ + register int ret asm ("a0") = sysnum; + register void *param0 asm ("a1") = addr; + + asm volatile ("\t.option push\n" + "\t.option norvc\n" + "\tj 1f\n" + "\t.align 4\n" + "\t1: slli zero, zero, 0x1f\n" + "\tebreak\n" + "\tsrai zero, zero, 7\n" + "\t.option pop\n" + : "+r" (ret) : "r" (param0) : "memory"); + + return ret; +} + +#if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK) +static bool _semihosting_enabled = true; +static bool try_semihosting = true; + +bool semihosting_enabled(void) +{ + unsigned long tmp = 0; + + register int ret asm ("a0") = SYSERRNO; + register void *param0 asm ("a1") = NULL; + + if (!try_semihosting) + return _semihosting_enabled; + + asm volatile ("\t.option push\n" + "\t.option norvc\n" + + "\tj _semihost_test_vector_next\n" + "\t.align 4\n" + "\t_semihost_test_vector:\n" + "\t\tcsrr %[en], sepc\n" + "\t\taddi %[en], %[en], 4\n" + "\t\tcsrw sepc, %[en]\n" + "\t\tadd %[en], zero, zero\n" + "\t\tsret\n" + "\t_semihost_test_vector_next:\n" + + "\tla %[tmp], _semihost_test_vector\n" + "\tcsrrw %[tmp], stvec, %[tmp]\n" + "\tj 1f\n" + "\t.align 4\n" + "\t1: slli zero, zero, 0x1f\n" + "\tebreak\n" + "\tsrai zero, zero, 7\n" + "\tcsrw stvec, %[tmp]\n" + + "\t.option pop\n" + : [tmp] "+r" (tmp), [en] "+r" (_semihosting_enabled), + [ret] "+r" (ret) + : "r" (param0) : "memory"); + + try_semihosting = false; + return _semihosting_enabled; +} + +void disable_semihosting(void) +{ + _semihosting_enabled = false; +} +#endif -- 2.34.1