@@ -2092,6 +2092,7 @@ void ppc_compat_add_property(Object *obj, const char *name,
#define SPR_VTB (0x351)
#define SPR_LDBAR (0x352)
#define SPR_MMCRC (0x353)
+#define SPR_PMSR (0x355)
#define SPR_PSSCR (0x357)
#define SPR_440_INV0 (0x370)
#define SPR_440_INV1 (0x371)
@@ -2099,6 +2100,7 @@ void ppc_compat_add_property(Object *obj, const char *name,
#define SPR_440_INV2 (0x372)
#define SPR_TRIG2 (0x372)
#define SPR_440_INV3 (0x373)
+#define SPR_PMCR (0x374)
#define SPR_440_ITV0 (0x374)
#define SPR_440_ITV1 (0x375)
#define SPR_440_ITV2 (0x376)
@@ -736,6 +736,8 @@ 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)
+DEF_HELPER_FLAGS_1(load_pmsr, TCG_CALL_NO_RWG_SE, tl, env)
+DEF_HELPER_FLAGS_2(store_pmcr, TCG_CALL_NO_RWG, void, env, tl)
#endif
DEF_HELPER_2(store_sdr1, void, env, tl)
DEF_HELPER_2(store_pidr, void, env, tl)
@@ -205,6 +205,8 @@ void spr_write_hmer(DisasContext *ctx, int sprn, int gprn);
void spr_read_tfmr(DisasContext *ctx, int gprn, int sprn);
void spr_write_tfmr(DisasContext *ctx, int sprn, int gprn);
void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn);
+void spr_read_pmsr(DisasContext *ctx, int gprn, int sprn);
+void spr_write_pmcr(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);
@@ -6468,6 +6468,17 @@ static void register_power9_common_sprs(CPUPPCState *env)
spr_read_generic, spr_write_generic,
KVM_REG_PPC_PSSCR, 0);
+ spr_register_hv(env, SPR_PMSR, "PMSR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_pmsr, SPR_NOACCESS,
+ 0);
+ spr_register_hv(env, SPR_PMCR, "PMCR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_pmcr,
+ PPC_BIT(63)); /* Version 1 (POWER9/10) */
+
}
static void init_proc_POWER9(CPUPPCState *env)
@@ -459,6 +459,59 @@ void helper_store_sprd(CPUPPCState *env, target_ulong val)
break;
}
}
+
+target_ulong helper_load_pmsr(CPUPPCState *env)
+{
+ target_ulong lowerps = extract64(env->spr[SPR_PMCR], PPC_BIT_NR(15), 8);
+ target_ulong val = 0;
+
+ val |= PPC_BIT(63); /* verion 0x1 (POWER9/10) */
+ /* Pmin = 0 */
+ /* XXX: POWER9 should be 3 */
+ val |= 4ULL << PPC_BIT_NR(31); /* Pmax */
+ val |= lowerps << PPC_BIT_NR(15); /* Local actual Pstate */
+ val |= lowerps << PPC_BIT_NR(7); /* Global actual Pstate */
+
+ return val;
+}
+
+static void ppc_set_pmcr(PowerPCCPU *cpu, target_ulong val)
+{
+ cpu->env.spr[SPR_PMCR] = val;
+}
+
+void helper_store_pmcr(CPUPPCState *env, target_ulong val)
+{
+ PowerPCCPU *cpu = env_archcpu(env);
+ CPUState *cs = env_cpu(env);
+ CPUState *ccs;
+
+ /* Leave version field unchanged (0x1) */
+ val &= ~PPC_BITMASK(60, 63);
+ val |= PPC_BIT(63);
+
+ val &= ~PPC_BITMASK(0, 7); /* UpperPS ignored */
+ if (val & PPC_BITMASK(16, 59)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Non-zero PMCR reserved bits "
+ TARGET_FMT_lx"\n", val);
+ val &= ~PPC_BITMASK(16, 59);
+ }
+
+ /* DPDES behaves as 1-thread in LPAR-per-thread mode */
+ if (ppc_cpu_lpar_single_threaded(cs)) {
+ ppc_set_pmcr(cpu, val);
+ return;
+ }
+
+ /* Does iothread need to be locked for walking CPU list? */
+ bql_lock();
+ THREAD_SIBLING_FOREACH(cs, ccs) {
+ PowerPCCPU *ccpu = POWERPC_CPU(ccs);
+ ppc_set_pmcr(ccpu, val);
+ }
+ bql_unlock();
+}
+
#endif /* defined(TARGET_PPC64) */
void helper_store_pidr(CPUPPCState *env, target_ulong val)
@@ -1288,6 +1288,22 @@ void spr_write_lpcr(DisasContext *ctx, int sprn, int gprn)
translator_io_start(&ctx->base);
gen_helper_store_lpcr(tcg_env, cpu_gpr[gprn]);
}
+
+void spr_read_pmsr(DisasContext *ctx, int gprn, int sprn)
+{
+ translator_io_start(&ctx->base);
+ gen_helper_load_pmsr(cpu_gpr[gprn], tcg_env);
+}
+
+void spr_write_pmcr(DisasContext *ctx, int sprn, int gprn)
+{
+ if (!gen_serialize_core_lpar(ctx)) {
+ return;
+ }
+ translator_io_start(&ctx->base);
+ gen_helper_store_pmcr(tcg_env, cpu_gpr[gprn]);
+}
+
#endif /* !defined(CONFIG_USER_ONLY) */
void spr_read_tar(DisasContext *ctx, int gprn, int sprn)
Linux power management code accesses these registers for pstate management. Wire up a very simple implementation. Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- After OCC fixes in QEMU pnv model and skiboot (since they have suffered some bitrot), Linux will start performing PM SPR accesses. This is a very simple implementation that makes it a bit happier. Thanks, Nick --- target/ppc/cpu.h | 2 ++ target/ppc/helper.h | 2 ++ target/ppc/spr_common.h | 2 ++ target/ppc/cpu_init.c | 11 +++++++++ target/ppc/misc_helper.c | 53 ++++++++++++++++++++++++++++++++++++++++ target/ppc/translate.c | 16 ++++++++++++ 6 files changed, 86 insertions(+)