xref: /kvm-unit-tests/s390x/topology.c (revision fbc8a89be628e7d3b426d1f1dcf9076e70da07f0)
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>
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 cpus_in_masks;
276f33f0b7SPierre Morel static int max_cpus;
286f33f0b7SPierre Morel 
296f33f0b7SPierre Morel /*
306f33f0b7SPierre Morel  * Topology level as defined by architecture, all levels exists with
316f33f0b7SPierre Morel  * a single container unless overwritten by the QEMU -smp parameter.
326f33f0b7SPierre Morel  */
336f33f0b7SPierre 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 
1726f33f0b7SPierre Morel /*
1736f33f0b7SPierre Morel  * stsi_check_maxcpus
1746f33f0b7SPierre Morel  * @info: Pointer to the stsi information
1756f33f0b7SPierre Morel  *
1766f33f0b7SPierre Morel  * The product of the numbers of containers per level
1776f33f0b7SPierre Morel  * is the maximum number of CPU allowed by the machine.
1786f33f0b7SPierre Morel  */
1796f33f0b7SPierre Morel static void stsi_check_maxcpus(struct sysinfo_15_1_x *info)
1806f33f0b7SPierre Morel {
1816f33f0b7SPierre Morel 	int n, i;
1826f33f0b7SPierre Morel 
1836f33f0b7SPierre Morel 	for (i = 0, n = 1; i < CPU_TOPOLOGY_MAX_LEVEL; i++)
1846f33f0b7SPierre Morel 		n *= info->mag[i] ?: 1;
1856f33f0b7SPierre Morel 
1866f33f0b7SPierre Morel 	report(n == max_cpus, "Calculated max CPUs: %d", n);
1876f33f0b7SPierre Morel }
1886f33f0b7SPierre Morel 
1896f33f0b7SPierre Morel /*
1906f33f0b7SPierre Morel  * stsi_check_mag
1916f33f0b7SPierre Morel  * @info: Pointer to the stsi information
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  */
1966f33f0b7SPierre Morel static void stsi_check_mag(struct sysinfo_15_1_x *info)
1976f33f0b7SPierre Morel {
1986f33f0b7SPierre Morel 	int i;
1996f33f0b7SPierre Morel 
2006f33f0b7SPierre Morel 	report_prefix_push("MAG");
2016f33f0b7SPierre Morel 
2026f33f0b7SPierre Morel 	stsi_check_maxcpus(info);
2036f33f0b7SPierre Morel 
2046f33f0b7SPierre Morel 	/*
2056f33f0b7SPierre Morel 	 * It is not clear how the MAG fields are calculated when mnest
2066f33f0b7SPierre Morel 	 * in the SYSIB 15.x is different from the maximum nested level
2076f33f0b7SPierre Morel 	 * in the SCLP info, so we skip here for now.
2086f33f0b7SPierre Morel 	 */
2096f33f0b7SPierre Morel 	if (max_nested_lvl != info->mnest) {
2106f33f0b7SPierre Morel 		report_skip("No specification on layer aggregation");
2116f33f0b7SPierre Morel 		goto done;
2126f33f0b7SPierre Morel 	}
2136f33f0b7SPierre Morel 
2146f33f0b7SPierre Morel 	/*
2156f33f0b7SPierre Morel 	 * MAG up to max_nested_lvl must match the architecture
2166f33f0b7SPierre Morel 	 * defined containers.
2176f33f0b7SPierre Morel 	 */
2186f33f0b7SPierre Morel 	for (i = 0; i < max_nested_lvl; i++)
2196f33f0b7SPierre Morel 		report(info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1] == expected_topo_lvl[i],
2206f33f0b7SPierre Morel 		       "MAG %d field match %d == %d",
2216f33f0b7SPierre Morel 		       i + 1,
2226f33f0b7SPierre Morel 		       info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1],
2236f33f0b7SPierre Morel 		       expected_topo_lvl[i]);
2246f33f0b7SPierre Morel 
2256f33f0b7SPierre Morel 	/* Above max_nested_lvl the MAG field must be null */
2266f33f0b7SPierre Morel 	for (; i < CPU_TOPOLOGY_MAX_LEVEL; i++)
2276f33f0b7SPierre Morel 		report(info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1] == 0,
2286f33f0b7SPierre Morel 		       "MAG %d field match %d == %d", i + 1,
2296f33f0b7SPierre Morel 		       info->mag[CPU_TOPOLOGY_MAX_LEVEL - i - 1], 0);
2306f33f0b7SPierre Morel 
2316f33f0b7SPierre Morel done:
2326f33f0b7SPierre Morel 	report_prefix_pop();
2336f33f0b7SPierre Morel }
2346f33f0b7SPierre Morel 
2356f33f0b7SPierre Morel /**
2366f33f0b7SPierre Morel  * check_tle:
2376f33f0b7SPierre Morel  * @tc: pointer to first TLE
2386f33f0b7SPierre Morel  *
2396f33f0b7SPierre Morel  * Recursively check the containers TLEs until we
2406f33f0b7SPierre Morel  * find a CPU TLE.
2416f33f0b7SPierre Morel  */
2426f33f0b7SPierre Morel static uint8_t *check_tle(void *tc)
2436f33f0b7SPierre Morel {
2446f33f0b7SPierre Morel 	struct topology_container *container = tc;
2456f33f0b7SPierre Morel 	struct topology_core *cpus;
2466f33f0b7SPierre Morel 	int n;
2476f33f0b7SPierre Morel 
2486f33f0b7SPierre Morel 	if (container->nl) {
2496f33f0b7SPierre Morel 		report_info("NL: %d id: %d", container->nl, container->id);
2506f33f0b7SPierre Morel 
2516f33f0b7SPierre Morel 		report(!(*(uint64_t *)tc & CONTAINER_TLE_RES_BITS),
2526f33f0b7SPierre Morel 		       "reserved bits %016lx",
2536f33f0b7SPierre Morel 		       *(uint64_t *)tc & CONTAINER_TLE_RES_BITS);
2546f33f0b7SPierre Morel 
2556f33f0b7SPierre Morel 		return check_tle(tc + sizeof(*container));
2566f33f0b7SPierre Morel 	}
2576f33f0b7SPierre Morel 
2586f33f0b7SPierre Morel 	report_info("NL: %d", container->nl);
2596f33f0b7SPierre Morel 	cpus = tc;
2606f33f0b7SPierre Morel 
2616f33f0b7SPierre Morel 	report(!(*(uint64_t *)tc & CPUS_TLE_RES_BITS), "reserved bits %016lx",
2626f33f0b7SPierre Morel 	       *(uint64_t *)tc & CPUS_TLE_RES_BITS);
2636f33f0b7SPierre Morel 
26431ebf55bSNina Schoetterl-Glausch 	report(cpus->type == CPU_TYPE_IFL, "type IFL");
2656f33f0b7SPierre Morel 
2666f33f0b7SPierre Morel 	report_info("origin: %d", cpus->origin);
2676f33f0b7SPierre Morel 	report_info("mask: %016lx", cpus->mask);
2686f33f0b7SPierre Morel 	report_info("dedicated: %d entitlement: %d", cpus->d, cpus->pp);
2696f33f0b7SPierre Morel 
2706f33f0b7SPierre Morel 	n = __builtin_popcountl(cpus->mask);
2716f33f0b7SPierre Morel 	report(n <= expected_topo_lvl[0], "CPUs per mask: %d out of max %d",
2726f33f0b7SPierre Morel 	       n, expected_topo_lvl[0]);
2736f33f0b7SPierre Morel 	cpus_in_masks += n;
2746f33f0b7SPierre Morel 
2756f33f0b7SPierre Morel 	if (!cpus->d)
2766f33f0b7SPierre Morel 		report_skip("Not dedicated");
2776f33f0b7SPierre Morel 	else
27831ebf55bSNina Schoetterl-Glausch 		report(cpus->pp == POLARIZATION_VERTICAL_HIGH ||
27931ebf55bSNina Schoetterl-Glausch 		       cpus->pp == POLARIZATION_HORIZONTAL,
28047abae77SNina Schoetterl-Glausch 		       "Dedicated CPUs are either horizontally polarized or have high entitlement");
2816f33f0b7SPierre Morel 
2826f33f0b7SPierre Morel 	return tc + sizeof(*cpus);
2836f33f0b7SPierre Morel }
2846f33f0b7SPierre Morel 
2856f33f0b7SPierre Morel /**
2866f33f0b7SPierre Morel  * stsi_check_tle_coherency:
2876f33f0b7SPierre Morel  * @info: Pointer to the stsi information
2886f33f0b7SPierre Morel  *
2896f33f0b7SPierre Morel  * We verify that we get the expected number of Topology List Entry
2906f33f0b7SPierre Morel  * containers for a specific level.
2916f33f0b7SPierre Morel  */
2926f33f0b7SPierre Morel static void stsi_check_tle_coherency(struct sysinfo_15_1_x *info)
2936f33f0b7SPierre Morel {
2946f33f0b7SPierre Morel 	void *tc, *end;
2956f33f0b7SPierre Morel 
2966f33f0b7SPierre Morel 	report_prefix_push("TLE");
2976f33f0b7SPierre Morel 	cpus_in_masks = 0;
2986f33f0b7SPierre Morel 
2996f33f0b7SPierre Morel 	tc = info->tle;
3006f33f0b7SPierre Morel 	end = (void *)info + info->length;
3016f33f0b7SPierre Morel 
3026f33f0b7SPierre Morel 	while (tc < end)
3036f33f0b7SPierre Morel 		tc = check_tle(tc);
3046f33f0b7SPierre Morel 
3056f33f0b7SPierre Morel 	report(cpus_in_masks == number_of_cpus, "CPUs in mask %d",
3066f33f0b7SPierre Morel 	       cpus_in_masks);
3076f33f0b7SPierre Morel 
3086f33f0b7SPierre Morel 	report_prefix_pop();
3096f33f0b7SPierre Morel }
3106f33f0b7SPierre Morel 
3116f33f0b7SPierre Morel /**
3126f33f0b7SPierre Morel  * stsi_get_sysib:
3136f33f0b7SPierre Morel  * @info: pointer to the STSI info structure
3146f33f0b7SPierre Morel  * @sel2: the selector giving the topology level to check
3156f33f0b7SPierre Morel  *
3166f33f0b7SPierre Morel  * Fill the sysinfo_15_1_x info structure and check the
3176f33f0b7SPierre Morel  * SYSIB header.
3186f33f0b7SPierre Morel  *
3196f33f0b7SPierre Morel  * Returns instruction validity.
3206f33f0b7SPierre Morel  */
3216f33f0b7SPierre Morel static int stsi_get_sysib(struct sysinfo_15_1_x *info, int sel2)
3226f33f0b7SPierre Morel {
3236f33f0b7SPierre Morel 	int ret;
3246f33f0b7SPierre Morel 
3256f33f0b7SPierre Morel 	report_prefix_pushf("SYSIB");
3266f33f0b7SPierre Morel 
327*fbc8a89bSNina Schoetterl-Glausch 	ret = stsi(info, 15, 1, sel2);
3286f33f0b7SPierre Morel 
3296f33f0b7SPierre Morel 	if (max_nested_lvl >= sel2) {
3306f33f0b7SPierre Morel 		report(!ret, "Valid instruction");
3316f33f0b7SPierre Morel 		report(sel2 == info->mnest, "Valid mnest");
3326f33f0b7SPierre Morel 	} else {
3336f33f0b7SPierre Morel 		report(ret, "Invalid instruction");
3346f33f0b7SPierre Morel 	}
3356f33f0b7SPierre Morel 
3366f33f0b7SPierre Morel 	report_prefix_pop();
3376f33f0b7SPierre Morel 
3386f33f0b7SPierre Morel 	return ret;
3396f33f0b7SPierre Morel }
3406f33f0b7SPierre Morel 
3416f33f0b7SPierre Morel /**
3426f33f0b7SPierre Morel  * check_sysinfo_15_1_x:
3436f33f0b7SPierre Morel  * @info: pointer to the STSI info structure
3446f33f0b7SPierre Morel  * @sel2: the selector giving the topology level to check
3456f33f0b7SPierre Morel  *
3466f33f0b7SPierre Morel  * Check if the validity of the STSI instruction and then
3476f33f0b7SPierre Morel  * calls specific checks on the information buffer.
3486f33f0b7SPierre Morel  */
3496f33f0b7SPierre Morel static void check_sysinfo_15_1_x(struct sysinfo_15_1_x *info, int sel2)
3506f33f0b7SPierre Morel {
3516f33f0b7SPierre Morel 	int ret;
3526f33f0b7SPierre Morel 	int cc;
3536f33f0b7SPierre Morel 	unsigned long rc;
3546f33f0b7SPierre Morel 
3556f33f0b7SPierre Morel 	report_prefix_pushf("15_1_%d", sel2);
3566f33f0b7SPierre Morel 
3576f33f0b7SPierre Morel 	ret = stsi_get_sysib(info, sel2);
3586f33f0b7SPierre Morel 	if (ret) {
3596f33f0b7SPierre Morel 		report_skip("Selector 2 not supported by architecture");
3606f33f0b7SPierre Morel 		goto end;
3616f33f0b7SPierre Morel 	}
3626f33f0b7SPierre Morel 
3636f33f0b7SPierre Morel 	report_prefix_pushf("H");
3646f33f0b7SPierre Morel 	cc = ptf(PTF_REQ_HORIZONTAL, &rc);
3656f33f0b7SPierre Morel 	if (cc != 0 && rc != PTF_ERR_ALRDY_POLARIZED) {
3666f33f0b7SPierre Morel 		report_fail("Unable to set horizontal polarization");
3676f33f0b7SPierre Morel 		goto vertical;
3686f33f0b7SPierre Morel 	}
3696f33f0b7SPierre Morel 
3706f33f0b7SPierre Morel 	stsi_check_mag(info);
3716f33f0b7SPierre Morel 	stsi_check_tle_coherency(info);
3726f33f0b7SPierre Morel 
3736f33f0b7SPierre Morel vertical:
3746f33f0b7SPierre Morel 	report_prefix_pop();
3756f33f0b7SPierre Morel 	report_prefix_pushf("V");
3766f33f0b7SPierre Morel 
3776f33f0b7SPierre Morel 	cc = ptf(PTF_REQ_VERTICAL, &rc);
3786f33f0b7SPierre Morel 	if (cc != 0 && rc != PTF_ERR_ALRDY_POLARIZED) {
3796f33f0b7SPierre Morel 		report_fail("Unable to set vertical polarization");
3806f33f0b7SPierre Morel 		goto end;
3816f33f0b7SPierre Morel 	}
3826f33f0b7SPierre Morel 
3836f33f0b7SPierre Morel 	stsi_check_mag(info);
3846f33f0b7SPierre Morel 	stsi_check_tle_coherency(info);
3856f33f0b7SPierre Morel 	report_prefix_pop();
3866f33f0b7SPierre Morel 
3876f33f0b7SPierre Morel end:
3886f33f0b7SPierre Morel 	report_prefix_pop();
3896f33f0b7SPierre Morel }
3906f33f0b7SPierre Morel 
3916f33f0b7SPierre Morel /*
3926f33f0b7SPierre Morel  * The Maximum Nested level is given by SCLP READ_SCP_INFO if the MNEST facility
3936f33f0b7SPierre Morel  * is available.
3946f33f0b7SPierre Morel  * If the MNEST facility is not available, sclp_get_stsi_mnest  returns 0 and the
3956f33f0b7SPierre Morel  * Maximum Nested level is 2
3966f33f0b7SPierre Morel  */
3976f33f0b7SPierre Morel #define S390_DEFAULT_MNEST	2
3986f33f0b7SPierre Morel static int sclp_get_mnest(void)
3996f33f0b7SPierre Morel {
4006f33f0b7SPierre Morel 	return sclp_get_stsi_mnest() ?: S390_DEFAULT_MNEST;
4016f33f0b7SPierre Morel }
4026f33f0b7SPierre Morel 
4036f33f0b7SPierre Morel static int expected_num_cpus(void)
4046f33f0b7SPierre Morel {
4056f33f0b7SPierre Morel 	int i;
4066f33f0b7SPierre Morel 	int ncpus = 1;
4076f33f0b7SPierre Morel 
4086f33f0b7SPierre Morel 	for (i = 0; i < CPU_TOPOLOGY_MAX_LEVEL; i++)
4096f33f0b7SPierre Morel 		ncpus *= expected_topo_lvl[i] ?: 1;
4106f33f0b7SPierre Morel 
4116f33f0b7SPierre Morel 	return ncpus;
4126f33f0b7SPierre Morel }
4136f33f0b7SPierre Morel 
4146f33f0b7SPierre Morel /**
4156f33f0b7SPierre Morel  * test_stsi:
4166f33f0b7SPierre Morel  *
4176f33f0b7SPierre Morel  * Retrieves the maximum nested topology level supported by the architecture
4186f33f0b7SPierre Morel  * and the number of CPUs.
4196f33f0b7SPierre Morel  * Calls the checking for the STSI instruction in sel2 reverse level order
4206f33f0b7SPierre Morel  * from 6 (CPU_TOPOLOGY_MAX_LEVEL) to 2 to have the most interesting level,
4216f33f0b7SPierre Morel  * the one triggering a topology-change-report-pending condition, level 2,
4226f33f0b7SPierre Morel  * at the end of the report.
4236f33f0b7SPierre Morel  *
4246f33f0b7SPierre Morel  */
4256f33f0b7SPierre Morel static void test_stsi(void)
4266f33f0b7SPierre Morel {
4276f33f0b7SPierre Morel 	int sel2;
4286f33f0b7SPierre Morel 
4296f33f0b7SPierre Morel 	max_cpus = expected_num_cpus();
4306f33f0b7SPierre Morel 	report_info("Architecture max CPUs: %d", max_cpus);
4316f33f0b7SPierre Morel 
4326f33f0b7SPierre Morel 	max_nested_lvl = sclp_get_mnest();
4336f33f0b7SPierre Morel 	report_info("SCLP maximum nested level : %d", max_nested_lvl);
4346f33f0b7SPierre Morel 
4356f33f0b7SPierre Morel 	number_of_cpus = sclp_get_cpu_num();
4366f33f0b7SPierre Morel 	report_info("SCLP number of CPU: %d", number_of_cpus);
4376f33f0b7SPierre Morel 
4386f33f0b7SPierre Morel 	/* STSI selector 2 can takes values between 2 and 6 */
4396f33f0b7SPierre Morel 	for (sel2 = 6; sel2 >= 2; sel2--)
4406f33f0b7SPierre Morel 		check_sysinfo_15_1_x((struct sysinfo_15_1_x *)pagebuf, sel2);
4416f33f0b7SPierre Morel }
4426f33f0b7SPierre Morel 
4436f33f0b7SPierre Morel /**
4446f33f0b7SPierre Morel  * parse_topology_args:
4456f33f0b7SPierre Morel  * @argc: number of arguments
4466f33f0b7SPierre Morel  * @argv: argument array
4476f33f0b7SPierre Morel  *
4486f33f0b7SPierre Morel  * This function initialize the architecture topology levels
4496f33f0b7SPierre Morel  * which should be the same as the one provided by the hypervisor.
4506f33f0b7SPierre Morel  *
4516f33f0b7SPierre Morel  * We use the current names found in IBM/Z literature, Linux and QEMU:
4526f33f0b7SPierre Morel  * cores, sockets/packages, books, drawers and nodes to facilitate the
4536f33f0b7SPierre Morel  * human machine interface but store the result in a machine abstract
4546f33f0b7SPierre Morel  * array of architecture topology levels.
4556f33f0b7SPierre Morel  * Note that when QEMU uses socket as a name for the topology level 1
4566f33f0b7SPierre Morel  * Linux uses package or physical_package.
4576f33f0b7SPierre Morel  */
4586f33f0b7SPierre Morel static void parse_topology_args(int argc, char **argv)
4596f33f0b7SPierre Morel {
4606f33f0b7SPierre Morel 	int i;
4616f33f0b7SPierre Morel 	static const char * const levels[] = { "cores", "sockets",
4626f33f0b7SPierre Morel 					       "books", "drawers" };
4636f33f0b7SPierre Morel 
4646f33f0b7SPierre Morel 	for (i = 1; i < argc; i++) {
4656f33f0b7SPierre Morel 		char *flag = argv[i];
4666f33f0b7SPierre Morel 		int level;
4676f33f0b7SPierre Morel 
4686f33f0b7SPierre Morel 		if (flag[0] != '-')
4696f33f0b7SPierre Morel 			report_abort("Argument is expected to begin with '-'");
4706f33f0b7SPierre Morel 		flag++;
4716f33f0b7SPierre Morel 		for (level = 0; ARRAY_SIZE(levels); level++) {
4726f33f0b7SPierre Morel 			if (!strcmp(levels[level], flag))
4736f33f0b7SPierre Morel 				break;
4746f33f0b7SPierre Morel 		}
4756f33f0b7SPierre Morel 		if (level == ARRAY_SIZE(levels))
4766f33f0b7SPierre Morel 			report_abort("Unknown parameter %s", flag);
4776f33f0b7SPierre Morel 
4786f33f0b7SPierre Morel 		expected_topo_lvl[level] = atol(argv[++i]);
4796f33f0b7SPierre Morel 		report_info("%s: %d", levels[level], expected_topo_lvl[level]);
4806f33f0b7SPierre Morel 	}
4816f33f0b7SPierre Morel }
4826f33f0b7SPierre Morel 
483fb9a0c74SPierre Morel static struct {
484fb9a0c74SPierre Morel 	const char *name;
485fb9a0c74SPierre Morel 	void (*func)(void);
486fb9a0c74SPierre Morel } tests[] = {
487fb9a0c74SPierre Morel 	{ "PTF", test_ptf },
4886f33f0b7SPierre Morel 	{ "STSI", test_stsi },
489fb9a0c74SPierre Morel 	{ NULL, NULL }
490fb9a0c74SPierre Morel };
491fb9a0c74SPierre Morel 
492fb9a0c74SPierre Morel int main(int argc, char *argv[])
493fb9a0c74SPierre Morel {
494fb9a0c74SPierre Morel 	int i;
495fb9a0c74SPierre Morel 
496fb9a0c74SPierre Morel 	report_prefix_push("CPU Topology");
497fb9a0c74SPierre Morel 
4986f33f0b7SPierre Morel 	parse_topology_args(argc, argv);
4996f33f0b7SPierre Morel 
500fb9a0c74SPierre Morel 	if (!test_facility(11)) {
501fb9a0c74SPierre Morel 		report_skip("Topology facility not present");
502fb9a0c74SPierre Morel 		goto end;
503fb9a0c74SPierre Morel 	}
504fb9a0c74SPierre Morel 
505fb9a0c74SPierre Morel 	report_info("Virtual machine level %ld", stsi_get_fc());
506fb9a0c74SPierre Morel 
507fb9a0c74SPierre Morel 	for (i = 0; tests[i].name; i++) {
508fb9a0c74SPierre Morel 		report_prefix_push(tests[i].name);
509fb9a0c74SPierre Morel 		tests[i].func();
510fb9a0c74SPierre Morel 		report_prefix_pop();
511fb9a0c74SPierre Morel 	}
512fb9a0c74SPierre Morel 
513fb9a0c74SPierre Morel end:
514fb9a0c74SPierre Morel 	report_prefix_pop();
515fb9a0c74SPierre Morel 	return report_summary();
516fb9a0c74SPierre Morel }
517