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:carstenl@mips.com |\ /|||___)(___ 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 diff -u -r1.36 signal.c --- linux/arch/mips/kernel/signal.c 2001/06/18 22:43:35 1.36 +++ linux/arch/mips/kernel/signal.c 2001/08/15 12:33:38 @@ -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 diff -u -r1.12 unaligned.c --- linux/arch/mips/kernel/unaligned.c 2001/07/11 22:05:11 1.12 +++ linux/arch/mips/kernel/unaligned.c 2001/08/15 12:33:38 @@ -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 diff -u -r1.7 cp1emu.c --- linux/arch/mips/math-emu/cp1emu.c 2001/08/02 21:55:26 1.7 +++ linux/arch/mips/math-emu/cp1emu.c 2001/08/15 12:33:38 @@ -6,7 +6,7 @@ * http://www.algor.co.uk * * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com - * Copyright (C) 2000 MIPS Technologies, Inc. + * Copyright (C) 2000-2001 MIPS Technologies, Inc. * * This program is free software; you can distribute it and/or modify it * under the terms of the GNU General Public License (Version 2) as @@ -273,7 +273,10 @@ fpuemuprivate.stats.errors++; return SIGBUS; } + /* __computer_return_epc() will have updated cp0_epc */ contpc = REG_TO_VA xcp->cp0_epc; + /* In order not to confuse ptrace() et al, tweak context */ + xcp->cp0_epc = VA_TO_REG emulpc - 4; } else { emulpc = REG_TO_VA xcp->cp0_epc; contpc = REG_TO_VA xcp->cp0_epc + 4; @@ -753,29 +756,73 @@ * execution of delay-slot instruction execution. */ +* Instruction inserted following delay slot instruction to force trap */ + +#define AdELOAD 0x8c000001 /* lw $0,1($0) */ + +/* Instruction inserted following the AdELOAD to further tag the sequence */ + +#define BD_COOKIE 0x0000bd36 /* tne $0,$0 with baggage */ + int do_dsemulret(struct pt_regs *xcp) { + unsigned long *pinst; + unsigned long stackitem; + int err = 0; + + /* See if this trap was deliberate. First check the instruction */ + + pinst = (unsigned long *) REG_TO_VA(xcp->cp0_epc); + + /* + * If we can't even access the area, something is very + * wrong, but we'll leave that to the default handling + */ + if (verify_area(VERIFY_READ, pinst, sizeof(unsigned long) * 3)) + return 0; + + /* Is the instruction pointed to by the EPC an AdELOAD? */ + stackitem = mips_get_word(xcp, pinst, &err); + if (err || (stackitem != AdELOAD)) return 0; + /* Is the following memory word the BD_COOKIE? */ + stackitem = mips_get_word(xcp, pinst+1, &err); + if (err || (stackitem != BD_COOKIE)) return 0; + + /* + * At this point, we are satisfied that it's a BD emulation + * trap. Yes, a user might have deliberately put two + * malformed and useless instructions in a row in his program, + * in which case he's in for a nasty surprise - the + * next instruction will be treated as a continuation + * address! Alas, this seems to be the only way that + * we can handle signals, recursion, and longjmps() + * in the context of emulating the branch delay instruction. + */ + #ifdef DSEMUL_TRACE printk("desemulret\n"); #endif + + /* Fetch the Saved EPC to Resume */ + + stackitem = mips_get_word(xcp, pinst+2, &err); + if (err) { + /* This is not a good situation to be in */ + fpuemuprivate.stats.errors++; + force_sig(SIGBUS, current); + return(1); + } + /* Set EPC to return to post-branch instruction */ - xcp->cp0_epc = current->thread.dsemul_epc; - /* - * Clear the state that got us here. - */ - current->thread.dsemul_aerpc = (unsigned long) 0; + xcp->cp0_epc = stackitem; - return 0; + return 1; } - -#define AdELOAD 0x8c000001 /* lw $0,1($0) */ - static int mips_dsemul(struct pt_regs *xcp, mips_instruction ir, vaddr_t cpc) { mips_instruction *dsemul_insns; - mips_instruction forcetrap; extern asmlinkage void handle_dsemulret(void); if (ir == 0) { /* a nop is easy */ @@ -791,13 +838,22 @@ * and put a trap after it which we can catch and jump to * the required address any alternative apart from full * instruction emulation!!. + * + * Algorithmics used a system call instruction, and + * borrowed that vector. MIPS/Linux version is a bit + * more heavyweight in the interests of portability and + * multiprocessor support. We flag the thread for special + * handling in the unaligned access handler and force an + * address error excpetion. */ - dsemul_insns = (mips_instruction *) (xcp->regs[29] & ~3); - dsemul_insns -= 3; /* Two instructions, plus one for luck ;-) */ + + /* Ensure that the two instructions are in the same cache line */ + dsemul_insns = (mips_instruction *) (xcp->regs[29] & ~0xf); + dsemul_insns -= 4; /* Retain 16-byte alignment */ /* Verify that the stack pointer is not competely insane */ if (verify_area(VERIFY_WRITE, dsemul_insns, - sizeof(mips_instruction) * 2)) + sizeof(mips_instruction) * 4)) return SIGBUS; if (mips_put_word(xcp, &dsemul_insns[0], ir)) { @@ -805,29 +861,22 @@ return SIGBUS; } - /* - * Algorithmics used a system call instruction, and - * borrowed that vector. MIPS/Linux version is a bit - * more heavyweight in the interests of portability and - * multiprocessor support. We flag the thread for special - * handling in the unaligned access handler and force an - * address error excpetion. - */ + if (mips_put_word(xcp, &dsemul_insns[1], (mips_instruction)AdELOAD)) { + fpuemuprivate.stats.errors++; + return (SIGBUS); + } - /* If one is *really* paranoid, one tests for a bad stack pointer */ - if ((xcp->regs[29] & 0x3) == 0x3) - forcetrap = AdELOAD - 1; - else - forcetrap = AdELOAD; + if (mips_put_word(xcp, &dsemul_insns[2], + (mips_instruction)BD_COOKIE)) { + fpuemuprivate.stats.errors++; + return (SIGBUS); + } - if (mips_put_word(xcp, &dsemul_insns[1], forcetrap)) { + if (mips_put_word(xcp, &dsemul_insns[3], (mips_instruction)cpc)) { fpuemuprivate.stats.errors++; return (SIGBUS); } - /* Set thread state to catch and handle the exception */ - current->thread.dsemul_epc = (unsigned long) cpc; - current->thread.dsemul_aerpc = (unsigned long) &dsemul_insns[1]; xcp->cp0_epc = VA_TO_REG & dsemul_insns[0]; flush_cache_sigtramp((unsigned long) dsemul_insns); 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 diff -u -r1.3 kernel_linkage.c --- linux/arch/mips/math-emu/kernel_linkage.c 2001/01/13 18:17:58 1.3 +++ linux/arch/mips/math-emu/kernel_linkage.c 2001/08/15 12:33:38 @@ -3,7 +3,7 @@ * arch/mips/math_emu/kernel_linkage.c * * Kevin D. Kissell, kevink@mips and Carsten Langgaard, carstenl@mips.com - * 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 diff -u -r1.16 offset.c --- linux/arch/mips/tools/offset.c 2000/12/10 07:56:02 1.16 +++ linux/arch/mips/tools/offset.c 2001/08/15 12:33:39 @@ -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 diff -u -r1.37 processor.h --- linux/include/asm-mips/processor.h 2001/07/23 00:17:39 1.37 +++ linux/include/asm-mips/processor.h 2001/08/15 12:33:42 @@ -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; \