]> git.karo-electronics.de Git - linux-beck.git/commitdiff
KVM: s390: vsie: support STFLE interpretation
authorDavid Hildenbrand <dahi@linux.vnet.ibm.com>
Thu, 26 Nov 2015 13:11:19 +0000 (14:11 +0100)
committerChristian Borntraeger <borntraeger@de.ibm.com>
Tue, 21 Jun 2016 07:43:36 +0000 (09:43 +0200)
Issuing STFLE is extremely rare. Instead of copying 2k on every
VSIE call, let's do this lazily, when a guest 3 tries to execute
STFLE. We can setup the block and retry.

Unfortunately, we can't directly forward that facility list, as
we only have a 31 bit address for the facility list designation.
So let's use a DMA allocation for our vsie_page instead for now.

Acked-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: David Hildenbrand <dahi@linux.vnet.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
arch/s390/kvm/vsie.c

index aaed63ce29b23d160be5a17f1691aa94afc4ece8..cd4bbfa72881dd9ec8198acc82869301a7779fe4 100644 (file)
@@ -18,6 +18,7 @@
 #include <asm/mmu_context.h>
 #include <asm/sclp.h>
 #include <asm/nmi.h>
+#include <asm/dis.h>
 #include "kvm-s390.h"
 #include "gaccess.h"
 
@@ -27,7 +28,8 @@ struct vsie_page {
        struct kvm_s390_sie_block *scb_o;       /* 0x0200 */
        /* the shadow gmap in use by the vsie_page */
        struct gmap *gmap;                      /* 0x0208 */
-       __u8 reserved[0x1000 - 0x0210];         /* 0x0210 */
+       __u8 reserved[0x0800 - 0x0210];         /* 0x0210 */
+       __u8 fac[S390_ARCH_FAC_LIST_SIZE_BYTE]; /* 0x0800 */
 } __packed;
 
 /* trigger a validity icpt for the given scb */
@@ -194,6 +196,7 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
        scb_s->ecb2 = 0;
        scb_s->ecb3 = 0;
        scb_s->ecd = 0;
+       scb_s->fac = 0;
 
        rc = prepare_cpuflags(vcpu, vsie_page);
        if (rc)
@@ -521,6 +524,44 @@ static inline void clear_vsie_icpt(struct vsie_page *vsie_page)
        vsie_page->scb_s.icptcode = 0;
 }
 
+/* rewind the psw and clear the vsie icpt, so we can retry execution */
+static void retry_vsie_icpt(struct vsie_page *vsie_page)
+{
+       struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
+       int ilen = insn_length(scb_s->ipa >> 8);
+
+       /* take care of EXECUTE instructions */
+       if (scb_s->icptstatus & 1) {
+               ilen = (scb_s->icptstatus >> 4) & 0x6;
+               if (!ilen)
+                       ilen = 4;
+       }
+       scb_s->gpsw.addr = __rewind_psw(scb_s->gpsw, ilen);
+       clear_vsie_icpt(vsie_page);
+}
+
+/*
+ * Try to shadow + enable the guest 2 provided facility list.
+ * Retry instruction execution if enabled for and provided by guest 2.
+ *
+ * Returns: - 0 if handled (retry or guest 2 icpt)
+ *          - > 0 if control has to be given to guest 2
+ */
+static int handle_stfle(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
+{
+       struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
+       __u32 fac = vsie_page->scb_o->fac & 0x7ffffff8U;
+
+       if (fac && test_kvm_facility(vcpu->kvm, 7)) {
+               retry_vsie_icpt(vsie_page);
+               if (read_guest_real(vcpu, fac, &vsie_page->fac,
+                                   sizeof(vsie_page->fac)))
+                       return set_validity_icpt(scb_s, 0x1090U);
+               scb_s->fac = (__u32)(__u64) &vsie_page->fac;
+       }
+       return 0;
+}
+
 /*
  * Run the vsie on a shadow scb and a shadow gmap, without any further
  * sanity checks, handling SIE faults.
@@ -558,6 +599,10 @@ static int do_vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
                return handle_fault(vcpu, vsie_page);
 
        switch (scb_s->icptcode) {
+       case ICPT_INST:
+               if (scb_s->ipa == 0xb2b0)
+                       rc = handle_stfle(vcpu, vsie_page);
+               break;
        case ICPT_STOP:
                /* stop not requested by g2 - must have been a kick */
                if (!(atomic_read(&scb_o->cpuflags) & CPUSTAT_STOP_INT))
@@ -690,7 +735,7 @@ static struct vsie_page *get_vsie_page(struct kvm *kvm, unsigned long addr)
 
        mutex_lock(&kvm->arch.vsie.mutex);
        if (kvm->arch.vsie.page_count < nr_vcpus) {
-               page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+               page = alloc_page(GFP_KERNEL | __GFP_ZERO | GFP_DMA);
                if (!page) {
                        mutex_unlock(&kvm->arch.vsie.mutex);
                        return ERR_PTR(-ENOMEM);