1 /* SPDX-License-Identifier: LGPL-2.0-only */
2 /*
3 * Test interrupts
4 *
5 * Copyright 2024 Nicholas Piggin, IBM Corp.
6 */
7 #include <libcflat.h>
8 #include <util.h>
9 #include <migrate.h>
10 #include <alloc.h>
11 #include <asm/setup.h>
12 #include <asm/handlers.h>
13 #include <asm/hcall.h>
14 #include <asm/processor.h>
15 #include <asm/time.h>
16 #include <asm/barrier.h>
17 #include <asm/mmu.h>
18 #include "alloc_phys.h"
19 #include "vmalloc.h"
20
21 static volatile bool got_interrupt;
22 static volatile struct pt_regs recorded_regs;
23
mce_handler(struct pt_regs * regs,void * opaque)24 static void mce_handler(struct pt_regs *regs, void *opaque)
25 {
26 bool *is_fetch = opaque;
27
28 got_interrupt = true;
29 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
30 if (*is_fetch)
31 regs->nip = regs->link;
32 else
33 regs_advance_insn(regs);
34 }
35
fault_handler(struct pt_regs * regs,void * opaque)36 static void fault_handler(struct pt_regs *regs, void *opaque)
37 {
38 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
39 if (regs->trap == 0x400 || regs->trap == 0x480)
40 regs->nip = regs->link;
41 else
42 regs_advance_insn(regs);
43 }
44
test_mce(void)45 static void test_mce(void)
46 {
47 unsigned long addr = -4ULL;
48 uint8_t tmp;
49 bool is_fetch;
50 bool mmu = mmu_enabled();
51
52 report_prefix_push("mce");
53
54 handle_exception(0x200, mce_handler, &is_fetch);
55 handle_exception(0x300, fault_handler, NULL);
56 handle_exception(0x380, fault_handler, NULL);
57 handle_exception(0x400, fault_handler, NULL);
58 handle_exception(0x480, fault_handler, NULL);
59
60 if (mmu)
61 mmu_disable();
62
63 if (machine_is_powernv()) {
64 enable_mcheck();
65 } else {
66 report(mfmsr() & MSR_ME, "pseries machine has MSR[ME]=1");
67 if (!(mfmsr() & MSR_ME)) { /* try to fix it */
68 enable_mcheck();
69 }
70 if (mfmsr() & MSR_ME) {
71 disable_mcheck();
72 report(mfmsr() & MSR_ME, "pseries is unable to change MSR[ME]");
73 if (!(mfmsr() & MSR_ME)) { /* try to fix it */
74 enable_mcheck();
75 }
76 }
77 }
78
79 is_fetch = false;
80 asm volatile("lbz %0,0(%1)" : "=r"(tmp) : "r"(addr));
81
82 /* KVM does not MCE on access outside partition scope */
83 report_kfail(host_is_kvm, got_interrupt, "MCE on access to invalid real address");
84 if (got_interrupt) {
85 report(mfspr(SPR_DAR) == addr, "MCE sets DAR correctly");
86 if (cpu_has_power_mce)
87 report(recorded_regs.msr & (1ULL << 21), "d-side MCE sets SRR1[42]");
88 got_interrupt = false;
89 }
90
91 is_fetch = true;
92 asm volatile("mtctr %0 ; bctrl" :: "r"(addr) : "ctr", "lr");
93 /* KVM does not MCE on access outside partition scope */
94 report_kfail(host_is_kvm, got_interrupt, "MCE on fetch from invalid real address");
95 if (got_interrupt) {
96 report(recorded_regs.nip == addr, "MCE sets SRR0 correctly");
97 if (cpu_has_power_mce)
98 report(!(recorded_regs.msr & (1ULL << 21)), "i-side MCE clears SRR1[42]");
99 got_interrupt = false;
100 }
101
102 if (mmu)
103 mmu_enable(NULL);
104
105 handle_exception(0x200, NULL, NULL);
106 handle_exception(0x300, NULL, NULL);
107 handle_exception(0x380, NULL, NULL);
108 handle_exception(0x400, NULL, NULL);
109 handle_exception(0x480, NULL, NULL);
110
111 report_prefix_pop();
112 }
113
dside_handler(struct pt_regs * regs,void * data)114 static void dside_handler(struct pt_regs *regs, void *data)
115 {
116 got_interrupt = true;
117 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
118 regs_advance_insn(regs);
119 }
120
iside_handler(struct pt_regs * regs,void * data)121 static void iside_handler(struct pt_regs *regs, void *data)
122 {
123 got_interrupt = true;
124 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
125 regs->nip = regs->link;
126 }
127
test_dseg_nommu(void)128 static void test_dseg_nommu(void)
129 {
130 uint64_t msr, tmp;
131
132 report_prefix_push("dseg");
133
134 /* Some HV start in radix mode and need 0x300 */
135 handle_exception(0x300, &dside_handler, NULL);
136 handle_exception(0x380, &dside_handler, NULL);
137
138 asm volatile(
139 " mfmsr %0 \n \
140 ori %1,%0,%2 \n \
141 mtmsrd %1 \n \
142 lbz %1,0(0) \n \
143 mtmsrd %0 "
144 : "=r"(msr), "=r"(tmp) : "i"(MSR_DR): "memory");
145
146 report(got_interrupt, "interrupt on NULL dereference");
147 got_interrupt = false;
148
149 handle_exception(0x300, NULL, NULL);
150 handle_exception(0x380, NULL, NULL);
151
152 report_prefix_pop();
153 }
154
test_mmu(void)155 static void test_mmu(void)
156 {
157 uint64_t tmp, addr;
158 phys_addr_t base, top;
159
160 if (!mmu_enabled()) {
161 test_dseg_nommu();
162 return;
163 }
164
165 phys_alloc_get_unused(&base, &top);
166
167 report_prefix_push("dsi");
168 addr = top + PAGE_SIZE;
169 handle_exception(0x300, &dside_handler, NULL);
170 asm volatile("lbz %0,0(%1)" : "=r"(tmp) : "r"(addr));
171 report(got_interrupt, "dsi on out of range dereference");
172 report(mfspr(SPR_DAR) == addr, "DAR set correctly");
173 report(mfspr(SPR_DSISR) & (1ULL << 30), "DSISR set correctly");
174 got_interrupt = false;
175 handle_exception(0x300, NULL, NULL);
176 report_prefix_pop();
177
178 report_prefix_push("dseg");
179 addr = -4ULL;
180 handle_exception(0x380, &dside_handler, NULL);
181 asm volatile("lbz %0,0(%1)" : "=r"(tmp) : "r"(addr));
182 report(got_interrupt, "dseg on out of range dereference");
183 report(mfspr(SPR_DAR) == addr, "DAR set correctly");
184 got_interrupt = false;
185 handle_exception(0x380, NULL, NULL);
186 report_prefix_pop();
187
188 report_prefix_push("isi");
189 addr = top + PAGE_SIZE;
190 handle_exception(0x400, &iside_handler, NULL);
191 asm volatile("mtctr %0 ; bctrl" :: "r"(addr) : "ctr", "lr");
192 report(got_interrupt, "isi on out of range fetch");
193 report(recorded_regs.nip == addr, "SRR0 set correctly");
194 report(recorded_regs.msr & (1ULL << 30), "SRR1 set correctly");
195 got_interrupt = false;
196 handle_exception(0x400, NULL, NULL);
197 report_prefix_pop();
198
199 report_prefix_push("iseg");
200 addr = -4ULL;
201 handle_exception(0x480, &iside_handler, NULL);
202 asm volatile("mtctr %0 ; bctrl" :: "r"(addr) : "ctr", "lr");
203 report(got_interrupt, "isi on out of range fetch");
204 report(recorded_regs.nip == addr, "SRR0 set correctly");
205 got_interrupt = false;
206 handle_exception(0x480, NULL, NULL);
207 report_prefix_pop();
208 }
209
dec_handler(struct pt_regs * regs,void * data)210 static void dec_handler(struct pt_regs *regs, void *data)
211 {
212 got_interrupt = true;
213 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
214 regs->msr &= ~MSR_EE;
215 }
216
test_dec(void)217 static void test_dec(void)
218 {
219 uint64_t msr;
220 uint64_t tb;
221
222 report_prefix_push("decrementer");
223
224 handle_exception(0x900, &dec_handler, NULL);
225
226 asm volatile(
227 " mtdec %1 \n \
228 mfmsr %0 \n \
229 ori %0,%0,%2 \n \
230 mtmsrd %0,1 "
231 : "=r"(msr) : "r"(10000), "i"(MSR_EE): "memory");
232
233 tb = get_tb();
234 while (!got_interrupt) {
235 if (get_tb() - tb > tb_hz * 5)
236 break; /* timeout 5s */
237 }
238
239 report(got_interrupt, "interrupt on decrementer underflow");
240 got_interrupt = false;
241
242 handle_exception(0x900, NULL, NULL);
243
244 if (!machine_is_powernv())
245 goto done; /* Skip HV tests */
246
247 handle_exception(0x980, &dec_handler, NULL);
248
249 mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_HDICE);
250 asm volatile(
251 " mtspr 0x136,%1 \n \
252 mtdec %3 \n \
253 mfmsr %0 \n \
254 ori %0,%0,%2 \n \
255 mtmsrd %0,1 "
256 : "=r"(msr) : "r"(10000), "i"(MSR_EE), "r"(0x7fffffff): "memory");
257
258 tb = get_tb();
259 while (!got_interrupt) {
260 if (get_tb() - tb > tb_hz * 5)
261 break; /* timeout 5s */
262 }
263
264 mtspr(SPR_LPCR, mfspr(SPR_LPCR) & ~LPCR_HDICE);
265
266 report(got_interrupt, "interrupt on hdecrementer underflow");
267 got_interrupt = false;
268
269 handle_exception(0x980, NULL, NULL);
270
271 done:
272 report_prefix_pop();
273 }
274
275
276 static volatile uint64_t recorded_heir;
277
heai_handler(struct pt_regs * regs,void * data)278 static void heai_handler(struct pt_regs *regs, void *data)
279 {
280 got_interrupt = true;
281 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
282 regs_advance_insn(regs);
283 if (cpu_has_heai)
284 recorded_heir = mfspr(SPR_HEIR);
285 }
286
program_handler(struct pt_regs * regs,void * data)287 static void program_handler(struct pt_regs *regs, void *data)
288 {
289 got_interrupt = true;
290 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
291 regs_advance_insn(regs);
292 }
293
294 /*
295 * This tests invalid instruction handling. powernv (HV) should take an
296 * HEAI interrupt with the HEIR SPR set to the instruction image. pseries
297 * (guest) should take a program interrupt. CPUs which support prefix
298 * should report prefix instruction in (H)SRR1[34].
299 */
test_illegal(void)300 static void test_illegal(void)
301 {
302 report_prefix_push("illegal instruction");
303
304 if (machine_is_powernv()) {
305 handle_exception(0xe40, &heai_handler, NULL);
306 } else {
307 handle_exception(0x700, &program_handler, NULL);
308 }
309
310 asm volatile(".long 0x12345678" ::: "memory");
311 report(got_interrupt, "interrupt on invalid instruction");
312 got_interrupt = false;
313 if (cpu_has_heai)
314 report(recorded_heir == 0x12345678, "HEIR: 0x%08lx", recorded_heir);
315 report(!regs_is_prefix(&recorded_regs), "(H)SRR1 prefix bit clear");
316
317 if (cpu_has_prefix) {
318 asm volatile(".balign 8 ; .long 0x04000123; .long 0x00badc0d");
319 report(got_interrupt, "interrupt on invalid prefix instruction");
320 got_interrupt = false;
321 if (cpu_has_heai)
322 report(recorded_heir == 0x0400012300badc0d, "HEIR: 0x%08lx", recorded_heir);
323 report(regs_is_prefix(&recorded_regs), "(H)SRR1 prefix bit set");
324 }
325
326 handle_exception(0xe40, NULL, NULL);
327 handle_exception(0x700, NULL, NULL);
328
329 report_prefix_pop();
330 }
331
dec_ignore_handler(struct pt_regs * regs,void * data)332 static void dec_ignore_handler(struct pt_regs *regs, void *data)
333 {
334 mtspr(SPR_DEC, 0x7fffffff);
335 }
336
test_privileged(void)337 static void test_privileged(void)
338 {
339 unsigned long msr;
340
341 if (!mmu_enabled())
342 return;
343
344 report_prefix_push("privileged instruction");
345
346 handle_exception(0x700, &program_handler, NULL);
347 handle_exception(0x900, &dec_ignore_handler, NULL);
348 enter_usermode();
349 asm volatile("mfmsr %0" : "=r"(msr) :: "memory");
350 exit_usermode();
351 report(got_interrupt, "interrupt on privileged instruction");
352 got_interrupt = false;
353 handle_exception(0x900, NULL, NULL);
354 handle_exception(0x700, NULL, NULL);
355
356 report_prefix_pop();
357 }
358
sc_handler(struct pt_regs * regs,void * data)359 static void sc_handler(struct pt_regs *regs, void *data)
360 {
361 got_interrupt = true;
362 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
363 }
364
test_sc(void)365 static void test_sc(void)
366 {
367 report_prefix_push("syscall");
368
369 handle_exception(0xc00, &sc_handler, NULL);
370
371 asm volatile("sc 0" ::: "memory");
372
373 report(got_interrupt, "interrupt on sc 0 instruction");
374 got_interrupt = false;
375 if (cpu_has_sc_lev)
376 report(((recorded_regs.msr >> 20) & 0x3) == 0, "SRR1 set LEV=0");
377 if (machine_is_powernv()) {
378 asm volatile("sc 1" ::: "memory");
379
380 report(got_interrupt, "interrupt on sc 1 instruction");
381 got_interrupt = false;
382 if (cpu_has_sc_lev)
383 report(((recorded_regs.msr >> 20) & 0x3) == 1, "SRR1 set LEV=1");
384 }
385
386 handle_exception(0xc00, NULL, NULL);
387
388 report_prefix_pop();
389 }
390
391
trace_handler(struct pt_regs * regs,void * data)392 static void trace_handler(struct pt_regs *regs, void *data)
393 {
394 got_interrupt = true;
395 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
396 regs->msr &= ~(MSR_SE | MSR_BE);
397 }
398
program_trace_handler(struct pt_regs * regs,void * data)399 static void program_trace_handler(struct pt_regs *regs, void *data)
400 {
401 regs->msr &= ~(MSR_SE | MSR_BE);
402 regs->nip += 4;
403 }
404
405 extern char trace_insn[];
406 extern char trace_insn2[];
407 extern char trace_insn3[];
408 extern char trace_rfid[];
409
test_trace(void)410 static void test_trace(void)
411 {
412 unsigned long msr;
413
414 report_prefix_push("trace");
415
416 handle_exception(0xd00, &trace_handler, NULL);
417
418 msr = mfmsr() | MSR_SE;
419 asm volatile(
420 " mtmsr %0 \n"
421 ".global trace_insn \n"
422 "trace_insn: \n"
423 " nop \n"
424 : : "r"(msr) : "memory");
425
426 report(got_interrupt, "interrupt on single step");
427 got_interrupt = false;
428 report(recorded_regs.nip == (unsigned long)trace_insn + 4,
429 "single step interrupt at the correct address");
430 if (cpu_has_siar)
431 report(mfspr(SPR_SIAR) == (unsigned long)trace_insn,
432 "single step recorded SIAR at the correct address");
433
434 msr = mfmsr() | MSR_SE;
435 asm volatile(
436 " mtmsr %0 \n"
437 ".global trace_insn2 \n"
438 "trace_insn2: \n"
439 " b 1f \n"
440 " nop \n"
441 "1: \n"
442 : : "r"(msr) : "memory");
443
444 report(got_interrupt, "interrupt on single step branch");
445 got_interrupt = false;
446 report(recorded_regs.nip == (unsigned long)trace_insn2 + 8,
447 "single step interrupt at the correct address");
448 if (cpu_has_siar)
449 report(mfspr(SPR_SIAR) == (unsigned long)trace_insn2,
450 "single step recorded SIAR at the correct address");
451
452 msr = mfmsr() | MSR_BE;
453 asm volatile(
454 " mtmsr %0 \n"
455 ".global trace_insn3 \n"
456 "trace_insn3: \n"
457 " nop \n"
458 " b 1f \n"
459 " nop \n"
460 "1: \n"
461 : : "r"(msr) : "memory");
462
463 report(got_interrupt, "interrupt on branch trace");
464 got_interrupt = false;
465 report(recorded_regs.nip == (unsigned long)trace_insn3 + 12,
466 "branch trace interrupt at the correct address");
467 if (cpu_has_siar)
468 report(mfspr(SPR_SIAR) == (unsigned long)trace_insn3 + 4,
469 "branch trace recorded SIAR at the correct address");
470
471 handle_exception(0x700, &program_trace_handler, NULL);
472 msr = mfmsr() | MSR_SE;
473 asm volatile(
474 " mtmsr %0 \n"
475 " trap \n"
476 : : "r"(msr) : "memory");
477
478 report(!got_interrupt, "no interrupt on single step trap");
479 got_interrupt = false;
480 handle_exception(0x700, NULL, NULL);
481
482 msr = mfmsr() | MSR_SE;
483 mtspr(SPR_SRR0, (unsigned long)trace_rfid);
484 mtspr(SPR_SRR1, mfmsr());
485 asm volatile(
486 " mtmsr %0 \n"
487 " rfid \n"
488 ".global trace_rfid \n"
489 "trace_rfid: \n"
490 : : "r"(msr) : "memory");
491
492 report(!got_interrupt, "no interrupt on single step rfid");
493 got_interrupt = false;
494 handle_exception(0xd00, NULL, NULL);
495
496 report_prefix_pop();
497 }
498
499
main(int argc,char ** argv)500 int main(int argc, char **argv)
501 {
502 report_prefix_push("interrupts");
503
504 if (vm_available())
505 setup_vm();
506
507 if (cpu_has_power_mce)
508 test_mce();
509 test_mmu();
510 test_illegal();
511 test_privileged();
512 test_dec();
513 test_sc();
514 test_trace();
515
516 report_prefix_pop();
517
518 return report_summary();
519 }
520