powerpc/powernv: Switch off MMU before entering nap/sleep/rvwinkle mode
authorPaul Mackerras <paulus@samba.org>
Tue, 9 Dec 2014 18:56:50 +0000 (00:26 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Sun, 14 Dec 2014 23:46:32 +0000 (10:46 +1100)
Currently, when going idle, we set the flag indicating that we are in
nap mode (paca->kvm_hstate.hwthread_state) and then execute the nap
(or sleep or rvwinkle) instruction, all with the MMU on.  This is bad
for two reasons: (a) the architecture specifies that those instructions
must be executed with the MMU off, and in fact with only the SF, HV, ME
and possibly RI bits set, and (b) this introduces a race, because as
soon as we set the flag, another thread can switch the MMU to a guest
context.  If the race is lost, this thread will typically start looping
on relocation-on ISIs at 0xc...4400.

This fixes it by setting the MSR as required by the architecture before
setting the flag or executing the nap/sleep/rvwinkle instruction.

Cc: stable@vger.kernel.org
[ shreyas@linux.vnet.ibm.com: Edited to handle LE ]
Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Shreyas B. Prabhu <shreyas@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: linuxppc-dev@lists.ozlabs.org
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/include/asm/reg.h
arch/powerpc/kernel/idle_power7.S

index c998279bd85b1b5cec6cf719b88fadc90aacd79f..a68ee15964b33b9ca3ba67c96e3c3d155b29554a 100644 (file)
 #define __MSR          (MSR_ME | MSR_RI | MSR_IR | MSR_DR | MSR_ISF |MSR_HV)
 #ifdef __BIG_ENDIAN__
 #define MSR_           __MSR
+#define MSR_IDLE       (MSR_ME | MSR_SF | MSR_HV)
 #else
 #define MSR_           (__MSR | MSR_LE)
+#define MSR_IDLE       (MSR_ME | MSR_SF | MSR_HV | MSR_LE)
 #endif
 #define MSR_KERNEL     (MSR_ | MSR_64BIT)
 #define MSR_USER32     (MSR_ | MSR_PR | MSR_EE)
index 18c0687e5ab3d2b9c4dc13587af411c755eef776..e5aba6abbe6c486e663c9ba6b5e870eef2c8c69d 100644 (file)
@@ -101,7 +101,23 @@ _GLOBAL(power7_powersave_common)
        std     r9,_MSR(r1)
        std     r1,PACAR1(r13)
 
-_GLOBAL(power7_enter_nap_mode)
+       /*
+        * Go to real mode to do the nap, as required by the architecture.
+        * Also, we need to be in real mode before setting hwthread_state,
+        * because as soon as we do that, another thread can switch
+        * the MMU context to the guest.
+        */
+       LOAD_REG_IMMEDIATE(r5, MSR_IDLE)
+       li      r6, MSR_RI
+       andc    r6, r9, r6
+       LOAD_REG_ADDR(r7, power7_enter_nap_mode)
+       mtmsrd  r6, 1           /* clear RI before setting SRR0/1 */
+       mtspr   SPRN_SRR0, r7
+       mtspr   SPRN_SRR1, r5
+       rfid
+
+       .globl  power7_enter_nap_mode
+power7_enter_nap_mode:
 #ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLE
        /* Tell KVM we're napping */
        li      r4,KVM_HWTHREAD_IN_NAP