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

Reply via email to