diff --git a/disas/riscv.c b/disas/riscv.c index ba73dbaf70..94db0341a7 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -543,6 +543,10 @@ typedef enum { rv_op_cincoffsetimm, rv_op_csetboundsimm, + // Zero operand + rv_op_modesw_cap, + rv_op_modesw_int, + // Two operand rv_op_cgetperm, rv_op_cgettype, @@ -1303,7 +1307,9 @@ const rv_opcode_data opcode_data[] = { [rv_op_csc] = { "csc", rv_codec_s, rv_fmt_cs2_offset_cs1, NULL, 0, 0, 0 }, [rv_op_cincoffsetimm] = { "cincoffset", rv_codec_i, rv_fmt_cd_cs1_imm, NULL, 0, 0, 0 }, [rv_op_csetboundsimm] = { "csetbounds", rv_codec_i, rv_fmt_cd_cs1_imm, NULL, 0, 0, 0 }, - + // Zero operand + [rv_op_modesw_cap] = { "modesw.cap", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, + [rv_op_modesw_int] = { "modesw.int", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 }, // Two operand [rv_op_cgetperm] = { "cgetperm", rv_codec_r, rv_fmt_rd_cs1, NULL, 0, 0, 0 }, [rv_op_cgettype] = { "cgettype", rv_codec_r, rv_fmt_rd_cs1, NULL, 0, 0, 0 }, @@ -2003,6 +2009,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa, int flags) case 45: op = rv_op_minu; break; case 46: op = rv_op_max; break; case 47: op = rv_op_maxu; break; + case 73: op = rv_op_modesw_cap; break; + case 81: op = rv_op_modesw_int; break; case 130: op = rv_op_sh1add; break; case 132: op = rv_op_sh2add; break; case 134: op = rv_op_sh3add; break; diff --git a/target/cheri-common/cheri_defs.h b/target/cheri-common/cheri_defs.h index 5023acc9f3..260d912ce7 100644 --- a/target/cheri-common/cheri_defs.h +++ b/target/cheri-common/cheri_defs.h @@ -149,7 +149,8 @@ typedef enum CheriPermissions { } CheriPermissions; typedef enum CheriFlags { - CHERI_FLAG_CAPMODE = (1 << 0), + CHERI_FLAG_INTMODE = 0, + CHERI_FLAG_CAPMODE = 1, } CheriFlags; typedef enum CheriTbFlags { diff --git a/target/riscv/helper.h b/target/riscv/helper.h index fb1eaec710..0c7b2e294f 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -71,6 +71,7 @@ DEF_HELPER_3(lr_c_cap, void, env, i32, i32) DEF_HELPER_3(sc_c_modedep, tl, env, i32, i32) DEF_HELPER_3(sc_c_ddc, tl, env, i32, i32) DEF_HELPER_3(sc_c_cap, tl, env, i32, i32) +DEF_HELPER_2(modesw, void, env, int) #endif #ifdef CONFIG_TCG_LOG_INSTR diff --git a/target/riscv/insn32-cheri.decode b/target/riscv/insn32-cheri.decode index ffa8e43a93..6ec2e57b07 100644 --- a/target/riscv/insn32-cheri.decode +++ b/target/riscv/insn32-cheri.decode @@ -175,3 +175,8 @@ sc_w_cap 1111100 ..... ..... 000 11010 1011011 @atom_st_cap_or_ddc # 11101 is reserved # 11110 is reserved # 11111 is reserved + +### Zero operands + +modesw_cap 0001001 00000 00000 001 00000 0110011 +modesw_int 0001010 00000 00000 001 00000 0110011 diff --git a/target/riscv/insn_trans/trans_cheri.c.inc b/target/riscv/insn_trans/trans_cheri.c.inc index 7d4f8c02bf..a4b158256d 100644 --- a/target/riscv/insn_trans/trans_cheri.c.inc +++ b/target/riscv/insn_trans/trans_cheri.c.inc @@ -599,6 +599,63 @@ static inline bool trans_amoswap_c(DisasContext *ctx, arg_amoswap_c *a) return true; } +static inline bool do_trans_modesw(DisasContext *ctx, bool to_capmode) +{ + if (ctx->capmode == to_capmode) { + qemu_log_mask_and_addr(CPU_LOG_INSTR, ctx->base.pc_first, + "Redundant modesw at " TARGET_FMT_lx " (%s)", + ctx->base.pc_first, + lookup_symbol(ctx->base.pc_first)); + return true; + } + gen_helper_modesw(cpu_env, tcg_const_i32(to_capmode)); + + /* + * There's a number of RISC-V instructions whose behaviour depends on the + * mode. For some of them, the mode is checked at translation time (not at + * execution time). + * -> We have to process the mode update before any further translations. + * + * This comes down to ending the current translation block (tb). The tb is + * then cached and executed. After that, the status is updated by + * cheri_cpu_get_tb_cpu_state before the next tb is translated. + * + * We were wondering if the tb cache would cause issues in a scenario + * such as + * - tb with modesw is executed + * - mode update + * - next tb is to be translated - but it's already in the cache + * - cached tb is executed - but it was translated based on + * previous mode + * + * Note: The key for locating a cached tb in the hashtable includes the cpu + * status (part of which is the mode), so even if the next PC address has a + * cached TB, the lookup will not find the mismatched one. + * + * TODO: it should be possible to update the flag in DisasContext and + * continue translation after the modesw, but I am not confident this will + * be correct. Once we have some unit tests that we can run we should try + * to make this change since it will improve the performance of hybrid code. + * See Morello code which uses DISAS_UPDATE_EXIT. + */ + /* create tcg instruction to exit the tb */ + gen_update_cpu_pc(ctx->pc_succ_insn); + tcg_gen_exit_tb(NULL, 0); + /* This indicates to riscv_tr_tb_stop that no cleanup is needed. */ + ctx->base.is_jmp = DISAS_NORETURN; + return true; +} + +static inline bool trans_modesw_cap(DisasContext *ctx, arg_modesw_cap *a) +{ + return do_trans_modesw(ctx, /*to_capmode=*/true); +} + +static inline bool trans_modesw_int(DisasContext *ctx, arg_modesw_int *a) +{ + return do_trans_modesw(ctx, /*to_capmode=*/false); +} + // Explicit CAP/DDC atomic ops (no unsigned versions): // Reuses gen_lr_impl, defined in trans_rva.c.inc static inline bool gen_lr_impl(DisasContext *ctx, TCGv_cap_checked_ptr addr, diff --git a/target/riscv/op_helper_cheri.c b/target/riscv/op_helper_cheri.c index c00816793b..928070e90c 100644 --- a/target/riscv/op_helper_cheri.c +++ b/target/riscv/op_helper_cheri.c @@ -233,6 +233,15 @@ void HELPER(cjal)(CPUArchState *env, uint32_t cd, target_ulong target_addr, 0, GETPC()); } +void HELPER(modesw)(CPUArchState *env, int to_capmode) +{ + _Static_assert(CAP_FLAGS_ALL_BITS == 1, "Only one flag should exist"); + assert(cheri_in_capmode(env) != to_capmode && + "Should have skipped this call during translate"); + CAP_cc(update_flags)(&env->pcc, + to_capmode ? CHERI_FLAG_CAPMODE : CHERI_FLAG_INTMODE); +} + void HELPER(amoswap_cap)(CPUArchState *env, uint32_t dest_reg, uint32_t addr_reg, uint32_t val_reg) {