提交 d368514c 编写于 作者: A Anton Blanchard 提交者: Benjamin Herrenschmidt

powerpc: Fix corruption when grabbing FWNMI data

The FWNMI code uses a global buffer without any locks to read the RTAS error
information. If two CPUs take a machine check at once then we will corrupt
this buffer.

Since most FWNMI rtas messages are not of the extended type, we can create a
64bit percpu buffer and use it where possible. If we do receive an extended
RTAS log then we fall back to the old behaviour of using the global buffer.
Signed-off-by: NAnton Blanchard <anton@samba.org>
Signed-off-by: NBenjamin Herrenschmidt <benh@kernel.crashing.org>
上级 d47d1d8a
...@@ -54,7 +54,8 @@ ...@@ -54,7 +54,8 @@
static unsigned char ras_log_buf[RTAS_ERROR_LOG_MAX]; static unsigned char ras_log_buf[RTAS_ERROR_LOG_MAX];
static DEFINE_SPINLOCK(ras_log_buf_lock); static DEFINE_SPINLOCK(ras_log_buf_lock);
static char mce_data_buf[RTAS_ERROR_LOG_MAX]; static char global_mce_data_buf[RTAS_ERROR_LOG_MAX];
static DEFINE_PER_CPU(__u64, mce_data_buf);
static int ras_get_sensor_state_token; static int ras_get_sensor_state_token;
static int ras_check_exception_token; static int ras_check_exception_token;
...@@ -196,12 +197,24 @@ static irqreturn_t ras_error_interrupt(int irq, void *dev_id) ...@@ -196,12 +197,24 @@ static irqreturn_t ras_error_interrupt(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/* Get the error information for errors coming through the /*
* Some versions of FWNMI place the buffer inside the 4kB page starting at
* 0x7000. Other versions place it inside the rtas buffer. We check both.
*/
#define VALID_FWNMI_BUFFER(A) \
((((A) >= 0x7000) && ((A) < 0x7ff0)) || \
(((A) >= rtas.base) && ((A) < (rtas.base + rtas.size - 16))))
/*
* Get the error information for errors coming through the
* FWNMI vectors. The pt_regs' r3 will be updated to reflect * FWNMI vectors. The pt_regs' r3 will be updated to reflect
* the actual r3 if possible, and a ptr to the error log entry * the actual r3 if possible, and a ptr to the error log entry
* will be returned if found. * will be returned if found.
* *
* The mce_data_buf does not have any locks or protection around it, * If the RTAS error is not of the extended type, then we put it in a per
* cpu 64bit buffer. If it is the extended type we use global_mce_data_buf.
*
* The global_mce_data_buf does not have any locks or protection around it,
* if a second machine check comes in, or a system reset is done * if a second machine check comes in, or a system reset is done
* before we have logged the error, then we will get corruption in the * before we have logged the error, then we will get corruption in the
* error log. This is preferable over holding off on calling * error log. This is preferable over holding off on calling
...@@ -210,20 +223,31 @@ static irqreturn_t ras_error_interrupt(int irq, void *dev_id) ...@@ -210,20 +223,31 @@ static irqreturn_t ras_error_interrupt(int irq, void *dev_id)
*/ */
static struct rtas_error_log *fwnmi_get_errinfo(struct pt_regs *regs) static struct rtas_error_log *fwnmi_get_errinfo(struct pt_regs *regs)
{ {
unsigned long errdata = regs->gpr[3];
struct rtas_error_log *errhdr = NULL;
unsigned long *savep; unsigned long *savep;
struct rtas_error_log *h, *errhdr = NULL;
if ((errdata >= 0x7000 && errdata < 0x7fff0) || if (!VALID_FWNMI_BUFFER(regs->gpr[3])) {
(errdata >= rtas.base && errdata < rtas.base + rtas.size - 16)) { printk(KERN_ERR "FWNMI: corrupt r3\n");
savep = __va(errdata); return NULL;
regs->gpr[3] = savep[0]; /* restore original r3 */ }
memset(mce_data_buf, 0, RTAS_ERROR_LOG_MAX);
memcpy(mce_data_buf, (char *)(savep + 1), RTAS_ERROR_LOG_MAX); savep = __va(regs->gpr[3]);
errhdr = (struct rtas_error_log *)mce_data_buf; regs->gpr[3] = savep[0]; /* restore original r3 */
/* If it isn't an extended log we can use the per cpu 64bit buffer */
h = (struct rtas_error_log *)&savep[1];
if (!h->extended) {
memcpy(&__get_cpu_var(mce_data_buf), h, sizeof(__u64));
errhdr = (struct rtas_error_log *)&__get_cpu_var(mce_data_buf);
} else { } else {
printk("FWNMI: corrupt r3\n"); int len;
len = max_t(int, 8+h->extended_log_length, RTAS_ERROR_LOG_MAX);
memset(global_mce_data_buf, 0, RTAS_ERROR_LOG_MAX);
memcpy(global_mce_data_buf, h, len);
errhdr = (struct rtas_error_log *)global_mce_data_buf;
} }
return errhdr; return errhdr;
} }
...@@ -235,7 +259,7 @@ static void fwnmi_release_errinfo(void) ...@@ -235,7 +259,7 @@ static void fwnmi_release_errinfo(void)
{ {
int ret = rtas_call(rtas_token("ibm,nmi-interlock"), 0, 1, NULL); int ret = rtas_call(rtas_token("ibm,nmi-interlock"), 0, 1, NULL);
if (ret != 0) if (ret != 0)
printk("FWNMI: nmi-interlock failed: %d\n", ret); printk(KERN_ERR "FWNMI: nmi-interlock failed: %d\n", ret);
} }
int pSeries_system_reset_exception(struct pt_regs *regs) int pSeries_system_reset_exception(struct pt_regs *regs)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册