From: Miodrag Dinic <miodrag.di...@imgtec.com>

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.di...@imgtec.com>
Signed-off-by: Aleksandar Markovic <aleksandar.marko...@imgtech.com>
---
 arch/mips/include/uapi/asm/inst.h |  11 +++
 arch/mips/kernel/unaligned.c      | 174 ++++++++++++++++++++++----------------
 2 files changed, 111 insertions(+), 74 deletions(-)

diff --git a/arch/mips/include/uapi/asm/inst.h 
b/arch/mips/include/uapi/asm/inst.h
index b5e46ae..302ad9a 100644
--- a/arch/mips/include/uapi/asm/inst.h
+++ b/arch/mips/include/uapi/asm/inst.h
@@ -755,6 +755,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,
@@ -1046,6 +1056,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;
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
index f806ee5..67946bb 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;
-- 
2.7.4

Reply via email to