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> |
e22a2274 MS |
30 | |
31 | /* flush SLBs and reload */ | |
32 | static void flush_and_reload_slb(void) | |
33 | { | |
34 | struct slb_shadow *slb; | |
35 | unsigned long i, n; | |
36 | ||
37 | /* Invalidate all SLBs */ | |
38 | asm volatile("slbmte %0,%0; slbia" : : "r" (0)); | |
39 | ||
40 | #ifdef CONFIG_KVM_BOOK3S_HANDLER | |
41 | /* | |
42 | * If machine check is hit when in guest or in transition, we will | |
43 | * only flush the SLBs and continue. | |
44 | */ | |
45 | if (get_paca()->kvm_hstate.in_guest) | |
46 | return; | |
47 | #endif | |
48 | ||
49 | /* For host kernel, reload the SLBs from shadow SLB buffer. */ | |
50 | slb = get_slb_shadow(); | |
51 | if (!slb) | |
52 | return; | |
53 | ||
a68c33f3 | 54 | n = min_t(u32, be32_to_cpu(slb->persistent), SLB_MIN_SIZE); |
e22a2274 MS |
55 | |
56 | /* Load up the SLB entries from shadow SLB */ | |
57 | for (i = 0; i < n; i++) { | |
a68c33f3 AB |
58 | unsigned long rb = be64_to_cpu(slb->save_area[i].esid); |
59 | unsigned long rs = be64_to_cpu(slb->save_area[i].vsid); | |
e22a2274 MS |
60 | |
61 | rb = (rb & ~0xFFFul) | i; | |
62 | asm volatile("slbmte %0,%1" : : "r" (rs), "r" (rb)); | |
63 | } | |
64 | } | |
65 | ||
66 | static long mce_handle_derror(uint64_t dsisr, uint64_t slb_error_bits) | |
67 | { | |
68 | long handled = 1; | |
69 | ||
70 | /* | |
71 | * flush and reload SLBs for SLB errors and flush TLBs for TLB errors. | |
72 | * reset the error bits whenever we handle them so that at the end | |
73 | * we can check whether we handled all of them or not. | |
74 | * */ | |
75 | if (dsisr & slb_error_bits) { | |
76 | flush_and_reload_slb(); | |
77 | /* reset error bits */ | |
78 | dsisr &= ~(slb_error_bits); | |
79 | } | |
80 | if (dsisr & P7_DSISR_MC_TLB_MULTIHIT_MFTLB) { | |
81 | if (cur_cpu_spec && cur_cpu_spec->flush_tlb) | |
682e77c8 | 82 | cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET); |
e22a2274 MS |
83 | /* reset error bits */ |
84 | dsisr &= ~P7_DSISR_MC_TLB_MULTIHIT_MFTLB; | |
85 | } | |
86 | /* Any other errors we don't understand? */ | |
87 | if (dsisr & 0xffffffffUL) | |
88 | handled = 0; | |
89 | ||
90 | return handled; | |
91 | } | |
92 | ||
93 | static long mce_handle_derror_p7(uint64_t dsisr) | |
94 | { | |
95 | return mce_handle_derror(dsisr, P7_DSISR_MC_SLB_ERRORS); | |
96 | } | |
97 | ||
98 | static long mce_handle_common_ierror(uint64_t srr1) | |
99 | { | |
100 | long handled = 0; | |
101 | ||
102 | switch (P7_SRR1_MC_IFETCH(srr1)) { | |
103 | case 0: | |
104 | break; | |
105 | case P7_SRR1_MC_IFETCH_SLB_PARITY: | |
106 | case P7_SRR1_MC_IFETCH_SLB_MULTIHIT: | |
107 | /* flush and reload SLBs for SLB errors. */ | |
108 | flush_and_reload_slb(); | |
109 | handled = 1; | |
110 | break; | |
111 | case P7_SRR1_MC_IFETCH_TLB_MULTIHIT: | |
112 | if (cur_cpu_spec && cur_cpu_spec->flush_tlb) { | |
682e77c8 | 113 | cur_cpu_spec->flush_tlb(TLBIEL_INVAL_SET); |
e22a2274 MS |
114 | handled = 1; |
115 | } | |
116 | break; | |
117 | default: | |
118 | break; | |
119 | } | |
120 | ||
121 | return handled; | |
122 | } | |
123 | ||
124 | static long mce_handle_ierror_p7(uint64_t srr1) | |
125 | { | |
126 | long handled = 0; | |
127 | ||
128 | handled = mce_handle_common_ierror(srr1); | |
129 | ||
130 | if (P7_SRR1_MC_IFETCH(srr1) == P7_SRR1_MC_IFETCH_SLB_BOTH) { | |
131 | flush_and_reload_slb(); | |
132 | handled = 1; | |
133 | } | |
134 | return handled; | |
135 | } | |
136 | ||
36df96f8 MS |
137 | static void mce_get_common_ierror(struct mce_error_info *mce_err, uint64_t srr1) |
138 | { | |
139 | switch (P7_SRR1_MC_IFETCH(srr1)) { | |
140 | case P7_SRR1_MC_IFETCH_SLB_PARITY: | |
141 | mce_err->error_type = MCE_ERROR_TYPE_SLB; | |
142 | mce_err->u.slb_error_type = MCE_SLB_ERROR_PARITY; | |
143 | break; | |
144 | case P7_SRR1_MC_IFETCH_SLB_MULTIHIT: | |
145 | mce_err->error_type = MCE_ERROR_TYPE_SLB; | |
146 | mce_err->u.slb_error_type = MCE_SLB_ERROR_MULTIHIT; | |
147 | break; | |
148 | case P7_SRR1_MC_IFETCH_TLB_MULTIHIT: | |
149 | mce_err->error_type = MCE_ERROR_TYPE_TLB; | |
150 | mce_err->u.tlb_error_type = MCE_TLB_ERROR_MULTIHIT; | |
151 | break; | |
152 | case P7_SRR1_MC_IFETCH_UE: | |
153 | case P7_SRR1_MC_IFETCH_UE_IFU_INTERNAL: | |
154 | mce_err->error_type = MCE_ERROR_TYPE_UE; | |
155 | mce_err->u.ue_error_type = MCE_UE_ERROR_IFETCH; | |
156 | break; | |
157 | case P7_SRR1_MC_IFETCH_UE_TLB_RELOAD: | |
158 | mce_err->error_type = MCE_ERROR_TYPE_UE; | |
159 | mce_err->u.ue_error_type = | |
160 | MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH; | |
161 | break; | |
162 | } | |
163 | } | |
164 | ||
165 | static void mce_get_ierror_p7(struct mce_error_info *mce_err, uint64_t srr1) | |
166 | { | |
167 | mce_get_common_ierror(mce_err, srr1); | |
168 | if (P7_SRR1_MC_IFETCH(srr1) == P7_SRR1_MC_IFETCH_SLB_BOTH) { | |
169 | mce_err->error_type = MCE_ERROR_TYPE_SLB; | |
170 | mce_err->u.slb_error_type = MCE_SLB_ERROR_INDETERMINATE; | |
171 | } | |
172 | } | |
173 | ||
174 | static void mce_get_derror_p7(struct mce_error_info *mce_err, uint64_t dsisr) | |
175 | { | |
176 | if (dsisr & P7_DSISR_MC_UE) { | |
177 | mce_err->error_type = MCE_ERROR_TYPE_UE; | |
178 | mce_err->u.ue_error_type = MCE_UE_ERROR_LOAD_STORE; | |
179 | } else if (dsisr & P7_DSISR_MC_UE_TABLEWALK) { | |
180 | mce_err->error_type = MCE_ERROR_TYPE_UE; | |
181 | mce_err->u.ue_error_type = | |
182 | MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE; | |
183 | } else if (dsisr & P7_DSISR_MC_ERAT_MULTIHIT) { | |
184 | mce_err->error_type = MCE_ERROR_TYPE_ERAT; | |
185 | mce_err->u.erat_error_type = MCE_ERAT_ERROR_MULTIHIT; | |
186 | } else if (dsisr & P7_DSISR_MC_SLB_MULTIHIT) { | |
187 | mce_err->error_type = MCE_ERROR_TYPE_SLB; | |
188 | mce_err->u.slb_error_type = MCE_SLB_ERROR_MULTIHIT; | |
189 | } else if (dsisr & P7_DSISR_MC_SLB_PARITY_MFSLB) { | |
190 | mce_err->error_type = MCE_ERROR_TYPE_SLB; | |
191 | mce_err->u.slb_error_type = MCE_SLB_ERROR_PARITY; | |
192 | } else if (dsisr & P7_DSISR_MC_TLB_MULTIHIT_MFTLB) { | |
193 | mce_err->error_type = MCE_ERROR_TYPE_TLB; | |
194 | mce_err->u.tlb_error_type = MCE_TLB_ERROR_MULTIHIT; | |
195 | } else if (dsisr & P7_DSISR_MC_SLB_MULTIHIT_PARITY) { | |
196 | mce_err->error_type = MCE_ERROR_TYPE_SLB; | |
197 | mce_err->u.slb_error_type = MCE_SLB_ERROR_INDETERMINATE; | |
198 | } | |
199 | } | |
200 | ||
55672ecf MS |
201 | static long mce_handle_ue_error(struct pt_regs *regs) |
202 | { | |
203 | long handled = 0; | |
204 | ||
205 | /* | |
206 | * On specific SCOM read via MMIO we may get a machine check | |
207 | * exception with SRR0 pointing inside opal. If that is the | |
208 | * case OPAL may have recovery address to re-read SCOM data in | |
209 | * different way and hence we can recover from this MC. | |
210 | */ | |
211 | ||
212 | if (ppc_md.mce_check_early_recovery) { | |
213 | if (ppc_md.mce_check_early_recovery(regs)) | |
214 | handled = 1; | |
215 | } | |
216 | return handled; | |
217 | } | |
218 | ||
e22a2274 MS |
219 | long __machine_check_early_realmode_p7(struct pt_regs *regs) |
220 | { | |
55672ecf | 221 | uint64_t srr1, nip, addr; |
e22a2274 | 222 | long handled = 1; |
36df96f8 | 223 | struct mce_error_info mce_error_info = { 0 }; |
e22a2274 MS |
224 | |
225 | srr1 = regs->msr; | |
55672ecf | 226 | nip = regs->nip; |
e22a2274 | 227 | |
36df96f8 MS |
228 | /* |
229 | * Handle memory errors depending whether this was a load/store or | |
230 | * ifetch exception. Also, populate the mce error_type and | |
231 | * type-specific error_type from either SRR1 or DSISR, depending | |
232 | * whether this was a load/store or ifetch exception | |
233 | */ | |
234 | if (P7_SRR1_MC_LOADSTORE(srr1)) { | |
e22a2274 | 235 | handled = mce_handle_derror_p7(regs->dsisr); |
36df96f8 MS |
236 | mce_get_derror_p7(&mce_error_info, regs->dsisr); |
237 | addr = regs->dar; | |
238 | } else { | |
e22a2274 | 239 | handled = mce_handle_ierror_p7(srr1); |
36df96f8 MS |
240 | mce_get_ierror_p7(&mce_error_info, srr1); |
241 | addr = regs->nip; | |
242 | } | |
e22a2274 | 243 | |
55672ecf MS |
244 | /* Handle UE error. */ |
245 | if (mce_error_info.error_type == MCE_ERROR_TYPE_UE) | |
246 | handled = mce_handle_ue_error(regs); | |
247 | ||
248 | save_mce_event(regs, handled, &mce_error_info, nip, addr); | |
e22a2274 MS |
249 | return handled; |
250 | } | |
ae744f34 | 251 | |
36df96f8 MS |
252 | static void mce_get_ierror_p8(struct mce_error_info *mce_err, uint64_t srr1) |
253 | { | |
254 | mce_get_common_ierror(mce_err, srr1); | |
255 | if (P7_SRR1_MC_IFETCH(srr1) == P8_SRR1_MC_IFETCH_ERAT_MULTIHIT) { | |
256 | mce_err->error_type = MCE_ERROR_TYPE_ERAT; | |
257 | mce_err->u.erat_error_type = MCE_ERAT_ERROR_MULTIHIT; | |
258 | } | |
259 | } | |
260 | ||
261 | static void mce_get_derror_p8(struct mce_error_info *mce_err, uint64_t dsisr) | |
262 | { | |
263 | mce_get_derror_p7(mce_err, dsisr); | |
264 | if (dsisr & P8_DSISR_MC_ERAT_MULTIHIT_SEC) { | |
265 | mce_err->error_type = MCE_ERROR_TYPE_ERAT; | |
266 | mce_err->u.erat_error_type = MCE_ERAT_ERROR_MULTIHIT; | |
267 | } | |
268 | } | |
269 | ||
ae744f34 MS |
270 | static long mce_handle_ierror_p8(uint64_t srr1) |
271 | { | |
272 | long handled = 0; | |
273 | ||
274 | handled = mce_handle_common_ierror(srr1); | |
275 | ||
276 | if (P7_SRR1_MC_IFETCH(srr1) == P8_SRR1_MC_IFETCH_ERAT_MULTIHIT) { | |
277 | flush_and_reload_slb(); | |
278 | handled = 1; | |
279 | } | |
280 | return handled; | |
281 | } | |
282 | ||
283 | static long mce_handle_derror_p8(uint64_t dsisr) | |
284 | { | |
285 | return mce_handle_derror(dsisr, P8_DSISR_MC_SLB_ERRORS); | |
286 | } | |
287 | ||
288 | long __machine_check_early_realmode_p8(struct pt_regs *regs) | |
289 | { | |
55672ecf | 290 | uint64_t srr1, nip, addr; |
ae744f34 | 291 | long handled = 1; |
36df96f8 | 292 | struct mce_error_info mce_error_info = { 0 }; |
ae744f34 MS |
293 | |
294 | srr1 = regs->msr; | |
55672ecf | 295 | nip = regs->nip; |
ae744f34 | 296 | |
36df96f8 | 297 | if (P7_SRR1_MC_LOADSTORE(srr1)) { |
ae744f34 | 298 | handled = mce_handle_derror_p8(regs->dsisr); |
36df96f8 MS |
299 | mce_get_derror_p8(&mce_error_info, regs->dsisr); |
300 | addr = regs->dar; | |
301 | } else { | |
ae744f34 | 302 | handled = mce_handle_ierror_p8(srr1); |
36df96f8 MS |
303 | mce_get_ierror_p8(&mce_error_info, srr1); |
304 | addr = regs->nip; | |
305 | } | |
ae744f34 | 306 | |
55672ecf MS |
307 | /* Handle UE error. */ |
308 | if (mce_error_info.error_type == MCE_ERROR_TYPE_UE) | |
309 | handled = mce_handle_ue_error(regs); | |
310 | ||
311 | save_mce_event(regs, handled, &mce_error_info, nip, addr); | |
ae744f34 MS |
312 | return handled; |
313 | } |