xref: /kernel/linux/linux-6.6/arch/s390/kernel/sthyi.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * store hypervisor information instruction emulation functions.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright IBM Corp. 2016
662306a36Sopenharmony_ci * Author(s): Janosch Frank <frankja@linux.vnet.ibm.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/errno.h>
962306a36Sopenharmony_ci#include <linux/pagemap.h>
1062306a36Sopenharmony_ci#include <linux/vmalloc.h>
1162306a36Sopenharmony_ci#include <linux/syscalls.h>
1262306a36Sopenharmony_ci#include <linux/mutex.h>
1362306a36Sopenharmony_ci#include <asm/asm-offsets.h>
1462306a36Sopenharmony_ci#include <asm/sclp.h>
1562306a36Sopenharmony_ci#include <asm/diag.h>
1662306a36Sopenharmony_ci#include <asm/sysinfo.h>
1762306a36Sopenharmony_ci#include <asm/ebcdic.h>
1862306a36Sopenharmony_ci#include <asm/facility.h>
1962306a36Sopenharmony_ci#include <asm/sthyi.h>
2062306a36Sopenharmony_ci#include "entry.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define DED_WEIGHT 0xffff
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * CP and IFL as EBCDIC strings, SP/0x40 determines the end of string
2562306a36Sopenharmony_ci * as they are justified with spaces.
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci#define CP  0xc3d7404040404040UL
2862306a36Sopenharmony_ci#define IFL 0xc9c6d34040404040UL
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cienum hdr_flags {
3162306a36Sopenharmony_ci	HDR_NOT_LPAR   = 0x10,
3262306a36Sopenharmony_ci	HDR_STACK_INCM = 0x20,
3362306a36Sopenharmony_ci	HDR_STSI_UNAV  = 0x40,
3462306a36Sopenharmony_ci	HDR_PERF_UNAV  = 0x80,
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cienum mac_validity {
3862306a36Sopenharmony_ci	MAC_NAME_VLD = 0x20,
3962306a36Sopenharmony_ci	MAC_ID_VLD   = 0x40,
4062306a36Sopenharmony_ci	MAC_CNT_VLD  = 0x80,
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cienum par_flag {
4462306a36Sopenharmony_ci	PAR_MT_EN = 0x80,
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cienum par_validity {
4862306a36Sopenharmony_ci	PAR_GRP_VLD  = 0x08,
4962306a36Sopenharmony_ci	PAR_ID_VLD   = 0x10,
5062306a36Sopenharmony_ci	PAR_ABS_VLD  = 0x20,
5162306a36Sopenharmony_ci	PAR_WGHT_VLD = 0x40,
5262306a36Sopenharmony_ci	PAR_PCNT_VLD  = 0x80,
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistruct hdr_sctn {
5662306a36Sopenharmony_ci	u8 infhflg1;
5762306a36Sopenharmony_ci	u8 infhflg2; /* reserved */
5862306a36Sopenharmony_ci	u8 infhval1; /* reserved */
5962306a36Sopenharmony_ci	u8 infhval2; /* reserved */
6062306a36Sopenharmony_ci	u8 reserved[3];
6162306a36Sopenharmony_ci	u8 infhygct;
6262306a36Sopenharmony_ci	u16 infhtotl;
6362306a36Sopenharmony_ci	u16 infhdln;
6462306a36Sopenharmony_ci	u16 infmoff;
6562306a36Sopenharmony_ci	u16 infmlen;
6662306a36Sopenharmony_ci	u16 infpoff;
6762306a36Sopenharmony_ci	u16 infplen;
6862306a36Sopenharmony_ci	u16 infhoff1;
6962306a36Sopenharmony_ci	u16 infhlen1;
7062306a36Sopenharmony_ci	u16 infgoff1;
7162306a36Sopenharmony_ci	u16 infglen1;
7262306a36Sopenharmony_ci	u16 infhoff2;
7362306a36Sopenharmony_ci	u16 infhlen2;
7462306a36Sopenharmony_ci	u16 infgoff2;
7562306a36Sopenharmony_ci	u16 infglen2;
7662306a36Sopenharmony_ci	u16 infhoff3;
7762306a36Sopenharmony_ci	u16 infhlen3;
7862306a36Sopenharmony_ci	u16 infgoff3;
7962306a36Sopenharmony_ci	u16 infglen3;
8062306a36Sopenharmony_ci	u8 reserved2[4];
8162306a36Sopenharmony_ci} __packed;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistruct mac_sctn {
8462306a36Sopenharmony_ci	u8 infmflg1; /* reserved */
8562306a36Sopenharmony_ci	u8 infmflg2; /* reserved */
8662306a36Sopenharmony_ci	u8 infmval1;
8762306a36Sopenharmony_ci	u8 infmval2; /* reserved */
8862306a36Sopenharmony_ci	u16 infmscps;
8962306a36Sopenharmony_ci	u16 infmdcps;
9062306a36Sopenharmony_ci	u16 infmsifl;
9162306a36Sopenharmony_ci	u16 infmdifl;
9262306a36Sopenharmony_ci	char infmname[8];
9362306a36Sopenharmony_ci	char infmtype[4];
9462306a36Sopenharmony_ci	char infmmanu[16];
9562306a36Sopenharmony_ci	char infmseq[16];
9662306a36Sopenharmony_ci	char infmpman[4];
9762306a36Sopenharmony_ci	u8 reserved[4];
9862306a36Sopenharmony_ci} __packed;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistruct par_sctn {
10162306a36Sopenharmony_ci	u8 infpflg1;
10262306a36Sopenharmony_ci	u8 infpflg2; /* reserved */
10362306a36Sopenharmony_ci	u8 infpval1;
10462306a36Sopenharmony_ci	u8 infpval2; /* reserved */
10562306a36Sopenharmony_ci	u16 infppnum;
10662306a36Sopenharmony_ci	u16 infpscps;
10762306a36Sopenharmony_ci	u16 infpdcps;
10862306a36Sopenharmony_ci	u16 infpsifl;
10962306a36Sopenharmony_ci	u16 infpdifl;
11062306a36Sopenharmony_ci	u16 reserved;
11162306a36Sopenharmony_ci	char infppnam[8];
11262306a36Sopenharmony_ci	u32 infpwbcp;
11362306a36Sopenharmony_ci	u32 infpabcp;
11462306a36Sopenharmony_ci	u32 infpwbif;
11562306a36Sopenharmony_ci	u32 infpabif;
11662306a36Sopenharmony_ci	char infplgnm[8];
11762306a36Sopenharmony_ci	u32 infplgcp;
11862306a36Sopenharmony_ci	u32 infplgif;
11962306a36Sopenharmony_ci} __packed;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistruct sthyi_sctns {
12262306a36Sopenharmony_ci	struct hdr_sctn hdr;
12362306a36Sopenharmony_ci	struct mac_sctn mac;
12462306a36Sopenharmony_ci	struct par_sctn par;
12562306a36Sopenharmony_ci} __packed;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistruct cpu_inf {
12862306a36Sopenharmony_ci	u64 lpar_cap;
12962306a36Sopenharmony_ci	u64 lpar_grp_cap;
13062306a36Sopenharmony_ci	u64 lpar_weight;
13162306a36Sopenharmony_ci	u64 all_weight;
13262306a36Sopenharmony_ci	int cpu_num_ded;
13362306a36Sopenharmony_ci	int cpu_num_shd;
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistruct lpar_cpu_inf {
13762306a36Sopenharmony_ci	struct cpu_inf cp;
13862306a36Sopenharmony_ci	struct cpu_inf ifl;
13962306a36Sopenharmony_ci};
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/*
14262306a36Sopenharmony_ci * STHYI requires extensive locking in the higher hypervisors
14362306a36Sopenharmony_ci * and is very computational/memory expensive. Therefore we
14462306a36Sopenharmony_ci * cache the retrieved data whose valid period is 1s.
14562306a36Sopenharmony_ci */
14662306a36Sopenharmony_ci#define CACHE_VALID_JIFFIES	HZ
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistruct sthyi_info {
14962306a36Sopenharmony_ci	void *info;
15062306a36Sopenharmony_ci	unsigned long end;
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic DEFINE_MUTEX(sthyi_mutex);
15462306a36Sopenharmony_cistatic struct sthyi_info sthyi_cache;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic inline u64 cpu_id(u8 ctidx, void *diag224_buf)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	return *((u64 *)(diag224_buf + (ctidx + 1) * DIAG204_CPU_NAME_LEN));
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/*
16262306a36Sopenharmony_ci * Scales the cpu capping from the lpar range to the one expected in
16362306a36Sopenharmony_ci * sthyi data.
16462306a36Sopenharmony_ci *
16562306a36Sopenharmony_ci * diag204 reports a cap in hundredths of processor units.
16662306a36Sopenharmony_ci * z/VM's range for one core is 0 - 0x10000.
16762306a36Sopenharmony_ci */
16862306a36Sopenharmony_cistatic u32 scale_cap(u32 in)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	return (0x10000 * in) / 100;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic void fill_hdr(struct sthyi_sctns *sctns)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	sctns->hdr.infhdln = sizeof(sctns->hdr);
17662306a36Sopenharmony_ci	sctns->hdr.infmoff = sizeof(sctns->hdr);
17762306a36Sopenharmony_ci	sctns->hdr.infmlen = sizeof(sctns->mac);
17862306a36Sopenharmony_ci	sctns->hdr.infplen = sizeof(sctns->par);
17962306a36Sopenharmony_ci	sctns->hdr.infpoff = sctns->hdr.infhdln + sctns->hdr.infmlen;
18062306a36Sopenharmony_ci	sctns->hdr.infhtotl = sctns->hdr.infpoff + sctns->hdr.infplen;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic void fill_stsi_mac(struct sthyi_sctns *sctns,
18462306a36Sopenharmony_ci			  struct sysinfo_1_1_1 *sysinfo)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	sclp_ocf_cpc_name_copy(sctns->mac.infmname);
18762306a36Sopenharmony_ci	if (*(u64 *)sctns->mac.infmname != 0)
18862306a36Sopenharmony_ci		sctns->mac.infmval1 |= MAC_NAME_VLD;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (stsi(sysinfo, 1, 1, 1))
19162306a36Sopenharmony_ci		return;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	memcpy(sctns->mac.infmtype, sysinfo->type, sizeof(sctns->mac.infmtype));
19462306a36Sopenharmony_ci	memcpy(sctns->mac.infmmanu, sysinfo->manufacturer, sizeof(sctns->mac.infmmanu));
19562306a36Sopenharmony_ci	memcpy(sctns->mac.infmpman, sysinfo->plant, sizeof(sctns->mac.infmpman));
19662306a36Sopenharmony_ci	memcpy(sctns->mac.infmseq, sysinfo->sequence, sizeof(sctns->mac.infmseq));
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	sctns->mac.infmval1 |= MAC_ID_VLD;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void fill_stsi_par(struct sthyi_sctns *sctns,
20262306a36Sopenharmony_ci			  struct sysinfo_2_2_2 *sysinfo)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	if (stsi(sysinfo, 2, 2, 2))
20562306a36Sopenharmony_ci		return;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	sctns->par.infppnum = sysinfo->lpar_number;
20862306a36Sopenharmony_ci	memcpy(sctns->par.infppnam, sysinfo->name, sizeof(sctns->par.infppnam));
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	sctns->par.infpval1 |= PAR_ID_VLD;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic void fill_stsi(struct sthyi_sctns *sctns)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	void *sysinfo;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* Errors are handled through the validity bits in the response. */
21862306a36Sopenharmony_ci	sysinfo = (void *)__get_free_page(GFP_KERNEL);
21962306a36Sopenharmony_ci	if (!sysinfo)
22062306a36Sopenharmony_ci		return;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	fill_stsi_mac(sctns, sysinfo);
22362306a36Sopenharmony_ci	fill_stsi_par(sctns, sysinfo);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	free_pages((unsigned long)sysinfo, 0);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic void fill_diag_mac(struct sthyi_sctns *sctns,
22962306a36Sopenharmony_ci			  struct diag204_x_phys_block *block,
23062306a36Sopenharmony_ci			  void *diag224_buf)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	int i;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	for (i = 0; i < block->hdr.cpus; i++) {
23562306a36Sopenharmony_ci		switch (cpu_id(block->cpus[i].ctidx, diag224_buf)) {
23662306a36Sopenharmony_ci		case CP:
23762306a36Sopenharmony_ci			if (block->cpus[i].weight == DED_WEIGHT)
23862306a36Sopenharmony_ci				sctns->mac.infmdcps++;
23962306a36Sopenharmony_ci			else
24062306a36Sopenharmony_ci				sctns->mac.infmscps++;
24162306a36Sopenharmony_ci			break;
24262306a36Sopenharmony_ci		case IFL:
24362306a36Sopenharmony_ci			if (block->cpus[i].weight == DED_WEIGHT)
24462306a36Sopenharmony_ci				sctns->mac.infmdifl++;
24562306a36Sopenharmony_ci			else
24662306a36Sopenharmony_ci				sctns->mac.infmsifl++;
24762306a36Sopenharmony_ci			break;
24862306a36Sopenharmony_ci		}
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci	sctns->mac.infmval1 |= MAC_CNT_VLD;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/* Returns a pointer to the the next partition block. */
25462306a36Sopenharmony_cistatic struct diag204_x_part_block *lpar_cpu_inf(struct lpar_cpu_inf *part_inf,
25562306a36Sopenharmony_ci						 bool this_lpar,
25662306a36Sopenharmony_ci						 void *diag224_buf,
25762306a36Sopenharmony_ci						 struct diag204_x_part_block *block)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	int i, capped = 0, weight_cp = 0, weight_ifl = 0;
26062306a36Sopenharmony_ci	struct cpu_inf *cpu_inf;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	for (i = 0; i < block->hdr.rcpus; i++) {
26362306a36Sopenharmony_ci		if (!(block->cpus[i].cflag & DIAG204_CPU_ONLINE))
26462306a36Sopenharmony_ci			continue;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		switch (cpu_id(block->cpus[i].ctidx, diag224_buf)) {
26762306a36Sopenharmony_ci		case CP:
26862306a36Sopenharmony_ci			cpu_inf = &part_inf->cp;
26962306a36Sopenharmony_ci			if (block->cpus[i].cur_weight < DED_WEIGHT)
27062306a36Sopenharmony_ci				weight_cp |= block->cpus[i].cur_weight;
27162306a36Sopenharmony_ci			break;
27262306a36Sopenharmony_ci		case IFL:
27362306a36Sopenharmony_ci			cpu_inf = &part_inf->ifl;
27462306a36Sopenharmony_ci			if (block->cpus[i].cur_weight < DED_WEIGHT)
27562306a36Sopenharmony_ci				weight_ifl |= block->cpus[i].cur_weight;
27662306a36Sopenharmony_ci			break;
27762306a36Sopenharmony_ci		default:
27862306a36Sopenharmony_ci			continue;
27962306a36Sopenharmony_ci		}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		if (!this_lpar)
28262306a36Sopenharmony_ci			continue;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		capped |= block->cpus[i].cflag & DIAG204_CPU_CAPPED;
28562306a36Sopenharmony_ci		cpu_inf->lpar_cap |= block->cpus[i].cpu_type_cap;
28662306a36Sopenharmony_ci		cpu_inf->lpar_grp_cap |= block->cpus[i].group_cpu_type_cap;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		if (block->cpus[i].weight == DED_WEIGHT)
28962306a36Sopenharmony_ci			cpu_inf->cpu_num_ded += 1;
29062306a36Sopenharmony_ci		else
29162306a36Sopenharmony_ci			cpu_inf->cpu_num_shd += 1;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	if (this_lpar && capped) {
29562306a36Sopenharmony_ci		part_inf->cp.lpar_weight = weight_cp;
29662306a36Sopenharmony_ci		part_inf->ifl.lpar_weight = weight_ifl;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci	part_inf->cp.all_weight += weight_cp;
29962306a36Sopenharmony_ci	part_inf->ifl.all_weight += weight_ifl;
30062306a36Sopenharmony_ci	return (struct diag204_x_part_block *)&block->cpus[i];
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic void fill_diag(struct sthyi_sctns *sctns)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	int i, r, pages;
30662306a36Sopenharmony_ci	bool this_lpar;
30762306a36Sopenharmony_ci	void *diag204_buf;
30862306a36Sopenharmony_ci	void *diag224_buf = NULL;
30962306a36Sopenharmony_ci	struct diag204_x_info_blk_hdr *ti_hdr;
31062306a36Sopenharmony_ci	struct diag204_x_part_block *part_block;
31162306a36Sopenharmony_ci	struct diag204_x_phys_block *phys_block;
31262306a36Sopenharmony_ci	struct lpar_cpu_inf lpar_inf = {};
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* Errors are handled through the validity bits in the response. */
31562306a36Sopenharmony_ci	pages = diag204((unsigned long)DIAG204_SUBC_RSI |
31662306a36Sopenharmony_ci			(unsigned long)DIAG204_INFO_EXT, 0, NULL);
31762306a36Sopenharmony_ci	if (pages <= 0)
31862306a36Sopenharmony_ci		return;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	diag204_buf = __vmalloc_node(array_size(pages, PAGE_SIZE),
32162306a36Sopenharmony_ci				     PAGE_SIZE, GFP_KERNEL, NUMA_NO_NODE,
32262306a36Sopenharmony_ci				     __builtin_return_address(0));
32362306a36Sopenharmony_ci	if (!diag204_buf)
32462306a36Sopenharmony_ci		return;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	r = diag204((unsigned long)DIAG204_SUBC_STIB7 |
32762306a36Sopenharmony_ci		    (unsigned long)DIAG204_INFO_EXT, pages, diag204_buf);
32862306a36Sopenharmony_ci	if (r < 0)
32962306a36Sopenharmony_ci		goto out;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	diag224_buf = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
33262306a36Sopenharmony_ci	if (!diag224_buf || diag224(diag224_buf))
33362306a36Sopenharmony_ci		goto out;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	ti_hdr = diag204_buf;
33662306a36Sopenharmony_ci	part_block = diag204_buf + sizeof(*ti_hdr);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	for (i = 0; i < ti_hdr->npar; i++) {
33962306a36Sopenharmony_ci		/*
34062306a36Sopenharmony_ci		 * For the calling lpar we also need to get the cpu
34162306a36Sopenharmony_ci		 * caps and weights. The time information block header
34262306a36Sopenharmony_ci		 * specifies the offset to the partition block of the
34362306a36Sopenharmony_ci		 * caller lpar, so we know when we process its data.
34462306a36Sopenharmony_ci		 */
34562306a36Sopenharmony_ci		this_lpar = (void *)part_block - diag204_buf == ti_hdr->this_part;
34662306a36Sopenharmony_ci		part_block = lpar_cpu_inf(&lpar_inf, this_lpar, diag224_buf,
34762306a36Sopenharmony_ci					  part_block);
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	phys_block = (struct diag204_x_phys_block *)part_block;
35162306a36Sopenharmony_ci	part_block = diag204_buf + ti_hdr->this_part;
35262306a36Sopenharmony_ci	if (part_block->hdr.mtid)
35362306a36Sopenharmony_ci		sctns->par.infpflg1 = PAR_MT_EN;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	sctns->par.infpval1 |= PAR_GRP_VLD;
35662306a36Sopenharmony_ci	sctns->par.infplgcp = scale_cap(lpar_inf.cp.lpar_grp_cap);
35762306a36Sopenharmony_ci	sctns->par.infplgif = scale_cap(lpar_inf.ifl.lpar_grp_cap);
35862306a36Sopenharmony_ci	memcpy(sctns->par.infplgnm, part_block->hdr.hardware_group_name,
35962306a36Sopenharmony_ci	       sizeof(sctns->par.infplgnm));
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	sctns->par.infpscps = lpar_inf.cp.cpu_num_shd;
36262306a36Sopenharmony_ci	sctns->par.infpdcps = lpar_inf.cp.cpu_num_ded;
36362306a36Sopenharmony_ci	sctns->par.infpsifl = lpar_inf.ifl.cpu_num_shd;
36462306a36Sopenharmony_ci	sctns->par.infpdifl = lpar_inf.ifl.cpu_num_ded;
36562306a36Sopenharmony_ci	sctns->par.infpval1 |= PAR_PCNT_VLD;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	sctns->par.infpabcp = scale_cap(lpar_inf.cp.lpar_cap);
36862306a36Sopenharmony_ci	sctns->par.infpabif = scale_cap(lpar_inf.ifl.lpar_cap);
36962306a36Sopenharmony_ci	sctns->par.infpval1 |= PAR_ABS_VLD;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/*
37262306a36Sopenharmony_ci	 * Everything below needs global performance data to be
37362306a36Sopenharmony_ci	 * meaningful.
37462306a36Sopenharmony_ci	 */
37562306a36Sopenharmony_ci	if (!(ti_hdr->flags & DIAG204_LPAR_PHYS_FLG)) {
37662306a36Sopenharmony_ci		sctns->hdr.infhflg1 |= HDR_PERF_UNAV;
37762306a36Sopenharmony_ci		goto out;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	fill_diag_mac(sctns, phys_block, diag224_buf);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (lpar_inf.cp.lpar_weight) {
38362306a36Sopenharmony_ci		sctns->par.infpwbcp = sctns->mac.infmscps * 0x10000 *
38462306a36Sopenharmony_ci			lpar_inf.cp.lpar_weight / lpar_inf.cp.all_weight;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (lpar_inf.ifl.lpar_weight) {
38862306a36Sopenharmony_ci		sctns->par.infpwbif = sctns->mac.infmsifl * 0x10000 *
38962306a36Sopenharmony_ci			lpar_inf.ifl.lpar_weight / lpar_inf.ifl.all_weight;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci	sctns->par.infpval1 |= PAR_WGHT_VLD;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ciout:
39462306a36Sopenharmony_ci	free_page((unsigned long)diag224_buf);
39562306a36Sopenharmony_ci	vfree(diag204_buf);
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic int sthyi(u64 vaddr, u64 *rc)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	union register_pair r1 = { .even = 0, }; /* subcode */
40162306a36Sopenharmony_ci	union register_pair r2 = { .even = vaddr, };
40262306a36Sopenharmony_ci	int cc;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	asm volatile(
40562306a36Sopenharmony_ci		".insn   rre,0xB2560000,%[r1],%[r2]\n"
40662306a36Sopenharmony_ci		"ipm     %[cc]\n"
40762306a36Sopenharmony_ci		"srl     %[cc],28\n"
40862306a36Sopenharmony_ci		: [cc] "=&d" (cc), [r2] "+&d" (r2.pair)
40962306a36Sopenharmony_ci		: [r1] "d" (r1.pair)
41062306a36Sopenharmony_ci		: "memory", "cc");
41162306a36Sopenharmony_ci	*rc = r2.odd;
41262306a36Sopenharmony_ci	return cc;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cistatic int fill_dst(void *dst, u64 *rc)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct sthyi_sctns *sctns = (struct sthyi_sctns *)dst;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	/*
42062306a36Sopenharmony_ci	 * If the facility is on, we don't want to emulate the instruction.
42162306a36Sopenharmony_ci	 * We ask the hypervisor to provide the data.
42262306a36Sopenharmony_ci	 */
42362306a36Sopenharmony_ci	if (test_facility(74))
42462306a36Sopenharmony_ci		return sthyi((u64)dst, rc);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	fill_hdr(sctns);
42762306a36Sopenharmony_ci	fill_stsi(sctns);
42862306a36Sopenharmony_ci	fill_diag(sctns);
42962306a36Sopenharmony_ci	*rc = 0;
43062306a36Sopenharmony_ci	return 0;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int sthyi_init_cache(void)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	if (sthyi_cache.info)
43662306a36Sopenharmony_ci		return 0;
43762306a36Sopenharmony_ci	sthyi_cache.info = (void *)get_zeroed_page(GFP_KERNEL);
43862306a36Sopenharmony_ci	if (!sthyi_cache.info)
43962306a36Sopenharmony_ci		return -ENOMEM;
44062306a36Sopenharmony_ci	sthyi_cache.end = jiffies - 1; /* expired */
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic int sthyi_update_cache(u64 *rc)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	int r;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	memset(sthyi_cache.info, 0, PAGE_SIZE);
44962306a36Sopenharmony_ci	r = fill_dst(sthyi_cache.info, rc);
45062306a36Sopenharmony_ci	if (r)
45162306a36Sopenharmony_ci		return r;
45262306a36Sopenharmony_ci	sthyi_cache.end = jiffies + CACHE_VALID_JIFFIES;
45362306a36Sopenharmony_ci	return r;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci/*
45762306a36Sopenharmony_ci * sthyi_fill - Fill page with data returned by the STHYI instruction
45862306a36Sopenharmony_ci *
45962306a36Sopenharmony_ci * @dst: Pointer to zeroed page
46062306a36Sopenharmony_ci * @rc:  Pointer for storing the return code of the instruction
46162306a36Sopenharmony_ci *
46262306a36Sopenharmony_ci * Fills the destination with system information returned by the STHYI
46362306a36Sopenharmony_ci * instruction. The data is generated by emulation or execution of STHYI,
46462306a36Sopenharmony_ci * if available. The return value is either a negative error value or
46562306a36Sopenharmony_ci * the condition code that would be returned, the rc parameter is the
46662306a36Sopenharmony_ci * return code which is passed in register R2 + 1.
46762306a36Sopenharmony_ci */
46862306a36Sopenharmony_ciint sthyi_fill(void *dst, u64 *rc)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	int r;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	mutex_lock(&sthyi_mutex);
47362306a36Sopenharmony_ci	r = sthyi_init_cache();
47462306a36Sopenharmony_ci	if (r)
47562306a36Sopenharmony_ci		goto out;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (time_is_before_jiffies(sthyi_cache.end)) {
47862306a36Sopenharmony_ci		/* cache expired */
47962306a36Sopenharmony_ci		r = sthyi_update_cache(rc);
48062306a36Sopenharmony_ci		if (r)
48162306a36Sopenharmony_ci			goto out;
48262306a36Sopenharmony_ci	}
48362306a36Sopenharmony_ci	*rc = 0;
48462306a36Sopenharmony_ci	memcpy(dst, sthyi_cache.info, PAGE_SIZE);
48562306a36Sopenharmony_ciout:
48662306a36Sopenharmony_ci	mutex_unlock(&sthyi_mutex);
48762306a36Sopenharmony_ci	return r;
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sthyi_fill);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ciSYSCALL_DEFINE4(s390_sthyi, unsigned long, function_code, void __user *, buffer,
49262306a36Sopenharmony_ci		u64 __user *, return_code, unsigned long, flags)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	u64 sthyi_rc;
49562306a36Sopenharmony_ci	void *info;
49662306a36Sopenharmony_ci	int r;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	if (flags)
49962306a36Sopenharmony_ci		return -EINVAL;
50062306a36Sopenharmony_ci	if (function_code != STHYI_FC_CP_IFL_CAP)
50162306a36Sopenharmony_ci		return -EOPNOTSUPP;
50262306a36Sopenharmony_ci	info = (void *)get_zeroed_page(GFP_KERNEL);
50362306a36Sopenharmony_ci	if (!info)
50462306a36Sopenharmony_ci		return -ENOMEM;
50562306a36Sopenharmony_ci	r = sthyi_fill(info, &sthyi_rc);
50662306a36Sopenharmony_ci	if (r < 0)
50762306a36Sopenharmony_ci		goto out;
50862306a36Sopenharmony_ci	if (return_code && put_user(sthyi_rc, return_code)) {
50962306a36Sopenharmony_ci		r = -EFAULT;
51062306a36Sopenharmony_ci		goto out;
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci	if (copy_to_user(buffer, info, PAGE_SIZE))
51362306a36Sopenharmony_ci		r = -EFAULT;
51462306a36Sopenharmony_ciout:
51562306a36Sopenharmony_ci	free_page((unsigned long)info);
51662306a36Sopenharmony_ci	return r;
51762306a36Sopenharmony_ci}
518