As BOOKE doesn't have hardware support for virtualization, hardware never know who's guest and host. When enable hardware single step in guest, we cannot disabled it at the point we switch back to host. Thus, we'll see that an single step interrupt happens at the beginning of guest exit path. Then we need to recognize this kind of single step interrupt and fix the exit_nr to the original value. So that everything looks like normal. 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 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) -- 1.6.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