mirror of
				https://github.com/riscv-software-src/opensbi
				synced 2025-11-04 05:50:22 +00:00 
			
		
		
		
	For some debuggers that do not implement SYSWRITEC and SYSREADC operations, we have to use SYSWRITE and SYSREAD. Instead of implementing semihosting_putc() using SYSWRITE, let us simply remove semihosting_putc() because console_putc/console_puts are now interchangeable. Signed-off-by: Chen Pei <cp0613@linux.alibaba.com> Signed-off-by: Xiang W <wxjstz@126.com> Reviewed-by: Guo Ren <guoren@kernel.org> Reviewed-by: Anup Patel <anup@brainfault.org>
		
			
				
	
	
		
			210 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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 SYSWRITE    0x05
 | 
						|
#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 int semihosting_outfd = 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;
 | 
						|
}
 | 
						|
 | 
						|
static long semihosting_write(long fd, const void *memp, size_t len)
 | 
						|
{
 | 
						|
	long ret;
 | 
						|
	struct semihosting_rdwr_s write;
 | 
						|
 | 
						|
	write.fd = fd;
 | 
						|
	write.memp = (void *)memp;
 | 
						|
	write.len = len;
 | 
						|
 | 
						|
	ret = semihosting_trap(SYSWRITE, &write);
 | 
						|
	if (ret < 0)
 | 
						|
		return semihosting_errno();
 | 
						|
	return len - ret;
 | 
						|
}
 | 
						|
 | 
						|
/* clang-format on */
 | 
						|
 | 
						|
static unsigned long semihosting_puts(const char *str, unsigned long len)
 | 
						|
{
 | 
						|
	char ch;
 | 
						|
	long ret;
 | 
						|
	unsigned long i;
 | 
						|
 | 
						|
	if (semihosting_outfd < 0) {
 | 
						|
		for (i = 0; i < len; i++) {
 | 
						|
			ch = str[i];
 | 
						|
			semihosting_trap(SYSWRITEC, &ch);
 | 
						|
		}
 | 
						|
		ret = len;
 | 
						|
	} else
 | 
						|
		ret = semihosting_write(semihosting_outfd, str, len);
 | 
						|
 | 
						|
	return (ret < 0) ? 0 : ret;
 | 
						|
}
 | 
						|
 | 
						|
static int semihosting_getc(void)
 | 
						|
{
 | 
						|
	char ch = 0;
 | 
						|
	int ret;
 | 
						|
 | 
						|
	if (semihosting_infd < 0)  {
 | 
						|
		ret = semihosting_trap(SYSREADC, NULL);
 | 
						|
		ret = ret < 0 ? -1 : ret;
 | 
						|
	} else
 | 
						|
		ret = semihosting_read(semihosting_infd, &ch, 1) > 0 ? ch : -1;
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static struct sbi_console_device semihosting_console = {
 | 
						|
	.name = "semihosting",
 | 
						|
	.console_puts = semihosting_puts,
 | 
						|
	.console_getc = semihosting_getc
 | 
						|
};
 | 
						|
 | 
						|
int semihosting_init(void)
 | 
						|
{
 | 
						|
	semihosting_infd = semihosting_open(":tt", MODE_READ);
 | 
						|
	semihosting_outfd = semihosting_open(":tt", MODE_WRITE);
 | 
						|
 | 
						|
	sbi_console_set_device(&semihosting_console);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 |