]> Pileus Git - ~andy/linux/blobdiff - arch/powerpc/kvm/book3s_hv_rmhandlers.S
KVM: PPC: Book3S HV: Consolidate code that checks reason for wake from nap
[~andy/linux] / arch / powerpc / kvm / book3s_hv_rmhandlers.S
index 19f8819f90fa5829277b7c893db2e338df2be7fd..386e141dbf1657d587a7fac0d5cf493607ef193e 100644 (file)
@@ -193,8 +193,10 @@ kvm_novcpu_wakeup:
        stb     r0, HSTATE_NAPPING(r13)
        stb     r0, HSTATE_HWTHREAD_REQ(r13)
 
+       /* check the wake reason */
+       bl      kvmppc_check_wake_reason
+       
        /* see if any other thread is already exiting */
-       li      r12, 0
        lwz     r0, VCORE_ENTRY_EXIT(r5)
        cmpwi   r0, 0x100
        bge     kvm_novcpu_exit
@@ -204,23 +206,14 @@ kvm_novcpu_wakeup:
        li      r0, 1
        sld     r0, r0, r7
        addi    r6, r5, VCORE_NAPPING_THREADS
-4:     lwarx   r3, 0, r6
-       andc    r3, r3, r0
-       stwcx.  r3, 0, r6
+4:     lwarx   r7, 0, r6
+       andc    r7, r7, r0
+       stwcx.  r7, 0, r6
        bne     4b
 
-       /* Check the wake reason in SRR1 to see why we got here */
-       mfspr   r3, SPRN_SRR1
-       rlwinm  r3, r3, 44-31, 0x7      /* extract wake reason field */
-       cmpwi   r3, 4                   /* was it an external interrupt? */
-       bne     kvm_novcpu_exit         /* if not, exit the guest */
-
-       /* extern interrupt - read and handle it */
-       li      r12, BOOK3S_INTERRUPT_EXTERNAL
-       bl      kvmppc_read_intr
+       /* See if the wake reason means we need to exit */
        cmpdi   r3, 0
        bge     kvm_novcpu_exit
-       li      r12, 0
 
        /* Got an IPI but other vcpus aren't yet exiting, must be a latecomer */
        ld      r4, HSTATE_KVM_VCPU(r13)
@@ -264,40 +257,16 @@ kvm_start_guest:
         */
 
        /* Check the wake reason in SRR1 to see why we got here */
-       mfspr   r3,SPRN_SRR1
-       rlwinm  r3,r3,44-31,0x7         /* extract wake reason field */
-       cmpwi   r3,4                    /* was it an external interrupt? */
-       bne     27f                     /* if not */
-       ld      r5,HSTATE_XICS_PHYS(r13)
-       li      r7,XICS_XIRR            /* if it was an external interrupt, */
-       lwzcix  r8,r5,r7                /* get and ack the interrupt */
-       sync
-       clrldi. r9,r8,40                /* get interrupt source ID. */
-       beq     28f                     /* none there? */
-       cmpwi   r9,XICS_IPI             /* was it an IPI? */
-       bne     29f
-       li      r0,0xff
-       li      r6,XICS_MFRR
-       stbcix  r0,r5,r6                /* clear IPI */
-       stwcix  r8,r5,r7                /* EOI the interrupt */
-       sync                            /* order loading of vcpu after that */
+       bl      kvmppc_check_wake_reason
+       cmpdi   r3, 0
+       bge     kvm_no_guest
 
        /* get vcpu pointer, NULL if we have no vcpu to run */
        ld      r4,HSTATE_KVM_VCPU(r13)
        cmpdi   r4,0
        /* if we have no vcpu to run, go back to sleep */
        beq     kvm_no_guest
-       b       30f
 
-27:    /* XXX should handle hypervisor maintenance interrupts etc. here */
-       b       kvm_no_guest
-28:    /* SRR1 said external but ICP said nope?? */
-       b       kvm_no_guest
-29:    /* External non-IPI interrupt to offline secondary thread? help?? */
-       stw     r8,HSTATE_SAVED_XIRR(r13)
-       b       kvm_no_guest
-
-30:
        /* Set HSTATE_DSCR(r13) to something sensible */
        LOAD_REG_ADDR(r6, dscr_default)
        ld      r6, 0(r6)
@@ -310,18 +279,6 @@ kvm_start_guest:
        li      r0, 0
        std     r0, HSTATE_KVM_VCPU(r13)
        lwsync
-       /* Clear any pending IPI - we're an offline thread */
-       ld      r5, HSTATE_XICS_PHYS(r13)
-       li      r7, XICS_XIRR
-       lwzcix  r3, r5, r7              /* ack any pending interrupt */
-       rlwinm. r0, r3, 0, 0xffffff     /* any pending? */
-       beq     37f
-       sync
-       li      r0, 0xff
-       li      r6, XICS_MFRR
-       stbcix  r0, r5, r6              /* clear the IPI */
-       stwcix  r3, r5, r7              /* EOI it */
-37:    sync
 
        /* increment the nap count and then go to nap mode */
        ld      r4, HSTATE_KVM_VCORE(r13)
