Merge branch 'linus' into core/objtool, to pick up dependent commits
[sfrench/cifs-2.6.git] / arch / s390 / kvm / intercept.c
index a4752bf6b526d0e45164ed146931a2d0ea177706..8fe034beb623217f42bc990c58dea9984426ef30 100644 (file)
@@ -18,6 +18,7 @@
 #include <asm/kvm_host.h>
 #include <asm/asm-offsets.h>
 #include <asm/irq.h>
+#include <asm/sysinfo.h>
 
 #include "kvm-s390.h"
 #include "gaccess.h"
@@ -360,6 +361,61 @@ static int handle_partial_execution(struct kvm_vcpu *vcpu)
        return -EOPNOTSUPP;
 }
 
+/*
+ * Handle the sthyi instruction that provides the guest with system
+ * information, like current CPU resources available at each level of
+ * the machine.
+ */
+int handle_sthyi(struct kvm_vcpu *vcpu)
+{
+       int reg1, reg2, r = 0;
+       u64 code, addr, cc = 0, rc = 0;
+       struct sthyi_sctns *sctns = NULL;
+
+       if (!test_kvm_facility(vcpu->kvm, 74))
+               return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
+
+       kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
+       code = vcpu->run->s.regs.gprs[reg1];
+       addr = vcpu->run->s.regs.gprs[reg2];
+
+       vcpu->stat.instruction_sthyi++;
+       VCPU_EVENT(vcpu, 3, "STHYI: fc: %llu addr: 0x%016llx", code, addr);
+       trace_kvm_s390_handle_sthyi(vcpu, code, addr);
+
+       if (reg1 == reg2 || reg1 & 1 || reg2 & 1)
+               return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
+       if (code & 0xffff) {
+               cc = 3;
+               rc = 4;
+               goto out;
+       }
+
+       if (addr & ~PAGE_MASK)
+               return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
+       sctns = (void *)get_zeroed_page(GFP_KERNEL);
+       if (!sctns)
+               return -ENOMEM;
+
+       cc = sthyi_fill(sctns, &rc);
+
+out:
+       if (!cc) {
+               r = write_guest(vcpu, addr, reg2, sctns, PAGE_SIZE);
+               if (r) {
+                       free_page((unsigned long)sctns);
+                       return kvm_s390_inject_prog_cond(vcpu, r);
+               }
+       }
+
+       free_page((unsigned long)sctns);
+       vcpu->run->s.regs.gprs[reg2 + 1] = rc;
+       kvm_s390_set_psw_cc(vcpu, cc);
+       return r;
+}
+
 static int handle_operexc(struct kvm_vcpu *vcpu)
 {
        psw_t oldpsw, newpsw;