diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-07-20 14:56:40 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1997-07-20 14:56:40 +0000 |
commit | e308faf24f68e262d92d294a01ddca7a17e76762 (patch) | |
tree | 22c47cb315811834861f013067878ff664e95abd /arch/sparc64/kernel/signal.c | |
parent | 30c6397ce63178fcb3e7963ac247f0a03132aca9 (diff) |
Sync with Linux 2.1.46.
Diffstat (limited to 'arch/sparc64/kernel/signal.c')
-rw-r--r-- | arch/sparc64/kernel/signal.c | 276 |
1 files changed, 199 insertions, 77 deletions
diff --git a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c index fe4615a6b..cfc55fc2e 100644 --- a/arch/sparc64/kernel/signal.c +++ b/arch/sparc64/kernel/signal.c @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.6 1997/05/29 12:44:48 jj Exp $ +/* $Id: signal.c,v 1.20 1997/07/14 03:10:28 davem Exp $ * arch/sparc64/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds @@ -8,6 +8,7 @@ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) */ +#include <linux/config.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/signal.h> @@ -23,6 +24,7 @@ #include <asm/svr4.h> #include <asm/pgtable.h> #include <asm/fpumacro.h> +#include <asm/uctx.h> #include <asm/smp_lock.h> #define _S(nr) (1<<((nr)-1)) @@ -38,6 +40,124 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, /* This turned off for production... */ /* #define DEBUG_SIGNALS 1 */ +/* {set, get}context() needed for 64-bit SparcLinux userland. */ +asmlinkage void sparc64_set_context(struct pt_regs *regs) +{ + struct ucontext *ucp = (struct ucontext *) regs->u_regs[UREG_I0]; + struct thread_struct *tp = ¤t->tss; + mc_gregset_t *grp; + unsigned long pc, npc, tstate; + unsigned long fp, i7; + unsigned char fenab; + + __asm__ __volatile__("flushw"); + if(tp->w_saved || + (((unsigned long)ucp) & (sizeof(unsigned long)-1)) || + (!__access_ok((unsigned long)ucp, sizeof(*ucp)))) + do_exit(SIGSEGV); + grp = &ucp->uc_mcontext.mc_gregs; + __get_user(pc, &((*grp)[MC_PC])); + __get_user(npc, &((*grp)[MC_NPC])); + if((pc | npc) & 3) + do_exit(SIGSEGV); + if(regs->u_regs[UREG_I1]) { + __get_user(current->blocked, &ucp->uc_sigmask); + current->blocked &= _BLOCKABLE; + } + regs->tpc = pc; + regs->tnpc = npc; + __get_user(regs->y, &((*grp)[MC_Y])); + __get_user(tstate, &((*grp)[MC_Y])); + regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); + regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_XCC)); + __get_user(regs->u_regs[UREG_G1], (&(*grp)[MC_G1])); + __get_user(regs->u_regs[UREG_G2], (&(*grp)[MC_G2])); + __get_user(regs->u_regs[UREG_G3], (&(*grp)[MC_G3])); + __get_user(regs->u_regs[UREG_G4], (&(*grp)[MC_G4])); + __get_user(regs->u_regs[UREG_G5], (&(*grp)[MC_G5])); + __get_user(regs->u_regs[UREG_G6], (&(*grp)[MC_G6])); + __get_user(regs->u_regs[UREG_G7], (&(*grp)[MC_G7])); + __get_user(regs->u_regs[UREG_I0], (&(*grp)[MC_O0])); + __get_user(regs->u_regs[UREG_I1], (&(*grp)[MC_O1])); + __get_user(regs->u_regs[UREG_I2], (&(*grp)[MC_O2])); + __get_user(regs->u_regs[UREG_I3], (&(*grp)[MC_O3])); + __get_user(regs->u_regs[UREG_I4], (&(*grp)[MC_O4])); + __get_user(regs->u_regs[UREG_I5], (&(*grp)[MC_O5])); + __get_user(regs->u_regs[UREG_I6], (&(*grp)[MC_O6])); + __get_user(regs->u_regs[UREG_I7], (&(*grp)[MC_O7])); + + __get_user(fp, &(ucp->uc_mcontext.mc_fp)); + __get_user(i7, &(ucp->uc_mcontext.mc_i7)); + __put_user(fp, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[6]))); + __put_user(i7, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[7]))); + + __get_user(fenab, &(ucp->uc_mcontext.mc_fpregs.mcfpu_enab)); + if(fenab) { + unsigned long *fpregs = (unsigned long *)(regs+1); + copy_from_user(fpregs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs), + (sizeof(unsigned long) * 32)); + __get_user(fpregs[32], &(ucp->uc_mcontext.mc_fpregs.mcfpu_fsr)); + __get_user(fpregs[33], &(ucp->uc_mcontext.mc_fpregs.mcfpu_gsr)); + regs->fprs = FPRS_FEF; + } +} + +asmlinkage void sparc64_get_context(struct pt_regs *regs) +{ + struct ucontext *ucp = (struct ucontext *) regs->u_regs[UREG_I0]; + struct thread_struct *tp = ¤t->tss; + mc_gregset_t *grp; + mcontext_t *mcp; + unsigned long fp, i7; + unsigned char fenab = (current->flags & PF_USEDFPU); + + synchronize_user_stack(); + if(tp->w_saved || clear_user(ucp, sizeof(*ucp))) + do_exit(SIGSEGV); + mcp = &ucp->uc_mcontext; + grp = &mcp->mc_gregs; + + /* Skip over the trap instruction, first. */ + regs->tpc = regs->tnpc; + regs->tnpc += 4; + + __put_user(current->blocked, &ucp->uc_sigmask); + __put_user(regs->tstate, &((*grp)[MC_TSTATE])); + __put_user(regs->tpc, &((*grp)[MC_PC])); + __put_user(regs->tnpc, &((*grp)[MC_NPC])); + __put_user(regs->y, &((*grp)[MC_Y])); + __put_user(regs->u_regs[UREG_G1], &((*grp)[MC_G1])); + __put_user(regs->u_regs[UREG_G2], &((*grp)[MC_G2])); + __put_user(regs->u_regs[UREG_G3], &((*grp)[MC_G3])); + __put_user(regs->u_regs[UREG_G4], &((*grp)[MC_G4])); + __put_user(regs->u_regs[UREG_G5], &((*grp)[MC_G5])); + __put_user(regs->u_regs[UREG_G6], &((*grp)[MC_G6])); + __put_user(regs->u_regs[UREG_G6], &((*grp)[MC_G7])); + __put_user(regs->u_regs[UREG_I0], &((*grp)[MC_O0])); + __put_user(regs->u_regs[UREG_I1], &((*grp)[MC_O1])); + __put_user(regs->u_regs[UREG_I2], &((*grp)[MC_O2])); + __put_user(regs->u_regs[UREG_I3], &((*grp)[MC_O3])); + __put_user(regs->u_regs[UREG_I4], &((*grp)[MC_O4])); + __put_user(regs->u_regs[UREG_I5], &((*grp)[MC_O5])); + __put_user(regs->u_regs[UREG_I6], &((*grp)[MC_O6])); + __put_user(regs->u_regs[UREG_I7], &((*grp)[MC_O7])); + + __get_user(fp, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[6]))); + __get_user(i7, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[7]))); + __put_user(fp, &(mcp->mc_fp)); + __put_user(i7, &(mcp->mc_i7)); + + __put_user(fenab, &(mcp->mc_fpregs.mcfpu_enab)); + if(fenab) { + unsigned long *fpregs = (unsigned long *)(regs+1); + copy_to_user(&(mcp->mc_fpregs.mcfpu_fregs), fpregs, + (sizeof(unsigned long) * 32)); + __put_user(fpregs[32], &(mcp->mc_fpregs.mcfpu_fsr)); + __put_user(fpregs[33], &(mcp->mc_fpregs.mcfpu_gsr)); + __put_user(FPRS_FEF, &(mcp->mc_fpregs.mcfpu_fprs)); + } +} + /* * The new signal frame, intended to be used for Linux applications only * (we have enough in there to work with clone). @@ -65,7 +185,8 @@ asmlinkage void _sigpause_common(unsigned int set, struct pt_regs *regs) #ifdef CONFIG_SPARC32_COMPAT if (current->tss.flags & SPARC_FLAG_32BIT) { - extern asmlinkage void _sigpause32_common(unsigned int, struct pt_regs *); + extern asmlinkage void _sigpause32_common(unsigned int, + struct pt_regs *); _sigpause32_common(set, regs); return; } @@ -111,22 +232,12 @@ asmlinkage void do_sigsuspend(struct pt_regs *regs) static inline void restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu) { -#ifdef __SMP__ - if (current->flags & PF_USEDFPU) - regs->tstate &= ~(TSTATE_PEF); -#else - if (current == last_task_used_math) { - last_task_used_math = 0; - regs->tstate &= ~(TSTATE_PEF); - } -#endif - current->used_math = 1; - current->flags &= ~PF_USEDFPU; - - copy_from_user(¤t->tss.float_regs[0], - &fpu->si_float_regs[0], + unsigned long *fpregs = (unsigned long *)(regs+1); + copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 64)); - __get_user(current->tss.fsr, &fpu->si_fsr); + __get_user(fpregs[32], &fpu->si_fsr); + __get_user(fpregs[33], &fpu->si_gsr); + regs->fprs = FPRS_FEF; } void do_sigreturn(struct pt_regs *regs) @@ -139,24 +250,25 @@ void do_sigreturn(struct pt_regs *regs) #ifdef CONFIG_SPARC32_COMPAT if (current->tss.flags & SPARC_FLAG_32BIT) { extern asmlinkage void do_sigreturn32(struct pt_regs *); - do_sigreturn32(regs); - return; + return do_sigreturn32(regs); } #endif synchronize_user_stack (); - sf = (struct new_signal_frame *) regs->u_regs [UREG_FP]; + sf = (struct new_signal_frame *) + (regs->u_regs [UREG_FP] + STACK_BIAS); + /* 1. Make sure we are not getting garbage from the user */ - if (verify_area (VERIFY_READ, sf, sizeof (*sf))){ + if (verify_area (VERIFY_READ, sf, sizeof (*sf))) goto segv; - } - if (((unsigned long) sf) & 3){ + + if (((unsigned long) sf) & 3) goto segv; - } + get_user(tpc, &sf->info.si_regs.tpc); __get_user(tnpc, &sf->info.si_regs.tnpc); - if ((tpc | tnpc) & 3){ + if ((tpc | tnpc) & 3) goto segv; - } + regs->tpc = tpc; regs->tnpc = tnpc; @@ -165,9 +277,9 @@ void do_sigreturn(struct pt_regs *regs) __get_user(tstate, &sf->info.si_regs.tstate); copy_from_user(regs->u_regs, sf->info.si_regs.u_regs, sizeof(regs->u_regs)); - /* User can only change condition codes and FPU enabling in %tstate. */ - regs->tstate &= ~(TSTATE_ICC | TSTATE_PEF); - regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_PEF)); + /* User can only change condition codes in %tstate. */ + regs->tstate &= ~(TSTATE_ICC); + regs->tstate |= (tstate & TSTATE_ICC); __get_user(fpu_save, &sf->fpu_save); if (fpu_save) @@ -191,27 +303,12 @@ static int invalid_frame_pointer(void *fp, int fplen) static inline void save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu) { -#ifdef __SMP__ - if (current->flags & PF_USEDFPU) { - fprs_write(FPRS_FEF); - fpsave((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); - regs->tstate &= ~(TSTATE_PEF); - current->flags &= ~(PF_USEDFPU); - } -#else - if (current == last_task_used_math) { - fprs_write(FPRS_FEF); - fpsave((unsigned long *)¤t->tss.float_regs[0], - ¤t->tss.fsr); - last_task_used_math = 0; - regs->tstate &= ~(TSTATE_PEF); - } -#endif - copy_to_user(&fpu->si_float_regs[0], ¤t->tss.float_regs[0], + unsigned long *fpregs = (unsigned long *)(regs+1); + copy_to_user(&fpu->si_float_regs[0], fpregs, (sizeof(unsigned int) * 64)); - __put_user(current->tss.fsr, &fpu->si_fsr); - current->used_math = 0; + __put_user(fpregs[32], &fpu->si_fsr); + __put_user(fpregs[33], &fpu->si_gsr); + regs->fprs = 0; } static inline void @@ -220,33 +317,29 @@ new_setup_frame(struct sigaction *sa, struct pt_regs *regs, { struct new_signal_frame *sf; int sigframe_size; - unsigned long tmp; - int i; /* 1. Make sure everything is clean */ synchronize_user_stack(); sigframe_size = NF_ALIGNEDSZ; - if (!current->used_math) + if (!(current->flags & PF_USEDFPU)) sigframe_size -= sizeof(__siginfo_fpu_t); - sf = (struct new_signal_frame *)(regs->u_regs[UREG_FP] - sigframe_size); + sf = (struct new_signal_frame *) + (regs->u_regs[UREG_FP] + STACK_BIAS - sigframe_size); - if (invalid_frame_pointer (sf, sigframe_size)){ - lock_kernel (); - do_exit(SIGILL); - } + if (invalid_frame_pointer (sf, sigframe_size)) + goto sigill; - if (current->tss.w_saved != 0){ + if (current->tss.w_saved != 0) { printk ("%s[%d]: Invalid user stack frame for " "signal delivery.\n", current->comm, current->pid); - lock_kernel (); - do_exit (SIGILL); + goto sigill; } /* 2. Save the current process state */ copy_to_user(&sf->info.si_regs, regs, sizeof (*regs)); - if (current->used_math) { + if (current->flags & PF_USEDFPU) { save_fpu_state(regs, &sf->fpu_state); __put_user((u64)&sf->fpu_state, &sf->fpu_save); } else { @@ -254,17 +347,17 @@ new_setup_frame(struct sigaction *sa, struct pt_regs *regs, } __put_user(oldmask, &sf->info.si_mask); - for (i = 0; i < sizeof(struct reg_window)/8; i++) { - __get_user(tmp, (((u64 *)regs->u_regs[UREG_FP])+i)); - __put_user(tmp, (((u64 *)sf)+i)); - } + + copy_in_user((u64 *)sf, + (u64 *)(regs->u_regs[UREG_FP]+STACK_BIAS), + sizeof(struct reg_window)); /* 3. return to kernel instructions */ __put_user(0x821020d8, &sf->insns[0]); /* mov __NR_sigreturn, %g1 */ __put_user(0x91d02011, &sf->insns[1]); /* t 0x11 */ /* 4. signal handler back-trampoline and parameters */ - regs->u_regs[UREG_FP] = (unsigned long) sf; + regs->u_regs[UREG_FP] = ((unsigned long) sf) - STACK_BIAS; regs->u_regs[UREG_I0] = signo; regs->u_regs[UREG_I1] = (unsigned long) &sf->info; regs->u_regs[UREG_I7] = (unsigned long) (&(sf->insns[0]) - 2); @@ -274,15 +367,27 @@ new_setup_frame(struct sigaction *sa, struct pt_regs *regs, regs->tnpc = (regs->tpc + 4); /* Flush instruction space. */ - __asm__ __volatile__(" - membar #StoreStore - stxa %%g0, [%0] %2 - stxa %%g0, [%1] %2 - flush %%g4 - " : /* no outputs */ - : "r" (((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)), - "r" ((((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)) + PAGE_SIZE), - "i" (ASI_IC_TAG)); + { + unsigned long address = ((unsigned long)&(sf->insns[0])); + pgd_t *pgdp = pgd_offset(current->mm, address); + pmd_t *pmdp = pmd_offset(pgdp, address); + pte_t *ptep = pte_offset(pmdp, address); + + if(pte_present(*ptep)) { + unsigned long page = pte_page(*ptep); + + __asm__ __volatile__(" + membar #StoreStore + flush %0 + %1" + : : "r" (page), "r" (address & (PAGE_SIZE - 1)) + : "memory"); + } + } + return; + +sigill: + lock_kernel(); + do_exit(SIGILL); } static inline void handle_signal(unsigned long signr, struct sigaction *sa, @@ -291,8 +396,11 @@ static inline void handle_signal(unsigned long signr, struct sigaction *sa, new_setup_frame(sa, regs, signr, oldmask); if(sa->sa_flags & SA_ONESHOT) sa->sa_handler = NULL; - if(!(sa->sa_flags & SA_NOMASK)) + if(!(sa->sa_flags & SA_NOMASK)) { + spin_lock_irq(¤t->sigmask_lock); current->blocked |= (sa->sa_mask | _S(signr)) & _BLOCKABLE; + spin_unlock_irq(¤t->sigmask_lock); + } } static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs, @@ -334,7 +442,11 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, #endif while ((signr = current->signal & mask) != 0) { signr = ffz(~signr); - clear_bit(signr + 32, ¤t->signal); + + spin_lock_irq(¤t->sigmask_lock); + current->signal &= ~(1 << signr); + spin_unlock_irq(¤t->sigmask_lock); + sa = current->sig->action + signr; signr++; if ((current->flags & PF_PTRACED) && signr != SIGKILL) { @@ -348,7 +460,9 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, if (signr == SIGSTOP) continue; if (_S(signr) & current->blocked) { + spin_lock_irq(¤t->sigmask_lock); current->signal |= _S(signr); + spin_unlock_irq(¤t->sigmask_lock); continue; } sa = current->sig->action + signr - 1; @@ -391,8 +505,10 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, case SIGQUIT: case SIGILL: case SIGTRAP: case SIGABRT: case SIGFPE: case SIGSEGV: case SIGBUS: if(current->binfmt && current->binfmt->core_dump) { + lock_kernel(); if(current->binfmt->core_dump(signr, regs)) signr |= 0x80; + unlock_kernel(); } #ifdef DEBUG_SIGNALS /* Very useful to debug dynamic linker problems */ @@ -401,9 +517,15 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs, #endif /* fall through */ default: + spin_lock_irq(¤t->sigmask_lock); current->signal |= _S(signr & 0x7f); + spin_unlock_irq(¤t->sigmask_lock); + current->flags |= PF_SIGNALED; + + lock_kernel(); do_exit(signr); + unlock_kernel(); } } if(restart_syscall) |