@@ -818,47 +775,46 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
        mtctr   r6
        mtxer   r7
 
+kvmppc_cede_reentry:           /* r4 = vcpu, r13 = paca */
        ld      r10, VCPU_PC(r4)
        ld      r11, VCPU_MSR(r4)
-kvmppc_cede_reentry:           /* r4 = vcpu, r13 = paca */
        ld      r6, VCPU_SRR0(r4)
        ld      r7, VCPU_SRR1(r4)
+       mtspr   SPRN_SRR0, r6
+       mtspr   SPRN_SRR1, r7
 
+deliver_guest_interrupt:
        /* r11 = vcpu->arch.msr & ~MSR_HV */
        rldicl  r11, r11, 63 - MSR_HV_LG, 1
        rotldi  r11, r11, 1 + MSR_HV_LG
        ori     r11, r11, MSR_ME
 
        /* Check if we can deliver an external or decrementer interrupt now */
-       ld      r0,VCPU_PENDING_EXC(r4)
-       lis     r8,(1 << BOOK3S_IRQPRIO_EXTERNAL_LEVEL)@h
-       and     r0,r0,r8
-       cmpdi   cr1,r0,0
-       andi.   r0,r11,MSR_EE
-       beq     cr1,11f
+       ld      r0, VCPU_PENDING_EXC(r4)
+       rldicl  r0, r0, 64 - BOOK3S_IRQPRIO_EXTERNAL_LEVEL, 63
+       cmpdi   cr1, r0, 0
+       andi.   r8, r11, MSR_EE
 BEGIN_FTR_SECTION
-       mfspr   r8,SPRN_LPCR
-       ori     r8,r8,LPCR_MER
-       mtspr   SPRN_LPCR,r8
+       mfspr   r8, SPRN_LPCR
+       /* Insert EXTERNAL_LEVEL bit into LPCR at the MER bit position */
+       rldimi  r8, r0, LPCR_MER_SH, 63 - LPCR_MER_SH
+       mtspr   SPRN_LPCR, r8
        isync
 END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206)
        beq     5f
-       li      r0,BOOK3S_INTERRUPT_EXTERNAL
-12:    mr      r6,r10
+       li      r0, BOOK3S_INTERRUPT_EXTERNAL
+       bne     cr1, 12f
+       mfspr   r0, SPRN_DEC
+       cmpwi   r0, 0
+       li      r0, BOOK3S_INTERRUPT_DECREMENTER
+       bge     5f
+
+12:    mtspr   SPRN_SRR0, r10
        mr      r10,r0
-       mr      r7,r11
+       mtspr   SPRN_SRR1, r11
        li      r11,(MSR_ME << 1) | 1   /* synthesize MSR_SF | MSR_ME */
        rotldi  r11,r11,63
-       b       5f
-11:    beq     5f
-       mfspr   r0,SPRN_DEC
-       cmpwi   r0,0
-       li      r0,BOOK3S_INTERRUPT_DECREMENTER
-       blt     12b
-
-       /* Move SRR0 and SRR1 into the respective regs */
-5:     mtspr   SPRN_SRR0, r6
-       mtspr   SPRN_SRR1, r7
+5:
 
 /*
  * Required state:
@@ -1047,39 +1003,19 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206)
        /* External interrupt, first check for host_ipi. If this is
         * set, we know the host wants us out so let's do it now
         */
-do_ext_interrupt:
        bl      kvmppc_read_intr
        cmpdi   r3, 0
        bgt     ext_interrupt_to_host
 
-       /* Allright, looks like an IPI for the guest, we need to set MER */
        /* Check if any CPU is heading out to the host, if so head out too */
        ld      r5, HSTATE_KVM_VCORE(r13)
        lwz     r0, VCORE_ENTRY_EXIT(r5)
        cmpwi   r0, 0x100
        bge     ext_interrupt_to_host
 
-       /* See if there is a pending interrupt for the guest */
-       mfspr   r8, SPRN_LPCR
-       ld      r0, VCPU_PENDING_EXC(r9)
-       /* Insert EXTERNAL_LEVEL bit into LPCR at the MER bit position */
-       rldicl. r0, r0, 64 - BOOK3S_IRQPRIO_EXTERNAL_LEVEL, 63
-       rldimi  r8, r0, LPCR_MER_SH, 63 - LPCR_MER_SH
-       beq     2f
-
-       /* And if the guest EE is set, we can deliver immediately, else
-        * we return to the guest with MER set
-        */
-       andi.   r0, r11, MSR_EE
-       beq     2f
-       mtspr   SPRN_SRR0, r10
-       mtspr   SPRN_SRR1, r11
-       li      r10, BOOK3S_INTERRUPT_EXTERNAL
-       li      r11, (MSR_ME << 1) | 1  /* synthesize MSR_SF | MSR_ME */
-       rotldi  r11, r11, 63
-2:     mr      r4, r9
-       mtspr   SPRN_LPCR, r8
-       b       fast_guest_return
+       /* Return to guest after delivering any pending interrupt */
+       mr      r4, r9
+       b       deliver_guest_interrupt
 
 ext_interrupt_to_host:
 
