lib: Add common semihosting library
We factor out the arch-independent parts of the ARM semihosting implementation as a common library so that it can be shared with RISC-V. Signed-off-by: Kautuk Consul <kconsul@ventanamicro.com> Reviewed-by: Leo Yu-Chi Liang <ycliang@andestech.com>
This commit is contained in:
parent
14f2d087a3
commit
1c03ab9f4b
@ -413,52 +413,6 @@ config ARM_SMCCC
|
||||
This should be enabled if U-Boot needs to communicate with system
|
||||
firmware (for example, PSCI) according to SMCCC.
|
||||
|
||||
config SEMIHOSTING
|
||||
bool "Support ARM 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 && ARM64
|
||||
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 ARM 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 && ARM64
|
||||
select ARMV8_SPL_EXCEPTION_VECTORS
|
||||
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 SYS_THUMB_BUILD
|
||||
bool "Build U-Boot using the Thumb instruction set"
|
||||
depends on !ARM64
|
||||
|
@ -5,20 +5,6 @@
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <log.h>
|
||||
#include <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
|
||||
|
||||
/*
|
||||
* Macro to force the compiler to *populate* memory (for an array or struct)
|
||||
@ -39,7 +25,7 @@
|
||||
/*
|
||||
* Call the handler
|
||||
*/
|
||||
static long smh_trap(unsigned int sysnum, void *addr)
|
||||
long smh_trap(unsigned int sysnum, void *addr)
|
||||
{
|
||||
register long result asm("r0");
|
||||
register void *_addr asm("r1") = addr;
|
||||
@ -59,168 +45,3 @@ static long smh_trap(unsigned int sysnum, void *addr)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK)
|
||||
static bool _semihosting_enabled = true;
|
||||
static bool try_semihosting = true;
|
||||
|
||||
bool semihosting_enabled(void)
|
||||
{
|
||||
if (try_semihosting) {
|
||||
smh_trap(SYSERRNO, NULL);
|
||||
try_semihosting = false;
|
||||
}
|
||||
|
||||
return _semihosting_enabled;
|
||||
}
|
||||
|
||||
void disable_semihosting(void)
|
||||
{
|
||||
_semihosting_enabled = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
@ -17,6 +17,17 @@
|
||||
#define SMH_T32_SVC 0xDFAB
|
||||
#define SMH_T32_HLT 0xBABC
|
||||
|
||||
/**
|
||||
* smh_trap() - ARCH-specific semihosting call.
|
||||
*
|
||||
* Semihosting library/driver can use this function to do the
|
||||
* actual semihosting calls.
|
||||
*
|
||||
* Return: Error code defined by semihosting spec.
|
||||
*/
|
||||
|
||||
long smh_trap(unsigned int sysnum, void *addr);
|
||||
|
||||
#if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK)
|
||||
/**
|
||||
* semihosting_enabled() - Determine whether semihosting is supported
|
||||
|
47
lib/Kconfig
47
lib/Kconfig
@ -71,6 +71,53 @@ config HAVE_PRIVATE_LIBGCC
|
||||
config LIB_UUID
|
||||
bool
|
||||
|
||||
config SEMIHOSTING
|
||||
bool "Support semihosting"
|
||||
depends on ARM
|
||||
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 && ARM64
|
||||
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 semihosting in SPL"
|
||||
depends on SPL && ARM
|
||||
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 && ARM64
|
||||
select ARMV8_SPL_EXCEPTION_VECTORS
|
||||
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 PRINTF
|
||||
bool
|
||||
default y
|
||||
|
@ -146,6 +146,8 @@ obj-y += date.o
|
||||
obj-y += rtc-lib.o
|
||||
obj-$(CONFIG_LIB_ELF) += elf.o
|
||||
|
||||
obj-$(CONFIG_$(SPL_TPL_)SEMIHOSTING) += semihosting.o
|
||||
|
||||
#
|
||||
# Build a fast OID lookup registry from include/linux/oid_registry.h
|
||||
#
|
||||
|
186
lib/semihosting.c
Normal file
186
lib/semihosting.c
Normal file
@ -0,0 +1,186 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
|
||||
* Copyright 2014 Broadcom Corporation
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <log.h>
|
||||
#include <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
|
||||
|
||||
#if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK)
|
||||
static bool _semihosting_enabled = true;
|
||||
static bool try_semihosting = true;
|
||||
|
||||
bool semihosting_enabled(void)
|
||||
{
|
||||
if (try_semihosting) {
|
||||
smh_trap(SYSERRNO, NULL);
|
||||
try_semihosting = false;
|
||||
}
|
||||
|
||||
return _semihosting_enabled;
|
||||
}
|
||||
|
||||
void disable_semihosting(void)
|
||||
{
|
||||
_semihosting_enabled = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user