/*
* linux/arch/m32r/kernel/ptrace.c
*
* Copyright (C) 2002 Hirokazu Takata, Takeo Takahashi
* Copyright (C) 2004 Hirokazu Takata, Kei Sakamoto
*
* Original x86 implementation:
* By Ross Biro 1/23/92
* edited by Linus Torvalds
*
* Some code taken from sh version:
* Copyright (C) 1999, 2000 Kaz Kojima & Niibe Yutaka
* Some code taken from arm version:
* Copyright (C) 2000 Russell King
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/string.h>
#include <linux/signal.h>
#include <asm/cacheflush.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/processor.h>
#include <asm/mmu_context.h>
/*
* This routine will get a word off of the process kernel stack.
*/
static inline unsigned long int
get_stack_long(struct task_struct *task, int offset)
{
unsigned long *stack;
stack = (unsigned long *)task_pt_regs(task);
return stack[offset];
}
/*
* This routine will put a word on the process kernel stack.
*/
static inline int
put_stack_long(struct task_struct *task, int offset, unsigned long data)
{
unsigned long *stack;
stack = (unsigned long *)task_pt_regs(task);
stack[offset] = data;
return 0;
}
static int reg_offset[] = {
PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7,
PT_R8, PT_R9, PT_R10, PT_R11, PT_R12, PT_FP, PT_LR, PT_SPU,
};
/*
* Read the word at offset "off" into the "struct user". We
* actually access the pt_regs stored on the kernel stack.
*/
static int ptrace_read_user(struct task_struct *tsk, unsigned long off,
unsigned long __user *data)
{
unsigned long tmp;
#ifndef NO_FPU
struct user * dummy = NULL;
#endif
if ((off & 3) || (off < 0) || (off > sizeof(struct user) - 3))
return -EIO;
off >>= 2;
switch (off) {
case PT_EVB:
__asm__ __volatile__ (
"mvfc %0, cr5 \n\t"
: "=r" (tmp)
);
break;
case PT_CBR: {
unsigned long psw;
psw = get_stack_long(tsk, PT_PSW);
tmp = ((psw >> 8) & 1);
}
break;
case PT_PSW: {
unsigned long psw, bbpsw;
psw = get_stack_long(tsk, PT_PSW);
bbpsw = get_stack_long(tsk, PT_BBPSW);
tmp = ((psw >> 8) & 0xff) | ((bbpsw & 0xff) << 8);
}
break;
case PT_PC:
tmp = get_stack_long(tsk, PT_BPC);
break;
case PT_BPC:
off = PT_BBPC;
/* fall through */
default:
if (off < (sizeof(struct pt_regs) >> 2))
tmp = get_stack_long(tsk, off);
#ifndef NO_FPU
else if (off >= (long)(&dummy->fpu >> 2) &&
off < (long)(&dummy->u_fpvalid >> 2)) {
if (!tsk_used_math(tsk)) {
if (off == (long)(&dummy->fpu.fpscr >> 2))
tmp = FPSCR_INIT;
else
tmp = 0;
} else
tmp = ((long *)(&tsk->thread.fpu >> 2))
[off - (long)&dummy->fpu];
} else if (off == (long)(&dummy->u_fpvalid >> 2))
tmp = !!tsk_used_math(tsk);
#endif /* not NO_FPU */
else
tmp = 0;
}
return put_user(tmp, data);
}
static int ptrace_write_user(struct task_struct *tsk, unsigned long off,
unsigned long data)
{
int ret = -EIO;
#ifndef NO_FPU
struct user * dummy = NULL;
#endif
if ((off & 3) || off < 0 ||
off > sizeof(struct user) - 3)
return -EIO;
off >>= 2;
switch (off) {
case PT_EVB:
case PT_BPC:
case PT_SPI:
/* We don't allow to modify evb. */
ret = 0;
break;
case PT_PSW:
case PT_CBR: {
/* We allow to modify only cbr in psw */
unsigned long psw;
psw = get_stack_long(tsk, PT_PSW);
psw = (psw & ~0x100) | ((data & 1) << 8);
ret = put_stack_long(tsk, PT_PSW, psw);
}
break;
case PT_PC:
off = PT_BPC;
data &= ~1;
/* fall through */
default:
if (off < (sizeof(struct pt_regs) >> 2))
ret = put_stack_long(tsk, off, data);
#ifndef NO_FPU
else if (off >= (long)(&dummy->fpu >> 2) &&
off < (long)(&dummy->u_fpvalid >>