Commit 1f8870ed authored by Miles Bader's avatar Miles Bader Committed by Arnaldo Carvalho de Melo

[PATCH] Add v850 support for hardware single-step (via ptrace)

parent 7d136361
This diff is collapsed.
/* /*
* arch/v850/kernel/ptrace.c -- `ptrace' system call * arch/v850/kernel/ptrace.c -- `ptrace' system call
* *
* Copyright (C) 2002 NEC Corporation * Copyright (C) 2002,03 NEC Electronics Corporation
* Copyright (C) 2002 Miles Bader <miles@gnu.org> * Copyright (C) 2002,03 Miles Bader <miles@gnu.org>
* *
* Derived from arch/mips/kernel/ptrace.c: * Derived from arch/mips/kernel/ptrace.c:
* *
...@@ -29,6 +29,89 @@ ...@@ -29,6 +29,89 @@
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
/* Returns the address where the register at REG_OFFS in P is stashed away. */
static v850_reg_t *reg_save_addr (unsigned reg_offs, struct task_struct *t)
{
struct pt_regs *regs;
/* Three basic cases:
(1) A register normally saved before calling the scheduler, is
available in the kernel entry pt_regs structure at the top
of the kernel stack. The kernel trap/irq exit path takes
care to save/restore almost all registers for ptrace'd
processes.
(2) A call-clobbered register, where the process P entered the
kernel via [syscall] trap, is not stored anywhere; that's
OK, because such registers are not expected to be preserved
when the trap returns anyway (so we don't actually bother to
test for this case).
(3) A few registers not used at all by the kernel, and so
normally never saved except by context-switches, are in the
context switch state. */
if (reg_offs == PT_CTPC || reg_offs == PT_CTPSW || reg_offs == PT_CTBP)
/* Register saved during context switch. */
regs = thread_saved_regs (t);
else
/* Register saved during kernel entry (or not available). */
regs = task_regs (t);
return (v850_reg_t *)((char *)regs + reg_offs);
}
/* Set the bits SET and clear the bits CLEAR in the v850e DIR
(`debug information register'). Returns the new value of DIR. */
static inline v850_reg_t set_dir (v850_reg_t set, v850_reg_t clear)
{
register v850_reg_t rval asm ("r10");
register v850_reg_t arg0 asm ("r6") = set;
register v850_reg_t arg1 asm ("r7") = clear;
/* The dbtrap handler has exactly this functionality when called
from kernel mode. 0xf840 is a `dbtrap' insn. */
asm (".short 0xf840" : "=r" (rval) : "r" (arg0), "r" (arg1));
return rval;
}
/* Makes sure hardware single-stepping is (globally) enabled.
Returns true if successful. */
static inline int enable_single_stepping (void)
{
static int enabled = 0; /* Remember whether we already did it. */
if (! enabled) {
/* Turn on the SE (`single-step enable') bit, 0x100, in the
DIR (`debug information register'). This may fail if a
processor doesn't support it or something. We also try
to clear bit 0x40 (`INI'), which is necessary to use the
debug stuff on the v850e2; on the v850e, clearing 0x40
shouldn't cause any problem. */
v850_reg_t dir = set_dir (0x100, 0x40);
/* Make sure it really got set. */
if (dir & 0x100)
enabled = 1;
}
return enabled;
}
/* Try to set CHILD's single-step flag to VAL. Returns true if successful. */
static int set_single_step (struct task_struct *t, int val)
{
v850_reg_t *psw_addr = reg_save_addr(PT_PSW, t);
if (val) {
/* Make sure single-stepping is enabled. */
if (! enable_single_stepping ())
return 0;
/* Set T's single-step flag. */
*psw_addr |= 0x800;
} else
*psw_addr &= ~0x800;
return 1;
}
int sys_ptrace(long request, long pid, long addr, long data) int sys_ptrace(long request, long pid, long addr, long data)
{ {
struct task_struct *child; struct task_struct *child;
...@@ -36,12 +119,6 @@ int sys_ptrace(long request, long pid, long addr, long data) ...@@ -36,12 +119,6 @@ int sys_ptrace(long request, long pid, long addr, long data)
lock_kernel(); lock_kernel();
#if 0
printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n",
(int) request, (int) pid, (unsigned long) addr,
(unsigned long) data);
#endif
if (request == PTRACE_TRACEME) { if (request == PTRACE_TRACEME) {
/* are we already being traced? */ /* are we already being traced? */
if (current->ptrace & PT_PTRACED) { if (current->ptrace & PT_PTRACED) {
...@@ -81,31 +158,15 @@ int sys_ptrace(long request, long pid, long addr, long data) ...@@ -81,31 +158,15 @@ int sys_ptrace(long request, long pid, long addr, long data)
goto out_tsk; goto out_tsk;
switch (request) { switch (request) {
case PTRACE_PEEKTEXT: /* read word at location addr. */ unsigned long val, copied;
case PTRACE_PEEKDATA:{
unsigned long tmp;
int copied;
copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); case PTRACE_PEEKTEXT: /* read word at location addr. */
case PTRACE_PEEKDATA:
copied = access_process_vm(child, addr, &val, sizeof(val), 0);
rval = -EIO; rval = -EIO;
if (copied != sizeof(tmp)) if (copied != sizeof(val))
break; break;
rval = put_user(tmp,(unsigned long *) data); rval = put_user(val, (unsigned long *)data);
goto out;
}
/* Read the word at location addr in the USER area. */
case PTRACE_PEEKUSR:
if (addr >= 0 && addr < PT_SIZE && (addr & 0x3) == 0) {
struct pt_regs *regs = task_regs (child);
unsigned long val =
*(unsigned long *)((char *)regs + addr);
rval = put_user (val, (unsigned long *)data);
} else {
rval = 0;
rval = -EIO;
}
goto out; goto out;
case PTRACE_POKETEXT: /* write the word at location addr. */ case PTRACE_POKETEXT: /* write the word at location addr. */
...@@ -117,35 +178,62 @@ int sys_ptrace(long request, long pid, long addr, long data) ...@@ -117,35 +178,62 @@ int sys_ptrace(long request, long pid, long addr, long data)
rval = -EIO; rval = -EIO;
goto out; goto out;
/* Read/write the word at location ADDR in the registers. */
case PTRACE_PEEKUSR:
case PTRACE_POKEUSR: case PTRACE_POKEUSR:
if (addr >= 0 && addr < PT_SIZE && (addr & 0x3) == 0) { rval = 0;
struct pt_regs *regs = task_regs (child); if (addr >= PT_SIZE && request == PTRACE_PEEKUSR) {
unsigned long *loc = /* Special requests that don't actually correspond
(unsigned long *)((char *)regs + addr); to offsets in struct pt_regs. */
*loc = data; if (addr == PT_TEXT_ADDR)
} else { val = child->mm->start_code;
rval = 0; else if (addr == PT_DATA_ADDR)
rval = -EIO; val = child->mm->start_data;
} else if (addr == PT_TEXT_LEN)
val = child->mm->end_code
- child->mm->start_code;
else
rval = -EIO;
} else if (addr >= 0 && addr < PT_SIZE && (addr & 0x3) == 0) {
v850_reg_t *reg_addr = reg_save_addr(addr, child);
if (request == PTRACE_PEEKUSR)
val = *reg_addr;
else
*reg_addr = data;
} else
rval = -EIO;
if (rval == 0 && request == PTRACE_PEEKUSR)
rval = put_user (val, (unsigned long *)data);
goto out; goto out;
case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ /* Continue and stop at next (return from) syscall */
case PTRACE_CONT: /* rvaltart after signal. */ case PTRACE_SYSCALL:
/* Restart after a signal. */
case PTRACE_CONT:
/* Execute a single instruction. */
case PTRACE_SINGLESTEP:
rval = -EIO; rval = -EIO;
if ((unsigned long) data > _NSIG) if ((unsigned long) data > _NSIG)
break; break;
/* Turn CHILD's single-step flag on or off. */
if (! set_single_step (child, request == PTRACE_SINGLESTEP))
break;
if (request == PTRACE_SYSCALL) if (request == PTRACE_SYSCALL)
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
else else
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data; child->exit_code = data;
wake_up_process(child); wake_up_process(child);
rval = 0; rval = 0;
break; break;
/* /*
* make the child exit. Best I can do is send it a sigkill. * make the child exit. Best I can do is send it a sigkill.
* perhaps it should be put in the status that it wants to * perhaps it should be put in the status that it wants to
* exit. * exit.
*/ */
case PTRACE_KILL: case PTRACE_KILL:
...@@ -157,6 +245,7 @@ int sys_ptrace(long request, long pid, long addr, long data) ...@@ -157,6 +245,7 @@ int sys_ptrace(long request, long pid, long addr, long data)
break; break;
case PTRACE_DETACH: /* detach a process that was attached. */ case PTRACE_DETACH: /* detach a process that was attached. */
set_single_step (child, 0); /* Clear single-step flag */
rval = ptrace_detach(child, data); rval = ptrace_detach(child, data);
break; break;
...@@ -181,7 +270,7 @@ asmlinkage void syscall_trace(void) ...@@ -181,7 +270,7 @@ asmlinkage void syscall_trace(void)
/* The 0x80 provides a way for the tracing parent to distinguish /* The 0x80 provides a way for the tracing parent to distinguish
between a syscall stop and SIGTRAP delivery */ between a syscall stop and SIGTRAP delivery */
current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
? 0x80 : 0); ? 0x80 : 0);
current->state = TASK_STOPPED; current->state = TASK_STOPPED;
notify_parent(current, SIGCHLD); notify_parent(current, SIGCHLD);
schedule(); schedule();
......
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