* Copyright IBM Corp. 2016
  * Author(s): Janosch Frank <frankja@linux.vnet.ibm.com>
  */
-#include <linux/kvm_host.h>
 #include <linux/errno.h>
 #include <linux/pagemap.h>
 #include <linux/vmalloc.h>
 #include <linux/ratelimit.h>
 
-#include <asm/kvm_host.h>
 #include <asm/asm-offsets.h>
 #include <asm/sclp.h>
 #include <asm/diag.h>
 #include <asm/sysinfo.h>
 #include <asm/ebcdic.h>
-
-#include "kvm-s390.h"
-#include "gaccess.h"
-#include "trace.h"
+#include <asm/facility.h>
 
 #define DED_WEIGHT 0xffff
 /*
        vfree(diag204_buf);
 }
 
-static int sthyi(u64 vaddr)
+static int sthyi(u64 vaddr, u64 *rc)
 {
        register u64 code asm("0") = 0;
        register u64 addr asm("2") = vaddr;
+       register u64 rcode asm("3");
        int cc;
 
        asm volatile(
                ".insn   rre,0xB2560000,%[code],%[addr]\n"
                "ipm     %[cc]\n"
                "srl     %[cc],28\n"
-               : [cc] "=d" (cc)
+               : [cc] "=d" (cc), "=d" (rcode)
                : [code] "d" (code), [addr] "a" (addr)
-               : "3", "memory", "cc");
+               : "memory", "cc");
+       *rc = rcode;
        return cc;
 }
 
-int handle_sthyi(struct kvm_vcpu *vcpu)
+/*
+ * sthyi_fill - Fill page with data returned by the STHYI instruction
+ *
+ * @dst: Pointer to zeroed page
+ * @rc:  Pointer for storing the return code of the instruction
+ *
+ * Fills the destination with system information returned by the STHYI
+ * instruction. The data is generated by emulation or execution of STHYI,
+ * if available. The return value is the condition code that would be
+ * returned, the rc parameter is the return code which is passed in
+ * register R2 + 1.
+ */
+int sthyi_fill(void *dst, u64 *rc)
 {
-       int reg1, reg2, r = 0;
-       u64 code, addr, cc = 0;
-       struct sthyi_sctns *sctns = NULL;
-
-       if (!test_kvm_facility(vcpu->kvm, 74))
-               return kvm_s390_inject_program_int(vcpu, PGM_OPERATION);
-
-       /*
-        * STHYI requires extensive locking in the higher hypervisors
-        * and is very computational/memory expensive. Therefore we
-        * ratelimit the executions per VM.
-        */
-       if (!__ratelimit(&vcpu->kvm->arch.sthyi_limit)) {
-               kvm_s390_retry_instr(vcpu);
-               return 0;
-       }
-
-       kvm_s390_get_regs_rre(vcpu, ®1, ®2);
-       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;
-               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;
+       struct sthyi_sctns *sctns = (struct sthyi_sctns *)dst;
 
        /*
-        * If we are a guest, we don't want to emulate an emulated
-        * instruction. We ask the hypervisor to provide the data.
+        * If the facility is on, we don't want to emulate the instruction.
+        * We ask the hypervisor to provide the data.
         */
-       if (test_facility(74)) {
-               cc = sthyi((u64)sctns);
-               goto out;
-       }
+       if (test_facility(74))
+               return sthyi((u64)dst, rc);
 
        fill_hdr(sctns);
        fill_stsi(sctns);
        fill_diag(sctns);
 
-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] = cc ? 4 : 0;
-       kvm_s390_set_psw_cc(vcpu, cc);
-       return r;
+       *rc = 0;
+       return 0;
 }
+EXPORT_SYMBOL_GPL(sthyi_fill);
 
 #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"
        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);
+
+       /*
+        * STHYI requires extensive locking in the higher hypervisors
+        * and is very computational/memory expensive. Therefore we
+        * ratelimit the executions per VM.
+        */
+       if (!__ratelimit(&vcpu->kvm->arch.sthyi_limit)) {
+               kvm_s390_retry_instr(vcpu);
+               return 0;
+       }
+
+       kvm_s390_get_regs_rre(vcpu, ®1, ®2);
+       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;