Subject: Should send SIGFPE to .*

From: Florian Lohoff <[email-masked]>
Date: Thu, 20 Apr 2000 14:41:39 +0200

Hi,
i just rediscovered the kernel messages in the kern.log ...

Apr 19 20:12:41 repeat kernel: Setting flush to zero for top.
Apr 19 20:12:41 repeat kernel: Unimplemented exception for insn 46001124 at 0x00403588 in top.
Apr 19 20:12:41 repeat kernel: Should send SIGFPE to top

This is something i not really understand - After "Setting flush to zero" the
instruction is "retried" and enters the exception again and say
"Unimplemented exception" which means - The CPU doesnt support it
and then "Simulates" this instruction in "simfp(insn)" - Until
now nothing bad has happened and these two kernel messages
should be encapsulated by a "#if DEBUG_FP_EXCEPTION" or something.

Now i get an "Should send SIGFPE to top" which i dont understand - Shouldnt
SIGFPE signal Floating Point errors (like div by zero) - As the code
gets simulated in simfp this function should be responsible for 
sending the signal if something goes wrong ?

arch/mips/kernel/traps.c

    354                 printk(KERN_DEBUG "Unimplemented exception for insn %08x at 0x%08lx in %s.\n",
    355                        insn, regs->cp0_epc, current->comm);
    356                 simfp(insn);
    357         }
    358 
    359         if (compute_return_epc(regs))
    360                 goto out;
    361         //force_sig(SIGFPE, current);
    362         printk(KERN_DEBUG "Should send SIGFPE to %s\n", current->comm);
    363 
    364 out:
    365         unlock_kernel();

Might it be that compute_return_epc in branch.c does not support
the mentioned instructions (FP instructions ?) and though can not
calculate the correct epc ?

Flo
-- 
Florian Lohoff		[email-masked]		      	+49-subject-2-change
"Technology is a constant battle between manufacturers producing bigger and
more idiot-proof systems and nature producing bigger and better idiots."iii

---

From: "Kevin D. Kissell" <[email-masked]>
Date: Thu, 20 Apr 2000 18:42:15 +0200

>calculate the correct epc ?

My previous response was a bit confused, because I was
looking at our already-modified code and reconstructing
the old logic.  Looking at the more-or-less-current 2.3
tree, I see the further bug to which you are referring.
There *should* be another goto following the simfp()
call.  The code that does the compute_return_epc()
followed by the force_sig(SIGFP) should be executed
in all cases where the emulator was not invoked,
which is to say in the cases of real FP exceptions,
but not if the mini-emulator has handled the situation.
Presumably, the absence of that bypass path was causing
bogus SIGFPE's, prompting someone to comment
out the force_sig() in all cases and add the printk(),
but that's not the correct behaviour.

The overall handler (MIPS version) looks like this:
(and yes, it still can be improved, as I mentioned earlier...)

/*
 * XXX Delayed fp exceptions when doing a lazy ctx switch XXX
 */
void do_fpe(struct pt_regs *regs, unsigned long fcr31)
{

#ifdef CONFIG_MIPS_FPU_EMULATOR
        if(!(mips_cpu.options & MIPS_CPU_FPU))
                panic("Floating Point Exception with No FPU");
#endif

#ifdef CONFIG_MIPS_FPE_MODULE
        if (fpe_handler != NULL) {
                fpe_handler(regs, fcr31);
                return;
        }
#endif

        lock_kernel();
        if (fcr31 & FPU_CSR_UNI_X) {
#ifdef CONFIG_MIPS_FPU_EMULATOR
                extern void r4xx0_save_fp(struct task_struct *);
                extern void r4xx0_restore_fp(struct task_struct *);
                int sig;
                /*
                 * Unimplemented operation exception.  If we've got the
                 * Full software emulator on-board, let's use it...
                 *
                 * Force FPU to dump state into task/thread context.
                 * We're moving a lot of data here for what is probably
                 * a single instruction, but the alternative is to
                 * pre-decode the FP register operands before invoking
                 * the emulator, which seems a bit extreme for what
                 * should be an infrequent event.
                 */
                r4xx0_save_fp(current);

                /* Run the emulator */
                sig = fpu_emulator_cop1Handler(0, regs);

                /* Restore the hardware register state */
                r4xx0_restore_fp(current);

                /* If something went wrong, signal */
                if(sig) {
                        force_sig(sig, current);
                }
#else
                /* Else use mini-emulator */

                extern void simfp(int);
                unsigned long pc;
                unsigned int insn;

                /* Retry instruction with flush to zero ...  */
                if (!(fcr31 & (1<<24))) {
                        printk("Setting flush to zero for %s.\n",
                               current->comm);
                        fcr31 &= ~FPU_CSR_UNI_X;
                        fcr31 |= (1<<24);
                        __asm__ __volatile__(
                                "ctc1\t%0,$31"
                                : /* No outputs */
                                : "r" (fcr31));
                        goto out;
                }
                pc = regs->cp0_epc + ((regs->cp0_cause & CAUSEF_BD) ? 4 : 0);
                if(pc & 0x80000000) insn = *(unsigned int *)pc;
                else if (get_user(insn, (unsigned int *)pc)) {
                        /* XXX Can this happen?  */
                        force_sig(SIGSEGV, current);
                    }

                printk(KERN_DEBUG "Unimplemented exception for insn %08x at
0x%8lx in %s.\n",
                       insn, regs->cp0_epc, current->comm);
                simfp(MIPSInst(insn));
                compute_return_epc(regs);
#endif /* CONFIG_MIPS_FPU_EMULATOR */

                goto out;
        }

        if (compute_return_epc(regs)) {
                goto out;
        }
        force_sig(SIGFPE, current);
        printk(KERN_DEBUG "Sent SIGFPE to %s\n", current->comm);

out:
        unlock_kernel();
}