summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKanoj Sarcar <kanoj@engr.sgi.com>2000-05-11 19:21:22 +0000
committerKanoj Sarcar <kanoj@engr.sgi.com>2000-05-11 19:21:22 +0000
commitaed9bea016318b0f4cd8b3ac9aae9cb0f78526ef (patch)
tree3b9d4419011b35ae2d30b8f397331ac5d4a883e6
parentcbfdca13f959e36f8d5b5703d88bbe30f3fd23c0 (diff)
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.
-rw-r--r--arch/mips64/kernel/process.c1
-rw-r--r--arch/mips64/kernel/signal.c56
-rw-r--r--arch/mips64/kernel/signal32.c56
3 files changed, 90 insertions, 23 deletions
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],
+ &current->thread.fpu.hard.fp_regs[0], NUM_FPU_REGS *
+ sizeof(unsigned long));
+ err |= __copy_to_user(&sc->sc_fpc_csr, &current->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(&current->thread.fpu.hard.fp_regs[0],
+ &sc->sc_fpregs[0], NUM_FPU_REGS * sizeof(unsigned long));
+ err |= __copy_from_user(&current->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],
+ &current->thread.fpu.hard.fp_regs[0], NUM_FPU_REGS *
+ sizeof(unsigned long));
+ err |= __copy_to_user(&sc->sc_fpc_csr, &current->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(&current->thread.fpu.hard.fp_regs[0],
+ &sc->sc_fpregs[0], NUM_FPU_REGS * sizeof(unsigned long));
+ err |= __copy_from_user(&current->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;
}