Commit ce7f2a39 authored by Chris Metcalf's avatar Chris Metcalf

arch/tile: make ptrace() work properly for TILE-Gx COMPAT mode

Previously, we tried to pass 64-bit arguments through the
"COMPAT" mode 32-bit syscall API, which turned out not to work
well.  Now we just use straight 32-bit arguments in COMPAT mode,
thus requiring individual registers to be read/written with
two syscalls.  Of course this is uncommon, since usually all
the registers are read or written at once.

The restructuring applies to all the tile platforms, but is
plausibly better than the original code in any case.
Signed-off-by: default avatarChris Metcalf <cmetcalf@tilera.com>
parent c569cac8
...@@ -154,8 +154,8 @@ long tile_compat_sys_msgrcv(int msqid, ...@@ -154,8 +154,8 @@ long tile_compat_sys_msgrcv(int msqid,
#define compat_sys_fstat64 sys_newfstat #define compat_sys_fstat64 sys_newfstat
#define compat_sys_fstatat64 sys_newfstatat #define compat_sys_fstatat64 sys_newfstatat
/* Pass full 64-bit values through ptrace. */ /* The native sys_ptrace dynamically handles compat binaries. */
#define compat_sys_ptrace tile_compat_sys_ptrace #define compat_sys_ptrace sys_ptrace
/* Call the trampolines to manage pt_regs where necessary. */ /* Call the trampolines to manage pt_regs where necessary. */
#define compat_sys_execve _compat_sys_execve #define compat_sys_execve _compat_sys_execve
......
...@@ -31,25 +31,6 @@ void user_disable_single_step(struct task_struct *child) ...@@ -31,25 +31,6 @@ void user_disable_single_step(struct task_struct *child)
clear_tsk_thread_flag(child, TIF_SINGLESTEP); clear_tsk_thread_flag(child, TIF_SINGLESTEP);
} }
/*
* This routine will put a word on the process's privileged stack.
*/
static void putreg(struct task_struct *task,
unsigned long addr, unsigned long value)
{
unsigned int regno = addr / sizeof(unsigned long);
struct pt_regs *childregs = task_pt_regs(task);
childregs->regs[regno] = value;
childregs->flags |= PT_FLAGS_RESTORE_REGS;
}
static unsigned long getreg(struct task_struct *task, unsigned long addr)
{
unsigned int regno = addr / sizeof(unsigned long);
struct pt_regs *childregs = task_pt_regs(task);
return childregs->regs[regno];
}
/* /*
* Called by kernel/ptrace.c when detaching.. * Called by kernel/ptrace.c when detaching..
*/ */
...@@ -66,59 +47,72 @@ void ptrace_disable(struct task_struct *child) ...@@ -66,59 +47,72 @@ void ptrace_disable(struct task_struct *child)
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)
{ {
unsigned long __user *datap; unsigned long __user *datap = (long __user __force *)data;
unsigned long tmp; unsigned long tmp;
int i; int i;
long ret = -EIO; long ret = -EIO;
unsigned long *childregs;
#ifdef CONFIG_COMPAT char *childreg;
if (task_thread_info(current)->status & TS_COMPAT)
data = (u32)data;
if (task_thread_info(child)->status & TS_COMPAT)
addr = (u32)addr;
#endif
datap = (unsigned long __user __force *)data;
switch (request) { switch (request) {
case PTRACE_PEEKUSR: /* Read register from pt_regs. */ case PTRACE_PEEKUSR: /* Read register from pt_regs. */
if (addr & (sizeof(data)-1))
break;
if (addr < 0 || addr >= PTREGS_SIZE) if (addr < 0 || addr >= PTREGS_SIZE)
break; break;
tmp = getreg(child, addr); /* Read register */ childreg = (char *)task_pt_regs(child) + addr;
ret = put_user(tmp, datap); #ifdef CONFIG_COMPAT
if (is_compat_task()) {
if (addr & (sizeof(compat_long_t)-1))
break;
ret = put_user(*(compat_long_t *)childreg,
(compat_long_t __user *)datap);
} else
#endif
{
if (addr & (sizeof(long)-1))
break;
ret = put_user(*(long *)childreg, datap);
}
break; break;
case PTRACE_POKEUSR: /* Write register in pt_regs. */ case PTRACE_POKEUSR: /* Write register in pt_regs. */
if (addr & (sizeof(data)-1))
break;
if (addr < 0 || addr >= PTREGS_SIZE) if (addr < 0 || addr >= PTREGS_SIZE)
break; break;
putreg(child, addr, data); /* Write register */ childreg = (char *)task_pt_regs(child) + addr;
#ifdef CONFIG_COMPAT
if (is_compat_task()) {
if (addr & (sizeof(compat_long_t)-1))
break;
*(compat_long_t *)childreg = data;
} else
#endif
{
if (addr & (sizeof(long)-1))
break;
*(long *)childreg = data;
}
ret = 0; ret = 0;
break; break;
case PTRACE_GETREGS: /* Get all registers from the child. */ case PTRACE_GETREGS: /* Get all registers from the child. */
if (!access_ok(VERIFY_WRITE, datap, PTREGS_SIZE)) if (!access_ok(VERIFY_WRITE, datap, PTREGS_SIZE))
break; break;
for (i = 0; i < PTREGS_SIZE; i += sizeof(long)) { childregs = (long *)task_pt_regs(child);
ret = __put_user(getreg(child, i), datap); for (i = 0; i < sizeof(struct pt_regs)/sizeof(long); ++i) {
ret = __put_user(childregs[i], &datap[i]);
if (ret != 0) if (ret != 0)
break; break;
datap++;
} }
break; break;
case PTRACE_SETREGS: /* Set all registers in the child. */ case PTRACE_SETREGS: /* Set all registers in the child. */
if (!access_ok(VERIFY_READ, datap, PTREGS_SIZE)) if (!access_ok(VERIFY_READ, datap, PTREGS_SIZE))
break; break;
for (i = 0; i < PTREGS_SIZE; i += sizeof(long)) { childregs = (long *)task_pt_regs(child);
ret = __get_user(tmp, datap); for (i = 0; i < sizeof(struct pt_regs)/sizeof(long); ++i) {
ret = __get_user(childregs[i], &datap[i]);
if (ret != 0) if (ret != 0)
break; break;
putreg(child, i, tmp);
datap++;
} }
break; break;
......
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