summaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/unaligned.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/kernel/unaligned.c')
-rw-r--r--arch/mips/kernel/unaligned.c205
1 files changed, 75 insertions, 130 deletions
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
index 126a5f3f4e4c..df4b708c04a9 100644
--- a/arch/mips/kernel/unaligned.c
+++ b/arch/mips/kernel/unaligned.c
@@ -93,6 +93,8 @@
#include <asm/mmu_context.h>
#include <linux/uaccess.h>
+#include "access-helper.h"
+
enum {
UNALIGNED_ACTION_QUIET,
UNALIGNED_ACTION_SIGNAL,
@@ -107,14 +109,13 @@ static u32 unaligned_action;
extern void show_registers(struct pt_regs *regs);
static void emulate_load_store_insn(struct pt_regs *regs,
- void __user *addr, unsigned int __user *pc)
+ void __user *addr, unsigned int *pc)
{
unsigned long origpc, orig31, value;
union mips_instruction insn;
unsigned int res;
-#ifdef CONFIG_EVA
- mm_segment_t seg;
-#endif
+ bool user = user_mode(regs);
+
origpc = (unsigned long)pc;
orig31 = regs->regs[31];
@@ -123,7 +124,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
/*
* This load never faults.
*/
- __get_user(insn.word, pc);
+ __get_inst32(&insn.word, pc, user);
switch (insn.i_format.opcode) {
/*
@@ -163,7 +164,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
if (insn.dsp_format.func == lx_op) {
switch (insn.dsp_format.op) {
case lwx_op:
- if (!access_ok(addr, 4))
+ if (user && !access_ok(addr, 4))
goto sigbus;
LoadW(addr, value, res);
if (res)
@@ -172,7 +173,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
regs->regs[insn.dsp_format.rd] = value;
break;
case lhx_op:
- if (!access_ok(addr, 2))
+ if (user && !access_ok(addr, 2))
goto sigbus;
LoadHW(addr, value, res);
if (res)
@@ -191,93 +192,66 @@ static void emulate_load_store_insn(struct pt_regs *regs,
* memory, so we need to "switch" the address limit to
* user space, so that address check can work properly.
*/
- seg = force_uaccess_begin();
switch (insn.spec3_format.func) {
case lhe_op:
- if (!access_ok(addr, 2)) {
- force_uaccess_end(seg);
+ if (!access_ok(addr, 2))
goto sigbus;
- }
LoadHWE(addr, value, res);
- if (res) {
- force_uaccess_end(seg);
+ if (res)
goto fault;
- }
compute_return_epc(regs);
regs->regs[insn.spec3_format.rt] = value;
break;
case lwe_op:
- if (!access_ok(addr, 4)) {
- force_uaccess_end(seg);
+ if (!access_ok(addr, 4))
goto sigbus;
- }
LoadWE(addr, value, res);
- if (res) {
- force_uaccess_end(seg);
+ if (res)
goto fault;
- }
compute_return_epc(regs);
regs->regs[insn.spec3_format.rt] = value;
break;
case lhue_op:
- if (!access_ok(addr, 2)) {
- force_uaccess_end(seg);
+ if (!access_ok(addr, 2))
goto sigbus;
- }
LoadHWUE(addr, value, res);
- if (res) {
- force_uaccess_end(seg);
+ if (res)
goto fault;
- }
compute_return_epc(regs);
regs->regs[insn.spec3_format.rt] = value;
break;
case she_op:
- if (!access_ok(addr, 2)) {
- force_uaccess_end(seg);
+ if (!access_ok(addr, 2))
goto sigbus;
- }
compute_return_epc(regs);
value = regs->regs[insn.spec3_format.rt];
StoreHWE(addr, value, res);
- if (res) {
- force_uaccess_end(seg);
+ if (res)
goto fault;
- }
break;
case swe_op:
- if (!access_ok(addr, 4)) {
- force_uaccess_end(seg);
+ if (!access_ok(addr, 4))
goto sigbus;
- }
compute_return_epc(regs);
value = regs->regs[insn.spec3_format.rt];
StoreWE(addr, value, res);
- if (res) {
- force_uaccess_end(seg);
+ if (res)
goto fault;
- }
break;
default:
- force_uaccess_end(seg);
goto sigill;
}
- force_uaccess_end(seg);
}
#endif
break;
case lh_op:
- if (!access_ok(addr, 2))
+ if (user && !access_ok(addr, 2))
goto sigbus;
- if (IS_ENABLED(CONFIG_EVA)) {
- if (uaccess_kernel())
- LoadHW(addr, value, res);
- else
- LoadHWE(addr, value, res);
- } else {
+ if (IS_ENABLED(CONFIG_EVA) && user)
+ LoadHWE(addr, value, res);
+ else
LoadHW(addr, value, res);
- }
if (res)
goto fault;
@@ -286,17 +260,13 @@ static void emulate_load_store_insn(struct pt_regs *regs,
break;
case lw_op:
- if (!access_ok(addr, 4))
+ if (user && !access_ok(addr, 4))
goto sigbus;
- if (IS_ENABLED(CONFIG_EVA)) {
- if (uaccess_kernel())
- LoadW(addr, value, res);
- else
- LoadWE(addr, value, res);
- } else {
+ if (IS_ENABLED(CONFIG_EVA) && user)
+ LoadWE(addr, value, res);
+ else
LoadW(addr, value, res);
- }
if (res)
goto fault;
@@ -305,17 +275,13 @@ static void emulate_load_store_insn(struct pt_regs *regs,
break;
case lhu_op:
- if (!access_ok(addr, 2))
+ if (user && !access_ok(addr, 2))
goto sigbus;
- if (IS_ENABLED(CONFIG_EVA)) {
- if (uaccess_kernel())
- LoadHWU(addr, value, res);
- else
- LoadHWUE(addr, value, res);
- } else {
+ if (IS_ENABLED(CONFIG_EVA) && user)
+ LoadHWUE(addr, value, res);
+ else
LoadHWU(addr, value, res);
- }
if (res)
goto fault;
@@ -332,7 +298,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
* would blow up, so for now we don't handle unaligned 64-bit
* instructions on 32-bit kernels.
*/
- if (!access_ok(addr, 4))
+ if (user && !access_ok(addr, 4))
goto sigbus;
LoadWU(addr, value, res);
@@ -355,7 +321,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
* would blow up, so for now we don't handle unaligned 64-bit
* instructions on 32-bit kernels.
*/
- if (!access_ok(addr, 8))
+ if (user && !access_ok(addr, 8))
goto sigbus;
LoadDW(addr, value, res);
@@ -370,40 +336,32 @@ static void emulate_load_store_insn(struct pt_regs *regs,
goto sigill;
case sh_op:
- if (!access_ok(addr, 2))
+ if (user && !access_ok(addr, 2))
goto sigbus;
compute_return_epc(regs);
value = regs->regs[insn.i_format.rt];
- if (IS_ENABLED(CONFIG_EVA)) {
- if (uaccess_kernel())
- StoreHW(addr, value, res);
- else
- StoreHWE(addr, value, res);
- } else {
+ if (IS_ENABLED(CONFIG_EVA) && user)
+ StoreHWE(addr, value, res);
+ else
StoreHW(addr, value, res);
- }
if (res)
goto fault;
break;
case sw_op:
- if (!access_ok(addr, 4))
+ if (user && !access_ok(addr, 4))
goto sigbus;
compute_return_epc(regs);
value = regs->regs[insn.i_format.rt];
- if (IS_ENABLED(CONFIG_EVA)) {
- if (uaccess_kernel())
- StoreW(addr, value, res);
- else
- StoreWE(addr, value, res);
- } else {
+ if (IS_ENABLED(CONFIG_EVA) && user)
+ StoreWE(addr, value, res);
+ else
StoreW(addr, value, res);
- }
if (res)
goto fault;
@@ -418,7 +376,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
* would blow up, so for now we don't handle unaligned 64-bit
* instructions on 32-bit kernels.
*/
- if (!access_ok(addr, 8))
+ if (user && !access_ok(addr, 8))
goto sigbus;
compute_return_epc(regs);
@@ -626,6 +584,7 @@ static void emulate_load_store_microMIPS(struct pt_regs *regs,
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];
@@ -689,7 +648,7 @@ static void emulate_load_store_microMIPS(struct pt_regs *regs,
if (reg == 31)
goto sigbus;
- if (!access_ok(addr, 8))
+ if (user && !access_ok(addr, 8))
goto sigbus;
LoadW(addr, value, res);
@@ -708,7 +667,7 @@ static void emulate_load_store_microMIPS(struct pt_regs *regs,
if (reg == 31)
goto sigbus;
- if (!access_ok(addr, 8))
+ if (user && !access_ok(addr, 8))
goto sigbus;
value = regs->regs[reg];
@@ -728,7 +687,7 @@ static void emulate_load_store_microMIPS(struct pt_regs *regs,
if (reg == 31)
goto sigbus;
- if (!access_ok(addr, 16))
+ if (user && !access_ok(addr, 16))
goto sigbus;
LoadDW(addr, value, res);
@@ -751,7 +710,7 @@ static void emulate_load_store_microMIPS(struct pt_regs *regs,
if (reg == 31)
goto sigbus;
- if (!access_ok(addr, 16))
+ if (user && !access_ok(addr, 16))
goto sigbus;
value = regs->regs[reg];
@@ -774,10 +733,10 @@ static void emulate_load_store_microMIPS(struct pt_regs *regs,
if ((rvar > 9) || !reg)
goto sigill;
if (reg & 0x10) {
- if (!access_ok(addr, 4 * (rvar + 1)))
+ if (user && !access_ok(addr, 4 * (rvar + 1)))
goto sigbus;
} else {
- if (!access_ok(addr, 4 * rvar))
+ if (user && !access_ok(addr, 4 * rvar))
goto sigbus;
}
if (rvar == 9)
@@ -810,10 +769,10 @@ static void emulate_load_store_microMIPS(struct pt_regs *regs,
if ((rvar > 9) || !reg)
goto sigill;
if (reg & 0x10) {
- if (!access_ok(addr, 4 * (rvar + 1)))
+ if (user && !access_ok(addr, 4 * (rvar + 1)))
goto sigbus;
} else {
- if (!access_ok(addr, 4 * rvar))
+ if (user && !access_ok(addr, 4 * rvar))
goto sigbus;
}
if (rvar == 9)
@@ -847,10 +806,10 @@ static void emulate_load_store_microMIPS(struct pt_regs *regs,
if ((rvar > 9) || !reg)
goto sigill;
if (reg & 0x10) {
- if (!access_ok(addr, 8 * (rvar + 1)))
+ if (user && !access_ok(addr, 8 * (rvar + 1)))
goto sigbus;
} else {
- if (!access_ok(addr, 8 * rvar))
+ if (user && !access_ok(addr, 8 * rvar))
goto sigbus;
}
if (rvar == 9)
@@ -888,10 +847,10 @@ static void emulate_load_store_microMIPS(struct pt_regs *regs,
if ((rvar > 9) || !reg)
goto sigill;
if (reg & 0x10) {
- if (!access_ok(addr, 8 * (rvar + 1)))
+ if (user && !access_ok(addr, 8 * (rvar + 1)))
goto sigbus;
} else {
- if (!access_ok(addr, 8 * rvar))
+ if (user && !access_ok(addr, 8 * rvar))
goto sigbus;
}
if (rvar == 9)
@@ -1010,7 +969,7 @@ fpu_emul:
case mm_lwm16_op:
reg = insn.mm16_m_format.rlist;
rvar = reg + 1;
- if (!access_ok(addr, 4 * rvar))
+ if (user && !access_ok(addr, 4 * rvar))
goto sigbus;
for (i = 16; rvar; rvar--, i++) {
@@ -1030,7 +989,7 @@ fpu_emul:
case mm_swm16_op:
reg = insn.mm16_m_format.rlist;
rvar = reg + 1;
- if (!access_ok(addr, 4 * rvar))
+ if (user && !access_ok(addr, 4 * rvar))
goto sigbus;
for (i = 16; rvar; rvar--, i++) {
@@ -1084,7 +1043,7 @@ fpu_emul:
}
loadHW:
- if (!access_ok(addr, 2))
+ if (user && !access_ok(addr, 2))
goto sigbus;
LoadHW(addr, value, res);
@@ -1094,7 +1053,7 @@ loadHW:
goto success;
loadHWU:
- if (!access_ok(addr, 2))
+ if (user && !access_ok(addr, 2))
goto sigbus;
LoadHWU(addr, value, res);
@@ -1104,7 +1063,7 @@ loadHWU:
goto success;
loadW:
- if (!access_ok(addr, 4))
+ if (user && !access_ok(addr, 4))
goto sigbus;
LoadW(addr, value, res);
@@ -1122,7 +1081,7 @@ loadWU:
* would blow up, so for now we don't handle unaligned 64-bit
* instructions on 32-bit kernels.
*/
- if (!access_ok(addr, 4))
+ if (user && !access_ok(addr, 4))
goto sigbus;
LoadWU(addr, value, res);
@@ -1144,7 +1103,7 @@ loadDW:
* would blow up, so for now we don't handle unaligned 64-bit
* instructions on 32-bit kernels.
*/
- if (!access_ok(addr, 8))
+ if (user && !access_ok(addr, 8))
goto sigbus;
LoadDW(addr, value, res);
@@ -1158,7 +1117,7 @@ loadDW:
goto sigill;
storeHW:
- if (!access_ok(addr, 2))
+ if (user && !access_ok(addr, 2))
goto sigbus;
value = regs->regs[reg];
@@ -1168,7 +1127,7 @@ storeHW:
goto success;
storeW:
- if (!access_ok(addr, 4))
+ if (user && !access_ok(addr, 4))
goto sigbus;
value = regs->regs[reg];
@@ -1186,7 +1145,7 @@ storeDW:
* would blow up, so for now we don't handle unaligned 64-bit
* instructions on 32-bit kernels.
*/
- if (!access_ok(addr, 8))
+ if (user && !access_ok(addr, 8))
goto sigbus;
value = regs->regs[reg];
@@ -1243,6 +1202,7 @@ static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr)
union mips16e_instruction mips16inst, oldinst;
unsigned int opcode;
int extended = 0;
+ bool user = user_mode(regs);
origpc = regs->cp0_epc;
orig31 = regs->regs[31];
@@ -1344,7 +1304,7 @@ static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr)
goto sigbus;
case MIPS16e_lh_op:
- if (!access_ok(addr, 2))
+ if (user && !access_ok(addr, 2))
goto sigbus;
LoadHW(addr, value, res);
@@ -1355,7 +1315,7 @@ static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr)
break;
case MIPS16e_lhu_op:
- if (!access_ok(addr, 2))
+ if (user && !access_ok(addr, 2))
goto sigbus;
LoadHWU(addr, value, res);
@@ -1368,7 +1328,7 @@ static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr)
case MIPS16e_lw_op:
case MIPS16e_lwpc_op:
case MIPS16e_lwsp_op:
- if (!access_ok(addr, 4))
+ if (user && !access_ok(addr, 4))
goto sigbus;
LoadW(addr, value, res);
@@ -1387,7 +1347,7 @@ static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr)
* would blow up, so for now we don't handle unaligned 64-bit
* instructions on 32-bit kernels.
*/
- if (!access_ok(addr, 4))
+ if (user && !access_ok(addr, 4))
goto sigbus;
LoadWU(addr, value, res);
@@ -1411,7 +1371,7 @@ loadDW:
* would blow up, so for now we don't handle unaligned 64-bit
* instructions on 32-bit kernels.
*/
- if (!access_ok(addr, 8))
+ if (user && !access_ok(addr, 8))
goto sigbus;
LoadDW(addr, value, res);
@@ -1426,7 +1386,7 @@ loadDW:
goto sigill;
case MIPS16e_sh_op:
- if (!access_ok(addr, 2))
+ if (user && !access_ok(addr, 2))
goto sigbus;
MIPS16e_compute_return_epc(regs, &oldinst);
@@ -1439,7 +1399,7 @@ loadDW:
case MIPS16e_sw_op:
case MIPS16e_swsp_op:
case MIPS16e_i8_op: /* actually - MIPS16e_swrasp_func */
- if (!access_ok(addr, 4))
+ if (user && !access_ok(addr, 4))
goto sigbus;
MIPS16e_compute_return_epc(regs, &oldinst);
@@ -1459,7 +1419,7 @@ writeDW:
* would blow up, so for now we don't handle unaligned 64-bit
* instructions on 32-bit kernels.
*/
- if (!access_ok(addr, 8))
+ if (user && !access_ok(addr, 8))
goto sigbus;
MIPS16e_compute_return_epc(regs, &oldinst);
@@ -1515,8 +1475,7 @@ sigill:
asmlinkage void do_ade(struct pt_regs *regs)
{
enum ctx_state prev_state;
- unsigned int __user *pc;
- mm_segment_t seg;
+ unsigned int *pc;
prev_state = exception_enter();
perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS,
@@ -1551,24 +1510,14 @@ asmlinkage void do_ade(struct pt_regs *regs)
show_registers(regs);
if (cpu_has_mmips) {
- seg = get_fs();
- if (!user_mode(regs))
- set_fs(KERNEL_DS);
emulate_load_store_microMIPS(regs,
(void __user *)regs->cp0_badvaddr);
- set_fs(seg);
-
return;
}
if (cpu_has_mips16) {
- seg = get_fs();
- if (!user_mode(regs))
- set_fs(KERNEL_DS);
emulate_load_store_MIPS16e(regs,
(void __user *)regs->cp0_badvaddr);
- set_fs(seg);
-
return;
}
@@ -1577,13 +1526,9 @@ asmlinkage void do_ade(struct pt_regs *regs)
if (unaligned_action == UNALIGNED_ACTION_SHOW)
show_registers(regs);
- pc = (unsigned int __user *)exception_epc(regs);
+ pc = (unsigned int *)exception_epc(regs);
- seg = get_fs();
- if (!user_mode(regs))
- set_fs(KERNEL_DS);
emulate_load_store_insn(regs, (void __user *)regs->cp0_badvaddr, pc);
- set_fs(seg);
return;