MIPS: unaligned: Add DSP lwx & lhx missaligned access support
Add handling of missaligned access for DSP load instructions lwx & lhx. Since DSP instructions share SPECIAL3 opcode with other non-DSP instructions, necessary logic was inserted for distinguishing between instructions with SPECIAL3 opcode. For that purpose, the instruction format for DSP instructions is added to arch/mips/include/uapi/asm/inst.h. Signed-off-by: Miodrag Dinic <miodrag.dinic@imgtec.com> Signed-off-by: Aleksandar Markovic <aleksandar.markovic@imgtech.com> Cc: James.Hogan@imgtec.com Cc: Paul.Burton@imgtec.com Cc: Raghu.Gandham@imgtec.com Cc: Leonid.Yegoshin@imgtec.com Cc: Douglas.Leung@imgtec.com Cc: Petar.Jovanovic@imgtec.com Cc: Goran.Ferenc@imgtec.com Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/16511/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
Родитель
3daf281f2c
Коммит
3f88ec6333
|
@ -762,6 +762,16 @@ struct msa_mi10_format { /* MSA MI10 */
|
|||
;))))))
|
||||
};
|
||||
|
||||
struct dsp_format { /* SPEC3 DSP format instructions */
|
||||
__BITFIELD_FIELD(unsigned int opcode : 6,
|
||||
__BITFIELD_FIELD(unsigned int base : 5,
|
||||
__BITFIELD_FIELD(unsigned int index : 5,
|
||||
__BITFIELD_FIELD(unsigned int rd : 5,
|
||||
__BITFIELD_FIELD(unsigned int op : 5,
|
||||
__BITFIELD_FIELD(unsigned int func : 6,
|
||||
;))))))
|
||||
};
|
||||
|
||||
struct spec3_format { /* SPEC3 */
|
||||
__BITFIELD_FIELD(unsigned int opcode:6,
|
||||
__BITFIELD_FIELD(unsigned int rs:5,
|
||||
|
@ -1053,6 +1063,7 @@ union mips_instruction {
|
|||
struct b_format b_format;
|
||||
struct ps_format ps_format;
|
||||
struct v_format v_format;
|
||||
struct dsp_format dsp_format;
|
||||
struct spec3_format spec3_format;
|
||||
struct fb_format fb_format;
|
||||
struct fp0_format fp0_format;
|
||||
|
|
|
@ -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;
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
set_fs(seg);
|
||||
goto fault;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
set_fs(seg);
|
||||
goto sigill;
|
||||
}
|
||||
set_fs(seg);
|
||||
break;
|
||||
#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) {
|
||||
set_fs(seg);
|
||||
goto fault;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
set_fs(seg);
|
||||
goto sigill;
|
||||
}
|
||||
set_fs(seg);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case lh_op:
|
||||
if (!access_ok(VERIFY_READ, addr, 2))
|
||||
goto sigbus;
|
||||
|
|
Загрузка…
Ссылка в новой задаче