]> 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 1027eac6d474fea3ae3a94efe6aca91cd48a594c..bbd15906900d1791c9137a4b0860ed0e6d65a780 100644 (file)
 #define OP_31_XOP_MFMSR                83
 #define OP_31_XOP_MTMSR                146
 #define OP_31_XOP_MTMSRD       178
+#define OP_31_XOP_MTSR         210
 #define OP_31_XOP_MTSRIN       242
 #define OP_31_XOP_TLBIEL       274
 #define OP_31_XOP_TLBIE                306
 #define OP_31_XOP_SLBMTE       402
 #define OP_31_XOP_SLBIE                434
 #define OP_31_XOP_SLBIA                498
+#define OP_31_XOP_MFSR         595
 #define OP_31_XOP_MFSRIN       659
 #define OP_31_XOP_SLBMFEV      851
 #define OP_31_XOP_EIOIO                854
 /* 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
+#define SPRN_GQR3              915
+#define SPRN_GQR4              916
+#define SPRN_GQR5              917
+#define SPRN_GQR6              918
+#define SPRN_GQR7              919
+
 int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                            unsigned int inst, int *advance)
 {
@@ -65,11 +81,11 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
        case 31:
                switch (get_xop(inst)) {
                case OP_31_XOP_MFMSR:
-                       vcpu->arch.gpr[get_rt(inst)] = vcpu->arch.msr;
+                       kvmppc_set_gpr(vcpu, get_rt(inst), vcpu->arch.msr);
                        break;
                case OP_31_XOP_MTMSRD:
                {
-                       ulong rs = vcpu->arch.gpr[get_rs(inst)];
+                       ulong rs = kvmppc_get_gpr(vcpu, get_rs(inst));
                        if (inst & 0x10000) {
                                vcpu->arch.msr &= ~(MSR_RI | MSR_EE);
                                vcpu->arch.msr |= rs & (MSR_RI | MSR_EE);
@@ -78,30 +94,47 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        break;
                }
                case OP_31_XOP_MTMSR:
-                       kvmppc_set_msr(vcpu, vcpu->arch.gpr[get_rs(inst)]);
+                       kvmppc_set_msr(vcpu, kvmppc_get_gpr(vcpu, get_rs(inst)));
+                       break;
+               case OP_31_XOP_MFSR:
+               {
+                       int srnum;
+
+                       srnum = kvmppc_get_field(inst, 12 + 32, 15 + 32);
+                       if (vcpu->arch.mmu.mfsrin) {
+                               u32 sr;
+                               sr = vcpu->arch.mmu.mfsrin(vcpu, srnum);
+                               kvmppc_set_gpr(vcpu, get_rt(inst), sr);
+                       }
                        break;
+               }
                case OP_31_XOP_MFSRIN:
                {
                        int srnum;
 
-                       srnum = (vcpu->arch.gpr[get_rb(inst)] >> 28) & 0xf;
+                       srnum = (kvmppc_get_gpr(vcpu, get_rb(inst)) >> 28) & 0xf;
                        if (vcpu->arch.mmu.mfsrin) {
                                u32 sr;
                                sr = vcpu->arch.mmu.mfsrin(vcpu, srnum);
-                               vcpu->arch.gpr[get_rt(inst)] = sr;
+                               kvmppc_set_gpr(vcpu, get_rt(inst), sr);
                        }
                        break;
                }
+               case OP_31_XOP_MTSR:
+                       vcpu->arch.mmu.mtsrin(vcpu,
+                               (inst >> 16) & 0xf,
+                               kvmppc_get_gpr(vcpu, get_rs(inst)));
+                       break;
                case OP_31_XOP_MTSRIN:
                        vcpu->arch.mmu.mtsrin(vcpu,
-                               (vcpu->arch.gpr[get_rb(inst)] >> 28) & 0xf,
-                               vcpu->arch.gpr[get_rs(inst)]);
+                               (kvmppc_get_gpr(vcpu, get_rb(inst)) >> 28) & 0xf,
+                               kvmppc_get_gpr(vcpu, get_rs(inst)));
                        break;
                case OP_31_XOP_TLBIE:
                case OP_31_XOP_TLBIEL:
                {
                        bool large = (inst & 0x00200000) ? true : false;
-                       ulong addr = vcpu->arch.gpr[get_rb(inst)];
+                       ulong addr = kvmppc_get_gpr(vcpu, get_rb(inst));
                        vcpu->arch.mmu.tlbie(vcpu, addr, large);
                        break;
                }
@@ -111,14 +144,16 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        if (!vcpu->arch.mmu.slbmte)
                                return EMULATE_FAIL;
 
-                       vcpu->arch.mmu.slbmte(vcpu, vcpu->arch.gpr[get_rs(inst)],
-                                               vcpu->arch.gpr[get_rb(inst)]);
+                       vcpu->arch.mmu.slbmte(vcpu,
+                                       kvmppc_get_gpr(vcpu, get_rs(inst)),
+                                       kvmppc_get_gpr(vcpu, get_rb(inst)));
                        break;
                case OP_31_XOP_SLBIE:
                        if (!vcpu->arch.mmu.slbie)
                                return EMULATE_FAIL;
 
-                       vcpu->arch.mmu.slbie(vcpu, vcpu->arch.gpr[get_rb(inst)]);
+                       vcpu->arch.mmu.slbie(vcpu,
+                                       kvmppc_get_gpr(vcpu, get_rb(inst)));
                        break;
                case OP_31_XOP_SLBIA:
                        if (!vcpu->arch.mmu.slbia)
@@ -132,9 +167,9 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        } else {
                                ulong t, rb;
 
-                               rb = vcpu->arch.gpr[get_rb(inst)];
+                               rb = kvmppc_get_gpr(vcpu, get_rb(inst));
                                t = vcpu->arch.mmu.slbmfee(vcpu, rb);
-                               vcpu->arch.gpr[get_rt(inst)] = t;
+                               kvmppc_set_gpr(vcpu, get_rt(inst), t);
                        }
                        break;
                case OP_31_XOP_SLBMFEV:
@@ -143,33 +178,45 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                        } else {
                                ulong t, rb;
 
-                               rb = vcpu->arch.gpr[get_rb(inst)];
+                               rb = kvmppc_get_gpr(vcpu, get_rb(inst));
                                t = vcpu->arch.mmu.slbmfev(vcpu, rb);
-                               vcpu->arch.gpr[get_rt(inst)] = t;
+                               kvmppc_set_gpr(vcpu, get_rt(inst), t);
                        }
                        break;
                case OP_31_XOP_DCBZ:
                {
-                       ulong rb =  vcpu->arch.gpr[get_rb(inst)];
+                       ulong rb = kvmppc_get_gpr(vcpu, get_rb(inst));
                        ulong ra = 0;
-                       ulong addr;
+                       ulong addr, vaddr;
                        u32 zeros[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+                       u32 dsisr;
+                       int r;
 
                        if (get_ra(inst))
-                               ra = vcpu->arch.gpr[get_ra(inst)];
+                               ra = kvmppc_get_gpr(vcpu, get_ra(inst));
 
                        addr = (ra + rb) & ~31ULL;
                        if (!(vcpu->arch.msr & MSR_SF))
                                addr &= 0xffffffff;
+                       vaddr = addr;
+
+                       r = kvmppc_st(vcpu, &addr, 32, zeros, true);
+                       if ((r == -ENOENT) || (r == -EPERM)) {
+                               *advance = 0;
+                               vcpu->arch.dear = vaddr;
+                               vcpu->arch.fault_dear = vaddr;
+
+                               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;
 
-                       if (kvmppc_st(vcpu, addr, 32, zeros)) {
-                               vcpu->arch.dear = addr;
-                               vcpu->arch.fault_dear = addr;
-                               to_book3s(vcpu)->dsisr = DSISR_PROTFAULT |
-                                                     DSISR_ISSTORE;
                                kvmppc_book3s_queue_irqprio(vcpu,
                                        BOOK3S_INTERRUPT_DATA_STORAGE);
-                               kvmppc_mmu_pte_flush(vcpu, addr, ~0xFFFULL);
                        }
 
                        break;
@@ -182,6 +229,9 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu,
                emulated = EMULATE_FAIL;
        }
 
+       if (emulated == EMULATE_FAIL)
+               emulated = kvmppc_emulate_paired_single(run, vcpu);
+
        return emulated;
 }
 
@@ -205,6 +255,34 @@ void kvmppc_set_bat(struct kvm_vcpu *vcpu, struct kvmppc_bat *bat, bool upper,
        }
 }
 
+static u32 kvmppc_read_bat(struct kvm_vcpu *vcpu, int sprn)
+{
+       struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);
+       struct kvmppc_bat *bat;
+
+       switch (sprn) {
+       case SPRN_IBAT0U ... SPRN_IBAT3L:
+               bat = &vcpu_book3s->ibat[(sprn - SPRN_IBAT0U) / 2];
+               break;
+       case SPRN_IBAT4U ... SPRN_IBAT7L:
+               bat = &vcpu_book3s->ibat[4 + ((sprn - SPRN_IBAT4U) / 2)];
+               break;
+       case SPRN_DBAT0U ... SPRN_DBAT3L:
+               bat = &vcpu_book3s->dbat[(sprn - SPRN_DBAT0U) / 2];
+               break;
+       case SPRN_DBAT4U ... SPRN_DBAT7L:
+               bat = &vcpu_book3s->dbat[4 + ((sprn - SPRN_DBAT4U) / 2)];
+               break;
+       default:
+               BUG();
+       }
+
+       if (sprn % 2)
+               return bat->raw >> 32;
+       else
+               return bat->raw;
+}
+
 static void kvmppc_write_bat(struct kvm_vcpu *vcpu, int sprn, u32 val)
 {
        struct kvmppc_vcpu_book3s *vcpu_book3s = to_book3s(vcpu);
@@ -215,13 +293,13 @@ static void kvmppc_write_bat(struct kvm_vcpu *vcpu, int sprn, u32 val)
                bat = &vcpu_book3s->ibat[(sprn - SPRN_IBAT0U) / 2];
                break;
        case SPRN_IBAT4U ... SPRN_IBAT7L:
-               bat = &vcpu_book3s->ibat[(sprn - SPRN_IBAT4U) / 2];
+               bat = &vcpu_book3s->ibat[4 + ((sprn - SPRN_IBAT4U) / 2)];
                break;
        case SPRN_DBAT0U ... SPRN_DBAT3L:
                bat = &vcpu_book3s->dbat[(sprn - SPRN_DBAT0U) / 2];
                break;
        case SPRN_DBAT4U ... SPRN_DBAT7L:
-               bat = &vcpu_book3s->dbat[(sprn - SPRN_DBAT4U) / 2];
+               bat = &vcpu_book3s->dbat[4 + ((sprn - SPRN_DBAT4U) / 2)];
                break;
        default:
                BUG();
@@ -233,54 +311,96 @@ static void kvmppc_write_bat(struct kvm_vcpu *vcpu, int sprn, u32 val)
 int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs)
 {
        int emulated = EMULATE_DONE;
+       ulong spr_val = kvmppc_get_gpr(vcpu, rs);
 
        switch (sprn) {
        case SPRN_SDR1:
-               to_book3s(vcpu)->sdr1 = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->sdr1 = spr_val;
                break;
        case SPRN_DSISR:
-               to_book3s(vcpu)->dsisr = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->dsisr = spr_val;
                break;
        case SPRN_DAR:
-               vcpu->arch.dear = vcpu->arch.gpr[rs];
+               vcpu->arch.dear = spr_val;
                break;
        case SPRN_HIOR:
-               to_book3s(vcpu)->hior = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->hior = spr_val;
                break;
        case SPRN_IBAT0U ... SPRN_IBAT3L:
        case SPRN_IBAT4U ... SPRN_IBAT7L:
        case SPRN_DBAT0U ... SPRN_DBAT3L:
        case SPRN_DBAT4U ... SPRN_DBAT7L:
-               kvmppc_write_bat(vcpu, sprn, (u32)vcpu->arch.gpr[rs]);
+               kvmppc_write_bat(vcpu, sprn, (u32)spr_val);
                /* BAT writes happen so rarely that we're ok to flush
                 * everything here */
                kvmppc_mmu_pte_flush(vcpu, 0, 0);
