Merge branch 'fix/misc' into for-linus
[pandora-kernel.git] / arch / x86 / kvm / svm.c
index 3899c2d..445c594 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/highmem.h>
 #include <linux/sched.h>
 #include <linux/ftrace_event.h>
+#include <linux/slab.h>
 
 #include <asm/desc.h>
 
@@ -231,7 +232,7 @@ static void svm_set_efer(struct kvm_vcpu *vcpu, u64 efer)
                efer &= ~EFER_LME;
 
        to_svm(vcpu)->vmcb->save.efer = efer | EFER_SVME;
-       vcpu->arch.shadow_efer = efer;
+       vcpu->arch.efer = efer;
 }
 
 static void svm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr,
@@ -540,6 +541,8 @@ static void init_vmcb(struct vcpu_svm *svm)
        struct vmcb_control_area *control = &svm->vmcb->control;
        struct vmcb_save_area *save = &svm->vmcb->save;
 
+       svm->vcpu.fpu_active = 1;
+
        control->intercept_cr_read =    INTERCEPT_CR0_MASK |
                                        INTERCEPT_CR3_MASK |
                                        INTERCEPT_CR4_MASK;
@@ -552,13 +555,19 @@ static void init_vmcb(struct vcpu_svm *svm)
        control->intercept_dr_read =    INTERCEPT_DR0_MASK |
                                        INTERCEPT_DR1_MASK |
                                        INTERCEPT_DR2_MASK |
-                                       INTERCEPT_DR3_MASK;
+                                       INTERCEPT_DR3_MASK |
+                                       INTERCEPT_DR4_MASK |
+                                       INTERCEPT_DR5_MASK |
+                                       INTERCEPT_DR6_MASK |
+                                       INTERCEPT_DR7_MASK;
 
        control->intercept_dr_write =   INTERCEPT_DR0_MASK |
                                        INTERCEPT_DR1_MASK |
                                        INTERCEPT_DR2_MASK |
                                        INTERCEPT_DR3_MASK |
+                                       INTERCEPT_DR4_MASK |
                                        INTERCEPT_DR5_MASK |
+                                       INTERCEPT_DR6_MASK |
                                        INTERCEPT_DR7_MASK;
 
        control->intercept_exceptions = (1 << PF_VECTOR) |
@@ -569,6 +578,7 @@ static void init_vmcb(struct vcpu_svm *svm)
        control->intercept =    (1ULL << INTERCEPT_INTR) |
                                (1ULL << INTERCEPT_NMI) |
                                (1ULL << INTERCEPT_SMI) |
+                               (1ULL << INTERCEPT_SELECTIVE_CR0) |
                                (1ULL << INTERCEPT_CPUID) |
                                (1ULL << INTERCEPT_INVD) |
                                (1ULL << INTERCEPT_HLT) |
@@ -641,10 +651,8 @@ static void init_vmcb(struct vcpu_svm *svm)
                control->intercept &= ~((1ULL << INTERCEPT_TASK_SWITCH) |
                                        (1ULL << INTERCEPT_INVLPG));
                control->intercept_exceptions &= ~(1 << PF_VECTOR);
-               control->intercept_cr_read &= ~(INTERCEPT_CR0_MASK|
-                                               INTERCEPT_CR3_MASK);
-               control->intercept_cr_write &= ~(INTERCEPT_CR0_MASK|
-                                                INTERCEPT_CR3_MASK);
+               control->intercept_cr_read &= ~INTERCEPT_CR3_MASK;
+               control->intercept_cr_write &= ~INTERCEPT_CR3_MASK;
                save->g_pat = 0x0007040600070406ULL;
                save->cr3 = 0;
                save->cr4 = 0;
@@ -730,7 +738,6 @@ static struct kvm_vcpu *svm_create_vcpu(struct kvm *kvm, unsigned int id)
        init_vmcb(svm);
 
        fx_init(&svm->vcpu);
