0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/kernel.h>
0012 #include <linux/kprobes.h>
0013 #include <asm/opcodes.h>
0014
0015 #include "core.h"
0016
0017
0018 static void __kprobes simulate_ldm1stm1(probes_opcode_t insn,
0019 struct arch_probes_insn *asi,
0020 struct pt_regs *regs)
0021 {
0022 int rn = (insn >> 16) & 0xf;
0023 int lbit = insn & (1 << 20);
0024 int wbit = insn & (1 << 21);
0025 int ubit = insn & (1 << 23);
0026 int pbit = insn & (1 << 24);
0027 long *addr = (long *)regs->uregs[rn];
0028 int reg_bit_vector;
0029 int reg_count;
0030
0031 reg_count = 0;
0032 reg_bit_vector = insn & 0xffff;
0033 while (reg_bit_vector) {
0034 reg_bit_vector &= (reg_bit_vector - 1);
0035 ++reg_count;
0036 }
0037
0038 if (!ubit)
0039 addr -= reg_count;
0040 addr += (!pbit == !ubit);
0041
0042 reg_bit_vector = insn & 0xffff;
0043 while (reg_bit_vector) {
0044 int reg = __ffs(reg_bit_vector);
0045 reg_bit_vector &= (reg_bit_vector - 1);
0046 if (lbit)
0047 regs->uregs[reg] = *addr++;
0048 else
0049 *addr++ = regs->uregs[reg];
0050 }
0051
0052 if (wbit) {
0053 if (!ubit)
0054 addr -= reg_count;
0055 addr -= (!pbit == !ubit);
0056 regs->uregs[rn] = (long)addr;
0057 }
0058 }
0059
0060 static void __kprobes simulate_stm1_pc(probes_opcode_t insn,
0061 struct arch_probes_insn *asi,
0062 struct pt_regs *regs)
0063 {
0064 unsigned long addr = regs->ARM_pc - 4;
0065
0066 regs->ARM_pc = (long)addr + str_pc_offset;
0067 simulate_ldm1stm1(insn, asi, regs);
0068 regs->ARM_pc = (long)addr + 4;
0069 }
0070
0071 static void __kprobes simulate_ldm1_pc(probes_opcode_t insn,
0072 struct arch_probes_insn *asi,
0073 struct pt_regs *regs)
0074 {
0075 simulate_ldm1stm1(insn, asi, regs);
0076 load_write_pc(regs->ARM_pc, regs);
0077 }
0078
0079 static void __kprobes
0080 emulate_generic_r0_12_noflags(probes_opcode_t insn,
0081 struct arch_probes_insn *asi, struct pt_regs *regs)
0082 {
0083 register void *rregs asm("r1") = regs;
0084 register void *rfn asm("lr") = asi->insn_fn;
0085
0086 __asm__ __volatile__ (
0087 ARM( "stmdb sp!, {%[regs], r11} \n\t" )
0088 THUMB( "stmdb sp!, {%[regs], r7} \n\t" )
0089 "ldmia %[regs], {r0-r12} \n\t"
0090 #if __LINUX_ARM_ARCH__ >= 6
0091 "blx %[fn] \n\t"
0092 #else
0093 "str %[fn], [sp, #-4]! \n\t"
0094 "adr lr, 1f \n\t"
0095 "ldr pc, [sp], #4 \n\t"
0096 "1: \n\t"
0097 #endif
0098 "ldr lr, [sp], #4 \n\t"
0099 "stmia lr, {r0-r12} \n\t"
0100 ARM( "ldr r11, [sp], #4 \n\t" )
0101 THUMB( "ldr r7, [sp], #4 \n\t" )
0102 : [regs] "=r" (rregs), [fn] "=r" (rfn)
0103 : "0" (rregs), "1" (rfn)
0104 : "r0", "r2", "r3", "r4", "r5", "r6", ARM("r7") THUMB("r11"),
0105 "r8", "r9", "r10", "r12", "memory", "cc"
0106 );
0107 }
0108
0109 static void __kprobes
0110 emulate_generic_r2_14_noflags(probes_opcode_t insn,
0111 struct arch_probes_insn *asi, struct pt_regs *regs)
0112 {
0113 emulate_generic_r0_12_noflags(insn, asi,
0114 (struct pt_regs *)(regs->uregs+2));
0115 }
0116
0117 static void __kprobes
0118 emulate_ldm_r3_15(probes_opcode_t insn,
0119 struct arch_probes_insn *asi, struct pt_regs *regs)
0120 {
0121 emulate_generic_r0_12_noflags(insn, asi,
0122 (struct pt_regs *)(regs->uregs+3));
0123 load_write_pc(regs->ARM_pc, regs);
0124 }
0125
0126 enum probes_insn __kprobes
0127 kprobe_decode_ldmstm(probes_opcode_t insn, struct arch_probes_insn *asi,
0128 const struct decode_header *h)
0129 {
0130 probes_insn_handler_t *handler = 0;
0131 unsigned reglist = insn & 0xffff;
0132 int is_ldm = insn & 0x100000;
0133 int rn = (insn >> 16) & 0xf;
0134
0135 if (rn <= 12 && (reglist & 0xe000) == 0) {
0136
0137 handler = emulate_generic_r0_12_noflags;
0138
0139 } else if (rn >= 2 && (reglist & 0x8003) == 0) {
0140
0141 rn -= 2;
0142 reglist >>= 2;
0143 handler = emulate_generic_r2_14_noflags;
0144
0145 } else if (rn >= 3 && (reglist & 0x0007) == 0) {
0146
0147 if (is_ldm && (reglist & 0x8000)) {
0148 rn -= 3;
0149 reglist >>= 3;
0150 handler = emulate_ldm_r3_15;
0151 }
0152 }
0153
0154 if (handler) {
0155
0156 asi->insn[0] = __opcode_to_mem_arm((insn & 0xfff00000) |
0157 (rn << 16) | reglist);
0158 asi->insn_handler = handler;
0159 return INSN_GOOD;
0160 }
0161
0162
0163 if (reglist & 0x8000)
0164 handler = is_ldm ? simulate_ldm1_pc : simulate_stm1_pc;
0165 else
0166 handler = simulate_ldm1stm1;
0167 asi->insn_handler = handler;
0168 return INSN_GOOD_NO_SLOT;
0169 }
0170