+               kvmppc_mmu_flush_segments(vcpu);
                break;
        case SPRN_HID0:
-               to_book3s(vcpu)->hid[0] = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->hid[0] = spr_val;
                break;
        case SPRN_HID1:
-               to_book3s(vcpu)->hid[1] = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->hid[1] = spr_val;
                break;
        case SPRN_HID2:
-               to_book3s(vcpu)->hid[2] = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->hid[2] = spr_val;
+               break;
+       case SPRN_HID2_GEKKO:
+               to_book3s(vcpu)->hid[2] = spr_val;
+               /* HID2.PSE controls paired single on gekko */
+               switch (vcpu->arch.pvr) {
+               case 0x00080200:        /* lonestar 2.0 */
+               case 0x00088202:        /* lonestar 2.2 */
+               case 0x70000100:        /* gekko 1.0 */
+               case 0x00080100:        /* gekko 2.0 */
+               case 0x00083203:        /* gekko 2.3a */
+               case 0x00083213:        /* gekko 2.3b */
+               case 0x00083204:        /* gekko 2.4 */
+               case 0x00083214:        /* gekko 2.4e (8SE) - retail HW2 */
+                       if (spr_val & (1 << 29)) { /* HID2.PSE */
+                               vcpu->arch.hflags |= BOOK3S_HFLAG_PAIRED_SINGLE;
+                               kvmppc_giveup_ext(vcpu, MSR_FP);
+                       } else {
+                               vcpu->arch.hflags &= ~BOOK3S_HFLAG_PAIRED_SINGLE;
+                       }
+                       break;
+               }
                break;
        case SPRN_HID4:
