]> Pileus Git - ~andy/linux/blobdiff - arch/powerpc/kvm/booke.c
KVM: PPC: booke: category E.HV (GS-mode) support
[~andy/linux] / arch / powerpc / kvm / booke.c
index bb6c988f010aa29b0732d628788547456e6b577c..75dbaeb2efa350061c9f4457b988da353ecfcc1d 100644 (file)
@@ -17,6 +17,8 @@
  *
  * Authors: Hollis Blanchard <hollisb@us.ibm.com>
  *          Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
+ *          Scott Wood <scottwood@freescale.com>
+ *          Varun Sethi <varun.sethi@freescale.com>
  */
 
 #include <linux/errno.h>
 #include <asm/cputable.h>
 #include <asm/uaccess.h>
 #include <asm/kvm_ppc.h>
-#include "timing.h"
 #include <asm/cacheflush.h>
+#include <asm/dbell.h>
+#include <asm/hw_irq.h>
+#include <asm/irq.h>
 
+#include "timing.h"
 #include "booke.h"
 
 unsigned long kvmppc_booke_handlers;
@@ -55,6 +60,8 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
        { "dec",        VCPU_STAT(dec_exits) },
        { "ext_intr",   VCPU_STAT(ext_intr_exits) },
        { "halt_wakeup", VCPU_STAT(halt_wakeup) },
+       { "doorbell", VCPU_STAT(dbell_exits) },
+       { "guest doorbell", VCPU_STAT(gdbell_exits) },
        { NULL }
 };
 
@@ -121,15 +128,13 @@ void kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr)
 {
        u32 old_msr = vcpu->arch.shared->msr;
 
+#ifdef CONFIG_KVM_BOOKE_HV
+       new_msr |= MSR_GS;
+#endif
+
        vcpu->arch.shared->msr = new_msr;
 
        kvmppc_mmu_msr_notify(vcpu, old_msr);
-
-       if (vcpu->arch.shared->msr & MSR_WE) {
-               kvm_vcpu_block(vcpu);
-               kvmppc_set_exit_type(vcpu, EMULATED_MTMSRWE_EXITS);
-       };
-
        kvmppc_vcpu_sync_spe(vcpu);
 }
 
@@ -201,6 +206,75 @@ void kvmppc_core_dequeue_external(struct kvm_vcpu *vcpu,
        clear_bit(BOOKE_IRQPRIO_EXTERNAL_LEVEL, &vcpu->arch.pending_exceptions);
 }
 
+static void set_guest_srr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1)
+{
+#ifdef CONFIG_KVM_BOOKE_HV
+       mtspr(SPRN_GSRR0, srr0);
+       mtspr(SPRN_GSRR1, srr1);
+#else
+       vcpu->arch.shared->srr0 = srr0;
+       vcpu->arch.shared->srr1 = srr1;
+#endif
+}
+
+static void set_guest_csrr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1)
+{
+       vcpu->arch.csrr0 = srr0;
+       vcpu->arch.csrr1 = srr1;
+}
+
+static void set_guest_dsrr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1)
+{
+       if (cpu_has_feature(CPU_FTR_DEBUG_LVL_EXC)) {
+               vcpu->arch.dsrr0 = srr0;
+               vcpu->arch.dsrr1 = srr1;
+       } else {
+               set_guest_csrr(vcpu, srr0, srr1);
+       }
+}
+
+static void set_guest_mcsrr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1)
+{
+       vcpu->arch.mcsrr0 = srr0;
+       vcpu->arch.mcsrr1 = srr1;
+}
+
+static unsigned long get_guest_dear(struct kvm_vcpu *vcpu)
+{
+#ifdef CONFIG_KVM_BOOKE_HV
+       return mfspr(SPRN_GDEAR);
+#else
+       return vcpu->arch.shared->dar;
+#endif
+}
+
+static void set_guest_dear(struct kvm_vcpu *vcpu, unsigned long dear)
+{
+#ifdef CONFIG_KVM_BOOKE_HV
+       mtspr(SPRN_GDEAR, dear);
+#else
+       vcpu->arch.shared->dar = dear;
+#endif
+}
+
+static unsigned long get_guest_esr(struct kvm_vcpu *vcpu)
+{
+#ifdef CONFIG_KVM_BOOKE_HV
+       return mfspr(SPRN_GESR);
+#else
+       return vcpu->arch.shared->esr;
+#endif
+}
+
+static void set_guest_esr(struct kvm_vcpu *vcpu, u32 esr)
+{
+#ifdef CONFIG_KVM_BOOKE_HV
+       mtspr(SPRN_GESR, esr);
+#else
+       vcpu->arch.shared->esr = esr;
+#endif
+}
+
 /* Deliver the interrupt of the corresponding priority, if possible. */
 static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
                                         unsigned int priority)
