riscv: Add semihosting support

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>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-Id: <20210107170717.2098982-6-keithp@keithp.com>
Message-Id: <20210108224256.2321-17-alex.bennee@linaro.org>
This commit is contained in:
Keith Packard 2021-01-08 22:42:52 +00:00 committed by Alex Bennée
parent 095f8c0293
commit a10b9d93ec
13 changed files with 162 additions and 12 deletions

View File

@ -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:
#

View File

@ -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:
#

View File

@ -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

View File

@ -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

View File

@ -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"
@ -222,6 +226,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.
@ -398,6 +438,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;
}
@ -741,6 +787,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.
*
@ -1179,6 +1256,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:

View File

@ -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

View File

@ -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;

View File

@ -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
*/

View File

@ -4207,10 +4207,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.
@ -4222,10 +4222,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
@ -4240,6 +4240,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

View File

@ -542,6 +542,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

View File

@ -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)
{
@ -847,6 +848,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) {

View File

@ -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;

View File

@ -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)