[PATCH] Single step hack for guest debug

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

 



As booke doesn't have hardware support for virtualization,
hardware never know guest and host.

So when enable hardware single step for guest, it cannot disable it timely
if guest failed on certain instruction and then exit.

Thus, we'll see that an single step interrupt happens at
the very beginning of guest exit path.
Then we need to recognize this kind of single step interrupt
and fix the exit_nr to the corret value.

Signed-off-by: Liu Yu <yu.liu@xxxxxxxxxxxxx>
---
 arch/powerpc/kvm/booke.c            |   82 +++++++++++++++++++++++++++++++++++
 arch/powerpc/kvm/booke_interrupts.S |    9 ++--
 2 files changed, 87 insertions(+), 4 deletions(-)

diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
index 7f47003..b042265 100644
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -24,6 +24,7 @@
 #include <linux/module.h>
 #include <linux/vmalloc.h>
 #include <linux/fs.h>
+#include <linux/highmem.h>
 
 #include <asm/cputable.h>
 #include <asm/uaccess.h>
@@ -34,6 +35,8 @@
 #include "booke.h"
 
 unsigned long kvmppc_booke_handlers;
+unsigned long kvmppc_booke_handler_addr[16];
+#define handler_vector_num (sizeof(kvmppc_booke_handler_addr)/sizeof(kvmppc_booke_handler_addr[0]))
 
 #define VM_STAT(x) offsetof(struct kvm, stat.x), KVM_STAT_VM
 #define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU
@@ -176,6 +179,80 @@ void kvmppc_core_deliver_interrupts(struct kvm_vcpu *vcpu)
 	}
 }
 
+int kvmppc_read_guest(struct kvm_vcpu *vcpu, unsigned long geaddr,
+		      void *data, int len)
+{
+	int gtlb_index;
+	gpa_t gpa;
+	gfn_t gfn;
+	struct page *page;
+	void *headdr, *from;
+
+	/* Check the guest TLB. */
+	gtlb_index = kvmppc_mmu_itlb_index(vcpu, geaddr);
+	if (gtlb_index < 0)
+		return -EFAULT;
+
+	gpa = kvmppc_mmu_xlate(vcpu, gtlb_index, geaddr);
+	gfn = gpa >> PAGE_SHIFT;
+
+	page = gfn_to_page(vcpu->kvm, gfn);
+	if (page == bad_page)
+		return -EFAULT;
+
+	headdr = kmap_atomic(page, KM_USER0);
+	if (!headdr)
+		return -EFAULT;
+	from = headdr + (geaddr & (PAGE_SIZE - 1));
+	memcpy(data, from, len);
+	kunmap_atomic(headdr, KM_USER0);
+
+	return 0;
+}
+
+static unsigned int kvmppc_guest_debug_exit_fixup(struct kvm_vcpu *vcpu,
+                                                  unsigned int exit_nr)
+{
+	unsigned int ret = exit_nr;
+
+	u32 csrr0 = mfspr(SPRN_CSRR0);
+	u32 dbsr = mfspr(SPRN_DBSR);
+
+	if ((dbsr | DBSR_IC) &&
+	    csrr0 >= kvmppc_booke_handlers &&
+	    csrr0 < kvmppc_booke_handlers + (PAGE_SIZE << VCPU_SIZE_ORDER)) {
+		int i = 0;
+
+		for (i = 0; i < handler_vector_num; i++) {
+			if (kvmppc_booke_handler_addr[i] &&
+			    csrr0 == kvmppc_booke_handler_addr[i] + 4) {
+				mtspr(SPRN_DBSR, ~0);
+				ret = i;
+				break;
+			}
+		}
+
+	}
+
+	switch (ret) {
+	case BOOKE_INTERRUPT_DEBUG:
+	case BOOKE_INTERRUPT_ITLB_MISS:
+	case BOOKE_INTERRUPT_EXTERNAL:
+	case BOOKE_INTERRUPT_DECREMENTER:
+		break;
+
+	case BOOKE_INTERRUPT_PROGRAM:
+	case BOOKE_INTERRUPT_DTLB_MISS:
+		/* Need to save the last instruction */
+		kvmppc_read_guest(vcpu, vcpu->arch.pc, &vcpu->arch.last_inst, 4);
+		break;
+	default:
+		printk("Unhandled debug after interrupt:%d\n", ret);
+	}
+
+	return ret;
+}
+
 /**
  * kvmppc_handle_exit
  *
@@ -195,6 +272,9 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
 	run->exit_reason = KVM_EXIT_UNKNOWN;
 	run->ready_for_interrupt_injection = 1;
 
+	if (exit_nr == BOOKE_INTERRUPT_DEBUG)
+		exit_nr = kvmppc_guest_debug_exit_fixup(vcpu, exit_nr);
+
 	switch (exit_nr) {
 	case BOOKE_INTERRUPT_MACHINE_CHECK:
 		printk("MACHINE CHECK: %lx\n", mfspr(SPRN_MCSR));
@@ -693,6 +773,8 @@ int kvmppc_booke_init(void)
 		memcpy((void *)kvmppc_booke_handlers + ivor[i],
 		       kvmppc_handlers_start + i * kvmppc_handler_len,
 		       kvmppc_handler_len);
+		kvmppc_booke_handler_addr[i] =
+			(void *)kvmppc_booke_handlers + ivor[i];
 	}
 	flush_icache_range(kvmppc_booke_handlers,
 	                   kvmppc_booke_handlers + max_ivor + kvmppc_handler_len);
diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S
index d0c6f84..45ff93f 100644
--- a/arch/powerpc/kvm/booke_interrupts.S
+++ b/arch/powerpc/kvm/booke_interrupts.S
@@ -42,16 +42,17 @@
 #define HOST_STACK_LR   (HOST_STACK_SIZE + 4) /* In caller stack frame. */
 
 #define NEED_INST_MASK ((1<<BOOKE_INTERRUPT_PROGRAM) | \
-                        (1<<BOOKE_INTERRUPT_DTLB_MISS) | \
-                        (1<<BOOKE_INTERRUPT_DEBUG))
+                        (1<<BOOKE_INTERRUPT_DTLB_MISS))
 
 #define NEED_DEAR_MASK ((1<<BOOKE_INTERRUPT_DATA_STORAGE) | \
-                        (1<<BOOKE_INTERRUPT_DTLB_MISS))
+                        (1<<BOOKE_INTERRUPT_DTLB_MISS) | \
+                        (1<<BOOKE_INTERRUPT_DEBUG))
 
 #define NEED_ESR_MASK ((1<<BOOKE_INTERRUPT_DATA_STORAGE) | \
                        (1<<BOOKE_INTERRUPT_INST_STORAGE) | \
                        (1<<BOOKE_INTERRUPT_PROGRAM) | \
-                       (1<<BOOKE_INTERRUPT_DTLB_MISS))
+                       (1<<BOOKE_INTERRUPT_DTLB_MISS) | \
+                        (1<<BOOKE_INTERRUPT_DEBUG))
 
 .macro KVM_HANDLER ivor_nr
 _GLOBAL(kvmppc_handler_\ivor_nr)
-- 
1.5.4

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

[Index of Archives]     [KVM Development]     [KVM ARM]     [KVM ia64]     [Linux Virtualization]     [Linux USB Devel]     [Linux Video]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux