Browse Source

arch: xtensa: Add semihosting support

Add semihosting support for Xtensa architecture.

Existing semihosting instructions are based on ARM, so they are
converted to Xtensa codes before the semihosting call is invoked.

Return codes of read, write and seek calls had to be converted to match
semihosting API definitions.

Signed-off-by: Tahsin Mutlugun <Tahsin.Mutlugun@analog.com>
pull/86618/head
Tahsin Mutlugun 3 weeks ago committed by Benjamin Cabé
parent
commit
784b3d6ea0
  1. 8
      arch/common/Kconfig
  2. 5
      arch/xtensa/core/CMakeLists.txt
  3. 249
      arch/xtensa/core/semihost.c

8
arch/common/Kconfig

@ -4,11 +4,11 @@ @@ -4,11 +4,11 @@
# SPDX-License-Identifier: Apache-2.0
config SEMIHOST
bool "Semihosting support for ARM and RISC-V targets"
depends on ARM || ARM64 || RISCV
bool "Semihosting support for ARM, RISC-V and Xtensa targets"
depends on ARM || ARM64 || RISCV || (XTENSA && !SIMULATOR_XTENSA)
help
Semihosting is a mechanism that enables code running on an ARM or
RISC-V target to communicate and use the Input/Output facilities on
Semihosting is a mechanism that enables code running on an ARM, RISC-V
or Xtensa target to communicate and use the Input/Output facilities on
a host computer that is running a debugger.
Additional information can be found in:
https://developer.arm.com/documentation/dui0471/m/what-is-semihosting-

5
arch/xtensa/core/CMakeLists.txt

