xref: /kvm-unit-tests/s390x/intercept.c (revision 4363f1d9a646a5c7ea673bee8fc33ca6f2cddbd8)
1 /*
2  * Interception tests - for s390x CPU instruction that cause a VM exit
3  *
4  * Copyright (c) 2017 Red Hat Inc
5  *
6  * Authors:
7  *  Thomas Huth <thuth@redhat.com>
8  *
9  * This code is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Library General Public License version 2.
11  */
12 #include <libcflat.h>
13 #include <asm/asm-offsets.h>
14 #include <asm/interrupt.h>
15 #include <asm/page.h>
16 
17 static uint8_t pagebuf[PAGE_SIZE * 2] __attribute__((aligned(PAGE_SIZE * 2)));
18 
19 static unsigned long nr_iterations;
20 static unsigned long time_to_run;
21 
22 /* Test the STORE PREFIX instruction */
23 static void test_stpx(void)
24 {
25 	uint32_t old_prefix = -1U, tst_prefix = -1U;
26 	uint32_t new_prefix = (uint32_t)(intptr_t)pagebuf;
27 
28 	/* Can we successfully change the prefix? */
29 	asm volatile (
30 		" stpx	%0\n"
31 		" spx	%2\n"
32 		" stpx	%1\n"
33 		" spx	%0\n"
34 		: "+Q"(old_prefix), "+Q"(tst_prefix)
35 		: "Q"(new_prefix));
36 	report("store prefix", old_prefix == 0 && tst_prefix == new_prefix);
37 
38 	expect_pgm_int();
39 	low_prot_enable();
40 	asm volatile(" stpx 0(%0) " : : "r"(8));
41 	low_prot_disable();
42 	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
43 
44 	expect_pgm_int();
45 	asm volatile(" stpx 0(%0) " : : "r"(1));
46 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
47 
48 	expect_pgm_int();
49 	asm volatile(" stpx 0(%0) " : : "r"(-8));
50 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
51 }
52 
53 /* Test the SET PREFIX instruction */
54 static void test_spx(void)
55 {
56 	uint32_t new_prefix = (uint32_t)(intptr_t)pagebuf;
57 	uint32_t old_prefix;
58 
59 	memset(pagebuf, 0, PAGE_SIZE * 2);
60 
61 	/*
62 	 * Temporarily change the prefix page to our buffer, and store
63 	 * some facility bits there ... at least some of them should be
64 	 * set in our buffer afterwards.
65 	 */
66 	asm volatile (
67 		" stpx	%0\n"
68 		" spx	%1\n"
69 		" stfl	0\n"
70 		" spx	%0\n"
71 		: "+Q"(old_prefix)
72 		: "Q"(new_prefix)
73 		: "memory");
74 	report("stfl to new prefix", pagebuf[GEN_LC_STFL] != 0);
75 
76 	expect_pgm_int();
77 	asm volatile(" spx 0(%0) " : : "r"(1));
78 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
79 
80 	expect_pgm_int();
81 	asm volatile(" spx 0(%0) " : : "r"(-8));
82 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
83 }
84 
85 /* Test the STORE CPU ADDRESS instruction */
86 static void test_stap(void)
87 {
88 	uint16_t cpuid = 0xffff;
89 
90 	asm volatile ("stap %0\n" : "+Q"(cpuid));
91 	report("get cpu address", cpuid != 0xffff);
92 
93 	expect_pgm_int();
94 	low_prot_enable();
95 	asm volatile ("stap 0(%0)\n" : : "r"(8));
96 	low_prot_disable();
97 	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
98 
99 	expect_pgm_int();
100 	asm volatile ("stap 0(%0)\n" : : "r"(1));
101 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
102 
103 	expect_pgm_int();
104 	asm volatile ("stap 0(%0)\n" : : "r"(-8));
105 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
106 }
107 
108 /* Test the STORE CPU ID instruction */
109 static void test_stidp(void)
110 {
111 	struct cpuid id = {};
112 
113 	asm volatile ("stidp %0\n" : "+Q"(id));
114 	report("type set", id.type);
115 	report("version valid", !id.version || id.version == 0xff);
116 	report("reserved bits not set", !id.reserved);
117 
118 	expect_pgm_int();
119 	low_prot_enable();
120 	asm volatile ("stidp 0(%0)\n" : : "r"(8));
121 	low_prot_disable();
122 	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
123 
124 	expect_pgm_int();
125 	asm volatile ("stidp 0(%0)\n" : : "r"(1));
126 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
127 
128 	expect_pgm_int();
129 	asm volatile ("stidp 0(%0)\n" : : "r"(-8));
130 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
131 }
132 
133 /* Test the TEST BLOCK instruction */
134 static void test_testblock(void)
135 {
136 	int cc;
137 
138 	memset(pagebuf, 0xaa, PAGE_SIZE);
139 
140 	asm volatile (
141 		" lghi	%%r0,0\n"
142 		" tb	%1\n"
143 		" ipm	%0\n"
144 		" srl	%0,28\n"
145 		: "=d" (cc)
146 		: "a"(pagebuf + 0x123)
147 		: "memory", "0", "cc");
148 	report("page cleared",
149 	       cc == 0 && pagebuf[0] == 0 &&  pagebuf[PAGE_SIZE - 1] == 0);
150 
151 	expect_pgm_int();
152 	low_prot_enable();
153 	asm volatile (" tb %0 " : : "r"(4096));
154 	low_prot_disable();
155 	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
156 
157 	expect_pgm_int();
158 	asm volatile (" tb %0 " : : "r"(-4096));
159 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
160 }
161 
162 static uint64_t get_clock_ms(void)
163 {
164 	uint64_t clk;
165 
166 	asm volatile(" stck %0 " : : "Q"(clk) : "memory");
167 
168 	/* Bit 51 is incrememented each microsecond */
169 	return (clk >> (63 - 51)) / 1000;
170 }
171 
172 struct {
173 	const char *name;
174 	void (*func)(void);
175 	bool run_it;
176 } tests[] = {
177 	{ "stpx", test_stpx, false },
178 	{ "spx", test_spx, false },
179 	{ "stap", test_stap, false },
180 	{ "stidp", test_stidp, false },
181 	{ "testblock", test_testblock, false },
182 	{ NULL, NULL, false }
183 };
184 
185 static void parse_intercept_test_args(int argc, char **argv)
186 {
187 	int i, ti;
188 	bool run_all = true;
189 
190 	for (i = 1; i < argc; i++) {
191 		if (!strcmp("-i", argv[i])) {
192 			i++;
193 			if (i >= argc)
194 				report_abort("-i needs a parameter");
195 			nr_iterations = atol(argv[i]);
196 		} else if (!strcmp("-t", argv[i])) {
197 			i++;
198 			if (i >= argc)
199 				report_abort("-t needs a parameter");
200 			time_to_run = atol(argv[i]);
201 		} else for (ti = 0; tests[ti].name != NULL; ti++) {
202 			if (!strcmp(tests[ti].name, argv[i])) {
203 				run_all = false;
204 				tests[ti].run_it = true;
205 				break;
206 			} else if (tests[ti + 1].name == NULL) {
207 				report_abort("Unsupported parameter '%s'",
208 					     argv[i]);
209 			}
210 		}
211 	}
212 
213 	if (run_all) {
214 		for (ti = 0; tests[ti].name != NULL; ti++)
215 			tests[ti].run_it = true;
216 	}
217 }
218 
219 int main(int argc, char **argv)
220 {
221 	uint64_t startclk;
222 	int ti;
223 
224 	parse_intercept_test_args(argc, argv);
225 
226 	if (nr_iterations == 0 && time_to_run == 0)
227 		nr_iterations = 1;
228 
229 	report_prefix_push("intercept");
230 
231 	startclk = get_clock_ms();
232 	for (;;) {
233 		for (ti = 0; tests[ti].name != NULL; ti++) {
234 			report_prefix_push(tests[ti].name);
235 			if (tests[ti].run_it)
236 				tests[ti].func();
237 			report_prefix_pop();
238 		}
239 		if (nr_iterations) {
240 			nr_iterations -= 1;
241 			if (nr_iterations == 0)
242 				break;
243 		}
244 		if (time_to_run) {
245 			if (get_clock_ms() - startclk > time_to_run)
246 				break;
247 		}
248 	}
249 
250 	report_prefix_pop();
251 
252 	return report_summary();
253 }
254