aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/powernv/opal.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-01-28 00:11:26 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-01-28 00:11:26 -0500
commit1b17366d695c8ab03f98d0155357e97a427e1dce (patch)
treed223c79cc33ca1d890d264a202a1dd9c29655039 /arch/powerpc/platforms/powernv/opal.c
parentd12de1ef5eba3adb88f8e9dd81b6a60349466378 (diff)
parent7179ba52889bef7e5e23f72908270e1ab2b7fc6f (diff)
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
Pull powerpc updates from Ben Herrenschmidt: "So here's my next branch for powerpc. A bit late as I was on vacation last week. It's mostly the same stuff that was in next already, I just added two patches today which are the wiring up of lockref for powerpc, which for some reason fell through the cracks last time and is trivial. The highlights are, in addition to a bunch of bug fixes: - Reworked Machine Check handling on kernels running without a hypervisor (or acting as a hypervisor). Provides hooks to handle some errors in real mode such as TLB errors, handle SLB errors, etc... - Support for retrieving memory error information from the service processor on IBM servers running without a hypervisor and routing them to the memory poison infrastructure. - _PAGE_NUMA support on server processors - 32-bit BookE relocatable kernel support - FSL e6500 hardware tablewalk support - A bunch of new/revived board support - FSL e6500 deeper idle states and altivec powerdown support You'll notice a generic mm change here, it has been acked by the relevant authorities and is a pre-req for our _PAGE_NUMA support" * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc: (121 commits) powerpc: Implement arch_spin_is_locked() using arch_spin_value_unlocked() powerpc: Add support for the optimised lockref implementation powerpc/powernv: Call OPAL sync before kexec'ing powerpc/eeh: Escalate error on non-existing PE powerpc/eeh: Handle multiple EEH errors powerpc: Fix transactional FP/VMX/VSX unavailable handlers powerpc: Don't corrupt transactional state when using FP/VMX in kernel powerpc: Reclaim two unused thread_info flag bits powerpc: Fix races with irq_work Move precessing of MCE queued event out from syscall exit path. pseries/cpuidle: Remove redundant call to ppc64_runlatch_off() in cpu idle routines powerpc: Make add_system_ram_resources() __init powerpc: add SATA_MV to ppc64_defconfig powerpc/powernv: Increase candidate fw image size powerpc: Add debug checks to catch invalid cpu-to-node mappings powerpc: Fix the setup of CPU-to-Node mappings during CPU online powerpc/iommu: Don't detach device without IOMMU group powerpc/eeh: Hotplug improvement powerpc/eeh: Call opal_pci_reinit() on powernv for restoring config space powerpc/eeh: Add restore_config operation ...
Diffstat (limited to 'arch/powerpc/platforms/powernv/opal.c')
-rw-r--r--arch/powerpc/platforms/powernv/opal.c267
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[];
38static unsigned int *opal_irqs; 41static unsigned int *opal_irqs;
39static unsigned int opal_irq_count; 42static unsigned int opal_irq_count;
40static ATOMIC_NOTIFIER_HEAD(opal_notifier_head); 43static ATOMIC_NOTIFIER_HEAD(opal_notifier_head);
44static struct atomic_notifier_head opal_msg_notifier_head[OPAL_MSG_TYPE_MAX];
41static DEFINE_SPINLOCK(opal_notifier_lock); 45static DEFINE_SPINLOCK(opal_notifier_lock);
42static uint64_t last_notified_mask = 0x0ul; 46static uint64_t last_notified_mask = 0x0ul;
43static atomic_t opal_notifier_hold = ATOMIC_INIT(0); 47static 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 */
176int 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
193static 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
200static 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
230static 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
238static struct notifier_block opal_message_nb = {
239 .notifier_call = opal_message_notify,
240 .next = NULL,
241 .priority = 0,
242};
243
244static 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}
259early_initcall(opal_message_init);
260
172int opal_get_chars(uint32_t vtermno, char *buf, int count) 261int 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
346static 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
257int opal_machine_check(struct pt_regs *regs) 384int 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
372static irqreturn_t opal_interrupt(int irq, void *data) 404static irqreturn_t opal_interrupt(int irq, void *data)
@@ -451,10 +483,25 @@ subsys_initcall(opal_init);
451void opal_shutdown(void) 483void 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}