--- zzzz-none-000/linux-2.4.17/arch/mips/kernel/signal.c 2001-09-09 17:43:01.000000000 +0000 +++ sangam-fb-322/linux-2.4.17/arch/mips/kernel/signal.c 2004-11-24 13:22:35.000000000 +0000 @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ #include #include +#include #include #include #include @@ -59,6 +61,11 @@ switch (from->si_code >> 16) { case __SI_FAULT >> 16: break; + case __SI_TIMER >> 16: + err |= __put_user(from->si_tid, &to->si_tid); + err |= __put_user(from->si_overrun, &to->si_overrun); + err |= __put_user(from->si_ptr, &to->si_ptr); + break; case __SI_CHLD >> 16: err |= __put_user(from->si_utime, &to->si_utime); err |= __put_user(from->si_stime, &to->si_stime); @@ -76,8 +83,7 @@ * Atomically swap in the new signal mask, and wait for a signal. */ save_static_function(sys_sigsuspend); -static_unused int -_sys_sigsuspend(struct pt_regs regs) +static_unused int _sys_sigsuspend(struct pt_regs regs) { sigset_t *uset, saveset, newset; @@ -102,10 +108,8 @@ } } - save_static_function(sys_rt_sigsuspend); -static_unused int -_sys_rt_sigsuspend(struct pt_regs regs) +static_unused int _sys_rt_sigsuspend(struct pt_regs regs) { sigset_t *unewset, saveset, newset; size_t sigsetsize; @@ -136,8 +140,8 @@ } } -asmlinkage int -sys_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) +asmlinkage int sys_sigaction(int sig, const struct sigaction *act, + struct sigaction *oact) { struct k_sigaction new_ka, old_ka; int ret; @@ -177,8 +181,7 @@ return ret; } -asmlinkage int -sys_sigaltstack(struct pt_regs regs) +asmlinkage int sys_sigaltstack(struct pt_regs regs) { const stack_t *uss = (const stack_t *) regs.regs[4]; stack_t *uoss = (stack_t *) regs.regs[5]; @@ -187,8 +190,57 @@ return do_sigaltstack(uss, uoss, usp); } -asmlinkage int -restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) +static inline int restore_thread_fp_context(struct sigcontext *sc) +{ + u64 *pfreg = ¤t->thread.fpu.soft.regs[0]; + int err = 0; + + /* + * Copy all 32 64-bit values, for two reasons. First, the R3000 and + * R4000/MIPS32 kernels use the thread FP register storage differently, + * such that a full copy is essentially necessary to support both. + */ + +#define restore_fpr(i) \ + do { err |= __get_user(pfreg[i], &sc->sc_fpregs[i]); } while(0); + + restore_fpr( 0); restore_fpr( 1); restore_fpr( 2); restore_fpr( 3); + restore_fpr( 4); restore_fpr( 5); restore_fpr( 6); restore_fpr( 7); + restore_fpr( 8); restore_fpr( 9); restore_fpr(10); restore_fpr(11); + restore_fpr(12); restore_fpr(13); restore_fpr(14); restore_fpr(15); + restore_fpr(16); restore_fpr(17); restore_fpr(18); restore_fpr(19); + restore_fpr(20); restore_fpr(21); restore_fpr(22); restore_fpr(23); + restore_fpr(24); restore_fpr(25); restore_fpr(26); restore_fpr(27); + restore_fpr(28); restore_fpr(29); restore_fpr(30); restore_fpr(31); + + err |= __get_user(current->thread.fpu.soft.sr, &sc->sc_fpc_csr); + + return err; +} + +static inline int save_thread_fp_context(struct sigcontext *sc) +{ + u64 *pfreg = ¤t->thread.fpu.soft.regs[0]; + int err = 0; + +#define save_fpr(i) \ + do { err |= __put_user(pfreg[i], &sc->sc_fpregs[i]); } while(0) + + save_fpr( 0); save_fpr( 1); save_fpr( 2); save_fpr( 3); + save_fpr( 4); save_fpr( 5); save_fpr( 6); save_fpr( 7); + save_fpr( 8); save_fpr( 9); save_fpr(10); save_fpr(11); + save_fpr(12); save_fpr(13); save_fpr(14); save_fpr(15); + save_fpr(16); save_fpr(17); save_fpr(18); save_fpr(19); + save_fpr(20); save_fpr(21); save_fpr(22); save_fpr(23); + save_fpr(24); save_fpr(25); save_fpr(26); save_fpr(27); + save_fpr(28); save_fpr(29); save_fpr(30); save_fpr(31); + + err |= __put_user(current->thread.fpu.soft.sr, &sc->sc_fpc_csr); + + return err; +} + +static int restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) { int owned_fp; int err = 0; @@ -219,11 +271,24 @@ #undef restore_gp_reg err |= __get_user(owned_fp, &sc->sc_ownedfp); + err |= __get_user(current->used_math, &sc->sc_used_math); + if (owned_fp) { err |= restore_fp_context(sc); - last_task_used_math = current; + goto out; } + if (current == last_task_used_math) { + /* Signal handler acquired FPU - give it back */ + last_task_used_math = NULL; + regs->cp0_status &= ~ST0_CU1; + } + if (current->used_math) { + /* Undo possible contamination of thread state */ + err |= restore_thread_fp_context(sc); + } + +out: return err; } @@ -241,8 +306,7 @@ struct ucontext rs_uc; }; -asmlinkage void -sys_sigreturn(struct pt_regs regs) +asmlinkage void sys_sigreturn(struct pt_regs regs) { struct sigframe *frame; sigset_t blocked; @@ -278,8 +342,7 @@ force_sig(SIGSEGV, current); } -asmlinkage void -sys_rt_sigreturn(struct pt_regs regs) +asmlinkage void sys_rt_sigreturn(struct pt_regs regs) { struct rt_sigframe *frame; sigset_t set; @@ -320,8 +383,7 @@ force_sig(SIGSEGV, current); } -static int inline -setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc) +static int inline setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc) { int owned_fp; int err = 0; @@ -352,29 +414,46 @@ owned_fp = (current == last_task_used_math); err |= __put_user(owned_fp, &sc->sc_ownedfp); + err |= __put_user(current->used_math, &sc->sc_used_math); - if (current->used_math) { /* fp is active. */ - set_cp0_status(ST0_CU1); + if (!current->used_math) + goto out; + + /* There exists FP thread state that may be trashed by signal */ + if (owned_fp) { + /* fp is active. Save context from FPU */ err |= save_fp_context(sc); - last_task_used_math = NULL; - regs->cp0_status &= ~ST0_CU1; - current->used_math = 0; + goto out; } + /* + * Someone else has FPU. + * Copy Thread context into signal context + */ + err |= save_thread_fp_context(sc); + +out: return err; } /* * Determine which stack to use.. */ -static inline void * -get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) +static inline void * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, + size_t frame_size) { unsigned long sp; /* Default to using normal stack */ sp = regs->regs[29]; + /* + * FPU emulator may have it's own trampoline active just + * above the user stack, 16-bytes before the next lowest + * 16 byte boundary. Try to avoid trashing it. + */ + sp -= 32; + /* This is the X/Open sanctioned signal stack switching. */ if ((ka->sa.sa_flags & SA_ONSTACK) && ! on_sig_stack(sp)) sp = current->sas_ss_sp + current->sas_ss_size; @@ -382,9 +461,8 @@ return (void *)((sp - frame_size) & ALMASK); } -static void inline -setup_frame(struct k_sigaction * ka, struct pt_regs *regs, - int signr, sigset_t *set) +static void inline setup_frame(struct k_sigaction * ka, struct pt_regs *regs, + int signr, sigset_t *set) { struct sigframe *frame; int err = 0; @@ -434,8 +512,8 @@ regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; #if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=0x%p pc=0x%p ra=0x%p\n", - current->comm, current->pid, frame, regs->cp0_epc, frame->code); + printk("SIG deliver (%s:%d): sp=0x%p pc=0x%p ra=0x%p\n", current->comm, + current->pid, frame, regs->cp0_epc, frame->code); #endif return; @@ -445,9 +523,8 @@ force_sig(SIGSEGV, current); } -static void inline -setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, - int signr, sigset_t *set, siginfo_t *info) +static void inline setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs, + int signr, sigset_t *set, siginfo_t *info) { struct rt_sigframe *frame; int err = 0; @@ -510,8 +587,8 @@ regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler; #if DEBUG_SIG - printk("SIG deliver (%s:%d): sp=0x%p pc=0x%p ra=0x%p\n", - current->comm, current->pid, frame, regs->cp0_epc, frame->code); + printk("SIG deliver (%s:%d): sp=0x%p pc=0x%p ra=0x%p\n", current->comm, + current->pid, frame, regs->cp0_epc, frame->rs_code); #endif return; @@ -521,8 +598,7 @@ force_sig(SIGSEGV, current); } -static inline void -handle_signal(unsigned long sig, struct k_sigaction *ka, +static inline void handle_signal(unsigned long sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) { if (ka->sa.sa_flags & SA_SIGINFO) @@ -541,8 +617,7 @@ } } -static inline void -syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) +static inline void syscall_restart(struct pt_regs *regs, struct k_sigaction *ka) { switch(regs->regs[0]) { case ERESTARTNOHAND: