[S390] pgtables: Fix race in enable_sie vs. page table ops
[linux-2.6.git] / arch / powerpc / kvm / powerpc.c
index bad40bd2d3ac5e966926487cb10df89513e2acc6..90a6fc422b238ccbd38becc6d36546c0a7ca0cd0 100644 (file)
@@ -27,6 +27,7 @@
 #include <asm/cputable.h>
 #include <asm/uaccess.h>
 #include <asm/kvm_ppc.h>
+#include <asm/tlbflush.h>
 
 
 gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
@@ -36,13 +37,12 @@ gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
 
 int kvm_cpu_has_interrupt(struct kvm_vcpu *v)
 {
-       /* XXX implement me */
-       return 0;
+       return !!(v->arch.pending_exceptions);
 }
 
 int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
 {
-       return 1;
+       return !(v->arch.msr & MSR_WE);
 }
 
 
@@ -146,6 +146,9 @@ int kvm_dev_ioctl_check_extension(long ext)
        case KVM_CAP_USER_MEMORY:
                r = 1;
                break;
+       case KVM_CAP_COALESCED_MMIO:
+               r = KVM_COALESCED_MMIO_PAGE_OFFSET;
+               break;
        default:
                r = 0;
                break;
@@ -168,6 +171,10 @@ int kvm_arch_set_memory_region(struct kvm *kvm,
        return 0;
 }
 
+void kvm_arch_flush_shadow(struct kvm *kvm)
+{
+}
+
 struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, unsigned int id)
 {
        struct kvm_vcpu *vcpu;
@@ -214,6 +221,11 @@ static void kvmppc_decrementer_func(unsigned long data)
        struct kvm_vcpu *vcpu = (struct kvm_vcpu *)data;
 
        kvmppc_queue_exception(vcpu, BOOKE_INTERRUPT_DECREMENTER);
+
+       if (waitqueue_active(&vcpu->wq)) {
+               wake_up_interruptible(&vcpu->wq);
+               vcpu->stat.halt_wakeup++;
+       }
 }
 
 int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
@@ -228,22 +240,114 @@ void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu)
 {
 }
 
-void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+/* Note: clearing MSR[DE] just means that the debug interrupt will not be
+ * delivered *immediately*. Instead, it simply sets the appropriate DBSR bits.
+ * If those DBSR bits are still set when MSR[DE] is re-enabled, the interrupt
+ * will be delivered as an "imprecise debug event" (which is indicated by
+ * DBSR[IDE].
+ */
+static void kvmppc_disable_debug_interrupts(void)
 {
+       mtmsr(mfmsr() & ~MSR_DE);
 }
 
