From: Roland McGrath Date: Wed, 30 Jan 2008 12:30:52 +0000 (+0100) Subject: x86: x86-64 ptrace debugreg cleanup X-Git-Tag: v2.6.25-rc1~1143^2~695 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=962ff3804d31a4d090bbcbd3d06a4b63e3a5b5fd;p=linux-2.6 x86: x86-64 ptrace debugreg cleanup 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 Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner --- diff --git a/arch/x86/kernel/ptrace_64.c b/arch/x86/kernel/ptrace_64.c index d0a0aeaaa0..4ba66d8af7 100644 --- a/arch/x86/kernel/ptrace_64.c +++ b/arch/x86/kernel/ptrace_64.c @@ -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 diff --git a/include/asm-x86/ptrace.h b/include/asm-x86/ptrace.h index fe75422f03..d223decd7b 100644 --- a/include/asm-x86/ptrace.h +++ b/include/asm-x86/ptrace.h @@ -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);