@@ -212,6 +286,7 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
        ulong crit_r1 = kvmppc_get_gpr(vcpu, 1);
        bool crit;
        bool keep_irq = false;
+       enum int_class int_class;
 
        /* Truncate crit indicators in 32 bit mode */
        if (!(vcpu->arch.shared->msr & MSR_SF)) {
@@ -247,54 +322,107 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu,
        case BOOKE_IRQPRIO_AP_UNAVAIL:
        case BOOKE_IRQPRIO_ALIGNMENT:
                allowed = 1;
-               msr_mask = MSR_CE|MSR_ME|MSR_DE;
+               msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE;
+               int_class = INT_CLASS_NONCRIT;
                break;
        case BOOKE_IRQPRIO_CRITICAL:
-       case BOOKE_IRQPRIO_WATCHDOG:
                allowed = vcpu->arch.shared->msr & MSR_CE;
-               msr_mask = MSR_ME;
+               allowed = allowed && !crit;
+               msr_mask = MSR_GS | MSR_ME;
+               int_class = INT_CLASS_CRIT;
                break;
        case BOOKE_IRQPRIO_MACHINE_CHECK:
                allowed = vcpu->arch.shared->msr & MSR_ME;
-               msr_mask = 0;
+               allowed = allowed && !crit;
+               msr_mask = MSR_GS;
+               int_class = INT_CLASS_MC;
                break;
-       case BOOKE_IRQPRIO_EXTERNAL:
        case BOOKE_IRQPRIO_DECREMENTER:
        case BOOKE_IRQPRIO_FIT:
+               keep_irq = true;
+               /* fall through */
+       case BOOKE_IRQPRIO_EXTERNAL:
                allowed = vcpu->arch.shared->msr & MSR_EE;
                allowed = allowed && !crit;
-               msr_mask = MSR_CE|MSR_ME|MSR_DE;
+               msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE;
+               int_class = INT_CLASS_NONCRIT;
                break;
        case BOOKE_IRQPRIO_DEBUG:
                allowed = vcpu->arch.shared->msr & MSR_DE;
-               msr_mask = MSR_ME;
+               allowed = allowed && !crit;
+               msr_mask = MSR_GS | MSR_ME;
+               int_class = INT_CLASS_CRIT;
                break;
        }
 
        if (allowed) {
-               vcpu->arch.shared->srr0 = vcpu->arch.pc;
-               vcpu->arch.shared->srr1 = vcpu->arch.shared->msr;
+               switch (int_class) {
+               case INT_CLASS_NONCRIT:
+                       set_guest_srr(vcpu, vcpu->arch.pc,
+                                     vcpu->arch.shared->msr);
+                       break;
+               case INT_CLASS_CRIT:
+                       set_guest_csrr(vcpu, vcpu->arch.pc,
+                                      vcpu->arch.shared->msr);
+                       break;
+               case INT_CLASS_DBG:
+                       set_guest_dsrr(vcpu, vcpu->arch.pc,
+                                      vcpu->arch.shared->msr);
+                       break;
+               case INT_CLASS_MC:
+                       set_guest_mcsrr(vcpu, vcpu->arch.pc,
+                                       vcpu->arch.shared->msr);
+                       break;
+               }
+
                vcpu->arch.pc = vcpu->arch.ivpr | vcpu->arch.ivor[priority];
                if (update_esr == true)
-                       vcpu->arch.esr = vcpu->arch.queued_esr;
+                       set_guest_esr(vcpu, vcpu->arch.queued_esr);
                if (update_dear == true)
-                       vcpu->arch.shared->dar = vcpu->arch.queued_dear;
+                       set_guest_dear(vcpu, vcpu->arch.queued_dear);
                kvmppc_set_msr(vcpu, vcpu->arch.shared->msr & msr_mask);
 
                if (!keep_irq)
                        clear_bit(priority, &vcpu->arch.pending_exceptions);
        }
 
