KVM: PPC: Implement 'skip instruction' mode
authorAlexander Graf <agraf@suse.de>
Fri, 8 Jan 2010 01:58:04 +0000 (02:58 +0100)
committerMarcelo Tosatti <mtosatti@redhat.com>
Mon, 1 Mar 2010 15:35:48 +0000 (12:35 -0300)
To fetch the last instruction we were interrupted on, we enable DR in early
exit code, where we are still in a very transitional phase between guest
and host state.

Most of the time this seemed to work, but another CPU can easily flush our
TLB and HTAB which makes us go in the Linux page fault handler which totally
breaks because we still use the guest's SLB entries.

To work around that, let's introduce a second KVM guest mode that defines
that whenever we get a trap, we don't call the Linux handler or go into
the KVM exit code, but just jump over the faulting instruction.

That way a potentially bad lwz doesn't trigger any faults and we can later
on interpret the invalid instruction we fetched as "fetch didn't work".

Signed-off-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Avi Kivity <avi@redhat.com>
arch/powerpc/include/asm/kvm_asm.h
arch/powerpc/kvm/book3s_64_rmhandlers.S
arch/powerpc/kvm/book3s_64_slb.S
arch/powerpc/kvm/emulate.c

index af2abe74f54440199fdccfeccdd2de085ec7a203..aadf2dd6f84e15c919c9020e9271c3e8f7427a35 100644 (file)
 #define RESUME_HOST             RESUME_FLAG_HOST
 #define RESUME_HOST_NV          (RESUME_FLAG_HOST|RESUME_FLAG_NV)
 
+#define KVM_GUEST_MODE_NONE    0
+#define KVM_GUEST_MODE_GUEST   1
+#define KVM_GUEST_MODE_SKIP    2
+
+#define KVM_INST_FETCH_FAILED  -1
+
 #endif /* __POWERPC_KVM_ASM_H__ */
index cd9f0b609e4808e3333cb8633d70c8a26ea8ab77..9ad1c2645d6f7c2c6b1d96012aa47671127c87c7 100644 (file)
@@ -49,7 +49,7 @@ kvmppc_trampoline_\intno:
        mfcr    r12
        stw     r12, PACA_KVM_SCRATCH1(r13)
        lbz     r12, PACA_KVM_IN_GUEST(r13)
-       cmpwi   r12, 0
+       cmpwi   r12, KVM_GUEST_MODE_NONE
        bne     ..kvmppc_handler_hasmagic_\intno
        /* No KVM guest? Then jump back to the Linux handler! */
        lwz     r12, PACA_KVM_SCRATCH1(r13)
@@ -60,6 +60,11 @@ kvmppc_trampoline_\intno:
 
        /* Now we know we're handling a KVM guest */
 ..kvmppc_handler_hasmagic_\intno:
+
+       /* Should we just skip the faulting instruction? */
+       cmpwi   r12, KVM_GUEST_MODE_SKIP
+       beq     kvmppc_handler_skip_ins
+
        /* Let's store which interrupt we're handling */
        li      r12, \intno
 
@@ -85,6 +90,38 @@ INTERRUPT_TRAMPOLINE BOOK3S_INTERRUPT_PERFMON
 INTERRUPT_TRAMPOLINE   BOOK3S_INTERRUPT_ALTIVEC
 INTERRUPT_TRAMPOLINE   BOOK3S_INTERRUPT_VSX
 
+/*
+ * Bring us back to the faulting code, but skip the
+ * faulting instruction.
+ *
+ * This is a generic exit path from the interrupt
+ * trampolines above.
+ *
+ * Input Registers:
+ *
+ * R12               = free
+ * R13               = PACA
+ * PACA.KVM.SCRATCH0 = guest R12
+ * PACA.KVM.SCRATCH1 = guest CR
+ * SPRG_SCRATCH0     = guest R13
+ *
+ */
+kvmppc_handler_skip_ins:
+
+       /* Patch the IP to the next instruction */
+       mfsrr0  r12
+       addi    r12, r12, 4
+       mtsrr0  r12
+
+       /* Clean up all state */
+       lwz     r12, PACA_KVM_SCRATCH1(r13)
+       mtcr    r12
+       ld      r12, PACA_KVM_SCRATCH0(r13)
+       mfspr   r13, SPRN_SPRG_SCRATCH0
+
+       /* And get back into the code */
+       RFI
+
 /*
  * This trampoline brings us back to a real mode handler
  *
index 7188c11ed7d124fefb6edea196a0f0e45ea1a095..d07b88617b2cf6915c3c6c0e077540736ad85d91 100644 (file)
@@ -212,10 +212,6 @@ kvmppc_handler_trampoline_exit:
        mfdar   r5
        mfdsisr r6
 
-       /* Unset guest state */
-       li      r9, 0
-       stb     r9, PACA_KVM_IN_GUEST(r13)
-
        /*
         * In order for us to easily get the last instruction,
         * we got the #vmexit at, we exploit the fact that the
@@ -233,18 +229,28 @@ kvmppc_handler_trampoline_exit:
 
 ld_last_inst:
        /* Save off the guest instruction we're at */
+
+       /* Set guest mode to 'jump over instruction' so if lwz faults
+        * we'll just continue at the next IP. */
+       li      r9, KVM_GUEST_MODE_SKIP
+       stb     r9, PACA_KVM_IN_GUEST(r13)
+
        /*    1) enable paging for data */
        mfmsr   r9
        ori     r11, r9, MSR_DR                 /* Enable paging for data */
        mtmsr   r11
        /*    2) fetch the instruction */
-       /* XXX implement PACA_KVM_IN_GUEST=2 path to safely jump over this */
+       li      r0, KVM_INST_FETCH_FAILED       /* In case lwz faults */
        lwz     r0, 0(r3)
        /*    3) disable paging again */
        mtmsr   r9
 
 no_ld_last_inst:
 
+       /* Unset guest mode */
+       li      r9, KVM_GUEST_MODE_NONE
+       stb     r9, PACA_KVM_IN_GUEST(r13)
+
        /* Restore bolted entries from the shadow and fix it along the way */
 
        /* We don't store anything in entry 0, so we don't need to take care of it */
index 38219af0cd0e59b2faafb91a73945713637c928a..04e317c1bbeef239e88ff7e70d6c02bf0c6c41f5 100644 (file)
@@ -143,6 +143,10 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
 
        pr_debug(KERN_INFO "Emulating opcode %d / %d\n", get_op(inst), get_xop(inst));
 
+       /* Try again next time */
+       if (inst == KVM_INST_FETCH_FAILED)
+               return EMULATE_DONE;
+
        switch (get_op(inst)) {
        case OP_TRAP:
 #ifdef CONFIG_PPC64