]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - arch/s390/kvm/priv.c
Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[mirror_ubuntu-artful-kernel.git] / arch / s390 / kvm / priv.c
index c03106c428cfa89e1f25297efbd2d667a73b5fd7..8a1dac793d6b0ad0685ffd7a35743ca511274035 100644 (file)
@@ -24,6 +24,7 @@
 #include <asm/ebcdic.h>
 #include <asm/sysinfo.h>
 #include <asm/pgtable.h>
+#include <asm/page-states.h>
 #include <asm/pgalloc.h>
 #include <asm/gmap.h>
 #include <asm/io.h>
@@ -361,7 +362,7 @@ static int handle_sske(struct kvm_vcpu *vcpu)
                }
        }
        if (m3 & SSKE_MB) {
-               if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_AMODE_64BIT)
+               if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_64BIT)
                        vcpu->run->s.regs.gprs[reg2] &= ~PAGE_MASK;
                else
                        vcpu->run->s.regs.gprs[reg2] &= ~0xfffff000UL;
@@ -374,7 +375,7 @@ static int handle_sske(struct kvm_vcpu *vcpu)
 static int handle_ipte_interlock(struct kvm_vcpu *vcpu)
 {
        vcpu->stat.instruction_ipte_interlock++;
-       if (psw_bits(vcpu->arch.sie_block->gpsw).p)
+       if (psw_bits(vcpu->arch.sie_block->gpsw).pstate)
                return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
        wait_event(vcpu->kvm->arch.ipte_wq, !ipte_lock_held(vcpu));
        kvm_s390_retry_instr(vcpu);
@@ -901,7 +902,7 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
                /* only support 2G frame size if EDAT2 is available and we are
                   not in 24-bit addressing mode */
                if (!test_kvm_facility(vcpu->kvm, 78) ||
-                   psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_AMODE_24BIT)
+                   psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_24BIT)
                        return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
                end = (start + (1UL << 31)) & ~((1UL << 31) - 1);
                break;
@@ -938,7 +939,7 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
                start += PAGE_SIZE;
        }
        if (vcpu->run->s.regs.gprs[reg1] & PFMF_FSC) {
-               if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_AMODE_64BIT) {
+               if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_64BIT) {
                        vcpu->run->s.regs.gprs[reg2] = end;
                } else {
                        vcpu->run->s.regs.gprs[reg2] &= ~0xffffffffUL;
@@ -949,13 +950,72 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
        return 0;
 }
 
+static inline int do_essa(struct kvm_vcpu *vcpu, const int orc)
+{
+       struct kvm_s390_migration_state *ms = vcpu->kvm->arch.migration_state;
+       int r1, r2, nappended, entries;
+       unsigned long gfn, hva, res, pgstev, ptev;
+       unsigned long *cbrlo;
+
+       /*
+        * We don't need to set SD.FPF.SK to 1 here, because if we have a
+        * machine check here we either handle it or crash
+        */
+
+       kvm_s390_get_regs_rre(vcpu, &r1, &r2);
+       gfn = vcpu->run->s.regs.gprs[r2] >> PAGE_SHIFT;
+       hva = gfn_to_hva(vcpu->kvm, gfn);
+       entries = (vcpu->arch.sie_block->cbrlo & ~PAGE_MASK) >> 3;
+
+       if (kvm_is_error_hva(hva))
+               return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+
+       nappended = pgste_perform_essa(vcpu->kvm->mm, hva, orc, &ptev, &pgstev);
+       if (nappended < 0) {
+               res = orc ? 0x10 : 0;
+               vcpu->run->s.regs.gprs[r1] = res; /* Exception Indication */
+               return 0;
+       }
+       res = (pgstev & _PGSTE_GPS_USAGE_MASK) >> 22;
+       /*
+        * Set the block-content state part of the result. 0 means resident, so
+        * nothing to do if the page is valid. 2 is for preserved pages
+        * (non-present and non-zero), and 3 for zero pages (non-present and
+        * zero).
+        */
+       if (ptev & _PAGE_INVALID) {
+               res |= 2;
+               if (pgstev & _PGSTE_GPS_ZERO)
+                       res |= 1;
+       }
+       vcpu->run->s.regs.gprs[r1] = res;
+       /*
+        * It is possible that all the normal 511 slots were full, in which case
+        * we will now write in the 512th slot, which is reserved for host use.
+        * In both cases we let the normal essa handling code process all the
+        * slots, including the reserved one, if needed.
+        */
+       if (nappended > 0) {
+               cbrlo = phys_to_virt(vcpu->arch.sie_block->cbrlo & PAGE_MASK);
+               cbrlo[entries] = gfn << PAGE_SHIFT;
+       }
+
+       if (orc) {
+               /* increment only if we are really flipping the bit to 1 */
+               if (!test_and_set_bit(gfn, ms->pgste_bitmap))
+                       atomic64_inc(&ms->dirty_pages);
+       }
+
+       return nappended;
+}
+
 static int handle_essa(struct kvm_vcpu *vcpu)
 {
        /* entries expected to be 1FF */
        int entries = (vcpu->arch.sie_block->cbrlo & ~PAGE_MASK) >> 3;
        unsigned long *cbrlo;
        struct gmap *gmap;
-       int i;
+       int i, orc;
 
        VCPU_EVENT(vcpu, 4, "ESSA: release %d pages", entries);
        gmap = vcpu->arch.gmap;
@@ -965,12 +1025,45 @@ static int handle_essa(struct kvm_vcpu *vcpu)
 
        if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
                return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
-
-       if (((vcpu->arch.sie_block->ipb & 0xf0000000) >> 28) > 6)
+       /* Check for invalid operation request code */
+       orc = (vcpu->arch.sie_block->ipb & 0xf0000000) >> 28;
+       if (orc > ESSA_MAX)
                return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
-       /* Retry the ESSA instruction */
-       kvm_s390_retry_instr(vcpu);
+       if (likely(!vcpu->kvm->arch.migration_state)) {
+               /*
+                * CMMA is enabled in the KVM settings, but is disabled in
+                * the SIE block and in the mm_context, and we are not doing
+                * a migration. Enable CMMA in the mm_context.
+                * Since we need to take a write lock to write to the context
+                * to avoid races with storage keys handling, we check if the
+                * value really needs to be written to; if the value is
+                * already correct, we do nothing and avoid the lock.
+                */
+               if (vcpu->kvm->mm->context.use_cmma == 0) {
+                       down_write(&vcpu->kvm->mm->mmap_sem);
+                       vcpu->kvm->mm->context.use_cmma = 1;
+                       up_write(&vcpu->kvm->mm->mmap_sem);
+               }
+               /*
+                * If we are here, we are supposed to have CMMA enabled in
+                * the SIE block. Enabling CMMA works on a per-CPU basis,
+                * while the context use_cmma flag is per process.
+                * It's possible that the context flag is enabled and the
+                * SIE flag is not, so we set the flag always; if it was
+                * already set, nothing changes, otherwise we enable it
+                * on this CPU too.
+                */
+               vcpu->arch.sie_block->ecb2 |= ECB2_CMMA;
+               /* Retry the ESSA instruction */
+               kvm_s390_retry_instr(vcpu);
+       } else {
+               /* Account for the possible extra cbrl entry */
+               i = do_essa(vcpu, orc);
+               if (i < 0)
+                       return i;
+               entries += i;
+       }
        vcpu->arch.sie_block->cbrlo &= PAGE_MASK;       /* reset nceo */
        cbrlo = phys_to_virt(vcpu->arch.sie_block->cbrlo);
        down_read(&gmap->mm->mmap_sem);