[PATCH] [v7] Add gdb break point support to PowerPC kvm

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

 



4 files changed, 142 insertions(+), 2 deletions(-)
arch/powerpc/kvm/booke_guest.c      |   16 ++++
arch/powerpc/kvm/booke_interrupts.S |    3 
arch/powerpc/kvm/powerpc.c          |  120 ++++++++++++++++++++++++++++++++++-
include/asm-powerpc/kvm_host.h      |    5 +


   *Placed wrong definitions when disabling registers in dbcr0
    before restoring guest and host registers. Doh! This fixes it. Also
    add more registers in dbcr0 to disable just in case.

This patch adds the ability to use breakpoints from a gdb stub in userpace (currently qemu). It does this through the use of hardware debug registers.

Signed-off-by: Jerone Young <jyoung5@xxxxxxxxxx>

diff --git a/arch/powerpc/kvm/booke_guest.c b/arch/powerpc/kvm/booke_guest.c
--- a/arch/powerpc/kvm/booke_guest.c
+++ b/arch/powerpc/kvm/booke_guest.c
@@ -410,6 +410,22 @@ int kvmppc_handle_exit(struct kvm_run *r
 		break;
 	}
 
+	case BOOKE_INTERRUPT_DEBUG: {
+		u32 dbsr;
+
+		vcpu->arch.pc = mfspr(SPRN_CSRR0);
+
+		/* clear IAC events in DBSR register */
+		dbsr = mfspr(SPRN_DBSR);
+
+		dbsr &= (DBSR_IAC1| DBSR_IAC2| DBSR_IAC3| DBSR_IAC4);
+		mtspr (SPRN_DBSR, dbsr);
+
+		run->exit_reason = KVM_EXIT_DEBUG;
+		r = RESUME_HOST;
+		break;
+	}
+
 	default:
 		printk(KERN_EMERG "exit_nr %d\n", exit_nr);
 		BUG();
diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S
--- a/arch/powerpc/kvm/booke_interrupts.S
+++ b/arch/powerpc/kvm/booke_interrupts.S
@@ -42,7 +42,8 @@
 #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_DTLB_MISS) | \
