xref: /kvm-unit-tests/s390x/intercept.c (revision 97b5f9553a48a15296462dc4c6170200b0dd70b8)
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 #include <asm/facility.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 
79 /* Test the STORE CPU ADDRESS instruction */
80 static void test_stap(void)
81 {
82 	uint16_t cpuid = 0xffff;
83 
84 	asm volatile ("stap %0\n" : "+Q"(cpuid));
85 	report(cpuid != 0xffff, "get cpu address");
86 
87 	expect_pgm_int();
88 	low_prot_enable();
89 	asm volatile ("stap 0(%0)\n" : : "r"(8));
90 	low_prot_disable();
91 	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
92 
93 	expect_pgm_int();
94 	asm volatile ("stap 0(%0)\n" : : "r"(1));
95 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
96 
97 	expect_pgm_int();
98 	asm volatile ("stap 0(%0)\n" : : "r"(-8L));
99 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
100 }
101 
102 /* Test the STORE CPU ID instruction */
103 static void test_stidp(void)
104 {
105 	struct cpuid id = {};
106 
107 	asm volatile ("stidp %0\n" : "+Q"(id));
108 	report(id.type, "type set");
109 	report(!id.version || id.version == 0xff, "version valid");
110 	report(!id.reserved, "reserved bits not set");
111 
112 	expect_pgm_int();
113 	low_prot_enable();
114 	asm volatile ("stidp 0(%0)\n" : : "r"(8));
115 	low_prot_disable();
116 	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
117 
118 	expect_pgm_int();
119 	asm volatile ("stidp 0(%0)\n" : : "r"(1));
120 	check_pgm_int_code(PGM_INT_CODE_SPECIFICATION);
121 
122 	expect_pgm_int();
123 	asm volatile ("stidp 0(%0)\n" : : "r"(-8L));
124 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
125 }
126 
127 /* Test the TEST BLOCK instruction */
128 static void test_testblock(void)
129 {
130 	int cc;
131 
132 	memset(pagebuf, 0xaa, PAGE_SIZE);
133 
134 	asm volatile (
135 		" lghi	%%r0,0\n"
136 		" .insn	rre,0xb22c0000,0,%1\n"
137 		" ipm	%0\n"
138 		" srl	%0,28\n"
139 		: "=d" (cc)
140 		: "a"(pagebuf + 0x123)
141 		: "memory", "0", "cc");
142 	report(cc == 0 && pagebuf[0] == 0 && pagebuf[PAGE_SIZE - 1] == 0,
143 	       "page cleared");
144 
145 	expect_pgm_int();
146 	low_prot_enable();
147 	asm volatile (" .insn	rre,0xb22c0000,0,%0\n" : : "r"(4096));
148 	low_prot_disable();
149 	check_pgm_int_code(PGM_INT_CODE_PROTECTION);
150 
151 	expect_pgm_int();
152 	asm volatile (" .insn	rre,0xb22c0000,0,%0\n" : : "r"(-4096L));
153 	check_pgm_int_code(PGM_INT_CODE_ADDRESSING);
154 }
155 
156 static uint64_t get_clock_ms(void)
157 {
158 	uint64_t clk;
159 
160 	asm volatile(" stck %0 " : : "Q"(clk) : "memory");
161 
162 	/* Bit 51 is incrememented each microsecond */
163 	return (clk >> (63 - 51)) / 1000;
164 }
165 
166 struct {
167 	const char *name;
168 	void (*func)(void);
169 	bool run_it;
170 } tests[] = {
171 	{ "stpx", test_stpx, false },
172 	{ "spx", test_spx, false },
173 	{ "stap", test_stap, false },
174 	{ "stidp", test_stidp, false },
175 	{ "testblock", test_testblock, false },
176 	{ NULL, NULL, false }
177 };
178 
179 static void parse_intercept_test_args(int argc, char **argv)
180 {
181 	int i, ti;
182 	bool run_all = true;
183 
184 	for (i = 1; i < argc; i++) {
185 		if (!strcmp("-i", argv[i])) {
186 			i++;
187 			if (i >= argc)
188 				report_abort("-i needs a parameter");
189 			nr_iterations = atol(argv[i]);
190 		} else if (!strcmp("-t", argv[i])) {
191 			i++;
192 			if (i >= argc)
193 				report_abort("-t needs a parameter");
194 			time_to_run = atol(argv[i]);
195 		} else for (ti = 0; tests[ti].name != NULL; ti++) {
196 			if (!strcmp(tests[ti].name, argv[i])) {
197 				run_all = false;
198 				tests[ti].run_it = true;
199 				break;
200 			} else if (tests[ti + 1].name == NULL) {
201 				report_abort("Unsupported parameter '%s'",
202 					     argv[i]);
203 			}
204 		}
205 	}
206 
207 	if (run_all) {
208 		for (ti = 0; tests[ti].name != NULL; ti++)
209 			tests[ti].run_it = true;
210 	}
211 }
212 
213 int main(int argc, char **argv)
214 {
215 	uint64_t startclk;
216 	int ti;
217 
218 	parse_intercept_test_args(argc, argv);
219 
220 	if (nr_iterations == 0 && time_to_run == 0)
221 		nr_iterations = 1;
222 
223 	report_prefix_push("intercept");
224 
225 	startclk = get_clock_ms();
226 	for (;;) {
227 		for (ti = 0; tests[ti].name != NULL; ti++) {
228 			report_prefix_push(tests[ti].name);
229 			if (tests[ti].run_it)
230 				tests[ti].func();
231 			report_prefix_pop();
232 		}
233 		if (nr_iterations) {
234 			nr_iterations -= 1;
235 			if (nr_iterations == 0)
236 				break;
237 		}
238 		if (time_to_run) {
239 			if (get_clock_ms() - startclk > time_to_run)
240 				break;
241 		}
242 	}
243 
244 	report_prefix_pop();
245 
246 	return report_summary();
247 }
248