From: Nadav Amit <[email protected]>

This adds support for guest data and I/O breakpoints during instruction
emulation.

Watchpoints are examined during data and io interceptions: segmented_read,
segmented_write, em_in, em_out, segmented_read_std and kvm_fast_pio_out.

When such a breakpoint is triggered, trap is reported by DB_VECTOR
exception.

Signed-off-by: Andrey Isakov <[email protected]>
Signed-off-by: Rami Burstein <[email protected]>
Signed-off-by: Nadav Amit <[email protected]>
---
 arch/x86/include/asm/kvm_emulate.h |  3 ++
 arch/x86/kvm/emulate.c             | 32 +++++++++++++++++
 arch/x86/kvm/x86.c                 | 74 +++++++++++++++++++++++++++++---------
 3 files changed, 92 insertions(+), 17 deletions(-)

diff --git a/arch/x86/include/asm/kvm_emulate.h 
b/arch/x86/include/asm/kvm_emulate.h
index e16466e..f6d5d6c 100644
--- a/arch/x86/include/asm/kvm_emulate.h
+++ b/arch/x86/include/asm/kvm_emulate.h
@@ -211,6 +211,9 @@ struct x86_emulate_ops {
        void (*get_cpuid)(struct x86_emulate_ctxt *ctxt,
                          u32 *eax, u32 *ebx, u32 *ecx, u32 *edx);
        void (*set_nmi_mask)(struct x86_emulate_ctxt *ctxt, bool masked);
+       bool (*check_watchpoint)(struct x86_emulate_ctxt *ctxt,
+                                unsigned long addr, unsigned int length,
+                                int type);
 };
 
 typedef u32 __attribute__((vector_size(16))) sse128_t;
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index b372a75..4e91b7b 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -24,6 +24,7 @@
 #include "kvm_cache_regs.h"
 #include <linux/module.h>
 #include <asm/kvm_emulate.h>
+#include <asm/debugreg.h>
 #include <linux/stringify.h>
 #include <asm/debugreg.h>
 
@@ -564,6 +565,17 @@ static int emulate_db(struct x86_emulate_ctxt *ctxt)
        return emulate_exception(ctxt, DB_VECTOR, 0, false);
 }
 
+static void emulate_db_trap(struct x86_emulate_ctxt *ctxt)
+{
+       /*
+        * If a fault is later encountered, the exception information will be
+        * overridden. Otherwise the trap would be handled after the emulation
+        * is completed.
+        */
+       (void)emulate_exception(ctxt, DB_VECTOR, 0, false);
+       ctxt->have_exception = true;
+}
+
 static int emulate_gp(struct x86_emulate_ctxt *ctxt, int err)
 {
        return emulate_exception(ctxt, GP_VECTOR, err, true);
@@ -776,6 +788,10 @@ static int segmented_read_std(struct x86_emulate_ctxt 
*ctxt,
        rc = linearize(ctxt, addr, size, false, &linear);
        if (rc != X86EMUL_CONTINUE)
                return rc;
+
+       if (ctxt->ops->check_watchpoint(ctxt, linear, size, DR_RW_READ))
+               emulate_db_trap(ctxt);
+
        return ctxt->ops->read_std(ctxt, linear, data, size, &ctxt->exception);
 }
 
@@ -1369,6 +1385,10 @@ static int segmented_read(struct x86_emulate_ctxt *ctxt,
        rc = linearize(ctxt, addr, size, false, &linear);
        if (rc != X86EMUL_CONTINUE)
                return rc;
+
+       if (ctxt->ops->check_watchpoint(ctxt, linear, size, DR_RW_READ))
+               emulate_db_trap(ctxt);
+
        return read_emulated(ctxt, linear, data, size);
 }
 
@@ -1383,6 +1403,10 @@ static int segmented_write(struct x86_emulate_ctxt *ctxt,
        rc = linearize(ctxt, addr, size, true, &linear);
        if (rc != X86EMUL_CONTINUE)
                return rc;
+
+       if (ctxt->ops->check_watchpoint(ctxt, linear, size, DR_RW_WRITE))
+               emulate_db_trap(ctxt);
+
        return ctxt->ops->write_emulated(ctxt, linear, data, size,
                                         &ctxt->exception);
 }
@@ -3729,11 +3753,19 @@ static int em_in(struct x86_emulate_ctxt *ctxt)
                             &ctxt->dst.val))
                return X86EMUL_IO_NEEDED;
 
+       if (ctxt->ops->check_watchpoint(ctxt, ctxt->src.val, ctxt->dst.bytes,
+                                       DR_RW_PORT))
+               emulate_db_trap(ctxt);
+
        return X86EMUL_CONTINUE;
 }
 
 static int em_out(struct x86_emulate_ctxt *ctxt)
 {
+       if (ctxt->ops->check_watchpoint(ctxt, ctxt->dst.val, ctxt->src.bytes,
+                                       DR_RW_PORT))
+               emulate_db_trap(ctxt);
+
        ctxt->ops->pio_out_emulated(ctxt, ctxt->src.bytes, ctxt->dst.val,
                                    &ctxt->src.val, 1);
        /* Disable writeback. */
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 3e4d032..ba75f76 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4831,6 +4831,55 @@ static void emulator_set_nmi_mask(struct 
x86_emulate_ctxt *ctxt, bool masked)
        kvm_x86_ops->set_nmi_mask(emul_to_vcpu(ctxt), masked);
 }
 
