On 7/13/2026 1:07 PM, Matheus Tavares Bernardino wrote:
> The Hexagon semihosting ABI extends the arm-compatible set with
> operations like OPEN, WRITECREG, WRITE0, ISTTY, STAT, FSTAT, FTELL,
> SEEK, FTRUNC, ACCESS, and GETCWD. Implement these trap0 handlers so
> that baremetal programs using the standard Hexagon simulator ABI can
> perform file I/O when running on qemu-system-hexagon.
>
> Signed-off-by: Matheus Tavares Bernardino
> <[email protected]>
> Signed-off-by: Brian Cain <[email protected]>
> ---
> include/semihosting/common-semi.h | 1 +
> semihosting/arm-compat-semi.c | 2 +-
> target/hexagon/hexswi.c | 293 +++++++++++++++++++++++++++++-
> 3 files changed, 294 insertions(+), 2 deletions(-)
>
> diff --git a/include/semihosting/common-semi.h
> b/include/semihosting/common-semi.h
> index a11905ef4ef..1e7699d7bbd 100644
> --- a/include/semihosting/common-semi.h
> +++ b/include/semihosting/common-semi.h
> @@ -34,6 +34,7 @@
> #ifndef COMMON_SEMI_H
> #define COMMON_SEMI_H
>
> +void common_semi_cb(CPUState *cs, uint64_t ret, int err);
> void do_common_semihosting(CPUState *cs);
> uint64_t common_semi_arg(CPUState *cs, int argno);
> void common_semi_set_ret(CPUState *cs, uint64_t ret);
> diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c
> index c54753f696f..b930a028649 100644
> --- a/semihosting/arm-compat-semi.c
> +++ b/semihosting/arm-compat-semi.c
> @@ -229,7 +229,7 @@ static inline uint32_t get_swi_errno(CPUState *cs)
> #endif
> }
>
> -static void common_semi_cb(CPUState *cs, uint64_t ret, int err)
> +void common_semi_cb(CPUState *cs, uint64_t ret, int err)
> {
> if (err) {
> #ifdef CONFIG_USER_ONLY
> diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c
> index ce9c85ad6aa..2c60e4d9e9f 100644
> --- a/target/hexagon/hexswi.c
> +++ b/target/hexagon/hexswi.c
> @@ -119,6 +119,14 @@ static void do_preload(CPUHexagonState *env,
> target_ulong swi_info, bool load)
> hexagon_touch_memory(env, addr, count, retaddr);
> }
>
> +static void common_semi_ftell_cb(CPUState *cs, uint64_t ret, int err)
> +{
> + if (err) {
> + ret = -1;
> + }
> + common_semi_cb(cs, ret, err);
> +}
> +
> static void sim_handle_trap0(CPUHexagonState *env)
> {
> target_ulong what_swi, swi_info;
> @@ -174,7 +182,290 @@ static void sim_handle_trap0(CPUHexagonState *env)
> qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
> break;
>
> - /* TODO: implement other hexagon-specific semihosting calls */
> +#ifndef _WIN32
> + case HEX_SYS_OPEN:
> + {
> + char filename[BUFSIZ];
> + target_ulong physicalFilenameAddr;
style: physical_filename_addr;
> + unsigned int filemode;
> + int length;
> + int real_openmode;
> + int ret, err = 0;
> + int i = 0;
> + static const unsigned int mode_table[] = {
> + O_RDONLY,
> + O_RDONLY | O_BINARY,
> + O_RDWR,
> + O_RDWR | O_BINARY,
> + O_WRONLY | O_CREAT | O_TRUNC,
> + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
> + O_RDWR | O_CREAT | O_TRUNC,
> + O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
> + O_WRONLY | O_APPEND | O_CREAT,
> + O_WRONLY | O_APPEND | O_CREAT | O_BINARY,
> + O_RDWR | O_APPEND | O_CREAT,
> + O_RDWR | O_APPEND | O_CREAT | O_BINARY,
> + O_RDWR | O_CREAT,
> + O_RDWR | O_CREAT | O_EXCL
> + };
> +
Better to extract this with a static const, and a useful name.
angel_to_host_filemode_table.
> +
> + hexagon_read_memory(env, swi_info, 4, &physicalFilenameAddr,
> retaddr);
> + hexagon_read_memory(env, swi_info + 4, 4, &filemode, retaddr);
> + hexagon_read_memory(env, swi_info + 8, 4, &length, retaddr);
> +
> + if (length >= BUFSIZ) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: filename too large (%d)\n",
> + __func__, length);
> + common_semi_cb(cs, -1, ENAMETOOLONG);
> + break;
> + }
> +
> + do {
> + hexagon_read_memory(env, physicalFilenameAddr + i, 1,
> &filename[i],
> + retaddr);
> + i++;
> + } while (filename[i - 1]);
> +
> + /* convert ARM ANGEL filemode into host filemode */
> + if (filemode < 14) {
Also, compare to sizeof table introduced, or define a constant.
ANGEL_FILEMODE_MAX = 13.
> + real_openmode = mode_table[filemode];
> + } else {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: invalid OPEN mode: %u\n",
> + __func__, filemode);
> + common_semi_cb(cs, -1, EINVAL);
> + break;
> + }
> +
> + if (strcmp(filename, ":tt") == 0 &&
> + qemu_semihosting_console_has_chardev()) {
> + ret = alloc_guestfd();
> + console_guestfd(ret);
> + } else {
> + ret = open(filename, real_openmode | O_BINARY, 0644);
> +
> + if (ret == -1) {
> + err = errno;
> + } else {
> + int guestfd = alloc_guestfd();
> + associate_guestfd(guestfd, ret);
> + ret = guestfd;
> + }
> + }
> + common_semi_cb(cs, ret, err);
> + }
> + break;
> +
> + case HEX_SYS_WRITECREG:
> + {
> + char c = swi_info;
> + qemu_semihosting_console_write(&c, 1);
> + }
> + break;
> +
> + /*
> + * Hexagon's SYS_ISTTY is a bit different than arm's: we do not return -1
> + * on error, neither errno. So we override with out own implementation.
> + */
s/out/our
> + case HEX_SYS_ISTTY:
> + {
> + int fd;
> + hexagon_read_memory(env, swi_info, 4, &fd, retaddr);
> + common_semi_cb(cs, isatty(fd), 0);
> + }
> + break;
> +
> + case HEX_SYS_SEEK:
> + {
> + int fd;
> + target_ulong off;
> + hexagon_read_memory(env, swi_info, 4, &fd, retaddr);
> + hexagon_read_memory(env, swi_info + 4, 4, &off, retaddr);
> + semihost_sys_lseek(env_cpu(env), common_semi_ftell_cb, fd, off,
> + GDB_SEEK_SET);
> + }
> + break;
> +
> + case HEX_SYS_STAT:
> + case HEX_SYS_FSTAT:
> + {
> + /*
> + * This must match the caller's definition, it would be in the
> + * caller's angel.h or equivalent header.
> + */
It would be great to move this definition out of this function also.
> + struct __SYS_STAT {
> + uint64_t dev;
> + uint64_t ino;
> + uint32_t mode;
> + uint32_t nlink;
> + uint64_t rdev;
> + uint32_t size;
> + uint32_t __pad1;
> + uint32_t atime;
> + uint32_t mtime;
> + uint32_t ctime;
> + uint32_t __pad2;
> + } sys_stat;
> + struct stat st_buf;
> + uint8_t *st_bufptr = (uint8_t *)&sys_stat;
> + int rc, err = 0;
> + char filename[BUFSIZ];
> + target_ulong physicalFilenameAddr;
> + target_ulong statBufferAddr;
> + hexagon_read_memory(env, swi_info, 4, &physicalFilenameAddr,
> retaddr);
> +
> + if (what_swi == HEX_SYS_STAT) {
> + int i = 0;
> + do {
> + hexagon_read_memory(env, physicalFilenameAddr + i, 1,
> + &filename[i], retaddr);
> + i++;
> + } while ((i < BUFSIZ) && filename[i - 1]);
> + rc = stat(filename, &st_buf);
> + err = errno;
> + } else {
> + int fd = physicalFilenameAddr;
> + GuestFD *gf = get_guestfd(fd);
> + if (!gf || gf->type != GuestFDHost) {
> + qemu_log_mask(LOG_UNIMP,
> + "fstat semihosting only implemented"
> + " for native mode\n");
> + g_assert_not_reached();
> + }
> + rc = fstat(gf->hostfd, &st_buf);
> + err = errno;
> + }
> + if (rc == 0) {
> + sys_stat.dev = st_buf.st_dev;
> + sys_stat.ino = st_buf.st_ino;
> + sys_stat.mode = st_buf.st_mode;
> + sys_stat.nlink = (uint32_t) st_buf.st_nlink;
> + sys_stat.rdev = st_buf.st_rdev;
> + sys_stat.size = (uint32_t) st_buf.st_size;
> + sys_stat.atime = (uint32_t) st_buf.st_atim.tv_sec;
> + sys_stat.mtime = (uint32_t) st_buf.st_mtim.tv_sec;
> + sys_stat.ctime = (uint32_t) st_buf.st_ctim.tv_sec;
> + }
> + hexagon_read_memory(env, swi_info + 4, 4, &statBufferAddr, retaddr);
> +
> + for (int i = 0; i < sizeof(sys_stat); i++) {
> + hexagon_write_memory(env, statBufferAddr + i, 1, st_bufptr[i],
> + retaddr);
> + }
> + common_semi_cb(cs, rc, rc == 0 ? 0 : err);
> + }
> + break;
> +
> + case HEX_SYS_FTRUNC:
> + {
> + int fd;
> + off_t size_limit;
> + hexagon_read_memory(env, swi_info, 4, &fd, retaddr);
> + hexagon_read_memory(env, swi_info + 4, 8, &size_limit, retaddr);
> + semihost_sys_ftruncate(cs, common_semi_cb, fd, size_limit);
> + }
> + break;
> +
> + case HEX_SYS_ACCESS:
> + {
> + char filename[BUFSIZ];
> + uint32_t FileNameAddr;
> + uint32_t BufferMode;
> + int rc;
> +
> + int i = 0;
> +
> + hexagon_read_memory(env, swi_info, 4, &FileNameAddr, retaddr);
> + do {
> + hexagon_read_memory(env, FileNameAddr + i, 1, &filename[i],
> + retaddr);
> + i++;
> + } while ((i < BUFSIZ) && (filename[i - 1]));
> + filename[i] = 0;
> +
> + hexagon_read_memory(env, swi_info + 4, 4, &BufferMode, retaddr);
> +
> + rc = access(filename, BufferMode);
> + common_semi_cb(cs, rc, rc == 0 ? 0 : errno);
> + }
> + break;
> +
> + case HEX_SYS_GETCWD:
> + {
> + char cwdPtr[PATH_MAX];
> + uint32_t BufferAddr;
> + uint32_t BufferSize;
> + uint32_t rc = 0, err = 0;
> +
> + hexagon_read_memory(env, swi_info, 4, &BufferAddr, retaddr);
> + hexagon_read_memory(env, swi_info + 4, 4, &BufferSize, retaddr);
> +
> + if (!getcwd(cwdPtr, PATH_MAX)) {
> + err = errno;
> + } else {
> + size_t cwd_size = strlen(cwdPtr);
> + if (cwd_size > BufferSize) {
> + err = ERANGE;
> + } else {
> + for (int i = 0; i < cwd_size; i++) {
> + hexagon_write_memory(env, BufferAddr + i, 1,
> + (uint64_t)cwdPtr[i], retaddr);
> + }
> + rc = BufferAddr;
> + }
> + }
> + common_semi_cb(cs, rc, rc != 0 ? 0 : err);
> + break;
> + }
> +
> + case HEX_SYS_EXEC:
> + {
> + qemu_log_mask(LOG_UNIMP, "SYS_EXEC is deprecated\n");
> + common_semi_cb(cs, -1, ENOSYS);
> + }
> + break;
> +
> + case HEX_SYS_FTELL:
> + {
> + int fd;
> + hexagon_read_memory(env, swi_info, 4, &fd, retaddr);
> + semihost_sys_lseek(cs, common_semi_ftell_cb, fd, 0, GDB_SEEK_CUR);
> + }
> + break;
> +
> + case HEX_SYS_READ_CYCLES:
> + case HEX_SYS_READ_TCYCLES:
> + case HEX_SYS_READ_ICOUNT:
> + {
> + arch_set_thread_reg(env, HEX_REG_R00, 0);
> + arch_set_thread_reg(env, HEX_REG_R01, 0);
> + break;
> + }
> +
> + case HEX_SYS_READ_PCYCLES:
> + {
> + arch_set_thread_reg(env, HEX_REG_R00,
> + arch_get_system_reg(env, HEX_SREG_PCYCLELO));
> + arch_set_thread_reg(env, HEX_REG_R01,
> + arch_get_system_reg(env, HEX_SREG_PCYCLEHI));
> + break;
> + }
> +
> + case HEX_SYS_PROF_ON:
> + case HEX_SYS_PROF_OFF:
> + case HEX_SYS_PROF_STATSRESET:
> + case HEX_SYS_DUMP_PMU_STATS:
> + case HEX_SYS_HEAPINFO:
> + common_semi_cb(cs, -1, ENOSYS);
> + qemu_log_mask(LOG_UNIMP,
> + "SWI call %" PRIx32
> + " is unimplemented in QEMU\n",
> + (uint32_t)what_swi);
> + break;
> +
> +#endif /* ! _WIN32 */
>
> default:
> qemu_log_mask(LOG_GUEST_ERROR,