From cbd96310ad656895a3d8661e5b12390015c5fa19 Mon Sep 17 00:00:00 2001 From: Zhibo Hong Date: Thu, 15 Oct 2026 21:43:35 +0800 Subject: [PATCH 01/29] target/riscv: Add support for the Zvabd extension The draft Zvabd extension version 0.9 provides vector integer absolute-difference operations for image, video, and other data-processing workloads. It introduces eight new instructions: - vabd.vv, vabd.vx, vabdu.vv, and vabdu.vx support SEW values from 8 through ELEN, with SEW 64 requiring Zve64x. - vwabda.vv, vwabda.vx, vwabdau.vv, and vwabdau.vx support SEW 8 and 16 and accumulate into a double-width destination. This patch implements the extension, including instruction decoding, TCG translation, and helper functions. This implementation targets version 0.9, whose instruction encodings differ from version 0.7. The specification is under review at: https://github.com/riscv/riscv-isa-manual/pull/3279 Reviewed-by: Daniel Henrique Barboza Reviewed-by: Joel Stanley Signed-off-by: Zhibo Hong Signed-off-by: Mingliang Liu Tested-by: Chao Liu Message-ID: <20261015134337.3354723-2-liumingliang.dev@bytedance.com> Signed-off-by: Alistair Francis --- disas/riscv-op.c.inc | 8 +++ disas/riscv.c | 8 +++ target/riscv/cpu.c | 1 + target/riscv/cpu_cfg_fields.h.inc | 1 + target/riscv/helper.h | 26 ++++++++++ target/riscv/insn32.decode | 10 ++++ target/riscv/tcg/insn_trans/trans_rvv.c.inc | 45 +++++++++++++++++ target/riscv/tcg/tcg-cpu.c | 6 +++ target/riscv/tcg/vector_helper.c | 56 +++++++++++++++++++++ 9 files changed, 161 insertions(+) diff --git a/disas/riscv-op.c.inc b/disas/riscv-op.c.inc index 1d334e4857..07b56f5d52 100644 --- a/disas/riscv-op.c.inc +++ b/disas/riscv-op.c.inc @@ -490,6 +490,10 @@ OP(vaadd_vv, "vaadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vaadd_vx, "vaadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) OP(vaaddu_vv, "vaaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vaaddu_vx, "vaaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vabd_vv, "vabd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vabd_vx, "vabd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vabdu_vv, "vabdu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vabdu_vx, "vabdu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) OP(vadc_vim, "vadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl) OP(vadc_vvm, "vadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) OP(vadc_vxm, "vadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) @@ -861,6 +865,10 @@ OP(vsuxei16_v, "vsuxei16.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) OP(vsuxei32_v, "vsuxei32.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) OP(vsuxei64_v, "vsuxei64.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) OP(vsuxei8_v, "vsuxei8.v", rv_codec_v_r, rv_fmt_ldst_vd_rs1_vs2_vm) +OP(vwabda_vv, "vwabda.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwabda_vx, "vwabda.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vwabdau_vv, "vwabdau.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) +OP(vwabdau_vx, "vwabdau.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) OP(vwadd_vv, "vwadd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vwadd_vx, "vwadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) OP(vwadd_wv, "vwadd.wv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) diff --git a/disas/riscv.c b/disas/riscv.c index d755172164..fd6e0bd613 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -2025,6 +2025,8 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) case 48: return &op_vwredsumu_vs; case 49: return &op_vwredsum_vs; case 53: return &op_vwsll_vv; + case 61: return &op_vwabda_vv; + case 62: return &op_vwabdau_vv; } break; case 1: @@ -2167,6 +2169,8 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) break; } break; + case 21: return &op_vabd_vv; + case 22: return &op_vabdu_vv; case 23: if ((inst >> 25) & 1) { return &op_vcompress_vm; @@ -2352,6 +2356,8 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) case 46: return &op_vnclipu_wx; case 47: return &op_vnclip_wx; case 53: return &op_vwsll_vx; + case 61: return &op_vwabda_vx; + case 62: return &op_vwabdau_vx; } break; case 5: @@ -2428,6 +2434,8 @@ static const rv_opcode_data *decode_inst_opcode(rv_decode *dec, rv_isa isa) } } break; + case 21: return &op_vabd_vx; + case 22: return &op_vabdu_vx; case 32: return &op_vdivu_vx; case 33: return &op_vdiv_vx; case 34: return &op_vremu_vx; diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index ae750f85c6..916038e8e4 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -222,6 +222,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zksh, PRIV_VERSION_1_12_0, ext_zksh), ISA_EXT_DATA_ENTRY(zkt, PRIV_VERSION_1_12_0, ext_zkt), ISA_EXT_DATA_ENTRY(ztso, PRIV_VERSION_1_12_0, ext_ztso), + ISA_EXPERIMENTAL_EXT_DATA_ENTRY(zvabd, PRIV_VERSION_1_13_0, ext_zvabd), ISA_EXT_DATA_ENTRY(zvbb, PRIV_VERSION_1_12_0, ext_zvbb), ISA_EXT_DATA_ENTRY(zvbc, PRIV_VERSION_1_12_0, ext_zvbc), ISA_EXT_DATA_ENTRY(zve32f, PRIV_VERSION_1_10_0, ext_zve32f), diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc index bcff8f55f1..85fc3ad8bb 100644 --- a/target/riscv/cpu_cfg_fields.h.inc +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -83,6 +83,7 @@ BOOL_FIELD(ext_zve32x) BOOL_FIELD(ext_zve64f) BOOL_FIELD(ext_zve64d) BOOL_FIELD(ext_zve64x) +BOOL_FIELD(ext_zvabd) BOOL_FIELD(ext_zvbb) BOOL_FIELD(ext_zvbc) BOOL_FIELD(ext_zvkb) diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 372f104972..1cffcad580 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -1353,6 +1353,32 @@ DEF_HELPER_5(vsm4k_vi, void, ptr, ptr, i32, env, i32) DEF_HELPER_4(vsm4r_vv, void, ptr, ptr, env, i32) DEF_HELPER_4(vsm4r_vs, void, ptr, ptr, env, i32) +/* Zvabd Extension */ +DEF_HELPER_6(vabd_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vabd_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vabd_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vabd_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vabd_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vabd_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vabd_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vabd_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vabdu_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vabdu_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vabdu_vv_w, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vabdu_vv_d, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vabdu_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vabdu_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vabdu_vx_w, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vabdu_vx_d, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwabda_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwabda_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwabda_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwabda_vx_h, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwabdau_vv_b, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwabdau_vv_h, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_6(vwabdau_vx_b, void, ptr, ptr, tl, ptr, env, i32) +DEF_HELPER_6(vwabdau_vx_h, void, ptr, ptr, tl, ptr, env, i32) + /* CFI (zicfiss) helpers */ #ifndef CONFIG_USER_ONLY DEF_HELPER_1(ssamoswap_disabled, void, env) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 46b00411ff..795badc8a7 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -1087,3 +1087,13 @@ sb_aqrl 00111 . . ..... ..... 000 ..... 0101111 @atom_st sh_aqrl 00111 . . ..... ..... 001 ..... 0101111 @atom_st sw_aqrl 00111 . . ..... ..... 010 ..... 0101111 @atom_st sd_aqrl 00111 . . ..... ..... 011 ..... 0101111 @atom_st + +# *** Zvabd Extension *** +vabd_vv 010101 . ..... ..... 010 ..... 1010111 @r_vm +vabd_vx 010101 . ..... ..... 110 ..... 1010111 @r_vm +vabdu_vv 010110 . ..... ..... 010 ..... 1010111 @r_vm +vabdu_vx 010110 . ..... ..... 110 ..... 1010111 @r_vm +vwabda_vv 111101 . ..... ..... 000 ..... 1010111 @r_vm +vwabda_vx 111101 . ..... ..... 100 ..... 1010111 @r_vm +vwabdau_vv 111110 . ..... ..... 000 ..... 1010111 @r_vm +vwabdau_vx 111110 . ..... ..... 100 ..... 1010111 @r_vm diff --git a/target/riscv/tcg/insn_trans/trans_rvv.c.inc b/target/riscv/tcg/insn_trans/trans_rvv.c.inc index a22e2cae6c..14ef961607 100644 --- a/target/riscv/tcg/insn_trans/trans_rvv.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvv.c.inc @@ -4091,3 +4091,48 @@ GEN_INT_EXT_TRANS(vzext_vf8, 3, 2) GEN_INT_EXT_TRANS(vsext_vf2, 1, 3) GEN_INT_EXT_TRANS(vsext_vf4, 2, 4) GEN_INT_EXT_TRANS(vsext_vf8, 3, 5) + +/* Zvabd Extension */ +#define GEN_ABD_CHECK(NAME, CHECK_FN, MAX_SEW) \ +static bool NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + return CHECK_FN(s, a) && s->cfg_ptr->ext_zvabd && \ + s->sew <= MAX_SEW && \ + (s->sew != MO_64 || s->cfg_ptr->ext_zve64x); \ +} +GEN_ABD_CHECK(vabd_vv_check, opivv_check, MO_64) +GEN_ABD_CHECK(vabd_vx_check, opivx_check, MO_64) +GEN_ABD_CHECK(vwabd_vv_check, opivv_overwrite_widen_check, MO_16) +GEN_ABD_CHECK(vwabd_vx_check, opivx_overwrite_widen_check, MO_16) + +GEN_OPIVV_TRANS(vabd_vv, vabd_vv_check) +GEN_OPIVX_TRANS(vabd_vx, vabd_vx_check) +GEN_OPIVV_TRANS(vabdu_vv, vabd_vv_check) +GEN_OPIVX_TRANS(vabdu_vx, vabd_vx_check) + +#define GEN_ABD_OPIVV_WIDEN_TRANS(NAME, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + static gen_helper_gvec_4_ptr * const fns[2] = { \ + gen_helper_##NAME##_b, gen_helper_##NAME##_h, \ + }; \ + return s->sew <= MO_16 && do_opivv_widen(s, a, fns[s->sew], CHECK); \ +} + +#define GEN_ABD_OPIVX_WIDEN_TRANS(NAME, CHECK) \ +static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ +{ \ + if (CHECK(s, a)) { \ + static gen_helper_opivx * const fns[2] = { \ + gen_helper_##NAME##_b, gen_helper_##NAME##_h, \ + }; \ + return opivx_trans(a->rd, a->rs1, a->rs2, a->vm, \ + fns[s->sew], s); \ + } \ + return false; \ +} + +GEN_ABD_OPIVV_WIDEN_TRANS(vwabda_vv, vwabd_vv_check) +GEN_ABD_OPIVX_WIDEN_TRANS(vwabda_vx, vwabd_vx_check) +GEN_ABD_OPIVV_WIDEN_TRANS(vwabdau_vv, vwabd_vv_check) +GEN_ABD_OPIVX_WIDEN_TRANS(vwabdau_vx, vwabd_vx_check) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 80c0463617..1369b7c4de 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -760,6 +760,12 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } + if ((cpu->cfg.ext_zvabd) && !cpu->cfg.ext_zve32x) { + error_setg(errp, + "Zvabd extensions require V or Zve* extensions"); + return; + } + if (cpu->cfg.ext_zicntr && !cpu->cfg.ext_zicsr) { if (cpu_cfg_ext_is_user_set(cpu, CPU_CFG_OFFSET(ext_zicntr))) { error_setg(errp, "zicntr requires zicsr"); diff --git a/target/riscv/tcg/vector_helper.c b/target/riscv/tcg/vector_helper.c index 0d1e4e0964..917a48b510 100644 --- a/target/riscv/tcg/vector_helper.c +++ b/target/riscv/tcg/vector_helper.c @@ -5911,3 +5911,59 @@ GEN_VEXT_INT_EXT(vsext_vf2_d, int64_t, int32_t, H8, H4) GEN_VEXT_INT_EXT(vsext_vf4_w, int32_t, int8_t, H4, H1) GEN_VEXT_INT_EXT(vsext_vf4_d, int64_t, int16_t, H8, H2) GEN_VEXT_INT_EXT(vsext_vf8_d, int64_t, int8_t, H8, H1) + +/* Zvabd Extension */ +#define DO_ABD(N, M) (N > M ? N - M : M - N) +#define DO_ABDACC(N, M, D) (DO_ABD(N, M) + D) + +RVVCALL(OPIVV2, vabd_vv_b, OP_SSS_B, H1, H1, H1, DO_ABD) +RVVCALL(OPIVV2, vabd_vv_h, OP_SSS_H, H2, H2, H2, DO_ABD) +RVVCALL(OPIVV2, vabd_vv_w, OP_SSS_W, H4, H4, H4, DO_ABD) +RVVCALL(OPIVV2, vabd_vv_d, OP_SSS_D, H8, H8, H8, DO_ABD) +RVVCALL(OPIVX2, vabd_vx_b, OP_SSS_B, H1, H1, DO_ABD) +RVVCALL(OPIVX2, vabd_vx_h, OP_SSS_H, H2, H2, DO_ABD) +RVVCALL(OPIVX2, vabd_vx_w, OP_SSS_W, H4, H4, DO_ABD) +RVVCALL(OPIVX2, vabd_vx_d, OP_SSS_D, H8, H8, DO_ABD) +RVVCALL(OPIVV2, vabdu_vv_b, OP_UUU_B, H1, H1, H1, DO_ABD) +RVVCALL(OPIVV2, vabdu_vv_h, OP_UUU_H, H2, H2, H2, DO_ABD) +RVVCALL(OPIVV2, vabdu_vv_w, OP_UUU_W, H4, H4, H4, DO_ABD) +RVVCALL(OPIVV2, vabdu_vv_d, OP_UUU_D, H8, H8, H8, DO_ABD) +RVVCALL(OPIVX2, vabdu_vx_b, OP_UUU_B, H1, H1, DO_ABD) +RVVCALL(OPIVX2, vabdu_vx_h, OP_UUU_H, H2, H2, DO_ABD) +RVVCALL(OPIVX2, vabdu_vx_w, OP_UUU_W, H4, H4, DO_ABD) +RVVCALL(OPIVX2, vabdu_vx_d, OP_UUU_D, H8, H8, DO_ABD) + +RVVCALL(OPIVV3, vwabda_vv_b, WOP_SSS_B, H2, H1, H1, DO_ABDACC) +RVVCALL(OPIVV3, vwabda_vv_h, WOP_SSS_H, H4, H2, H2, DO_ABDACC) +RVVCALL(OPIVX3, vwabda_vx_b, WOP_SSS_B, H2, H1, DO_ABDACC) +RVVCALL(OPIVX3, vwabda_vx_h, WOP_SSS_H, H4, H2, DO_ABDACC) +RVVCALL(OPIVV3, vwabdau_vv_b, WOP_UUU_B, H2, H1, H1, DO_ABDACC) +RVVCALL(OPIVV3, vwabdau_vv_h, WOP_UUU_H, H4, H2, H2, DO_ABDACC) +RVVCALL(OPIVX3, vwabdau_vx_b, WOP_UUU_B, H2, H1, DO_ABDACC) +RVVCALL(OPIVX3, vwabdau_vx_h, WOP_UUU_H, H4, H2, DO_ABDACC) + +GEN_VEXT_VV(vabd_vv_b, 1) +GEN_VEXT_VV(vabd_vv_h, 2) +GEN_VEXT_VV(vabd_vv_w, 4) +GEN_VEXT_VV(vabd_vv_d, 8) +GEN_VEXT_VX(vabd_vx_b, 1) +GEN_VEXT_VX(vabd_vx_h, 2) +GEN_VEXT_VX(vabd_vx_w, 4) +GEN_VEXT_VX(vabd_vx_d, 8) +GEN_VEXT_VV(vabdu_vv_b, 1) +GEN_VEXT_VV(vabdu_vv_h, 2) +GEN_VEXT_VV(vabdu_vv_w, 4) +GEN_VEXT_VV(vabdu_vv_d, 8) +GEN_VEXT_VX(vabdu_vx_b, 1) +GEN_VEXT_VX(vabdu_vx_h, 2) +GEN_VEXT_VX(vabdu_vx_w, 4) +GEN_VEXT_VX(vabdu_vx_d, 8) + +GEN_VEXT_VV(vwabda_vv_b, 2) +GEN_VEXT_VV(vwabda_vv_h, 4) +GEN_VEXT_VX(vwabda_vx_b, 2) +GEN_VEXT_VX(vwabda_vx_h, 4) +GEN_VEXT_VV(vwabdau_vv_b, 2) +GEN_VEXT_VV(vwabdau_vv_h, 4) +GEN_VEXT_VX(vwabdau_vx_b, 2) +GEN_VEXT_VX(vwabdau_vx_h, 4) From 937b7e33e0c2af2a07d85f20c408148b78794bf7 Mon Sep 17 00:00:00 2001 From: Mingliang Liu Date: Thu, 15 Oct 2026 21:43:36 +0800 Subject: [PATCH 02/29] disas/riscv: Support vabs.v pseudo-instruction Reviewed-by: Daniel Henrique Barboza Signed-off-by: Mingliang Liu Tested-by: Chao Liu Message-ID: <20261015134337.3354723-3-liumingliang.dev@bytedance.com> Signed-off-by: Alistair Francis --- disas/riscv-op.c.inc | 3 ++- disas/riscv.c | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/disas/riscv-op.c.inc b/disas/riscv-op.c.inc index 07b56f5d52..425473ee61 100644 --- a/disas/riscv-op.c.inc +++ b/disas/riscv-op.c.inc @@ -491,9 +491,10 @@ OP(vaadd_vx, "vaadd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) OP(vaaddu_vv, "vaaddu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vaaddu_vx, "vaaddu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) OP(vabd_vv, "vabd.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) -OP(vabd_vx, "vabd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vabd_vx, "vabd.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm, rvcp_vabd_vx) OP(vabdu_vv, "vabdu.vv", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vm) OP(vabdu_vx, "vabdu.vx", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vm) +OP(vabs_v, "vabs.v", rv_codec_illegal, rv_fmt_vd_vs2_vm) OP(vadc_vim, "vadc.vim", rv_codec_v_i, rv_fmt_vd_vs2_imm_vl) OP(vadc_vvm, "vadc.vvm", rv_codec_v_r, rv_fmt_vd_vs2_vs1_vl) OP(vadc_vxm, "vadc.vxm", rv_codec_v_r, rv_fmt_vd_vs2_rs1_vl) diff --git a/disas/riscv.c b/disas/riscv.c index fd6e0bd613..ca0d6e90b3 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -104,6 +104,7 @@ static const rvc_constraint rvcc_j[] = { rvc_rd_eq_x0, rvc_end }; static const rvc_constraint rvcc_ret[] = { rvc_rs1_eq_ra, rvc_end }; static const rvc_constraint rvcc_jr[] = { rvc_rd_eq_x0, rvc_imm_eq_zero, rvc_end }; +static const rvc_constraint rvcc_vabs_v[] = { rvc_rs1_eq_x0, rvc_end }; static const rvc_constraint rvcc_true[] = { rvc_end }; /* pseudo-instruction metadata */ @@ -243,6 +244,11 @@ static const rv_comp_data rvcp_fsgnjx_q[] = { { }, }; +static const rv_comp_data rvcp_vabd_vx[] = { + { &op_vabs_v, rvcc_vabs_v }, + { }, +}; + /* Convert compressed insns into normal insns via pseudo expansion. */ #define DECOMP(X) &(const rv_comp_data){ &X, rvcc_true } From 6746227d78565a5b411e6222e819b607de3ad140 Mon Sep 17 00:00:00 2001 From: Mingliang Liu Date: Thu, 15 Oct 2026 21:43:37 +0800 Subject: [PATCH 03/29] tests/tcg/riscv64: Add Zvabd extension tests Add a RV64 TCG test covering the masked and unmasked vector-vector and vector-scalar forms of vabd, vabdu, vwabda, and vwabdau. Assemblers do not yet support the version 0.9 instruction mnemonics, so the instruction encodings are hardcoded for the time being. GNU binutils 2.47 supports the incompatible version 0.7 instruction encodings. Therefore, this test must not be built with that version, even after it is converted to use instruction mnemonics. Co-Author: Daniel Henrique Barboza Signed-off-by: Daniel Henrique Barboza Signed-off-by: Mingliang Liu Tested-by: Chao Liu Message-ID: <20261015134337.3354723-4-liumingliang.dev@bytedance.com> Signed-off-by: Alistair Francis --- tests/tcg/riscv64/system/meson.build | 1 + tests/tcg/riscv64/test-zvabd.S | 659 +++++++++++++++++++++++++++ 2 files changed, 660 insertions(+) create mode 100644 tests/tcg/riscv64/test-zvabd.S diff --git a/tests/tcg/riscv64/system/meson.build b/tests/tcg/riscv64/system/meson.build index 728526dc7a..99ba9913ac 100644 --- a/tests/tcg/riscv64/system/meson.build +++ b/tests/tcg/riscv64/system/meson.build @@ -25,6 +25,7 @@ tests += { 'test-minstret-ecall.S': setup + {'qemu_args': ['-icount', 'shift=1', qemu_args]}, 'test-minstret-wfi.S': setup + {'qemu_args': ['-icount', 'shift=1', qemu_args]}, 'test-misa-w.S': setup + {'qemu_args': ['-cpu', 'rv64,x-misa-w=true,c=true,v=true', qemu_args]}, + 'test-zvabd.S': setup + {'qemu_args': ['-cpu', 'rv64,v=true,vlen=256,x-zvabd=true', qemu_args]}, } tests += { diff --git a/tests/tcg/riscv64/test-zvabd.S b/tests/tcg/riscv64/test-zvabd.S new file mode 100644 index 0000000000..4f91acea0c --- /dev/null +++ b/tests/tcg/riscv64/test-zvabd.S @@ -0,0 +1,659 @@ +/* + * Test the Zvabd vector absolute-difference instructions and the vabs.v + * pseudoinstruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + .option arch, +v + .option norvc + + .text + + .global _start +_start: + /* Enable the vector unit (mstatus.VS = Initial). */ + li t0, 1 << 9 + csrs mstatus, t0 + + /* Route synchronous traps to trap_handler (mtvec direct mode). */ + la t0, trap_handler + csrw mtvec, t0 + + /* Run each test function. */ + call test_vabs_v + bnez a0, _exit + call test_vabs_v_mask + bnez a0, _exit + + call test_vabd_vv + bnez a0, _exit + call test_vabd_vv_mask + bnez a0, _exit + + call test_vabd_vx + bnez a0, _exit + call test_vabd_vx_mask + bnez a0, _exit + + call test_vabdu_vv + bnez a0, _exit + call test_vabdu_vv_mask + bnez a0, _exit + + call test_vabdu_vx + bnez a0, _exit + call test_vabdu_vx_mask + bnez a0, _exit + + call test_vwabda_vv + bnez a0, _exit + call test_vwabda_vv_mask + bnez a0, _exit + + call test_vwabda_vx + bnez a0, _exit + call test_vwabda_vx_mask + bnez a0, _exit + + call test_vwabdau_vv + bnez a0, _exit + call test_vwabdau_vv_mask + bnez a0, _exit + + call test_vwabdau_vx + bnez a0, _exit + call test_vwabdau_vx_mask + bnez a0, _exit + + j _exit + +test_vabs_v: + vsetivli zero, 2, e8, m1, ta, ma + + /* Load .arr_neg array in v1 */ + la t0,.arr_neg + vle8.v v1,0(t0) + + /* vabs.v v2, v1 (vabd.vx v2, v1, x0) raw opcode */ + .insn r 0x57, 0x6, 0x2b, x2, x0, x1 + + /* Load .arr_pos in v3 */ + la t1,.arr_pos + vle8.v v3,0(t1) + + /* Compare v2 and v3 into v0 */ + vmsne.vv v0,v2,v3 + vcpop.m a0,v0 + snez a0,a0 + + ret + +test_vabs_v_mask: + vsetivli zero, 2, e8, m1, ta, mu + + /* Load .arr_neg array in v1 */ + la t0, .arr_neg + vle8.v v1, 0(t0) + + /* Zero v2 using .arr_zero */ + la t0,.arr_zero + vle8.v v2,0(t0) + + /* Load .v0_mask array in v0 */ + la t1,.v0_mask + vle8.v v0,0(t1) + + /* vabs.v v2, v1, v0.t raw opcode */ + .insn r 0x57, 0x6, 0x2a, x2, x0, x1 + + /* Load .arr_masked in v3 */ + la t0,.arr_masked + vle8.v v3,0(t0) + + /* Compare v2 and v3 into v0 */ + vmsne.vv v0,v2,v3 + vcpop.m a0,v0 + snez a0,a0 + + ret + +test_vabd_vv: + /* + * Test signed vector-vector absolute difference: + * abs({-5, -7} - {5, 7}) = {10, 14}. + */ + vsetivli zero, 2, e8, m1, ta, mu + + /* Load the two signed source vectors into v1 and v3. */ + la t0, .arr_neg + vle8.v v1, 0(t0) + la t0, .arr_pos + vle8.v v3, 0(t0) + + /* Execute vabd.vv v2, v1, v3. */ + .insn r 0x57, 0x2, 0x2b, x2, x3, x1 + + /* Load the expected result into v3. */ + la t0, .expect_vabd_vv + vle8.v v3, 0(t0) + + /* Compare v2 and v3 into v0 */ + vmsne.vv v0, v2, v3 + vcpop.m a0, v0 + snez a0, a0 + + ret + +test_vabd_vv_mask: + /* + * Test masked vabd.vv. Element 0 remains 9 and element 1 becomes + * abs(-7 - 7) = 14. + */ + vsetivli zero, 2, e8, m1, ta, mu + + /* Load the two signed source vectors into v1 and v3. */ + la t0, .arr_neg + vle8.v v1, 0(t0) + la t0, .arr_pos + vle8.v v3, 0(t0) + + /* Load original value into v2 */ + la t0, .arr_init + vle8.v v2, 0(t0) + + /* Load mask value into v0 */ + la t0, .v0_mask + vle8.v v0, 0(t0) + + .insn r 0x57, 0x2, 0x2a, x2, x3, x1 + + /* Load the expected result into v3. */ + la t0, .expect_vabd_vv_masked + vle8.v v3, 0(t0) + + vmsne.vv v0, v2, v3 + vcpop.m a0, v0 + snez a0, a0 + ret + +test_vabd_vx: + /* + * Test signed vector-scalar absolute difference: + * abs({-5, -7} - 3) = {8, 10}. + */ + vsetivli zero, 2, e8, m1, ta, mu + la t0, .arr_neg + vle8.v v1, 0(t0) + li a2, 3 + + /* Execute vabd.vx v2, v1, a2. */ + .insn r 0x57, 0x6, 0x2b, x2, x12, x1 + + /* Load the expected result into v3. */ + la t0, .expect_vabd_vx + vle8.v v3, 0(t0) + + vmsne.vv v0, v2, v3 + vcpop.m a0, v0 + snez a0, a0 + ret + +test_vabd_vx_mask: + /* + * Test masked vabd.vx. Element 0 remains 9 and element 1 becomes + * abs(-7 - 3) = 10. + */ + vsetivli zero, 2, e8, m1, ta, mu + + la t0, .arr_neg + vle8.v v1, 0(t0) + li a2, 3 + + la t0, .arr_init + vle8.v v2, 0(t0) + + la t0, .v0_mask + vle8.v v0, 0(t0) + + .insn r 0x57, 0x6, 0x2a, x2, x12, x1 + + la t0, .expect_vabd_vx_masked + vle8.v v3, 0(t0) + + vmsne.vv v0, v2, v3 + vcpop.m a0, v0 + snez a0, a0 + ret + +test_vabdu_vv: + /* + * Test unsigned vector-vector absolute difference: + * abs({0, 255} - {255, 0}) = {255, 255}. + */ + vsetivli zero, 2, e8, m1, ta, mu + + /* Load the two unsigned source vectors into v1 and v3. */ + la t0, .arr_unsigned_lhs + vle8.v v1, 0(t0) + la t0, .arr_unsigned_rhs + vle8.v v3, 0(t0) + + /* Execute vabdu.vv v2, v1, v3. */ + .insn r 0x57, 0x2, 0x2d, x2, x3, x1 + + /* Load the expected result into v3. */ + la t0, .expect_vabdu_vv + vle8.v v3, 0(t0) + + vmsne.vv v0, v2, v3 + vcpop.m a0, v0 + snez a0, a0 + ret + +test_vabdu_vv_mask: + /* + * Test masked vabdu.vv. Element 0 remains 9 and element 1 becomes + * abs(255 - 0) = 255. + */ + vsetivli zero, 2, e8, m1, ta, mu + + la t0, .arr_unsigned_lhs + vle8.v v1, 0(t0) + la t0, .arr_unsigned_rhs + vle8.v v3, 0(t0) + + la t0, .arr_init + vle8.v v2, 0(t0) + + la t0, .v0_mask + vle8.v v0, 0(t0) + + .insn r 0x57, 0x2, 0x2c, x2, x3, x1 + + la t0, .expect_vabdu_vv_masked + vle8.v v3, 0(t0) + + vmsne.vv v0, v2, v3 + vcpop.m a0, v0 + snez a0, a0 + ret + +test_vabdu_vx: + /* + * Test unsigned vector-scalar absolute difference: + * abs({0, 255} - 128) = {128, 127}. + */ + vsetivli zero, 2, e8, m1, ta, mu + + la t0, .arr_unsigned_lhs + vle8.v v1, 0(t0) + li a2, 128 + + /* Execute vabdu.vx v2, v1, a2. */ + .insn r 0x57, 0x6, 0x2d, x2, x12, x1 + + la t0, .expect_vabdu_vx + vle8.v v3, 0(t0) + + vmsne.vv v0, v2, v3 + vcpop.m a0, v0 + snez a0, a0 + ret + +test_vabdu_vx_mask: + /* + * Test masked vabdu.vx. Element 0 remains 9 and element 1 becomes + * abs(255 - 128) = 127. + */ + vsetivli zero, 2, e8, m1, ta, mu + + la t0, .arr_unsigned_lhs + vle8.v v1, 0(t0) + li a2, 128 + + la t0, .arr_init + vle8.v v2, 0(t0) + + la t0, .v0_mask + vle8.v v0, 0(t0) + + .insn r 0x57, 0x6, 0x2c, x2, x12, x1 + + la t0, .expect_vabdu_vx_masked + vle8.v v3, 0(t0) + + vmsne.vv v0, v2, v3 + vcpop.m a0, v0 + snez a0, a0 + ret + +test_vwabda_vv: + /* + * Test signed widening vector-vector absolute difference and + * accumulation. Start with v4 = {1000, -1000}, and the expected + * widened result is {1000, -1000} + abs({-5, -7} - {5, 7}) = + * {1010, -986}. + */ + /* Load the 16-bit accumulator before selecting 8-bit operands. */ + vsetivli zero, 2, e16, m2, ta, mu + la t0, .arr_signed_acc + vle16.v v4, 0(t0) + + vsetivli zero, 2, e8, m1, ta, mu + la t0, .arr_neg + vle8.v v1, 0(t0) + la t0, .arr_pos + vle8.v v2, 0(t0) + + /* Execute vwabda.vv v4, v1, v2. */ + .insn r 0x57, 0x0, 0x7b, x4, x2, x1 + + /* Select the widened EEW and compare v4 with the expected result. */ + vsetivli zero, 2, e16, m2, ta, mu + la t0, .expect_vwabda_vv + vle16.v v6, 0(t0) + + vmsne.vv v0, v4, v6 + vcpop.m a0, v0 + snez a0, a0 + ret + +test_vwabda_vv_mask: + /* + * Test masked vwabda.vv. + * Start with v4 = {1000, -1000}, v0 = {0, 1}, and the expected widened + * result is {1000, -1000} + abs({-5, -7} - {5, 7}) = {1000, -986}. + */ + vsetivli zero, 2, e16, m2, ta, mu + la t0, .arr_signed_acc + vle16.v v4, 0(t0) + + vsetivli zero, 2, e8, m1, ta, mu + la t0, .arr_neg + vle8.v v1, 0(t0) + la t0, .arr_pos + vle8.v v2, 0(t0) + + la t0, .v0_mask + vle8.v v0, 0(t0) + + .insn r 0x57, 0x0, 0x7a, x4, x2, x1 + + vsetivli zero, 2, e16, m2, ta, mu + la t0, .expect_vwabda_vv_masked + + vle16.v v6, 0(t0) + vmsne.vv v0, v4, v6 + vcpop.m a0, v0 + snez a0, a0 + ret + +test_vwabda_vx: + /* + * Test signed widening vector-scalar absolute difference and + * accumulation. With scalar 3, v4 becomes {1000 + 8, -1000 + 10}. + */ + /* Load the 16-bit accumulator before selecting 8-bit operands. */ + vsetivli zero, 2, e16, m2, ta, mu + la t0, .arr_signed_acc + vle16.v v4, 0(t0) + + vsetivli zero, 2, e8, m1, ta, mu + la t0, .arr_neg + vle8.v v1, 0(t0) + li a2, 3 + + /* Execute vwabda.vx v4, v1, a2. */ + .insn r 0x57, 0x4, 0x7b, x4, x12, x1 + + /* Select the widened EEW and compare v4 with the expected result. */ + vsetivli zero, 2, e16, m2, ta, mu + la t0, .expect_vwabda_vx + vle16.v v6, 0(t0) + + vmsne.vv v0, v4, v6 + vcpop.m a0, v0 + snez a0, a0 + ret + +test_vwabda_vx_mask: + /* + * Test masked vwabda.vx. Element 0 remains 1000 while element 1 + * accumulates 10 and becomes -990. + */ + vsetivli zero, 2, e16, m2, ta, mu + + la t0, .arr_signed_acc + vle16.v v4, 0(t0) + + vsetivli zero, 2, e8, m1, ta, mu + la t0, .arr_neg + vle8.v v1, 0(t0) + li a2, 3 + + la t0, .v0_mask + vle8.v v0, 0(t0) + + .insn r 0x57, 0x4, 0x7a, x4, x12, x1 + + vsetivli zero, 2, e16, m2, ta, mu + la t0, .expect_vwabda_vx_masked + vle16.v v6, 0(t0) + + vmsne.vv v0, v4, v6 + vcpop.m a0, v0 + snez a0, a0 + ret + +test_vwabdau_vv: + /* + * Test unsigned widening vector-vector absolute difference and + * accumulation. Start with v4 = {1000, 2000}, adding {255, 255} + * produces {1255, 2255}. + */ + /* Load the unsigned 16-bit accumulator and 8-bit source vectors. */ + vsetivli zero, 2, e16, m2, ta, mu + + la t0, .arr_unsigned_acc + vle16.v v4, 0(t0) + vsetivli zero, 2, e8, m1, ta, mu + + la t0, .arr_unsigned_lhs + vle8.v v1, 0(t0) + la t0, .arr_unsigned_rhs + vle8.v v2, 0(t0) + + /* Execute vwabdau.vv v4, v1, v2. */ + .insn r 0x57, 0x0, 0x7d, x4, x2, x1 + + /* Select the widened EEW and compare v4 with the expected result. */ + vsetivli zero, 2, e16, m2, ta, mu + la t0, .expect_vwabdau_vv + vle16.v v6, 0(t0) + + vmsne.vv v0, v4, v6 + vcpop.m a0, v0 + snez a0, a0 + ret + +test_vwabdau_vv_mask: + /* + * Test masked vwabdau.vv. Element 0 remains 1000 while element 1 + * accumulates 255 and becomes 2255. + */ + vsetivli zero, 2, e16, m2, ta, mu + + la t0, .arr_unsigned_acc + vle16.v v4, 0(t0) + + vsetivli zero, 2, e8, m1, ta, mu + la t0, .arr_unsigned_lhs + vle8.v v1, 0(t0) + la t0, .arr_unsigned_rhs + vle8.v v2, 0(t0) + + la t0, .v0_mask + vle8.v v0, 0(t0) + + .insn r 0x57, 0x0, 0x7c, x4, x2, x1 + + vsetivli zero, 2, e16, m2, ta, mu + la t0, .expect_vwabdau_vv_masked + vle16.v v6, 0(t0) + + vmsne.vv v0, v4, v6 + vcpop.m a0, v0 + snez a0, a0 + ret + +test_vwabdau_vx: + /* + * Test unsigned widening vector-scalar absolute difference and + * accumulation. With scalar 128, v4 becomes {1000 + 128, 2000 + 127}. + */ + /* Load the unsigned 16-bit accumulator and 8-bit source vector. */ + vsetivli zero, 2, e16, m2, ta, mu + + la t0, .arr_unsigned_acc + vle16.v v4, 0(t0) + + vsetivli zero, 2, e8, m1, ta, mu + la t0, .arr_unsigned_lhs + vle8.v v1, 0(t0) + li a2, 128 + + /* Execute vwabdau.vx v4, v1, a2. */ + .insn r 0x57, 0x4, 0x7d, x4, x12, x1 + + /* Select the widened EEW and compare v4 with the expected result. */ + vsetivli zero, 2, e16, m2, ta, mu + la t0, .expect_vwabdau_vx + vle16.v v6, 0(t0) + + vmsne.vv v0, v4, v6 + vcpop.m a0, v0 + snez a0, a0 + ret + +test_vwabdau_vx_mask: + /* + * Test masked vwabdau.vx. Element 0 remains 1000 while element 1 + * accumulates 127 and becomes 2127. + */ + vsetivli zero, 2, e16, m2, ta, mu + + la t0, .arr_unsigned_acc + vle16.v v4, 0(t0) + + vsetivli zero, 2, e8, m1, ta, mu + la t0, .arr_unsigned_lhs + vle8.v v1, 0(t0) + li a2, 128 + + la t0, .v0_mask + vle8.v v0, 0(t0) + + .insn r 0x57, 0x4, 0x7c, x4, x12, x1 + + vsetivli zero, 2, e16, m2, ta, mu + la t0, .expect_vwabdau_vx_masked + vle16.v v6, 0(t0) + + vmsne.vv v0, v4, v6 + vcpop.m a0, v0 + snez a0, a0 + ret + + +/* Exit through the semihosting SYS_EXIT_EXTENDED call with a0 as the code. */ +_exit: + la a1, semiargs + li t0, 0x20026 /* ADP_Stopped_ApplicationExit */ + sd t0, 0(a1) + sd a0, 8(a1) + li a0, 0x20 /* TARGET_SYS_EXIT_EXTENDED */ + .balign 16 + slli zero, zero, 0x1f + ebreak + srai zero, zero, 0x7 + j . + + .balign 4 +trap_handler: + csrr t4, mcause + la t5, trap_mcause + sd t4, 0(t5) + csrr t4, mtval + la t5, trap_mtval + sd t4, 0(t5) + li a0, 1 + j _exit + + .data + .balign 8 +semiargs: + .space 16 +trap_mcause: + .space 8 +trap_mtval: + .space 8 +.arr_neg: + .byte -5, -7 +.arr_pos: + .byte 5, 7 +.arr_zero: + .byte 0, 0 +.arr_init: + .byte 9, 9 +.v0_mask: + .byte 0b10 +.arr_masked: + .byte 0, 7 + +/* Unsigned input vectors used by vabdu and vwabdau. */ +.arr_unsigned_lhs: + .byte 0, 255 +.arr_unsigned_rhs: + .byte 255, 0 + .balign 2 + +/* Nonzero accumulators verify the add part of widening operations. */ +.arr_signed_acc: + .half 1000, -1000 +.arr_unsigned_acc: + .half 1000, 2000 + +/* Expected results for the unmasked and masked forms. */ +.expect_vabd_vv: + .byte 10, 14 +.expect_vabd_vv_masked: + .byte 9, 14 +.expect_vabd_vx: + .byte 8, 10 +.expect_vabd_vx_masked: + .byte 9, 10 +.expect_vabdu_vv: + .byte 255, 255 +.expect_vabdu_vv_masked: + .byte 9, 255 +.expect_vabdu_vx: + .byte 128, 127 +.expect_vabdu_vx_masked: + .byte 9, 127 +.expect_vwabda_vv: + .half 1010, -986 +.expect_vwabda_vv_masked: + .half 1000, -986 +.expect_vwabda_vx: + .half 1008, -990 +.expect_vwabda_vx_masked: + .half 1000, -990 +.expect_vwabdau_vv: + .half 1255, 2255 +.expect_vwabdau_vv_masked: + .half 1000, 2255 +.expect_vwabdau_vx: + .half 1128, 2127 +.expect_vwabdau_vx_masked: + .half 1000, 2127 From 9b6fc2a75102ff1713deb1d86bca90ebdf9594fc Mon Sep 17 00:00:00 2001 From: Zephyr Li Date: Tue, 15 Sep 2026 10:49:44 +0800 Subject: [PATCH 04/29] target/riscv: mask vsepc according to IALIGN vsepc is the VS-mode counterpart of sepc and is a WARL register, but unlike mepc and sepc its accessors do not mask the low address bits according to IALIGN. As a result, values with invalid low bits can be observed through vsepc. Apply get_xepc_mask() to both vsepc reads and writes, matching the existing mepc/sepc handling. Add a TCG regression test covering both IALIGN=16 and IALIGN=32. Fixes: 8747c9eeb2aa ("target/riscv: Add Hypervisor virtual CSRs accesses") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4420 Signed-off-by: Zephyr Li Reviewed-by: Daniel Henrique Barboza Message-ID: <20260915024945.32329-1-fritchleybohrer@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/csr.c | 4 +- tests/tcg/riscv64/system/meson.build | 11 ++++++ tests/tcg/riscv64/test-vsepc-masking.S | 54 ++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 tests/tcg/riscv64/test-vsepc-masking.S diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c index 063590f56a..e6d25e5c4e 100644 --- a/target/riscv/tcg/csr.c +++ b/target/riscv/tcg/csr.c @@ -5484,14 +5484,14 @@ static RISCVException write_vsscratch(CPURISCVState *env, int csrno, static RISCVException read_vsepc(CPURISCVState *env, int csrno, target_ulong *val) { - *val = env->vsepc; + *val = env->vsepc & get_xepc_mask(env); return RISCV_EXCP_NONE; } static RISCVException write_vsepc(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { - env->vsepc = val; + env->vsepc = val & get_xepc_mask(env); return RISCV_EXCP_NONE; } diff --git a/tests/tcg/riscv64/system/meson.build b/tests/tcg/riscv64/system/meson.build index 99ba9913ac..3c8b018d9e 100644 --- a/tests/tcg/riscv64/system/meson.build +++ b/tests/tcg/riscv64/system/meson.build @@ -26,6 +26,17 @@ tests += { 'test-minstret-wfi.S': setup + {'qemu_args': ['-icount', 'shift=1', qemu_args]}, 'test-misa-w.S': setup + {'qemu_args': ['-cpu', 'rv64,x-misa-w=true,c=true,v=true', qemu_args]}, 'test-zvabd.S': setup + {'qemu_args': ['-cpu', 'rv64,v=true,vlen=256,x-zvabd=true', qemu_args]}, + 'test-vsepc-masking.S': setup + { + 'test_name': 'test-vsepc-masking-c', + 'qemu_args': ['-cpu', 'rv64,h=true,c=true', qemu_args], + }, +} + +tests += { + 'test-vsepc-masking.S': { + 'test_name': 'test-vsepc-masking-no-c', + 'qemu_args': ['-cpu', 'rv64,h=true,c=false,zca=false', qemu_args], + }, } tests += { diff --git a/tests/tcg/riscv64/test-vsepc-masking.S b/tests/tcg/riscv64/test-vsepc-masking.S new file mode 100644 index 0000000000..394a7f361c --- /dev/null +++ b/tests/tcg/riscv64/test-vsepc-masking.S @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#define MISA_C (1 << 2) + + .option norvc + .text + .global _start +_start: + csrr s0, misa + andi s0, s0, MISA_C + + lla s1, test_addr + ori t0, s1, 3 + csrw vsepc, t0 + csrr t1, vsepc + + beqz s0, ialign32 + addi t2, s1, 2 /* IALIGN=16: bit 0 reads as zero. */ + j check + +ialign32: + /* IALIGN=32: bits [1:0] read as zero. */ + mv t2, s1 + +check: + li a0, 2 + bne t1, t2, _exit + + li a0, 0 + j _exit + + .balign 4 +test_addr: + nop + +/* Exit code in a0. */ +_exit: + lla a1, semiargs + li t0, 0x20026 /* ADP_Stopped_ApplicationExit */ + sd t0, 0(a1) + sd a0, 8(a1) + li a0, 0x20 /* TARGET_SYS_EXIT_EXTENDED */ + + /* Semihosting call sequence. */ + .balign 16 + slli zero, zero, 0x1f + ebreak + srai zero, zero, 0x7 + j . + + .data + .balign 16 +semiargs: + .space 16 From 67bf26c40ac6bfc5664ab65406f908feee096a4f Mon Sep 17 00:00:00 2001 From: Wang Yechao Date: Wed, 30 Sep 2026 17:39:37 +0800 Subject: [PATCH 05/29] target/riscv/kvm: Reset vCPU on VM reset via MP_STATE_INIT_RECEIVED When a VM reset is requested, KVM needs to reset the register state of the vCPUs. Without this, stale KVM-internal vCPU state (such as the SBI STA shared memory address) is preserved across the reset, which can cause KVM to keep writing steal-time data to an address that now belongs to firmware code, corrupting it. Add support for the KVM_CAP_RISCV_MP_STATE_RESET capability. When enabled, QEMU resets the vCPU by setting MP_STATE_INIT_RECEIVED through the KVM_SET_MP_STATE ioctl during the KVM_PUT_RESET_STATE phase of kvm_arch_put_registers(). KVM performs the register reset while preserving the original MP_STATE, so no state restoration is needed on the QEMU side. This reset path is reached both when the guest initiates an SBI system reset and when userspace (e.g. the QEMU monitor) requests a VM reset, covering both reboot scenarios. The capability is optional and only activated if the host kernel advertises KVM_CAP_RISCV_MP_STATE_RESET, ensuring backward compatibility with older kernels. Signed-off-by: Wang Yechao Reviewed-by: Daniel Henrique Barboza Message-ID: <20260930093940.117729-1-wang.yechao255@zte.com.cn> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 45ad5e1525..c085b22b23 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -57,6 +57,7 @@ void riscv_kvm_aplic_request(void *opaque, int irq, int level) } static bool cap_has_mp_state; +static bool mp_state_reset; #define KVM_RISCV_REG_ID_U32(type, idx) (KVM_REG_RISCV | KVM_REG_SIZE_U32 | \ type | idx) @@ -1443,10 +1444,31 @@ static int kvm_riscv_put_mp_state(CPUState *cs) return kvm_vcpu_ioctl(cs, KVM_SET_MP_STATE, &mp_state); } +static int kvm_riscv_mp_state_init_received(CPUState *cs) +{ + struct kvm_mp_state mp_state = { + .mp_state = KVM_MP_STATE_INIT_RECEIVED, + }; + + if (!cap_has_mp_state || !mp_state_reset) { + return 0; + } + + /* Let KVM resets the VCPU, The original MP_STATE is preserved*/ + return kvm_vcpu_ioctl(cs, KVM_SET_MP_STATE, &mp_state); +} + int kvm_arch_put_registers(CPUState *cs, KvmPutState level, Error **errp) { int ret = 0; + if (KVM_PUT_RESET_STATE == level) { + ret = kvm_riscv_mp_state_init_received(cs); + if (ret) { + return ret; + } + } + ret = kvm_riscv_put_regs_core(cs); if (ret) { return ret; @@ -1623,6 +1645,11 @@ int kvm_arch_get_default_type(MachineState *ms) int kvm_arch_init(MachineState *ms, KVMState *s) { cap_has_mp_state = kvm_check_extension(s, KVM_CAP_MP_STATE); + + if (cap_has_mp_state) { + mp_state_reset = !kvm_vm_enable_cap(s, KVM_CAP_RISCV_MP_STATE_RESET, 0); + } + return 0; } From eacbf11025a2c339619f5b2fb61b349893d9c213 Mon Sep 17 00:00:00 2001 From: Portia Stephens Date: Thu, 15 Oct 2026 10:28:47 +1000 Subject: [PATCH 06/29] hw/riscv/atlantis: Use dw8250 UART model Replace the ns16550 UART with a Designware UART now that a model is available. The hack which sets the rest of the UART register space as unimplemented can be removed. Signed-off-by: Portia Stephens Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20261015002849.1640109-2-portias@oss.tenstorrent.com> Signed-off-by: Alistair Francis --- hw/riscv/Kconfig | 2 +- hw/riscv/tt_atlantis.c | 48 +++++++++++----------------------- include/hw/riscv/tt_atlantis.h | 4 +-- 3 files changed, 18 insertions(+), 36 deletions(-) diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index fb05031228..d89fdfcc86 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -144,7 +144,7 @@ config TENSTORRENT select RISCV_ACLINT select RISCV_APLIC select RISCV_IMSIC - select SERIAL_MM + select DW8250 select DEVICE_TREE select DESIGNWARE_I2C select DS1338 diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c index 4e6c39982c..dfd6bc6ba6 100644 --- a/hw/riscv/tt_atlantis.c +++ b/hw/riscv/tt_atlantis.c @@ -24,9 +24,8 @@ #include "hw/riscv/fdt-common.h" #include "hw/riscv/riscv_hart.h" -#include "hw/char/serial-mm.h" +#include "hw/char/dw8250.h" #include "hw/intc/riscv_aclint.h" -#include "hw/misc/unimp.h" #include "hw/misc/tt_atlantis_prcm.h" #include "system/system.h" @@ -268,11 +267,15 @@ static void create_fdt_uart(void *fdt, const MemMapEntry *mem, int irq, int irqchip_phandle, uint32_t clk_parent_phandle, uint32_t clk_phandle) { + static const char * const compat[2] = { + "snps,dw-apb-uart", "ns16550a" + }; g_autofree char *name = g_strdup_printf("/soc/serial@%"HWADDR_PRIX, mem->base); qemu_fdt_add_subnode(fdt, name); - qemu_fdt_setprop_string(fdt, name, "compatible", "ns16550a"); + qemu_fdt_setprop_string_array(fdt, name, "compatible", + (char **)&compat, ARRAY_SIZE(compat)); qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size); qemu_fdt_setprop_cell(fdt, name, "reg-shift", 2); qemu_fdt_setprop_cell(fdt, name, "reg-io-width", 4); @@ -466,17 +469,6 @@ static void load_fdt(TTAtlantisState *ams) create_fdt_memory(ms->fdt, &ams->soc); } -static void mmio_map_unimplemented(MemoryRegion *memory, SysBusDevice *dev, - const char *name, hwaddr addr, uint64_t size) -{ - qdev_prop_set_string(DEVICE(dev), "name", name); - qdev_prop_set_uint64(DEVICE(dev), "size", size); - sysbus_realize(dev, &error_abort); - - memory_region_add_subregion_overlap(memory, addr, - sysbus_mmio_get_region(dev, 0), -1000); -} - static void tt_atlantis_machine_done(Notifier *n, void *data) { TTAtlantisState *ams = container_of(n, TTAtlantisState, machine_done); @@ -545,8 +537,7 @@ static void tt_atlantis_soc_init(Object *obj) object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY); - object_initialize_child(obj, "uart1", &s->uart1, - TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "uart1", &s->uart1, TYPE_DW8250); object_initialize_child(obj, "prcm-rcpu", &s->prcm[TT_ATLANTIS_PRCM_DOMAIN_RCPU], @@ -653,11 +644,6 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(s->memory, s->memmap[TT_ATL_BOOTROM].base, &s->bootrom); - /* UART1, the soc console (UART0 is for the boot microcontroller) */ - serial_mm_init(s->memory, s->memmap[TT_ATL_UART1].base, 2, - qdev_get_gpio_in(s->irqchip, TT_ATL_UART1_IRQ), - 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); - /* 4 PRCM blocks */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_RCPU]), errp)) { @@ -695,18 +681,14 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp) sysbus_mmio_get_region( SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_MM]), 0)); - /* - * Atlantis contains a DesignWare uart while the QEMU machine - * uses the serial_mm model with the base ns16550 register set. - * Linux's dw driver writes outside of serial_mm's 0x20 sized - * mapping and faults. - * - * Create an unimplemented device region so writes don't fault - * and reads return zero, which keeps Linux happy. - */ - mmio_map_unimplemented(s->memory, SYS_BUS_DEVICE(&s->uart1), - "tt-atlantis.uart1", s->memmap[TT_ATL_UART1].base, - s->memmap[TT_ATL_UART1].size); + /* UART1, the soc console (UART0 is for the boot microcontroller) */ + qdev_prop_set_chr(DEVICE(&s->uart1), "chardev", serial_hd(0)); + sysbus_realize(SYS_BUS_DEVICE(&s->uart1), &error_fatal); + memory_region_add_subregion(s->memory, + s->memmap[TT_ATL_UART1].base, + sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->uart1), 0)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart1), 0, + qdev_get_gpio_in(s->irqchip, TT_ATL_UART1_IRQ)); /* I2C */ for (int i = 0; i < TT_ATL_NUM_I2C; i++) { diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h index 577c867d71..fc7a4337cc 100644 --- a/include/hw/riscv/tt_atlantis.h +++ b/include/hw/riscv/tt_atlantis.h @@ -13,8 +13,8 @@ #include "hw/core/sysbus.h" #include "hw/i2c/designware_i2c.h" #include "hw/intc/riscv_imsic.h" -#include "hw/misc/unimp.h" #include "hw/misc/tt_atlantis_prcm.h" +#include "hw/char/dw8250.h" #include "hw/riscv/riscv_hart.h" #define TYPE_TT_ATLANTIS_MACHINE MACHINE_TYPE_NAME("tt-atlantis") @@ -41,7 +41,7 @@ struct TTAtlantisSoCState { RISCVHartArrayState cpus; DeviceState *irqchip; DesignWareI2CState i2c[TT_ATL_NUM_I2C]; - UnimplementedDeviceState uart1; + DW8250State uart1; MemoryRegion bootrom; TTAtlantisPRCMState prcm[TT_ATL_NUM_PRCM]; From cc2f76ddb130e375a3731ec9cb4b6a8c73e72fe2 Mon Sep 17 00:00:00 2001 From: Portia Stephens Date: Thu, 15 Oct 2026 10:28:48 +1000 Subject: [PATCH 07/29] hw/riscv/atlantis: Add all UARTs for Atlantis SoC Previously only the Ascalon console UART was enabled, this enables all 5 UARTs on the SoC. Only UART1 is added to the serial interface for now. Signed-off-by: Portia Stephens Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20261015002849.1640109-3-portias@oss.tenstorrent.com> Signed-off-by: Alistair Francis --- hw/riscv/tt_atlantis.c | 36 ++++++++++++++++++++++++++-------- include/hw/riscv/tt_atlantis.h | 11 ++++++++++- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c index dfd6bc6ba6..311de64eda 100644 --- a/hw/riscv/tt_atlantis.c +++ b/hw/riscv/tt_atlantis.c @@ -64,7 +64,11 @@ static const MemMapEntry tt_atlantis_memmap[] = { [TT_ATL_I2C2] = { 0xd4060000, 0x10000 }, [TT_ATL_I2C3] = { 0xd4070000, 0x10000 }, [TT_ATL_I2C4] = { 0xd4080000, 0x10000 }, + [TT_ATL_UART0] = { 0xd4100000, 0x10000 }, [TT_ATL_UART1] = { 0xd4110000, 0x10000 }, + [TT_ATL_UART2] = { 0xd4120000, 0x10000 }, + [TT_ATL_UART3] = { 0xd4130000, 0x10000 }, + [TT_ATL_UART4] = { 0xd4140000, 0x10000 }, [TT_ATL_PRCM_PCIE] = { 0xd8000000, 0x100 }, [TT_ATL_PRCM_MM] = { 0xdc000000, 0x1000 }, [TT_ATL_PRCM_HSIO] = { 0xe00c0000, 0x510 }, @@ -531,13 +535,21 @@ static void tt_atlantis_machine_done(Notifier *n, void *data) fdt_load_addr); } +static inline int tt_atlantis_uart_index(int uart_dev) +{ + return uart_dev - TT_ATL_UART0; +} + static void tt_atlantis_soc_init(Object *obj) { TTAtlantisSoCState *s = TT_ATLANTIS_SOC(obj); object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY); - object_initialize_child(obj, "uart1", &s->uart1, TYPE_DW8250); + for (int i = 0; i < TT_ATL_NUM_UARTS; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], + TYPE_DW8250); + } object_initialize_child(obj, "prcm-rcpu", &s->prcm[TT_ATLANTIS_PRCM_DOMAIN_RCPU], @@ -682,13 +694,21 @@ static void tt_atlantis_soc_realize(DeviceState *dev, Error **errp) SYS_BUS_DEVICE(&s->prcm[TT_ATLANTIS_PRCM_DOMAIN_MM]), 0)); /* UART1, the soc console (UART0 is for the boot microcontroller) */ - qdev_prop_set_chr(DEVICE(&s->uart1), "chardev", serial_hd(0)); - sysbus_realize(SYS_BUS_DEVICE(&s->uart1), &error_fatal); - memory_region_add_subregion(s->memory, - s->memmap[TT_ATL_UART1].base, - sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->uart1), 0)); - sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart1), 0, - qdev_get_gpio_in(s->irqchip, TT_ATL_UART1_IRQ)); + qdev_prop_set_chr(DEVICE(&s->uart[tt_atlantis_uart_index(TT_ATL_UART1)]), + "chardev", serial_hd(0)); + + for (int i = 0; i < TT_ATL_NUM_UARTS; i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->uart[i]); + if (!sysbus_realize(sbd, errp)) { + return; + } + + memory_region_add_subregion(s->memory, + s->memmap[TT_ATL_UART0 + i].base, + sysbus_mmio_get_region(sbd, 0)); + sysbus_connect_irq(sbd, 0, + qdev_get_gpio_in(s->irqchip, TT_ATL_UART0_IRQ + i)); + } /* I2C */ for (int i = 0; i < TT_ATL_NUM_I2C; i++) { diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h index fc7a4337cc..f965bf23fc 100644 --- a/include/hw/riscv/tt_atlantis.h +++ b/include/hw/riscv/tt_atlantis.h @@ -25,6 +25,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisSoCState, TT_ATLANTIS_SOC) #define TT_ATL_NUM_I2C 5 #define TT_ATL_NUM_PRCM 4 +#define TT_ATL_NUM_UARTS 5 struct TTAtlantisSoCState { /*< private >*/ @@ -41,7 +42,7 @@ struct TTAtlantisSoCState { RISCVHartArrayState cpus; DeviceState *irqchip; DesignWareI2CState i2c[TT_ATL_NUM_I2C]; - DW8250State uart1; + DW8250State uart[TT_ATL_NUM_UARTS]; MemoryRegion bootrom; TTAtlantisPRCMState prcm[TT_ATL_NUM_PRCM]; @@ -66,7 +67,11 @@ enum { TT_ATL_I2C2_IRQ = 35, TT_ATL_I2C3_IRQ = 36, TT_ATL_I2C4_IRQ = 37, + TT_ATL_UART0_IRQ = 38, TT_ATL_UART1_IRQ = 39, + TT_ATL_UART2_IRQ = 40, + TT_ATL_UART3_IRQ = 41, + TT_ATL_UART4_IRQ = 42, }; enum { @@ -83,7 +88,11 @@ enum { TT_ATL_MIMSIC, TT_ATL_SAPLIC, TT_ATL_SIMSIC, + TT_ATL_UART0, TT_ATL_UART1, + TT_ATL_UART2, + TT_ATL_UART3, + TT_ATL_UART4, TT_ATL_PRCM_RCPU, TT_ATL_PRCM_HSIO, TT_ATL_PRCM_PCIE, From fd559e1273381ba99400cc64dd3b87ff9814bbe2 Mon Sep 17 00:00:00 2001 From: Portia Stephens Date: Thu, 15 Oct 2026 10:28:49 +1000 Subject: [PATCH 08/29] docs/atlantis: Update Atlantis UART documentation Update Atlantis documentation to describe the DesignWare UART used. Signed-off-by: Portia Stephens Reviewed-by: Alistair Francis Message-ID: <20261015002849.1640109-4-portias@oss.tenstorrent.com> Signed-off-by: Alistair Francis --- docs/system/riscv/tt_atlantis.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/system/riscv/tt_atlantis.rst b/docs/system/riscv/tt_atlantis.rst index 539c22354f..d94d064f4f 100644 --- a/docs/system/riscv/tt_atlantis.rst +++ b/docs/system/riscv/tt_atlantis.rst @@ -13,7 +13,7 @@ tt-atlantis QEMU model features * 8-core Ascalon-X CPU Cluster * RISC-V compliant Advanced Interrupt Architecture -* 16550A compatible UART +* DW8250 compatible UART x5 * 4 PRCMs (Clock and Reset Controllers) Known limitations @@ -25,9 +25,8 @@ real platform. Notably: * There is no PCI host bridge, so virtio-pci devices cannot be attached. Boots that need block storage must use ``-initrd`` with an initramfs. -* The DesignWare UART is modelled with QEMU's ns16550-compatible - ``serial_mm`` device; DesignWare-specific registers beyond that - set return 0. +* The Atlantis platform has 5 UARTs but only ``UART1`` is currently exposed + on the command line serial interface. ``UART1`` is the Ascalon console. Supported software ------------------ From 9c20897e50f44837e53896fe504a63a6dd696c34 Mon Sep 17 00:00:00 2001 From: Tan Chi Date: Mon, 5 Oct 2026 19:15:23 +0800 Subject: [PATCH 09/29] hw/riscv/riscv-iommu: fix entry leak with ioatc-limit=0 riscv_iommu_translate() allocates an IOATC entry before passing it to riscv_iommu_iot_update(). The update helper normally transfers ownership to the hash table. When ioatc-limit is zero, however, the helper returns without inserting or freeing the entry. Every cacheable non-identity translation therefore leaks one RISCVIOMMUEntry. Avoid this by disabling caching at the caller when ioatc-limit is zero, so riscv_iommu_translate() does not allocate an IOATC entry in the first place. This avoids special-casing entry freeing in riscv_iommu_iot_update() and keeps the ownership semantics simpler. Suggested-by: Daniel Henrique Barboza Fixes: 9d085a1c3cb2 ("hw/riscv/riscv-iommu: add Address Translation Cache (IOATC)") Cc: qemu-stable@nongnu.org Signed-off-by: Tan Chi Reviewed-by: Daniel Henrique Barboza Reviewed-by: Nutty Liu Message-ID: <20261005111523.277550-1-tanchi25@mails.ucas.ac.cn> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 323a041b4a..866f49cc39 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -2919,7 +2919,8 @@ static IOMMUTLBEntry riscv_iommu_memory_region_translate( /* Translation disabled or invalid. */ iotlb.addr_mask = 0; iotlb.perm = IOMMU_NONE; - } else if (riscv_iommu_translate(as->iommu, ctx, &iotlb, true)) { + } else if (riscv_iommu_translate(as->iommu, ctx, &iotlb, + as->iommu->iot_limit > 0)) { /* Translation disabled or fault reported. */ iotlb.addr_mask = 0; iotlb.perm = IOMMU_NONE; From cc06f6ba8aadbda88ced159455d3543aefb62d7e Mon Sep 17 00:00:00 2001 From: Tao Ding Date: Sun, 18 Oct 2026 22:10:02 +0800 Subject: [PATCH 10/29] hw/misc: fix calculating output address overflow in k230_decomp_gzip Use hwaddr for address calculations to avoid 32-bit overflow. Resolves: Coverity CID 1685515 Reported-by: Alistair Francis Reviewed-by: Alistair Francis Signed-off-by: Tao Ding Message-ID: <20261018141006.16398-2-dingtao0430@163.com> Signed-off-by: Alistair Francis --- hw/misc/k230_decomp_gzip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/k230_decomp_gzip.c b/hw/misc/k230_decomp_gzip.c index 0d8a3b2d2e..ac06cc1571 100644 --- a/hw/misc/k230_decomp_gzip.c +++ b/hw/misc/k230_decomp_gzip.c @@ -53,7 +53,7 @@ static bool k230_decomp_gzip_write_mem(K230DecompGzipState *s, hwaddr addr, static hwaddr k230_decomp_gzip_output_addr(K230DecompGzipState *s) { return K230_DECOMP_GZIP_SRAM_OUT_BASE + - s->output.slot * K230_DECOMP_GZIP_BLOCK_SIZE + + (hwaddr) s->output.slot * K230_DECOMP_GZIP_BLOCK_SIZE + s->output.current_offset; } From 68b44faee92359eec2f98999ca49ab969348d7cd Mon Sep 17 00:00:00 2001 From: Tao Ding Date: Sun, 18 Oct 2026 22:10:03 +0800 Subject: [PATCH 11/29] hw/misc: fix calculating input address overflow in k230_decomp_gzip Use hwaddr for address calculations to avoid 32-bit overflow. Resolves: Coverity CID 1685517 Reported-by: Alistair Francis Reviewed-by: Alistair Francis Signed-off-by: Tao Ding Message-ID: <20261018141006.16398-3-dingtao0430@163.com> Signed-off-by: Alistair Francis --- hw/misc/k230_decomp_gzip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/k230_decomp_gzip.c b/hw/misc/k230_decomp_gzip.c index ac06cc1571..da4dc6f954 100644 --- a/hw/misc/k230_decomp_gzip.c +++ b/hw/misc/k230_decomp_gzip.c @@ -59,7 +59,7 @@ static hwaddr k230_decomp_gzip_output_addr(K230DecompGzipState *s) static hwaddr k230_decomp_gzip_input_addr(K230DecompGzipState *s) { - return s->input.slot * K230_DECOMP_GZIP_BLOCK_SIZE + + return (hwaddr) s->input.slot * K230_DECOMP_GZIP_BLOCK_SIZE + K230_DECOMP_GZIP_SRAM_IN_BASE; } From e70a0737371fd498c7ce019614d0df5d60ddba22 Mon Sep 17 00:00:00 2001 From: Tao Ding Date: Sun, 18 Oct 2026 22:10:04 +0800 Subject: [PATCH 12/29] hw/misc: use hwaddr for the k230_decomp_gzip input address Use hwaddr for address calculations to avoid 32-bit overflow and address truncation. Reviewed-by: Alistair Francis Signed-off-by: Tao Ding Message-ID: <20261018141006.16398-4-dingtao0430@163.com> Signed-off-by: Alistair Francis --- hw/misc/k230_decomp_gzip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/k230_decomp_gzip.c b/hw/misc/k230_decomp_gzip.c index da4dc6f954..54303c0e7f 100644 --- a/hw/misc/k230_decomp_gzip.c +++ b/hw/misc/k230_decomp_gzip.c @@ -102,7 +102,7 @@ static void k230_decomp_gzip_finish(K230DecompGzipState *s, bool crc_ok) } static bool k230_decomp_gzip_load_input(K230DecompGzipState *s, - uint32_t addr, uint32_t size) + hwaddr addr, uint32_t size) { s->input.current_offset = 0; From f0af71d33dc09d5b00270f569260166e49a2fb9e Mon Sep 17 00:00:00 2001 From: Tao Ding Date: Sun, 18 Oct 2026 22:10:05 +0800 Subject: [PATCH 13/29] qtest: fix k230_decomp_gzip test integer handling issues Cast slot to hwaddr before multiplication to prevent 32-bit overflow when calculating the SRAM input address. Resolves: Coverity CID 1685516 Reported-by: Alistair Francis Reviewed-by: Alistair Francis Signed-off-by: Tao Ding Message-ID: <20261018141006.16398-5-dingtao0430@163.com> Signed-off-by: Alistair Francis --- tests/qtest/k230-decomp-gzip-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/k230-decomp-gzip-test.c b/tests/qtest/k230-decomp-gzip-test.c index e80113fd28..249c8ae82c 100644 --- a/tests/qtest/k230-decomp-gzip-test.c +++ b/tests/qtest/k230-decomp-gzip-test.c @@ -46,7 +46,7 @@ static inline hwaddr k230_sram_addr(hwaddr off) static inline hwaddr k230_sram_input_addr(unsigned int slot) { return k230_sram_addr(K230_DECOMP_GZIP_SRAM_IN_BASE + - slot * K230_DECOMP_GZIP_BLOCK_SIZE); + (hwaddr) slot * K230_DECOMP_GZIP_BLOCK_SIZE); } /* From 02378a2eef1965de25ecf9432ef1495760e12120 Mon Sep 17 00:00:00 2001 From: Tao Ding Date: Sun, 18 Oct 2026 22:10:06 +0800 Subject: [PATCH 14/29] hw/dma: fix k230_gsdma integer handling issues Separate global and channel register handling to prevent unsigned address underflow when calculating GSDMA channel offsets. Resolves: Coverity CID 1685514 Reported-by: Alistair Francis Signed-off-by: Tao Ding Message-ID: <20261018141006.16398-6-dingtao0430@163.com> Signed-off-by: Alistair Francis --- hw/dma/k230_gsdma.c | 88 ++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/hw/dma/k230_gsdma.c b/hw/dma/k230_gsdma.c index 7635cd19e8..bd6db9e64d 100644 --- a/hw/dma/k230_gsdma.c +++ b/hw/dma/k230_gsdma.c @@ -304,54 +304,54 @@ static uint64_t k230_gsdma_read(void *opaque, hwaddr addr, unsigned int size) hwaddr ch_off; uint64_t ret = 0; - switch (addr) { - case K230_GSDMA_DMA_CH_EN: - ret = s->dma_ch_en; - break; - case K230_GSDMA_DMA_INT_MASK: - ret = s->dma_int_mask; - break; - case K230_GSDMA_DMA_INT_STAT: - ret = s->dma_int_stat; - break; - case K230_GSDMA_DMA_CFG: - ret = s->dma_cfg; - break; - case K230_GSDMA_DMA_WEIGHT: - ret = s->dma_weight; - break; - default: - if (addr < K230_GSDMA_CH_BASE) { + if (addr < K230_GSDMA_CH_BASE) { + switch (addr) { + case K230_GSDMA_DMA_CH_EN: + ret = s->dma_ch_en; + break; + case K230_GSDMA_DMA_INT_MASK: + ret = s->dma_int_mask; + break; + case K230_GSDMA_DMA_INT_STAT: + ret = s->dma_int_stat; + break; + case K230_GSDMA_DMA_CFG: + ret = s->dma_cfg; + break; + case K230_GSDMA_DMA_WEIGHT: + ret = s->dma_weight; + break; + default: qemu_log_mask(LOG_GUEST_ERROR, "%s: not implement gdma at offset 0x%" HWADDR_PRIx "\n", __func__, addr); + return ret; } - break; - } - - ch = (addr - K230_GSDMA_CH_BASE) / K230_GSDMA_CH_STRIDE; - ch_off = (addr - K230_GSDMA_CH_BASE) % K230_GSDMA_CH_STRIDE; - if (ch < K230_GSDMA_NUM_SDMA_CHANNELS) { - switch (ch_off) { - case K230_GSDMA_CH_CTL: - break; - case K230_GSDMA_CH_STATUS: - ret = s->channels[ch].status; - break; - case K230_GSDMA_CH_CFG: - ret = s->channels[ch].cfg; - break; - case K230_GSDMA_CH_USR_DATA: - ret = s->channels[ch].usr_data; - break; - case K230_GSDMA_CH_LLT_SADDR: - ret = s->channels[ch].llt_saddr; - break; - case K230_GSDMA_CH_CURRENT_LLT: - ret = s->channels[ch].current_llt; - break; - default: - break; + } else { + ch = (addr - K230_GSDMA_CH_BASE) / K230_GSDMA_CH_STRIDE; + ch_off = (addr - K230_GSDMA_CH_BASE) % K230_GSDMA_CH_STRIDE; + if (ch < K230_GSDMA_NUM_SDMA_CHANNELS) { + switch (ch_off) { + case K230_GSDMA_CH_CTL: + break; + case K230_GSDMA_CH_STATUS: + ret = s->channels[ch].status; + break; + case K230_GSDMA_CH_CFG: + ret = s->channels[ch].cfg; + break; + case K230_GSDMA_CH_USR_DATA: + ret = s->channels[ch].usr_data; + break; + case K230_GSDMA_CH_LLT_SADDR: + ret = s->channels[ch].llt_saddr; + break; + case K230_GSDMA_CH_CURRENT_LLT: + ret = s->channels[ch].current_llt; + break; + default: + break; + } } } From 5a99bd8f8f589080d642bec056f2d2ab30c8efd1 Mon Sep 17 00:00:00 2001 From: Tan Chi Date: Sat, 17 Oct 2026 22:45:32 +0800 Subject: [PATCH 15/29] hw/riscv/riscv-iommu: serialize IOATC accesses The IOATC currently keeps references to the hash table while cached entries are being used. The following change will remove matching entries during invalidation, which requires cached entry lifetime to be protected explicitly. Add an iot_lock to serialize IOATC lookup, update, invalidation, and reset operations. Copy the fields needed by the translation path while holding the lock, then release it before updating the IOMMUTLBEntry. Keep the page-table walk outside the critical section so the lock is not held across potentially expensive memory accesses. Signed-off-by: Tan Chi Reviewed-by: Daniel Henrique Barboza Message-ID: <20261017144533.15610-1-tanchi25@mails.ucas.ac.cn> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.c | 53 ++++++++++++++++++++++++------------------ hw/riscv/riscv-iommu.h | 1 + 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 866f49cc39..2ac479ee50 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -1682,7 +1682,7 @@ static void riscv_iommu_iot_inval_gscid_pscid_iova(gpointer key, gpointer value, } } -/* caller should keep ref-count for iot_cache object */ +/* caller must hold iot_lock */ static RISCVIOMMUEntry *riscv_iommu_iot_lookup(RISCVIOMMUContext *ctx, GHashTable *iot_cache, hwaddr iova, RISCVIOMMUTransTag transtag) { @@ -1695,27 +1695,26 @@ static RISCVIOMMUEntry *riscv_iommu_iot_lookup(RISCVIOMMUContext *ctx, return g_hash_table_lookup(iot_cache, &key); } -/* caller should keep ref-count for iot_cache object */ -static void riscv_iommu_iot_update(RISCVIOMMUState *s, - GHashTable *iot_cache, RISCVIOMMUEntry *iot) +/* caller must hold iot_lock */ +static void riscv_iommu_iot_update(RISCVIOMMUState *s, RISCVIOMMUEntry *iot) { if (!s->iot_limit) { return; } if (g_hash_table_size(s->iot_cache) >= s->iot_limit) { - iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash, - riscv_iommu_iot_equal, - g_free, NULL); - g_hash_table_unref(qatomic_xchg(&s->iot_cache, iot_cache)); + GHashTable *old_cache = s->iot_cache; + s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash, + riscv_iommu_iot_equal, + g_free, NULL); + g_hash_table_unref(old_cache); } - g_hash_table_add(iot_cache, iot); + g_hash_table_add(s->iot_cache, iot); } static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHFunc func, uint32_t gscid, uint32_t pscid, hwaddr iova, RISCVIOMMUTransTag transtag) { - GHashTable *iot_cache; RISCVIOMMUEntry key = { .tag = transtag, .gscid = gscid, @@ -1723,9 +1722,9 @@ static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHFunc func, .iova = PPN_DOWN(iova), }; - iot_cache = g_hash_table_ref(s->iot_cache); - g_hash_table_foreach(iot_cache, func, &key); - g_hash_table_unref(iot_cache); + qemu_mutex_lock(&s->iot_lock); + g_hash_table_foreach(s->iot_cache, func, &key); + qemu_mutex_unlock(&s->iot_lock); } static RISCVIOMMUTransTag riscv_iommu_get_transtag(RISCVIOMMUContext *ctx) @@ -1747,15 +1746,14 @@ static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, { RISCVIOMMUTransTag transtag = riscv_iommu_get_transtag(ctx); RISCVIOMMUEntry *iot; - IOMMUAccessFlags perm; + IOMMUAccessFlags perm = IOMMU_NONE; bool enable_pid; bool enable_pri; - GHashTable *iot_cache; + hwaddr phys = 0; int fault; riscv_iommu_hpm_incr_ctr(s, ctx, RISCV_IOMMU_HPMEVENT_URQ); - iot_cache = g_hash_table_ref(s->iot_cache); enable_pri = (iotlb->perm == IOMMU_NONE) && (ctx->tc & RISCV_IOMMU_DC_TC_EN_PRI); enable_pid = (ctx->tc & RISCV_IOMMU_DC_TC_PDTV); @@ -1771,10 +1769,15 @@ static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, } } - iot = riscv_iommu_iot_lookup(ctx, iot_cache, iotlb->iova, transtag); - perm = iot ? iot->perm : IOMMU_NONE; + qemu_mutex_lock(&s->iot_lock); + iot = riscv_iommu_iot_lookup(ctx, s->iot_cache, iotlb->iova, transtag); + if (iot) { + perm = iot->perm; + phys = iot->phys; + } + qemu_mutex_unlock(&s->iot_lock); if (perm != IOMMU_NONE) { - iotlb->translated_addr = PPN_PHYS(iot->phys); + iotlb->translated_addr = PPN_PHYS(phys); iotlb->addr_mask = ~TARGET_PAGE_MASK; iotlb->perm = perm; fault = 0; @@ -1805,12 +1808,12 @@ static int riscv_iommu_translate(RISCVIOMMUState *s, RISCVIOMMUContext *ctx, iot->pscid = get_field(ctx->ta, RISCV_IOMMU_DC_TA_PSCID); iot->perm = iotlb->perm; iot->tag = transtag; - riscv_iommu_iot_update(s, iot_cache, iot); + qemu_mutex_lock(&s->iot_lock); + riscv_iommu_iot_update(s, iot); + qemu_mutex_unlock(&s->iot_lock); } done: - g_hash_table_unref(iot_cache); - if (enable_pri && fault) { struct riscv_iommu_pq_record pr = {0}; if (enable_pid) { @@ -2674,6 +2677,7 @@ static void riscv_iommu_instance_init(Object *obj) s->iot_cache = g_hash_table_new_full(riscv_iommu_iot_hash, riscv_iommu_iot_equal, g_free, NULL); + qemu_mutex_init(&s->iot_lock); s->iommus.le_next = NULL; s->iommus.le_prev = NULL; @@ -2690,6 +2694,8 @@ static void riscv_iommu_instance_finalize(Object *obj) g_hash_table_unref(s->ctx_cache); g_hash_table_unref(s->iot_cache); + + qemu_mutex_destroy(&s->iot_lock); } static void riscv_iommu_realize(DeviceState *dev, Error **errp) @@ -2850,7 +2856,10 @@ void riscv_iommu_reset(RISCVIOMMUState *s) riscv_iommu_reg_set32(s, RISCV_IOMMU_REG_IPSR, 0); g_hash_table_remove_all(s->ctx_cache); + + qemu_mutex_lock(&s->iot_lock); g_hash_table_remove_all(s->iot_cache); + qemu_mutex_unlock(&s->iot_lock); } static const Property riscv_iommu_properties[] = { diff --git a/hw/riscv/riscv-iommu.h b/hw/riscv/riscv-iommu.h index 7c470ba084..403d10b321 100644 --- a/hw/riscv/riscv-iommu.h +++ b/hw/riscv/riscv-iommu.h @@ -73,6 +73,7 @@ struct RISCVIOMMUState { GHashTable *iot_cache; /* IO Translated Address Cache */ unsigned iot_limit; /* IO Translation Cache size limit */ + QemuMutex iot_lock; /* MMIO Hardware Interface */ MemoryRegion regs_mr; From c7821ce4561ed4b92bc2b1c5e09953fbd8100d95 Mon Sep 17 00:00:00 2001 From: Tan Chi Date: Sat, 17 Oct 2026 22:45:33 +0800 Subject: [PATCH 16/29] hw/riscv/riscv-iommu: reclaim invalidated IOATC entries IOTINVAL currently walks the IOATC hash table and invalidates matching entries by setting iot->perm to IOMMU_NONE. The invalidated entries remain in the hash table afterwards. Under workloads with frequent DMA map/unmap activity, invalid entries can accumulate while only a smaller number of entries remain valid. Subsequent IOTINVAL operations continue to scan these entries. They are also included in g_hash_table_size(), causing the IOATC to reach iot_limit earlier and the entire cache to be flushed more frequently. Remove matching entries during invalidation instead of leaving them in the hash table with IOMMU_NONE. The measurements use a Linux 7.3-rc5 guest based on linux-iommu/next commit cec2dc7663e7 with v6 of the "iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO" series applied. The QEMU behavior is not specific to this guest kernel; this configuration makes it easier to exercise through normal DMA map/unmap activity. In a high-churn NVMe workload with frequent DMA map/unmap activity, runtime drops from 168 seconds to 35 seconds, a 4.8x speedup. A short fio workload with a small working set remains within measurement noise. Across three runs for each version of a 300-second fio workload with a 4 GiB working set, median throughput improves from 68.6 MiB/s to 75.0 MiB/s, an increase of 9.3%. Signed-off-by: Tan Chi Reviewed-by: Daniel Henrique Barboza Message-ID: <20261017144533.15610-2-tanchi25@mails.ucas.ac.cn> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.c | 122 +++++++++++++++++++---------------------- 1 file changed, 56 insertions(+), 66 deletions(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 2ac479ee50..77b93fab5f 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -1582,104 +1582,93 @@ static guint riscv_iommu_iot_hash(gconstpointer v) /* GV: 0 AV: 0 PSCV: 0 GVMA: 0 */ /* GV: 0 AV: 0 GVMA: 1 */ static -void riscv_iommu_iot_inval_all(gpointer key, gpointer value, gpointer data) +gboolean riscv_iommu_iot_inval_all(gpointer key, gpointer value, gpointer data) { RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; - if (iot->tag == arg->tag) { - iot->perm = IOMMU_NONE; - } + return iot->tag == arg->tag; } /* GV: 0 AV: 0 PSCV: 1 GVMA: 0 */ static -void riscv_iommu_iot_inval_pscid(gpointer key, gpointer value, gpointer data) +gboolean riscv_iommu_iot_inval_pscid(gpointer key, gpointer value, + gpointer data) { RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; - if (iot->tag == arg->tag && - iot->pscid == arg->pscid) { - iot->perm = IOMMU_NONE; - } + return iot->tag == arg->tag && + iot->pscid == arg->pscid; } /* GV: 0 AV: 1 PSCV: 0 GVMA: 0 */ static -void riscv_iommu_iot_inval_iova(gpointer key, gpointer value, gpointer data) +gboolean riscv_iommu_iot_inval_iova(gpointer key, gpointer value, gpointer data) { RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; - if (iot->tag == arg->tag && - iot->iova == arg->iova) { - iot->perm = IOMMU_NONE; - } + return iot->tag == arg->tag && + iot->iova == arg->iova; } /* GV: 0 AV: 1 PSCV: 1 GVMA: 0 */ -static void riscv_iommu_iot_inval_pscid_iova(gpointer key, gpointer value, - gpointer data) +static gboolean riscv_iommu_iot_inval_pscid_iova(gpointer key, + gpointer value, + gpointer data) { RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; - if (iot->tag == arg->tag && - iot->pscid == arg->pscid && - iot->iova == arg->iova) { - iot->perm = IOMMU_NONE; - } + return iot->tag == arg->tag && + iot->pscid == arg->pscid && + iot->iova == arg->iova; } /* GV: 1 AV: 0 PSCV: 0 GVMA: 0 */ /* GV: 1 AV: 0 GVMA: 1 */ -static -void riscv_iommu_iot_inval_gscid(gpointer key, gpointer value, gpointer data) +static gboolean riscv_iommu_iot_inval_gscid(gpointer key, gpointer value, + gpointer data) { RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; - if (iot->tag == arg->tag && - iot->gscid == arg->gscid) { - iot->perm = IOMMU_NONE; - } + return iot->tag == arg->tag && + iot->gscid == arg->gscid; } /* GV: 1 AV: 0 PSCV: 1 GVMA: 0 */ -static void riscv_iommu_iot_inval_gscid_pscid(gpointer key, gpointer value, - gpointer data) +static gboolean riscv_iommu_iot_inval_gscid_pscid(gpointer key, + gpointer value, + gpointer data) { RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; - if (iot->tag == arg->tag && - iot->gscid == arg->gscid && - iot->pscid == arg->pscid) { - iot->perm = IOMMU_NONE; - } + return iot->tag == arg->tag && + iot->gscid == arg->gscid && + iot->pscid == arg->pscid; } /* GV: 1 AV: 1 PSCV: 0 GVMA: 0 */ /* GV: 1 AV: 1 GVMA: 1 */ -static void riscv_iommu_iot_inval_gscid_iova(gpointer key, gpointer value, - gpointer data) +static gboolean riscv_iommu_iot_inval_gscid_iova(gpointer key, + gpointer value, + gpointer data) { RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; - if (iot->tag == arg->tag && - iot->gscid == arg->gscid && - iot->iova == arg->iova) { - iot->perm = IOMMU_NONE; - } + return iot->tag == arg->tag && + iot->gscid == arg->gscid && + iot->iova == arg->iova; } /* GV: 1 AV: 1 PSCV: 1 GVMA: 0 */ -static void riscv_iommu_iot_inval_gscid_pscid_iova(gpointer key, gpointer value, - gpointer data) +static gboolean riscv_iommu_iot_inval_gscid_pscid_iova(gpointer key, + gpointer value, + gpointer data) { RISCVIOMMUEntry *iot = (RISCVIOMMUEntry *) value; RISCVIOMMUEntry *arg = (RISCVIOMMUEntry *) data; - if (iot->tag == arg->tag && - iot->gscid == arg->gscid && - iot->pscid == arg->pscid && - iot->iova == arg->iova) { - iot->perm = IOMMU_NONE; - } + return iot->tag == arg->tag && + iot->gscid == arg->gscid && + iot->pscid == arg->pscid && + iot->iova == arg->iova; } /* caller must hold iot_lock */ @@ -1712,7 +1701,7 @@ static void riscv_iommu_iot_update(RISCVIOMMUState *s, RISCVIOMMUEntry *iot) g_hash_table_add(s->iot_cache, iot); } -static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHFunc func, +static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHRFunc func, uint32_t gscid, uint32_t pscid, hwaddr iova, RISCVIOMMUTransTag transtag) { RISCVIOMMUEntry key = { @@ -1723,7 +1712,7 @@ static void riscv_iommu_iot_inval(RISCVIOMMUState *s, GHFunc func, }; qemu_mutex_lock(&s->iot_lock); - g_hash_table_foreach(s->iot_cache, func, &key); + g_hash_table_foreach_remove(s->iot_cache, func, &key); qemu_mutex_unlock(&s->iot_lock); } @@ -1972,7 +1961,8 @@ static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s) dma_addr_t addr; uint32_t tail, head, ctrl; uint64_t cmd_opcode; - GHFunc func; + GHRFunc iot_func; + GHFunc ctx_func; ctrl = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQCSR); tail = riscv_iommu_reg_get32(s, RISCV_IOMMU_REG_CQT) & s->cq_mask; @@ -2035,18 +2025,18 @@ static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s) goto cmd_ill; } - func = riscv_iommu_iot_inval_all; + iot_func = riscv_iommu_iot_inval_all; if (gv) { - func = (av) ? riscv_iommu_iot_inval_gscid_iova : + iot_func = (av) ? riscv_iommu_iot_inval_gscid_iova : riscv_iommu_iot_inval_gscid; } riscv_iommu_iot_inval( - s, func, gscid, pscid, iova, RISCV_IOMMU_TRANS_TAG_VG); + s, iot_func, gscid, pscid, iova, RISCV_IOMMU_TRANS_TAG_VG); riscv_iommu_iot_inval( - s, func, gscid, pscid, iova, RISCV_IOMMU_TRANS_TAG_VN); + s, iot_func, gscid, pscid, iova, RISCV_IOMMU_TRANS_TAG_VN); break; } @@ -2066,24 +2056,24 @@ static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s) if (gv) { transtag = RISCV_IOMMU_TRANS_TAG_VN; if (pscv) { - func = (av) ? riscv_iommu_iot_inval_gscid_pscid_iova : + iot_func = (av) ? riscv_iommu_iot_inval_gscid_pscid_iova : riscv_iommu_iot_inval_gscid_pscid; } else { - func = (av) ? riscv_iommu_iot_inval_gscid_iova : + iot_func = (av) ? riscv_iommu_iot_inval_gscid_iova : riscv_iommu_iot_inval_gscid; } } else { transtag = RISCV_IOMMU_TRANS_TAG_SS; if (pscv) { - func = (av) ? riscv_iommu_iot_inval_pscid_iova : + iot_func = (av) ? riscv_iommu_iot_inval_pscid_iova : riscv_iommu_iot_inval_pscid; } else { - func = (av) ? riscv_iommu_iot_inval_iova : + iot_func = (av) ? riscv_iommu_iot_inval_iova : riscv_iommu_iot_inval_all; } } - riscv_iommu_iot_inval(s, func, gscid, pscid, iova, transtag); + riscv_iommu_iot_inval(s, iot_func, gscid, pscid, iova, transtag); break; } @@ -2091,12 +2081,12 @@ static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s) RISCV_IOMMU_CMD_IODIR_OPCODE): if (!(cmd.dword0 & RISCV_IOMMU_CMD_IODIR_DV)) { /* invalidate all device context cache mappings */ - func = riscv_iommu_ctx_inval_all; + ctx_func = riscv_iommu_ctx_inval_all; } else { /* invalidate all device context matching DID */ - func = riscv_iommu_ctx_inval_devid; + ctx_func = riscv_iommu_ctx_inval_devid; } - riscv_iommu_ctx_inval(s, func, + riscv_iommu_ctx_inval(s, ctx_func, get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID), 0); break; @@ -2106,9 +2096,9 @@ static void riscv_iommu_process_cq_tail(RISCVIOMMUState *s) /* illegal command arguments IODIR_PDT & DV == 0 */ goto cmd_ill; } else { - func = riscv_iommu_ctx_inval_devid_procid; + ctx_func = riscv_iommu_ctx_inval_devid_procid; } - riscv_iommu_ctx_inval(s, func, + riscv_iommu_ctx_inval(s, ctx_func, get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_DID), get_field(cmd.dword0, RISCV_IOMMU_CMD_IODIR_PID)); break; From 4ac8803488c0a7c1b5cd793c0154c9a7e6c966a7 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 21 Oct 2026 14:31:38 -0300 Subject: [PATCH 17/29] target/riscv/cpu.c: duct tape fix for QMP + RV profiles introspection Markus reported in [1] a problem with QMP introspection with qemu-system-riscv64 that happens after querying a profile CPU. QEMU crashes hitting an abort during satp_mode logic that, somehow, is being triggered via profile code. For every profile CPU that goes through init we're setting an 'enabled' flag in the static RISCVProfile object. This sounded like an okay idea back when profiles were introduced, but now we've realized that QMP will querying CPUs without exiting the QEMU process while also going multiple times through cpu_init() and friends. The result is that we're in a situation where we have runtime information being stored in static arrays that persists between CPU definitions being introspected. A proper fix is to get rid of profile->enabled and turn profiles into cpu->cfg flags like we do with every other extension. Unfortunately we've been using profile->enabled for lots of things and a redesign would be in order, one which I'm not comfortable doing right now - we're too close to the QEMU 11.2 code freeze for such a change. For now let's silver tape a fix in cpu_init() to reset profile->enabled for all available profiles. We'll do a proper redesign in the next release cycle. [1] https://lore.kernel.org/qemu-devel/87ld7rmdi6.fsf@pond.sub.org/ Reported-by: Markus Armbruster Signed-off-by: Daniel Henrique Barboza Acked-by: Alistair Francis Tested-by: Markus Armbruster Message-ID: <20261021173138.2388231-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 916038e8e4..c162d28033 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1550,6 +1550,25 @@ static void riscv_cpu_init(Object *obj) cpu->env.vext_ver = VEXT_VERSION_1_00_0; cpu->cfg.max_satp_mode = -1; + /* + * The way we're handling profiles does not cope well with QMP + * instropection. We have a static array that stores RISCVCPUProfiles + * objs that hold runtime state for the lifetime of the QEMU process. + * Thus setting profile->enabled once, e.g. when instropecting a + * profile CPU, will leave the flag set for subsequent intropections. + * + * For now, clear all 'enabled' flags to avoid misleading QMP w.r.t + * profiles being set. + * + * FIXME: a better long term solution is to remove profile->enabled + * and just use regular cpu->cfg flags, avoiding static variables + * storing dynamic state. This will help us not only with QMP but + * also single-binary support. + */ + for (int i = 0; riscv_profiles[i] != NULL; i++) { + riscv_profiles[i]->enabled = false; + } + if (mcc->def->profile) { mcc->def->profile->enabled = true; } From 11409db6c326da96869c0bf469028431aaef9d31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 20 Oct 2026 09:59:27 +0200 Subject: [PATCH 18/29] docs: Move RISC-V 'Z' CPU properties section to removed features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RISC-V 'Z' CPU properties were removed in commit 455c0fa9eef, for the release v10.1.0. Move the corresponding section from deprecated.rst to removed-features.rst, adjusting the wording accordingly. Fixes: 455c0fa9eef ("target/riscv: remove capital 'Z' CPU properties") Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-ID: <20261020075927.56983-1-philmd@oss.qualcomm.com> Signed-off-by: Alistair Francis --- docs/about/deprecated.rst | 24 ------------------------ docs/about/removed-features.rst | 7 +++++++ 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 153fe11815..dab47b6ca3 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -362,30 +362,6 @@ the addition of volatile memory support, it is now necessary to distinguish between persistent and volatile memory backends. As such, memdev is deprecated in favor of persistent-memdev. - -RISC-V CPU properties which start with capital 'Z' (since 8.2) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -All RISC-V CPU properties which start with capital 'Z' are being deprecated -starting in 8.2. The reason is that they were wrongly added with capital 'Z' -in the past. CPU properties were later added with lower-case names, which -is the format we want to use from now on. - -Users which try to use these deprecated properties will receive a warning -recommending to switch to their stable counterparts: - -- "Zifencei" should be replaced with "zifencei" -- "Zicsr" should be replaced with "zicsr" -- "Zihintntl" should be replaced with "zihintntl" -- "Zihintpause" should be replaced with "zihintpause" -- "Zawrs" should be replaced with "zawrs" -- "Zfa" should be replaced with "zfa" -- "Zfh" should be replaced with "zfh" -- "Zfhmin" should be replaced with "zfhmin" -- "Zve32f" should be replaced with "zve32f" -- "Zve64f" should be replaced with "zve64f" -- "Zve64d" should be replaced with "zve64d" - Block device options '''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 6a9944a0c7..225841bd9b 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1038,6 +1038,13 @@ initial RISC-V QEMU port. Its usage was always been unclear: users don't know what to expect from a CPU called 'any', and in fact the CPU does not do anything special that isn't already done by the default CPUs rv32/rv64. +RISC-V CPU properties which start with capital 'Z' (removed in 10.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +All RISC-V CPU properties which start with capital 'Z' have been removed. +They were wrongly added with capital 'Z' in the past. CPU properties +with lower-case names are used instead. + Power8E and Power8NVL CPUs (removed in 11.1) '''''''''''''''''''''''''''''''''''''''''''' From 14068a8dd81281ec26e07481c808563ff22a161d Mon Sep 17 00:00:00 2001 From: Rick Richard Date: Fri, 2 Oct 2026 06:47:58 -0700 Subject: [PATCH 19/29] target/riscv: Use TSO barriers instead of full barriers for Ztso With Ztso, plain loads and stores follow RVTSO, but the barriers around them use TCG_MO_ALL. That also orders a store before a later load, which TSO doesn't require, and is expensive: on x86 hosts it adds a locked instruction around every load and store in a multi-threaded guest. The barrier after a load now only keeps later loads and stores behind it, and the barrier before a store only keeps earlier loads and stores ahead of it. That is all TSO needs. Signed-off-by: Rick Richard Reviewed-by: Daniel Henrique Barboza Reviewed-by: Palmer Dabbelt Message-ID: Signed-off-by: Alistair Francis --- target/riscv/tcg/insn_trans/trans_rvi.c.inc | 4 ++-- target/riscv/tcg/insn_trans/trans_rvv.c.inc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/target/riscv/tcg/insn_trans/trans_rvi.c.inc b/target/riscv/tcg/insn_trans/trans_rvi.c.inc index cc1b5dbbad..4784ea1607 100644 --- a/target/riscv/tcg/insn_trans/trans_rvi.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvi.c.inc @@ -424,7 +424,7 @@ static bool gen_load(DisasContext *ctx, arg_lb *a, MemOp memop) } if (ctx->ztso) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + tcg_gen_mb(TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_BAR_LDAQ); } return out; @@ -485,7 +485,7 @@ static bool gen_store_tl(DisasContext *ctx, arg_sb *a, MemOp memop) TCGv data = get_gpr(ctx, a->rs2, EXT_NONE); if (ctx->ztso) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + tcg_gen_mb(TCG_MO_LD_ST | TCG_MO_ST_ST | TCG_BAR_STRL); } tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, memop); diff --git a/target/riscv/tcg/insn_trans/trans_rvv.c.inc b/target/riscv/tcg/insn_trans/trans_rvv.c.inc index 14ef961607..7e1295f33e 100644 --- a/target/riscv/tcg/insn_trans/trans_rvv.c.inc +++ b/target/riscv/tcg/insn_trans/trans_rvv.c.inc @@ -731,7 +731,7 @@ static bool ldst_us_trans(uint32_t vd, uint32_t rs1, uint32_t data, * around the loop. */ if (is_store && s->ztso) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + tcg_gen_mb(TCG_MO_LD_ST | TCG_MO_ST_ST | TCG_BAR_STRL); } mark_vs_dirty(s); @@ -739,7 +739,7 @@ static bool ldst_us_trans(uint32_t vd, uint32_t rs1, uint32_t data, fn(dest, mask, base, tcg_env, desc); if (!is_store && s->ztso) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + tcg_gen_mb(TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_BAR_LDAQ); } finalize_rvv_inst(s); From 8478697ac0e5b8c316ed791d4a5cea07264d1afc Mon Sep 17 00:00:00 2001 From: wangyang Date: Thu, 15 Oct 2026 08:05:45 +0000 Subject: [PATCH 20/29] target/riscv: make mnstatus.MNPELP writable with Zicfilp Zicfilp adds MNPELP to mnstatus, and MNRET consumes it when restoring ELP. Include the field in write_mnstatus() when Zicfilp is implemented. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4222 Reviewed-by: Alistair Francis Signed-off-by: wangyang Message-ID: <6AD0895A.0F1C22.05843@cstnet.cn> Signed-off-by: Alistair Francis --- target/riscv/tcg/csr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c index e6d25e5c4e..242f5caa0d 100644 --- a/target/riscv/tcg/csr.c +++ b/target/riscv/tcg/csr.c @@ -5766,6 +5766,10 @@ static RISCVException write_mnstatus(CPURISCVState *env, int csrno, mask |= MNSTATUS_MNPV; } + if (env_archcpu(env)->cfg.ext_zicfilp) { + mask |= MNSTATUS_MNPELP; + } + /* mnstatus.mnie can only be cleared by hardware. */ env->mnstatus = (env->mnstatus & MNSTATUS_NMIE) | (val & mask); return RISCV_EXCP_NONE; From e979f6a15552bdca6d26eacf98f41fc2aa9155f1 Mon Sep 17 00:00:00 2001 From: wangyang Date: Thu, 15 Oct 2026 08:05:51 +0000 Subject: [PATCH 21/29] target/riscv: gate envcfg CBO fields on extensions The envcfg CBO fields are provided by Zicbom and Zicboz. Keep them read-only zero when the corresponding extension is absent in all three envcfg CSRs. Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/4225 Reviewed-by: Alistair Francis Signed-off-by: wangyang Message-ID: <6AD08960.0F1C24.05843@cstnet.cn> Signed-off-by: Alistair Francis --- target/riscv/tcg/csr.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c index 242f5caa0d..13675b4a84 100644 --- a/target/riscv/tcg/csr.c +++ b/target/riscv/tcg/csr.c @@ -3388,10 +3388,19 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); - uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | - MENVCFG_CBZE; + uint64_t mask = MENVCFG_FIOM; bool stce_changed = false; + /* CBIE/CBCFE are read-only zero when Zicbom is not implemented. */ + if (cfg->ext_zicbom) { + mask |= MENVCFG_CBIE | MENVCFG_CBCFE; + } + + /* CBZE is read-only zero when Zicboz is not implemented. */ + if (cfg->ext_zicboz) { + mask |= MENVCFG_CBZE; + } + /* * menvcfg.LPE (Zicfilp) and menvcfg.SSE (Zicfiss) reside in the low * 32 bits and are defined for both RV32 and RV64, so they must be @@ -3492,8 +3501,18 @@ static RISCVException read_senvcfg(CPURISCVState *env, int csrno, static RISCVException write_senvcfg(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { - uint64_t mask = SENVCFG_FIOM | SENVCFG_CBIE | SENVCFG_CBCFE | SENVCFG_CBZE; + uint64_t mask = SENVCFG_FIOM; RISCVException ret; + + /* CBIE/CBCFE are read-only zero when Zicbom is not implemented. */ + if (env_archcpu(env)->cfg.ext_zicbom) { + mask |= SENVCFG_CBIE | SENVCFG_CBCFE; + } + + /* CBZE is read-only zero when Zicboz is not implemented. */ + if (env_archcpu(env)->cfg.ext_zicboz) { + mask |= SENVCFG_CBZE; + } /* Update PMM field only if the value is valid according to Zjpm v1.0 */ if (env_archcpu(env)->cfg.ext_ssnpm && riscv_cpu_mxl(env) == MXL_RV64 && @@ -3550,10 +3569,20 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); - uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE; + uint64_t mask = HENVCFG_FIOM; RISCVException ret; bool stce_changed = false; + /* CBIE/CBCFE are read-only zero when Zicbom is not implemented. */ + if (cfg->ext_zicbom) { + mask |= HENVCFG_CBIE | HENVCFG_CBCFE; + } + + /* CBZE is read-only zero when Zicboz is not implemented. */ + if (cfg->ext_zicboz) { + mask |= HENVCFG_CBZE; + } + ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG); if (ret != RISCV_EXCP_NONE) { return ret; From 387ed674f599fa728abe17225d8d183d69e7a144 Mon Sep 17 00:00:00 2001 From: Sebastian Alba Vives Date: Wed, 21 Oct 2026 22:02:57 -0600 Subject: [PATCH 22/29] target/riscv: pmp: clear A and L on every implemented PMP entry at reset pmp_unlock_entries() bounds its loop with pmp_get_num_rules(), which returns the number of entries whose A field is not OFF rather than the number of implemented entries. With a sparse configuration the loop stops early: with entries 0 and 1 OFF and entries 2 and 3 locked, num_rules is 2, the loop covers i = 0 and i = 1, and entries 2 and 3 keep their lock and address-match bits across reset. riscv_cpu_reset_hold() clears mseccfg, and with it RLB, before calling this, so pmp_is_readonly() then silently discards every subsequent pmpcfg write to those entries. The stale rules survive for the lifetime of the machine. Bound the loop by the number of implemented regions instead. Fixes: 4bf501dc0118 ("target/riscv: pmp: Clear pmp/smepmp bits on reset") Signed-off-by: Sebastian Alba Vives Reviewed-by: Alistair Francis Message-ID: <20261022040258.1509306-2-sebasjosue84@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/pmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/tcg/pmp.c b/target/riscv/tcg/pmp.c index 41b55519a8..061f0e078d 100644 --- a/target/riscv/tcg/pmp.c +++ b/target/riscv/tcg/pmp.c @@ -201,7 +201,7 @@ static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) void pmp_unlock_entries(CPURISCVState *env) { - uint32_t pmp_num = pmp_get_num_rules(env); + uint32_t pmp_num = riscv_cpu_cfg(env)->pmp_regions; int i; for (i = 0; i < pmp_num; i++) { From b35dc006ae07a7bf6709920c2f621d7512ea76d5 Mon Sep 17 00:00:00 2001 From: Sebastian Alba Vives Date: Wed, 21 Oct 2026 22:02:58 -0600 Subject: [PATCH 23/29] target/riscv: pmp: reset the derived PMP bounds and rule count The priv spec only requires the A and L fields to be reset, which the previous patch now does for every implemented entry. QEMU additionally keeps derived state next to the architectural registers: pmp_state.addr[] holds the decoded start and end address of each entry and pmp_state.num_rules caches the active rule count. Both are recomputed from the pmpaddr and pmpcfg write paths only, never at reset. pmp_is_in_range() compares against addr[].sa and addr[].ea without consulting the A field, and the partially-inside check in pmp_hart_has_privs() runs before the A field is tested, so a range left over from before the reset can still deny an access that straddles its boundary once the guest configures any entry again. Clear the derived state alongside the architectural fields so the two cannot disagree. Signed-off-by: Sebastian Alba Vives Reviewed-by: Alistair Francis Message-ID: <20261022040258.1509306-3-sebasjosue84@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/pmp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/riscv/tcg/pmp.c b/target/riscv/tcg/pmp.c index 061f0e078d..04df53c234 100644 --- a/target/riscv/tcg/pmp.c +++ b/target/riscv/tcg/pmp.c @@ -207,6 +207,14 @@ void pmp_unlock_entries(CPURISCVState *env) for (i = 0; i < pmp_num; i++) { env->pmp_state.pmp[i].cfg_reg &= ~(PMP_LOCK | PMP_AMATCH); } + + /* + * addr[] and num_rules are derived from the pmpaddr and pmpcfg write + * paths and are not recomputed at reset. Clear them here so they + * cannot disagree with the architectural fields cleared above. + */ + memset(env->pmp_state.addr, 0, sizeof(env->pmp_state.addr)); + env->pmp_state.num_rules = 0; } static void pmp_decode_napot(hwaddr a, hwaddr *sa, hwaddr *ea) From a6607aee5355d75e2c394899ad7bc45ae6bbe41b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 22 Oct 2026 21:10:37 +0100 Subject: [PATCH 24/29] target/riscv: Use 1ULL for a value we're going to shift by 63 In prop_marchid_set() we set mxlen to either 32 or 64, and then calculate invalid_val = 1LL << (mxlen - 1); which shifts the signed value 1LL up into its sign bit when mxlen is 64, which Coverity complains about. This is not UB for QEMU because we use -fwrapv, but it makes more sense to use an explicitly unsigned 1ULL here when we clearly want to be treating it as an unsigned value and the 'invalid_val' variable is of type uint64_t. Resolves: Coverity CID 1547552 Fixes: d6a427e2c0b24 ("target/riscv/cpu.c: restrict 'marchid' value") Signed-off-by: Peter Maydell Reviewed-by: Chao Liu Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20261022201037.764619-1-peter.maydell@linaro.org> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index c162d28033..5a0529fbda 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2317,7 +2317,7 @@ static void prop_marchid_set(Object *obj, Visitor *v, const char *name, g_assert_not_reached(); } - invalid_val = 1LL << (mxlen - 1); + invalid_val = 1ULL << (mxlen - 1); if (value == invalid_val) { error_setg(errp, "Unable to set marchid with MSB (%u) bit set " From dac0e54c96315200e7b7422e7f9b4bf4228d0a9d Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 22 Oct 2026 15:19:57 -0300 Subject: [PATCH 25/29] include/hw/intc/riscv_imsic.h: remove unused macro Found while reading IMSIC code. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Chao Liu Message-ID: <20261022181957.21760-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis --- include/hw/intc/riscv_imsic.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/hw/intc/riscv_imsic.h b/include/hw/intc/riscv_imsic.h index 0a1df1c2f8..c744836657 100644 --- a/include/hw/intc/riscv_imsic.h +++ b/include/hw/intc/riscv_imsic.h @@ -31,7 +31,6 @@ DECLARE_INSTANCE_CHECKER(RISCVIMSICState, RISCV_IMSIC, TYPE_RISCV_IMSIC) #define IMSIC_MMIO_PAGE_SZ (1UL << IMSIC_MMIO_PAGE_SHIFT) #define IMSIC_MMIO_SIZE(__num_pages) ((__num_pages) * IMSIC_MMIO_PAGE_SZ) -#define IMSIC_MMIO_HART_GUEST_MAX_BTIS 6 #define IMSIC_MMIO_GROUP_MIN_SHIFT 24 #define IMSIC_HART_NUM_GUESTS(__guest_bits) \ From 87c60331651d15f253f512de573177cc9a352e3d Mon Sep 17 00:00:00 2001 From: Zephyr Li Date: Thu, 22 Oct 2026 14:56:47 +0800 Subject: [PATCH 26/29] hw/riscv/riscv-iommu: encode debug translation ranges The debug translation response aligns the translated physical address to the effective translation range, but always reports S=0. For a request inside a superpage, the response therefore identifies the wrong 4 KiB physical page. For effective ranges larger than 4 KiB, set S and encode the range size using trailing ones in the PPN field. Keep the existing response format for 4 KiB translations. The RISC-V IOMMU specification permits reporting a 4 KiB translation even when the page tables use superpages. Reporting the effective range is an implementation choice; this change does not claim that S=1 is mandatory for every translation through a superpage mapping. Add debug-response tests for S-stage-only 4 KiB, 64 KiB NAPOT, and 2 MiB translations, G-stage-only 2 MiB translations, and nested S4K/G2M, S2M/G4K, and S2M/G2M mappings. Check requests inside large ranges and at both ends. The mixed nested cases verify that the response uses the smaller effective translation granularity. Fixes: a7aa525b93c3 ("hw/riscv/riscv-iommu: add DBG support") Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3552 Signed-off-by: Zephyr Li Reviewed-by: Daniel Henrique Barboza Message-ID: <20261022065647.67150-1-fritchleybohrer@gmail.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.c | 14 ++- tests/qtest/iommu-riscv-test.c | 193 +++++++++++++++++++++++++++++++++ 2 files changed, 206 insertions(+), 1 deletion(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 77b93fab5f..0e6970773a 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -2274,8 +2274,20 @@ static void riscv_iommu_process_dbg(RISCVIOMMUState *s) if (fault) { iova = RISCV_IOMMU_TR_RESPONSE_FAULT | (((uint64_t) fault) << 10); } else { + uint64_t ppn; + iova = iotlb.translated_addr & ~iotlb.addr_mask; - iova = set_field(0, RISCV_IOMMU_TR_RESPONSE_PPN, PPN_DOWN(iova)); + ppn = PPN_DOWN(iova); + + if (iotlb.addr_mask > TARGET_PAGE_SIZE - 1) { + /* Encode the translation range as trailing ones in the PPN. */ + ppn |= PPN_DOWN(iotlb.addr_mask) >> 1; + iova = RISCV_IOMMU_TR_RESPONSE_S; + } else { + iova = 0; + } + + iova = set_field(iova, RISCV_IOMMU_TR_RESPONSE_PPN, ppn); } riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE, iova); } diff --git a/tests/qtest/iommu-riscv-test.c b/tests/qtest/iommu-riscv-test.c index 2638024891..1cd753c4f2 100644 --- a/tests/qtest/iommu-riscv-test.c +++ b/tests/qtest/iommu-riscv-test.c @@ -19,6 +19,12 @@ #define DMA_LEN 4 +#define DBG_PAGE_SIZE 0x1000ull +#define DBG_NAPOT_SIZE 0x10000ull +#define DBG_SUPERPAGE_SIZE 0x200000ull +#define DBG_TRANSLATED_PA 0x90000000ull +#define DBG_NESTED_GPA 0x200000ull + /* RISC-V virt machine PCI configuration */ #define RISCV_GPEX_PIO_BASE 0x3000000 #define RISCV_BUS_PIO_LIMIT 0x10000 @@ -35,6 +41,64 @@ typedef struct RiscvIommuTestState { uint64_t iommu_base; } RiscvIommuTestState; +typedef struct RiscvIommuDbgTest { + const char *name; + QRIOMMUTransMode mode; + uint64_t s_page_size; + uint64_t g_page_size; + uint64_t pa; + uint64_t expected_response; +} RiscvIommuDbgTest; + +static const RiscvIommuDbgTest debug_tests[] = { + { + .name = "page-4k", + .mode = QRIOMMU_TM_S_STAGE_ONLY, + .s_page_size = DBG_PAGE_SIZE, + .pa = DBG_TRANSLATED_PA + DBG_PAGE_SIZE, + .expected_response = 0x24000400ull, + }, { + .name = "napot-64k", + .mode = QRIOMMU_TM_S_STAGE_ONLY, + .s_page_size = DBG_NAPOT_SIZE, + .pa = DBG_TRANSLATED_PA, + .expected_response = 0x24001e00ull, + }, { + .name = "superpage", + .mode = QRIOMMU_TM_S_STAGE_ONLY, + .s_page_size = DBG_SUPERPAGE_SIZE, + .pa = DBG_TRANSLATED_PA, + .expected_response = 0x2403fe00ull, + }, { + .name = "g-stage-superpage", + .mode = QRIOMMU_TM_G_STAGE_ONLY, + .g_page_size = DBG_SUPERPAGE_SIZE, + .pa = DBG_TRANSLATED_PA, + .expected_response = 0x2403fe00ull, + }, { + .name = "nested-s4k-g2m", + .mode = QRIOMMU_TM_NESTED, + .s_page_size = DBG_PAGE_SIZE, + .g_page_size = DBG_SUPERPAGE_SIZE, + .pa = DBG_TRANSLATED_PA, + .expected_response = 0x24001000ull, + }, { + .name = "nested-s2m-g4k", + .mode = QRIOMMU_TM_NESTED, + .s_page_size = DBG_SUPERPAGE_SIZE, + .g_page_size = DBG_PAGE_SIZE, + .pa = DBG_TRANSLATED_PA, + .expected_response = 0x24001000ull, + }, { + .name = "nested-s2m-g2m", + .mode = QRIOMMU_TM_NESTED, + .s_page_size = DBG_SUPERPAGE_SIZE, + .g_page_size = DBG_SUPERPAGE_SIZE, + .pa = DBG_TRANSLATED_PA, + .expected_response = 0x2403fe00ull, + }, +}; + static void riscv_config_qpci_bus(QGenericPCIBus *qpci) { qpci->gpex_pio_base = RISCV_GPEX_PIO_BASE; @@ -264,6 +328,129 @@ static void test_riscv_iommu_nested(void) run_riscv_iommu_translation(&cfg); } +static void riscv_iommu_dbg_map_leaf(QTestState *qts, uint64_t l1_pa, + uint64_t l2_pa, uint64_t va, uint64_t pa, + uint64_t page_size) +{ + uint64_t pte = ((pa >> 12) << 10) | QRIOMMU_LEAF_PTE_RW_MASK; + uint64_t pte_addr; + + g_assert_cmphex(pa & (page_size - 1), ==, 0); + + if (page_size == DBG_SUPERPAGE_SIZE) { + pte_addr = l1_pa + (((va >> 21) & 0x1ff) * sizeof(uint64_t)); + qtest_writeq(qts, pte_addr, pte); + } else if (page_size == DBG_NAPOT_SIZE) { + /* A 64 KiB NAPOT PTE has N=1 and PPN[3:0]=1000. */ + pte |= BIT_ULL(63) | (8ull << 10); + va &= ~(DBG_NAPOT_SIZE - 1); + for (unsigned i = 0; i < DBG_NAPOT_SIZE / DBG_PAGE_SIZE; i++) { + pte_addr = l2_pa + ((((va >> 12) + i) & 0x1ff) * + sizeof(uint64_t)); + qtest_writeq(qts, pte_addr, pte); + } + } else { + g_assert_cmphex(page_size, ==, DBG_PAGE_SIZE); + pte_addr = l2_pa + (((va >> 12) & 0x1ff) * sizeof(uint64_t)); + qtest_writeq(qts, pte_addr, pte); + } +} + +static uint64_t riscv_iommu_dbg_translate(RiscvIommuTestState *state, + uint64_t iova) +{ + uint64_t request; + + qtest_writeq(state->qts, + state->iommu_base + RISCV_IOMMU_REG_TR_REQ_IOVA, iova); + request = ((uint64_t)state->testdev->devfn << 40) | + RISCV_IOMMU_TR_REQ_CTL_GO_BUSY; + qtest_writeq(state->qts, + state->iommu_base + RISCV_IOMMU_REG_TR_REQ_CTL, request); + + request = qtest_readq(state->qts, + state->iommu_base + RISCV_IOMMU_REG_TR_REQ_CTL); + g_assert_cmpuint(request & RISCV_IOMMU_TR_REQ_CTL_GO_BUSY, ==, 0); + + return qtest_readq(state->qts, + state->iommu_base + RISCV_IOMMU_REG_TR_RESPONSE); +} + +static void test_riscv_iommu_dbg(gconstpointer data) +{ + const RiscvIommuDbgTest *cfg = data; + RiscvIommuTestState state = { 0 }; + uint64_t iova = QRIOMMU_IOVA & ~(DBG_PAGE_SIZE - 1); + uint64_t s_pa = cfg->pa; + uint64_t page_size = cfg->mode == QRIOMMU_TM_G_STAGE_ONLY ? + cfg->g_page_size : cfg->s_page_size; + uint64_t response; + + if (!riscv_iommu_test_setup(&state)) { + return; + } + + riscv_iommu_check(state.qts, state.iommu_base, cfg->mode); + qriommu_build_translation(state.qts, cfg->mode, state.testdev->devfn); + + if (cfg->mode == QRIOMMU_TM_NESTED) { + uint64_t g_l1_pa = QRIOMMU_SPACE_OFFS + QRIOMMU_G_L0_PTE_VAL; + uint64_t g_l2_pa = QRIOMMU_SPACE_OFFS + QRIOMMU_G_L1_PTE_VAL; + uint64_t offset = iova & (DBG_SUPERPAGE_SIZE - 1); + uint64_t gpa = DBG_NESTED_GPA + offset; + uint64_t pte_addr; + uint64_t pte; + + /* Map all S-stage page tables through one 2 MiB G-stage leaf. */ + riscv_iommu_dbg_map_leaf(state.qts, g_l1_pa, g_l2_pa, 0, + QRIOMMU_SPACE_OFFS, DBG_SUPERPAGE_SIZE); + + if (cfg->g_page_size == DBG_PAGE_SIZE) { + pte_addr = g_l1_pa + (((gpa >> 21) & 0x1ff) * sizeof(uint64_t)); + pte = ((g_l2_pa >> 12) << 10) | QRIOMMU_NON_LEAF_PTE_MASK; + qtest_writeq(state.qts, pte_addr, pte); + } + riscv_iommu_dbg_map_leaf(state.qts, g_l1_pa, g_l2_pa, gpa, + cfg->pa + (offset & ~(cfg->g_page_size - 1)), + cfg->g_page_size); + s_pa = gpa & ~(cfg->s_page_size - 1); + page_size = MIN(page_size, cfg->g_page_size); + } + + if (cfg->mode == QRIOMMU_TM_G_STAGE_ONLY) { + riscv_iommu_dbg_map_leaf(state.qts, + QRIOMMU_SPACE_OFFS + QRIOMMU_G_L0_PTE_VAL, + QRIOMMU_SPACE_OFFS + QRIOMMU_G_L1_PTE_VAL, + iova, cfg->pa, cfg->g_page_size); + } else { + riscv_iommu_dbg_map_leaf(state.qts, + QRIOMMU_SPACE_OFFS + QRIOMMU_L0_PTE_VAL, + QRIOMMU_SPACE_OFFS + QRIOMMU_L1_PTE_VAL, + iova, s_pa, cfg->s_page_size); + } + qriommu_program_regs(state.qts, state.iommu_base); + + /* + * The spec permits reporting a smaller range. These tests check QEMU's + * choice to report the effective range returned by an uncached walk. + * No DMA precedes these requests, and debug requests do not fill the IOTLB. + */ + response = riscv_iommu_dbg_translate(&state, iova); + g_assert_cmphex(response, ==, cfg->expected_response); + + if (page_size > DBG_PAGE_SIZE) { + /* Check both ends as well as the interior of the reported range. */ + iova &= ~(page_size - 1); + response = riscv_iommu_dbg_translate(&state, iova); + g_assert_cmphex(response, ==, cfg->expected_response); + response = riscv_iommu_dbg_translate(&state, + iova + page_size - DBG_PAGE_SIZE); + g_assert_cmphex(response, ==, cfg->expected_response); + } + + riscv_iommu_test_teardown(&state); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -275,5 +462,11 @@ int main(int argc, char **argv) test_riscv_iommu_g_stage_only); qtest_add_func("/iommu-testdev/translation/ns-nested", test_riscv_iommu_nested); + for (unsigned i = 0; i < G_N_ELEMENTS(debug_tests); i++) { + g_autofree char *path = g_strdup_printf("/iommu-testdev/debug/%s", + debug_tests[i].name); + + qtest_add_data_func(path, &debug_tests[i], test_riscv_iommu_dbg); + } return g_test_run(); } From 07cea261f0ce30c5f6881a8ea529f2c9f9d29006 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Sat, 24 Oct 2026 03:59:30 -0300 Subject: [PATCH 27/29] target/riscv/csr.c: allow regular mstatus MPV/GVA writes again In commit 18645f195789 we removed the capability of MPV/GVA writes by software, claiming that the ISA says that these bits are changed by the implementation (i.e. hardware). This fact alone can't be interpret as "so software can't change them". The ISA never mentions that, and we shouldn't just assume read/write accesses based on whether hardware updates it or not. M-mode software can do whatever they want, unless clearly stated in the ISA, and that includes mstatus MPV and GVA updates. And in fact existing software is writing to these bits, as reported by Fangyu in [1], and that change ended up causing real problems in the end. Revert "target/riscv/csr.c: do not allow mstatus MPV/GVA writes". This reverts commit 18645f19578955ec5ff2c40cd2c8753d6bc460c2. [1] https://lore.kernel.org/qemu-devel/20261022120111.24298-1-fangyu.yu@linux.alibaba.com/ Reported-by: Fangyu Yu Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20261024065930.26750-1-daniel.barboza@oss.qualcomm.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/csr.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/target/riscv/tcg/csr.c b/target/riscv/tcg/csr.c index 13675b4a84..af444e3e6f 100644 --- a/target/riscv/tcg/csr.c +++ b/target/riscv/tcg/csr.c @@ -2108,6 +2108,9 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno, } if (xl != MXL_RV32 || env->debugger) { + if (riscv_has_ext(env, RVH)) { + mask |= MSTATUS_MPV | MSTATUS_GVA; + } if ((val & MSTATUS64_SXL) != 0) { mask |= MSTATUS64_SXL; val = riscv_write_uxl(env, val, MSTATUS64_SXL); @@ -2150,7 +2153,7 @@ static RISCVException write_mstatush(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { uint64_t valh = (uint64_t)val << 32; - uint64_t mask = 0; + uint64_t mask = riscv_has_ext(env, RVH) ? MSTATUS_MPV | MSTATUS_GVA : 0; if (riscv_cpu_cfg(env)->ext_smdbltrp) { mask |= MSTATUS_MDT; From 9164d533ecaebd9d07f61e5fc7026cc9afa80e92 Mon Sep 17 00:00:00 2001 From: Harshit Mukhedkar Date: Fri, 23 Oct 2026 15:05:22 +0530 Subject: [PATCH 28/29] target/riscv: fix uninitialised upper half in pmu_timer_trigger_irq() pmu_timer_trigger_irq() reads the counter by passing (target_ulong *)&curr_ctr_val to riscv_pmu_read_ctr(). On RV32 that pointer is to a 32 bit type, so only half of the uint64_t gets written and the rest is whatever was on the stack. The high word read from riscv_pmu_read_ctr() is then ORed in on top of that, and the result is compared against the software written counter to decide whether to raise LCOFI. A stray bit in the garbage half can make that comparison wrong. Read the low and high halves into their own target_ulong variables and assemble the 64-bit value explicitly. This was found while looking at #4724, whose reported mhpmcounterh mismatch is fixed separately by 57b484e2d232. Fixes: 74112400df65 ("target/riscv: More accurately model priv mode filtering.") Cc: qemu-stable@nongnu.org Signed-off-by: Harshit Mukhedkar Reviewed-by: Alistair Francis Message-ID: <20261023093522.34335-1-harshit007k@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/pmu.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/target/riscv/tcg/pmu.c b/target/riscv/tcg/pmu.c index 1a4658319b..0165faf06b 100644 --- a/target/riscv/tcg/pmu.c +++ b/target/riscv/tcg/pmu.c @@ -355,7 +355,8 @@ static void pmu_timer_trigger_irq(RISCVCPU *cpu, CPURISCVState *env = &cpu->env; PMUCTRState *counter; int64_t irq_trigger_at; - uint64_t curr_ctr_val, curr_ctrh_val; + target_ulong ctr_lo = 0, ctr_hi = 0; + uint64_t curr_ctr_val; uint64_t ctr_val; if (evt_idx != RISCV_PMU_EVENT_HW_CPU_CYCLES && @@ -383,12 +384,19 @@ static void pmu_timer_trigger_irq(RISCVCPU *cpu, return; } - riscv_pmu_read_ctr(env, (target_ulong *)&curr_ctr_val, false, ctr_idx); - ctr_val = counter->mhpmcounter_val; + /* + * riscv_pmu_read_ctr() stores a target_ulong, so on RV32 each call fills + * only 32 bits. Read each half into its own target_ulong and build the + * 64 bit value explicitly instead of passing a cast uint64_t pointer, + * which would leave the upper half uninitialised. + */ + riscv_pmu_read_ctr(env, &ctr_lo, false, ctr_idx); + curr_ctr_val = ctr_lo; if (riscv_cpu_mxl(env) == MXL_RV32) { - riscv_pmu_read_ctr(env, (target_ulong *)&curr_ctrh_val, true, ctr_idx); - curr_ctr_val = curr_ctr_val | (curr_ctrh_val << 32); + riscv_pmu_read_ctr(env, &ctr_hi, true, ctr_idx); + curr_ctr_val |= (uint64_t)ctr_hi << 32; } + ctr_val = counter->mhpmcounter_val; /* * We can not accommodate for inhibited modes when setting up timer. Check From bf67d56f282c1ee25b67dfda3f8ca54d2bd4648e Mon Sep 17 00:00:00 2001 From: Anton Johansson Date: Fri, 23 Oct 2026 08:30:25 +0200 Subject: [PATCH 29/29] target/riscv: Fix CPU state access on big-endian hosts mstatus and mstatus_hs are uint64_t but accessed through tcg_gen_[ld|st]_tl(..., offsetof(mstatus)) which on a big-endian host running riscv32 incorrectly accesses the top 32 bits rather than the lower bits. Widen the operations to 64 bit so the lower bits are modified. Fixes: 284d697c74ef ("target/riscv: Merge m/vsstatus and m/vsstatush into one uint64_t unit") Reviewed-by: Tommaso Califano Signed-off-by: Anton Johansson Reviewed-by: Alistair Francis Message-ID: <20261023063025.8827-1-anjo@rev.ng> Signed-off-by: Alistair Francis --- target/riscv/tcg/translate.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/target/riscv/tcg/translate.c b/target/riscv/tcg/translate.c index ede9c2563f..79b0f444ef 100644 --- a/target/riscv/tcg/translate.c +++ b/target/riscv/tcg/translate.c @@ -693,7 +693,7 @@ static TCGv get_address_indexed(DisasContext *ctx, int rs1, TCGv offs) */ static void mark_fs_dirty(DisasContext *ctx) { - TCGv tmp; + TCGv_i64 tmp; if (!has_ext(ctx, RVF)) { return; @@ -703,15 +703,15 @@ static void mark_fs_dirty(DisasContext *ctx) /* Remember the state change for the rest of the TB. */ ctx->mstatus_fs = EXT_STATUS_DIRTY; - tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); - tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); - tcg_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); + tmp = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); + tcg_gen_ori_i64(tmp, tmp, MSTATUS_FS); + tcg_gen_st_i64(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); if (ctx->virt_enabled) { - tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); - tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); - tcg_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); + tcg_gen_ori_i64(tmp, tmp, MSTATUS_FS); + tcg_gen_st_i64(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); } } } @@ -726,21 +726,21 @@ static inline void mark_fs_dirty(DisasContext *ctx) { } */ static void mark_vs_dirty(DisasContext *ctx) { - TCGv tmp; + TCGv_i64 tmp; if (ctx->mstatus_vs != EXT_STATUS_DIRTY) { /* Remember the state change for the rest of the TB. */ ctx->mstatus_vs = EXT_STATUS_DIRTY; - tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); - tcg_gen_ori_tl(tmp, tmp, MSTATUS_VS); - tcg_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); + tmp = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); + tcg_gen_ori_i64(tmp, tmp, MSTATUS_VS); + tcg_gen_st_i64(tmp, tcg_env, offsetof(CPURISCVState, mstatus)); if (ctx->virt_enabled) { - tcg_gen_ld_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); - tcg_gen_ori_tl(tmp, tmp, MSTATUS_VS); - tcg_gen_st_tl(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); + tcg_gen_ld_i64(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); + tcg_gen_ori_i64(tmp, tmp, MSTATUS_VS); + tcg_gen_st_i64(tmp, tcg_env, offsetof(CPURISCVState, mstatus_hs)); } } }