Skip to contentSkip to conversation

FP emulator patch

PATCH Carsten Langgaard 4 participants 7 messages Aug 15, 2001 – Aug 16, 2001 MIPS

lore
Share & tools
lore
#0 lore raw

There has been some reports regarding FP emulator failures, which the attached patch should solve.
The patch include a fix for emulation of instructions in a COP1 delay-slot, a fix for FP context switching and some additional stuff , which was needed to pass our torture test.

Ralf could you please apply this patch.

/Carsten

--

_    _ ____  ___   Carsten Langgaard   Mailto:[email protected]
|\  /|||___)(___   MIPS Denmark        Direct: +45 4486 5527
| \/ |||    ____)  Lautrupvang 4B      Switch: +45 4486 5555
  TECHNOLOGIES     2750 Ballerup       Fax...: +45 4486 5556
                   Denmark             http://www.mips.com

Index: linux/arch/mips/kernel/signal.c
===================================================================
RCS file: /cvs/linux/arch/mips/kernel/signal.c,v
retrieving revision 1.36

arch/mips/kernel/signal.c +11 -3
diff -u -r1.36 signal.c
@@ -220,6 +220,8 @@
 
 	err |= __get_user(owned_fp, &sc->sc_ownedfp);
 	if (owned_fp) {
+		if (last_task_used_math && (last_task_used_math != current))
+			last_task_used_math->thread.cp0_status &= ~ST0_CU1;
 		err |= restore_fp_context(sc);
 		last_task_used_math = current;
 	}
@@ -353,12 +355,11 @@
 	owned_fp = (current == last_task_used_math);
 	err |= __put_user(owned_fp, &sc->sc_ownedfp);
 
-	if (current->used_math) {	/* fp is active.  */
+	if (owned_fp) { /* fp is active.  */
 		set_cp0_status(ST0_CU1);
 		err |= save_fp_context(sc);
 		last_task_used_math = NULL;
 		regs->cp0_status &= ~ST0_CU1;
-		current->used_math = 0;
 	}
 
 	return err;
@@ -375,6 +376,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;
@@ -435,7 +443,7 @@
 
 #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);
+	       current->comm, current->pid, frame, regs->cp0_epc, frame->sf_code);
 #endif
         return;
 

Index: linux/arch/mips/kernel/unaligned.c
===================================================================
RCS file: /cvs/linux/arch/mips/kernel/unaligned.c,v retrieving revision 1.12

arch/mips/kernel/unaligned.c +3 -8
diff -u -r1.12 unaligned.c
@@ -365,15 +365,12 @@
 		return;
 	}
 
-	die_if_kernel ("Unhandled kernel unaligned access", regs);
 	send_sig(SIGSEGV, current, 1);
 	return;
 sigbus:
-	die_if_kernel ("Unhandled kernel unaligned access", regs);
 	send_sig(SIGBUS, current, 1);
 	return;
 sigill:
-	die_if_kernel ("Unhandled kernel unaligned access or invalid instruction", regs);
 	send_sig(SIGILL, current, 1);
 	return;
 }
@@ -391,11 +388,10 @@
 	 * of the CPU after executing the instruction
 	 * in the delay slot of an emulated branch.
 	 */
+	/* Terminate if exception was recognized as a delay slot return */
+	if(do_dsemulret(regs)) return;
 
