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
|
This should be enabled if U-Boot needs to communicate with system
|
||||||
firmware (for example, PSCI) according to SMCCC.
|
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
|
config SYS_THUMB_BUILD
|
||||||
bool "Build U-Boot using the Thumb instruction set"
|
bool "Build U-Boot using the Thumb instruction set"
|
||||||
depends on !ARM64
|
depends on !ARM64
|
||||||
|
@ -5,20 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <common.h>
|
#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)
|
* Macro to force the compiler to *populate* memory (for an array or struct)
|
||||||
@ -39,7 +25,7 @@
|
|||||||
/*
|
/*
|
||||||
* Call the handler
|
* 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 long result asm("r0");
|
||||||
register void *_addr asm("r1") = addr;
|
register void *_addr asm("r1") = addr;
|
||||||
@ -59,168 +45,3 @@ static long smh_trap(unsigned int sysnum, void *addr)
|
|||||||
|
|
||||||
return result;
|
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_SVC 0xDFAB
|
||||||
#define SMH_T32_HLT 0xBABC
|
#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)
|
#if CONFIG_IS_ENABLED(SEMIHOSTING_FALLBACK)
|
||||||
/**
|
/**
|
||||||
* semihosting_enabled() - Determine whether semihosting is supported
|
* semihosting_enabled() - Determine whether semihosting is supported
|
||||||
|
47
lib/Kconfig
47
lib/Kconfig
@ -71,6 +71,53 @@ config HAVE_PRIVATE_LIBGCC
|
|||||||
config LIB_UUID
|
config LIB_UUID
|
||||||
bool
|
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
|
config PRINTF
|
||||||
bool
|
bool
|
||||||
default y
|
default y
|
||||||
|
@ -146,6 +146,8 @@ obj-y += date.o
|
|||||||
obj-y += rtc-lib.o
|
obj-y += rtc-lib.o
|
||||||
obj-$(CONFIG_LIB_ELF) += elf.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
|
# 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