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 31static 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 */ 63void __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 */ 75void __flush_tlb_power8(unsigned int action) 76{ 77 flush_tlb_206(POWER8_TLB_SETS, action); 78} 79 80/* flush SLBs and reload */ 81static 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 115static 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 142static long mce_handle_derror_p7(uint64_t dsisr) 143{ 144 return mce_handle_derror(dsisr, P7_DSISR_MC_SLB_ERRORS); 145} 146 147static 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 173static 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 186static 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 214static 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 223static 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 250static 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 268long __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 301static 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 310static 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 319static 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 332static long mce_handle_derror_p8(uint64_t dsisr) 333{ 334 return mce_handle_derror(dsisr, P8_DSISR_MC_SLB_ERRORS); 335} 336 337long __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