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