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 be disabled timely if guest want to exit.

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 corret value.

Signed-off-by: Liu Yu <yu....@freescale.com>
---
 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 4e7954f..d3cfd85 100644
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -27,6 +27,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>
@@ -37,6 +38,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
@@ -179,6 +182,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
  *
@@ -198,6 +275,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));
@@ -650,6 +730,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 52592fe..d7cd37e 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to