[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-ppc] [PATCH 25/77] ppc: Add P7/P8 Power Management instructions
From: |
Benjamin Herrenschmidt |
Subject: |
[Qemu-ppc] [PATCH 25/77] ppc: Add P7/P8 Power Management instructions |
Date: |
Wed, 11 Nov 2015 11:27:38 +1100 |
This adds the ISA 2.06 and later power management instructions
(doze, nap, sleep and rvwinkle) and associated wakeup cause testing
in LPCR
Signed-off-by: Benjamin Herrenschmidt <address@hidden>
---
target-ppc/cpu.h | 26 ++++++++++++-
target-ppc/excp_helper.c | 59 +++++++++++++++++++++++++++++
target-ppc/helper.h | 1 +
target-ppc/translate.c | 66 ++++++++++++++++++++++++++++++++
target-ppc/translate_init.c | 92 ++++++++++++++++++++++++++++++++++++++++++++-
5 files changed, 241 insertions(+), 3 deletions(-)
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
index 3d22a4f..a7236cf 100644
--- a/target-ppc/cpu.h
+++ b/target-ppc/cpu.h
@@ -300,6 +300,15 @@ enum {
};
/*****************************************************************************/
+/* PM instructions */
+typedef enum {
+ PPC_PM_DOZE,
+ PPC_PM_NAP,
+ PPC_PM_SLEEP,
+ PPC_PM_RVWINKLE,
+} powerpc_pm_insn_t;
+
+/*****************************************************************************/
/* Input pins model */
typedef enum powerpc_input_t powerpc_input_t;
enum powerpc_input_t {
@@ -490,6 +499,14 @@ struct ppc_slb_t {
#define LPCR_LPES1 (1ull << (63-61))
#define LPCR_AIL_SHIFT (63-40) /* Alternate interrupt location */
#define LPCR_AIL (3ull << LPCR_AIL_SHIFT)
+#define LPCR_P7_PECE0 (1ull << (63-49))
+#define LPCR_P7_PECE1 (1ull << (63-50))
+#define LPCR_P7_PECE2 (1ull << (63-51))
+#define LPCR_P8_PECE0 (1ull << (63-47))
+#define LPCR_P8_PECE1 (1ull << (63-48))
+#define LPCR_P8_PECE2 (1ull << (63-49))
+#define LPCR_P8_PECE3 (1ull << (63-50))
+#define LPCR_P8_PECE4 (1ull << (63-51))
#define msr_sf ((env->msr >> MSR_SF) & 1)
#define msr_isf ((env->msr >> MSR_ISF) & 1)
@@ -1126,6 +1143,11 @@ struct CPUPPCState {
* instructions and SPRs are diallowed if MSR:HV is 0
*/
bool has_hv_mode;
+ /* On P7/P8, set when in PM state, we need to handle resume
+ * in a special way (such as routing some resume causes to
+ * 0x100), so flag this here.
+ */
+ bool in_pm_state;
#endif
/* Those resources are used only during code translation */
@@ -2069,6 +2091,8 @@ enum {
PPC2_FP_CVT_S64 = 0x0000000000010000ULL,
/* Transactional Memory (ISA 2.07, Book II) */
PPC2_TM = 0x0000000000020000ULL,
+ /* Server PM instructgions (ISA 2.06, Book III) */
+ PPC2_PM_ISA206 = 0x0000000000040000ULL,
#define PPC_TCG_INSNS2 (PPC2_BOOKE206 | PPC2_VSX | PPC2_PRCNTL | PPC2_DBRX | \
PPC2_ISA205 | PPC2_VSX207 | PPC2_PERM_ISA206 | \
@@ -2076,7 +2100,7 @@ enum {
PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206 | \
PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | \
PPC2_ALTIVEC_207 | PPC2_ISA207S | PPC2_DFP | \
- PPC2_FP_CVT_S64 | PPC2_TM)
+ PPC2_FP_CVT_S64 | PPC2_TM | PPC2_PM_ISA206)
};
/*****************************************************************************/
diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c
index 80a70f4..3f77df7 100644
--- a/target-ppc/excp_helper.c
+++ b/target-ppc/excp_helper.c
@@ -100,6 +100,44 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int
excp_model, int excp)
asrr0 = -1;
asrr1 = -1;
+ /* check for special resume at 0x100 from doze/nap/sleep/winkle on P7/P8 */
+ if (env->in_pm_state) {
+ env->in_pm_state = false;
+
+ /* Pretend to be returning from doze always as we don't lose state */
+ msr |= (0x1ull << (63 - 47));
+
+ /* Non-machine check are routed to 0x100 with a wakeup cause
+ * encoded in SRR1
+ */
+ if (excp != POWERPC_EXCP_MCHECK) {
+ switch(excp) {
+ case POWERPC_EXCP_RESET:
+ msr |= 0x4ull << (63-45);
+ break;
+ case POWERPC_EXCP_EXTERNAL:
+ msr |= 0x8ull << (63-45);
+ break;
+ case POWERPC_EXCP_DECR:
+ msr |= 0x6ull << (63-45);
+ break;
+ case POWERPC_EXCP_SDOOR:
+ msr |= 0x5ull << (63-45);
+ break;
+ case POWERPC_EXCP_SDOOR_HV:
+ msr |= 0x3ull << (63-45);
+ break;
+ case POWERPC_EXCP_HV_MAINT:
+ msr |= 0xaull << (63-45);
+ break;
+ default:
+ cpu_abort(cs, "Unsupported exception %d in Power Save mode\n",
+ excp);
+ }
+ excp = POWERPC_EXCP_RESET;
+ }
+ }
+
/* Exception targetting modifiers
*
* LPES0 is supported on POWER7/8
@@ -898,6 +936,27 @@ void helper_store_msr(CPUPPCState *env, target_ulong val)
}
}
+#if defined(TARGET_PPC64)
+void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
+{
+ CPUState *cs;
+
+ cs = CPU(ppc_env_get_cpu(env));
+ cs->halted = 1;
+ env->in_pm_state = true;
+
+ /* Technically, nap doesn't set EE, but if we don't set it
+ * then ppc_hw_interrupt() won't deliver. We could add some
+ * other tests there based on LPCR but it's simpler to just
+ * whack EE in. It will be cleared by the 0x100 at wakeup
+ * anyway. It will still be observable by the guest in SRR1
+ * but this doesn't seem to be a problem.
+ */
+ env->msr |= (1ull << MSR_EE);
+ helper_raise_exception(env, EXCP_HLT);
+}
+#endif /* defined(TARGET_PPC64) */
+
static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr,
target_ulong msrm, int keep_msrh)
{
diff --git a/target-ppc/helper.h b/target-ppc/helper.h
index ff2d50b..8292dd8 100644
--- a/target-ppc/helper.h
+++ b/target-ppc/helper.h
@@ -13,6 +13,7 @@ DEF_HELPER_1(rfci, void, env)
DEF_HELPER_1(rfdi, void, env)
DEF_HELPER_1(rfmci, void, env)
#if defined(TARGET_PPC64)
+DEF_HELPER_2(pminsn, void, env, i32)
DEF_HELPER_1(rfid, void, env)
DEF_HELPER_1(hrfid, void, env)
#endif
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
index ac62942..f76a0c3 100644
--- a/target-ppc/translate.c
+++ b/target-ppc/translate.c
@@ -3567,6 +3567,68 @@ static void gen_wait(DisasContext *ctx)
gen_exception_err(ctx, EXCP_HLT, 1);
}
+#if defined(TARGET_PPC64)
+static void gen_doze(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ GEN_PRIV;
+#else
+ TCGv_i32 t;
+
+ CHK_HV;
+ t = tcg_const_i32(PPC_PM_DOZE);
+ gen_helper_pminsn(cpu_env, t);
+ tcg_temp_free_i32(t);
+ gen_stop_exception(ctx);
+#endif /* defined(CONFIG_USER_ONLY) */
+}
+
+static void gen_nap(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ GEN_PRIV;
+#else
+ TCGv_i32 t;
+
+ CHK_HV;
+ t = tcg_const_i32(PPC_PM_NAP);
+ gen_helper_pminsn(cpu_env, t);
+ tcg_temp_free_i32(t);
+ gen_stop_exception(ctx);
+#endif /* defined(CONFIG_USER_ONLY) */
+}
+
+static void gen_sleep(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ GEN_PRIV;
+#else
+ TCGv_i32 t;
+
+ CHK_HV;
+ t = tcg_const_i32(PPC_PM_SLEEP);
+ gen_helper_pminsn(cpu_env, t);
+ tcg_temp_free_i32(t);
+ gen_stop_exception(ctx);
+#endif /* defined(CONFIG_USER_ONLY) */
+}
+
+static void gen_rvwinkle(DisasContext *ctx)
+{
+#if defined(CONFIG_USER_ONLY)
+ GEN_PRIV;
+#else
+ TCGv_i32 t;
+
+ CHK_HV;
+ t = tcg_const_i32(PPC_PM_RVWINKLE);
+ gen_helper_pminsn(cpu_env, t);
+ tcg_temp_free_i32(t);
+ gen_stop_exception(ctx);
+#endif /* defined(CONFIG_USER_ONLY) */
+}
+#endif /* #if defined(TARGET_PPC64) */
+
/*** Floating-point load ***/
#define GEN_LDF(name, ldop, opc, type) \
static void glue(gen_, name)(DisasContext *ctx)
\
@@ -9828,6 +9890,10 @@ GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001,
PPC_INTEGER),
GEN_HANDLER(rfi, 0x13, 0x12, 0x01, 0x03FF8001, PPC_FLOW),
#if defined(TARGET_PPC64)
GEN_HANDLER(rfid, 0x13, 0x12, 0x00, 0x03FF8001, PPC_64B),
+GEN_HANDLER_E(doze, 0x13, 0x12, 0x0c, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
+GEN_HANDLER_E(nap, 0x13, 0x12, 0x0d, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
+GEN_HANDLER_E(sleep, 0x13, 0x12, 0x0e, 0x03FFF801, PPC_NONE, PPC2_PM_ISA206),
+GEN_HANDLER_E(rvwinkle, 0x13, 0x12, 0x0f, 0x03FFF801, PPC_NONE,
PPC2_PM_ISA206),
GEN_HANDLER(hrfid, 0x13, 0x12, 0x08, 0x03FF8001, PPC_64H),
#endif
GEN_HANDLER(sc, 0x11, 0xFF, 0xFF, 0x03FFF01D, PPC_FLOW),
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
index 8d82bc8..8a1ce85 100644
--- a/target-ppc/translate_init.c
+++ b/target-ppc/translate_init.c
@@ -8297,10 +8297,45 @@ static bool ppc_pvr_match_power7(PowerPCCPUClass *pcc,
uint32_t pvr)
return false;
}
+static bool cpu_has_work_POWER7(CPUState *cs)
+{
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ CPUPPCState *env = &cpu->env;
+
+ if (cs->halted) {
+ if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
+ return false;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
+ (env->spr[SPR_LPCR] & LPCR_P7_PECE0)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
+ (env->spr[SPR_LPCR] & LPCR_P7_PECE1)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) &&
+ (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) &&
+ (env->spr[SPR_LPCR] & LPCR_P7_PECE2)) {
+ return true;
+ }
+ if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
+ return true;
+ }
+ return false;
+ } else {
+ return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD);
+ }
+}
+
POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
+ CPUClass *cc = CPU_CLASS(oc);
dc->fw_name = "PowerPC,POWER7";
dc->desc = "POWER7";
@@ -8309,6 +8344,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06;
pcc->init_proc = init_proc_POWER7;
pcc->check_pow = check_pow_nocheck;
+ cc->has_work = cpu_has_work_POWER7;
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
@@ -8325,7 +8361,8 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
pcc->insns_flags2 = PPC2_VSX | PPC2_DFP | PPC2_DBRX | PPC2_ISA205 |
PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 |
PPC2_ATOMIC_ISA206 | PPC2_FP_CVT_ISA206 |
- PPC2_FP_TST_ISA206 | PPC2_FP_CVT_S64;
+ PPC2_FP_TST_ISA206 | PPC2_FP_CVT_S64 |
+ PPC2_PM_ISA206;
pcc->msr_mask = (1ull << MSR_SF) |
(1ull << MSR_VR) |
(1ull << MSR_VSX) |
@@ -8375,10 +8412,53 @@ static bool ppc_pvr_match_power8(PowerPCCPUClass *pcc,
uint32_t pvr)
return false;
}
+static bool cpu_has_work_POWER8(CPUState *cs)
+{
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+ CPUPPCState *env = &cpu->env;
+
+ if (cs->halted) {
+ if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) {
+ return false;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE2)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE3)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_MCK)) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HMI)) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE4)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DOORBELL)) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE0)) {
+ return true;
+ }
+ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HDOORBELL)) &&
+ (env->spr[SPR_LPCR] & LPCR_P8_PECE1)) {
+ return true;
+ }
+ if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) {
+ return true;
+ }
+ return false;
+ } else {
+ return msr_ee && (cs->interrupt_request & CPU_INTERRUPT_HARD);
+ }
+}
+
POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
+ CPUClass *cc = CPU_CLASS(oc);
dc->fw_name = "PowerPC,POWER8";
dc->desc = "POWER8";
@@ -8387,6 +8467,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
pcc->pcr_mask = PCR_COMPAT_2_05 | PCR_COMPAT_2_06;
pcc->init_proc = init_proc_POWER8;
pcc->check_pow = check_pow_nocheck;
+ cc->has_work = cpu_has_work_POWER8;
pcc->insns_flags = PPC_INSNS_BASE | PPC_ISEL | PPC_STRING | PPC_MFTB |
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
@@ -8406,7 +8487,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 |
PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 |
PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 |
- PPC2_TM;
+ PPC2_TM | PPC2_PM_ISA206;
pcc->msr_mask = (1ull << MSR_SF) |
(1ull << MSR_SHV) |
(1ull << MSR_TM) |
@@ -8464,6 +8545,13 @@ void cpu_ppc_set_papr(PowerPCCPU *cpu)
lpcr->default_value &= ~(LPCR_VPM0 | LPCR_VPM1 | LPCR_ISL | LPCR_KBV);
lpcr->default_value |= LPCR_LPES0 | LPCR_LPES1;
+ /* P7 and P8 has slightly different PECE bits, mostly because P8 adds
+ * bit 47 and 48 which are reserved on P7. Here we set them all, which
+ * will work as expected for both implementations
+ */
+ lpcr->default_value |= LPCR_P8_PECE0 | LPCR_P8_PECE1 | LPCR_P8_PECE2 |
+ LPCR_P8_PECE3 | LPCR_P8_PECE4;
+
/* We should be followed by a CPU reset but update the active value
* just in case...
*/
--
2.5.0
- Re: [Qemu-ppc] [PATCH 18/77] ppc: Rework POWER7 & POWER8 exception model, (continued)
[Qemu-ppc] [PATCH 28/77] ppc/xics: Rename existing XICS classe to XICS_SPAPR, Benjamin Herrenschmidt, 2015/11/10
[Qemu-ppc] [PATCH 25/77] ppc: Add P7/P8 Power Management instructions,
Benjamin Herrenschmidt <=
[Qemu-ppc] [PATCH 30/77] ppc/xics: Implement H_IPOLL using an accessor, Benjamin Herrenschmidt, 2015/11/10
[Qemu-ppc] [PATCH 31/77] ppc/xics: Remove unused xics_set_irq_type(), Benjamin Herrenschmidt, 2015/11/10
[Qemu-ppc] [PATCH 29/77] ppc/xics: Move SPAPR specific code to a separate file, Benjamin Herrenschmidt, 2015/11/10
[Qemu-ppc] [PATCH 36/77] ppc/xics: Use a helper to add a new ICS, Benjamin Herrenschmidt, 2015/11/10
[Qemu-ppc] [PATCH 23/77] ppc: Turn a bunch of booleans from int to bool, Benjamin Herrenschmidt, 2015/11/10
[Qemu-ppc] [PATCH 26/77] ppc/pnv: Add skeletton PowerNV platform, Benjamin Herrenschmidt, 2015/11/10