]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ARM: Thumb-2: Support Thumb-2 in undefined instruction handler
authorJon Medhurst <tixy@yxit.co.uk>
Sat, 26 Mar 2011 19:19:07 +0000 (19:19 +0000)
committerTixy <tixy@medhuaa1.miniserver.com>
Wed, 13 Jul 2011 17:32:40 +0000 (17:32 +0000)
This patch allows undef_hook's to be specified for 32-bit Thumb
instructions and also to be used for thumb kernel-side code.

32-bit Thumb instructions are specified in the form:
((first_half << 16 ) | second_half)
which matches the layout used by the ARM ARM.

ptrace was handling 32-bit Thumb instructions by hooking the first
halfword and manually checking the second half. This method would be
broken by this patch so it is migrated to make use of the new Thumb-2
support.

Signed-off-by: Jon Medhurst <tixy@yxit.co.uk>
Acked-by: Nicolas Pitre <nicolas.pitre@linaro.org>
arch/arm/include/asm/ptrace.h
arch/arm/kernel/ptrace.c
arch/arm/kernel/traps.c

index 312d10877bd74d4df144bbdbbd99e083ea784828..d484871698da38160828ee96845061d07043950a 100644 (file)
@@ -199,6 +199,14 @@ extern unsigned long profile_pc(struct pt_regs *regs);
 #define predicate(x)           ((x) & 0xf0000000)
 #define PREDICATE_ALWAYS       0xe0000000
 
+/*
+ * True if instr is a 32-bit thumb instruction. This works if instr
+ * is the first or only half-word of a thumb instruction. It also works
+ * when instr holds all 32-bits of a wide thumb instruction if stored
+ * in the form (first_half<<16)|(second_half)
+ */
+#define is_wide_instruction(instr)     ((unsigned)(instr) >= 0xe800)
+
 /*
  * kprobe-based event tracer support
  */
index 97260060bf2605e5809eb2655eba33a3acf60759..897ade059f58e7c8c4314502f892955e73d4cccb 100644 (file)
@@ -228,34 +228,12 @@ static struct undef_hook thumb_break_hook = {
        .fn             = break_trap,
 };
 
-static int thumb2_break_trap(struct pt_regs *regs, unsigned int instr)
-{
-       unsigned int instr2;
-       void __user *pc;
-
-       /* Check the second half of the instruction.  */
-       pc = (void __user *)(instruction_pointer(regs) + 2);
-
-       if (processor_mode(regs) == SVC_MODE) {
-               instr2 = *(u16 *) pc;
-       } else {
-               get_user(instr2, (u16 __user *)pc);
-       }
-
-       if (instr2 == 0xa000) {
-               ptrace_break(current, regs);
-               return 0;
-       } else {
-               return 1;
-       }
-}
-
 static struct undef_hook thumb2_break_hook = {
-       .instr_mask     = 0xffff,
-       .instr_val      = 0xf7f0,
+       .instr_mask     = 0xffffffff,
+       .instr_val      = 0xf7f0a000,
        .cpsr_mask      = PSR_T_BIT,
        .cpsr_val       = PSR_T_BIT,
-       .fn             = thumb2_break_trap,
+       .fn             = break_trap,
 };
 
 static int __init ptrace_break_init(void)
index 6807cb1e76ddb119f78100aa3ccc7d59b1184721..2d3436e9f71f034ec3f8cb70e636fa07f5d15bc2 100644 (file)
@@ -355,9 +355,24 @@ asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
        pc = (void __user *)instruction_pointer(regs);
 
        if (processor_mode(regs) == SVC_MODE) {
-               instr = *(u32 *) pc;
+#ifdef CONFIG_THUMB2_KERNEL
+               if (thumb_mode(regs)) {
+                       instr = ((u16 *)pc)[0];
+                       if (is_wide_instruction(instr)) {
+                               instr <<= 16;
+                               instr |= ((u16 *)pc)[1];
+                       }
+               } else
+#endif
+                       instr = *(u32 *) pc;
        } else if (thumb_mode(regs)) {
                get_user(instr, (u16 __user *)pc);
+               if (is_wide_instruction(instr)) {
+                       unsigned int instr2;
+                       get_user(instr2, (u16 __user *)pc+1);
+                       instr <<= 16;
+                       instr |= instr2;
+               }
        } else {
                get_user(instr, (u32 __user *)pc);
        }