+#ifdef CONFIG_KVM_BOOKE_HV
+       /*
+        * If an interrupt is pending but masked, raise a guest doorbell
+        * so that we are notified when the guest enables the relevant
+        * MSR bit.
+        */
+       if (vcpu->arch.pending_exceptions & BOOKE_IRQMASK_EE)
+               kvmppc_set_pending_interrupt(vcpu, INT_CLASS_NONCRIT);
+       if (vcpu->arch.pending_exceptions & BOOKE_IRQMASK_CE)
+               kvmppc_set_pending_interrupt(vcpu, INT_CLASS_CRIT);
+       if (vcpu->arch.pending_exceptions & BOOKE_IRQPRIO_MACHINE_CHECK)
+               kvmppc_set_pending_interrupt(vcpu, INT_CLASS_MC);
+#endif
+
        return allowed;
 }
 
-/* Check pending exceptions and deliver one, if possible. */
-void kvmppc_core_deliver_interrupts(struct kvm_vcpu *vcpu)
+static void update_timer_ints(struct kvm_vcpu *vcpu)
+{
+       if ((vcpu->arch.tcr & TCR_DIE) && (vcpu->arch.tsr & TSR_DIS))
+               kvmppc_core_queue_dec(vcpu);
+       else
+               kvmppc_core_dequeue_dec(vcpu);
+}
+
+static void kvmppc_core_check_exceptions(struct kvm_vcpu *vcpu)
 {
        unsigned long *pending = &vcpu->arch.pending_exceptions;
-       unsigned long old_pending = vcpu->arch.pending_exceptions;
        unsigned int priority;
 
+       if (vcpu->requests) {
+               if (kvm_check_request(KVM_REQ_PENDING_TIMER, vcpu)) {
+                       smp_mb();
+                       update_timer_ints(vcpu);
+               }
+       }
+
        priority = __ffs(*pending);
        while (priority <= BOOKE_IRQPRIO_MAX) {
                if (kvmppc_booke_irqprio_deliver(vcpu, priority))
@@ -306,10 +434,24 @@ void kvmppc_core_deliver_interrupts(struct kvm_vcpu *vcpu)
        }
 
        /* Tell the guest about our interrupt status */
-       if (*pending)
-               vcpu->arch.shared->int_pending = 1;
-       else if (old_pending)
-               vcpu->arch.shared->int_pending = 0;
+       vcpu->arch.shared->int_pending = !!*pending;
+}
+
+/* Check pending exceptions and deliver one, if possible. */
+void kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu)
+{
+       WARN_ON_ONCE(!irqs_disabled());
+
+       kvmppc_core_check_exceptions(vcpu);
+
+       if (vcpu->arch.shared->msr & MSR_WE) {
+               local_irq_enable();
+               kvm_vcpu_block(vcpu);
+               local_irq_disable();
+
+               kvmppc_set_exit_type(vcpu, EMULATED_MTMSRWE_EXITS);
+               kvmppc_core_check_exceptions(vcpu);
+       };
 }
 
 int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
@@ -321,15 +463,62 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
                return -EINVAL;
        }
 
+       if (!current->thread.kvm_vcpu) {
+               WARN(1, "no vcpu\n");
+               return -EPERM;
+       }
+
        local_irq_disable();
+
+       kvmppc_core_prepare_to_enter(vcpu);
+
+       if (signal_pending(current)) {
+               kvm_run->exit_reason = KVM_EXIT_INTR;
+               ret = -EINTR;
+               goto out;
+       }
+
        kvm_guest_enter();
        ret = __kvmppc_vcpu_run(kvm_run, vcpu);
        kvm_guest_exit();
-       local_irq_enable();
 
+out:
+       local_irq_enable();
        return ret;
 }
 
