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