lib: utils/serial: add semihosting support

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@ventanamicro.com>
Signed-off-by: Kautuk Consul <kconsul@ventanamicro.com>
Reviewed-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
Kautuk Consul 2022-09-12 16:22:53 +05:30 committed by Anup Patel
parent 49372f2691
commit 7f09fba86e
6 changed files with 241 additions and 1 deletions

View File

@ -0,0 +1,47 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Ventana Micro Systems Inc.
*
* Authors:
* Anup Patel <apatel@ventanamicro.com>
* Kautuk Consul <kconsul@ventanamicro.com>
*/
#ifndef __SERIAL_SEMIHOSTING_H__
#define __SERIAL_SEMIHOSTING_H__
#include <sbi/sbi_types.h>
/**
* enum semihosting_open_mode - Numeric file modes for use with semihosting_open()
* MODE_READ: 'r'
* MODE_BINARY: 'b'
* MODE_PLUS: '+'
* MODE_WRITE: 'w'
* MODE_APPEND: 'a'
*
* These modes represent the mode string used by fopen(3) in a form which can
* be passed to semihosting_open(). These do NOT correspond directly to %O_RDONLY,
* %O_CREAT, etc; see fopen(3) for details. In particular, @MODE_PLUS
* effectively results in adding %O_RDWR, and @MODE_WRITE will add %O_TRUNC.
* For compatibility, @MODE_BINARY should be added when opening non-text files
* (such as images).
*/
enum semihosting_open_mode {
MODE_READ = 0x0,
MODE_BINARY = 0x1,
MODE_PLUS = 0x2,
MODE_WRITE = 0x4,
MODE_APPEND = 0x8,
};
#ifdef CONFIG_SERIAL_SEMIHOSTING
int semihosting_init(void);
int semihosting_enabled(void);
#else
static inline int semihosting_init(void) { return SBI_ENODEV; }
static inline int semihosting_enabled(void) { return 0; }
#endif
#endif

View File

@ -79,4 +79,8 @@ config SERIAL_XILINX_UARTLITE
bool "Xilinx UART Lite support"
default n
config SERIAL_SEMIHOSTING
bool "Semihosting support"
default n
endmenu

View File

@ -41,3 +41,4 @@ libsbiutils-objs-$(CONFIG_SERIAL_SIFIVE) += serial/sifive-uart.o
libsbiutils-objs-$(CONFIG_SERIAL_LITEX) += serial/litex-uart.o
libsbiutils-objs-$(CONFIG_SERIAL_UART8250) += serial/uart8250.o
libsbiutils-objs-$(CONFIG_SERIAL_XILINX_UARTLITE) += serial/xlnx-uartlite.o
libsbiutils-objs-$(CONFIG_SERIAL_SEMIHOSTING) += serial/semihosting.o

View File