+                        (1<<BOOKE_INTERRUPT_DEBUG))
 
 #define NEED_DEAR_MASK ((1<<BOOKE_INTERRUPT_DATA_STORAGE) | \
                         (1<<BOOKE_INTERRUPT_DTLB_MISS))
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -232,18 +232,136 @@ void kvm_arch_vcpu_uninit(struct kvm_vcp
 {
 }
 
+static void disable_msr_debug_interrupts(void)
+{
+	u32 msr;
+
+	msr = mfmsr();
+	msr &= ~MSR_DE;
+	mtmsr(msr);
+}
+
+static void disable_debug_regs(void)
+{
+	u32 dbcr0 = 0;
+
+	mfspr(SPRN_DBCR0);
+	dbcr0 &= ~(DBCR0_IAC1| DBCR0_IAC2| DBCR0_IAC3| DBCR0_IAC4| \
+			DBCR0_DAC1R| DBCR0_DAC1W| DBCR0_DAC2R| DBCR0_DAC2W| \
+			DBCR0_RET);
+	mtspr(SPRN_DBCR0, dbcr0);
+}
+
+static void kvmppc_restore_host_debug_state(struct kvm_vcpu *vcpu)
+{
+	struct kvm_guest_debug *dbg = &vcpu->guest_debug;
+
+	if (dbg->enabled) {
+		disable_debug_regs();
+		disable_msr_debug_interrupts();
+		
+		mtspr(SPRN_IAC1, vcpu->arch.host_iac[0]);
+		mtspr(SPRN_IAC2, vcpu->arch.host_iac[1]);
+		mtspr(SPRN_IAC3, vcpu->arch.host_iac[2]);
+		mtspr(SPRN_IAC4, vcpu->arch.host_iac[3]);
+
+		mtspr(SPRN_DBCR1, vcpu->arch.host_dbcr1);
+		mtspr(SPRN_DBCR2, vcpu->arch.host_dbcr2);
+		mtspr(SPRN_DBCR0, vcpu->arch.host_dbcr0);
+
+		mtmsr(vcpu->arch.host_msr);
+	}
+}
+
+static void kvmppc_load_guest_debug_registers(struct kvm_vcpu *vcpu)
+{
+	struct kvm_guest_debug *dbg = &vcpu->guest_debug;
+	u32 dbcr0 = 0;
+
+	if (dbg->enabled) {
+		/* save hosts regs */
+		/* save host msr */
+		vcpu->arch.host_msr = mfmsr();
+
+		/* save host dbcr0 */
+		vcpu->arch.host_dbcr0 = mfspr(SPRN_DBCR0);
+		
+		disable_debug_regs();
+		disable_msr_debug_interrupts();
+
+		/* save host debug registers */
+		vcpu->arch.host_iac[0] = mfspr(SPRN_IAC1);
+		vcpu->arch.host_iac[1] = mfspr(SPRN_IAC2);
+		vcpu->arch.host_iac[2] = mfspr(SPRN_IAC3);
+		vcpu->arch.host_iac[3] = mfspr(SPRN_IAC4);
+
+
+		/* save host dbcr1 */
+		vcpu->arch.host_dbcr1 = mfspr(SPRN_DBCR1);
+
+		/* save host dbcr2 */
+		vcpu->arch.host_dbcr2 = mfspr(SPRN_DBCR2);		
+
+		/* set registers up for guest */
+		/* set dbcr0 & set iac */
+
+		if (dbg->bp[0]) {
+			mtspr(SPRN_IAC1, dbg->bp[0]);
+			dbcr0 |= DBCR0_IAC1;
+		}
+		if (dbg->bp[1]) {
+			mtspr(SPRN_IAC2, dbg->bp[1]);
+			dbcr0 |= DBCR0_IAC2;
+		}
+		if (dbg->bp[2]) {
+			mtspr(SPRN_IAC3, dbg->bp[2]);
+			dbcr0 |= DBCR0_IAC3;
+		}
+		if (dbg->bp[3]) {
+			mtspr(SPRN_IAC4, dbg->bp[3]);
+			dbcr0 |= DBCR0_IAC4;
+		}
+
+		mtspr(SPRN_DBCR1, 0);
+		mtspr(SPRN_DBCR2, 0);
+		mtspr(SPRN_DBCR0, dbcr0| DBCR0_IDM);
+	}
+}
+
 void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 {
+	kvmppc_load_guest_debug_registers(vcpu);
 }
 
 void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
 {
+	kvmppc_restore_host_debug_state(vcpu);
+}
+
+int guest_debug(struct kvm_vcpu *vcpu,
+		struct kvm_debug_guest *dbg)
+{
+	int ret=0;
+	int i;
+
+	vcpu->guest_debug.enabled = dbg->enabled;
+	if (vcpu->guest_debug.enabled) {
+		for (i=0; i < ARRAY_SIZE(vcpu->guest_debug.bp); i++) {
+			if (dbg->breakpoints[i].enabled)
+				vcpu->guest_debug.bp[i] = \
+					dbg->breakpoints[i].address;
+			else
+				vcpu->guest_debug.bp[i] = 0;
+		}
+	}
+
+	return ret;
 }
 
 int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu,
                                     struct kvm_debug_guest *dbg)
 {
-	return -ENOTSUPP;
+	return guest_debug(vcpu, dbg);
 }
 
 static void kvmppc_complete_dcr_load(struct kvm_vcpu *vcpu,
diff --git a/include/asm-powerpc/kvm_host.h b/include/asm-powerpc/kvm_host.h
--- a/include/asm-powerpc/kvm_host.h
+++ b/include/asm-powerpc/kvm_host.h
@@ -84,6 +84,11 @@ struct kvm_vcpu_arch {
 
 	u32 host_stack;
 	u32 host_pid;
+	u32 host_dbcr0;
+	u32 host_dbcr1;
+	u32 host_dbcr2;
+	u32 host_iac[4];
+	u32 host_msr;
 
 	u64 fpr[32];
 	u32 gpr[32];
--
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