+static int emulation_exit(struct kvm_run *run, struct kvm_vcpu *vcpu)
+{
+       enum emulation_result er;
+
+       er = kvmppc_emulate_instruction(run, vcpu);
+       switch (er) {
+       case EMULATE_DONE:
+               /* don't overwrite subtypes, just account kvm_stats */
+               kvmppc_account_exit_stat(vcpu, EMULATED_INST_EXITS);
+               /* Future optimization: only reload non-volatiles if
+                * they were actually modified by emulation. */
+               return RESUME_GUEST_NV;
+
+       case EMULATE_DO_DCR:
+               run->exit_reason = KVM_EXIT_DCR;
+               return RESUME_HOST;
+
+       case EMULATE_FAIL:
+               /* XXX Deliver Program interrupt to guest. */
+               printk(KERN_CRIT "%s: emulation at %lx failed (%08x)\n",
+                      __func__, vcpu->arch.pc, vcpu->arch.last_inst);
+               /* For debugging, encode the failing instruction and
+                * report it to userspace. */
+               run->hw.hardware_exit_reason = ~0ULL << 32;
+               run->hw.hardware_exit_reason |= vcpu->arch.last_inst;
+               return RESUME_HOST;
+
+       default:
+               BUG();
+       }
+}
+
 /**
  * kvmppc_handle_exit
  *
@@ -338,12 +527,30 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
 int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        unsigned int exit_nr)
 {
-       enum emulation_result er;
        int r = RESUME_HOST;
 
        /* update before a new last_exit_type is rewritten */
        kvmppc_update_timing_stats(vcpu);
 
+       switch (exit_nr) {
+       case BOOKE_INTERRUPT_EXTERNAL:
+               do_IRQ(current->thread.regs);
+               break;
+
+       case BOOKE_INTERRUPT_DECREMENTER:
+               timer_interrupt(current->thread.regs);
+               break;
+
+#if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3E_64)
+       case BOOKE_INTERRUPT_DOORBELL:
+               doorbell_exception(current->thread.regs);
+               break;
+#endif
+       case BOOKE_INTERRUPT_MACHINE_CHECK:
+               /* FIXME */
+               break;
+       }
+
        local_irq_enable();
 
        run->exit_reason = KVM_EXIT_UNKNOWN;
