KVM: PPC: Book3S PR: Handle Facility interrupt and FSCR
authorAlexander Graf <agraf@suse.de>
Tue, 29 Apr 2014 14:48:44 +0000 (16:48 +0200)
committerAlexander Graf <agraf@suse.de>
Fri, 30 May 2014 12:26:22 +0000 (14:26 +0200)
POWER8 introduced a new interrupt type called "Facility unavailable interrupt"
which contains its status message in a new register called FSCR.

Handle these exits and try to emulate instructions for unhandled facilities.
Follow-on patches enable KVM to expose specific facilities into the guest.

Signed-off-by: Alexander Graf <agraf@suse.de>
arch/powerpc/include/asm/kvm_asm.h
arch/powerpc/include/asm/kvm_book3s_asm.h
arch/powerpc/include/asm/kvm_host.h
arch/powerpc/kernel/asm-offsets.c
arch/powerpc/kvm/book3s.c
arch/powerpc/kvm/book3s_emulate.c
arch/powerpc/kvm/book3s_hv.c
arch/powerpc/kvm/book3s_pr.c
arch/powerpc/kvm/book3s_segment.S

index 19eb74a95b592f8c41b2562d32244906debe0468..9601741080e51ca7e9611f3ecb928b05464a2965 100644 (file)
 #define BOOK3S_INTERRUPT_PERFMON       0xf00
 #define BOOK3S_INTERRUPT_ALTIVEC       0xf20
 #define BOOK3S_INTERRUPT_VSX           0xf40
+#define BOOK3S_INTERRUPT_FAC_UNAVAIL   0xf60
 #define BOOK3S_INTERRUPT_H_FAC_UNAVAIL 0xf80
 
 #define BOOK3S_IRQPRIO_SYSTEM_RESET            0
 #define BOOK3S_IRQPRIO_FP_UNAVAIL              7
 #define BOOK3S_IRQPRIO_ALTIVEC                 8
 #define BOOK3S_IRQPRIO_VSX                     9
-#define BOOK3S_IRQPRIO_SYSCALL                 10
-#define BOOK3S_IRQPRIO_MACHINE_CHECK           11
-#define BOOK3S_IRQPRIO_DEBUG                   12
-#define BOOK3S_IRQPRIO_EXTERNAL                        13
-#define BOOK3S_IRQPRIO_DECREMENTER             14
-#define BOOK3S_IRQPRIO_PERFORMANCE_MONITOR     15
-#define BOOK3S_IRQPRIO_EXTERNAL_LEVEL          16
-#define BOOK3S_IRQPRIO_MAX                     17
+#define BOOK3S_IRQPRIO_FAC_UNAVAIL             10
+#define BOOK3S_IRQPRIO_SYSCALL                 11
+#define BOOK3S_IRQPRIO_MACHINE_CHECK           12
+#define BOOK3S_IRQPRIO_DEBUG                   13
+#define BOOK3S_IRQPRIO_EXTERNAL                        14
+#define BOOK3S_IRQPRIO_DECREMENTER             15
+#define BOOK3S_IRQPRIO_PERFORMANCE_MONITOR     16
+#define BOOK3S_IRQPRIO_EXTERNAL_LEVEL          17
+#define BOOK3S_IRQPRIO_MAX                     18
 
 #define BOOK3S_HFLAG_DCBZ32                    0x1
 #define BOOK3S_HFLAG_SLB                       0x2
index 821725c1bf4624d218cd54c290eb1ed411fe1f1a..5bdfb5dd34002348578b8b9c0a6164a04b3d4820 100644 (file)
@@ -104,6 +104,7 @@ struct kvmppc_host_state {
 #ifdef CONFIG_PPC_BOOK3S_64
        u64 cfar;
        u64 ppr;
+       u64 host_fscr;
 #endif
 };
 
@@ -133,6 +134,7 @@ struct kvmppc_book3s_shadow_vcpu {
                u64     esid;
                u64     vsid;
        } slb[64];                      /* guest SLB */
+       u64 shadow_fscr;
 #endif
 };
 
