MIPS: unaligned: Add DSP lwx & lhx missaligned access support
authorMiodrag Dinic <miodrag.dinic@imgtec.com>
Mon, 19 Jun 2017 15:50:11 +0000 (17:50 +0200)
committerRalf Baechle <ralf@linux-mips.org>
Thu, 29 Jun 2017 00:42:24 +0000 (02:42 +0200)
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>
arch/mips/include/uapi/asm/inst.h
arch/mips/kernel/unaligned.c

index e5f5385..d618975 100644 (file)
@@ -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;
index f806ee5..67946bb 100644 (file)
@@ -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;