Commit 962ff380 authored by Roland McGrath's avatar Roland McGrath Committed by Ingo Molnar

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: default avatarRoland McGrath <roland@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent e4aed6cc
...@@ -183,9 +183,63 @@ static unsigned long getreg(struct task_struct *child, unsigned long regno) ...@@ -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 arch_ptrace(struct task_struct *child, long request, long addr, long data)
{ {
long i, ret; long ret;
unsigned ui; unsigned ui;
switch (request) { switch (request) {
...@@ -204,32 +258,14 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ...@@ -204,32 +258,14 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
addr > sizeof(struct user) - 7) addr > sizeof(struct user) - 7)
break; break;
switch (addr) {
case 0 ... sizeof(struct user_regs_struct) - sizeof(long):
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; tmp = 0;
break; if (addr < sizeof(struct user_regs_struct))
tmp = getreg(child, addr);
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); ret = put_user(tmp,(unsigned long __user *) data);
break; break;
} }
...@@ -241,63 +277,19 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) ...@@ -241,63 +277,19 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
break; break;
case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ 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; ret = -EIO;
if ((addr & 7) || if ((addr & 7) ||
addr > sizeof(struct user) - 7) addr > sizeof(struct user) - 7)
break; break;
switch (addr) { if (addr < sizeof(struct user_regs_struct))
case 0 ... sizeof(struct user_regs_struct) - sizeof(long):
ret = putreg(child, addr, data); ret = putreg(child, addr, data);
break; else if (addr >= offsetof(struct user, u_debugreg[0])) {
/* Disallows to set a breakpoint into the vsyscall */ addr -= offsetof(struct user, u_debugreg[0]);
case offsetof(struct user, u_debugreg[0]): ret = ptrace_set_debugreg(child,
if (data >= TASK_SIZE_OF(child) - dsize) break; addr / sizeof(long), data);
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;
} }
break; break;
}
#ifdef CONFIG_IA32_EMULATION #ifdef CONFIG_IA32_EMULATION
/* This makes only sense with 32bit programs. Allow a /* This makes only sense with 32bit programs. Allow a
......
...@@ -110,6 +110,9 @@ void signal_fault(struct pt_regs *regs, void __user *frame, char *where); ...@@ -110,6 +110,9 @@ void signal_fault(struct pt_regs *regs, void __user *frame, char *where);
struct task_struct; 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 extern unsigned long
convert_rip_to_linear(struct task_struct *child, struct pt_regs *regs); convert_rip_to_linear(struct task_struct *child, struct pt_regs *regs);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment