aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorMahesh Salgaonkar <mahesh@linux.vnet.ibm.com>2013-10-30 10:35:11 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2013-12-05 00:04:39 -0500
commite22a22740c1ac23aaa10835f026b3549ee3e4e75 (patch)
treeb9657323d8977882dab4d970316f88281466e9ed /arch
parent0440705049b041d84268ea57f6e90e2f16618897 (diff)
powerpc/book3s: Flush SLB/TLBs if we get SLB/TLB machine check errors on power7.
If we get a machine check exception due to SLB or TLB errors, then flush SLBs/TLBs and reload SLBs to recover. We do this in real mode before turning on MMU. Otherwise we would run into nested machine checks. If we get a machine check when we are in guest, then just flush the SLBs and continue. This patch handles errors for power7. The next patch will handle errors for power8 Signed-off-by: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com> Signed-off-by: Paul Mackerras <paulus@samba.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/include/asm/bitops.h5
-rw-r--r--arch/powerpc/include/asm/mce.h67
-rw-r--r--arch/powerpc/kernel/Makefile1
-rw-r--r--arch/powerpc/kernel/cputable.c4
-rw-r--r--arch/powerpc/kernel/mce_power.c150
5 files changed, 227 insertions, 0 deletions
diff --git a/arch/powerpc/include/asm/bitops.h b/arch/powerpc/include/asm/bitops.h
index 910194e9a1e2..a5e9a7d494d8 100644
--- a/arch/powerpc/include/asm/bitops.h
+++ b/arch/powerpc/include/asm/bitops.h
@@ -46,6 +46,11 @@
46#include <asm/asm-compat.h> 46#include <asm/asm-compat.h>
47#include <asm/synch.h> 47#include <asm/synch.h>
48 48
49/* PPC bit number conversion */
50#define PPC_BITLSHIFT(be) (BITS_PER_LONG - 1 - (be))
51#define PPC_BIT(bit) (1UL << PPC_BITLSHIFT(bit))
52#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs))
53
49/* 54/*
50 * clear_bit doesn't imply a memory barrier 55 * clear_bit doesn't imply a memory barrier
51 */ 56 */
diff --git a/arch/powerpc/include/asm/mce.h b/arch/powerpc/include/asm/mce.h
new file mode 100644
index 000000000000..8157d4eaead6
--- /dev/null
+++ b/arch/powerpc/include/asm/mce.h
@@ -0,0 +1,67 @@
1/*
2 * Machine check exception header file.
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#ifndef __ASM_PPC64_MCE_H__
23#define __ASM_PPC64_MCE_H__
24
25#include <linux/bitops.h>
26
27/*
28 * Machine Check bits on power7 and power8
29 */
30#define P7_SRR1_MC_LOADSTORE(srr1) ((srr1) & PPC_BIT(42)) /* P8 too */
31
32/* SRR1 bits for machine check (On Power7 and Power8) */
33#define P7_SRR1_MC_IFETCH(srr1) ((srr1) & PPC_BITMASK(43, 45)) /* P8 too */
34
35#define P7_SRR1_MC_IFETCH_UE (0x1 << PPC_BITLSHIFT(45)) /* P8 too */
36#define P7_SRR1_MC_IFETCH_SLB_PARITY (0x2 << PPC_BITLSHIFT(45)) /* P8 too */
37#define P7_SRR1_MC_IFETCH_SLB_MULTIHIT (0x3 << PPC_BITLSHIFT(45)) /* P8 too */
38#define P7_SRR1_MC_IFETCH_SLB_BOTH (0x4 << PPC_BITLSHIFT(45))
39#define P7_SRR1_MC_IFETCH_TLB_MULTIHIT (0x5 << PPC_BITLSHIFT(45)) /* P8 too */
40#define P7_SRR1_MC_IFETCH_UE_TLB_RELOAD (0x6 << PPC_BITLSHIFT(45)) /* P8 too */
41#define P7_SRR1_MC_IFETCH_UE_IFU_INTERNAL (0x7 << PPC_BITLSHIFT(45))
42
43/* SRR1 bits for machine check (On Power8) */
44#define P8_SRR1_MC_IFETCH_ERAT_MULTIHIT (0x4 << PPC_BITLSHIFT(45))
45
46/* DSISR bits for machine check (On Power7 and Power8) */
47#define P7_DSISR_MC_UE (PPC_BIT(48)) /* P8 too */
48#define P7_DSISR_MC_UE_TABLEWALK (PPC_BIT(49)) /* P8 too */
49#define P7_DSISR_MC_ERAT_MULTIHIT (PPC_BIT(52)) /* P8 too */
50#define P7_DSISR_MC_TLB_MULTIHIT_MFTLB (PPC_BIT(53)) /* P8 too */
51#define P7_DSISR_MC_SLB_PARITY_MFSLB (PPC_BIT(55)) /* P8 too */
52#define P7_DSISR_MC_SLB_MULTIHIT (PPC_BIT(56)) /* P8 too */
53#define P7_DSISR_MC_SLB_MULTIHIT_PARITY (PPC_BIT(57)) /* P8 too */
54
55/*
56 * DSISR bits for machine check (Power8) in addition to above.
57 * Secondary DERAT Multihit
58 */
59#define P8_DSISR_MC_ERAT_MULTIHIT_SEC (PPC_BIT(54))
60
61/* SLB error bits */
62#define P7_DSISR_MC_SLB_ERRORS (P7_DSISR_MC_ERAT_MULTIHIT | \
63 P7_DSISR_MC_SLB_PARITY_MFSLB | \
64 P7_DSISR_MC_SLB_MULTIHIT | \
65 P7_DSISR_MC_SLB_MULTIHIT_PARITY)
66
67#endif /* __ASM_PPC64_MCE_H__ */
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 445cb6e39d5b..07c63d0aa759 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_PPC64) += setup_64.o sys_ppc32.o \
39obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o 39obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
40obj-$(CONFIG_PPC_BOOK3S_64) += cpu_setup_ppc970.o cpu_setup_pa6t.o 40obj-$(CONFIG_PPC_BOOK3S_64) += cpu_setup_ppc970.o cpu_setup_pa6t.o
41obj-$(CONFIG_PPC_BOOK3S_64) += cpu_setup_power.o 41obj-$(CONFIG_PPC_BOOK3S_64) += cpu_setup_power.o
42obj-$(CONFIG_PPC_BOOK3S_64) += mce_power.o
42obj64-$(CONFIG_RELOCATABLE) += reloc_64.o 43obj64-$(CONFIG_RELOCATABLE) += reloc_64.o
43obj-$(CONFIG_PPC_BOOK3E_64) += exceptions-64e.o idle_book3e.o 44obj-$(CONFIG_PPC_BOOK3E_64) += exceptions-64e.o idle_book3e.o
44obj-$(CONFIG_PPC_A2) += cpu_setup_a2.o 45obj-$(CONFIG_PPC_A2) += cpu_setup_a2.o
diff --git a/arch/powerpc/kernel/cputable.c b/arch/powerpc/kernel/cputable.c
index 55d0f9c282b8..c54188bcdd9e 100644
--- a/arch/powerpc/kernel/cputable.c
+++ b/arch/powerpc/kernel/cputable.c
@@ -73,6 +73,7 @@ extern void __restore_cpu_power8(void);
73extern void __restore_cpu_a2(void); 73extern void __restore_cpu_a2(void);
74extern void __flush_tlb_power7(unsigned long inval_selector); 74extern void __flush_tlb_power7(unsigned long inval_selector);
75extern void __flush_tlb_power8(unsigned long inval_selector); 75extern void __flush_tlb_power8(unsigned long inval_selector);
76extern long __machine_check_early_realmode_p7(struct pt_regs *regs);
76#endif /* CONFIG_PPC64 */ 77#endif /* CONFIG_PPC64 */
77#if defined(CONFIG_E500) 78#if defined(CONFIG_E500)
78extern void __setup_cpu_e5500(unsigned long offset, struct cpu_spec* spec); 79extern void __setup_cpu_e5500(unsigned long offset, struct cpu_spec* spec);
@@ -443,6 +444,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
443 .cpu_setup = __setup_cpu_power7, 444 .cpu_setup = __setup_cpu_power7,
444 .cpu_restore = __restore_cpu_power7, 445 .cpu_restore = __restore_cpu_power7,
445 .flush_tlb = __flush_tlb_power7, 446 .flush_tlb = __flush_tlb_power7,
447 .machine_check_early = __machine_check_early_realmode_p7,
446 .platform = "power7", 448 .platform = "power7",
447 }, 449 },
448 { /* 2.07-compliant processor, i.e. Power8 "architected" mode */ 450 { /* 2.07-compliant processor, i.e. Power8 "architected" mode */
@@ -479,6 +481,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
479 .cpu_setup = __setup_cpu_power7, 481 .cpu_setup = __setup_cpu_power7,
480 .cpu_restore = __restore_cpu_power7, 482 .cpu_restore = __restore_cpu_power7,
481 .flush_tlb = __flush_tlb_power7, 483 .flush_tlb = __flush_tlb_power7,
484 .machine_check_early = __machine_check_early_realmode_p7,
482 .platform = "power7", 485 .platform = "power7",
483 }, 486 },
484 { /* Power7+ */ 487 { /* Power7+ */
@@ -498,6 +501,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
498 .cpu_setup = __setup_cpu_power7, 501 .cpu_setup = __setup_cpu_power7,
499 .cpu_restore = __restore_cpu_power7, 502 .cpu_restore = __restore_cpu_power7,
500 .flush_tlb = __flush_tlb_power7, 503 .flush_tlb = __flush_tlb_power7,
504 .machine_check_early = __machine_check_early_realmode_p7,
501 .platform = "power7+", 505 .platform = "power7+",
502 }, 506 },
503 { /* Power8E */ 507 { /* Power8E */
diff --git a/arch/powerpc/kernel/mce_power.c b/arch/powerpc/kernel/mce_power.c
new file mode 100644
index 000000000000..690547319b03
--- /dev/null
+++ b/arch/powerpc/kernel/mce_power.c
@@ -0,0 +1,150 @@
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
30/* flush SLBs and reload */
31static void flush_and_reload_slb(void)
32{
33 struct slb_shadow *slb;
34 unsigned long i, n;
35
36 /* Invalidate all SLBs */
37 asm volatile("slbmte %0,%0; slbia" : : "r" (0));
38
39#ifdef CONFIG_KVM_BOOK3S_HANDLER
40 /*
41 * If machine check is hit when in guest or in transition, we will
42 * only flush the SLBs and continue.
43 */
44 if (get_paca()->kvm_hstate.in_guest)
45 return;
46#endif
47
48 /* For host kernel, reload the SLBs from shadow SLB buffer. */
49 slb = get_slb_shadow();
50 if (!slb)
51 return;
52
53 n = min_t(u32, slb->persistent, SLB_MIN_SIZE);
54
55 /* Load up the SLB entries from shadow SLB */
56 for (i = 0; i < n; i++) {
57 unsigned long rb = slb->save_area[i].esid;
58 unsigned long rs = slb->save_area[i].vsid;
59
60 rb = (rb & ~0xFFFul) | i;
61 asm volatile("slbmte %0,%1" : : "r" (rs), "r" (rb));
62 }
63}
64
65static long mce_handle_derror(uint64_t dsisr, uint64_t slb_error_bits)
66{
67 long handled = 1;
68
69 /*
70 * flush and reload SLBs for SLB errors and flush TLBs for TLB errors.
71 * reset the error bits whenever we handle them so that at the end
72 * we can check whether we handled all of them or not.
73 * */
74 if (dsisr & slb_error_bits) {
75 flush_and_reload_slb();
76 /* reset error bits */
77 dsisr &= ~(slb_error_bits);
78 }
79 if (dsisr & P7_DSISR_MC_TLB_MULTIHIT_MFTLB) {
80 if (cur_cpu_spec && cur_cpu_spec->flush_tlb)
81 cur_cpu_spec->flush_tlb(TLBIEL_INVAL_PAGE);
82 /* reset error bits */
83 dsisr &= ~P7_DSISR_MC_TLB_MULTIHIT_MFTLB;
84 }
85 /* Any other errors we don't understand? */
86 if (dsisr & 0xffffffffUL)
87 handled = 0;
88
89 return handled;
90}
91
92static long mce_handle_derror_p7(uint64_t dsisr)
93{
94 return mce_handle_derror(dsisr, P7_DSISR_MC_SLB_ERRORS);
95}
96
97static long mce_handle_common_ierror(uint64_t srr1)
98{
99 long handled = 0;
100
101 switch (P7_SRR1_MC_IFETCH(srr1)) {
102 case 0:
103 break;
104 case P7_SRR1_MC_IFETCH_SLB_PARITY:
105 case P7_SRR1_MC_IFETCH_SLB_MULTIHIT:
106 /* flush and reload SLBs for SLB errors. */
107 flush_and_reload_slb();
108 handled = 1;
109 break;
110 case P7_SRR1_MC_IFETCH_TLB_MULTIHIT:
111 if (cur_cpu_spec && cur_cpu_spec->flush_tlb) {
112 cur_cpu_spec->flush_tlb(TLBIEL_INVAL_PAGE);
113 handled = 1;
114 }
115 break;
116 default:
117 break;
118 }
119
120 return handled;
121}
122
123static long mce_handle_ierror_p7(uint64_t srr1)
124{
125 long handled = 0;
126
127 handled = mce_handle_common_ierror(srr1);
128
129 if (P7_SRR1_MC_IFETCH(srr1) == P7_SRR1_MC_IFETCH_SLB_BOTH) {
130 flush_and_reload_slb();
131 handled = 1;
132 }
133 return handled;
134}
135
136long __machine_check_early_realmode_p7(struct pt_regs *regs)
137{
138 uint64_t srr1;
139 long handled = 1;
140
141 srr1 = regs->msr;
142
143 if (P7_SRR1_MC_LOADSTORE(srr1))
144 handled = mce_handle_derror_p7(regs->dsisr);
145 else
146 handled = mce_handle_ierror_p7(srr1);
147
148 /* TODO: Decode machine check reason. */
149 return handled;
150}