index 15f19d3cc584366504877f93836b43b025d86870..232ec5f0b886cce88f8a8dc798d27ebb33749ed9 100644 (file)
@@ -475,6 +475,7 @@ struct kvm_vcpu_arch {
        ulong ppr;
        ulong pspb;
        ulong fscr;
+       ulong shadow_fscr;
        ulong ebbhr;
        ulong ebbrr;
        ulong bescr;
index bbf3b9a3e2af87c607dc1e83b55b578b4b9d8a48..e2b86b5c02b330bafe3af4d1331c5a540db82919 100644 (file)
@@ -537,6 +537,7 @@ int main(void)
        DEFINE(VCPU_CFAR, offsetof(struct kvm_vcpu, arch.cfar));
        DEFINE(VCPU_PPR, offsetof(struct kvm_vcpu, arch.ppr));
        DEFINE(VCPU_FSCR, offsetof(struct kvm_vcpu, arch.fscr));
+       DEFINE(VCPU_SHADOW_FSCR, offsetof(struct kvm_vcpu, arch.shadow_fscr));
        DEFINE(VCPU_PSPB, offsetof(struct kvm_vcpu, arch.pspb));
        DEFINE(VCPU_EBBHR, offsetof(struct kvm_vcpu, arch.ebbhr));
        DEFINE(VCPU_EBBRR, offsetof(struct kvm_vcpu, arch.ebbrr));
@@ -618,6 +619,7 @@ int main(void)
 #ifdef CONFIG_PPC64
        SVCPU_FIELD(SVCPU_SLB, slb);
        SVCPU_FIELD(SVCPU_SLB_MAX, slb_max);
+       SVCPU_FIELD(SVCPU_SHADOW_FSCR, shadow_fscr);
 #endif
 
        HSTATE_FIELD(HSTATE_HOST_R1, host_r1);
@@ -653,6 +655,7 @@ int main(void)
 #ifdef CONFIG_PPC_BOOK3S_64
        HSTATE_FIELD(HSTATE_CFAR, cfar);
        HSTATE_FIELD(HSTATE_PPR, ppr);
+       HSTATE_FIELD(HSTATE_HOST_FSCR, host_fscr);
 #endif /* CONFIG_PPC_BOOK3S_64 */
 
 #else /* CONFIG_PPC_BOOK3S */
index 81abc5cef0fea1259674ebaf2b367a9630bbb918..79cfa2d10238b15c5c0599282c807e504b778e1b 100644 (file)
@@ -145,6 +145,7 @@ static int kvmppc_book3s_vec2irqprio(unsigned int vec)
        case 0xd00: prio = BOOK3S_IRQPRIO_DEBUG;                break;
        case 0xf20: prio = BOOK3S_IRQPRIO_ALTIVEC;              break;
        case 0xf40: prio = BOOK3S_IRQPRIO_VSX;                  break;
+       case 0xf60: prio = BOOK3S_IRQPRIO_FAC_UNAVAIL;          break;
        default:    prio = BOOK3S_IRQPRIO_MAX;                  break;
        }
 
@@ -275,6 +276,9 @@ int kvmppc_book3s_irqprio_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
        case BOOK3S_IRQPRIO_PERFORMANCE_MONITOR:
                vec = BOOK3S_INTERRUPT_PERFMON;
                break;
+       case BOOK3S_IRQPRIO_FAC_UNAVAIL:
+               vec = BOOK3S_INTERRUPT_FAC_UNAVAIL;
+               break;
        default:
                deliver = 0;
                printk(KERN_ERR "KVM: Unknown interrupt: 0x%x\n", priority);
@@ -627,6 +631,9 @@ int kvm_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
                        val = get_reg_val(reg->id, kvmppc_xics_get_icp(vcpu));
                        break;
 #endif /* CONFIG_KVM_XICS */
+               case KVM_REG_PPC_FSCR:
+                       val = get_reg_val(reg->id, vcpu->arch.fscr);
+                       break;
                default:
                        r = -EINVAL;
                        break;
@@ -716,6 +723,9 @@ int kvm_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, struct kvm_one_reg *reg)
                                                set_reg_val(reg->id, val));
                        break;
 #endif /* CONFIG_KVM_XICS */
+               case KVM_REG_PPC_FSCR:
+                       vcpu->arch.fscr = set_reg_val(reg->id, val);
+                       break;
                default:
                        r = -EINVAL;
                        break;
index 0a1de29a4caf4ddfc18490f2420097b316d2586b..e8133e5e89c6a66278d3a1609ee43a785131df69 100644 (file)
@@ -438,6 +438,9 @@ int kvmppc_core_emulate_mtspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong spr_val)
        case SPRN_GQR7:
                to_book3s(vcpu)->gqr[sprn - SPRN_GQR0] = spr_val;
                break;
+       case SPRN_FSCR:
+               vcpu->arch.fscr = spr_val;
+               break;
        case SPRN_ICTC:
        case SPRN_THRM1:
        case SPRN_THRM2:
@@ -545,6 +548,9 @@ int kvmppc_core_emulate_mfspr_pr(struct kvm_vcpu *vcpu, int sprn, ulong *spr_val
        case SPRN_GQR7:
                *spr_val = to_book3s(vcpu)->gqr[sprn - SPRN_GQR0];
                break;
+       case SPRN_FSCR:
+               *spr_val = vcpu->arch.fscr;
+               break;
        case SPRN_THRM1:
        case SPRN_THRM2:
        case SPRN_THRM3:
index 030821a414a821af6f1c5437edeadfb475788f55..0092e12b3e46c216b3b2aa8a0ba8ca0e22e0e533 100644 (file)
@@ -879,9 +879,6 @@ static int kvmppc_get_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
        case KVM_REG_PPC_IAMR:
                *val = get_reg_val(id, vcpu->arch.iamr);
                break;
-       case KVM_REG_PPC_FSCR:
-               *val = get_reg_val(id, vcpu->arch.fscr);
-               break;
        case KVM_REG_PPC_PSPB:
                *val = get_reg_val(id, vcpu->arch.pspb);
                break;
@@ -1091,9 +1088,6 @@ static int kvmppc_set_one_reg_hv(struct kvm_vcpu *vcpu, u64 id,
        case KVM_REG_PPC_IAMR:
                vcpu->arch.iamr = set_reg_val(id, *val);
                break;
-       case KVM_REG_PPC_FSCR:
-               vcpu->arch.fscr = set_reg_val(id, *val);
-               break;
        case KVM_REG_PPC_PSPB:
                vcpu->arch.pspb = set_reg_val(id, *val);
                break;
index 6e55934dda0a89bcd05677b1aa51523da31df078..ddc626eea2dacada291e4489b1803be5fc8b46ab 100644 (file)
@@ -53,6 +53,7 @@
 
 static int kvmppc_handle_ext(struct kvm_vcpu *vcpu, unsigned int exit_nr,
                             ulong msr);
+static void kvmppc_giveup_fac(struct kvm_vcpu *vcpu, ulong fac);
 
 /* Some compatibility defines */
 #ifdef CONFIG_PPC_BOOK3S_32
@@ -115,6 +116,9 @@ void kvmppc_copy_to_svcpu(struct kvmppc_book3s_shadow_vcpu *svcpu,
        svcpu->ctr = vcpu->arch.ctr;
        svcpu->lr  = vcpu->arch.lr;
        svcpu->pc  = vcpu->arch.pc;
+#ifdef CONFIG_PPC_BOOK3S_64
+       svcpu->shadow_fscr = vcpu->arch.shadow_fscr;
+#endif
        svcpu->in_use = true;
 }
 
@@ -158,6 +162,9 @@ void kvmppc_copy_from_svcpu(struct kvm_vcpu *vcpu,
        vcpu->arch.fault_dar   = svcpu->fault_dar;
        vcpu->arch.fault_dsisr = svcpu->fault_dsisr;
        vcpu->arch.last_inst   = svcpu->last_inst;
+#ifdef CONFIG_PPC_BOOK3S_64
+       vcpu->arch.shadow_fscr = svcpu->shadow_fscr;
+#endif
        svcpu->in_use = false;
 
 out:
@@ -610,6 +617,17 @@ void kvmppc_giveup_ext(struct kvm_vcpu *vcpu, ulong msr)
        kvmppc_recalc_shadow_msr(vcpu);
 }
 
+/* Give up facility (TAR / EBB / DSCR) */
+static void kvmppc_giveup_fac(struct kvm_vcpu *vcpu, ulong fac)
+{
+#ifdef CONFIG_PPC_BOOK3S_64
+       if (!(vcpu->arch.shadow_fscr & (1ULL << fac))) {
+               /* Facility not available to the guest, ignore giveup request*/
+               return;
+       }
+#endif
+}
+
 static int kvmppc_read_inst(struct kvm_vcpu *vcpu)
 {
        ulong srr0 = kvmppc_get_pc(vcpu);
@@ -741,6 +759,50 @@ static void kvmppc_handle_lost_ext(struct kvm_vcpu *vcpu)
        current->thread.regs->msr |= lost_ext;
 }
 
+#ifdef CONFIG_PPC_BOOK3S_64
+
+static void kvmppc_trigger_fac_interrupt(struct kvm_vcpu *vcpu, ulong fac)
+{
+       /* Inject the Interrupt Cause field and trigger a guest interrupt */
+       vcpu->arch.fscr &= ~(0xffULL << 56);
+       vcpu->arch.fscr |= (fac << 56);
+       kvmppc_book3s_queue_irqprio(vcpu, BOOK3S_INTERRUPT_FAC_UNAVAIL);
+}
+
+static void kvmppc_emulate_fac(struct kvm_vcpu *vcpu, ulong fac)
+{
+       enum emulation_result er = EMULATE_FAIL;
+
+       if (!(kvmppc_get_msr(vcpu) & MSR_PR))
+               er = kvmppc_emulate_instruction(vcpu->run, vcpu);
+
+       if ((er != EMULATE_DONE) && (er != EMULATE_AGAIN)) {
+               /* Couldn't emulate, trigger interrupt in guest */
+               kvmppc_trigger_fac_interrupt(vcpu, fac);
+       }
+}
+
+/* Enable facilities (TAR, EBB, DSCR) for the guest */
+static int kvmppc_handle_fac(struct kvm_vcpu *vcpu, ulong fac)
+{
+       BUG_ON(!cpu_has_feature(CPU_FTR_ARCH_207S));
+
+       if (!(vcpu->arch.fscr & (1ULL << fac))) {
+               /* Facility not enabled by the guest */
+               kvmppc_trigger_fac_interrupt(vcpu, fac);
+               return RESUME_GUEST;
+       }
+
+       switch (fac) {
+       default:
+               kvmppc_emulate_fac(vcpu, fac);
+               break;
+       }
+
+       return RESUME_GUEST;
+}
+#endif
+
 int kvmppc_handle_exit_pr(struct kvm_run *run, struct kvm_vcpu *vcpu,
                          unsigned int exit_nr)
 {
@@ -1015,6 +1077,12 @@ program_interrupt:
                }
                r = RESUME_GUEST;
                break;
+#ifdef CONFIG_PPC_BOOK3S_64
+       case BOOK3S_INTERRUPT_FAC_UNAVAIL:
+               kvmppc_handle_fac(vcpu, vcpu->arch.shadow_fscr >> 56);
+               r = RESUME_GUEST;
+               break;
+#endif
        case BOOK3S_INTERRUPT_MACHINE_CHECK:
        case BOOK3S_INTERRUPT_TRACE:
                kvmppc_book3s_queue_irqprio(vcpu, exit_nr);
index 1e0cc2adfd40d9aaee3ee93662d6f4ee48d8d659..acee37cde840a3b18a512a09bfc79634a8e26f07 100644 (file)
@@ -90,6 +90,15 @@ kvmppc_handler_trampoline_enter:
        LOAD_GUEST_SEGMENTS
 
 #ifdef CONFIG_PPC_BOOK3S_64
+BEGIN_FTR_SECTION
+       /* Save host FSCR */
+       mfspr   r8, SPRN_FSCR
+       std     r8, HSTATE_HOST_FSCR(r13)
+       /* Set FSCR during guest execution */
+       ld      r9, SVCPU_SHADOW_FSCR(r13)
+       mtspr   SPRN_FSCR, r9
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
+
        /* Some guests may need to have dcbz set to 32 byte length.
         *
         * Usually we ensure that by patching the guest's instructions
@@ -255,6 +264,10 @@ BEGIN_FTR_SECTION
        cmpwi   r12, BOOK3S_INTERRUPT_H_EMUL_ASSIST
        beq-    ld_last_inst
 END_FTR_SECTION_IFSET(CPU_FTR_HVMODE)
+BEGIN_FTR_SECTION
+       cmpwi   r12, BOOK3S_INTERRUPT_FAC_UNAVAIL
+       beq-    ld_last_inst
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
 #endif
 
        b       no_ld_last_inst
@@ -311,6 +324,18 @@ no_ld_last_inst:
 
 no_dcbz32_off:
 
+BEGIN_FTR_SECTION
+       /* Save guest FSCR on a FAC_UNAVAIL interrupt */
+       cmpwi   r12, BOOK3S_INTERRUPT_FAC_UNAVAIL
+       bne+    no_fscr_save
+       mfspr   r7, SPRN_FSCR
+       std     r7, SVCPU_SHADOW_FSCR(r13)
+no_fscr_save:
+       /* Restore host FSCR */
+       ld      r8, HSTATE_HOST_FSCR(r13)
+       mtspr   SPRN_FSCR, r8
+END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
+
 #endif /* CONFIG_PPC_BOOK3S_64 */
 
        /*