-               to_book3s(vcpu)->hid[4] = vcpu->arch.gpr[rs];
+       case SPRN_HID4_GEKKO:
+               to_book3s(vcpu)->hid[4] = spr_val;
                break;
        case SPRN_HID5:
-               to_book3s(vcpu)->hid[5] = vcpu->arch.gpr[rs];
+               to_book3s(vcpu)->hid[5] = spr_val;
                /* guest HID5 set can change is_dcbz32 */
                if (vcpu->arch.mmu.is_dcbz32(vcpu) &&
                    (mfmsr() & MSR_HV))
                        vcpu->arch.hflags |= BOOK3S_HFLAG_DCBZ32;
                break;
+       case SPRN_GQR0:
+       case SPRN_GQR1:
+       case SPRN_GQR2:
+       case SPRN_GQR3:
+       case SPRN_GQR4:
+       case SPRN_GQR5:
+       case SPRN_GQR6:
+       case SPRN_GQR7:
+               to_book3s(vcpu)->gqr[sprn - SPRN_GQR0] = spr_val;
+               break;
        case SPRN_ICTC:
        case SPRN_THRM1:
        case SPRN_THRM2:
        case SPRN_THRM3:
        case SPRN_CTRLF:
        case SPRN_CTRLT:
+       case SPRN_L2CR:
+       case SPRN_MMCR0_GEKKO:
+       case SPRN_MMCR1_GEKKO:
+       case SPRN_PMC1_GEKKO:
+       case SPRN_PMC2_GEKKO:
+       case SPRN_PMC3_GEKKO:
+       case SPRN_PMC4_GEKKO:
+       case SPRN_WPAR_GEKKO:
                break;
        default:
                printk(KERN_INFO "KVM: invalid SPR write: %d\n", sprn);