@ -23,6 +23,7 @@ zephyr_library_sources_ifdef(CONFIG_XTENSA_ENABLE_BACKTRACE debug_helpers_asm.S) @@ -23,6 +23,7 @@ zephyr_library_sources_ifdef(CONFIG_XTENSA_ENABLE_BACKTRACE debug_helpers_asm.S)
zephyr_library_sources_ifdef(CONFIG_DEBUG_COREDUMP coredump.c)
zephyr_library_sources_ifdef(CONFIG_TIMING_FUNCTIONS timing.c)
zephyr_library_sources_ifdef(CONFIG_GDBSTUB gdbstub.c)
zephyr_library_sources_ifdef(CONFIG_SEMIHOST semihost.c)
zephyr_library_sources_ifdef(CONFIG_XTENSA_MMU ptables.c mmu.c)
zephyr_library_sources_ifdef(CONFIG_XTENSA_MPU mpu.c)
zephyr_library_sources_ifdef(CONFIG_USERSPACE userspace.S syscall_helper.c)
@ -90,6 +91,10 @@ else() @@ -90,6 +91,10 @@ else()
set(NEED_FLUSH_SCRATCH_REG false)
endif()
if(CONFIG_SEMIHOST)
zephyr_library_include_directories(${ZEPHYR_BASE}/arch/common/include)
endif()
# Generates a list of device-specific scratch register choices
set(ZSR_H ${CMAKE_BINARY_DIR}/zephyr/include/generated/zephyr/zsr.h)
add_custom_command(OUTPUT ${ZSR_H} DEPENDS ${CORE_ISA_DM}

249
arch/xtensa/core/semihost.c

@ -0,0 +1,249 @@ @@ -0,0 +1,249 @@
/*
* Copyright (c) 2022 Intel Corporation.
* Copyright (c) 2025 Analog Devices, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/sys/byteorder.h>
#include <zephyr/arch/common/semihost.h>
#include "semihost_types.h"
#define XTENSA_SEMIHOST_OPEN (-2)
#define XTENSA_SEMIHOST_CLOSE (-3)
#define XTENSA_SEMIHOST_READ (-4)
#define XTENSA_SEMIHOST_WRITE (-5)
#define XTENSA_SEMIHOST_LSEEK (-6)
#define XTENSA_SEMIHOST_RENAME (-7)
#define XTENSA_SEMIHOST_FSTAT (-10)
enum semihost_open_flag {
SEMIHOST_RDONLY = 0x0,
SEMIHOST_WRONLY = 0x1,
SEMIHOST_RDWR = 0x2,
SEMIHOST_APPEND = 0x8,
SEMIHOST_CREAT = 0x200,
SEMIHOST_TRUNC = 0x400,
SEMIHOST_EXCL = 0x800,
};
uint32_t semihost_flags(enum semihost_open_mode mode)
{
uint32_t flags = 0;
switch (mode) {
case SEMIHOST_OPEN_R:
case SEMIHOST_OPEN_RB:
flags = SEMIHOST_RDONLY;
break;
case SEMIHOST_OPEN_R_PLUS:
case SEMIHOST_OPEN_RB_PLUS:
flags = SEMIHOST_RDWR;
break;
case SEMIHOST_OPEN_W:
case SEMIHOST_OPEN_WB:
flags = SEMIHOST_WRONLY | SEMIHOST_CREAT | SEMIHOST_TRUNC;
break;
case SEMIHOST_OPEN_W_PLUS:
case SEMIHOST_OPEN_WB_PLUS:
flags = SEMIHOST_RDWR | SEMIHOST_CREAT | SEMIHOST_TRUNC;
break;
case SEMIHOST_OPEN_A:
case SEMIHOST_OPEN_AB:
flags = SEMIHOST_WRONLY | SEMIHOST_CREAT | SEMIHOST_APPEND;
break;
case SEMIHOST_OPEN_A_PLUS:
case SEMIHOST_OPEN_AB_PLUS:
flags = SEMIHOST_RDWR | SEMIHOST_CREAT | SEMIHOST_APPEND;
break;
default:
return -1;
}
return flags;
}
uint32_t semihost_mode(enum semihost_open_mode mode)
{
switch (mode) {
case SEMIHOST_OPEN_W:
case SEMIHOST_OPEN_WB:
case SEMIHOST_OPEN_W_PLUS:
case SEMIHOST_OPEN_WB_PLUS:
case SEMIHOST_OPEN_A:
case SEMIHOST_OPEN_AB:
case SEMIHOST_OPEN_A_PLUS:
case SEMIHOST_OPEN_AB_PLUS:
/* Octal 0600, S_IRUSR | S_IWUSR */
return 0x180;
default:
return 0;
}
}
static inline uintptr_t xtensa_semihost_call_4(uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
uintptr_t arg4, uintptr_t call_id)
{
register uintptr_t a2 __asm__("%a2") = call_id;
register uintptr_t a6 __asm__("%a6") = arg1;
register uintptr_t a3 __asm__("%a3") = arg2;
register uintptr_t a4 __asm__("%a4") = arg3;
register uintptr_t a5 __asm__("%a5") = arg4;
__asm__ volatile("break 1, 14\n\t"
: "=r"(a2)
: "r"(a2), "r"(a6), "r"(a3), "r"(a4), "r"(a5)
: "memory");
return a2;
}
static inline uintptr_t xtensa_semihost_call_3(uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
uintptr_t call_id)
{
register uintptr_t a2 __asm__("%a2") = call_id;
register uintptr_t a6 __asm__("%a6") = arg1;
register uintptr_t a3 __asm__("%a3") = arg2;
register uintptr_t a4 __asm__("%a4") = arg3;
__asm__ volatile("break 1, 14\n\t"
: "=r"(a2)
: "r"(a2), "r"(a6), "r"(a3), "r"(a4)
: "memory");
return a2;
}
static inline uintptr_t xtensa_semihost_call_2(uintptr_t arg1, uintptr_t arg2, uintptr_t call_id)
{
register uintptr_t a2 __asm__("%a2") = call_id;
register uintptr_t a6 __asm__("%a6") = arg1;
register uintptr_t a3 __asm__("%a3") = arg2;
__asm__ volatile("break 1, 14\n\t" : "=r"(a2) : "r"(a2), "r"(a6), "r"(a3) : "memory");
return a2;
}
static inline uintptr_t xtensa_semihost_call_1(uintptr_t arg1, uintptr_t call_id)
{
register uintptr_t a2 __asm__("%a2") = call_id;
register uintptr_t a6 __asm__("%a6") = arg1;
__asm__ volatile("break 1, 14\n\t" : "=r"(a2) : "r"(a2), "r"(a6) : "memory");
return a2;
}
long xtensa_semihost_open(struct semihost_open_args *args)
{
return xtensa_semihost_call_4((uintptr_t)args->path, semihost_flags(args->mode),
semihost_mode(args->mode), args->path_len,
XTENSA_SEMIHOST_OPEN);
}
long xtensa_semihost_close(long fd)
{
return xtensa_semihost_call_1(fd, XTENSA_SEMIHOST_CLOSE);
}
long xtensa_semihost_write(long fd, const char *buf, long len)
{
long ret;
ret = (long)xtensa_semihost_call_3(fd, (uintptr_t)buf, len, XTENSA_SEMIHOST_WRITE);
/* semihost_write assumes that data was written successfully if ret == 0. */
if (ret == len) {
return 0;
}
return -1;
}
long xtensa_semihost_read(long fd, void *buf, long len)
{
long ret;
ret = (long)xtensa_semihost_call_3(fd, (uintptr_t)buf, len, XTENSA_SEMIHOST_READ);
/* semihost_read assumes that all bytes were read if ret == 0.
* If ret == len, it means EOF was reached.
*/
if (ret == len) {
return 0;
} else if (ret <= 0) {
return len;
} else {
return ret;
}
}
long xtensa_semihost_read_char(long fd)
{
char c = 0;
xtensa_semihost_call_3(fd, (uintptr_t)&c, 1, XTENSA_SEMIHOST_READ);
return (long)c;
}
long xtensa_semihost_seek(struct semihost_seek_args *args)
{
long ret;
ret = (long)xtensa_semihost_call_3(args->fd, args->offset, 0, XTENSA_SEMIHOST_LSEEK);
if (ret == args->offset) {
return 0;
}
return ret;
}
long xtensa_semihost_flen(long fd)
{
uint8_t buf[64] = {0};
long ret;
ret = (long)xtensa_semihost_call_2(fd, (uintptr_t)buf, XTENSA_SEMIHOST_FSTAT);
if (ret < 0) {
return -1;
}
/* Struct stat is 64 bytes, bytes 28-35 correspond to st_size
* field. 8-bytes cannot fit into long data type so return
* only the lower 4 bytes.
*/
ret = *((long *)&buf[32]);
return sys_be32_to_cpu(ret);
}
long semihost_exec(enum semihost_instr instr, void *args)
{
switch (instr) {
case SEMIHOST_OPEN:
return xtensa_semihost_open((struct semihost_open_args *)args);
case SEMIHOST_CLOSE:
return xtensa_semihost_close(((struct semihost_close_args *)args)->fd);
case SEMIHOST_WRITEC:
return xtensa_semihost_write(1, (char *)args, 1);
case SEMIHOST_WRITE:
return xtensa_semihost_write(((struct semihost_write_args *)args)->fd,
((struct semihost_write_args *)args)->buf,
((struct semihost_write_args *)args)->len);
case SEMIHOST_READ:
return xtensa_semihost_read(((struct semihost_read_args *)args)->fd,
((struct semihost_read_args *)args)->buf,
((struct semihost_read_args *)args)->len);
case SEMIHOST_READC:
return xtensa_semihost_read_char(((struct semihost_poll_in_args *)args)->zero);
case SEMIHOST_SEEK:
return xtensa_semihost_seek((struct semihost_seek_args *)args);
case SEMIHOST_FLEN:
return xtensa_semihost_flen(((struct semihost_flen_args *)args)->fd);
default:
return -1;
}
}
Loading…
Cancel
Save