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