@@ -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);
@@ -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)