From patchwork Mon Jun 13 05:24:56 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?C=C3=A9dric_Le_Goater?= X-Patchwork-Id: 9171997 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id EB897604DB for ; Mon, 13 Jun 2016 05:41:05 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DED0721C9A for ; Mon, 13 Jun 2016 05:41:05 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D380625EF7; Mon, 13 Jun 2016 05:41:05 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id A2FA021C9A for ; Mon, 13 Jun 2016 05:41:04 +0000 (UTC) Received: from localhost ([::1]:53916 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bCKcR-0004aL-Ql for patchwork-qemu-devel@patchwork.kernel.org; Mon, 13 Jun 2016 01:41:03 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:56348) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bCKNy-0001bw-Qg for qemu-devel@nongnu.org; Mon, 13 Jun 2016 01:26:11 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bCKNs-00024e-On for qemu-devel@nongnu.org; Mon, 13 Jun 2016 01:26:05 -0400 Received: from 4.mo178.mail-out.ovh.net ([46.105.49.171]:51681) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bCKNs-00024V-An for qemu-devel@nongnu.org; Mon, 13 Jun 2016 01:26:00 -0400 Received: from player791.ha.ovh.net (b9.ovh.net [213.186.33.59]) by mo178.mail-out.ovh.net (Postfix) with ESMTP id C75591001029 for ; Mon, 13 Jun 2016 07:25:59 +0200 (CEST) Received: from hermes.kaod.org (LFbn-1-2234-107.w90-76.abo.wanadoo.fr [90.76.55.107]) (Authenticated sender: clg@kaod.org) by player791.ha.ovh.net (Postfix) with ESMTPSA id 66AD6420073; Mon, 13 Jun 2016 07:25:54 +0200 (CEST) From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= To: David Gibson Date: Mon, 13 Jun 2016 07:24:56 +0200 Message-Id: <1465795496-15071-11-git-send-email-clg@kaod.org> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1465795496-15071-1-git-send-email-clg@kaod.org> References: <1465795496-15071-1-git-send-email-clg@kaod.org> MIME-Version: 1.0 X-Ovh-Tracer-Id: 4892879521771981798 X-VR-SPAMSTATE: OK X-VR-SPAMSCORE: -100 X-VR-SPAMCAUSE: gggruggvucftvghtrhhoucdtuddrfeekledrkedtgddvheejucetufdoteggodetrfdotffvucfrrhhofhhilhgvmecuqfggjfenuceurghilhhouhhtmecufedttdenucesvcftvggtihhpihgvnhhtshculddquddttddm X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x (no timestamps) [generic] X-Received-From: 46.105.49.171 Subject: [Qemu-devel] [PATCH 10/10] ppc: Add P7/P8 Power Management instructions X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: qemu-ppc@nongnu.org, qemu-devel@nongnu.org, Cedric Le Goater Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP From: Benjamin Herrenschmidt 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 [clg: fixed checkpatch.pl errors ] Signed-off-by: Cédric Le Goater Reviewed-by: David Gibson --- target-ppc/cpu-qom.h | 9 +++++ target-ppc/cpu.h | 17 ++++++++- target-ppc/excp_helper.c | 59 +++++++++++++++++++++++++++++ target-ppc/helper.h | 1 + target-ppc/translate.c | 66 ++++++++++++++++++++++++++++++++ target-ppc/translate_init.c | 92 ++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 241 insertions(+), 3 deletions(-) diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h index 969ecdfbd40a..0fad2def0a94 100644 --- a/target-ppc/cpu-qom.h +++ b/target-ppc/cpu-qom.h @@ -126,6 +126,15 @@ enum powerpc_excp_t { }; /*****************************************************************************/ +/* 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 { diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 61a24b19ffce..b1354a4791db 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -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) }; /*****************************************************************************/ diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index 054c12de3bff..533866b87b60 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -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)); diff --git a/target-ppc/helper.h b/target-ppc/helper.h index f4410a836954..ab9592926f7b 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 600d5db2bb9a..2dd74c3244ed 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -3574,6 +3574,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) \ @@ -9885,6 +9947,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 6f2f760728bb..0b39a088458d 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8371,10 +8371,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"; @@ -8384,6 +8419,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 | @@ -8400,7 +8436,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) | @@ -8453,10 +8490,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"; @@ -8466,6 +8546,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 | @@ -8485,7 +8566,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) | @@ -8544,6 +8625,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... */