KVM: x86: hyper-v: Fix Hyper-V context null-ptr-deref
[linux-2.6-microblaze.git] / arch / x86 / kvm / hyperv.c
index 1482cad..58fa8c0 100644 (file)
@@ -37,6 +37,9 @@
 #include "trace.h"
 #include "irq.h"
 
+/* "Hv#1" signature */
+#define HYPERV_CPUID_SIGNATURE_EAX 0x31237648
+
 #define KVM_HV_MAX_SPARSE_VCPU_SET_BITS DIV_ROUND_UP(KVM_MAX_VCPUS, 64)
 
 static void stimer_mark_pending(struct kvm_vcpu_hv_stimer *stimer,
@@ -142,10 +145,10 @@ static struct kvm_vcpu *get_vcpu_by_vpidx(struct kvm *kvm, u32 vpidx)
                return NULL;
 
        vcpu = kvm_get_vcpu(kvm, vpidx);
-       if (vcpu && to_hv_vcpu(vcpu)->vp_index == vpidx)
+       if (vcpu && kvm_hv_get_vpindex(vcpu) == vpidx)
                return vcpu;
        kvm_for_each_vcpu(i, vcpu, kvm)
-               if (to_hv_vcpu(vcpu)->vp_index == vpidx)
+               if (kvm_hv_get_vpindex(vcpu) == vpidx)
                        return vcpu;
        return NULL;
 }
@@ -156,7 +159,7 @@ static struct kvm_vcpu_hv_synic *synic_get(struct kvm *kvm, u32 vpidx)
        struct kvm_vcpu_hv_synic *synic;
 
        vcpu = get_vcpu_by_vpidx(kvm, vpidx);
-       if (!vcpu)
+       if (!vcpu || !to_hv_vcpu(vcpu))
                return NULL;
        synic = to_hv_synic(vcpu);
        return (synic->active) ? synic : NULL;
@@ -377,9 +380,7 @@ static int syndbg_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata, bool host)
                break;
        }
 
-       trace_kvm_hv_syndbg_get_msr(vcpu->vcpu_id,
-                                   to_hv_vcpu(vcpu)->vp_index, msr,
-                                   *pdata);
+       trace_kvm_hv_syndbg_get_msr(vcpu->vcpu_id, kvm_hv_get_vpindex(vcpu), msr, *pdata);
 
        return 0;
 }
@@ -806,6 +807,9 @@ void kvm_hv_process_stimers(struct kvm_vcpu *vcpu)
        u64 time_now, exp_time;
        int i;
 
+       if (!hv_vcpu)
+               return;
+
        for (i = 0; i < ARRAY_SIZE(hv_vcpu->stimer); i++)
                if (test_and_clear_bit(i, hv_vcpu->stimer_pending_bitmap)) {
                        stimer = &hv_vcpu->stimer[i];
@@ -834,14 +838,23 @@ void kvm_hv_vcpu_uninit(struct kvm_vcpu *vcpu)
        struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
        int i;
 
+       if (!hv_vcpu)
+               return;
+
        for (i = 0; i < ARRAY_SIZE(hv_vcpu->stimer); i++)
                stimer_cleanup(&hv_vcpu->stimer[i]);
+
+       kfree(hv_vcpu);
+       vcpu->arch.hyperv = NULL;
 }
 
 bool kvm_hv_assist_page_enabled(struct kvm_vcpu *vcpu)
 {
        struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
 
+       if (!hv_vcpu)
+               return false;
+
        if (!(hv_vcpu->hv_vapic & HV_X64_MSR_VP_ASSIST_PAGE_ENABLE))
                return false;
        return vcpu->arch.pv_eoi.msr_val & KVM_MSR_ENABLED;
@@ -882,28 +895,41 @@ static void stimer_init(struct kvm_vcpu_hv_stimer *stimer, int timer_index)
        stimer_prepare_msg(stimer);
 }
 
