linux/arch/mips/math-emu/dsemul.c
<<
>>
Prefs
   1#include <asm/branch.h>
   2#include <asm/cacheflush.h>
   3#include <asm/fpu_emulator.h>
   4#include <asm/inst.h>
   5#include <asm/mipsregs.h>
   6#include <asm/uaccess.h>
   7
   8#include "ieee754.h"
   9
  10/*
  11 * Emulate the arbritrary instruction ir at xcp->cp0_epc.  Required when
  12 * we have to emulate the instruction in a COP1 branch delay slot.  Do
  13 * not change cp0_epc due to the instruction
  14 *
  15 * According to the spec:
  16 * 1) it shouldn't be a branch :-)
  17 * 2) it can be a COP instruction :-(
  18 * 3) if we are tring to run a protected memory space we must take
  19 *    special care on memory access instructions :-(
  20 */
  21
  22/*
  23 * "Trampoline" return routine to catch exception following
  24 *  execution of delay-slot instruction execution.
  25 */
  26
  27struct emuframe {
  28        mips_instruction        emul;
  29        mips_instruction        badinst;
  30        mips_instruction        cookie;
  31        unsigned long           epc;
  32};
  33
  34/*
  35 * Set up an emulation frame for instruction IR, from a delay slot of
  36 * a branch jumping to CPC.  Return 0 if successful, -1 if no emulation
  37 * required, otherwise a signal number causing a frame setup failure.
  38 */
  39int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc)
  40{
  41        int isa16 = get_isa16_mode(regs->cp0_epc);
  42        mips_instruction break_math;
  43        struct emuframe __user *fr;
  44        int err;
  45
  46        /* NOP is easy */
  47        if (ir == 0)
  48                return -1;
  49
  50        /* microMIPS instructions */
  51        if (isa16) {
  52                union mips_instruction insn = { .word = ir };
  53
  54                /* NOP16 aka MOVE16 $0, $0 */
  55                if ((ir >> 16) == MM_NOP16)
  56                        return -1;
  57
  58                /* ADDIUPC */
  59                if (insn.mm_a_format.opcode == mm_addiupc_op) {
  60                        unsigned int rs;
  61                        s32 v;
  62
  63                        rs = (((insn.mm_a_format.rs + 0x1e) & 0xf) + 2);
  64                        v = regs->cp0_epc & ~3;
  65                        v += insn.mm_a_format.simmediate << 2;
  66                        regs->regs[rs] = (long)v;
  67                        return -1;
  68                }
  69        }
  70
  71        pr_debug("dsemul %lx %lx\n", regs->cp0_epc, cpc);
  72
  73        /*
  74         * The strategy is to push the instruction onto the user stack
  75         * and put a trap after it which we can catch and jump to
  76         * the required address any alternative apart from full
  77         * instruction emulation!!.
  78         *
  79         * Algorithmics used a system call instruction, and
  80         * borrowed that vector.  MIPS/Linux version is a bit
  81         * more heavyweight in the interests of portability and
  82         * multiprocessor support.  For Linux we use a BREAK 514
  83         * instruction causing a breakpoint exception.
  84         */
  85        break_math = BREAK_MATH(isa16);
  86
  87        /* Ensure that the two instructions are in the same cache line */
  88        fr = (struct emuframe __user *)
  89                ((regs->regs[29] - sizeof(struct emuframe)) & ~0x7);
  90
  91        /* Verify that the stack pointer is not competely insane */
  92        if (unlikely(!access_ok(VERIFY_WRITE, fr, sizeof(struct emuframe))))
  93                return SIGBUS;
  94
  95        if (isa16) {
  96                err = __put_user(ir >> 16,
  97                                 (u16 __user *)(&fr->emul));
  98                err |= __put_user(ir & 0xffff,
  99                                  (u16 __user *)((long)(&fr->emul) + 2));
 100                err |= __put_user(break_math >> 16,
 101                                  (u16 __user *)(&fr->badinst));
 102                err |= __put_user(break_math & 0xffff,
 103                                  (u16 __user *)((long)(&fr->badinst) + 2));
 104        } else {
 105                err = __put_user(ir, &fr->emul);
 106                err |= __put_user(break_math, &fr->badinst);
 107        }
 108
 109        err |= __put_user((mips_instruction)BD_COOKIE, &fr->cookie);
 110        err |= __put_user(cpc, &fr->epc);
 111
 112        if (unlikely(err)) {
 113                MIPS_FPU_EMU_INC_STATS(errors);
 114                return SIGBUS;
 115        }
 116
 117        regs->cp0_epc = (unsigned long)&fr->emul | isa16;
 118
 119        flush_cache_sigtramp((unsigned long)&fr->emul);
 120
 121        return 0;
 122}
 123
 124int do_dsemulret(struct pt_regs *xcp)
 125{
 126        int isa16 = get_isa16_mode(xcp->cp0_epc);
 127        struct emuframe __user *fr;
 128        unsigned long epc;
 129        u32 insn, cookie;
 130        int err = 0;
 131        u16 instr[2];
 132
 133        fr = (struct emuframe __user *)
 134                (msk_isa16_mode(xcp->cp0_epc) - sizeof(mips_instruction));
 135
 136        /*
 137         * If we can't even access the area, something is very wrong, but we'll
 138         * leave that to the default handling
 139         */
 140        if (!access_ok(VERIFY_READ, fr, sizeof(struct emuframe)))
 141                return 0;
 142
 143        /*
 144         * Do some sanity checking on the stackframe:
 145         *
 146         *  - Is the instruction pointed to by the EPC an BREAK_MATH?
 147         *  - Is the following memory word the BD_COOKIE?
 148         */
 149        if (isa16) {
 150                err = __get_user(instr[0],
 151                                 (u16 __user *)(&fr->badinst));
 152                err |= __get_user(instr[1],
 153                                  (u16 __user *)((long)(&fr->badinst) + 2));
 154                insn = (instr[0] << 16) | instr[1];
 155        } else {
 156                err = __get_user(insn, &fr->badinst);
 157        }
 158        err |= __get_user(cookie, &fr->cookie);
 159
 160        if (unlikely(err ||
 161                     insn != BREAK_MATH(isa16) || cookie != BD_COOKIE)) {
 162                MIPS_FPU_EMU_INC_STATS(errors);
 163                return 0;
 164        }
 165
 166        /*
 167         * At this point, we are satisfied that it's a BD emulation trap.  Yes,
 168         * a user might have deliberately put two malformed and useless
 169         * instructions in a row in his program, in which case he's in for a
 170         * nasty surprise - the next instruction will be treated as a
 171         * continuation address!  Alas, this seems to be the only way that we
 172         * can handle signals, recursion, and longjmps() in the context of
 173         * emulating the branch delay instruction.
 174         */
 175
 176        pr_debug("dsemulret\n");
 177
 178        if (__get_user(epc, &fr->epc)) {                /* Saved EPC */
 179                /* This is not a good situation to be in */
 180                force_sig(SIGBUS, current);
 181
 182                return 0;
 183        }
 184
 185        /* Set EPC to return to post-branch instruction */
 186        xcp->cp0_epc = epc;
 187        MIPS_FPU_EMU_INC_STATS(ds_emul);
 188        return 1;
 189}
 190