diff options
Diffstat (limited to 'arch/mips/kernel/signal.c')
| -rw-r--r-- | arch/mips/kernel/signal.c | 143 |
1 files changed, 56 insertions, 87 deletions
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 0209c1dd1429..9202a17db8f7 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. | 8 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. |
| 9 | */ | 9 | */ |
| 10 | #include <linux/config.h> | 10 | #include <linux/config.h> |
| 11 | #include <linux/cache.h> | ||
| 11 | #include <linux/sched.h> | 12 | #include <linux/sched.h> |
| 12 | #include <linux/mm.h> | 13 | #include <linux/mm.h> |
| 13 | #include <linux/personality.h> | 14 | #include <linux/personality.h> |
| @@ -21,6 +22,7 @@ | |||
| 21 | #include <linux/unistd.h> | 22 | #include <linux/unistd.h> |
| 22 | #include <linux/compiler.h> | 23 | #include <linux/compiler.h> |
| 23 | 24 | ||
| 25 | #include <asm/abi.h> | ||
| 24 | #include <asm/asm.h> | 26 | #include <asm/asm.h> |
| 25 | #include <linux/bitops.h> | 27 | #include <linux/bitops.h> |
| 26 | #include <asm/cacheflush.h> | 28 | #include <asm/cacheflush.h> |
| @@ -29,6 +31,7 @@ | |||
| 29 | #include <asm/uaccess.h> | 31 | #include <asm/uaccess.h> |
| 30 | #include <asm/ucontext.h> | 32 | #include <asm/ucontext.h> |
| 31 | #include <asm/cpu-features.h> | 33 | #include <asm/cpu-features.h> |
| 34 | #include <asm/war.h> | ||
| 32 | 35 | ||
| 33 | #include "signal-common.h" | 36 | #include "signal-common.h" |
| 34 | 37 | ||
| @@ -36,7 +39,7 @@ | |||
| 36 | 39 | ||
| 37 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | 40 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) |
| 38 | 41 | ||
| 39 | static int do_signal(sigset_t *oldset, struct pt_regs *regs); | 42 | int do_signal(sigset_t *oldset, struct pt_regs *regs); |
| 40 | 43 | ||
| 41 | /* | 44 | /* |
| 42 | * Atomically swap in the new signal mask, and wait for a signal. | 45 | * Atomically swap in the new signal mask, and wait for a signal. |
| @@ -47,9 +50,10 @@ save_static_function(sys_sigsuspend); | |||
| 47 | __attribute_used__ noinline static int | 50 | __attribute_used__ noinline static int |
| 48 | _sys_sigsuspend(nabi_no_regargs struct pt_regs regs) | 51 | _sys_sigsuspend(nabi_no_regargs struct pt_regs regs) |
| 49 | { | 52 | { |
| 50 | sigset_t *uset, saveset, newset; | 53 | sigset_t saveset, newset; |
| 54 | sigset_t __user *uset; | ||
| 51 | 55 | ||
| 52 | uset = (sigset_t *) regs.regs[4]; | 56 | uset = (sigset_t __user *) regs.regs[4]; |
| 53 | if (copy_from_user(&newset, uset, sizeof(sigset_t))) | 57 | if (copy_from_user(&newset, uset, sizeof(sigset_t))) |
| 54 | return -EFAULT; | 58 | return -EFAULT; |
| 55 | sigdelsetmask(&newset, ~_BLOCKABLE); | 59 | sigdelsetmask(&newset, ~_BLOCKABLE); |
| @@ -75,7 +79,8 @@ save_static_function(sys_rt_sigsuspend); | |||
| 75 | __attribute_used__ noinline static int | 79 | __attribute_used__ noinline static int |
| 76 | _sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs) | 80 | _sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs) |
| 77 | { | 81 | { |
| 78 | sigset_t *unewset, saveset, newset; | 82 | sigset_t saveset, newset; |
| 83 | sigset_t __user *unewset; | ||
| 79 | size_t sigsetsize; | 84 | size_t sigsetsize; |
| 80 | 85 | ||
| 81 | /* XXX Don't preclude handling different sized sigset_t's. */ | 86 | /* XXX Don't preclude handling different sized sigset_t's. */ |
| @@ -83,7 +88,7 @@ _sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs) | |||
| 83 | if (sigsetsize != sizeof(sigset_t)) | 88 | if (sigsetsize != sizeof(sigset_t)) |
| 84 | return -EINVAL; | 89 | return -EINVAL; |
| 85 | 90 | ||
| 86 | unewset = (sigset_t *) regs.regs[4]; | 91 | unewset = (sigset_t __user *) regs.regs[4]; |
| 87 | if (copy_from_user(&newset, unewset, sizeof(newset))) | 92 | if (copy_from_user(&newset, unewset, sizeof(newset))) |
| 88 | return -EFAULT; | 93 | return -EFAULT; |
| 89 | sigdelsetmask(&newset, ~_BLOCKABLE); | 94 | sigdelsetmask(&newset, ~_BLOCKABLE); |
| @@ -147,33 +152,46 @@ asmlinkage int sys_sigaction(int sig, const struct sigaction *act, | |||
| 147 | 152 | ||
| 148 | asmlinkage int sys_sigaltstack(nabi_no_regargs struct pt_regs regs) | 153 | asmlinkage int sys_sigaltstack(nabi_no_regargs struct pt_regs regs) |
| 149 | { | 154 | { |
| 150 | const stack_t *uss = (const stack_t *) regs.regs[4]; | 155 | const stack_t __user *uss = (const stack_t __user *) regs.regs[4]; |
| 151 | stack_t *uoss = (stack_t *) regs.regs[5]; | 156 | stack_t __user *uoss = (stack_t __user *) regs.regs[5]; |
| 152 | unsigned long usp = regs.regs[29]; | 157 | unsigned long usp = regs.regs[29]; |
| 153 | 158 | ||
| 154 | return do_sigaltstack(uss, uoss, usp); | 159 | return do_sigaltstack(uss, uoss, usp); |
| 155 | } | 160 | } |
| 156 | 161 | ||
| 157 | #if PLAT_TRAMPOLINE_STUFF_LINE | 162 | /* |
| 158 | #define __tramp __attribute__((aligned(PLAT_TRAMPOLINE_STUFF_LINE))) | 163 | * Horribly complicated - with the bloody RM9000 workarounds enabled |
| 159 | #else | 164 | * the signal trampolines is moving to the end of the structure so we can |
| 160 | #define __tramp | 165 | * increase the alignment without breaking software compatibility. |
| 161 | #endif | 166 | */ |
| 162 | |||
| 163 | #ifdef CONFIG_TRAD_SIGNALS | 167 | #ifdef CONFIG_TRAD_SIGNALS |
| 164 | struct sigframe { | 168 | struct sigframe { |
| 165 | u32 sf_ass[4]; /* argument save space for o32 */ | 169 | u32 sf_ass[4]; /* argument save space for o32 */ |
| 166 | u32 sf_code[2] __tramp; /* signal trampoline */ | 170 | #if ICACHE_REFILLS_WORKAROUND_WAR |
| 167 | struct sigcontext sf_sc __tramp; | 171 | u32 sf_pad[2]; |
| 172 | #else | ||
| 173 | u32 sf_code[2]; /* signal trampoline */ | ||
| 174 | #endif | ||
| 175 | struct sigcontext sf_sc; | ||
| 168 | sigset_t sf_mask; | 176 | sigset_t sf_mask; |
| 177 | #if ICACHE_REFILLS_WORKAROUND_WAR | ||
| 178 | u32 sf_code[8] ____cacheline_aligned; /* signal trampoline */ | ||
| 179 | #endif | ||
| 169 | }; | 180 | }; |
| 170 | #endif | 181 | #endif |
| 171 | 182 | ||
| 172 | struct rt_sigframe { | 183 | struct rt_sigframe { |
| 173 | u32 rs_ass[4]; /* argument save space for o32 */ | 184 | u32 rs_ass[4]; /* argument save space for o32 */ |
| 174 | u32 rs_code[2] __tramp; /* signal trampoline */ | 185 | #if ICACHE_REFILLS_WORKAROUND_WAR |
| 175 | struct siginfo rs_info __tramp; | 186 | u32 rs_pad[2]; |
| 187 | #else | ||
| 188 | u32 rs_code[2]; /* signal trampoline */ | ||
| 189 | #endif | ||
| 190 | struct siginfo rs_info; | ||
| 176 | struct ucontext rs_uc; | 191 | struct ucontext rs_uc; |
| 192 | #if ICACHE_REFILLS_WORKAROUND_WAR | ||
| 193 | u32 rs_code[8] ____cacheline_aligned; /* signal trampoline */ | ||
| 194 | #endif | ||
| 177 | }; | 195 | }; |
| 178 | 196 | ||
| 179 | #ifdef CONFIG_TRAD_SIGNALS | 197 | #ifdef CONFIG_TRAD_SIGNALS |
| @@ -214,7 +232,7 @@ _sys_sigreturn(nabi_no_regargs struct pt_regs regs) | |||
| 214 | badframe: | 232 | badframe: |
| 215 | force_sig(SIGSEGV, current); | 233 | force_sig(SIGSEGV, current); |
| 216 | } | 234 | } |
| 217 | #endif | 235 | #endif /* CONFIG_TRAD_SIGNALS */ |
| 218 | 236 | ||
| 219 | save_static_function(sys_rt_sigreturn); | 237 | save_static_function(sys_rt_sigreturn); |
| 220 | __attribute_used__ noinline static void | 238 | __attribute_used__ noinline static void |
| @@ -260,7 +278,7 @@ badframe: | |||
| 260 | } | 278 | } |
| 261 | 279 | ||
| 262 | #ifdef CONFIG_TRAD_SIGNALS | 280 | #ifdef CONFIG_TRAD_SIGNALS |
| 263 | static void inline setup_frame(struct k_sigaction * ka, struct pt_regs *regs, | 281 | int setup_frame(struct k_sigaction * ka, struct pt_regs *regs, |
| 264 | int signr, sigset_t *set) | 282 | int signr, sigset_t *set) |
| 265 | { | 283 | { |
| 266 | struct sigframe *frame; | 284 | struct sigframe *frame; |
| @@ -270,17 +288,7 @@ static void inline setup_frame(struct k_sigaction * ka, struct pt_regs *regs, | |||
| 270 | if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) | 288 | if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) |
| 271 | goto give_sigsegv; | 289 | goto give_sigsegv; |
| 272 | 290 | ||
| 273 | /* | 291 | install_sigtramp(frame->sf_code, __NR_sigreturn); |
| 274 | * Set up the return code ... | ||
| 275 | * | ||
| 276 | * li v0, __NR_sigreturn | ||
| 277 | * syscall | ||
| 278 | */ | ||
| 279 | if (PLAT_TRAMPOLINE_STUFF_LINE) | ||
| 280 | __clear_user(frame->sf_code, PLAT_TRAMPOLINE_STUFF_LINE); | ||
| 281 | err |= __put_user(0x24020000 + __NR_sigreturn, frame->sf_code + 0); | ||
| 282 | err |= __put_user(0x0000000c , frame->sf_code + 1); | ||
| 283 | flush_cache_sigtramp((unsigned long) frame->sf_code); | ||
| 284 | 292 | ||
| 285 | err |= setup_sigcontext(regs, &frame->sf_sc); | 293 | err |= setup_sigcontext(regs, &frame->sf_sc); |
| 286 | err |= __copy_to_user(&frame->sf_mask, set, sizeof(*set)); | 294 | err |= __copy_to_user(&frame->sf_mask, set, sizeof(*set)); |
| @@ -309,14 +317,15 @@ static void inline setup_frame(struct k_sigaction * ka, struct pt_regs *regs, | |||
| 309 | current->comm, current->pid, | 317 | current->comm, current->pid, |
| 310 | frame, regs->cp0_epc, frame->regs[31]); | 318 | frame, regs->cp0_epc, frame->regs[31]); |
| 311 | #endif | 319 | #endif |
| 312 | return; | 320 | return 1; |
| 313 | 321 | ||
| 314 | give_sigsegv: | 322 | give_sigsegv: |
| 315 | force_sigsegv(signr, current); | 323 | force_sigsegv(signr, current); |
| 324 | return 0; | ||
| 316 | } | 325 | } |
| 317 | #endif | 326 | #endif |
| 318 | 327 | ||
| 319 | static void inline setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, | 328 | int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, |
| 320 | int signr, sigset_t *set, siginfo_t *info) | 329 | int signr, sigset_t *set, siginfo_t *info) |
| 321 | { | 330 | { |
| 322 | struct rt_sigframe *frame; | 331 | struct rt_sigframe *frame; |
| @@ -326,17 +335,7 @@ static void inline setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, | |||
| 326 | if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) | 335 | if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame))) |
| 327 | goto give_sigsegv; | 336 | goto give_sigsegv; |
| 328 | 337 | ||
| 329 | /* | 338 | install_sigtramp(frame->rs_code, __NR_rt_sigreturn); |
| 330 | * Set up the return code ... | ||
| 331 | * | ||
| 332 | * li v0, __NR_rt_sigreturn | ||
| 333 | * syscall | ||
| 334 | */ | ||
| 335 | if (PLAT_TRAMPOLINE_STUFF_LINE) | ||
| 336 | __clear_user(frame->rs_code, PLAT_TRAMPOLINE_STUFF_LINE); | ||
| 337 | err |= __put_user(0x24020000 + __NR_rt_sigreturn, frame->rs_code + 0); | ||
| 338 | err |= __put_user(0x0000000c , frame->rs_code + 1); | ||
| 339 | flush_cache_sigtramp((unsigned long) frame->rs_code); | ||
| 340 | 339 | ||
| 341 | /* Create siginfo. */ | 340 | /* Create siginfo. */ |
| 342 | err |= copy_siginfo_to_user(&frame->rs_info, info); | 341 | err |= copy_siginfo_to_user(&frame->rs_info, info); |
| @@ -378,18 +377,21 @@ static void inline setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, | |||
| 378 | current->comm, current->pid, | 377 | current->comm, current->pid, |
| 379 | frame, regs->cp0_epc, regs->regs[31]); | 378 | frame, regs->cp0_epc, regs->regs[31]); |
| 380 | #endif | 379 | #endif |
| 381 | return; | 380 | return 1; |
| 382 | 381 | ||
| 383 | give_sigsegv: | 382 | give_sigsegv: |
| 384 | force_sigsegv(signr, current); | 383 | force_sigsegv(signr, current); |
| 384 | return 0; | ||
| 385 | } | 385 | } |
| 386 | 386 | ||
| 387 | extern void setup_rt_frame_n32(struct k_sigaction * ka, | 387 | extern void setup_rt_frame_n32(struct k_sigaction * ka, |
| 388 | struct pt_regs *regs, int signr, sigset_t *set, siginfo_t *info); | 388 | struct pt_regs *regs, int signr, sigset_t *set, siginfo_t *info); |
| 389 | 389 | ||
| 390 | static inline void handle_signal(unsigned long sig, siginfo_t *info, | 390 | static inline int handle_signal(unsigned long sig, siginfo_t *info, |
| 391 | struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) | 391 | struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) |
| 392 | { | 392 | { |
| 393 | int ret; | ||
| 394 | |||
| 393 | switch(regs->regs[0]) { | 395 | switch(regs->regs[0]) { |
| 394 | case ERESTART_RESTARTBLOCK: | 396 | case ERESTART_RESTARTBLOCK: |
| 395 | case ERESTARTNOHAND: | 397 | case ERESTARTNOHAND: |
| @@ -408,22 +410,10 @@ static inline void handle_signal(unsigned long sig, siginfo_t *info, | |||
| 408 | 410 | ||
| 409 | regs->regs[0] = 0; /* Don't deal with this again. */ | 411 | regs->regs[0] = 0; /* Don't deal with this again. */ |
| 410 | 412 | ||
| 411 | #ifdef CONFIG_TRAD_SIGNALS | 413 | if (sig_uses_siginfo(ka)) |
| 412 | if (ka->sa.sa_flags & SA_SIGINFO) { | 414 | ret = current->thread.abi->setup_rt_frame(ka, regs, sig, oldset, info); |
| 413 | #else | ||
| 414 | if (1) { | ||
| 415 | #endif | ||
| 416 | #ifdef CONFIG_MIPS32_N32 | ||
| 417 | if ((current->thread.mflags & MF_ABI_MASK) == MF_N32) | ||
| 418 | setup_rt_frame_n32 (ka, regs, sig, oldset, info); | ||
| 419 | else | ||
| 420 | #endif | ||
| 421 | setup_rt_frame(ka, regs, sig, oldset, info); | ||
| 422 | } | ||
| 423 | #ifdef CONFIG_TRAD_SIGNALS | ||
| 424 | else | 415 | else |
| 425 | setup_frame(ka, regs, sig, oldset); | 416 | ret = current->thread.abi->setup_frame(ka, regs, sig, oldset); |
| 426 | #endif | ||
| 427 | 417 | ||
| 428 | spin_lock_irq(¤t->sighand->siglock); | 418 | spin_lock_irq(¤t->sighand->siglock); |
| 429 | sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); | 419 | sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); |
| @@ -431,23 +421,16 @@ static inline void handle_signal(unsigned long sig, siginfo_t *info, | |||
| 431 | sigaddset(¤t->blocked,sig); | 421 | sigaddset(¤t->blocked,sig); |
| 432 | recalc_sigpending(); | 422 | recalc_sigpending(); |
| 433 | spin_unlock_irq(¤t->sighand->siglock); | 423 | spin_unlock_irq(¤t->sighand->siglock); |
| 434 | } | ||
| 435 | 424 | ||
| 436 | extern int do_signal32(sigset_t *oldset, struct pt_regs *regs); | 425 | return ret; |
| 437 | extern int do_irix_signal(sigset_t *oldset, struct pt_regs *regs); | 426 | } |
| 438 | 427 | ||
| 439 | static int do_signal(sigset_t *oldset, struct pt_regs *regs) | 428 | int do_signal(sigset_t *oldset, struct pt_regs *regs) |
| 440 | { | 429 | { |
| 441 | struct k_sigaction ka; | 430 | struct k_sigaction ka; |
| 442 | siginfo_t info; | 431 | siginfo_t info; |
| 443 | int signr; | 432 | int signr; |
| 444 | 433 | ||
| 445 | #ifdef CONFIG_BINFMT_ELF32 | ||
| 446 | if ((current->thread.mflags & MF_ABI_MASK) == MF_O32) { | ||
| 447 | return do_signal32(oldset, regs); | ||
| 448 | } | ||
| 449 | #endif | ||
| 450 | |||
| 451 | /* | 434 | /* |
| 452 | * We want the common case to go fast, which is why we may in certain | 435 | * We want the common case to go fast, which is why we may in certain |
| 453 | * cases get here from kernel mode. Just return without doing anything | 436 | * cases get here from kernel mode. Just return without doing anything |
| @@ -463,10 +446,8 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs) | |||
| 463 | oldset = ¤t->blocked; | 446 | oldset = ¤t->blocked; |
| 464 | 447 | ||
| 465 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | 448 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); |
| 466 | if (signr > 0) { | 449 | if (signr > 0) |
| 467 | handle_signal(signr, &info, &ka, oldset, regs); | 450 | return handle_signal(signr, &info, &ka, oldset, regs); |
| 468 | return 1; | ||
| 469 | } | ||
| 470 | 451 | ||
| 471 | no_signal: | 452 | no_signal: |
| 472 | /* | 453 | /* |
| @@ -499,18 +480,6 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, sigset_t *oldset, | |||
| 499 | { | 480 | { |
| 500 | /* deal with pending signal delivery */ | 481 | /* deal with pending signal delivery */ |
| 501 | if (thread_info_flags & _TIF_SIGPENDING) { | 482 | if (thread_info_flags & _TIF_SIGPENDING) { |
| 502 | #ifdef CONFIG_BINFMT_ELF32 | 483 | current->thread.abi->do_signal(oldset, regs); |
| 503 | if (likely((current->thread.mflags & MF_ABI_MASK) == MF_O32)) { | ||
| 504 | do_signal32(oldset, regs); | ||
| 505 | return; | ||
| 506 | } | ||
| 507 | #endif | ||
| 508 | #ifdef CONFIG_BINFMT_IRIX | ||
| 509 | if (unlikely(current->personality != PER_LINUX)) { | ||
| 510 | do_irix_signal(oldset, regs); | ||
| 511 | return; | ||
| 512 | } | ||
| 513 | #endif | ||
| 514 | do_signal(oldset, regs); | ||
| 515 | } | 484 | } |
| 516 | } | 485 | } |
