xref: /kvm-unit-tests/s390x/topology.c (revision 6f33f0b7912953a2f8e6fde5a441dfef2d850cae)
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