[PATCH 2/2] KVM: x86: Guest watchpoints during emulation.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Nadav Amit <nadav.amit@xxxxxxxxx>

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 <andreyisakov7@xxxxxxxxx>
Signed-off-by: Rami Burstein <ramibnet@xxxxxxxxx>
Signed-off-by: Nadav Amit <nadav.amit@xxxxxxxxx>
---
 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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux