Signed-off-by: Liu Yu <yu.liu@xxxxxxxxxxxxx> --- arch/powerpc/include/asm/kvm.h | 20 +++++ arch/powerpc/include/asm/kvm_host.h | 18 +++++ arch/powerpc/include/asm/kvm_ppc.h | 5 + arch/powerpc/kvm/booke.c | 140 ++++++++++++++++++++++++++++++++++- arch/powerpc/kvm/e500.c | 8 -- arch/powerpc/kvm/powerpc.c | 8 ++- 6 files changed, 186 insertions(+), 13 deletions(-) diff --git a/arch/powerpc/include/asm/kvm.h b/arch/powerpc/include/asm/kvm.h index bb2de6a..55d029f 100644 --- a/arch/powerpc/include/asm/kvm.h +++ b/arch/powerpc/include/asm/kvm.h @@ -22,6 +22,9 @@ #include <linux/types.h> +/* Select powerpc specific features in <linux/kvm.h> */ +#define __KVM_HAVE_GUEST_DEBUG + struct kvm_regs { __u64 pc; __u64 cr; @@ -53,10 +56,27 @@ struct kvm_fpu { }; struct kvm_debug_exit_arch { + __u32 exception; + __u32 pc; + __u32 status; }; +#define KVM_INST_GUESTGDB 0x44000022 + +#define KVM_GUESTDBG_USE_SW_BP 0x00010000 +#define KVM_GUESTDBG_USE_HW_BP 0x00020000 + +#define KVMPPC_DEBUG_NOTYPE 0x0 +#define KVMPPC_DEBUG_BREAKPOINT 0x1 +#define KVMPPC_DEBUG_WATCH_ACCESS 0x2 +#define KVMPPC_DEBUG_WATCH_WRITE 0x3 + /* for KVM_SET_GUEST_DEBUG */ struct kvm_guest_debug_arch { + struct { + __u32 addr; + __u32 type; + } bp[6]; }; #endif /* __LINUX_KVM_POWERPC_H */ diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 3625424..5bed55a 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -108,9 +108,22 @@ struct kvmppc_exit_timing { struct kvm_arch { }; +struct kvmppc_debug_reg { + u32 dbcr0; + u32 iac[0]; + u32 iac1; + u32 iac2; + u32 iac3; + u32 iac4; + u32 dac[0]; + u32 dac1; + u32 dac2; +}; + struct kvm_vcpu_arch { u32 host_stack; u32 host_pid; + u32 host_msr; u64 fpr[32]; ulong gpr[32]; @@ -159,6 +172,10 @@ struct kvm_vcpu_arch { u32 dbcr1; u32 dbsr; + struct kvmppc_debug_reg dbg_reg; + struct kvmppc_debug_reg host_dbg_reg; + bool has_load_host_dbg; + #ifdef CONFIG_KVM_EXIT_TIMING struct kvmppc_exit_timing timing_exit; struct kvmppc_exit_timing timing_last_enter; @@ -186,6 +203,7 @@ struct kvm_vcpu_arch { struct timer_list dec_timer; unsigned long pending_exceptions; + struct kvm_guest_debug_arch dbg; }; #endif /* __POWERPC_KVM_HOST_H__ */ diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index 2c6ee34..4f3656b 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -89,6 +89,11 @@ extern int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu, extern int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs); extern int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt); +extern void kvmppc_core_load_host_debugstate(struct kvm_vcpu *vcpu); +extern void kvmppc_core_load_guest_debugstate(struct kvm_vcpu *vcpu); +extern int kvmppc_core_set_guest_debug(struct kvm_vcpu *vcpu, + struct kvm_guest_debug *dbg); + extern int kvmppc_booke_init(void); extern void kvmppc_booke_exit(void); diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 642e420..7f47003 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -230,6 +230,16 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, break; } + if (unlikely(vcpu->guest_debug & KVM_GUESTDBG_ENABLE) && + (vcpu->arch.last_inst == KVM_INST_GUESTGDB)) { + run->exit_reason = KVM_EXIT_DEBUG; + run->debug.arch.pc = vcpu->arch.pc; + run->debug.arch.exception = exit_nr; + kvmppc_account_exit(vcpu, DEBUG_EXITS); + r = RESUME_HOST; + break; + } + er = kvmppc_emulate_instruction(run, vcpu); switch (er) { case EMULATE_DONE: @@ -256,6 +266,12 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, default: BUG(); } + + if (unlikely(vcpu->guest_debug & KVM_GUESTDBG_ENABLE) && + (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)) { + run->exit_reason = KVM_EXIT_DEBUG; + r = RESUME_HOST; + } break; case BOOKE_INTERRUPT_FP_UNAVAIL: @@ -386,12 +402,23 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, 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->debug.arch.pc = vcpu->arch.pc; + run->debug.arch.status = 0; + + if (dbsr & (DBSR_IAC1 | DBSR_IAC2 | DBSR_IAC3 | DBSR_IAC4)) { + run->debug.arch.status |= KVMPPC_DEBUG_BREAKPOINT; + } else if (dbsr & (DBSR_DAC1W | DBSR_DAC2W)) { + if (dbsr & (DBSR_DAC1R | DBSR_DAC2R)) + run->debug.arch.status |= KVMPPC_DEBUG_WATCH_ACCESS; + else + run->debug.arch.status |= KVMPPC_DEBUG_WATCH_WRITE; + } + /* clear events in DBSR register */ + mtspr(SPRN_DBSR, ~0); + + run->debug.arch.exception = exit_nr; run->exit_reason = KVM_EXIT_DEBUG; kvmppc_account_exit(vcpu, DEBUG_EXITS); r = RESUME_HOST; @@ -463,6 +490,7 @@ int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) for (i = 0; i < ARRAY_SIZE(regs->gpr); i++) regs->gpr[i] = vcpu->arch.gpr[i]; + return 0; } @@ -520,6 +548,110 @@ int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, return kvmppc_core_vcpu_translate(vcpu, tr); } +static inline void kvmppc_booke_disable_debug_interrupts(void) +{ + mtmsr(mfmsr() & ~MSR_DE); +} + +void kvmppc_core_load_host_debugstate(struct kvm_vcpu *vcpu) +{ + kvmppc_booke_disable_debug_interrupts(); + + /* Load host debug register */ + mtspr(SPRN_DBCR0, vcpu->arch.host_dbg_reg.dbcr0); + mtspr(SPRN_IAC1, vcpu->arch.host_dbg_reg.iac1); + mtspr(SPRN_IAC2, vcpu->arch.host_dbg_reg.iac2); + mtspr(SPRN_IAC3, vcpu->arch.host_dbg_reg.iac3); + mtspr(SPRN_IAC4, vcpu->arch.host_dbg_reg.iac4); + mtspr(SPRN_DAC1, vcpu->arch.host_dbg_reg.dac1); + mtspr(SPRN_DAC2, vcpu->arch.host_dbg_reg.dac2); + mtmsr(vcpu->arch.host_msr); +} + +void kvmppc_core_load_guest_debugstate(struct kvm_vcpu *vcpu) +{ + + vcpu->arch.host_msr = mfmsr(); + + kvmppc_booke_disable_debug_interrupts(); + + /* Save host debug register */ + if (vcpu->arch.has_load_host_dbg == false) { + vcpu->arch.host_dbg_reg.dbcr0 = mfspr(SPRN_DBCR0); + vcpu->arch.host_dbg_reg.iac1 = mfspr(SPRN_IAC1); + vcpu->arch.host_dbg_reg.iac2 = mfspr(SPRN_IAC2); + vcpu->arch.host_dbg_reg.iac3 = mfspr(SPRN_IAC3); + vcpu->arch.host_dbg_reg.iac4 = mfspr(SPRN_IAC4); + vcpu->arch.host_dbg_reg.dac1 = mfspr(SPRN_DAC1); + vcpu->arch.host_dbg_reg.dac2 = mfspr(SPRN_DAC2); + vcpu->arch.has_load_host_dbg = true; + } + + /* Load guest debug register */ + mtspr(SPRN_DBCR0, vcpu->arch.dbg_reg.dbcr0); + mtspr(SPRN_IAC1, vcpu->arch.dbg_reg.iac1); + mtspr(SPRN_IAC2, vcpu->arch.dbg_reg.iac2); + mtspr(SPRN_IAC3, vcpu->arch.dbg_reg.iac3); + mtspr(SPRN_IAC4, vcpu->arch.dbg_reg.iac4); + mtspr(SPRN_DAC1, vcpu->arch.dbg_reg.dac1); + mtspr(SPRN_DAC2, vcpu->arch.dbg_reg.dac2); +} + +int kvmppc_core_set_guest_debug(struct kvm_vcpu *vcpu, + struct kvm_guest_debug *dbg) +{ + if (dbg->control & KVM_GUESTDBG_ENABLE) + vcpu->guest_debug = dbg->control; + else + return 0; + + vcpu->arch.dbg_reg.dbcr0 = 0; + + if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) + vcpu->arch.dbg_reg.dbcr0 |= DBCR0_IDM | DBCR0_IC; + + if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP) { + struct kvmppc_debug_reg *gdbgr = &(vcpu->arch.dbg_reg); + int n, b = 0, w = 0; + const u32 bp_code[] = { + DBCR0_IAC1 | DBCR0_IDM, + DBCR0_IAC2 | DBCR0_IDM, + DBCR0_IAC3 | DBCR0_IDM, + DBCR0_IAC4 | DBCR0_IDM + }; + const u32 wp_code[] = { + DBCR0_DAC1W | DBCR0_IDM, + DBCR0_DAC2W | DBCR0_IDM, + DBCR0_DAC1R | DBCR0_IDM, + DBCR0_DAC2R | DBCR0_IDM + }; + + for (n = 0; n < 6 && dbg->arch.bp[n].type; n++) { + switch (dbg->arch.bp[n].type) { + case KVMPPC_DEBUG_BREAKPOINT: + gdbgr->dbcr0 |= bp_code[b]; + gdbgr->iac[b] = dbg->arch.bp[n].addr; + b++; + break; + case KVMPPC_DEBUG_WATCH_ACCESS: + gdbgr->dbcr0 |= wp_code[w + 2]; + /* go through */ + case KVMPPC_DEBUG_WATCH_WRITE: + gdbgr->dbcr0 |= wp_code[w]; + gdbgr->dac[w] |= dbg->arch.bp[n].addr; + w++; + break; + default: + printk("Undefined breakpoint type\n"); + } + } + } + + kvmppc_core_load_guest_debugstate(vcpu); + + return 0; +} + int kvmppc_booke_init(void) { unsigned long ivor[16]; diff --git a/arch/powerpc/kvm/e500.c b/arch/powerpc/kvm/e500.c index c075d94..bdb2870 100644 --- a/arch/powerpc/kvm/e500.c +++ b/arch/powerpc/kvm/e500.c @@ -24,14 +24,6 @@ #include "booke.h" #include "e500_tlb.h" -void kvmppc_core_load_host_debugstate(struct kvm_vcpu *vcpu) -{ -} - -void kvmppc_core_load_guest_debugstate(struct kvm_vcpu *vcpu) -{ -} - void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { kvmppc_e500_tlb_load(vcpu, cpu); diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 9057335..02d73e7 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -221,18 +221,24 @@ void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { + if (unlikely(vcpu->guest_debug & KVM_GUESTDBG_ENABLE)) + kvmppc_core_load_guest_debugstate(vcpu); + kvmppc_core_vcpu_load(vcpu, cpu); } void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) { + if (unlikely(vcpu->guest_debug & KVM_GUESTDBG_ENABLE)) + kvmppc_core_load_host_debugstate(vcpu); + kvmppc_core_vcpu_put(vcpu); } int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, struct kvm_guest_debug *dbg) { - return -EINVAL; + return kvmppc_core_set_guest_debug(vcpu, dbg); } static void kvmppc_complete_dcr_load(struct kvm_vcpu *vcpu, -- 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