-       svm->vcpu.fpu_active = 1;
        svm->vcpu.arch.apic_base = 0xfee00000 | MSR_IA32_APICBASE_ENABLE;
        if (kvm_vcpu_is_bsp(&svm->vcpu))
                svm->vcpu.arch.apic_base |= MSR_IA32_APICBASE_BSP;
@@ -964,38 +971,51 @@ static void svm_decache_cr4_guest_bits(struct kvm_vcpu *vcpu)
 {
 }
 
+static void update_cr0_intercept(struct vcpu_svm *svm)
+{
+       ulong gcr0 = svm->vcpu.arch.cr0;
+       u64 *hcr0 = &svm->vmcb->save.cr0;
+
+       if (!svm->vcpu.fpu_active)
+               *hcr0 |= SVM_CR0_SELECTIVE_MASK;
+       else
+               *hcr0 = (*hcr0 & ~SVM_CR0_SELECTIVE_MASK)
+                       | (gcr0 & SVM_CR0_SELECTIVE_MASK);
+
+
+       if (gcr0 == *hcr0 && svm->vcpu.fpu_active) {
+               svm->vmcb->control.intercept_cr_read &= ~INTERCEPT_CR0_MASK;
+               svm->vmcb->control.intercept_cr_write &= ~INTERCEPT_CR0_MASK;
+       } else {
+               svm->vmcb->control.intercept_cr_read |= INTERCEPT_CR0_MASK;
+               svm->vmcb->control.intercept_cr_write |= INTERCEPT_CR0_MASK;
+       }
+}
+
 static void svm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
 
 #ifdef CONFIG_X86_64
-       if (vcpu->arch.shadow_efer & EFER_LME) {
+       if (vcpu->arch.efer & EFER_LME) {
                if (!is_paging(vcpu) && (cr0 & X86_CR0_PG)) {
-                       vcpu->arch.shadow_efer |= EFER_LMA;
+                       vcpu->arch.efer |= EFER_LMA;
                        svm->vmcb->save.efer |= EFER_LMA | EFER_LME;
                }
 
                if (is_paging(vcpu) && !(cr0 & X86_CR0_PG)) {
-                       vcpu->arch.shadow_efer &= ~EFER_LMA;
+                       vcpu->arch.efer &= ~EFER_LMA;
                        svm->vmcb->save.efer &= ~(EFER_LMA | EFER_LME);
                }
        }
 #endif
-       if (npt_enabled)
-               goto set;
+       vcpu->arch.cr0 = cr0;
 
-       if (kvm_read_cr0_bits(vcpu, X86_CR0_TS) && !(cr0 & X86_CR0_TS)) {
-               svm->vmcb->control.intercept_exceptions &= ~(1 << NM_VECTOR);
-               vcpu->fpu_active = 1;
-       }
+       if (!npt_enabled)
+               cr0 |= X86_CR0_PG | X86_CR0_WP;
 
-       vcpu->arch.cr0 = cr0;
-       cr0 |= X86_CR0_PG | X86_CR0_WP;
-       if (!vcpu->fpu_active) {
-               svm->vmcb->control.intercept_exceptions |= (1 << NM_VECTOR);
+       if (!vcpu->fpu_active)
                cr0 |= X86_CR0_TS;
-       }
-set:
        /*
         * re-enable caching here because the QEMU bios
         * does not do it - this results in some delay at
@@ -1003,6 +1023,7 @@ set:
         */
        cr0 &= ~(X86_CR0_CD | X86_CR0_NW);
        svm->vmcb->save.cr0 = cr0;
+       update_cr0_intercept(svm);
 }
 
 static void svm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
@@ -1108,76 +1129,70 @@ static void new_asid(struct vcpu_svm *svm, struct svm_cpu_data *sd)
        svm->vmcb->control.asid = sd->next_asid++;
 }
 
