diff options
author | Michael Ellerman <mpe@ellerman.id.au> | 2017-07-31 20:20:29 +1000 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2017-07-31 20:20:29 +1000 |
commit | bb272221e9db79f13d454e1f3fb6b05013be985e (patch) | |
tree | 36f4acc50e3fabac71fadd34c720c0a6011db470 /arch/mips/kernel/unaligned.c | |
parent | 253fd51e2f533552ae35a0c661705da6c4842c1b (diff) | |
parent | 5771a8c08880cdca3bfb4a3fc6d309d6bba20877 (diff) | |
download | linux-bb272221e9db79f13d454e1f3fb6b05013be985e.tar.gz linux-bb272221e9db79f13d454e1f3fb6b05013be985e.tar.bz2 linux-bb272221e9db79f13d454e1f3fb6b05013be985e.zip |
Merge tag 'v4.13-rc1' into fixes
The fixes branch is based off a random pre-rc1 commit, because we had
some fixes that needed to go in before rc1 was released.
However we now need to fix some code that went in after that point, but
before rc1, so merge rc1 to get that code into fixes so we can fix it!
Diffstat (limited to 'arch/mips/kernel/unaligned.c')
-rw-r--r-- | arch/mips/kernel/unaligned.c | 213 |
1 files changed, 137 insertions, 76 deletions
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c index f806ee56e639..5eaf2578ac04 100644 --- a/arch/mips/kernel/unaligned.c +++ b/arch/mips/kernel/unaligned.c @@ -939,88 +939,114 @@ static void emulate_load_store_insn(struct pt_regs *regs, * The remaining opcodes are the ones that are really of * interest. */ -#ifdef CONFIG_EVA case spec3_op: - /* - * we can land here only from kernel accessing user memory, - * so we need to "switch" the address limit to user space, so - * address check can work properly. - */ - seg = get_fs(); - set_fs(USER_DS); - switch (insn.spec3_format.func) { - case lhe_op: - if (!access_ok(VERIFY_READ, addr, 2)) { - set_fs(seg); - goto sigbus; - } - LoadHWE(addr, value, res); - if (res) { - set_fs(seg); - goto fault; - } - compute_return_epc(regs); - regs->regs[insn.spec3_format.rt] = value; - break; - case lwe_op: - if (!access_ok(VERIFY_READ, addr, 4)) { - set_fs(seg); - goto sigbus; + if (insn.dsp_format.func == lx_op) { + switch (insn.dsp_format.op) { + 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.dsp_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.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. + */ + seg = get_fs(); + set_fs(USER_DS); + switch (insn.spec3_format.func) { + case lhe_op: + if (!access_ok(VERIFY_READ, addr, 2)) { + set_fs(seg); + goto sigbus; + } + LoadHWE(addr, value, res); + if (res) { + set_fs(seg); + goto fault; + } + compute_return_epc(regs); + regs->regs[insn.spec3_format.rt] = value; + break; + case lwe_op: + if (!access_ok(VERIFY_READ, addr, 4)) { + set_fs(seg); + goto sigbus; + } LoadWE(addr, value, res); - if (res) { - set_fs(seg); - goto fault; - } - compute_return_epc(regs); - regs->regs[insn.spec3_format.rt] = value; - break; - case lhue_op: - if (!access_ok(VERIFY_READ, addr, 2)) { - set_fs(seg); - goto sigbus; - } - LoadHWUE(addr, value, res); - if (res) { - set_fs(seg); - goto fault; - } - compute_return_epc(regs); - regs->regs[insn.spec3_format.rt] = value; - break; - case she_op: - if (!access_ok(VERIFY_WRITE, addr, 2)) { - set_fs(seg); - goto sigbus; - } - compute_return_epc(regs); - value = regs->regs[insn.spec3_format.rt]; - StoreHWE(addr, value, res); - if (res) { - set_fs(seg); - goto fault; - } - break; - case swe_op: - if (!access_ok(VERIFY_WRITE, addr, 4)) { - set_fs(seg); - goto sigbus; - } - compute_return_epc(regs); - value = regs->regs[insn.spec3_format.rt]; - StoreWE(addr, value, res); - if (res) { + if (res) { + set_fs(seg); + goto fault; + } + compute_return_epc(regs); + regs->regs[insn.spec3_format.rt] = value; + break; + case lhue_op: + if (!access_ok(VERIFY_READ, addr, 2)) { + set_fs(seg); + goto sigbus; + } + LoadHWUE(addr, value, res); + if (res) { + set_fs(seg); + goto fault; + } + compute_return_epc(regs); + regs->regs[insn.spec3_format.rt] = value; + break; + case she_op: + if (!access_ok(VERIFY_WRITE, addr, 2)) { + set_fs(seg); + goto sigbus; + } + compute_return_epc(regs); + value = regs->regs[insn.spec3_format.rt]; + StoreHWE(addr, value, res); + if (res) { + set_fs(seg); + goto fault; + } + break; + case swe_op: + if (!access_ok(VERIFY_WRITE, addr, 4)) { + set_fs(seg); + goto sigbus; + } + compute_return_epc(regs); + value = regs->regs[insn.spec3_format.rt]; + StoreWE(addr, value, res); + if (res) { + set_fs(seg); + goto fault; + } + break; + default: set_fs(seg); - goto fault; + goto sigill; } - break; - default: set_fs(seg); - goto sigill; } - set_fs(seg); - break; #endif + break; case lh_op: if (!access_ok(VERIFY_READ, addr, 2)) goto sigbus; @@ -1984,6 +2010,8 @@ static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr) u16 __user *pc16; unsigned long origpc; union mips16e_instruction mips16inst, oldinst; + unsigned int opcode; + int extended = 0; origpc = regs->cp0_epc; orig31 = regs->regs[31]; @@ -1996,6 +2024,7 @@ static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr) /* skip EXTEND instruction */ if (mips16inst.ri.opcode == MIPS16e_extend_op) { + extended = 1; pc16++; __get_user(mips16inst.full, pc16); } else if (delay_slot(regs)) { @@ -2008,7 +2037,8 @@ static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr) goto sigbus; } - switch (mips16inst.ri.opcode) { + 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: @@ -2028,9 +2058,40 @@ static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr) 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: @@ -2044,7 +2105,7 @@ static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr) break; } - switch (mips16inst.ri.opcode) { + switch (opcode) { case MIPS16e_lb_op: case MIPS16e_lbu_op: |