Blob Blame History Raw
From: QingFeng Hao <haoqf@linux.vnet.ibm.com>
Subject: s390/sthyi: reorganize sthyi implementation
Patch-mainline: v4.15-rc1
Git-commit: b7c92f1a4e131e459bcf53a570e7265e5ce64455
References: FATE#324258, LTC#160415, bsc#1068273


Heiko Carstens <heiko.carstens@de.ibm.com>
Summary:     kernel: STHYI for LPAR
Description: This feature makes STHYI(Store Hypervisor Information) available
             in LPAR using the syscall of NO. 380, and re-using the
             implementation for KVM by Janosch Frank - commit 95ca2cb57985
             ("KVM: s390: Add sthyi emulation").
             STHYI is an emulated z/VM instruction that provides a guest with
             basic information about the layers it is running on.
             This includes information about the cpu configuration of both the
             machine and the lpar, as well as their names, machine model and
             machine type. This information enables an application to determine
             the maximum capacity of CPs and IFLs available to software.
             More detailes are in link:
             http://www.ibm.com/support/knowledgecenter/SSB27U_6.3.0
             /com.ibm.zvm.v630.hcpb4/hcpb4sth.htm
             It is important to check the validity flags in the sections before
             using data from any structure member. It is not guaranteed that
             all members will be valid on all machines/machine configurations.

Upstream-Description:

             s390/sthyi: reorganize sthyi implementation

             As we need to support sthyi instruction on LPAR too, move the common code
             to kernel part and kvm related code to intercept.c for better reuse.

             Signed-off-by: QingFeng Hao <haoqf@linux.vnet.ibm.com>
             Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
             Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>


Signed-off-by: QingFeng Hao <haoqf@linux.vnet.ibm.com>
Acked-by: Hannes Reinecke <hare@suse.com>
Acked-by: Alexander Graf <agraf@suse.com>
---
 arch/s390/include/asm/sysinfo.h |    1 
 arch/s390/kernel/Makefile       |    2 
 arch/s390/kernel/sthyi.c        |  428 +++++++++++++++++++++++++++++++++++
 arch/s390/kvm/Makefile          |    2 
 arch/s390/kvm/intercept.c       |   66 +++++
 arch/s390/kvm/kvm-s390.h        |    5 
 arch/s390/kvm/sthyi.c           |  477 ----------------------------------------
 7 files changed, 499 insertions(+), 482 deletions(-)

