arch: common: semihost: add semihosting operations

Add an API that utilizes the ARM semihosting mechanism to interact with
the host system when a device is being emulated or run under a debugger.

RISCV is implemented in terms of the ARM implementation, and therefore
the ARM definitions cross enough architectures to be defined 'common'.

Functionality is exposed as a separate API instead of syscall
implementations (`_lseek`, `_open`, etc) due to various quirks with
the ARM mechanisms that means function arguments are not standard.

For more information see:
https://developer.arm.com/documentation/dui0471/m/what-is-semihosting-

Signed-off-by: Jordan Yates <jordan.yates@data61.csiro.au>

impl
This commit is contained in:
Jordan Yates 2022-03-30 20:43:41 +10:00 committed by Carles Cufí
parent 070422db46
commit d8f186aa4a
11 changed files with 426 additions and 0 deletions

View file

@ -15,3 +15,4 @@ zephyr_library_sources(
) )
zephyr_library_sources_ifdef(CONFIG_USERSPACE thread.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE thread.c)
zephyr_library_sources_ifdef(CONFIG_SEMIHOST semihost.c)

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2022, Commonwealth Scientific and Industrial Research
* Organisation (CSIRO) ABN 41 687 119 230.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/sys/util_macro.h>
#include <zephyr/arch/common/semihost.h>
#if !(defined(CONFIG_ISA_ARM) || defined(CONFIG_ISA_THUMB2))
#error Unsupported ISA
#endif
long semihost_exec(enum semihost_instr instr, void *args)
{
register unsigned long r0 __asm__ ("r0") = instr;
register void *r1 __asm__ ("r1") = args;
register long ret __asm__ ("r0");
if (IS_ENABLED(CONFIG_ISA_THUMB2)) {
__asm__ __volatile__ ("svc 0xab" : : "r" (r0), "r" (r1) : "memory");
} else {
__asm__ __volatile__ ("svc 0x123456" : : "r" (r0), "r" (r1) : "memory");
}
return ret;
}

View file

