diff options
author | Roland McGrath <roland@redhat.com> | 2008-01-30 13:30:52 +0100 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-01-30 13:30:52 +0100 |
commit | 962ff3804d31a4d090bbcbd3d06a4b63e3a5b5fd (patch) | |
tree | 46e45a1076777b7454fd026f006f7517229b34b7 /arch/x86/kernel | |
parent | e4aed6cc45f06acd35e3dfbbaf632c5d5aa897c0 (diff) |
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 <roland@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/x86/kernel')
-rw-r--r-- | arch/x86/kernel/ptrace_64.c | 140 |
1 files changed, 66 insertions, 74 deletions
diff --git a/arch/x86/kernel/ptrace_64.c b/arch/x86/kernel/ptrace_64.c index d0a0aeaaa0c..4ba66d8af71 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 |