-	if ((unsigned long)regs->cp0_epc == current->thread.dsemul_aerpc) {
-		do_dsemulret(regs);
-		return;
-	}
+	/* Otherwise handle as normal */
 
 	/*
 	 * Did we catch a fault trying to load an instruction?
@@ -417,7 +413,6 @@
 	return;
 
 sigbus:
-	die_if_kernel ("Kernel unaligned instruction access", regs);
 	force_sig(SIGBUS, current);
 
 	return;

Index: linux/arch/mips/math-emu/cp1emu.c
===================================================================
RCS file: /cvs/linux/arch/mips/math-emu/cp1emu.c,v retrieving revision 1.7

arch/mips/math-emu/cp1emu.c +80 -31

165 lines not on this page · raw message

Index: linux/arch/mips/math-emu/kernel_linkage.c
===================================================================
RCS file: /cvs/linux/arch/mips/math-emu/kernel_linkage.c,v retrieving revision 1.3

arch/mips/math-emu/kernel_linkage.c +2 -2
diff -u -r1.3 kernel_linkage.c
@@ -3,7 +3,7 @@
  *  arch/mips/math_emu/kernel_linkage.c
  *
  *  Kevin D. Kissell, kevink@mips and Carsten Langgaard, [email protected]
- *  Copyright (C) 2000 MIPS Technologies, Inc.  All rights reserved.
+ *  Copyright (C) 2000-2001 MIPS Technologies, Inc.  All rights reserved.
  *
  * ########################################################################
  *
@@ -45,7 +45,7 @@
  
 	if (first) {
 		first = 0;
-		printk("Algorithmics/MIPS FPU Emulator v1.4\n");
+		printk("Algorithmics/MIPS FPU Emulator v1.5\n");
 	}
 
 	current->thread.fpu.soft.sr = 0;

Index: linux/arch/mips/tools/offset.c
===================================================================
RCS file: /cvs/linux/arch/mips/tools/offset.c,v
retrieving revision 1.16

arch/mips/tools/offset.c +0 -4
diff -u -r1.16 offset.c
@@ -121,10 +121,6 @@
 	       thread.irix_trampoline);
 	offset("#define THREAD_OLDCTX  ", struct task_struct, \
 	       thread.irix_oldctx);
-	offset("#define THREAD_DSEEPC  ", struct task_struct, \
-	       thread.dsemul_epc);
-	offset("#define THREAD_DSEAERPC ", struct task_struct, \
-	       thread.dsemul_aerpc);
 	linefeed;
 }
 

Index: linux/include/asm-mips/processor.h
===================================================================
RCS file: /cvs/linux/include/asm-mips/processor.h,v retrieving revision 1.37

include/asm-mips/processor.h +2 -22
diff -u -r1.37 processor.h
@@ -151,21 +151,6 @@
 	mm_segment_t current_ds;
 	unsigned long irix_trampoline;  /* Wheee... */
 	unsigned long irix_oldctx;
-
-	/*
-	 * These are really only needed if the full FPU emulator is configured.
-	 * Would be made conditional on MIPS_FPU_EMULATOR if it weren't for the
-	 * fact that having offset.h rebuilt differently for different config
-	 * options would be asking for trouble.
-	 *
-	 * Saved EPC during delay-slot emulation (see math-emu/cp1emu.c)
-	 */
-	unsigned long dsemul_epc;
-
-	/*
-	 * Pointer to instruction used to induce address error
-	 */
-	unsigned long dsemul_aerpc;
 };
 
 #endif /* !defined (_LANGUAGE_ASSEMBLY) */
@@ -195,11 +180,6 @@
 	 * For now the default is to fix address errors \
 	 */ \
 	MF_FIXADE, { 0 }, 0, 0, \
-	/* \
-	 * dsemul_epc and dsemul_aerpc should never be used uninitialized, \
-	 * but... \
-	 */ \
-	0 ,0 \
 }
 
 #ifdef __KERNEL__
@@ -235,8 +215,8 @@
  * Do necessary setup to start up a newly executed thread.
  */
 #define start_thread(regs, new_pc, new_sp) do {				\
-	/* New thread looses kernel privileges. */			\
-	regs->cp0_status = (regs->cp0_status & ~(ST0_CU0|ST0_KSU)) | KU_USER;\
+	/* New thread loses kernel and FPU privileges. */               \
+        regs->cp0_status = (regs->cp0_status & ~(ST0_CU0|ST0_KSU|ST0_CU1)) | KU_USER;\
 	regs->cp0_epc = new_pc;						\
 	regs->regs[29] = new_sp;					\
 	current->thread.current_ds = USER_DS;				\
#1 lore raw
Quoted 7 lines · Carsten Langgaard

