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,


Reply via email to