@@ -298,39 +418,66 @@ int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt)
        int emulated = EMULATE_DONE;
 
        switch (sprn) {
+       case SPRN_IBAT0U ... SPRN_IBAT3L:
+       case SPRN_IBAT4U ... SPRN_IBAT7L:
+       case SPRN_DBAT0U ... SPRN_DBAT3L:
+       case SPRN_DBAT4U ... SPRN_DBAT7L:
+               kvmppc_set_gpr(vcpu, rt, kvmppc_read_bat(vcpu, sprn));
+               break;
        case SPRN_SDR1:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->sdr1;
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->sdr1);
                break;
        case SPRN_DSISR:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->dsisr;
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->dsisr);
                break;
        case SPRN_DAR:
-               vcpu->arch.gpr[rt] = vcpu->arch.dear;
+               kvmppc_set_gpr(vcpu, rt, vcpu->arch.dear);
                break;
        case SPRN_HIOR:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->hior;
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->hior);
                break;
        case SPRN_HID0:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->hid[0];
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->hid[0]);
                break;
        case SPRN_HID1:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->hid[1];
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->hid[1]);
                break;
        case SPRN_HID2:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->hid[2];
+       case SPRN_HID2_GEKKO:
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->hid[2]);
                break;
        case SPRN_HID4:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->hid[4];
+       case SPRN_HID4_GEKKO:
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->hid[4]);
                break;
        case SPRN_HID5:
-               vcpu->arch.gpr[rt] = to_book3s(vcpu)->hid[5];
+               kvmppc_set_gpr(vcpu, rt, to_book3s(vcpu)->hid[5]);
+               break;
+       case SPRN_GQR0:
+       case SPRN_GQR1:
+       case SPRN_GQR2:
+       case SPRN_GQR3:
+       case SPRN_GQR4:
+       case SPRN_GQR5:
+       case SPRN_GQR6:
+       case SPRN_GQR7:
+               kvmppc_set_gpr(vcpu, rt,
+                              to_book3s(vcpu)->gqr[sprn - SPRN_GQR0]);
                break;
        case SPRN_THRM1:
        case SPRN_THRM2:
        case SPRN_THRM3:
        case SPRN_CTRLF:
        case SPRN_CTRLT:
-               vcpu->arch.gpr[rt] = 0;
+       case SPRN_L2CR:
+       case SPRN_MMCR0_GEKKO:
+       case SPRN_MMCR1_GEKKO:
+       case SPRN_PMC1_GEKKO:
+       case SPRN_PMC2_GEKKO:
+       case SPRN_PMC3_GEKKO:
+       case SPRN_PMC4_GEKKO:
+       case SPRN_WPAR_GEKKO:
+               kvmppc_set_gpr(vcpu, rt, 0);
                break;
        default:
                printk(KERN_INFO "KVM: invalid SPR read: %d\n", sprn);
@@ -343,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;
+}