diff options
-rw-r--r-- | arch/arm/include/asm/ptrace.h | 8 | ||||
-rw-r--r-- | arch/arm/kernel/ptrace.c | 28 | ||||
-rw-r--r-- | arch/arm/kernel/traps.c | 17 |
3 files changed, 27 insertions, 26 deletions
diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h index 312d10877bd..d484871698d 100644 --- a/arch/arm/include/asm/ptrace.h +++ b/arch/arm/include/asm/ptrace.h | |||
@@ -200,6 +200,14 @@ extern unsigned long profile_pc(struct pt_regs *regs); | |||
200 | #define PREDICATE_ALWAYS 0xe0000000 | 200 | #define PREDICATE_ALWAYS 0xe0000000 |
201 | 201 | ||
202 | /* | 202 | /* |
203 | * True if instr is a 32-bit thumb instruction. This works if instr | ||
204 | * is the first or only half-word of a thumb instruction. It also works | ||
205 | * when instr holds all 32-bits of a wide thumb instruction if stored | ||
206 | * in the form (first_half<<16)|(second_half) | ||
207 | */ | ||
208 | #define is_wide_instruction(instr) ((unsigned)(instr) >= 0xe800) | ||
209 | |||
210 | /* | ||
203 | * kprobe-based event tracer support | 211 | * kprobe-based event tracer support |
204 | */ | 212 | */ |
205 | #include <linux/stddef.h> | 213 | #include <linux/stddef.h> |
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 97260060bf2..897ade059f5 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c | |||
@@ -228,34 +228,12 @@ static struct undef_hook thumb_break_hook = { | |||
228 | .fn = break_trap, | 228 | .fn = break_trap, |
229 | }; | 229 | }; |
230 | 230 | ||
231 | static int thumb2_break_trap(struct pt_regs *regs, unsigned int instr) | ||
232 | { | ||
233 | unsigned int instr2; | ||
234 | void __user *pc; | ||
235 | |||
236 | /* Check the second half of the instruction. */ | ||
237 | pc = (void __user *)(instruction_pointer(regs) + 2); | ||
238 | |||
239 | if (processor_mode(regs) == SVC_MODE) { | ||
240 | instr2 = *(u16 *) pc; | ||
241 | } else { | ||
242 | get_user(instr2, (u16 __user *)pc); | ||
243 | } | ||
244 | |||
245 | if (instr2 == 0xa000) { | ||
246 | ptrace_break(current, regs); | ||
247 | return 0; | ||
248 | } else { | ||
249 | return 1; | ||
250 | } | ||
251 | } | ||
252 | |||
253 | static struct undef_hook thumb2_break_hook = { | 231 | static struct undef_hook thumb2_break_hook = { |
254 | .instr_mask = 0xffff, | 232 | .instr_mask = 0xffffffff, |
255 | .instr_val = 0xf7f0, | 233 | .instr_val = 0xf7f0a000, |
256 | .cpsr_mask = PSR_T_BIT, | 234 | .cpsr_mask = PSR_T_BIT, |
257 | .cpsr_val = PSR_T_BIT, | 235 | .cpsr_val = PSR_T_BIT, |
258 | .fn = thumb2_break_trap, | 236 | .fn = break_trap, |
259 | }; | 237 | }; |
260 | 238 | ||
261 | static int __init ptrace_break_init(void) | 239 | static int __init ptrace_break_init(void) |
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 6807cb1e76d..2d3436e9f71 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c | |||
@@ -355,9 +355,24 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs) | |||
355 | pc = (void __user *)instruction_pointer(regs); | 355 | pc = (void __user *)instruction_pointer(regs); |
356 | 356 | ||
357 | if (processor_mode(regs) == SVC_MODE) { | 357 | if (processor_mode(regs) == SVC_MODE) { |
358 | instr = *(u32 *) pc; | 358 | #ifdef CONFIG_THUMB2_KERNEL |
359 | if (thumb_mode(regs)) { | ||
360 | instr = ((u16 *)pc)[0]; | ||
361 | if (is_wide_instruction(instr)) { | ||
362 | instr <<= 16; | ||
363 | instr |= ((u16 *)pc)[1]; | ||
364 | } | ||
365 | } else | ||
366 | #endif | ||
367 | instr = *(u32 *) pc; | ||
359 | } else if (thumb_mode(regs)) { | 368 | } else if (thumb_mode(regs)) { |
360 | get_user(instr, (u16 __user *)pc); | 369 | get_user(instr, (u16 __user *)pc); |
370 | if (is_wide_instruction(instr)) { | ||
371 | unsigned int instr2; | ||
372 | get_user(instr2, (u16 __user *)pc+1); | ||
373 | instr <<= 16; | ||
374 | instr |= instr2; | ||
375 | } | ||
361 | } else { | 376 | } else { |
362 | get_user(instr, (u32 __user *)pc); | 377 | get_user(instr, (u32 __user *)pc); |
363 | } | 378 | } |