diff options
Diffstat (limited to 'arch/powerpc/platforms/powernv/opal.c')
-rw-r--r-- | arch/powerpc/platforms/powernv/opal.c | 267 |
1 files changed, 157 insertions, 110 deletions
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c index 1c798cd55372..65499adaecff 100644 --- a/arch/powerpc/platforms/powernv/opal.c +++ b/arch/powerpc/platforms/powernv/opal.c | |||
@@ -18,9 +18,12 @@ | |||
18 | #include <linux/interrupt.h> | 18 | #include <linux/interrupt.h> |
19 | #include <linux/notifier.h> | 19 | #include <linux/notifier.h> |
20 | #include <linux/slab.h> | 20 | #include <linux/slab.h> |
21 | #include <linux/sched.h> | ||
21 | #include <linux/kobject.h> | 22 | #include <linux/kobject.h> |
23 | #include <linux/delay.h> | ||
22 | #include <asm/opal.h> | 24 | #include <asm/opal.h> |
23 | #include <asm/firmware.h> | 25 | #include <asm/firmware.h> |
26 | #include <asm/mce.h> | ||
24 | 27 | ||
25 | #include "powernv.h" | 28 | #include "powernv.h" |
26 | 29 | ||
@@ -38,6 +41,7 @@ extern u64 opal_mc_secondary_handler[]; | |||
38 | static unsigned int *opal_irqs; | 41 | static unsigned int *opal_irqs; |
39 | static unsigned int opal_irq_count; | 42 | static unsigned int opal_irq_count; |
40 | static ATOMIC_NOTIFIER_HEAD(opal_notifier_head); | 43 | static ATOMIC_NOTIFIER_HEAD(opal_notifier_head); |
44 | static struct atomic_notifier_head opal_msg_notifier_head[OPAL_MSG_TYPE_MAX]; | ||
41 | static DEFINE_SPINLOCK(opal_notifier_lock); | 45 | static DEFINE_SPINLOCK(opal_notifier_lock); |
42 | static uint64_t last_notified_mask = 0x0ul; | 46 | static uint64_t last_notified_mask = 0x0ul; |
43 | static atomic_t opal_notifier_hold = ATOMIC_INIT(0); | 47 | static atomic_t opal_notifier_hold = ATOMIC_INIT(0); |
@@ -88,14 +92,10 @@ static int __init opal_register_exception_handlers(void) | |||
88 | if (!(powerpc_firmware_features & FW_FEATURE_OPAL)) | 92 | if (!(powerpc_firmware_features & FW_FEATURE_OPAL)) |
89 | return -ENODEV; | 93 | return -ENODEV; |
90 | 94 | ||
91 | /* Hookup some exception handlers. We use the fwnmi area at 0x7000 | 95 | /* Hookup some exception handlers except machine check. We use the |
92 | * to provide the glue space to OPAL | 96 | * fwnmi area at 0x7000 to provide the glue space to OPAL |
93 | */ | 97 | */ |
94 | glue = 0x7000; | 98 | glue = 0x7000; |
95 | opal_register_exception_handler(OPAL_MACHINE_CHECK_HANDLER, | ||
96 | __pa(opal_mc_secondary_handler[0]), | ||
97 | glue); | ||
98 | glue += 128; | ||
99 | opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER, | 99 | opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER, |
100 | 0, glue); | 100 | 0, glue); |
101 | glue += 128; | 101 | glue += 128; |
@@ -169,6 +169,95 @@ void opal_notifier_disable(void) | |||
169 | atomic_set(&opal_notifier_hold, 1); | 169 | atomic_set(&opal_notifier_hold, 1); |
170 | } | 170 | } |
171 | 171 | ||
172 | /* | ||
173 | * Opal message notifier based on message type. Allow subscribers to get | ||
174 | * notified for specific messgae type. | ||
175 | */ | ||
176 | int opal_message_notifier_register(enum OpalMessageType msg_type, | ||
177 | struct notifier_block *nb) | ||
178 | { | ||
179 | if (!nb) { | ||
180 | pr_warning("%s: Invalid argument (%p)\n", | ||
181 | __func__, nb); | ||
182 | return -EINVAL; | ||
183 | } | ||
184 | if (msg_type > OPAL_MSG_TYPE_MAX) { | ||
185 | pr_warning("%s: Invalid message type argument (%d)\n", | ||
186 | __func__, msg_type); | ||
187 | return -EINVAL; | ||
188 | } | ||
189 | return atomic_notifier_chain_register( | ||
190 | &opal_msg_notifier_head[msg_type], nb); | ||
191 | } | ||
192 | |||
193 | static void opal_message_do_notify(uint32_t msg_type, void *msg) | ||
194 | { | ||
195 | /* notify subscribers */ | ||
196 | atomic_notifier_call_chain(&opal_msg_notifier_head[msg_type], | ||
197 | msg_type, msg); | ||
198 | } | ||
199 | |||
200 | static void opal_handle_message(void) | ||
201 | { | ||
202 | s64 ret; | ||
203 | /* | ||
204 | * TODO: pre-allocate a message buffer depending on opal-msg-size | ||
205 | * value in /proc/device-tree. | ||
206 | */ | ||
207 | static struct opal_msg msg; | ||
208 | |||
209 | ret = opal_get_msg(__pa(&msg), sizeof(msg)); | ||
210 | /* No opal message pending. */ | ||
211 | if (ret == OPAL_RESOURCE) | ||
212 | return; | ||
213 | |||
214 | /* check for errors. */ | ||
215 | if (ret) { | ||
216 | pr_warning("%s: Failed to retrive opal message, err=%lld\n", | ||
217 | __func__, ret); | ||
218 | return; | ||
219 | } | ||
220 | |||
221 | /* Sanity check */ | ||
222 | if (msg.msg_type > OPAL_MSG_TYPE_MAX) { | ||
223 | pr_warning("%s: Unknown message type: %u\n", | ||
224 | __func__, msg.msg_type); | ||
225 | return; | ||
226 | } | ||
227 | opal_message_do_notify(msg.msg_type, (void *)&msg); | ||
228 | } | ||
229 | |||
230 | static int opal_message_notify(struct notifier_block *nb, | ||
231 | unsigned long events, void *change) | ||
232 | { | ||
233 | if (events & OPAL_EVENT_MSG_PENDING) | ||
234 | opal_handle_message(); | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static struct notifier_block opal_message_nb = { | ||
239 | .notifier_call = opal_message_notify, | ||
240 | .next = NULL, | ||
241 | .priority = 0, | ||
242 | }; | ||
243 | |||
244 | static int __init opal_message_init(void) | ||
245 | { | ||
246 | int ret, i; | ||
247 | |||
248 | for (i = 0; i < OPAL_MSG_TYPE_MAX; i++) | ||
249 | ATOMIC_INIT_NOTIFIER_HEAD(&opal_msg_notifier_head[i]); | ||
250 | |||
251 | ret = opal_notifier_register(&opal_message_nb); | ||
252 | if (ret) { | ||
253 | pr_err("%s: Can't register OPAL event notifier (%d)\n", | ||
254 | __func__, ret); | ||
255 | return ret; | ||
256 | } | ||
257 | return 0; | ||
258 | } | ||
259 | early_initcall(opal_message_init); | ||
260 | |||
172 | int opal_get_chars(uint32_t vtermno, char *buf, int count) | 261 | int opal_get_chars(uint32_t vtermno, char *buf, int count) |
173 | { | 262 | { |
174 | s64 rc; | 263 | s64 rc; |
@@ -254,119 +343,62 @@ int opal_put_chars(uint32_t vtermno, const char *data, int total_len) | |||
254 | return written; | 343 | return written; |
255 | } | 344 | } |
256 | 345 | ||
346 | static int opal_recover_mce(struct pt_regs *regs, | ||
347 | struct machine_check_event *evt) | ||
348 | { | ||
349 | int recovered = 0; | ||
350 | uint64_t ea = get_mce_fault_addr(evt); | ||
351 | |||
352 | if (!(regs->msr & MSR_RI)) { | ||
353 | /* If MSR_RI isn't set, we cannot recover */ | ||
354 | recovered = 0; | ||
355 | } else if (evt->disposition == MCE_DISPOSITION_RECOVERED) { | ||
356 | /* Platform corrected itself */ | ||
357 | recovered = 1; | ||
358 | } else if (ea && !is_kernel_addr(ea)) { | ||
359 | /* | ||
360 | * Faulting address is not in kernel text. We should be fine. | ||
361 | * We need to find which process uses this address. | ||
362 | * For now, kill the task if we have received exception when | ||
363 | * in userspace. | ||
364 | * | ||
365 | * TODO: Queue up this address for hwpoisioning later. | ||
366 | */ | ||
367 | if (user_mode(regs) && !is_global_init(current)) { | ||
368 | _exception(SIGBUS, regs, BUS_MCEERR_AR, regs->nip); | ||
369 | recovered = 1; | ||
370 | } else | ||
371 | recovered = 0; | ||
372 | } else if (user_mode(regs) && !is_global_init(current) && | ||
373 | evt->severity == MCE_SEV_ERROR_SYNC) { | ||
374 | /* | ||
375 | * If we have received a synchronous error when in userspace | ||
376 | * kill the task. | ||
377 | */ | ||
378 | _exception(SIGBUS, regs, BUS_MCEERR_AR, regs->nip); | ||
379 | recovered = 1; | ||
380 | } | ||
381 | return recovered; | ||
382 | } | ||
383 | |||
257 | int opal_machine_check(struct pt_regs *regs) | 384 | int opal_machine_check(struct pt_regs *regs) |
258 | { | 385 | { |
259 | struct opal_machine_check_event *opal_evt = get_paca()->opal_mc_evt; | 386 | struct machine_check_event evt; |
260 | struct opal_machine_check_event evt; | 387 | |
261 | const char *level, *sevstr, *subtype; | 388 | if (!get_mce_event(&evt, MCE_EVENT_RELEASE)) |
262 | static const char *opal_mc_ue_types[] = { | 389 | return 0; |
263 | "Indeterminate", | ||
264 | "Instruction fetch", | ||
265 | "Page table walk ifetch", | ||
266 | "Load/Store", | ||
267 | "Page table walk Load/Store", | ||
268 | }; | ||
269 | static const char *opal_mc_slb_types[] = { | ||
270 | "Indeterminate", | ||
271 | "Parity", | ||
272 | "Multihit", | ||
273 | }; | ||
274 | static const char *opal_mc_erat_types[] = { | ||
275 | "Indeterminate", | ||
276 | "Parity", | ||
277 | "Multihit", | ||
278 | }; | ||
279 | static const char *opal_mc_tlb_types[] = { | ||
280 | "Indeterminate", | ||
281 | "Parity", | ||
282 | "Multihit", | ||
283 | }; | ||
284 | |||
285 | /* Copy the event structure and release the original */ | ||
286 | evt = *opal_evt; | ||
287 | opal_evt->in_use = 0; | ||
288 | 390 | ||
289 | /* Print things out */ | 391 | /* Print things out */ |
290 | if (evt.version != OpalMCE_V1) { | 392 | if (evt.version != MCE_V1) { |
291 | pr_err("Machine Check Exception, Unknown event version %d !\n", | 393 | pr_err("Machine Check Exception, Unknown event version %d !\n", |
292 | evt.version); | 394 | evt.version); |
293 | return 0; | 395 | return 0; |
294 | } | 396 | } |
295 | switch(evt.severity) { | 397 | machine_check_print_event_info(&evt); |
296 | case OpalMCE_SEV_NO_ERROR: | ||
297 | level = KERN_INFO; | ||
298 | sevstr = "Harmless"; | ||
299 | break; | ||
300 | case OpalMCE_SEV_WARNING: | ||
301 | level = KERN_WARNING; | ||
302 | sevstr = ""; | ||
303 | break; | ||
304 | case OpalMCE_SEV_ERROR_SYNC: | ||
305 | level = KERN_ERR; | ||
306 | sevstr = "Severe"; | ||
307 | break; | ||
308 | case OpalMCE_SEV_FATAL: | ||
309 | default: | ||
310 | level = KERN_ERR; | ||
311 | sevstr = "Fatal"; | ||
312 | break; | ||
313 | } | ||
314 | 398 | ||
315 | printk("%s%s Machine check interrupt [%s]\n", level, sevstr, | 399 | if (opal_recover_mce(regs, &evt)) |
316 | evt.disposition == OpalMCE_DISPOSITION_RECOVERED ? | 400 | return 1; |
317 | "Recovered" : "[Not recovered"); | 401 | return 0; |
318 | printk("%s Initiator: %s\n", level, | ||
319 | evt.initiator == OpalMCE_INITIATOR_CPU ? "CPU" : "Unknown"); | ||
320 | switch(evt.error_type) { | ||
321 | case OpalMCE_ERROR_TYPE_UE: | ||
322 | subtype = evt.u.ue_error.ue_error_type < | ||
323 | ARRAY_SIZE(opal_mc_ue_types) ? | ||
324 | opal_mc_ue_types[evt.u.ue_error.ue_error_type] | ||
325 | : "Unknown"; | ||
326 | printk("%s Error type: UE [%s]\n", level, subtype); | ||
327 | if (evt.u.ue_error.effective_address_provided) | ||
328 | printk("%s Effective address: %016llx\n", | ||
329 | level, evt.u.ue_error.effective_address); | ||
330 | if (evt.u.ue_error.physical_address_provided) | ||
331 | printk("%s Physial address: %016llx\n", | ||
332 | level, evt.u.ue_error.physical_address); | ||
333 | break; | ||
334 | case OpalMCE_ERROR_TYPE_SLB: | ||
335 | subtype = evt.u.slb_error.slb_error_type < | ||
336 | ARRAY_SIZE(opal_mc_slb_types) ? | ||
337 | opal_mc_slb_types[evt.u.slb_error.slb_error_type] | ||
338 | : "Unknown"; | ||
339 | printk("%s Error type: SLB [%s]\n", level, subtype); | ||
340 | if (evt.u.slb_error.effective_address_provided) | ||
341 | printk("%s Effective address: %016llx\n", | ||
342 | level, evt.u.slb_error.effective_address); | ||
343 | break; | ||
344 | case OpalMCE_ERROR_TYPE_ERAT: | ||
345 | subtype = evt.u.erat_error.erat_error_type < | ||
346 | ARRAY_SIZE(opal_mc_erat_types) ? | ||
347 | opal_mc_erat_types[evt.u.erat_error.erat_error_type] | ||
348 | : "Unknown"; | ||
349 | printk("%s Error type: ERAT [%s]\n", level, subtype); | ||
350 | if (evt.u.erat_error.effective_address_provided) | ||
351 | printk("%s Effective address: %016llx\n", | ||
352 | level, evt.u.erat_error.effective_address); | ||
353 | break; | ||
354 | case OpalMCE_ERROR_TYPE_TLB: | ||
355 | subtype = evt.u.tlb_error.tlb_error_type < | ||
356 | ARRAY_SIZE(opal_mc_tlb_types) ? | ||
357 | opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type] | ||
358 | : "Unknown"; | ||
359 | printk("%s Error type: TLB [%s]\n", level, subtype); | ||
360 | if (evt.u.tlb_error.effective_address_provided) | ||
361 | printk("%s Effective address: %016llx\n", | ||
362 | level, evt.u.tlb_error.effective_address); | ||
363 | break; | ||
364 | default: | ||
365 | case OpalMCE_ERROR_TYPE_UNKNOWN: | ||
366 | printk("%s Error type: Unknown\n", level); | ||
367 | break; | ||
368 | } | ||
369 | return evt.severity == OpalMCE_SEV_FATAL ? 0 : 1; | ||
370 | } | 402 | } |
371 | 403 | ||
372 | static irqreturn_t opal_interrupt(int irq, void *data) | 404 | static irqreturn_t opal_interrupt(int irq, void *data) |
@@ -451,10 +483,25 @@ subsys_initcall(opal_init); | |||
451 | void opal_shutdown(void) | 483 | void opal_shutdown(void) |
452 | { | 484 | { |
453 | unsigned int i; | 485 | unsigned int i; |
486 | long rc = OPAL_BUSY; | ||
454 | 487 | ||
488 | /* First free interrupts, which will also mask them */ | ||
455 | for (i = 0; i < opal_irq_count; i++) { | 489 | for (i = 0; i < opal_irq_count; i++) { |
456 | if (opal_irqs[i]) | 490 | if (opal_irqs[i]) |
457 | free_irq(opal_irqs[i], NULL); | 491 | free_irq(opal_irqs[i], NULL); |
458 | opal_irqs[i] = 0; | 492 | opal_irqs[i] = 0; |
459 | } | 493 | } |
494 | |||
495 | /* | ||
496 | * Then sync with OPAL which ensure anything that can | ||
497 | * potentially write to our memory has completed such | ||
498 | * as an ongoing dump retrieval | ||
499 | */ | ||
500 | while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { | ||
501 | rc = opal_sync_host_reboot(); | ||
502 | if (rc == OPAL_BUSY) | ||
503 | opal_poll_events(NULL); | ||
504 | else | ||
505 | mdelay(10); | ||
506 | } | ||
460 | } | 507 | } |