1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Testsuite for NMI: IPIs
4 *
5 * Started by Don Zickus:
6 * (using lib/locking-selftest.c as a guide)
7 *
8 * Copyright (C) 2011 Red Hat, Inc., Don Zickus <dzickus@redhat.com>
9 */
10
11 #include <linux/smp.h>
12 #include <linux/cpumask.h>
13 #include <linux/delay.h>
14 #include <linux/init.h>
15 #include <linux/percpu.h>
16
17 #include <asm/apic.h>
18 #include <asm/nmi.h>
19
20 #define SUCCESS 0
21 #define FAILURE 1
22 #define TIMEOUT 2
23
24 static int __initdata nmi_fail;
25
26 /* check to see if NMI IPIs work on this machine */
27 static DECLARE_BITMAP(nmi_ipi_mask, NR_CPUS) __initdata;
28
29 static int __initdata testcase_total;
30 static int __initdata testcase_successes;
31 static int __initdata unexpected_testcase_failures;
32 static int __initdata unexpected_testcase_unknowns;
33
nmi_unk_cb(unsigned int val,struct pt_regs * regs)34 static int __init nmi_unk_cb(unsigned int val, struct pt_regs *regs)
35 {
36 unexpected_testcase_unknowns++;
37 return NMI_HANDLED;
38 }
39
init_nmi_testsuite(void)40 static void __init init_nmi_testsuite(void)
41 {
42 /* trap all the unknown NMIs we may generate */
43 register_nmi_handler(NMI_UNKNOWN, nmi_unk_cb, 0, "nmi_selftest_unk",
44 __initdata);
45 }
46
cleanup_nmi_testsuite(void)47 static void __init cleanup_nmi_testsuite(void)
48 {
49 unregister_nmi_handler(NMI_UNKNOWN, "nmi_selftest_unk");
50 }
51
test_nmi_ipi_callback(unsigned int val,struct pt_regs * regs)52 static int __init test_nmi_ipi_callback(unsigned int val, struct pt_regs *regs)
53 {
54 int cpu = raw_smp_processor_id();
55
56 if (cpumask_test_and_clear_cpu(cpu, to_cpumask(nmi_ipi_mask)))
57 return NMI_HANDLED;
58
59 return NMI_DONE;
60 }
61
test_nmi_ipi(struct cpumask * mask)62 static void __init test_nmi_ipi(struct cpumask *mask)
63 {
64 unsigned long timeout;
65
66 if (register_nmi_handler(NMI_LOCAL, test_nmi_ipi_callback,
67 NMI_FLAG_FIRST, "nmi_selftest", __initdata)) {
68 nmi_fail = FAILURE;
69 return;
70 }
71
72 /* sync above data before sending NMI */
73 wmb();
74
75 __apic_send_IPI_mask(mask, NMI_VECTOR);
76
77 /* Don't wait longer than a second */
78 timeout = USEC_PER_SEC;
79 while (!cpumask_empty(mask) && --timeout)
80 udelay(1);
81
82 /* What happens if we timeout, do we still unregister?? */
83 unregister_nmi_handler(NMI_LOCAL, "nmi_selftest");
84
85 if (!timeout)
86 nmi_fail = TIMEOUT;
87 return;
88 }
89
remote_ipi(void)90 static void __init remote_ipi(void)
91 {
92 cpumask_copy(to_cpumask(nmi_ipi_mask), cpu_online_mask);
93 cpumask_clear_cpu(smp_processor_id(), to_cpumask(nmi_ipi_mask));
94 if (!cpumask_empty(to_cpumask(nmi_ipi_mask)))
95 test_nmi_ipi(to_cpumask(nmi_ipi_mask));
96 }
97
local_ipi(void)98 static void __init local_ipi(void)
99 {
100 cpumask_clear(to_cpumask(nmi_ipi_mask));
101 cpumask_set_cpu(smp_processor_id(), to_cpumask(nmi_ipi_mask));
102 test_nmi_ipi(to_cpumask(nmi_ipi_mask));
103 }
104
reset_nmi(void)105 static void __init reset_nmi(void)
106 {
107 nmi_fail = 0;
108 }
109
dotest(void (* testcase_fn)(void),int expected)110 static void __init dotest(void (*testcase_fn)(void), int expected)
111 {
112 testcase_fn();
113 /*
114 * Filter out expected failures:
115 */
116 if (nmi_fail != expected) {
117 unexpected_testcase_failures++;
118
119 if (nmi_fail == FAILURE)
120 pr_cont("FAILED |");
121 else if (nmi_fail == TIMEOUT)
122 pr_cont("TIMEOUT|");
123 else
124 pr_cont("ERROR |");
125 dump_stack();
126 } else {
127 testcase_successes++;
128 pr_cont(" ok |");
129 }
130 pr_cont("\n");
131
132 testcase_total++;
133 reset_nmi();
134 }
135
nmi_selftest(void)136 void __init nmi_selftest(void)
137 {
138 init_nmi_testsuite();
139
140 /*
141 * Run the testsuite:
142 */
143 pr_info("----------------\n");
144 pr_info("| NMI testsuite:\n");
145 pr_info("--------------------\n");
146
147 pr_info("%12s:", "remote IPI");
148 dotest(remote_ipi, SUCCESS);
149
150 pr_info("%12s:", "local IPI");
151 dotest(local_ipi, SUCCESS);
152
153 cleanup_nmi_testsuite();
154
155 pr_info("--------------------\n");
156 if (unexpected_testcase_failures) {
157 pr_info("BUG: %3d unexpected failures (out of %3d) - debugging disabled! |\n",
158 unexpected_testcase_failures, testcase_total);
159 } else {
160 pr_info("Good, all %3d testcases passed! |\n",
161 testcase_successes);
162 }
163 pr_info("-----------------------------------------------------------------\n");
164 }
165