patch-2.4.20 linux-2.4.20/arch/mips64/kernel/signal32.c
Next file: linux-2.4.20/arch/mips64/kernel/smp.c
Previous file: linux-2.4.20/arch/mips64/kernel/signal.c
Back to the patch index
Back to the overall index
- Lines: 339
- Date:
Thu Nov 28 15:53:10 2002
- Orig file:
linux-2.4.19/arch/mips64/kernel/signal32.c
- Orig date:
Fri Aug 2 17:39:43 2002
diff -urN linux-2.4.19/arch/mips64/kernel/signal32.c linux-2.4.20/arch/mips64/kernel/signal32.c
@@ -31,8 +31,8 @@
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
extern asmlinkage int do_signal32(sigset_t *oldset, struct pt_regs *regs);
-extern asmlinkage int save_fp_context(struct sigcontext *sc);
-extern asmlinkage int restore_fp_context(struct sigcontext *sc);
+extern asmlinkage int (*save_fp_context)(struct sigcontext *sc);
+extern asmlinkage int (*restore_fp_context)(struct sigcontext *sc);
extern asmlinkage void syscall_trace(void);
@@ -63,38 +63,6 @@
int ss_flags;
} stack32_t;
-
-static inline int store_fp_context(struct sigcontext *sc)
-{
- unsigned int fcr0;
- int err = 0;
-
- err |= __copy_to_user(&sc->sc_fpregs[0],
- ¤t->thread.fpu.hard.fp_regs[0],
- NUM_FPU_REGS * sizeof(unsigned long));
- err |= __copy_to_user(&sc->sc_fpc_csr,
- ¤t->thread.fpu.hard.control,
- sizeof(unsigned int));
- __asm__ __volatile__("cfc1 %0, $0\n\t" : "=r" (fcr0));
- err |= __copy_to_user(&sc->sc_fpc_eir, &fcr0, sizeof(unsigned int));
-
- return err;
-}
-
-static inline int refill_fp_context(struct sigcontext *sc)
-{
- int err = 0;
-
- if (verify_area(VERIFY_READ, sc, sizeof(*sc)))
- return -EFAULT;
- err |= __copy_from_user(¤t->thread.fpu.hard.fp_regs[0],
- &sc->sc_fpregs[0],
- NUM_FPU_REGS * sizeof(unsigned long));
- err |= __copy_from_user(¤t->thread.fpu.hard.control,
- &sc->sc_fpc_csr, sizeof(unsigned int));
- return err;
-}
-
extern void __put_sigset_unknown_nsig(void);
extern void __get_sigset_unknown_nsig(void);
@@ -289,6 +257,54 @@
return ret;
}
+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.
+ */
+
+#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 asmlinkage int restore_sigcontext(struct pt_regs *regs,
struct sigcontext *sc)
{
@@ -316,15 +332,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) {
- if (IS_FPU_OWNER()) {
- CLEAR_FPU_OWNER();
- regs->cp0_status &= ~ST0_CU1;
- }
- current->used_math = 1;
- err |= refill_fp_context(sc);
+ err |= restore_fp_context(sc);
+ goto out;
+ }
+
+ if (IS_FPU_OWNER()) {
+ /* Signal handler acquired FPU - give it back */
+ CLEAR_FPU_OWNER();
+ regs->cp0_status &= ~ST0_CU1;
+ }
+ if (current->used_math) {
+ /* Undo possible contamination of thread state */
+ err |= restore_thread_fp_context(sc);
}
+out:
return err;
}
@@ -335,13 +360,55 @@
sigset_t sf_mask;
};
-struct rt_sigframe {
+struct rt_sigframe32 {
u32 rs_ass[4]; /* argument save space for o32 */
u32 rs_code[2]; /* signal trampoline */
- struct siginfo rs_info;
+ struct siginfo32 rs_info;
struct ucontext rs_uc;
};
+static int copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from)
+{
+ int err;
+
+ if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t32)))
+ return -EFAULT;
+
+ /* If you change siginfo_t structure, please be sure
+ this code is fixed accordingly.
+ It should never copy any pad contained in the structure
+ to avoid security leaks, but must copy the generic
+ 3 ints plus the relevant union member.
+ This routine must convert siginfo from 64bit to 32bit as well
+ at the same time. */
+ err = __put_user(from->si_signo, &to->si_signo);
+ err |= __put_user(from->si_errno, &to->si_errno);
+ err |= __put_user((short)from->si_code, &to->si_code);
+ if (from->si_code < 0)
+ err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE);
+ else {
+ switch (from->si_code >> 16) {
+ case __SI_CHLD >> 16:
+ err |= __put_user(from->si_utime, &to->si_utime);
+ err |= __put_user(from->si_stime, &to->si_stime);
+ err |= __put_user(from->si_status, &to->si_status);
+ default:
+ err |= __put_user(from->si_pid, &to->si_pid);
+ err |= __put_user(from->si_uid, &to->si_uid);
+ break;
+ case __SI_FAULT >> 16:
+ err |= __put_user((long)from->si_addr, &to->si_addr);
+ break;
+ case __SI_POLL >> 16:
+ err |= __put_user(from->si_band, &to->si_band);
+ err |= __put_user(from->si_fd, &to->si_fd);
+ break;
+ /* case __SI_RT: This is not generated by the kernel as of now. */
+ }
+ }
+ return err;
+}
+
asmlinkage void sys32_sigreturn(abi64_no_regargs, struct pt_regs regs)
{
struct sigframe *frame;
@@ -369,7 +436,7 @@
syscall_trace();
__asm__ __volatile__(
"move\t$29, %0\n\t"
- "j\to32_ret_from_sys_call"
+ "j\tret_from_sys_call"
:/* no outputs */
:"r" (®s));
/* Unreached */
@@ -380,11 +447,11 @@
asmlinkage void sys32_rt_sigreturn(abi64_no_regargs, struct pt_regs regs)
{
- struct rt_sigframe *frame;
+ struct rt_sigframe32 *frame;
sigset_t set;
stack_t st;
- frame = (struct rt_sigframe *) regs.regs[29];
+ frame = (struct rt_sigframe32 *) regs.regs[29];
if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
goto badframe;
if (__copy_from_user(&set, &frame->rs_uc.uc_sigmask, sizeof(set)))
@@ -410,7 +477,7 @@
*/
__asm__ __volatile__(
"move\t$29, %0\n\t"
- "j\to32_ret_from_sys_call"
+ "j\tret_from_sys_call"
:/* no outputs */
:"r" (®s));
/* Unreached */
@@ -422,9 +489,11 @@
static int inline setup_sigcontext(struct pt_regs *regs,
struct sigcontext *sc)
{
+ int owned_fp;
int err = 0;
err |= __put_user(regs->cp0_epc, &sc->sc_pc);
+ err |= __put_user(regs->cp0_status, &sc->sc_status);
#define save_gp_reg(i) { \
err |= __put_user(regs->regs[i], &sc->sc_regs[i]); \
@@ -445,20 +514,27 @@
err |= __put_user(regs->cp0_cause, &sc->sc_cause);
err |= __put_user(regs->cp0_badvaddr, &sc->sc_badvaddr);
- if (current->used_math) { /* fp is active. */
- if (IS_FPU_OWNER()) {
- lazy_fpu_switch(current, 0);
- CLEAR_FPU_OWNER();
- regs->cp0_status &= ~ST0_CU1;
- }
- err |= __put_user(1, &sc->sc_ownedfp);
- err |= store_fp_context(sc);
- current->used_math = 0;
- } else {
- err |= __put_user(0, &sc->sc_ownedfp);
+ owned_fp = IS_FPU_OWNER();
+ err |= __put_user(owned_fp, &sc->sc_ownedfp);
+ err |= __put_user(current->used_math, &sc->sc_used_math);
+
+ 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);
+ goto out;
}
- err |= __put_user(regs->cp0_status, &sc->sc_status);
+ /*
+ * Someone else has FPU.
+ * Copy Thread context into signal context
+ */
+ err |= save_thread_fp_context(sc);
+
+out:
return err;
}
@@ -473,6 +549,13 @@
/* 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;
@@ -547,7 +630,7 @@
struct pt_regs *regs, int signr,
sigset_t *set, siginfo_t *info)
{
- struct rt_sigframe *frame;
+ struct rt_sigframe32 *frame;
int err = 0;
frame = get_sigframe(ka, regs, sizeof(*frame));
@@ -572,8 +655,8 @@
flush_cache_sigtramp((unsigned long) frame->rs_code);
}
- /* Create siginfo. */
- err |= __copy_to_user(&frame->rs_info, info, sizeof(*info));
+ /* Convert (siginfo_t -> siginfo_t32) and copy to user. */
+ err |= copy_siginfo_to_user32(&frame->rs_info, info);
/* Create the ucontext. */
err |= __put_user(0, &frame->rs_uc.uc_flags);
@@ -598,7 +681,7 @@
* a2 = pointer to ucontext
*
* $25 and c0_epc point to the signal handler, $29 points to
- * the struct rt_sigframe.
+ * the struct rt_sigframe32.
*/
regs->regs[ 4] = signr;
regs->regs[ 5] = (unsigned long) &frame->rs_info;
@@ -784,7 +867,7 @@
extern asmlinkage int sys_sigprocmask(int how, old_sigset_t *set,
old_sigset_t *oset);
-asmlinkage int sys32_sigprocmask(int how, old_sigset_t32 *set,
+asmlinkage int sys32_sigprocmask(int how, old_sigset_t32 *set,
old_sigset_t32 *oset)
{
old_sigset_t s;
@@ -878,7 +961,7 @@
if (set && get_sigset(&new_set, set))
return -EFAULT;
-
+
set_fs (KERNEL_DS);
ret = sys_rt_sigprocmask(how, set ? &new_set : NULL,
oset ? &old_set : NULL, sigsetsize);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)