Skip to content

Commit 784b3d6

Browse files
ttmutkartben
authored andcommitted
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>
1 parent 38d0300 commit 784b3d6

File tree

3 files changed

+258
-4
lines changed

3 files changed

+258
-4
lines changed

arch/common/Kconfig

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
# SPDX-License-Identifier: Apache-2.0
55

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

arch/xtensa/core/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ zephyr_library_sources_ifdef(CONFIG_XTENSA_ENABLE_BACKTRACE debug_helpers_asm.S)
2323
zephyr_library_sources_ifdef(CONFIG_DEBUG_COREDUMP coredump.c)
2424
zephyr_library_sources_ifdef(CONFIG_TIMING_FUNCTIONS timing.c)
2525
zephyr_library_sources_ifdef(CONFIG_GDBSTUB gdbstub.c)
26+
zephyr_library_sources_ifdef(CONFIG_SEMIHOST semihost.c)
2627
zephyr_library_sources_ifdef(CONFIG_XTENSA_MMU ptables.c mmu.c)
2728
zephyr_library_sources_ifdef(CONFIG_XTENSA_MPU mpu.c)
2829
zephyr_library_sources_ifdef(CONFIG_USERSPACE userspace.S syscall_helper.c)
@@ -90,6 +91,10 @@ else()
9091
set(NEED_FLUSH_SCRATCH_REG false)
9192
endif()
9293

