PSSCR aliases to two SPR numbers, one HV only and one where some fields
are available to supervisor. Supervisor also has some restrictions on
where it can execute 'stop' instruction, based on PSSCR field values.

Signed-off-by: Nicholas Piggin <npig...@gmail.com>
---
 target/ppc/cpu.h             |  9 ++++++++-
 target/ppc/helper.h          |  2 ++
 target/ppc/spr_common.h      |  2 ++
 target/ppc/cpu_init.c        | 12 +++++++++---
 target/ppc/misc_helper.c     | 34 ++++++++++++++++++++++++++++++++++
 target/ppc/tcg-excp_helper.c | 27 ++++++++++++++++++++++++---
 target/ppc/translate.c       | 10 ++++++++++
 7 files changed, 89 insertions(+), 7 deletions(-)

diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h
index dca84ca23cd..74ed28c8dac 100644
--- a/target/ppc/cpu.h
+++ b/target/ppc/cpu.h
@@ -651,12 +651,16 @@ FIELD(MSR, LE, MSR_LE, 1)
                           LPCR_HR |                \
                           LPCR_TC)
 /* PSSCR bits */
+#define PSSCR_HV_PRIV     PPC_BITMASK(41, 47) /* Hypervisor-only fields */
+#define PSSCR_PLS         PPC_BITMASK(0, 3) /* Power-Saving Level Status */
+#define PSSCR_SD          PPC_BIT(41) /* Status Disable */
 #define PSSCR_ESL         PPC_BIT(42) /* Enable State Loss */
 #define PSSCR_EC          PPC_BIT(43) /* Exit Criterion */
 
 /* HFSCR bits */
 #define HFSCR_MSGP     PPC_BIT_NR(53) /* Privileged Message Send Facilities */
 #define HFSCR_BHRB     PPC_BIT_NR(59) /* BHRB Instructions */
+#define HFSCR_IC_STOP  0x9
 #define HFSCR_IC_MSGP  0xA
 
 #define DBCR0_ICMP (1 << 27)
