diff options
Diffstat (limited to 'arch/arm/kernel/kprobes-common.c')
-rw-r--r-- | arch/arm/kernel/kprobes-common.c | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/arch/arm/kernel/kprobes-common.c b/arch/arm/kernel/kprobes-common.c index 86fdc4c4c2ce..43d663cafdd1 100644 --- a/arch/arm/kernel/kprobes-common.c +++ b/arch/arm/kernel/kprobes-common.c | |||
@@ -166,6 +166,76 @@ void __kprobes kprobe_emulate_none(struct kprobe *p, struct pt_regs *regs) | |||
166 | p->ainsn.insn_fn(); | 166 | p->ainsn.insn_fn(); |
167 | } | 167 | } |
168 | 168 | ||
169 | static void __kprobes simulate_ldm1stm1(struct kprobe *p, struct pt_regs *regs) | ||
170 | { | ||
171 | kprobe_opcode_t insn = p->opcode; | ||
172 | int rn = (insn >> 16) & 0xf; | ||
173 | int lbit = insn & (1 << 20); | ||
174 | int wbit = insn & (1 << 21); | ||
175 | int ubit = insn & (1 << 23); | ||
176 | int pbit = insn & (1 << 24); | ||
177 | long *addr = (long *)regs->uregs[rn]; | ||
178 | int reg_bit_vector; | ||
179 | int reg_count; | ||
180 | |||
181 | reg_count = 0; | ||
182 | reg_bit_vector = insn & 0xffff; | ||
183 | while (reg_bit_vector) { | ||
184 | reg_bit_vector &= (reg_bit_vector - 1); | ||
185 | ++reg_count; | ||
186 | } | ||
187 | |||
188 | if (!ubit) | ||
189 | addr -= reg_count; | ||
190 | addr += (!pbit == !ubit); | ||
191 | |||
192 | reg_bit_vector = insn & 0xffff; | ||
193 | while (reg_bit_vector) { | ||
194 | int reg = __ffs(reg_bit_vector); | ||
195 | reg_bit_vector &= (reg_bit_vector - 1); | ||
196 | if (lbit) | ||
197 | regs->uregs[reg] = *addr++; | ||
198 | else | ||
199 | *addr++ = regs->uregs[reg]; | ||
200 | } | ||
201 | |||
202 | if (wbit) { | ||
203 | if (!ubit) | ||
204 | addr -= reg_count; | ||
205 | addr -= (!pbit == !ubit); | ||
206 | regs->uregs[rn] = (long)addr; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | static void __kprobes simulate_stm1_pc(struct kprobe *p, struct pt_regs *regs) | ||
211 | { | ||
212 | regs->ARM_pc = (long)p->addr + str_pc_offset; | ||
213 | simulate_ldm1stm1(p, regs); | ||
214 | regs->ARM_pc = (long)p->addr + 4; | ||
215 | } | ||
216 | |||
217 | static void __kprobes simulate_ldm1_pc(struct kprobe *p, struct pt_regs *regs) | ||
218 | { | ||
219 | simulate_ldm1stm1(p, regs); | ||
220 | load_write_pc(regs->ARM_pc, regs); | ||
221 | } | ||
222 | |||
223 | enum kprobe_insn __kprobes | ||
224 | kprobe_decode_ldmstm(kprobe_opcode_t insn, struct arch_specific_insn *asi) | ||
225 | { | ||
226 | kprobe_insn_handler_t *handler = 0; | ||
227 | unsigned reglist = insn & 0xffff; | ||
228 | int is_ldm = insn & 0x100000; | ||
229 | |||
230 | if (reglist & 0x8000) | ||
231 | handler = is_ldm ? simulate_ldm1_pc : simulate_stm1_pc; | ||
232 | else | ||
233 | handler = simulate_ldm1stm1; | ||
234 | asi->insn_handler = handler; | ||
235 | return INSN_GOOD_NO_SLOT; | ||
236 | } | ||
237 | |||
238 | |||
169 | /* | 239 | /* |
170 | * Prepare an instruction slot to receive an instruction for emulating. | 240 | * Prepare an instruction slot to receive an instruction for emulating. |
171 | * This is done by placing a subroutine return after the location where the | 241 | * This is done by placing a subroutine return after the location where the |