On Thu, Apr 11, 2024, 10:15 PM Richard Henderson < richard.hender...@linaro.org> wrote:
> Reads are done with execute access. It is not clear whether writes > are legal at all -- for now, leave helper_st_asi unchanged, so that > we continue to raise an mmu fault. > > This generalizes the exiting code for ASI_KERNELTXT to be usable for > ASI_USERTXT as well, by passing down the MemOpIdx to use. > > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2281 > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2059 > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1609 > Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1166 > Signed-off-by: Richard Henderson <richard.hender...@linaro.org> > --- > target/sparc/helper.h | 3 ++ > target/sparc/ldst_helper.c | 65 ++++++++++++++++++++++++++------------ > target/sparc/translate.c | 49 ++++++++++++++++++++++++++-- > 3 files changed, 95 insertions(+), 22 deletions(-) > > diff --git a/target/sparc/helper.h b/target/sparc/helper.h > index e55fad5b8c..b8087d0d2b 100644 > --- a/target/sparc/helper.h > +++ b/target/sparc/helper.h > @@ -32,6 +32,9 @@ DEF_HELPER_FLAGS_3(udiv, TCG_CALL_NO_WG, i64, env, tl, > tl) > DEF_HELPER_FLAGS_3(sdiv, TCG_CALL_NO_WG, i64, env, tl, tl) > DEF_HELPER_3(taddcctv, tl, env, tl, tl) > DEF_HELPER_3(tsubcctv, tl, env, tl, tl) > +#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) > +DEF_HELPER_FLAGS_3(ld_code, TCG_CALL_NO_WG, i64, env, tl, i32) > +#endif > #if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) > DEF_HELPER_FLAGS_4(ld_asi, TCG_CALL_NO_WG, i64, env, tl, int, i32) > DEF_HELPER_FLAGS_5(st_asi, TCG_CALL_NO_WG, void, env, tl, i64, int, i32) > diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c > index e581bb42ac..2846a86cc4 100644 > --- a/target/sparc/ldst_helper.c > +++ b/target/sparc/ldst_helper.c > @@ -585,7 +585,6 @@ uint64_t helper_ld_asi(CPUSPARCState *env, > target_ulong addr, > #if defined(DEBUG_MXCC) || defined(DEBUG_ASI) > uint32_t last_addr = addr; > #endif > - MemOpIdx oi; > > do_check_align(env, addr, size - 1, GETPC()); > switch (asi) { > @@ -684,24 +683,6 @@ uint64_t helper_ld_asi(CPUSPARCState *env, > target_ulong addr, > case ASI_M_DIAGS: /* Turbosparc DTLB Diagnostic */ > case ASI_M_IODIAG: /* Turbosparc IOTLB Diagnostic */ > break; > - case ASI_KERNELTXT: /* Supervisor code access */ > - oi = make_memop_idx(memop, cpu_mmu_index(env_cpu(env), true)); > - switch (size) { > - case 1: > - ret = cpu_ldb_code_mmu(env, addr, oi, GETPC()); > - break; > - case 2: > - ret = cpu_ldw_code_mmu(env, addr, oi, GETPC()); > - break; > - default: > - case 4: > - ret = cpu_ldl_code_mmu(env, addr, oi, GETPC()); > - break; > - case 8: > - ret = cpu_ldq_code_mmu(env, addr, oi, GETPC()); > - break; > - } > - break; > case ASI_M_TXTC_TAG: /* SparcStation 5 I-cache tag */ > case ASI_M_TXTC_DATA: /* SparcStation 5 I-cache data */ > case ASI_M_DATAC_TAG: /* SparcStation 5 D-cache tag */ > @@ -779,7 +760,6 @@ uint64_t helper_ld_asi(CPUSPARCState *env, > target_ulong addr, > case 0x4c: /* SuperSPARC MMU Breakpoint Action */ > ret = env->mmubpaction; > break; > - case ASI_USERTXT: /* User code access, XXX */ > default: > sparc_raise_mmu_fault(cs, addr, false, false, asi, size, GETPC()); > ret = 0; > @@ -787,6 +767,8 @@ uint64_t helper_ld_asi(CPUSPARCState *env, > target_ulong addr, > > case ASI_USERDATA: /* User data access */ > case ASI_KERNELDATA: /* Supervisor data access */ > + case ASI_USERTXT: /* User code access */ > + case ASI_KERNELTXT: /* Supervisor code access */ > case ASI_P: /* Implicit primary context data access (v9 only?) */ > case ASI_M_BYPASS: /* MMU passthrough */ > case ASI_LEON_BYPASS: /* LEON MMU passthrough */ > @@ -1161,6 +1143,49 @@ void helper_st_asi(CPUSPARCState *env, target_ulong > addr, uint64_t val, > #endif > } > > +uint64_t helper_ld_code(CPUSPARCState *env, target_ulong addr, uint32_t > oi) > +{ > + MemOp mop = get_memop(oi); > + uintptr_t ra = GETPC(); > + uint64_t ret; > + > + switch (mop & MO_SIZE) { > + case MO_8: > + ret = cpu_ldb_code_mmu(env, addr, oi, ra); > + if (mop & MO_SIGN) { > + ret = (int8_t)ret; > + } > + break; > + case MO_16: > + ret = cpu_ldw_code_mmu(env, addr, oi, ra); > + if ((mop & MO_BSWAP) != MO_TE) { > + ret = bswap16(ret); > + } > + if (mop & MO_SIGN) { > + ret = (int16_t)ret; > + } > + break; > + case MO_32: > + ret = cpu_ldl_code_mmu(env, addr, oi, ra); > + if ((mop & MO_BSWAP) != MO_TE) { > + ret = bswap32(ret); > + } > + if (mop & MO_SIGN) { > + ret = (int32_t)ret; > + } > + break; > + case MO_64: > + ret = cpu_ldq_code_mmu(env, addr, oi, ra); > + if ((mop & MO_BSWAP) != MO_TE) { > + ret = bswap64(ret); > + } > + break; > + default: > + g_assert_not_reached(); > + } > + return ret; > +} > + > #endif /* CONFIG_USER_ONLY */ > #else /* TARGET_SPARC64 */ > > diff --git a/target/sparc/translate.c b/target/sparc/translate.c > index 319934d9bd..c9b9b047df 100644 > --- a/target/sparc/translate.c > +++ b/target/sparc/translate.c > @@ -1117,6 +1117,7 @@ typedef enum { > GET_ASI_EXCP, > GET_ASI_DIRECT, > GET_ASI_DTWINX, > + GET_ASI_CODE, > GET_ASI_BLOCK, > GET_ASI_SHORT, > GET_ASI_BCOPY, > @@ -1159,14 +1160,22 @@ static DisasASI resolve_asi(DisasContext *dc, int > asi, MemOp memop) > || (asi == ASI_USERDATA > && (dc->def->features & CPU_FEATURE_CASA))) { > switch (asi) { > - case ASI_USERDATA: /* User data access */ > + case ASI_USERDATA: /* User data access */ > mem_idx = MMU_USER_IDX; > type = GET_ASI_DIRECT; > break; > - case ASI_KERNELDATA: /* Supervisor data access */ > + case ASI_KERNELDATA: /* Supervisor data access */ > mem_idx = MMU_KERNEL_IDX; > type = GET_ASI_DIRECT; > break; > + case ASI_USERTXT: /* User text access */ > + mem_idx = MMU_USER_IDX; > + type = GET_ASI_CODE; > + break; > + case ASI_KERNELTXT: /* Supervisor text access */ > + mem_idx = MMU_KERNEL_IDX; > + type = GET_ASI_CODE; > + break; > case ASI_M_BYPASS: /* MMU passthrough */ > case ASI_LEON_BYPASS: /* LEON MMU passthrough */ > mem_idx = MMU_PHYS_IDX; > @@ -1379,6 +1388,22 @@ static void gen_ld_asi(DisasContext *dc, DisasASI > *da, TCGv dst, TCGv addr) > case GET_ASI_DIRECT: > tcg_gen_qemu_ld_tl(dst, addr, da->mem_idx, da->memop | MO_ALIGN); > break; > + > + case GET_ASI_CODE: > +#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) > + { > + MemOpIdx oi = make_memop_idx(da->memop, da->mem_idx); > + TCGv_i32 r_oi = tcg_constant_i32(oi); > + TCGv_i64 t64 = tcg_temp_new_i64(); > + > + gen_helper_ld_code(t64, tcg_env, addr, r_oi); > + tcg_gen_trunc_i64_tl(dst, t64); > + } > + break; > +#else > + g_assert_not_reached(); > +#endif > + > default: > { > TCGv_i32 r_asi = tcg_constant_i32(da->asi); > @@ -1791,6 +1816,26 @@ static void gen_ldda_asi(DisasContext *dc, DisasASI > *da, TCGv addr, int rd) > } > break; > > + case GET_ASI_CODE: > +#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) > + { > + TCGv_i64 tmp = tcg_temp_new_i64(); > + MemOpIdx oi = make_memop_idx(da->memop, da->mem_idx); > + > + gen_helper_ld_code(tmp, tcg_env, addr, tcg_constant_i32(oi)); > + > + /* See above. */ > + if ((da->memop & MO_BSWAP) == MO_TE) { > + tcg_gen_extr_i64_tl(lo, hi, tmp); > + } else { > + tcg_gen_extr_i64_tl(hi, lo, tmp); > + } > + } > + break; > +#else > + g_assert_not_reached(); > +#endif > + > default: > /* ??? In theory we've handled all of the ASIs that are valid > for ldda, and this should raise DAE_invalid_asi. However, > -- > 2.34.1 > Hi Richard, I see this is in your hands now. I agree with your take on leaving writes alone. I'm also grateful for the opportunity to collaborate with you. It brings a smile for the community members who will be touched by this amazing contribution. I see them happily realizing that this perplexing bug has been solved, and in our case finally able to use the debuggers we love! :D Thanks for the proper fix, qemu sensei! -bazz >