mirror of https://github.com/xemu-project/xemu.git
ppc: Add P7/P8 Power Management instructions
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 <benh@kernel.crashing.org> [clg: fixed checkpatch.pl errors ] Signed-off-by: Cédric Le Goater <clg@kaod.org> Reviewed-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
parent
b9971cc53e
commit
7778a575c7
|
@ -125,6 +125,15 @@ enum powerpc_excp_t {
|
|||
POWERPC_EXCP_POWER8,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/* 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;
|
||||
|
|
|
@ -383,6 +383,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)
|
||||
|
@ -1059,6 +1067,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 */
|
||||
|
@ -2068,6 +2081,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 | \
|
||||
|
@ -2075,7 +2090,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)
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -101,6 +101,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
|
||||
|
@ -897,6 +935,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)
|
||||
{
|
||||
CPUState *cs = CPU(ppc_env_get_cpu(env));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -3603,6 +3603,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) \
|
||||
|
@ -9911,6 +9973,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),
|
||||
|
|
|
@ -8380,10 +8380,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";
|
||||
|
@ -8393,6 +8428,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
|
|||
pcc->pcr_supported = PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
|
||||
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 |
|
||||
|
@ -8409,7 +8445,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) |
|
||||
|
@ -8462,10 +8499,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";
|
||||
|
@ -8475,6 +8555,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
|
|||
pcc->pcr_supported = PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_COMPAT_2_05;
|
||||
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 |
|
||||
|
@ -8494,7 +8575,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) |
|
||||
|
@ -8553,6 +8634,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...
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue