[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