Message ID | 20201125213617.2496935-6-keithp@keithp.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add RISC-V semihosting 0.2. Finish ARM semihosting 2.0 | expand |
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.
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;
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
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;
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)
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(-)