* [PATCH v3 01/13] target/hexagon: fix improper assign of cause code to exception index
2026-07-20 17:40 [PATCH v3 00/13] hexagon: add semihosting support Matheus Tavares Bernardino
@ 2026-07-20 17:40 ` Matheus Tavares Bernardino
2026-07-20 17:40 ` [PATCH v3 02/13] target/hexagon: fix PC advancement for non-COF TB terminators Matheus Tavares Bernardino
` (11 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Matheus Tavares Bernardino @ 2026-07-20 17:40 UTC (permalink / raw)
To: qemu-devel; +Cc: brian.cain, pierrick.bouvier, marco.liebel, philmd, ale, anjo
hexagon_get_tb_cpu_state() detects a misaligned PC and raises an
exception, but it passed HEX_CAUSE_PC_NOT_ALIGNED (a cause code) as the
event argument to hexagon_raise_exception_err(), whose second parameter
is the event index (it ends up in cs->exception_index). This causes an
infinite reboot loop when we reache a misaligned PC.
Similarly, gen_exception_decode_fail() is called with a cause code
(HEX_CAUSE_INVALID_PACKET) but unconditionally routes it through
gen_exception(), which treats its argument as an event index and stores
it directly in cs->exception_index.
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com>
---
target/hexagon/cpu.c | 3 ++-
target/hexagon/translate.c | 4 ++++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index 12d27ce1381..e6b2ac1939d 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -320,7 +320,8 @@ static TCGTBCPUState hexagon_get_tb_cpu_state(CPUState *cs)
hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP, 1);
}
if (pc & PCALIGN_MASK) {
- hexagon_raise_exception_err(env, HEX_CAUSE_PC_NOT_ALIGNED, 0);
+ env->cause_code = HEX_CAUSE_PC_NOT_ALIGNED;
+ hexagon_raise_exception_err(env, HEX_EVENT_PRECISE, (uint32_t)pc);
}
#ifndef CONFIG_USER_ONLY
diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c
index 199b4f8c2ec..534b065caad 100644
--- a/target/hexagon/translate.c
+++ b/target/hexagon/translate.c
@@ -245,7 +245,11 @@ static void gen_exception_decode_fail(DisasContext *ctx, int nwords, int excp)
gen_exec_counters(ctx);
tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], fail_pc);
+#ifdef CONFIG_USER_ONLY
gen_exception(excp, fail_pc);
+#else
+ gen_precise_exception(excp, fail_pc);
+#endif
ctx->base.is_jmp = DISAS_NORETURN;
ctx->base.pc_next = fail_pc;
}
--
2.37.2
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 02/13] target/hexagon: fix PC advancement for non-COF TB terminators
2026-07-20 17:40 [PATCH v3 00/13] hexagon: add semihosting support Matheus Tavares Bernardino
2026-07-20 17:40 ` [PATCH v3 01/13] target/hexagon: fix improper assign of cause code to exception index Matheus Tavares Bernardino
@ 2026-07-20 17:40 ` Matheus Tavares Bernardino
2026-07-20 17:40 ` [PATCH v3 03/13] target/hexagon: add aux functions for guest mem load/store Matheus Tavares Bernardino
` (10 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Matheus Tavares Bernardino @ 2026-07-20 17:40 UTC (permalink / raw)
To: qemu-devel; +Cc: brian.cain, pierrick.bouvier, marco.liebel, philmd, ale, anjo
Some packets end a translation block without a change-of-flow. These do
not update the PC during commit, so gen_end_tb() leaves
hex_gpr[HEX_REG_PC] pointing at the same packet and the block gets
re-entered forever. Set PC explicitly to next_PC in that gen_end_tb()
branch to fix execution of next blocks.
Note that gen_start_packet() used to have a special case for
tlblock/k0lock, setting ctx->next_PC to the packet's own address. This
relies on the tlblock/k0lock helpers to properly advance the PC, which
is never done (only env->next_PC is updated, but that never reaches
hex_gpr[HEX_REG_PC]). Drop the special case so these packets advance to
the following packet like any other non-COF TB terminator.
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com>
---
target/hexagon/translate.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c
index 534b065caad..133d9f10ee8 100644
--- a/target/hexagon/translate.c
+++ b/target/hexagon/translate.c
@@ -217,6 +217,12 @@ static void gen_end_tb(DisasContext *ctx)
gen_goto_tb(ctx, 0, ctx->base.tb->pc, true);
gen_set_label(skip);
gen_goto_tb(ctx, 1, ctx->next_PC, false);
+ } else if (!ctx->pkt.pkt_has_cof) {
+ /*
+ * Packet with no deferred COF that still ends the TB. PC is
+ * not updated during commit, so set it explicitly to next_PC.
+ */
+ gen_goto_tb(ctx, 0, ctx->next_PC, true);
} else {
tcg_gen_lookup_and_goto_ptr();
}
@@ -293,6 +299,7 @@ static bool check_for_attrib(Packet *pkt, int attrib)
return false;
}
+#ifndef CONFIG_USER_ONLY
static bool check_for_opcode(Packet *pkt, uint16_t opcode)
{
for (int i = 0; i < pkt->num_insns; i++) {
@@ -302,6 +309,7 @@ static bool check_for_opcode(Packet *pkt, uint16_t opcode)
}
return false;
}
+#endif
static bool need_slot_cancelled(Packet *pkt)
{
@@ -581,10 +589,7 @@ static void analyze_packet(DisasContext *ctx)
static void gen_start_packet(DisasContext *ctx)
{
Packet *pkt = &ctx->pkt;
- target_ulong next_PC = (check_for_opcode(pkt, Y2_k0lock) ||
- check_for_opcode(pkt, Y2_tlblock)) ?
- ctx->base.pc_next :
- ctx->base.pc_next + pkt->encod_pkt_size_in_bytes;
+ target_ulong next_PC = ctx->base.pc_next + pkt->encod_pkt_size_in_bytes;
int i;
/* Clear out the disassembly context */
--
2.37.2
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 03/13] target/hexagon: add aux functions for guest mem load/store
2026-07-20 17:40 [PATCH v3 00/13] hexagon: add semihosting support Matheus Tavares Bernardino
2026-07-20 17:40 ` [PATCH v3 01/13] target/hexagon: fix improper assign of cause code to exception index Matheus Tavares Bernardino
2026-07-20 17:40 ` [PATCH v3 02/13] target/hexagon: fix PC advancement for non-COF TB terminators Matheus Tavares Bernardino
@ 2026-07-20 17:40 ` Matheus Tavares Bernardino
2026-07-20 17:41 ` [PATCH v3 04/13] hexagon: cpu_helper: add reg reading/writing helpers Matheus Tavares Bernardino
` (9 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Matheus Tavares Bernardino @ 2026-07-20 17:40 UTC (permalink / raw)
To: qemu-devel; +Cc: brian.cain, pierrick.bouvier, marco.liebel, philmd, ale, anjo
Will be used for semihosting.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com>
---
target/hexagon/cpu_helper.h | 6 ++
target/hexagon/cpu_helper.c | 133 ++++++++++++++++++++++++++++++++++++
2 files changed, 139 insertions(+)
diff --git a/target/hexagon/cpu_helper.h b/target/hexagon/cpu_helper.h
index d1767503156..ca2e13ab1d1 100644
--- a/target/hexagon/cpu_helper.h
+++ b/target/hexagon/cpu_helper.h
@@ -7,6 +7,12 @@
#ifndef HEXAGON_CPU_HELPER_H
#define HEXAGON_CPU_HELPER_H
+void hexagon_read_memory(CPUHexagonState *env, target_ulong vaddr, int size,
+ void *retptr, uintptr_t retaddr);
+void hexagon_write_memory(CPUHexagonState *env, target_ulong vaddr,
+ int size, uint64_t data, uintptr_t retaddr);
+void hexagon_peek_memory_range(CPUHexagonState *env, uint32_t start_addr,
+ uint32_t length, uintptr_t retaddr);
uint32_t hexagon_get_pmu_counter(CPUHexagonState *cur_env, int index);
void hexagon_modify_ssr(CPUHexagonState *env, uint32_t new, uint32_t old);
int get_cpu_mode(CPUHexagonState *env);
diff --git a/target/hexagon/cpu_helper.c b/target/hexagon/cpu_helper.c
index 64c5746c6d9..e981e11a35d 100644
--- a/target/hexagon/cpu_helper.c
+++ b/target/hexagon/cpu_helper.c
@@ -25,6 +25,139 @@
#include "sys_macros.h"
#include "arch.h"
+#ifndef CONFIG_USER_ONLY
+
+static bool hexagon_read_memory_small(CPUHexagonState *env, target_ulong addr,
+ int byte_count, unsigned char *dstbuf,
+ int mmu_idx, uintptr_t retaddr)
+ {
+ /* handle small sizes */
+ switch (byte_count) {
+ case 1:
+ *dstbuf = cpu_ldub_mmuidx_ra(env, addr, mmu_idx, retaddr);
+ return true;
+
+ case 2:
+ if (QEMU_IS_ALIGNED(addr, 2)) {
+ *(unsigned short *)dstbuf =
+ cpu_lduw_le_mmuidx_ra(env, addr, mmu_idx, retaddr);
+ return true;
+ }
+ break;
+
+ case 4:
+ if (QEMU_IS_ALIGNED(addr, 4)) {
+ *(uint32_t *)dstbuf =
+ cpu_ldl_le_mmuidx_ra(env, addr, mmu_idx, retaddr);
+ return true;
+ }
+ break;
+
+ case 8:
+ if (QEMU_IS_ALIGNED(addr, 8)) {
+ *(uint64_t *)dstbuf =
+ cpu_ldq_le_mmuidx_ra(env, addr, mmu_idx, retaddr);
+ return true;
+ }
+ break;
+
+ default:
+ /* larger request, handle elsewhere */
+ return false;
+ }
+
+ /* not aligned, copy bytes */
+ for (int i = 0; i < byte_count; ++i) {
+ *dstbuf++ = cpu_ldub_mmuidx_ra(env, addr++, mmu_idx, retaddr);
+ }
+ return true;
+}
+
+void hexagon_read_memory(CPUHexagonState *env, target_ulong vaddr, int size,
+ void *retptr, uintptr_t retaddr)
+{
+ BQL_LOCK_GUARD();
+ CPUState *cs = env_cpu(env);
+ unsigned mmu_idx = cpu_mmu_index(cs, false);
+ if (!hexagon_read_memory_small(env, vaddr, size, retptr, mmu_idx,
+ retaddr)) {
+ cpu_abort(cs, "%s: ERROR: bad size = %d!\n", __func__, size);
+ }
+}
+
+static bool hexagon_write_memory_small(CPUHexagonState *env, target_ulong addr,
+ int byte_count, unsigned char *srcbuf,
+ int mmu_idx, uintptr_t retaddr)
+{
+ /* handle small sizes */
+ switch (byte_count) {
+ case 1:
+ cpu_stb_mmuidx_ra(env, addr, *srcbuf, mmu_idx, retaddr);
+ return true;
+
+ case 2:
+ if (QEMU_IS_ALIGNED(addr, 2)) {
+ cpu_stw_le_mmuidx_ra(env, addr, *(uint16_t *)srcbuf, mmu_idx, retaddr);
+ return true;
+ }
+ break;
+
+ case 4:
+ if (QEMU_IS_ALIGNED(addr, 4)) {
+ cpu_stl_le_mmuidx_ra(env, addr, *(uint32_t *)srcbuf, mmu_idx, retaddr);
+ return true;
+ }
+ break;
+
+ case 8:
+ if (QEMU_IS_ALIGNED(addr, 8)) {
+ cpu_stq_le_mmuidx_ra(env, addr, *(uint64_t *)srcbuf, mmu_idx, retaddr);
+ return true;
+ }
+ break;
+
+ default:
+ /* larger request, handle elsewhere */
+ return false;
+ }
+
+ /* not aligned, copy bytes */
+ for (int i = 0; i < byte_count; ++i) {
+ cpu_stb_mmuidx_ra(env, addr++, *srcbuf++, mmu_idx, retaddr);
+ }
+
+ return true;
+}
+
+void hexagon_write_memory(CPUHexagonState *env, target_ulong vaddr,
+ int size, uint64_t data, uintptr_t retaddr)
+{
+ CPUState *cs = env_cpu(env);
+ unsigned mmu_idx = cpu_mmu_index(cs, false);
+ if (!hexagon_write_memory_small(env, vaddr, size, (unsigned char *)&data,
+ mmu_idx, retaddr)) {
+ cpu_abort(cs, "%s: ERROR: bad size = %d!\n", __func__, size);
+ }
+}
+
+static inline uint32_t page_start(uint32_t addr)
+{
+ uint32_t page_align = ~(TARGET_PAGE_SIZE - 1);
+ return addr & page_align;
+}
+
+void hexagon_peek_memory_range(CPUHexagonState *env, uint32_t start_addr,
+ uint32_t length, uintptr_t retaddr)
+{
+ unsigned int warm;
+ uint32_t first = page_start(start_addr);
+ uint32_t last = page_start(start_addr + length - 1);
+ for (uint32_t page = first; page <= last; page += TARGET_PAGE_SIZE) {
+ hexagon_read_memory(env, page, 1, &warm, retaddr);
+ }
+}
+
+#endif
uint32_t hexagon_get_pmu_counter(CPUHexagonState *cur_env, int index)
{
--
2.37.2
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 04/13] hexagon: cpu_helper: add reg reading/writing helpers
2026-07-20 17:40 [PATCH v3 00/13] hexagon: add semihosting support Matheus Tavares Bernardino
` (2 preceding siblings ...)
2026-07-20 17:40 ` [PATCH v3 03/13] target/hexagon: add aux functions for guest mem load/store Matheus Tavares Bernardino
@ 2026-07-20 17:41 ` Matheus Tavares Bernardino
2026-07-20 17:41 ` [PATCH v3 05/13] semihosting: add APIs for chardev-aware guest fd routing Matheus Tavares Bernardino
` (8 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Matheus Tavares Bernardino @ 2026-07-20 17:41 UTC (permalink / raw)
To: qemu-devel; +Cc: brian.cain, pierrick.bouvier, marco.liebel, philmd, ale, anjo
And adjust op_helper to use those. They will also be used on upcoming
semihosting commits.
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com>
---
target/hexagon/cpu_helper.h | 18 +++++++++++++++++
target/hexagon/cpu_helper.c | 40 +++++++++++++++++++++++++++++++++++++
target/hexagon/op_helper.c | 18 ++---------------
3 files changed, 60 insertions(+), 16 deletions(-)
diff --git a/target/hexagon/cpu_helper.h b/target/hexagon/cpu_helper.h
index ca2e13ab1d1..757a49fc4d4 100644
--- a/target/hexagon/cpu_helper.h
+++ b/target/hexagon/cpu_helper.h
@@ -7,6 +7,24 @@
#ifndef HEXAGON_CPU_HELPER_H
#define HEXAGON_CPU_HELPER_H
+static inline void arch_set_thread_reg(CPUHexagonState *env, uint32_t reg,
+ uint32_t val)
+{
+ g_assert(reg < TOTAL_PER_THREAD_REGS);
+ env->gpr[reg] = val;
+}
+
+static inline uint32_t arch_get_thread_reg(CPUHexagonState *env, uint32_t reg)
+{
+ g_assert(reg < TOTAL_PER_THREAD_REGS);
+ return env->gpr[reg];
+}
+
+void arch_set_system_reg(CPUHexagonState *env, uint32_t reg, uint32_t val);
+void arch_set_system_reg_masked(CPUHexagonState *env, uint32_t reg,
+ uint32_t val);
+uint32_t arch_get_system_reg(CPUHexagonState *env, uint32_t reg);
+
void hexagon_read_memory(CPUHexagonState *env, target_ulong vaddr, int size,
void *retptr, uintptr_t retaddr);
void hexagon_write_memory(CPUHexagonState *env, target_ulong vaddr,
diff --git a/target/hexagon/cpu_helper.c b/target/hexagon/cpu_helper.c
index e981e11a35d..98ce599571e 100644
--- a/target/hexagon/cpu_helper.c
+++ b/target/hexagon/cpu_helper.c
@@ -27,6 +27,46 @@
#ifndef CONFIG_USER_ONLY
+uint32_t arch_get_system_reg(CPUHexagonState *env, uint32_t reg)
+{
+ if (reg == HEX_SREG_PCYCLELO) {
+ return hexagon_get_sys_pcycle_count_low(env);
+ } else if (reg == HEX_SREG_PCYCLEHI) {
+ return hexagon_get_sys_pcycle_count_high(env);
+ }
+
+ g_assert(reg < NUM_SREGS);
+ if (reg < HEX_SREG_GLB_START) {
+ return env->t_sreg[reg];
+ } else {
+ HexagonCPU *cpu = env_archcpu(env);
+ return hexagon_globalreg_read(cpu->globalregs, reg, env->threadId);
+ }
+}
+
+void arch_set_system_reg(CPUHexagonState *env, uint32_t reg, uint32_t val)
+{
+ g_assert(reg < NUM_SREGS);
+ if (reg < HEX_SREG_GLB_START) {
+ env->t_sreg[reg] = val;
+ } else {
+ HexagonCPU *cpu = env_archcpu(env);
+ hexagon_globalreg_write(cpu->globalregs, reg, val, env->threadId);
+ }
+}
+
+void arch_set_system_reg_masked(CPUHexagonState *env, uint32_t reg,
+ uint32_t val)
+{
+ g_assert(reg < NUM_SREGS);
+ if (reg < HEX_SREG_GLB_START) {
+ env->t_sreg[reg] = val;
+ } else {
+ HexagonCPU *cpu = env_archcpu(env);
+ hexagon_globalreg_write_masked(cpu->globalregs, reg, val);
+ }
+}
+
static bool hexagon_read_memory_small(CPUHexagonState *env, target_ulong addr,
int byte_count, unsigned char *dstbuf,
int mmu_idx, uintptr_t retaddr)
diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
index 3ce223caba3..324a9632dd4 100644
--- a/target/hexagon/op_helper.c
+++ b/target/hexagon/op_helper.c
@@ -1846,28 +1846,14 @@ void HELPER(setimask)(CPUHexagonState *env, uint32_t tid, uint32_t imask)
void HELPER(sreg_write_masked)(CPUHexagonState *env, uint32_t reg, uint32_t val)
{
BQL_LOCK_GUARD();
- if (reg < HEX_SREG_GLB_START) {
- env->t_sreg[reg] = val;
- } else {
- HexagonCPU *cpu = env_archcpu(env);
- if (cpu->globalregs) {
- hexagon_globalreg_write_masked(cpu->globalregs, reg, val);
- }
- }
+ arch_set_system_reg_masked(env, reg, val);
}
static inline QEMU_ALWAYS_INLINE uint32_t sreg_read(CPUHexagonState *env,
uint32_t reg)
{
- HexagonCPU *cpu;
-
g_assert(bql_locked());
- if (reg < HEX_SREG_GLB_START) {
- return env->t_sreg[reg];
- }
- cpu = env_archcpu(env);
- return cpu->globalregs ?
- hexagon_globalreg_read(cpu->globalregs, reg, env->threadId) : 0;
+ return arch_get_system_reg(env, reg);
}
uint32_t HELPER(sreg_read)(CPUHexagonState *env, uint32_t reg)
--
2.37.2
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 05/13] semihosting: add APIs for chardev-aware guest fd routing
2026-07-20 17:40 [PATCH v3 00/13] hexagon: add semihosting support Matheus Tavares Bernardino
` (3 preceding siblings ...)
2026-07-20 17:41 ` [PATCH v3 04/13] hexagon: cpu_helper: add reg reading/writing helpers Matheus Tavares Bernardino
@ 2026-07-20 17:41 ` Matheus Tavares Bernardino
2026-07-20 17:41 ` [PATCH v3 06/13] semihosting: add callback to set error Matheus Tavares Bernardino
` (7 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Matheus Tavares Bernardino @ 2026-07-20 17:41 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, pierrick.bouvier, marco.liebel, philmd, ale, anjo,
Alex Bennée
From: Brian Cain <brian.cain@oss.qualcomm.com>
Add qemu_semihosting_console_has_chardev() to query whether the
semihosting console is backed by a chardev, and console_guestfd()
to initialize a guest file descriptor as GuestFDConsole.
These APIs enable callers to route semihosting I/O through a chardev
rather than directly to host stdio.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
---
include/semihosting/console.h | 9 +++++++++
include/semihosting/guestfd.h | 9 +++++++++
semihosting/console.c | 5 +++++
semihosting/guestfd.c | 8 ++++++++
4 files changed, 31 insertions(+)
diff --git a/include/semihosting/console.h b/include/semihosting/console.h
index 1c12e178ee3..d5f149e2088 100644
--- a/include/semihosting/console.h
+++ b/include/semihosting/console.h
@@ -54,4 +54,13 @@ void qemu_semihosting_console_block_until_ready(CPUState *cs);
*/
bool qemu_semihosting_console_ready(void);
+/**
+ * qemu_semihosting_console_has_chardev:
+ *
+ * Return true if the semihosting console is backed by a chardev.
+ * When true, console I/O goes through the chardev rather than
+ * host stdio.
+ */
+bool qemu_semihosting_console_has_chardev(void);
+
#endif /* SEMIHOST_CONSOLE_H */
diff --git a/include/semihosting/guestfd.h b/include/semihosting/guestfd.h
index a7ea1041ea0..76ca0fcf1a7 100644
--- a/include/semihosting/guestfd.h
+++ b/include/semihosting/guestfd.h
@@ -70,6 +70,15 @@ GuestFD *get_guestfd(int guestfd);
*/
void associate_guestfd(int guestfd, int hostfd);
+/**
+ * console_guestfd:
+ * @guestfd: GuestFD index
+ *
+ * Initialize the GuestFD for @guestfd to GuestFDConsole.
+ * I/O will be routed through the semihosting console chardev.
+ */
+void console_guestfd(int guestfd);
+
/**
* staticfile_guestfd:
* @guestfd: GuestFD index
diff --git a/semihosting/console.c b/semihosting/console.c
index 91e5d50d502..9d786833564 100644
--- a/semihosting/console.c
+++ b/semihosting/console.c
@@ -108,6 +108,11 @@ int qemu_semihosting_console_read(CPUState *cs, void *buf, int len)
return ret;
}
+bool qemu_semihosting_console_has_chardev(void)
+{
+ return console.chr != NULL;
+}
+
int qemu_semihosting_console_write(void *buf, int len)
{
if (console.chr) {
diff --git a/semihosting/guestfd.c b/semihosting/guestfd.c
index e8f236c690c..8b5770bab0d 100644
--- a/semihosting/guestfd.c
+++ b/semihosting/guestfd.c
@@ -117,6 +117,14 @@ void associate_guestfd(int guestfd, int hostfd)
gf->hostfd = hostfd;
}
+void console_guestfd(int guestfd)
+{
+ GuestFD *gf = do_get_guestfd(guestfd);
+
+ assert(gf);
+ gf->type = GuestFDConsole;
+}
+
void staticfile_guestfd(int guestfd, const uint8_t *data, size_t len)
{
GuestFD *gf = do_get_guestfd(guestfd);
--
2.37.2
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 06/13] semihosting: add callback to set error
2026-07-20 17:40 [PATCH v3 00/13] hexagon: add semihosting support Matheus Tavares Bernardino
` (4 preceding siblings ...)
2026-07-20 17:41 ` [PATCH v3 05/13] semihosting: add APIs for chardev-aware guest fd routing Matheus Tavares Bernardino
@ 2026-07-20 17:41 ` Matheus Tavares Bernardino
2026-07-20 17:41 ` [PATCH v3 07/13] target/hexagon: add semihosting support Matheus Tavares Bernardino
` (6 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Matheus Tavares Bernardino @ 2026-07-20 17:41 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, pierrick.bouvier, marco.liebel, philmd, ale, anjo,
Daniel Henrique Barboza, Alex Bennée, Peter Maydell,
Palmer Dabbelt, Alistair Francis, Weiwei Li, Liu Zhiwei, Chao Liu,
qemu-arm, qemu-riscv
This is currently unused by existing semihosting targets, but it will be
used by hexagon in the following commit.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Signed-off-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com>
---
include/semihosting/common-semi.h | 1 +
semihosting/arm-compat-semi.c | 1 +
target/arm/common-semi-target.c | 4 ++++
target/riscv/common-semi-target.c | 4 ++++
4 files changed, 10 insertions(+)
diff --git a/include/semihosting/common-semi.h b/include/semihosting/common-semi.h
index aa511a46f42..a11905ef4ef 100644
--- a/include/semihosting/common-semi.h
+++ b/include/semihosting/common-semi.h
@@ -37,6 +37,7 @@
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);
+void common_semi_set_err(CPUState *cs, int err);
bool is_64bit_semihosting(CPUArchState *env);
bool common_semi_sys_exit_is_extended(CPUState *cs);
uint64_t common_semi_stack_bottom(CPUState *cs);
diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c
index 5e5f181b908..c54753f696f 100644
--- a/semihosting/arm-compat-semi.c
+++ b/semihosting/arm-compat-semi.c
@@ -237,6 +237,7 @@ static void common_semi_cb(CPUState *cs, uint64_t ret, int err)
ts->swi_errno = err;
#else
syscall_err = err;
+ common_semi_set_err(cs, err);
#endif
}
common_semi_set_ret(cs, ret);
diff --git a/target/arm/common-semi-target.c b/target/arm/common-semi-target.c
index 2b77ce9c17b..38d52deb3d6 100644
--- a/target/arm/common-semi-target.c
+++ b/target/arm/common-semi-target.c
@@ -34,6 +34,10 @@ void common_semi_set_ret(CPUState *cs, uint64_t ret)
}
}
+void common_semi_set_err(CPUState *cs, int err)
+{
+}
+
bool common_semi_sys_exit_is_extended(CPUState *cs)
{
return is_a64(cpu_env(cs));
diff --git a/target/riscv/common-semi-target.c b/target/riscv/common-semi-target.c
index aeaeb88d536..38a00e160c3 100644
--- a/target/riscv/common-semi-target.c
+++ b/target/riscv/common-semi-target.c
@@ -26,6 +26,10 @@ void common_semi_set_ret(CPUState *cs, uint64_t ret)
env->gpr[xA0] = ret;
}
+void common_semi_set_err(CPUState *cs, int err)
+{
+}
+
bool is_64bit_semihosting(CPUArchState *env)
{
return riscv_cpu_mxl(env) != MXL_RV32;
--
2.37.2
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 07/13] target/hexagon: add semihosting support
2026-07-20 17:40 [PATCH v3 00/13] hexagon: add semihosting support Matheus Tavares Bernardino
` (5 preceding siblings ...)
2026-07-20 17:41 ` [PATCH v3 06/13] semihosting: add callback to set error Matheus Tavares Bernardino
@ 2026-07-20 17:41 ` Matheus Tavares Bernardino
2026-07-20 17:41 ` [PATCH v3 08/13] semihosting: add ftruncate helper (to be used for hexagon) Matheus Tavares Bernardino
` (5 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Matheus Tavares Bernardino @ 2026-07-20 17:41 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, pierrick.bouvier, marco.liebel, philmd, ale, anjo,
Paolo Bonzini
Baremetal Hexagon programs use trap0 #0 to invoke
semihosting calls for I/O and process control. Wire up the
arm-compatible semihosting framework for softmmu by enabling
CONFIG_ARM_COMPATIBLE_SEMIHOSTING and routing trap0 to the
semihosting handler.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com>
---
docs/system/target-hexagon.rst | 8 +-
configs/targets/hexagon-softmmu.mak | 2 +
hw/hexagon/hexagon_dsp.c | 2 +
target/hexagon/common-semi-target.c | 51 +++++++++
target/hexagon/hexswi.c | 167 +++++++++++++++++++++++++++-
hw/hexagon/Kconfig | 1 +
qemu-options.hx | 8 +-
target/hexagon/meson.build | 3 +
8 files changed, 232 insertions(+), 10 deletions(-)
create mode 100644 target/hexagon/common-semi-target.c
diff --git a/docs/system/target-hexagon.rst b/docs/system/target-hexagon.rst
index 416b8f7be76..a9f8c29810e 100644
--- a/docs/system/target-hexagon.rst
+++ b/docs/system/target-hexagon.rst
@@ -91,9 +91,11 @@ Semihosting
-----------
Hexagon supports a semihosting interface similar to other architectures'.
The ``trap0`` instruction can activate these semihosting calls so that the
-guest software can access the host console and filesystem. Semihosting
-is not yet implemented in QEMU hexagon.
-
+guest software can access the host console and filesystem. Read the
+`Hexagon Semihosting Specification
+<https://docs.qualcomm.com/doc/80-N2040-101_102648/topic/semihosting-specification.html>`__
+for details. Semihosting is enabled by default on hexagon-sim-compatible
+machines. This can be further configured through ``-semihosting-config``.
Hexagon Features
================
diff --git a/configs/targets/hexagon-softmmu.mak b/configs/targets/hexagon-softmmu.mak
index a77c100f0c5..6cbdc64be56 100644
--- a/configs/targets/hexagon-softmmu.mak
+++ b/configs/targets/hexagon-softmmu.mak
@@ -6,3 +6,5 @@ TARGET_LONG_BITS=32
TARGET_NOT_USING_LEGACY_LDST_PHYS_API=y
TARGET_NOT_USING_LEGACY_NATIVE_ENDIAN_API=y
TARGET_NEED_FDT=y
+CONFIG_SEMIHOSTING=y
+CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
diff --git a/hw/hexagon/hexagon_dsp.c b/hw/hexagon/hexagon_dsp.c
index aa493993229..315733cded3 100644
--- a/hw/hexagon/hexagon_dsp.c
+++ b/hw/hexagon/hexagon_dsp.c
@@ -27,6 +27,7 @@
#include "target/hexagon/internal.h"
#include "system/physmem.h"
#include "system/reset.h"
+#include "semihosting/semihost.h"
#include "machine_cfg_v66g_1024.h.inc"
@@ -178,6 +179,7 @@ static void init_mc(MachineClass *mc)
mc->no_serial = 1;
mc->is_default = false;
mc->max_cpus = 8;
+ qemu_semihosting_enable();
}
/* ----------------------------------------------------------------- */
diff --git a/target/hexagon/common-semi-target.c b/target/hexagon/common-semi-target.c
new file mode 100644
index 00000000000..9a7720514bb
--- /dev/null
+++ b/target/hexagon/common-semi-target.c
@@ -0,0 +1,51 @@
+/*
+ * Target-specific parts of semihosting/arm-compat-semi.c.
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "cpu_helper.h"
+#include "semihosting/common-semi.h"
+
+uint64_t common_semi_arg(CPUState *cs, int argno)
+{
+ CPUHexagonState *env = cpu_env(cs);
+ return arch_get_thread_reg(env, HEX_REG_R00 + argno);
+}
+
+void common_semi_set_ret(CPUState *cs, uint64_t ret)
+{
+ CPUHexagonState *env = cpu_env(cs);
+ arch_set_thread_reg(env, HEX_REG_R00, ret);
+}
+
+void common_semi_set_err(CPUState *cs, int err)
+{
+ CPUHexagonState *env = cpu_env(cs);
+ arch_set_thread_reg(env, HEX_REG_R01, err);
+}
+
+bool common_semi_sys_exit_is_extended(CPUState *cs)
+{
+ return false;
+}
+
+bool is_64bit_semihosting(CPUArchState *env)
+{
+ return false;
+}
+
+uint64_t common_semi_stack_bottom(CPUState *cs)
+{
+ CPUHexagonState *env = cpu_env(cs);
+ return arch_get_thread_reg(env, HEX_REG_SP);
+}
+
+bool common_semi_has_synccache(CPUArchState *env)
+{
+ return false;
+}
diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c
index 43c373ea2ee..2aedd8368e2 100644
--- a/target/hexagon/hexswi.c
+++ b/target/hexagon/hexswi.c
@@ -24,9 +24,171 @@
#error "This file is only used in system emulation"
#endif
+#include "semihosting/common-semi.h"
+#include "semihosting/console.h"
+#include "semihosting/syscalls.h"
+#include "semihosting/guestfd.h"
+#include "system/runstate.h"
+
+/* non-arm-compatible semihosting calls */
+#define HEXAGON_SPECIFIC_SWI_FLAGS \
+ DEF_SWI_FLAG(OPEN, 0x01) \
+ DEF_SWI_FLAG(ISTTY, 0x09) \
+ DEF_SWI_FLAG(HEAPINFO, 0x16) \
+ DEF_SWI_FLAG(EXCEPTION, 0x18) \
+ DEF_SWI_FLAG(SEEK, 0x0A) \
+ DEF_SWI_FLAG(READ_CYCLES, 0x40) \
+ DEF_SWI_FLAG(PROF_ON, 0x41) \
+ DEF_SWI_FLAG(PROF_OFF, 0x42) \
+ DEF_SWI_FLAG(WRITECREG, 0x43) \
+ DEF_SWI_FLAG(READ_TCYCLES, 0x44) \
+ DEF_SWI_FLAG(LOG_EVENT, 0x45) \
+ DEF_SWI_FLAG(REDRAW, 0x46) \
+ DEF_SWI_FLAG(READ_ICOUNT, 0x47) \
+ DEF_SWI_FLAG(PROF_STATSRESET, 0x48) \
+ DEF_SWI_FLAG(DUMP_PMU_STATS, 0x4a) \
+ DEF_SWI_FLAG(READ_PCYCLES, 0x52) \
+ DEF_SWI_FLAG(COREDUMP, 0xCD) \
+ DEF_SWI_FLAG(FTELL, 0x100) \
+ DEF_SWI_FLAG(FSTAT, 0x101) \
+ DEF_SWI_FLAG(STAT, 0x103) \
+ DEF_SWI_FLAG(GETCWD, 0x104) \
+ DEF_SWI_FLAG(ACCESS, 0x105) \
+ DEF_SWI_FLAG(OPENDIR, 0x180) \
+ DEF_SWI_FLAG(CLOSEDIR, 0x181) \
+ DEF_SWI_FLAG(READDIR, 0x182) \
+ DEF_SWI_FLAG(EXEC, 0x185) \
+ DEF_SWI_FLAG(FTRUNC, 0x186)
+
+/*
+ * We use the arm-compatible semihosting routines for these ones, but we do
+ * need some hexagon-specific preprocessing.
+ */
+#define HEX_SYS_WRITE 0x05
+#define HEX_SYS_READ 0x06
+#define HEX_SYS_READC 0x07
+
+#define DEF_SWI_FLAG(name, val) HEX_SYS_ ##name = val,
+enum hex_swi_flag {
+ HEXAGON_SPECIFIC_SWI_FLAGS
+};
+#undef DEF_SWI_FLAG
+
+#define DEF_SWI_FLAG(_, val) case val:
+static inline bool is_hexagon_specific_swi_flag(enum hex_swi_flag what_swi)
+{
+ switch (what_swi) {
+ HEXAGON_SPECIFIC_SWI_FLAGS
+ return true;
+ }
+ return false;
+}
+#undef DEF_SWI_FLAG
+
+static void init_semihosting_guestfds(void)
+{
+ static gsize initialized;
+
+ if (g_once_init_enter(&initialized)) {
+ if (qemu_semihosting_console_has_chardev()) {
+ alloc_guestfd();
+ console_guestfd(0);
+ alloc_guestfd();
+ console_guestfd(1);
+ alloc_guestfd();
+ console_guestfd(2);
+ } else {
+ alloc_guestfd();
+ associate_guestfd(0, 0);
+ alloc_guestfd();
+ associate_guestfd(1, 1);
+ alloc_guestfd();
+ associate_guestfd(2, 2);
+ }
+ g_once_init_leave(&initialized, 1);
+ }
+}
+
+static void do_preload(CPUHexagonState *env, target_ulong swi_info, bool load)
+{
+ uint32_t addr, count;
+ uintptr_t retaddr = 0;
+
+ hexagon_read_memory(env, swi_info + 4, 4, &addr, retaddr);
+ hexagon_read_memory(env, swi_info + 8, 4, &count, retaddr);
+ hexagon_peek_memory_range(env, addr, count, retaddr);
+}
+
+static void sim_handle_trap0(CPUHexagonState *env)
+{
+ target_ulong what_swi, swi_info;
+ CPUState *cs = env_cpu(env);
+
+ g_assert(bql_locked());
+ init_semihosting_guestfds();
+
+ what_swi = arch_get_thread_reg(env, HEX_REG_R00);
+ swi_info = arch_get_thread_reg(env, HEX_REG_R01);
+
+ qemu_log_mask(CPU_LOG_INT,
+ "sim_handle_trap0: swi=0x%" PRIx32
+ " info=0x%" PRIx32 " PC=0x%" PRIx32
+ " thread=%" PRId32 "\n",
+ (uint32_t)what_swi, (uint32_t)swi_info,
+ (uint32_t)arch_get_thread_reg(env, HEX_REG_PC),
+ (uint32_t)env->threadId);
+
+ if (!is_hexagon_specific_swi_flag(what_swi)) {
+ if (what_swi == HEX_SYS_READ || what_swi == HEX_SYS_READC ||
+ what_swi == HEX_SYS_WRITE) {
+ /*
+ * Avoid page faults if the buffer is not in memory yet.
+ * NOTE: Counterintuitive, but a WRITE must be able to LOAD from
+ * the input address. The contents of that buffer will be
+ * directed to the SWI interface.
+ */
+ do_preload(env, swi_info, (what_swi == HEX_SYS_WRITE));
+ }
+ /*
+ * ARM-compat semihosting SWI numbers are all <= 0x31.
+ * If R0 holds a value outside that range (e.g. guest code
+ * executing trap0(#0) with an arbitrary R0), treat it as an
+ * unrecognized request rather than forwarding to
+ * do_common_semihosting() which would abort.
+ */
+ if (what_swi > 0x31) {
+ qemu_log_mask(LOG_UNIMP,
+ "trap0(#0): unrecognized request in r0: "
+ "0x" TARGET_FMT_lx "\n", what_swi);
+ return;
+ }
+ do_common_semihosting(cs);
+ return;
+ }
+
+ switch (what_swi) {
+
+ case HEX_SYS_EXCEPTION:
+ {
+ uint32_t ret = arch_get_thread_reg(env, HEX_REG_R02);
+ arch_set_system_reg(env, HEX_SREG_MODECTL, 0);
+ gdb_exit(ret);
+ exit(ret);
+ }
+ break;
+
+ /* TODO: implement other hexagon-specific semihosting calls */
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "unknown swi request: 0x%" PRIx32 "\n",
+ (uint32_t)what_swi);
+ common_semi_cb(cs, -1, ENOSYS);
+ }
+}
+
static void set_addresses(CPUHexagonState *env, uint32_t pc_offset,
uint32_t exception_index)
-
{
HexagonCPU *cpu = env_archcpu(env);
uint32_t evb = cpu->globalregs ?
@@ -95,8 +257,7 @@ void hexagon_cpu_do_interrupt(CPUState *cs)
switch (cs->exception_index) {
case HEX_EVENT_TRAP0:
if (env->cause_code == 0) {
- qemu_log_mask(LOG_UNIMP,
- "trap0 is unhandled, no semihosting available\n");
+ sim_handle_trap0(env);
}
hexagon_ssr_set_cause(env, env->cause_code);
diff --git a/hw/hexagon/Kconfig b/hw/hexagon/Kconfig
index 52065ab3b22..3a8ff17812b 100644
--- a/hw/hexagon/Kconfig
+++ b/hw/hexagon/Kconfig
@@ -2,6 +2,7 @@ config HEX_DSP
bool
default y
depends on HEXAGON
+ select ARM_COMPATIBLE_SEMIHOSTING
config HEX_VIRT
bool
diff --git a/qemu-options.hx b/qemu-options.hx
index 34970fffc94..56f42b02c8a 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -5507,7 +5507,7 @@ ERST
DEF("semihosting", 0, QEMU_OPTION_semihosting,
"-semihosting semihosting mode\n",
QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA |
- QEMU_ARCH_MIPS | QEMU_ARCH_RISCV)
+ QEMU_ARCH_MIPS | QEMU_ARCH_RISCV | QEMU_ARCH_HEXAGON)
SRST
``-semihosting``
Enable :ref:`Semihosting` mode (ARM, M68K, Xtensa, MIPS, RISC-V only).
@@ -5523,11 +5523,11 @@ DEF("semihosting-config", HAS_ARG, QEMU_OPTION_semihosting_config,
"-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,userspace=on|off][,arg=str[,...]]\n" \
" semihosting configuration\n",
QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA |
-QEMU_ARCH_MIPS | QEMU_ARCH_RISCV)
+QEMU_ARCH_MIPS | QEMU_ARCH_RISCV | QEMU_ARCH_HEXAGON)
SRST
``-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,userspace=on|off][,arg=str[,...]]``
- Enable and configure :ref:`Semihosting` (ARM, M68K, Xtensa, MIPS, RISC-V
- only).
+ Enable and configure :ref:`Semihosting` (ARM, M68K, Xtensa, MIPS, RISC-V,
+ Hexagon only).
.. warning::
Note that this allows guest direct access to the host filesystem, so
diff --git a/target/hexagon/meson.build b/target/hexagon/meson.build
index 59cb09c1070..69f01bd2f70 100644
--- a/target/hexagon/meson.build
+++ b/target/hexagon/meson.build
@@ -262,6 +262,9 @@ hexagon_softmmu_ss.add(files(
'machine.c',
))
+hexagon_softmmu_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING',
+ if_true: files('common-semi-target.c'))
+
#
# Step 4.5
# We use flex/bison based idef-parser to generate TCG code for a lot
--
2.37.2
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 08/13] semihosting: add ftruncate helper (to be used for hexagon)
2026-07-20 17:40 [PATCH v3 00/13] hexagon: add semihosting support Matheus Tavares Bernardino
` (6 preceding siblings ...)
2026-07-20 17:41 ` [PATCH v3 07/13] target/hexagon: add semihosting support Matheus Tavares Bernardino
@ 2026-07-20 17:41 ` Matheus Tavares Bernardino
2026-07-20 17:41 ` [PATCH v3 09/13] target/hexagon: add main arch-specific semihosting operations Matheus Tavares Bernardino
` (4 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Matheus Tavares Bernardino @ 2026-07-20 17:41 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, pierrick.bouvier, marco.liebel, philmd, ale, anjo,
Alex Bennée
The common semihosting syscall layer has no ftruncate
helper, but the Hexagon semihosting ABI includes an FTRUNC
operation. Add semihost_sys_ftruncate() so that Hexagon
can delegate to it rather than calling ftruncate directly.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com>
---
include/semihosting/syscalls.h | 2 ++
semihosting/syscalls.c | 29 +++++++++++++++++++++++++++++
2 files changed, 31 insertions(+)
diff --git a/include/semihosting/syscalls.h b/include/semihosting/syscalls.h
index 03aa45b7bb9..3919ab64c7c 100644
--- a/include/semihosting/syscalls.h
+++ b/include/semihosting/syscalls.h
@@ -75,4 +75,6 @@ void semihost_sys_gettimeofday(CPUState *cs, gdb_syscall_complete_cb complete,
void semihost_sys_poll_one(CPUState *cs, gdb_syscall_complete_cb complete,
int fd, GIOCondition cond, int timeout);
+void semihost_sys_ftruncate(CPUState *cs, gdb_syscall_complete_cb complete,
+ int fd, off_t len);
#endif /* SEMIHOSTING_SYSCALLS_H */
diff --git a/semihosting/syscalls.c b/semihosting/syscalls.c
index 20f155f869a..0456db0befb 100644
--- a/semihosting/syscalls.c
+++ b/semihosting/syscalls.c
@@ -8,10 +8,12 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
+#include "qemu/error-report.h"
#include "gdbstub/syscalls.h"
#include "semihosting/guestfd.h"
#include "semihosting/syscalls.h"
#include "semihosting/console.h"
+#include "semihosting/semihost.h"
#ifdef CONFIG_USER_ONLY
#include "qemu.h"
#else
@@ -541,6 +543,13 @@ static void host_poll_one(CPUState *cs, gdb_syscall_complete_cb complete,
}
#endif
+static void host_ftruncate(CPUState *cs, gdb_syscall_complete_cb complete,
+ GuestFD *gf, off_t len)
+{
+ int err = ftruncate(gf->hostfd, len);
+ complete(cs, err, err < 0 ? errno : 0);
+}
+
/*
* Static file semihosting syscall implementations.
*/
@@ -982,3 +991,23 @@ void semihost_sys_poll_one(CPUState *cs, gdb_syscall_complete_cb complete,
}
}
#endif
+
+void semihost_sys_ftruncate(CPUState *cs, gdb_syscall_complete_cb complete,
+ int fd, off_t len)
+{
+ GuestFD *gf = get_guestfd(fd);
+ if (!gf) {
+ complete(cs, -1, EBADF);
+ return;
+ }
+
+ switch (gf->type) {
+ case GuestFDHost:
+ host_ftruncate(cs, complete, gf, len);
+ break;
+ default:
+ error_report("ftruncate call not implemented "
+ "for this semihosting mode.");
+ g_assert_not_reached();
+ }
+}
--
2.37.2
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 09/13] target/hexagon: add main arch-specific semihosting operations
2026-07-20 17:40 [PATCH v3 00/13] hexagon: add semihosting support Matheus Tavares Bernardino
` (7 preceding siblings ...)
2026-07-20 17:41 ` [PATCH v3 08/13] semihosting: add ftruncate helper (to be used for hexagon) Matheus Tavares Bernardino
@ 2026-07-20 17:41 ` Matheus Tavares Bernardino
2026-07-20 17:41 ` [PATCH v3 10/13] target/hexagon: add COREDUMP semihosting operation Matheus Tavares Bernardino
` (3 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Matheus Tavares Bernardino @ 2026-07-20 17:41 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, pierrick.bouvier, marco.liebel, philmd, ale, anjo,
Alex Bennée
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 <brian.cain@oss.qualcomm.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com>
---
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,
--
2.37.2
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 10/13] target/hexagon: add COREDUMP semihosting operation
2026-07-20 17:40 [PATCH v3 00/13] hexagon: add semihosting support Matheus Tavares Bernardino
` (8 preceding siblings ...)
2026-07-20 17:41 ` [PATCH v3 09/13] target/hexagon: add main arch-specific semihosting operations Matheus Tavares Bernardino
@ 2026-07-20 17:41 ` Matheus Tavares Bernardino
2026-07-20 17:41 ` [PATCH v3 11/13] target/hexagon: Add an errno mapping Matheus Tavares Bernardino
` (2 subsequent siblings)
12 siblings, 0 replies; 14+ messages in thread
From: Matheus Tavares Bernardino @ 2026-07-20 17:41 UTC (permalink / raw)
To: qemu-devel; +Cc: brian.cain, pierrick.bouvier, marco.liebel, philmd, ale, anjo
Baremetal Hexagon programs invoke SYS_COREDUMP (0xCD)
through semihosting to dump CPU state on a fatal exception.
Implement the handler to decode the SSR cause field and
print the full register file, matching hexagon-sim behavior.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com>
---
target/hexagon/internal.h | 1 +
target/hexagon/cpu.c | 2 +-
target/hexagon/hexswi.c | 143 ++++++++++++++++++++++++++++++++++++++
3 files changed, 145 insertions(+), 1 deletion(-)
diff --git a/target/hexagon/internal.h b/target/hexagon/internal.h
index 00b37aea7af..05d1129916e 100644
--- a/target/hexagon/internal.h
+++ b/target/hexagon/internal.h
@@ -28,6 +28,7 @@ int hexagon_hvx_gdb_write_register(CPUState *env, uint8_t *mem_buf, int n);
void hexagon_debug_vreg(CPUHexagonState *env, int regnum);
void hexagon_debug_qreg(CPUHexagonState *env, int regnum);
void hexagon_debug(CPUHexagonState *env);
+void hexagon_dump(CPUHexagonState *env, FILE *f, int flags);
extern const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS];
#ifndef CONFIG_USER_ONLY
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index e6b2ac1939d..06c378c5e02 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -232,7 +232,7 @@ void hexagon_debug_qreg(CPUHexagonState *env, int regnum)
print_qreg(stdout, env, regnum, false);
}
-static void hexagon_dump(CPUHexagonState *env, FILE *f, int flags)
+void hexagon_dump(CPUHexagonState *env, FILE *f, int flags)
{
HexagonCPU *cpu = env_archcpu(env);
diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c
index 6efc00fedf9..564a11e557a 100644
--- a/target/hexagon/hexswi.c
+++ b/target/hexagon/hexswi.c
@@ -162,6 +162,145 @@ static void common_semi_ftell_cb(CPUState *cs, uint64_t ret, int err)
common_semi_cb(cs, ret, err);
}
+static void coredump(CPUHexagonState *env)
+{
+ uint32_t ssr = arch_get_system_reg(env, HEX_SREG_SSR);
+ FILE *f = qemu_log_trylock();
+
+ if (!f) {
+ return;
+ }
+
+ fprintf(f, "CRASH!\n");
+ fprintf(f, "I think the exception was: ");
+ switch (GET_SSR_FIELD(SSR_CAUSE, ssr)) {
+ case 0x43:
+ fprintf(f, "0x43, NMI");
+ break;
+ case 0x42:
+ fprintf(f, "0x42, Data abort");
+ break;
+ case 0x44:
+ fprintf(f, "0x44, Multi TLB match");
+ break;
+ case HEX_CAUSE_BIU_PRECISE:
+ fprintf(f, "0x%x, Bus Error (Precise BIU error)",
+ HEX_CAUSE_BIU_PRECISE);
+ break;
+ case HEX_CAUSE_DOUBLE_EXCEPT:
+ fprintf(f, "0x%x, Exception observed when EX = 1"
+ " (double exception)",
+ HEX_CAUSE_DOUBLE_EXCEPT);
+ break;
+ case HEX_CAUSE_FETCH_NO_XPAGE:
+ fprintf(f, "0x%x, Privilege violation: User/Guest mode execute"
+ " to page with no execute permissions",
+ HEX_CAUSE_FETCH_NO_XPAGE);
+ break;
+ case HEX_CAUSE_FETCH_NO_UPAGE:
+ fprintf(f, "0x%x, Privilege violation: "
+ "User mode execute to page with no user permissions",
+ HEX_CAUSE_FETCH_NO_UPAGE);
+ break;
+ case HEX_CAUSE_INVALID_PACKET:
+ fprintf(f, "0x%x, Invalid packet",
+ HEX_CAUSE_INVALID_PACKET);
+ break;
+ case HEX_CAUSE_PRIV_USER_NO_GINSN:
+ fprintf(f, "0x%x, Privilege violation:"
+ " guest mode insn in user mode",
+ HEX_CAUSE_PRIV_USER_NO_GINSN);
+ break;
+ case HEX_CAUSE_PRIV_USER_NO_SINSN:
+ fprintf(f, "0x%x, Privilege violation: "
+ "monitor mode insn in user/guest mode",
+ HEX_CAUSE_PRIV_USER_NO_SINSN);
+ break;
+ case HEX_CAUSE_REG_WRITE_CONFLICT:
+ fprintf(f, "0x%x, Multiple writes to same register",
+ HEX_CAUSE_REG_WRITE_CONFLICT);
+ break;
+ case HEX_CAUSE_PC_NOT_ALIGNED:
+ fprintf(f, "0x%x, PC not aligned",
+ HEX_CAUSE_PC_NOT_ALIGNED);
+ break;
+ case HEX_CAUSE_MISALIGNED_LOAD:
+ fprintf(f, "0x%x, Misaligned Load @ 0x%" PRIx32,
+ HEX_CAUSE_MISALIGNED_LOAD,
+ arch_get_system_reg(env, HEX_SREG_BADVA));
+ break;
+ case HEX_CAUSE_MISALIGNED_STORE:
+ fprintf(f, "0x%x, Misaligned Store @ 0x%" PRIx32,
+ HEX_CAUSE_MISALIGNED_STORE,
+ arch_get_system_reg(env, HEX_SREG_BADVA));
+ break;
+ case HEX_CAUSE_PRIV_NO_READ:
+ fprintf(f, "0x%x, Privilege violation: "
+ "user/guest read permission @ 0x%" PRIx32,
+ HEX_CAUSE_PRIV_NO_READ,
+ arch_get_system_reg(env, HEX_SREG_BADVA));
+ break;
+ case HEX_CAUSE_PRIV_NO_WRITE:
+ fprintf(f, "0x%x, Privilege violation: "
+ "user/guest write permission @ 0x%" PRIx32,
+ HEX_CAUSE_PRIV_NO_WRITE,
+ arch_get_system_reg(env, HEX_SREG_BADVA));
+ break;
+ case HEX_CAUSE_PRIV_NO_UREAD:
+ fprintf(f, "0x%x, Privilege violation:"
+ " user read permission @ 0x%" PRIx32,
+ HEX_CAUSE_PRIV_NO_UREAD,
+ arch_get_system_reg(env, HEX_SREG_BADVA));
+ break;
+ case HEX_CAUSE_PRIV_NO_UWRITE:
+ fprintf(f, "0x%x, Privilege violation:"
+ " user write permission @ 0x%" PRIx32,
+ HEX_CAUSE_PRIV_NO_UWRITE,
+ arch_get_system_reg(env, HEX_SREG_BADVA));
+ break;
+ case HEX_CAUSE_COPROC_LDST:
+ fprintf(f, "0x%x, Coprocessor VMEM address error @ 0x%" PRIx32,
+ HEX_CAUSE_COPROC_LDST,
+ arch_get_system_reg(env, HEX_SREG_BADVA));
+ break;
+ case HEX_CAUSE_STACK_LIMIT:
+ fprintf(f, "0x%x, Stack limit check error",
+ HEX_CAUSE_STACK_LIMIT);
+ break;
+ case HEX_CAUSE_FPTRAP_CAUSE_BADFLOAT:
+ fprintf(f, "0x%x, Floating-Point: Execution of Floating-Point "
+ "instruction resulted in exception",
+ HEX_CAUSE_FPTRAP_CAUSE_BADFLOAT);
+ break;
+ case HEX_CAUSE_NO_COPROC_ENABLE:
+ fprintf(f, "0x%x, Illegal Execution of Coprocessor Instruction",
+ HEX_CAUSE_NO_COPROC_ENABLE);
+ break;
+ case HEX_CAUSE_NO_COPROC2_ENABLE:
+ fprintf(f, "0x%x, Illegal Execution of Secondary"
+ " Coprocessor Instruction",
+ HEX_CAUSE_NO_COPROC2_ENABLE);
+ break;
+ case HEX_CAUSE_UNSUPPORTED_HVX_64B:
+ fprintf(f, "0x%x, Unsupported Execution of"
+ " Coprocessor Instruction with 64bits Mode On",
+ HEX_CAUSE_UNSUPPORTED_HVX_64B);
+ break;
+ case HEX_CAUSE_VWCTRL_WINDOW_MISS:
+ fprintf(f, "0x%x, Thread accessing a region"
+ " outside VWCTRL window",
+ HEX_CAUSE_VWCTRL_WINDOW_MISS);
+ break;
+ default:
+ fprintf(f, "unknown cause 0x%" PRIx32,
+ GET_SSR_FIELD(SSR_CAUSE, ssr));
+ break;
+ }
+ fprintf(f, "\nRegister Dump:\n");
+ hexagon_dump(env, f, 0);
+ qemu_log_unlock(f);
+}
+
static void sim_handle_trap0(CPUHexagonState *env)
{
target_ulong what_swi, swi_info;
@@ -437,6 +576,10 @@ static void sim_handle_trap0(CPUHexagonState *env)
}
break;
+ case HEX_SYS_COREDUMP:
+ coredump(env);
+ break;
+
case HEX_SYS_FTELL:
{
int fd;
--
2.37.2
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 11/13] target/hexagon: Add an errno mapping
2026-07-20 17:40 [PATCH v3 00/13] hexagon: add semihosting support Matheus Tavares Bernardino
` (9 preceding siblings ...)
2026-07-20 17:41 ` [PATCH v3 10/13] target/hexagon: add COREDUMP semihosting operation Matheus Tavares Bernardino
@ 2026-07-20 17:41 ` Matheus Tavares Bernardino
2026-07-20 17:41 ` [PATCH v3 12/13] python/machine: support routing semihosting output to the test console Matheus Tavares Bernardino
2026-07-20 17:41 ` [PATCH v3 13/13] tests/functional: Add hexagon semihosting systests Matheus Tavares Bernardino
12 siblings, 0 replies; 14+ messages in thread
From: Matheus Tavares Bernardino @ 2026-07-20 17:41 UTC (permalink / raw)
To: qemu-devel; +Cc: brian.cain, pierrick.bouvier, marco.liebel, philmd, ale, anjo
From: Brian Cain <brian.cain@oss.qualcomm.com>
The Hexagon semihosting ABI defines its own errno values
which may differ from the host's. Translate host errno to
the Hexagon-specific values before returning results to the
guest so that baremetal programs see the expected error codes.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com>
---
target/hexagon/hexswi.c | 75 ++++++++++++++++++++++++++++++++++-------
1 file changed, 63 insertions(+), 12 deletions(-)
diff --git a/target/hexagon/hexswi.c b/target/hexagon/hexswi.c
index 564a11e557a..d93789e39c7 100644
--- a/target/hexagon/hexswi.c
+++ b/target/hexagon/hexswi.c
@@ -154,12 +154,63 @@ static void do_preload(CPUHexagonState *env, target_ulong swi_info, bool load)
hexagon_peek_memory_range(env, addr, count, retaddr);
}
+/* Hexagon semihosting errno values */
+#define HEX_EINVAL 22
+#define HEX_ERRNOS \
+ HEX_ERRNO(EPERM, 1) \
+ HEX_ERRNO(ENOENT, 2) \
+ HEX_ERRNO(EINTR, 4) \
+ HEX_ERRNO(EIO, 5) \
+ HEX_ERRNO(ENXIO, 6) \
+ HEX_ERRNO(EBADF, 9) \
+ HEX_ERRNO(EAGAIN, 11) \
+ HEX_ERRNO(ENOMEM, 12) \
+ HEX_ERRNO(EACCES, 13) \
+ HEX_ERRNO(EFAULT, 14) \
+ HEX_ERRNO(EBUSY, 16) \
+ HEX_ERRNO(EEXIST, 17) \
+ HEX_ERRNO(EXDEV, 18) \
+ HEX_ERRNO(ENODEV, 19) \
+ HEX_ERRNO(ENOTDIR, 20) \
+ HEX_ERRNO(EISDIR, 21) \
+ HEX_ERRNO(EINVAL, HEX_EINVAL) \
+ HEX_ERRNO(ENFILE, 23) \
+ HEX_ERRNO(EMFILE, 24) \
+ HEX_ERRNO(ENOTTY, 25) \
+ HEX_ERRNO(ETXTBSY, 26) \
+ HEX_ERRNO(EFBIG, 27) \
+ HEX_ERRNO(ENOSPC, 28) \
+ HEX_ERRNO(ESPIPE, 29) \
+ HEX_ERRNO(EROFS, 30) \
+ HEX_ERRNO(EMLINK, 31) \
+ HEX_ERRNO(EPIPE, 32) \
+ HEX_ERRNO(ERANGE, 34) \
+ HEX_ERRNO(ENAMETOOLONG, 36) \
+ HEX_ERRNO(ENOSYS, 38) \
+ HEX_ERRNO(ELOOP, 40) \
+ HEX_ERRNO(EOVERFLOW, 75)
+
+/* Map host errno to hexagon semihosting errno */
+static void semi_cb(CPUState *cs, uint64_t ret, int err)
+{
+#define HEX_ERRNO(NAME, CODE) case NAME: err = CODE; break;
+ switch (err) {
+ case 0:
+ break;
+ HEX_ERRNOS
+ default:
+ err = HEX_EINVAL;
+ break;
+ }
+ common_semi_cb(cs, ret, err);
+}
+
static void common_semi_ftell_cb(CPUState *cs, uint64_t ret, int err)
{
if (err) {
ret = -1;
}
- common_semi_cb(cs, ret, err);
+ semi_cb(cs, ret, err);
}
static void coredump(CPUHexagonState *env)
@@ -378,7 +429,7 @@ static void sim_handle_trap0(CPUHexagonState *env)
qemu_log_mask(LOG_GUEST_ERROR,
"%s: filename too large (%d)\n",
__func__, length);
- common_semi_cb(cs, -1, ENAMETOOLONG);
+ semi_cb(cs, -1, ENAMETOOLONG);
break;
}
@@ -395,7 +446,7 @@ static void sim_handle_trap0(CPUHexagonState *env)
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid OPEN mode: %u\n",
__func__, filemode);
- common_semi_cb(cs, -1, EINVAL);
+ semi_cb(cs, -1, EINVAL);
break;
}
@@ -414,7 +465,7 @@ static void sim_handle_trap0(CPUHexagonState *env)
ret = guestfd;
}
}
- common_semi_cb(cs, ret, err);
+ semi_cb(cs, ret, err);
}
break;
@@ -433,7 +484,7 @@ static void sim_handle_trap0(CPUHexagonState *env)
{
int fd;
hexagon_read_memory(env, swi_info, 4, &fd, retaddr);
- common_semi_cb(cs, isatty(fd), 0);
+ semi_cb(cs, isatty(fd), 0);
}
break;
@@ -503,7 +554,7 @@ static void sim_handle_trap0(CPUHexagonState *env)
hexagon_write_memory(env, statBufferAddr + i, 1, st_bufptr[i],
retaddr);
}
- common_semi_cb(cs, rc, rc == 0 ? 0 : err);
+ semi_cb(cs, rc, rc == 0 ? 0 : err);
}
break;
@@ -513,7 +564,7 @@ static void sim_handle_trap0(CPUHexagonState *env)
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);
+ semihost_sys_ftruncate(cs, semi_cb, fd, size_limit);
}
break;
@@ -537,7 +588,7 @@ static void sim_handle_trap0(CPUHexagonState *env)
hexagon_read_memory(env, swi_info + 4, 4, &BufferMode, retaddr);
rc = access(filename, BufferMode);
- common_semi_cb(cs, rc, rc == 0 ? 0 : errno);
+ semi_cb(cs, rc, rc == 0 ? 0 : errno);
}
break;
@@ -565,14 +616,14 @@ static void sim_handle_trap0(CPUHexagonState *env)
rc = BufferAddr;
}
}
- common_semi_cb(cs, rc, rc != 0 ? 0 : err);
+ 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);
+ semi_cb(cs, -1, ENOSYS);
}
break;
@@ -611,7 +662,7 @@ static void sim_handle_trap0(CPUHexagonState *env)
case HEX_SYS_PROF_STATSRESET:
case HEX_SYS_DUMP_PMU_STATS:
case HEX_SYS_HEAPINFO:
- common_semi_cb(cs, -1, ENOSYS);
+ semi_cb(cs, -1, ENOSYS);
qemu_log_mask(LOG_UNIMP,
"SWI call %" PRIx32
" is unimplemented in QEMU\n",
@@ -622,7 +673,7 @@ static void sim_handle_trap0(CPUHexagonState *env)
qemu_log_mask(LOG_GUEST_ERROR,
"unknown swi request: 0x%" PRIx32 "\n",
(uint32_t)what_swi);
- common_semi_cb(cs, -1, ENOSYS);
+ semi_cb(cs, -1, ENOSYS);
}
}
--
2.37.2
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 12/13] python/machine: support routing semihosting output to the test console
2026-07-20 17:40 [PATCH v3 00/13] hexagon: add semihosting support Matheus Tavares Bernardino
` (10 preceding siblings ...)
2026-07-20 17:41 ` [PATCH v3 11/13] target/hexagon: Add an errno mapping Matheus Tavares Bernardino
@ 2026-07-20 17:41 ` Matheus Tavares Bernardino
2026-07-20 17:41 ` [PATCH v3 13/13] tests/functional: Add hexagon semihosting systests Matheus Tavares Bernardino
12 siblings, 0 replies; 14+ messages in thread
From: Matheus Tavares Bernardino @ 2026-07-20 17:41 UTC (permalink / raw)
To: qemu-devel
Cc: brian.cain, pierrick.bouvier, marco.liebel, philmd, ale, anjo,
John Snow, Cleber Rosa
From: Brian Cain <brian.cain@oss.qualcomm.com>
The hexagon semihosting systests will wait for output on the semihosting
console.
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com>
---
python/qemu/machine/machine.py | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/python/qemu/machine/machine.py b/python/qemu/machine/machine.py
index bfb59d4c0bf..39375065558 100644
--- a/python/qemu/machine/machine.py
+++ b/python/qemu/machine/machine.py
@@ -186,6 +186,7 @@ def __init__(self,
self._console_index = 0
self._console_set = False
self._console_device_type: Optional[str] = None
+ self._console_semihosting = False
self._console_socket: Optional[socket.socket] = None
self._console_file: Optional[socket.SocketIO] = None
self._remove_files: List[str] = []
@@ -321,7 +322,10 @@ def _console_args(self, interactive: bool = False) -> List[str]:
fd = self._cons_sock_pair[0].fileno()
chardev = f"socket,id=console,fd={fd}"
args.extend(['-chardev', chardev])
- if self._console_device_type is None:
+ if self._console_semihosting:
+ args.extend(['-semihosting-config',
+ 'enable=on,chardev=console'])
+ elif self._console_device_type is None:
args.extend(['-serial', 'chardev:console'])
else:
device = '%s,chardev=console' % self._console_device_type
@@ -892,7 +896,8 @@ def set_machine(self, machine_type: str) -> None:
def set_console(self,
device_type: Optional[str] = None,
- console_index: int = 0) -> None:
+ console_index: int = 0,
+ semihosting: bool = False) -> None:
"""
Sets the device type for a console device
@@ -921,6 +926,7 @@ def set_console(self,
self._console_set = True
self._console_device_type = device_type
self._console_index = console_index
+ self._console_semihosting = semihosting
@property
def console_socket(self) -> socket.socket:
--
2.37.2
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH v3 13/13] tests/functional: Add hexagon semihosting systests
2026-07-20 17:40 [PATCH v3 00/13] hexagon: add semihosting support Matheus Tavares Bernardino
` (11 preceding siblings ...)
2026-07-20 17:41 ` [PATCH v3 12/13] python/machine: support routing semihosting output to the test console Matheus Tavares Bernardino
@ 2026-07-20 17:41 ` Matheus Tavares Bernardino
12 siblings, 0 replies; 14+ messages in thread
From: Matheus Tavares Bernardino @ 2026-07-20 17:41 UTC (permalink / raw)
To: qemu-devel; +Cc: brian.cain, pierrick.bouvier, marco.liebel, philmd, ale, anjo
From: Brian Cain <brian.cain@oss.qualcomm.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>
Signed-off-by: Matheus Tavares Bernardino <matheus.bernardino@oss.qualcomm.com>
---
tests/functional/hexagon/meson.build | 5 +
tests/functional/hexagon/test_systests.py | 110 ++++++++++++++++++++++
2 files changed, 115 insertions(+)
create mode 100755 tests/functional/hexagon/test_systests.py
diff --git a/tests/functional/hexagon/meson.build b/tests/functional/hexagon/meson.build
index 2b5a1a8f264..44053e64676 100644
--- a/tests/functional/hexagon/meson.build
+++ b/tests/functional/hexagon/meson.build
@@ -2,4 +2,9 @@
tests_hexagon_system_thorough = [
'arch_tests',
+ 'systests',
]
+
+test_hexagon_timeouts = {
+ 'systests': 180,
+}
diff --git a/tests/functional/hexagon/test_systests.py b/tests/functional/hexagon/test_systests.py
new file mode 100755
index 00000000000..8977aece43d
--- /dev/null
+++ b/tests/functional/hexagon/test_systests.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python3
+#
+# Copyright(c) Qualcomm Innovation Center, Inc. All Rights Reserved.
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import os
+import re
+import time
+import unittest
+
+from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern
+
+
+_TARBALL_BIN_PATH = (
+ "systests_standalone_package",
+ "StandaloneSysTests_6.4.0.2_v68",
+ "bin",
+)
+
+class SysTestsStandaloneTests(QemuSystemTest):
+ SYSTEST_TIMEOUT_SEC = 30
+
+ ASSET_TARBALL = Asset(
+ "https://github.com/qualcomm/qemu-hexagon-testing/releases/download/v0.2.11/systests_standalone.tar.gz",
+ "b5777aa65245de7710a7a08d717953c1362be7c8b60d9014c9fee8b17610ad1c",
+ )
+
+ def setUp(self):
+ super().setUp()
+ self.archive_extract(self.ASSET_TARBALL)
+
+ def binary(self, name):
+ """Return the full path to binary *name* inside bin dir."""
+ path = self.scratch_file(*_TARBALL_BIN_PATH, name)
+ self.assertTrue(os.path.exists(path))
+ return path
+
+ def run_exit_zero(self, binary_name, *extra_args, machine="V66G_1024"):
+ """Launch *binary_name* and assert it exits with code 0.
+
+ :param binary_name: name of the binary inside bin dir.
+ :param extra_args: optional pairs of (flag, value) strings passed
+ to set_vm_arg(), e.g. ('-append', 'myarg').
+ :param machine: QEMU machine type (default "V66G_1024").
+ """
+ self.set_machine(machine)
+ self.set_vm_arg("-display", "none")
+ self.set_vm_arg("-kernel", self.binary(binary_name))
+ for flag, value in zip(extra_args[::2], extra_args[1::2]):
+ self.set_vm_arg(flag, value)
+ self.vm.launch()
+ self.vm.wait(timeout=60.0)
+ self.assertEqual(self.vm.exitcode(), 0,
+ f"Test {binary_name} exited with "
+ f"code {self.vm.exitcode()}, expected 0")
+
+ def run_console_pattern(self, binary_name, pattern, *extra_args,
+ machine="V66G_1024"):
+ """Launch *binary_name* and wait for *pattern* on the semihosting console.
+
+ :param binary_name: name of the binary inside bin dir.
+ :param pattern: string pattern to wait for via wait_for_console_pattern.
+ :param extra_args: optional pairs of (flag, value) strings passed
+ to set_vm_arg(), e.g. ('-append', 'myarg').
+ :param machine: QEMU machine type (default "V66G_1024").
+ """
+ self.set_machine(machine)
+ self.set_vm_arg("-display", "none")
+ self.set_vm_arg("-kernel", self.binary(binary_name))
+ for flag, value in zip(extra_args[::2], extra_args[1::2]):
+ self.set_vm_arg(flag, value)
+ self.vm.set_console(semihosting=True)
+ self.vm.launch()
+ try:
+ wait_for_console_pattern(self, pattern)
+ finally:
+ self.vm.kill()
+
+ def test_fopen(self):
+ """fopen reads a file passed via --append and verifies its contents."""
+ import tempfile
+ # The fopen binary has a short cmdline buffer; use a short path.
+ dummy = os.path.join(tempfile.gettempdir(), "qemu_fopen_test.so")
+ with open(dummy, "w") as f:
+ f.write("valid\n")
+ self.run_exit_zero("fopen", "-append", dummy)
+
+ def test_ftrunc(self):
+ """ftrunc truncates _testfile_ftrunc from 6 bytes to 1 byte."""
+ ftrunc_path = self.scratch_file("_testfile_ftrunc")
+ with open(ftrunc_path, "w") as f:
+ f.write("valid\n")
+ # Sleep 1 s so mtime change is observable
+ time.sleep(1)
+ self.run_exit_zero("ftrunc", "-append", ftrunc_path)
+ self.assertEqual(os.path.getsize(ftrunc_path), 1,
+ "_testfile_ftrunc should be 1 byte after ftrunc")
+
+ def test_access(self):
+ """access checks R_OK|W_OK on _testfile_access."""
+ testfile = self.scratch_file("_testfile_access")
+ with open(testfile, "w") as f:
+ f.write("valid\n")
+ self.run_exit_zero("access", "-append", testfile)
+
+ def test_semihost(self):
+ self.run_console_pattern("semihost", "PASS", "-append", "arg1", "arg2")
+
+if __name__ == "__main__":
+ QemuSystemTest.main()
--
2.37.2
^ permalink raw reply related [flat|nested] 14+ messages in thread