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