Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
e22a2274 MS |
2 | /* |
3 | * Machine check exception handling CPU-side for power7 and power8 | |
4 | * | |
e22a2274 MS |
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> | |
895e3dce | 14 | #include <linux/extable.h> |
65fddcfc | 15 | #include <linux/pgtable.h> |
e22a2274 MS |
16 | #include <asm/mmu.h> |
17 | #include <asm/mce.h> | |
55672ecf | 18 | #include <asm/machdep.h> |
ba41e1e1 BS |
19 | #include <asm/pte-walk.h> |
20 | #include <asm/sstep.h> | |
21 | #include <asm/exception-64s.h> | |
895e3dce | 22 | #include <asm/extable.h> |
94afd069 | 23 | #include <asm/inst.h> |
ba41e1e1 BS |
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 | */ | |
7f177f98 | 29 | unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr) |
ba41e1e1 | 30 | { |
0da81b65 | 31 | pte_t *ptep, pte; |
99ead78a | 32 | unsigned int shift; |
d9101bfa | 33 | unsigned long pfn, flags; |
ba41e1e1 BS |
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); | |
99ead78a | 42 | ptep = __find_linux_pte(mm->pgd, addr, NULL, &shift); |
0da81b65 AK |
43 | if (!ptep) { |
44 | pfn = ULONG_MAX; | |
45 | goto out; | |
46 | } | |
47 | pte = READ_ONCE(*ptep); | |
99ead78a | 48 | |
0da81b65 | 49 | if (!pte_present(pte) || pte_special(pte)) { |
d9101bfa AK |
50 | pfn = ULONG_MAX; |
51 | goto out; | |
52 | } | |
99ead78a | 53 | |
d9101bfa | 54 | if (shift <= PAGE_SHIFT) |
0da81b65 | 55 | pfn = pte_pfn(pte); |
d9101bfa | 56 | else { |
99ead78a | 57 | unsigned long rpnmask = (1ul << shift) - PAGE_SIZE; |
0da81b65 | 58 | pfn = pte_pfn(__pte(pte_val(pte) | (addr & rpnmask))); |
99ead78a | 59 | } |
d9101bfa AK |
60 | out: |
61 | local_irq_restore(flags); | |
62 | return pfn; | |
ba41e1e1 | 63 | } |
e22a2274 | 64 | |
0ce23826 NP |
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 | ||
e22a2274 | 79 | /* flush SLBs and reload */ |
387e220a | 80 | #ifdef CONFIG_PPC_64S_HASH_MMU |
a43c1590 | 81 | void flush_and_reload_slb(void) |
e22a2274 | 82 | { |
e7e81847 | 83 | if (early_radix_enabled()) |
e22a2274 MS |
84 | return; |
85 | ||
310dce62 NP |
86 | /* Invalidate all SLBs */ |
87 | slb_flush_all_realmode(); | |
88 | ||
e7e81847 NP |
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; | |
e22a2274 | 95 | |
e7e81847 | 96 | slb_restore_bolted_realmode(); |
e22a2274 | 97 | } |
caca285e | 98 | #endif |
e22a2274 | 99 | |
82f70a05 | 100 | void flush_erat(void) |
7b9f71f9 | 101 | { |
387e220a | 102 | #ifdef CONFIG_PPC_64S_HASH_MMU |
bc276ecb NP |
103 | if (!early_cpu_has_feature(CPU_FTR_ARCH_300)) { |
104 | flush_and_reload_slb(); | |
105 | return; | |
106 | } | |
107 | #endif | |
fe7946ce | 108 | asm volatile(PPC_ISA_3_0_INVALIDATE_ERAT : : :"memory"); |
7b9f71f9 NP |
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 | { | |
387e220a | 117 | #ifdef CONFIG_PPC_64S_HASH_MMU |
7b9f71f9 NP |
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) { | |
d4748276 NP |
128 | tlbiel_all(); |
129 | return 1; | |
7b9f71f9 NP |
130 | } |
131 | ||
132 | return 0; | |
133 | } | |
134 | ||
755309be | 135 | #define SRR1_MC_LOADSTORE(srr1) ((srr1) & PPC_BIT(42)) |
58c8d17f | 136 | |
631bc46c NP |
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; | |
50dbabe0 | 143 | unsigned int error_class; |
631bc46c NP |
144 | unsigned int initiator; |
145 | unsigned int severity; | |
cda6618d | 146 | bool sync_error; |
631bc46c NP |
147 | }; |
148 | ||
149 | static const struct mce_ierror_table mce_p7_ierror_table[] = { | |
150 | { 0x00000000001c0000, 0x0000000000040000, true, | |
50dbabe0 | 151 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
cda6618d | 152 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 153 | { 0x00000000001c0000, 0x0000000000080000, true, |
50dbabe0 | 154 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
cda6618d | 155 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 156 | { 0x00000000001c0000, 0x00000000000c0000, true, |
50dbabe0 | 157 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 158 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c NP |
159 | { 0x00000000001c0000, 0x0000000000100000, true, |
160 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_INDETERMINATE, /* BOTH */ | |
50dbabe0 | 161 | MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 162 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 163 | { 0x00000000001c0000, 0x0000000000140000, true, |
50dbabe0 | 164 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 165 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 166 | { 0x00000000001c0000, 0x0000000000180000, true, |
50dbabe0 | 167 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_HARDWARE, |
cda6618d | 168 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 169 | { 0x00000000001c0000, 0x00000000001c0000, true, |
50dbabe0 | 170 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
cda6618d MS |
171 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
172 | { 0, 0, 0, 0, 0, 0, 0 } }; | |
631bc46c NP |
173 | |
174 | static const struct mce_ierror_table mce_p8_ierror_table[] = { | |
c7e790c5 | 175 | { 0x00000000081c0000, 0x0000000000040000, true, |
50dbabe0 | 176 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
cda6618d | 177 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
c7e790c5 | 178 | { 0x00000000081c0000, 0x0000000000080000, true, |
50dbabe0 | 179 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
cda6618d | 180 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
c7e790c5 | 181 | { 0x00000000081c0000, 0x00000000000c0000, true, |
50dbabe0 | 182 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 183 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
c7e790c5 | 184 | { 0x00000000081c0000, 0x0000000000100000, true, |
50dbabe0 | 185 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 186 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
c7e790c5 | 187 | { 0x00000000081c0000, 0x0000000000140000, true, |
50dbabe0 | 188 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 189 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
c7e790c5 | 190 | { 0x00000000081c0000, 0x0000000000180000, true, |
631bc46c | 191 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, |
50dbabe0 | 192 | MCE_ECLASS_HARDWARE, |
cda6618d | 193 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
c7e790c5 | 194 | { 0x00000000081c0000, 0x00000000001c0000, true, |
50dbabe0 | 195 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
cda6618d | 196 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
c7e790c5 | 197 | { 0x00000000081c0000, 0x0000000008000000, true, |
50dbabe0 | 198 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_IFETCH_TIMEOUT, MCE_ECLASS_HARDWARE, |
cda6618d | 199 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
c7e790c5 NP |
200 | { 0x00000000081c0000, 0x0000000008040000, true, |
201 | MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_PAGE_TABLE_WALK_IFETCH_TIMEOUT, | |
50dbabe0 | 202 | MCE_ECLASS_HARDWARE, |
cda6618d MS |
203 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
204 | { 0, 0, 0, 0, 0, 0, 0 } }; | |
631bc46c NP |
205 | |
206 | static const struct mce_ierror_table mce_p9_ierror_table[] = { | |
207 | { 0x00000000081c0000, 0x0000000000040000, true, | |
50dbabe0 | 208 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
cda6618d | 209 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 210 | { 0x00000000081c0000, 0x0000000000080000, true, |
50dbabe0 | 211 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
cda6618d | 212 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 213 | { 0x00000000081c0000, 0x00000000000c0000, true, |
50dbabe0 | 214 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 215 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 216 | { 0x00000000081c0000, 0x0000000000100000, true, |
50dbabe0 | 217 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 218 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 219 | { 0x00000000081c0000, 0x0000000000140000, true, |
50dbabe0 | 220 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 221 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 222 | { 0x00000000081c0000, 0x0000000000180000, true, |
50dbabe0 | 223 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_HARDWARE, |
cda6618d | 224 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
90df4bfb | 225 | { 0x00000000081c0000, 0x00000000001c0000, true, |
50dbabe0 | 226 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_IFETCH_FOREIGN, MCE_ECLASS_SOFTWARE, |
cda6618d | 227 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 228 | { 0x00000000081c0000, 0x0000000008000000, true, |
50dbabe0 | 229 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_IFETCH_TIMEOUT, MCE_ECLASS_HARDWARE, |
cda6618d | 230 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
231 | { 0x00000000081c0000, 0x0000000008040000, true, |
232 | MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_PAGE_TABLE_WALK_IFETCH_TIMEOUT, | |
50dbabe0 | 233 | MCE_ECLASS_HARDWARE, |
cda6618d | 234 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 235 | { 0x00000000081c0000, 0x00000000080c0000, true, |
50dbabe0 | 236 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_IFETCH, MCE_ECLASS_SOFTWARE, |
cda6618d | 237 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 238 | { 0x00000000081c0000, 0x0000000008100000, true, |
50dbabe0 | 239 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_SOFTWARE, |
cda6618d | 240 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 241 | { 0x00000000081c0000, 0x0000000008140000, false, |
50dbabe0 | 242 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_STORE, MCE_ECLASS_HARDWARE, |
cda6618d | 243 | MCE_INITIATOR_CPU, MCE_SEV_FATAL, false }, /* ASYNC is fatal */ |
631bc46c NP |
244 | { 0x00000000081c0000, 0x0000000008180000, false, |
245 | MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_STORE_TIMEOUT, | |
cda6618d | 246 | MCE_INITIATOR_CPU, MCE_SEV_FATAL, false }, /* ASYNC is fatal */ |
50dbabe0 | 247 | { 0x00000000081c0000, 0x00000000081c0000, true, MCE_ECLASS_HARDWARE, |
631bc46c | 248 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH_FOREIGN, |
cda6618d MS |
249 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
250 | { 0, 0, 0, 0, 0, 0, 0 } }; | |
631bc46c | 251 | |
201220bb NP |
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 | ||
631bc46c NP |
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; | |
50dbabe0 | 296 | unsigned int error_class; |
631bc46c NP |
297 | unsigned int initiator; |
298 | unsigned int severity; | |
cda6618d | 299 | bool sync_error; |
631bc46c NP |
300 | }; |
301 | ||
302 | static const struct mce_derror_table mce_p7_derror_table[] = { | |
303 | { 0x00008000, false, | |
50dbabe0 | 304 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE, |
cda6618d | 305 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
306 | { 0x00004000, true, |
307 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE, | |
50dbabe0 | 308 | MCE_ECLASS_HARDWARE, |
cda6618d | 309 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 310 | { 0x00000800, true, |
50dbabe0 | 311 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 312 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 313 | { 0x00000400, true, |
50dbabe0 | 314 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 315 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
54dbcfc2 | 316 | { 0x00000080, true, |
50dbabe0 | 317 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 318 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 319 | { 0x00000100, true, |
50dbabe0 | 320 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
cda6618d | 321 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
322 | { 0x00000040, true, |
323 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_INDETERMINATE, /* BOTH */ | |
50dbabe0 | 324 | MCE_ECLASS_HARD_INDETERMINATE, |
cda6618d MS |
325 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
326 | { 0, false, 0, 0, 0, 0, 0 } }; | |
631bc46c NP |
327 | |
328 | static const struct mce_derror_table mce_p8_derror_table[] = { | |
329 | { 0x00008000, false, | |
50dbabe0 | 330 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE, |
cda6618d | 331 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
332 | { 0x00004000, true, |
333 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE, | |
50dbabe0 | 334 | MCE_ECLASS_HARDWARE, |
cda6618d | 335 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
c7e790c5 | 336 | { 0x00002000, true, |
50dbabe0 | 337 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_LOAD_TIMEOUT, MCE_ECLASS_HARDWARE, |
cda6618d | 338 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
c7e790c5 NP |
339 | { 0x00001000, true, |
340 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_PAGE_TABLE_WALK_LOAD_STORE_TIMEOUT, | |
50dbabe0 | 341 | MCE_ECLASS_HARDWARE, |
cda6618d | 342 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 343 | { 0x00000800, true, |
50dbabe0 | 344 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 345 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 346 | { 0x00000400, true, |
50dbabe0 | 347 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 348 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c NP |
349 | { 0x00000200, true, |
350 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, /* SECONDARY ERAT */ | |
50dbabe0 | 351 | MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 352 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
54dbcfc2 ME |
353 | { 0x00000080, true, |
354 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, /* Before PARITY */ | |
50dbabe0 | 355 | MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 356 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 357 | { 0x00000100, true, |
50dbabe0 | 358 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
cda6618d MS |
359 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
360 | { 0, false, 0, 0, 0, 0, 0 } }; | |
631bc46c NP |
361 | |
362 | static const struct mce_derror_table mce_p9_derror_table[] = { | |
363 | { 0x00008000, false, | |
50dbabe0 | 364 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE, |
cda6618d | 365 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
366 | { 0x00004000, true, |
367 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE, | |
50dbabe0 | 368 | MCE_ECLASS_HARDWARE, |
cda6618d | 369 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 370 | { 0x00002000, true, |
50dbabe0 | 371 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_LOAD_TIMEOUT, MCE_ECLASS_HARDWARE, |
cda6618d | 372 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
373 | { 0x00001000, true, |
374 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_PAGE_TABLE_WALK_LOAD_STORE_TIMEOUT, | |
50dbabe0 | 375 | MCE_ECLASS_HARDWARE, |
cda6618d | 376 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 377 | { 0x00000800, true, |
50dbabe0 | 378 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 379 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 380 | { 0x00000400, true, |
50dbabe0 | 381 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 382 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 383 | { 0x00000200, false, |
50dbabe0 | 384 | MCE_ERROR_TYPE_USER, MCE_USER_ERROR_TLBIE, MCE_ECLASS_SOFTWARE, |
cda6618d | 385 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
54dbcfc2 ME |
386 | { 0x00000080, true, |
387 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, /* Before PARITY */ | |
50dbabe0 | 388 | MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 389 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 390 | { 0x00000100, true, |
50dbabe0 | 391 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
cda6618d | 392 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 393 | { 0x00000040, true, |
50dbabe0 | 394 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_LOAD, MCE_ECLASS_HARDWARE, |
cda6618d | 395 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
396 | { 0x00000020, false, |
397 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE, | |
50dbabe0 | 398 | MCE_ECLASS_HARDWARE, |
cda6618d | 399 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
400 | { 0x00000010, false, |
401 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE_FOREIGN, | |
50dbabe0 | 402 | MCE_ECLASS_HARDWARE, |
cda6618d | 403 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 404 | { 0x00000008, false, |
50dbabe0 | 405 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_LOAD_STORE_FOREIGN, MCE_ECLASS_HARDWARE, |
cda6618d MS |
406 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
407 | { 0, false, 0, 0, 0, 0, 0 } }; | |
631bc46c | 408 | |
201220bb NP |
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 | ||
99ead78a | 449 | static int mce_find_instr_ea_and_phys(struct pt_regs *regs, uint64_t *addr, |
ba41e1e1 BS |
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 | */ | |
c545b9f0 | 458 | ppc_inst_t instr; |
ba41e1e1 BS |
459 | unsigned long pfn, instr_addr; |
460 | struct instruction_op op; | |
461 | struct pt_regs tmp = *regs; | |
462 | ||
463 | pfn = addr_to_pfn(regs, regs->nip); | |
464 | if (pfn != ULONG_MAX) { | |
465 | instr_addr = (pfn << PAGE_SHIFT) + (regs->nip & ~PAGE_MASK); | |
69d4d6e5 | 466 | instr = ppc_inst_read((u32 *)instr_addr); |
ba41e1e1 BS |
467 | if (!analyse_instr(&op, &tmp, instr)) { |
468 | pfn = addr_to_pfn(regs, 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 | ||
3729e0ec | 484 | static int mce_handle_ierror(struct pt_regs *regs, unsigned long srr1, |
631bc46c | 485 | const struct mce_ierror_table table[], |
01eaac2b BS |
486 | struct mce_error_info *mce_err, uint64_t *addr, |
487 | uint64_t *phys_addr) | |
e22a2274 | 488 | { |
755309be | 489 | int handled = 0; |
631bc46c NP |
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 | ||
0ce23826 NP |
498 | if (!mce_in_guest()) { |
499 | /* attempt to correct the error */ | |
500 | switch (table[i].error_type) { | |
501 | case MCE_ERROR_TYPE_SLB: | |
387e220a | 502 | #ifdef CONFIG_PPC_64S_HASH_MMU |
0ce23826 NP |
503 | if (local_paca->in_mce == 1) |
504 | slb_save_contents(local_paca->mce_faulty_slbs); | |
387e220a | 505 | #endif |
0ce23826 NP |
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 | } | |
755309be NP |
515 | } |
516 | ||
517 | /* now fill in mce_error_info */ | |
631bc46c | 518 | mce_err->error_type = table[i].error_type; |
50dbabe0 | 519 | mce_err->error_class = table[i].error_class; |
631bc46c NP |
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 | } | |
cda6618d | 543 | mce_err->sync_error = table[i].sync_error; |
631bc46c NP |
544 | mce_err->severity = table[i].severity; |
545 | mce_err->initiator = table[i].initiator; | |
0ce23826 | 546 | if (table[i].nip_valid && !mce_in_guest()) { |
631bc46c | 547 | *addr = regs->nip; |
cda6618d | 548 | if (mce_err->sync_error && |
01eaac2b BS |
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, regs->nip); | |
554 | if (pfn != ULONG_MAX) { | |
555 | *phys_addr = | |
556 | (pfn << PAGE_SHIFT); | |
01eaac2b BS |
557 | } |
558 | } | |
559 | } | |
560 | } | |
755309be | 561 | return handled; |
e22a2274 | 562 | } |
e22a2274 | 563 | |
631bc46c | 564 | mce_err->error_type = MCE_ERROR_TYPE_UNKNOWN; |
50dbabe0 | 565 | mce_err->error_class = MCE_ECLASS_UNKNOWN; |
cda6618d | 566 | mce_err->severity = MCE_SEV_SEVERE; |
631bc46c | 567 | mce_err->initiator = MCE_INITIATOR_CPU; |
cda6618d | 568 | mce_err->sync_error = true; |
755309be NP |
569 | |
570 | return 0; | |
36df96f8 MS |
571 | } |
572 | ||
755309be | 573 | static int mce_handle_derror(struct pt_regs *regs, |
631bc46c | 574 | const struct mce_derror_table table[], |
ba41e1e1 BS |
575 | struct mce_error_info *mce_err, uint64_t *addr, |
576 | uint64_t *phys_addr) | |
36df96f8 | 577 | { |
631bc46c | 578 | uint64_t dsisr = regs->dsisr; |
755309be NP |
579 | int handled = 0; |
580 | int found = 0; | |
631bc46c NP |
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 | ||
0ce23826 NP |
589 | if (!mce_in_guest()) { |
590 | /* attempt to correct the error */ | |
591 | switch (table[i].error_type) { | |
592 | case MCE_ERROR_TYPE_SLB: | |
387e220a | 593 | #ifdef CONFIG_PPC_64S_HASH_MMU |
0ce23826 NP |
594 | if (local_paca->in_mce == 1) |
595 | slb_save_contents(local_paca->mce_faulty_slbs); | |
387e220a | 596 | #endif |
0ce23826 NP |
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 | } | |
755309be NP |
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 */ | |
631bc46c | 620 | mce_err->error_type = table[i].error_type; |
50dbabe0 | 621 | mce_err->error_class = table[i].error_class; |
631bc46c NP |
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 | } | |
cda6618d | 645 | mce_err->sync_error = table[i].sync_error; |
631bc46c NP |
646 | mce_err->severity = table[i].severity; |
647 | mce_err->initiator = table[i].initiator; | |
648 | if (table[i].dar_valid) | |
649 | *addr = regs->dar; | |
0ce23826 | 650 | else if (mce_err->sync_error && !mce_in_guest() && |
ba41e1e1 BS |
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) | |
99ead78a BS |
657 | mce_find_instr_ea_and_phys(regs, addr, |
658 | phys_addr); | |
ba41e1e1 | 659 | } |
755309be | 660 | found = 1; |
36df96f8 | 661 | } |
631bc46c | 662 | |
755309be NP |
663 | if (found) |
664 | return handled; | |
665 | ||
631bc46c | 666 | mce_err->error_type = MCE_ERROR_TYPE_UNKNOWN; |
50dbabe0 | 667 | mce_err->error_class = MCE_ECLASS_UNKNOWN; |
cda6618d | 668 | mce_err->severity = MCE_SEV_SEVERE; |
631bc46c | 669 | mce_err->initiator = MCE_INITIATOR_CPU; |
cda6618d | 670 | mce_err->sync_error = true; |
755309be NP |
671 | |
672 | return 0; | |
36df96f8 MS |
673 | } |
674 | ||
895e3dce BS |
675 | static long mce_handle_ue_error(struct pt_regs *regs, |
676 | struct mce_error_info *mce_err) | |
55672ecf | 677 | { |
0ce23826 NP |
678 | if (mce_in_guest()) |
679 | return 0; | |
895e3dce | 680 | |
efbc4303 GG |
681 | mce_common_process_ue(regs, mce_err); |
682 | if (mce_err->ignore_event) | |
895e3dce | 683 | return 1; |
55672ecf MS |
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)) | |
0ce23826 | 694 | return 1; |
55672ecf | 695 | } |
0ce23826 NP |
696 | |
697 | return 0; | |
55672ecf MS |
698 | } |
699 | ||
755309be | 700 | static long mce_handle_error(struct pt_regs *regs, |
3729e0ec | 701 | unsigned long srr1, |
755309be NP |
702 | const struct mce_derror_table dtable[], |
703 | const struct mce_ierror_table itable[]) | |
e22a2274 | 704 | { |
755309be | 705 | struct mce_error_info mce_err = { 0 }; |
75ecfb49 | 706 | uint64_t addr, phys_addr = ULONG_MAX; |
755309be | 707 | long handled; |
e22a2274 | 708 | |
755309be | 709 | if (SRR1_MC_LOADSTORE(srr1)) |
ba41e1e1 BS |
710 | handled = mce_handle_derror(regs, dtable, &mce_err, &addr, |
711 | &phys_addr); | |
755309be | 712 | else |
3729e0ec | 713 | handled = mce_handle_ierror(regs, srr1, itable, &mce_err, &addr, |
01eaac2b | 714 | &phys_addr); |
631bc46c | 715 | |
755309be | 716 | if (!handled && mce_err.error_type == MCE_ERROR_TYPE_UE) |
895e3dce | 717 | handled = mce_handle_ue_error(regs, &mce_err); |
55672ecf | 718 | |
ba41e1e1 | 719 | save_mce_event(regs, handled, &mce_err, regs->nip, addr, phys_addr); |
55672ecf | 720 | |
ae744f34 MS |
721 | return handled; |
722 | } | |
7b9f71f9 | 723 | |
755309be | 724 | long __machine_check_early_realmode_p7(struct pt_regs *regs) |
7b9f71f9 | 725 | { |
755309be NP |
726 | /* P7 DD1 leaves top bits of DSISR undefined */ |
727 | regs->dsisr &= 0x0000ffff; | |
7b9f71f9 | 728 | |
3729e0ec NP |
729 | return mce_handle_error(regs, regs->msr, |
730 | mce_p7_derror_table, mce_p7_ierror_table); | |
7b9f71f9 NP |
731 | } |
732 | ||
755309be | 733 | long __machine_check_early_realmode_p8(struct pt_regs *regs) |
7b9f71f9 | 734 | { |
3729e0ec NP |
735 | return mce_handle_error(regs, regs->msr, |
736 | mce_p8_derror_table, mce_p8_ierror_table); | |
7b9f71f9 NP |
737 | } |
738 | ||
7b9f71f9 NP |
739 | long __machine_check_early_realmode_p9(struct pt_regs *regs) |
740 | { | |
3729e0ec NP |
741 | unsigned long srr1 = regs->msr; |
742 | ||
d8bd9f3f MN |
743 | /* |
744 | * On POWER9 DD2.1 and below, it's possible to get a machine check | |
bca73f59 | 745 | * caused by a paste instruction where only DSISR bit 25 is set. This |
d8bd9f3f MN |
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 | */ | |
bca73f59 | 753 | if (SRR1_MC_LOADSTORE(regs->msr) && regs->dsisr == 0x02000000) |
d8bd9f3f MN |
754 | return 1; |
755 | ||
3729e0ec NP |
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 | mce_p9_derror_table, mce_p9_ierror_table); | |
7b9f71f9 | 771 | } |
201220bb NP |
772 | |
773 | long __machine_check_early_realmode_p10(struct pt_regs *regs) | |
774 | { | |
3729e0ec NP |
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 | mce_p10_derror_table, mce_p10_ierror_table); | |
201220bb | 791 | } |