aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r--arch/powerpc/platforms/powernv/opal.c100
-rw-r--r--arch/powerpc/platforms/powernv/setup.c1
2 files changed, 98 insertions, 3 deletions
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index 65499adaecff..d5f11d689d6c 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -21,6 +21,7 @@
21#include <linux/sched.h> 21#include <linux/sched.h>
22#include <linux/kobject.h> 22#include <linux/kobject.h>
23#include <linux/delay.h> 23#include <linux/delay.h>
24#include <linux/memblock.h>
24#include <asm/opal.h> 25#include <asm/opal.h>
25#include <asm/firmware.h> 26#include <asm/firmware.h>
26#include <asm/mce.h> 27#include <asm/mce.h>
@@ -33,8 +34,18 @@ struct kobject *opal_kobj;
33struct opal { 34struct opal {
34 u64 base; 35 u64 base;
35 u64 entry; 36 u64 entry;
37 u64 size;
36} opal; 38} opal;
37 39
40struct mcheck_recoverable_range {
41 u64 start_addr;
42 u64 end_addr;
43 u64 recover_addr;
44};
45
46static struct mcheck_recoverable_range *mc_recoverable_range;
47static int mc_recoverable_range_len;
48
38static struct device_node *opal_node; 49static struct device_node *opal_node;
39static DEFINE_SPINLOCK(opal_write_lock); 50static DEFINE_SPINLOCK(opal_write_lock);
40extern u64 opal_mc_secondary_handler[]; 51extern u64 opal_mc_secondary_handler[];
@@ -49,25 +60,29 @@ static atomic_t opal_notifier_hold = ATOMIC_INIT(0);
49int __init early_init_dt_scan_opal(unsigned long node, 60int __init early_init_dt_scan_opal(unsigned long node,
50 const char *uname, int depth, void *data) 61 const char *uname, int depth, void *data)
51{ 62{
52 const void *basep, *entryp; 63 const void *basep, *entryp, *sizep;
53 unsigned long basesz, entrysz; 64 unsigned long basesz, entrysz, runtimesz;
54 65
55 if (depth != 1 || strcmp(uname, "ibm,opal") != 0) 66 if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
56 return 0; 67 return 0;
57 68
58 basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz); 69 basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz);
59 entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz); 70 entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz);
71 sizep = of_get_flat_dt_prop(node, "opal-runtime-size", &runtimesz);
60 72
61 if (!basep || !entryp) 73 if (!basep || !entryp || !sizep)
62 return 1; 74 return 1;
63 75
64 opal.base = of_read_number(basep, basesz/4); 76 opal.base = of_read_number(basep, basesz/4);
65 opal.entry = of_read_number(entryp, entrysz/4); 77 opal.entry = of_read_number(entryp, entrysz/4);
78 opal.size = of_read_number(sizep, runtimesz/4);
66 79
67 pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n", 80 pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n",
68 opal.base, basep, basesz); 81 opal.base, basep, basesz);
69 pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n", 82 pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n",
70 opal.entry, entryp, entrysz); 83 opal.entry, entryp, entrysz);
84 pr_debug("OPAL Entry = 0x%llx (sizep=%p runtimesz=%ld)\n",
85 opal.size, sizep, runtimesz);
71 86
72 powerpc_firmware_features |= FW_FEATURE_OPAL; 87 powerpc_firmware_features |= FW_FEATURE_OPAL;
73 if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) { 88 if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) {
@@ -84,6 +99,53 @@ int __init early_init_dt_scan_opal(unsigned long node,
84 return 1; 99 return 1;
85} 100}
86 101
102int __init early_init_dt_scan_recoverable_ranges(unsigned long node,
103 const char *uname, int depth, void *data)
104{
105 unsigned long i, size;
106 const __be32 *prop;
107
108 if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
109 return 0;
110
111 prop = of_get_flat_dt_prop(node, "mcheck-recoverable-ranges", &size);
112
113 if (!prop)
114 return 1;
115
116 pr_debug("Found machine check recoverable ranges.\n");
117
118 /*
119 * Allocate a buffer to hold the MC recoverable ranges. We would be
120 * accessing them in real mode, hence it needs to be within
121 * RMO region.
122 */
123 mc_recoverable_range =__va(memblock_alloc_base(size, __alignof__(u64),
124 ppc64_rma_size));
125 memset(mc_recoverable_range, 0, size);
126
127 /*
128 * Each recoverable address entry is an (start address,len,
129 * recover address) pair, * 2 cells each, totalling 4 cells per entry.
130 */
131 for (i = 0; i < size / (sizeof(*prop) * 5); i++) {
132 mc_recoverable_range[i].start_addr =
133 of_read_number(prop + (i * 5) + 0, 2);
134 mc_recoverable_range[i].end_addr =
135 mc_recoverable_range[i].start_addr +
136 of_read_number(prop + (i * 5) + 2, 1);
137 mc_recoverable_range[i].recover_addr =
138 of_read_number(prop + (i * 5) + 3, 2);
139
140 pr_debug("Machine check recoverable range: %llx..%llx: %llx\n",
141 mc_recoverable_range[i].start_addr,
142 mc_recoverable_range[i].end_addr,
143 mc_recoverable_range[i].recover_addr);
144 }
145 mc_recoverable_range_len = i;
146 return 1;
147}
148
87static int __init opal_register_exception_handlers(void) 149static int __init opal_register_exception_handlers(void)
88{ 150{
89#ifdef __BIG_ENDIAN__ 151#ifdef __BIG_ENDIAN__
@@ -401,6 +463,38 @@ int opal_machine_check(struct pt_regs *regs)
401 return 0; 463 return 0;
402} 464}
403 465
466static uint64_t find_recovery_address(uint64_t nip)
467{
468 int i;
469
470 for (i = 0; i < mc_recoverable_range_len; i++)
471 if ((nip >= mc_recoverable_range[i].start_addr) &&
472 (nip < mc_recoverable_range[i].end_addr))
473 return mc_recoverable_range[i].recover_addr;
474 return 0;
475}
476
477bool opal_mce_check_early_recovery(struct pt_regs *regs)
478{
479 uint64_t recover_addr = 0;
480
481 if (!opal.base || !opal.size)
482 goto out;
483
484 if ((regs->nip >= opal.base) &&
485 (regs->nip <= (opal.base + opal.size)))
486 recover_addr = find_recovery_address(regs->nip);
487
488 /*
489 * Setup regs->nip to rfi into fixup address.
490 */
491 if (recover_addr)
492 regs->nip = recover_addr;
493
494out:
495 return !!recover_addr;
496}
497
404static irqreturn_t opal_interrupt(int irq, void *data) 498static irqreturn_t opal_interrupt(int irq, void *data)
405{ 499{
406 __be64 events; 500 __be64 events;
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 110f4fbd319f..2d8084565676 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -188,6 +188,7 @@ static void __init pnv_setup_machdep_opal(void)
188 ppc_md.power_off = pnv_power_off; 188 ppc_md.power_off = pnv_power_off;
189 ppc_md.halt = pnv_halt; 189 ppc_md.halt = pnv_halt;
190 ppc_md.machine_check_exception = opal_machine_check; 190 ppc_md.machine_check_exception = opal_machine_check;
191 ppc_md.mce_check_early_recovery = opal_mce_check_early_recovery;
191} 192}
192 193
193#ifdef CONFIG_PPC_POWERNV_RTAS 194#ifdef CONFIG_PPC_POWERNV_RTAS