@ -17,6 +17,7 @@ zephyr_library_sources(
zephyr_library_sources_ifdef(CONFIG_USERSPACE thread.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE thread.c)
zephyr_library_sources_ifdef(CONFIG_DEBUG_COREDUMP coredump.c) zephyr_library_sources_ifdef(CONFIG_DEBUG_COREDUMP coredump.c)
zephyr_library_sources_ifdef(CONFIG_THREAD_LOCAL_STORAGE __aeabi_read_tp.S) zephyr_library_sources_ifdef(CONFIG_THREAD_LOCAL_STORAGE __aeabi_read_tp.S)
zephyr_library_sources_ifdef(CONFIG_SEMIHOST semihost.c)
if(CONFIG_NULL_POINTER_EXCEPTION_DETECTION_DWT) if(CONFIG_NULL_POINTER_EXCEPTION_DETECTION_DWT)
zephyr_library_sources(debug.c) zephyr_library_sources(debug.c)

View file

@ -0,0 +1,18 @@
/*
* Copyright (c) 2022, Commonwealth Scientific and Industrial Research
* Organisation (CSIRO) ABN 41 687 119 230.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/arch/common/semihost.h>
long semihost_exec(enum semihost_instr instr, void *args)
{
register unsigned int r0 __asm__ ("r0") = instr;
register void *r1 __asm__ ("r1") = args;
register int ret __asm__ ("r0");
__asm__ __volatile__ ("bkpt 0xab" : : "r" (r0), "r" (r1) : "memory");
return ret;
}

View file

@ -25,6 +25,7 @@ zephyr_library_sources_ifdef(CONFIG_THREAD_LOCAL_STORAGE tls.c)
zephyr_library_sources_ifdef(CONFIG_HAS_ARM_SMCCC smccc-call.S) zephyr_library_sources_ifdef(CONFIG_HAS_ARM_SMCCC smccc-call.S)
zephyr_library_sources_ifdef(CONFIG_AARCH64_IMAGE_HEADER header.S) zephyr_library_sources_ifdef(CONFIG_AARCH64_IMAGE_HEADER header.S)
zephyr_library_sources_ifdef(CONFIG_CACHE_MANAGEMENT cache.c) zephyr_library_sources_ifdef(CONFIG_CACHE_MANAGEMENT cache.c)
zephyr_library_sources_ifdef(CONFIG_SEMIHOST semihost.c)
if ((CONFIG_MP_NUM_CPUS GREATER 1) OR (CONFIG_SMP)) if ((CONFIG_MP_NUM_CPUS GREATER 1) OR (CONFIG_SMP))
zephyr_library_sources(smp.c) zephyr_library_sources(smp.c)
endif () endif ()

View file

@ -0,0 +1,18 @@
/*
* Copyright (c) 2022, Commonwealth Scientific and Industrial Research
* Organisation (CSIRO) ABN 41 687 119 230.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/arch/common/semihost.h>
long semihost_exec(enum semihost_instr instr, void *args)
{
register unsigned long w0 __asm__ ("w0") = instr;
register void *x1 __asm__ ("x1") = args;
register long ret __asm__ ("x0");
__asm__ volatile ("hlt 0xf000" : : "r" (w0), "r" (x1) : "memory");
return ret;
}

View file

@ -67,3 +67,5 @@ if(CONFIG_COVERAGE)
zephyr_compile_options($<TARGET_PROPERTY:compiler,coverage>) zephyr_compile_options($<TARGET_PROPERTY:compiler,coverage>)
zephyr_link_libraries($<TARGET_PROPERTY:linker,coverage>) zephyr_link_libraries($<TARGET_PROPERTY:linker,coverage>)
endif() endif()
zephyr_sources_ifdef(CONFIG_SEMIHOST semihost.c)

131
arch/common/semihost.c Normal file
View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2022, Commonwealth Scientific and Industrial Research
* Organisation (CSIRO) ABN 41 687 119 230.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/kernel.h>
#include <zephyr/arch/common/semihost.h>
struct semihost_poll_in_args {
long zero;
} __packed;
struct semihost_open_args {
const char *path;
long mode;
long path_len;
} __packed;
struct semihost_close_args {
long fd;
} __packed;
struct semihost_flen_args {
long fd;
} __packed;
struct semihost_seek_args {
long fd;
long offset;
} __packed;
struct semihost_read_args {
long fd;
char *buf;
long len;
} __packed;
struct semihost_write_args {
long fd;
const char *buf;
long len;
} __packed;
char semihost_poll_in(void)
{
struct semihost_poll_in_args args = {
.zero = 0
};
return (char)semihost_exec(SEMIHOST_READC, &args);
}
void semihost_poll_out(char c)
{
/* WRITEC takes a pointer directly to the character */
(void)semihost_exec(SEMIHOST_WRITEC, &c);
}
long semihost_open(const char *path, long mode)
{
struct semihost_open_args args = {
.path = path,
.mode = mode,
.path_len = strlen(path)
};
return semihost_exec(SEMIHOST_OPEN, &args);
}
long semihost_close(long fd)
{
struct semihost_close_args args = {
.fd = fd
};
return semihost_exec(SEMIHOST_CLOSE, &args);
}
long semihost_flen(long fd)
{
struct semihost_flen_args args = {
.fd = fd
};
return semihost_exec(SEMIHOST_FLEN, &args);
}
long semihost_seek(long fd, long offset)
{
struct semihost_seek_args args = {
.fd = fd,
.offset = offset
};
return semihost_exec(SEMIHOST_SEEK, &args);
}
long semihost_read(long fd, void *buf, long len)
{
struct semihost_read_args args = {
.fd = fd,
.buf = buf,
.len = len
};
long ret;
ret = semihost_exec(SEMIHOST_READ, &args);
/* EOF condition */
if (ret == len) {
ret = -EIO;
}
/* All bytes read */
else if (ret == 0) {
ret = len;
}
return ret;
}
long semihost_write(long fd, const void *buf, long len)
{
struct semihost_write_args args = {
.fd = fd,
.buf = buf,
.len = len
};
return semihost_exec(SEMIHOST_WRITE, &args);
}

