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