Re: [PATCH v3 09/13] target/hexagon: add main arch-specific semihosting operations
Brian Cain <[email protected]>
| Newsgroups | gmane.comp.emulators.qemu |
|---|---|
| Message-ID | <[email protected]> |
On 7/20/2026 12:41 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: Brian Cain <[email protected]> > Reviewed-by: Pierrick Bouvier <[email protected]> > Signed-off-by: Matheus Tavares Bernardino <[email protected]> With the changes to omit arch_{sg}et_system_reg(): Reviewed-by: Brian Cain <[email protected]> > --- > include/semihosting/common-semi.h | 1 + > semihosting/arm-compat-semi.c | 2 +- > target/hexagon/hexswi.c | 298 +++++++++++++++++++++++++++++- > 3 files changed, 299 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 2aedd8368e2..6efc00fedf9 100644 > --- a/target/hexagon/hexswi.c > +++ b/target/hexagon/hexswi.c > @@ -85,6 +85,41 @@ static inline bool is_hexagon_specific_swi_flag(enum hex_swi_flag what_swi) > } > #undef DEF_SWI_FLAG > > +static const unsigned int angel_to_host_filemode_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 > +}; > + > +/* > + * This must match the caller's definition, it would be in the > + * caller's angel.h or equivalent header. > + */ > +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; > + > static void init_semihosting_guestfds(void) > { > static gsize initialized; > @@ -119,10 +154,19 @@ static void do_preload(CPUHexagonState *env, target_ulong swi_info, bool load) > hexagon_peek_memory_range(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; > CPUState *cs = env_cpu(env); > + uintptr_t retaddr = 0; > > g_assert(bql_locked()); > init_semihosting_guestfds(); > @@ -177,7 +221,259 @@ static void sim_handle_trap0(CPUHexagonState *env) > } > break; > > - /* TODO: implement other hexagon-specific semihosting calls */ > + case HEX_SYS_OPEN: > + { > + char filename[BUFSIZ]; > + target_ulong physical_filename_addr; > + unsigned int filemode; > + int length; > + int real_openmode; > + int ret, err = 0; > + int i = 0; > + > + hexagon_read_memory(env, swi_info, 4, &physical_filename_addr, 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, physical_filename_addr + i, 1, &filename[i], > + retaddr); > + i++; > + } while (filename[i - 1]); > + > + /* convert ARM ANGEL filemode into host filemode */ > + if (filemode < ARRAY_SIZE(angel_to_host_filemode_table)) { > + real_openmode = angel_to_host_filemode_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 our own implementation. > + */ > + 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: > + { > + struct stat st_buf; > + uint8_t *st_bufptr = (uint8_t *)&sys_stat; > + int rc, err = 0; > + char filename[BUFSIZ]; > + target_ulong physical_filename_addr; > + target_ulong statBufferAddr; > + hexagon_read_memory(env, swi_info, 4, &physical_filename_addr, retaddr); > + > + if (what_swi == HEX_SYS_STAT) { > + int i = 0; > + do { > + hexagon_read_memory(env, physical_filename_addr + i, 1, > + &filename[i], retaddr); > + i++; > + } while ((i < BUFSIZ) && filename[i - 1]); > + rc = stat(filename, &st_buf); > + err = errno; > + } else { > + int fd = physical_filename_addr; > + 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; > +#if defined(__linux__) > + 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; > +#elif defined(_WIN32) > + sys_stat.atime = st_buf.st_atime; > + sys_stat.mtime = st_buf.st_mtime; > + sys_stat.ctime = st_buf.st_ctime; > +#endif > + } > + 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; > > default: > qemu_log_mask(LOG_GUEST_ERROR,