123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * Machine check exception handling CPU-side for power7 and power8
- *
- * Copyright 2013 IBM Corporation
- * Author: Mahesh Salgaonkar <[email protected]>
- */
- #undef DEBUG
- #define pr_fmt(fmt) "mce_power: " fmt
- #include <linux/types.h>
- #include <linux/ptrace.h>
- #include <linux/extable.h>
- #include <linux/pgtable.h>
- #include <asm/mmu.h>
- #include <asm/mce.h>
- #include <asm/machdep.h>
- #include <asm/pte-walk.h>
- #include <asm/sstep.h>
- #include <asm/exception-64s.h>
- #include <asm/extable.h>
- #include <asm/inst.h>
- /*
- * Convert an address related to an mm to a PFN. NOTE: we are in real
- * mode, we could potentially race with page table updates.
- */
- unsigned long addr_to_pfn(struct pt_regs *regs, unsigned long addr)
- {
- pte_t *ptep, pte;
- unsigned int shift;
- unsigned long pfn, flags;
- struct mm_struct *mm;
- if (user_mode(regs))
- mm = current->mm;
- else
- mm = &init_mm;
- local_irq_save(flags);
- ptep = __find_linux_pte(mm->pgd, addr, NULL, &shift);
- if (!ptep) {
- pfn = ULONG_MAX;
- goto out;
- }
- pte = READ_ONCE(*ptep);
- if (!pte_present(pte) || pte_special(pte)) {
- pfn = ULONG_MAX;
- goto out;
- }
- if (shift <= PAGE_SHIFT)
- pfn = pte_pfn(pte);
- else {
- unsigned long rpnmask = (1ul << shift) - PAGE_SIZE;
- pfn = pte_pfn(__pte(pte_val(pte) | (addr & rpnmask)));
- }
- out:
- local_irq_restore(flags);
- return pfn;
- }
- static bool mce_in_guest(void)
- {
- #ifdef CONFIG_KVM_BOOK3S_HANDLER
- /*
- * If machine check is hit when in guest context or low level KVM
- * code, avoid looking up any translations or making any attempts
- * to recover, just record the event and pass to KVM.
- */
- if (get_paca()->kvm_hstate.in_guest)
- return true;
- #endif
- return false;
- }
- /* flush SLBs and reload */
- #ifdef CONFIG_PPC_64S_HASH_MMU
- void flush_and_reload_slb(void)
- {
- if (early_radix_enabled())
- return;
- /* Invalidate all SLBs */
- slb_flush_all_realmode();
- /*
- * This probably shouldn't happen, but it may be possible it's
- * called in early boot before SLB shadows are allocated.
- */
- if (!get_slb_shadow())
- return;
- slb_restore_bolted_realmode();
- }
- #endif
- void flush_erat(void)
- {
- #ifdef CONFIG_PPC_64S_HASH_MMU
- if (!early_cpu_has_feature(CPU_FTR_ARCH_300)) {
- flush_and_reload_slb();
- return;
- }
- #endif
- asm volatile(PPC_ISA_3_0_INVALIDATE_ERAT : : :"memory");
- }
- #define MCE_FLUSH_SLB 1
- #define MCE_FLUSH_TLB 2
- #define MCE_FLUSH_ERAT 3
- static int mce_flush(int what)
- {
- #ifdef CONFIG_PPC_64S_HASH_MMU
- if (what == MCE_FLUSH_SLB) {
- flush_and_reload_slb();
- return 1;
- }
- #endif
- if (what == MCE_FLUSH_ERAT) {
- flush_erat();
- return 1;
- }
- if (what == MCE_FLUSH_TLB) {
- tlbiel_all();
- return 1;
- }
- return 0;
- }
- #define SRR1_MC_LOADSTORE(srr1) ((srr1) & PPC_BIT(42))
- struct mce_ierror_table {
- unsigned long srr1_mask;
- unsigned long srr1_value;
- bool nip_valid; /* nip is a valid indicator of faulting address */
- unsigned int error_type;
- unsigned int error_subtype;
- unsigned int error_class;
- unsigned int initiator;
- unsigned int severity;
- bool sync_error;
- };
- static const struct mce_ierror_table mce_p7_ierror_table[] = {
- { 0x00000000001c0000, 0x0000000000040000, true,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000001c0000, 0x0000000000080000, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000001c0000, 0x00000000000c0000, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000000001c0000, 0x0000000000100000, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_INDETERMINATE, /* BOTH */
- MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000000001c0000, 0x0000000000140000, true,
- MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000000001c0000, 0x0000000000180000, true,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000001c0000, 0x00000000001c0000, true,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0, 0, 0, 0, 0, 0, 0 } };
- static const struct mce_ierror_table mce_p8_ierror_table[] = {
- { 0x00000000081c0000, 0x0000000000040000, true,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x0000000000080000, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x00000000000c0000, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000000081c0000, 0x0000000000100000, true,
- MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000000081c0000, 0x0000000000140000, true,
- MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000000081c0000, 0x0000000000180000, true,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH,
- MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x00000000001c0000, true,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x0000000008000000, true,
- MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_IFETCH_TIMEOUT, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x0000000008040000, true,
- MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_PAGE_TABLE_WALK_IFETCH_TIMEOUT,
- MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0, 0, 0, 0, 0, 0, 0 } };
- static const struct mce_ierror_table mce_p9_ierror_table[] = {
- { 0x00000000081c0000, 0x0000000000040000, true,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x0000000000080000, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x00000000000c0000, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000000081c0000, 0x0000000000100000, true,
- MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000000081c0000, 0x0000000000140000, true,
- MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000000081c0000, 0x0000000000180000, true,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x00000000001c0000, true,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_IFETCH_FOREIGN, MCE_ECLASS_SOFTWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x0000000008000000, true,
- MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_IFETCH_TIMEOUT, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x0000000008040000, true,
- MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_PAGE_TABLE_WALK_IFETCH_TIMEOUT,
- MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x00000000080c0000, true,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_IFETCH, MCE_ECLASS_SOFTWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x0000000008100000, true,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_SOFTWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x0000000008140000, false,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_STORE, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_FATAL, false }, /* ASYNC is fatal */
- { 0x00000000081c0000, 0x0000000008180000, false,
- MCE_ERROR_TYPE_LINK,MCE_LINK_ERROR_STORE_TIMEOUT,
- MCE_INITIATOR_CPU, MCE_SEV_FATAL, false }, /* ASYNC is fatal */
- { 0x00000000081c0000, 0x00000000081c0000, true, MCE_ECLASS_HARDWARE,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH_FOREIGN,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0, 0, 0, 0, 0, 0, 0 } };
- static const struct mce_ierror_table mce_p10_ierror_table[] = {
- { 0x00000000081c0000, 0x0000000000040000, true,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_IFETCH, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x0000000000080000, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x00000000000c0000, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000000081c0000, 0x0000000000100000, true,
- MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000000081c0000, 0x0000000000140000, true,
- MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000000081c0000, 0x0000000000180000, true,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x00000000001c0000, true,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_IFETCH_FOREIGN, MCE_ECLASS_SOFTWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x0000000008080000, true,
- MCE_ERROR_TYPE_USER,MCE_USER_ERROR_SCV, MCE_ECLASS_SOFTWARE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000000081c0000, 0x00000000080c0000, true,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_IFETCH, MCE_ECLASS_SOFTWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x0000000008100000, true,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH, MCE_ECLASS_SOFTWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000000081c0000, 0x0000000008140000, false,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_STORE, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_FATAL, false }, /* ASYNC is fatal */
- { 0x00000000081c0000, 0x00000000081c0000, true, MCE_ECLASS_HARDWARE,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_IFETCH_FOREIGN,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0, 0, 0, 0, 0, 0, 0 } };
- struct mce_derror_table {
- unsigned long dsisr_value;
- bool dar_valid; /* dar is a valid indicator of faulting address */
- unsigned int error_type;
- unsigned int error_subtype;
- unsigned int error_class;
- unsigned int initiator;
- unsigned int severity;
- bool sync_error;
- };
- static const struct mce_derror_table mce_p7_derror_table[] = {
- { 0x00008000, false,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00004000, true,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE,
- MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000800, true,
- MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000400, true,
- MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000080, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000100, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000040, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_INDETERMINATE, /* BOTH */
- MCE_ECLASS_HARD_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0, false, 0, 0, 0, 0, 0 } };
- static const struct mce_derror_table mce_p8_derror_table[] = {
- { 0x00008000, false,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00004000, true,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE,
- MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00002000, true,
- MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_LOAD_TIMEOUT, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00001000, true,
- MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_PAGE_TABLE_WALK_LOAD_STORE_TIMEOUT,
- MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000800, true,
- MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000400, true,
- MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000200, true,
- MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, /* SECONDARY ERAT */
- MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000080, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, /* Before PARITY */
- MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000100, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0, false, 0, 0, 0, 0, 0 } };
- static const struct mce_derror_table mce_p9_derror_table[] = {
- { 0x00008000, false,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00004000, true,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE,
- MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00002000, true,
- MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_LOAD_TIMEOUT, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00001000, true,
- MCE_ERROR_TYPE_LINK, MCE_LINK_ERROR_PAGE_TABLE_WALK_LOAD_STORE_TIMEOUT,
- MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000800, true,
- MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000400, true,
- MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000200, false,
- MCE_ERROR_TYPE_USER, MCE_USER_ERROR_TLBIE, MCE_ECLASS_SOFTWARE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000080, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, /* Before PARITY */
- MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000100, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000040, true,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_LOAD, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000020, false,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE,
- MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000010, false,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE_FOREIGN,
- MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000008, false,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_LOAD_STORE_FOREIGN, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0, false, 0, 0, 0, 0, 0 } };
- static const struct mce_derror_table mce_p10_derror_table[] = {
- { 0x00008000, false,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_LOAD_STORE, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00004000, true,
- MCE_ERROR_TYPE_UE, MCE_UE_ERROR_PAGE_TABLE_WALK_LOAD_STORE,
- MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000800, true,
- MCE_ERROR_TYPE_ERAT, MCE_ERAT_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000400, true,
- MCE_ERROR_TYPE_TLB, MCE_TLB_ERROR_MULTIHIT, MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000200, false,
- MCE_ERROR_TYPE_USER, MCE_USER_ERROR_TLBIE, MCE_ECLASS_SOFTWARE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000080, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_MULTIHIT, /* Before PARITY */
- MCE_ECLASS_SOFT_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_WARNING, true },
- { 0x00000100, true,
- MCE_ERROR_TYPE_SLB, MCE_SLB_ERROR_PARITY, MCE_ECLASS_HARD_INDETERMINATE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000040, true,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_LOAD, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000020, false,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE,
- MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000010, false,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_PAGE_TABLE_WALK_LOAD_STORE_FOREIGN,
- MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0x00000008, false,
- MCE_ERROR_TYPE_RA, MCE_RA_ERROR_LOAD_STORE_FOREIGN, MCE_ECLASS_HARDWARE,
- MCE_INITIATOR_CPU, MCE_SEV_SEVERE, true },
- { 0, false, 0, 0, 0, 0, 0 } };
- static int mce_find_instr_ea_and_phys(struct pt_regs *regs, uint64_t *addr,
- uint64_t *phys_addr)
- {
- /*
- * Carefully look at the NIP to determine
- * the instruction to analyse. Reading the NIP
- * in real-mode is tricky and can lead to recursive
- * faults
- */
- ppc_inst_t instr;
- unsigned long pfn, instr_addr;
- struct instruction_op op;
- struct pt_regs tmp = *regs;
- pfn = addr_to_pfn(regs, regs->nip);
- if (pfn != ULONG_MAX) {
- instr_addr = (pfn << PAGE_SHIFT) + (regs->nip & ~PAGE_MASK);
- instr = ppc_inst_read((u32 *)instr_addr);
- if (!analyse_instr(&op, &tmp, instr)) {
- pfn = addr_to_pfn(regs, op.ea);
- *addr = op.ea;
- *phys_addr = (pfn << PAGE_SHIFT);
- return 0;
- }
- /*
- * analyse_instr() might fail if the instruction
- * is not a load/store, although this is unexpected
- * for load/store errors or if we got the NIP
- * wrong
- */
- }
- *addr = 0;
- return -1;
- }
- static int 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)
- {
- int handled = 0;
- int i;
- *addr = 0;
- for (i = 0; table[i].srr1_mask; i++) {
- if ((srr1 & table[i].srr1_mask) != table[i].srr1_value)
- continue;
- if (!mce_in_guest()) {
- /* attempt to correct the error */
- switch (table[i].error_type) {
- case MCE_ERROR_TYPE_SLB:
- #ifdef CONFIG_PPC_64S_HASH_MMU
- if (local_paca->in_mce == 1)
- slb_save_contents(local_paca->mce_faulty_slbs);
- #endif
- handled = mce_flush(MCE_FLUSH_SLB);
- break;
- case MCE_ERROR_TYPE_ERAT:
- handled = mce_flush(MCE_FLUSH_ERAT);
- break;
- case MCE_ERROR_TYPE_TLB:
- handled = mce_flush(MCE_FLUSH_TLB);
- break;
- }
- }
- /* now fill in mce_error_info */
- mce_err->error_type = table[i].error_type;
- mce_err->error_class = table[i].error_class;
- switch (table[i].error_type) {
- case MCE_ERROR_TYPE_UE:
- mce_err->u.ue_error_type = table[i].error_subtype;
- break;
- case MCE_ERROR_TYPE_SLB:
- mce_err->u.slb_error_type = table[i].error_subtype;
- break;
- case MCE_ERROR_TYPE_ERAT:
- mce_err->u.erat_error_type = table[i].error_subtype;
- break;
- case MCE_ERROR_TYPE_TLB:
- mce_err->u.tlb_error_type = table[i].error_subtype;
- break;
- case MCE_ERROR_TYPE_USER:
- mce_err->u.user_error_type = table[i].error_subtype;
- break;
- case MCE_ERROR_TYPE_RA:
- mce_err->u.ra_error_type = table[i].error_subtype;
- break;
- case MCE_ERROR_TYPE_LINK:
- mce_err->u.link_error_type = table[i].error_subtype;
- break;
- }
- mce_err->sync_error = table[i].sync_error;
- mce_err->severity = table[i].severity;
- mce_err->initiator = table[i].initiator;
- if (table[i].nip_valid && !mce_in_guest()) {
- *addr = regs->nip;
- if (mce_err->sync_error &&
- table[i].error_type == MCE_ERROR_TYPE_UE) {
- unsigned long pfn;
- if (get_paca()->in_mce < MAX_MCE_DEPTH) {
- pfn = addr_to_pfn(regs, regs->nip);
- if (pfn != ULONG_MAX) {
- *phys_addr =
- (pfn << PAGE_SHIFT);
- }
- }
- }
- }
- return handled;
- }
- mce_err->error_type = MCE_ERROR_TYPE_UNKNOWN;
- mce_err->error_class = MCE_ECLASS_UNKNOWN;
- mce_err->severity = MCE_SEV_SEVERE;
- mce_err->initiator = MCE_INITIATOR_CPU;
- mce_err->sync_error = true;
- return 0;
- }
- static int 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)
- {
- uint64_t dsisr = regs->dsisr;
- int handled = 0;
- int found = 0;
- int i;
- *addr = 0;
- for (i = 0; table[i].dsisr_value; i++) {
- if (!(dsisr & table[i].dsisr_value))
- continue;
- if (!mce_in_guest()) {
- /* attempt to correct the error */
- switch (table[i].error_type) {
- case MCE_ERROR_TYPE_SLB:
- #ifdef CONFIG_PPC_64S_HASH_MMU
- if (local_paca->in_mce == 1)
- slb_save_contents(local_paca->mce_faulty_slbs);
- #endif
- if (mce_flush(MCE_FLUSH_SLB))
- handled = 1;
- break;
- case MCE_ERROR_TYPE_ERAT:
- if (mce_flush(MCE_FLUSH_ERAT))
- handled = 1;
- break;
- case MCE_ERROR_TYPE_TLB:
- if (mce_flush(MCE_FLUSH_TLB))
- handled = 1;
- break;
- }
- }
- /*
- * Attempt to handle multiple conditions, but only return
- * one. Ensure uncorrectable errors are first in the table
- * to match.
- */
- if (found)
- continue;
- /* now fill in mce_error_info */
- mce_err->error_type = table[i].error_type;
- mce_err->error_class = table[i].error_class;
- switch (table[i].error_type) {
- case MCE_ERROR_TYPE_UE:
- mce_err->u.ue_error_type = table[i].error_subtype;
- break;
- case MCE_ERROR_TYPE_SLB:
- mce_err->u.slb_error_type = table[i].error_subtype;
- break;
- case MCE_ERROR_TYPE_ERAT:
- mce_err->u.erat_error_type = table[i].error_subtype;
- break;
- case MCE_ERROR_TYPE_TLB:
- mce_err->u.tlb_error_type = table[i].error_subtype;
- break;
- case MCE_ERROR_TYPE_USER:
- mce_err->u.user_error_type = table[i].error_subtype;
- break;
- case MCE_ERROR_TYPE_RA:
- mce_err->u.ra_error_type = table[i].error_subtype;
- break;
- case MCE_ERROR_TYPE_LINK:
- mce_err->u.link_error_type = table[i].error_subtype;
- break;
- }
- mce_err->sync_error = table[i].sync_error;
- mce_err->severity = table[i].severity;
- mce_err->initiator = table[i].initiator;
- if (table[i].dar_valid)
- *addr = regs->dar;
- else if (mce_err->sync_error && !mce_in_guest() &&
- table[i].error_type == MCE_ERROR_TYPE_UE) {
- /*
- * We do a maximum of 4 nested MCE calls, see
- * kernel/exception-64s.h
- */
- if (get_paca()->in_mce < MAX_MCE_DEPTH)
- mce_find_instr_ea_and_phys(regs, addr,
- phys_addr);
- }
- found = 1;
- }
- if (found)
- return handled;
- mce_err->error_type = MCE_ERROR_TYPE_UNKNOWN;
- mce_err->error_class = MCE_ECLASS_UNKNOWN;
- mce_err->severity = MCE_SEV_SEVERE;
- mce_err->initiator = MCE_INITIATOR_CPU;
- mce_err->sync_error = true;
- return 0;
- }
- static long mce_handle_ue_error(struct pt_regs *regs,
- struct mce_error_info *mce_err)
- {
- if (mce_in_guest())
- return 0;
- mce_common_process_ue(regs, mce_err);
- if (mce_err->ignore_event)
- return 1;
- /*
- * On specific SCOM read via MMIO we may get a machine check
- * exception with SRR0 pointing inside opal. If that is the
- * case OPAL may have recovery address to re-read SCOM data in
- * different way and hence we can recover from this MC.
- */
- if (ppc_md.mce_check_early_recovery) {
- if (ppc_md.mce_check_early_recovery(regs))
- return 1;
- }
- return 0;
- }
- static long mce_handle_error(struct pt_regs *regs,
- unsigned long srr1,
- const struct mce_derror_table dtable[],
- const struct mce_ierror_table itable[])
- {
- struct mce_error_info mce_err = { 0 };
- uint64_t addr, phys_addr = ULONG_MAX;
- long handled;
- if (SRR1_MC_LOADSTORE(srr1))
- handled = mce_handle_derror(regs, dtable, &mce_err, &addr,
- &phys_addr);
- else
- handled = mce_handle_ierror(regs, srr1, itable, &mce_err, &addr,
- &phys_addr);
- if (!handled && mce_err.error_type == MCE_ERROR_TYPE_UE)
- handled = mce_handle_ue_error(regs, &mce_err);
- save_mce_event(regs, handled, &mce_err, regs->nip, addr, phys_addr);
- return handled;
- }
- long __machine_check_early_realmode_p7(struct pt_regs *regs)
- {
- /* P7 DD1 leaves top bits of DSISR undefined */
- regs->dsisr &= 0x0000ffff;
- return mce_handle_error(regs, regs->msr,
- mce_p7_derror_table, mce_p7_ierror_table);
- }
- long __machine_check_early_realmode_p8(struct pt_regs *regs)
- {
- return mce_handle_error(regs, regs->msr,
- mce_p8_derror_table, mce_p8_ierror_table);
- }
- long __machine_check_early_realmode_p9(struct pt_regs *regs)
- {
- unsigned long srr1 = regs->msr;
- /*
- * On POWER9 DD2.1 and below, it's possible to get a machine check
- * caused by a paste instruction where only DSISR bit 25 is set. This
- * will result in the MCE handler seeing an unknown event and the kernel
- * crashing. An MCE that occurs like this is spurious, so we don't need
- * to do anything in terms of servicing it. If there is something that
- * needs to be serviced, the CPU will raise the MCE again with the
- * correct DSISR so that it can be serviced properly. So detect this
- * case and mark it as handled.
- */
- if (SRR1_MC_LOADSTORE(regs->msr) && regs->dsisr == 0x02000000)
- return 1;
- /*
- * Async machine check due to bad real address from store or foreign
- * link time out comes with the load/store bit (PPC bit 42) set in
- * SRR1, but the cause comes in SRR1 not DSISR. Clear bit 42 so we're
- * directed to the ierror table so it will find the cause (which
- * describes it correctly as a store error).
- */
- if (SRR1_MC_LOADSTORE(srr1) &&
- ((srr1 & 0x081c0000) == 0x08140000 ||
- (srr1 & 0x081c0000) == 0x08180000)) {
- srr1 &= ~PPC_BIT(42);
- }
- return mce_handle_error(regs, srr1,
- mce_p9_derror_table, mce_p9_ierror_table);
- }
- long __machine_check_early_realmode_p10(struct pt_regs *regs)
- {
- unsigned long srr1 = regs->msr;
- /*
- * Async machine check due to bad real address from store comes with
- * the load/store bit (PPC bit 42) set in SRR1, but the cause comes in
- * SRR1 not DSISR. Clear bit 42 so we're directed to the ierror table
- * so it will find the cause (which describes it correctly as a store
- * error).
- */
- if (SRR1_MC_LOADSTORE(srr1) &&
- (srr1 & 0x081c0000) == 0x08140000) {
- srr1 &= ~PPC_BIT(42);
- }
- return mce_handle_error(regs, srr1,
- mce_p10_derror_table, mce_p10_ierror_table);
- }
|