KVM: x86: rdpmc emulation checks the counter incorrectly
authorNadav Amit <namit@cs.technion.ac.il>
Mon, 2 Jun 2014 15:34:09 +0000 (18:34 +0300)
committerPaolo Bonzini <pbonzini@redhat.com>
Wed, 18 Jun 2014 15:46:18 +0000 (17:46 +0200)
The rdpmc emulation checks that the counter (ECX) is not higher than 2, without
taking into considerations bits 30:31 role (e.g., bit 30 marks whether the
counter is fixed). The fix uses the pmu information for checking the validity
of the pmu counter.

Signed-off-by: Nadav Amit <namit@cs.technion.ac.il>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/include/asm/kvm_emulate.h
arch/x86/include/asm/kvm_host.h
arch/x86/kvm/emulate.c
arch/x86/kvm/pmu.c
arch/x86/kvm/x86.c

index a04fe4eb237d3321b0ef7af916e3350864e009cd..ffa2671a7f2f8f3403955144b3bd6447dea90d96 100644 (file)
@@ -194,6 +194,7 @@ struct x86_emulate_ops {
        int (*set_dr)(struct x86_emulate_ctxt *ctxt, int dr, ulong value);
        int (*set_msr)(struct x86_emulate_ctxt *ctxt, u32 msr_index, u64 data);
        int (*get_msr)(struct x86_emulate_ctxt *ctxt, u32 msr_index, u64 *pdata);
+       int (*check_pmc)(struct x86_emulate_ctxt *ctxt, u32 pmc);
        int (*read_pmc)(struct x86_emulate_ctxt *ctxt, u32 pmc, u64 *pdata);
        void (*halt)(struct x86_emulate_ctxt *ctxt);
        void (*wbinvd)(struct x86_emulate_ctxt *ctxt);
index 49314155b66c801103ffb89b40585ac649393fdb..63e020be3da78e1c426aaf06ee48e478e0247d66 100644 (file)
@@ -1070,6 +1070,7 @@ void kvm_pmu_cpuid_update(struct kvm_vcpu *vcpu);
 bool kvm_pmu_msr(struct kvm_vcpu *vcpu, u32 msr);
 int kvm_pmu_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *data);
 int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info);
+int kvm_pmu_check_pmc(struct kvm_vcpu *vcpu, unsigned pmc);
 int kvm_pmu_read_pmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data);
 void kvm_handle_pmu_event(struct kvm_vcpu *vcpu);
 void kvm_deliver_pmi(struct kvm_vcpu *vcpu);
index be3f7645e39d7b86f77e63130d771eebe231d1fa..3da8d82acb319e5fea897e84991c5e447d66ce19 100644 (file)
@@ -3507,7 +3507,7 @@ static int check_rdpmc(struct x86_emulate_ctxt *ctxt)
        u64 rcx = reg_read(ctxt, VCPU_REGS_RCX);
 
        if ((!(cr4 & X86_CR4_PCE) && ctxt->ops->cpl(ctxt)) ||
-           (rcx > 3))
+           ctxt->ops->check_pmc(ctxt, rcx))
                return emulate_gp(ctxt, 0);
 
        return X86EMUL_CONTINUE;
index cbecaa90399c1fbb1555ea5309a43f392f36fda2..3dd6accb64ec130d28db81f501f95aa7726c4002 100644 (file)
@@ -428,6 +428,15 @@ int kvm_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
        return 1;
 }
 
+int kvm_pmu_check_pmc(struct kvm_vcpu *vcpu, unsigned pmc)
+{
+       struct kvm_pmu *pmu = &vcpu->arch.pmu;
+       bool fixed = pmc & (1u << 30);
+       pmc &= ~(3u << 30);
+       return (!fixed && pmc >= pmu->nr_arch_gp_counters) ||
+               (fixed && pmc >= pmu->nr_arch_fixed_counters);
+}
+
 int kvm_pmu_read_pmc(struct kvm_vcpu *vcpu, unsigned pmc, u64 *data)
 {
        struct kvm_pmu *pmu = &vcpu->arch.pmu;
index f32a02578c0d1985b424edaf992aede89b5cd7b4..451d6acea808edbb1b67a872b55c1aea14938a04 100644 (file)
@@ -4762,6 +4762,12 @@ static int emulator_set_msr(struct x86_emulate_ctxt *ctxt,
        return kvm_set_msr(emul_to_vcpu(ctxt), &msr);
 }
 
+static int emulator_check_pmc(struct x86_emulate_ctxt *ctxt,
+                             u32 pmc)
+{
+       return kvm_pmu_check_pmc(emul_to_vcpu(ctxt), pmc);
+}
+
 static int emulator_read_pmc(struct x86_emulate_ctxt *ctxt,
                             u32 pmc, u64 *pdata)
 {
@@ -4838,6 +4844,7 @@ static const struct x86_emulate_ops emulate_ops = {
        .set_dr              = emulator_set_dr,
        .set_msr             = emulator_set_msr,
        .get_msr             = emulator_get_msr,
+       .check_pmc           = emulator_check_pmc,
        .read_pmc            = emulator_read_pmc,
        .halt                = emulator_halt,
        .wbinvd              = emulator_wbinvd,