diff --git a/arch/s390/include/asm/sysinfo.h b/arch/s390/include/asm/sysinfo.h
index 2b498e58b914..e4a28307bc5d 100644
--- a/arch/s390/include/asm/sysinfo.h
+++ b/arch/s390/include/asm/sysinfo.h
@@ -198,4 +198,5 @@ struct service_level {
 int register_service_level(struct service_level *);
 int unregister_service_level(struct service_level *);
 
+int sthyi_fill(void *dst, u64 *rc);
 #endif /* __ASM_S390_SYSINFO_H */
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
index adb3fe2e3d42..1fefb7f9216f 100644
--- a/arch/s390/kernel/Makefile
+++ b/arch/s390/kernel/Makefile
@@ -55,7 +55,7 @@ obj-y	:= traps.o time.o process.o base.o early.o setup.o idle.o vtime.o
 obj-y	+= processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o
 obj-y	+= debug.o irq.o ipl.o dis.o diag.o vdso.o als.o
 obj-y	+= sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o
-obj-y	+= runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o
+obj-y	+= runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o
 obj-y	+= entry.o reipl.o relocate_kernel.o kdebugfs.o
 
 extra-y				+= head.o head64.o vmlinux.lds
diff --git a/arch/s390/kernel/sthyi.c b/arch/s390/kernel/sthyi.c
new file mode 100644
index 000000000000..3d51f86f9dec
--- /dev/null
+++ b/arch/s390/kernel/sthyi.c
@@ -0,0 +1,428 @@
+/*
+ * store hypervisor information instruction emulation functions.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ *
+ * Copyright IBM Corp. 2016
+ * Author(s): Janosch Frank <frankja@linux.vnet.ibm.com>
+ */
+#include <linux/errno.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/ratelimit.h>
+
+#include <asm/asm-offsets.h>
+#include <asm/sclp.h>
+#include <asm/diag.h>
+#include <asm/sysinfo.h>
+#include <asm/ebcdic.h>
+#include <asm/facility.h>
+
+#define DED_WEIGHT 0xffff
+/*
+ * CP and IFL as EBCDIC strings, SP/0x40 determines the end of string
+ * as they are justified with spaces.
+ */
+#define CP  0xc3d7404040404040UL
+#define IFL 0xc9c6d34040404040UL
+
+enum hdr_flags {
+	HDR_NOT_LPAR   = 0x10,
+	HDR_STACK_INCM = 0x20,
+	HDR_STSI_UNAV  = 0x40,
+	HDR_PERF_UNAV  = 0x80,
+};
+
+enum mac_validity {
+	MAC_NAME_VLD = 0x20,
+	MAC_ID_VLD   = 0x40,
+	MAC_CNT_VLD  = 0x80,
+};
+
+enum par_flag {
+	PAR_MT_EN = 0x80,
+};
+
+enum par_validity {
+	PAR_GRP_VLD  = 0x08,
+	PAR_ID_VLD   = 0x10,
+	PAR_ABS_VLD  = 0x20,
+	PAR_WGHT_VLD = 0x40,
+	PAR_PCNT_VLD  = 0x80,
+};
+
+struct hdr_sctn {
+	u8 infhflg1;
+	u8 infhflg2; /* reserved */
+	u8 infhval1; /* reserved */
+	u8 infhval2; /* reserved */
+	u8 reserved[3];
+	u8 infhygct;
+	u16 infhtotl;
+	u16 infhdln;
+	u16 infmoff;
+	u16 infmlen;
+	u16 infpoff;
+	u16 infplen;
+	u16 infhoff1;
+	u16 infhlen1;
+	u16 infgoff1;
+	u16 infglen1;
+	u16 infhoff2;
+	u16 infhlen2;
+	u16 infgoff2;
+	u16 infglen2;
+	u16 infhoff3;
+	u16 infhlen3;
+	u16 infgoff3;
+	u16 infglen3;
+	u8 reserved2[4];
+} __packed;
+
+struct mac_sctn {
+	u8 infmflg1; /* reserved */
+	u8 infmflg2; /* reserved */
+	u8 infmval1;
+	u8 infmval2; /* reserved */
+	u16 infmscps;
+	u16 infmdcps;
+	u16 infmsifl;
+	u16 infmdifl;
+	char infmname[8];
+	char infmtype[4];
+	char infmmanu[16];
+	char infmseq[16];
+	char infmpman[4];
+	u8 reserved[4];
+} __packed;
+
+struct par_sctn {
+	u8 infpflg1;
+	u8 infpflg2; /* reserved */
+	u8 infpval1;
+	u8 infpval2; /* reserved */
+	u16 infppnum;
+	u16 infpscps;
+	u16 infpdcps;
+	u16 infpsifl;
+	u16 infpdifl;
+	u16 reserved;
+	char infppnam[8];
+	u32 infpwbcp;
+	u32 infpabcp;
+	u32 infpwbif;
+	u32 infpabif;
+	char infplgnm[8];
+	u32 infplgcp;
+	u32 infplgif;
+} __packed;
+
+struct sthyi_sctns {
+	struct hdr_sctn hdr;
+	struct mac_sctn mac;
+	struct par_sctn par;
+} __packed;
+
+struct cpu_inf {
+	u64 lpar_cap;
+	u64 lpar_grp_cap;
+	u64 lpar_weight;
+	u64 all_weight;
+	int cpu_num_ded;
+	int cpu_num_shd;
+};
+
+struct lpar_cpu_inf {
+	struct cpu_inf cp;
+	struct cpu_inf ifl;
+};
+
+static inline u64 cpu_id(u8 ctidx, void *diag224_buf)
+{
+	return *((u64 *)(diag224_buf + (ctidx + 1) * DIAG204_CPU_NAME_LEN));
+}
+
+/*
+ * Scales the cpu capping from the lpar range to the one expected in
+ * sthyi data.
+ *
+ * diag204 reports a cap in hundredths of processor units.
+ * z/VM's range for one core is 0 - 0x10000.
+ */
+static u32 scale_cap(u32 in)
+{
+	return (0x10000 * in) / 100;
+}
+
+static void fill_hdr(struct sthyi_sctns *sctns)
+{
+	sctns->hdr.infhdln = sizeof(sctns->hdr);
+	sctns->hdr.infmoff = sizeof(sctns->hdr);
+	sctns->hdr.infmlen = sizeof(sctns->mac);
+	sctns->hdr.infplen = sizeof(sctns->par);
+	sctns->hdr.infpoff = sctns->hdr.infhdln + sctns->hdr.infmlen;
+	sctns->hdr.infhtotl = sctns->hdr.infpoff + sctns->hdr.infplen;
+}
+
+static void fill_stsi_mac(struct sthyi_sctns *sctns,
+			  struct sysinfo_1_1_1 *sysinfo)
+{
+	if (stsi(sysinfo, 1, 1, 1))
+		return;
+
+	sclp_ocf_cpc_name_copy(sctns->mac.infmname);
+
+	memcpy(sctns->mac.infmtype, sysinfo->type, sizeof(sctns->mac.infmtype));
+	memcpy(sctns->mac.infmmanu, sysinfo->manufacturer, sizeof(sctns->mac.infmmanu));
+	memcpy(sctns->mac.infmpman, sysinfo->plant, sizeof(sctns->mac.infmpman));
+	memcpy(sctns->mac.infmseq, sysinfo->sequence, sizeof(sctns->mac.infmseq));
+
+	sctns->mac.infmval1 |= MAC_ID_VLD | MAC_NAME_VLD;
+}
+
+static void fill_stsi_par(struct sthyi_sctns *sctns,
+			  struct sysinfo_2_2_2 *sysinfo)
+{
+	if (stsi(sysinfo, 2, 2, 2))
+		return;
+
+	sctns->par.infppnum = sysinfo->lpar_number;
+	memcpy(sctns->par.infppnam, sysinfo->name, sizeof(sctns->par.infppnam));
+
+	sctns->par.infpval1 |= PAR_ID_VLD;
+}
+
+static void fill_stsi(struct sthyi_sctns *sctns)
+{
+	void *sysinfo;
+
+	/* Errors are handled through the validity bits in the response. */
+	sysinfo = (void *)__get_free_page(GFP_KERNEL);
+	if (!sysinfo)
+		return;
+
+	fill_stsi_mac(sctns, sysinfo);
+	fill_stsi_par(sctns, sysinfo);
+
+	free_pages((unsigned long)sysinfo, 0);
+}
+
+static void fill_diag_mac(struct sthyi_sctns *sctns,
+			  struct diag204_x_phys_block *block,
+			  void *diag224_buf)
+{
+	int i;
+
+	for (i = 0; i < block->hdr.cpus; i++) {
+		switch (cpu_id(block->cpus[i].ctidx, diag224_buf)) {
+		case CP:
+			if (block->cpus[i].weight == DED_WEIGHT)
+				sctns->mac.infmdcps++;
+			else
+				sctns->mac.infmscps++;
+			break;
+		case IFL:
+			if (block->cpus[i].weight == DED_WEIGHT)
+				sctns->mac.infmdifl++;
+			else
+				sctns->mac.infmsifl++;
+			break;
+		}
+	}
+	sctns->mac.infmval1 |= MAC_CNT_VLD;
+}
+
+/* Returns a pointer to the the next partition block. */
+static struct diag204_x_part_block *lpar_cpu_inf(struct lpar_cpu_inf *part_inf,
+						 bool this_lpar,
+						 void *diag224_buf,
+						 struct diag204_x_part_block *block)
+{
+	int i, capped = 0, weight_cp = 0, weight_ifl = 0;
+	struct cpu_inf *cpu_inf;
+
+	for (i = 0; i < block->hdr.rcpus; i++) {
+		if (!(block->cpus[i].cflag & DIAG204_CPU_ONLINE))
+			continue;
+
+		switch (cpu_id(block->cpus[i].ctidx, diag224_buf)) {
+		case CP:
+			cpu_inf = &part_inf->cp;
+			if (block->cpus[i].cur_weight < DED_WEIGHT)
+				weight_cp |= block->cpus[i].cur_weight;
+			break;
+		case IFL:
+			cpu_inf = &part_inf->ifl;
+			if (block->cpus[i].cur_weight < DED_WEIGHT)
+				weight_ifl |= block->cpus[i].cur_weight;
+			break;
+		default:
+			continue;
+		}
+
+		if (!this_lpar)
+			continue;
+
+		capped |= block->cpus[i].cflag & DIAG204_CPU_CAPPED;
+		cpu_inf->lpar_cap |= block->cpus[i].cpu_type_cap;
+		cpu_inf->lpar_grp_cap |= block->cpus[i].group_cpu_type_cap;
+
+		if (block->cpus[i].weight == DED_WEIGHT)
+			cpu_inf->cpu_num_ded += 1;
+		else
+			cpu_inf->cpu_num_shd += 1;
+	}
+
+	if (this_lpar && capped) {
+		part_inf->cp.lpar_weight = weight_cp;
+		part_inf->ifl.lpar_weight = weight_ifl;
+	}
+	part_inf->cp.all_weight += weight_cp;
+	part_inf->ifl.all_weight += weight_ifl;
+	return (struct diag204_x_part_block *)&block->cpus[i];
+}
+
+static void fill_diag(struct sthyi_sctns *sctns)
+{
+	int i, r, pages;
+	bool this_lpar;
+	void *diag204_buf;
+	void *diag224_buf = NULL;
+	struct diag204_x_info_blk_hdr *ti_hdr;
+	struct diag204_x_part_block *part_block;
+	struct diag204_x_phys_block *phys_block;
+	struct lpar_cpu_inf lpar_inf = {};
+
+	/* Errors are handled through the validity bits in the response. */
+	pages = diag204((unsigned long)DIAG204_SUBC_RSI |
+			(unsigned long)DIAG204_INFO_EXT, 0, NULL);
+	if (pages <= 0)
+		return;
+
+	diag204_buf = vmalloc(PAGE_SIZE * pages);
+	if (!diag204_buf)
+		return;
+
+	r = diag204((unsigned long)DIAG204_SUBC_STIB7 |
+		    (unsigned long)DIAG204_INFO_EXT, pages, diag204_buf);
+	if (r < 0)
+		goto out;
+
+	diag224_buf = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
+	if (!diag224_buf || diag224(diag224_buf))
+		goto out;
+
+	ti_hdr = diag204_buf;
+	part_block = diag204_buf + sizeof(*ti_hdr);
+
+	for (i = 0; i < ti_hdr->npar; i++) {
+		/*
+		 * For the calling lpar we also need to get the cpu
+		 * caps and weights. The time information block header
+		 * specifies the offset to the partition block of the
+		 * caller lpar, so we know when we process its data.
+		 */
+		this_lpar = (void *)part_block - diag204_buf == ti_hdr->this_part;
+		part_block = lpar_cpu_inf(&lpar_inf, this_lpar, diag224_buf,
+					  part_block);
+	}
+
+	phys_block = (struct diag204_x_phys_block *)part_block;
+	part_block = diag204_buf + ti_hdr->this_part;
+	if (part_block->hdr.mtid)
+		sctns->par.infpflg1 = PAR_MT_EN;
+
+	sctns->par.infpval1 |= PAR_GRP_VLD;
+	sctns->par.infplgcp = scale_cap(lpar_inf.cp.lpar_grp_cap);
+	sctns->par.infplgif = scale_cap(lpar_inf.ifl.lpar_grp_cap);
+	memcpy(sctns->par.infplgnm, part_block->hdr.hardware_group_name,
+	       sizeof(sctns->par.infplgnm));
+
+	sctns->par.infpscps = lpar_inf.cp.cpu_num_shd;
+	sctns->par.infpdcps = lpar_inf.cp.cpu_num_ded;
+	sctns->par.infpsifl = lpar_inf.ifl.cpu_num_shd;
+	sctns->par.infpdifl = lpar_inf.ifl.cpu_num_ded;
+	sctns->par.infpval1 |= PAR_PCNT_VLD;
+
+	sctns->par.infpabcp = scale_cap(lpar_inf.cp.lpar_cap);
+	sctns->par.infpabif = scale_cap(lpar_inf.ifl.lpar_cap);
+	sctns->par.infpval1 |= PAR_ABS_VLD;
+
+	/*
+	 * Everything below needs global performance data to be
+	 * meaningful.
+	 */
+	if (!(ti_hdr->flags & DIAG204_LPAR_PHYS_FLG)) {
+		sctns->hdr.infhflg1 |= HDR_PERF_UNAV;
+		goto out;
+	}
+
+	fill_diag_mac(sctns, phys_block, diag224_buf);
+
+	if (lpar_inf.cp.lpar_weight) {
+		sctns->par.infpwbcp = sctns->mac.infmscps * 0x10000 *
+			lpar_inf.cp.lpar_weight / lpar_inf.cp.all_weight;
+	}
+
+	if (lpar_inf.ifl.lpar_weight) {
+		sctns->par.infpwbif = sctns->mac.infmsifl * 0x10000 *
+			lpar_inf.ifl.lpar_weight / lpar_inf.ifl.all_weight;
+	}
+	sctns->par.infpval1 |= PAR_WGHT_VLD;
+
+out:
+	free_page((unsigned long)diag224_buf);
+	vfree(diag204_buf);
+}
+
+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), "=d" (rcode)
+		: [code] "d" (code), [addr] "a" (addr)
+		: "memory", "cc");
+	*rc = rcode;
+	return cc;
+}
+
+/*
+ * 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)
+{
+	struct sthyi_sctns *sctns = (struct sthyi_sctns *)dst;
+
+	/*
+	 * 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))
+		return sthyi((u64)dst, rc);
+
+	fill_hdr(sctns);
+	fill_stsi(sctns);
+	fill_diag(sctns);
+
+	*rc = 0;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sthyi_fill);
diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile
index 09a9e6dfc09f..6048b1c6e580 100644
--- a/arch/s390/kvm/Makefile
+++ b/arch/s390/kvm/Makefile
@@ -12,6 +12,6 @@ common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o  $(KVM)/async_pf.o $(KVM)/irqch
 ccflags-y := -Ivirt/kvm -Iarch/s390/kvm
 
 kvm-objs := $(common-objs) kvm-s390.o intercept.o interrupt.o priv.o sigp.o
-kvm-objs += diag.o gaccess.o guestdbg.o sthyi.o vsie.o
+kvm-objs += diag.o gaccess.o guestdbg.o vsie.o
 
 obj-$(CONFIG_KVM) += kvm.o
diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c
index a4752bf6b526..46adda5e2b2c 100644
--- a/arch/s390/kvm/intercept.c
+++ b/arch/s390/kvm/intercept.c
@@ -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,71 @@ 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);
+
+	/*
+	 * 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, &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;
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
index 9f8fdd7b2311..10d65dfbc306 100644
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -242,6 +242,8 @@ static inline void kvm_s390_retry_instr(struct kvm_vcpu *vcpu)
 	kvm_s390_rewind_psw(vcpu, kvm_s390_get_ilen(vcpu));
 }
 
+int handle_sthyi(struct kvm_vcpu *vcpu);
+
 /* implemented in priv.c */
 int is_valid_psw(psw_t *psw);
 int kvm_s390_handle_aa(struct kvm_vcpu *vcpu);
