/*
* linux/arch/arm/kernel/ptrace.c
*
* By Ross Biro 1/23/92
* edited by Linus Torvalds
* ARM modifications Copyright (C) 2000 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/init.h>
#include <linux/signal.h>
#include <linux/uaccess.h>
#include <asm/pgtable.h>
#include <asm/system.h>
#include <asm/traps.h>
#include "ptrace.h"
#define REG_PC 15
#define REG_PSR 16
/*
* does not yet catch signals sent when the child dies.
* in exit.c or in signal.c.
*/
#if 0
/*
* Breakpoint SWI instruction: SWI &9F0001
*/
#define BREAKINST_ARM 0xef9f0001
#define BREAKINST_THUMB 0xdf00 /* fill this in later */
#else
/*
* New breakpoints - use an undefined instruction. The ARM architecture
* reference manual guarantees that the following instruction space
* will produce an undefined instruction exception on all CPUs:
*
* ARM: xxxx 0111 1111 xxxx xxxx xxxx 1111 xxxx
* Thumb: 1101 1110 xxxx xxxx
*/
#define BREAKINST_ARM 0xe7f001f0
#define BREAKINST_THUMB 0xde01
#endif
/*
* this routine will get a word off of the processes privileged stack.
* the offset is how far from the base addr as stored in the THREAD.
* this routine assumes that all the privileged stacks are in our
* data space.
*/
static inline long get_user_reg(struct task_struct *task, int offset)
{
return task_pt_regs(task)->uregs[offset];
}
/*
* this routine will put a word on the processes privileged stack.
* the offset is how far from the base addr as stored in the THREAD.
* this routine assumes that all the privileged stacks are in our
* data space.
*/
static inline int
put_user_reg(struct task_struct *task, int offset, long data)
{
struct pt_regs newregs, *regs = task_pt_regs(task);
int ret = -EINVAL;
newregs = *regs;
newregs.uregs[offset] = data;
if (valid_user_regs(&newregs)) {
regs->uregs[offset] = data;
ret = 0;
}
return ret;
}
static inline int
read_u32(struct task_struct *task, unsigned long addr, u32 *res)
{
int ret;
ret = access_process_vm(task, addr, res, sizeof(*res), 0);
return ret == sizeof(*res) ? 0 : -EIO;
}
static inline int
read_instr(struct task_struct *task, unsigned long addr, u32 *res)
{
int ret;
if (addr & 1) {
u16 val;
ret = access_process_vm(task, addr & ~1, &val, sizeof(val), 0);
ret = ret == sizeof(val) ? 0 : -EIO;
*res = val;
} else {
u32 val;
ret = access_process_vm(task, addr & ~3, &val, sizeof(val), 0);
ret = ret == sizeof(val) ? 0 : -EIO;
*res = val;
}
return ret;
}
/*
* Get value of register `rn' (in the instruction)
*/
static unsigned long
ptrace_getrn(struct task_struct *child, unsigned long insn)
{
unsigned int reg = (insn >> 16) & 15;
unsigned long val;
val = get_user_reg(child, reg);
if (reg == 15)
val += 8;
return val;
}
/*
* Get value of operand 2 (in an ALU instruction)
*/
static unsigned long
ptrace_getaluop2(struct task_struct *child, unsigned long insn)
{
unsigned long val;
int shift;
int type;
if (insn & 1 << 25) {
val = insn & 255;
shift = (insn >> 8) & 15;
type = 3;
} else {
val = get_user_reg (child, insn & 15);
if (insn & (1 << 4))
shift = (int)get_user_reg (child, (insn >> 8) & 15);
else
shift = (insn >> 7) & 31;
type = (insn >> 5) & 3;
}
switch (type) {
case 0: val <<= shift; break;
case 1: val >>= shift; break;
case 2:
val = (((signed long)val) >> shift);
break;
case 3:
val = (val >> shift) | (val << (32 - shift));
break;
}
return val;
}
/*
* Get value of operand 2 (in a LDR instruction)
*/
static unsigned long
ptrace_getldrop2(struct task_struct *child, unsigned long insn)
{
unsigned long val;
int shift;
int type;
val = get_user_reg(child, insn & 15);
shift = (insn >> 7) & 31;
type = (insn >> 5) & 3;
switch (type) {
case 0: val <<= shift; break;