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