Commit 87d6f514 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Linus Torvalds

[PATCH] ppc64: Fix RTAS races on pSeries

The low level kernel interface to RTAS (the firmware runtime services)
was plagued with races that could cause from bogus results of RTAS
operations to total machine crashes in some circumstances. This patch
fix the ones I could identify, hoping I didn't miss any. I also added
a WARN_ON (well, it's asm equivalent) to enter_rtas to make sure we
never _ever_ try to call that with interrupts enabled.
parent 514fb5be
...@@ -487,7 +487,7 @@ _GLOBAL(enter_rtas) ...@@ -487,7 +487,7 @@ _GLOBAL(enter_rtas)
mflr r0 mflr r0
std r0,16(r1) std r0,16(r1)
stdu r1,-RTAS_FRAME_SIZE(r1) /* Save SP and create stack space. */ stdu r1,-RTAS_FRAME_SIZE(r1) /* Save SP and create stack space. */
/* Because RTAS is running in 32b mode, it clobbers the high order half /* Because RTAS is running in 32b mode, it clobbers the high order half
* of all registers that it saves. We therefore save those registers * of all registers that it saves. We therefore save those registers
* RTAS might touch to the stack. (r0, r3-r13 are caller saved) * RTAS might touch to the stack. (r0, r3-r13 are caller saved)
...@@ -512,12 +512,25 @@ _GLOBAL(enter_rtas) ...@@ -512,12 +512,25 @@ _GLOBAL(enter_rtas)
mfsrr1 r10 mfsrr1 r10
std r10,_SRR1(r1) std r10,_SRR1(r1)
/* There is no way it is acceptable to get here with interrupts enabled,
* check it with the asm equivalent of WARN_ON
*/
mfmsr r6
andi. r0,r6,MSR_EE
1: tdnei r0,0
.section __bug_table,"a"
.llong 1b,__LINE__ + 0x1000000, 1f, 2f
.previous
.section .rodata,"a"
1: .asciz __FILE__
2: .asciz "enter_rtas"
.previous
/* Unfortunately, the stack pointer and the MSR are also clobbered, /* Unfortunately, the stack pointer and the MSR are also clobbered,
* so they are saved in the PACA which allows us to restore * so they are saved in the PACA which allows us to restore
* our original state after RTAS returns. * our original state after RTAS returns.
*/ */
std r1,PACAR1(r13) std r1,PACAR1(r13)
mfmsr r6
std r6,PACASAVEDMSR(r13) std r6,PACASAVEDMSR(r13)
/* Setup our real return addr */ /* Setup our real return addr */
......
...@@ -68,15 +68,20 @@ char rtas_data_buf[RTAS_DATA_BUF_SIZE]__page_aligned; ...@@ -68,15 +68,20 @@ char rtas_data_buf[RTAS_DATA_BUF_SIZE]__page_aligned;
void void
call_rtas_display_status(char c) call_rtas_display_status(char c)
{ {
struct rtas_args *rtas = &(get_paca()->xRtas); struct rtas_args *args = &(get_paca()->xRtas);
unsigned long s;
spin_lock_irqsave(&rtas.lock, s);
rtas->token = 10; args->token = 10;
rtas->nargs = 1; args->nargs = 1;
rtas->nret = 1; args->nret = 1;
rtas->rets = (rtas_arg_t *)&(rtas->args[1]); args->rets = (rtas_arg_t *)&(args->args[1]);
rtas->args[0] = (int)c; args->args[0] = (int)c;
enter_rtas((void *)__pa((unsigned long)rtas)); enter_rtas((void *)__pa((unsigned long)args));
spin_unlock_irqrestore(&rtas.lock, s);
} }
int int
...@@ -91,8 +96,9 @@ rtas_token(const char *service) ...@@ -91,8 +96,9 @@ rtas_token(const char *service)
return tokp ? *tokp : RTAS_UNKNOWN_SERVICE; return tokp ? *tokp : RTAS_UNKNOWN_SERVICE;
} }
void
log_rtas_error(struct rtas_args *rtas_args) static int
__log_rtas_error(struct rtas_args *rtas_args)
{ {
struct rtas_args err_args, temp_args; struct rtas_args err_args, temp_args;
...@@ -111,13 +117,24 @@ log_rtas_error(struct rtas_args *rtas_args) ...@@ -111,13 +117,24 @@ log_rtas_error(struct rtas_args *rtas_args)
PPCDBG(PPCDBG_RTAS, "\tentering rtas with 0x%lx\n", PPCDBG(PPCDBG_RTAS, "\tentering rtas with 0x%lx\n",
(void *)__pa((unsigned long)&err_args)); (void *)__pa((unsigned long)&err_args));
enter_rtas((void *)__pa((unsigned long)&get_paca()->xRtas)); enter_rtas((void *)__pa((unsigned long)&get_paca()->xRtas));
PPCDBG(PPCDBG_RTAS, "\treturned from rtas ...\n"); PPCDBG(PPCDBG_RTAS, "\treturned from rtas ...\n");
err_args = get_paca()->xRtas; err_args = get_paca()->xRtas;
get_paca()->xRtas = temp_args; get_paca()->xRtas = temp_args;
if (err_args.rets[0] == 0) return err_args.rets[0];
}
void
log_rtas_error(struct rtas_args *rtas_args)
{
unsigned long s;
int rc;
spin_lock_irqsave(&rtas.lock, s);
rc = __log_rtas_error(rtas_args);
spin_unlock_irqrestore(&rtas.lock, s);
if (rc == 0)
log_error(rtas_err_buf, ERR_TYPE_RTAS_LOG, 0); log_error(rtas_err_buf, ERR_TYPE_RTAS_LOG, 0);
} }
...@@ -126,9 +143,10 @@ rtas_call(int token, int nargs, int nret, ...@@ -126,9 +143,10 @@ rtas_call(int token, int nargs, int nret,
unsigned long *outputs, ...) unsigned long *outputs, ...)
{ {
va_list list; va_list list;
int i; int i, logit = 0;
unsigned long s; unsigned long s;
struct rtas_args *rtas_args = &(get_paca()->xRtas); struct rtas_args *rtas_args = &(get_paca()->xRtas);
long ret;
PPCDBG(PPCDBG_RTAS, "Entering rtas_call\n"); PPCDBG(PPCDBG_RTAS, "Entering rtas_call\n");
PPCDBG(PPCDBG_RTAS, "\ttoken = 0x%x\n", token); PPCDBG(PPCDBG_RTAS, "\ttoken = 0x%x\n", token);
...@@ -138,6 +156,9 @@ rtas_call(int token, int nargs, int nret, ...@@ -138,6 +156,9 @@ rtas_call(int token, int nargs, int nret,
if (token == RTAS_UNKNOWN_SERVICE) if (token == RTAS_UNKNOWN_SERVICE)
return -1; return -1;
/* Gotta do something different here, use global lock for now... */
spin_lock_irqsave(&rtas.lock, s);
rtas_args->token = token; rtas_args->token = token;
rtas_args->nargs = nargs; rtas_args->nargs = nargs;
rtas_args->nret = nret; rtas_args->nret = nret;
...@@ -150,26 +171,16 @@ rtas_call(int token, int nargs, int nret, ...@@ -150,26 +171,16 @@ rtas_call(int token, int nargs, int nret,
va_end(list); va_end(list);
for (i = 0; i < nret; ++i) for (i = 0; i < nret; ++i)
rtas_args->rets[i] = 0; rtas_args->rets[i] = 0;
#if 0 /* Gotta do something different here, use global lock for now... */
spin_lock_irqsave(&rtas_args->lock, s);
#else
spin_lock_irqsave(&rtas.lock, s);
#endif
PPCDBG(PPCDBG_RTAS, "\tentering rtas with 0x%lx\n", PPCDBG(PPCDBG_RTAS, "\tentering rtas with 0x%lx\n",
(void *)__pa((unsigned long)rtas_args)); (void *)__pa((unsigned long)rtas_args));
enter_rtas((void *)__pa((unsigned long)rtas_args)); enter_rtas((void *)__pa((unsigned long)rtas_args));
PPCDBG(PPCDBG_RTAS, "\treturned from rtas ...\n"); PPCDBG(PPCDBG_RTAS, "\treturned from rtas ...\n");
if (rtas_args->rets[0] == -1) if (rtas_args->rets[0] == -1)
log_rtas_error(rtas_args); logit = (__log_rtas_error(rtas_args) == 0);
#if 0 /* Gotta do something different here, use global lock for now... */
spin_unlock_irqrestore(&rtas_args->lock, s);
#else
spin_unlock_irqrestore(&rtas.lock, s);
#endif
ifppcdebug(PPCDBG_RTAS) { ifppcdebug(PPCDBG_RTAS) {
for(i=0; i < nret ;i++) for(i=0; i < nret ;i++)
udbg_printf("\tnret[%d] = 0x%lx\n", i, (ulong)rtas_args->rets[i]); udbg_printf("\tnret[%d] = 0x%lx\n", i, (ulong)rtas_args->rets[i]);
...@@ -178,7 +189,15 @@ rtas_call(int token, int nargs, int nret, ...@@ -178,7 +189,15 @@ rtas_call(int token, int nargs, int nret,
if (nret > 1 && outputs != NULL) if (nret > 1 && outputs != NULL)
for (i = 0; i < nret-1; ++i) for (i = 0; i < nret-1; ++i)
outputs[i] = rtas_args->rets[i+1]; outputs[i] = rtas_args->rets[i+1];
return (ulong)((nret > 0) ? rtas_args->rets[0] : 0); ret = (ulong)((nret > 0) ? rtas_args->rets[0] : 0);
/* Gotta do something different here, use global lock for now... */
spin_unlock_irqrestore(&rtas.lock, s);
if (logit)
log_error(rtas_err_buf, ERR_TYPE_RTAS_LOG, 0);
return ret;
} }
/* Given an RTAS status code of 990n compute the hinted delay of 10^n /* Given an RTAS status code of 990n compute the hinted delay of 10^n
...@@ -464,12 +483,12 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) ...@@ -464,12 +483,12 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
enter_rtas((void *)__pa((unsigned long)&get_paca()->xRtas)); enter_rtas((void *)__pa((unsigned long)&get_paca()->xRtas));
args = get_paca()->xRtas; args = get_paca()->xRtas;
spin_unlock_irqrestore(&rtas.lock, flags);
args.rets = (rtas_arg_t *)&(args.args[nargs]); args.rets = (rtas_arg_t *)&(args.args[nargs]);
if (args.rets[0] == -1) if (args.rets[0] == -1)
log_rtas_error(&args); log_rtas_error(&args);
spin_unlock_irqrestore(&rtas.lock, flags);
/* Copy out args. */ /* Copy out args. */
if (copy_to_user(uargs->args + nargs, if (copy_to_user(uargs->args + nargs,
args.args + nargs, args.args + nargs,
...@@ -485,7 +504,9 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) ...@@ -485,7 +504,9 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
void rtas_stop_self(void) void rtas_stop_self(void)
{ {
struct rtas_args *rtas_args = &(get_paca()->xRtas); struct rtas_args *rtas_args = &(get_paca()->xRtas);
unsigned long s;
spin_lock_irqsave(&rtas.lock, s);
rtas_args->token = rtas_token("stop-self"); rtas_args->token = rtas_token("stop-self");
BUG_ON(rtas_args->token == RTAS_UNKNOWN_SERVICE); BUG_ON(rtas_args->token == RTAS_UNKNOWN_SERVICE);
rtas_args->nargs = 0; rtas_args->nargs = 0;
...@@ -495,6 +516,8 @@ void rtas_stop_self(void) ...@@ -495,6 +516,8 @@ void rtas_stop_self(void)
printk("%u %u Ready to die...\n", printk("%u %u Ready to die...\n",
smp_processor_id(), hard_smp_processor_id()); smp_processor_id(), hard_smp_processor_id());
enter_rtas((void *)__pa(rtas_args)); enter_rtas((void *)__pa(rtas_args));
spin_unlock_irqrestore(&rtas.lock, s);
panic("Alas, I survived.\n"); panic("Alas, I survived.\n");
} }
#endif /* CONFIG_HOTPLUG_CPU */ #endif /* CONFIG_HOTPLUG_CPU */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment