| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 1991, 1992  Linus Torvalds | 
|  | 3 | * Copyright 2010 Tilera Corporation. All Rights Reserved. | 
|  | 4 | * | 
|  | 5 | *   This program is free software; you can redistribute it and/or | 
|  | 6 | *   modify it under the terms of the GNU General Public License | 
|  | 7 | *   as published by the Free Software Foundation, version 2. | 
|  | 8 | * | 
|  | 9 | *   This program is distributed in the hope that it will be useful, but | 
|  | 10 | *   WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 11 | *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | 
|  | 12 | *   NON INFRINGEMENT.  See the GNU General Public License for | 
|  | 13 | *   more details. | 
|  | 14 | */ | 
|  | 15 |  | 
|  | 16 | #include <linux/sched.h> | 
|  | 17 | #include <linux/mm.h> | 
|  | 18 | #include <linux/smp.h> | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 19 | #include <linux/kernel.h> | 
|  | 20 | #include <linux/signal.h> | 
|  | 21 | #include <linux/errno.h> | 
|  | 22 | #include <linux/wait.h> | 
|  | 23 | #include <linux/unistd.h> | 
|  | 24 | #include <linux/stddef.h> | 
|  | 25 | #include <linux/personality.h> | 
|  | 26 | #include <linux/suspend.h> | 
|  | 27 | #include <linux/ptrace.h> | 
|  | 28 | #include <linux/elf.h> | 
|  | 29 | #include <linux/compat.h> | 
|  | 30 | #include <linux/syscalls.h> | 
|  | 31 | #include <linux/uaccess.h> | 
|  | 32 | #include <asm/processor.h> | 
|  | 33 | #include <asm/ucontext.h> | 
|  | 34 | #include <asm/sigframe.h> | 
| Chris Metcalf | 0707ad3 | 2010-06-25 17:04:17 -0400 | [diff] [blame] | 35 | #include <asm/syscalls.h> | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 36 | #include <arch/interrupts.h> | 
|  | 37 |  | 
|  | 38 | #define DEBUG_SIG 0 | 
|  | 39 |  | 
|  | 40 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | 
|  | 41 |  | 
| Chris Metcalf | d929b6a | 2010-10-14 14:34:33 -0400 | [diff] [blame] | 42 | SYSCALL_DEFINE3(sigaltstack, const stack_t __user *, uss, | 
|  | 43 | stack_t __user *, uoss, struct pt_regs *, regs) | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 44 | { | 
|  | 45 | return do_sigaltstack(uss, uoss, regs->sp); | 
|  | 46 | } | 
|  | 47 |  | 
|  | 48 |  | 
|  | 49 | /* | 
|  | 50 | * Do a signal return; undo the signal stack. | 
|  | 51 | */ | 
|  | 52 |  | 
|  | 53 | int restore_sigcontext(struct pt_regs *regs, | 
| Chris Metcalf | 81711ce | 2010-12-14 16:07:25 -0500 | [diff] [blame] | 54 | struct sigcontext __user *sc) | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 55 | { | 
|  | 56 | int err = 0; | 
|  | 57 | int i; | 
|  | 58 |  | 
|  | 59 | /* Always make any pending restarted system calls return -EINTR */ | 
|  | 60 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | 
|  | 61 |  | 
| Chris Metcalf | 74fca9d | 2010-09-15 11:16:08 -0400 | [diff] [blame] | 62 | /* | 
|  | 63 | * Enforce that sigcontext is like pt_regs, and doesn't mess | 
|  | 64 | * up our stack alignment rules. | 
|  | 65 | */ | 
|  | 66 | BUILD_BUG_ON(sizeof(struct sigcontext) != sizeof(struct pt_regs)); | 
|  | 67 | BUILD_BUG_ON(sizeof(struct sigcontext) % 8 != 0); | 
|  | 68 |  | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 69 | for (i = 0; i < sizeof(struct pt_regs)/sizeof(long); ++i) | 
| Chris Metcalf | 74fca9d | 2010-09-15 11:16:08 -0400 | [diff] [blame] | 70 | err |= __get_user(regs->regs[i], &sc->gregs[i]); | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 71 |  | 
| Chris Metcalf | 1deb9c5 | 2010-10-28 15:47:06 -0400 | [diff] [blame] | 72 | /* Ensure that the PL is always set to USER_PL. */ | 
|  | 73 | regs->ex1 = PL_ICS_EX1(USER_PL, EX1_ICS(regs->ex1)); | 
|  | 74 |  | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 75 | regs->faultnum = INT_SWINT_1_SIGRETURN; | 
|  | 76 |  | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 77 | return err; | 
|  | 78 | } | 
|  | 79 |  | 
| Chris Metcalf | 571d76a | 2011-05-16 14:23:44 -0400 | [diff] [blame] | 80 | void signal_fault(const char *type, struct pt_regs *regs, | 
|  | 81 | void __user *frame, int sig) | 
|  | 82 | { | 
|  | 83 | trace_unhandled_signal(type, regs, (unsigned long)frame, SIGSEGV); | 
|  | 84 | force_sigsegv(sig, current); | 
|  | 85 | } | 
|  | 86 |  | 
| Chris Metcalf | 81711ce | 2010-12-14 16:07:25 -0500 | [diff] [blame] | 87 | /* The assembly shim for this function arranges to ignore the return value. */ | 
| Chris Metcalf | d929b6a | 2010-10-14 14:34:33 -0400 | [diff] [blame] | 88 | SYSCALL_DEFINE1(rt_sigreturn, struct pt_regs *, regs) | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 89 | { | 
|  | 90 | struct rt_sigframe __user *frame = | 
|  | 91 | (struct rt_sigframe __user *)(regs->sp); | 
|  | 92 | sigset_t set; | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 93 |  | 
|  | 94 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | 
|  | 95 | goto badframe; | 
|  | 96 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | 
|  | 97 | goto badframe; | 
|  | 98 |  | 
|  | 99 | sigdelsetmask(&set, ~_BLOCKABLE); | 
|  | 100 | spin_lock_irq(¤t->sighand->siglock); | 
|  | 101 | current->blocked = set; | 
|  | 102 | recalc_sigpending(); | 
|  | 103 | spin_unlock_irq(¤t->sighand->siglock); | 
|  | 104 |  | 
| Chris Metcalf | 81711ce | 2010-12-14 16:07:25 -0500 | [diff] [blame] | 105 | if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 106 | goto badframe; | 
|  | 107 |  | 
|  | 108 | if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->sp) == -EFAULT) | 
|  | 109 | goto badframe; | 
|  | 110 |  | 
| Chris Metcalf | 81711ce | 2010-12-14 16:07:25 -0500 | [diff] [blame] | 111 | return 0; | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 112 |  | 
|  | 113 | badframe: | 
| Chris Metcalf | 571d76a | 2011-05-16 14:23:44 -0400 | [diff] [blame] | 114 | signal_fault("bad sigreturn frame", regs, frame, 0); | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 115 | return 0; | 
|  | 116 | } | 
|  | 117 |  | 
|  | 118 | /* | 
|  | 119 | * Set up a signal frame. | 
|  | 120 | */ | 
|  | 121 |  | 
|  | 122 | int setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs) | 
|  | 123 | { | 
|  | 124 | int i, err = 0; | 
|  | 125 |  | 
|  | 126 | for (i = 0; i < sizeof(struct pt_regs)/sizeof(long); ++i) | 
| Chris Metcalf | 74fca9d | 2010-09-15 11:16:08 -0400 | [diff] [blame] | 127 | err |= __put_user(regs->regs[i], &sc->gregs[i]); | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 128 |  | 
|  | 129 | return err; | 
|  | 130 | } | 
|  | 131 |  | 
|  | 132 | /* | 
|  | 133 | * Determine which stack to use.. | 
|  | 134 | */ | 
|  | 135 | static inline void __user *get_sigframe(struct k_sigaction *ka, | 
|  | 136 | struct pt_regs *regs, | 
|  | 137 | size_t frame_size) | 
|  | 138 | { | 
|  | 139 | unsigned long sp; | 
|  | 140 |  | 
|  | 141 | /* Default to using normal stack */ | 
|  | 142 | sp = regs->sp; | 
|  | 143 |  | 
|  | 144 | /* | 
|  | 145 | * If we are on the alternate signal stack and would overflow | 
|  | 146 | * it, don't.  Return an always-bogus address instead so we | 
|  | 147 | * will die with SIGSEGV. | 
|  | 148 | */ | 
|  | 149 | if (on_sig_stack(sp) && !likely(on_sig_stack(sp - frame_size))) | 
| Chris Metcalf | 0707ad3 | 2010-06-25 17:04:17 -0400 | [diff] [blame] | 150 | return (void __user __force *)-1UL; | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 151 |  | 
|  | 152 | /* This is the X/Open sanctioned signal stack switching.  */ | 
|  | 153 | if (ka->sa.sa_flags & SA_ONSTACK) { | 
|  | 154 | if (sas_ss_flags(sp) == 0) | 
|  | 155 | sp = current->sas_ss_sp + current->sas_ss_size; | 
|  | 156 | } | 
|  | 157 |  | 
|  | 158 | sp -= frame_size; | 
|  | 159 | /* | 
|  | 160 | * Align the stack pointer according to the TILE ABI, | 
|  | 161 | * i.e. so that on function entry (sp & 15) == 0. | 
|  | 162 | */ | 
|  | 163 | sp &= -16UL; | 
|  | 164 | return (void __user *) sp; | 
|  | 165 | } | 
|  | 166 |  | 
|  | 167 | static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | 
|  | 168 | sigset_t *set, struct pt_regs *regs) | 
|  | 169 | { | 
|  | 170 | unsigned long restorer; | 
|  | 171 | struct rt_sigframe __user *frame; | 
|  | 172 | int err = 0; | 
|  | 173 | int usig; | 
|  | 174 |  | 
|  | 175 | frame = get_sigframe(ka, regs, sizeof(*frame)); | 
|  | 176 |  | 
|  | 177 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | 
|  | 178 | goto give_sigsegv; | 
|  | 179 |  | 
|  | 180 | usig = current_thread_info()->exec_domain | 
|  | 181 | && current_thread_info()->exec_domain->signal_invmap | 
|  | 182 | && sig < 32 | 
|  | 183 | ? current_thread_info()->exec_domain->signal_invmap[sig] | 
|  | 184 | : sig; | 
|  | 185 |  | 
|  | 186 | /* Always write at least the signal number for the stack backtracer. */ | 
|  | 187 | if (ka->sa.sa_flags & SA_SIGINFO) { | 
|  | 188 | /* At sigreturn time, restore the callee-save registers too. */ | 
|  | 189 | err |= copy_siginfo_to_user(&frame->info, info); | 
|  | 190 | regs->flags |= PT_FLAGS_RESTORE_REGS; | 
|  | 191 | } else { | 
|  | 192 | err |= __put_user(info->si_signo, &frame->info.si_signo); | 
|  | 193 | } | 
|  | 194 |  | 
|  | 195 | /* Create the ucontext.  */ | 
|  | 196 | err |= __clear_user(&frame->save_area, sizeof(frame->save_area)); | 
|  | 197 | err |= __put_user(0, &frame->uc.uc_flags); | 
| Chris Metcalf | 0707ad3 | 2010-06-25 17:04:17 -0400 | [diff] [blame] | 198 | err |= __put_user(NULL, &frame->uc.uc_link); | 
|  | 199 | err |= __put_user((void __user *)(current->sas_ss_sp), | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 200 | &frame->uc.uc_stack.ss_sp); | 
|  | 201 | err |= __put_user(sas_ss_flags(regs->sp), | 
|  | 202 | &frame->uc.uc_stack.ss_flags); | 
|  | 203 | err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); | 
|  | 204 | err |= setup_sigcontext(&frame->uc.uc_mcontext, regs); | 
|  | 205 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | 
|  | 206 | if (err) | 
|  | 207 | goto give_sigsegv; | 
|  | 208 |  | 
|  | 209 | restorer = VDSO_BASE; | 
|  | 210 | if (ka->sa.sa_flags & SA_RESTORER) | 
|  | 211 | restorer = (unsigned long) ka->sa.sa_restorer; | 
|  | 212 |  | 
|  | 213 | /* | 
|  | 214 | * Set up registers for signal handler. | 
|  | 215 | * Registers that we don't modify keep the value they had from | 
|  | 216 | * user-space at the time we took the signal. | 
| Chris Metcalf | 74fca9d | 2010-09-15 11:16:08 -0400 | [diff] [blame] | 217 | * We always pass siginfo and mcontext, regardless of SA_SIGINFO, | 
|  | 218 | * since some things rely on this (e.g. glibc's debug/segfault.c). | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 219 | */ | 
|  | 220 | regs->pc = (unsigned long) ka->sa.sa_handler; | 
|  | 221 | regs->ex1 = PL_ICS_EX1(USER_PL, 1); /* set crit sec in handler */ | 
|  | 222 | regs->sp = (unsigned long) frame; | 
|  | 223 | regs->lr = restorer; | 
|  | 224 | regs->regs[0] = (unsigned long) usig; | 
| Chris Metcalf | 74fca9d | 2010-09-15 11:16:08 -0400 | [diff] [blame] | 225 | regs->regs[1] = (unsigned long) &frame->info; | 
|  | 226 | regs->regs[2] = (unsigned long) &frame->uc; | 
|  | 227 | regs->flags |= PT_FLAGS_CALLER_SAVES; | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 228 |  | 
|  | 229 | /* | 
|  | 230 | * Notify any tracer that was single-stepping it. | 
|  | 231 | * The tracer may want to single-step inside the | 
|  | 232 | * handler too. | 
|  | 233 | */ | 
|  | 234 | if (test_thread_flag(TIF_SINGLESTEP)) | 
|  | 235 | ptrace_notify(SIGTRAP); | 
|  | 236 |  | 
|  | 237 | return 0; | 
|  | 238 |  | 
|  | 239 | give_sigsegv: | 
| Chris Metcalf | 571d76a | 2011-05-16 14:23:44 -0400 | [diff] [blame] | 240 | signal_fault("bad setup frame", regs, frame, sig); | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 241 | return -EFAULT; | 
|  | 242 | } | 
|  | 243 |  | 
|  | 244 | /* | 
|  | 245 | * OK, we're invoking a handler | 
|  | 246 | */ | 
|  | 247 |  | 
|  | 248 | static int handle_signal(unsigned long sig, siginfo_t *info, | 
|  | 249 | struct k_sigaction *ka, sigset_t *oldset, | 
|  | 250 | struct pt_regs *regs) | 
|  | 251 | { | 
|  | 252 | int ret; | 
|  | 253 |  | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 254 | /* Are we from a system call? */ | 
|  | 255 | if (regs->faultnum == INT_SWINT_1) { | 
|  | 256 | /* If so, check system call restarting.. */ | 
|  | 257 | switch (regs->regs[0]) { | 
|  | 258 | case -ERESTART_RESTARTBLOCK: | 
|  | 259 | case -ERESTARTNOHAND: | 
|  | 260 | regs->regs[0] = -EINTR; | 
|  | 261 | break; | 
|  | 262 |  | 
|  | 263 | case -ERESTARTSYS: | 
|  | 264 | if (!(ka->sa.sa_flags & SA_RESTART)) { | 
|  | 265 | regs->regs[0] = -EINTR; | 
|  | 266 | break; | 
|  | 267 | } | 
|  | 268 | /* fallthrough */ | 
|  | 269 | case -ERESTARTNOINTR: | 
|  | 270 | /* Reload caller-saves to restore r0..r5 and r10. */ | 
|  | 271 | regs->flags |= PT_FLAGS_CALLER_SAVES; | 
|  | 272 | regs->regs[0] = regs->orig_r0; | 
|  | 273 | regs->pc -= 8; | 
|  | 274 | } | 
|  | 275 | } | 
|  | 276 |  | 
|  | 277 | /* Set up the stack frame */ | 
|  | 278 | #ifdef CONFIG_COMPAT | 
|  | 279 | if (is_compat_task()) | 
|  | 280 | ret = compat_setup_rt_frame(sig, ka, info, oldset, regs); | 
|  | 281 | else | 
|  | 282 | #endif | 
|  | 283 | ret = setup_rt_frame(sig, ka, info, oldset, regs); | 
|  | 284 | if (ret == 0) { | 
|  | 285 | /* This code is only called from system calls or from | 
|  | 286 | * the work_pending path in the return-to-user code, and | 
|  | 287 | * either way we can re-enable interrupts unconditionally. | 
|  | 288 | */ | 
|  | 289 | spin_lock_irq(¤t->sighand->siglock); | 
|  | 290 | sigorsets(¤t->blocked, | 
|  | 291 | ¤t->blocked, &ka->sa.sa_mask); | 
|  | 292 | if (!(ka->sa.sa_flags & SA_NODEFER)) | 
|  | 293 | sigaddset(¤t->blocked, sig); | 
|  | 294 | recalc_sigpending(); | 
|  | 295 | spin_unlock_irq(¤t->sighand->siglock); | 
|  | 296 | } | 
|  | 297 |  | 
|  | 298 | return ret; | 
|  | 299 | } | 
|  | 300 |  | 
|  | 301 | /* | 
|  | 302 | * Note that 'init' is a special process: it doesn't get signals it doesn't | 
|  | 303 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | 
|  | 304 | * mistake. | 
|  | 305 | */ | 
|  | 306 | void do_signal(struct pt_regs *regs) | 
|  | 307 | { | 
|  | 308 | siginfo_t info; | 
|  | 309 | int signr; | 
|  | 310 | struct k_sigaction ka; | 
|  | 311 | sigset_t *oldset; | 
|  | 312 |  | 
|  | 313 | /* | 
|  | 314 | * i386 will check if we're coming from kernel mode and bail out | 
|  | 315 | * here.  In my experience this just turns weird crashes into | 
|  | 316 | * weird spin-hangs.  But if we find a case where this seems | 
|  | 317 | * helpful, we can reinstate the check on "!user_mode(regs)". | 
|  | 318 | */ | 
|  | 319 |  | 
|  | 320 | if (current_thread_info()->status & TS_RESTORE_SIGMASK) | 
|  | 321 | oldset = ¤t->saved_sigmask; | 
|  | 322 | else | 
|  | 323 | oldset = ¤t->blocked; | 
|  | 324 |  | 
|  | 325 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | 
|  | 326 | if (signr > 0) { | 
|  | 327 | /* Whee! Actually deliver the signal.  */ | 
|  | 328 | if (handle_signal(signr, &info, &ka, oldset, regs) == 0) { | 
|  | 329 | /* | 
|  | 330 | * A signal was successfully delivered; the saved | 
|  | 331 | * sigmask will have been stored in the signal frame, | 
|  | 332 | * and will be restored by sigreturn, so we can simply | 
|  | 333 | * clear the TS_RESTORE_SIGMASK flag. | 
|  | 334 | */ | 
|  | 335 | current_thread_info()->status &= ~TS_RESTORE_SIGMASK; | 
|  | 336 | } | 
|  | 337 |  | 
| Chris Metcalf | 34a89d2 | 2010-10-28 15:03:30 -0400 | [diff] [blame] | 338 | goto done; | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 339 | } | 
|  | 340 |  | 
|  | 341 | /* Did we come from a system call? */ | 
|  | 342 | if (regs->faultnum == INT_SWINT_1) { | 
|  | 343 | /* Restart the system call - no handlers present */ | 
|  | 344 | switch (regs->regs[0]) { | 
|  | 345 | case -ERESTARTNOHAND: | 
|  | 346 | case -ERESTARTSYS: | 
|  | 347 | case -ERESTARTNOINTR: | 
|  | 348 | regs->flags |= PT_FLAGS_CALLER_SAVES; | 
|  | 349 | regs->regs[0] = regs->orig_r0; | 
|  | 350 | regs->pc -= 8; | 
|  | 351 | break; | 
|  | 352 |  | 
|  | 353 | case -ERESTART_RESTARTBLOCK: | 
|  | 354 | regs->flags |= PT_FLAGS_CALLER_SAVES; | 
|  | 355 | regs->regs[TREG_SYSCALL_NR] = __NR_restart_syscall; | 
|  | 356 | regs->pc -= 8; | 
|  | 357 | break; | 
|  | 358 | } | 
|  | 359 | } | 
|  | 360 |  | 
|  | 361 | /* If there's no signal to deliver, just put the saved sigmask back. */ | 
|  | 362 | if (current_thread_info()->status & TS_RESTORE_SIGMASK) { | 
|  | 363 | current_thread_info()->status &= ~TS_RESTORE_SIGMASK; | 
|  | 364 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | 
|  | 365 | } | 
| Chris Metcalf | 34a89d2 | 2010-10-28 15:03:30 -0400 | [diff] [blame] | 366 |  | 
|  | 367 | done: | 
|  | 368 | /* Avoid double syscall restart if there are nested signals. */ | 
|  | 369 | regs->faultnum = INT_SWINT_1_SIGRETURN; | 
| Chris Metcalf | 867e359 | 2010-05-28 23:09:12 -0400 | [diff] [blame] | 370 | } | 
| Chris Metcalf | 571d76a | 2011-05-16 14:23:44 -0400 | [diff] [blame] | 371 |  | 
|  | 372 | int show_unhandled_signals = 1; | 
|  | 373 |  | 
|  | 374 | static int __init crashinfo(char *str) | 
|  | 375 | { | 
|  | 376 | unsigned long val; | 
|  | 377 | const char *word; | 
|  | 378 |  | 
|  | 379 | if (*str == '\0') | 
|  | 380 | val = 2; | 
|  | 381 | else if (*str != '=' || strict_strtoul(++str, 0, &val) != 0) | 
|  | 382 | return 0; | 
|  | 383 | show_unhandled_signals = val; | 
|  | 384 | switch (show_unhandled_signals) { | 
|  | 385 | case 0: | 
|  | 386 | word = "No"; | 
|  | 387 | break; | 
|  | 388 | case 1: | 
|  | 389 | word = "One-line"; | 
|  | 390 | break; | 
|  | 391 | default: | 
|  | 392 | word = "Detailed"; | 
|  | 393 | break; | 
|  | 394 | } | 
|  | 395 | pr_info("%s crash reports will be generated on the console\n", word); | 
|  | 396 | return 1; | 
|  | 397 | } | 
|  | 398 | __setup("crashinfo", crashinfo); | 
|  | 399 |  | 
|  | 400 | static void dump_mem(void __user *address) | 
|  | 401 | { | 
|  | 402 | void __user *addr; | 
|  | 403 | enum { region_size = 256, bytes_per_line = 16 }; | 
|  | 404 | int i, j, k; | 
|  | 405 | int found_readable_mem = 0; | 
|  | 406 |  | 
|  | 407 | pr_err("\n"); | 
|  | 408 | if (!access_ok(VERIFY_READ, address, 1)) { | 
|  | 409 | pr_err("Not dumping at address 0x%lx (kernel address)\n", | 
|  | 410 | (unsigned long)address); | 
|  | 411 | return; | 
|  | 412 | } | 
|  | 413 |  | 
|  | 414 | addr = (void __user *) | 
|  | 415 | (((unsigned long)address & -bytes_per_line) - region_size/2); | 
|  | 416 | if (addr > address) | 
|  | 417 | addr = NULL; | 
|  | 418 | for (i = 0; i < region_size; | 
|  | 419 | addr += bytes_per_line, i += bytes_per_line) { | 
|  | 420 | unsigned char buf[bytes_per_line]; | 
|  | 421 | char line[100]; | 
|  | 422 | if (copy_from_user(buf, addr, bytes_per_line)) | 
|  | 423 | continue; | 
|  | 424 | if (!found_readable_mem) { | 
|  | 425 | pr_err("Dumping memory around address 0x%lx:\n", | 
|  | 426 | (unsigned long)address); | 
|  | 427 | found_readable_mem = 1; | 
|  | 428 | } | 
|  | 429 | j = sprintf(line, REGFMT":", (unsigned long)addr); | 
|  | 430 | for (k = 0; k < bytes_per_line; ++k) | 
|  | 431 | j += sprintf(&line[j], " %02x", buf[k]); | 
|  | 432 | pr_err("%s\n", line); | 
|  | 433 | } | 
|  | 434 | if (!found_readable_mem) | 
|  | 435 | pr_err("No readable memory around address 0x%lx\n", | 
|  | 436 | (unsigned long)address); | 
|  | 437 | } | 
|  | 438 |  | 
|  | 439 | void trace_unhandled_signal(const char *type, struct pt_regs *regs, | 
|  | 440 | unsigned long address, int sig) | 
|  | 441 | { | 
|  | 442 | struct task_struct *tsk = current; | 
|  | 443 |  | 
|  | 444 | if (show_unhandled_signals == 0) | 
|  | 445 | return; | 
|  | 446 |  | 
|  | 447 | /* If the signal is handled, don't show it here. */ | 
|  | 448 | if (!is_global_init(tsk)) { | 
|  | 449 | void __user *handler = | 
|  | 450 | tsk->sighand->action[sig-1].sa.sa_handler; | 
|  | 451 | if (handler != SIG_IGN && handler != SIG_DFL) | 
|  | 452 | return; | 
|  | 453 | } | 
|  | 454 |  | 
|  | 455 | /* Rate-limit the one-line output, not the detailed output. */ | 
|  | 456 | if (show_unhandled_signals <= 1 && !printk_ratelimit()) | 
|  | 457 | return; | 
|  | 458 |  | 
|  | 459 | printk("%s%s[%d]: %s at %lx pc "REGFMT" signal %d", | 
|  | 460 | task_pid_nr(tsk) > 1 ? KERN_INFO : KERN_EMERG, | 
|  | 461 | tsk->comm, task_pid_nr(tsk), type, address, regs->pc, sig); | 
|  | 462 |  | 
|  | 463 | print_vma_addr(KERN_CONT " in ", regs->pc); | 
|  | 464 |  | 
|  | 465 | printk(KERN_CONT "\n"); | 
|  | 466 |  | 
|  | 467 | if (show_unhandled_signals > 1) { | 
|  | 468 | switch (sig) { | 
|  | 469 | case SIGILL: | 
|  | 470 | case SIGFPE: | 
|  | 471 | case SIGSEGV: | 
|  | 472 | case SIGBUS: | 
|  | 473 | pr_err("User crash: signal %d," | 
|  | 474 | " trap %ld, address 0x%lx\n", | 
|  | 475 | sig, regs->faultnum, address); | 
|  | 476 | show_regs(regs); | 
|  | 477 | dump_mem((void __user *)address); | 
|  | 478 | break; | 
|  | 479 | default: | 
|  | 480 | pr_err("User crash: signal %d, trap %ld\n", | 
|  | 481 | sig, regs->faultnum); | 
|  | 482 | break; | 
|  | 483 | } | 
|  | 484 | } | 
|  | 485 | } |