|
@@ -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
|