ARM: probes: fix instruction fetch order with <asm/opcodes.h>
authorBen Dooks <ben.dooks@codethink.co.uk>
Fri, 8 Nov 2013 18:29:25 +0000 (18:29 +0000)
committerTaras Kondratiuk <taras@ti.com>
Tue, 1 Apr 2014 13:45:19 +0000 (16:45 +0300)
If we are running BE8, the data and instruction endianness do not
match, so use <asm/opcodes.h> to correctly translate memory accesses
into ARM instructions.

Acked-by: Jon Medhurst <tixy@linaro.org>
Signed-off-by: Ben Dooks <ben.dooks@codethink.co.uk>
[taras.kondratiuk@linaro.org: fixed Thumb instruction fetch order]
Signed-off-by: Taras Kondratiuk <taras.kondratiuk@linaro.org>
arch/arm/kernel/kprobes-common.c
arch/arm/kernel/kprobes-thumb.c
arch/arm/kernel/kprobes.c
arch/arm/kernel/probes.c

index c311ed9..0bf5d64 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/kernel.h>
 #include <linux/kprobes.h>
+#include <asm/opcodes.h>
 
 #include "kprobes.h"
 
@@ -153,7 +154,8 @@ kprobe_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi,
 
        if (handler) {
                /* We can emulate the instruction in (possibly) modified form */
-               asi->insn[0] = (insn & 0xfff00000) | (rn << 16) | reglist;
+               asi->insn[0] = __opcode_to_mem_arm((insn & 0xfff00000) |
+                                                  (rn << 16) | reglist);
                asi->insn_handler = handler;
                return INSN_GOOD;
        }
index 6619188..9495d7f 100644 (file)
@@ -149,9 +149,9 @@ t32_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi,
        enum probes_insn ret = kprobe_decode_ldmstm(insn, asi, d);
 
        /* Fixup modified instruction to have halfwords in correct order...*/
-       insn = asi->insn[0];
-       ((u16 *)asi->insn)[0] = insn >> 16;
-       ((u16 *)asi->insn)[1] = insn & 0xffff;
+       insn = __mem_to_opcode_arm(asi->insn[0]);
+       ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn >> 16);
+       ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0xffff);
 
        return ret;
 }
@@ -516,7 +516,7 @@ t16_decode_hiregs(probes_opcode_t insn, struct arch_probes_insn *asi,
 {
        insn &= ~0x00ff;
        insn |= 0x001; /* Set Rdn = R1 and Rm = R0 */
-       ((u16 *)asi->insn)[0] = insn;
+       ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(insn);
        asi->insn_handler = t16_emulate_hiregs;
        return INSN_GOOD;
 }
@@ -547,8 +547,10 @@ t16_decode_push(probes_opcode_t insn, struct arch_probes_insn *asi,
         * and call it with R9=SP and LR in the register list represented
         * by R8.
         */
-       ((u16 *)asi->insn)[0] = 0xe929;         /* 1st half STMDB R9!,{} */
-       ((u16 *)asi->insn)[1] = insn & 0x1ff;   /* 2nd half (register list) */
+       /* 1st half STMDB R9!,{} */
+       ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe929);
+       /* 2nd half (register list) */
+       ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff);
        asi->insn_handler = t16_emulate_push;
        return INSN_GOOD;
 }
@@ -600,8 +602,10 @@ t16_decode_pop(probes_opcode_t insn, struct arch_probes_insn *asi,
         * and call it with R9=SP and PC in the register list represented
         * by R8.
         */
-       ((u16 *)asi->insn)[0] = 0xe8b9;         /* 1st half LDMIA R9!,{} */
-       ((u16 *)asi->insn)[1] = insn & 0x1ff;   /* 2nd half (register list) */
+       /* 1st half LDMIA R9!,{} */
+       ((u16 *)asi->insn)[0] = __opcode_to_mem_thumb16(0xe8b9);
+       /* 2nd half (register list) */
+       ((u16 *)asi->insn)[1] = __opcode_to_mem_thumb16(insn & 0x1ff);
        asi->insn_handler = insn & 0x100 ? t16_emulate_pop_pc
                                         : t16_emulate_pop_nopc;
        return INSN_GOOD;
index 8795f9f..6d64420 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/stop_machine.h>
 #include <linux/stringify.h>
 #include <asm/traps.h>
+#include <asm/opcodes.h>
 #include <asm/cacheflush.h>
 #include <linux/percpu.h>
 #include <linux/bug.h>
@@ -67,10 +68,10 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
 #ifdef CONFIG_THUMB2_KERNEL
        thumb = true;
        addr &= ~1; /* Bit 0 would normally be set to indicate Thumb code */
-       insn = ((u16 *)addr)[0];
+       insn = __mem_to_opcode_thumb16(((u16 *)addr)[0]);
        if (is_wide_instruction(insn)) {
-               insn <<= 16;
-               insn |= ((u16 *)addr)[1];
+               u16 inst2 = __mem_to_opcode_thumb16(((u16 *)addr)[1]);
+               insn = __opcode_thumb32_compose(insn, inst2);
                decode_insn = thumb32_probes_decode_insn;
                actions = kprobes_t32_actions;
        } else {
@@ -81,7 +82,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
        thumb = false;
        if (addr & 0x3)
                return -EINVAL;
-       insn = *p->addr;
+       insn = __mem_to_opcode_arm(*p->addr);
        decode_insn = arm_probes_decode_insn;
        actions = kprobes_arm_actions;
 #endif
index b41873f..a8ab540 100644 (file)
@@ -202,13 +202,14 @@ prepare_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
 #ifdef CONFIG_THUMB2_KERNEL
        if (thumb) {
                u16 *thumb_insn = (u16 *)asi->insn;
-               thumb_insn[1] = 0x4770; /* Thumb bx lr */
-               thumb_insn[2] = 0x4770; /* Thumb bx lr */
+               /* Thumb bx lr */
+               thumb_insn[1] = __opcode_to_mem_thumb16(0x4770);
+               thumb_insn[2] = __opcode_to_mem_thumb16(0x4770);
                return insn;
        }
-       asi->insn[1] = 0xe12fff1e; /* ARM bx lr */
+       asi->insn[1] = __opcode_to_mem_arm(0xe12fff1e); /* ARM bx lr */
 #else
-       asi->insn[1] = 0xe1a0f00e; /* mov pc, lr */
+       asi->insn[1] = __opcode_to_mem_arm(0xe1a0f00e); /* mov pc, lr */
 #endif
        /* Make an ARM instruction unconditional */
        if (insn < 0xe0000000)
@@ -228,12 +229,12 @@ set_emulated_insn(probes_opcode_t insn, struct arch_probes_insn *asi,
        if (thumb) {
                u16 *ip = (u16 *)asi->insn;
                if (is_wide_instruction(insn))
-                       *ip++ = insn >> 16;
-               *ip++ = insn;
+                       *ip++ = __opcode_to_mem_thumb16(insn >> 16);
+               *ip++ = __opcode_to_mem_thumb16(insn);
                return;
        }
 #endif
-       asi->insn[0] = insn;
+       asi->insn[0] = __opcode_to_mem_arm(insn);
 }
 
 /*