@@ -1675,6 +1679,8 @@ bool slb_lookup_rmap(CPUPPCState *env, target_ulong va, 
bool is_1tb,
 #endif
 
 void ppc_store_fpscr(CPUPPCState *env, target_ulong val);
+void helper_raise_hv_fu_exception(CPUPPCState *env, const char *caller,
+                                  uint32_t cause);
 void helper_hfscr_facility_check(CPUPPCState *env, uint32_t bit,
                                  const char *caller, uint32_t cause);
 
@@ -2127,13 +2133,14 @@ void ppc_compat_add_property(Object *obj, const char 
*name,
 #define SPR_UDEXCR            (0x32C)
 #define SPR_TAR               (0x32F)
 #define SPR_ASDR              (0x330)
+#define SPR_PSSCR             (0x337)
 #define SPR_DEXCR             (0x33C)
 #define SPR_IC                (0x350)
 #define SPR_VTB               (0x351)
 #define SPR_LDBAR             (0x352)
 #define SPR_MMCRC             (0x353)
 #define SPR_PMSR              (0x355)
-#define SPR_PSSCR             (0x357)
+#define SPR_HPSSCR            (0x357)
 #define SPR_440_INV0          (0x370)
 #define SPR_440_INV1          (0x371)
 #define SPR_TRIG1             (0x371)
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index 53d74a67ffa..3e2a4a46823 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -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_psscr, TCG_CALL_NO_RWG_SE, tl, env)
+DEF_HELPER_FLAGS_2(store_psscr, 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
diff --git a/target/ppc/spr_common.h b/target/ppc/spr_common.h
index b57533dfd8e..fcbdf44cb9e 100644
--- a/target/ppc/spr_common.h
+++ b/target/ppc/spr_common.h
@@ -207,6 +207,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_psscr(DisasContext *ctx, int gprn, int sprn);
+void spr_write_psscr(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);
diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c
index 36742136309..1eb6cc10478 100644
--- a/target/ppc/cpu_init.c
+++ b/target/ppc/cpu_init.c
@@ -6475,10 +6475,16 @@ static void register_power9_common_sprs(CPUPPCState 
*env)
     register_power8_rpr_sprs(env);
     register_power9_mmu_sprs(env);
 
-    /* FIXME: Filter fields properly based on privilege level */
-    spr_register_kvm_hv(env, SPR_PSSCR, "PSSCR", NULL, NULL, NULL, NULL,
-                        spr_read_generic, spr_write_generic,
+    spr_register_kvm_hv(env, SPR_PSSCR, "PSSCR",
+                        SPR_NOACCESS, SPR_NOACCESS,
+                        &spr_read_psscr, &spr_write_psscr,
+                        &spr_read_psscr, &spr_write_psscr,
                         KVM_REG_PPC_PSSCR, 0);
+    spr_register_hv(env, SPR_HPSSCR, "HPSSCR",
+                        SPR_NOACCESS, SPR_NOACCESS,
+                        SPR_NOACCESS, SPR_NOACCESS,
+                        &spr_read_psscr, &spr_write_psscr,
+                        0);
 
     spr_register_hv(env, SPR_PMSR, "PMSR",
                     SPR_NOACCESS, SPR_NOACCESS,
diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c
index 641f07eeb7e..d335db1180f 100644
--- a/target/ppc/misc_helper.c
+++ b/target/ppc/misc_helper.c
@@ -113,6 +113,14 @@ static void raise_fu_exception(CPUPPCState *env, uint32_t 
bit,
 }
 #endif
 
+void helper_raise_hv_fu_exception(CPUPPCState *env, const char *caller,
+                                  uint32_t cause)
+{
+#ifdef TARGET_PPC64
+    raise_hv_fu_exception(env, cause, caller, cause, GETPC());
+#endif
+}
+
 void helper_hfscr_facility_check(CPUPPCState *env, uint32_t bit,
                                  const char *caller, uint32_t cause)
 {
@@ -418,6 +426,32 @@ void helper_store_sprd(CPUPPCState *env, target_ulong val)
     }
 }
 
+target_ulong helper_load_psscr(CPUPPCState *env)
+{
+    target_ulong val = env->spr[SPR_PSSCR];
+
+    if (val & PSSCR_SD) { /* Status Disable field */
+        val &= ~PSSCR_PLS; /* Mask Power-Saving Level Status field */
+    }
+
+    if (!FIELD_EX64(env->msr, MSR, HV)) {
+        val &= ~PSSCR_HV_PRIV;
+    }
+
+    return val;
+}
+
+void helper_store_psscr(CPUPPCState *env, target_ulong val)
+{
+    if (!FIELD_EX64(env->msr, MSR, HV)) {
+        val &= ~PSSCR_HV_PRIV;
+        val |= env->spr[SPR_PSSCR] & PSSCR_HV_PRIV;
+    }
+
+    env->spr[SPR_PSSCR] = val;
+}
+
+
 target_ulong helper_load_pmsr(CPUPPCState *env)
 {
     target_ulong lowerps = extract64(env->spr[SPR_PMCR], PPC_BIT_NR(15), 8);
diff --git a/target/ppc/tcg-excp_helper.c b/target/ppc/tcg-excp_helper.c
index 5a189dc3d70..a0e5669c669 100644
--- a/target/ppc/tcg-excp_helper.c
+++ b/target/ppc/tcg-excp_helper.c
@@ -457,9 +457,30 @@ void helper_pminsn(CPUPPCState *env, uint32_t insn)
 
     cs->halted = 1;
 
-    /* Condition for waking up at 0x100 */
-    env->resume_as_sreset = (insn != PPC_PM_STOP) ||
-        (env->spr[SPR_PSSCR] & PSSCR_EC);
+    if (insn == PPC_PM_STOP) {
+        target_ulong psscr = env->spr[SPR_PSSCR];
+
+#ifdef TARGET_PPC64
+        if ((env->msr_mask & MSR_HVB) && !FIELD_EX64(env->msr, MSR, HV)) {
+            target_ulong psll = extract64(psscr, PPC_BIT_NR(47), 4);
+            target_ulong mtl = extract64(psscr, PPC_BIT_NR(59), 4);
+            target_ulong rl = extract64(psscr, PPC_BIT_NR(63), 4);
+
+            /* Supervisor-mode facility check */
+            if ((psscr & PSSCR_EC) || (psscr & PSSCR_ESL) ||
+                (mtl > psll) || (rl > psll)) {
+                helper_raise_hv_fu_exception(env, "STOP insn", HFSCR_IC_STOP);
+            }
+        }
+#endif
+        /* We don't model any power saving levels above 0 */
+        env->spr[SPR_PSSCR] &= ~PSSCR_PLS;
+
+        /* Condition for waking up at 0x100 */
+        env->resume_as_sreset = psscr & PSSCR_EC;
+    } else {
+        env->resume_as_sreset = true;
+    }
 
     /* HDECR is not to wake from PM state, it may have already fired */
     if (env->resume_as_sreset) {
diff --git a/target/ppc/translate.c b/target/ppc/translate.c
index 31446dcd78d..7f933537aaa 100644
--- a/target/ppc/translate.c
+++ b/target/ppc/translate.c
@@ -1349,6 +1349,16 @@ void spr_write_lpcr(DisasContext *ctx, int sprn, int 
gprn)
     gen_helper_store_lpcr(tcg_env, cpu_gpr[gprn]);
 }
 
+void spr_read_psscr(DisasContext *ctx, int gprn, int sprn)
+{
+    gen_helper_load_psscr(cpu_gpr[gprn], tcg_env);
+}
+
+void spr_write_psscr(DisasContext *ctx, int sprn, int gprn)
+{
+    gen_helper_store_psscr(tcg_env, cpu_gpr[gprn]);
+}
+
 void spr_read_pmsr(DisasContext *ctx, int gprn, int sprn)
 {
     translator_io_start(&ctx->base);
-- 
2.47.1


Reply via email to