diff mbox

[4/4] kvmppc/booke: exit_nr fixup for guest debug single step

Message ID 1265183633-2230-5-git-send-email-yu.liu@freescale.com (mailing list archive)
State New, archived
Headers show

Commit Message

Liu Yu-B13201 Feb. 3, 2010, 7:53 a.m. UTC
None
diff mbox

Patch

diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
index ec2722d..9056708 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
@@ -214,6 +217,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_exitnr_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
  *
@@ -233,6 +310,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 (unlikely(exit_nr == BOOKE_INTERRUPT_DEBUG))
+		exit_nr = kvmppc_guest_debug_exitnr_fixup(vcpu, exit_nr);
+
 	switch (exit_nr) {
 	case BOOKE_INTERRUPT_MACHINE_CHECK:
 		printk("MACHINE CHECK: %lx\n", mfspr(SPRN_MCSR));
@@ -686,6 +766,8 @@  int __init 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] =
+			(unsigned long)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 644ff1d..fdc48c1 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)