xref: /kvm-unit-tests/s390x/intercept.c (revision d0b9e61dcc801bf55de50102bd614d693edb2c6d)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Interception tests - for s390x CPU instruction that cause a VM exit
4  *
5  * Copyright (c) 2017 Red Hat Inc
6  *
7  * Authors:
8  *  Thomas Huth <thuth@redhat.com>
9  */
10 #include <libcflat.h>
11 #include <sclp.h>
12 #include <asm/asm-offsets.h>
13 #include <asm/interrupt.h>
14 #include <asm/page.h>
15 #include <asm/facility.h>
16 #include <asm/time.h>
17 #include <hardware.h>
18 
19 static uint8_t pagebuf[PAGE_SIZE * 2] __attribute__((aligned(PAGE_SIZE * 2)));
20 
21 static unsigned long nr_iterations;
22 static unsigned long time_to_run;
23 
24 /* Test the STORE PREFIX instruction */
test_stpx(void)25 static void test_stpx(void)
26 {
27 	uint32_t old_prefix = -1U, tst_prefix = -1U;
28 	uint32_t new_prefix = (uint32_t)(intptr_t)pagebuf;
29 
30 	/* Can we successfully change the prefix? */
31 	old_prefix = get_prefix();
32 	set_prefix(new_prefix);
33 	tst_prefix = get_prefix();
34 	set_prefix(old_prefix);
35 	report(old_prefix == 0 && tst_prefix == new_prefix, "store prefix");
36 
37 	expect_pgm_int();
38 	low_prot_enable();
39 	asm volatile(" stpx 0(%0) " : : "a"(8));
40 	low_prot_disable();
41 	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
42 
43 	expect_pgm_int();
44 	asm volatile(" stpx 0(%0) " : : "a"(1));
45 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
46 
47 	expect_pgm_int();
48 	asm volatile(" stpx 0(%0) " : : "a"(-8L));
49 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
50 }
51 
52 /* Test the SET PREFIX instruction */
test_spx(void)53 static void test_spx(void)
54 {
55 	uint32_t new_prefix = (uint32_t)(intptr_t)pagebuf;
56 	uint32_t old_prefix;
57 
58 	memset(pagebuf, 0, PAGE_SIZE * 2);
59 
60 	/*
61 	 * Temporarily change the prefix page to our buffer, and store
62 	 * some facility bits there ... at least some of them should be
63 	 * set in our buffer afterwards.
64 	 */
65 	old_prefix = get_prefix();
66 	set_prefix(new_prefix);
67 	stfl();
68 	set_prefix(old_prefix);
69 	report(pagebuf[GEN_LC_STFL] != 0, "stfl to new prefix");
70 
71 	report_prefix_push("operand not word aligned");
72 	expect_pgm_int();
73 	asm volatile(" spx 0(%0) " : : "a"(1));
74 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
75 	report_prefix_pop();
76 
77 	report_prefix_push("operand outside memory");
78 	expect_pgm_int();
79 	asm volatile(" spx 0(%0) " : : "a"(-8L));
80 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
81 	report_prefix_pop();
82 
83 	report_prefix_push("new prefix outside memory");
84 	new_prefix = get_ram_size() & 0x7fffe000;
85 	/* TODO: Remove host_is_tcg() checks once CIs are using QEMU >= 7.1 */
86 	if (!host_is_tcg() && (get_ram_size() - new_prefix < 2 * PAGE_SIZE)) {
87 		expect_pgm_int();
88 		asm volatile("spx	%0 " : : "Q"(new_prefix));
89 		check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
90 
91 		/*
92 		 * Cannot test inaccessibility of the second page the same way.
93 		 * If we try to use the last page as first half of the prefix
94 		 * area and our ram size is a multiple of 8k, after SPX aligns
95 		 * the address to 8k we have a completely accessible area.
96 		 */
97 	} else {
98 		if (host_is_tcg())
99 			report_skip("inaccessible prefix area (workaround for TCG bug)");
100 		else
101 			report_skip("inaccessible prefix area");
102 	}
103 	report_prefix_pop();
104 }
105 
106 /* Test the STORE CPU ADDRESS instruction */
test_stap(void)107 static void test_stap(void)
108 {
109 	uint16_t cpuid = 0xffff;
110 
111 	asm volatile ("stap %0\n" : "+Q"(cpuid));
112 	report(cpuid != 0xffff, "get cpu address");
113 
114 	expect_pgm_int();
115 	low_prot_enable();
116 	asm volatile ("stap 0(%0)\n" : : "a"(8));
117 	low_prot_disable();
118 	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
119 
120 	expect_pgm_int();
121 	asm volatile ("stap 0(%0)\n" : : "a"(1));
122 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
123 
124 	expect_pgm_int();
125 	asm volatile ("stap 0(%0)\n" : : "a"(-8L));
126 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
127 }
128 
129 /* Test the STORE CPU ID instruction */
test_stidp(void)130 static void test_stidp(void)
131 {
132 	struct cpuid id = {};
133 
134 	asm volatile ("stidp %0\n" : "+Q"(id));
135 	report(id.type, "type set");
136 	report(!id.version || id.version == 0xff, "version valid");
137 	report(!id.reserved, "reserved bits not set");
138 
139 	expect_pgm_int();
140 	low_prot_enable();
141 	asm volatile ("stidp 0(%0)\n" : : "a"(8));
142 	low_prot_disable();
143 	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
144 
145 	expect_pgm_int();
146 	asm volatile ("stidp 0(%0)\n" : : "a"(1));
147 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
148 
149 	expect_pgm_int();
150 	asm volatile ("stidp 0(%0)\n" : : "a"(-8L));
151 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
152 }
153 
154 /* Test the TEST BLOCK instruction */
test_testblock(void)155 static void test_testblock(void)
156 {
157 	int cc;
158 
159 	memset(pagebuf, 0xaa, PAGE_SIZE);
160 
161 	asm volatile (
162 		" lghi	%%r0,0\n"
163 		" .insn	rre,0xb22c0000,0,%1\n"
164 		" ipm	%0\n"
165 		" srl	%0,28\n"
166 		: "=d" (cc)
167 		: "a"(pagebuf + 0x123)
168 		: "memory", "0", "cc");
169 	report(cc == 0 && pagebuf[0] == 0 && pagebuf[PAGE_SIZE - 1] == 0,
170 	       "page cleared");
171 
172 	expect_pgm_int();
173 	low_prot_enable();
174 	asm volatile (" .insn	rre,0xb22c0000,0,%0\n" : : "r"(4096));
175 	low_prot_disable();
176 	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
177 
178 	expect_pgm_int();
179 	asm volatile (" .insn	rre,0xb22c0000,0,%0\n" : : "r"(-4096L));
180 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
181 }
182 
test_diag318(void)183 static void test_diag318(void)
184 {
185 	expect_pgm_int();
186 	enter_pstate();
187 	asm volatile("diag %0,0,0x318\n" : : "d" (0x42));
188 	check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION);
189 
190 	if (!sclp_facilities.has_diag318)
191 		expect_pgm_int();
192 
193 	asm volatile("diag %0,0,0x318\n" : : "d" (0x42));
194 
195 	if (!sclp_facilities.has_diag318)
196 		check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
197 
198 }
199 
200 struct {
201 	const char *name;
202 	void (*func)(void);
203 	bool run_it;
204 } tests[] = {
205 	{ "stpx", test_stpx, false },
206 	{ "spx", test_spx, false },
207 	{ "stap", test_stap, false },
208 	{ "stidp", test_stidp, false },
209 	{ "testblock", test_testblock, false },
210 	{ "diag318", test_diag318, false },
211 	{ NULL, NULL, false }
212 };
213 
parse_intercept_test_args(int argc,char ** argv)214 static void parse_intercept_test_args(int argc, char **argv)
215 {
216 	int i, ti;
217 	bool run_all = true;
218 
219 	for (i = 1; i < argc; i++) {
220 		if (!strcmp("-i", argv[i])) {
221 			i++;
222 			if (i >= argc)
223 				report_abort("-i needs a parameter");
224 			nr_iterations = atol(argv[i]);
225 		} else if (!strcmp("-t", argv[i])) {
226 			i++;
227 			if (i >= argc)
228 				report_abort("-t needs a parameter");
229 			time_to_run = atol(argv[i]);
230 		} else for (ti = 0; tests[ti].name != NULL; ti++) {
231 			if (!strcmp(tests[ti].name, argv[i])) {
232 				run_all = false;
233 				tests[ti].run_it = true;
234 				break;
235 			} else if (tests[ti + 1].name == NULL) {
236 				report_abort("Unsupported parameter '%s'",
237 					     argv[i]);
238 			}
239 		}
240 	}
241 
242 	if (run_all) {
243 		for (ti = 0; tests[ti].name != NULL; ti++)
244 			tests[ti].run_it = true;
245 	}
246 }
247 
main(int argc,char ** argv)248 int main(int argc, char **argv)
249 {
250 	uint64_t startclk;
251 	int ti;
252 
253 	parse_intercept_test_args(argc, argv);
254 
255 	if (nr_iterations == 0 && time_to_run == 0)
256 		nr_iterations = 1;
257 
258 	report_prefix_push("intercept");
259 
260 	startclk = get_clock_ms();
261 	for (;;) {
262 		for (ti = 0; tests[ti].name != NULL; ti++) {
263 			report_prefix_push(tests[ti].name);
264 			if (tests[ti].run_it)
265 				tests[ti].func();
266 			report_prefix_pop();
267 		}
268 		if (nr_iterations) {
269 			nr_iterations -= 1;
270 			if (nr_iterations == 0)
271 				break;
272 		}
273 		if (time_to_run) {
274 			if (get_clock_ms() - startclk > time_to_run)
275 				break;
276 		}
277 	}
278 
279 	report_prefix_pop();
280 
281 	return report_summary();
282 }
283