Commit ec097c84 authored by Roland McGrath's avatar Roland McGrath Committed by Benjamin Herrenschmidt
Browse files

powerpc: Add PTRACE_SINGLEBLOCK support



Reworked by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

This adds block-step support on powerpc, including a PTRACE_SINGLEBLOCK
request for ptrace.

The BookE implementation is tweaked to fire a single step after a
block step in order to mimmic the server behaviour.

Signed-off-by: default avatarRoland McGrath <roland@redhat.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent dac4ccfb
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -135,7 +135,9 @@ do { \
 * These are defined as per linux/ptrace.h, which see.
 */
#define arch_has_single_step()	(1)
#define arch_has_block_step()	(!cpu_has_feature(CPU_FTR_601))
extern void user_enable_single_step(struct task_struct *);
extern void user_enable_block_step(struct task_struct *);
extern void user_disable_single_step(struct task_struct *);

#endif /* __ASSEMBLY__ */
@@ -288,4 +290,6 @@ extern void user_disable_single_step(struct task_struct *);
#define PPC_PTRACE_PEEKUSR_3264  0x91
#define PPC_PTRACE_POKEUSR_3264  0x90

#define PTRACE_SINGLEBLOCK	0x100	/* resume execution until next branch */

#endif /* _ASM_POWERPC_PTRACE_H */
+5 −5
Original line number Diff line number Diff line
@@ -256,7 +256,7 @@ label:
	 * off DE in the DSRR1 value and clearing the debug status.	      \
	 */								      \
	mfspr	r10,SPRN_DBSR;		/* check single-step/branch taken */  \
	andis.	r10,r10,DBSR_IC@h;					      \
	andis.	r10,r10,(DBSR_IC|DBSR_BT)@h;				      \
	beq+	2f;							      \
									      \
	lis	r10,KERNELBASE@h;	/* check if exception in vectors */   \
@@ -271,7 +271,7 @@ label:
									      \
	/* here it looks like we got an inappropriate debug exception. */     \
1:	rlwinm	r9,r9,0,~MSR_DE;	/* clear DE in the CDRR1 value */     \
	lis	r10,DBSR_IC@h;		/* clear the IC event */	      \
	lis	r10,(DBSR_IC|DBSR_BT)@h;	/* clear the IC event */      \
	mtspr	SPRN_DBSR,r10;						      \
	/* restore state and get out */					      \
	lwz	r10,_CCR(r11);						      \
@@ -309,7 +309,7 @@ label:
	 * off DE in the CSRR1 value and clearing the debug status.	      \
	 */								      \
	mfspr	r10,SPRN_DBSR;		/* check single-step/branch taken */  \
	andis.	r10,r10,DBSR_IC@h;					      \
	andis.	r10,r10,(DBSR_IC|DBSR_BT)@h;				      \
	beq+	2f;							      \
									      \
	lis	r10,KERNELBASE@h;	/* check if exception in vectors */   \
@@ -324,7 +324,7 @@ label:
									      \
	/* here it looks like we got an inappropriate debug exception. */     \
1:	rlwinm	r9,r9,0,~MSR_DE;	/* clear DE in the CSRR1 value */     \
	lis	r10,DBSR_IC@h;		/* clear the IC event */	      \
	lis	r10,(DBSR_IC|DBSR_BT)@h;	/* clear the IC event */      \
	mtspr	SPRN_DBSR,r10;						      \
	/* restore state and get out */					      \
	lwz	r10,_CCR(r11);						      \
+21 −2
Original line number Diff line number Diff line
@@ -704,15 +704,34 @@ void user_enable_single_step(struct task_struct *task)

	if (regs != NULL) {
#if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
		task->thread.dbcr0 &= ~DBCR0_BT;
		task->thread.dbcr0 |= DBCR0_IDM | DBCR0_IC;
		regs->msr |= MSR_DE;
#else
		regs->msr &= ~MSR_BE;
		regs->msr |= MSR_SE;
#endif
	}
	set_tsk_thread_flag(task, TIF_SINGLESTEP);
}

void user_enable_block_step(struct task_struct *task)
{
	struct pt_regs *regs = task->thread.regs;

	if (regs != NULL) {
#if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
		task->thread.dbcr0 &= ~DBCR0_IC;
		task->thread.dbcr0 = DBCR0_IDM | DBCR0_BT;
		regs->msr |= MSR_DE;
#else
		regs->msr &= ~MSR_SE;
		regs->msr |= MSR_BE;
#endif
	}
	set_tsk_thread_flag(task, TIF_SINGLESTEP);
}

void user_disable_single_step(struct task_struct *task)
{
	struct pt_regs *regs = task->thread.regs;
@@ -726,10 +745,10 @@ void user_disable_single_step(struct task_struct *task)

	if (regs != NULL) {
#if defined(CONFIG_40x) || defined(CONFIG_BOOKE)
		task->thread.dbcr0 &= ~(DBCR0_IC | DBCR0_IDM);
		task->thread.dbcr0 &= ~(DBCR0_IC | DBCR0_BT | DBCR0_IDM);
		regs->msr &= ~MSR_DE;
#else
		regs->msr &= ~MSR_SE;
		regs->msr &= ~(MSR_SE | MSR_BE);
#endif
	}
	clear_tsk_thread_flag(task, TIF_SINGLESTEP);
+30 −4
Original line number Diff line number Diff line
@@ -1041,7 +1041,34 @@ void SoftwareEmulation(struct pt_regs *regs)

void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status)
{
	if (debug_status & DBSR_IC) {	/* instruction completion */
	/* Hack alert: On BookE, Branch Taken stops on the branch itself, while
	 * on server, it stops on the target of the branch. In order to simulate
	 * the server behaviour, we thus restart right away with a single step
	 * instead of stopping here when hitting a BT
	 */
	if (debug_status & DBSR_BT) {
		regs->msr &= ~MSR_DE;

		/* Disable BT */
		mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~DBCR0_BT);
		/* Clear the BT event */
		mtspr(SPRN_DBSR, DBSR_BT);

		/* Do the single step trick only when coming from userspace */
		if (user_mode(regs)) {
			current->thread.dbcr0 &= ~DBCR0_BT;
			current->thread.dbcr0 |= DBCR0_IDM | DBCR0_IC;
			regs->msr |= MSR_DE;
			return;
		}

		if (notify_die(DIE_SSTEP, "block_step", regs, 5,
			       5, SIGTRAP) == NOTIFY_STOP) {
			return;
		}
		if (debugger_sstep(regs))
			return;
	} else if (debug_status & DBSR_IC) { 	/* Instruction complete */
		regs->msr &= ~MSR_DE;

		/* Disable instruction completion */
@@ -1057,9 +1084,8 @@ void __kprobes DebugException(struct pt_regs *regs, unsigned long debug_status)
		if (debugger_sstep(regs))
			return;

		if (user_mode(regs)) {
			current->thread.dbcr0 &= ~DBCR0_IC;
		}
		if (user_mode(regs))
			current->thread.dbcr0 &= ~(DBCR0_IC);

		_exception(SIGTRAP, regs, TRAP_TRACE, regs->nip);
	} else if (debug_status & (DBSR_DAC1R | DBSR_DAC1W)) {