There has been some reports regarding FP emulator failures, which the attached patch should solve.
The patch include a fix for emulation of instructions in a COP1 delay-slot, a fix for FP context switching and some additional stuff , which was needed to pass our torture test.

Ralf could you please apply this patch.

Two comments, especially since parts of this seem to be the patch I posted here over a month ago.

Quoted 6 lines

Index: linux/arch/mips/kernel/signal.c

arch/mips/kernel/signal.c +0 -0
@@ -353,12 +355,11 @@
 	owned_fp = (current == last_task_used_math);
 	err |= __put_user(owned_fp, &sc->sc_ownedfp);
 

arch/mips/kernel/signal.c line 365

Quoted 10 lines
arch/mips/kernel/signal.c +1 -2
-	if (current->used_math) {	/* fp is active.  */
+	if (owned_fp) { /* fp is active.  */
 		set_cp0_status(ST0_CU1);
 		err |= save_fp_context(sc);
 		last_task_used_math = NULL;
 		regs->cp0_status &= ~ST0_CU1;
-		current->used_math = 0;
 	}
 
 	return err;

This is absolutely not right. It's righter than the status quo. If we don't own the FP, you don't save the FP. Then we can use FP in the signal handler, corrupting the process's original floating point context.

Quoted 4 lines

Index: linux/include/asm-mips/processor.h

include/asm-mips/processor.h line 222

Quoted 10 lines
include/asm-mips/processor.h +2 -2
@@ -235,8 +215,8 @@
  * Do necessary setup to start up a newly executed thread.
  */
 #define start_thread(regs, new_pc, new_sp) do {				\
-	/* New thread looses kernel privileges. */			\
-	regs->cp0_status = (regs->cp0_status & ~(ST0_CU0|ST0_KSU)) | KU_USER;\
+	/* New thread loses kernel and FPU privileges. */               \
+        regs->cp0_status = (regs->cp0_status & ~(ST0_CU0|ST0_KSU|ST0_CU1)) | KU_USER;\
 	regs->cp0_epc = new_pc;						\
 	regs->regs[29] = new_sp;					\
 	current->thread.current_ds = USER_DS;				\

I could be misremembering, but I believe that Ralf said this should be unnecessary and the problem was somewhere else. On the other hand, I still think it's a good idea.

Signature · 2 lines
Daniel Jacobowitz                           Carnegie Mellon University
MontaVista Software                         Debian GNU/Linux Developer
#2 lore raw
Quoted 18 lines

Two comments, especially since parts of this seem to be the patch I posted here over a month ago.

Quoted 15 lines

Index: linux/arch/mips/kernel/signal.c

arch/mips/kernel/signal.c +1 -2
@@ -353,12 +355,11 @@
 owned_fp = (current == last_task_used_math);
 err |= __put_user(owned_fp, &sc->sc_ownedfp);
 
- if (current->used_math) { /* fp is active.  */
+ if (owned_fp) { /* fp is active.  */
 set_cp0_status(ST0_CU1);
 err |= save_fp_context(sc);
 last_task_used_math = NULL;
 regs->cp0_status &= ~ST0_CU1;
- current->used_math = 0;
 }
 
Quoted 6 lines

arch/mips/kernel/signal.c line 365

Quoted 1 line
arch/mips/kernel/signal.c +0 -0
 return err;

This is absolutely not right. It's righter than the status quo. If we don't own the FP, you don't save the FP. Then we can use FP in the signal handler, corrupting the process's original floating point context.

I only worked on the FP branch emulation fix, and hadn't looked
at the stack signal context problem until now. If the current thread
has any FPU state, it needs to be saved ahead of signal delivery for the reasons you suggest above. I assume that's the reason for the previous "if (current->used_math)" condition. If there's been a problem with FP register corruption in the face of signals and context switches, it's presumably because, while we're saving the state in the sigcontext so that it will be restored on the signal return, we're also clearing last_task_used_math and curren->used_math. The former means that if
we invoke the signal handler, it returns, and we will restore the FP context to the register file. But if the signal handler *doesn't* do any FP, we've got a "dirty" FPU and no owner for the state, and Bad Things happen. And the way the current code is written, if the signal handler does happen to acquire the FPU, the thread inherits the enable, the register contents, and an obligation to save the FPU state uselessly on the next context switch.

I don't understand why there is any manipulation of the FPU ownership status going on in setup_sigcontext(), nor any persistent modification of the Cp0.Status state. Shouldn't the code in setup_sigcontext() look more like:

    if(owned_fp) {
        /* Not clear to me how we could have owned_fp 
           true with CU1 off.  Double check this... */
        someaccursednewtemp = regs->cp0_status;
        set_cp0_status(ST0_CU1);
        err |= save_fp_context(sc);
        regs->cp0_status = someaccursednewtemp;
    }

Then, in the symmetric code fragment in restore_sigcontext()

    if(owned_fp) {
        err |= restore_fp_context(sc);
    } else {
        if (current == last_task_used_math) {
        /* signal handler acquired FPU - give it back */
            last_task_used_math = NULL;
            current->used_math = 0;
            clear_cp0_status(ST0_CU1);
        }
    }

Disclaimer: The above was typed into Outlook Express and may contain typos, but I think it expresses the idea well enough for someone to tell me if I'm completely off base.

Regards,

Kevin K.

#3 lore raw
In reply to Daniel Jacobowitz
Quoted 16 lines
Quoted 16 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line · Daniel Jacobowitz

Index: linux/arch/mips/kernel/signal.c

(quoted diff) +1 -2
@@ -353,12 +355,11 @@
 	owned_fp = (current == last_task_used_math);
 	err |= __put_user(owned_fp, &sc->sc_ownedfp);
 
-	if (current->used_math) {	/* fp is active.  */
+	if (owned_fp) { /* fp is active.  */
 		set_cp0_status(ST0_CU1);
 		err |= save_fp_context(sc);
 		last_task_used_math = NULL;
 		regs->cp0_status &= ~ST0_CU1;
-		current->used_math = 0;
 	}
 
Quoted 1 line

Review line 365

Quoted 1 line
(quoted diff) +0 -0
 	return err;

dan> This is absolutely not right. It's righter than the status quo. dan> If we don't own the FP, you don't save the FP. Then we can use dan> FP in the signal handler, corrupting the process's original dan> floating point context.

I also am trying to fix this problem. How about my patch?

restore_sigcontext() can be more optimized, but I think this is a smallest patch to fix the problem.

---
Atsushi Nemoto

arch/mips/kernel/signal.c +8 -1
diff -ur linux.sgi/arch/mips/kernel/signal.c linux/arch/mips/kernel/signal.c
@@ -350,11 +350,18 @@
 	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);
+	/* restore_sigcontext must restore the fp context even if this
+	   process was not last_task_used_math. */
+	owned_fp = current->used_math;
 	err |= __put_user(owned_fp, &sc->sc_ownedfp);
 
 	if (current->used_math) {	/* fp is active.  */
+#if 0
+		/* Do not set CU1 here.  If this process does not
+		   owned fp, save_fp_context causes lazy_fpu_switch
+		   (and fp-owner's context will saved). */
 		set_cp0_status(ST0_CU1);
+#endif
 		err |= save_fp_context(sc);
 		last_task_used_math = NULL;
 		regs->cp0_status &= ~ST0_CU1;
#4 lore raw
Quoted 4 lines

I also am trying to fix this problem. How about my patch?

restore_sigcontext() can be more optimized, but I think this is a smallest patch to fix the problem.

I beleive that your patch might fix a symptom of the bugs that I talked about in my mail last night, but it doesn't solve the underlying problem. With the signal setup/restore changes I posted last night, your patch should not be necessary.

Kevin K.

#5 lore raw
In reply to Daniel Jacobowitz
Quoted 11 lines · Daniel Jacobowitz
Quoted 7 lines · Carsten Langgaard

There has been some reports regarding FP emulator failures, which the attached patch should solve.
The patch include a fix for emulation of instructions in a COP1 delay-slot, a fix for FP context switching and some additional stuff , which was needed to pass our torture test.

Ralf could you please apply this patch.

Two comments, especially since parts of this seem to be the patch I posted here over a month ago.

You are absolutely right, it's your old patch.

Quoted 16 lines
Quoted 16 lines

Index: linux/arch/mips/kernel/signal.c

arch/mips/kernel/signal.c +1 -2
@@ -353,12 +355,11 @@
      owned_fp = (current == last_task_used_math);
      err |= __put_user(owned_fp, &sc->sc_ownedfp);
 
-     if (current->used_math) {       /* fp is active.  */
+     if (owned_fp) { /* fp is active.  */
              set_cp0_status(ST0_CU1);
              err |= save_fp_context(sc);
              last_task_used_math = NULL;
              regs->cp0_status &= ~ST0_CU1;
-             current->used_math = 0;
      }
 
Quoted 6 lines

arch/mips/kernel/signal.c line 365

Quoted 1 line
arch/mips/kernel/signal.c +0 -0
      return err;

This is absolutely not right. It's righter than the status quo. If we don't own the FP, you don't save the FP. Then we can use FP in the signal handler, corrupting the process's original floating point context.

You are probably right, this is not a proper fix, but at least it solves the problems people has been seeing.
So until we come up with a better fix, this is still better than the current sources.

Quoted 19 lines
Quoted 4 lines

Index: linux/include/asm-mips/processor.h

include/asm-mips/processor.h line 222

Quoted 10 lines
include/asm-mips/processor.h +2 -2
@@ -235,8 +215,8 @@
  * Do necessary setup to start up a newly executed thread.
  */
 #define start_thread(regs, new_pc, new_sp) do {                              \
-     /* New thread looses kernel privileges. */                      \
-     regs->cp0_status = (regs->cp0_status & ~(ST0_CU0|ST0_KSU)) | KU_USER;\
+     /* New thread loses kernel and FPU privileges. */               \
+        regs->cp0_status = (regs->cp0_status & ~(ST0_CU0|ST0_KSU|ST0_CU1)) | KU_USER;\
      regs->cp0_epc = new_pc;                                         \
      regs->regs[29] = new_sp;                                        \
      current->thread.current_ds = USER_DS;                           \

I could be misremembering, but I believe that Ralf said this should be unnecessary and the problem was somewhere else. On the other hand, I still think it's a good idea.

Quoted 3 lines

--
Daniel Jacobowitz Carnegie Mellon University
MontaVista Software Debian GNU/Linux Developer

--

_    _ ____  ___   Carsten Langgaard   Mailto:[email protected]
|\  /|||___)(___   MIPS Denmark        Direct: +45 4486 5527
| \/ |||    ____)  Lautrupvang 4B      Switch: +45 4486 5555
  TECHNOLOGIES     2750 Ballerup       Fax...: +45 4486 5556
                   Denmark             http://www.mips.com
#6 lore raw
In reply to Carsten Langgaard
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line · Carsten Langgaard

carstenl> There has been some reports regarding FP emulator failures, carstenl> which the attached patch should solve. The patch include a carstenl> fix for emulation of instructions in a COP1 delay-slot, a carstenl> fix for FP context switching and some additional stuff , carstenl> which was needed to pass our torture test.

There is another bug in FPU emulator. An instruction in delay slot of bc1tl/bc1fl executed(or emulated) even if the branch not taken. Here is a patch to fix this.

Since current kernel calls FPU emulator on FP exception and FPU emulator handles continuous FP instructions in one call, this bug affects CPUs with FPU (not only CPUs without FPU).

---
Atsushi Nemoto

arch/mips/math-emu/cp1emu.c +1 -1
diff -ur linux.sgi/arch/mips/math-emu/cp1emu.c linux/arch/mips/math-emu/cp1emu.c
@@ -686,7 +686,7 @@
 					 * branch likely nullifies dslot if not
 					 * taken
 					 */
-					xcp->cp0_epc += 4;
+					contpc += 4;
 					/* else continue & execute dslot as normal insn */
 			}
 			break;