Merge tag 'kvm-3.15-1' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[linux-2.6-microblaze.git] / arch / s390 / kvm / priv.c
index ae9e8ee..476e9e2 100644 (file)
@@ -631,8 +631,49 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
        return 0;
 }
 
+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, cbrle;
+       struct gmap *gmap;
+       int i;
+
+       VCPU_EVENT(vcpu, 5, "cmma release %d pages", entries);
+       gmap = vcpu->arch.gmap;
+       vcpu->stat.instruction_essa++;
+       if (!kvm_enabled_cmma() || !vcpu->arch.sie_block->cbrlo)
+               return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
+
+       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)
+               return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
+       /* Rewind PSW to repeat the ESSA instruction */
+       vcpu->arch.sie_block->gpsw.addr =
+               __rewind_psw(vcpu->arch.sie_block->gpsw, 4);
+       vcpu->arch.sie_block->cbrlo &= PAGE_MASK;       /* reset nceo */
+       cbrlo = phys_to_virt(vcpu->arch.sie_block->cbrlo);
+       down_read(&gmap->mm->mmap_sem);
+       for (i = 0; i < entries; ++i) {
+               cbrle = cbrlo[i];
+               if (unlikely(cbrle & ~PAGE_MASK || cbrle < 2 * PAGE_SIZE))
+                       /* invalid entry */
+                       break;
+               /* try to free backing */
+               __gmap_zap(cbrle, gmap);
+       }
+       up_read(&gmap->mm->mmap_sem);
+       if (i < entries)
+               return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+       return 0;
+}
+
 static const intercept_handler_t b9_handlers[256] = {
        [0x8d] = handle_epsw,
+       [0xab] = handle_essa,
        [0xaf] = handle_pfmf,
 };