X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fppc64%2Fkernel%2Frtas.c;h=438d258dcea397b9d7b4f049216eec2aee2b3d06;hb=9bf4aaab3e101692164d49b7ca357651eb691cb6;hp=ff59b0057b0d1b3b7ee40f56ebc39d1ca57c4e60;hpb=db216c3d5e4c040e557a50f8f5d35d5c415e8c1c;p=linux-2.6.git diff --git a/arch/ppc64/kernel/rtas.c b/arch/ppc64/kernel/rtas.c index ff59b0057..438d258dc 100644 --- a/arch/ppc64/kernel/rtas.c +++ b/arch/ppc64/kernel/rtas.c @@ -31,28 +31,7 @@ #include #include -struct flash_block_list_header rtas_firmware_flash_list = {0, 0}; - -/* - * prom_init() is called very early on, before the kernel text - * and data have been mapped to KERNELBASE. At this point the code - * is running at whatever address it has been loaded at, so - * references to extern and static variables must be relocated - * explicitly. The procedure reloc_offset() returns the address - * we're currently running at minus the address we were linked at. - * (Note that strings count as static variables.) - * - * Because OF may have mapped I/O devices into the area starting at - * KERNELBASE, particularly on CHRP machines, we can't safely call - * OF once the kernel has been mapped to KERNELBASE. Therefore all - * OF calls should be done within prom_init(), and prom_init() - * and all routines called within it must be careful to relocate - * references as necessary. - * - * Note that the bss is cleared *after* prom_init runs, so we have - * to make sure that any static or extern variables it accesses - * are put in the data segment. - */ +struct flash_block_list_header rtas_firmware_flash_list = {0, NULL}; struct rtas_t rtas = { .lock = SPIN_LOCK_UNLOCKED @@ -60,15 +39,13 @@ struct rtas_t rtas = { char rtas_err_buf[RTAS_ERROR_LOG_MAX]; -extern unsigned long reloc_offset(void); - spinlock_t rtas_data_buf_lock = SPIN_LOCK_UNLOCKED; char rtas_data_buf[RTAS_DATA_BUF_SIZE]__page_aligned; void call_rtas_display_status(char c) { - struct rtas_args *args = &(get_paca()->xRtas); + struct rtas_args *args = &rtas.args; unsigned long s; spin_lock_irqsave(&rtas.lock, s); @@ -97,10 +74,16 @@ rtas_token(const char *service) } +/** Return a copy of the detailed error text associated with the + * most recent failed call to rtas. Because the error text + * might go stale if there are any other intervening rtas calls, + * this routine must be called atomically with whatever produced + * the error (i.e. with rtas.lock still held from the previous call). + */ static int -__log_rtas_error(struct rtas_args *rtas_args) +__fetch_rtas_last_error(void) { - struct rtas_args err_args, temp_args; + struct rtas_args err_args, save_args; err_args.token = rtas_token("rtas-last-error"); err_args.nargs = 2; @@ -111,42 +94,28 @@ __log_rtas_error(struct rtas_args *rtas_args) err_args.args[1] = RTAS_ERROR_LOG_MAX; err_args.args[2] = 0; - temp_args = *rtas_args; - get_paca()->xRtas = err_args; + save_args = rtas.args; + rtas.args = err_args; PPCDBG(PPCDBG_RTAS, "\tentering rtas with 0x%lx\n", __pa(&err_args)); - enter_rtas(__pa(&get_paca()->xRtas)); + enter_rtas(__pa(&rtas.args)); PPCDBG(PPCDBG_RTAS, "\treturned from rtas ...\n"); - err_args = get_paca()->xRtas; - get_paca()->xRtas = temp_args; + err_args = rtas.args; + rtas.args = save_args; 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); -} - -long -rtas_call(int token, int nargs, int nret, - unsigned long *outputs, ...) +int rtas_call(int token, int nargs, int nret, int *outputs, ...) { va_list list; int i, logit = 0; unsigned long s; - struct rtas_args *rtas_args = &(get_paca()->xRtas); - long ret; + struct rtas_args *rtas_args; + char * buff_copy = NULL; + int ret; PPCDBG(PPCDBG_RTAS, "Entering rtas_call\n"); PPCDBG(PPCDBG_RTAS, "\ttoken = 0x%x\n", token); @@ -158,6 +127,7 @@ rtas_call(int token, int nargs, int nret, /* Gotta do something different here, use global lock for now... */ spin_lock_irqsave(&rtas.lock, s); + rtas_args = &rtas.args; rtas_args->token = token; rtas_args->nargs = nargs; @@ -165,8 +135,8 @@ rtas_call(int token, int nargs, int nret, rtas_args->rets = (rtas_arg_t *)&(rtas_args->args[nargs]); va_start(list, outputs); for (i = 0; i < nargs; ++i) { - rtas_args->args[i] = (rtas_arg_t)LONG_LSW(va_arg(list, ulong)); - PPCDBG(PPCDBG_RTAS, "\tnarg[%d] = 0x%lx\n", i, rtas_args->args[i]); + rtas_args->args[i] = va_arg(list, rtas_arg_t); + PPCDBG(PPCDBG_RTAS, "\tnarg[%d] = 0x%x\n", i, rtas_args->args[i]); } va_end(list); @@ -178,8 +148,10 @@ rtas_call(int token, int nargs, int nret, enter_rtas(__pa(rtas_args)); PPCDBG(PPCDBG_RTAS, "\treturned from rtas ...\n"); + /* A -1 return code indicates that the last command couldn't + be completed due to a hardware error. */ if (rtas_args->rets[0] == -1) - logit = (__log_rtas_error(rtas_args) == 0); + logit = (__fetch_rtas_last_error() == 0); ifppcdebug(PPCDBG_RTAS) { for(i=0; i < nret ;i++) @@ -189,14 +161,23 @@ rtas_call(int token, int nargs, int nret, if (nret > 1 && outputs != NULL) for (i = 0; i < nret-1; ++i) outputs[i] = rtas_args->rets[i+1]; - ret = (ulong)((nret > 0) ? rtas_args->rets[0] : 0); + ret = (nret > 0)? rtas_args->rets[0]: 0; + + /* Log the error in the unlikely case that there was one. */ + if (unlikely(logit)) { + buff_copy = kmalloc(RTAS_ERROR_LOG_MAX, GFP_ATOMIC); + if (buff_copy) { + memcpy(buff_copy, rtas_err_buf, RTAS_ERROR_LOG_MAX); + } + } /* 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); - + if (buff_copy) { + log_error(buff_copy, ERR_TYPE_RTAS_LOG, 0); + kfree(buff_copy); + } return ret; } @@ -216,7 +197,7 @@ rtas_extended_busy_delay_time(int status) /* Use microseconds for reasonable accuracy */ for (ms=1; order > 0; order--) - ms *= 10; + ms *= 10; return ms; } @@ -225,20 +206,13 @@ int rtas_get_power_level(int powerdomain, int *level) { int token = rtas_token("get-power-level"); - long powerlevel; int rc; if (token == RTAS_UNKNOWN_SERVICE) return RTAS_UNKNOWN_OP; - while(1) { - rc = (int) rtas_call(token, 1, 2, &powerlevel, powerdomain); - if (rc == RTAS_BUSY) - udelay(1); - else - break; - } - *level = (int) powerlevel; + while ((rc = rtas_call(token, 1, 2, level, powerdomain)) == RTAS_BUSY) + udelay(1); return rc; } @@ -247,25 +221,21 @@ rtas_set_power_level(int powerdomain, int level, int *setlevel) { int token = rtas_token("set-power-level"); unsigned int wait_time; - long returned_level; int rc; if (token == RTAS_UNKNOWN_SERVICE) return RTAS_UNKNOWN_OP; while (1) { - rc = (int) rtas_call(token, 2, 2, &returned_level, powerdomain, - level); + rc = rtas_call(token, 2, 2, setlevel, powerdomain, level); if (rc == RTAS_BUSY) udelay(1); else if (rtas_is_extended_busy(rc)) { wait_time = rtas_extended_busy_delay_time(rc); udelay(wait_time * 1000); - } - else + } else break; } - *setlevel = (int) returned_level; return rc; } @@ -274,25 +244,21 @@ rtas_get_sensor(int sensor, int index, int *state) { int token = rtas_token("get-sensor-state"); unsigned int wait_time; - long returned_state; int rc; if (token == RTAS_UNKNOWN_SERVICE) return RTAS_UNKNOWN_OP; while (1) { - rc = (int) rtas_call(token, 2, 2, &returned_state, sensor, - index); + rc = rtas_call(token, 2, 2, state, sensor, index); if (rc == RTAS_BUSY) udelay(1); else if (rtas_is_extended_busy(rc)) { wait_time = rtas_extended_busy_delay_time(rc); udelay(wait_time * 1000); - } - else + } else break; } - *state = (int) returned_state; return rc; } @@ -307,8 +273,7 @@ rtas_set_indicator(int indicator, int index, int new_value) return RTAS_UNKNOWN_OP; while (1) { - rc = (int) rtas_call(token, 3, 1, NULL, indicator, index, - new_value); + rc = rtas_call(token, 3, 1, NULL, indicator, index, new_value); if (rc == RTAS_BUSY) udelay(1); else if (rtas_is_extended_busy(rc)) { @@ -364,7 +329,7 @@ rtas_flash_firmware(void) if (f->next) f->next = (struct flash_block_list *)virt_to_abs(f->next); else - f->next = 0LL; + f->next = NULL; /* make num_blocks into the version/length field */ f->num_blocks = (FLASH_BLOCK_LIST_VERSION << 56) | ((f->num_blocks+1)*16); } @@ -407,9 +372,9 @@ rtas_restart(char *cmd) if (rtas_firmware_flash_list.next) rtas_flash_firmware(); - printk("RTAS system-reboot returned %ld\n", + printk("RTAS system-reboot returned %d\n", rtas_call(rtas_token("system-reboot"), 0, 1, NULL)); - for (;;); + for (;;); } void @@ -417,10 +382,10 @@ rtas_power_off(void) { if (rtas_firmware_flash_list.next) rtas_flash_bypass_warning(); - /* allow power on only with power button press */ - printk("RTAS power-off returned %ld\n", - rtas_call(rtas_token("power-off"), 2, 1, NULL,0xffffffff,0xffffffff)); - for (;;); + /* allow power on only with power button press */ + printk("RTAS power-off returned %d\n", + rtas_call(rtas_token("power-off"), 2, 1, NULL, -1, -1)); + for (;;); } void @@ -428,7 +393,7 @@ rtas_halt(void) { if (rtas_firmware_flash_list.next) rtas_flash_bypass_warning(); - rtas_power_off(); + rtas_power_off(); } /* Must be in the RMO region, so we place it here */ @@ -436,7 +401,7 @@ static char rtas_os_term_buf[2048]; void rtas_os_term(char *str) { - long status; + int status; snprintf(rtas_os_term_buf, 2048, "OS panic: %s", str); @@ -447,7 +412,7 @@ void rtas_os_term(char *str) if (status == RTAS_BUSY) udelay(1); else if (status != 0) - printk(KERN_EMERG "ibm,os-term call failed %ld\n", + printk(KERN_EMERG "ibm,os-term call failed %d\n", status); } while (status == RTAS_BUSY); } @@ -458,7 +423,9 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) { struct rtas_args args; unsigned long flags; + char * buff_copy; int nargs; + int err_rc = 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -477,17 +444,33 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) nargs * sizeof(rtas_arg_t)) != 0) return -EFAULT; + buff_copy = kmalloc(RTAS_ERROR_LOG_MAX, GFP_KERNEL); + spin_lock_irqsave(&rtas.lock, flags); - get_paca()->xRtas = args; - enter_rtas(__pa(&get_paca()->xRtas)); - args = get_paca()->xRtas; + rtas.args = args; + enter_rtas(__pa(&rtas.args)); + args = rtas.args; + + args.rets = &args.args[nargs]; + + /* A -1 return code indicates that the last command couldn't + be completed due to a hardware error. */ + if (args.rets[0] == -1) { + err_rc = __fetch_rtas_last_error(); + if ((err_rc == 0) && buff_copy) { + memcpy(buff_copy, rtas_err_buf, RTAS_ERROR_LOG_MAX); + } + } spin_unlock_irqrestore(&rtas.lock, flags); - args.rets = (rtas_arg_t *)&(args.args[nargs]); - if (args.rets[0] == -1) - log_rtas_error(&args); + if (buff_copy) { + if ((args.rets[0] == -1) && (err_rc == 0)) { + log_error(buff_copy, ERR_TYPE_RTAS_LOG, 0); + } + kfree(buff_copy); + } /* Copy out args. */ if (copy_to_user(uargs->args + nargs, @@ -499,21 +482,25 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) } #ifdef CONFIG_HOTPLUG_CPU -/* This version can't take the spinlock. */ +/* This version can't take the spinlock, because it never returns */ + +struct rtas_args rtas_stop_self_args = { + /* The token is initialized for real in setup_system() */ + .token = RTAS_UNKNOWN_SERVICE, + .nargs = 0, + .nret = 1, + .rets = &rtas_stop_self_args.args[0], +}; void rtas_stop_self(void) { - struct rtas_args *rtas_args = &(get_paca()->xRtas); + struct rtas_args *rtas_args = &rtas_stop_self_args; local_irq_disable(); - rtas_args->token = rtas_token("stop-self"); BUG_ON(rtas_args->token == RTAS_UNKNOWN_SERVICE); - rtas_args->nargs = 0; - rtas_args->nret = 1; - rtas_args->rets = &(rtas_args->args[0]); - printk("%u %u Ready to die...\n", + printk("cpu %u (hwid %u) Ready to die...\n", smp_processor_id(), hard_smp_processor_id()); enter_rtas(__pa(rtas_args));