[PATCH 1/2] arch/riscv: add semihosting support for RISC-V
Kautuk Consul
kconsul at ventanamicro.com
Thu Sep 15 14:45:25 CEST 2022
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 <apatel at ventanamicro.com>
Signed-off-by: Kautuk Consul <kconsul at 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
More information about the U-Boot
mailing list