@@ -268,9 +270,6 @@ void kvm_s390_vsie_destroy(struct kvm *kvm);
 int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu);
 int kvm_s390_handle_sigp_pei(struct kvm_vcpu *vcpu);
 
-/* implemented in sthyi.c */
-int handle_sthyi(struct kvm_vcpu *vcpu);
-
 /* implemented in kvm-s390.c */
 void kvm_s390_set_tod_clock_ext(struct kvm *kvm,
 				 const struct kvm_s390_vm_tod_clock *gtod);
diff --git a/arch/s390/kvm/sthyi.c b/arch/s390/kvm/sthyi.c
deleted file mode 100644
index 395926b8c1ed..000000000000
--- a/arch/s390/kvm/sthyi.c
+++ /dev/null
@@ -1,469 +0,0 @@
-/*
- * store hypervisor information instruction emulation functions.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License (version 2 only)
- * as published by the Free Software Foundation.
- *
- * 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"
-
-#define DED_WEIGHT 0xffff
-/*
- * CP and IFL as EBCDIC strings, SP/0x40 determines the end of string
- * as they are justified with spaces.
- */
-#define CP  0xc3d7404040404040UL
-#define IFL 0xc9c6d34040404040UL
-
-enum hdr_flags {
-	HDR_NOT_LPAR   = 0x10,
-	HDR_STACK_INCM = 0x20,
-	HDR_STSI_UNAV  = 0x40,
-	HDR_PERF_UNAV  = 0x80,
-};
-
-enum mac_validity {
-	MAC_NAME_VLD = 0x20,
-	MAC_ID_VLD   = 0x40,
-	MAC_CNT_VLD  = 0x80,
-};
-
-enum par_flag {
-	PAR_MT_EN = 0x80,
-};
-
-enum par_validity {
-	PAR_GRP_VLD  = 0x08,
-	PAR_ID_VLD   = 0x10,
-	PAR_ABS_VLD  = 0x20,
-	PAR_WGHT_VLD = 0x40,
-	PAR_PCNT_VLD  = 0x80,
-};
-
-struct hdr_sctn {
-	u8 infhflg1;
-	u8 infhflg2; /* reserved */
-	u8 infhval1; /* reserved */
-	u8 infhval2; /* reserved */
-	u8 reserved[3];
-	u8 infhygct;
-	u16 infhtotl;
-	u16 infhdln;
-	u16 infmoff;
-	u16 infmlen;
-	u16 infpoff;
-	u16 infplen;
-	u16 infhoff1;
-	u16 infhlen1;
-	u16 infgoff1;
-	u16 infglen1;
-	u16 infhoff2;
-	u16 infhlen2;
-	u16 infgoff2;
-	u16 infglen2;
-	u16 infhoff3;
-	u16 infhlen3;
-	u16 infgoff3;
-	u16 infglen3;
-	u8 reserved2[4];
-} __packed;
-
-struct mac_sctn {
-	u8 infmflg1; /* reserved */
-	u8 infmflg2; /* reserved */
-	u8 infmval1;
-	u8 infmval2; /* reserved */
-	u16 infmscps;
-	u16 infmdcps;
-	u16 infmsifl;
-	u16 infmdifl;
-	char infmname[8];
-	char infmtype[4];
-	char infmmanu[16];
-	char infmseq[16];
-	char infmpman[4];
-	u8 reserved[4];
-} __packed;
-
-struct par_sctn {
-	u8 infpflg1;
-	u8 infpflg2; /* reserved */
-	u8 infpval1;
-	u8 infpval2; /* reserved */
-	u16 infppnum;
-	u16 infpscps;
-	u16 infpdcps;
-	u16 infpsifl;
-	u16 infpdifl;
-	u16 reserved;
-	char infppnam[8];
-	u32 infpwbcp;
-	u32 infpabcp;
-	u32 infpwbif;
-	u32 infpabif;
-	char infplgnm[8];
-	u32 infplgcp;
-	u32 infplgif;
-} __packed;
-
-struct sthyi_sctns {
-	struct hdr_sctn hdr;
-	struct mac_sctn mac;
-	struct par_sctn par;
-} __packed;
-
-struct cpu_inf {
-	u64 lpar_cap;
-	u64 lpar_grp_cap;
-	u64 lpar_weight;
-	u64 all_weight;
-	int cpu_num_ded;
-	int cpu_num_shd;
-};
-
-struct lpar_cpu_inf {
-	struct cpu_inf cp;
-	struct cpu_inf ifl;
-};
-
-static inline u64 cpu_id(u8 ctidx, void *diag224_buf)
-{
-	return *((u64 *)(diag224_buf + (ctidx + 1) * DIAG204_CPU_NAME_LEN));
-}
-
-/*
- * Scales the cpu capping from the lpar range to the one expected in
- * sthyi data.
- *
- * diag204 reports a cap in hundredths of processor units.
- * z/VM's range for one core is 0 - 0x10000.
- */
-static u32 scale_cap(u32 in)
-{
-	return (0x10000 * in) / 100;
-}
-
-static void fill_hdr(struct sthyi_sctns *sctns)
-{
-	sctns->hdr.infhdln = sizeof(sctns->hdr);
-	sctns->hdr.infmoff = sizeof(sctns->hdr);
-	sctns->hdr.infmlen = sizeof(sctns->mac);
-	sctns->hdr.infplen = sizeof(sctns->par);
-	sctns->hdr.infpoff = sctns->hdr.infhdln + sctns->hdr.infmlen;
-	sctns->hdr.infhtotl = sctns->hdr.infpoff + sctns->hdr.infplen;
-}
-
-static void fill_stsi_mac(struct sthyi_sctns *sctns,
-			  struct sysinfo_1_1_1 *sysinfo)
-{
-	if (stsi(sysinfo, 1, 1, 1))
-		return;
-
-	sclp_ocf_cpc_name_copy(sctns->mac.infmname);
-
-	memcpy(sctns->mac.infmtype, sysinfo->type, sizeof(sctns->mac.infmtype));
-	memcpy(sctns->mac.infmmanu, sysinfo->manufacturer, sizeof(sctns->mac.infmmanu));
-	memcpy(sctns->mac.infmpman, sysinfo->plant, sizeof(sctns->mac.infmpman));
-	memcpy(sctns->mac.infmseq, sysinfo->sequence, sizeof(sctns->mac.infmseq));
-
-	sctns->mac.infmval1 |= MAC_ID_VLD | MAC_NAME_VLD;
-}
-
-static void fill_stsi_par(struct sthyi_sctns *sctns,
-			  struct sysinfo_2_2_2 *sysinfo)
-{
-	if (stsi(sysinfo, 2, 2, 2))
-		return;
-
-	sctns->par.infppnum = sysinfo->lpar_number;
-	memcpy(sctns->par.infppnam, sysinfo->name, sizeof(sctns->par.infppnam));
-
-	sctns->par.infpval1 |= PAR_ID_VLD;
-}
-
-static void fill_stsi(struct sthyi_sctns *sctns)
-{
-	void *sysinfo;
-
-	/* Errors are handled through the validity bits in the response. */
-	sysinfo = (void *)__get_free_page(GFP_KERNEL);
-	if (!sysinfo)
-		return;
-
-	fill_stsi_mac(sctns, sysinfo);
-	fill_stsi_par(sctns, sysinfo);
-
-	free_pages((unsigned long)sysinfo, 0);
-}
-
-static void fill_diag_mac(struct sthyi_sctns *sctns,
-			  struct diag204_x_phys_block *block,
-			  void *diag224_buf)
-{
-	int i;
-
-	for (i = 0; i < block->hdr.cpus; i++) {
-		switch (cpu_id(block->cpus[i].ctidx, diag224_buf)) {
-		case CP:
-			if (block->cpus[i].weight == DED_WEIGHT)
-				sctns->mac.infmdcps++;
-			else
-				sctns->mac.infmscps++;
-			break;
-		case IFL:
-			if (block->cpus[i].weight == DED_WEIGHT)
-				sctns->mac.infmdifl++;
-			else
-				sctns->mac.infmsifl++;
-			break;
-		}
-	}
-	sctns->mac.infmval1 |= MAC_CNT_VLD;
-}
-
-/* Returns a pointer to the the next partition block. */
-static struct diag204_x_part_block *lpar_cpu_inf(struct lpar_cpu_inf *part_inf,
-						 bool this_lpar,
-						 void *diag224_buf,
-						 struct diag204_x_part_block *block)
-{
-	int i, capped = 0, weight_cp = 0, weight_ifl = 0;
-	struct cpu_inf *cpu_inf;
-
-	for (i = 0; i < block->hdr.rcpus; i++) {
-		if (!(block->cpus[i].cflag & DIAG204_CPU_ONLINE))
-			continue;
-
-		switch (cpu_id(block->cpus[i].ctidx, diag224_buf)) {
-		case CP:
-			cpu_inf = &part_inf->cp;
-			if (block->cpus[i].cur_weight < DED_WEIGHT)
-				weight_cp |= block->cpus[i].cur_weight;
-			break;
-		case IFL:
-			cpu_inf = &part_inf->ifl;
-			if (block->cpus[i].cur_weight < DED_WEIGHT)
-				weight_ifl |= block->cpus[i].cur_weight;
-			break;
-		default:
-			continue;
-		}
-
-		if (!this_lpar)
-			continue;
-
-		capped |= block->cpus[i].cflag & DIAG204_CPU_CAPPED;
-		cpu_inf->lpar_cap |= block->cpus[i].cpu_type_cap;
-		cpu_inf->lpar_grp_cap |= block->cpus[i].group_cpu_type_cap;
-
-		if (block->cpus[i].weight == DED_WEIGHT)
-			cpu_inf->cpu_num_ded += 1;
-		else
-			cpu_inf->cpu_num_shd += 1;
-	}
-
-	if (this_lpar && capped) {
-		part_inf->cp.lpar_weight = weight_cp;
-		part_inf->ifl.lpar_weight = weight_ifl;
-	}
-	part_inf->cp.all_weight += weight_cp;
-	part_inf->ifl.all_weight += weight_ifl;
-	return (struct diag204_x_part_block *)&block->cpus[i];
-}
-
-static void fill_diag(struct sthyi_sctns *sctns)
-{
-	int i, r, pages;
-	bool this_lpar;
-	void *diag204_buf;
-	void *diag224_buf = NULL;
-	struct diag204_x_info_blk_hdr *ti_hdr;
-	struct diag204_x_part_block *part_block;
-	struct diag204_x_phys_block *phys_block;
-	struct lpar_cpu_inf lpar_inf = {};
-
-	/* Errors are handled through the validity bits in the response. */
-	pages = diag204((unsigned long)DIAG204_SUBC_RSI |
-			(unsigned long)DIAG204_INFO_EXT, 0, NULL);
-	if (pages <= 0)
-		return;
-
-	diag204_buf = vmalloc(PAGE_SIZE * pages);
-	if (!diag204_buf)
-		return;
-
-	r = diag204((unsigned long)DIAG204_SUBC_STIB7 |
-		    (unsigned long)DIAG204_INFO_EXT, pages, diag204_buf);
-	if (r < 0)
-		goto out;
-
-	diag224_buf = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
-	if (!diag224_buf || diag224(diag224_buf))
-		goto out;
-
-	ti_hdr = diag204_buf;
-	part_block = diag204_buf + sizeof(*ti_hdr);
-
-	for (i = 0; i < ti_hdr->npar; i++) {
-		/*
-		 * For the calling lpar we also need to get the cpu
-		 * caps and weights. The time information block header
-		 * specifies the offset to the partition block of the
-		 * caller lpar, so we know when we process its data.
-		 */
-		this_lpar = (void *)part_block - diag204_buf == ti_hdr->this_part;
-		part_block = lpar_cpu_inf(&lpar_inf, this_lpar, diag224_buf,
-					  part_block);
-	}
-
-	phys_block = (struct diag204_x_phys_block *)part_block;
-	part_block = diag204_buf + ti_hdr->this_part;
-	if (part_block->hdr.mtid)
-		sctns->par.infpflg1 = PAR_MT_EN;
-
-	sctns->par.infpval1 |= PAR_GRP_VLD;
-	sctns->par.infplgcp = scale_cap(lpar_inf.cp.lpar_grp_cap);
-	sctns->par.infplgif = scale_cap(lpar_inf.ifl.lpar_grp_cap);
-	memcpy(sctns->par.infplgnm, part_block->hdr.hardware_group_name,
-	       sizeof(sctns->par.infplgnm));
-
-	sctns->par.infpscps = lpar_inf.cp.cpu_num_shd;
-	sctns->par.infpdcps = lpar_inf.cp.cpu_num_ded;
-	sctns->par.infpsifl = lpar_inf.ifl.cpu_num_shd;
-	sctns->par.infpdifl = lpar_inf.ifl.cpu_num_ded;
-	sctns->par.infpval1 |= PAR_PCNT_VLD;
-
-	sctns->par.infpabcp = scale_cap(lpar_inf.cp.lpar_cap);
-	sctns->par.infpabif = scale_cap(lpar_inf.ifl.lpar_cap);
-	sctns->par.infpval1 |= PAR_ABS_VLD;
-
-	/*
-	 * Everything below needs global performance data to be
-	 * meaningful.
-	 */
-	if (!(ti_hdr->flags & DIAG204_LPAR_PHYS_FLG)) {
-		sctns->hdr.infhflg1 |= HDR_PERF_UNAV;
-		goto out;
-	}
-
-	fill_diag_mac(sctns, phys_block, diag224_buf);
-
-	if (lpar_inf.cp.lpar_weight) {
-		sctns->par.infpwbcp = sctns->mac.infmscps * 0x10000 *
-			lpar_inf.cp.lpar_weight / lpar_inf.cp.all_weight;
-	}
-
-	if (lpar_inf.ifl.lpar_weight) {
-		sctns->par.infpwbif = sctns->mac.infmsifl * 0x10000 *
-			lpar_inf.ifl.lpar_weight / lpar_inf.ifl.all_weight;
-	}
-	sctns->par.infpval1 |= PAR_WGHT_VLD;
-
-out:
-	free_page((unsigned long)diag224_buf);
-	vfree(diag204_buf);
-}
-
-static int sthyi(u64 vaddr)
-{
-	register u64 code asm("0") = 0;
-	register u64 addr asm("2") = vaddr;
-	int cc;
-
-	asm volatile(
-		".insn   rre,0xB2560000,%[code],%[addr]\n"
-		"ipm     %[cc]\n"
-		"srl     %[cc],28\n"
-		: [cc] "=d" (cc)
-		: [code] "d" (code), [addr] "a" (addr)
-		: "3", "memory", "cc");
-	return cc;
-}
-
-int handle_sthyi(struct kvm_vcpu *vcpu)
-{
-	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, &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;
-		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;
-
-	/*
-	 * If we are a guest, we don't want to emulate an emulated
-	 * instruction. We ask the hypervisor to provide the data.
-	 */
-	if (test_facility(74)) {
-		cc = sthyi((u64)sctns);
-		goto out;
-	}
-
-	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;
-}