View file

@ -21,3 +21,4 @@ zephyr_library_sources_ifdef(CONFIG_DEBUG_COREDUMP coredump.c)
zephyr_library_sources_ifdef(CONFIG_IRQ_OFFLOAD irq_offload.c) zephyr_library_sources_ifdef(CONFIG_IRQ_OFFLOAD irq_offload.c)
zephyr_library_sources_ifdef(CONFIG_THREAD_LOCAL_STORAGE tls.c) zephyr_library_sources_ifdef(CONFIG_THREAD_LOCAL_STORAGE tls.c)
zephyr_library_sources_ifdef(CONFIG_USERSPACE userspace.S) zephyr_library_sources_ifdef(CONFIG_USERSPACE userspace.S)
zephyr_library_sources_ifdef(CONFIG_SEMIHOST semihost.c)

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2022, Commonwealth Scientific and Industrial Research
* Organisation (CSIRO) ABN 41 687 119 230.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/arch/common/semihost.h>
long semihost_exec(enum semihost_instr instr, void *args)
{
register unsigned long a0 __asm__ ("a0") = instr;
register void *a1 __asm__ ("a1") = args;
register long ret __asm__ ("a0");
__asm__ volatile (
".option push\n\t"
".option norvc\n\t"
"slli zero, zero, 0x1f\n\t"
"ebreak\n\t"
"srai zero, zero, 0x7\n\t"
".option pop"
: : "r" (a0), "r" (a1) : "memory");
return ret;
}

View file

