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: Matheus Tavares Bernardino <[email protected]> Signed-off-by: Brian Cain <[email protected]> Reviewed-by: Pierrick Bouvier <[email protected]> --- 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..36115137626 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 on qemu-system-hexagon machines by default, +and it can be 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
