Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615/* * Handle unaligned accesses by emulation. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1996, 1998, 1999, 2002 by Ralf Baechle * Copyright (C) 1999 Silicon Graphics, Inc. * Copyright (C) 2014 Imagination Technologies Ltd. * * This file contains exception handler for address error exception with the * special capability to execute faulting instructions in software. The * handler does not try to handle the case when the program counter points * to an address not aligned to a word boundary. * * Putting data to unaligned addresses is a bad practice even on Intel where * only the performance is affected. Much worse is that such code is non- * portable. Due to several programs that die on MIPS due to alignment * problems I decided to implement this handler anyway though I originally * didn't intend to do this at all for user code. * * For now I enable fixing of address errors by default to make life easier. * I however intend to disable this somewhen in the future when the alignment * problems with user programs have been fixed. For programmers this is the * right way to go. * * Fixing address errors is a per process option. The option is inherited * across fork(2) and execve(2) calls. If you really want to use the * option in your user programs - I discourage the use of the software * emulation strongly - use the following code in your userland stuff: * * #include <sys/sysmips.h> * * ... * sysmips(MIPS_FIXADE, x); * ... * * The argument x is 0 for disabling software emulation, enabled otherwise. * * Below a little program to play around with this feature. * * #include <stdio.h> * #include <sys/sysmips.h> * * struct foo { * unsigned char bar[8]; * }; * * main(int argc, char *argv[]) * { * struct foo x = {0, 1, 2, 3, 4, 5, 6, 7}; * unsigned int *p = (unsigned int *) (x.bar + 3); * int i; * * if (argc > 1) * sysmips(MIPS_FIXADE, atoi(argv[1])); * * printf("*p = %08lx\n", *p); * * *p = 0xdeadface; * * for(i = 0; i <= 7; i++) * printf("%02x ", x.bar[i]); * printf("\n"); * } * * Coprocessor loads are not supported; I think this case is unimportant * in the practice. * * TODO: Handle ndc (attempted store to doubleword in uncached memory) * exception for the R6000. * A store crossing a page boundary might be executed only partially. * Undo the partial store in this case. */#include <linux/context_tracking.h>#include <linux/mm.h>#include <linux/signal.h>#include <linux/smp.h>#include <linux/sched.h>#include <linux/debugfs.h>#include <linux/perf_event.h>
#include <asm/asm.h>#include <asm/branch.h>#include <asm/byteorder.h>#include <asm/cop2.h>#include <asm/debug.h>#include <asm/fpu.h>#include <asm/fpu_emulator.h>#include <asm/inst.h>#include <asm/unaligned-emul.h>#include <asm/mmu_context.h>#include <asm/traps.h>#include <linux/uaccess.h>
#include "access-helper.h"
enum { UNALIGNED_ACTION_QUIET, UNALIGNED_ACTION_SIGNAL, UNALIGNED_ACTION_SHOW,};#ifdef CONFIG_DEBUG_FSstatic u32 unaligned_instructions;static u32 unaligned_action;#else#define unaligned_action UNALIGNED_ACTION_QUIET#endifextern void show_registers(struct pt_regs *regs);
static void emulate_load_store_insn(struct pt_regs *regs, void __user *addr, unsigned int *pc){ unsigned long origpc, orig31, value; union mips_instruction insn; unsigned int res; bool user = user_mode(regs);
origpc = (unsigned long)pc; orig31 = regs->regs[31];
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
/* * This load never faults. */ __get_inst32(&insn.word, pc, user);
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;
/* * The remaining opcodes are the ones that are really of * interest. */#ifdef CONFIG_MACH_INGENIC case spec2_op: if (insn.mxu_lx_format.func != mxu_lx_op) goto sigbus; /* other MXU instructions we don't care */
switch (insn.mxu_lx_format.op) { case mxu_lxw_op: if (user && !access_ok(addr, 4)) goto sigbus; LoadW(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.mxu_lx_format.rd] = value; break; case mxu_lxh_op: if (user && !access_ok(addr, 2)) goto sigbus; LoadHW(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.dsp_format.rd] = value; break; case mxu_lxhu_op: if (user && !access_ok(addr, 2)) goto sigbus; LoadHWU(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.dsp_format.rd] = value; break; case mxu_lxb_op: case mxu_lxbu_op: goto sigbus; default: goto sigill; } break;#endif case spec3_op: if (insn.dsp_format.func == lx_op) { switch (insn.dsp_format.op) { case lwx_op: if (user && !access_ok(addr, 4)) goto sigbus; LoadW(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.dsp_format.rd] = value; break; case lhx_op: if (user && !access_ok(addr, 2)) goto sigbus; LoadHW(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.dsp_format.rd] = value; break; default: goto sigill; } }#ifdef CONFIG_EVA else { /* * we can land here only from kernel accessing user * memory, so we need to "switch" the address limit to * user space, so that address check can work properly. */ switch (insn.spec3_format.func) { case lhe_op: if (!access_ok(addr, 2)) goto sigbus; LoadHWE(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.spec3_format.rt] = value; break; case lwe_op: if (!access_ok(addr, 4)) goto sigbus; LoadWE(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.spec3_format.rt] = value; break; case lhue_op: if (!access_ok(addr, 2)) goto sigbus; LoadHWUE(addr, value, res); if (res) goto fault; compute_return_epc(regs); regs->regs[insn.spec3_format.rt] = value; break; case she_op: if (!access_ok(addr, 2)) goto sigbus; compute_return_epc(regs); value = regs->regs[insn.spec3_format.rt]; StoreHWE(addr, value, res); if (res) goto fault; break; case swe_op: if (!access_ok(addr, 4)) goto sigbus; compute_return_epc(regs); value = regs->regs[insn.spec3_format.rt]; StoreWE(addr, value, res); if (res) goto fault; break; default: goto sigill; } }#endif break; case lh_op: if (user && !access_ok(addr, 2)) goto sigbus;
if (IS_ENABLED(CONFIG_EVA) && user) LoadHWE(addr, value, res); else LoadHW(addr, value, res);
if (res) goto fault; compute_return_epc(regs); regs->regs[insn.i_format.rt] = value; break;
case lw_op: if (user && !access_ok(addr, 4)) goto sigbus;
if (IS_ENABLED(CONFIG_EVA) && user) LoadWE(addr, value, res); else LoadW(addr, value, res);
if (res) goto fault; compute_return_epc(regs); regs->regs[insn.i_format.rt] = value; break;
case lhu_op: if (user && !access_ok(addr, 2)) goto sigbus;
if (IS_ENABLED(CONFIG_EVA) && user) LoadHWUE(addr, value, res); else 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 (user && !access_ok(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 (user && !access_ok(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 (user && !access_ok(addr, 2)) goto sigbus;
compute_return_epc(regs); value = regs->regs[insn.i_format.rt];
if (IS_ENABLED(CONFIG_EVA) && user) StoreHWE(addr, value, res); else StoreHW(addr, value, res);
if (res) goto fault; break;
case sw_op: if (user && !access_ok(addr, 4)) goto sigbus;
compute_return_epc(regs); value = regs->regs[insn.i_format.rt];
if (IS_ENABLED(CONFIG_EVA) && user) StoreWE(addr, value, res); else 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 (user && !access_ok(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;
#ifdef CONFIG_MIPS_FP_SUPPORT
case lwc1_op: case ldc1_op: case swc1_op: case sdc1_op: case cop1x_op: { void __user *fault_addr = NULL;
die_if_kernel("Unaligned FP access in kernel code", regs); BUG_ON(!used_math());
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, 0);
if (res == 0) break; return; }#endif /* CONFIG_MIPS_FP_SUPPORT */
#ifdef CONFIG_CPU_HAS_MSA
case msa_op: { unsigned int wd, preempted; enum msa_2b_fmt df; union fpureg *fpr;
if (!cpu_has_msa) goto sigill;
/* * If we've reached this point then userland should have taken * the MSA disabled exception & initialised vector context at * some point in the past. */ BUG_ON(!thread_msa_context_live());
df = insn.msa_mi10_format.df; wd = insn.msa_mi10_format.wd; fpr = ¤t->thread.fpu.fpr[wd];
switch (insn.msa_mi10_format.func) { case msa_ld_op: if (!access_ok(addr, sizeof(*fpr))) goto sigbus;
do { /* * If we have live MSA context keep track of * whether we get preempted in order to avoid * the register context we load being clobbered * by the live context as it's saved during * preemption. If we don't have live context * then it can't be saved to clobber the value * we load. */ preempted = test_thread_flag(TIF_USEDMSA);
res = __copy_from_user_inatomic(fpr, addr, sizeof(*fpr)); if (res) goto fault;
/* * Update the hardware register if it is in use * by the task in this quantum, in order to * avoid having to save & restore the whole * vector context. */ preempt_disable(); if (test_thread_flag(TIF_USEDMSA)) { write_msa_wr(wd, fpr, df); preempted = 0; } preempt_enable(); } while (preempted); break;
case msa_st_op: if (!access_ok(addr, sizeof(*fpr))) goto sigbus;
/* * Update from the hardware register if it is in use by * the task in this quantum, in order to avoid having to * save & restore the whole vector context. */ preempt_disable(); if (test_thread_flag(TIF_USEDMSA)) read_msa_wr(wd, fpr, df); preempt_enable();
res = __copy_to_user_inatomic(addr, fpr, sizeof(*fpr)); if (res) goto fault; break;
default: goto sigbus; }
compute_return_epc(regs); break; }#endif /* CONFIG_CPU_HAS_MSA */
#ifndef CONFIG_CPU_MIPSR6 /* * 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. * * This instruction has been reallocated in Release 6 */ 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;#endif 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);
return;
sigbus: die_if_kernel("Unhandled kernel unaligned access", regs); force_sig(SIGBUS);
return;
sigill: die_if_kernel ("Unhandled kernel unaligned access or invalid instruction", regs); force_sig(SIGILL);}
/* Recode table from 16-bit register notation to 32-bit GPR. */const int reg16to32[] = { 16, 17, 2, 3, 4, 5, 6, 7 };
/* Recode table from 16-bit STORE register notation to 32-bit GPR. */static const int reg16to32st[] = { 0, 17, 2, 3, 4, 5, 6, 7 };
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; bool user = user_mode(regs);
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 (user && !access_ok(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 (user && !access_ok(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 (user && !access_ok(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 (user && !access_ok(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 (user && !access_ok(addr, 4 * (rvar + 1))) goto sigbus; } else { if (user && !access_ok(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 (user && !access_ok(addr, 4 * (rvar + 1))) goto sigbus; } else { if (user && !access_ok(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 (user && !access_ok(addr, 8 * (rvar + 1))) goto sigbus; } else { if (user && !access_ok(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 (user && !access_ok(addr, 8 * (rvar + 1))) goto sigbus; } else { if (user && !access_ok(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;
#ifdef CONFIG_MIPS_FP_SUPPORT 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: { void __user *fault_addr = NULL;
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());
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, 0);
if (res == 0) goto success; return; }#endif /* CONFIG_MIPS_FP_SUPPORT */
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 (user && !access_ok(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 (user && !access_ok(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 (user && !access_ok(addr, 2)) goto sigbus;
LoadHW(addr, value, res); if (res) goto fault; regs->regs[reg] = value; goto success;
loadHWU: if (user && !access_ok(addr, 2)) goto sigbus;
LoadHWU(addr, value, res); if (res) goto fault; regs->regs[reg] = value; goto success;
loadW: if (user && !access_ok(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 (user && !access_ok(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 (user && !access_ok(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 (user && !access_ok(addr, 2)) goto sigbus;
value = regs->regs[reg]; StoreHW(addr, value, res); if (res) goto fault; goto success;
storeW: if (user && !access_ok(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 (user && !access_ok(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);
return;
sigbus: die_if_kernel("Unhandled kernel unaligned access", regs); force_sig(SIGBUS);
return;
sigill: die_if_kernel ("Unhandled kernel unaligned access or invalid instruction", regs); force_sig(SIGILL);}
static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr){ unsigned long value; unsigned int res; int reg; unsigned long orig31; u16 __user *pc16; unsigned long origpc; union mips16e_instruction mips16inst, oldinst; unsigned int opcode; int extended = 0; bool user = user_mode(regs);
origpc = regs->cp0_epc; orig31 = regs->regs[31]; pc16 = (unsigned short __user *)msk_isa16_mode(origpc); /* * This load never faults. */ __get_user(mips16inst.full, pc16); oldinst = mips16inst;
/* skip EXTEND instruction */ if (mips16inst.ri.opcode == MIPS16e_extend_op) { extended = 1; pc16++; __get_user(mips16inst.full, pc16); } else if (delay_slot(regs)) { /* skip jump instructions */ /* JAL/JALX are 32 bits but have OPCODE in first short int */ if (mips16inst.ri.opcode == MIPS16e_jal_op) pc16++; pc16++; if (get_user(mips16inst.full, pc16)) goto sigbus; }
opcode = mips16inst.ri.opcode; switch (opcode) { case MIPS16e_i64_op: /* I64 or RI64 instruction */ switch (mips16inst.i64.func) { /* I64/RI64 func field check */ case MIPS16e_ldpc_func: case MIPS16e_ldsp_func: reg = reg16to32[mips16inst.ri64.ry]; goto loadDW;
case MIPS16e_sdsp_func: reg = reg16to32[mips16inst.ri64.ry]; goto writeDW;
case MIPS16e_sdrasp_func: reg = 29; /* GPRSP */ goto writeDW; }
goto sigbus;
case MIPS16e_swsp_op: reg = reg16to32[mips16inst.ri.rx]; if (extended && cpu_has_mips16e2) switch (mips16inst.ri.imm >> 5) { case 0: /* SWSP */ case 1: /* SWGP */ break; case 2: /* SHGP */ opcode = MIPS16e_sh_op; break; default: goto sigbus; } break;
case MIPS16e_lwpc_op: reg = reg16to32[mips16inst.ri.rx]; break;
case MIPS16e_lwsp_op: reg = reg16to32[mips16inst.ri.rx]; if (extended && cpu_has_mips16e2) switch (mips16inst.ri.imm >> 5) { case 0: /* LWSP */ case 1: /* LWGP */ break; case 2: /* LHGP */ opcode = MIPS16e_lh_op; break; case 4: /* LHUGP */ opcode = MIPS16e_lhu_op; break; default: goto sigbus; } break;
case MIPS16e_i8_op: if (mips16inst.i8.func != MIPS16e_swrasp_func) goto sigbus; reg = 29; /* GPRSP */ break;
default: reg = reg16to32[mips16inst.rri.ry]; break; }
switch (opcode) {
case MIPS16e_lb_op: case MIPS16e_lbu_op: case MIPS16e_sb_op: goto sigbus;
case MIPS16e_lh_op: if (user && !access_ok(addr, 2)) goto sigbus;
LoadHW(addr, value, res); if (res) goto fault; MIPS16e_compute_return_epc(regs, &oldinst); regs->regs[reg] = value; break;
case MIPS16e_lhu_op: if (user && !access_ok(addr, 2)) goto sigbus;
LoadHWU(addr, value, res); if (res) goto fault; MIPS16e_compute_return_epc(regs, &oldinst); regs->regs[reg] = value; break;
case MIPS16e_lw_op: case MIPS16e_lwpc_op: case MIPS16e_lwsp_op: if (user && !access_ok(addr, 4)) goto sigbus;
LoadW(addr, value, res); if (res) goto fault; MIPS16e_compute_return_epc(regs, &oldinst); regs->regs[reg] = value; break;
case MIPS16e_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 (user && !access_ok(addr, 4)) goto sigbus;
LoadWU(addr, value, res); if (res) goto fault; MIPS16e_compute_return_epc(regs, &oldinst); regs->regs[reg] = value; break;#endif /* CONFIG_64BIT */
/* Cannot handle 64-bit instructions in 32-bit kernel */ goto sigill;
case MIPS16e_ld_op: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 (user && !access_ok(addr, 8)) goto sigbus;
LoadDW(addr, value, res); if (res) goto fault; MIPS16e_compute_return_epc(regs, &oldinst); regs->regs[reg] = value; break;#endif /* CONFIG_64BIT */
/* Cannot handle 64-bit instructions in 32-bit kernel */ goto sigill;
case MIPS16e_sh_op: if (user && !access_ok(addr, 2)) goto sigbus;
MIPS16e_compute_return_epc(regs, &oldinst); value = regs->regs[reg]; StoreHW(addr, value, res); if (res) goto fault; break;
case MIPS16e_sw_op: case MIPS16e_swsp_op: case MIPS16e_i8_op: /* actually - MIPS16e_swrasp_func */ if (user && !access_ok(addr, 4)) goto sigbus;
MIPS16e_compute_return_epc(regs, &oldinst); value = regs->regs[reg]; StoreW(addr, value, res); if (res) goto fault; break;
case MIPS16e_sd_op:writeDW:#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 (user && !access_ok(addr, 8)) goto sigbus;
MIPS16e_compute_return_epc(regs, &oldinst); value = regs->regs[reg]; StoreDW(addr, value, res); if (res) goto fault; break;#endif /* CONFIG_64BIT */
/* Cannot handle 64-bit instructions in 32-bit kernel */ goto sigill;
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);
return;
sigbus: die_if_kernel("Unhandled kernel unaligned access", regs); force_sig(SIGBUS);
return;
sigill: die_if_kernel ("Unhandled kernel unaligned access or invalid instruction", regs); force_sig(SIGILL);}
asmlinkage void do_ade(struct pt_regs *regs){ enum ctx_state prev_state; unsigned int *pc;
prev_state = exception_enter(); perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, regs->cp0_badvaddr);
#ifdef CONFIG_64BIT /* * check, if we are hitting space between CPU implemented maximum * virtual user address and 64bit maximum virtual user address * and do exception handling to get EFAULTs for get_user/put_user */ if ((regs->cp0_badvaddr >= (1UL << cpu_vmbits)) && (regs->cp0_badvaddr < XKSSEG)) { if (fixup_exception(regs)) { current->thread.cp0_baduaddr = regs->cp0_badvaddr; return; } goto sigbus; }#endif
/* * Did we catch a fault trying to load an instruction? */ if (regs->cp0_badvaddr == regs->cp0_epc) goto sigbus;
if (user_mode(regs) && !test_thread_flag(TIF_FIXADE)) goto sigbus; if (unaligned_action == UNALIGNED_ACTION_SIGNAL) goto sigbus;
/* * Do branch emulation only if we didn't forward the exception. * This is all so but ugly ... */
/* * Are we running in microMIPS mode? */ if (get_isa16_mode(regs->cp0_epc)) { /* * Did we catch a fault trying to load an instruction in * 16-bit mode? */ if (regs->cp0_badvaddr == msk_isa16_mode(regs->cp0_epc)) goto sigbus; if (unaligned_action == UNALIGNED_ACTION_SHOW) show_registers(regs);
if (cpu_has_mmips) { emulate_load_store_microMIPS(regs, (void __user *)regs->cp0_badvaddr); return; }
if (cpu_has_mips16) { emulate_load_store_MIPS16e(regs, (void __user *)regs->cp0_badvaddr); return; }
goto sigbus; }
if (unaligned_action == UNALIGNED_ACTION_SHOW) show_registers(regs); pc = (unsigned int *)exception_epc(regs);
emulate_load_store_insn(regs, (void __user *)regs->cp0_badvaddr, pc);
return;
sigbus: die_if_kernel("Kernel unaligned instruction access", regs); force_sig(SIGBUS);
/* * XXX On return from the signal handler we should advance the epc */ exception_exit(prev_state);}
#ifdef CONFIG_DEBUG_FSstatic int __init debugfs_unaligned(void){ debugfs_create_u32("unaligned_instructions", S_IRUGO, mips_debugfs_dir, &unaligned_instructions); debugfs_create_u32("unaligned_action", S_IRUGO | S_IWUSR, mips_debugfs_dir, &unaligned_action); return 0;}arch_initcall(debugfs_unaligned);#endif