]> err.no Git - linux-2.6/commitdiff
x86: x86-64 ptrace debugreg cleanup
authorRoland McGrath <roland@redhat.com>
Wed, 30 Jan 2008 12:30:52 +0000 (13:30 +0100)
committerIngo Molnar <mingo@elte.hu>
Wed, 30 Jan 2008 12:30:52 +0000 (13:30 +0100)
This cleans up the 64-bit ptrace code to separate the guts of the
debug register access from the implementation of PTRACE_PEEKUSR and
PTRACE_POKEUSR.  The new functions ptrace_[gs]et_debugreg are made
global so that the ia32 code can later be changed to call them too.

Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/x86/kernel/ptrace_64.c
include/asm-x86/ptrace.h

index d0a0aeaaa0c244a9e7999ccab5bbe0176660a91a..4ba66d8af717998fbc79bdef91d530d6d4020730 100644 (file)
@@ -183,9 +183,63 @@ static unsigned long getreg(struct task_struct *child, unsigned long regno)
 
 }
 
+unsigned long ptrace_get_debugreg(struct task_struct *child, int n)
+{
+       switch (n) {
+       case 0:         return child->thread.debugreg0;
+       case 1:         return child->thread.debugreg1;
+       case 2:         return child->thread.debugreg2;
+       case 3:         return child->thread.debugreg3;
+       case 6:         return child->thread.debugreg6;
+       case 7:         return child->thread.debugreg7;
+       }
+       return 0;
+}
+
+int ptrace_set_debugreg(struct task_struct *child, int n, unsigned long data)
+{
+       int i;
+
+       if (n < 4) {
+               int dsize = test_tsk_thread_flag(child, TIF_IA32) ? 3 : 7;
+               if (unlikely(data >= TASK_SIZE_OF(child) - dsize))
+                       return -EIO;
+       }
+
+       switch (n) {
+       case 0:         child->thread.debugreg0 = data; break;
+       case 1:         child->thread.debugreg1 = data; break;
+       case 2:         child->thread.debugreg2 = data; break;
+       case 3:         child->thread.debugreg3 = data; break;
+
+       case 6:
+               if (data >> 32)
+                       return -EIO;
+               child->thread.debugreg6 = data;
+               break;
+
+       case 7:
+               /*
+                * See ptrace_32.c for an explanation of this awkward check.
+                */
+               data &= ~DR_CONTROL_RESERVED;
+               for (i = 0; i < 4; i++)
+                       if ((0x5554 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
+                               return -EIO;
+               child->thread.debugreg7 = data;
+               if (data)
+                       set_tsk_thread_flag(child, TIF_DEBUG);
+               else
+                       clear_tsk_thread_flag(child, TIF_DEBUG);
+               break;
+       }
+
+       return 0;
+}
+
 long arch_ptrace(struct task_struct *child, long request, long addr, long data)
 {
-       long i, ret;
+       long ret;
        unsigned ui;
 
        switch (request) {
@@ -204,32 +258,14 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
                    addr > sizeof(struct user) - 7)
                        break;
 
-               switch (addr) { 
-               case 0 ... sizeof(struct user_regs_struct) - sizeof(long):
+               tmp = 0;
+               if (addr < sizeof(struct user_regs_struct))
                        tmp = getreg(child, addr);
-                       break;
-               case offsetof(struct user, u_debugreg[0]):
-                       tmp = child->thread.debugreg0;
-                       break;
-               case offsetof(struct user, u_debugreg[1]):
-                       tmp = child->thread.debugreg1;
-                       break;
-               case offsetof(struct user, u_debugreg[2]):
-                       tmp = child->thread.debugreg2;
-                       break;
-               case offsetof(struct user, u_debugreg[3]):
-                       tmp = child->thread.debugreg3;
-                       break;
-               case offsetof(struct user, u_debugreg[6]):
-                       tmp = child->thread.debugreg6;
-                       break;
-               case offsetof(struct user, u_debugreg[7]):
-                       tmp = child->thread.debugreg7;
-                       break;
-               default:
-                       tmp = 0;
-                       break;
+               else if (addr >= offsetof(struct user, u_debugreg[0])) {
+                       addr -= offsetof(struct user, u_debugreg[0]);
+                       tmp = ptrace_get_debugreg(child, addr / sizeof(long));
                }
+
                ret = put_user(tmp,(unsigned long __user *) data);
                break;
        }
@@ -241,63 +277,19 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
                break;
 
        case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
-       {
-               int dsize = test_tsk_thread_flag(child, TIF_IA32) ? 3 : 7;
                ret = -EIO;
                if ((addr & 7) ||
                    addr > sizeof(struct user) - 7)
                        break;
 
-               switch (addr) { 
-               case 0 ... sizeof(struct user_regs_struct) - sizeof(long):
+               if (addr < sizeof(struct user_regs_struct))
                        ret = putreg(child, addr, data);
-                       break;
-               /* Disallows to set a breakpoint into the vsyscall */
-               case offsetof(struct user, u_debugreg[0]):
-                       if (data >= TASK_SIZE_OF(child) - dsize) break;
-                       child->thread.debugreg0 = data;
-                       ret = 0;
-                       break;
-               case offsetof(struct user, u_debugreg[1]):
-                       if (data >= TASK_SIZE_OF(child) - dsize) break;
-                       child->thread.debugreg1 = data;
-                       ret = 0;
-                       break;
-               case offsetof(struct user, u_debugreg[2]):
-                       if (data >= TASK_SIZE_OF(child) - dsize) break;
-                       child->thread.debugreg2 = data;
-                       ret = 0;
-                       break;
-               case offsetof(struct user, u_debugreg[3]):
-                       if (data >= TASK_SIZE_OF(child) - dsize) break;
-                       child->thread.debugreg3 = data;
-                       ret = 0;
-                       break;
-               case offsetof(struct user, u_debugreg[6]):
-                                 if (data >> 32)
-                               break; 
-                       child->thread.debugreg6 = data;
-                       ret = 0;
-                       break;
-               case offsetof(struct user, u_debugreg[7]):
-                       /* See arch/i386/kernel/ptrace.c for an explanation of
-                        * this awkward check.*/
-                       data &= ~DR_CONTROL_RESERVED;
-                       for(i=0; i<4; i++)
-                               if ((0x5554 >> ((data >> (16 + 4*i)) & 0xf)) & 1)
-                                       break;
-                       if (i == 4) {
-                         child->thread.debugreg7 = data;
-                         if (data)
-                               set_tsk_thread_flag(child, TIF_DEBUG);
-                         else
-                               clear_tsk_thread_flag(child, TIF_DEBUG);
-                         ret = 0;
-                       }
-                 break;
+               else if (addr >= offsetof(struct user, u_debugreg[0])) {
+                       addr -= offsetof(struct user, u_debugreg[0]);
+                       ret = ptrace_set_debugreg(child,
+                                                 addr / sizeof(long), data);
                }
                break;
-       }
 
 #ifdef CONFIG_IA32_EMULATION
                /* This makes only sense with 32bit programs. Allow a
index fe75422f034b1a3fa78cc684777ad5cd04492cd2..d223decd7b013441814ca0cb5aa3eb6781284ba8 100644 (file)
@@ -110,6 +110,9 @@ void signal_fault(struct pt_regs *regs, void __user *frame, char *where);
 
 struct task_struct;
 
+extern unsigned long ptrace_get_debugreg(struct task_struct *child, int n);
+extern int ptrace_set_debugreg(struct task_struct *child, int n, unsigned long);
+
 extern unsigned long
 convert_rip_to_linear(struct task_struct *child, struct pt_regs *regs);