sh: hw-breakpoints: Add preliminary support for SH-4A UBC.

This adds preliminary support for the SH-4A UBC to the hw-breakpoints API.
Presently only a single channel is implemented, and the ptrace interface
still needs to be converted. This is the first step to cleaning up the
long-standing UBC mess, making the UBC more generally accessible, and
finally making it SMP safe.

An additional abstraction will be layered on top of this as with the perf
events code to permit the various CPU families to wire up support for
their own specific UBCs, as many variations exist.

Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
Paul Mundt
2009-11-09 16:27:40 +09:00
parent 6ec22f9b03
commit 09a0729477
13 changed files with 495 additions and 117 deletions

View File

@@ -25,6 +25,7 @@
#include <linux/fs.h>
#include <linux/ftrace.h>
#include <linux/preempt.h>
#include <linux/hw_breakpoint.h>
#include <asm/uaccess.h>
#include <asm/mmu_context.h>
#include <asm/pgalloc.h>
@@ -34,8 +35,6 @@
#include <asm/syscalls.h>
#include <asm/watchdog.h>
int ubc_usercnt = 0;
#ifdef CONFIG_32BIT
static void watchdog_trigger_immediate(void)
{
@@ -148,16 +147,15 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
*/
void exit_thread(void)
{
if (current->thread.ubc_pc) {
current->thread.ubc_pc = 0;
ubc_usercnt -= 1;
}
}
void flush_thread(void)
{
#if defined(CONFIG_SH_FPU)
struct task_struct *tsk = current;
flush_ptrace_hw_breakpoint(tsk);
#if defined(CONFIG_SH_FPU)
/* Forget lazy FPU state */
clear_fpu(tsk, task_pt_regs(tsk));
clear_used_math();
@@ -195,9 +193,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
{
struct thread_info *ti = task_thread_info(p);
struct pt_regs *childregs;
#if defined(CONFIG_SH_FPU) || defined(CONFIG_SH_DSP)
struct task_struct *tsk = current;
#endif
#if defined(CONFIG_SH_FPU)
unlazy_fpu(tsk, regs);
@@ -234,53 +230,11 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
p->thread.sp = (unsigned long) childregs;
p->thread.pc = (unsigned long) ret_from_fork;
p->thread.ubc_pc = 0;
memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps));
return 0;
}
/* Tracing by user break controller. */
static void ubc_set_tracing(int asid, unsigned long pc)
{
#if defined(CONFIG_CPU_SH4A)
unsigned long val;
val = (UBC_CBR_ID_INST | UBC_CBR_RW_READ | UBC_CBR_CE);
val |= (UBC_CBR_AIE | UBC_CBR_AIV_SET(asid));
ctrl_outl(val, UBC_CBR0);
ctrl_outl(pc, UBC_CAR0);
ctrl_outl(0x0, UBC_CAMR0);
ctrl_outl(0x0, UBC_CBCR);
val = (UBC_CRR_RES | UBC_CRR_PCB | UBC_CRR_BIE);
ctrl_outl(val, UBC_CRR0);
/* Read UBC register that we wrote last, for checking update */
val = ctrl_inl(UBC_CRR0);
#else /* CONFIG_CPU_SH4A */
ctrl_outl(pc, UBC_BARA);
#ifdef CONFIG_MMU
ctrl_outb(asid, UBC_BASRA);
#endif
ctrl_outl(0, UBC_BAMRA);
if (current_cpu_data.type == CPU_SH7729 ||
current_cpu_data.type == CPU_SH7710 ||
current_cpu_data.type == CPU_SH7712 ||
current_cpu_data.type == CPU_SH7203){
ctrl_outw(BBR_INST | BBR_READ | BBR_CPU, UBC_BBRA);
ctrl_outl(BRCR_PCBA | BRCR_PCTE, UBC_BRCR);
} else {
ctrl_outw(BBR_INST | BBR_READ, UBC_BBRA);
ctrl_outw(BRCR_PCBA, UBC_BRCR);
}
#endif /* CONFIG_CPU_SH4A */
}
/*
* switch_to(x,y) should switch tasks from x to y.
*
@@ -302,25 +256,6 @@ __switch_to(struct task_struct *prev, struct task_struct *next)
: "r" (task_thread_info(next)));
#endif
/* If no tasks are using the UBC, we're done */
if (ubc_usercnt == 0)
/* If no tasks are using the UBC, we're done */;
else if (next->thread.ubc_pc && next->mm) {
int asid = 0;
#ifdef CONFIG_MMU
asid |= cpu_asid(smp_processor_id(), next->mm);
#endif
ubc_set_tracing(asid, next->thread.ubc_pc);
} else {
#if defined(CONFIG_CPU_SH4A)
ctrl_outl(UBC_CBR_INIT, UBC_CBR0);
ctrl_outl(UBC_CRR_INIT, UBC_CRR0);
#else
ctrl_outw(0, UBC_BBRA);
ctrl_outw(0, UBC_BBRB);
#endif
}
return prev;
}
@@ -412,20 +347,3 @@ unsigned long get_wchan(struct task_struct *p)
return pc;
}
asmlinkage void break_point_trap(void)
{
/* Clear tracing. */
#if defined(CONFIG_CPU_SH4A)
ctrl_outl(UBC_CBR_INIT, UBC_CBR0);
ctrl_outl(UBC_CRR_INIT, UBC_CRR0);
#else
ctrl_outw(0, UBC_BBRA);
ctrl_outw(0, UBC_BBRB);
ctrl_outl(0, UBC_BRCR);
#endif
current->thread.ubc_pc = 0;
ubc_usercnt -= 1;
force_sig(SIGTRAP, current);
}