diff options
author | Keith Packard <keithp@keithp.com> | 2021-01-08 22:42:52 +0000 |
---|---|---|
committer | Alex Bennée <alex.bennee@linaro.org> | 2021-01-18 10:05:06 +0000 |
commit | a10b9d93ecea0a8f01eb6de56274b1bcb101083b (patch) | |
tree | 24d8b952a691501fa22791881425539e33535d5a /target | |
parent | 095f8c029319b79cce487e3b566cd826b93da3e6 (diff) |
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>
Diffstat (limited to 'target')
-rw-r--r-- | target/riscv/cpu_bits.h | 1 | ||||
-rw-r--r-- | target/riscv/cpu_helper.c | 10 | ||||
-rw-r--r-- | target/riscv/insn_trans/trans_privileged.c.inc | 37 | ||||
-rw-r--r-- | target/riscv/translate.c | 11 |
4 files changed, 58 insertions, 1 deletions
diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index b41e8836c3..4196ef8b69 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -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 diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index a2afb95fa1..f8350f5f78 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) { @@ -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) { 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) |