@@ -351,30 +558,56 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
 
        switch (exit_nr) {
        case BOOKE_INTERRUPT_MACHINE_CHECK:
-               printk("MACHINE CHECK: %lx\n", mfspr(SPRN_MCSR));
-               kvmppc_dump_vcpu(vcpu);
-               r = RESUME_HOST;
+               kvm_resched(vcpu);
+               r = RESUME_GUEST;
                break;
 
        case BOOKE_INTERRUPT_EXTERNAL:
                kvmppc_account_exit(vcpu, EXT_INTR_EXITS);
-               if (need_resched())
-                       cond_resched();
+               kvm_resched(vcpu);
                r = RESUME_GUEST;
                break;
 
        case BOOKE_INTERRUPT_DECREMENTER:
-               /* Since we switched IVPR back to the host's value, the host
-                * handled this interrupt the moment we enabled interrupts.
-                * Now we just offer it a chance to reschedule the guest. */
                kvmppc_account_exit(vcpu, DEC_EXITS);
-               if (need_resched())
-                       cond_resched();
+               kvm_resched(vcpu);
                r = RESUME_GUEST;
                break;
 
+       case BOOKE_INTERRUPT_DOORBELL:
+               kvmppc_account_exit(vcpu, DBELL_EXITS);
+               kvm_resched(vcpu);
+               r = RESUME_GUEST;
+               break;
+
+       case BOOKE_INTERRUPT_GUEST_DBELL_CRIT:
+               kvmppc_account_exit(vcpu, GDBELL_EXITS);
+
+               /*
+                * We are here because there is a pending guest interrupt
+                * which could not be delivered as MSR_CE or MSR_ME was not
+                * set.  Once we break from here we will retry delivery.
+                */
+               r = RESUME_GUEST;
+               break;
+
+       case BOOKE_INTERRUPT_GUEST_DBELL:
+               kvmppc_account_exit(vcpu, GDBELL_EXITS);
+
+               /*
+                * We are here because there is a pending guest interrupt
+                * which could not be delivered as MSR_EE was not set.  Once
+                * we break from here we will retry delivery.
+                */
+               r = RESUME_GUEST;
+               break;
+
+       case BOOKE_INTERRUPT_HV_PRIV:
+               r = emulation_exit(run, vcpu);
+               break;
+
        case BOOKE_INTERRUPT_PROGRAM:
-               if (vcpu->arch.shared->msr & MSR_PR) {
+               if (vcpu->arch.shared->msr & (MSR_PR | MSR_GS)) {
                        /* Program traps generated by user-level software must be handled
                         * by the guest kernel. */
                        kvmppc_core_queue_program(vcpu, vcpu->arch.fault_esr);
@@ -383,32 +616,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        break;
                }
 
-               er = kvmppc_emulate_instruction(run, vcpu);
-               switch (er) {
-               case EMULATE_DONE:
-                       /* don't overwrite subtypes, just account kvm_stats */
-                       kvmppc_account_exit_stat(vcpu, EMULATED_INST_EXITS);
-                       /* Future optimization: only reload non-volatiles if
-                        * they were actually modified by emulation. */
-                       r = RESUME_GUEST_NV;
-                       break;
-               case EMULATE_DO_DCR:
-                       run->exit_reason = KVM_EXIT_DCR;
-                       r = RESUME_HOST;
-                       break;
-               case EMULATE_FAIL:
-                       /* XXX Deliver Program interrupt to guest. */
-                       printk(KERN_CRIT "%s: emulation at %lx failed (%08x)\n",
-                              __func__, vcpu->arch.pc, vcpu->arch.last_inst);
-                       /* For debugging, encode the failing instruction and
-                        * report it to userspace. */
-                       run->hw.hardware_exit_reason = ~0ULL << 32;
-                       run->hw.hardware_exit_reason |= vcpu->arch.last_inst;
-                       r = RESUME_HOST;
-                       break;
-               default:
-                       BUG();
-               }
+               r = emulation_exit(run, vcpu);
                break;
 
        case BOOKE_INTERRUPT_FP_UNAVAIL:
@@ -473,6 +681,21 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
                r = RESUME_GUEST;
                break;
 
+#ifdef CONFIG_KVM_BOOKE_HV
+       case BOOKE_INTERRUPT_HV_SYSCALL:
+               if (!(vcpu->arch.shared->msr & MSR_PR)) {
+                       kvmppc_set_gpr(vcpu, 3, kvmppc_kvm_pv(vcpu));
+               } else {
+                       /*
+                        * hcall from guest userspace -- send privileged
+                        * instruction program check.
+                        */
+                       kvmppc_core_queue_program(vcpu, ESR_PPR);
+               }
+
+               r = RESUME_GUEST;
+               break;
+#else
        case BOOKE_INTERRUPT_SYSCALL:
                if (!(vcpu->arch.shared->msr & MSR_PR) &&
                    (((u32)kvmppc_get_gpr(vcpu, 0)) == KVM_SC_MAGIC_R0)) {
@@ -486,6 +709,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
                kvmppc_account_exit(vcpu, SYSCALL_EXITS);
                r = RESUME_GUEST;
                break;
+#endif
 
        case BOOKE_INTERRUPT_DTLB_MISS: {
                unsigned long eaddr = vcpu->arch.fault_dear;
@@ -603,7 +827,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
 
        local_irq_disable();
 
-       kvmppc_core_deliver_interrupts(vcpu);
+       kvmppc_core_prepare_to_enter(vcpu);
 
        if (!(r & RESUME_HOST)) {
                /* To avoid clobbering exit_reason, only check for signals if
@@ -626,11 +850,15 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
        int r;
 
        vcpu->arch.pc = 0;
-       vcpu->arch.shared->msr = 0;
-       vcpu->arch.shadow_msr = MSR_USER | MSR_DE | MSR_IS | MSR_DS;
+       vcpu->arch.shared->pir = vcpu->vcpu_id;
        kvmppc_set_gpr(vcpu, 1, (16<<20) - 8); /* -8 for the callee-save LR slot */
+       kvmppc_set_msr(vcpu, 0);
 
+#ifndef CONFIG_KVM_BOOKE_HV
+       vcpu->arch.shadow_msr = MSR_USER | MSR_DE | MSR_IS | MSR_DS;
        vcpu->arch.shadow_pid = 1;
+       vcpu->arch.shared->msr = 0;
+#endif
 
        /* Eye-catching numbers so we know if the guest takes an interrupt
         * before it's programmed its own IVPR/IVORs. */
@@ -662,10 +890,10 @@ int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
        regs->sprg1 = vcpu->arch.shared->sprg1;
        regs->sprg2 = vcpu->arch.shared->sprg2;
        regs->sprg3 = vcpu->arch.shared->sprg3;
-       regs->sprg4 = vcpu->arch.sprg4;
-       regs->sprg5 = vcpu->arch.sprg5;
-       regs->sprg6 = vcpu->arch.sprg6;
-       regs->sprg7 = vcpu->arch.sprg7;
+       regs->sprg4 = vcpu->arch.shared->sprg4;
+       regs->sprg5 = vcpu->arch.shared->sprg5;
+       regs->sprg6 = vcpu->arch.shared->sprg6;
+       regs->sprg7 = vcpu->arch.shared->sprg7;
 
        for (i = 0; i < ARRAY_SIZE(regs->gpr); i++)
                regs->gpr[i] = kvmppc_get_gpr(vcpu, i);
@@ -690,10 +918,10 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
        vcpu->arch.shared->sprg1 = regs->sprg1;
        vcpu->arch.shared->sprg2 = regs->sprg2;
        vcpu->arch.shared->sprg3 = regs->sprg3;
-       vcpu->arch.sprg4 = regs->sprg4;
-       vcpu->arch.sprg5 = regs->sprg5;
-       vcpu->arch.sprg6 = regs->sprg6;
-       vcpu->arch.sprg7 = regs->sprg7;
+       vcpu->arch.shared->sprg4 = regs->sprg4;
+       vcpu->arch.shared->sprg5 = regs->sprg5;
+       vcpu->arch.shared->sprg6 = regs->sprg6;
+       vcpu->arch.shared->sprg7 = regs->sprg7;
 
        for (i = 0; i < ARRAY_SIZE(regs->gpr); i++)
                kvmppc_set_gpr(vcpu, i, regs->gpr[i]);
@@ -711,8 +939,8 @@ static void get_sregs_base(struct kvm_vcpu *vcpu,
        sregs->u.e.csrr0 = vcpu->arch.csrr0;
        sregs->u.e.csrr1 = vcpu->arch.csrr1;
        sregs->u.e.mcsr = vcpu->arch.mcsr;
-       sregs->u.e.esr = vcpu->arch.esr;
-       sregs->u.e.dear = vcpu->arch.shared->dar;
+       sregs->u.e.esr = get_guest_esr(vcpu);
+       sregs->u.e.dear = get_guest_dear(vcpu);
        sregs->u.e.tsr = vcpu->arch.tsr;
        sregs->u.e.tcr = vcpu->arch.tcr;
        sregs->u.e.dec = kvmppc_get_dec(vcpu, tb);
@@ -729,28 +957,19 @@ static int set_sregs_base(struct kvm_vcpu *vcpu,
        vcpu->arch.csrr0 = sregs->u.e.csrr0;
        vcpu->arch.csrr1 = sregs->u.e.csrr1;
        vcpu->arch.mcsr = sregs->u.e.mcsr;
-       vcpu->arch.esr = sregs->u.e.esr;
-       vcpu->arch.shared->dar = sregs->u.e.dear;
+       set_guest_esr(vcpu, sregs->u.e.esr);
+       set_guest_dear(vcpu, sregs->u.e.dear);
        vcpu->arch.vrsave = sregs->u.e.vrsave;
-       vcpu->arch.tcr = sregs->u.e.tcr;
+       kvmppc_set_tcr(vcpu, sregs->u.e.tcr);
 
-       if (sregs->u.e.update_special & KVM_SREGS_E_UPDATE_DEC)
+       if (sregs->u.e.update_special & KVM_SREGS_E_UPDATE_DEC) {
                vcpu->arch.dec = sregs->u.e.dec;
-
-       kvmppc_emulate_dec(vcpu);
+               kvmppc_emulate_dec(vcpu);
+       }
 
        if (sregs->u.e.update_special & KVM_SREGS_E_UPDATE_TSR) {
-               /*
-                * FIXME: existing KVM timer handling is incomplete.
-                * TSR cannot be read by the guest, and its value in
-                * vcpu->arch is always zero.  For now, just handle
-                * the case where the caller is trying to inject a
-                * decrementer interrupt.
-                */
-
-               if ((sregs->u.e.tsr & TSR_DIS) &&
-                   (vcpu->arch.tcr & TCR_DIE))
-                       kvmppc_core_queue_dec(vcpu);
+               vcpu->arch.tsr = sregs->u.e.tsr;
+               update_timer_ints(vcpu);
        }
 
        return 0;
@@ -761,7 +980,7 @@ static void get_sregs_arch206(struct kvm_vcpu *vcpu,
 {
        sregs->u.e.features |= KVM_SREGS_E_ARCH206;
 
-       sregs->u.e.pir = 0;
+       sregs->u.e.pir = vcpu->vcpu_id;
        sregs->u.e.mcsrr0 = vcpu->arch.mcsrr0;
        sregs->u.e.mcsrr1 = vcpu->arch.mcsrr1;
        sregs->u.e.decar = vcpu->arch.decar;
@@ -774,7 +993,7 @@ static int set_sregs_arch206(struct kvm_vcpu *vcpu,
        if (!(sregs->u.e.features & KVM_SREGS_E_ARCH206))
                return 0;
 
-       if (sregs->u.e.pir != 0)
+       if (sregs->u.e.pir != vcpu->vcpu_id)
                return -EINVAL;
 
        vcpu->arch.mcsrr0 = sregs->u.e.mcsrr0;
@@ -862,6 +1081,16 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
        return kvmppc_core_set_sregs(vcpu, sregs);
 }
 
+int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
+{
+       return -EINVAL;
+}
+
+int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
+{
+       return -EINVAL;
+}
+
 int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
 {
        return -ENOTSUPP;
@@ -897,17 +1126,46 @@ void kvmppc_core_commit_memory_region(struct kvm *kvm,
 {
 }
 
-int kvmppc_core_init_vm(struct kvm *kvm)
+void kvmppc_set_tcr(struct kvm_vcpu *vcpu, u32 new_tcr)
 {
-       return 0;
+       vcpu->arch.tcr = new_tcr;
+       update_timer_ints(vcpu);
 }
 
-void kvmppc_core_destroy_vm(struct kvm *kvm)
+void kvmppc_set_tsr_bits(struct kvm_vcpu *vcpu, u32 tsr_bits)
 {
+       set_bits(tsr_bits, &vcpu->arch.tsr);
+       smp_wmb();
+       kvm_make_request(KVM_REQ_PENDING_TIMER, vcpu);
+       kvm_vcpu_kick(vcpu);
+}
+
+void kvmppc_clr_tsr_bits(struct kvm_vcpu *vcpu, u32 tsr_bits)
+{
+       clear_bits(tsr_bits, &vcpu->arch.tsr);
+       update_timer_ints(vcpu);
+}
+
+void kvmppc_decrementer_func(unsigned long data)
+{
+       struct kvm_vcpu *vcpu = (struct kvm_vcpu *)data;
+
+       kvmppc_set_tsr_bits(vcpu, TSR_DIS);
+}
+
+void kvmppc_booke_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+{
+       current->thread.kvm_vcpu = vcpu;
+}
+
+void kvmppc_booke_vcpu_put(struct kvm_vcpu *vcpu)
+{
+       current->thread.kvm_vcpu = NULL;
 }
 
 int __init kvmppc_booke_init(void)
 {
+#ifndef CONFIG_KVM_BOOKE_HV
        unsigned long ivor[16];
        unsigned long max_ivor = 0;
        int i;
@@ -950,7 +1208,7 @@ int __init kvmppc_booke_init(void)
        }
        flush_icache_range(kvmppc_booke_handlers,
                           kvmppc_booke_handlers + max_ivor + kvmppc_handler_len);
-
+#endif /* !BOOKE_HV */
        return 0;
 }