X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fparisc%2Fkernel%2Ftraps.c;h=ba37a0679a36821b32c1994e649021436612dd45;hb=refs%2Fheads%2Fvserver;hp=ff80b3c1f8a2765ea51b7f440dfaf5032e919b23;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/arch/parisc/kernel/traps.c b/arch/parisc/kernel/traps.c index ff80b3c1f..ba37a0679 100644 --- a/arch/parisc/kernel/traps.c +++ b/arch/parisc/kernel/traps.c @@ -10,13 +10,13 @@ * state in 'asm.s'. */ -#include #include #include #include #include #include #include +#include #include #include #include @@ -46,7 +46,7 @@ /* dumped to the console via printk) */ #if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) -spinlock_t pa_dbit_lock = SPIN_LOCK_UNLOCKED; +DEFINE_SPINLOCK(pa_dbit_lock); #endif int printbinary(char *buf, unsigned long x, int nbits) @@ -66,48 +66,69 @@ int printbinary(char *buf, unsigned long x, int nbits) #else #define RFMT "%08lx" #endif +#define FFMT "%016llx" /* fpregs are 64-bit always */ -void show_regs(struct pt_regs *regs) +#define PRINTREGS(lvl,r,f,fmt,x) \ + printk("%s%s%02d-%02d " fmt " " fmt " " fmt " " fmt "\n", \ + lvl, f, (x), (x+3), (r)[(x)+0], (r)[(x)+1], \ + (r)[(x)+2], (r)[(x)+3]) + +static void print_gr(char *level, struct pt_regs *regs) { int i; - char buf[128], *p; - char *level; - unsigned long cr30; - unsigned long cr31; - - level = user_mode(regs) ? KERN_DEBUG : KERN_CRIT; - - printk("%s\n", level); /* don't want to have that pretty register dump messed up */ + char buf[64]; + printk("%s\n", level); printk("%s YZrvWESTHLNXBCVMcbcbcbcbOGFRQPDI\n", level); printbinary(buf, regs->gr[0], 32); printk("%sPSW: %s %s\n", level, buf, print_tainted()); - for (i = 0; i < 32; i += 4) { - int j; - p = buf; - p += sprintf(p, "%sr%02d-%02d ", level, i, i + 3); - for (j = 0; j < 4; j++) { - p += sprintf(p, " " RFMT, (i+j) == 0 ? 0 : regs->gr[i + j]); - } - printk("%s\n", buf); - } + for (i = 0; i < 32; i += 4) + PRINTREGS(level, regs->gr, "r", RFMT, i); +} - for (i = 0; i < 8; i += 4) { - int j; - p = buf; - p += sprintf(p, "%ssr%d-%d ", level, i, i + 3); - for (j = 0; j < 4; j++) { - p += sprintf(p, " " RFMT, regs->sr[i + j]); - } - printk("%s\n", buf); - } +static void print_fr(char *level, struct pt_regs *regs) +{ + int i; + char buf[64]; + struct { u32 sw[2]; } s; + + /* FR are 64bit everywhere. Need to use asm to get the content + * of fpsr/fper1, and we assume that we won't have a FP Identify + * in our way, otherwise we're screwed. + * The fldd is used to restore the T-bit if there was one, as the + * store clears it anyway. + * PA2.0 book says "thou shall not use fstw on FPSR/FPERs" - T-Bone */ + asm volatile ("fstd %%fr0,0(%1) \n\t" + "fldd 0(%1),%%fr0 \n\t" + : "=m" (s) : "r" (&s) : "r0"); -#if RIDICULOUSLY_VERBOSE - for (i = 0; i < 32; i += 2) - printk("%sFR%02d : %016lx FR%2d : %016lx", level, i, - regs->fr[i], i+1, regs->fr[i+1]); -#endif + printk("%s\n", level); + printk("%s VZOUICununcqcqcqcqcqcrmunTDVZOUI\n", level); + printbinary(buf, s.sw[0], 32); + printk("%sFPSR: %s\n", level, buf); + printk("%sFPER1: %08x\n", level, s.sw[1]); + + /* here we'll print fr0 again, tho it'll be meaningless */ + for (i = 0; i < 32; i += 4) + PRINTREGS(level, regs->fr, "fr", FFMT, i); +} + +void show_regs(struct pt_regs *regs) +{ + int i; + char *level; + unsigned long cr30, cr31; + + level = user_mode(regs) ? KERN_DEBUG : KERN_CRIT; + + print_gr(level, regs); + + for (i = 0; i < 8; i += 4) + PRINTREGS(level, regs->sr, "sr", RFMT, i); + + if (user_mode(regs)) + print_fr(level, regs); cr30 = mfctl(30); cr31 = mfctl(31); @@ -135,32 +156,19 @@ void dump_stack(void) EXPORT_SYMBOL(dump_stack); -void show_stack(struct task_struct *task, unsigned long *s) +static void do_show_stack(struct unwind_frame_info *info) { int i = 1; - struct unwind_frame_info info; - - if (!task) { - unsigned long sp, ip, rp; - -HERE: - asm volatile ("copy %%r30, %0" : "=r"(sp)); - ip = (unsigned long)&&HERE; - rp = (unsigned long)__builtin_return_address(0); - unwind_frame_init(&info, current, sp, ip, rp); - } else { - unwind_frame_init_from_blocked_task(&info, task); - } printk("Backtrace:\n"); while (i <= 16) { - if (unwind_once(&info) < 0 || info.ip == 0) + if (unwind_once(info) < 0 || info->ip == 0) break; - if (__kernel_text_address(info.ip)) { - printk(" [<" RFMT ">] ", info.ip); + if (__kernel_text_address(info->ip)) { + printk(" [<" RFMT ">] ", info->ip); #ifdef CONFIG_KALLSYMS - print_symbol("%s\n", info.ip); + print_symbol("%s\n", info->ip); #else if ((i & 0x03) == 0) printk("\n"); @@ -171,14 +179,40 @@ HERE: printk("\n"); } +void show_stack(struct task_struct *task, unsigned long *s) +{ + struct unwind_frame_info info; + + if (!task) { + unsigned long sp; + struct pt_regs *r; + +HERE: + asm volatile ("copy %%r30, %0" : "=r"(sp)); + r = kzalloc(sizeof(struct pt_regs), GFP_KERNEL); + if (!r) + return; + r->iaoq[0] = (unsigned long)&&HERE; + r->gr[2] = (unsigned long)__builtin_return_address(0); + r->gr[30] = sp; + unwind_frame_init(&info, current, r); + kfree(r); + } else { + unwind_frame_init_from_blocked_task(&info, task); + } + + do_show_stack(&info); +} + void die_if_kernel(char *str, struct pt_regs *regs, long err) { if (user_mode(regs)) { if (err == 0) return; /* STFU */ - printk(KERN_CRIT "%s (pid %d): %s (code %ld) at " RFMT "\n", - current->comm, current->pid, str, err, regs->iaoq[0]); + printk(KERN_CRIT "%s (pid %d:#%u): %s (code %ld) at " RFMT "\n", + current->comm, current->pid, current->xid, + str, err, regs->iaoq[0]); #ifdef PRINT_USER_FAULTS /* XXX for debugging only */ show_regs(regs); @@ -209,10 +243,19 @@ void die_if_kernel(char *str, struct pt_regs *regs, long err) if (!console_drivers) pdc_console_restart(); - printk(KERN_CRIT "%s (pid %d): %s (code %ld)\n", - current->comm, current->pid, str, err); + printk(KERN_CRIT "%s (pid %d:#%u): %s (code %ld)\n", + current->comm, current->pid, current->xid, str, err); show_regs(regs); + if (in_interrupt()) + panic("Fatal exception in interrupt"); + + if (panic_on_oops) { + printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n"); + ssleep(5); + panic("Fatal exception"); + } + /* Wot's wrong wif bein' racy? */ if (current->thread.flags & PARISC_KERNEL_DEATH) { printk(KERN_CRIT "%s() recursion detected.\n", __FUNCTION__); @@ -236,7 +279,7 @@ void handle_gdb_break(struct pt_regs *regs, int wot) struct siginfo si; si.si_code = wot; - si.si_addr = (void *) (regs->iaoq[0] & ~3); + si.si_addr = (void __user *) (regs->iaoq[0] & ~3); si.si_signo = SIGTRAP; si.si_errno = 0; force_sig_info(SIGTRAP, &si, current); @@ -257,7 +300,7 @@ void handle_break(unsigned iir, struct pt_regs *regs) show_regs(regs); #endif si.si_code = TRAP_BRKPT; - si.si_addr = (void *) (regs->iaoq[0] & ~3); + si.si_addr = (void __user *) (regs->iaoq[0] & ~3); si.si_signo = SIGTRAP; force_sig_info(SIGTRAP, &si, current); break; @@ -275,7 +318,7 @@ void handle_break(unsigned iir, struct pt_regs *regs) #endif si.si_signo = SIGTRAP; si.si_code = TRAP_BRKPT; - si.si_addr = (void *) (regs->iaoq[0] & ~3); + si.si_addr = (void __user *) (regs->iaoq[0] & ~3); force_sig_info(SIGTRAP, &si, current); return; } @@ -373,13 +416,13 @@ void transfer_pim_to_trap_frame(struct pt_regs *regs) /* - * This routine handles page faults. It determines the address, - * and the problem, and then passes it off to one of the appropriate - * routines. + * This routine is called as a last resort when everything else + * has gone clearly wrong. We get called for faults in kernel space, + * and HPMC's. */ void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long offset) { - static spinlock_t terminate_lock = SPIN_LOCK_UNLOCKED; + static DEFINE_SPINLOCK(terminate_lock); oops_in_progress = 1; @@ -407,7 +450,12 @@ void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long o } - show_stack(NULL, (unsigned long *)regs->gr[30]); + { + /* show_stack(NULL, (unsigned long *)regs->gr[30]); */ + struct unwind_frame_info info; + unwind_frame_init(&info, current, regs); + do_show_stack(&info); + } printk("\n"); printk(KERN_CRIT "%s: Code=%d regs=%p (Addr=" RFMT ")\n", @@ -421,9 +469,16 @@ void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long o * system will shut down immediately right here. */ pdc_soft_power_button(0); - /* Gutter the processor! */ - for(;;) - ; + /* Call kernel panic() so reboot timeouts work properly + * FIXME: This function should be on the list of + * panic notifiers, and we should call panic + * directly from the location that we wish. + * e.g. We should not call panic from + * parisc_terminate, but rather the oter way around. + * This hack works, prints the panic message twice, + * and it enables reboot timers! + */ + panic(msg); } void handle_interruption(int code, struct pt_regs *regs) @@ -437,6 +492,36 @@ void handle_interruption(int code, struct pt_regs *regs) else local_irq_enable(); + /* Security check: + * If the priority level is still user, and the + * faulting space is not equal to the active space + * then the user is attempting something in a space + * that does not belong to them. Kill the process. + * + * This is normally the situation when the user + * attempts to jump into the kernel space at the + * wrong offset, be it at the gateway page or a + * random location. + * + * We cannot normally signal the process because it + * could *be* on the gateway page, and processes + * executing on the gateway page can't have signals + * delivered. + * + * We merely readjust the address into the users + * space, at a destination address of zero, and + * allow processing to continue. + */ + if (((unsigned long)regs->iaoq[0] & 3) && + ((unsigned long)regs->iasq[0] != (unsigned long)regs->sr[7])) { + /* Kill the user process later */ + regs->iaoq[0] = 0 | 3; + regs->iaoq[1] = regs->iaoq[0] + 4; + regs->iasq[0] = regs->iasq[0] = regs->sr[7]; + regs->gr[0] &= ~PSW_B; + return; + } + #if 0 printk(KERN_CRIT "Interruption # %d\n", code); #endif @@ -461,7 +546,7 @@ void handle_interruption(int code, struct pt_regs *regs) case 3: /* Recovery counter trap */ regs->gr[0] &= ~PSW_R; - if (regs->iasq[0]) + if (user_space(regs)) handle_gdb_break(regs, TRAP_TRACE); /* else this must be the start of a syscall - just let it run */ return; @@ -521,7 +606,7 @@ void handle_interruption(int code, struct pt_regs *regs) give_sigill: si.si_signo = SIGILL; si.si_errno = 0; - si.si_addr = (void *) regs->iaoq[0]; + si.si_addr = (void __user *) regs->iaoq[0]; force_sig_info(SIGILL, &si, current); return; @@ -529,7 +614,7 @@ void handle_interruption(int code, struct pt_regs *regs) /* Overflow Trap, let the userland signal handler do the cleanup */ si.si_signo = SIGFPE; si.si_code = FPE_INTOVF; - si.si_addr = (void *) regs->iaoq[0]; + si.si_addr = (void __user *) regs->iaoq[0]; force_sig_info(SIGFPE, &si, current); return; @@ -542,12 +627,12 @@ void handle_interruption(int code, struct pt_regs *regs) /* Set to zero, and let the userspace app figure it out from the insn pointed to by si_addr */ si.si_code = 0; - si.si_addr = (void *) regs->iaoq[0]; + si.si_addr = (void __user *) regs->iaoq[0]; force_sig_info(SIGFPE, &si, current); return; - } else - /* The kernel doesn't want to handle condition codes */ - break; + } + /* The kernel doesn't want to handle condition codes */ + break; case 14: /* Assist Exception Trap, i.e. floating point exception. */ @@ -565,9 +650,16 @@ void handle_interruption(int code, struct pt_regs *regs) /* Fall through */ case 17: /* Non-access data TLB miss fault/Non-access data page fault */ - /* TODO: Still need to add slow path emulation code here */ - /* TODO: Understand what is meant by the TODO listed - above this one. (Carlos) */ + /* FIXME: + Still need to add slow path emulation code here! + If the insn used a non-shadow register, then the tlb + handlers could not have their side-effect (e.g. probe + writing to a target register) emulated since rfir would + erase the changes to said register. Instead we have to + setup everything, call this function we are in, and emulate + by hand. Technically we need to emulate: + fdc,fdce,pdc,"fic,4f",prober,probeir,probew, probeiw + */ fault_address = regs->ior; fault_space = regs->isr; break; @@ -598,7 +690,7 @@ void handle_interruption(int code, struct pt_regs *regs) case 25: /* Taken branch trap */ regs->gr[0] &= ~PSW_T; - if (regs->iasq[0]) + if (user_space(regs)) handle_gdb_break(regs, TRAP_BRANCH); /* else this must be the start of a syscall - just let it * run. @@ -644,9 +736,9 @@ void handle_interruption(int code, struct pt_regs *regs) si.si_signo = SIGSEGV; si.si_errno = 0; if (code == 7) - si.si_addr = (void *) regs->iaoq[0]; + si.si_addr = (void __user *) regs->iaoq[0]; else - si.si_addr = (void *) regs->ior; + si.si_addr = (void __user *) regs->ior; force_sig_info(SIGSEGV, &si, current); return; @@ -666,7 +758,7 @@ void handle_interruption(int code, struct pt_regs *regs) si.si_signo = SIGBUS; si.si_code = BUS_OBJERR; si.si_errno = 0; - si.si_addr = (void *) regs->ior; + si.si_addr = (void __user *) regs->ior; force_sig_info(SIGBUS, &si, current); return; } @@ -677,7 +769,7 @@ void handle_interruption(int code, struct pt_regs *regs) } if (user_mode(regs)) { - if ((fault_space>>SPACEID_SHIFT) != (regs->sr[7] >> SPACEID_SHIFT)) { + if ((fault_space >> SPACEID_SHIFT) != (regs->sr[7] >> SPACEID_SHIFT)) { #ifdef PRINT_USER_FAULTS if (fault_space == 0) printk(KERN_DEBUG "User Fault on Kernel Space "); @@ -690,7 +782,7 @@ void handle_interruption(int code, struct pt_regs *regs) si.si_signo = SIGSEGV; si.si_errno = 0; si.si_code = SEGV_MAPERR; - si.si_addr = (void *) regs->ior; + si.si_addr = (void __user *) regs->ior; force_sig_info(SIGSEGV, &si, current); return; }