@ -0,0 +1,178 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Ventana Micro Systems Inc.
*
* Authors:
* Anup Patel <apatel@ventanamicro.com>
* Kautuk Consul <kconsul@ventanamicro.com>
*/
#include <sbi/sbi_console.h>
#include <sbi/sbi_string.h>
#include <sbi/sbi_error.h>
#include <sbi_utils/serial/semihosting.h>
#define SYSOPEN 0x01
#define SYSWRITEC 0x03
#define SYSREAD 0x06
#define SYSREADC 0x07
#define SYSERRNO 0x13
static long semihosting_trap(int sysnum, void *addr)
{
register int ret asm ("a0") = sysnum;
register void *param0 asm ("a1") = addr;
asm volatile (
" .align 4\n"
" .option push\n"
" .option norvc\n"
" slli zero, zero, 0x1f\n"
" ebreak\n"
" srai zero, zero, 7\n"
" .option pop\n"
: "+r" (ret) : "r" (param0) : "memory");
return ret;
}
static bool _semihosting_enabled = true;
static bool try_semihosting = true;
bool semihosting_enabled(void)
{
register int ret asm ("a0") = SYSERRNO;
register void *param0 asm ("a1") = NULL;
unsigned long tmp = 0;
if (!try_semihosting)
return _semihosting_enabled;
asm volatile (
" .align 4\n"
" .option push\n"
" .option norvc\n"
" j _semihost_test_vector_next\n"
" .align 4\n"
"_semihost_test_vector:\n"
" csrr %[en], mepc\n"
" addi %[en], %[en], 4\n"
" csrw mepc, %[en]\n"
" add %[en], zero, zero\n"
" mret\n"
"_semihost_test_vector_next:\n"
" la %[tmp], _semihost_test_vector\n"
" csrrw %[tmp], mtvec, %[tmp]\n"
" .align 4\n"
" slli zero, zero, 0x1f\n"
" ebreak\n"
" srai zero, zero, 7\n"
" csrw mtvec, %[tmp]\n"
" .option pop\n"
: [tmp] "+r" (tmp), [en] "+r" (_semihosting_enabled),
[ret] "+r" (ret)
: "r" (param0) : "memory");
try_semihosting = false;
return _semihosting_enabled;
}
static int semihosting_errno(void)
{
long ret = semihosting_trap(SYSERRNO, NULL);
if (ret > 0)
return -ret;
return SBI_EIO;
}
static int semihosting_infd = SBI_ENODEV;
static long semihosting_open(const char *fname, enum semihosting_open_mode mode)
{
long fd;
struct semihosting_open_s {
const char *fname;
unsigned long mode;
size_t len;
} open;
open.fname = fname;
open.len = sbi_strlen(fname);
open.mode = mode;
/* Open the file on the host */
fd = semihosting_trap(SYSOPEN, &open);
if (fd == -1)
return semihosting_errno();
return fd;
}
/**
* struct semihosting_rdwr_s - Arguments for read and write
* @fd: A file descriptor returned from semihosting_open()
* @memp: Pointer to a buffer of memory of at least @len bytes
* @len: The number of bytes to read or write
*/
struct semihosting_rdwr_s {
long fd;
void *memp;
size_t len;
};
static long semihosting_read(long fd, void *memp, size_t len)
{
long ret;
struct semihosting_rdwr_s read;
read.fd = fd;
read.memp = memp;
read.len = len;
ret = semihosting_trap(SYSREAD, &read);
if (ret < 0)
return semihosting_errno();
return len - ret;
}
/* clang-format on */
static void semihosting_putc(char ch)
{
semihosting_trap(SYSWRITEC, &ch);
}
static int semihosting_getc(void)
{
char ch = 0;
int ret;
if (semihosting_infd < 0) {
ch = semihosting_trap(SYSREADC, NULL);
ret = ch > -1 ? ch : -1;
} else
ret = semihosting_read(semihosting_infd, &ch, 1) > 0 ? ch : -1;
return ret;
}
static struct sbi_console_device semihosting_console = {
.name = "semihosting",
.console_putc = semihosting_putc,
.console_getc = semihosting_getc
};
int semihosting_init(void)
{
semihosting_infd = semihosting_open(":tt", MODE_READ);
sbi_console_set_device(&semihosting_console);
return 0;
}

View File

@ -28,3 +28,4 @@ CONFIG_FDT_SERIAL_UART8250=y
CONFIG_FDT_SERIAL_XILINX_UARTLITE=y
CONFIG_FDT_TIMER=y
CONFIG_FDT_TIMER_MTIMER=y
CONFIG_SERIAL_SEMIHOSTING=y

View File

@ -23,6 +23,7 @@
#include <sbi_utils/timer/fdt_timer.h>
#include <sbi_utils/ipi/fdt_ipi.h>
#include <sbi_utils/reset/fdt_reset.h>
#include <sbi_utils/serial/semihosting.h>
/* List of platform override modules generated at compile time */
extern const struct platform_override *platform_override_modules[];
@ -242,6 +243,14 @@ static uint64_t generic_pmu_xlate_to_mhpmevent(uint32_t event_idx,
return evt_val;
}
static int generic_console_init(void)
{
if (semihosting_enabled())
return semihosting_init();
else
return fdt_serial_init();
}
const struct sbi_platform_operations platform_ops = {
.nascent_init = generic_nascent_init,
.early_init = generic_early_init,
@ -249,7 +258,7 @@ const struct sbi_platform_operations platform_ops = {
.early_exit = generic_early_exit,
.final_exit = generic_final_exit,
.domains_init = generic_domains_init,
.console_init = fdt_serial_init,
.console_init = generic_console_init,
.irqchip_init = fdt_irqchip_init,
.irqchip_exit = fdt_irqchip_exit,
.ipi_init = fdt_ipi_init,