From aed9bea016318b0f4cd8b3ac9aae9cb0f78526ef Mon Sep 17 00:00:00 2001 From: Kanoj Sarcar Date: Thu, 11 May 2000 19:21:22 +0000 Subject: When last_task_used_math gets the cpu, ST0_CU1 is set during context switch. All other FP programs have ST0_CU1 cleared in their thread_struct, so that they incur faults on first touching the FPU, and are made the FPU owner, aka last_task_used_math. used_math determines whether a thread has used the FPU before; if not, the FPU needs to be initialized before it can own the FPU. Note: a FP program that has already been using the fpu (not neccesarily the owner), must reinit the fpu if it uses the fpu in a signal handler; a sigreturn restores the original context, discarding the context of the handler. --- arch/mips64/kernel/process.c | 1 - arch/mips64/kernel/signal.c | 56 ++++++++++++++++++++++++++++++++++--------- arch/mips64/kernel/signal32.c | 56 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 90 insertions(+), 23 deletions(-) (limited to 'arch/mips64') diff --git a/arch/mips64/kernel/process.c b/arch/mips64/kernel/process.c index c2c077e35..d6d8b7435 100644 --- a/arch/mips64/kernel/process.c +++ b/arch/mips64/kernel/process.c @@ -78,7 +78,6 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, childksp = (unsigned long)p + KERNEL_STACK_SIZE - 32; if (last_task_used_math == current) { - set_cp0_status(ST0_CU1, ST0_CU1); save_fp(p); } /* set up new TSS. */ diff --git a/arch/mips64/kernel/signal.c b/arch/mips64/kernel/signal.c index 7823efb3b..40a727831 100644 --- a/arch/mips64/kernel/signal.c +++ b/arch/mips64/kernel/signal.c @@ -37,6 +37,35 @@ extern asmlinkage int do_signal(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); +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; +} + /* * Atomically swap in the new signal mask, and wait for a signal. */ @@ -180,8 +209,12 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) err |= __get_user(owned_fp, &sc->sc_ownedfp); if (owned_fp) { - err |= restore_fp_context(sc); - last_task_used_math = current; + if (current == last_task_used_math) { + last_task_used_math = 0; + regs->cp0_status &= ~ST0_CU1; + } + current->used_math = 1; + err |= refill_fp_context(sc); } return err; @@ -281,11 +314,9 @@ badframe: 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]); \ @@ -306,16 +337,19 @@ setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc) err |= __put_user(regs->cp0_cause, &sc->sc_cause); err |= __put_user(regs->cp0_badvaddr, &sc->sc_badvaddr); - owned_fp = (current == last_task_used_math); - err |= __put_user(owned_fp, &sc->sc_ownedfp); - if (current->used_math) { /* fp is active. */ - set_cp0_status(ST0_CU1, ST0_CU1); - err |= save_fp_context(sc); - last_task_used_math = NULL; - regs->cp0_status &= ~ST0_CU1; + if (current == last_task_used_math) { + lazy_fpu_switch(current, 0); + last_task_used_math = NULL; + 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); } + err |= __put_user(regs->cp0_status, &sc->sc_status); return err; } diff --git a/arch/mips64/kernel/signal32.c b/arch/mips64/kernel/signal32.c index 5a1ee45b0..4addc707c 100644 --- a/arch/mips64/kernel/signal32.c +++ b/arch/mips64/kernel/signal32.c @@ -64,6 +64,35 @@ typedef struct sigaltstack32 { } 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; +} + /* * Atomically swap in the new signal mask, and wait for a signal. */ @@ -210,8 +239,12 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) err |= __get_user(owned_fp, &sc->sc_ownedfp); if (owned_fp) { - err |= restore_fp_context(sc); - last_task_used_math = current; + if (current == last_task_used_math) { + last_task_used_math = 0; + regs->cp0_status &= ~ST0_CU1; + } + current->used_math = 1; + err |= refill_fp_context(sc); } return err; @@ -316,11 +349,9 @@ badframe: 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]); \ @@ -341,16 +372,19 @@ setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc) err |= __put_user(regs->cp0_cause, &sc->sc_cause); err |= __put_user(regs->cp0_badvaddr, &sc->sc_badvaddr); - owned_fp = (current == last_task_used_math); - err |= __put_user(owned_fp, &sc->sc_ownedfp); - if (current->used_math) { /* fp is active. */ - set_cp0_status(ST0_CU1, ST0_CU1); - err |= save_fp_context(sc); - last_task_used_math = NULL; - regs->cp0_status &= ~ST0_CU1; + if (current == last_task_used_math) { + lazy_fpu_switch(current, 0); + last_task_used_math = NULL; + 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); } + err |= __put_user(regs->cp0_status, &sc->sc_status); return err; } -- cgit v1.2.3