Adapt the arm semihosting support code for RISCV. This implementation
is based on the standard for RISC-V semihosting version 0.2 as
documented in
https://github.com/riscv/riscv-semihosting-spec/releases/tag/0.2
Signed-off-by: Keith Packard <keithp@keithp.com>
---
v2:
Update PC after exception is handled to follow
change in the ARM version for SYS_READC
v3:
Disallow semihosting in user mode; report a regular
breakpoint in that case.
v4:
Fix errors reported by checkpatch
v5:
Reference current RISC-V semihosting specification
v6:
Add support for semihosting in riscv64-linux-user and
riscv32-linux-user
v7:
Add meson build support
v8:
Fix errors reported by checkpatch that crept in.
v9:
Changes suggested by Alistair Francis <alistair23@gmail.com>:
Don't add me to the MAINTAINERS file.
Remove duplicate #include in target/riscv/cpu.h
Reference RISC-V semihosting spec in target/riscv/riscv-semi.c
v10:
Use common semihosting implementation instead of a separate copy.
Make sure addresses of the three breakpoint-signaling
instructions all lie within the same page. Change suggested by
Richard Henderson <richard.henderson@linaro.org>
v11:
Use CONFIG_ARM_COMPATIBLE_SEMIHOSTING
v12:
Fix bug in SYS_EXIT support on rv64
v13:
Add common_semi_rambase implementation. This locates the
memory region containing the stack and uses the base of that.
Fix SET_ARG and GET_ARG on rv64 targets to operate on 64-bit
values rather than 32-bit. Put_user_ual/get_user_ual are
confusingly defined by softmmu-semi.h as being equivalent to
put_user_u32/get_user_u32.
---
default-configs/devices/riscv32-softmmu.mak | 2 +
default-configs/devices/riscv64-softmmu.mak | 2 +
.../targets/riscv32-linux-user.mak | 1 +
.../targets/riscv64-linux-user.mak | 1 +
hw/semihosting/common-semi.c | 82 ++++++++++++++++++-
hw/semihosting/common-semi.h | 5 +-
linux-user/qemu.h | 4 +-
linux-user/semihost.c | 8 +-
qemu-options.hx | 10 ++-
target/riscv/cpu_bits.h | 1 +
target/riscv/cpu_helper.c | 10 +++
.../riscv/insn_trans/trans_privileged.c.inc | 37 ++++++++-
target/riscv/translate.c | 11 +++
13 files changed, 162 insertions(+), 12 deletions(-)
diff --git a/default-configs/devices/riscv32-softmmu.mak b/default-configs/devices/riscv32-softmmu.mak
index 94a236c9c2..d847bd5692 100644
--- a/default-configs/devices/riscv32-softmmu.mak
+++ b/default-configs/devices/riscv32-softmmu.mak
@@ -3,6 +3,8 @@
# Uncomment the following lines to disable these optional devices:
#
#CONFIG_PCI_DEVICES=n
+CONFIG_SEMIHOSTING=y
+CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
# Boards:
#
diff --git a/default-configs/devices/riscv64-softmmu.mak b/default-configs/devices/riscv64-softmmu.mak
index 76b6195648..d5eec75f05 100644
--- a/default-configs/devices/riscv64-softmmu.mak
+++ b/default-configs/devices/riscv64-softmmu.mak
@@ -3,6 +3,8 @@
# Uncomment the following lines to disable these optional devices:
#
#CONFIG_PCI_DEVICES=n
+CONFIG_SEMIHOSTING=y
+CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
# Boards:
#
diff --git a/default-configs/targets/riscv32-linux-user.mak b/default-configs/targets/riscv32-linux-user.mak
index dfb259e8aa..6a9d1b1bc1 100644
--- a/default-configs/targets/riscv32-linux-user.mak
+++ b/default-configs/targets/riscv32-linux-user.mak
@@ -2,3 +2,4 @@ TARGET_ARCH=riscv32
TARGET_BASE_ARCH=riscv
TARGET_ABI_DIR=riscv
TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-csr.xml gdb-xml/riscv-32bit-virtual.xml
+CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
diff --git a/default-configs/targets/riscv64-linux-user.mak b/default-configs/targets/riscv64-linux-user.mak
index b13895f3b0..0a92849a1b 100644
--- a/default-configs/targets/riscv64-linux-user.mak
+++ b/default-configs/targets/riscv64-linux-user.mak
@@ -2,3 +2,4 @@ TARGET_ARCH=riscv64
TARGET_BASE_ARCH=riscv
TARGET_ABI_DIR=riscv
TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-csr.xml gdb-xml/riscv-64bit-virtual.xml
+CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
diff --git a/hw/semihosting/common-semi.c b/hw/semihosting/common-semi.c
index ddfa448cc2..2b6a3fd9fd 100644
--- a/hw/semihosting/common-semi.c
+++ b/hw/semihosting/common-semi.c
@@ -1,6 +1,6 @@
/*
* Semihosting support for systems modeled on the Arm "Angel"
- * semihosting syscalls design.
+ * semihosting syscalls design. This includes Arm and RISC-V processors
*
* Copyright (c) 2005, 2007 CodeSourcery.
* Copyright (c) 2019 Linaro
@@ -25,6 +25,10 @@
* ARM Semihosting is documented in:
* Semihosting for AArch32 and AArch64 Release 2.0
* https://static.docs.arm.com/100863/0200/semihosting.pdf
+ *
+ * RISC-V Semihosting is documented in:
+ * RISC-V Semihosting
+ * https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc
*/
#include "qemu/osdep.h"
@@ -217,6 +221,42 @@ common_semi_rambase(CPUState *cs)
#endif /* TARGET_ARM */
+#ifdef TARGET_RISCV
+static inline target_ulong
+common_semi_arg(CPUState *cs, int argno)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ return env->gpr[xA0 + argno];
+}
+
+static inline void
+common_semi_set_ret(CPUState *cs, target_ulong ret)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ env->gpr[xA0] = ret;
+}
+
+static inline bool
+common_semi_sys_exit_extended(CPUState *cs, int nr)
+{
+ return (nr == TARGET_SYS_EXIT_EXTENDED || sizeof(target_ulong) == 8);
+}
+
+#ifndef CONFIG_USER_ONLY
+
+static inline target_ulong
+common_semi_rambase(CPUState *cs)
+{
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ return common_semi_find_region_base(env->gpr[xSP]);
+}
+#endif
+
+#endif
+
/*
* Allocate a new guest file descriptor and return it; if we
* couldn't allocate a new fd then return -1.
@@ -393,6 +433,12 @@ static target_ulong common_semi_flen_buf(CPUState *cs)
sp = env->regs[13];
}
#endif
+#ifdef TARGET_RISCV
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+
+ sp = env->gpr[xSP];
+#endif
return sp - 64;
}
@@ -736,6 +782,37 @@ static const GuestFDFunctions guestfd_fns[] = {
put_user_u32(val, args + (n) * 4))
#endif
+#ifdef TARGET_RISCV
+
+/*
+ * get_user_ual is defined as get_user_u32 in softmmu-semi.h,
+ * we need a macro that fetches a target_ulong
+ */
+#define get_user_utl(arg, p) \
+ ((sizeof(target_ulong) == 8) ? \
+ get_user_u64(arg, p) : \
+ get_user_u32(arg, p))
+
+/*
+ * put_user_ual is defined as put_user_u32 in softmmu-semi.h,
+ * we need a macro that stores a target_ulong
+ */
+#define put_user_utl(arg, p) \
+ ((sizeof(target_ulong) == 8) ? \
+ put_user_u64(arg, p) : \
+ put_user_u32(arg, p))
+
+#define GET_ARG(n) do { \
+ if (get_user_utl(arg ## n, args + (n) * sizeof(target_ulong))) { \
+ errno = EFAULT; \
+ return set_swi_errno(cs, -1); \
+ } \
+ } while (0)
+
+#define SET_ARG(n, val) \
+ put_user_utl(val, args + (n) * sizeof(target_ulong))
+#endif
+
/*
* Do a semihosting call.
*
@@ -1174,6 +1251,9 @@ target_ulong do_common_semihosting(CPUState *cs)
if (is_a64(cs->env_ptr)) {
return 0;
}
+#endif
+#ifdef TARGET_RISCV
+ return 0;
#endif
/* fall through -- invalid for A32/T32 */
default:
diff --git a/hw/semihosting/common-semi.h b/hw/semihosting/common-semi.h
index bc53e92c79..0bfab1c669 100644
--- a/hw/semihosting/common-semi.h
+++ b/hw/semihosting/common-semi.h
@@ -1,6 +1,6 @@
/*
* Semihosting support for systems modeled on the Arm "Angel"
- * semihosting syscalls design.
+ * semihosting syscalls design. This includes Arm and RISC-V processors
*
* Copyright (c) 2005, 2007 CodeSourcery.
* Copyright (c) 2019 Linaro
@@ -26,6 +26,9 @@
* Semihosting for AArch32 and AArch64 Release 2.0
* https://static.docs.arm.com/100863/0200/semihosting.pdf
*
+ * RISC-V Semihosting is documented in:
+ * RISC-V Semihosting
+ * https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc
*/
#ifndef COMMON_SEMI_H
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 534753ca12..17aa992165 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -109,6 +109,8 @@ typedef struct TaskState {
/* FPA state */
FPA11 fpa;
# endif
+#endif
+#if defined(TARGET_ARM) || defined(TARGET_RISCV)
int swi_errno;
#endif
#if defined(TARGET_I386) && !defined(TARGET_X86_64)
@@ -122,7 +124,7 @@ typedef struct TaskState {
#ifdef TARGET_M68K
abi_ulong tp_value;
#endif
-#if defined(TARGET_ARM) || defined(TARGET_M68K)
+#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_RISCV)
/* Extra fields for semihosted binaries. */
abi_ulong heap_base;
abi_ulong heap_limit;
diff --git a/linux-user/semihost.c b/linux-user/semihost.c
index a1f0f6050e..c0015ee7f6 100644
--- a/linux-user/semihost.c
+++ b/linux-user/semihost.c
@@ -1,11 +1,11 @@
/*
- * ARM Semihosting Console Support
+ * ARM Compatible Semihosting Console Support.
*
* Copyright (c) 2019 Linaro Ltd
*
- * Currently ARM is unique in having support for semihosting support
- * in linux-user. So for now we implement the common console API but
- * just for arm linux-user.
+ * Currently ARM and RISC-V are unique in having support for
+ * semihosting support in linux-user. So for now we implement the
+ * common console API but just for arm and risc-v linux-user.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
diff --git a/qemu-options.hx b/qemu-options.hx
index 104632ea34..22b4d704b6 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4186,10 +4186,10 @@ ERST
DEF("semihosting", 0, QEMU_OPTION_semihosting,
"-semihosting semihosting mode\n",
QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32 |
- QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2)
+ QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2 | QEMU_ARCH_RISCV)
SRST
``-semihosting``
- Enable semihosting mode (ARM, M68K, Xtensa, MIPS, Nios II only).
+ Enable semihosting mode (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V only).
Note that this allows guest direct access to the host filesystem, so
should only be used with a trusted guest OS.
@@ -4201,10 +4201,10 @@ DEF("semihosting-config", HAS_ARG, QEMU_OPTION_semihosting_config,
"-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,arg=str[,...]]\n" \
" semihosting configuration\n",
QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32 |
-QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2)
+QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2 | QEMU_ARCH_RISCV)
SRST
``-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,arg=str[,...]]``
- Enable and configure semihosting (ARM, M68K, Xtensa, MIPS, Nios II
+ Enable and configure semihosting (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V
only).
Note that this allows guest direct access to the host filesystem, so
@@ -4219,6 +4219,8 @@ SRST
open/read/write/seek/select. Tensilica baremetal libc for ISS and
linux platform "sim" use this interface.
+ On RISC-V this implements the standard semihosting API, version 0.2.
+
``target=native|gdb|auto``
Defines where the semihosting calls will be addressed, to QEMU
(``native``) or to GDB (``gdb``). The default is ``auto``, which
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
index 24b24c69c5..f0d3d1c07e 100644
--- a/target/riscv/cpu_bits.h
+++ b/target/riscv/cpu_bits.h
@@ -544,6 +544,7 @@
#define RISCV_EXCP_INST_PAGE_FAULT 0xc /* since: priv-1.10.0 */
#define RISCV_EXCP_LOAD_PAGE_FAULT 0xd /* since: priv-1.10.0 */
#define RISCV_EXCP_STORE_PAGE_FAULT 0xf /* since: priv-1.10.0 */
+#define RISCV_EXCP_SEMIHOST 0x10
#define RISCV_EXCP_INST_GUEST_PAGE_FAULT 0x14
#define RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT 0x15
#define RISCV_EXCP_VIRT_INSTRUCTION_FAULT 0x16
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index a2787b1d48..e730fd1fe9 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -24,6 +24,7 @@
#include "exec/exec-all.h"
#include "tcg/tcg-op.h"
#include "trace.h"
+#include "hw/semihosting/common-semi.h"
int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
{
@@ -844,6 +845,15 @@ void riscv_cpu_do_interrupt(CPUState *cs)
target_ulong htval = 0;
target_ulong mtval2 = 0;
+ if (cause == RISCV_EXCP_SEMIHOST) {
+ if (env->priv >= PRV_S) {
+ env->gpr[xA0] = do_common_semihosting(cs);
+ env->pc += 4;
+ return;
+ }
+ cause = RISCV_EXCP_BREAKPOINT;
+ }
+
if (!async) {
/* set tval to badaddr for traps with address information */
switch (cause) {
diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
index 2a61a853bf..32312be202 100644
--- a/target/riscv/insn_trans/trans_privileged.c.inc
+++ b/target/riscv/insn_trans/trans_privileged.c.inc
@@ -29,7 +29,42 @@ static bool trans_ecall(DisasContext *ctx, arg_ecall *a)
static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a)
{
- generate_exception(ctx, RISCV_EXCP_BREAKPOINT);
+ target_ulong ebreak_addr = ctx->base.pc_next;
+ target_ulong pre_addr = ebreak_addr - 4;
+ target_ulong post_addr = ebreak_addr + 4;
+ uint32_t pre = 0;
+ uint32_t ebreak = 0;
+ uint32_t post = 0;
+
+ /*
+ * The RISC-V semihosting spec specifies the following
+ * three-instruction sequence to flag a semihosting call:
+ *
+ * slli zero, zero, 0x1f 0x01f01013
+ * ebreak 0x00100073
+ * srai zero, zero, 0x7 0x40705013
+ *
+ * The two shift operations on the zero register are no-ops, used
+ * here to signify a semihosting exception, rather than a breakpoint.
+ *
+ * Uncompressed instructions are required so that the sequence is easy
+ * to validate.
+ *
+ * The three instructions are required to lie in the same page so
+ * that no exception will be raised when fetching them.
+ */
+
+ if ((pre_addr & TARGET_PAGE_MASK) == (post_addr & TARGET_PAGE_MASK)) {
+ pre = opcode_at(&ctx->base, pre_addr);
+ ebreak = opcode_at(&ctx->base, ebreak_addr);
+ post = opcode_at(&ctx->base, post_addr);
+ }
+
+ if (pre == 0x01f01013 && ebreak == 0x00100073 && post == 0x40705013) {
+ generate_exception(ctx, RISCV_EXCP_SEMIHOST);
+ } else {
+ generate_exception(ctx, RISCV_EXCP_BREAKPOINT);
+ }
exit_tb(ctx); /* no chaining */
ctx->base.is_jmp = DISAS_NORETURN;
return true;
diff --git a/target/riscv/translate.c b/target/riscv/translate.c
index 554d52a4be..0f28b5f41e 100644
--- a/target/riscv/translate.c
+++ b/target/riscv/translate.c
@@ -64,6 +64,7 @@ typedef struct DisasContext {
uint16_t vlen;
uint16_t mlen;
bool vl_eq_vlmax;
+ CPUState *cs;
} DisasContext;
#ifdef TARGET_RISCV64
@@ -747,6 +748,15 @@ static bool gen_shift(DisasContext *ctx, arg_r *a,
return true;
}
+static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc)
+{
+ DisasContext *ctx = container_of(dcbase, DisasContext, base);
+ CPUState *cpu = ctx->cs;
+ CPURISCVState *env = cpu->env_ptr;
+
+ return cpu_ldl_code(env, pc);
+}
+
/* Include insn module translation function */
#include "insn_trans/trans_rvi.c.inc"
#include "insn_trans/trans_rvm.c.inc"
@@ -814,6 +824,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
ctx->lmul = FIELD_EX32(tb_flags, TB_FLAGS, LMUL);
ctx->mlen = 1 << (ctx->sew + 3 - ctx->lmul);
ctx->vl_eq_vlmax = FIELD_EX32(tb_flags, TB_FLAGS, VL_EQ_VLMAX);
+ ctx->cs = cs;
}
static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu)
--
2.29.2
Hi Keith:
Thanks for the patch, I've verified with newlib semihosting support
which is contributed by Craig Blackmore from embecosm,
and I would like to add semihosting to user mode, do you mind add this
patch into this patch series?
On Thu, Nov 26, 2020 at 5:41 AM Keith Packard via <qemu-riscv@nongnu.org> wrote:
>
> Adapt the arm semihosting support code for RISCV. This implementation
> is based on the standard for RISC-V semihosting version 0.2 as
> documented in
>
> https://github.com/riscv/riscv-semihosting-spec/releases/tag/0.2
>
> Signed-off-by: Keith Packard <keithp@keithp.com>
>
> ---
>
> v2:
> Update PC after exception is handled to follow
> change in the ARM version for SYS_READC
>
> v3:
> Disallow semihosting in user mode; report a regular
> breakpoint in that case.
>
> v4:
> Fix errors reported by checkpatch
>
> v5:
> Reference current RISC-V semihosting specification
>
> v6:
> Add support for semihosting in riscv64-linux-user and
> riscv32-linux-user
>
> v7:
> Add meson build support
>
> v8:
> Fix errors reported by checkpatch that crept in.
>
> v9:
> Changes suggested by Alistair Francis <alistair23@gmail.com>:
> Don't add me to the MAINTAINERS file.
> Remove duplicate #include in target/riscv/cpu.h
> Reference RISC-V semihosting spec in target/riscv/riscv-semi.c
>
> v10:
> Use common semihosting implementation instead of a separate copy.
>
> Make sure addresses of the three breakpoint-signaling
> instructions all lie within the same page. Change suggested by
> Richard Henderson <richard.henderson@linaro.org>
>
> v11:
> Use CONFIG_ARM_COMPATIBLE_SEMIHOSTING
>
> v12:
> Fix bug in SYS_EXIT support on rv64
>
> v13:
> Add common_semi_rambase implementation. This locates the
> memory region containing the stack and uses the base of that.
>
> Fix SET_ARG and GET_ARG on rv64 targets to operate on 64-bit
> values rather than 32-bit. Put_user_ual/get_user_ual are
> confusingly defined by softmmu-semi.h as being equivalent to
> put_user_u32/get_user_u32.
> ---
> default-configs/devices/riscv32-softmmu.mak | 2 +
> default-configs/devices/riscv64-softmmu.mak | 2 +
> .../targets/riscv32-linux-user.mak | 1 +
> .../targets/riscv64-linux-user.mak | 1 +
> hw/semihosting/common-semi.c | 82 ++++++++++++++++++-
> hw/semihosting/common-semi.h | 5 +-
> linux-user/qemu.h | 4 +-
> linux-user/semihost.c | 8 +-
> qemu-options.hx | 10 ++-
> target/riscv/cpu_bits.h | 1 +
> target/riscv/cpu_helper.c | 10 +++
> .../riscv/insn_trans/trans_privileged.c.inc | 37 ++++++++-
> target/riscv/translate.c | 11 +++
> 13 files changed, 162 insertions(+), 12 deletions(-)
>
> diff --git a/default-configs/devices/riscv32-softmmu.mak b/default-configs/devices/riscv32-softmmu.mak
> index 94a236c9c2..d847bd5692 100644
> --- a/default-configs/devices/riscv32-softmmu.mak
> +++ b/default-configs/devices/riscv32-softmmu.mak
> @@ -3,6 +3,8 @@
> # Uncomment the following lines to disable these optional devices:
> #
> #CONFIG_PCI_DEVICES=n
> +CONFIG_SEMIHOSTING=y
> +CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>
> # Boards:
> #
> diff --git a/default-configs/devices/riscv64-softmmu.mak b/default-configs/devices/riscv64-softmmu.mak
> index 76b6195648..d5eec75f05 100644
> --- a/default-configs/devices/riscv64-softmmu.mak
> +++ b/default-configs/devices/riscv64-softmmu.mak
> @@ -3,6 +3,8 @@
> # Uncomment the following lines to disable these optional devices:
> #
> #CONFIG_PCI_DEVICES=n
> +CONFIG_SEMIHOSTING=y
> +CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
>
> # Boards:
> #
> diff --git a/default-configs/targets/riscv32-linux-user.mak b/default-configs/targets/riscv32-linux-user.mak
> index dfb259e8aa..6a9d1b1bc1 100644
> --- a/default-configs/targets/riscv32-linux-user.mak
> +++ b/default-configs/targets/riscv32-linux-user.mak
> @@ -2,3 +2,4 @@ TARGET_ARCH=riscv32
> TARGET_BASE_ARCH=riscv
> TARGET_ABI_DIR=riscv
> TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-csr.xml gdb-xml/riscv-32bit-virtual.xml
> +CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> diff --git a/default-configs/targets/riscv64-linux-user.mak b/default-configs/targets/riscv64-linux-user.mak
> index b13895f3b0..0a92849a1b 100644
> --- a/default-configs/targets/riscv64-linux-user.mak
> +++ b/default-configs/targets/riscv64-linux-user.mak
> @@ -2,3 +2,4 @@ TARGET_ARCH=riscv64
> TARGET_BASE_ARCH=riscv
> TARGET_ABI_DIR=riscv
> TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-csr.xml gdb-xml/riscv-64bit-virtual.xml
> +CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
> diff --git a/hw/semihosting/common-semi.c b/hw/semihosting/common-semi.c
> index ddfa448cc2..2b6a3fd9fd 100644
> --- a/hw/semihosting/common-semi.c
> +++ b/hw/semihosting/common-semi.c
> @@ -1,6 +1,6 @@
> /*
> * Semihosting support for systems modeled on the Arm "Angel"
> - * semihosting syscalls design.
> + * semihosting syscalls design. This includes Arm and RISC-V processors
> *
> * Copyright (c) 2005, 2007 CodeSourcery.
> * Copyright (c) 2019 Linaro
> @@ -25,6 +25,10 @@
> * ARM Semihosting is documented in:
> * Semihosting for AArch32 and AArch64 Release 2.0
> * https://static.docs.arm.com/100863/0200/semihosting.pdf
> + *
> + * RISC-V Semihosting is documented in:
> + * RISC-V Semihosting
> + * https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc
> */
>
> #include "qemu/osdep.h"
> @@ -217,6 +221,42 @@ common_semi_rambase(CPUState *cs)
>
> #endif /* TARGET_ARM */
>
> +#ifdef TARGET_RISCV
> +static inline target_ulong
> +common_semi_arg(CPUState *cs, int argno)
> +{
> + RISCVCPU *cpu = RISCV_CPU(cs);
> + CPURISCVState *env = &cpu->env;
> + return env->gpr[xA0 + argno];
> +}
> +
> +static inline void
> +common_semi_set_ret(CPUState *cs, target_ulong ret)
> +{
> + RISCVCPU *cpu = RISCV_CPU(cs);
> + CPURISCVState *env = &cpu->env;
> + env->gpr[xA0] = ret;
> +}
> +
> +static inline bool
> +common_semi_sys_exit_extended(CPUState *cs, int nr)
> +{
> + return (nr == TARGET_SYS_EXIT_EXTENDED || sizeof(target_ulong) == 8);
> +}
> +
> +#ifndef CONFIG_USER_ONLY
> +
> +static inline target_ulong
> +common_semi_rambase(CPUState *cs)
> +{
> + RISCVCPU *cpu = RISCV_CPU(cs);
> + CPURISCVState *env = &cpu->env;
> + return common_semi_find_region_base(env->gpr[xSP]);
> +}
> +#endif
> +
> +#endif
> +
> /*
> * Allocate a new guest file descriptor and return it; if we
> * couldn't allocate a new fd then return -1.
> @@ -393,6 +433,12 @@ static target_ulong common_semi_flen_buf(CPUState *cs)
> sp = env->regs[13];
> }
> #endif
> +#ifdef TARGET_RISCV
> + RISCVCPU *cpu = RISCV_CPU(cs);
> + CPURISCVState *env = &cpu->env;
> +
> + sp = env->gpr[xSP];
> +#endif
>
> return sp - 64;
> }
> @@ -736,6 +782,37 @@ static const GuestFDFunctions guestfd_fns[] = {
> put_user_u32(val, args + (n) * 4))
> #endif
>
> +#ifdef TARGET_RISCV
> +
> +/*
> + * get_user_ual is defined as get_user_u32 in softmmu-semi.h,
> + * we need a macro that fetches a target_ulong
> + */
> +#define get_user_utl(arg, p) \
> + ((sizeof(target_ulong) == 8) ? \
> + get_user_u64(arg, p) : \
> + get_user_u32(arg, p))
> +
> +/*
> + * put_user_ual is defined as put_user_u32 in softmmu-semi.h,
> + * we need a macro that stores a target_ulong
> + */
> +#define put_user_utl(arg, p) \
> + ((sizeof(target_ulong) == 8) ? \
> + put_user_u64(arg, p) : \
> + put_user_u32(arg, p))
> +
> +#define GET_ARG(n) do { \
> + if (get_user_utl(arg ## n, args + (n) * sizeof(target_ulong))) { \
> + errno = EFAULT; \
> + return set_swi_errno(cs, -1); \
> + } \
> + } while (0)
> +
> +#define SET_ARG(n, val) \
> + put_user_utl(val, args + (n) * sizeof(target_ulong))
> +#endif
> +
> /*
> * Do a semihosting call.
> *
> @@ -1174,6 +1251,9 @@ target_ulong do_common_semihosting(CPUState *cs)
> if (is_a64(cs->env_ptr)) {
> return 0;
> }
> +#endif
> +#ifdef TARGET_RISCV
> + return 0;
> #endif
> /* fall through -- invalid for A32/T32 */
> default:
> diff --git a/hw/semihosting/common-semi.h b/hw/semihosting/common-semi.h
> index bc53e92c79..0bfab1c669 100644
> --- a/hw/semihosting/common-semi.h
> +++ b/hw/semihosting/common-semi.h
> @@ -1,6 +1,6 @@
> /*
> * Semihosting support for systems modeled on the Arm "Angel"
> - * semihosting syscalls design.
> + * semihosting syscalls design. This includes Arm and RISC-V processors
> *
> * Copyright (c) 2005, 2007 CodeSourcery.
> * Copyright (c) 2019 Linaro
> @@ -26,6 +26,9 @@
> * Semihosting for AArch32 and AArch64 Release 2.0
> * https://static.docs.arm.com/100863/0200/semihosting.pdf
> *
> + * RISC-V Semihosting is documented in:
> + * RISC-V Semihosting
> + * https://github.com/riscv/riscv-semihosting-spec/blob/main/riscv-semihosting-spec.adoc
> */
>
> #ifndef COMMON_SEMI_H
> diff --git a/linux-user/qemu.h b/linux-user/qemu.h
> index 534753ca12..17aa992165 100644
> --- a/linux-user/qemu.h
> +++ b/linux-user/qemu.h
> @@ -109,6 +109,8 @@ typedef struct TaskState {
> /* FPA state */
> FPA11 fpa;
> # endif
> +#endif
> +#if defined(TARGET_ARM) || defined(TARGET_RISCV)
> int swi_errno;
> #endif
> #if defined(TARGET_I386) && !defined(TARGET_X86_64)
> @@ -122,7 +124,7 @@ typedef struct TaskState {
> #ifdef TARGET_M68K
> abi_ulong tp_value;
> #endif
> -#if defined(TARGET_ARM) || defined(TARGET_M68K)
> +#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_RISCV)
> /* Extra fields for semihosted binaries. */
> abi_ulong heap_base;
> abi_ulong heap_limit;
> diff --git a/linux-user/semihost.c b/linux-user/semihost.c
> index a1f0f6050e..c0015ee7f6 100644
> --- a/linux-user/semihost.c
> +++ b/linux-user/semihost.c
> @@ -1,11 +1,11 @@
> /*
> - * ARM Semihosting Console Support
> + * ARM Compatible Semihosting Console Support.
> *
> * Copyright (c) 2019 Linaro Ltd
> *
> - * Currently ARM is unique in having support for semihosting support
> - * in linux-user. So for now we implement the common console API but
> - * just for arm linux-user.
> + * Currently ARM and RISC-V are unique in having support for
> + * semihosting support in linux-user. So for now we implement the
> + * common console API but just for arm and risc-v linux-user.
> *
> * SPDX-License-Identifier: GPL-2.0-or-later
> */
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 104632ea34..22b4d704b6 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -4186,10 +4186,10 @@ ERST
> DEF("semihosting", 0, QEMU_OPTION_semihosting,
> "-semihosting semihosting mode\n",
> QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32 |
> - QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2)
> + QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2 | QEMU_ARCH_RISCV)
> SRST
> ``-semihosting``
> - Enable semihosting mode (ARM, M68K, Xtensa, MIPS, Nios II only).
> + Enable semihosting mode (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V only).
>
> Note that this allows guest direct access to the host filesystem, so
> should only be used with a trusted guest OS.
> @@ -4201,10 +4201,10 @@ DEF("semihosting-config", HAS_ARG, QEMU_OPTION_semihosting_config,
> "-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,arg=str[,...]]\n" \
> " semihosting configuration\n",
> QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA | QEMU_ARCH_LM32 |
> -QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2)
> +QEMU_ARCH_MIPS | QEMU_ARCH_NIOS2 | QEMU_ARCH_RISCV)
> SRST
> ``-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,arg=str[,...]]``
> - Enable and configure semihosting (ARM, M68K, Xtensa, MIPS, Nios II
> + Enable and configure semihosting (ARM, M68K, Xtensa, MIPS, Nios II, RISC-V
> only).
>
> Note that this allows guest direct access to the host filesystem, so
> @@ -4219,6 +4219,8 @@ SRST
> open/read/write/seek/select. Tensilica baremetal libc for ISS and
> linux platform "sim" use this interface.
>
> + On RISC-V this implements the standard semihosting API, version 0.2.
> +
> ``target=native|gdb|auto``
> Defines where the semihosting calls will be addressed, to QEMU
> (``native``) or to GDB (``gdb``). The default is ``auto``, which
> diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h
> index 24b24c69c5..f0d3d1c07e 100644
> --- a/target/riscv/cpu_bits.h
> +++ b/target/riscv/cpu_bits.h
> @@ -544,6 +544,7 @@
> #define RISCV_EXCP_INST_PAGE_FAULT 0xc /* since: priv-1.10.0 */
> #define RISCV_EXCP_LOAD_PAGE_FAULT 0xd /* since: priv-1.10.0 */
> #define RISCV_EXCP_STORE_PAGE_FAULT 0xf /* since: priv-1.10.0 */
> +#define RISCV_EXCP_SEMIHOST 0x10
> #define RISCV_EXCP_INST_GUEST_PAGE_FAULT 0x14
> #define RISCV_EXCP_LOAD_GUEST_ACCESS_FAULT 0x15
> #define RISCV_EXCP_VIRT_INSTRUCTION_FAULT 0x16
> diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
> index a2787b1d48..e730fd1fe9 100644
> --- a/target/riscv/cpu_helper.c
> +++ b/target/riscv/cpu_helper.c
> @@ -24,6 +24,7 @@
> #include "exec/exec-all.h"
> #include "tcg/tcg-op.h"
> #include "trace.h"
> +#include "hw/semihosting/common-semi.h"
>
> int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
> {
> @@ -844,6 +845,15 @@ void riscv_cpu_do_interrupt(CPUState *cs)
> target_ulong htval = 0;
> target_ulong mtval2 = 0;
>
> + if (cause == RISCV_EXCP_SEMIHOST) {
> + if (env->priv >= PRV_S) {
> + env->gpr[xA0] = do_common_semihosting(cs);
> + env->pc += 4;
> + return;
> + }
> + cause = RISCV_EXCP_BREAKPOINT;
> + }
> +
> if (!async) {
> /* set tval to badaddr for traps with address information */
> switch (cause) {
> diff --git a/target/riscv/insn_trans/trans_privileged.c.inc b/target/riscv/insn_trans/trans_privileged.c.inc
> index 2a61a853bf..32312be202 100644
> --- a/target/riscv/insn_trans/trans_privileged.c.inc
> +++ b/target/riscv/insn_trans/trans_privileged.c.inc
> @@ -29,7 +29,42 @@ static bool trans_ecall(DisasContext *ctx, arg_ecall *a)
>
> static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a)
> {
> - generate_exception(ctx, RISCV_EXCP_BREAKPOINT);
> + target_ulong ebreak_addr = ctx->base.pc_next;
> + target_ulong pre_addr = ebreak_addr - 4;
> + target_ulong post_addr = ebreak_addr + 4;
> + uint32_t pre = 0;
> + uint32_t ebreak = 0;
> + uint32_t post = 0;
> +
> + /*
> + * The RISC-V semihosting spec specifies the following
> + * three-instruction sequence to flag a semihosting call:
> + *
> + * slli zero, zero, 0x1f 0x01f01013
> + * ebreak 0x00100073
> + * srai zero, zero, 0x7 0x40705013
> + *
> + * The two shift operations on the zero register are no-ops, used
> + * here to signify a semihosting exception, rather than a breakpoint.
> + *
> + * Uncompressed instructions are required so that the sequence is easy
> + * to validate.
> + *
> + * The three instructions are required to lie in the same page so
> + * that no exception will be raised when fetching them.
> + */
> +
> + if ((pre_addr & TARGET_PAGE_MASK) == (post_addr & TARGET_PAGE_MASK)) {
> + pre = opcode_at(&ctx->base, pre_addr);
> + ebreak = opcode_at(&ctx->base, ebreak_addr);
> + post = opcode_at(&ctx->base, post_addr);
> + }
> +
> + if (pre == 0x01f01013 && ebreak == 0x00100073 && post == 0x40705013) {
> + generate_exception(ctx, RISCV_EXCP_SEMIHOST);
> + } else {
> + generate_exception(ctx, RISCV_EXCP_BREAKPOINT);
> + }
> exit_tb(ctx); /* no chaining */
> ctx->base.is_jmp = DISAS_NORETURN;
> return true;
> diff --git a/target/riscv/translate.c b/target/riscv/translate.c
> index 554d52a4be..0f28b5f41e 100644
> --- a/target/riscv/translate.c
> +++ b/target/riscv/translate.c
> @@ -64,6 +64,7 @@ typedef struct DisasContext {
> uint16_t vlen;
> uint16_t mlen;
> bool vl_eq_vlmax;
> + CPUState *cs;
> } DisasContext;
>
> #ifdef TARGET_RISCV64
> @@ -747,6 +748,15 @@ static bool gen_shift(DisasContext *ctx, arg_r *a,
> return true;
> }
>
> +static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc)
> +{
> + DisasContext *ctx = container_of(dcbase, DisasContext, base);
> + CPUState *cpu = ctx->cs;
> + CPURISCVState *env = cpu->env_ptr;
> +
> + return cpu_ldl_code(env, pc);
> +}
> +
> /* Include insn module translation function */
> #include "insn_trans/trans_rvi.c.inc"
> #include "insn_trans/trans_rvm.c.inc"
> @@ -814,6 +824,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
> ctx->lmul = FIELD_EX32(tb_flags, TB_FLAGS, LMUL);
> ctx->mlen = 1 << (ctx->sew + 3 - ctx->lmul);
> ctx->vl_eq_vlmax = FIELD_EX32(tb_flags, TB_FLAGS, VL_EQ_VLMAX);
> + ctx->cs = cs;
> }
>
> static void riscv_tr_tb_start(DisasContextBase *db, CPUState *cpu)
> --
> 2.29.2
>
>
Kito Cheng <kito.cheng@gmail.com> writes: > Hi Keith: > > Thanks for the patch, I've verified with newlib semihosting support > which is contributed by Craig Blackmore from embecosm, > and I would like to add semihosting to user mode, do you mind add this > patch into this patch series? I tried to add that already, but I admit that I haven't tested it in a while. You should find that there are patches to linux-user/semihost.c and linux-user/qemu.h to enable it. -- -keith
Hi Keith:
Thanks for your reply, but it seems like we need some more modification in
linux-user/riscv/cpu_loop.c to enable that, I guess I should post that in mail
rather than attachment :)
Patch here:
From 2f1eb5825a6dda177d3289106970eab05cb08445 Mon Sep 17 00:00:00 2001
From: Kito Cheng <kito.cheng@sifive.com>
Date: Wed, 9 Dec 2020 14:24:51 +0800
Subject: [PATCH] riscv: Add semihosting support for user mode
This could make testing easier and ARM/AArch64 has support on
their linux user mode too, so I think it should be reasonable.
Verified GCC testsuite with newlib/semihosting.
Signed-off-by: Kito Cheng <kito.cheng@sifive.com>
---
linux-user/riscv/cpu_loop.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c
index aa9e437875..9665dabb09 100644
--- a/linux-user/riscv/cpu_loop.c
+++ b/linux-user/riscv/cpu_loop.c
@@ -23,6 +23,7 @@
#include "qemu.h"
#include "cpu_loop-common.h"
#include "elf.h"
+#include "hw/semihosting/common-semi.h"
void cpu_loop(CPURISCVState *env)
{
@@ -91,6 +92,10 @@ void cpu_loop(CPURISCVState *env)
sigcode = TARGET_SEGV_MAPERR;
sigaddr = env->badaddr;
break;
+ case RISCV_EXCP_SEMIHOST:
+ env->gpr[xA0] = do_common_semihosting(cs);
+ env->pc += 4;
+ break;
case EXCP_DEBUG:
gdbstep:
signum = TARGET_SIGTRAP;
--
2.29.2
On Thu, Dec 10, 2020 at 12:30 AM Keith Packard via
<qemu-devel@nongnu.org> wrote:
>
> Kito Cheng <kito.cheng@gmail.com> writes:
>
> > Hi Keith:
> >
> > Thanks for the patch, I've verified with newlib semihosting support
> > which is contributed by Craig Blackmore from embecosm,
> > and I would like to add semihosting to user mode, do you mind add this
> > patch into this patch series?
>
> I tried to add that already, but I admit that I haven't tested it in a
> while. You should find that there are patches to linux-user/semihost.c
> and linux-user/qemu.h to enable it.
>
> --
> -keith
Kito Cheng <kito.cheng@gmail.com> writes:
> Hi Keith:
>
> Thanks for your reply, but it seems like we need some more modification in
> linux-user/riscv/cpu_loop.c to enable that, I guess I should post that in mail
> rather than attachment :)
>
> Patch here:
>
> From 2f1eb5825a6dda177d3289106970eab05cb08445 Mon Sep 17 00:00:00 2001
> From: Kito Cheng <kito.cheng@sifive.com>
> Date: Wed, 9 Dec 2020 14:24:51 +0800
> Subject: [PATCH] riscv: Add semihosting support for user mode
>
> This could make testing easier and ARM/AArch64 has support on
> their linux user mode too, so I think it should be reasonable.
>
> Verified GCC testsuite with newlib/semihosting.
>
> Signed-off-by: Kito Cheng <kito.cheng@sifive.com>
If the riscv linux-user maintainer is happy with it I'll take it through
semihosting/next. It certainly has a use case in the ARM case.
> ---
> linux-user/riscv/cpu_loop.c | 5 +++++
> 1 file changed, 5 insertions(+)
>
> diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c
> index aa9e437875..9665dabb09 100644
> --- a/linux-user/riscv/cpu_loop.c
> +++ b/linux-user/riscv/cpu_loop.c
> @@ -23,6 +23,7 @@
> #include "qemu.h"
> #include "cpu_loop-common.h"
> #include "elf.h"
> +#include "hw/semihosting/common-semi.h"
>
> void cpu_loop(CPURISCVState *env)
> {
> @@ -91,6 +92,10 @@ void cpu_loop(CPURISCVState *env)
> sigcode = TARGET_SEGV_MAPERR;
> sigaddr = env->badaddr;
> break;
> + case RISCV_EXCP_SEMIHOST:
> + env->gpr[xA0] = do_common_semihosting(cs);
> + env->pc += 4;
> + break;
> case EXCP_DEBUG:
> gdbstep:
> signum = TARGET_SIGTRAP;
--
Alex Bennée
Kito Cheng <kito.cheng@gmail.com> writes:
> Hi Keith:
>
> Thanks for your reply, but it seems like we need some more modification in
> linux-user/riscv/cpu_loop.c to enable that, I guess I should post that in mail
> rather than attachment :)
Ah, I completely missed the attachment! So sorry. That applies cleanly
on top of the rest of my series, so I think we can just leave it like
that unless someone wants it presented differently. Not sure why I
thought this was working before; I clearly missed the most important
bit.
Reviewed-by: Keith Packard <keithp@keithp.com>
I've got a github repo with these bits in case anyone wants to look at
the whole tree:
https://github.com/keith-packard/qemu/tree/semihosting
--
-keith
© 2016 - 2026 Red Hat, Inc.