Reviewed-by: Glenn Miles <mil...@linux.ibm.com> Thanks,
Glenn On Tue, 2024-05-21 at 11:30 +1000, Nicholas Piggin wrote: > This implements the POWER SPRC/SPRD SPRs, and SCRATCH0-7 registers > that > can be accessed via these indirect SPRs. > > SCRATCH registers only provide storage, but they are used by firmware > for low level crash and progress data, so this implementation logs > writes to the registers to help with analysis. > > Signed-off-by: Nicholas Piggin <npig...@gmail.com> > --- > target/ppc/cpu.h | 7 +++-- > target/ppc/helper.h | 3 ++ > target/ppc/spr_common.h | 3 ++ > target/ppc/cpu_init.c | 10 ++++++ > target/ppc/misc_helper.c | 66 > ++++++++++++++++++++++++++++++++++++++++ > target/ppc/translate.c | 18 +++++++++++ > 6 files changed, 105 insertions(+), 2 deletions(-) > > diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h > index 823be85d03..e4c342b17d 100644 > --- a/target/ppc/cpu.h > +++ b/target/ppc/cpu.h > @@ -1264,6 +1264,9 @@ struct CPUArchState { > ppc_slb_t slb[MAX_SLB_ENTRIES]; /* PowerPC 64 SLB area */ > struct CPUBreakpoint *ciabr_breakpoint; > struct CPUWatchpoint *dawr0_watchpoint; > + > + /* POWER CPU regs/state */ > + target_ulong scratch[8]; /* SCRATCH registers (shared across > core) */ > #endif > target_ulong sr[32]; /* segment registers */ > uint32_t nb_BATs; /* number of BATs */ > @@ -1806,9 +1809,9 @@ void ppc_compat_add_property(Object *obj, const > char *name, > #define SPR_SPRG2 (0x112) > #define SPR_SPRG3 (0x113) > #define SPR_SPRG4 (0x114) > -#define SPR_SCOMC (0x114) > +#define SPR_POWER_SPRC (0x114) > #define SPR_SPRG5 (0x115) > -#define SPR_SCOMD (0x115) > +#define SPR_POWER_SPRD (0x115) > #define SPR_SPRG6 (0x116) > #define SPR_SPRG7 (0x117) > #define SPR_ASR (0x118) > diff --git a/target/ppc/helper.h b/target/ppc/helper.h > index 09d50f9b76..57bf8354e7 100644 > --- a/target/ppc/helper.h > +++ b/target/ppc/helper.h > @@ -730,6 +730,9 @@ DEF_HELPER_2(book3s_msgsndp, void, env, tl) > DEF_HELPER_2(book3s_msgclrp, void, env, tl) > DEF_HELPER_1(load_tfmr, tl, env) > DEF_HELPER_2(store_tfmr, void, env, tl) > +DEF_HELPER_FLAGS_2(store_sprc, TCG_CALL_NO_RWG, void, env, tl) > +DEF_HELPER_FLAGS_1(load_sprd, TCG_CALL_NO_RWG_SE, tl, env) > +DEF_HELPER_FLAGS_2(store_sprd, TCG_CALL_NO_RWG, void, env, tl) > #endif > DEF_HELPER_2(store_sdr1, void, env, tl) > DEF_HELPER_2(store_pidr, void, env, tl) > diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h > index 85f73b860b..01aff449bc 100644 > --- a/target/ppc/spr_common.h > +++ b/target/ppc/spr_common.h > @@ -207,6 +207,9 @@ void spr_write_lpcr(DisasContext *ctx, int sprn, > int gprn); > void spr_read_dexcr_ureg(DisasContext *ctx, int gprn, int sprn); > void spr_read_ppr32(DisasContext *ctx, int sprn, int gprn); > void spr_write_ppr32(DisasContext *ctx, int sprn, int gprn); > +void spr_write_sprc(DisasContext *ctx, int sprn, int gprn); > +void spr_read_sprd(DisasContext *ctx, int sprn, int gprn); > +void spr_write_sprd(DisasContext *ctx, int sprn, int gprn); > #endif > > void register_low_BATs(CPUPPCState *env); > diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c > index 7f2f8e5a4a..f21dbcfefb 100644 > --- a/target/ppc/cpu_init.c > +++ b/target/ppc/cpu_init.c > @@ -5794,6 +5794,16 @@ static void > register_power_common_book4_sprs(CPUPPCState *env) > SPR_NOACCESS, SPR_NOACCESS, > &spr_read_generic, &spr_core_write_generic, > 0x00000000); > + spr_register_hv(env, SPR_POWER_SPRC, "SPRC", > + SPR_NOACCESS, SPR_NOACCESS, > + SPR_NOACCESS, SPR_NOACCESS, > + &spr_read_generic, &spr_write_sprc, > + 0x00000000); > + spr_register_hv(env, SPR_POWER_SPRD, "SPRD", > + SPR_NOACCESS, SPR_NOACCESS, > + SPR_NOACCESS, SPR_NOACCESS, > + &spr_read_sprd, &spr_write_sprd, > + 0x00000000); > #endif > } > > diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c > index a67930d031..fa47be2298 100644 > --- a/target/ppc/misc_helper.c > +++ b/target/ppc/misc_helper.c > @@ -307,6 +307,72 @@ void helper_store_dpdes(CPUPPCState *env, > target_ulong val) > } > bql_unlock(); > } > + > +/* Indirect SCOM (SPRC/SPRD) access to SCRATCH0-7 are implemented. > */ > +void helper_store_sprc(CPUPPCState *env, target_ulong val) > +{ > + if (val & ~0x3f8ULL) { > + qemu_log_mask(LOG_GUEST_ERROR, "Invalid SPRC register value > " > + TARGET_FMT_lx"\n", val); > + return; > + } > + env->spr[SPR_POWER_SPRC] = val; > +} > + > +target_ulong helper_load_sprd(CPUPPCState *env) > +{ > + target_ulong sprc = env->spr[SPR_POWER_SPRC]; > + > + switch (sprc & 0x3c0) { > + case 0: /* SCRATCH0-7 */ > + return env->scratch[(sprc >> 3) & 0x7]; > + default: > + qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" > + TARGET_FMT_lx"\n", sprc); > + break; > + } > + return 0; > +} > + > +static void do_store_scratch(CPUPPCState *env, int nr, target_ulong > val) > +{ > + CPUState *cs = env_cpu(env); > + CPUState *ccs; > + uint32_t nr_threads = cs->nr_threads; > + > + /* > + * Log stores to SCRATCH, because some firmware uses these for > debugging > + * and logging, but they would normally be read by the BMC, > which is > + * not implemented in QEMU yet. This gives a way to get at the > information. > + * Could also dump these upon checkstop. > + */ > + qemu_log("SPRD write 0x" TARGET_FMT_lx " to SCRATCH%d\n", val, > nr); > + > + if (nr_threads == 1) { > + env->scratch[nr] = val; > + return; > + } > + > + THREAD_SIBLING_FOREACH(cs, ccs) { > + CPUPPCState *cenv = &POWERPC_CPU(ccs)->env; > + cenv->scratch[nr] = val; > + } > +} > + > +void helper_store_sprd(CPUPPCState *env, target_ulong val) > +{ > + target_ulong sprc = env->spr[SPR_POWER_SPRC]; > + > + switch (sprc & 0x3c0) { > + case 0: /* SCRATCH0-7 */ > + do_store_scratch(env, (sprc >> 3) & 0x7, val); > + break; > + default: > + qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" > + TARGET_FMT_lx"\n", sprc); > + break; > + } > +} > #endif /* defined(TARGET_PPC64) */ > > void helper_store_pidr(CPUPPCState *env, target_ulong val) > diff --git a/target/ppc/translate.c b/target/ppc/translate.c > index 76f829ad12..ab11e48e3f 100644 > --- a/target/ppc/translate.c > +++ b/target/ppc/translate.c > @@ -1363,6 +1363,24 @@ void spr_write_tfmr(DisasContext *ctx, int > sprn, int gprn) > gen_helper_store_tfmr(tcg_env, cpu_gpr[gprn]); > } > > +void spr_write_sprc(DisasContext *ctx, int sprn, int gprn) > +{ > + gen_helper_store_sprc(tcg_env, cpu_gpr[gprn]); > +} > + > +void spr_read_sprd(DisasContext *ctx, int gprn, int sprn) > +{ > + gen_helper_load_sprd(cpu_gpr[gprn], tcg_env); > +} > + > +void spr_write_sprd(DisasContext *ctx, int sprn, int gprn) > +{ > + if (!gen_serialize_core(ctx)) { > + return; > + } > + gen_helper_store_sprd(tcg_env, cpu_gpr[gprn]); > +} > + > void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn) > { > translator_io_start(&ctx->base);