Enhance MOV DR instruction emulation used by SVM so that it properly
injects faults and handles DR4/5 correctly.

Signed-off-by: Jan Kiszka <[email protected]>
---

 arch/x86/include/asm/kvm_host.h |    5 +--
 arch/x86/kvm/svm.c              |   72 ++++++++++++++++++++++-----------------
 arch/x86/kvm/x86.c              |   19 +---------
 3 files changed, 45 insertions(+), 51 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 6046e6f..38eef27 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -498,9 +498,8 @@ struct kvm_x86_ops {
        void (*set_idt)(struct kvm_vcpu *vcpu, struct descriptor_table *dt);
        void (*get_gdt)(struct kvm_vcpu *vcpu, struct descriptor_table *dt);
        void (*set_gdt)(struct kvm_vcpu *vcpu, struct descriptor_table *dt);
-       unsigned long (*get_dr)(struct kvm_vcpu *vcpu, int dr);
-       void (*set_dr)(struct kvm_vcpu *vcpu, int dr, unsigned long value,
-                      int *exception);
+       int (*get_dr)(struct kvm_vcpu *vcpu, int dr, unsigned long *dest);
+       int (*set_dr)(struct kvm_vcpu *vcpu, int dr, unsigned long value);
        void (*cache_reg)(struct kvm_vcpu *vcpu, enum kvm_reg reg);
        unsigned long (*get_rflags)(struct kvm_vcpu *vcpu);
        void (*set_rflags)(struct kvm_vcpu *vcpu, unsigned long rflags);
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 2df9b45..4305969 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -1106,76 +1106,86 @@ static void new_asid(struct vcpu_svm *svm, struct 
svm_cpu_data *svm_data)
        svm->vmcb->control.asid = svm_data->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 (vcpu->arch.cr4 & X86_CR4_DE) {
+                       kvm_queue_exception(vcpu, UD_VECTOR);
+                       return -1;
+               }
+               /* 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 (vcpu->arch.cr4 & X86_CR4_DE) {
+                       kvm_queue_exception(vcpu, UD_VECTOR);
+                       return -1;
+               }
+               /* 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 0;
 }
 
-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 (vcpu->arch.cr4 & X86_CR4_DE) {
+                       kvm_queue_exception(vcpu, UD_VECTOR);
+                       return -1;
+               }
+               /* fall through */
        case 6:
                if (value & 0xffffffff00000000ULL) {
-                       *exception = GP_VECTOR;
-                       return;
+                       kvm_inject_gp(vcpu, 0);
+                       return -1;
                }
                vcpu->arch.dr6 = (value & DR6_VOLATILE) | DR6_FIXED_1;
-               return;
+               break;
+       case 5:
+               if (vcpu->arch.cr4 & X86_CR4_DE) {
+                       kvm_queue_exception(vcpu, UD_VECTOR);
+                       return -1;
+               }
+               /* fall through */
        case 7:
                if (value & 0xffffffff00000000ULL) {
-                       *exception = GP_VECTOR;
-                       return;
+                       kvm_inject_gp(vcpu, 0);
+                       return -1;
                }
                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 0;
 }
 
 static int pf_interception(struct vcpu_svm *svm)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 036a2c5..2ca7a1d 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -2695,29 +2695,14 @@ int emulate_clts(struct kvm_vcpu *vcpu)
 
 int emulator_get_dr(struct x86_emulate_ctxt *ctxt, int dr, unsigned long *dest)
 {
-       struct kvm_vcpu *vcpu = ctxt->vcpu;
-
-       switch (dr) {
-       case 0 ... 3:
-               *dest = kvm_x86_ops->get_dr(vcpu, dr);
-               return X86EMUL_CONTINUE;
-       default:
-               pr_unimpl(vcpu, "%s: unexpected dr %u\n", __func__, dr);
-               return X86EMUL_UNHANDLEABLE;
-       }
+       return kvm_x86_ops->get_dr(ctxt->vcpu, dr, dest);
 }
 
 int emulator_set_dr(struct x86_emulate_ctxt *ctxt, int dr, unsigned long value)
 {
        unsigned long mask = (ctxt->mode == X86EMUL_MODE_PROT64) ? ~0ULL : ~0U;
-       int exception;
 
-       kvm_x86_ops->set_dr(ctxt->vcpu, dr, value & mask, &exception);
-       if (exception) {
-               /* FIXME: better handling */
-               return X86EMUL_UNHANDLEABLE;
-       }
-       return X86EMUL_CONTINUE;
+       return kvm_x86_ops->set_dr(ctxt->vcpu, dr, value & mask);
 }
 
 void kvm_report_emulation_failure(struct kvm_vcpu *vcpu, const char *context)

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to