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