static inline int is_sp_move_ins(union mips_instruction *ip) { #ifdef CONFIG_CPU_MICROMIPS /* * addiusp -imm * addius5 sp,-imm * addiu32 sp,sp,-imm * jradiussp - NOT SUPPORTED * * microMIPS is not more fun... */ if (mm_insn_16bit(ip->halfword[0])) { union mips_instruction mmi; mmi.word = (ip->halfword[0] << 16); return (mmi.mm16_r3_format.opcode == mm_pool16d_op && mmi.mm16_r3_format.simmediate && mm_addiusp_func) || (mmi.mm16_r5_format.opcode == mm_pool16d_op && mmi.mm16_r5_format.rt == 29); } return ip->mm_i_format.opcode == mm_addiu32_op && ip->mm_i_format.rt == 29 && ip->mm_i_format.rs == 29; #else /* addiu/daddiu sp,sp,-imm */ if (ip->i_format.rs != 29 || ip->i_format.rt != 29) return 0; if (ip->i_format.opcode == addiu_op || ip->i_format.opcode == daddiu_op) return 1; #endif return 0; }
static inline int is_ra_save_ins(union mips_instruction *ip) { #ifdef CONFIG_CPU_MICROMIPS union mips_instruction mmi; /* * swsp ra,offset * swm16 reglist,offset(sp) * swm32 reglist,offset(sp) * sw32 ra,offset(sp) * jradiussp - NOT SUPPORTED * * microMIPS is way more fun... */ if (mm_insn_16bit(ip->halfword[0])) { mmi.word = (ip->halfword[0] << 16); return (mmi.mm16_r5_format.opcode == mm_swsp16_op && mmi.mm16_r5_format.rt == 31) || (mmi.mm16_m_format.opcode == mm_pool16c_op && mmi.mm16_m_format.func == mm_swm16_op); } else { mmi.halfword[0] = ip->halfword[1]; mmi.halfword[1] = ip->halfword[0]; return (mmi.mm_m_format.opcode == mm_pool32b_op && mmi.mm_m_format.rd > 9 && mmi.mm_m_format.base == 29 && mmi.mm_m_format.func == mm_swm32_func) || (mmi.i_format.opcode == mm_sw32_op && mmi.i_format.rs == 29 && mmi.i_format.rt == 31); } #else /* sw / sd $ra, offset($sp) */ return (ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op) && ip->i_format.rs == 29 && ip->i_format.rt == 31; #endif }
static void emulate_load_store_microMIPS(struct pt_regs *regs, void __user *addr) { unsigned long value; unsigned int res; int i; unsigned int reg = 0, rvar; unsigned long orig31; u16 __user *pc16; u16 halfword; unsigned int word; unsigned long origpc, contpc; union mips_instruction insn; struct mm_decoded_insn mminsn; void __user *fault_addr = NULL; origpc = regs->cp0_epc; orig31 = regs->regs[31]; mminsn.micro_mips_mode = 1; /* * This load never faults. */ pc16 = (unsigned short __user *)msk_isa16_mode(regs->cp0_epc); __get_user(halfword, pc16); pc16++; contpc = regs->cp0_epc + 2; word = ((unsigned int)halfword << 16); mminsn.pc_inc = 2; if (!mm_insn_16bit(halfword)) { __get_user(halfword, pc16); pc16++; contpc = regs->cp0_epc + 4; mminsn.pc_inc = 4; word |= halfword; } mminsn.insn = word; if (get_user(halfword, pc16)) goto fault; mminsn.next_pc_inc = 2; word = ((unsigned int)halfword << 16); if (!mm_insn_16bit(halfword)) { pc16++; if (get_user(halfword, pc16)) goto fault; mminsn.next_pc_inc = 4; word |= halfword; } mminsn.next_insn = word; insn = (union mips_instruction)(mminsn.insn); if (mm_isBranchInstr(regs, mminsn, &contpc)) insn = (union mips_instruction)(mminsn.next_insn); /* Parse instruction to find what to do */ switch (insn.mm_i_format.opcode) { case mm_pool32a_op: switch (insn.mm_x_format.func) { case mm_lwxs_op: reg = insn.mm_x_format.rd; goto loadW; } goto sigbus; case mm_pool32b_op: switch (insn.mm_m_format.func) { case mm_lwp_func: reg = insn.mm_m_format.rd; if (reg == 31) goto sigbus; if (!access_ok(VERIFY_READ, addr, 8)) goto sigbus; LoadW(addr, value, res); if (res) goto fault; regs->regs[reg] = value; addr += 4; LoadW(addr, value, res); if (res) goto fault; regs->regs[reg + 1] = value; goto success; case mm_swp_func: reg = insn.mm_m_format.rd; if (reg == 31) goto sigbus; if (!access_ok(VERIFY_WRITE, addr, 8)) goto sigbus; value = regs->regs[reg]; StoreW(addr, value, res); if (res) goto fault; addr += 4; value = regs->regs[reg + 1]; StoreW(addr, value, res); if (res) goto fault; goto success; case mm_ldp_func: #ifdef CONFIG_64BIT reg = insn.mm_m_format.rd; if (reg == 31) goto sigbus; if (!access_ok(VERIFY_READ, addr, 16)) goto sigbus; LoadDW(addr, value, res); if (res) goto fault; regs->regs[reg] = value; addr += 8; LoadDW(addr, value, res); if (res) goto fault; regs->regs[reg + 1] = value; goto success; #endif /* CONFIG_64BIT */ goto sigill; case mm_sdp_func: #ifdef CONFIG_64BIT reg = insn.mm_m_format.rd; if (reg == 31) goto sigbus; if (!access_ok(VERIFY_WRITE, addr, 16)) goto sigbus; value = regs->regs[reg]; StoreDW(addr, value, res); if (res) goto fault; addr += 8; value = regs->regs[reg + 1]; StoreDW(addr, value, res); if (res) goto fault; goto success; #endif /* CONFIG_64BIT */ goto sigill; case mm_lwm32_func: reg = insn.mm_m_format.rd; rvar = reg & 0xf; if ((rvar > 9) || !reg) goto sigill; if (reg & 0x10) { if (!access_ok (VERIFY_READ, addr, 4 * (rvar + 1))) goto sigbus; } else { if (!access_ok(VERIFY_READ, addr, 4 * rvar)) goto sigbus; } if (rvar == 9) rvar = 8; for (i = 16; rvar; rvar--, i++) { LoadW(addr, value, res); if (res) goto fault; addr += 4; regs->regs[i] = value; } if ((reg & 0xf) == 9) { LoadW(addr, value, res); if (res) goto fault; addr += 4; regs->regs[30] = value; } if (reg & 0x10) { LoadW(addr, value, res); if (res) goto fault; regs->regs[31] = value; } goto success; case mm_swm32_func: reg = insn.mm_m_format.rd; rvar = reg & 0xf; if ((rvar > 9) || !reg) goto sigill; if (reg & 0x10) { if (!access_ok (VERIFY_WRITE, addr, 4 * (rvar + 1))) goto sigbus; } else { if (!access_ok(VERIFY_WRITE, addr, 4 * rvar)) goto sigbus; } if (rvar == 9) rvar = 8; for (i = 16; rvar; rvar--, i++) { value = regs->regs[i]; StoreW(addr, value, res); if (res) goto fault; addr += 4; } if ((reg & 0xf) == 9) { value = regs->regs[30]; StoreW(addr, value, res); if (res) goto fault; addr += 4; } if (reg & 0x10) { value = regs->regs[31]; StoreW(addr, value, res); if (res) goto fault; } goto success; case mm_ldm_func: #ifdef CONFIG_64BIT reg = insn.mm_m_format.rd; rvar = reg & 0xf; if ((rvar > 9) || !reg) goto sigill; if (reg & 0x10) { if (!access_ok (VERIFY_READ, addr, 8 * (rvar + 1))) goto sigbus; } else { if (!access_ok(VERIFY_READ, addr, 8 * rvar)) goto sigbus; } if (rvar == 9) rvar = 8; for (i = 16; rvar; rvar--, i++) { LoadDW(addr, value, res); if (res) goto fault; addr += 4; regs->regs[i] = value; } if ((reg & 0xf) == 9) { LoadDW(addr, value, res); if (res) goto fault; addr += 8; regs->regs[30] = value; } if (reg & 0x10) { LoadDW(addr, value, res); if (res) goto fault; regs->regs[31] = value; } goto success; #endif /* CONFIG_64BIT */ goto sigill; case mm_sdm_func: #ifdef CONFIG_64BIT reg = insn.mm_m_format.rd; rvar = reg & 0xf; if ((rvar > 9) || !reg) goto sigill; if (reg & 0x10) { if (!access_ok (VERIFY_WRITE, addr, 8 * (rvar + 1))) goto sigbus; } else { if (!access_ok(VERIFY_WRITE, addr, 8 * rvar)) goto sigbus; } if (rvar == 9) rvar = 8; for (i = 16; rvar; rvar--, i++) { value = regs->regs[i]; StoreDW(addr, value, res); if (res) goto fault; addr += 8; } if ((reg & 0xf) == 9) { value = regs->regs[30]; StoreDW(addr, value, res); if (res) goto fault; addr += 8; } if (reg & 0x10) { value = regs->regs[31]; StoreDW(addr, value, res); if (res) goto fault; } goto success; #endif /* CONFIG_64BIT */ goto sigill; /* LWC2, SWC2, LDC2, SDC2 are not serviced */ } goto sigbus; case mm_pool32c_op: switch (insn.mm_m_format.func) { case mm_lwu_func: reg = insn.mm_m_format.rd; goto loadWU; } /* LL,SC,LLD,SCD are not serviced */ goto sigbus; case mm_pool32f_op: switch (insn.mm_x_format.func) { case mm_lwxc1_func: case mm_swxc1_func: case mm_ldxc1_func: case mm_sdxc1_func: goto fpu_emul; } goto sigbus; case mm_ldc132_op: case mm_sdc132_op: case mm_lwc132_op: case mm_swc132_op: fpu_emul: /* roll back jump/branch */ regs->cp0_epc = origpc; regs->regs[31] = orig31; die_if_kernel("Unaligned FP access in kernel code", regs); BUG_ON(!used_math()); BUG_ON(!is_fpu_owner()); lose_fpu(1); /* save the FPU state for the emulator */ res = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1, &fault_addr); own_fpu(1); /* restore FPU state */ /* If something went wrong, signal */ process_fpemu_return(res, fault_addr); if (res == 0) goto success; return; case mm_lh32_op: reg = insn.mm_i_format.rt; goto loadHW; case mm_lhu32_op: reg = insn.mm_i_format.rt; goto loadHWU; case mm_lw32_op: reg = insn.mm_i_format.rt; goto loadW; case mm_sh32_op: reg = insn.mm_i_format.rt; goto storeHW; case mm_sw32_op: reg = insn.mm_i_format.rt; goto storeW; case mm_ld32_op: reg = insn.mm_i_format.rt; goto loadDW; case mm_sd32_op: reg = insn.mm_i_format.rt; goto storeDW; case mm_pool16c_op: switch (insn.mm16_m_format.func) { case mm_lwm16_op: reg = insn.mm16_m_format.rlist; rvar = reg + 1; if (!access_ok(VERIFY_READ, addr, 4 * rvar)) goto sigbus; for (i = 16; rvar; rvar--, i++) { LoadW(addr, value, res); if (res) goto fault; addr += 4; regs->regs[i] = value; } LoadW(addr, value, res); if (res) goto fault; regs->regs[31] = value; goto success; case mm_swm16_op: reg = insn.mm16_m_format.rlist; rvar = reg + 1; if (!access_ok(VERIFY_WRITE, addr, 4 * rvar)) goto sigbus; for (i = 16; rvar; rvar--, i++) { value = regs->regs[i]; StoreW(addr, value, res); if (res) goto fault; addr += 4; } value = regs->regs[31]; StoreW(addr, value, res); if (res) goto fault; goto success; } goto sigbus; case mm_lhu16_op: reg = reg16to32[insn.mm16_rb_format.rt]; goto loadHWU; case mm_lw16_op: reg = reg16to32[insn.mm16_rb_format.rt]; goto loadW; case mm_sh16_op: reg = reg16to32st[insn.mm16_rb_format.rt]; goto storeHW; case mm_sw16_op: reg = reg16to32st[insn.mm16_rb_format.rt]; goto storeW; case mm_lwsp16_op: reg = insn.mm16_r5_format.rt; goto loadW; case mm_swsp16_op: reg = insn.mm16_r5_format.rt; goto storeW; case mm_lwgp16_op: reg = reg16to32[insn.mm16_r3_format.rt]; goto loadW; default: goto sigill; } loadHW: if (!access_ok(VERIFY_READ, addr, 2)) goto sigbus; LoadHW(addr, value, res); if (res) goto fault; regs->regs[reg] = value; goto success; loadHWU: if (!access_ok(VERIFY_READ, addr, 2)) goto sigbus; LoadHWU(addr, value, res); if (res) goto fault; regs->regs[reg] = value; goto success; loadW: if (!access_ok(VERIFY_READ, addr, 4)) goto sigbus; LoadW(addr, value, res); if (res) goto fault; regs->regs[reg] = value; goto success; loadWU: #ifdef CONFIG_64BIT /* * A 32-bit kernel might be running on a 64-bit processor. But * if we're on a 32-bit processor and an i-cache incoherency * or race makes us see a 64-bit instruction here the sdl/sdr * would blow up, so for now we don't handle unaligned 64-bit * instructions on 32-bit kernels. */ if (!access_ok(VERIFY_READ, addr, 4)) goto sigbus; LoadWU(addr, value, res); if (res) goto fault; regs->regs[reg] = value; goto success; #endif /* CONFIG_64BIT */ /* Cannot handle 64-bit instructions in 32-bit kernel */ goto sigill; loadDW: #ifdef CONFIG_64BIT /* * A 32-bit kernel might be running on a 64-bit processor. But * if we're on a 32-bit processor and an i-cache incoherency * or race makes us see a 64-bit instruction here the sdl/sdr * would blow up, so for now we don't handle unaligned 64-bit * instructions on 32-bit kernels. */ if (!access_ok(VERIFY_READ, addr, 8)) goto sigbus; LoadDW(addr, value, res); if (res) goto fault; regs->regs[reg] = value; goto success; #endif /* CONFIG_64BIT */ /* Cannot handle 64-bit instructions in 32-bit kernel */ goto sigill; storeHW: if (!access_ok(VERIFY_WRITE, addr, 2)) goto sigbus; value = regs->regs[reg]; StoreHW(addr, value, res); if (res) goto fault; goto success; storeW: if (!access_ok(VERIFY_WRITE, addr, 4)) goto sigbus; value = regs->regs[reg]; StoreW(addr, value, res); if (res) goto fault; goto success; storeDW: #ifdef CONFIG_64BIT /* * A 32-bit kernel might be running on a 64-bit processor. But * if we're on a 32-bit processor and an i-cache incoherency * or race makes us see a 64-bit instruction here the sdl/sdr * would blow up, so for now we don't handle unaligned 64-bit * instructions on 32-bit kernels. */ if (!access_ok(VERIFY_WRITE, addr, 8)) goto sigbus; value = regs->regs[reg]; StoreDW(addr, value, res); if (res) goto fault; goto success; #endif /* CONFIG_64BIT */ /* Cannot handle 64-bit instructions in 32-bit kernel */ goto sigill; success: regs->cp0_epc = contpc; /* advance or branch */ #ifdef CONFIG_DEBUG_FS unaligned_instructions++; #endif return; fault: /* roll back jump/branch */ regs->cp0_epc = origpc; regs->regs[31] = orig31; /* Did we have an exception handler installed? */ if (fixup_exception(regs)) return; die_if_kernel("Unhandled kernel unaligned access", regs); force_sig(SIGSEGV, current); return; sigbus: die_if_kernel("Unhandled kernel unaligned access", regs); force_sig(SIGBUS, current); return; sigill: die_if_kernel ("Unhandled kernel unaligned access or invalid instruction", regs); force_sig(SIGILL, current); }