-void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
+static void kvmppc_restore_host_debug_state(struct kvm_vcpu *vcpu)
+{
+       kvmppc_disable_debug_interrupts();
+
+       mtspr(SPRN_IAC1, vcpu->arch.host_iac[0]);
+       mtspr(SPRN_IAC2, vcpu->arch.host_iac[1]);
+       mtspr(SPRN_IAC3, vcpu->arch.host_iac[2]);
+       mtspr(SPRN_IAC4, vcpu->arch.host_iac[3]);
+       mtspr(SPRN_DBCR1, vcpu->arch.host_dbcr1);
+       mtspr(SPRN_DBCR2, vcpu->arch.host_dbcr2);
+       mtspr(SPRN_DBCR0, vcpu->arch.host_dbcr0);
+       mtmsr(vcpu->arch.host_msr);
+}
+
+static void kvmppc_load_guest_debug_registers(struct kvm_vcpu *vcpu)
+{
+       struct kvm_guest_debug *dbg = &vcpu->guest_debug;
+       u32 dbcr0 = 0;
+
+       vcpu->arch.host_msr = mfmsr();
+       kvmppc_disable_debug_interrupts();
+
+       /* Save host debug register state. */
+       vcpu->arch.host_iac[0] = mfspr(SPRN_IAC1);
+       vcpu->arch.host_iac[1] = mfspr(SPRN_IAC2);
+       vcpu->arch.host_iac[2] = mfspr(SPRN_IAC3);
+       vcpu->arch.host_iac[3] = mfspr(SPRN_IAC4);
+       vcpu->arch.host_dbcr0 = mfspr(SPRN_DBCR0);
+       vcpu->arch.host_dbcr1 = mfspr(SPRN_DBCR1);
+       vcpu->arch.host_dbcr2 = mfspr(SPRN_DBCR2);
+
+       /* set registers up for guest */
+
+       if (dbg->bp[0]) {
+               mtspr(SPRN_IAC1, dbg->bp[0]);
+               dbcr0 |= DBCR0_IAC1 | DBCR0_IDM;
+       }
+       if (dbg->bp[1]) {
+               mtspr(SPRN_IAC2, dbg->bp[1]);
+               dbcr0 |= DBCR0_IAC2 | DBCR0_IDM;
+       }
+       if (dbg->bp[2]) {
+               mtspr(SPRN_IAC3, dbg->bp[2]);
+               dbcr0 |= DBCR0_IAC3 | DBCR0_IDM;
+       }
+       if (dbg->bp[3]) {
+               mtspr(SPRN_IAC4, dbg->bp[3]);
+               dbcr0 |= DBCR0_IAC4 | DBCR0_IDM;
+       }
+
+       mtspr(SPRN_DBCR0, dbcr0);
+       mtspr(SPRN_DBCR1, 0);
+       mtspr(SPRN_DBCR2, 0);
+}
+
+void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 {
+       int i;
+
+       if (vcpu->guest_debug.enabled)
+               kvmppc_load_guest_debug_registers(vcpu);
+
+       /* Mark every guest entry in the shadow TLB entry modified, so that they
+        * will all be reloaded on the next vcpu run (instead of being
+        * demand-faulted). */
+       for (i = 0; i <= tlb_44x_hwater; i++)
+               kvmppc_tlbe_set_modified(vcpu, i);
 }
 
-void decache_vcpus_on_cpu(int cpu)
+void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
 {
+       if (vcpu->guest_debug.enabled)
+               kvmppc_restore_host_debug_state(vcpu);
+
+       /* Don't leave guest TLB entries resident when being de-scheduled. */
+       /* XXX It would be nice to differentiate between heavyweight exit and
+        * sched_out here, since we could avoid the TLB flush for heavyweight
+        * exits. */
+       _tlbia();
 }
 
 int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu,
                                     struct kvm_debug_guest *dbg)
 {
-       return -ENOTSUPP;
+       int i;
+
+       vcpu->guest_debug.enabled = dbg->enabled;
+       if (vcpu->guest_debug.enabled) {
+               for (i=0; i < ARRAY_SIZE(vcpu->guest_debug.bp); i++) {
+                       if (dbg->breakpoints[i].enabled)
+                               vcpu->guest_debug.bp[i] = dbg->breakpoints[i].address;
+                       else
+                               vcpu->guest_debug.bp[i] = 0;
+               }
+       }
+
+       return 0;
 }
 
 static void kvmppc_complete_dcr_load(struct kvm_vcpu *vcpu,
@@ -339,6 +443,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
        int r;
        sigset_t sigsaved;
 
+       vcpu_load(vcpu);
+
        if (vcpu->sigset_active)
                sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);
 
@@ -363,12 +469,20 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
        if (vcpu->sigset_active)
                sigprocmask(SIG_SETMASK, &sigsaved, NULL);
 
+       vcpu_put(vcpu);
+
        return r;
 }
 
 int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq)
 {
        kvmppc_queue_exception(vcpu, BOOKE_INTERRUPT_EXTERNAL);
+
+       if (waitqueue_active(&vcpu->wq)) {
+               wake_up_interruptible(&vcpu->wq);
+               vcpu->stat.halt_wakeup++;
+       }
+
        return 0;
 }