/* ptrace.c: Sparc process tracing support.
*
* Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net)
*
* Based upon code written by Ross Biro, Linus Torvalds, Bob Manson,
* and David Mosberger.
*
* Added Linux support -miguel (weird, eh?, the original code was meant
* to emulate SunOS).
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/security.h>
#include <linux/signal.h>
#include <linux/regset.h>
#include <linux/elf.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#define MAGIC_CONSTANT 0x80000000
/* Returning from ptrace is a bit tricky because the syscall return
* low level code assumes any value returned which is negative and
* is a valid errno will mean setting the condition codes to indicate
* an error return. This doesn't work, so we have this hook.
*/
static inline void pt_error_return(struct pt_regs *regs, unsigned long error)
{
regs->u_regs[UREG_I0] = error;
regs->psr |= PSR_C;
regs->pc = regs->npc;
regs->npc += 4;
}
static inline void pt_succ_return(struct pt_regs *regs, unsigned long value)
{
regs->u_regs[UREG_I0] = value;
regs->psr &= ~PSR_C;
regs->pc = regs->npc;
regs->npc += 4;
}
static void
pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long __user *addr)
{
if (put_user(value, addr)) {
pt_error_return(regs, EFAULT);
return;
}
regs->u_regs[UREG_I0] = 0;
regs->psr &= ~PSR_C;
regs->pc = regs->npc;
regs->npc += 4;
}
static void
pt_os_succ_return (struct pt_regs *regs, unsigned long val, long __user *addr)
{
if (current->personality == PER_SUNOS)
pt_succ_return (regs, val);
else
pt_succ_return_linux (regs, val, addr);
}
/* Fuck me gently with a chainsaw... */
static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset,
struct task_struct *tsk, long __user *addr)
{
struct pt_regs *cregs = tsk->thread.kregs;
struct thread_info *t = task_thread_info(tsk);
int v;
if(offset >= 1024)
offset -= 1024; /* whee... */
if(offset & ((sizeof(unsigned long) - 1))) {
pt_error_return(regs, EIO);
return;
}
if(offset >= 16 && offset < 784) {
offset -= 16; offset >>= 2;
pt_os_succ_return(regs, *(((unsigned long *)(&t->reg_window[0]))+offset), addr);
return;
}
if(offset >= 784 && offset < 832) {
offset -= 784; offset >>= 2;
pt_os_succ_return(regs, *(((unsigned long *)(&t->rwbuf_stkptrs[0]))+offset), addr);
return;
}
switch(offset) {
case 0:
v = t->ksp;
break;
case 4:
v = t->kpc;
break;
case 8:
v = t->kpsr;
break;
case 12:
v = t->uwinmask;
break;
case 832:
v = t->w_saved;
break;
case 896:
v = cregs->u_regs[UREG_I0];
break;
case 900:
v = cregs->u_regs[UREG_I1];
break;
case 904:
v = cregs->u_regs[UREG_I2];
break;
case 908:
v = cregs->u_regs[UREG_I3];
break;
case 912:
v = cregs->u_regs[UREG_I4];
break;
case 916:
v = cregs->u_regs[UREG_I5];
break;
case 920:
v = cregs->u_regs[UREG_I6];
break;
case 924:
if(tsk->thread.flags & MAGIC_CONSTANT)
v = cregs->u_regs[UREG_G1];
else
v = 0;
break;
case 940:
v = cregs->u_regs[UREG_I0];
break;
case 944:
v = cregs->u_regs[UREG_I1];
break;
case 948:
/* Isn't binary compatibility _fun_??? */
if(cregs->psr & PSR_C)
v = cregs->u_regs[UREG_I0] << 24;
else
v = 0;
break;
/* Rest of them are completely unsupported. */
default:
printk("%s [%d]: Wants to read user offset %ld\n",
current->comm, task_pid_nr(current), offset);
pt_error_return(regs, EIO);
return;
}
if (current->personality == PER_SUNOS)
pt_succ_return (regs, v);
else
pt_succ_return_linux (regs, v, addr);
return;
}
static inline void write_sunos_user(struct pt_regs *regs,