blob: 9a3e27b863ce518451f7eb0ecd33b4f15760bb57 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * Copyright (C) 2001 Dave Engebretsen IBM Corporation
Michael Ellermand9953102005-10-24 15:07:30 +10003 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
Michael Ellermand9953102005-10-24 15:07:30 +10008 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
Michael Ellermand9953102005-10-24 15:07:30 +100013 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070014 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/sched.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <linux/interrupt.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <linux/irq.h>
Anton Blanchard90128992012-03-21 15:59:04 +000022#include <linux/of.h>
Anton Blanchard55fc0c52012-03-21 15:49:59 +000023#include <linux/fs.h>
24#include <linux/reboot.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include <asm/machdep.h>
27#include <asm/rtas.h>
Michael Ellerman8c4f1f22005-12-04 18:39:33 +110028#include <asm/firmware.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
Michael Ellerman577830b2007-02-08 18:33:51 +110030#include "pseries.h"
Arnd Bergmannc902be72006-01-04 19:55:53 +000031
Linus Torvalds1da177e2005-04-16 15:20:36 -070032static unsigned char ras_log_buf[RTAS_ERROR_LOG_MAX];
33static DEFINE_SPINLOCK(ras_log_buf_lock);
34
Anton Blanchardd3685142011-01-11 19:50:51 +000035static char global_mce_data_buf[RTAS_ERROR_LOG_MAX];
36static DEFINE_PER_CPU(__u64, mce_data_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
Linus Torvalds1da177e2005-04-16 15:20:36 -070038static int ras_check_exception_token;
39
40#define EPOW_SENSOR_TOKEN 9
41#define EPOW_SENSOR_INDEX 0
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
Vipin K Parasharb4af2792015-12-01 16:43:42 +053043/* EPOW events counter variable */
44static int num_epow_events;
45
David Howells7d12e782006-10-05 14:55:46 +010046static irqreturn_t ras_epow_interrupt(int irq, void *dev_id);
47static irqreturn_t ras_error_interrupt(int irq, void *dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
Benjamin Herrenschmidt0ebfff12006-07-03 21:36:01 +100049
Linus Torvalds1da177e2005-04-16 15:20:36 -070050/*
51 * Initialize handlers for the set of interrupts caused by hardware errors
52 * and power system events.
53 */
54static int __init init_ras_IRQ(void)
55{
56 struct device_node *np;
57
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 ras_check_exception_token = rtas_token("check-exception");
59
60 /* Internal Errors */
61 np = of_find_node_by_path("/event-sources/internal-errors");
62 if (np != NULL) {
Mark Nelson32c96f72010-05-18 22:51:00 +000063 request_event_sources_irqs(np, ras_error_interrupt,
64 "RAS_ERROR");
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 of_node_put(np);
66 }
67
68 /* EPOW Events */
69 np = of_find_node_by_path("/event-sources/epow-events");
70 if (np != NULL) {
Mark Nelson32c96f72010-05-18 22:51:00 +000071 request_event_sources_irqs(np, ras_epow_interrupt, "RAS_EPOW");
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 of_node_put(np);
73 }
74
Anton Blanchard69ed3322006-03-28 14:08:39 +110075 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070076}
Michael Ellerman8e83e902014-07-16 12:02:43 +100077machine_subsys_initcall(pseries, init_ras_IRQ);
Linus Torvalds1da177e2005-04-16 15:20:36 -070078
Anton Blanchard55fc0c52012-03-21 15:49:59 +000079#define EPOW_SHUTDOWN_NORMAL 1
80#define EPOW_SHUTDOWN_ON_UPS 2
81#define EPOW_SHUTDOWN_LOSS_OF_CRITICAL_FUNCTIONS 3
82#define EPOW_SHUTDOWN_AMBIENT_TEMPERATURE_TOO_HIGH 4
83
84static void handle_system_shutdown(char event_modifier)
85{
86 switch (event_modifier) {
87 case EPOW_SHUTDOWN_NORMAL:
Vipin K Parasharb4af2792015-12-01 16:43:42 +053088 pr_emerg("Power off requested\n");
liguang1b7e0cb2013-05-30 15:20:33 +080089 orderly_poweroff(true);
Anton Blanchard55fc0c52012-03-21 15:49:59 +000090 break;
91
92 case EPOW_SHUTDOWN_ON_UPS:
Vipin K Parasharb4af2792015-12-01 16:43:42 +053093 pr_emerg("Loss of system power detected. System is running on"
94 " UPS/battery. Check RTAS error log for details\n");
Anshuman Khandual79872e32014-10-21 13:41:29 +053095 orderly_poweroff(true);
Anton Blanchard55fc0c52012-03-21 15:49:59 +000096 break;
97
98 case EPOW_SHUTDOWN_LOSS_OF_CRITICAL_FUNCTIONS:
Vipin K Parasharb4af2792015-12-01 16:43:42 +053099 pr_emerg("Loss of system critical functions detected. Check"
100 " RTAS error log for details\n");
liguang1b7e0cb2013-05-30 15:20:33 +0800101 orderly_poweroff(true);
Anton Blanchard55fc0c52012-03-21 15:49:59 +0000102 break;
103
104 case EPOW_SHUTDOWN_AMBIENT_TEMPERATURE_TOO_HIGH:
Vipin K Parasharb4af2792015-12-01 16:43:42 +0530105 pr_emerg("High ambient temperature detected. Check RTAS"
106 " error log for details\n");
liguang1b7e0cb2013-05-30 15:20:33 +0800107 orderly_poweroff(true);
Anton Blanchard55fc0c52012-03-21 15:49:59 +0000108 break;
109
110 default:
Vipin K Parasharb4af2792015-12-01 16:43:42 +0530111 pr_err("Unknown power/cooling shutdown event (modifier = %d)\n",
Anton Blanchard55fc0c52012-03-21 15:49:59 +0000112 event_modifier);
113 }
114}
115
116struct epow_errorlog {
117 unsigned char sensor_value;
118 unsigned char event_modifier;
119 unsigned char extended_modifier;
120 unsigned char reserved;
121 unsigned char platform_reason;
122};
123
124#define EPOW_RESET 0
125#define EPOW_WARN_COOLING 1
126#define EPOW_WARN_POWER 2
127#define EPOW_SYSTEM_SHUTDOWN 3
128#define EPOW_SYSTEM_HALT 4
129#define EPOW_MAIN_ENCLOSURE 5
130#define EPOW_POWER_OFF 7
131
Anton Blancharde51df2c2014-08-20 08:55:18 +1000132static void rtas_parse_epow_errlog(struct rtas_error_log *log)
Anton Blanchard55fc0c52012-03-21 15:49:59 +0000133{
134 struct pseries_errorlog *pseries_log;
135 struct epow_errorlog *epow_log;
136 char action_code;
137 char modifier;
138
139 pseries_log = get_pseries_errorlog(log, PSERIES_ELOG_SECT_ID_EPOW);
140 if (pseries_log == NULL)
141 return;
142
143 epow_log = (struct epow_errorlog *)pseries_log->data;
144 action_code = epow_log->sensor_value & 0xF; /* bottom 4 bits */
145 modifier = epow_log->event_modifier & 0xF; /* bottom 4 bits */
146
147 switch (action_code) {
148 case EPOW_RESET:
Vipin K Parasharb4af2792015-12-01 16:43:42 +0530149 if (num_epow_events) {
150 pr_info("Non critical power/cooling issue cleared\n");
151 num_epow_events--;
152 }
Anton Blanchard55fc0c52012-03-21 15:49:59 +0000153 break;
154
155 case EPOW_WARN_COOLING:
Vipin K Parasharb4af2792015-12-01 16:43:42 +0530156 pr_info("Non-critical cooling issue detected. Check RTAS error"
157 " log for details\n");
Anton Blanchard55fc0c52012-03-21 15:49:59 +0000158 break;
159
160 case EPOW_WARN_POWER:
Vipin K Parasharb4af2792015-12-01 16:43:42 +0530161 pr_info("Non-critical power issue detected. Check RTAS error"
162 " log for details\n");
Anton Blanchard55fc0c52012-03-21 15:49:59 +0000163 break;
164
165 case EPOW_SYSTEM_SHUTDOWN:
166 handle_system_shutdown(epow_log->event_modifier);
167 break;
168
169 case EPOW_SYSTEM_HALT:
Vipin K Parasharb4af2792015-12-01 16:43:42 +0530170 pr_emerg("Critical power/cooling issue detected. Check RTAS"
171 " error log for details. Powering off.\n");
liguang1b7e0cb2013-05-30 15:20:33 +0800172 orderly_poweroff(true);
Anton Blanchard55fc0c52012-03-21 15:49:59 +0000173 break;
174
175 case EPOW_MAIN_ENCLOSURE:
176 case EPOW_POWER_OFF:
Vipin K Parasharb4af2792015-12-01 16:43:42 +0530177 pr_emerg("System about to lose power. Check RTAS error log "
178 " for details. Powering off immediately.\n");
Anton Blanchard55fc0c52012-03-21 15:49:59 +0000179 emergency_sync();
180 kernel_power_off();
181 break;
182
183 default:
Vipin K Parasharb4af2792015-12-01 16:43:42 +0530184 pr_err("Unknown power/cooling event (action code = %d)\n",
Anton Blanchard55fc0c52012-03-21 15:49:59 +0000185 action_code);
186 }
Vipin K Parasharb4af2792015-12-01 16:43:42 +0530187
188 /* Increment epow events counter variable */
189 if (action_code != EPOW_RESET)
190 num_epow_events++;
Anton Blanchard55fc0c52012-03-21 15:49:59 +0000191}
192
193/* Handle environmental and power warning (EPOW) interrupts. */
David Howells7d12e782006-10-05 14:55:46 +0100194static irqreturn_t ras_epow_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195{
Anton Blanchard55fc0c52012-03-21 15:49:59 +0000196 int status;
197 int state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 int critical;
199
Thomas Huth1c2cb592015-07-17 12:46:58 +0200200 status = rtas_get_sensor_fast(EPOW_SENSOR_TOKEN, EPOW_SENSOR_INDEX,
201 &state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202
203 if (state > 3)
Anton Blanchard55fc0c52012-03-21 15:49:59 +0000204 critical = 1; /* Time Critical */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 else
206 critical = 0;
207
208 spin_lock(&ras_log_buf_lock);
209
210 status = rtas_call(ras_check_exception_token, 6, 1, NULL,
Mark Nelsonb08e2812010-05-26 21:40:39 +0000211 RTAS_VECTOR_EXTERNAL_INTERRUPT,
Grant Likely476eb492011-05-04 15:02:15 +1000212 virq_to_hw(irq),
Anton Blanchard6f437472012-03-21 15:56:49 +0000213 RTAS_EPOW_WARNING,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 critical, __pa(&ras_log_buf),
215 rtas_get_error_log_max());
216
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 log_error(ras_log_buf, ERR_TYPE_RTAS_LOG, 0);
218
Anton Blanchard55fc0c52012-03-21 15:49:59 +0000219 rtas_parse_epow_errlog((struct rtas_error_log *)ras_log_buf);
220
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 spin_unlock(&ras_log_buf_lock);
222 return IRQ_HANDLED;
223}
224
225/*
226 * Handle hardware error interrupts.
227 *
228 * RTAS check-exception is called to collect data on the exception. If
229 * the error is deemed recoverable, we log a warning and return.
230 * For nonrecoverable errors, an error is logged and we stop all processing
231 * as quickly as possible in order to prevent propagation of the failure.
232 */
David Howells7d12e782006-10-05 14:55:46 +0100233static irqreturn_t ras_error_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234{
235 struct rtas_error_log *rtas_elog;
Anton Blanchardcc8b5262012-03-21 15:58:03 +0000236 int status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 int fatal;
238
239 spin_lock(&ras_log_buf_lock);
240
241 status = rtas_call(ras_check_exception_token, 6, 1, NULL,
Mark Nelsonb08e2812010-05-26 21:40:39 +0000242 RTAS_VECTOR_EXTERNAL_INTERRUPT,
Grant Likely476eb492011-05-04 15:02:15 +1000243 virq_to_hw(irq),
Anton Blanchardcc8b5262012-03-21 15:58:03 +0000244 RTAS_INTERNAL_ERROR, 1 /* Time Critical */,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 __pa(&ras_log_buf),
246 rtas_get_error_log_max());
247
248 rtas_elog = (struct rtas_error_log *)ras_log_buf;
249
Greg Kurza08a53ea2014-04-04 09:35:13 +0200250 if (status == 0 &&
251 rtas_error_severity(rtas_elog) >= RTAS_SEVERITY_ERROR_SYNC)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 fatal = 1;
253 else
254 fatal = 0;
255
256 /* format and print the extended information */
257 log_error(ras_log_buf, ERR_TYPE_RTAS_LOG, fatal);
258
259 if (fatal) {
Vipin K Parasharb4af2792015-12-01 16:43:42 +0530260 pr_emerg("Fatal hardware error detected. Check RTAS error"
261 " log for details. Powering off immediately\n");
Anton Blanchardcc8b5262012-03-21 15:58:03 +0000262 emergency_sync();
263 kernel_power_off();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 } else {
Vipin K Parasharb4af2792015-12-01 16:43:42 +0530265 pr_err("Recoverable hardware error detected\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 }
267
268 spin_unlock(&ras_log_buf_lock);
269 return IRQ_HANDLED;
270}
271
Anton Blanchardd3685142011-01-11 19:50:51 +0000272/*
273 * Some versions of FWNMI place the buffer inside the 4kB page starting at
274 * 0x7000. Other versions place it inside the rtas buffer. We check both.
275 */
276#define VALID_FWNMI_BUFFER(A) \
277 ((((A) >= 0x7000) && ((A) < 0x7ff0)) || \
278 (((A) >= rtas.base) && ((A) < (rtas.base + rtas.size - 16))))
279
280/*
281 * Get the error information for errors coming through the
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 * FWNMI vectors. The pt_regs' r3 will be updated to reflect
283 * the actual r3 if possible, and a ptr to the error log entry
284 * will be returned if found.
285 *
Anton Blanchardd3685142011-01-11 19:50:51 +0000286 * If the RTAS error is not of the extended type, then we put it in a per
287 * cpu 64bit buffer. If it is the extended type we use global_mce_data_buf.
288 *
289 * The global_mce_data_buf does not have any locks or protection around it,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 * if a second machine check comes in, or a system reset is done
291 * before we have logged the error, then we will get corruption in the
292 * error log. This is preferable over holding off on calling
293 * ibm,nmi-interlock which would result in us checkstopping if a
294 * second machine check did come in.
295 */
296static struct rtas_error_log *fwnmi_get_errinfo(struct pt_regs *regs)
297{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 unsigned long *savep;
Anton Blanchardd3685142011-01-11 19:50:51 +0000299 struct rtas_error_log *h, *errhdr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300
Mahesh Salgaonkaree1dd1e2013-07-10 18:32:56 +0530301 /* Mask top two bits */
302 regs->gpr[3] &= ~(0x3UL << 62);
303
Anton Blanchardd3685142011-01-11 19:50:51 +0000304 if (!VALID_FWNMI_BUFFER(regs->gpr[3])) {
Anton Blanchardf0e939a2011-05-10 13:34:03 +0000305 printk(KERN_ERR "FWNMI: corrupt r3 0x%016lx\n", regs->gpr[3]);
Anton Blanchardd3685142011-01-11 19:50:51 +0000306 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 }
Anton Blanchardd3685142011-01-11 19:50:51 +0000308
309 savep = __va(regs->gpr[3]);
310 regs->gpr[3] = savep[0]; /* restore original r3 */
311
312 /* If it isn't an extended log we can use the per cpu 64bit buffer */
313 h = (struct rtas_error_log *)&savep[1];
Greg Kurza08a53ea2014-04-04 09:35:13 +0200314 if (!rtas_error_extended(h)) {
Christoph Lameter69111ba2014-10-21 15:23:25 -0500315 memcpy(this_cpu_ptr(&mce_data_buf), h, sizeof(__u64));
316 errhdr = (struct rtas_error_log *)this_cpu_ptr(&mce_data_buf);
Anton Blanchardd3685142011-01-11 19:50:51 +0000317 } else {
Greg Kurza08a53ea2014-04-04 09:35:13 +0200318 int len, error_log_length;
Anton Blanchardd3685142011-01-11 19:50:51 +0000319
Greg Kurza08a53ea2014-04-04 09:35:13 +0200320 error_log_length = 8 + rtas_error_extended_log_length(h);
321 len = max_t(int, error_log_length, RTAS_ERROR_LOG_MAX);
Anton Blanchardd3685142011-01-11 19:50:51 +0000322 memset(global_mce_data_buf, 0, RTAS_ERROR_LOG_MAX);
323 memcpy(global_mce_data_buf, h, len);
324 errhdr = (struct rtas_error_log *)global_mce_data_buf;
325 }
326
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 return errhdr;
328}
329
330/* Call this when done with the data returned by FWNMI_get_errinfo.
331 * It will release the saved data area for other CPUs in the
332 * partition to receive FWNMI errors.
333 */
334static void fwnmi_release_errinfo(void)
335{
336 int ret = rtas_call(rtas_token("ibm,nmi-interlock"), 0, 1, NULL);
337 if (ret != 0)
Anton Blanchardd3685142011-01-11 19:50:51 +0000338 printk(KERN_ERR "FWNMI: nmi-interlock failed: %d\n", ret);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339}
340
Arnd Bergmannc902be72006-01-04 19:55:53 +0000341int pSeries_system_reset_exception(struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342{
343 if (fwnmi_active) {
344 struct rtas_error_log *errhdr = fwnmi_get_errinfo(regs);
345 if (errhdr) {
346 /* XXX Should look at FWNMI information */
347 }
348 fwnmi_release_errinfo();
349 }
Arnd Bergmannc902be72006-01-04 19:55:53 +0000350 return 0; /* need to perform reset */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351}
352
353/*
354 * See if we can recover from a machine check exception.
355 * This is only called on power4 (or above) and only via
356 * the Firmware Non-Maskable Interrupts (fwnmi) handler
357 * which provides the error analysis for us.
358 *
359 * Return 1 if corrected (or delivered a signal).
360 * Return 0 if there is nothing we can do.
361 */
Anton Blanchardd47d1d82011-01-11 19:49:19 +0000362static int recover_mce(struct pt_regs *regs, struct rtas_error_log *err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363{
Anton Blanchardd47d1d82011-01-11 19:49:19 +0000364 int recovered = 0;
Greg Kurza08a53ea2014-04-04 09:35:13 +0200365 int disposition = rtas_error_disposition(err);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
Anton Blanchardd47d1d82011-01-11 19:49:19 +0000367 if (!(regs->msr & MSR_RI)) {
368 /* If MSR_RI isn't set, we cannot recover */
369 recovered = 0;
370
Greg Kurza08a53ea2014-04-04 09:35:13 +0200371 } else if (disposition == RTAS_DISP_FULLY_RECOVERED) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 /* Platform corrected itself */
Anton Blanchardd47d1d82011-01-11 19:49:19 +0000373 recovered = 1;
374
Greg Kurza08a53ea2014-04-04 09:35:13 +0200375 } else if (disposition == RTAS_DISP_LIMITED_RECOVERY) {
Anton Blanchardd47d1d82011-01-11 19:49:19 +0000376 /* Platform corrected itself but could be degraded */
377 printk(KERN_ERR "MCE: limited recovery, system may "
378 "be degraded\n");
379 recovered = 1;
380
381 } else if (user_mode(regs) && !is_global_init(current) &&
Greg Kurza08a53ea2014-04-04 09:35:13 +0200382 rtas_error_severity(err) == RTAS_SEVERITY_ERROR_SYNC) {
Anton Blanchardd47d1d82011-01-11 19:49:19 +0000383
384 /*
385 * If we received a synchronous error when in userspace
386 * kill the task. Firmware may report details of the fail
387 * asynchronously, so we can't rely on the target and type
388 * fields being valid here.
389 */
390 printk(KERN_ERR "MCE: uncorrectable error, killing task "
391 "%s:%d\n", current->comm, current->pid);
392
393 _exception(SIGBUS, regs, BUS_MCEERR_AR, regs->nip);
394 recovered = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 }
396
Anton Blanchard3f9793e2011-01-11 19:46:29 +0000397 log_error((char *)err, ERR_TYPE_RTAS_LOG, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398
Anton Blanchardd47d1d82011-01-11 19:49:19 +0000399 return recovered;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400}
401
402/*
403 * Handle a machine check.
404 *
405 * Note that on Power 4 and beyond Firmware Non-Maskable Interrupts (fwnmi)
406 * should be present. If so the handler which called us tells us if the
407 * error was recovered (never true if RI=0).
408 *
409 * On hardware prior to Power 4 these exceptions were asynchronous which
410 * means we can't tell exactly where it occurred and so we can't recover.
411 */
412int pSeries_machine_check_exception(struct pt_regs *regs)
413{
414 struct rtas_error_log *errp;
415
416 if (fwnmi_active) {
417 errp = fwnmi_get_errinfo(regs);
418 fwnmi_release_errinfo();
419 if (errp && recover_mce(regs, errp))
420 return 1;
421 }
422
423 return 0;
424}