diff options
-rw-r--r-- | arch/powerpc/include/asm/opal.h | 2 | ||||
-rw-r--r-- | arch/powerpc/include/asm/paca.h | 8 | ||||
-rw-r--r-- | arch/powerpc/kernel/asm-offsets.c | 10 | ||||
-rw-r--r-- | arch/powerpc/kernel/exceptions-64s.S | 27 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/opal.c | 130 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/setup.c | 1 |
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); | |||
436 | extern unsigned long opal_get_boot_time(void); | 436 | extern unsigned long opal_get_boot_time(void); |
437 | extern void opal_nvram_init(void); | 437 | extern void opal_nvram_init(void); |
438 | 438 | ||
439 | extern 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 | ||
45 | struct task_struct; | 45 | struct task_struct; |
46 | struct 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 |
1153 | fwnmi_data_area: | 1153 | fwnmi_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 |
1194 | initial_stab: | 1197 | initial_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 | ||
28 | static struct device_node *opal_node; | 28 | static struct device_node *opal_node; |
29 | static DEFINE_SPINLOCK(opal_write_lock); | 29 | static DEFINE_SPINLOCK(opal_write_lock); |
30 | extern u64 opal_mc_secondary_handler[]; | ||
30 | 31 | ||
31 | int __init early_init_dt_scan_opal(unsigned long node, | 32 | int __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 | ||
154 | int 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 | |||
139 | static irqreturn_t opal_interrupt(int irq, void *data) | 269 | static 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 |