-void kvm_hv_vcpu_init(struct kvm_vcpu *vcpu)
+static int kvm_hv_vcpu_init(struct kvm_vcpu *vcpu)
 {
-       struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
+       struct kvm_vcpu_hv *hv_vcpu;
        int i;
 
+       hv_vcpu = kzalloc(sizeof(struct kvm_vcpu_hv), GFP_KERNEL_ACCOUNT);
+       if (!hv_vcpu)
+               return -ENOMEM;
+
+       vcpu->arch.hyperv = hv_vcpu;
+       hv_vcpu->vcpu = vcpu;
+
        synic_init(&hv_vcpu->synic);
 
        bitmap_zero(hv_vcpu->stimer_pending_bitmap, HV_SYNIC_STIMER_COUNT);
        for (i = 0; i < ARRAY_SIZE(hv_vcpu->stimer); i++)
                stimer_init(&hv_vcpu->stimer[i], i);
-}
-
-void kvm_hv_vcpu_postcreate(struct kvm_vcpu *vcpu)
-{
-       struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
 
        hv_vcpu->vp_index = kvm_vcpu_get_idx(vcpu);
+
+       return 0;
 }
 
 int kvm_hv_activate_synic(struct kvm_vcpu *vcpu, bool dont_zero_synic_pages)
 {
-       struct kvm_vcpu_hv_synic *synic = to_hv_synic(vcpu);
+       struct kvm_vcpu_hv_synic *synic;
+       int r;
+
+       if (!to_hv_vcpu(vcpu)) {
+               r = kvm_hv_vcpu_init(vcpu);
+               if (r)
+                       return r;
+       }
+
+       synic = to_hv_synic(vcpu);
 
        /*
         * Hyper-V SynIC auto EOI SINT's are
@@ -1457,6 +1483,14 @@ int kvm_hv_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data, bool host)
 {
        struct kvm_hv *hv = to_kvm_hv(vcpu->kvm);
 
+       if (!host && !vcpu->arch.hyperv_enabled)
+               return 1;
+
+       if (!to_hv_vcpu(vcpu)) {
+               if (kvm_hv_vcpu_init(vcpu))
+                       return 1;
+       }
+
        if (kvm_hv_msr_partition_wide(msr)) {
                int r;
 
@@ -1472,6 +1506,14 @@ int kvm_hv_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata, bool host)
 {
        struct kvm_hv *hv = to_kvm_hv(vcpu->kvm);
 
+       if (!host && !vcpu->arch.hyperv_enabled)
+               return 1;
+
+       if (!to_hv_vcpu(vcpu)) {
+               if (kvm_hv_vcpu_init(vcpu))
+                       return 1;
+       }
+
        if (kvm_hv_msr_partition_wide(msr)) {
                int r;
 
@@ -1504,8 +1546,7 @@ static __always_inline unsigned long *sparse_set_to_vcpu_mask(
 
        bitmap_zero(vcpu_bitmap, KVM_MAX_VCPUS);
        kvm_for_each_vcpu(i, vcpu, kvm) {
-               if (test_bit(to_hv_vcpu(vcpu)->vp_index,
-                            (unsigned long *)vp_bitmap))
+               if (test_bit(kvm_hv_get_vpindex(vcpu), (unsigned long *)vp_bitmap))
                        __set_bit(i, vcpu_bitmap);
        }
        return vcpu_bitmap;
@@ -1686,9 +1727,20 @@ ret_success:
        return HV_STATUS_SUCCESS;
 }
 
-bool kvm_hv_hypercall_enabled(struct kvm *kvm)
+void kvm_hv_set_cpuid(struct kvm_vcpu *vcpu)
+{
+       struct kvm_cpuid_entry2 *entry;
+
+       entry = kvm_find_cpuid_entry(vcpu, HYPERV_CPUID_INTERFACE, 0);
+       if (entry && entry->eax == HYPERV_CPUID_SIGNATURE_EAX)
+               vcpu->arch.hyperv_enabled = true;
+       else
+               vcpu->arch.hyperv_enabled = false;
+}
+
+bool kvm_hv_hypercall_enabled(struct kvm_vcpu *vcpu)
 {
-       return to_kvm_hv(kvm)->hv_guest_os_id != 0;
+       return vcpu->arch.hyperv_enabled && to_kvm_hv(vcpu->kvm)->hv_guest_os_id;
 }
 
 static void kvm_hv_hypercall_set_result(struct kvm_vcpu *vcpu, u64 result)
@@ -2021,8 +2073,7 @@ int kvm_get_hv_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid,
                        break;
 
                case HYPERV_CPUID_INTERFACE:
-                       memcpy(signature, "Hv#1\0\0\0\0\0\0\0\0", 12);
-                       ent->eax = signature[0];
+                       ent->eax = HYPERV_CPUID_SIGNATURE_EAX;
                        break;
 
                case HYPERV_CPUID_VERSION: