asmlinkage int restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) { int err = 0; /* Always make any pending restarted system calls return -EINTR */ current_thread_info()->restart_block.fn = do_no_restart_syscall; err |= __get_user(regs->cp0_epc, &sc->sc_pc); err |= __get_user(regs->hi, &sc->sc_mdhi); err |= __get_user(regs->lo, &sc->sc_mdlo); #define restore_gp_reg(i) do { \ err |= __get_user(regs->regs[i], &sc->sc_regs[i]); \ } while(0) restore_gp_reg( 1); restore_gp_reg( 2); restore_gp_reg( 3); restore_gp_reg( 4); restore_gp_reg( 5); restore_gp_reg( 6); restore_gp_reg( 7); restore_gp_reg( 8); restore_gp_reg( 9); restore_gp_reg(10); restore_gp_reg(11); restore_gp_reg(12); restore_gp_reg(13); restore_gp_reg(14); restore_gp_reg(15); restore_gp_reg(16); restore_gp_reg(17); restore_gp_reg(18); restore_gp_reg(19); restore_gp_reg(20); restore_gp_reg(21); restore_gp_reg(22); restore_gp_reg(23); restore_gp_reg(24); restore_gp_reg(25); restore_gp_reg(26); restore_gp_reg(27); restore_gp_reg(28); restore_gp_reg(29); restore_gp_reg(30); restore_gp_reg(31); #undef restore_gp_reg err |= __get_user(current->used_math, &sc->sc_used_math); if (current->used_math) { /* restore fpu context if we have used it before */ own_fpu(); err |= restore_fp_context(sc); } else { /* signal handler may have used FPU. Give it up. */ lose_fpu(); } return err; }
static int protected_restore_fp_context(struct sigcontext __user *sc) { int err, tmp __maybe_unused; #ifndef CONFIG_EVA while (1) { lock_fpu_owner(); if (is_fpu_owner()) { err = restore_fp_context(sc); unlock_fpu_owner(); } else { unlock_fpu_owner(); err = copy_fp_from_sigcontext(sc); } if (likely(!err)) break; /* touch the sigcontext and try again */ err = __get_user(tmp, &sc->sc_fpregs[0]) | __get_user(tmp, &sc->sc_fpregs[31]) | __get_user(tmp, &sc->sc_fpc_csr); if (err) break; /* really bad sigcontext */ } #else /* * EVA does not have FPU EVA instructions so restoring fpu context * directly does not work. */ lose_fpu(0); err = restore_fp_context(sc); /* this might fail */ #endif return err; }
/* * Helper routines */ static int protected_save_fp_context(struct sigcontext __user *sc) { int err; #ifndef CONFIG_EVA while (1) { lock_fpu_owner(); if (is_fpu_owner()) { err = save_fp_context(sc); unlock_fpu_owner(); } else { unlock_fpu_owner(); err = copy_fp_to_sigcontext(sc); } if (likely(!err)) break; /* touch the sigcontext and try again */ err = __put_user(0, &sc->sc_fpregs[0]) | __put_user(0, &sc->sc_fpregs[31]) | __put_user(0, &sc->sc_fpc_csr); if (err) break; /* really bad sigcontext */ } #else /* * EVA does not have FPU EVA instructions so saving fpu context directly * does not work. */ lose_fpu(1); err = save_fp_context(sc); /* this might fail */ #endif return err; }
static int restore_sigcontext32(struct pt_regs *regs, struct sigcontext32 __user *sc) { u32 used_math; int err = 0; s32 treg; /* Always make any pending restarted system calls return -EINTR */ current_thread_info()->restart_block.fn = do_no_restart_syscall; err |= __get_user(regs->cp0_epc, &sc->sc_pc); err |= __get_user(regs->hi, &sc->sc_mdhi); err |= __get_user(regs->lo, &sc->sc_mdlo); if (cpu_has_dsp) { err |= __get_user(treg, &sc->sc_hi1); mthi1(treg); err |= __get_user(treg, &sc->sc_lo1); mtlo1(treg); err |= __get_user(treg, &sc->sc_hi2); mthi2(treg); err |= __get_user(treg, &sc->sc_lo2); mtlo2(treg); err |= __get_user(treg, &sc->sc_hi3); mthi3(treg); err |= __get_user(treg, &sc->sc_lo3); mtlo3(treg); err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK); } #define restore_gp_reg(i) do { \ err |= __get_user(regs->regs[i], &sc->sc_regs[i]); \ } while(0) restore_gp_reg( 1); restore_gp_reg( 2); restore_gp_reg( 3); restore_gp_reg( 4); restore_gp_reg( 5); restore_gp_reg( 6); restore_gp_reg( 7); restore_gp_reg( 8); restore_gp_reg( 9); restore_gp_reg(10); restore_gp_reg(11); restore_gp_reg(12); restore_gp_reg(13); restore_gp_reg(14); restore_gp_reg(15); restore_gp_reg(16); restore_gp_reg(17); restore_gp_reg(18); restore_gp_reg(19); restore_gp_reg(20); restore_gp_reg(21); restore_gp_reg(22); restore_gp_reg(23); restore_gp_reg(24); restore_gp_reg(25); restore_gp_reg(26); restore_gp_reg(27); restore_gp_reg(28); restore_gp_reg(29); restore_gp_reg(30); restore_gp_reg(31); #undef restore_gp_reg err |= __get_user(used_math, &sc->sc_used_math); conditional_used_math(used_math); preempt_disable(); if (used_math()) { /* restore fpu context if we have used it before */ own_fpu(); err |= restore_fp_context32(sc); } else { /* signal handler may have used FPU. Give it up. */ lose_fpu(); } preempt_enable(); return err; }
int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) { unsigned int used_math; unsigned long treg; int err = 0; int i; /* Always make any pending restarted system calls return -EINTR */ current_thread_info()->restart_block.fn = do_no_restart_syscall; err |= __get_user(regs->cp0_epc, &sc->sc_pc); #ifdef CONFIG_CPU_HAS_SMARTMIPS err |= __get_user(regs->acx, &sc->sc_acx); #endif err |= __get_user(regs->hi, &sc->sc_mdhi); err |= __get_user(regs->lo, &sc->sc_mdlo); if (cpu_has_dsp) { err |= __get_user(treg, &sc->sc_hi1); mthi1(treg); err |= __get_user(treg, &sc->sc_lo1); mtlo1(treg); err |= __get_user(treg, &sc->sc_hi2); mthi2(treg); err |= __get_user(treg, &sc->sc_lo2); mtlo2(treg); err |= __get_user(treg, &sc->sc_hi3); mthi3(treg); err |= __get_user(treg, &sc->sc_lo3); mtlo3(treg); err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK); } for (i = 1; i < 32; i++) err |= __get_user(regs->regs[i], &sc->sc_regs[i]); err |= __get_user(used_math, &sc->sc_used_math); conditional_used_math(used_math); if (used_math) { /* restore fpu context if we have used it before */ if (!err) err = check_and_restore_fp_context(sc); } else { /* signal handler may have used FPU. Give it up. */ lose_fpu(0); } if (cpu_has_mxu) { unsigned int regs[NUM_MXU_REGS]; for (i = 0; i < NUM_MXU_REGS; i++){ err |= __get_user(regs[i], &sc->sc_mxu[i]); } __let_mxu_regs(current,regs); } return err; }
static int protected_restore_fp_context(struct sigcontext __user *sc, unsigned used_math) { int err, tmp __maybe_unused; bool restore_msa = cpu_has_msa && (used_math & USEDMATH_MSA); #ifndef CONFIG_EVA while (1) { lock_fpu_owner(); if (is_fpu_owner()) { err = restore_fp_context(sc); if (restore_msa && !err) { enable_msa(); err = _restore_msa_context(sc); } else { /* signal handler may have used MSA */ disable_msa(); } unlock_fpu_owner(); } else { unlock_fpu_owner(); err = copy_fp_from_sigcontext(sc); if (!err && (used_math & USEDMATH_MSA)) err = copy_msa_from_sigcontext(sc); } if (likely(!err)) break; /* touch the sigcontext and try again */ err = __get_user(tmp, &sc->sc_fpregs[0]) | __get_user(tmp, &sc->sc_fpregs[31]) | __get_user(tmp, &sc->sc_fpc_csr); if (err) break; /* really bad sigcontext */ } #else /* * EVA does not have FPU EVA instructions so restoring fpu context * directly does not work. */ enable_msa(); lose_fpu(0); err = restore_fp_context(sc); /* this might fail */ if (restore_msa && !err) err = copy_msa_from_sigcontext(sc); #endif return err; }
static int restore_sigcontext32(struct pt_regs *regs, struct sigcontext32 __user *sc) { u32 used_math; int err = 0; s32 treg; int i; /* Always make any pending restarted system calls return -EINTR */ current_thread_info()->restart_block.fn = do_no_restart_syscall; err |= __get_user(regs->cp0_epc, &sc->sc_pc); err |= __get_user(regs->hi, &sc->sc_mdhi); err |= __get_user(regs->lo, &sc->sc_mdlo); if (cpu_has_dsp) { err |= __get_user(treg, &sc->sc_hi1); mthi1(treg); err |= __get_user(treg, &sc->sc_lo1); mtlo1(treg); err |= __get_user(treg, &sc->sc_hi2); mthi2(treg); err |= __get_user(treg, &sc->sc_lo2); mtlo2(treg); err |= __get_user(treg, &sc->sc_hi3); mthi3(treg); err |= __get_user(treg, &sc->sc_lo3); mtlo3(treg); err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK); } for (i = 1; i < 32; i++) err |= __get_user(regs->regs[i], &sc->sc_regs[i]); err |= __get_user(used_math, &sc->sc_used_math); conditional_used_math(used_math); if (used_math) { /* restore fpu context if we have used it before */ if (!err) err = check_and_restore_fp_context32(sc, used_math); } else { /* signal handler may have used FPU or MSA. Disable them. */ disable_msa(); lose_fpu(0); } return err; }
static int restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) { int err = 0; u64 reg; err |= __get_user(regs->cp0_epc, &sc->sc_pc); err |= __get_user(reg, &sc->sc_mdhi); regs->hi = (int) reg; err |= __get_user(reg, &sc->sc_mdlo); regs->lo = (int) reg; #define restore_gp_reg(i) do { \ err |= __get_user(reg, &sc->sc_regs[i]); \ regs->regs[i] = reg; \ } while(0) restore_gp_reg( 1); restore_gp_reg( 2); restore_gp_reg( 3); restore_gp_reg( 4); restore_gp_reg( 5); restore_gp_reg( 6); restore_gp_reg( 7); restore_gp_reg( 8); restore_gp_reg( 9); restore_gp_reg(10); restore_gp_reg(11); restore_gp_reg(12); restore_gp_reg(13); restore_gp_reg(14); restore_gp_reg(15); restore_gp_reg(16); restore_gp_reg(17); restore_gp_reg(18); restore_gp_reg(19); restore_gp_reg(20); restore_gp_reg(21); restore_gp_reg(22); restore_gp_reg(23); restore_gp_reg(24); restore_gp_reg(25); restore_gp_reg(26); restore_gp_reg(27); restore_gp_reg(28); restore_gp_reg(29); restore_gp_reg(30); restore_gp_reg(31); #undef restore_gp_reg err |= __get_user(current->used_math, &sc->sc_used_math); if (current->used_math) { /* restore fpu context if we have used it before */ own_fpu(); err |= restore_fp_context(sc); } else { /* signal handler may have used FPU. Give it up. */ lose_fpu(); } return err; }
void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp) { unsigned long status; /* New thread loses kernel privileges. */ status = regs->cp0_status & ~(ST0_CU0|ST0_CU1|KU_MASK); #ifdef CONFIG_64BIT status &= ~ST0_FR; status |= (current->thread.mflags & MF_32BIT_REGS) ? 0 : ST0_FR; #endif status |= KU_USER; regs->cp0_status = status; clear_used_math(); lose_fpu(); if (cpu_has_dsp) __init_dsp(); regs->cp0_epc = pc; regs->regs[29] = sp; current_thread_info()->addr_limit = USER_DS; }
/* * Helper routines */ static int protected_save_fp_context(struct sigcontext __user *sc, unsigned used_math) { int err; bool save_msa = cpu_has_msa && (used_math & USEDMATH_MSA); #ifndef CONFIG_EVA while (1) { lock_fpu_owner(); if (is_fpu_owner()) { err = save_fp_context(sc); if (save_msa && !err) err = _save_msa_context(sc); unlock_fpu_owner(); } else { unlock_fpu_owner(); err = copy_fp_to_sigcontext(sc); if (save_msa && !err) err = copy_msa_to_sigcontext(sc); } if (likely(!err)) break; /* touch the sigcontext and try again */ err = __put_user(0, &sc->sc_fpregs[0]) | __put_user(0, &sc->sc_fpregs[31]) | __put_user(0, &sc->sc_fpc_csr); if (err) break; /* really bad sigcontext */ } #else /* * EVA does not have FPU EVA instructions so saving fpu context directly * does not work. */ disable_msa(); lose_fpu(1); err = save_fp_context(sc); /* this might fail */ if (save_msa && !err) err = copy_msa_to_sigcontext(sc); #endif return err; }
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); }
static void emulate_load_store_insn(struct pt_regs *regs, void __user *addr, unsigned int __user *pc) { union mips_instruction insn; unsigned long value; unsigned int res; unsigned long origpc; unsigned long orig31; void __user *fault_addr = NULL; origpc = (unsigned long)pc; orig31 = regs->regs[31]; perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0); /* * This load never faults. */ __get_user(insn.word, pc); switch (insn.i_format.opcode) { /* * These are instructions that a compiler doesn't generate. We * can assume therefore that the code is MIPS-aware and * really buggy. Emulating these instructions would break the * semantics anyway. */ case ll_op: case lld_op: case sc_op: case scd_op: /* * For these instructions the only way to create an address * error is an attempted access to kernel/supervisor address * space. */ case ldl_op: case ldr_op: case lwl_op: case lwr_op: case sdl_op: case sdr_op: case swl_op: case swr_op: case lb_op: case lbu_op: case sb_op: goto sigbus; case spec3_op: if (insn.r_format.func != lx_op) goto sigill; switch (insn.r_format.re) { case lwx_op: if (!access_ok(VERIFY_READ, addr, 4)) goto sigbus; LoadW(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.r_format.rd] = value; break; case lhx_op: if (!access_ok(VERIFY_READ, addr, 2)) goto sigbus; LoadHW(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.r_format.rd] = value; break; #ifdef CONFIG_64BIT case ldx_op: if (!access_ok(VERIFY_READ, addr, 8)) goto sigbus; LoadDW(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.r_format.rd] = value; break; case lwux_op: if (!access_ok(VERIFY_READ, addr, 4)) goto sigbus; LoadWU(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.r_format.rd] = value; break; #endif /* CONFIG_64BIT */ case lhux_op: if (!access_ok(VERIFY_READ, addr, 2)) goto sigbus; LoadHWU(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.r_format.rd] = value; break; case lbux_op: case lbx_op: goto sigbus; default: goto sigill; } break; /* * The remaining opcodes are the ones that are really of * interest. */ case lh_op: if (!access_ok(VERIFY_READ, addr, 2)) goto sigbus; LoadHW(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.i_format.rt] = value; break; case lw_op: if (!access_ok(VERIFY_READ, addr, 4)) goto sigbus; LoadW(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.i_format.rt] = value; break; case lhu_op: if (!access_ok(VERIFY_READ, addr, 2)) goto sigbus; LoadHWU(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.i_format.rt] = value; break; case lwu_op: #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; compute_return_epc(regs); regs->regs[insn.i_format.rt] = value; break; #endif /* CONFIG_64BIT */ /* Cannot handle 64-bit instructions in 32-bit kernel */ goto sigill; case ld_op: #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; compute_return_epc(regs); regs->regs[insn.i_format.rt] = value; break; #endif /* CONFIG_64BIT */ /* Cannot handle 64-bit instructions in 32-bit kernel */ goto sigill; case sh_op: if (!access_ok(VERIFY_WRITE, addr, 2)) goto sigbus; compute_return_epc(regs); value = regs->regs[insn.i_format.rt]; StoreHW(addr, value, res); if (res) goto fault; break; case sw_op: if (!access_ok(VERIFY_WRITE, addr, 4)) goto sigbus; compute_return_epc(regs); value = regs->regs[insn.i_format.rt]; StoreW(addr, value, res); if (res) goto fault; break; case sd_op: #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; compute_return_epc(regs); value = regs->regs[insn.i_format.rt]; StoreDW(addr, value, res); if (res) goto fault; break; #endif /* CONFIG_64BIT */ /* Cannot handle 64-bit instructions in 32-bit kernel */ goto sigill; case lwc1_op: case ldc1_op: case swc1_op: case sdc1_op: die_if_kernel("Unaligned FP access in kernel code", regs); BUG_ON(!used_math()); BUG_ON(!is_fpu_owner()); lose_fpu(1); /* Save FPU state for the emulator. */ res = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1, &fault_addr); own_fpu(1); /* Restore FPU state. */ /* Signal if something went wrong. */ process_fpemu_return(res, fault_addr); if (res == 0) break; return; /* * COP2 is available to implementor for application specific use. * It's up to applications to register a notifier chain and do * whatever they have to do, including possible sending of signals. */ case lwc2_op: cu2_notifier_call_chain(CU2_LWC2_OP, regs); break; case ldc2_op: cu2_notifier_call_chain(CU2_LDC2_OP, regs); break; case swc2_op: cu2_notifier_call_chain(CU2_SWC2_OP, regs); break; case sdc2_op: cu2_notifier_call_chain(CU2_SDC2_OP, regs); break; default: /* * Pheeee... We encountered an yet unknown instruction or * cache coherence problem. Die sucker, die ... */ goto sigill; } #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); }