Commit f6737e3e authored by Russell King's avatar Russell King

[ARM] Rudimentary support for Thumb ptracing.

Add rudimentary support for Thumb ptracing; we aren't able to single
step through thumb branches yet, but this change provides enough
infrastructure to make this possible.
parent 2d3c0459
...@@ -367,8 +367,8 @@ void dump_thread(struct pt_regs * regs, struct user * dump) ...@@ -367,8 +367,8 @@ void dump_thread(struct pt_regs * regs, struct user * dump)
dump->u_debugreg[0] = tsk->thread.debug.bp[0].address; dump->u_debugreg[0] = tsk->thread.debug.bp[0].address;
dump->u_debugreg[1] = tsk->thread.debug.bp[1].address; dump->u_debugreg[1] = tsk->thread.debug.bp[1].address;
dump->u_debugreg[2] = tsk->thread.debug.bp[0].insn; dump->u_debugreg[2] = tsk->thread.debug.bp[0].insn.arm;
dump->u_debugreg[3] = tsk->thread.debug.bp[1].insn; dump->u_debugreg[3] = tsk->thread.debug.bp[1].insn.arm;
dump->u_debugreg[4] = tsk->thread.debug.nsaved; dump->u_debugreg[4] = tsk->thread.debug.nsaved;
if (dump->start_stack < 0x04000000) if (dump->start_stack < 0x04000000)
......
...@@ -32,10 +32,24 @@ ...@@ -32,10 +32,24 @@
* in exit.c or in signal.c. * in exit.c or in signal.c.
*/ */
#if 1
/* /*
* Breakpoint SWI instruction: SWI &9F0001 * Breakpoint SWI instruction: SWI &9F0001
*/ */
#define BREAKINST_ARM 0xef9f0001 #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
/* /*
* Get the address of the live pt_regs for the specified task. * Get the address of the live pt_regs for the specified task.
...@@ -89,23 +103,32 @@ put_user_reg(struct task_struct *task, int offset, long data) ...@@ -89,23 +103,32 @@ put_user_reg(struct task_struct *task, int offset, long data)
} }
static inline int static inline int
read_tsk_long(struct task_struct *child, unsigned long addr, unsigned long *res) read_u32(struct task_struct *task, unsigned long addr, u32 *res)
{ {
int copied; int ret;
copied = access_process_vm(child, addr, res, sizeof(*res), 0); ret = access_process_vm(task, addr, res, sizeof(*res), 0);
return copied != sizeof(*res) ? -EIO : 0; return ret == sizeof(*res) ? 0 : -EIO;
} }
static inline int static inline int
write_tsk_long(struct task_struct *child, unsigned long addr, unsigned long val) read_instr(struct task_struct *task, unsigned long addr, u32 *res)
{ {
int copied; int ret;
copied = access_process_vm(child, addr, &val, sizeof(val), 1);
return copied != sizeof(val) ? -EIO : 0; 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;
} }
/* /*
...@@ -206,7 +229,7 @@ ptrace_getldrop2(struct task_struct *child, unsigned long insn) ...@@ -206,7 +229,7 @@ ptrace_getldrop2(struct task_struct *child, unsigned long insn)
static unsigned long static unsigned long
get_branch_address(struct task_struct *child, unsigned long pc, unsigned long insn) get_branch_address(struct task_struct *child, unsigned long pc, unsigned long insn)
{ {
unsigned long alt = 0; u32 alt = 0;
switch (insn & 0x0e000000) { switch (insn & 0x0e000000) {
case 0x00000000: case 0x00000000:
...@@ -262,7 +285,7 @@ get_branch_address(struct task_struct *child, unsigned long pc, unsigned long in ...@@ -262,7 +285,7 @@ get_branch_address(struct task_struct *child, unsigned long pc, unsigned long in
else else
base -= aluop2; base -= aluop2;
} }
if (read_tsk_long(child, base, &alt) == 0) if (read_u32(child, base, &alt) == 0)
alt = pc_pointer(alt); alt = pc_pointer(alt);
} }
break; break;
...@@ -289,7 +312,7 @@ get_branch_address(struct task_struct *child, unsigned long pc, unsigned long in ...@@ -289,7 +312,7 @@ get_branch_address(struct task_struct *child, unsigned long pc, unsigned long in
base = ptrace_getrn(child, insn); base = ptrace_getrn(child, insn);
if (read_tsk_long(child, base + nr_regs, &alt) == 0) if (read_u32(child, base + nr_regs, &alt) == 0)
alt = pc_pointer(alt); alt = pc_pointer(alt);
break; break;
} }
...@@ -319,30 +342,71 @@ get_branch_address(struct task_struct *child, unsigned long pc, unsigned long in ...@@ -319,30 +342,71 @@ get_branch_address(struct task_struct *child, unsigned long pc, unsigned long in
} }
static int static int
add_breakpoint(struct task_struct *child, struct debug_info *dbg, unsigned long addr) swap_insn(struct task_struct *task, unsigned long addr,
void *old_insn, void *new_insn, int size)
{
int ret;
ret = access_process_vm(task, addr, old_insn, size, 0);
if (ret == size)
ret = access_process_vm(task, addr, new_insn, size, 1);
return ret;
}
static void
add_breakpoint(struct task_struct *task, struct debug_info *dbg, unsigned long addr)
{ {
int nr = dbg->nsaved; int nr = dbg->nsaved;
int res = -EINVAL;
if (nr < 2) { if (nr < 2) {
res = read_tsk_long(child, addr, &dbg->bp[nr].insn); u32 new_insn = BREAKINST_ARM;
if (res == 0) int res;
res = write_tsk_long(child, addr, BREAKINST_ARM);
if (res == 0) { res = swap_insn(task, addr, &dbg->bp[nr].insn, &new_insn, 4);
if (res == 4) {
dbg->bp[nr].address = addr; dbg->bp[nr].address = addr;
dbg->nsaved += 1; dbg->nsaved += 1;
} }
} else } else
printk(KERN_ERR "ptrace: too many breakpoints\n"); printk(KERN_ERR "ptrace: too many breakpoints\n");
}
/*
* Clear one breakpoint in the user program. We copy what the hardware
* does and use bit 0 of the address to indicate whether this is a Thumb
* breakpoint or an ARM breakpoint.
*/
static void clear_breakpoint(struct task_struct *task, struct debug_entry *bp)
{
unsigned long addr = bp->address;
union debug_insn old_insn;
int ret;
if (addr & 1) {
ret = swap_insn(task, addr & ~1, &old_insn.thumb,
&bp->insn.thumb, 2);
return res; if (ret != 2 || old_insn.thumb != BREAKINST_THUMB)
printk(KERN_ERR "%s:%d: corrupted Thumb breakpoint at "
"0x%08lx (0x%04x)\n", task->comm, task->pid,
addr, old_insn.thumb);
} else {
ret = swap_insn(task, addr & ~3, &old_insn.arm,
&bp->insn.arm, 4);
if (ret != 4 || old_insn.arm != BREAKINST_ARM)
printk(KERN_ERR "%s:%d: corrupted ARM breakpoint at "
"0x%08lx (0x%08x)\n", task->comm, task->pid,
addr, old_insn.arm);
}
} }
void ptrace_set_bpt(struct task_struct *child) void ptrace_set_bpt(struct task_struct *child)
{ {
struct pt_regs *regs; struct pt_regs *regs;
unsigned long pc, insn; unsigned long pc;
u32 insn;
int res; int res;
regs = get_user_regs(child); regs = get_user_regs(child);
...@@ -353,7 +417,7 @@ void ptrace_set_bpt(struct task_struct *child) ...@@ -353,7 +417,7 @@ void ptrace_set_bpt(struct task_struct *child)
return; return;
} }
res = read_tsk_long(child, pc, &insn); res = read_instr(child, pc, &insn);
if (!res) { if (!res) {
struct debug_info *dbg = &child->thread.debug; struct debug_info *dbg = &child->thread.debug;
unsigned long alt; unsigned long alt;
...@@ -362,7 +426,7 @@ void ptrace_set_bpt(struct task_struct *child) ...@@ -362,7 +426,7 @@ void ptrace_set_bpt(struct task_struct *child)
alt = get_branch_address(child, pc, insn); alt = get_branch_address(child, pc, insn);
if (alt) if (alt)
res = add_breakpoint(child, dbg, alt); add_breakpoint(child, dbg, alt);
/* /*
* Note that we ignore the result of setting the above * Note that we ignore the result of setting the above
...@@ -374,7 +438,7 @@ void ptrace_set_bpt(struct task_struct *child) ...@@ -374,7 +438,7 @@ void ptrace_set_bpt(struct task_struct *child)
* loose control of the thread during single stepping. * loose control of the thread during single stepping.
*/ */
if (!alt || predicate(insn) != PREDICATE_ALWAYS) if (!alt || predicate(insn) != PREDICATE_ALWAYS)
res = add_breakpoint(child, dbg, pc + 4); add_breakpoint(child, dbg, pc + 4);
} }
} }
...@@ -384,24 +448,17 @@ void ptrace_set_bpt(struct task_struct *child) ...@@ -384,24 +448,17 @@ void ptrace_set_bpt(struct task_struct *child)
*/ */
void __ptrace_cancel_bpt(struct task_struct *child) void __ptrace_cancel_bpt(struct task_struct *child)
{ {
struct debug_info *dbg = &child->thread.debug; int i, nsaved = child->thread.debug.nsaved;
int i, nsaved = dbg->nsaved;
dbg->nsaved = 0; child->thread.debug.nsaved = 0;
if (nsaved > 2) { if (nsaved > 2) {
printk("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved); printk("ptrace_cancel_bpt: bogus nsaved: %d!\n", nsaved);
nsaved = 2; nsaved = 2;
} }
for (i = 0; i < nsaved; i++) { for (i = 0; i < nsaved; i++)
unsigned long tmp; clear_breakpoint(child, &child->thread.debug.bp[i]);
read_tsk_long(child, dbg->bp[i].address, &tmp);
write_tsk_long(child, dbg->bp[i].address, dbg->bp[i].insn);
if (tmp != BREAKINST_ARM)
printk(KERN_ERR "ptrace_cancel_bpt: weirdness\n");
}
} }
/* /*
...@@ -537,9 +594,12 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat ...@@ -537,9 +594,12 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat
*/ */
case PTRACE_PEEKTEXT: case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA: case PTRACE_PEEKDATA:
ret = read_tsk_long(child, addr, &tmp); ret = access_process_vm(child, addr, &tmp,
if (!ret) sizeof(unsigned long), 0);
if (ret == sizeof(unsigned long))
ret = put_user(tmp, (unsigned long *) data); ret = put_user(tmp, (unsigned long *) data);
else
ret = -EIO;
break; break;
case PTRACE_PEEKUSR: case PTRACE_PEEKUSR:
...@@ -551,7 +611,12 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat ...@@ -551,7 +611,12 @@ static int do_ptrace(int request, struct task_struct *child, long addr, long dat
*/ */
case PTRACE_POKETEXT: case PTRACE_POKETEXT:
case PTRACE_POKEDATA: case PTRACE_POKEDATA:
ret = write_tsk_long(child, addr, data); ret = access_process_vm(child, addr, &data,
sizeof(unsigned long), 1);
if (ret == sizeof(unsigned long))
ret = 0;
else
ret = -EIO;
break; break;
case PTRACE_POKEUSR: case PTRACE_POKEUSR:
......
...@@ -28,22 +28,30 @@ ...@@ -28,22 +28,30 @@
#include <asm/procinfo.h> #include <asm/procinfo.h>
#include <asm/arch/memory.h> #include <asm/arch/memory.h>
#include <asm/proc/processor.h> #include <asm/proc/processor.h>
#include <asm/types.h>
union debug_insn {
u32 arm;
u16 thumb;
};
struct debug_entry {
u32 address;
union debug_insn insn;
};
struct debug_info { struct debug_info {
int nsaved; int nsaved;
struct { struct debug_entry bp[2];
unsigned long address;
unsigned long insn;
} bp[2];
}; };
struct thread_struct { struct thread_struct {
/* fault info */ /* fault info */
unsigned long address; unsigned long address;
unsigned long trap_no; unsigned long trap_no;
unsigned long error_code; unsigned long error_code;
/* debugging */ /* debugging */
struct debug_info debug; struct debug_info debug;
}; };
#define INIT_THREAD { } #define INIT_THREAD { }
......
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