xref: /openbmc/linux/arch/powerpc/kernel/mce_power.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2e22a2274SMahesh Salgaonkar /*
3e22a2274SMahesh Salgaonkar  * Machine check exception handling CPU-side for power7 and power8
4e22a2274SMahesh Salgaonkar  *
5e22a2274SMahesh Salgaonkar  * Copyright 2013 IBM Corporation
6e22a2274SMahesh Salgaonkar  * Author: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
7e22a2274SMahesh Salgaonkar  */
8e22a2274SMahesh Salgaonkar 
9e22a2274SMahesh Salgaonkar #undef DEBUG
10e22a2274SMahesh Salgaonkar #define pr_fmt(fmt) "mce_power: " fmt
11e22a2274SMahesh Salgaonkar 
12e22a2274SMahesh Salgaonkar #include <linux/types.h>
13e22a2274SMahesh Salgaonkar #include <linux/ptrace.h>
14895e3dceSBalbir Singh #include <linux/extable.h>
1565fddcfcSMike Rapoport #include <linux/pgtable.h>
16e22a2274SMahesh Salgaonkar #include <asm/mmu.h>
17e22a2274SMahesh Salgaonkar #include <asm/mce.h>
1855672ecfSMahesh Salgaonkar #include <asm/machdep.h>
19ba41e1e1SBalbir Singh #include <asm/pte-walk.h>
20ba41e1e1SBalbir Singh #include <asm/sstep.h>
21ba41e1e1SBalbir Singh #include <asm/exception-64s.h>
22895e3dceSBalbir Singh #include <asm/extable.h>
2394afd069SJordan Niethe #include <asm/inst.h>
24ba41e1e1SBalbir Singh 
25ba41e1e1SBalbir Singh /*
26ba41e1e1SBalbir Singh  * Convert an address related to an mm to a PFN. NOTE: we are in real
27ba41e1e1SBalbir Singh  * mode, we could potentially race with page table updates.
28ba41e1e1SBalbir Singh  */
addr_to_pfn(struct pt_regs * regs,unsigned long addr)297f177f98SGanesh Goudar unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr)
30ba41e1e1SBalbir Singh {
310da81b65SAneesh Kumar K.V 	pte_t *ptep, pte;
3299ead78aSBalbir Singh 	unsigned int shift;
33d9101bfaSAneesh Kumar K.V 	unsigned long pfn, flags;
34ba41e1e1SBalbir Singh 	struct mm_struct *mm;
35ba41e1e1SBalbir Singh 
36ba41e1e1SBalbir Singh 	if (user_mode(regs))
37ba41e1e1SBalbir Singh 		mm = current->mm;
38ba41e1e1SBalbir Singh 	else
39ba41e1e1SBalbir Singh 		mm = &init_mm;
40ba41e1e1SBalbir Singh 
41ba41e1e1SBalbir Singh 	local_irq_save(flags);
4299ead78aSBalbir Singh 	ptep = __find_linux_pte(mm->pgd, addr, NULL, &shift);
430da81b65SAneesh Kumar K.V 	if (!ptep) {
440da81b65SAneesh Kumar K.V 		pfn = ULONG_MAX;
450da81b65SAneesh Kumar K.V 		goto out;
460da81b65SAneesh Kumar K.V 	}
470da81b65SAneesh Kumar K.V 	pte = READ_ONCE(*ptep);
4899ead78aSBalbir Singh 
490da81b65SAneesh Kumar K.V 	if (!pte_present(pte) || pte_special(pte)) {
50d9101bfaSAneesh Kumar K.V 		pfn = ULONG_MAX;
51d9101bfaSAneesh Kumar K.V 		goto out;
5299ead78aSBalbir Singh 	}
5399ead78aSBalbir Singh 
54d9101bfaSAneesh Kumar K.V 	if (shift <= PAGE_SHIFT)
550da81b65SAneesh Kumar K.V 		pfn = pte_pfn(pte);
56d9101bfaSAneesh Kumar K.V 	else {
57d9101bfaSAneesh Kumar K.V 		unsigned long rpnmask = (1ul << shift) - PAGE_SIZE;
580da81b65SAneesh Kumar K.V 		pfn = pte_pfn(__pte(pte_val(pte) | (addr & rpnmask)));
59d9101bfaSAneesh Kumar K.V 	}
60d9101bfaSAneesh Kumar K.V out:
61d9101bfaSAneesh Kumar K.V 	local_irq_restore(flags);
62d9101bfaSAneesh Kumar K.V 	return pfn;
63ba41e1e1SBalbir Singh }
64e22a2274SMahesh Salgaonkar 
mce_in_guest(void)650ce23826SNicholas Piggin static bool mce_in_guest(void)
660ce23826SNicholas Piggin {
670ce23826SNicholas Piggin #ifdef CONFIG_KVM_BOOK3S_HANDLER
680ce23826SNicholas Piggin 	/*
690ce23826SNicholas Piggin 	 * If machine check is hit when in guest context or low level KVM
700ce23826SNicholas Piggin 	 * code, avoid looking up any translations or making any attempts
710ce23826SNicholas Piggin 	 * to recover, just record the event and pass to KVM.
720ce23826SNicholas Piggin 	 */
730ce23826SNicholas Piggin 	if (get_paca()->kvm_hstate.in_guest)
740ce23826SNicholas Piggin 		return true;
750ce23826SNicholas Piggin #endif
760ce23826SNicholas Piggin 	return false;
770ce23826SNicholas Piggin }
780ce23826SNicholas Piggin 
79e22a2274SMahesh Salgaonkar /* flush SLBs and reload */
80387e220aSNicholas Piggin #ifdef CONFIG_PPC_64S_HASH_MMU
flush_and_reload_slb(void)81a43c1590SMahesh Salgaonkar void flush_and_reload_slb(void)
82e22a2274SMahesh Salgaonkar {
83e7e81847SNicholas Piggin 	if (early_radix_enabled())
84e22a2274SMahesh Salgaonkar 		return;
85e22a2274SMahesh Salgaonkar 
86310dce62SNicholas Piggin 	/* Invalidate all SLBs */
87310dce62SNicholas Piggin 	slb_flush_all_realmode();
88310dce62SNicholas Piggin 
89e7e81847SNicholas Piggin 	/*
90e7e81847SNicholas Piggin 	 * This probably shouldn't happen, but it may be possible it's
91e7e81847SNicholas Piggin 	 * called in early boot before SLB shadows are allocated.
92e7e81847SNicholas Piggin 	 */
93e7e81847SNicholas Piggin 	if (!get_slb_shadow())
94e7e81847SNicholas Piggin 		return;
95e22a2274SMahesh Salgaonkar 
96e7e81847SNicholas Piggin 	slb_restore_bolted_realmode();
97e22a2274SMahesh Salgaonkar }
98caca285eSAneesh Kumar K.V #endif
99e22a2274SMahesh Salgaonkar 
flush_erat(void)10082f70a05SNicholas Piggin void flush_erat(void)
1017b9f71f9SNicholas Piggin {
102387e220aSNicholas Piggin #ifdef CONFIG_PPC_64S_HASH_MMU
103bc276ecbSNicholas Piggin 	if (!early_cpu_has_feature(CPU_FTR_ARCH_300)) {
104bc276ecbSNicholas Piggin 		flush_and_reload_slb();
105bc276ecbSNicholas Piggin 		return;
106bc276ecbSNicholas Piggin 	}
107bc276ecbSNicholas Piggin #endif
108fe7946ceSNicholas Piggin 	asm volatile(PPC_ISA_3_0_INVALIDATE_ERAT : : :"memory");
1097b9f71f9SNicholas Piggin }
1107b9f71f9SNicholas Piggin 
1117b9f71f9SNicholas Piggin #define MCE_FLUSH_SLB 1
1127b9f71f9SNicholas Piggin #define MCE_FLUSH_TLB 2
1137b9f71f9SNicholas Piggin #define MCE_FLUSH_ERAT 3
1147b9f71f9SNicholas Piggin 
mce_flush(int what)1157b9f71f9SNicholas Piggin static int mce_flush(int what)
1167b9f71f9SNicholas Piggin {
117387e220aSNicholas Piggin #ifdef CONFIG_PPC_64S_HASH_MMU
1187b9f71f9SNicholas Piggin 	if (what == MCE_FLUSH_SLB) {
1197b9f71f9SNicholas Piggin 		flush_and_reload_slb();
1207b9f71f9SNicholas Piggin 		return 1;
1217b9f71f9SNicholas Piggin 	}
1227b9f71f9SNicholas Piggin #endif
1237b9f71f9SNicholas Piggin 	if (what == MCE_FLUSH_ERAT) {
1247b9f71f9SNicholas Piggin 		flush_erat();
1257b9f71f9SNicholas Piggin 		return 1;
1267b9f71f9SNicholas Piggin 	}
1277b9f71f9SNicholas Piggin 	if (what == MCE_FLUSH_TLB) {
128d4748276SNicholas Piggin 		tlbiel_all();
1297b9f71f9SNicholas Piggin 		return 1;
1307b9f71f9SNicholas Piggin 	}
1317b9f71f9SNicholas Piggin 
1327b9f71f9SNicholas Piggin 	return 0;
1337b9f71f9SNicholas Piggin }
1347b9f71f9SNicholas Piggin 
135755309beSNicholas Piggin #define SRR1_MC_LOADSTORE(srr1)	((srr1) & PPC_BIT(42))
13658c8d17fSNicholas Piggin 
137631bc46cSNicholas Piggin struct mce_ierror_table {
138631bc46cSNicholas Piggin 	unsigned long srr1_mask;
139631bc46cSNicholas Piggin 	unsigned long srr1_value;
140631bc46cSNicholas Piggin 	bool nip_valid; /* nip is a valid indicator of faulting address */
141631bc46cSNicholas Piggin 	unsigned int error_type;
142631bc46cSNicholas Piggin 	unsigned int error_subtype;
14350dbabe0SMahesh Salgaonkar 	unsigned int error_class;
144631bc46cSNicholas Piggin 	unsigned int initiator;
145631bc46cSNicholas Piggin 	unsigned int severity;
146cda6618dSMahesh Salgaonkar 	bool sync_error;
147631bc46cSNicholas Piggin };
148631bc46cSNicholas Piggin 
149631bc46cSNicholas Piggin static const struct mce_ierror_table mce_p7_ierror_table[] = {
150631bc46cSNicholas Piggin { 0x00000000001c0000, 0x0000000000040000, true,
15150dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_UE,  MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE,
152cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
153631bc46cSNicholas Piggin { 0x00000000001c0000, 0x0000000000080000, true,
15450dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
155cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
156631bc46cSNicholas Piggin { 0x00000000001c0000, 0x00000000000c0000, true,
15750dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
158cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_WARNING, true },
159631bc46cSNicholas Piggin { 0x00000000001c0000, 0x0000000000100000, true,
160631bc46cSNicholas Piggin   MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_INDETERMINATE, /* BOTH */
16150dbabe0SMahesh Salgaonkar   MCE_ECLASS_SOFT_INDETERMINATE,
162cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_WARNING, true },
163631bc46cSNicholas Piggin { 0x00000000001c0000, 0x0000000000140000, true,
16450dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
165cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_WARNING, true },
166631bc46cSNicholas Piggin { 0x00000000001c0000, 0x0000000000180000, true,
16750dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_UE,  MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_HARDWARE,
168cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
169631bc46cSNicholas Piggin { 0x00000000001c0000, 0x00000000001c0000, true,
17050dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_UE,  MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE,
171cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
172cda6618dSMahesh Salgaonkar { 0, 0, 0, 0, 0, 0, 0 } };
173631bc46cSNicholas Piggin 
174631bc46cSNicholas Piggin static const struct mce_ierror_table mce_p8_ierror_table[] = {
175c7e790c5SNicholas Piggin { 0x00000000081c0000, 0x0000000000040000, true,
17650dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_UE,  MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE,
177cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
178c7e790c5SNicholas Piggin { 0x00000000081c0000, 0x0000000000080000, true,
17950dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
180cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
181c7e790c5SNicholas Piggin { 0x00000000081c0000, 0x00000000000c0000, true,
18250dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
183cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_WARNING, true },
184c7e790c5SNicholas Piggin { 0x00000000081c0000, 0x0000000000100000, true,
18550dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
186cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_WARNING, true },
187c7e790c5SNicholas Piggin { 0x00000000081c0000, 0x0000000000140000, true,
18850dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
189cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_WARNING, true },
190c7e790c5SNicholas Piggin { 0x00000000081c0000, 0x0000000000180000, true,
191631bc46cSNicholas Piggin   MCE_ERROR_TYPE_UE,  MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH,
19250dbabe0SMahesh Salgaonkar   MCE_ECLASS_HARDWARE,
193cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
194c7e790c5SNicholas Piggin { 0x00000000081c0000, 0x00000000001c0000, true,
19550dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_UE,  MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE,
196cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
197c7e790c5SNicholas Piggin { 0x00000000081c0000, 0x0000000008000000, true,
19850dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_IFETCH_TIMEOUT, MCE_ECLASS_HARDWARE,
199cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
200c7e790c5SNicholas Piggin { 0x00000000081c0000, 0x0000000008040000, true,
201c7e790c5SNicholas Piggin   MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_PAGE_TABLE_WALK_IFETCH_TIMEOUT,
20250dbabe0SMahesh Salgaonkar   MCE_ECLASS_HARDWARE,
203cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
204cda6618dSMahesh Salgaonkar { 0, 0, 0, 0, 0, 0, 0 } };
205631bc46cSNicholas Piggin 
206631bc46cSNicholas Piggin static const struct mce_ierror_table mce_p9_ierror_table[] = {
207631bc46cSNicholas Piggin { 0x00000000081c0000, 0x0000000000040000, true,
20850dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_UE,  MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE,
209cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
210631bc46cSNicholas Piggin { 0x00000000081c0000, 0x0000000000080000, true,
21150dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
212cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
213631bc46cSNicholas Piggin { 0x00000000081c0000, 0x00000000000c0000, true,
21450dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
215cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_WARNING, true },
216631bc46cSNicholas Piggin { 0x00000000081c0000, 0x0000000000100000, true,
21750dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
218cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_WARNING, true },
219631bc46cSNicholas Piggin { 0x00000000081c0000, 0x0000000000140000, true,
22050dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
221cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_WARNING, true },
222631bc46cSNicholas Piggin { 0x00000000081c0000, 0x0000000000180000, true,
22350dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_UE,  MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_HARDWARE,
224cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
22590df4bfbSNicholas Piggin { 0x00000000081c0000, 0x00000000001c0000, true,
22650dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_RA,  MCE_RA_ERROR_IFETCH_FOREIGN, MCE_ECLASS_SOFTWARE,
227cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
228631bc46cSNicholas Piggin { 0x00000000081c0000, 0x0000000008000000, true,
22950dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_IFETCH_TIMEOUT, MCE_ECLASS_HARDWARE,
230cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
231631bc46cSNicholas Piggin { 0x00000000081c0000, 0x0000000008040000, true,
232631bc46cSNicholas Piggin   MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_PAGE_TABLE_WALK_IFETCH_TIMEOUT,
23350dbabe0SMahesh Salgaonkar   MCE_ECLASS_HARDWARE,
234cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
235631bc46cSNicholas Piggin { 0x00000000081c0000, 0x00000000080c0000, true,
23650dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_RA,  MCE_RA_ERROR_IFETCH, MCE_ECLASS_SOFTWARE,
237cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
238631bc46cSNicholas Piggin { 0x00000000081c0000, 0x0000000008100000, true,
23950dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_RA,  MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_SOFTWARE,
240cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
241631bc46cSNicholas Piggin { 0x00000000081c0000, 0x0000000008140000, false,
24250dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_RA,  MCE_RA_ERROR_STORE, MCE_ECLASS_HARDWARE,
243cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_FATAL, false }, /* ASYNC is fatal */
244631bc46cSNicholas Piggin { 0x00000000081c0000, 0x0000000008180000, false,
245631bc46cSNicholas Piggin   MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_STORE_TIMEOUT,
246cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_FATAL, false }, /* ASYNC is fatal */
24750dbabe0SMahesh Salgaonkar { 0x00000000081c0000, 0x00000000081c0000, true, MCE_ECLASS_HARDWARE,
248631bc46cSNicholas Piggin   MCE_ERROR_TYPE_RA,  MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH_FOREIGN,
249cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
250cda6618dSMahesh Salgaonkar { 0, 0, 0, 0, 0, 0, 0 } };
251631bc46cSNicholas Piggin 
252201220bbSNicholas Piggin static const struct mce_ierror_table mce_p10_ierror_table[] = {
253201220bbSNicholas Piggin { 0x00000000081c0000, 0x0000000000040000, true,
254201220bbSNicholas Piggin   MCE_ERROR_TYPE_UE,  MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE,
255201220bbSNicholas Piggin   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
256201220bbSNicholas Piggin { 0x00000000081c0000, 0x0000000000080000, true,
257201220bbSNicholas Piggin   MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
258201220bbSNicholas Piggin   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
259201220bbSNicholas Piggin { 0x00000000081c0000, 0x00000000000c0000, true,
260201220bbSNicholas Piggin   MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
261201220bbSNicholas Piggin   MCE_INITIATOR_CPU,  MCE_SEV_WARNING, true },
262201220bbSNicholas Piggin { 0x00000000081c0000, 0x0000000000100000, true,
263201220bbSNicholas Piggin   MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
264201220bbSNicholas Piggin   MCE_INITIATOR_CPU,  MCE_SEV_WARNING, true },
265201220bbSNicholas Piggin { 0x00000000081c0000, 0x0000000000140000, true,
266201220bbSNicholas Piggin   MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
267201220bbSNicholas Piggin   MCE_INITIATOR_CPU,  MCE_SEV_WARNING, true },
268201220bbSNicholas Piggin { 0x00000000081c0000, 0x0000000000180000, true,
269201220bbSNicholas Piggin   MCE_ERROR_TYPE_UE,  MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_HARDWARE,
270201220bbSNicholas Piggin   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
271201220bbSNicholas Piggin { 0x00000000081c0000, 0x00000000001c0000, true,
272201220bbSNicholas Piggin   MCE_ERROR_TYPE_RA,  MCE_RA_ERROR_IFETCH_FOREIGN, MCE_ECLASS_SOFTWARE,
273201220bbSNicholas Piggin   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
274201220bbSNicholas Piggin { 0x00000000081c0000, 0x0000000008080000, true,
275201220bbSNicholas Piggin   MCE_ERROR_TYPE_USER,MCE_USER_ERROR_SCV, MCE_ECLASS_SOFTWARE,
276201220bbSNicholas Piggin   MCE_INITIATOR_CPU,  MCE_SEV_WARNING, true },
277201220bbSNicholas Piggin { 0x00000000081c0000, 0x00000000080c0000, true,
278201220bbSNicholas Piggin   MCE_ERROR_TYPE_RA,  MCE_RA_ERROR_IFETCH, MCE_ECLASS_SOFTWARE,
279201220bbSNicholas Piggin   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
280201220bbSNicholas Piggin { 0x00000000081c0000, 0x0000000008100000, true,
281201220bbSNicholas Piggin   MCE_ERROR_TYPE_RA,  MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_SOFTWARE,
282201220bbSNicholas Piggin   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
283201220bbSNicholas Piggin { 0x00000000081c0000, 0x0000000008140000, false,
284201220bbSNicholas Piggin   MCE_ERROR_TYPE_RA,  MCE_RA_ERROR_STORE, MCE_ECLASS_HARDWARE,
285201220bbSNicholas Piggin   MCE_INITIATOR_CPU,  MCE_SEV_FATAL, false }, /* ASYNC is fatal */
286201220bbSNicholas Piggin { 0x00000000081c0000, 0x00000000081c0000, true, MCE_ECLASS_HARDWARE,
287201220bbSNicholas Piggin   MCE_ERROR_TYPE_RA,  MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH_FOREIGN,
288201220bbSNicholas Piggin   MCE_INITIATOR_CPU,  MCE_SEV_SEVERE, true },
289201220bbSNicholas Piggin { 0, 0, 0, 0, 0, 0, 0 } };
290201220bbSNicholas Piggin 
291631bc46cSNicholas Piggin struct mce_derror_table {
292631bc46cSNicholas Piggin 	unsigned long dsisr_value;
293631bc46cSNicholas Piggin 	bool dar_valid; /* dar is a valid indicator of faulting address */
294631bc46cSNicholas Piggin 	unsigned int error_type;
295631bc46cSNicholas Piggin 	unsigned int error_subtype;
29650dbabe0SMahesh Salgaonkar 	unsigned int error_class;
297631bc46cSNicholas Piggin 	unsigned int initiator;
298631bc46cSNicholas Piggin 	unsigned int severity;
299cda6618dSMahesh Salgaonkar 	bool sync_error;
300631bc46cSNicholas Piggin };
301631bc46cSNicholas Piggin 
302631bc46cSNicholas Piggin static const struct mce_derror_table mce_p7_derror_table[] = {
303631bc46cSNicholas Piggin { 0x00008000, false,
30450dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_UE,   MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE,
305cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
306631bc46cSNicholas Piggin { 0x00004000, true,
307631bc46cSNicholas Piggin   MCE_ERROR_TYPE_UE,   MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE,
30850dbabe0SMahesh Salgaonkar   MCE_ECLASS_HARDWARE,
309cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
310631bc46cSNicholas Piggin { 0x00000800, true,
31150dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
312cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
313631bc46cSNicholas Piggin { 0x00000400, true,
31450dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_TLB,  MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
315cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
31654dbcfc2SMichael Ellerman { 0x00000080, true,
31750dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_SLB,  MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
318cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
319631bc46cSNicholas Piggin { 0x00000100, true,
32050dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_SLB,  MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
321cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
322631bc46cSNicholas Piggin { 0x00000040, true,
323631bc46cSNicholas Piggin   MCE_ERROR_TYPE_SLB,  MCE_SLB_ERROR_INDETERMINATE, /* BOTH */
32450dbabe0SMahesh Salgaonkar   MCE_ECLASS_HARD_INDETERMINATE,
325cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
326cda6618dSMahesh Salgaonkar { 0, false, 0, 0, 0, 0, 0 } };
327631bc46cSNicholas Piggin 
328631bc46cSNicholas Piggin static const struct mce_derror_table mce_p8_derror_table[] = {
329631bc46cSNicholas Piggin { 0x00008000, false,
33050dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_UE,   MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE,
331cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
332631bc46cSNicholas Piggin { 0x00004000, true,
333631bc46cSNicholas Piggin   MCE_ERROR_TYPE_UE,   MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE,
33450dbabe0SMahesh Salgaonkar   MCE_ECLASS_HARDWARE,
335cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
336c7e790c5SNicholas Piggin { 0x00002000, true,
33750dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_LOAD_TIMEOUT, MCE_ECLASS_HARDWARE,
338cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
339c7e790c5SNicholas Piggin { 0x00001000, true,
340c7e790c5SNicholas Piggin   MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_PAGE_TABLE_WALK_LOAD_STORE_TIMEOUT,
34150dbabe0SMahesh Salgaonkar   MCE_ECLASS_HARDWARE,
342cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
343631bc46cSNicholas Piggin { 0x00000800, true,
34450dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
345cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
346631bc46cSNicholas Piggin { 0x00000400, true,
34750dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_TLB,  MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
348cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
349631bc46cSNicholas Piggin { 0x00000200, true,
350631bc46cSNicholas Piggin   MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, /* SECONDARY ERAT */
35150dbabe0SMahesh Salgaonkar   MCE_ECLASS_SOFT_INDETERMINATE,
352cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
35354dbcfc2SMichael Ellerman { 0x00000080, true,
35454dbcfc2SMichael Ellerman   MCE_ERROR_TYPE_SLB,  MCE_SLB_ERROR_MULTIHIT,	/* Before PARITY */
35550dbabe0SMahesh Salgaonkar   MCE_ECLASS_SOFT_INDETERMINATE,
356cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
357631bc46cSNicholas Piggin { 0x00000100, true,
35850dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_SLB,  MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
359cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
360cda6618dSMahesh Salgaonkar { 0, false, 0, 0, 0, 0, 0 } };
361631bc46cSNicholas Piggin 
362631bc46cSNicholas Piggin static const struct mce_derror_table mce_p9_derror_table[] = {
363631bc46cSNicholas Piggin { 0x00008000, false,
36450dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_UE,   MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE,
365cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
366631bc46cSNicholas Piggin { 0x00004000, true,
367631bc46cSNicholas Piggin   MCE_ERROR_TYPE_UE,   MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE,
36850dbabe0SMahesh Salgaonkar   MCE_ECLASS_HARDWARE,
369cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
370631bc46cSNicholas Piggin { 0x00002000, true,
37150dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_LOAD_TIMEOUT, MCE_ECLASS_HARDWARE,
372cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
373631bc46cSNicholas Piggin { 0x00001000, true,
374631bc46cSNicholas Piggin   MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_PAGE_TABLE_WALK_LOAD_STORE_TIMEOUT,
37550dbabe0SMahesh Salgaonkar   MCE_ECLASS_HARDWARE,
376cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
377631bc46cSNicholas Piggin { 0x00000800, true,
37850dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
379cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
380631bc46cSNicholas Piggin { 0x00000400, true,
38150dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_TLB,  MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
382cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
383631bc46cSNicholas Piggin { 0x00000200, false,
38450dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_USER, MCE_USER_ERROR_TLBIE, MCE_ECLASS_SOFTWARE,
385cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
38654dbcfc2SMichael Ellerman { 0x00000080, true,
38754dbcfc2SMichael Ellerman   MCE_ERROR_TYPE_SLB,  MCE_SLB_ERROR_MULTIHIT,	/* Before PARITY */
38850dbabe0SMahesh Salgaonkar   MCE_ECLASS_SOFT_INDETERMINATE,
389cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
390631bc46cSNicholas Piggin { 0x00000100, true,
39150dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_SLB,  MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
392cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
393631bc46cSNicholas Piggin { 0x00000040, true,
39450dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_RA,   MCE_RA_ERROR_LOAD, MCE_ECLASS_HARDWARE,
395cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
396631bc46cSNicholas Piggin { 0x00000020, false,
397631bc46cSNicholas Piggin   MCE_ERROR_TYPE_RA,   MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE,
39850dbabe0SMahesh Salgaonkar   MCE_ECLASS_HARDWARE,
399cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
400631bc46cSNicholas Piggin { 0x00000010, false,
401631bc46cSNicholas Piggin   MCE_ERROR_TYPE_RA,   MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE_FOREIGN,
40250dbabe0SMahesh Salgaonkar   MCE_ECLASS_HARDWARE,
403cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
404631bc46cSNicholas Piggin { 0x00000008, false,
40550dbabe0SMahesh Salgaonkar   MCE_ERROR_TYPE_RA,   MCE_RA_ERROR_LOAD_STORE_FOREIGN, MCE_ECLASS_HARDWARE,
406cda6618dSMahesh Salgaonkar   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
407cda6618dSMahesh Salgaonkar { 0, false, 0, 0, 0, 0, 0 } };
408631bc46cSNicholas Piggin 
409201220bbSNicholas Piggin static const struct mce_derror_table mce_p10_derror_table[] = {
410201220bbSNicholas Piggin { 0x00008000, false,
411201220bbSNicholas Piggin   MCE_ERROR_TYPE_UE,   MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE,
412201220bbSNicholas Piggin   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
413201220bbSNicholas Piggin { 0x00004000, true,
414201220bbSNicholas Piggin   MCE_ERROR_TYPE_UE,   MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE,
415201220bbSNicholas Piggin   MCE_ECLASS_HARDWARE,
416201220bbSNicholas Piggin   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
417201220bbSNicholas Piggin { 0x00000800, true,
418201220bbSNicholas Piggin   MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
419201220bbSNicholas Piggin   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
420201220bbSNicholas Piggin { 0x00000400, true,
421201220bbSNicholas Piggin   MCE_ERROR_TYPE_TLB,  MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
422201220bbSNicholas Piggin   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
423201220bbSNicholas Piggin { 0x00000200, false,
424201220bbSNicholas Piggin   MCE_ERROR_TYPE_USER, MCE_USER_ERROR_TLBIE, MCE_ECLASS_SOFTWARE,
425201220bbSNicholas Piggin   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
426201220bbSNicholas Piggin { 0x00000080, true,
427201220bbSNicholas Piggin   MCE_ERROR_TYPE_SLB,  MCE_SLB_ERROR_MULTIHIT,	/* Before PARITY */
428201220bbSNicholas Piggin   MCE_ECLASS_SOFT_INDETERMINATE,
429201220bbSNicholas Piggin   MCE_INITIATOR_CPU,   MCE_SEV_WARNING, true },
430201220bbSNicholas Piggin { 0x00000100, true,
431201220bbSNicholas Piggin   MCE_ERROR_TYPE_SLB,  MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
432201220bbSNicholas Piggin   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
433201220bbSNicholas Piggin { 0x00000040, true,
434201220bbSNicholas Piggin   MCE_ERROR_TYPE_RA,   MCE_RA_ERROR_LOAD, MCE_ECLASS_HARDWARE,
435201220bbSNicholas Piggin   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
436201220bbSNicholas Piggin { 0x00000020, false,
437201220bbSNicholas Piggin   MCE_ERROR_TYPE_RA,   MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE,
438201220bbSNicholas Piggin   MCE_ECLASS_HARDWARE,
439201220bbSNicholas Piggin   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
440201220bbSNicholas Piggin { 0x00000010, false,
441201220bbSNicholas Piggin   MCE_ERROR_TYPE_RA,   MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE_FOREIGN,
442201220bbSNicholas Piggin   MCE_ECLASS_HARDWARE,
443201220bbSNicholas Piggin   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
444201220bbSNicholas Piggin { 0x00000008, false,
445201220bbSNicholas Piggin   MCE_ERROR_TYPE_RA,   MCE_RA_ERROR_LOAD_STORE_FOREIGN, MCE_ECLASS_HARDWARE,
446201220bbSNicholas Piggin   MCE_INITIATOR_CPU,   MCE_SEV_SEVERE, true },
447201220bbSNicholas Piggin { 0, false, 0, 0, 0, 0, 0 } };
448201220bbSNicholas Piggin 
mce_find_instr_ea_and_phys(struct pt_regs * regs,uint64_t * addr,uint64_t * phys_addr)44999ead78aSBalbir Singh static int mce_find_instr_ea_and_phys(struct pt_regs *regs, uint64_t *addr,
450ba41e1e1SBalbir Singh 					uint64_t *phys_addr)
451ba41e1e1SBalbir Singh {
452ba41e1e1SBalbir Singh 	/*
453ba41e1e1SBalbir Singh 	 * Carefully look at the NIP to determine
454ba41e1e1SBalbir Singh 	 * the instruction to analyse. Reading the NIP
455ba41e1e1SBalbir Singh 	 * in real-mode is tricky and can lead to recursive
456ba41e1e1SBalbir Singh 	 * faults
457ba41e1e1SBalbir Singh 	 */
458*c545b9f0SChristophe Leroy 	ppc_inst_t instr;
459ba41e1e1SBalbir Singh 	unsigned long pfn, instr_addr;
460ba41e1e1SBalbir Singh 	struct instruction_op op;
461ba41e1e1SBalbir Singh 	struct pt_regs tmp = *regs;
462ba41e1e1SBalbir Singh 
463ba41e1e1SBalbir Singh 	pfn = addr_to_pfn(regs, regs->nip);
464ba41e1e1SBalbir Singh 	if (pfn != ULONG_MAX) {
465ba41e1e1SBalbir Singh 		instr_addr = (pfn << PAGE_SHIFT) + (regs->nip & ~PAGE_MASK);
46669d4d6e5SChristophe Leroy 		instr = ppc_inst_read((u32 *)instr_addr);
467ba41e1e1SBalbir Singh 		if (!analyse_instr(&op, &tmp, instr)) {
468ba41e1e1SBalbir Singh 			pfn = addr_to_pfn(regs, op.ea);
469ba41e1e1SBalbir Singh 			*addr = op.ea;
470ba41e1e1SBalbir Singh 			*phys_addr = (pfn << PAGE_SHIFT);
471ba41e1e1SBalbir Singh 			return 0;
472ba41e1e1SBalbir Singh 		}
473ba41e1e1SBalbir Singh 		/*
474ba41e1e1SBalbir Singh 		 * analyse_instr() might fail if the instruction
475ba41e1e1SBalbir Singh 		 * is not a load/store, although this is unexpected
476ba41e1e1SBalbir Singh 		 * for load/store errors or if we got the NIP
477ba41e1e1SBalbir Singh 		 * wrong
478ba41e1e1SBalbir Singh 		 */
479ba41e1e1SBalbir Singh 	}
480ba41e1e1SBalbir Singh 	*addr = 0;
481ba41e1e1SBalbir Singh 	return -1;
482ba41e1e1SBalbir Singh }
483ba41e1e1SBalbir Singh 
mce_handle_ierror(struct pt_regs * regs,unsigned long srr1,const struct mce_ierror_table table[],struct mce_error_info * mce_err,uint64_t * addr,uint64_t * phys_addr)4843729e0ecSNicholas Piggin static int mce_handle_ierror(struct pt_regs *regs, unsigned long srr1,
485631bc46cSNicholas Piggin 		const struct mce_ierror_table table[],
48601eaac2bSBalbir Singh 		struct mce_error_info *mce_err, uint64_t *addr,
48701eaac2bSBalbir Singh 		uint64_t *phys_addr)
488631bc46cSNicholas Piggin {
489755309beSNicholas Piggin 	int handled = 0;
490631bc46cSNicholas Piggin 	int i;
491631bc46cSNicholas Piggin 
492631bc46cSNicholas Piggin 	*addr = 0;
493631bc46cSNicholas Piggin 
494631bc46cSNicholas Piggin 	for (i = 0; table[i].srr1_mask; i++) {
495631bc46cSNicholas Piggin 		if ((srr1 & table[i].srr1_mask) != table[i].srr1_value)
496631bc46cSNicholas Piggin 			continue;
497631bc46cSNicholas Piggin 
4980ce23826SNicholas Piggin 		if (!mce_in_guest()) {
499755309beSNicholas Piggin 			/* attempt to correct the error */
500755309beSNicholas Piggin 			switch (table[i].error_type) {
501755309beSNicholas Piggin 			case MCE_ERROR_TYPE_SLB:
502387e220aSNicholas Piggin #ifdef CONFIG_PPC_64S_HASH_MMU
5037290f3b3SNicholas Piggin 				if (local_paca->in_mce == 1)
5047290f3b3SNicholas Piggin 					slb_save_contents(local_paca->mce_faulty_slbs);
505387e220aSNicholas Piggin #endif
506755309beSNicholas Piggin 				handled = mce_flush(MCE_FLUSH_SLB);
507755309beSNicholas Piggin 				break;
508755309beSNicholas Piggin 			case MCE_ERROR_TYPE_ERAT:
509755309beSNicholas Piggin 				handled = mce_flush(MCE_FLUSH_ERAT);
510755309beSNicholas Piggin 				break;
511755309beSNicholas Piggin 			case MCE_ERROR_TYPE_TLB:
512755309beSNicholas Piggin 				handled = mce_flush(MCE_FLUSH_TLB);
513755309beSNicholas Piggin 				break;
514755309beSNicholas Piggin 			}
5150ce23826SNicholas Piggin 		}
516755309beSNicholas Piggin 
517755309beSNicholas Piggin 		/* now fill in mce_error_info */
518631bc46cSNicholas Piggin 		mce_err->error_type = table[i].error_type;
51950dbabe0SMahesh Salgaonkar 		mce_err->error_class = table[i].error_class;
520631bc46cSNicholas Piggin 		switch (table[i].error_type) {
521631bc46cSNicholas Piggin 		case MCE_ERROR_TYPE_UE:
522631bc46cSNicholas Piggin 			mce_err->u.ue_error_type = table[i].error_subtype;
523631bc46cSNicholas Piggin 			break;
524631bc46cSNicholas Piggin 		case MCE_ERROR_TYPE_SLB:
525631bc46cSNicholas Piggin 			mce_err->u.slb_error_type = table[i].error_subtype;
526631bc46cSNicholas Piggin 			break;
527631bc46cSNicholas Piggin 		case MCE_ERROR_TYPE_ERAT:
528631bc46cSNicholas Piggin 			mce_err->u.erat_error_type = table[i].error_subtype;
529631bc46cSNicholas Piggin 			break;
530631bc46cSNicholas Piggin 		case MCE_ERROR_TYPE_TLB:
531631bc46cSNicholas Piggin 			mce_err->u.tlb_error_type = table[i].error_subtype;
532631bc46cSNicholas Piggin 			break;
533631bc46cSNicholas Piggin 		case MCE_ERROR_TYPE_USER:
534631bc46cSNicholas Piggin 			mce_err->u.user_error_type = table[i].error_subtype;
535631bc46cSNicholas Piggin 			break;
536631bc46cSNicholas Piggin 		case MCE_ERROR_TYPE_RA:
537631bc46cSNicholas Piggin 			mce_err->u.ra_error_type = table[i].error_subtype;
538631bc46cSNicholas Piggin 			break;
539631bc46cSNicholas Piggin 		case MCE_ERROR_TYPE_LINK:
540631bc46cSNicholas Piggin 			mce_err->u.link_error_type = table[i].error_subtype;
541631bc46cSNicholas Piggin 			break;
542631bc46cSNicholas Piggin 		}
543cda6618dSMahesh Salgaonkar 		mce_err->sync_error = table[i].sync_error;
544631bc46cSNicholas Piggin 		mce_err->severity = table[i].severity;
545631bc46cSNicholas Piggin 		mce_err->initiator = table[i].initiator;
5460ce23826SNicholas Piggin 		if (table[i].nip_valid && !mce_in_guest()) {
547631bc46cSNicholas Piggin 			*addr = regs->nip;
548cda6618dSMahesh Salgaonkar 			if (mce_err->sync_error &&
54901eaac2bSBalbir Singh 				table[i].error_type == MCE_ERROR_TYPE_UE) {
55001eaac2bSBalbir Singh 				unsigned long pfn;
55101eaac2bSBalbir Singh 
55201eaac2bSBalbir Singh 				if (get_paca()->in_mce < MAX_MCE_DEPTH) {
55301eaac2bSBalbir Singh 					pfn = addr_to_pfn(regs, regs->nip);
55401eaac2bSBalbir Singh 					if (pfn != ULONG_MAX) {
55501eaac2bSBalbir Singh 						*phys_addr =
55601eaac2bSBalbir Singh 							(pfn << PAGE_SHIFT);
55701eaac2bSBalbir Singh 					}
55801eaac2bSBalbir Singh 				}
55901eaac2bSBalbir Singh 			}
56001eaac2bSBalbir Singh 		}
561755309beSNicholas Piggin 		return handled;
562631bc46cSNicholas Piggin 	}
563631bc46cSNicholas Piggin 
564631bc46cSNicholas Piggin 	mce_err->error_type = MCE_ERROR_TYPE_UNKNOWN;
56550dbabe0SMahesh Salgaonkar 	mce_err->error_class = MCE_ECLASS_UNKNOWN;
566cda6618dSMahesh Salgaonkar 	mce_err->severity = MCE_SEV_SEVERE;
567631bc46cSNicholas Piggin 	mce_err->initiator = MCE_INITIATOR_CPU;
568cda6618dSMahesh Salgaonkar 	mce_err->sync_error = true;
569755309beSNicholas Piggin 
570755309beSNicholas Piggin 	return 0;
571631bc46cSNicholas Piggin }
572631bc46cSNicholas Piggin 
mce_handle_derror(struct pt_regs * regs,const struct mce_derror_table table[],struct mce_error_info * mce_err,uint64_t * addr,uint64_t * phys_addr)573755309beSNicholas Piggin static int mce_handle_derror(struct pt_regs *regs,
574631bc46cSNicholas Piggin 		const struct mce_derror_table table[],
575ba41e1e1SBalbir Singh 		struct mce_error_info *mce_err, uint64_t *addr,
576ba41e1e1SBalbir Singh 		uint64_t *phys_addr)
577631bc46cSNicholas Piggin {
578631bc46cSNicholas Piggin 	uint64_t dsisr = regs->dsisr;
579755309beSNicholas Piggin 	int handled = 0;
580755309beSNicholas Piggin 	int found = 0;
581631bc46cSNicholas Piggin 	int i;
582631bc46cSNicholas Piggin 
583631bc46cSNicholas Piggin 	*addr = 0;
584631bc46cSNicholas Piggin 
585631bc46cSNicholas Piggin 	for (i = 0; table[i].dsisr_value; i++) {
586631bc46cSNicholas Piggin 		if (!(dsisr & table[i].dsisr_value))
587631bc46cSNicholas Piggin 			continue;
588631bc46cSNicholas Piggin 
5890ce23826SNicholas Piggin 		if (!mce_in_guest()) {
590755309beSNicholas Piggin 			/* attempt to correct the error */
591755309beSNicholas Piggin 			switch (table[i].error_type) {
592755309beSNicholas Piggin 			case MCE_ERROR_TYPE_SLB:
593387e220aSNicholas Piggin #ifdef CONFIG_PPC_64S_HASH_MMU
5947290f3b3SNicholas Piggin 				if (local_paca->in_mce == 1)
5957290f3b3SNicholas Piggin 					slb_save_contents(local_paca->mce_faulty_slbs);
596387e220aSNicholas Piggin #endif
597755309beSNicholas Piggin 				if (mce_flush(MCE_FLUSH_SLB))
598755309beSNicholas Piggin 					handled = 1;
599755309beSNicholas Piggin 				break;
600755309beSNicholas Piggin 			case MCE_ERROR_TYPE_ERAT:
601755309beSNicholas Piggin 				if (mce_flush(MCE_FLUSH_ERAT))
602755309beSNicholas Piggin 					handled = 1;
603755309beSNicholas Piggin 				break;
604755309beSNicholas Piggin 			case MCE_ERROR_TYPE_TLB:
605755309beSNicholas Piggin 				if (mce_flush(MCE_FLUSH_TLB))
606755309beSNicholas Piggin 					handled = 1;
607755309beSNicholas Piggin 				break;
608755309beSNicholas Piggin 			}
6090ce23826SNicholas Piggin 		}
610755309beSNicholas Piggin 
611755309beSNicholas Piggin 		/*
612755309beSNicholas Piggin 		 * Attempt to handle multiple conditions, but only return
613755309beSNicholas Piggin 		 * one. Ensure uncorrectable errors are first in the table
614755309beSNicholas Piggin 		 * to match.
615755309beSNicholas Piggin 		 */
616755309beSNicholas Piggin 		if (found)
617755309beSNicholas Piggin 			continue;
618755309beSNicholas Piggin 
619755309beSNicholas Piggin 		/* now fill in mce_error_info */
620631bc46cSNicholas Piggin 		mce_err->error_type = table[i].error_type;
62150dbabe0SMahesh Salgaonkar 		mce_err->error_class = table[i].error_class;
622631bc46cSNicholas Piggin 		switch (table[i].error_type) {
623631bc46cSNicholas Piggin 		case MCE_ERROR_TYPE_UE:
624631bc46cSNicholas Piggin 			mce_err->u.ue_error_type = table[i].error_subtype;
625631bc46cSNicholas Piggin 			break;
626631bc46cSNicholas Piggin 		case MCE_ERROR_TYPE_SLB:
627631bc46cSNicholas Piggin 			mce_err->u.slb_error_type = table[i].error_subtype;
628631bc46cSNicholas Piggin 			break;
629631bc46cSNicholas Piggin 		case MCE_ERROR_TYPE_ERAT:
630631bc46cSNicholas Piggin 			mce_err->u.erat_error_type = table[i].error_subtype;
631631bc46cSNicholas Piggin 			break;
632631bc46cSNicholas Piggin 		case MCE_ERROR_TYPE_TLB:
633631bc46cSNicholas Piggin 			mce_err->u.tlb_error_type = table[i].error_subtype;
634631bc46cSNicholas Piggin 			break;
635631bc46cSNicholas Piggin 		case MCE_ERROR_TYPE_USER:
636631bc46cSNicholas Piggin 			mce_err->u.user_error_type = table[i].error_subtype;
637631bc46cSNicholas Piggin 			break;
638631bc46cSNicholas Piggin 		case MCE_ERROR_TYPE_RA:
639631bc46cSNicholas Piggin 			mce_err->u.ra_error_type = table[i].error_subtype;
640631bc46cSNicholas Piggin 			break;
641631bc46cSNicholas Piggin 		case MCE_ERROR_TYPE_LINK:
642631bc46cSNicholas Piggin 			mce_err->u.link_error_type = table[i].error_subtype;
643631bc46cSNicholas Piggin 			break;
644631bc46cSNicholas Piggin 		}
645cda6618dSMahesh Salgaonkar 		mce_err->sync_error = table[i].sync_error;
646631bc46cSNicholas Piggin 		mce_err->severity = table[i].severity;
647631bc46cSNicholas Piggin 		mce_err->initiator = table[i].initiator;
648631bc46cSNicholas Piggin 		if (table[i].dar_valid)
649631bc46cSNicholas Piggin 			*addr = regs->dar;
6500ce23826SNicholas Piggin 		else if (mce_err->sync_error && !mce_in_guest() &&
651ba41e1e1SBalbir Singh 				table[i].error_type == MCE_ERROR_TYPE_UE) {
652ba41e1e1SBalbir Singh 			/*
653ba41e1e1SBalbir Singh 			 * We do a maximum of 4 nested MCE calls, see
654ba41e1e1SBalbir Singh 			 * kernel/exception-64s.h
655ba41e1e1SBalbir Singh 			 */
656ba41e1e1SBalbir Singh 			if (get_paca()->in_mce < MAX_MCE_DEPTH)
65799ead78aSBalbir Singh 				mce_find_instr_ea_and_phys(regs, addr,
65899ead78aSBalbir Singh 							   phys_addr);
659ba41e1e1SBalbir Singh 		}
660755309beSNicholas Piggin 		found = 1;
661631bc46cSNicholas Piggin 	}
662631bc46cSNicholas Piggin 
663755309beSNicholas Piggin 	if (found)
664755309beSNicholas Piggin 		return handled;
665755309beSNicholas Piggin 
666631bc46cSNicholas Piggin 	mce_err->error_type = MCE_ERROR_TYPE_UNKNOWN;
66750dbabe0SMahesh Salgaonkar 	mce_err->error_class = MCE_ECLASS_UNKNOWN;
668cda6618dSMahesh Salgaonkar 	mce_err->severity = MCE_SEV_SEVERE;
669631bc46cSNicholas Piggin 	mce_err->initiator = MCE_INITIATOR_CPU;
670cda6618dSMahesh Salgaonkar 	mce_err->sync_error = true;
671755309beSNicholas Piggin 
672755309beSNicholas Piggin 	return 0;
673631bc46cSNicholas Piggin }
674631bc46cSNicholas Piggin 
mce_handle_ue_error(struct pt_regs * regs,struct mce_error_info * mce_err)675895e3dceSBalbir Singh static long mce_handle_ue_error(struct pt_regs *regs,
676895e3dceSBalbir Singh 				struct mce_error_info *mce_err)
677631bc46cSNicholas Piggin {
6780ce23826SNicholas Piggin 	if (mce_in_guest())
6790ce23826SNicholas Piggin 		return 0;
680895e3dceSBalbir Singh 
681efbc4303SGanesh Goudar 	mce_common_process_ue(regs, mce_err);
682efbc4303SGanesh Goudar 	if (mce_err->ignore_event)
683895e3dceSBalbir Singh 		return 1;
684631bc46cSNicholas Piggin 
685631bc46cSNicholas Piggin 	/*
686631bc46cSNicholas Piggin 	 * On specific SCOM read via MMIO we may get a machine check
687631bc46cSNicholas Piggin 	 * exception with SRR0 pointing inside opal. If that is the
688631bc46cSNicholas Piggin 	 * case OPAL may have recovery address to re-read SCOM data in
689631bc46cSNicholas Piggin 	 * different way and hence we can recover from this MC.
690631bc46cSNicholas Piggin 	 */
691631bc46cSNicholas Piggin 
692631bc46cSNicholas Piggin 	if (ppc_md.mce_check_early_recovery) {
693631bc46cSNicholas Piggin 		if (ppc_md.mce_check_early_recovery(regs))
6940ce23826SNicholas Piggin 			return 1;
695631bc46cSNicholas Piggin 	}
6960ce23826SNicholas Piggin 
6970ce23826SNicholas Piggin 	return 0;
698631bc46cSNicholas Piggin }
699631bc46cSNicholas Piggin 
mce_handle_error(struct pt_regs * regs,unsigned long srr1,const struct mce_derror_table dtable[],const struct mce_ierror_table itable[])700755309beSNicholas Piggin static long mce_handle_error(struct pt_regs *regs,
7013729e0ecSNicholas Piggin 		unsigned long srr1,
702755309beSNicholas Piggin 		const struct mce_derror_table dtable[],
703755309beSNicholas Piggin 		const struct mce_ierror_table itable[])
704e22a2274SMahesh Salgaonkar {
705755309beSNicholas Piggin 	struct mce_error_info mce_err = { 0 };
70675ecfb49SMahesh Salgaonkar 	uint64_t addr, phys_addr = ULONG_MAX;
707755309beSNicholas Piggin 	long handled;
708e22a2274SMahesh Salgaonkar 
709755309beSNicholas Piggin 	if (SRR1_MC_LOADSTORE(srr1))
710ba41e1e1SBalbir Singh 		handled = mce_handle_derror(regs, dtable, &mce_err, &addr,
711ba41e1e1SBalbir Singh 				&phys_addr);
712755309beSNicholas Piggin 	else
7133729e0ecSNicholas Piggin 		handled = mce_handle_ierror(regs, srr1, itable, &mce_err, &addr,
71401eaac2bSBalbir Singh 				&phys_addr);
715e22a2274SMahesh Salgaonkar 
716755309beSNicholas Piggin 	if (!handled && mce_err.error_type == MCE_ERROR_TYPE_UE)
717895e3dceSBalbir Singh 		handled = mce_handle_ue_error(regs, &mce_err);
718755309beSNicholas Piggin 
719ba41e1e1SBalbir Singh 	save_mce_event(regs, handled, &mce_err, regs->nip, addr, phys_addr);
720755309beSNicholas Piggin 
721755309beSNicholas Piggin 	return handled;
722e22a2274SMahesh Salgaonkar }
723e22a2274SMahesh Salgaonkar 
__machine_check_early_realmode_p7(struct pt_regs * regs)724e22a2274SMahesh Salgaonkar long __machine_check_early_realmode_p7(struct pt_regs *regs)
725e22a2274SMahesh Salgaonkar {
726631bc46cSNicholas Piggin 	/* P7 DD1 leaves top bits of DSISR undefined */
727631bc46cSNicholas Piggin 	regs->dsisr &= 0x0000ffff;
728631bc46cSNicholas Piggin 
7293729e0ecSNicholas Piggin 	return mce_handle_error(regs, regs->msr,
7303729e0ecSNicholas Piggin 			mce_p7_derror_table, mce_p7_ierror_table);
731ae744f34SMahesh Salgaonkar }
732ae744f34SMahesh Salgaonkar 
__machine_check_early_realmode_p8(struct pt_regs * regs)733ae744f34SMahesh Salgaonkar long __machine_check_early_realmode_p8(struct pt_regs *regs)
734ae744f34SMahesh Salgaonkar {
7353729e0ecSNicholas Piggin 	return mce_handle_error(regs, regs->msr,
7363729e0ecSNicholas Piggin 			mce_p8_derror_table, mce_p8_ierror_table);
7377b9f71f9SNicholas Piggin }
7387b9f71f9SNicholas Piggin 
__machine_check_early_realmode_p9(struct pt_regs * regs)7397b9f71f9SNicholas Piggin long __machine_check_early_realmode_p9(struct pt_regs *regs)
7407b9f71f9SNicholas Piggin {
7413729e0ecSNicholas Piggin 	unsigned long srr1 = regs->msr;
7423729e0ecSNicholas Piggin 
743d8bd9f3fSMichael Neuling 	/*
744d8bd9f3fSMichael Neuling 	 * On POWER9 DD2.1 and below, it's possible to get a machine check
745bca73f59SMichael Neuling 	 * caused by a paste instruction where only DSISR bit 25 is set. This
746d8bd9f3fSMichael Neuling 	 * will result in the MCE handler seeing an unknown event and the kernel
747d8bd9f3fSMichael Neuling 	 * crashing. An MCE that occurs like this is spurious, so we don't need
748d8bd9f3fSMichael Neuling 	 * to do anything in terms of servicing it. If there is something that
749d8bd9f3fSMichael Neuling 	 * needs to be serviced, the CPU will raise the MCE again with the
750d8bd9f3fSMichael Neuling 	 * correct DSISR so that it can be serviced properly. So detect this
751d8bd9f3fSMichael Neuling 	 * case and mark it as handled.
752d8bd9f3fSMichael Neuling 	 */
753bca73f59SMichael Neuling 	if (SRR1_MC_LOADSTORE(regs->msr) && regs->dsisr == 0x02000000)
754d8bd9f3fSMichael Neuling 		return 1;
755d8bd9f3fSMichael Neuling 
7563729e0ecSNicholas Piggin 	/*
7573729e0ecSNicholas Piggin 	 * Async machine check due to bad real address from store or foreign
7583729e0ecSNicholas Piggin 	 * link time out comes with the load/store bit (PPC bit 42) set in
7593729e0ecSNicholas Piggin 	 * SRR1, but the cause comes in SRR1 not DSISR. Clear bit 42 so we're
7603729e0ecSNicholas Piggin 	 * directed to the ierror table so it will find the cause (which
7613729e0ecSNicholas Piggin 	 * describes it correctly as a store error).
7623729e0ecSNicholas Piggin 	 */
7633729e0ecSNicholas Piggin 	if (SRR1_MC_LOADSTORE(srr1) &&
7643729e0ecSNicholas Piggin 			((srr1 & 0x081c0000) == 0x08140000 ||
7653729e0ecSNicholas Piggin 			 (srr1 & 0x081c0000) == 0x08180000)) {
7663729e0ecSNicholas Piggin 		srr1 &= ~PPC_BIT(42);
7673729e0ecSNicholas Piggin 	}
7683729e0ecSNicholas Piggin 
7693729e0ecSNicholas Piggin 	return mce_handle_error(regs, srr1,
7703729e0ecSNicholas Piggin 			mce_p9_derror_table, mce_p9_ierror_table);
7717b9f71f9SNicholas Piggin }
772201220bbSNicholas Piggin 
__machine_check_early_realmode_p10(struct pt_regs * regs)773201220bbSNicholas Piggin long __machine_check_early_realmode_p10(struct pt_regs *regs)
774201220bbSNicholas Piggin {
7753729e0ecSNicholas Piggin 	unsigned long srr1 = regs->msr;
7763729e0ecSNicholas Piggin 
7773729e0ecSNicholas Piggin 	/*
7783729e0ecSNicholas Piggin 	 * Async machine check due to bad real address from store comes with
7793729e0ecSNicholas Piggin 	 * the load/store bit (PPC bit 42) set in SRR1, but the cause comes in
7803729e0ecSNicholas Piggin 	 * SRR1 not DSISR. Clear bit 42 so we're directed to the ierror table
7813729e0ecSNicholas Piggin 	 * so it will find the cause (which describes it correctly as a store
7823729e0ecSNicholas Piggin 	 * error).
7833729e0ecSNicholas Piggin 	 */
7843729e0ecSNicholas Piggin 	if (SRR1_MC_LOADSTORE(srr1) &&
7853729e0ecSNicholas Piggin 			(srr1 & 0x081c0000) == 0x08140000) {
7863729e0ecSNicholas Piggin 		srr1 &= ~PPC_BIT(42);
7873729e0ecSNicholas Piggin 	}
7883729e0ecSNicholas Piggin 
7893729e0ecSNicholas Piggin 	return mce_handle_error(regs, srr1,
7903729e0ecSNicholas Piggin 			mce_p10_derror_table, mce_p10_ierror_table);
791201220bbSNicholas Piggin }
792