94+
if(CONFIG_SEMIHOST)
95+
zephyr_library_include_directories(${ZEPHYR_BASE}/arch/common/include)
96+
endif()
97+
9398
# Generates a list of device-specific scratch register choices
9499
set(ZSR_H ${CMAKE_BINARY_DIR}/zephyr/include/generated/zephyr/zsr.h)
95100
add_custom_command(OUTPUT ${ZSR_H} DEPENDS ${CORE_ISA_DM}

arch/xtensa/core/semihost.c

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
/*
2+
* Copyright (c) 2022 Intel Corporation.
3+
* Copyright (c) 2025 Analog Devices, Inc.
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#include <zephyr/sys/byteorder.h>
9+
#include <zephyr/arch/common/semihost.h>
10+
#include "semihost_types.h"
11+
12+
#define XTENSA_SEMIHOST_OPEN (-2)
13+
#define XTENSA_SEMIHOST_CLOSE (-3)
14+
#define XTENSA_SEMIHOST_READ (-4)
15+
#define XTENSA_SEMIHOST_WRITE (-5)
16+
#define XTENSA_SEMIHOST_LSEEK (-6)
17+
#define XTENSA_SEMIHOST_RENAME (-7)
18+
#define XTENSA_SEMIHOST_FSTAT (-10)
19+
20+
enum semihost_open_flag {
21+
SEMIHOST_RDONLY = 0x0,
22+
SEMIHOST_WRONLY = 0x1,
23+
SEMIHOST_RDWR = 0x2,
24+
SEMIHOST_APPEND = 0x8,
25+
SEMIHOST_CREAT = 0x200,
26+
SEMIHOST_TRUNC = 0x400,
27+
SEMIHOST_EXCL = 0x800,
28+
};
29+
30+
uint32_t semihost_flags(enum semihost_open_mode mode)
31+
{
32+
uint32_t flags = 0;
33+
34+
switch (mode) {
35+
case SEMIHOST_OPEN_R:
36+
case SEMIHOST_OPEN_RB:
37+
flags = SEMIHOST_RDONLY;
38+
break;
39+
case SEMIHOST_OPEN_R_PLUS:
40+
case SEMIHOST_OPEN_RB_PLUS:
41+
flags = SEMIHOST_RDWR;
42+
break;
43+
case SEMIHOST_OPEN_W:
44+
case SEMIHOST_OPEN_WB:
45+
flags = SEMIHOST_WRONLY | SEMIHOST_CREAT | SEMIHOST_TRUNC;
46+
break;
47+
case SEMIHOST_OPEN_W_PLUS:
48+
case SEMIHOST_OPEN_WB_PLUS:
49+
flags = SEMIHOST_RDWR | SEMIHOST_CREAT | SEMIHOST_TRUNC;
50+
break;
51+
case SEMIHOST_OPEN_A:
52+
case SEMIHOST_OPEN_AB:
53+
flags = SEMIHOST_WRONLY | SEMIHOST_CREAT | SEMIHOST_APPEND;
54+
break;
55+
case SEMIHOST_OPEN_A_PLUS:
56+
case SEMIHOST_OPEN_AB_PLUS:
57+
flags = SEMIHOST_RDWR | SEMIHOST_CREAT | SEMIHOST_APPEND;
58+
break;
59+
default:
60+
return -1;
61+
}
62+
63+
return flags;
64+
}
65+
66+
uint32_t semihost_mode(enum semihost_open_mode mode)
67+
{
68+
switch (mode) {
69+
case SEMIHOST_OPEN_W:
70+
case SEMIHOST_OPEN_WB:
71+
case SEMIHOST_OPEN_W_PLUS:
72+
case SEMIHOST_OPEN_WB_PLUS:
73+
case SEMIHOST_OPEN_A:
74+
case SEMIHOST_OPEN_AB:
75+
case SEMIHOST_OPEN_A_PLUS:
76+
case SEMIHOST_OPEN_AB_PLUS:
77+
/* Octal 0600, S_IRUSR | S_IWUSR */
78+
return 0x180;
79+
default:
80+
return 0;
81+
}
82+
}
83+
84+
static inline uintptr_t xtensa_semihost_call_4(uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
85+
uintptr_t arg4, uintptr_t call_id)
86+
{
87+
register uintptr_t a2 __asm__("%a2") = call_id;
88+
register uintptr_t a6 __asm__("%a6") = arg1;
89+
register uintptr_t a3 __asm__("%a3") = arg2;
90+
register uintptr_t a4 __asm__("%a4") = arg3;
91+
register uintptr_t a5 __asm__("%a5") = arg4;
92+
93+
__asm__ volatile("break 1, 14\n\t"
94+
: "=r"(a2)
95+
: "r"(a2), "r"(a6), "r"(a3), "r"(a4), "r"(a5)
96+
: "memory");
97+
98+
return a2;
99+
}
100+
101+
static inline uintptr_t xtensa_semihost_call_3(uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
102+
uintptr_t call_id)
103+
{
104+
register uintptr_t a2 __asm__("%a2") = call_id;
105+
register uintptr_t a6 __asm__("%a6") = arg1;
106+
register uintptr_t a3 __asm__("%a3") = arg2;
107+
register uintptr_t a4 __asm__("%a4") = arg3;
108+
109+
__asm__ volatile("break 1, 14\n\t"
110+
: "=r"(a2)
111+
: "r"(a2), "r"(a6), "r"(a3), "r"(a4)
112+
: "memory");
113+
114+
return a2;
115+
}
116+
117+
static inline uintptr_t xtensa_semihost_call_2(uintptr_t arg1, uintptr_t arg2, uintptr_t call_id)
118+
{
119+
register uintptr_t a2 __asm__("%a2") = call_id;
120+
register uintptr_t a6 __asm__("%a6") = arg1;
121+
register uintptr_t a3 __asm__("%a3") = arg2;
122+
123+
__asm__ volatile("break 1, 14\n\t" : "=r"(a2) : "r"(a2), "r"(a6), "r"(a3) : "memory");
124+
125+
return a2;
126+
}
127+
128+
static inline uintptr_t xtensa_semihost_call_1(uintptr_t arg1, uintptr_t call_id)
129+
{
130+
register uintptr_t a2 __asm__("%a2") = call_id;
131+
register uintptr_t a6 __asm__("%a6") = arg1;
132+
133+
__asm__ volatile("break 1, 14\n\t" : "=r"(a2) : "r"(a2), "r"(a6) : "memory");
134+
135+
return a2;
136+
}
137+
138+
long xtensa_semihost_open(struct semihost_open_args *args)
139+
{
140+
return xtensa_semihost_call_4((uintptr_t)args->path, semihost_flags(args->mode),
141+
semihost_mode(args->mode), args->path_len,
142+
XTENSA_SEMIHOST_OPEN);
143+
}
144+
145+
long xtensa_semihost_close(long fd)
146+
{
147+
return xtensa_semihost_call_1(fd, XTENSA_SEMIHOST_CLOSE);
148+
}
149+
150+
long xtensa_semihost_write(long fd, const char *buf, long len)
151+
{
152+
long ret;
153+
154+
ret = (long)xtensa_semihost_call_3(fd, (uintptr_t)buf, len, XTENSA_SEMIHOST_WRITE);
155+
156+
/* semihost_write assumes that data was written successfully if ret == 0. */
157+
if (ret == len) {
158+
return 0;
159+
}
160+
161+
return -1;
162+
}
163+
164+
long xtensa_semihost_read(long fd, void *buf, long len)
165+
{
166+
long ret;
167+
168+
ret = (long)xtensa_semihost_call_3(fd, (uintptr_t)buf, len, XTENSA_SEMIHOST_READ);
169+
170+
/* semihost_read assumes that all bytes were read if ret == 0.
171+
* If ret == len, it means EOF was reached.
172+
*/
173+
if (ret == len) {
174+
return 0;
175+
} else if (ret <= 0) {
176+
return len;
177+
} else {
178+
return ret;
179+
}
180+
}
181+
182+
long xtensa_semihost_read_char(long fd)
183+
{
184+
char c = 0;
185+
186+
xtensa_semihost_call_3(fd, (uintptr_t)&c, 1, XTENSA_SEMIHOST_READ);
187+
188+
return (long)c;
189+
}
190+
191+
long xtensa_semihost_seek(struct semihost_seek_args *args)
192+
{
193+
long ret;
194+
195+
ret = (long)xtensa_semihost_call_3(args->fd, args->offset, 0, XTENSA_SEMIHOST_LSEEK);
196+
197+
if (ret == args->offset) {
198+
return 0;
199+
}
200+
201+
return ret;
202+
}
203+
204+
long xtensa_semihost_flen(long fd)
205+
{
206+
uint8_t buf[64] = {0};
207+
long ret;
208+
209+
ret = (long)xtensa_semihost_call_2(fd, (uintptr_t)buf, XTENSA_SEMIHOST_FSTAT);
210+
if (ret < 0) {
211+
return -1;
212+
}
213+
214+
/* Struct stat is 64 bytes, bytes 28-35 correspond to st_size
215+
* field. 8-bytes cannot fit into long data type so return
216+
* only the lower 4 bytes.
217+
*/
218+
ret = *((long *)&buf[32]);
219+
220+
return sys_be32_to_cpu(ret);
221+
}
222+
223+
long semihost_exec(enum semihost_instr instr, void *args)
224+
{
225+
switch (instr) {
226+
case SEMIHOST_OPEN:
227+
return xtensa_semihost_open((struct semihost_open_args *)args);
228+
case SEMIHOST_CLOSE:
229+
return xtensa_semihost_close(((struct semihost_close_args *)args)->fd);
230+
case SEMIHOST_WRITEC:
231+
return xtensa_semihost_write(1, (char *)args, 1);
232+
case SEMIHOST_WRITE:
233+
return xtensa_semihost_write(((struct semihost_write_args *)args)->fd,
234+
((struct semihost_write_args *)args)->buf,
235+
((struct semihost_write_args *)args)->len);
236+
case SEMIHOST_READ:
237+
return xtensa_semihost_read(((struct semihost_read_args *)args)->fd,
238+
((struct semihost_read_args *)args)->buf,
239+
((struct semihost_read_args *)args)->len);
240+
case SEMIHOST_READC:
241+
return xtensa_semihost_read_char(((struct semihost_poll_in_args *)args)->zero);
242+
case SEMIHOST_SEEK:
243+
return xtensa_semihost_seek((struct semihost_seek_args *)args);
244+
case SEMIHOST_FLEN:
245+
return xtensa_semihost_flen(((struct semihost_flen_args *)args)->fd);
246+
default:
247+
return -1;
248+
}
249+
}

0 commit comments

Comments
 (0)