+static int kvm_vcpu_check_hw_bp(unsigned long addr, u32 type, u32 len,
+                               u32 dr7, unsigned long *db)
+{
+       u32 dr6 = 0;
+       int i;
+       u32 enable, dr7_rw, dr7_len;
+       unsigned long align_db;
+
+       enable = dr7;
+       dr7_rw = dr7 >> 16;
+       dr7_len = dr7_rw >> 2;
+       for (i = 0; i < 4; i++, enable >>= 2, dr7_rw >>= 4, dr7_len >>= 4) {
+               if (!(enable & 3))
+                       continue;
+
+               /* Check type matches, but on writes, check read bp */
+               if (((dr7_rw & 3) != type &&
+                   !((dr7_rw & 3) == DR_RW_READ && type == DR_RW_WRITE)))
+                       continue;
+               align_db = db[i] & ~((unsigned long)dr7_len & 3);
+               if (addr < (align_db + (dr7_len & 3) + 1) &&
+                               align_db < (addr + len))
+                       dr6 |= (1 << i);
+       }
+       return dr6;
+}
+
+static bool emulator_check_watchpoint(struct x86_emulate_ctxt *ctxt,
+                             unsigned long addr, unsigned int length,
+                             int type)
+{
+       u32 dr6;
+       struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt);
+
+       if (likely(!(vcpu->arch.dr7 & DR7_BP_EN_MASK)) ||
+           (type == DR_RW_PORT && !(emulator_get_cr(ctxt, 4) & X86_CR4_DE)))
+               return false;
+
+       dr6 = kvm_vcpu_check_hw_bp(addr, type, length, vcpu->arch.dr7,
+                                  vcpu->arch.db);
+
+       if (dr6 != 0) {
+               __kvm_set_dr(vcpu, DR_STATUS,
+                            (vcpu->arch.dr6 & ~DR_TRAP_BITS) | dr6);
+               return true;
+       }
+       return false;
+}
+
 static const struct x86_emulate_ops emulate_ops = {
        .read_gpr            = emulator_read_gpr,
        .write_gpr           = emulator_write_gpr,
@@ -4869,6 +4918,7 @@ static const struct x86_emulate_ops emulate_ops = {
        .intercept           = emulator_intercept,
        .get_cpuid           = emulator_get_cpuid,
        .set_nmi_mask        = emulator_set_nmi_mask,
+       .check_watchpoint    = emulator_check_watchpoint,
 };
 
 static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask)
@@ -5118,21 +5168,6 @@ static void kvm_set_hflags(struct kvm_vcpu *vcpu, 
unsigned emul_flags)
                kvm_smm_changed(vcpu);
 }
 
-static int kvm_vcpu_check_hw_bp(unsigned long addr, u32 type, u32 dr7,
-                               unsigned long *db)
-{
-       u32 dr6 = 0;
-       int i;
-       u32 enable, rwlen;
-
-       enable = dr7;
-       rwlen = dr7 >> 16;
-       for (i = 0; i < 4; i++, enable >>= 2, rwlen >>= 4)
-               if ((enable & 3) && (rwlen & 15) == type && db[i] == addr)
-                       dr6 |= (1 << i);
-       return dr6;
-}
-
 static void kvm_vcpu_check_singlestep(struct kvm_vcpu *vcpu, unsigned long 
rflags, int *r)
 {
        struct kvm_run *kvm_run = vcpu->run;
@@ -5173,7 +5208,7 @@ static bool kvm_vcpu_check_breakpoint(struct kvm_vcpu 
*vcpu, int *r)
            (vcpu->arch.guest_debug_dr7 & DR7_BP_EN_MASK)) {
                struct kvm_run *kvm_run = vcpu->run;
                unsigned long eip = kvm_get_linear_rip(vcpu);
-               u32 dr6 = kvm_vcpu_check_hw_bp(eip, 0,
+               u32 dr6 = kvm_vcpu_check_hw_bp(eip, DR_RW_EXECUTE, 1,
                                           vcpu->arch.guest_debug_dr7,
                                           vcpu->arch.eff_db);
 
@@ -5190,7 +5225,7 @@ static bool kvm_vcpu_check_breakpoint(struct kvm_vcpu 
*vcpu, int *r)
        if (unlikely(vcpu->arch.dr7 & DR7_BP_EN_MASK) &&
            !(kvm_get_rflags(vcpu) & X86_EFLAGS_RF)) {
                unsigned long eip = kvm_get_linear_rip(vcpu);
-               u32 dr6 = kvm_vcpu_check_hw_bp(eip, 0,
+               u32 dr6 = kvm_vcpu_check_hw_bp(eip, DR_RW_EXECUTE, 1,
                                           vcpu->arch.dr7,
                                           vcpu->arch.db);
 
@@ -5346,6 +5381,11 @@ int kvm_fast_pio_out(struct kvm_vcpu *vcpu, int size, 
unsigned short port)
        unsigned long val = kvm_register_read(vcpu, VCPU_REGS_RAX);
        int ret = emulator_pio_out_emulated(&vcpu->arch.emulate_ctxt,
                                            size, port, &val, 1);
+
+       if (emulator_check_watchpoint(&vcpu->arch.emulate_ctxt, port, size,
+                                     DR_RW_PORT))
+               kvm_queue_exception(vcpu, DB_VECTOR);
+
        /* do not return to emulator after return from userspace */
        vcpu->arch.pio.count = 0;
        return ret;
-- 
2.1.4

--
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