-static unsigned long svm_get_dr(struct kvm_vcpu *vcpu, int dr)
+static int svm_get_dr(struct kvm_vcpu *vcpu, int dr, unsigned long *dest)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
-       unsigned long val;
 
        switch (dr) {
        case 0 ... 3:
-               val = vcpu->arch.db[dr];
+               *dest = vcpu->arch.db[dr];
                break;
+       case 4:
+               if (kvm_read_cr4_bits(vcpu, X86_CR4_DE))
+                       return EMULATE_FAIL; /* will re-inject UD */
+               /* fall through */
        case 6:
                if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
-                       val = vcpu->arch.dr6;
+                       *dest = vcpu->arch.dr6;
                else
-                       val = svm->vmcb->save.dr6;
+                       *dest = svm->vmcb->save.dr6;
                break;
+       case 5:
+               if (kvm_read_cr4_bits(vcpu, X86_CR4_DE))
+                       return EMULATE_FAIL; /* will re-inject UD */
+               /* fall through */
        case 7:
                if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)
-                       val = vcpu->arch.dr7;
+                       *dest = vcpu->arch.dr7;
                else
-                       val = svm->vmcb->save.dr7;
+                       *dest = svm->vmcb->save.dr7;
                break;
-       default:
-               val = 0;
        }
 
-       return val;
+       return EMULATE_DONE;
 }
 
-static void svm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long value,
-                      int *exception)
+static int svm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long value)
 {
        struct vcpu_svm *svm = to_svm(vcpu);
 
-       *exception = 0;
-
        switch (dr) {
        case 0 ... 3:
                vcpu->arch.db[dr] = value;
                if (!(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP))
                        vcpu->arch.eff_db[dr] = value;
-               return;
-       case 4 ... 5:
-               if (vcpu->arch.cr4 & X86_CR4_DE)
-                       *exception = UD_VECTOR;
-               return;
+               break;
+       case 4:
+               if (kvm_read_cr4_bits(vcpu, X86_CR4_DE))
+                       return EMULATE_FAIL; /* will re-inject UD */
+               /* fall through */
        case 6:
-               if (value & 0xffffffff00000000ULL) {
-                       *exception = GP_VECTOR;
-                       return;
-               }
                vcpu->arch.dr6 = (value & DR6_VOLATILE) | DR6_FIXED_1;
-               return;
+               break;
+       case 5:
+               if (kvm_read_cr4_bits(vcpu, X86_CR4_DE))
+                       return EMULATE_FAIL; /* will re-inject UD */
+               /* fall through */
        case 7:
-               if (value & 0xffffffff00000000ULL) {
-                       *exception = GP_VECTOR;
-                       return;
-               }
                vcpu->arch.dr7 = (value & DR7_VOLATILE) | DR7_FIXED_1;
                if (!(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP)) {
                        svm->vmcb->save.dr7 = vcpu->arch.dr7;
                        vcpu->arch.switch_db_regs = (value & DR7_BP_EN_MASK);
                }
-               return;
-       default:
-               /* FIXME: Possible case? */
-               printk(KERN_DEBUG "%s: unexpected dr %u\n",
-                      __func__, dr);
-               *exception = UD_VECTOR;
-               return;
+               break;
        }
+
+       return EMULATE_DONE;
 }
 
 static int pf_interception(struct vcpu_svm *svm)
@@ -1245,13 +1260,17 @@ static int ud_interception(struct vcpu_svm *svm)
        return 1;
 }
 
-static int nm_interception(struct vcpu_svm *svm)
+static void svm_fpu_activate(struct kvm_vcpu *vcpu)
 {
+       struct vcpu_svm *svm = to_svm(vcpu);
        svm->vmcb->control.intercept_exceptions &= ~(1 << NM_VECTOR);
-       if (!kvm_read_cr0_bits(&svm->vcpu, X86_CR0_TS))
-               svm->vmcb->save.cr0 &= ~X86_CR0_TS;
        svm->vcpu.fpu_active = 1;
+       update_cr0_intercept(svm);
+}
 
