1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Machine check exception handling CPU-side for power7 and power8 |
4 | * |
5 | * Copyright 2013 IBM Corporation |
6 | * Author: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> |
7 | */ |
8 | |
9 | #undef DEBUG |
10 | #define pr_fmt(fmt) "mce_power: " fmt |
11 | |
12 | #include <linux/types.h> |
13 | #include <linux/ptrace.h> |
14 | #include <linux/extable.h> |
15 | #include <linux/pgtable.h> |
16 | #include <asm/mmu.h> |
17 | #include <asm/mce.h> |
18 | #include <asm/machdep.h> |
19 | #include <asm/pte-walk.h> |
20 | #include <asm/sstep.h> |
21 | #include <asm/exception-64s.h> |
22 | #include <asm/extable.h> |
23 | #include <asm/inst.h> |
24 | |
25 | /* |
26 | * Convert an address related to an mm to a PFN. NOTE: we are in real |
27 | * mode, we could potentially race with page table updates. |
28 | */ |
29 | unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr) |
30 | { |
31 | pte_t *ptep, pte; |
32 | unsigned int shift; |
33 | unsigned long pfn, flags; |
34 | struct mm_struct *mm; |
35 | |
36 | if (user_mode(regs)) |
37 | mm = current->mm; |
38 | else |
39 | mm = &init_mm; |
40 | |
41 | local_irq_save(flags); |
42 | ptep = __find_linux_pte(mm->pgd, addr, NULL, &shift); |
43 | if (!ptep) { |
44 | pfn = ULONG_MAX; |
45 | goto out; |
46 | } |
47 | pte = READ_ONCE(*ptep); |
48 | |
49 | if (!pte_present(a: pte) || pte_special(pte)) { |
50 | pfn = ULONG_MAX; |
51 | goto out; |
52 | } |
53 | |
54 | if (shift <= PAGE_SHIFT) |
55 | pfn = pte_pfn(pte); |
56 | else { |
57 | unsigned long rpnmask = (1ul << shift) - PAGE_SIZE; |
58 | pfn = pte_pfn(pte: __pte(val: pte_val(pte) | (addr & rpnmask))); |
59 | } |
60 | out: |
61 | local_irq_restore(flags); |
62 | return pfn; |
63 | } |
64 | |
65 | static bool mce_in_guest(void) |
66 | { |
67 | #ifdef CONFIG_KVM_BOOK3S_HANDLER |
68 | /* |
69 | * If machine check is hit when in guest context or low level KVM |
70 | * code, avoid looking up any translations or making any attempts |
71 | * to recover, just record the event and pass to KVM. |
72 | */ |
73 | if (get_paca()->kvm_hstate.in_guest) |
74 | return true; |
75 | #endif |
76 | return false; |
77 | } |
78 | |
79 | /* flush SLBs and reload */ |
80 | #ifdef CONFIG_PPC_64S_HASH_MMU |
81 | void flush_and_reload_slb(void) |
82 | { |
83 | if (early_radix_enabled()) |
84 | return; |
85 | |
86 | /* Invalidate all SLBs */ |
87 | slb_flush_all_realmode(); |
88 | |
89 | /* |
90 | * This probably shouldn't happen, but it may be possible it's |
91 | * called in early boot before SLB shadows are allocated. |
92 | */ |
93 | if (!get_slb_shadow()) |
94 | return; |
95 | |
96 | slb_restore_bolted_realmode(); |
97 | } |
98 | #endif |
99 | |
100 | void flush_erat(void) |
101 | { |
102 | #ifdef CONFIG_PPC_64S_HASH_MMU |
103 | if (!early_cpu_has_feature(CPU_FTR_ARCH_300)) { |
104 | flush_and_reload_slb(); |
105 | return; |
106 | } |
107 | #endif |
108 | asm volatile(PPC_ISA_3_0_INVALIDATE_ERAT : : :"memory" ); |
109 | } |
110 | |
111 | #define MCE_FLUSH_SLB 1 |
112 | #define MCE_FLUSH_TLB 2 |
113 | #define MCE_FLUSH_ERAT 3 |
114 | |
115 | static int mce_flush(int what) |
116 | { |
117 | #ifdef CONFIG_PPC_64S_HASH_MMU |
118 | if (what == MCE_FLUSH_SLB) { |
119 | flush_and_reload_slb(); |
120 | return 1; |
121 | } |
122 | #endif |
123 | if (what == MCE_FLUSH_ERAT) { |
124 | flush_erat(); |
125 | return 1; |
126 | } |
127 | if (what == MCE_FLUSH_TLB) { |
128 | tlbiel_all(); |
129 | return 1; |
130 | } |
131 | |
132 | return 0; |
133 | } |
134 | |
135 | #define SRR1_MC_LOADSTORE(srr1) ((srr1) & PPC_BIT(42)) |
136 | |
137 | struct mce_ierror_table { |
138 | unsigned long srr1_mask; |
139 | unsigned long srr1_value; |
140 | bool nip_valid; /* nip is a valid indicator of faulting address */ |
141 | unsigned int error_type; |
142 | unsigned int error_subtype; |
143 | unsigned int error_class; |
144 | unsigned int initiator; |
145 | unsigned int severity; |
146 | bool sync_error; |
147 | }; |
148 | |
149 | static const struct mce_ierror_table mce_p7_ierror_table[] = { |
150 | { 0x00000000001c0000, 0x0000000000040000, true, |
151 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
152 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
153 | { 0x00000000001c0000, 0x0000000000080000, true, |
154 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
155 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
156 | { 0x00000000001c0000, 0x00000000000c0000, true, |
157 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
158 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
159 | { 0x00000000001c0000, 0x0000000000100000, true, |
160 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_INDETERMINATE, /* BOTH */ |
161 | MCE_ECLASS_SOFT_INDETERMINATE, |
162 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
163 | { 0x00000000001c0000, 0x0000000000140000, true, |
164 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
165 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
166 | { 0x00000000001c0000, 0x0000000000180000, true, |
167 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_HARDWARE, |
168 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
169 | { 0x00000000001c0000, 0x00000000001c0000, true, |
170 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
171 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
172 | { 0, 0, 0, 0, 0, 0, 0 } }; |
173 | |
174 | static const struct mce_ierror_table mce_p8_ierror_table[] = { |
175 | { 0x00000000081c0000, 0x0000000000040000, true, |
176 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
177 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
178 | { 0x00000000081c0000, 0x0000000000080000, true, |
179 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
180 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
181 | { 0x00000000081c0000, 0x00000000000c0000, true, |
182 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
183 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
184 | { 0x00000000081c0000, 0x0000000000100000, true, |
185 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
186 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
187 | { 0x00000000081c0000, 0x0000000000140000, true, |
188 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
189 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
190 | { 0x00000000081c0000, 0x0000000000180000, true, |
191 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, |
192 | MCE_ECLASS_HARDWARE, |
193 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
194 | { 0x00000000081c0000, 0x00000000001c0000, true, |
195 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
196 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
197 | { 0x00000000081c0000, 0x0000000008000000, true, |
198 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_IFETCH_TIMEOUT, MCE_ECLASS_HARDWARE, |
199 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
200 | { 0x00000000081c0000, 0x0000000008040000, true, |
201 | MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_PAGE_TABLE_WALK_IFETCH_TIMEOUT, |
202 | MCE_ECLASS_HARDWARE, |
203 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
204 | { 0, 0, 0, 0, 0, 0, 0 } }; |
205 | |
206 | static const struct mce_ierror_table mce_p9_ierror_table[] = { |
207 | { 0x00000000081c0000, 0x0000000000040000, true, |
208 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
209 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
210 | { 0x00000000081c0000, 0x0000000000080000, true, |
211 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
212 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
213 | { 0x00000000081c0000, 0x00000000000c0000, true, |
214 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
215 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
216 | { 0x00000000081c0000, 0x0000000000100000, true, |
217 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
218 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
219 | { 0x00000000081c0000, 0x0000000000140000, true, |
220 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
221 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
222 | { 0x00000000081c0000, 0x0000000000180000, true, |
223 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_HARDWARE, |
224 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
225 | { 0x00000000081c0000, 0x00000000001c0000, true, |
226 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_IFETCH_FOREIGN, MCE_ECLASS_SOFTWARE, |
227 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
228 | { 0x00000000081c0000, 0x0000000008000000, true, |
229 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_IFETCH_TIMEOUT, MCE_ECLASS_HARDWARE, |
230 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
231 | { 0x00000000081c0000, 0x0000000008040000, true, |
232 | MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_PAGE_TABLE_WALK_IFETCH_TIMEOUT, |
233 | MCE_ECLASS_HARDWARE, |
234 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
235 | { 0x00000000081c0000, 0x00000000080c0000, true, |
236 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_IFETCH, MCE_ECLASS_SOFTWARE, |
237 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
238 | { 0x00000000081c0000, 0x0000000008100000, true, |
239 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_SOFTWARE, |
240 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
241 | { 0x00000000081c0000, 0x0000000008140000, false, |
242 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_STORE, MCE_ECLASS_HARDWARE, |
243 | MCE_INITIATOR_CPU, MCE_SEV_FATAL, false }, /* ASYNC is fatal */ |
244 | { 0x00000000081c0000, 0x0000000008180000, false, |
245 | MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_STORE_TIMEOUT, |
246 | MCE_INITIATOR_CPU, MCE_SEV_FATAL, false }, /* ASYNC is fatal */ |
247 | { 0x00000000081c0000, 0x00000000081c0000, true, MCE_ECLASS_HARDWARE, |
248 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH_FOREIGN, |
249 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
250 | { 0, 0, 0, 0, 0, 0, 0 } }; |
251 | |
252 | static const struct mce_ierror_table mce_p10_ierror_table[] = { |
253 | { 0x00000000081c0000, 0x0000000000040000, true, |
254 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
255 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
256 | { 0x00000000081c0000, 0x0000000000080000, true, |
257 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
258 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
259 | { 0x00000000081c0000, 0x00000000000c0000, true, |
260 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
261 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
262 | { 0x00000000081c0000, 0x0000000000100000, true, |
263 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
264 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
265 | { 0x00000000081c0000, 0x0000000000140000, true, |
266 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
267 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
268 | { 0x00000000081c0000, 0x0000000000180000, true, |
269 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_HARDWARE, |
270 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
271 | { 0x00000000081c0000, 0x00000000001c0000, true, |
272 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_IFETCH_FOREIGN, MCE_ECLASS_SOFTWARE, |
273 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
274 | { 0x00000000081c0000, 0x0000000008080000, true, |
275 | MCE_ERROR_TYPE_USER,MCE_USER_ERROR_SCV, MCE_ECLASS_SOFTWARE, |
276 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
277 | { 0x00000000081c0000, 0x00000000080c0000, true, |
278 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_IFETCH, MCE_ECLASS_SOFTWARE, |
279 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
280 | { 0x00000000081c0000, 0x0000000008100000, true, |
281 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_SOFTWARE, |
282 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
283 | { 0x00000000081c0000, 0x0000000008140000, false, |
284 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_STORE, MCE_ECLASS_HARDWARE, |
285 | MCE_INITIATOR_CPU, MCE_SEV_FATAL, false }, /* ASYNC is fatal */ |
286 | { 0x00000000081c0000, 0x00000000081c0000, true, MCE_ECLASS_HARDWARE, |
287 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH_FOREIGN, |
288 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
289 | { 0, 0, 0, 0, 0, 0, 0 } }; |
290 | |
291 | struct mce_derror_table { |
292 | unsigned long dsisr_value; |
293 | bool dar_valid; /* dar is a valid indicator of faulting address */ |
294 | unsigned int error_type; |
295 | unsigned int error_subtype; |
296 | unsigned int error_class; |
297 | unsigned int initiator; |
298 | unsigned int severity; |
299 | bool sync_error; |
300 | }; |
301 | |
302 | static const struct mce_derror_table mce_p7_derror_table[] = { |
303 | { 0x00008000, false, |
304 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE, |
305 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
306 | { 0x00004000, true, |
307 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE, |
308 | MCE_ECLASS_HARDWARE, |
309 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
310 | { 0x00000800, true, |
311 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
312 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
313 | { 0x00000400, true, |
314 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
315 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
316 | { 0x00000080, true, |
317 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
318 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
319 | { 0x00000100, true, |
320 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
321 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
322 | { 0x00000040, true, |
323 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_INDETERMINATE, /* BOTH */ |
324 | MCE_ECLASS_HARD_INDETERMINATE, |
325 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
326 | { 0, false, 0, 0, 0, 0, 0 } }; |
327 | |
328 | static const struct mce_derror_table mce_p8_derror_table[] = { |
329 | { 0x00008000, false, |
330 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE, |
331 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
332 | { 0x00004000, true, |
333 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE, |
334 | MCE_ECLASS_HARDWARE, |
335 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
336 | { 0x00002000, true, |
337 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_LOAD_TIMEOUT, MCE_ECLASS_HARDWARE, |
338 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
339 | { 0x00001000, true, |
340 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_PAGE_TABLE_WALK_LOAD_STORE_TIMEOUT, |
341 | MCE_ECLASS_HARDWARE, |
342 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
343 | { 0x00000800, true, |
344 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
345 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
346 | { 0x00000400, true, |
347 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
348 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
349 | { 0x00000200, true, |
350 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, /* SECONDARY ERAT */ |
351 | MCE_ECLASS_SOFT_INDETERMINATE, |
352 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
353 | { 0x00000080, true, |
354 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, /* Before PARITY */ |
355 | MCE_ECLASS_SOFT_INDETERMINATE, |
356 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
357 | { 0x00000100, true, |
358 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
359 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
360 | { 0, false, 0, 0, 0, 0, 0 } }; |
361 | |
362 | static const struct mce_derror_table mce_p9_derror_table[] = { |
363 | { 0x00008000, false, |
364 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE, |
365 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
366 | { 0x00004000, true, |
367 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE, |
368 | MCE_ECLASS_HARDWARE, |
369 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
370 | { 0x00002000, true, |
371 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_LOAD_TIMEOUT, MCE_ECLASS_HARDWARE, |
372 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
373 | { 0x00001000, true, |
374 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_PAGE_TABLE_WALK_LOAD_STORE_TIMEOUT, |
375 | MCE_ECLASS_HARDWARE, |
376 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
377 | { 0x00000800, true, |
378 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
379 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
380 | { 0x00000400, true, |
381 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
382 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
383 | { 0x00000200, false, |
384 | MCE_ERROR_TYPE_USER, MCE_USER_ERROR_TLBIE, MCE_ECLASS_SOFTWARE, |
385 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
386 | { 0x00000080, true, |
387 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, /* Before PARITY */ |
388 | MCE_ECLASS_SOFT_INDETERMINATE, |
389 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
390 | { 0x00000100, true, |
391 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
392 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
393 | { 0x00000040, true, |
394 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_LOAD, MCE_ECLASS_HARDWARE, |
395 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
396 | { 0x00000020, false, |
397 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE, |
398 | MCE_ECLASS_HARDWARE, |
399 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
400 | { 0x00000010, false, |
401 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE_FOREIGN, |
402 | MCE_ECLASS_HARDWARE, |
403 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
404 | { 0x00000008, false, |
405 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_LOAD_STORE_FOREIGN, MCE_ECLASS_HARDWARE, |
406 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
407 | { 0, false, 0, 0, 0, 0, 0 } }; |
408 | |
409 | static const struct mce_derror_table mce_p10_derror_table[] = { |
410 | { 0x00008000, false, |
411 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE, |
412 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
413 | { 0x00004000, true, |
414 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE, |
415 | MCE_ECLASS_HARDWARE, |
416 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
417 | { 0x00000800, true, |
418 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
419 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
420 | { 0x00000400, true, |
421 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
422 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
423 | { 0x00000200, false, |
424 | MCE_ERROR_TYPE_USER, MCE_USER_ERROR_TLBIE, MCE_ECLASS_SOFTWARE, |
425 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
426 | { 0x00000080, true, |
427 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, /* Before PARITY */ |
428 | MCE_ECLASS_SOFT_INDETERMINATE, |
429 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
430 | { 0x00000100, true, |
431 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
432 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
433 | { 0x00000040, true, |
434 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_LOAD, MCE_ECLASS_HARDWARE, |
435 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
436 | { 0x00000020, false, |
437 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE, |
438 | MCE_ECLASS_HARDWARE, |
439 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
440 | { 0x00000010, false, |
441 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE_FOREIGN, |
442 | MCE_ECLASS_HARDWARE, |
443 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
444 | { 0x00000008, false, |
445 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_LOAD_STORE_FOREIGN, MCE_ECLASS_HARDWARE, |
446 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
447 | { 0, false, 0, 0, 0, 0, 0 } }; |
448 | |
449 | static int mce_find_instr_ea_and_phys(struct pt_regs *regs, uint64_t *addr, |
450 | uint64_t *phys_addr) |
451 | { |
452 | /* |
453 | * Carefully look at the NIP to determine |
454 | * the instruction to analyse. Reading the NIP |
455 | * in real-mode is tricky and can lead to recursive |
456 | * faults |
457 | */ |
458 | ppc_inst_t instr; |
459 | unsigned long pfn, instr_addr; |
460 | struct instruction_op op; |
461 | struct pt_regs tmp = *regs; |
462 | |
463 | pfn = addr_to_pfn(regs, addr: regs->nip); |
464 | if (pfn != ULONG_MAX) { |
465 | instr_addr = (pfn << PAGE_SHIFT) + (regs->nip & ~PAGE_MASK); |
466 | instr = ppc_inst_read((u32 *)instr_addr); |
467 | if (!analyse_instr(&op, &tmp, instr)) { |
468 | pfn = addr_to_pfn(regs, addr: op.ea); |
469 | *addr = op.ea; |
470 | *phys_addr = (pfn << PAGE_SHIFT); |
471 | return 0; |
472 | } |
473 | /* |
474 | * analyse_instr() might fail if the instruction |
475 | * is not a load/store, although this is unexpected |
476 | * for load/store errors or if we got the NIP |
477 | * wrong |
478 | */ |
479 | } |
480 | *addr = 0; |
481 | return -1; |
482 | } |
483 | |
484 | static int mce_handle_ierror(struct pt_regs *regs, unsigned long srr1, |
485 | const struct mce_ierror_table table[], |
486 | struct mce_error_info *mce_err, uint64_t *addr, |
487 | uint64_t *phys_addr) |
488 | { |
489 | int handled = 0; |
490 | int i; |
491 | |
492 | *addr = 0; |
493 | |
494 | for (i = 0; table[i].srr1_mask; i++) { |
495 | if ((srr1 & table[i].srr1_mask) != table[i].srr1_value) |
496 | continue; |
497 | |
498 | if (!mce_in_guest()) { |
499 | /* attempt to correct the error */ |
500 | switch (table[i].error_type) { |
501 | case MCE_ERROR_TYPE_SLB: |
502 | #ifdef CONFIG_PPC_64S_HASH_MMU |
503 | if (local_paca->in_mce == 1) |
504 | slb_save_contents(local_paca->mce_faulty_slbs); |
505 | #endif |
506 | handled = mce_flush(MCE_FLUSH_SLB); |
507 | break; |
508 | case MCE_ERROR_TYPE_ERAT: |
509 | handled = mce_flush(MCE_FLUSH_ERAT); |
510 | break; |
511 | case MCE_ERROR_TYPE_TLB: |
512 | handled = mce_flush(MCE_FLUSH_TLB); |
513 | break; |
514 | } |
515 | } |
516 | |
517 | /* now fill in mce_error_info */ |
518 | mce_err->error_type = table[i].error_type; |
519 | mce_err->error_class = table[i].error_class; |
520 | switch (table[i].error_type) { |
521 | case MCE_ERROR_TYPE_UE: |
522 | mce_err->u.ue_error_type = table[i].error_subtype; |
523 | break; |
524 | case MCE_ERROR_TYPE_SLB: |
525 | mce_err->u.slb_error_type = table[i].error_subtype; |
526 | break; |
527 | case MCE_ERROR_TYPE_ERAT: |
528 | mce_err->u.erat_error_type = table[i].error_subtype; |
529 | break; |
530 | case MCE_ERROR_TYPE_TLB: |
531 | mce_err->u.tlb_error_type = table[i].error_subtype; |
532 | break; |
533 | case MCE_ERROR_TYPE_USER: |
534 | mce_err->u.user_error_type = table[i].error_subtype; |
535 | break; |
536 | case MCE_ERROR_TYPE_RA: |
537 | mce_err->u.ra_error_type = table[i].error_subtype; |
538 | break; |
539 | case MCE_ERROR_TYPE_LINK: |
540 | mce_err->u.link_error_type = table[i].error_subtype; |
541 | break; |
542 | } |
543 | mce_err->sync_error = table[i].sync_error; |
544 | mce_err->severity = table[i].severity; |
545 | mce_err->initiator = table[i].initiator; |
546 | if (table[i].nip_valid && !mce_in_guest()) { |
547 | *addr = regs->nip; |
548 | if (mce_err->sync_error && |
549 | table[i].error_type == MCE_ERROR_TYPE_UE) { |
550 | unsigned long pfn; |
551 | |
552 | if (get_paca()->in_mce < MAX_MCE_DEPTH) { |
553 | pfn = addr_to_pfn(regs, addr: regs->nip); |
554 | if (pfn != ULONG_MAX) { |
555 | *phys_addr = |
556 | (pfn << PAGE_SHIFT); |
557 | } |
558 | } |
559 | } |
560 | } |
561 | return handled; |
562 | } |
563 | |
564 | mce_err->error_type = MCE_ERROR_TYPE_UNKNOWN; |
565 | mce_err->error_class = MCE_ECLASS_UNKNOWN; |
566 | mce_err->severity = MCE_SEV_SEVERE; |
567 | mce_err->initiator = MCE_INITIATOR_CPU; |
568 | mce_err->sync_error = true; |
569 | |
570 | return 0; |
571 | } |
572 | |
573 | static int mce_handle_derror(struct pt_regs *regs, |
574 | const struct mce_derror_table table[], |
575 | struct mce_error_info *mce_err, uint64_t *addr, |
576 | uint64_t *phys_addr) |
577 | { |
578 | uint64_t dsisr = regs->dsisr; |
579 | int handled = 0; |
580 | int found = 0; |
581 | int i; |
582 | |
583 | *addr = 0; |
584 | |
585 | for (i = 0; table[i].dsisr_value; i++) { |
586 | if (!(dsisr & table[i].dsisr_value)) |
587 | continue; |
588 | |
589 | if (!mce_in_guest()) { |
590 | /* attempt to correct the error */ |
591 | switch (table[i].error_type) { |
592 | case MCE_ERROR_TYPE_SLB: |
593 | #ifdef CONFIG_PPC_64S_HASH_MMU |
594 | if (local_paca->in_mce == 1) |
595 | slb_save_contents(local_paca->mce_faulty_slbs); |
596 | #endif |
597 | if (mce_flush(MCE_FLUSH_SLB)) |
598 | handled = 1; |
599 | break; |
600 | case MCE_ERROR_TYPE_ERAT: |
601 | if (mce_flush(MCE_FLUSH_ERAT)) |
602 | handled = 1; |
603 | break; |
604 | case MCE_ERROR_TYPE_TLB: |
605 | if (mce_flush(MCE_FLUSH_TLB)) |
606 | handled = 1; |
607 | break; |
608 | } |
609 | } |
610 | |
611 | /* |
612 | * Attempt to handle multiple conditions, but only return |
613 | * one. Ensure uncorrectable errors are first in the table |
614 | * to match. |
615 | */ |
616 | if (found) |
617 | continue; |
618 | |
619 | /* now fill in mce_error_info */ |
620 | mce_err->error_type = table[i].error_type; |
621 | mce_err->error_class = table[i].error_class; |
622 | switch (table[i].error_type) { |
623 | case MCE_ERROR_TYPE_UE: |
624 | mce_err->u.ue_error_type = table[i].error_subtype; |
625 | break; |
626 | case MCE_ERROR_TYPE_SLB: |
627 | mce_err->u.slb_error_type = table[i].error_subtype; |
628 | break; |
629 | case MCE_ERROR_TYPE_ERAT: |
630 | mce_err->u.erat_error_type = table[i].error_subtype; |
631 | break; |
632 | case MCE_ERROR_TYPE_TLB: |
633 | mce_err->u.tlb_error_type = table[i].error_subtype; |
634 | break; |
635 | case MCE_ERROR_TYPE_USER: |
636 | mce_err->u.user_error_type = table[i].error_subtype; |
637 | break; |
638 | case MCE_ERROR_TYPE_RA: |
639 | mce_err->u.ra_error_type = table[i].error_subtype; |
640 | break; |
641 | case MCE_ERROR_TYPE_LINK: |
642 | mce_err->u.link_error_type = table[i].error_subtype; |
643 | break; |
644 | } |
645 | mce_err->sync_error = table[i].sync_error; |
646 | mce_err->severity = table[i].severity; |
647 | mce_err->initiator = table[i].initiator; |
648 | if (table[i].dar_valid) |
649 | *addr = regs->dar; |
650 | else if (mce_err->sync_error && !mce_in_guest() && |
651 | table[i].error_type == MCE_ERROR_TYPE_UE) { |
652 | /* |
653 | * We do a maximum of 4 nested MCE calls, see |
654 | * kernel/exception-64s.h |
655 | */ |
656 | if (get_paca()->in_mce < MAX_MCE_DEPTH) |
657 | mce_find_instr_ea_and_phys(regs, addr, |
658 | phys_addr); |
659 | } |
660 | found = 1; |
661 | } |
662 | |
663 | if (found) |
664 | return handled; |
665 | |
666 | mce_err->error_type = MCE_ERROR_TYPE_UNKNOWN; |
667 | mce_err->error_class = MCE_ECLASS_UNKNOWN; |
668 | mce_err->severity = MCE_SEV_SEVERE; |
669 | mce_err->initiator = MCE_INITIATOR_CPU; |
670 | mce_err->sync_error = true; |
671 | |
672 | return 0; |
673 | } |
674 | |
675 | static long mce_handle_ue_error(struct pt_regs *regs, |
676 | struct mce_error_info *mce_err) |
677 | { |
678 | if (mce_in_guest()) |
679 | return 0; |
680 | |
681 | mce_common_process_ue(regs, mce_err); |
682 | if (mce_err->ignore_event) |
683 | return 1; |
684 | |
685 | /* |
686 | * On specific SCOM read via MMIO we may get a machine check |
687 | * exception with SRR0 pointing inside opal. If that is the |
688 | * case OPAL may have recovery address to re-read SCOM data in |
689 | * different way and hence we can recover from this MC. |
690 | */ |
691 | |
692 | if (ppc_md.mce_check_early_recovery) { |
693 | if (ppc_md.mce_check_early_recovery(regs)) |
694 | return 1; |
695 | } |
696 | |
697 | return 0; |
698 | } |
699 | |
700 | static long mce_handle_error(struct pt_regs *regs, |
701 | unsigned long srr1, |
702 | const struct mce_derror_table dtable[], |
703 | const struct mce_ierror_table itable[]) |
704 | { |
705 | struct mce_error_info mce_err = { 0 }; |
706 | uint64_t addr, phys_addr = ULONG_MAX; |
707 | long handled; |
708 | |
709 | if (SRR1_MC_LOADSTORE(srr1)) |
710 | handled = mce_handle_derror(regs, table: dtable, mce_err: &mce_err, addr: &addr, |
711 | phys_addr: &phys_addr); |
712 | else |
713 | handled = mce_handle_ierror(regs, srr1, table: itable, mce_err: &mce_err, addr: &addr, |
714 | phys_addr: &phys_addr); |
715 | |
716 | if (!handled && mce_err.error_type == MCE_ERROR_TYPE_UE) |
717 | handled = mce_handle_ue_error(regs, mce_err: &mce_err); |
718 | |
719 | save_mce_event(regs, handled, &mce_err, regs->nip, addr, phys_addr); |
720 | |
721 | return handled; |
722 | } |
723 | |
724 | long __machine_check_early_realmode_p7(struct pt_regs *regs) |
725 | { |
726 | /* P7 DD1 leaves top bits of DSISR undefined */ |
727 | regs->dsisr &= 0x0000ffff; |
728 | |
729 | return mce_handle_error(regs, srr1: regs->msr, |
730 | dtable: mce_p7_derror_table, itable: mce_p7_ierror_table); |
731 | } |
732 | |
733 | long __machine_check_early_realmode_p8(struct pt_regs *regs) |
734 | { |
735 | return mce_handle_error(regs, srr1: regs->msr, |
736 | dtable: mce_p8_derror_table, itable: mce_p8_ierror_table); |
737 | } |
738 | |
739 | long __machine_check_early_realmode_p9(struct pt_regs *regs) |
740 | { |
741 | unsigned long srr1 = regs->msr; |
742 | |
743 | /* |
744 | * On POWER9 DD2.1 and below, it's possible to get a machine check |
745 | * caused by a paste instruction where only DSISR bit 25 is set. This |
746 | * will result in the MCE handler seeing an unknown event and the kernel |
747 | * crashing. An MCE that occurs like this is spurious, so we don't need |
748 | * to do anything in terms of servicing it. If there is something that |
749 | * needs to be serviced, the CPU will raise the MCE again with the |
750 | * correct DSISR so that it can be serviced properly. So detect this |
751 | * case and mark it as handled. |
752 | */ |
753 | if (SRR1_MC_LOADSTORE(regs->msr) && regs->dsisr == 0x02000000) |
754 | return 1; |
755 | |
756 | /* |
757 | * Async machine check due to bad real address from store or foreign |
758 | * link time out comes with the load/store bit (PPC bit 42) set in |
759 | * SRR1, but the cause comes in SRR1 not DSISR. Clear bit 42 so we're |
760 | * directed to the ierror table so it will find the cause (which |
761 | * describes it correctly as a store error). |
762 | */ |
763 | if (SRR1_MC_LOADSTORE(srr1) && |
764 | ((srr1 & 0x081c0000) == 0x08140000 || |
765 | (srr1 & 0x081c0000) == 0x08180000)) { |
766 | srr1 &= ~PPC_BIT(42); |
767 | } |
768 | |
769 | return mce_handle_error(regs, srr1, |
770 | dtable: mce_p9_derror_table, itable: mce_p9_ierror_table); |
771 | } |
772 | |
773 | long __machine_check_early_realmode_p10(struct pt_regs *regs) |
774 | { |
775 | unsigned long srr1 = regs->msr; |
776 | |
777 | /* |
778 | * Async machine check due to bad real address from store comes with |
779 | * the load/store bit (PPC bit 42) set in SRR1, but the cause comes in |
780 | * SRR1 not DSISR. Clear bit 42 so we're directed to the ierror table |
781 | * so it will find the cause (which describes it correctly as a store |
782 | * error). |
783 | */ |
784 | if (SRR1_MC_LOADSTORE(srr1) && |
785 | (srr1 & 0x081c0000) == 0x08140000) { |
786 | srr1 &= ~PPC_BIT(42); |
787 | } |
788 | |
789 | return mce_handle_error(regs, srr1, |
790 | dtable: mce_p10_derror_table, itable: mce_p10_ierror_table); |
791 | } |
792 | |