[PATCH v4 1/3] lib: Add common semihosting library

Sean Anderson sean.anderson at seco.com
Thu Sep 22 19:00:07 CEST 2022



On 9/19/22 7:49 AM, Kautuk Consul wrote:
> 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 at ventanamicro.com>
> ---
>  arch/arm/Kconfig           |  46 ---------
>  arch/arm/lib/semihosting.c | 181 +-----------------------------------
>  include/semihosting.h      |  11 +++
>  lib/Kconfig                |  46 +++++++++
>  lib/Makefile               |   2 +
>  lib/semihosting.c          | 186 +++++++++++++++++++++++++++++++++++++
>  6 files changed, 246 insertions(+), 226 deletions(-)
>  create mode 100644 lib/semihosting.c
> 
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 82cd456f51..ee6a9fadd9 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -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
> diff --git a/arch/arm/lib/semihosting.c b/arch/arm/lib/semihosting.c
> index 01d652a6b8..11e7b85ee6 100644
> --- a/arch/arm/lib/semihosting.c
> +++ b/arch/arm/lib/semihosting.c
> @@ -10,25 +10,11 @@
>   * available in silicon now, fastmodel usage makes less sense for them.
>   */
>  #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
>  
>  /*
>   * Call the handler
>   */
> -static noinline long smh_trap(unsigned int sysnum, void *addr)
> +long smh_trap(unsigned int sysnum, void *addr)
>  {
>  	register long result asm("r0");
>  #if defined(CONFIG_ARM64)
> @@ -41,168 +27,3 @@ static noinline long smh_trap(unsigned int sysnum, void *addr)
>  #endif
>  	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);
> -}
> diff --git a/include/semihosting.h b/include/semihosting.h
> index f1f73464e4..4e844cbad8 100644
> --- a/include/semihosting.h
> +++ b/include/semihosting.h
> @@ -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
> diff --git a/lib/Kconfig b/lib/Kconfig
> index 6121c80dc8..97920e7552 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -71,6 +71,52 @@ config HAVE_PRIVATE_LIBGCC
>  config LIB_UUID
>  	bool
>  
> +config SEMIHOSTING
> +	bool "Support semihosting"

Should probably depend on ARM (and in the next patch RISCV).

> +	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

ditto

> +	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
> diff --git a/lib/Makefile b/lib/Makefile
> index e3deb15287..134c4319cd 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -145,6 +145,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
>  #
> diff --git a/lib/semihosting.c b/lib/semihosting.c
> new file mode 100644
> index 0000000000..831774e356
> --- /dev/null
> +++ b/lib/semihosting.c
> @@ -0,0 +1,186 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2022 Sean Anderson <sean.anderson at 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);
> +}
> 

Other than that,

Reviewed-by: Sean Anderson <sean.anderson at seco.com>


More information about the U-Boot mailing list