]> Pileus Git - ~andy/linux/blobdiff - arch/powerpc/kvm/book3s_64_emulate.c
KVM: PPC: Fix dcbz emulation
[~andy/linux] / arch / powerpc / kvm / book3s_64_emulate.c
index 39d5003e01f0489d9d5254e2227650e85e67c2b0..bbd15906900d1791c9137a4b0860ed0e6d65a780 100644 (file)
 /* DCBZ is actually 1014, but we patch it to 1010 so we get a trap */
 #define OP_31_XOP_DCBZ         1010
 
+#define OP_LFS                 48
+#define OP_LFD                 50
+#define OP_STFS                        52
+#define OP_STFD                        54
+
 #define SPRN_GQR0              912
 #define SPRN_GQR1              913
 #define SPRN_GQR2              914
@@ -184,6 +189,8 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        ulong ra = 0;
                        ulong addr, vaddr;
                        u32 zeros[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+                       u32 dsisr;
+                       int r;
 
                        if (get_ra(inst))
                                ra = kvmppc_get_gpr(vcpu, get_ra(inst));
@@ -193,14 +200,23 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                                addr &= 0xffffffff;
                        vaddr = addr;
 
-                       if (kvmppc_st(vcpu, &addr, 32, zeros, true)) {
+                       r = kvmppc_st(vcpu, &addr, 32, zeros, true);
+                       if ((r == -ENOENT) || (r == -EPERM)) {
+                               *advance = 0;
                                vcpu->arch.dear = vaddr;
                                vcpu->arch.fault_dear = vaddr;
-                               to_book3s(vcpu)->dsisr = DSISR_PROTFAULT |
-                                                     DSISR_ISSTORE;
+
+                               dsisr = DSISR_ISSTORE;
+                               if (r == -ENOENT)
+                                       dsisr |= DSISR_NOHPTE;
+                               else if (r == -EPERM)
+                                       dsisr |= DSISR_PROTFAULT;
+
+                               to_book3s(vcpu)->dsisr = dsisr;
+                               vcpu->arch.fault_dsisr = dsisr;
+
                                kvmppc_book3s_queue_irqprio(vcpu,
                                        BOOK3S_INTERRUPT_DATA_STORAGE);
-                               kvmppc_mmu_pte_flush(vcpu, vaddr, ~0xFFFULL);
                        }
 
                        break;
@@ -474,3 +490,73 @@ int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt)
        return emulated;
 }
 
+u32 kvmppc_alignment_dsisr(struct kvm_vcpu *vcpu, unsigned int inst)
+{
+       u32 dsisr = 0;
+
+       /*
+        * This is what the spec says about DSISR bits (not mentioned = 0):
+        *
+        * 12:13                [DS]    Set to bits 30:31
+        * 15:16                [X]     Set to bits 29:30
+        * 17                   [X]     Set to bit 25
+        *                      [D/DS]  Set to bit 5
+        * 18:21                [X]     Set to bits 21:24
+        *                      [D/DS]  Set to bits 1:4
+        * 22:26                        Set to bits 6:10 (RT/RS/FRT/FRS)
+        * 27:31                        Set to bits 11:15 (RA)
+        */
+
+       switch (get_op(inst)) {
+       /* D-form */
+       case OP_LFS:
+       case OP_LFD:
+       case OP_STFD:
+       case OP_STFS:
+               dsisr |= (inst >> 12) & 0x4000; /* bit 17 */
+               dsisr |= (inst >> 17) & 0x3c00; /* bits 18:21 */
+               break;
+       /* X-form */
+       case 31:
+               dsisr |= (inst << 14) & 0x18000; /* bits 15:16 */
+               dsisr |= (inst << 8)  & 0x04000; /* bit 17 */
+               dsisr |= (inst << 3)  & 0x03c00; /* bits 18:21 */
+               break;
+       default:
+               printk(KERN_INFO "KVM: Unaligned instruction 0x%x\n", inst);
+               break;
+       }
+
+       dsisr |= (inst >> 16) & 0x03ff; /* bits 22:31 */
+
+       return dsisr;
+}
+
+ulong kvmppc_alignment_dar(struct kvm_vcpu *vcpu, unsigned int inst)
+{
+       ulong dar = 0;
+       ulong ra;
+
+       switch (get_op(inst)) {
+       case OP_LFS:
+       case OP_LFD:
+       case OP_STFD:
+       case OP_STFS:
+               ra = get_ra(inst);
+               if (ra)
+                       dar = kvmppc_get_gpr(vcpu, ra);
+               dar += (s32)((s16)inst);
+               break;
+       case 31:
+               ra = get_ra(inst);
+               if (ra)
+                       dar = kvmppc_get_gpr(vcpu, ra);
+               dar += kvmppc_get_gpr(vcpu, get_rb(inst));
+               break;
+       default:
+               printk(KERN_INFO "KVM: Unaligned instruction 0x%x\n", inst);
+               break;
+       }
+
+       return dar;
+}