@@ -1887,7 +1823,6 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206)
        stb     r0,HSTATE_NAPPING(r13)
        /* order napping_threads update vs testing entry_exit_count */
        lwsync
-       mr      r4,r3
        lwz     r7,VCORE_ENTRY_EXIT(r5)
        cmpwi   r7,0x100
        bge     33f             /* another thread already exiting */
@@ -1940,6 +1875,11 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206)
        nap
        b       .
 
+33:    mr      r4, r3
+       li      r3, 0
+       li      r12, 0
+       b       34f
+
 kvm_end_cede:
        /* get vcpu pointer */
        ld      r4, HSTATE_KVM_VCPU(r13)
@@ -1969,12 +1909,15 @@ kvm_end_cede:
        ld      r29, VCPU_GPR(R29)(r4)
        ld      r30, VCPU_GPR(R30)(r4)
        ld      r31, VCPU_GPR(R31)(r4)
+       /* Check the wake reason in SRR1 to see why we got here */
+       bl      kvmppc_check_wake_reason
 
        /* clear our bit in vcore->napping_threads */
-33:    ld      r5,HSTATE_KVM_VCORE(r13)
-       lbz     r3,HSTATE_PTID(r13)
+34:    ld      r5,HSTATE_KVM_VCORE(r13)
+       lbz     r7,HSTATE_PTID(r13)
        li      r0,1
-       sld     r0,r0,r3
+       sld     r0,r0,r7
        addi    r6,r5,VCORE_NAPPING_THREADS
 32:    lwarx   r7,0,r6
        andc    r7,r7,r0
@@ -1983,23 +1926,18 @@ kvm_end_cede:
        li      r0,0
        stb     r0,HSTATE_NAPPING(r13)
 
-       /* Check the wake reason in SRR1 to see why we got here */
-       mfspr   r3, SPRN_SRR1
-       rlwinm  r3, r3, 44-31, 0x7      /* extract wake reason field */
-       cmpwi   r3, 4                   /* was it an external interrupt? */
-       li      r12, BOOK3S_INTERRUPT_EXTERNAL
+       /* See if the wake reason means we need to exit */
+       stw     r12, VCPU_TRAP(r4)
        mr      r9, r4
-       ld      r10, VCPU_PC(r9)
-       ld      r11, VCPU_MSR(r9)
-       beq     do_ext_interrupt        /* if so */
+       cmpdi   r3, 0
+       bgt     guest_exit_cont
 
        /* see if any other thread is already exiting */
        lwz     r0,VCORE_ENTRY_EXIT(r5)
        cmpwi   r0,0x100
-       blt     kvmppc_cede_reentry     /* if not go back to guest */
+       bge     guest_exit_cont
 
-       /* some threads are exiting, so go to the guest exit path */
-       b       hcall_real_fallback
+       b       kvmppc_cede_reentry     /* if not go back to guest */
 
        /* cede when already previously prodded case */
 kvm_cede_prodded:
@@ -2029,6 +1967,29 @@ machine_check_realmode:
        rotldi  r11, r11, 63
        b       fast_interrupt_c_return
 
+/*
+ * Check the reason we woke from nap, and take appropriate action.
+ * Returns:
+ *     0 if nothing needs to be done
+ *     1 if something happened that needs to be handled by the host
+ *     -1 if there was a guest wakeup (IPI)
+ *
+ * Also sets r12 to the interrupt vector for any interrupt that needs
+ * to be handled now by the host (0x500 for external interrupt), or zero.
+ */
+kvmppc_check_wake_reason:
+       mfspr   r6, SPRN_SRR1
+       rlwinm  r6, r6, 44-31, 0x7      /* extract wake reason field */
+       cmpwi   r6, 4                   /* was it an external interrupt? */
+       li      r12, BOOK3S_INTERRUPT_EXTERNAL
+       beq     kvmppc_read_intr        /* if so, see what it was */
+       li      r3, 0
+       li      r12, 0
+       cmpwi   r6, 6                   /* was it the decrementer? */
+       beq     0f
+       li      r3, 1                   /* anything else, return 1 */
+0:     blr
+
 /*
  * Determine what sort of external interrupt is pending (if any).
  * Returns:
@@ -2060,7 +2021,6 @@ kvmppc_read_intr:
         * interrupts directly to the guest
         */
        cmpwi   r3, XICS_IPI            /* if there is, is it an IPI? */
-       li      r3, 1
        bne     42f
 
        /* It's an IPI, clear the MFRR and EOI it */
@@ -2086,12 +2046,14 @@ kvmppc_read_intr:
         * before exit, it will be picked up by the host ICP driver
         */
        stw     r0, HSTATE_SAVED_XIRR(r13)
+       li      r3, 1
        b       1b
 
 43:    /* We raced with the host, we need to resend that IPI, bummer */
        li      r0, IPI_PRIORITY
        stbcix  r0, r6, r8              /* set the IPI */
        sync
+       li      r3, 1
        b       1b
 
 /*