@ -0,0 +1,200 @@
/*
* Copyright (c) 2022, Commonwealth Scientific and Industrial Research
* Organisation (CSIRO) ABN 41 687 119 230.
*
* SPDX-License-Identifier: Apache-2.0
*
* Based on the ARM semihosting API from:
* https://github.com/ARM-software/abi-aa/blob/main/semihosting/semihosting.rst
*
* RISC-V semihosting also follows these conventions:
* https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc
*/
/**
* @file
*
* @brief public Semihosting APIs based on ARM definitions.
* @defgroup semihost Semihosting APIs
* @{
*/
#ifndef ZEPHYR_INCLUDE_ARCH_COMMON_SEMIHOST_H_
#define ZEPHYR_INCLUDE_ARCH_COMMON_SEMIHOST_H_
/** @brief Semihosting instructions */
enum semihost_instr {
/*
* File I/O operations
*/
/** Open a file or stream on the host system. */
SEMIHOST_OPEN = 0x01,
/** Check whether a file is associated with a stream/terminal */
SEMIHOST_ISTTY = 0x09,
/** Write to a file or stream. */
SEMIHOST_WRITE = 0x05,
/** Read from a file at the current cursor position. */
SEMIHOST_READ = 0x06,
/** Closes a file on the host which has been opened by SEMIHOST_OPEN. */
SEMIHOST_CLOSE = 0x02,
/** Get the length of a file. */
SEMIHOST_FLEN = 0x0C,
/** Set the file cursor to a given position in a file. */
SEMIHOST_SEEK = 0x0A,
/** Get a temporary absolute file path to create a temporary file. */
SEMIHOST_TMPNAM = 0x0D,
/** Remove a file on the host system. Possibly insecure! */
SEMIHOST_REMOVE = 0x0E,
/** Rename a file on the host system. Possibly insecure! */
SEMIHOST_RENAME = 0x0F,
/*
* Terminal I/O operations
*/
/** Write one character to the debug terminal. */
SEMIHOST_WRITEC = 0x03,
/** Write a NULL terminated string to the debug terminal. */
SEMIHOST_WRITE0 = 0x04,
/** Read one character from the debug terminal. */
SEMIHOST_READC = 0x07,
/*
* Time operations
*/
SEMIHOST_CLOCK = 0x10,
SEMIHOST_ELAPSED = 0x30,
SEMIHOST_TICKFREQ = 0x31,
SEMIHOST_TIME = 0x11,
/*
* System/Misc. operations
*/
/** Retrieve the errno variable from semihosting operations. */
SEMIHOST_ERRNO = 0x13,
/** Get commandline parameters for the application to run with */
SEMIHOST_GET_CMDLINE = 0x15,
SEMIHOST_HEAPINFO = 0x16,
SEMIHOST_ISERROR = 0x08,
SEMIHOST_SYSTEM = 0x12
};
/**
* @brief Modes to open a file with
*
* Behaviour corresponds to equivalent fopen strings.
* i.e. SEMIHOST_OPEN_RB_PLUS == "rb+"
*/
enum semihost_open_mode {
SEMIHOST_OPEN_R = 0,
SEMIHOST_OPEN_RB = 1,
SEMIHOST_OPEN_R_PLUS = 2,
SEMIHOST_OPEN_RB_PLUS = 3,
SEMIHOST_OPEN_W = 4,
SEMIHOST_OPEN_WB = 5,
SEMIHOST_OPEN_W_PLUS = 6,
SEMIHOST_OPEN_WB_PLUS = 7,
SEMIHOST_OPEN_A = 8,
SEMIHOST_OPEN_AB = 9,
SEMIHOST_OPEN_A_PLUS = 10,
SEMIHOST_OPEN_AB_PLUS = 11,
};
/**
* @brief Manually execute a semihosting instruction
*
* @param instr instruction code to run
* @param args instruction specific arguments
*
* @return integer return code of instruction
*/
long semihost_exec(enum semihost_instr instr, void *args);
/**
* @brief Read a byte from the console
*
* @return char byte read from the console.
*/
char semihost_poll_in(void);
/**
* @brief Write a byte to the console
*
* @param c byte to write to console
*/
void semihost_poll_out(char c);
/**
* @brief Open a file on the host system
*
* @param path file path to open. Can be absolute or relative to current
* directory of the running process.
* @param mode value from @ref semihost_open_mode.
*
* @retval handle positive handle on success.
* @retval -1 on failure.
*/
long semihost_open(const char *path, long mode);
/**
* @brief Close a file
*
* @param fd handle returned by @ref semihost_open.
*
* @retval 0 on success.
* @retval -1 on failure.
*/
long semihost_close(long fd);
/**
* @brief Query the size of a file
*
* @param fd handle returned by @ref semihost_open.
*
* @retval positive file size on success.
* @retval -1 on failure.
*/
long semihost_flen(long fd);
/**
* @brief Seeks to an absolute position in a file.
*
* @param fd handle returned by @ref semihost_open.
* @param offset offset from the start of the file in bytes.
*
* @retval 0 on success.
* @retval -errno negative error code on failure.
*/
long semihost_seek(long fd, long offset);
/**
* @brief Read the contents of a file into a buffer.
*
* @param fd handle returned by @ref semihost_open.
* @param buf buffer to read data into.
* @param len number of bytes to read.
*
* @retval read number of bytes read on success.
* @retval -errno negative error code on failure.
*/
long semihost_read(long fd, void *buf, long len);
/**
* @brief Write the contents of a buffer into a file.
*
* @param fd handle returned by @ref semihost_open.
* @param buf buffer to write data from.
* @param len number of bytes to write.
*
* @retval 0 on success.
* @retval -errno negative error code on failure.
*/
long semihost_write(long fd, const void *buf, long len);
/**
* @}
*/
#endif /* ZEPHYR_INCLUDE_ARCH_COMMON_SEMIHOST_H_ */