+static int nm_interception(struct vcpu_svm *svm)
+{
+       svm_fpu_activate(&svm->vcpu);
        return 1;
 }
 
@@ -1343,7 +1362,7 @@ static int vmmcall_interception(struct vcpu_svm *svm)
 
 static int nested_svm_check_permissions(struct vcpu_svm *svm)
 {
-       if (!(svm->vcpu.arch.shadow_efer & EFER_SVME)
+       if (!(svm->vcpu.arch.efer & EFER_SVME)
            || !is_paging(&svm->vcpu)) {
                kvm_queue_exception(&svm->vcpu, UD_VECTOR);
                return 1;
@@ -1746,7 +1765,7 @@ static bool nested_svm_vmrun(struct vcpu_svm *svm)
        hsave->save.ds     = vmcb->save.ds;
        hsave->save.gdtr   = vmcb->save.gdtr;
        hsave->save.idtr   = vmcb->save.idtr;
-       hsave->save.efer   = svm->vcpu.arch.shadow_efer;
+       hsave->save.efer   = svm->vcpu.arch.efer;
        hsave->save.cr0    = kvm_read_cr0(&svm->vcpu);
        hsave->save.cr4    = svm->vcpu.arch.cr4;
        hsave->save.rflags = vmcb->save.rflags;
@@ -2159,9 +2178,10 @@ static int rdmsr_interception(struct vcpu_svm *svm)
        u32 ecx = svm->vcpu.arch.regs[VCPU_REGS_RCX];
        u64 data;
 
-       if (svm_get_msr(&svm->vcpu, ecx, &data))
+       if (svm_get_msr(&svm->vcpu, ecx, &data)) {
+               trace_kvm_msr_read_ex(ecx);
                kvm_inject_gp(&svm->vcpu, 0);
-       else {
+       else {
                trace_kvm_msr_read(ecx, data);
 
                svm->vcpu.arch.regs[VCPU_REGS_RAX] = data & 0xffffffff;
@@ -2253,13 +2273,15 @@ static int wrmsr_interception(struct vcpu_svm *svm)
        u64 data = (svm->vcpu.arch.regs[VCPU_REGS_RAX] & -1u)
                | ((u64)(svm->vcpu.arch.regs[VCPU_REGS_RDX] & -1u) << 32);
 
-       trace_kvm_msr_write(ecx, data);
 
        svm->next_rip = kvm_rip_read(&svm->vcpu) + 2;
-       if (svm_set_msr(&svm->vcpu, ecx, data))
+       if (svm_set_msr(&svm->vcpu, ecx, data)) {
+               trace_kvm_msr_write_ex(ecx, data);
                kvm_inject_gp(&svm->vcpu, 0);
-       else
+       } else {
+               trace_kvm_msr_write(ecx, data);
                skip_emulated_instruction(&svm->vcpu);
+       }
        return 1;
 }
 
@@ -2303,7 +2325,7 @@ static int (*svm_exit_handlers[])(struct vcpu_svm *svm) = {
        [SVM_EXIT_READ_CR3]                     = emulate_on_interception,
        [SVM_EXIT_READ_CR4]                     = emulate_on_interception,
        [SVM_EXIT_READ_CR8]                     = emulate_on_interception,
-       /* for now: */
+       [SVM_EXIT_CR0_SEL_WRITE]                = emulate_on_interception,
        [SVM_EXIT_WRITE_CR0]                    = emulate_on_interception,
        [SVM_EXIT_WRITE_CR3]                    = emulate_on_interception,
        [SVM_EXIT_WRITE_CR4]                    = emulate_on_interception,
@@ -2312,11 +2334,17 @@ static int (*svm_exit_handlers[])(struct vcpu_svm *svm) = {
        [SVM_EXIT_READ_DR1]                     = emulate_on_interception,
        [SVM_EXIT_READ_DR2]                     = emulate_on_interception,
        [SVM_EXIT_READ_DR3]                     = emulate_on_interception,
+       [SVM_EXIT_READ_DR4]                     = emulate_on_interception,
+       [SVM_EXIT_READ_DR5]                     = emulate_on_interception,
+       [SVM_EXIT_READ_DR6]                     = emulate_on_interception,
+       [SVM_EXIT_READ_DR7]                     = emulate_on_interception,
        [SVM_EXIT_WRITE_DR0]                    = emulate_on_interception,
        [SVM_EXIT_WRITE_DR1]                    = emulate_on_interception,
        [SVM_EXIT_WRITE_DR2]                    = emulate_on_interception,
        [SVM_EXIT_WRITE_DR3]                    = emulate_on_interception,
+       [SVM_EXIT_WRITE_DR4]                    = emulate_on_interception,
        [SVM_EXIT_WRITE_DR5]                    = emulate_on_interception,
+       [SVM_EXIT_WRITE_DR6]                    = emulate_on_interception,
        [SVM_EXIT_WRITE_DR7]                    = emulate_on_interception,
        [SVM_EXIT_EXCP_BASE + DB_VECTOR]        = db_interception,
        [SVM_EXIT_EXCP_BASE + BP_VECTOR]        = bp_interception,
@@ -2389,21 +2417,10 @@ static int handle_exit(struct kvm_vcpu *vcpu)
 
        svm_complete_interrupts(svm);
 
-       if (npt_enabled) {
-               int mmu_reload = 0;
-               if ((kvm_read_cr0_bits(vcpu, X86_CR0_PG) ^ svm->vmcb->save.cr0)
-                   & X86_CR0_PG) {
-                       svm_set_cr0(vcpu, svm->vmcb->save.cr0);
-                       mmu_reload = 1;
-               }
+       if (!(svm->vmcb->control.intercept_cr_write & INTERCEPT_CR0_MASK))
                vcpu->arch.cr0 = svm->vmcb->save.cr0;
+       if (npt_enabled)
                vcpu->arch.cr3 = svm->vmcb->save.cr3;
-               if (mmu_reload) {
-                       kvm_mmu_reset_context(vcpu);
-                       kvm_mmu_load(vcpu);
-               }
-       }
-
 
        if (svm->vmcb->control.exit_code == SVM_EXIT_ERR) {
                kvm_run->exit_reason = KVM_EXIT_FAIL_ENTRY;
@@ -2805,12 +2822,6 @@ static void svm_set_cr3(struct kvm_vcpu *vcpu, unsigned long root)
 
        svm->vmcb->save.cr3 = root;
        force_new_asid(vcpu);
-
-       if (vcpu->fpu_active) {
-               svm->vmcb->control.intercept_exceptions |= (1 << NM_VECTOR);
-               svm->vmcb->save.cr0 |= X86_CR0_TS;
-               vcpu->fpu_active = 0;
-       }
 }
 
 static int is_disabled(void)
@@ -2926,6 +2937,14 @@ static bool svm_rdtscp_supported(void)
        return false;
 }
 
+static void svm_fpu_deactivate(struct kvm_vcpu *vcpu)
+{
+       struct vcpu_svm *svm = to_svm(vcpu);
+
+       update_cr0_intercept(svm);
+       svm->vmcb->control.intercept_exceptions |= 1 << NM_VECTOR;
+}
+
 static struct kvm_x86_ops svm_x86_ops = {
        .cpu_has_kvm_support = has_svm,
        .disabled_by_bios = is_disabled,
@@ -2967,6 +2986,8 @@ static struct kvm_x86_ops svm_x86_ops = {
        .cache_reg = svm_cache_reg,
        .get_rflags = svm_get_rflags,
        .set_rflags = svm_set_rflags,
+       .fpu_activate = svm_fpu_activate,
+       .fpu_deactivate = svm_fpu_deactivate,
 
        .tlb_flush = svm_flush_tlb,