Commit a784ab71 authored by Corey Minyard's avatar Corey Minyard Committed by Linus Torvalds

[PATCH] PPC debug setcontext syscall implementation.

Add a debugging interface for PowerPC that allows signal handlers (or any
jump to a context, really) to perform debug functions.  It allows the a
user program to turn on single-stepping, for instance, and the thread will
get a trap after executing the next instruction.  It can also (on supported
PPC processors) turn on branch tracing and get a trap after the next branch
instruction is executed.  This is useful for in-application debugging.

Note that you can enable single-stepping on x86 processors directly from
signal handlers.  Newer x86 processors have the equivalent of a
branch-trace bit in the IA32_DEBUGCTL MSR and could have similar function
to this syscall.  Most other processors could benefit from a similar
interface, except for ARM which is extraordinarily broken for debugging.

Future uses of this could be adding the ability to set the hardware
breakpoint registers from a signal handler.
Signed-off-by: default avatarCorey Minyard <minyard@mvista.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent a47ac38f
...@@ -111,8 +111,10 @@ transfer_to_handler: ...@@ -111,8 +111,10 @@ transfer_to_handler:
addi r11,r1,STACK_FRAME_OVERHEAD addi r11,r1,STACK_FRAME_OVERHEAD
stw r11,PT_REGS(r12) stw r11,PT_REGS(r12)
#if defined(CONFIG_40x) || defined(CONFIG_BOOKE) #if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
lwz r12,PTRACE-THREAD(r12) /* Check to see if the dbcr0 register is set up to debug. Use the
andi. r12,r12,PT_PTRACED single-step bit to do this. */
lwz r12,THREAD_DBCR0(r12)
andis. r12,r12,DBCR0_IC@h
beq+ 3f beq+ 3f
/* From user and task is ptraced - load up global dbcr0 */ /* From user and task is ptraced - load up global dbcr0 */
li r12,-1 /* clear all pending debug events */ li r12,-1 /* clear all pending debug events */
...@@ -242,9 +244,10 @@ ret_from_syscall: ...@@ -242,9 +244,10 @@ ret_from_syscall:
bne- syscall_exit_work bne- syscall_exit_work
syscall_exit_cont: syscall_exit_cont:
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
/* If the process has its own DBCR0 value, load it up */ /* If the process has its own DBCR0 value, load it up. The single
lwz r0,PTRACE(r2) step bit tells us that dbcr0 should be loaded. */
andi. r0,r0,PT_PTRACED lwz r0,THREAD+THREAD_DBCR0(r2)
andis. r10,r0,DBCR0_IC@h
bnel- load_dbcr0 bnel- load_dbcr0
#endif #endif
stwcx. r0,0,r1 /* to clear the reservation */ stwcx. r0,0,r1 /* to clear the reservation */
...@@ -599,9 +602,10 @@ user_exc_return: /* r10 contains MSR_KERNEL here */ ...@@ -599,9 +602,10 @@ user_exc_return: /* r10 contains MSR_KERNEL here */
restore_user: restore_user:
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
/* Check whether this process has its own DBCR0 value */ /* Check whether this process has its own DBCR0 value. The single
lwz r0,PTRACE(r2) step bit tells us that dbcr0 should be loaded. */
andi. r0,r0,PT_PTRACED lwz r0,THREAD+THREAD_DBCR0(r2)
andis. r10,r0,DBCR0_IC@h
bnel- load_dbcr0 bnel- load_dbcr0
#endif #endif
...@@ -876,17 +880,17 @@ ret_from_mcheck_exc: ...@@ -876,17 +880,17 @@ ret_from_mcheck_exc:
/* /*
* Load the DBCR0 value for a task that is being ptraced, * Load the DBCR0 value for a task that is being ptraced,
* having first saved away the global DBCR0. * having first saved away the global DBCR0. Note that r0
* has the dbcr0 value to set upon entry to this.
*/ */
load_dbcr0: load_dbcr0:
mfmsr r0 /* first disable debug exceptions */ mfmsr r10 /* first disable debug exceptions */
rlwinm r0,r0,0,~MSR_DE rlwinm r10,r10,0,~MSR_DE
mtmsr r0 mtmsr r10
isync isync
mfspr r10,SPRN_DBCR0 mfspr r10,SPRN_DBCR0
lis r11,global_dbcr0@ha lis r11,global_dbcr0@ha
addi r11,r11,global_dbcr0@l addi r11,r11,global_dbcr0@l
lwz r0,THREAD+THREAD_DBCR0(r2)
stw r10,0(r11) stw r10,0(r11)
mtspr SPRN_DBCR0,r0 mtspr SPRN_DBCR0,r0
lwz r10,4(r11) lwz r10,4(r11)
......
...@@ -1434,7 +1434,7 @@ _GLOBAL(sys_call_table) ...@@ -1434,7 +1434,7 @@ _GLOBAL(sys_call_table)
.long sys_fstatfs64 .long sys_fstatfs64
.long ppc_fadvise64_64 .long ppc_fadvise64_64
.long sys_ni_syscall /* 255 - rtas (used on ppc64) */ .long sys_ni_syscall /* 255 - rtas (used on ppc64) */
.long sys_ni_syscall /* 256 reserved for sys_debug_setcontext */ .long sys_debug_setcontext
.long sys_ni_syscall /* 257 reserved for vserver */ .long sys_ni_syscall /* 257 reserved for vserver */
.long sys_ni_syscall /* 258 reserved for new sys_remap_file_pages */ .long sys_ni_syscall /* 258 reserved for new sys_remap_file_pages */
.long sys_ni_syscall /* 259 reserved for new sys_mbind */ .long sys_ni_syscall /* 259 reserved for new sys_mbind */
......
...@@ -509,6 +509,96 @@ int sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8, ...@@ -509,6 +509,96 @@ int sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
return 0; return 0;
} }
int sys_debug_setcontext(struct ucontext __user *ctx,
int ndbg, struct sig_dbg_op *dbg,
int r6, int r7, int r8,
struct pt_regs *regs)
{
struct sig_dbg_op op;
int i;
unsigned long new_msr = regs->msr;
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
unsigned long new_dbcr0 = current->thread.dbcr0;
#endif
for (i=0; i<ndbg; i++) {
if (__copy_from_user(&op, dbg, sizeof(op)))
return -EFAULT;
switch (op.dbg_type) {
case SIG_DBG_SINGLE_STEPPING:
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
if (op.dbg_value) {
new_msr |= MSR_DE;
new_dbcr0 |= (DBCR0_IDM | DBCR0_IC);
} else {
new_msr &= ~MSR_DE;
new_dbcr0 &= ~(DBCR0_IDM | DBCR0_IC);
}
#else
if (op.dbg_value)
new_msr |= MSR_SE;
else
new_msr &= ~MSR_SE;
#endif
break;
case SIG_DBG_BRANCH_TRACING:
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
return -EINVAL;
#else
if (op.dbg_value)
new_msr |= MSR_BE;
else
new_msr &= ~MSR_BE;
#endif
break;
default:
return -EINVAL;
}
}
/* We wait until here to actually install the values in the
registers so if we fail in the above loop, it will not
affect the contents of these registers. After this point,
failure is a problem, anyway, and it's very unlikely unless
the user is really doing something wrong. */
regs->msr = new_msr;
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
current->thread.dbcr0 = new_dbcr0;
#endif
/*
* If we get a fault copying the context into the kernel's
* image of the user's registers, we can't just return -EFAULT
* because the user's registers will be corrupted. For instance
* the NIP value may have been updated but not some of the
* other registers. Given that we have done the verify_area
* and successfully read the first and last bytes of the region
* above, this should only happen in an out-of-memory situation
* or if another thread unmaps the region containing the context.
* We kill the task with a SIGSEGV in this situation.
*/
if (do_setcontext(ctx, regs, 1)) {
force_sig(SIGSEGV, current);
goto out;
}
/*
* It's not clear whether or why it is desirable to save the
* sigaltstack setting on signal delivery and restore it on
* signal return. But other architectures do this and we have
* always done it up until now so it is probably better not to
* change it. -- paulus
*/
do_sigaltstack(&ctx->uc_stack, NULL, regs->gpr[1]);
sigreturn_exit(regs);
/* doesn't actually return back to here */
out:
return 0;
}
/* /*
* OK, we're invoking a handler * OK, we're invoking a handler
*/ */
......
...@@ -566,7 +566,7 @@ void ProgramCheckException(struct pt_regs *regs) ...@@ -566,7 +566,7 @@ void ProgramCheckException(struct pt_regs *regs)
void SingleStepException(struct pt_regs *regs) void SingleStepException(struct pt_regs *regs)
{ {
regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */ regs->msr &= ~(MSR_SE | MSR_BE); /* Turn off 'trace' bits */
if (debugger_sstep(regs)) if (debugger_sstep(regs))
return; return;
_exception(SIGTRAP, regs, TRAP_TRACE, 0); _exception(SIGTRAP, regs, TRAP_TRACE, 0);
......
...@@ -157,4 +157,23 @@ typedef struct sigaltstack { ...@@ -157,4 +157,23 @@ typedef struct sigaltstack {
#define ptrace_signal_deliver(regs, cookie) do { } while (0) #define ptrace_signal_deliver(regs, cookie) do { } while (0)
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
/*
* These are parameters to dbg_sigreturn syscall. They enable or
* disable certain debugging things that can be done from signal
* handlers. The dbg_sigreturn syscall *must* be called from a
* SA_SIGINFO signal so the ucontext can be passed to it. It takes an
* array of struct sig_dbg_op, which has the debug operations to
* perform before returning from the signal.
*/
struct sig_dbg_op {
int dbg_type;
unsigned long dbg_value;
};
/* Enable or disable single-stepping. The value sets the state. */
#define SIG_DBG_SINGLE_STEPPING 1
/* Enable or disable branch tracing. The value sets the state. */
#define SIG_DBG_BRANCH_TRACING 2
#endif #endif
...@@ -260,7 +260,7 @@ ...@@ -260,7 +260,7 @@
#define __NR_fstatfs64 253 #define __NR_fstatfs64 253
#define __NR_fadvise64_64 254 #define __NR_fadvise64_64 254
#define __NR_rtas 255 #define __NR_rtas 255
/* Number 256 is reserved for sys_debug_setcontext */ #define __NR_sys_debug_setcontext 256
/* Number 257 is reserved for vserver */ /* Number 257 is reserved for vserver */
/* Number 258 is reserved for new sys_remap_file_pages */ /* Number 258 is reserved for new sys_remap_file_pages */
/* Number 259 is reserved for new sys_mbind */ /* Number 259 is reserved for new sys_mbind */
......
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