1fb9a0c74SPierre Morel /* SPDX-License-Identifier: GPL-2.0-only */
2fb9a0c74SPierre Morel /*
3fb9a0c74SPierre Morel * CPU Topology
4fb9a0c74SPierre Morel *
5*59fa2015SNina Schoetterl-Glausch * Copyright IBM Corp. 2022, 2023
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>
206f33f0b7SPierre Morel #include <s390x/stsi.h>
216f33f0b7SPierre Morel
226f33f0b7SPierre Morel static uint8_t pagebuf[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
236f33f0b7SPierre Morel
246f33f0b7SPierre Morel static int max_nested_lvl;
256f33f0b7SPierre Morel static int number_of_cpus;
266f33f0b7SPierre Morel static int max_cpus;
276f33f0b7SPierre Morel
286f33f0b7SPierre Morel /*
296f33f0b7SPierre Morel * Topology level as defined by architecture, all levels exists with
306f33f0b7SPierre Morel * a single container unless overwritten by the QEMU -smp parameter.
316f33f0b7SPierre Morel */
326f33f0b7SPierre Morel static int expected_topo_lvl[CPU_TOPOLOGY_MAX_LEVEL] = { 1, 1, 1, 1, 1, 1 };
33fb9a0c74SPierre Morel
34fb9a0c74SPierre Morel #define PTF_REQ_HORIZONTAL 0
35fb9a0c74SPierre Morel #define PTF_REQ_VERTICAL 1
36fb9a0c74SPierre Morel #define PTF_CHECK 2
37fb9a0c74SPierre Morel
38fb9a0c74SPierre Morel #define PTF_ERR_NO_REASON 0
39fb9a0c74SPierre Morel #define PTF_ERR_ALRDY_POLARIZED 1
40fb9a0c74SPierre Morel #define PTF_ERR_IN_PROGRESS 2
41fb9a0c74SPierre Morel
42fb9a0c74SPierre Morel extern int diag308_load_reset(u64);
43fb9a0c74SPierre Morel
ptf(unsigned long fc,unsigned long * rc)44fb9a0c74SPierre Morel static int ptf(unsigned long fc, unsigned long *rc)
45fb9a0c74SPierre Morel {
46fb9a0c74SPierre Morel int cc;
47fb9a0c74SPierre Morel
48fb9a0c74SPierre Morel asm volatile(
49fb9a0c74SPierre Morel " ptf %1 \n"
50fb9a0c74SPierre Morel " ipm %0 \n"
51fb9a0c74SPierre Morel " srl %0,28 \n"
52fb9a0c74SPierre Morel : "=d" (cc), "+d" (fc)
53fb9a0c74SPierre Morel :
54fb9a0c74SPierre Morel : "cc");
55fb9a0c74SPierre Morel
56fb9a0c74SPierre Morel *rc = fc >> 8;
57fb9a0c74SPierre Morel return cc;
58fb9a0c74SPierre Morel }
59fb9a0c74SPierre Morel
check_privilege(int fc)60fb9a0c74SPierre Morel static void check_privilege(int fc)
61fb9a0c74SPierre Morel {
62fb9a0c74SPierre Morel unsigned long rc;
63fb9a0c74SPierre Morel
64fb9a0c74SPierre Morel report_prefix_pushf("Privileged fc %d", fc);
65fb9a0c74SPierre Morel enter_pstate();
66fb9a0c74SPierre Morel expect_pgm_int();
67fb9a0c74SPierre Morel ptf(fc, &rc);
68fb9a0c74SPierre Morel check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
69fb9a0c74SPierre Morel report_prefix_pop();
70fb9a0c74SPierre Morel }
71fb9a0c74SPierre Morel
check_specifications(void)72fb9a0c74SPierre Morel static void check_specifications(void)
73fb9a0c74SPierre Morel {
74fb9a0c74SPierre Morel unsigned long error = 0;
75fb9a0c74SPierre Morel unsigned long ptf_bits;
76fb9a0c74SPierre Morel unsigned long rc;
77fb9a0c74SPierre Morel int i;
78fb9a0c74SPierre Morel
79fb9a0c74SPierre Morel report_prefix_push("Specifications");
80fb9a0c74SPierre Morel
81fb9a0c74SPierre Morel /* Function codes above 3 are undefined */
82fb9a0c74SPierre Morel for (i = 4; i < 255; i++) {
83fb9a0c74SPierre Morel expect_pgm_int();
84fb9a0c74SPierre Morel ptf(i, &rc);
85fb9a0c74SPierre Morel if (clear_pgm_int() != PGM_INT_CODE_SPECIFICATION) {
86fb9a0c74SPierre Morel report_fail("FC %d did not yield specification exception", i);
87fb9a0c74SPierre Morel error = 1;
88fb9a0c74SPierre Morel }
89fb9a0c74SPierre Morel }
90fb9a0c74SPierre Morel report(!error, "Undefined function codes");
91fb9a0c74SPierre Morel
92fb9a0c74SPierre Morel /* Reserved bits must be 0 */
93fb9a0c74SPierre Morel for (i = 8, error = 0; i < 64; i++) {
94fb9a0c74SPierre Morel ptf_bits = 0x01UL << i;
95fb9a0c74SPierre Morel expect_pgm_int();
96fb9a0c74SPierre Morel ptf(ptf_bits, &rc);
97fb9a0c74SPierre Morel if (clear_pgm_int() != PGM_INT_CODE_SPECIFICATION) {
98fb9a0c74SPierre Morel report_fail("Reserved bit %d did not yield specification exception", i);
99fb9a0c74SPierre Morel error = 1;
100fb9a0c74SPierre Morel }
101fb9a0c74SPierre Morel }
102fb9a0c74SPierre Morel
103fb9a0c74SPierre Morel report(!error, "Reserved bits");
104fb9a0c74SPierre Morel
105fb9a0c74SPierre Morel report_prefix_pop();
106fb9a0c74SPierre Morel }
107fb9a0c74SPierre Morel
check_polarization_change(void)108fb9a0c74SPierre Morel static void check_polarization_change(void)
109fb9a0c74SPierre Morel {
110fb9a0c74SPierre Morel unsigned long rc;
111fb9a0c74SPierre Morel int cc;
112fb9a0c74SPierre Morel
113fb9a0c74SPierre Morel report_prefix_push("Polarization change");
114fb9a0c74SPierre Morel
115fb9a0c74SPierre Morel /* We expect a clean state through reset */
1161ad8ec94SNina Schoetterl-Glausch assert(diag308_load_reset(1));
117fb9a0c74SPierre Morel
118fb9a0c74SPierre Morel /*
119fb9a0c74SPierre Morel * Set vertical polarization to verify that RESET sets
120fb9a0c74SPierre Morel * horizontal polarization back.
121fb9a0c74SPierre Morel */
122fb9a0c74SPierre Morel cc = ptf(PTF_REQ_VERTICAL, &rc);
123fb9a0c74SPierre Morel report(cc == 0, "Set vertical polarization.");
124fb9a0c74SPierre Morel
1251ad8ec94SNina Schoetterl-Glausch assert(diag308_load_reset(1));
126fb9a0c74SPierre Morel
127fb9a0c74SPierre Morel cc = ptf(PTF_CHECK, &rc);
128fb9a0c74SPierre Morel report(cc == 0, "Reset should clear topology report");
129fb9a0c74SPierre Morel
130fb9a0c74SPierre Morel cc = ptf(PTF_REQ_HORIZONTAL, &rc);
131fb9a0c74SPierre Morel report(cc == 2 && rc == PTF_ERR_ALRDY_POLARIZED,
132fb9a0c74SPierre Morel "After RESET polarization is horizontal");
133fb9a0c74SPierre Morel
134fb9a0c74SPierre Morel /* Flip between vertical and horizontal polarization */
135fb9a0c74SPierre Morel cc = ptf(PTF_REQ_VERTICAL, &rc);
136fb9a0c74SPierre Morel report(cc == 0, "Change to vertical");
137fb9a0c74SPierre Morel
138fb9a0c74SPierre Morel cc = ptf(PTF_CHECK, &rc);
1391ad8ec94SNina Schoetterl-Glausch report(cc == 1, "Should report change after horizontal -> vertical");
140fb9a0c74SPierre Morel
141fb9a0c74SPierre Morel cc = ptf(PTF_REQ_VERTICAL, &rc);
142fb9a0c74SPierre Morel report(cc == 2 && rc == PTF_ERR_ALRDY_POLARIZED, "Double change to vertical");
143fb9a0c74SPierre Morel
144fb9a0c74SPierre Morel cc = ptf(PTF_CHECK, &rc);
1451ad8ec94SNina Schoetterl-Glausch report(cc == 0, "Should not report change after vertical -> vertical");
146fb9a0c74SPierre Morel
147fb9a0c74SPierre Morel cc = ptf(PTF_REQ_HORIZONTAL, &rc);
148fb9a0c74SPierre Morel report(cc == 0, "Change to horizontal");
149fb9a0c74SPierre Morel
150fb9a0c74SPierre Morel cc = ptf(PTF_CHECK, &rc);
1511ad8ec94SNina Schoetterl-Glausch report(cc == 1, "Should report change after vertical -> horizontal");
152fb9a0c74SPierre Morel
153fb9a0c74SPierre Morel cc = ptf(PTF_REQ_HORIZONTAL, &rc);
154fb9a0c74SPierre Morel report(cc == 2 && rc == PTF_ERR_ALRDY_POLARIZED, "Double change to horizontal");
155fb9a0c74SPierre Morel
156fb9a0c74SPierre Morel cc = ptf(PTF_CHECK, &rc);
1571ad8ec94SNina Schoetterl-Glausch report(cc == 0, "Should not report change after horizontal -> horizontal");
158fb9a0c74SPierre Morel
159fb9a0c74SPierre Morel report_prefix_pop();
160fb9a0c74SPierre Morel }
161fb9a0c74SPierre Morel
test_ptf(void)162fb9a0c74SPierre Morel static void test_ptf(void)
163fb9a0c74SPierre Morel {
164fb9a0c74SPierre Morel check_privilege(PTF_REQ_HORIZONTAL);
165fb9a0c74SPierre Morel check_privilege(PTF_REQ_VERTICAL);
166fb9a0c74SPierre Morel check_privilege(PTF_CHECK);
167fb9a0c74SPierre Morel check_specifications();
168fb9a0c74SPierre Morel check_polarization_change();
169fb9a0c74SPierre Morel }
170fb9a0c74SPierre Morel
1716f33f0b7SPierre Morel /*
1726f33f0b7SPierre Morel * stsi_check_maxcpus
1736f33f0b7SPierre Morel * @info: Pointer to the stsi information
1746f33f0b7SPierre Morel *
1756f33f0b7SPierre Morel * The product of the numbers of containers per level
1766f33f0b7SPierre Morel * is the maximum number of CPU allowed by the machine.
1776f33f0b7SPierre Morel */
stsi_check_maxcpus(struct sysinfo_15_1_x * info)1786f33f0b7SPierre Morel static void stsi_check_maxcpus(struct sysinfo_15_1_x *info)
1796f33f0b7SPierre Morel {
1806f33f0b7SPierre Morel int n, i;
1816f33f0b7SPierre Morel
1826f33f0b7SPierre Morel for (i = 0, n = 1; i < CPU_TOPOLOGY_MAX_LEVEL; i++)
1836f33f0b7SPierre Morel n *= info->mag[i] ?: 1;
1846f33f0b7SPierre Morel
1856f33f0b7SPierre Morel report(n == max_cpus, "Calculated max CPUs: %d", n);
1866f33f0b7SPierre Morel }
1876f33f0b7SPierre Morel
1886f33f0b7SPierre Morel /*
18922e8195cSNina Schoetterl-Glausch * stsi_check_header
1906f33f0b7SPierre Morel * @info: Pointer to the stsi information
19122e8195cSNina Schoetterl-Glausch * @sel2: stsi selector 2 value
1926f33f0b7SPierre Morel *
1936f33f0b7SPierre Morel * MAG field should match the architecture defined containers
1946f33f0b7SPierre Morel * when MNEST as returned by SCLP matches MNEST of the SYSIB.
1956f33f0b7SPierre Morel */
stsi_check_header(struct sysinfo_15_1_x * info,int sel2)19622e8195cSNina Schoetterl-Glausch static void stsi_check_header(struct sysinfo_15_1_x *info, int sel2)
1976f33f0b7SPierre Morel {
1986f33f0b7SPierre Morel int i;
1996f33f0b7SPierre Morel
20022e8195cSNina Schoetterl-Glausch report_prefix_push("Header");
2016f33f0b7SPierre Morel
20222e8195cSNina Schoetterl-Glausch /* Header is 16 bytes, each TLE 8 or 16, therefore alignment must be 8 at least */
20322e8195cSNina Schoetterl-Glausch report(IS_ALIGNED(info->length, 8), "Length %d multiple of 8", info->length);
20422e8195cSNina Schoetterl-Glausch report(info->length < PAGE_SIZE, "Length %d in bounds", info->length);
20522e8195cSNina Schoetterl-Glausch report(sel2 == info->mnest, "Valid mnest");
2066f33f0b7SPierre Morel stsi_check_maxcpus(info);
2076f33f0b7SPierre Morel
2086f33f0b7SPierre Morel /*
2096f33f0b7SPierre Morel * It is not clear how the MAG fields are calculated when mnest
2106f33f0b7SPierre Morel * in the SYSIB 15.x is different from the maximum nested level
2116f33f0b7SPierre Morel * in the SCLP info, so we skip here for now.
2126f33f0b7SPierre Morel */
2136f33f0b7SPierre Morel if (max_nested_lvl != info->mnest) {
2146f33f0b7SPierre Morel report_skip("No specification on layer aggregation");
2156f33f0b7SPierre Morel goto done;
2166f33f0b7SPierre Morel }
2176f33f0b7SPierre Morel
2186f33f0b7SPierre Morel /*
2196f33f0b7SPierre Morel * MAG up to max_nested_lvl must match the architecture
2206f33f0b7SPierre Morel * defined containers.
2216f33f0b7SPierre Morel */
2226f33f0b7SPierre Morel for (i = 0; i < max_nested_lvl; i++)
2236f33f0b7SPierre Morel report(info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1] == expected_topo_lvl[i],
2246f33f0b7SPierre Morel "MAG %d field match %d == %d",
2256f33f0b7SPierre Morel i + 1,
2266f33f0b7SPierre Morel info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1],
2276f33f0b7SPierre Morel expected_topo_lvl[i]);
2286f33f0b7SPierre Morel
2296f33f0b7SPierre Morel /* Above max_nested_lvl the MAG field must be null */
2306f33f0b7SPierre Morel for (; i < CPU_TOPOLOGY_MAX_LEVEL; i++)
2316f33f0b7SPierre Morel report(info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1] == 0,
2326f33f0b7SPierre Morel "MAG %d field match %d == %d", i + 1,
2336f33f0b7SPierre Morel info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1], 0);
2346f33f0b7SPierre Morel
2356f33f0b7SPierre Morel done:
2366f33f0b7SPierre Morel report_prefix_pop();
2376f33f0b7SPierre Morel }
2386f33f0b7SPierre Morel
2396f33f0b7SPierre Morel /**
2406f33f0b7SPierre Morel * stsi_get_sysib:
2416f33f0b7SPierre Morel * @info: pointer to the STSI info structure
2426f33f0b7SPierre Morel * @sel2: the selector giving the topology level to check
2436f33f0b7SPierre Morel *
2446f33f0b7SPierre Morel * Fill the sysinfo_15_1_x info structure and check the
2456f33f0b7SPierre Morel * SYSIB header.
2466f33f0b7SPierre Morel *
2476f33f0b7SPierre Morel * Returns instruction validity.
2486f33f0b7SPierre Morel */
stsi_get_sysib(struct sysinfo_15_1_x * info,int sel2)2496f33f0b7SPierre Morel static int stsi_get_sysib(struct sysinfo_15_1_x *info, int sel2)
2506f33f0b7SPierre Morel {
2516f33f0b7SPierre Morel int ret;
2526f33f0b7SPierre Morel
2536f33f0b7SPierre Morel report_prefix_pushf("SYSIB");
2546f33f0b7SPierre Morel
255fbc8a89bSNina Schoetterl-Glausch ret = stsi(info, 15, 1, sel2);
2566f33f0b7SPierre Morel
2576f33f0b7SPierre Morel if (max_nested_lvl >= sel2) {
2586f33f0b7SPierre Morel report(!ret, "Valid instruction");
2596f33f0b7SPierre Morel } else {
2606f33f0b7SPierre Morel report(ret, "Invalid instruction");
2616f33f0b7SPierre Morel }
2626f33f0b7SPierre Morel
2636f33f0b7SPierre Morel report_prefix_pop();
2646f33f0b7SPierre Morel
2656f33f0b7SPierre Morel return ret;
2666f33f0b7SPierre Morel }
2676f33f0b7SPierre Morel
check_cpu(union topology_cpu * cpu,union topology_container * parent)268*59fa2015SNina Schoetterl-Glausch static int check_cpu(union topology_cpu *cpu,
269*59fa2015SNina Schoetterl-Glausch union topology_container *parent)
270*59fa2015SNina Schoetterl-Glausch {
271*59fa2015SNina Schoetterl-Glausch report_prefix_pushf("%d:%d:%d:%d", cpu->d, cpu->pp, cpu->type, cpu->origin);
272*59fa2015SNina Schoetterl-Glausch
273*59fa2015SNina Schoetterl-Glausch report(!(cpu->raw[0] & CPUS_TLE_RES_BITS), "reserved bits %016lx",
274*59fa2015SNina Schoetterl-Glausch cpu->raw[0] & CPUS_TLE_RES_BITS);
275*59fa2015SNina Schoetterl-Glausch
276*59fa2015SNina Schoetterl-Glausch report(cpu->type == CPU_TYPE_IFL, "type IFL");
277*59fa2015SNina Schoetterl-Glausch
278*59fa2015SNina Schoetterl-Glausch if (cpu->d)
279*59fa2015SNina Schoetterl-Glausch report(cpu->pp == POLARIZATION_VERTICAL_HIGH ||
280*59fa2015SNina Schoetterl-Glausch cpu->pp == POLARIZATION_HORIZONTAL,
281*59fa2015SNina Schoetterl-Glausch "Dedicated CPUs are either horizontally polarized or have high entitlement");
282*59fa2015SNina Schoetterl-Glausch else
283*59fa2015SNina Schoetterl-Glausch report_skip("Not dedicated");
284*59fa2015SNina Schoetterl-Glausch
285*59fa2015SNina Schoetterl-Glausch report_prefix_pop();
286*59fa2015SNina Schoetterl-Glausch
287*59fa2015SNina Schoetterl-Glausch return __builtin_popcountl(cpu->mask);
288*59fa2015SNina Schoetterl-Glausch }
289*59fa2015SNina Schoetterl-Glausch
check_child_cpus(struct sysinfo_15_1_x * info,union topology_container * cont,union topology_cpu * child,unsigned int * cpus_in_masks)290*59fa2015SNina Schoetterl-Glausch static union topology_container *check_child_cpus(struct sysinfo_15_1_x *info,
291*59fa2015SNina Schoetterl-Glausch union topology_container *cont,
292*59fa2015SNina Schoetterl-Glausch union topology_cpu *child,
293*59fa2015SNina Schoetterl-Glausch unsigned int *cpus_in_masks)
294*59fa2015SNina Schoetterl-Glausch {
295*59fa2015SNina Schoetterl-Glausch void *last = ((void *)info) + info->length;
296*59fa2015SNina Schoetterl-Glausch union topology_cpu *prev_cpu = NULL;
297*59fa2015SNina Schoetterl-Glausch bool correct_ordering = true;
298*59fa2015SNina Schoetterl-Glausch unsigned int cpus = 0;
299*59fa2015SNina Schoetterl-Glausch int i;
300*59fa2015SNina Schoetterl-Glausch
301*59fa2015SNina Schoetterl-Glausch for (i = 0; (void *)&child[i] < last && child[i].nl == 0; prev_cpu = &child[i++]) {
302*59fa2015SNina Schoetterl-Glausch cpus += check_cpu(&child[i], cont);
303*59fa2015SNina Schoetterl-Glausch if (prev_cpu) {
304*59fa2015SNina Schoetterl-Glausch if (prev_cpu->type > child[i].type) {
305*59fa2015SNina Schoetterl-Glausch report_info("Incorrect ordering wrt type for child %d", i);
306*59fa2015SNina Schoetterl-Glausch correct_ordering = false;
307*59fa2015SNina Schoetterl-Glausch }
308*59fa2015SNina Schoetterl-Glausch if (prev_cpu->type < child[i].type)
309*59fa2015SNina Schoetterl-Glausch continue;
310*59fa2015SNina Schoetterl-Glausch if (prev_cpu->pp < child[i].pp) {
311*59fa2015SNina Schoetterl-Glausch report_info("Incorrect ordering wrt polarization for child %d", i);
312*59fa2015SNina Schoetterl-Glausch correct_ordering = false;
313*59fa2015SNina Schoetterl-Glausch }
314*59fa2015SNina Schoetterl-Glausch if (prev_cpu->pp > child[i].pp)
315*59fa2015SNina Schoetterl-Glausch continue;
316*59fa2015SNina Schoetterl-Glausch if (!prev_cpu->d && child[i].d) {
317*59fa2015SNina Schoetterl-Glausch report_info("Incorrect ordering wrt dedication for child %d", i);
318*59fa2015SNina Schoetterl-Glausch correct_ordering = false;
319*59fa2015SNina Schoetterl-Glausch }
320*59fa2015SNina Schoetterl-Glausch if (prev_cpu->d && !child[i].d)
321*59fa2015SNina Schoetterl-Glausch continue;
322*59fa2015SNina Schoetterl-Glausch if (prev_cpu->origin > child[i].origin) {
323*59fa2015SNina Schoetterl-Glausch report_info("Incorrect ordering wrt origin for child %d", i);
324*59fa2015SNina Schoetterl-Glausch correct_ordering = false;
325*59fa2015SNina Schoetterl-Glausch }
326*59fa2015SNina Schoetterl-Glausch }
327*59fa2015SNina Schoetterl-Glausch }
328*59fa2015SNina Schoetterl-Glausch report(correct_ordering, "children correctly ordered");
329*59fa2015SNina Schoetterl-Glausch report(cpus <= expected_topo_lvl[0], "%d children <= max of %d",
330*59fa2015SNina Schoetterl-Glausch cpus, expected_topo_lvl[0]);
331*59fa2015SNina Schoetterl-Glausch *cpus_in_masks += cpus;
332*59fa2015SNina Schoetterl-Glausch
333*59fa2015SNina Schoetterl-Glausch return (union topology_container *)&child[i];
334*59fa2015SNina Schoetterl-Glausch }
335*59fa2015SNina Schoetterl-Glausch
336*59fa2015SNina Schoetterl-Glausch static union topology_container *check_container(struct sysinfo_15_1_x *info,
337*59fa2015SNina Schoetterl-Glausch union topology_container *cont,
338*59fa2015SNina Schoetterl-Glausch union topology_entry *child,
339*59fa2015SNina Schoetterl-Glausch unsigned int *cpus_in_masks);
340*59fa2015SNina Schoetterl-Glausch
check_child_containers(struct sysinfo_15_1_x * info,union topology_container * cont,union topology_container * child,unsigned int * cpus_in_masks)341*59fa2015SNina Schoetterl-Glausch static union topology_container *check_child_containers(struct sysinfo_15_1_x *info,
342*59fa2015SNina Schoetterl-Glausch union topology_container *cont,
343*59fa2015SNina Schoetterl-Glausch union topology_container *child,
344*59fa2015SNina Schoetterl-Glausch unsigned int *cpus_in_masks)
345*59fa2015SNina Schoetterl-Glausch {
346*59fa2015SNina Schoetterl-Glausch void *last = ((void *)info) + info->length;
347*59fa2015SNina Schoetterl-Glausch union topology_container *entry;
348*59fa2015SNina Schoetterl-Glausch int i;
349*59fa2015SNina Schoetterl-Glausch
350*59fa2015SNina Schoetterl-Glausch for (i = 0, entry = child; (void *)entry < last && entry->nl == cont->nl - 1; i++) {
351*59fa2015SNina Schoetterl-Glausch entry = check_container(info, entry, (union topology_entry *)(entry + 1),
352*59fa2015SNina Schoetterl-Glausch cpus_in_masks);
353*59fa2015SNina Schoetterl-Glausch }
354*59fa2015SNina Schoetterl-Glausch if (max_nested_lvl == info->mnest)
355*59fa2015SNina Schoetterl-Glausch report(i <= expected_topo_lvl[cont->nl - 1], "%d children <= max of %d",
356*59fa2015SNina Schoetterl-Glausch i, expected_topo_lvl[cont->nl - 1]);
357*59fa2015SNina Schoetterl-Glausch
358*59fa2015SNina Schoetterl-Glausch return entry;
359*59fa2015SNina Schoetterl-Glausch }
360*59fa2015SNina Schoetterl-Glausch
check_container(struct sysinfo_15_1_x * info,union topology_container * cont,union topology_entry * child,unsigned int * cpus_in_masks)361*59fa2015SNina Schoetterl-Glausch static union topology_container *check_container(struct sysinfo_15_1_x *info,
362*59fa2015SNina Schoetterl-Glausch union topology_container *cont,
363*59fa2015SNina Schoetterl-Glausch union topology_entry *child,
364*59fa2015SNina Schoetterl-Glausch unsigned int *cpus_in_masks)
365*59fa2015SNina Schoetterl-Glausch {
366*59fa2015SNina Schoetterl-Glausch union topology_container *entry;
367*59fa2015SNina Schoetterl-Glausch
368*59fa2015SNina Schoetterl-Glausch report_prefix_pushf("%d", cont->id);
369*59fa2015SNina Schoetterl-Glausch
370*59fa2015SNina Schoetterl-Glausch report(cont->nl - 1 == child->nl, "Level %d one above child level %d",
371*59fa2015SNina Schoetterl-Glausch cont->nl, child->nl);
372*59fa2015SNina Schoetterl-Glausch report(!(cont->raw & CONTAINER_TLE_RES_BITS), "reserved bits %016lx",
373*59fa2015SNina Schoetterl-Glausch cont->raw & CONTAINER_TLE_RES_BITS);
374*59fa2015SNina Schoetterl-Glausch
375*59fa2015SNina Schoetterl-Glausch if (cont->nl > 1)
376*59fa2015SNina Schoetterl-Glausch entry = check_child_containers(info, cont, &child->container, cpus_in_masks);
377*59fa2015SNina Schoetterl-Glausch else
378*59fa2015SNina Schoetterl-Glausch entry = check_child_cpus(info, cont, &child->cpu, cpus_in_masks);
379*59fa2015SNina Schoetterl-Glausch
380*59fa2015SNina Schoetterl-Glausch report_prefix_pop();
381*59fa2015SNina Schoetterl-Glausch return entry;
382*59fa2015SNina Schoetterl-Glausch }
383*59fa2015SNina Schoetterl-Glausch
check_topology_list(struct sysinfo_15_1_x * info,int sel2)384*59fa2015SNina Schoetterl-Glausch static void check_topology_list(struct sysinfo_15_1_x *info, int sel2)
385*59fa2015SNina Schoetterl-Glausch {
386*59fa2015SNina Schoetterl-Glausch union topology_container dummy = { .nl = sel2, .id = 0 };
387*59fa2015SNina Schoetterl-Glausch unsigned int cpus_in_masks = 0;
388*59fa2015SNina Schoetterl-Glausch
389*59fa2015SNina Schoetterl-Glausch report_prefix_push("TLE");
390*59fa2015SNina Schoetterl-Glausch
391*59fa2015SNina Schoetterl-Glausch check_container(info, &dummy, info->tle, &cpus_in_masks);
392*59fa2015SNina Schoetterl-Glausch report(cpus_in_masks == number_of_cpus,
393*59fa2015SNina Schoetterl-Glausch "Number of CPUs %d equals %d CPUs in masks",
394*59fa2015SNina Schoetterl-Glausch number_of_cpus, cpus_in_masks);
395*59fa2015SNina Schoetterl-Glausch
396*59fa2015SNina Schoetterl-Glausch report_prefix_pop();
397*59fa2015SNina Schoetterl-Glausch }
398*59fa2015SNina Schoetterl-Glausch
3996f33f0b7SPierre Morel /**
4006f33f0b7SPierre Morel * check_sysinfo_15_1_x:
4016f33f0b7SPierre Morel * @info: pointer to the STSI info structure
4026f33f0b7SPierre Morel * @sel2: the selector giving the topology level to check
4036f33f0b7SPierre Morel *
4046f33f0b7SPierre Morel * Check if the validity of the STSI instruction and then
4056f33f0b7SPierre Morel * calls specific checks on the information buffer.
4066f33f0b7SPierre Morel */
check_sysinfo_15_1_x(struct sysinfo_15_1_x * info,int sel2)4076f33f0b7SPierre Morel static void check_sysinfo_15_1_x(struct sysinfo_15_1_x *info, int sel2)
4086f33f0b7SPierre Morel {
4096f33f0b7SPierre Morel int ret;
4106f33f0b7SPierre Morel int cc;
4116f33f0b7SPierre Morel unsigned long rc;
4126f33f0b7SPierre Morel
4136f33f0b7SPierre Morel report_prefix_pushf("15_1_%d", sel2);
4146f33f0b7SPierre Morel
4156f33f0b7SPierre Morel ret = stsi_get_sysib(info, sel2);
4166f33f0b7SPierre Morel if (ret) {
4176f33f0b7SPierre Morel report_skip("Selector 2 not supported by architecture");
4186f33f0b7SPierre Morel goto end;
4196f33f0b7SPierre Morel }
4206f33f0b7SPierre Morel
4216f33f0b7SPierre Morel report_prefix_pushf("H");
4226f33f0b7SPierre Morel cc = ptf(PTF_REQ_HORIZONTAL, &rc);
4236f33f0b7SPierre Morel if (cc != 0 && rc != PTF_ERR_ALRDY_POLARIZED) {
4246f33f0b7SPierre Morel report_fail("Unable to set horizontal polarization");
4256f33f0b7SPierre Morel goto vertical;
4266f33f0b7SPierre Morel }
4276f33f0b7SPierre Morel
42822e8195cSNina Schoetterl-Glausch stsi_check_header(info, sel2);
429*59fa2015SNina Schoetterl-Glausch check_topology_list(info, sel2);
4306f33f0b7SPierre Morel
4316f33f0b7SPierre Morel vertical:
4326f33f0b7SPierre Morel report_prefix_pop();
4336f33f0b7SPierre Morel report_prefix_pushf("V");
4346f33f0b7SPierre Morel
4356f33f0b7SPierre Morel cc = ptf(PTF_REQ_VERTICAL, &rc);
4366f33f0b7SPierre Morel if (cc != 0 && rc != PTF_ERR_ALRDY_POLARIZED) {
4376f33f0b7SPierre Morel report_fail("Unable to set vertical polarization");
4386f33f0b7SPierre Morel goto end;
4396f33f0b7SPierre Morel }
4406f33f0b7SPierre Morel
44122e8195cSNina Schoetterl-Glausch stsi_check_header(info, sel2);
442*59fa2015SNina Schoetterl-Glausch check_topology_list(info, sel2);
4436f33f0b7SPierre Morel report_prefix_pop();
4446f33f0b7SPierre Morel
4456f33f0b7SPierre Morel end:
4466f33f0b7SPierre Morel report_prefix_pop();
4476f33f0b7SPierre Morel }
4486f33f0b7SPierre Morel
4496f33f0b7SPierre Morel /*
4506f33f0b7SPierre Morel * The Maximum Nested level is given by SCLP READ_SCP_INFO if the MNEST facility
4516f33f0b7SPierre Morel * is available.
4526f33f0b7SPierre Morel * If the MNEST facility is not available, sclp_get_stsi_mnest returns 0 and the
4536f33f0b7SPierre Morel * Maximum Nested level is 2
4546f33f0b7SPierre Morel */
4556f33f0b7SPierre Morel #define S390_DEFAULT_MNEST 2
sclp_get_mnest(void)4566f33f0b7SPierre Morel static int sclp_get_mnest(void)
4576f33f0b7SPierre Morel {
4586f33f0b7SPierre Morel return sclp_get_stsi_mnest() ?: S390_DEFAULT_MNEST;
4596f33f0b7SPierre Morel }
4606f33f0b7SPierre Morel
expected_num_cpus(void)4616f33f0b7SPierre Morel static int expected_num_cpus(void)
4626f33f0b7SPierre Morel {
4636f33f0b7SPierre Morel int i;
4646f33f0b7SPierre Morel int ncpus = 1;
4656f33f0b7SPierre Morel
4666f33f0b7SPierre Morel for (i = 0; i < CPU_TOPOLOGY_MAX_LEVEL; i++)
4676f33f0b7SPierre Morel ncpus *= expected_topo_lvl[i] ?: 1;
4686f33f0b7SPierre Morel
4696f33f0b7SPierre Morel return ncpus;
4706f33f0b7SPierre Morel }
4716f33f0b7SPierre Morel
4726f33f0b7SPierre Morel /**
4736f33f0b7SPierre Morel * test_stsi:
4746f33f0b7SPierre Morel *
4756f33f0b7SPierre Morel * Retrieves the maximum nested topology level supported by the architecture
4766f33f0b7SPierre Morel * and the number of CPUs.
4776f33f0b7SPierre Morel * Calls the checking for the STSI instruction in sel2 reverse level order
4786f33f0b7SPierre Morel * from 6 (CPU_TOPOLOGY_MAX_LEVEL) to 2 to have the most interesting level,
4796f33f0b7SPierre Morel * the one triggering a topology-change-report-pending condition, level 2,
4806f33f0b7SPierre Morel * at the end of the report.
4816f33f0b7SPierre Morel *
4826f33f0b7SPierre Morel */
test_stsi(void)4836f33f0b7SPierre Morel static void test_stsi(void)
4846f33f0b7SPierre Morel {
4856f33f0b7SPierre Morel int sel2;
4866f33f0b7SPierre Morel
4876f33f0b7SPierre Morel max_cpus = expected_num_cpus();
4886f33f0b7SPierre Morel report_info("Architecture max CPUs: %d", max_cpus);
4896f33f0b7SPierre Morel
4906f33f0b7SPierre Morel max_nested_lvl = sclp_get_mnest();
4916f33f0b7SPierre Morel report_info("SCLP maximum nested level : %d", max_nested_lvl);
4926f33f0b7SPierre Morel
4936f33f0b7SPierre Morel number_of_cpus = sclp_get_cpu_num();
4946f33f0b7SPierre Morel report_info("SCLP number of CPU: %d", number_of_cpus);
4956f33f0b7SPierre Morel
4966f33f0b7SPierre Morel /* STSI selector 2 can takes values between 2 and 6 */
4976f33f0b7SPierre Morel for (sel2 = 6; sel2 >= 2; sel2--)
4986f33f0b7SPierre Morel check_sysinfo_15_1_x((struct sysinfo_15_1_x *)pagebuf, sel2);
4996f33f0b7SPierre Morel }
5006f33f0b7SPierre Morel
5016f33f0b7SPierre Morel /**
5026f33f0b7SPierre Morel * parse_topology_args:
5036f33f0b7SPierre Morel * @argc: number of arguments
5046f33f0b7SPierre Morel * @argv: argument array
5056f33f0b7SPierre Morel *
5066f33f0b7SPierre Morel * This function initialize the architecture topology levels
5076f33f0b7SPierre Morel * which should be the same as the one provided by the hypervisor.
5086f33f0b7SPierre Morel *
5096f33f0b7SPierre Morel * We use the current names found in IBM/Z literature, Linux and QEMU:
5106f33f0b7SPierre Morel * cores, sockets/packages, books, drawers and nodes to facilitate the
5116f33f0b7SPierre Morel * human machine interface but store the result in a machine abstract
5126f33f0b7SPierre Morel * array of architecture topology levels.
5136f33f0b7SPierre Morel * Note that when QEMU uses socket as a name for the topology level 1
5146f33f0b7SPierre Morel * Linux uses package or physical_package.
5156f33f0b7SPierre Morel */
parse_topology_args(int argc,char ** argv)5166f33f0b7SPierre Morel static void parse_topology_args(int argc, char **argv)
5176f33f0b7SPierre Morel {
5186f33f0b7SPierre Morel int i;
5196f33f0b7SPierre Morel static const char * const levels[] = { "cores", "sockets",
5206f33f0b7SPierre Morel "books", "drawers" };
5216f33f0b7SPierre Morel
5226f33f0b7SPierre Morel for (i = 1; i < argc; i++) {
5236f33f0b7SPierre Morel char *flag = argv[i];
5246f33f0b7SPierre Morel int level;
5256f33f0b7SPierre Morel
5266f33f0b7SPierre Morel if (flag[0] != '-')
5276f33f0b7SPierre Morel report_abort("Argument is expected to begin with '-'");
5286f33f0b7SPierre Morel flag++;
52985e1e05fSNina Schoetterl-Glausch for (level = 0; level < ARRAY_SIZE(levels); level++) {
5306f33f0b7SPierre Morel if (!strcmp(levels[level], flag))
5316f33f0b7SPierre Morel break;
5326f33f0b7SPierre Morel }
5336f33f0b7SPierre Morel if (level == ARRAY_SIZE(levels))
5346f33f0b7SPierre Morel report_abort("Unknown parameter %s", flag);
5356f33f0b7SPierre Morel
5366f33f0b7SPierre Morel expected_topo_lvl[level] = atol(argv[++i]);
5376f33f0b7SPierre Morel report_info("%s: %d", levels[level], expected_topo_lvl[level]);
5386f33f0b7SPierre Morel }
5396f33f0b7SPierre Morel }
5406f33f0b7SPierre Morel
541fb9a0c74SPierre Morel static struct {
542fb9a0c74SPierre Morel const char *name;
543fb9a0c74SPierre Morel void (*func)(void);
544fb9a0c74SPierre Morel } tests[] = {
545fb9a0c74SPierre Morel { "PTF", test_ptf },
5466f33f0b7SPierre Morel { "STSI", test_stsi },
547fb9a0c74SPierre Morel { NULL, NULL }
548fb9a0c74SPierre Morel };
549fb9a0c74SPierre Morel
main(int argc,char * argv[])550fb9a0c74SPierre Morel int main(int argc, char *argv[])
551fb9a0c74SPierre Morel {
552fb9a0c74SPierre Morel int i;
553fb9a0c74SPierre Morel
554fb9a0c74SPierre Morel report_prefix_push("CPU Topology");
555fb9a0c74SPierre Morel
5566f33f0b7SPierre Morel parse_topology_args(argc, argv);
5576f33f0b7SPierre Morel
558fb9a0c74SPierre Morel if (!test_facility(11)) {
559fb9a0c74SPierre Morel report_skip("Topology facility not present");
560fb9a0c74SPierre Morel goto end;
561fb9a0c74SPierre Morel }
562fb9a0c74SPierre Morel
563fb9a0c74SPierre Morel report_info("Virtual machine level %ld", stsi_get_fc());
564fb9a0c74SPierre Morel
565fb9a0c74SPierre Morel for (i = 0; tests[i].name; i++) {
566fb9a0c74SPierre Morel report_prefix_push(tests[i].name);
567fb9a0c74SPierre Morel tests[i].func();
568fb9a0c74SPierre Morel report_prefix_pop();
569fb9a0c74SPierre Morel }
570fb9a0c74SPierre Morel
571fb9a0c74SPierre Morel end:
572fb9a0c74SPierre Morel report_prefix_pop();
573fb9a0c74SPierre Morel return report_summary();
574fb9a0c74SPierre Morel }
575