Commit | Line | Data |
---|---|---|
e22a2274 MS |
1 | /* |
2 | * Machine check exception handling CPU-side for power7 and power8 | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write to the Free Software | |
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
17 | * | |
18 | * Copyright 2013 IBM Corporation | |
19 | * Author: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> | |
20 | */ | |
21 | ||
22 | #undef DEBUG | |
23 | #define pr_fmt(fmt) "mce_power: " fmt | |
24 | ||
25 | #include <linux/types.h> | |
26 | #include <linux/ptrace.h> | |
27 | #include <asm/mmu.h> | |
28 | #include <asm/mce.h> | |
55672ecf | 29 | #include <asm/machdep.h> |
ba41e1e1 BS |
30 | #include <asm/pgtable.h> |
31 | #include <asm/pte-walk.h> | |
32 | #include <asm/sstep.h> | |
33 | #include <asm/exception-64s.h> | |
34 | ||
35 | /* | |
36 | * Convert an address related to an mm to a PFN. NOTE: we are in real | |
37 | * mode, we could potentially race with page table updates. | |
38 | */ | |
7f177f98 | 39 | unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr) |
ba41e1e1 BS |
40 | { |
41 | pte_t *ptep; | |
42 | unsigned long flags; | |
43 | struct mm_struct *mm; | |
44 | ||
45 | if (user_mode(regs)) | |
46 | mm = current->mm; | |
47 | else | |
48 | mm = &init_mm; | |
49 | ||
50 | local_irq_save(flags); | |
51 | if (mm == current->mm) | |
52 | ptep = find_current_mm_pte(mm->pgd, addr, NULL, NULL); | |
53 | else | |
54 | ptep = find_init_mm_pte(addr, NULL); | |
55 | local_irq_restore(flags); | |
56 | if (!ptep || pte_special(*ptep)) | |
57 | return ULONG_MAX; | |
58 | return pte_pfn(*ptep); | |
59 | } | |
e22a2274 MS |
60 | |
61 | /* flush SLBs and reload */ | |
4e003747 | 62 | #ifdef CONFIG_PPC_BOOK3S_64 |
a43c1590 | 63 | void flush_and_reload_slb(void) |
e22a2274 | 64 | { |
e22a2274 | 65 | /* Invalidate all SLBs */ |
e7e81847 | 66 | slb_flush_all_realmode(); |
e22a2274 MS |
67 | |
68 | #ifdef CONFIG_KVM_BOOK3S_HANDLER | |
69 | /* | |
70 | * If machine check is hit when in guest or in transition, we will | |
71 | * only flush the SLBs and continue. | |
72 | */ | |
73 | if (get_paca()->kvm_hstate.in_guest) | |
74 | return; | |
75 | #endif | |
e7e81847 | 76 | if (early_radix_enabled()) |
e22a2274 MS |
77 | return; |
78 | ||
e7e81847 NP |
79 | /* |
80 | * This probably shouldn't happen, but it may be possible it's | |
81 | * called in early boot before SLB shadows are allocated. | |
82 | */ | |
83 | if (!get_slb_shadow()) | |
84 | return; | |
e22a2274 | 85 | |
e7e81847 | 86 | slb_restore_bolted_realmode(); |
e22a2274 | 87 | } |
caca285e | 88 | #endif |
e22a2274 | 89 | |
7b9f71f9 NP |
90 | static void flush_erat(void) |
91 | { | |
bc276ecb NP |
92 | #ifdef CONFIG_PPC_BOOK3S_64 |
93 | if (!early_cpu_has_feature(CPU_FTR_ARCH_300)) { | |
94 | flush_and_reload_slb(); | |
95 | return; | |
96 | } | |
97 | #endif | |
fe7946ce | 98 | asm volatile(PPC_ISA_3_0_INVALIDATE_ERAT : : :"memory"); |
7b9f71f9 NP |
99 | } |
100 | ||
101 | #define MCE_FLUSH_SLB 1 | |
102 | #define MCE_FLUSH_TLB 2 | |
103 | #define MCE_FLUSH_ERAT 3 | |
104 | ||
105 | static int mce_flush(int what) | |
106 | { | |
4e003747 | 107 | #ifdef CONFIG_PPC_BOOK3S_64 |
7b9f71f9 NP |
108 | if (what == MCE_FLUSH_SLB) { |
109 | flush_and_reload_slb(); | |
110 | return 1; | |
111 | } | |
112 | #endif | |
113 | if (what == MCE_FLUSH_ERAT) { | |
114 | flush_erat(); | |
115 | return 1; | |
116 | } | |
117 | if (what == MCE_FLUSH_TLB) { | |
d4748276 NP |
118 | tlbiel_all(); |
119 | return 1; | |
7b9f71f9 NP |
120 | } |
121 | ||
122 | return 0; | |
123 | } | |
124 | ||
755309be | 125 | #define SRR1_MC_LOADSTORE(srr1) ((srr1) & PPC_BIT(42)) |
58c8d17f | 126 | |
631bc46c NP |
127 | struct mce_ierror_table { |
128 | unsigned long srr1_mask; | |
129 | unsigned long srr1_value; | |
130 | bool nip_valid; /* nip is a valid indicator of faulting address */ | |
131 | unsigned int error_type; | |
132 | unsigned int error_subtype; | |
50dbabe0 | 133 | unsigned int error_class; |
631bc46c NP |
134 | unsigned int initiator; |
135 | unsigned int severity; | |
cda6618d | 136 | bool sync_error; |
631bc46c NP |
137 | }; |
138 | ||
139 | static const struct mce_ierror_table mce_p7_ierror_table[] = { | |
140 | { 0x00000000001c0000, 0x0000000000040000, true, | |
50dbabe0 | 141 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
cda6618d | 142 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 143 | { 0x00000000001c0000, 0x0000000000080000, true, |
50dbabe0 | 144 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
cda6618d | 145 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 146 | { 0x00000000001c0000, 0x00000000000c0000, true, |
50dbabe0 | 147 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 148 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c NP |
149 | { 0x00000000001c0000, 0x0000000000100000, true, |
150 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_INDETERMINATE, /* BOTH */ | |
50dbabe0 | 151 | MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 152 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 153 | { 0x00000000001c0000, 0x0000000000140000, true, |
50dbabe0 | 154 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 155 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 156 | { 0x00000000001c0000, 0x0000000000180000, true, |
50dbabe0 | 157 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_HARDWARE, |
cda6618d | 158 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 159 | { 0x00000000001c0000, 0x00000000001c0000, true, |
50dbabe0 | 160 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
cda6618d MS |
161 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
162 | { 0, 0, 0, 0, 0, 0, 0 } }; | |
631bc46c NP |
163 | |
164 | static const struct mce_ierror_table mce_p8_ierror_table[] = { | |
c7e790c5 | 165 | { 0x00000000081c0000, 0x0000000000040000, true, |
50dbabe0 | 166 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
cda6618d | 167 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
c7e790c5 | 168 | { 0x00000000081c0000, 0x0000000000080000, true, |
50dbabe0 | 169 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
cda6618d | 170 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
c7e790c5 | 171 | { 0x00000000081c0000, 0x00000000000c0000, true, |
50dbabe0 | 172 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 173 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
c7e790c5 | 174 | { 0x00000000081c0000, 0x0000000000100000, true, |
50dbabe0 | 175 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 176 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
c7e790c5 | 177 | { 0x00000000081c0000, 0x0000000000140000, true, |
50dbabe0 | 178 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 179 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
c7e790c5 | 180 | { 0x00000000081c0000, 0x0000000000180000, true, |
631bc46c | 181 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, |
50dbabe0 | 182 | MCE_ECLASS_HARDWARE, |
cda6618d | 183 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
c7e790c5 | 184 | { 0x00000000081c0000, 0x00000000001c0000, true, |
50dbabe0 | 185 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
cda6618d | 186 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
c7e790c5 | 187 | { 0x00000000081c0000, 0x0000000008000000, true, |
50dbabe0 | 188 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_IFETCH_TIMEOUT, MCE_ECLASS_HARDWARE, |
cda6618d | 189 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
c7e790c5 NP |
190 | { 0x00000000081c0000, 0x0000000008040000, true, |
191 | MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_PAGE_TABLE_WALK_IFETCH_TIMEOUT, | |
50dbabe0 | 192 | MCE_ECLASS_HARDWARE, |
cda6618d MS |
193 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
194 | { 0, 0, 0, 0, 0, 0, 0 } }; | |
631bc46c NP |
195 | |
196 | static const struct mce_ierror_table mce_p9_ierror_table[] = { | |
197 | { 0x00000000081c0000, 0x0000000000040000, true, | |
50dbabe0 | 198 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE, |
cda6618d | 199 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 200 | { 0x00000000081c0000, 0x0000000000080000, true, |
50dbabe0 | 201 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
cda6618d | 202 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 203 | { 0x00000000081c0000, 0x00000000000c0000, true, |
50dbabe0 | 204 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 205 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 206 | { 0x00000000081c0000, 0x0000000000100000, true, |
50dbabe0 | 207 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 208 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 209 | { 0x00000000081c0000, 0x0000000000140000, true, |
50dbabe0 | 210 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 211 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 212 | { 0x00000000081c0000, 0x0000000000180000, true, |
50dbabe0 | 213 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_HARDWARE, |
cda6618d | 214 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
90df4bfb | 215 | { 0x00000000081c0000, 0x00000000001c0000, true, |
50dbabe0 | 216 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_IFETCH_FOREIGN, MCE_ECLASS_SOFTWARE, |
cda6618d | 217 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 218 | { 0x00000000081c0000, 0x0000000008000000, true, |
50dbabe0 | 219 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_IFETCH_TIMEOUT, MCE_ECLASS_HARDWARE, |
cda6618d | 220 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
221 | { 0x00000000081c0000, 0x0000000008040000, true, |
222 | MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_PAGE_TABLE_WALK_IFETCH_TIMEOUT, | |
50dbabe0 | 223 | MCE_ECLASS_HARDWARE, |
cda6618d | 224 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 225 | { 0x00000000081c0000, 0x00000000080c0000, true, |
50dbabe0 | 226 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_IFETCH, MCE_ECLASS_SOFTWARE, |
cda6618d | 227 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 228 | { 0x00000000081c0000, 0x0000000008100000, true, |
50dbabe0 | 229 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_SOFTWARE, |
cda6618d | 230 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 231 | { 0x00000000081c0000, 0x0000000008140000, false, |
50dbabe0 | 232 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_STORE, MCE_ECLASS_HARDWARE, |
cda6618d | 233 | MCE_INITIATOR_CPU, MCE_SEV_FATAL, false }, /* ASYNC is fatal */ |
631bc46c NP |
234 | { 0x00000000081c0000, 0x0000000008180000, false, |
235 | MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_STORE_TIMEOUT, | |
cda6618d | 236 | MCE_INITIATOR_CPU, MCE_SEV_FATAL, false }, /* ASYNC is fatal */ |
50dbabe0 | 237 | { 0x00000000081c0000, 0x00000000081c0000, true, MCE_ECLASS_HARDWARE, |
631bc46c | 238 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH_FOREIGN, |
cda6618d MS |
239 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
240 | { 0, 0, 0, 0, 0, 0, 0 } }; | |
631bc46c NP |
241 | |
242 | struct mce_derror_table { | |
243 | unsigned long dsisr_value; | |
244 | bool dar_valid; /* dar is a valid indicator of faulting address */ | |
245 | unsigned int error_type; | |
246 | unsigned int error_subtype; | |
50dbabe0 | 247 | unsigned int error_class; |
631bc46c NP |
248 | unsigned int initiator; |
249 | unsigned int severity; | |
cda6618d | 250 | bool sync_error; |
631bc46c NP |
251 | }; |
252 | ||
253 | static const struct mce_derror_table mce_p7_derror_table[] = { | |
254 | { 0x00008000, false, | |
50dbabe0 | 255 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE, |
cda6618d | 256 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
257 | { 0x00004000, true, |
258 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE, | |
50dbabe0 | 259 | MCE_ECLASS_HARDWARE, |
cda6618d | 260 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 261 | { 0x00000800, true, |
50dbabe0 | 262 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 263 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 264 | { 0x00000400, true, |
50dbabe0 | 265 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 266 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
54dbcfc2 | 267 | { 0x00000080, true, |
50dbabe0 | 268 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 269 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 270 | { 0x00000100, true, |
50dbabe0 | 271 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
cda6618d | 272 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
273 | { 0x00000040, true, |
274 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_INDETERMINATE, /* BOTH */ | |
50dbabe0 | 275 | MCE_ECLASS_HARD_INDETERMINATE, |
cda6618d MS |
276 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
277 | { 0, false, 0, 0, 0, 0, 0 } }; | |
631bc46c NP |
278 | |
279 | static const struct mce_derror_table mce_p8_derror_table[] = { | |
280 | { 0x00008000, false, | |
50dbabe0 | 281 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE, |
cda6618d | 282 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
283 | { 0x00004000, true, |
284 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE, | |
50dbabe0 | 285 | MCE_ECLASS_HARDWARE, |
cda6618d | 286 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
c7e790c5 | 287 | { 0x00002000, true, |
50dbabe0 | 288 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_LOAD_TIMEOUT, MCE_ECLASS_HARDWARE, |
cda6618d | 289 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
c7e790c5 NP |
290 | { 0x00001000, true, |
291 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_PAGE_TABLE_WALK_LOAD_STORE_TIMEOUT, | |
50dbabe0 | 292 | MCE_ECLASS_HARDWARE, |
cda6618d | 293 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 294 | { 0x00000800, true, |
50dbabe0 | 295 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 296 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 297 | { 0x00000400, true, |
50dbabe0 | 298 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 299 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c NP |
300 | { 0x00000200, true, |
301 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, /* SECONDARY ERAT */ | |
50dbabe0 | 302 | MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 303 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
54dbcfc2 ME |
304 | { 0x00000080, true, |
305 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, /* Before PARITY */ | |
50dbabe0 | 306 | MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 307 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 308 | { 0x00000100, true, |
50dbabe0 | 309 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
cda6618d MS |
310 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
311 | { 0, false, 0, 0, 0, 0, 0 } }; | |
631bc46c NP |
312 | |
313 | static const struct mce_derror_table mce_p9_derror_table[] = { | |
314 | { 0x00008000, false, | |
50dbabe0 | 315 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE, |
cda6618d | 316 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
317 | { 0x00004000, true, |
318 | MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE, | |
50dbabe0 | 319 | MCE_ECLASS_HARDWARE, |
cda6618d | 320 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 321 | { 0x00002000, true, |
50dbabe0 | 322 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_LOAD_TIMEOUT, MCE_ECLASS_HARDWARE, |
cda6618d | 323 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
324 | { 0x00001000, true, |
325 | MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_PAGE_TABLE_WALK_LOAD_STORE_TIMEOUT, | |
50dbabe0 | 326 | MCE_ECLASS_HARDWARE, |
cda6618d | 327 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 328 | { 0x00000800, true, |
50dbabe0 | 329 | MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 330 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 331 | { 0x00000400, true, |
50dbabe0 | 332 | MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 333 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 334 | { 0x00000200, false, |
50dbabe0 | 335 | MCE_ERROR_TYPE_USER, MCE_USER_ERROR_TLBIE, MCE_ECLASS_SOFTWARE, |
cda6618d | 336 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
54dbcfc2 ME |
337 | { 0x00000080, true, |
338 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, /* Before PARITY */ | |
50dbabe0 | 339 | MCE_ECLASS_SOFT_INDETERMINATE, |
cda6618d | 340 | MCE_INITIATOR_CPU, MCE_SEV_WARNING, true }, |
631bc46c | 341 | { 0x00000100, true, |
50dbabe0 | 342 | MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE, |
cda6618d | 343 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 344 | { 0x00000040, true, |
50dbabe0 | 345 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_LOAD, MCE_ECLASS_HARDWARE, |
cda6618d | 346 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
347 | { 0x00000020, false, |
348 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE, | |
50dbabe0 | 349 | MCE_ECLASS_HARDWARE, |
cda6618d | 350 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c NP |
351 | { 0x00000010, false, |
352 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE_FOREIGN, | |
50dbabe0 | 353 | MCE_ECLASS_HARDWARE, |
cda6618d | 354 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
631bc46c | 355 | { 0x00000008, false, |
50dbabe0 | 356 | MCE_ERROR_TYPE_RA, MCE_RA_ERROR_LOAD_STORE_FOREIGN, MCE_ECLASS_HARDWARE, |
cda6618d MS |
357 | MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true }, |
358 | { 0, false, 0, 0, 0, 0, 0 } }; | |
631bc46c | 359 | |
ba41e1e1 BS |
360 | static int mce_find_instr_ea_and_pfn(struct pt_regs *regs, uint64_t *addr, |
361 | uint64_t *phys_addr) | |
362 | { | |
363 | /* | |
364 | * Carefully look at the NIP to determine | |
365 | * the instruction to analyse. Reading the NIP | |
366 | * in real-mode is tricky and can lead to recursive | |
367 | * faults | |
368 | */ | |
369 | int instr; | |
370 | unsigned long pfn, instr_addr; | |
371 | struct instruction_op op; | |
372 | struct pt_regs tmp = *regs; | |
373 | ||
374 | pfn = addr_to_pfn(regs, regs->nip); | |
375 | if (pfn != ULONG_MAX) { | |
376 | instr_addr = (pfn << PAGE_SHIFT) + (regs->nip & ~PAGE_MASK); | |
377 | instr = *(unsigned int *)(instr_addr); | |
378 | if (!analyse_instr(&op, &tmp, instr)) { | |
379 | pfn = addr_to_pfn(regs, op.ea); | |
380 | *addr = op.ea; | |
381 | *phys_addr = (pfn << PAGE_SHIFT); | |
382 | return 0; | |
383 | } | |
384 | /* | |
385 | * analyse_instr() might fail if the instruction | |
386 | * is not a load/store, although this is unexpected | |
387 | * for load/store errors or if we got the NIP | |
388 | * wrong | |
389 | */ | |
390 | } | |
391 | *addr = 0; | |
392 | return -1; | |
393 | } | |
394 | ||
755309be | 395 | static int mce_handle_ierror(struct pt_regs *regs, |
631bc46c | 396 | const struct mce_ierror_table table[], |
01eaac2b BS |
397 | struct mce_error_info *mce_err, uint64_t *addr, |
398 | uint64_t *phys_addr) | |
e22a2274 | 399 | { |
631bc46c | 400 | uint64_t srr1 = regs->msr; |
755309be | 401 | int handled = 0; |
631bc46c NP |
402 | int i; |
403 | ||
404 | *addr = 0; | |
405 | ||
406 | for (i = 0; table[i].srr1_mask; i++) { | |
407 | if ((srr1 & table[i].srr1_mask) != table[i].srr1_value) | |
408 | continue; | |
409 | ||
755309be NP |
410 | /* attempt to correct the error */ |
411 | switch (table[i].error_type) { | |
412 | case MCE_ERROR_TYPE_SLB: | |
413 | handled = mce_flush(MCE_FLUSH_SLB); | |
414 | break; | |
415 | case MCE_ERROR_TYPE_ERAT: | |
416 | handled = mce_flush(MCE_FLUSH_ERAT); | |
417 | break; | |
418 | case MCE_ERROR_TYPE_TLB: | |
419 | handled = mce_flush(MCE_FLUSH_TLB); | |
420 | break; | |
421 | } | |
422 | ||
423 | /* now fill in mce_error_info */ | |
631bc46c | 424 | mce_err->error_type = table[i].error_type; |
50dbabe0 | 425 | mce_err->error_class = table[i].error_class; |
631bc46c NP |
426 | switch (table[i].error_type) { |
427 | case MCE_ERROR_TYPE_UE: | |
428 | mce_err->u.ue_error_type = table[i].error_subtype; | |
429 | break; | |
430 | case MCE_ERROR_TYPE_SLB: | |
431 | mce_err->u.slb_error_type = table[i].error_subtype; | |
432 | break; | |
433 | case MCE_ERROR_TYPE_ERAT: | |
434 | mce_err->u.erat_error_type = table[i].error_subtype; | |
435 | break; | |
436 | case MCE_ERROR_TYPE_TLB: | |
437 | mce_err->u.tlb_error_type = table[i].error_subtype; | |
438 | break; | |
439 | case MCE_ERROR_TYPE_USER: | |
440 | mce_err->u.user_error_type = table[i].error_subtype; | |
441 | break; | |
442 | case MCE_ERROR_TYPE_RA: | |
443 | mce_err->u.ra_error_type = table[i].error_subtype; | |
444 | break; | |
445 | case MCE_ERROR_TYPE_LINK: | |
446 | mce_err->u.link_error_type = table[i].error_subtype; | |
447 | break; | |
448 | } | |
cda6618d | 449 | mce_err->sync_error = table[i].sync_error; |
631bc46c NP |
450 | mce_err->severity = table[i].severity; |
451 | mce_err->initiator = table[i].initiator; | |
01eaac2b | 452 | if (table[i].nip_valid) { |
631bc46c | 453 | *addr = regs->nip; |
cda6618d | 454 | if (mce_err->sync_error && |
01eaac2b BS |
455 | table[i].error_type == MCE_ERROR_TYPE_UE) { |
456 | unsigned long pfn; | |
457 | ||
458 | if (get_paca()->in_mce < MAX_MCE_DEPTH) { | |
459 | pfn = addr_to_pfn(regs, regs->nip); | |
460 | if (pfn != ULONG_MAX) { | |
461 | *phys_addr = | |
462 | (pfn << PAGE_SHIFT); | |
01eaac2b BS |
463 | } |
464 | } | |
465 | } | |
466 | } | |
755309be | 467 | return handled; |
e22a2274 | 468 | } |
e22a2274 | 469 | |
631bc46c | 470 | mce_err->error_type = MCE_ERROR_TYPE_UNKNOWN; |
50dbabe0 | 471 | mce_err->error_class = MCE_ECLASS_UNKNOWN; |
cda6618d | 472 | mce_err->severity = MCE_SEV_SEVERE; |
631bc46c | 473 | mce_err->initiator = MCE_INITIATOR_CPU; |
cda6618d | 474 | mce_err->sync_error = true; |
755309be NP |
475 | |
476 | return 0; | |
36df96f8 MS |
477 | } |
478 | ||
755309be | 479 | static int mce_handle_derror(struct pt_regs *regs, |
631bc46c | 480 | const struct mce_derror_table table[], |
ba41e1e1 BS |
481 | struct mce_error_info *mce_err, uint64_t *addr, |
482 | uint64_t *phys_addr) | |
36df96f8 | 483 | { |
631bc46c | 484 | uint64_t dsisr = regs->dsisr; |
755309be NP |
485 | int handled = 0; |
486 | int found = 0; | |
631bc46c NP |
487 | int i; |
488 | ||
489 | *addr = 0; | |
490 | ||
491 | for (i = 0; table[i].dsisr_value; i++) { | |
492 | if (!(dsisr & table[i].dsisr_value)) | |
493 | continue; | |
494 | ||
755309be NP |
495 | /* attempt to correct the error */ |
496 | switch (table[i].error_type) { | |
497 | case MCE_ERROR_TYPE_SLB: | |
498 | if (mce_flush(MCE_FLUSH_SLB)) | |
499 | handled = 1; | |
500 | break; | |
501 | case MCE_ERROR_TYPE_ERAT: | |
502 | if (mce_flush(MCE_FLUSH_ERAT)) | |
503 | handled = 1; | |
504 | break; | |
505 | case MCE_ERROR_TYPE_TLB: | |
506 | if (mce_flush(MCE_FLUSH_TLB)) | |
507 | handled = 1; | |
508 | break; | |
509 | } | |
510 | ||
511 | /* | |
512 | * Attempt to handle multiple conditions, but only return | |
513 | * one. Ensure uncorrectable errors are first in the table | |
514 | * to match. | |
515 | */ | |
516 | if (found) | |
517 | continue; | |
518 | ||
519 | /* now fill in mce_error_info */ | |
631bc46c | 520 | mce_err->error_type = table[i].error_type; |
50dbabe0 | 521 | mce_err->error_class = table[i].error_class; |
631bc46c NP |
522 | switch (table[i].error_type) { |
523 | case MCE_ERROR_TYPE_UE: | |
524 | mce_err->u.ue_error_type = table[i].error_subtype; | |
525 | break; | |
526 | case MCE_ERROR_TYPE_SLB: | |
527 | mce_err->u.slb_error_type = table[i].error_subtype; | |
528 | break; | |
529 | case MCE_ERROR_TYPE_ERAT: | |
530 | mce_err->u.erat_error_type = table[i].error_subtype; | |
531 | break; | |
532 | case MCE_ERROR_TYPE_TLB: | |
533 | mce_err->u.tlb_error_type = table[i].error_subtype; | |
534 | break; | |
535 | case MCE_ERROR_TYPE_USER: | |
536 | mce_err->u.user_error_type = table[i].error_subtype; | |
537 | break; | |
538 | case MCE_ERROR_TYPE_RA: | |
539 | mce_err->u.ra_error_type = table[i].error_subtype; | |
540 | break; | |
541 | case MCE_ERROR_TYPE_LINK: | |
542 | mce_err->u.link_error_type = table[i].error_subtype; | |
543 | break; | |
544 | } | |
cda6618d | 545 | mce_err->sync_error = table[i].sync_error; |
631bc46c NP |
546 | mce_err->severity = table[i].severity; |
547 | mce_err->initiator = table[i].initiator; | |
548 | if (table[i].dar_valid) | |
549 | *addr = regs->dar; | |
cda6618d | 550 | else if (mce_err->sync_error && |
ba41e1e1 BS |
551 | table[i].error_type == MCE_ERROR_TYPE_UE) { |
552 | /* | |
553 | * We do a maximum of 4 nested MCE calls, see | |
554 | * kernel/exception-64s.h | |
555 | */ | |
556 | if (get_paca()->in_mce < MAX_MCE_DEPTH) | |
75ecfb49 | 557 | mce_find_instr_ea_and_pfn(regs, addr, phys_addr); |
ba41e1e1 | 558 | } |
755309be | 559 | found = 1; |
36df96f8 | 560 | } |
631bc46c | 561 | |
755309be NP |
562 | if (found) |
563 | return handled; | |
564 | ||
631bc46c | 565 | mce_err->error_type = MCE_ERROR_TYPE_UNKNOWN; |
50dbabe0 | 566 | mce_err->error_class = MCE_ECLASS_UNKNOWN; |
cda6618d | 567 | mce_err->severity = MCE_SEV_SEVERE; |
631bc46c | 568 | mce_err->initiator = MCE_INITIATOR_CPU; |
cda6618d | 569 | mce_err->sync_error = true; |
755309be NP |
570 | |
571 | return 0; | |
36df96f8 MS |
572 | } |
573 | ||
55672ecf MS |
574 | static long mce_handle_ue_error(struct pt_regs *regs) |
575 | { | |
576 | long handled = 0; | |
577 | ||
578 | /* | |
579 | * On specific SCOM read via MMIO we may get a machine check | |
580 | * exception with SRR0 pointing inside opal. If that is the | |
581 | * case OPAL may have recovery address to re-read SCOM data in | |
582 | * different way and hence we can recover from this MC. | |
583 | */ | |
584 | ||
585 | if (ppc_md.mce_check_early_recovery) { | |
586 | if (ppc_md.mce_check_early_recovery(regs)) | |
587 | handled = 1; | |
588 | } | |
589 | return handled; | |
590 | } | |
591 | ||
755309be NP |
592 | static long mce_handle_error(struct pt_regs *regs, |
593 | const struct mce_derror_table dtable[], | |
594 | const struct mce_ierror_table itable[]) | |
e22a2274 | 595 | { |
755309be | 596 | struct mce_error_info mce_err = { 0 }; |
75ecfb49 | 597 | uint64_t addr, phys_addr = ULONG_MAX; |
755309be NP |
598 | uint64_t srr1 = regs->msr; |
599 | long handled; | |
e22a2274 | 600 | |
755309be | 601 | if (SRR1_MC_LOADSTORE(srr1)) |
ba41e1e1 BS |
602 | handled = mce_handle_derror(regs, dtable, &mce_err, &addr, |
603 | &phys_addr); | |
755309be | 604 | else |
01eaac2b BS |
605 | handled = mce_handle_ierror(regs, itable, &mce_err, &addr, |
606 | &phys_addr); | |
631bc46c | 607 | |
755309be | 608 | if (!handled && mce_err.error_type == MCE_ERROR_TYPE_UE) |
55672ecf MS |
609 | handled = mce_handle_ue_error(regs); |
610 | ||
ba41e1e1 | 611 | save_mce_event(regs, handled, &mce_err, regs->nip, addr, phys_addr); |
55672ecf | 612 | |
ae744f34 MS |
613 | return handled; |
614 | } | |
7b9f71f9 | 615 | |
755309be | 616 | long __machine_check_early_realmode_p7(struct pt_regs *regs) |
7b9f71f9 | 617 | { |
755309be NP |
618 | /* P7 DD1 leaves top bits of DSISR undefined */ |
619 | regs->dsisr &= 0x0000ffff; | |
7b9f71f9 | 620 | |
755309be | 621 | return mce_handle_error(regs, mce_p7_derror_table, mce_p7_ierror_table); |
7b9f71f9 NP |
622 | } |
623 | ||
755309be | 624 | long __machine_check_early_realmode_p8(struct pt_regs *regs) |
7b9f71f9 | 625 | { |
755309be | 626 | return mce_handle_error(regs, mce_p8_derror_table, mce_p8_ierror_table); |
7b9f71f9 NP |
627 | } |
628 | ||
7b9f71f9 NP |
629 | long __machine_check_early_realmode_p9(struct pt_regs *regs) |
630 | { | |
d8bd9f3f MN |
631 | /* |
632 | * On POWER9 DD2.1 and below, it's possible to get a machine check | |
bca73f59 | 633 | * caused by a paste instruction where only DSISR bit 25 is set. This |
d8bd9f3f MN |
634 | * will result in the MCE handler seeing an unknown event and the kernel |
635 | * crashing. An MCE that occurs like this is spurious, so we don't need | |
636 | * to do anything in terms of servicing it. If there is something that | |
637 | * needs to be serviced, the CPU will raise the MCE again with the | |
638 | * correct DSISR so that it can be serviced properly. So detect this | |
639 | * case and mark it as handled. | |
640 | */ | |
bca73f59 | 641 | if (SRR1_MC_LOADSTORE(regs->msr) && regs->dsisr == 0x02000000) |
d8bd9f3f MN |
642 | return 1; |
643 | ||
755309be | 644 | return mce_handle_error(regs, mce_p9_derror_table, mce_p9_ierror_table); |
7b9f71f9 | 645 | } |