1fb9a0c74SPierre Morel /* SPDX-License-Identifier: GPL-2.0-only */ 2fb9a0c74SPierre Morel /* 3fb9a0c74SPierre Morel * CPU Topology 4fb9a0c74SPierre Morel * 5fb9a0c74SPierre Morel * Copyright IBM Corp. 2022 6fb9a0c74SPierre Morel * 7fb9a0c74SPierre Morel * Authors: 8fb9a0c74SPierre Morel * Pierre Morel <pmorel@linux.ibm.com> 9fb9a0c74SPierre Morel */ 10fb9a0c74SPierre Morel 11fb9a0c74SPierre Morel #include <libcflat.h> 12fb9a0c74SPierre Morel #include <asm/page.h> 13fb9a0c74SPierre Morel #include <asm/asm-offsets.h> 14fb9a0c74SPierre Morel #include <asm/interrupt.h> 15fb9a0c74SPierre Morel #include <asm/facility.h> 16fb9a0c74SPierre Morel #include <asm/barrier.h> 17fb9a0c74SPierre Morel #include <smp.h> 18fb9a0c74SPierre Morel #include <sclp.h> 19fb9a0c74SPierre Morel #include <s390x/hardware.h> 20*6f33f0b7SPierre Morel #include <s390x/stsi.h> 21*6f33f0b7SPierre Morel 22*6f33f0b7SPierre Morel static uint8_t pagebuf[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); 23*6f33f0b7SPierre Morel 24*6f33f0b7SPierre Morel static int max_nested_lvl; 25*6f33f0b7SPierre Morel static int number_of_cpus; 26*6f33f0b7SPierre Morel static int cpus_in_masks; 27*6f33f0b7SPierre Morel static int max_cpus; 28*6f33f0b7SPierre Morel 29*6f33f0b7SPierre Morel /* 30*6f33f0b7SPierre Morel * Topology level as defined by architecture, all levels exists with 31*6f33f0b7SPierre Morel * a single container unless overwritten by the QEMU -smp parameter. 32*6f33f0b7SPierre Morel */ 33*6f33f0b7SPierre Morel static int expected_topo_lvl[CPU_TOPOLOGY_MAX_LEVEL] = { 1, 1, 1, 1, 1, 1 }; 34fb9a0c74SPierre Morel 35fb9a0c74SPierre Morel #define PTF_REQ_HORIZONTAL 0 36fb9a0c74SPierre Morel #define PTF_REQ_VERTICAL 1 37fb9a0c74SPierre Morel #define PTF_CHECK 2 38fb9a0c74SPierre Morel 39fb9a0c74SPierre Morel #define PTF_ERR_NO_REASON 0 40fb9a0c74SPierre Morel #define PTF_ERR_ALRDY_POLARIZED 1 41fb9a0c74SPierre Morel #define PTF_ERR_IN_PROGRESS 2 42fb9a0c74SPierre Morel 43fb9a0c74SPierre Morel extern int diag308_load_reset(u64); 44fb9a0c74SPierre Morel 45fb9a0c74SPierre Morel static int ptf(unsigned long fc, unsigned long *rc) 46fb9a0c74SPierre Morel { 47fb9a0c74SPierre Morel int cc; 48fb9a0c74SPierre Morel 49fb9a0c74SPierre Morel asm volatile( 50fb9a0c74SPierre Morel " ptf %1 \n" 51fb9a0c74SPierre Morel " ipm %0 \n" 52fb9a0c74SPierre Morel " srl %0,28 \n" 53fb9a0c74SPierre Morel : "=d" (cc), "+d" (fc) 54fb9a0c74SPierre Morel : 55fb9a0c74SPierre Morel : "cc"); 56fb9a0c74SPierre Morel 57fb9a0c74SPierre Morel *rc = fc >> 8; 58fb9a0c74SPierre Morel return cc; 59fb9a0c74SPierre Morel } 60fb9a0c74SPierre Morel 61fb9a0c74SPierre Morel static void check_privilege(int fc) 62fb9a0c74SPierre Morel { 63fb9a0c74SPierre Morel unsigned long rc; 64fb9a0c74SPierre Morel 65fb9a0c74SPierre Morel report_prefix_pushf("Privileged fc %d", fc); 66fb9a0c74SPierre Morel enter_pstate(); 67fb9a0c74SPierre Morel expect_pgm_int(); 68fb9a0c74SPierre Morel ptf(fc, &rc); 69fb9a0c74SPierre Morel check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION); 70fb9a0c74SPierre Morel report_prefix_pop(); 71fb9a0c74SPierre Morel } 72fb9a0c74SPierre Morel 73fb9a0c74SPierre Morel static void check_specifications(void) 74fb9a0c74SPierre Morel { 75fb9a0c74SPierre Morel unsigned long error = 0; 76fb9a0c74SPierre Morel unsigned long ptf_bits; 77fb9a0c74SPierre Morel unsigned long rc; 78fb9a0c74SPierre Morel int i; 79fb9a0c74SPierre Morel 80fb9a0c74SPierre Morel report_prefix_push("Specifications"); 81fb9a0c74SPierre Morel 82fb9a0c74SPierre Morel /* Function codes above 3 are undefined */ 83fb9a0c74SPierre Morel for (i = 4; i < 255; i++) { 84fb9a0c74SPierre Morel expect_pgm_int(); 85fb9a0c74SPierre Morel ptf(i, &rc); 86fb9a0c74SPierre Morel if (clear_pgm_int() != PGM_INT_CODE_SPECIFICATION) { 87fb9a0c74SPierre Morel report_fail("FC %d did not yield specification exception", i); 88fb9a0c74SPierre Morel error = 1; 89fb9a0c74SPierre Morel } 90fb9a0c74SPierre Morel } 91fb9a0c74SPierre Morel report(!error, "Undefined function codes"); 92fb9a0c74SPierre Morel 93fb9a0c74SPierre Morel /* Reserved bits must be 0 */ 94fb9a0c74SPierre Morel for (i = 8, error = 0; i < 64; i++) { 95fb9a0c74SPierre Morel ptf_bits = 0x01UL << i; 96fb9a0c74SPierre Morel expect_pgm_int(); 97fb9a0c74SPierre Morel ptf(ptf_bits, &rc); 98fb9a0c74SPierre Morel if (clear_pgm_int() != PGM_INT_CODE_SPECIFICATION) { 99fb9a0c74SPierre Morel report_fail("Reserved bit %d did not yield specification exception", i); 100fb9a0c74SPierre Morel error = 1; 101fb9a0c74SPierre Morel } 102fb9a0c74SPierre Morel } 103fb9a0c74SPierre Morel 104fb9a0c74SPierre Morel report(!error, "Reserved bits"); 105fb9a0c74SPierre Morel 106fb9a0c74SPierre Morel report_prefix_pop(); 107fb9a0c74SPierre Morel } 108fb9a0c74SPierre Morel 109fb9a0c74SPierre Morel static void check_polarization_change(void) 110fb9a0c74SPierre Morel { 111fb9a0c74SPierre Morel unsigned long rc; 112fb9a0c74SPierre Morel int cc; 113fb9a0c74SPierre Morel 114fb9a0c74SPierre Morel report_prefix_push("Polarization change"); 115fb9a0c74SPierre Morel 116fb9a0c74SPierre Morel /* We expect a clean state through reset */ 117fb9a0c74SPierre Morel report(diag308_load_reset(1), "load normal reset done"); 118fb9a0c74SPierre Morel 119fb9a0c74SPierre Morel /* 120fb9a0c74SPierre Morel * Set vertical polarization to verify that RESET sets 121fb9a0c74SPierre Morel * horizontal polarization back. 122fb9a0c74SPierre Morel */ 123fb9a0c74SPierre Morel cc = ptf(PTF_REQ_VERTICAL, &rc); 124fb9a0c74SPierre Morel report(cc == 0, "Set vertical polarization."); 125fb9a0c74SPierre Morel 126fb9a0c74SPierre Morel report(diag308_load_reset(1), "load normal reset done"); 127fb9a0c74SPierre Morel 128fb9a0c74SPierre Morel cc = ptf(PTF_CHECK, &rc); 129fb9a0c74SPierre Morel report(cc == 0, "Reset should clear topology report"); 130fb9a0c74SPierre Morel 131fb9a0c74SPierre Morel cc = ptf(PTF_REQ_HORIZONTAL, &rc); 132fb9a0c74SPierre Morel report(cc == 2 && rc == PTF_ERR_ALRDY_POLARIZED, 133fb9a0c74SPierre Morel "After RESET polarization is horizontal"); 134fb9a0c74SPierre Morel 135fb9a0c74SPierre Morel /* Flip between vertical and horizontal polarization */ 136fb9a0c74SPierre Morel cc = ptf(PTF_REQ_VERTICAL, &rc); 137fb9a0c74SPierre Morel report(cc == 0, "Change to vertical"); 138fb9a0c74SPierre Morel 139fb9a0c74SPierre Morel cc = ptf(PTF_CHECK, &rc); 140fb9a0c74SPierre Morel report(cc == 1, "Should report"); 141fb9a0c74SPierre Morel 142fb9a0c74SPierre Morel cc = ptf(PTF_REQ_VERTICAL, &rc); 143fb9a0c74SPierre Morel report(cc == 2 && rc == PTF_ERR_ALRDY_POLARIZED, "Double change to vertical"); 144fb9a0c74SPierre Morel 145fb9a0c74SPierre Morel cc = ptf(PTF_CHECK, &rc); 146fb9a0c74SPierre Morel report(cc == 0, "Should not report"); 147fb9a0c74SPierre Morel 148fb9a0c74SPierre Morel cc = ptf(PTF_REQ_HORIZONTAL, &rc); 149fb9a0c74SPierre Morel report(cc == 0, "Change to horizontal"); 150fb9a0c74SPierre Morel 151fb9a0c74SPierre Morel cc = ptf(PTF_CHECK, &rc); 152fb9a0c74SPierre Morel report(cc == 1, "Should Report"); 153fb9a0c74SPierre Morel 154fb9a0c74SPierre Morel cc = ptf(PTF_REQ_HORIZONTAL, &rc); 155fb9a0c74SPierre Morel report(cc == 2 && rc == PTF_ERR_ALRDY_POLARIZED, "Double change to horizontal"); 156fb9a0c74SPierre Morel 157fb9a0c74SPierre Morel cc = ptf(PTF_CHECK, &rc); 158fb9a0c74SPierre Morel report(cc == 0, "Should not report"); 159fb9a0c74SPierre Morel 160fb9a0c74SPierre Morel report_prefix_pop(); 161fb9a0c74SPierre Morel } 162fb9a0c74SPierre Morel 163fb9a0c74SPierre Morel static void test_ptf(void) 164fb9a0c74SPierre Morel { 165fb9a0c74SPierre Morel check_privilege(PTF_REQ_HORIZONTAL); 166fb9a0c74SPierre Morel check_privilege(PTF_REQ_VERTICAL); 167fb9a0c74SPierre Morel check_privilege(PTF_CHECK); 168fb9a0c74SPierre Morel check_specifications(); 169fb9a0c74SPierre Morel check_polarization_change(); 170fb9a0c74SPierre Morel } 171fb9a0c74SPierre Morel 172*6f33f0b7SPierre Morel /* 173*6f33f0b7SPierre Morel * stsi_check_maxcpus 174*6f33f0b7SPierre Morel * @info: Pointer to the stsi information 175*6f33f0b7SPierre Morel * 176*6f33f0b7SPierre Morel * The product of the numbers of containers per level 177*6f33f0b7SPierre Morel * is the maximum number of CPU allowed by the machine. 178*6f33f0b7SPierre Morel */ 179*6f33f0b7SPierre Morel static void stsi_check_maxcpus(struct sysinfo_15_1_x *info) 180*6f33f0b7SPierre Morel { 181*6f33f0b7SPierre Morel int n, i; 182*6f33f0b7SPierre Morel 183*6f33f0b7SPierre Morel for (i = 0, n = 1; i < CPU_TOPOLOGY_MAX_LEVEL; i++) 184*6f33f0b7SPierre Morel n *= info->mag[i] ?: 1; 185*6f33f0b7SPierre Morel 186*6f33f0b7SPierre Morel report(n == max_cpus, "Calculated max CPUs: %d", n); 187*6f33f0b7SPierre Morel } 188*6f33f0b7SPierre Morel 189*6f33f0b7SPierre Morel /* 190*6f33f0b7SPierre Morel * stsi_check_mag 191*6f33f0b7SPierre Morel * @info: Pointer to the stsi information 192*6f33f0b7SPierre Morel * 193*6f33f0b7SPierre Morel * MAG field should match the architecture defined containers 194*6f33f0b7SPierre Morel * when MNEST as returned by SCLP matches MNEST of the SYSIB. 195*6f33f0b7SPierre Morel */ 196*6f33f0b7SPierre Morel static void stsi_check_mag(struct sysinfo_15_1_x *info) 197*6f33f0b7SPierre Morel { 198*6f33f0b7SPierre Morel int i; 199*6f33f0b7SPierre Morel 200*6f33f0b7SPierre Morel report_prefix_push("MAG"); 201*6f33f0b7SPierre Morel 202*6f33f0b7SPierre Morel stsi_check_maxcpus(info); 203*6f33f0b7SPierre Morel 204*6f33f0b7SPierre Morel /* 205*6f33f0b7SPierre Morel * It is not clear how the MAG fields are calculated when mnest 206*6f33f0b7SPierre Morel * in the SYSIB 15.x is different from the maximum nested level 207*6f33f0b7SPierre Morel * in the SCLP info, so we skip here for now. 208*6f33f0b7SPierre Morel */ 209*6f33f0b7SPierre Morel if (max_nested_lvl != info->mnest) { 210*6f33f0b7SPierre Morel report_skip("No specification on layer aggregation"); 211*6f33f0b7SPierre Morel goto done; 212*6f33f0b7SPierre Morel } 213*6f33f0b7SPierre Morel 214*6f33f0b7SPierre Morel /* 215*6f33f0b7SPierre Morel * MAG up to max_nested_lvl must match the architecture 216*6f33f0b7SPierre Morel * defined containers. 217*6f33f0b7SPierre Morel */ 218*6f33f0b7SPierre Morel for (i = 0; i < max_nested_lvl; i++) 219*6f33f0b7SPierre Morel report(info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1] == expected_topo_lvl[i], 220*6f33f0b7SPierre Morel "MAG %d field match %d == %d", 221*6f33f0b7SPierre Morel i + 1, 222*6f33f0b7SPierre Morel info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1], 223*6f33f0b7SPierre Morel expected_topo_lvl[i]); 224*6f33f0b7SPierre Morel 225*6f33f0b7SPierre Morel /* Above max_nested_lvl the MAG field must be null */ 226*6f33f0b7SPierre Morel for (; i < CPU_TOPOLOGY_MAX_LEVEL; i++) 227*6f33f0b7SPierre Morel report(info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1] == 0, 228*6f33f0b7SPierre Morel "MAG %d field match %d == %d", i + 1, 229*6f33f0b7SPierre Morel info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1], 0); 230*6f33f0b7SPierre Morel 231*6f33f0b7SPierre Morel done: 232*6f33f0b7SPierre Morel report_prefix_pop(); 233*6f33f0b7SPierre Morel } 234*6f33f0b7SPierre Morel 235*6f33f0b7SPierre Morel /** 236*6f33f0b7SPierre Morel * check_tle: 237*6f33f0b7SPierre Morel * @tc: pointer to first TLE 238*6f33f0b7SPierre Morel * 239*6f33f0b7SPierre Morel * Recursively check the containers TLEs until we 240*6f33f0b7SPierre Morel * find a CPU TLE. 241*6f33f0b7SPierre Morel */ 242*6f33f0b7SPierre Morel static uint8_t *check_tle(void *tc) 243*6f33f0b7SPierre Morel { 244*6f33f0b7SPierre Morel struct topology_container *container = tc; 245*6f33f0b7SPierre Morel struct topology_core *cpus; 246*6f33f0b7SPierre Morel int n; 247*6f33f0b7SPierre Morel 248*6f33f0b7SPierre Morel if (container->nl) { 249*6f33f0b7SPierre Morel report_info("NL: %d id: %d", container->nl, container->id); 250*6f33f0b7SPierre Morel 251*6f33f0b7SPierre Morel report(!(*(uint64_t *)tc & CONTAINER_TLE_RES_BITS), 252*6f33f0b7SPierre Morel "reserved bits %016lx", 253*6f33f0b7SPierre Morel *(uint64_t *)tc & CONTAINER_TLE_RES_BITS); 254*6f33f0b7SPierre Morel 255*6f33f0b7SPierre Morel return check_tle(tc + sizeof(*container)); 256*6f33f0b7SPierre Morel } 257*6f33f0b7SPierre Morel 258*6f33f0b7SPierre Morel report_info("NL: %d", container->nl); 259*6f33f0b7SPierre Morel cpus = tc; 260*6f33f0b7SPierre Morel 261*6f33f0b7SPierre Morel report(!(*(uint64_t *)tc & CPUS_TLE_RES_BITS), "reserved bits %016lx", 262*6f33f0b7SPierre Morel *(uint64_t *)tc & CPUS_TLE_RES_BITS); 263*6f33f0b7SPierre Morel 264*6f33f0b7SPierre Morel report(cpus->type == 0x03, "type IFL"); 265*6f33f0b7SPierre Morel 266*6f33f0b7SPierre Morel report_info("origin: %d", cpus->origin); 267*6f33f0b7SPierre Morel report_info("mask: %016lx", cpus->mask); 268*6f33f0b7SPierre Morel report_info("dedicated: %d entitlement: %d", cpus->d, cpus->pp); 269*6f33f0b7SPierre Morel 270*6f33f0b7SPierre Morel n = __builtin_popcountl(cpus->mask); 271*6f33f0b7SPierre Morel report(n <= expected_topo_lvl[0], "CPUs per mask: %d out of max %d", 272*6f33f0b7SPierre Morel n, expected_topo_lvl[0]); 273*6f33f0b7SPierre Morel cpus_in_masks += n; 274*6f33f0b7SPierre Morel 275*6f33f0b7SPierre Morel if (!cpus->d) 276*6f33f0b7SPierre Morel report_skip("Not dedicated"); 277*6f33f0b7SPierre Morel else 278*6f33f0b7SPierre Morel report(cpus->pp == 3 || cpus->pp == 0, "Dedicated CPUs are either vertically polarized or have high entitlement"); 279*6f33f0b7SPierre Morel 280*6f33f0b7SPierre Morel return tc + sizeof(*cpus); 281*6f33f0b7SPierre Morel } 282*6f33f0b7SPierre Morel 283*6f33f0b7SPierre Morel /** 284*6f33f0b7SPierre Morel * stsi_check_tle_coherency: 285*6f33f0b7SPierre Morel * @info: Pointer to the stsi information 286*6f33f0b7SPierre Morel * 287*6f33f0b7SPierre Morel * We verify that we get the expected number of Topology List Entry 288*6f33f0b7SPierre Morel * containers for a specific level. 289*6f33f0b7SPierre Morel */ 290*6f33f0b7SPierre Morel static void stsi_check_tle_coherency(struct sysinfo_15_1_x *info) 291*6f33f0b7SPierre Morel { 292*6f33f0b7SPierre Morel void *tc, *end; 293*6f33f0b7SPierre Morel 294*6f33f0b7SPierre Morel report_prefix_push("TLE"); 295*6f33f0b7SPierre Morel cpus_in_masks = 0; 296*6f33f0b7SPierre Morel 297*6f33f0b7SPierre Morel tc = info->tle; 298*6f33f0b7SPierre Morel end = (void *)info + info->length; 299*6f33f0b7SPierre Morel 300*6f33f0b7SPierre Morel while (tc < end) 301*6f33f0b7SPierre Morel tc = check_tle(tc); 302*6f33f0b7SPierre Morel 303*6f33f0b7SPierre Morel report(cpus_in_masks == number_of_cpus, "CPUs in mask %d", 304*6f33f0b7SPierre Morel cpus_in_masks); 305*6f33f0b7SPierre Morel 306*6f33f0b7SPierre Morel report_prefix_pop(); 307*6f33f0b7SPierre Morel } 308*6f33f0b7SPierre Morel 309*6f33f0b7SPierre Morel /** 310*6f33f0b7SPierre Morel * stsi_get_sysib: 311*6f33f0b7SPierre Morel * @info: pointer to the STSI info structure 312*6f33f0b7SPierre Morel * @sel2: the selector giving the topology level to check 313*6f33f0b7SPierre Morel * 314*6f33f0b7SPierre Morel * Fill the sysinfo_15_1_x info structure and check the 315*6f33f0b7SPierre Morel * SYSIB header. 316*6f33f0b7SPierre Morel * 317*6f33f0b7SPierre Morel * Returns instruction validity. 318*6f33f0b7SPierre Morel */ 319*6f33f0b7SPierre Morel static int stsi_get_sysib(struct sysinfo_15_1_x *info, int sel2) 320*6f33f0b7SPierre Morel { 321*6f33f0b7SPierre Morel int ret; 322*6f33f0b7SPierre Morel 323*6f33f0b7SPierre Morel report_prefix_pushf("SYSIB"); 324*6f33f0b7SPierre Morel 325*6f33f0b7SPierre Morel ret = stsi(pagebuf, 15, 1, sel2); 326*6f33f0b7SPierre Morel 327*6f33f0b7SPierre Morel if (max_nested_lvl >= sel2) { 328*6f33f0b7SPierre Morel report(!ret, "Valid instruction"); 329*6f33f0b7SPierre Morel report(sel2 == info->mnest, "Valid mnest"); 330*6f33f0b7SPierre Morel } else { 331*6f33f0b7SPierre Morel report(ret, "Invalid instruction"); 332*6f33f0b7SPierre Morel } 333*6f33f0b7SPierre Morel 334*6f33f0b7SPierre Morel report_prefix_pop(); 335*6f33f0b7SPierre Morel 336*6f33f0b7SPierre Morel return ret; 337*6f33f0b7SPierre Morel } 338*6f33f0b7SPierre Morel 339*6f33f0b7SPierre Morel /** 340*6f33f0b7SPierre Morel * check_sysinfo_15_1_x: 341*6f33f0b7SPierre Morel * @info: pointer to the STSI info structure 342*6f33f0b7SPierre Morel * @sel2: the selector giving the topology level to check 343*6f33f0b7SPierre Morel * 344*6f33f0b7SPierre Morel * Check if the validity of the STSI instruction and then 345*6f33f0b7SPierre Morel * calls specific checks on the information buffer. 346*6f33f0b7SPierre Morel */ 347*6f33f0b7SPierre Morel static void check_sysinfo_15_1_x(struct sysinfo_15_1_x *info, int sel2) 348*6f33f0b7SPierre Morel { 349*6f33f0b7SPierre Morel int ret; 350*6f33f0b7SPierre Morel int cc; 351*6f33f0b7SPierre Morel unsigned long rc; 352*6f33f0b7SPierre Morel 353*6f33f0b7SPierre Morel report_prefix_pushf("15_1_%d", sel2); 354*6f33f0b7SPierre Morel 355*6f33f0b7SPierre Morel ret = stsi_get_sysib(info, sel2); 356*6f33f0b7SPierre Morel if (ret) { 357*6f33f0b7SPierre Morel report_skip("Selector 2 not supported by architecture"); 358*6f33f0b7SPierre Morel goto end; 359*6f33f0b7SPierre Morel } 360*6f33f0b7SPierre Morel 361*6f33f0b7SPierre Morel report_prefix_pushf("H"); 362*6f33f0b7SPierre Morel cc = ptf(PTF_REQ_HORIZONTAL, &rc); 363*6f33f0b7SPierre Morel if (cc != 0 && rc != PTF_ERR_ALRDY_POLARIZED) { 364*6f33f0b7SPierre Morel report_fail("Unable to set horizontal polarization"); 365*6f33f0b7SPierre Morel goto vertical; 366*6f33f0b7SPierre Morel } 367*6f33f0b7SPierre Morel 368*6f33f0b7SPierre Morel stsi_check_mag(info); 369*6f33f0b7SPierre Morel stsi_check_tle_coherency(info); 370*6f33f0b7SPierre Morel 371*6f33f0b7SPierre Morel vertical: 372*6f33f0b7SPierre Morel report_prefix_pop(); 373*6f33f0b7SPierre Morel report_prefix_pushf("V"); 374*6f33f0b7SPierre Morel 375*6f33f0b7SPierre Morel cc = ptf(PTF_REQ_VERTICAL, &rc); 376*6f33f0b7SPierre Morel if (cc != 0 && rc != PTF_ERR_ALRDY_POLARIZED) { 377*6f33f0b7SPierre Morel report_fail("Unable to set vertical polarization"); 378*6f33f0b7SPierre Morel goto end; 379*6f33f0b7SPierre Morel } 380*6f33f0b7SPierre Morel 381*6f33f0b7SPierre Morel stsi_check_mag(info); 382*6f33f0b7SPierre Morel stsi_check_tle_coherency(info); 383*6f33f0b7SPierre Morel report_prefix_pop(); 384*6f33f0b7SPierre Morel 385*6f33f0b7SPierre Morel end: 386*6f33f0b7SPierre Morel report_prefix_pop(); 387*6f33f0b7SPierre Morel } 388*6f33f0b7SPierre Morel 389*6f33f0b7SPierre Morel /* 390*6f33f0b7SPierre Morel * The Maximum Nested level is given by SCLP READ_SCP_INFO if the MNEST facility 391*6f33f0b7SPierre Morel * is available. 392*6f33f0b7SPierre Morel * If the MNEST facility is not available, sclp_get_stsi_mnest returns 0 and the 393*6f33f0b7SPierre Morel * Maximum Nested level is 2 394*6f33f0b7SPierre Morel */ 395*6f33f0b7SPierre Morel #define S390_DEFAULT_MNEST 2 396*6f33f0b7SPierre Morel static int sclp_get_mnest(void) 397*6f33f0b7SPierre Morel { 398*6f33f0b7SPierre Morel return sclp_get_stsi_mnest() ?: S390_DEFAULT_MNEST; 399*6f33f0b7SPierre Morel } 400*6f33f0b7SPierre Morel 401*6f33f0b7SPierre Morel static int expected_num_cpus(void) 402*6f33f0b7SPierre Morel { 403*6f33f0b7SPierre Morel int i; 404*6f33f0b7SPierre Morel int ncpus = 1; 405*6f33f0b7SPierre Morel 406*6f33f0b7SPierre Morel for (i = 0; i < CPU_TOPOLOGY_MAX_LEVEL; i++) 407*6f33f0b7SPierre Morel ncpus *= expected_topo_lvl[i] ?: 1; 408*6f33f0b7SPierre Morel 409*6f33f0b7SPierre Morel return ncpus; 410*6f33f0b7SPierre Morel } 411*6f33f0b7SPierre Morel 412*6f33f0b7SPierre Morel /** 413*6f33f0b7SPierre Morel * test_stsi: 414*6f33f0b7SPierre Morel * 415*6f33f0b7SPierre Morel * Retrieves the maximum nested topology level supported by the architecture 416*6f33f0b7SPierre Morel * and the number of CPUs. 417*6f33f0b7SPierre Morel * Calls the checking for the STSI instruction in sel2 reverse level order 418*6f33f0b7SPierre Morel * from 6 (CPU_TOPOLOGY_MAX_LEVEL) to 2 to have the most interesting level, 419*6f33f0b7SPierre Morel * the one triggering a topology-change-report-pending condition, level 2, 420*6f33f0b7SPierre Morel * at the end of the report. 421*6f33f0b7SPierre Morel * 422*6f33f0b7SPierre Morel */ 423*6f33f0b7SPierre Morel static void test_stsi(void) 424*6f33f0b7SPierre Morel { 425*6f33f0b7SPierre Morel int sel2; 426*6f33f0b7SPierre Morel 427*6f33f0b7SPierre Morel max_cpus = expected_num_cpus(); 428*6f33f0b7SPierre Morel report_info("Architecture max CPUs: %d", max_cpus); 429*6f33f0b7SPierre Morel 430*6f33f0b7SPierre Morel max_nested_lvl = sclp_get_mnest(); 431*6f33f0b7SPierre Morel report_info("SCLP maximum nested level : %d", max_nested_lvl); 432*6f33f0b7SPierre Morel 433*6f33f0b7SPierre Morel number_of_cpus = sclp_get_cpu_num(); 434*6f33f0b7SPierre Morel report_info("SCLP number of CPU: %d", number_of_cpus); 435*6f33f0b7SPierre Morel 436*6f33f0b7SPierre Morel /* STSI selector 2 can takes values between 2 and 6 */ 437*6f33f0b7SPierre Morel for (sel2 = 6; sel2 >= 2; sel2--) 438*6f33f0b7SPierre Morel check_sysinfo_15_1_x((struct sysinfo_15_1_x *)pagebuf, sel2); 439*6f33f0b7SPierre Morel } 440*6f33f0b7SPierre Morel 441*6f33f0b7SPierre Morel /** 442*6f33f0b7SPierre Morel * parse_topology_args: 443*6f33f0b7SPierre Morel * @argc: number of arguments 444*6f33f0b7SPierre Morel * @argv: argument array 445*6f33f0b7SPierre Morel * 446*6f33f0b7SPierre Morel * This function initialize the architecture topology levels 447*6f33f0b7SPierre Morel * which should be the same as the one provided by the hypervisor. 448*6f33f0b7SPierre Morel * 449*6f33f0b7SPierre Morel * We use the current names found in IBM/Z literature, Linux and QEMU: 450*6f33f0b7SPierre Morel * cores, sockets/packages, books, drawers and nodes to facilitate the 451*6f33f0b7SPierre Morel * human machine interface but store the result in a machine abstract 452*6f33f0b7SPierre Morel * array of architecture topology levels. 453*6f33f0b7SPierre Morel * Note that when QEMU uses socket as a name for the topology level 1 454*6f33f0b7SPierre Morel * Linux uses package or physical_package. 455*6f33f0b7SPierre Morel */ 456*6f33f0b7SPierre Morel static void parse_topology_args(int argc, char **argv) 457*6f33f0b7SPierre Morel { 458*6f33f0b7SPierre Morel int i; 459*6f33f0b7SPierre Morel static const char * const levels[] = { "cores", "sockets", 460*6f33f0b7SPierre Morel "books", "drawers" }; 461*6f33f0b7SPierre Morel 462*6f33f0b7SPierre Morel for (i = 1; i < argc; i++) { 463*6f33f0b7SPierre Morel char *flag = argv[i]; 464*6f33f0b7SPierre Morel int level; 465*6f33f0b7SPierre Morel 466*6f33f0b7SPierre Morel if (flag[0] != '-') 467*6f33f0b7SPierre Morel report_abort("Argument is expected to begin with '-'"); 468*6f33f0b7SPierre Morel flag++; 469*6f33f0b7SPierre Morel for (level = 0; ARRAY_SIZE(levels); level++) { 470*6f33f0b7SPierre Morel if (!strcmp(levels[level], flag)) 471*6f33f0b7SPierre Morel break; 472*6f33f0b7SPierre Morel } 473*6f33f0b7SPierre Morel if (level == ARRAY_SIZE(levels)) 474*6f33f0b7SPierre Morel report_abort("Unknown parameter %s", flag); 475*6f33f0b7SPierre Morel 476*6f33f0b7SPierre Morel expected_topo_lvl[level] = atol(argv[++i]); 477*6f33f0b7SPierre Morel report_info("%s: %d", levels[level], expected_topo_lvl[level]); 478*6f33f0b7SPierre Morel } 479*6f33f0b7SPierre Morel } 480*6f33f0b7SPierre Morel 481fb9a0c74SPierre Morel static struct { 482fb9a0c74SPierre Morel const char *name; 483fb9a0c74SPierre Morel void (*func)(void); 484fb9a0c74SPierre Morel } tests[] = { 485fb9a0c74SPierre Morel { "PTF", test_ptf }, 486*6f33f0b7SPierre Morel { "STSI", test_stsi }, 487fb9a0c74SPierre Morel { NULL, NULL } 488fb9a0c74SPierre Morel }; 489fb9a0c74SPierre Morel 490fb9a0c74SPierre Morel int main(int argc, char *argv[]) 491fb9a0c74SPierre Morel { 492fb9a0c74SPierre Morel int i; 493fb9a0c74SPierre Morel 494fb9a0c74SPierre Morel report_prefix_push("CPU Topology"); 495fb9a0c74SPierre Morel 496*6f33f0b7SPierre Morel parse_topology_args(argc, argv); 497*6f33f0b7SPierre Morel 498fb9a0c74SPierre Morel if (!test_facility(11)) { 499fb9a0c74SPierre Morel report_skip("Topology facility not present"); 500fb9a0c74SPierre Morel goto end; 501fb9a0c74SPierre Morel } 502fb9a0c74SPierre Morel 503fb9a0c74SPierre Morel report_info("Virtual machine level %ld", stsi_get_fc()); 504fb9a0c74SPierre Morel 505fb9a0c74SPierre Morel for (i = 0; tests[i].name; i++) { 506fb9a0c74SPierre Morel report_prefix_push(tests[i].name); 507fb9a0c74SPierre Morel tests[i].func(); 508fb9a0c74SPierre Morel report_prefix_pop(); 509fb9a0c74SPierre Morel } 510fb9a0c74SPierre Morel 511fb9a0c74SPierre Morel end: 512fb9a0c74SPierre Morel report_prefix_pop(); 513fb9a0c74SPierre Morel return report_summary(); 514fb9a0c74SPierre Morel } 515