summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCorey Minyard <minyard@acm.org>2005-01-03 04:38:48 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-01-03 04:38:48 -0800
commita784ab71a34fe92cf470d1fb4c878261b41e2adb (patch)
tree96d891d83e223272648f84aea039d7ada49d772b
parenta47ac38f85d31e47729abd91ffa1089cd7ac2d24 (diff)
[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: Corey Minyard <minyard@mvista.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--arch/ppc/kernel/entry.S30
-rw-r--r--arch/ppc/kernel/misc.S2
-rw-r--r--arch/ppc/kernel/signal.c90
-rw-r--r--arch/ppc/kernel/traps.c2
-rw-r--r--include/asm-ppc/signal.h19
-rw-r--r--include/asm-ppc/unistd.h2
6 files changed, 129 insertions, 16 deletions
diff --git a/arch/ppc/kernel/entry.S b/arch/ppc/kernel/entry.S
index 91318c15ccfa..30fd9535a8e3 100644
--- a/arch/ppc/kernel/entry.S
+++ b/arch/ppc/kernel/entry.S
@@ -111,8 +111,10 @@ transfer_to_handler:
addi r11,r1,STACK_FRAME_OVERHEAD
stw r11,PT_REGS(r12)
#if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
- lwz r12,PTRACE-THREAD(r12)
- andi. r12,r12,PT_PTRACED
+ /* Check to see if the dbcr0 register is set up to debug. Use the
+ single-step bit to do this. */
+ lwz r12,THREAD_DBCR0(r12)
+ andis. r12,r12,DBCR0_IC@h
beq+ 3f
/* From user and task is ptraced - load up global dbcr0 */
li r12,-1 /* clear all pending debug events */
@@ -242,9 +244,10 @@ ret_from_syscall:
bne- syscall_exit_work
syscall_exit_cont:
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
- /* If the process has its own DBCR0 value, load it up */
- lwz r0,PTRACE(r2)
- andi. r0,r0,PT_PTRACED
+ /* If the process has its own DBCR0 value, load it up. The single
+ step bit tells us that dbcr0 should be loaded. */
+ lwz r0,THREAD+THREAD_DBCR0(r2)
+ andis. r10,r0,DBCR0_IC@h
bnel- load_dbcr0
#endif
stwcx. r0,0,r1 /* to clear the reservation */
@@ -599,9 +602,10 @@ user_exc_return: /* r10 contains MSR_KERNEL here */
restore_user:
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
- /* Check whether this process has its own DBCR0 value */
- lwz r0,PTRACE(r2)
- andi. r0,r0,PT_PTRACED
+ /* Check whether this process has its own DBCR0 value. The single
+ step bit tells us that dbcr0 should be loaded. */
+ lwz r0,THREAD+THREAD_DBCR0(r2)
+ andis. r10,r0,DBCR0_IC@h
bnel- load_dbcr0
#endif
@@ -876,17 +880,17 @@ ret_from_mcheck_exc:
/*
* 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:
- mfmsr r0 /* first disable debug exceptions */
- rlwinm r0,r0,0,~MSR_DE
- mtmsr r0
+ mfmsr r10 /* first disable debug exceptions */
+ rlwinm r10,r10,0,~MSR_DE
+ mtmsr r10
isync
mfspr r10,SPRN_DBCR0
lis r11,global_dbcr0@ha
addi r11,r11,global_dbcr0@l
- lwz r0,THREAD+THREAD_DBCR0(r2)
stw r10,0(r11)
mtspr SPRN_DBCR0,r0
lwz r10,4(r11)
diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S
index 2e3a07675a10..2513cb15ea44 100644
--- a/arch/ppc/kernel/misc.S
+++ b/arch/ppc/kernel/misc.S
@@ -1434,7 +1434,7 @@ _GLOBAL(sys_call_table)
.long sys_fstatfs64
.long ppc_fadvise64_64
.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 /* 258 reserved for new sys_remap_file_pages */
.long sys_ni_syscall /* 259 reserved for new sys_mbind */
diff --git a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c
index b2bc832d785d..4b8b96d8a0c0 100644
--- a/arch/ppc/kernel/signal.c
+++ b/arch/ppc/kernel/signal.c
@@ -509,6 +509,96 @@ int sys_rt_sigreturn(int r3, int r4, int r5, int r6, int r7, int r8,
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
*/
diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c
index 00294709b3f2..7918c22c21f9 100644
--- a/arch/ppc/kernel/traps.c
+++ b/arch/ppc/kernel/traps.c
@@ -566,7 +566,7 @@ void ProgramCheckException(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))
return;
_exception(SIGTRAP, regs, TRAP_TRACE, 0);
diff --git a/include/asm-ppc/signal.h b/include/asm-ppc/signal.h
index 580fcc148259..8cc8b88d4edd 100644
--- a/include/asm-ppc/signal.h
+++ b/include/asm-ppc/signal.h
@@ -157,4 +157,23 @@ typedef struct sigaltstack {
#define ptrace_signal_deliver(regs, cookie) do { } while (0)
#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
diff --git a/include/asm-ppc/unistd.h b/include/asm-ppc/unistd.h
index 63a16a4a8cbc..749af517d72e 100644
--- a/include/asm-ppc/unistd.h
+++ b/include/asm-ppc/unistd.h
@@ -260,7 +260,7 @@
#define __NR_fstatfs64 253
#define __NR_fadvise64_64 254
#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 258 is reserved for new sys_remap_file_pages */
/* Number 259 is reserved for new sys_mbind */