aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-09-19 13:45:04 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-09-20 02:10:03 -0400
commited79ba9e15f84cef05aba5cbfe6e93f9b43c31f4 (patch)
tree7df9a2dc61e5c43e426562adc4367cb7e32380a0
parenta125e0928c736bc50cdd9a13151d4f4ee7821266 (diff)
powerpc/powernv: Machine check and other system interrupts
OPAL can handle various interrupt for us such as Machine Checks (it performs all sorts of recovery tasks and passes back control to us with informations about the error), Hardware Management Interrupts and Softpatch interrupts. This wires up the mechanisms and prints out specific informations returned by HAL when a machine check occurs. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--arch/powerpc/include/asm/opal.h2
-rw-r--r--arch/powerpc/include/asm/paca.h8
-rw-r--r--arch/powerpc/kernel/asm-offsets.c10
-rw-r--r--arch/powerpc/kernel/exceptions-64s.S27
-rw-r--r--arch/powerpc/platforms/powernv/opal.c130
-rw-r--r--arch/powerpc/platforms/powernv/setup.c1
6 files changed, 174 insertions, 4 deletions
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index 77ebe50020a2..2893e8f5406d 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -436,6 +436,8 @@ extern void opal_get_rtc_time(struct rtc_time *tm);
436extern unsigned long opal_get_boot_time(void); 436extern unsigned long opal_get_boot_time(void);
437extern void opal_nvram_init(void); 437extern void opal_nvram_init(void);
438 438
439extern int opal_machine_check(struct pt_regs *regs);
440
439#endif /* __ASSEMBLY__ */ 441#endif /* __ASSEMBLY__ */
440 442
441#endif /* __OPAL_H */ 443#endif /* __OPAL_H */
diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h
index 516bfb3f47d9..17722c73ba2e 100644
--- a/arch/powerpc/include/asm/paca.h
+++ b/arch/powerpc/include/asm/paca.h
@@ -43,6 +43,7 @@ extern unsigned int debug_smp_processor_id(void); /* from linux/smp.h */
43#define get_slb_shadow() (get_paca()->slb_shadow_ptr) 43#define get_slb_shadow() (get_paca()->slb_shadow_ptr)
44 44
45struct task_struct; 45struct task_struct;
46struct opal_machine_check_event;
46 47
47/* 48/*
48 * Defines the layout of the paca. 49 * Defines the layout of the paca.
@@ -135,6 +136,13 @@ struct paca_struct {
135 u8 io_sync; /* writel() needs spin_unlock sync */ 136 u8 io_sync; /* writel() needs spin_unlock sync */
136 u8 irq_work_pending; /* IRQ_WORK interrupt while soft-disable */ 137 u8 irq_work_pending; /* IRQ_WORK interrupt while soft-disable */
137 138
139#ifdef CONFIG_PPC_POWERNV
140 /* Pointer to OPAL machine check event structure set by the
141 * early exception handler for use by high level C handler
142 */
143 struct opal_machine_check_event *opal_mc_evt;
144#endif
145
138 /* Stuff for accurate time accounting */ 146 /* Stuff for accurate time accounting */
139 u64 user_time; /* accumulated usermode TB ticks */ 147 u64 user_time; /* accumulated usermode TB ticks */
140 u64 system_time; /* accumulated system TB ticks */ 148 u64 system_time; /* accumulated system TB ticks */
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 5f078bc2063e..536ffa897c6c 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -48,6 +48,9 @@
48#ifdef CONFIG_PPC_ISERIES 48#ifdef CONFIG_PPC_ISERIES
49#include <asm/iseries/alpaca.h> 49#include <asm/iseries/alpaca.h>
50#endif 50#endif
51#ifdef CONFIG_PPC_POWERNV
52#include <asm/opal.h>
53#endif
51#if defined(CONFIG_KVM) || defined(CONFIG_KVM_GUEST) 54#if defined(CONFIG_KVM) || defined(CONFIG_KVM_GUEST)
52#include <linux/kvm_host.h> 55#include <linux/kvm_host.h>
53#endif 56#endif
@@ -609,5 +612,12 @@ int main(void)
609 arch.timing_last_enter.tv32.tbl)); 612 arch.timing_last_enter.tv32.tbl));
610#endif 613#endif
611 614
615#ifdef CONFIG_PPC_POWERNV
616 DEFINE(OPAL_MC_GPR3, offsetof(struct opal_machine_check_event, gpr3));
617 DEFINE(OPAL_MC_SRR0, offsetof(struct opal_machine_check_event, srr0));
618 DEFINE(OPAL_MC_SRR1, offsetof(struct opal_machine_check_event, srr1));
619 DEFINE(PACA_OPAL_MC_EVT, offsetof(struct paca_struct, opal_mc_evt));
620#endif
621
612 return 0; 622 return 0;
613} 623}
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 41b02c792aa3..d51458fa8dee 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -1143,7 +1143,7 @@ _GLOBAL(do_stab_bolted)
1143 rfid 1143 rfid
1144 b . /* prevent speculative execution */ 1144 b . /* prevent speculative execution */
1145 1145
1146#ifdef CONFIG_PPC_PSERIES 1146#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV)
1147/* 1147/*
1148 * Data area reserved for FWNMI option. 1148 * Data area reserved for FWNMI option.
1149 * This address (0x7000) is fixed by the RPA. 1149 * This address (0x7000) is fixed by the RPA.
@@ -1151,7 +1151,7 @@ _GLOBAL(do_stab_bolted)
1151 .= 0x7000 1151 .= 0x7000
1152 .globl fwnmi_data_area 1152 .globl fwnmi_data_area
1153fwnmi_data_area: 1153fwnmi_data_area:
1154#endif /* CONFIG_PPC_PSERIES */ 1154#endif /* defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) */
1155 1155
1156 /* iSeries does not use the FWNMI stuff, so it is safe to put 1156 /* iSeries does not use the FWNMI stuff, so it is safe to put
1157 * this here, even if we later allow kernels that will boot on 1157 * this here, even if we later allow kernels that will boot on
@@ -1176,9 +1176,12 @@ xLparMap:
1176 1176
1177#endif /* CONFIG_PPC_ISERIES */ 1177#endif /* CONFIG_PPC_ISERIES */
1178 1178
1179#ifdef CONFIG_PPC_PSERIES 1179#if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV)
1180 /* pseries and powernv need to keep the whole page from
1181 * 0x7000 to 0x8000 free for use by the firmware
1182 */
1180 . = 0x8000 1183 . = 0x8000
1181#endif /* CONFIG_PPC_PSERIES */ 1184#endif /* defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) */
1182 1185
1183/* 1186/*
1184 * Space for CPU0's segment table. 1187 * Space for CPU0's segment table.
@@ -1193,3 +1196,19 @@ xLparMap:
1193 .globl initial_stab 1196 .globl initial_stab
1194initial_stab: 1197initial_stab:
1195 .space 4096 1198 .space 4096
1199#ifdef CONFIG_PPC_POWERNV
1200_GLOBAL(opal_mc_secondary_handler)
1201 HMT_MEDIUM
1202 SET_SCRATCH0(r13)
1203 GET_PACA(r13)
1204 clrldi r3,r3,2
1205 tovirt(r3,r3)
1206 std r3,PACA_OPAL_MC_EVT(r13)
1207 ld r13,OPAL_MC_SRR0(r3)
1208 mtspr SPRN_SRR0,r13
1209 ld r13,OPAL_MC_SRR1(r3)
1210 mtspr SPRN_SRR1,r13
1211 ld r3,OPAL_MC_GPR3(r3)
1212 GET_SCRATCH0(r13)
1213 b machine_check_pSeries
1214#endif /* CONFIG_PPC_POWERNV */
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 5a598caefcba..aaa0dba49471 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -27,12 +27,14 @@ struct opal {
27 27
28static struct device_node *opal_node; 28static struct device_node *opal_node;
29static DEFINE_SPINLOCK(opal_write_lock); 29static DEFINE_SPINLOCK(opal_write_lock);
30extern u64 opal_mc_secondary_handler[];
30 31
31int __init early_init_dt_scan_opal(unsigned long node, 32int __init early_init_dt_scan_opal(unsigned long node,
32 const char *uname, int depth, void *data) 33 const char *uname, int depth, void *data)
33{ 34{
34 const void *basep, *entryp; 35 const void *basep, *entryp;
35 unsigned long basesz, entrysz; 36 unsigned long basesz, entrysz;
37 u64 glue;
36 38
37 if (depth != 1 || strcmp(uname, "ibm,opal") != 0) 39 if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
38 return 0; 40 return 0;
@@ -59,6 +61,19 @@ int __init early_init_dt_scan_opal(unsigned long node,
59 printk("OPAL V1 detected !\n"); 61 printk("OPAL V1 detected !\n");
60 } 62 }
61 63
64 /* Hookup some exception handlers. We use the fwnmi area at 0x7000
65 * to provide the glue space to OPAL
66 */
67 glue = 0x7000;
68 opal_register_exception_handler(OPAL_MACHINE_CHECK_HANDLER,
69 __pa(opal_mc_secondary_handler[0]),
70 glue);
71 glue += 128;
72 opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER,
73 0, glue);
74 glue += 128;
75 opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue);
76
62 return 1; 77 return 1;
63} 78}
64 79
@@ -136,6 +151,121 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
136 return written; 151 return written;
137} 152}
138 153
154int opal_machine_check(struct pt_regs *regs)
155{
156 struct opal_machine_check_event *opal_evt = get_paca()->opal_mc_evt;
157 struct opal_machine_check_event evt;
158 const char *level, *sevstr, *subtype;
159 static const char *opal_mc_ue_types[] = {
160 "Indeterminate",
161 "Instruction fetch",
162 "Page table walk ifetch",
163 "Load/Store",
164 "Page table walk Load/Store",
165 };
166 static const char *opal_mc_slb_types[] = {
167 "Indeterminate",
168 "Parity",
169 "Multihit",
170 };
171 static const char *opal_mc_erat_types[] = {
172 "Indeterminate",
173 "Parity",
174 "Multihit",
175 };
176 static const char *opal_mc_tlb_types[] = {
177 "Indeterminate",
178 "Parity",
179 "Multihit",
180 };
181
182 /* Copy the event structure and release the original */
183 evt = *opal_evt;
184 opal_evt->in_use = 0;
185
186 /* Print things out */
187 if (evt.version != OpalMCE_V1) {
188 pr_err("Machine Check Exception, Unknown event version %d !\n",
189 evt.version);
190 return 0;
191 }
192 switch(evt.severity) {
193 case OpalMCE_SEV_NO_ERROR:
194 level = KERN_INFO;
195 sevstr = "Harmless";
196 break;
197 case OpalMCE_SEV_WARNING:
198 level = KERN_WARNING;
199 sevstr = "";
200 break;
201 case OpalMCE_SEV_ERROR_SYNC:
202 level = KERN_ERR;
203 sevstr = "Severe";
204 break;
205 case OpalMCE_SEV_FATAL:
206 default:
207 level = KERN_ERR;
208 sevstr = "Fatal";
209 break;
210 }
211
212 printk("%s%s Machine check interrupt [%s]\n", level, sevstr,
213 evt.disposition == OpalMCE_DISPOSITION_RECOVERED ?
214 "Recovered" : "[Not recovered");
215 printk("%s Initiator: %s\n", level,
216 evt.initiator == OpalMCE_INITIATOR_CPU ? "CPU" : "Unknown");
217 switch(evt.error_type) {
218 case OpalMCE_ERROR_TYPE_UE:
219 subtype = evt.u.ue_error.ue_error_type <
220 ARRAY_SIZE(opal_mc_ue_types) ?
221 opal_mc_ue_types[evt.u.ue_error.ue_error_type]
222 : "Unknown";
223 printk("%s Error type: UE [%s]\n", level, subtype);
224 if (evt.u.ue_error.effective_address_provided)
225 printk("%s Effective address: %016llx\n",
226 level, evt.u.ue_error.effective_address);
227 if (evt.u.ue_error.physical_address_provided)
228 printk("%s Physial address: %016llx\n",
229 level, evt.u.ue_error.physical_address);
230 break;
231 case OpalMCE_ERROR_TYPE_SLB:
232 subtype = evt.u.slb_error.slb_error_type <
233 ARRAY_SIZE(opal_mc_slb_types) ?
234 opal_mc_slb_types[evt.u.slb_error.slb_error_type]
235 : "Unknown";
236 printk("%s Error type: SLB [%s]\n", level, subtype);
237 if (evt.u.slb_error.effective_address_provided)
238 printk("%s Effective address: %016llx\n",
239 level, evt.u.slb_error.effective_address);
240 break;
241 case OpalMCE_ERROR_TYPE_ERAT:
242 subtype = evt.u.erat_error.erat_error_type <
243 ARRAY_SIZE(opal_mc_erat_types) ?
244 opal_mc_erat_types[evt.u.erat_error.erat_error_type]
245 : "Unknown";
246 printk("%s Error type: ERAT [%s]\n", level, subtype);
247 if (evt.u.erat_error.effective_address_provided)
248 printk("%s Effective address: %016llx\n",
249 level, evt.u.erat_error.effective_address);
250 break;
251 case OpalMCE_ERROR_TYPE_TLB:
252 subtype = evt.u.tlb_error.tlb_error_type <
253 ARRAY_SIZE(opal_mc_tlb_types) ?
254 opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type]
255 : "Unknown";
256 printk("%s Error type: TLB [%s]\n", level, subtype);
257 if (evt.u.tlb_error.effective_address_provided)
258 printk("%s Effective address: %016llx\n",
259 level, evt.u.tlb_error.effective_address);
260 break;
261 default:
262 case OpalMCE_ERROR_TYPE_UNKNOWN:
263 printk("%s Error type: Unknown\n", level);
264 break;
265 }
266 return evt.severity == OpalMCE_SEV_FATAL ? 0 : 1;
267}
268
139static irqreturn_t opal_interrupt(int irq, void *data) 269static irqreturn_t opal_interrupt(int irq, void *data)
140{ 270{
141 uint64_t events; 271 uint64_t events;
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 4a2b2e279593..f0242f3fd3e6 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -141,6 +141,7 @@ static void __init pnv_setup_machdep_opal(void)
141 ppc_md.restart = pnv_restart; 141 ppc_md.restart = pnv_restart;
142 ppc_md.power_off = pnv_power_off; 142 ppc_md.power_off = pnv_power_off;
143 ppc_md.halt = pnv_halt; 143 ppc_md.halt = pnv_halt;
144 ppc_md.machine_check_exception = opal_machine_check;
144} 145}
145 146
146#ifdef CONFIG_PPC_POWERNV_RTAS 147#ifdef CONFIG_PPC_POWERNV_RTAS