From 09a072947791088b88ae15111cf68fc5aaaf758d Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 9 Nov 2009 16:27:40 +0900 Subject: 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 --- arch/sh/kernel/hw_breakpoint.c | 416 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100644 arch/sh/kernel/hw_breakpoint.c (limited to 'arch/sh/kernel/hw_breakpoint.c') diff --git a/arch/sh/kernel/hw_breakpoint.c b/arch/sh/kernel/hw_breakpoint.c new file mode 100644 index 000000000000..ff3cb3d7df8f --- /dev/null +++ b/arch/sh/kernel/hw_breakpoint.c @@ -0,0 +1,416 @@ +/* + * arch/sh/kernel/hw_breakpoint.c + * + * Unified kernel/user-space hardware breakpoint facility for the on-chip UBC. + * + * Copyright (C) 2009 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ubc_context { + unsigned long pc; + unsigned long state; +}; + +/* Per cpu ubc channel state */ +static DEFINE_PER_CPU(struct ubc_context, ubc_ctx[HBP_NUM]); + +/* + * Stores the breakpoints currently in use on each breakpoint address + * register for each cpus + */ +static DEFINE_PER_CPU(struct perf_event *, bp_per_reg[HBP_NUM]); + +static int __init ubc_init(void) +{ + __raw_writel(0, UBC_CAMR0); + __raw_writel(0, UBC_CBR0); + __raw_writel(0, UBC_CBCR); + + __raw_writel(UBC_CRR_BIE | UBC_CRR_PCB, UBC_CRR0); + + /* dummy read for write posting */ + (void)__raw_readl(UBC_CRR0); + + return 0; +} +arch_initcall(ubc_init); + +/* + * Install a perf counter breakpoint. + * + * We seek a free UBC channel and use it for this breakpoint. + * + * Atomic: we hold the counter->ctx->lock and we only handle variables + * and registers local to this cpu. + */ +int arch_install_hw_breakpoint(struct perf_event *bp) +{ + struct arch_hw_breakpoint *info = counter_arch_bp(bp); + struct ubc_context *ubc_ctx; + int i; + + for (i = 0; i < HBP_NUM; i++) { + struct perf_event **slot = &__get_cpu_var(bp_per_reg[i]); + + if (!*slot) { + *slot = bp; + break; + } + } + + if (WARN_ONCE(i == HBP_NUM, "Can't find any breakpoint slot")) + return -EBUSY; + + ubc_ctx = &__get_cpu_var(ubc_ctx[i]); + + ubc_ctx->pc = info->address; + ubc_ctx->state = info->len | info->type; + + __raw_writel(UBC_CBR_CE | ubc_ctx->state, UBC_CBR0); + __raw_writel(ubc_ctx->pc, UBC_CAR0); + + return 0; +} + +/* + * Uninstall the breakpoint contained in the given counter. + * + * First we search the debug address register it uses and then we disable + * it. + * + * Atomic: we hold the counter->ctx->lock and we only handle variables + * and registers local to this cpu. + */ +void arch_uninstall_hw_breakpoint(struct perf_event *bp) +{ + struct arch_hw_breakpoint *info = counter_arch_bp(bp); + struct ubc_context *ubc_ctx; + int i; + + for (i = 0; i < HBP_NUM; i++) { + struct perf_event **slot = &__get_cpu_var(bp_per_reg[i]); + + if (*slot == bp) { + *slot = NULL; + break; + } + } + + if (WARN_ONCE(i == HBP_NUM, "Can't find any breakpoint slot")) + return; + + ubc_ctx = &__get_cpu_var(ubc_ctx[i]); + ubc_ctx->pc = 0; + ubc_ctx->state &= ~(info->len | info->type); + + __raw_writel(ubc_ctx->pc, UBC_CBR0); + __raw_writel(ubc_ctx->state, UBC_CAR0); +} + +static int get_hbp_len(u16 hbp_len) +{ + unsigned int len_in_bytes = 0; + + switch (hbp_len) { + case SH_BREAKPOINT_LEN_1: + len_in_bytes = 1; + break; + case SH_BREAKPOINT_LEN_2: + len_in_bytes = 2; + break; + case SH_BREAKPOINT_LEN_4: + len_in_bytes = 4; + break; + case SH_BREAKPOINT_LEN_8: + len_in_bytes = 8; + break; + } + return len_in_bytes; +} + +/* + * Check for virtual address in user space. + */ +int arch_check_va_in_userspace(unsigned long va, u16 hbp_len) +{ + unsigned int len; + + len = get_hbp_len(hbp_len); + + return (va <= TASK_SIZE - len); +} + +/* + * Check for virtual address in kernel space. + */ +static int arch_check_va_in_kernelspace(unsigned long va, u8 hbp_len) +{ + unsigned int len; + + len = get_hbp_len(hbp_len); + + return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE); +} + +/* + * Store a breakpoint's encoded address, length, and type. + */ +static int arch_store_info(struct perf_event *bp) +{ + struct arch_hw_breakpoint *info = counter_arch_bp(bp); + + /* + * User-space requests will always have the address field populated + * For kernel-addresses, either the address or symbol name can be + * specified. + */ + if (info->name) + info->address = (unsigned long)kallsyms_lookup_name(info->name); + if (info->address) { + info->asid = get_asid(); + return 0; + } + + return -EINVAL; +} + +int arch_bp_generic_fields(int sh_len, int sh_type, + int *gen_len, int *gen_type) +{ + /* Len */ + switch (sh_len) { + case SH_BREAKPOINT_LEN_1: + *gen_len = HW_BREAKPOINT_LEN_1; + break; + case SH_BREAKPOINT_LEN_2: + *gen_len = HW_BREAKPOINT_LEN_2; + break; + case SH_BREAKPOINT_LEN_4: + *gen_len = HW_BREAKPOINT_LEN_4; + break; + case SH_BREAKPOINT_LEN_8: + *gen_len = HW_BREAKPOINT_LEN_8; + break; + default: + return -EINVAL; + } + + /* Type */ + switch (sh_type) { + case SH_BREAKPOINT_READ: + *gen_type = HW_BREAKPOINT_R; + case SH_BREAKPOINT_WRITE: + *gen_type = HW_BREAKPOINT_W; + break; + case SH_BREAKPOINT_RW: + *gen_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int arch_build_bp_info(struct perf_event *bp) +{ + struct arch_hw_breakpoint *info = counter_arch_bp(bp); + + info->address = bp->attr.bp_addr; + + /* Len */ + switch (bp->attr.bp_len) { + case HW_BREAKPOINT_LEN_1: + info->len = SH_BREAKPOINT_LEN_1; + break; + case HW_BREAKPOINT_LEN_2: + info->len = SH_BREAKPOINT_LEN_2; + break; + case HW_BREAKPOINT_LEN_4: + info->len = SH_BREAKPOINT_LEN_4; + break; + case HW_BREAKPOINT_LEN_8: + info->len = SH_BREAKPOINT_LEN_8; + break; + default: + return -EINVAL; + } + + /* Type */ + switch (bp->attr.bp_type) { + case HW_BREAKPOINT_R: + info->type = SH_BREAKPOINT_READ; + break; + case HW_BREAKPOINT_W: + info->type = SH_BREAKPOINT_WRITE; + break; + case HW_BREAKPOINT_W | HW_BREAKPOINT_R: + info->type = SH_BREAKPOINT_RW; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * Validate the arch-specific HW Breakpoint register settings + */ +int arch_validate_hwbkpt_settings(struct perf_event *bp, + struct task_struct *tsk) +{ + struct arch_hw_breakpoint *info = counter_arch_bp(bp); + unsigned int align; + int ret; + + ret = arch_build_bp_info(bp); + if (ret) + return ret; + + ret = -EINVAL; + + switch (info->len) { + case SH_BREAKPOINT_LEN_1: + align = 0; + break; + case SH_BREAKPOINT_LEN_2: + align = 1; + break; + case SH_BREAKPOINT_LEN_4: + align = 3; + break; + case SH_BREAKPOINT_LEN_8: + align = 7; + break; + default: + return ret; + } + + if (bp->callback) + ret = arch_store_info(bp); + + if (ret < 0) + return ret; + + /* + * Check that the low-order bits of the address are appropriate + * for the alignment implied by len. + */ + if (info->address & align) + return -EINVAL; + + /* Check that the virtual address is in the proper range */ + if (tsk) { + if (!arch_check_va_in_userspace(info->address, info->len)) + return -EFAULT; + } else { + if (!arch_check_va_in_kernelspace(info->address, info->len)) + return -EFAULT; + } + + return 0; +} + +/* + * Release the user breakpoints used by ptrace + */ +void flush_ptrace_hw_breakpoint(struct task_struct *tsk) +{ + int i; + struct thread_struct *t = &tsk->thread; + + for (i = 0; i < HBP_NUM; i++) { + unregister_hw_breakpoint(t->ptrace_bps[i]); + t->ptrace_bps[i] = NULL; + } +} + +static int __kprobes hw_breakpoint_handler(struct die_args *args) +{ + int cpu, i, rc = NOTIFY_STOP; + struct perf_event *bp; + unsigned long val; + + val = __raw_readl(UBC_CBR0); + __raw_writel(val & ~UBC_CBR_CE, UBC_CBR0); + + cpu = get_cpu(); + for (i = 0; i < HBP_NUM; i++) { + /* + * The counter may be concurrently released but that can only + * occur from a call_rcu() path. We can then safely fetch + * the breakpoint, use its callback, touch its counter + * while we are in an rcu_read_lock() path. + */ + rcu_read_lock(); + + bp = per_cpu(bp_per_reg[i], cpu); + if (bp) { + rc = NOTIFY_DONE; + } else { + rcu_read_unlock(); + break; + } + + (bp->callback)(bp, args->regs); + + rcu_read_unlock(); + } + + if (bp) { + struct arch_hw_breakpoint *info = counter_arch_bp(bp); + + __raw_writel(UBC_CBR_CE | info->len | info->type, UBC_CBR0); + __raw_writel(info->address, UBC_CAR0); + } + + put_cpu(); + + return rc; +} + +BUILD_TRAP_HANDLER(breakpoint) +{ + unsigned long ex = lookup_exception_vector(); + TRAP_HANDLER_DECL; + + notify_die(DIE_BREAKPOINT, "breakpoint", regs, 0, ex, SIGTRAP); +} + +/* + * Handle debug exception notifications. + */ +int __kprobes hw_breakpoint_exceptions_notify(struct notifier_block *unused, + unsigned long val, void *data) +{ + if (val != DIE_BREAKPOINT) + return NOTIFY_DONE; + + return hw_breakpoint_handler(data); +} + +void hw_breakpoint_pmu_read(struct perf_event *bp) +{ + /* TODO */ +} + +void hw_breakpoint_pmu_unthrottle(struct perf_event *bp) +{ + /* TODO */ +} -- cgit v1.2.3 From a28b460ec7968f430a2d6ea2809b249ee9fe1d28 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 21 Dec 2009 15:56:24 +0900 Subject: sh: Fix up hw-breakpoints build for API changes. The event callback handling has been removed in favour of going through a generic event handler to handle overflows. Follows the x86 change. Signed-off-by: Paul Mundt --- arch/sh/kernel/hw_breakpoint.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'arch/sh/kernel/hw_breakpoint.c') diff --git a/arch/sh/kernel/hw_breakpoint.c b/arch/sh/kernel/hw_breakpoint.c index ff3cb3d7df8f..75b8c471120f 100644 --- a/arch/sh/kernel/hw_breakpoint.c +++ b/arch/sh/kernel/hw_breakpoint.c @@ -302,8 +302,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp, return ret; } - if (bp->callback) - ret = arch_store_info(bp); + ret = arch_store_info(bp); if (ret < 0) return ret; @@ -368,7 +367,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args) break; } - (bp->callback)(bp, args->regs); + perf_bp_event(bp, args->regs); rcu_read_unlock(); } -- cgit v1.2.3 From b74ab703b1326aafadb5604ff6200da62c61df49 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 22 Dec 2009 12:40:38 +0900 Subject: sh: Don't NOTIFY_STOP for non-UBC breakpoint traps. Presently the hw_breakpoint code is the primary notifier dispatch for breakpoint traps, although it's only UBC traps that are of particular interest there. This patches in a check to allow non-UBC generated breakpoints to pass through down the remainder of the notifier chain, giving things like kgdb a chance at getting notified. Signed-off-by: Paul Mundt --- arch/sh/kernel/hw_breakpoint.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'arch/sh/kernel/hw_breakpoint.c') diff --git a/arch/sh/kernel/hw_breakpoint.c b/arch/sh/kernel/hw_breakpoint.c index 75b8c471120f..022d8ed66bd6 100644 --- a/arch/sh/kernel/hw_breakpoint.c +++ b/arch/sh/kernel/hw_breakpoint.c @@ -398,9 +398,18 @@ BUILD_TRAP_HANDLER(breakpoint) int __kprobes hw_breakpoint_exceptions_notify(struct notifier_block *unused, unsigned long val, void *data) { + struct die_args *args = data; + if (val != DIE_BREAKPOINT) return NOTIFY_DONE; + /* + * If the breakpoint hasn't been triggered by the UBC, it's + * probably from a debugger, so don't do anything more here. + */ + if (args->trapnr != 0x1e0) + return NOTIFY_DONE; + return hw_breakpoint_handler(data); } -- cgit v1.2.3 From 34d0b5af50a063cded842716633501b38ff815fb Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 28 Dec 2009 17:53:47 +0900 Subject: sh: Convert ptrace to hw_breakpoint API. This is the initial step for converting singlestep handling via ptrace over to hw_breakpoints. Signed-off-by: Paul Mundt --- arch/sh/kernel/hw_breakpoint.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'arch/sh/kernel/hw_breakpoint.c') diff --git a/arch/sh/kernel/hw_breakpoint.c b/arch/sh/kernel/hw_breakpoint.c index 022d8ed66bd6..c515a3ecf562 100644 --- a/arch/sh/kernel/hw_breakpoint.c +++ b/arch/sh/kernel/hw_breakpoint.c @@ -20,6 +20,7 @@ #include #include #include +#include struct ubc_context { unsigned long pc; @@ -372,7 +373,7 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args) rcu_read_unlock(); } - if (bp) { + if (bp && bp->overflow_handler != ptrace_triggered) { struct arch_hw_breakpoint *info = counter_arch_bp(bp); __raw_writel(UBC_CBR_CE | info->len | info->type, UBC_CBR0); @@ -387,9 +388,19 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args) BUILD_TRAP_HANDLER(breakpoint) { unsigned long ex = lookup_exception_vector(); + siginfo_t info; + int err; TRAP_HANDLER_DECL; - notify_die(DIE_BREAKPOINT, "breakpoint", regs, 0, ex, SIGTRAP); + err = notify_die(DIE_BREAKPOINT, "breakpoint", regs, 0, ex, SIGTRAP); + if (err == NOTIFY_STOP) + return; + + /* Deliver the signal to userspace */ + info.si_signo = SIGTRAP; + info.si_errno = 0; + info.si_code = TRAP_HWBKPT; + force_sig_info(SIGTRAP, &info, current); } /* -- cgit v1.2.3 From 4352fc1b12fae4c753a063a2f162ddf9277af774 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 5 Jan 2010 19:06:45 +0900 Subject: sh: Abstracted SH-4A UBC support on hw-breakpoint core. This is the next big chunk of hw_breakpoint support. This decouples the SH-4A support from the core and moves it out in to its own stub, following many of the conventions established with the perf events layering. In addition to extending SH-4A support to encapsulate the remainder of the UBC channels, clock framework support for handling the UBC interface clock is added as well, allowing for dynamic clock gating. This also fixes up a regression introduced by the SIGTRAP handling that broke the ksym_tracer, to the extent that the current support works well with all of the ksym_tracer/ptrace/kgdb. The kprobes singlestep code will follow in turn. With this in place, the remaining UBC variants (SH-2A and SH-4) can now be trivially plugged in. Signed-off-by: Paul Mundt --- arch/sh/kernel/hw_breakpoint.c | 164 ++++++++++++++++++++++++----------------- 1 file changed, 96 insertions(+), 68 deletions(-) (limited to 'arch/sh/kernel/hw_breakpoint.c') diff --git a/arch/sh/kernel/hw_breakpoint.c b/arch/sh/kernel/hw_breakpoint.c index c515a3ecf562..e2f1753d275c 100644 --- a/arch/sh/kernel/hw_breakpoint.c +++ b/arch/sh/kernel/hw_breakpoint.c @@ -3,7 +3,7 @@ * * Unified kernel/user-space hardware breakpoint facility for the on-chip UBC. * - * Copyright (C) 2009 Paul Mundt + * Copyright (C) 2009 - 2010 Paul Mundt * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive @@ -18,38 +18,24 @@ #include #include #include +#include #include #include #include -struct ubc_context { - unsigned long pc; - unsigned long state; -}; - -/* Per cpu ubc channel state */ -static DEFINE_PER_CPU(struct ubc_context, ubc_ctx[HBP_NUM]); - /* * Stores the breakpoints currently in use on each breakpoint address * register for each cpus */ static DEFINE_PER_CPU(struct perf_event *, bp_per_reg[HBP_NUM]); -static int __init ubc_init(void) -{ - __raw_writel(0, UBC_CAMR0); - __raw_writel(0, UBC_CBR0); - __raw_writel(0, UBC_CBCR); - - __raw_writel(UBC_CRR_BIE | UBC_CRR_PCB, UBC_CRR0); - - /* dummy read for write posting */ - (void)__raw_readl(UBC_CRR0); +/* + * A dummy placeholder for early accesses until the CPUs get a chance to + * register their UBCs later in the boot process. + */ +static struct sh_ubc ubc_dummy = { .num_events = 0 }; - return 0; -} -arch_initcall(ubc_init); +static struct sh_ubc *sh_ubc __read_mostly = &ubc_dummy; /* * Install a perf counter breakpoint. @@ -62,10 +48,9 @@ arch_initcall(ubc_init); int arch_install_hw_breakpoint(struct perf_event *bp) { struct arch_hw_breakpoint *info = counter_arch_bp(bp); - struct ubc_context *ubc_ctx; int i; - for (i = 0; i < HBP_NUM; i++) { + for (i = 0; i < sh_ubc->num_events; i++) { struct perf_event **slot = &__get_cpu_var(bp_per_reg[i]); if (!*slot) { @@ -74,16 +59,11 @@ int arch_install_hw_breakpoint(struct perf_event *bp) } } - if (WARN_ONCE(i == HBP_NUM, "Can't find any breakpoint slot")) + if (WARN_ONCE(i == sh_ubc->num_events, "Can't find any breakpoint slot")) return -EBUSY; - ubc_ctx = &__get_cpu_var(ubc_ctx[i]); - - ubc_ctx->pc = info->address; - ubc_ctx->state = info->len | info->type; - - __raw_writel(UBC_CBR_CE | ubc_ctx->state, UBC_CBR0); - __raw_writel(ubc_ctx->pc, UBC_CAR0); + clk_enable(sh_ubc->clk); + sh_ubc->enable(info, i); return 0; } @@ -100,10 +80,9 @@ int arch_install_hw_breakpoint(struct perf_event *bp) void arch_uninstall_hw_breakpoint(struct perf_event *bp) { struct arch_hw_breakpoint *info = counter_arch_bp(bp); - struct ubc_context *ubc_ctx; int i; - for (i = 0; i < HBP_NUM; i++) { + for (i = 0; i < sh_ubc->num_events; i++) { struct perf_event **slot = &__get_cpu_var(bp_per_reg[i]); if (*slot == bp) { @@ -112,15 +91,11 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp) } } - if (WARN_ONCE(i == HBP_NUM, "Can't find any breakpoint slot")) + if (WARN_ONCE(i == sh_ubc->num_events, "Can't find any breakpoint slot")) return; - ubc_ctx = &__get_cpu_var(ubc_ctx[i]); - ubc_ctx->pc = 0; - ubc_ctx->state &= ~(info->len | info->type); - - __raw_writel(ubc_ctx->pc, UBC_CBR0); - __raw_writel(ubc_ctx->state, UBC_CAR0); + sh_ubc->disable(info, i); + clk_disable(sh_ubc->clk); } static int get_hbp_len(u16 hbp_len) @@ -182,10 +157,8 @@ static int arch_store_info(struct perf_event *bp) */ if (info->name) info->address = (unsigned long)kallsyms_lookup_name(info->name); - if (info->address) { - info->asid = get_asid(); + if (info->address) return 0; - } return -EINVAL; } @@ -335,7 +308,7 @@ void flush_ptrace_hw_breakpoint(struct task_struct *tsk) int i; struct thread_struct *t = &tsk->thread; - for (i = 0; i < HBP_NUM; i++) { + for (i = 0; i < sh_ubc->num_events; i++) { unregister_hw_breakpoint(t->ptrace_bps[i]); t->ptrace_bps[i] = NULL; } @@ -345,13 +318,32 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args) { int cpu, i, rc = NOTIFY_STOP; struct perf_event *bp; - unsigned long val; + unsigned int cmf, resume_mask; + + /* + * Do an early return if none of the channels triggered. + */ + cmf = sh_ubc->triggered_mask(); + if (unlikely(!cmf)) + return NOTIFY_DONE; + + /* + * By default, resume all of the active channels. + */ + resume_mask = sh_ubc->active_mask(); - val = __raw_readl(UBC_CBR0); - __raw_writel(val & ~UBC_CBR_CE, UBC_CBR0); + /* + * Disable breakpoints during exception handling. + */ + sh_ubc->disable_all(); cpu = get_cpu(); - for (i = 0; i < HBP_NUM; i++) { + for (i = 0; i < sh_ubc->num_events; i++) { + unsigned long event_mask = (1 << i); + + if (likely(!(cmf & event_mask))) + continue; + /* * The counter may be concurrently released but that can only * occur from a call_rcu() path. We can then safely fetch @@ -361,24 +353,52 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args) rcu_read_lock(); bp = per_cpu(bp_per_reg[i], cpu); - if (bp) { + if (bp) rc = NOTIFY_DONE; - } else { + + /* + * Reset the condition match flag to denote completion of + * exception handling. + */ + sh_ubc->clear_triggered_mask(event_mask); + + /* + * bp can be NULL due to concurrent perf counter + * removing. + */ + if (!bp) { rcu_read_unlock(); break; } + /* + * Don't restore the channel if the breakpoint is from + * ptrace, as it always operates in one-shot mode. + */ + if (bp->overflow_handler == ptrace_triggered) + resume_mask &= ~(1 << i); + perf_bp_event(bp, args->regs); + /* Deliver the signal to userspace */ + if (arch_check_va_in_userspace(bp->attr.bp_addr, + bp->attr.bp_len)) { + siginfo_t info; + + info.si_signo = args->signr; + info.si_errno = notifier_to_errno(rc); + info.si_code = TRAP_HWBKPT; + + force_sig_info(args->signr, &info, current); + } + rcu_read_unlock(); } - if (bp && bp->overflow_handler != ptrace_triggered) { - struct arch_hw_breakpoint *info = counter_arch_bp(bp); + if (cmf == 0) + rc = NOTIFY_DONE; - __raw_writel(UBC_CBR_CE | info->len | info->type, UBC_CBR0); - __raw_writel(info->address, UBC_CAR0); - } + sh_ubc->enable_all(resume_mask); put_cpu(); @@ -388,19 +408,9 @@ static int __kprobes hw_breakpoint_handler(struct die_args *args) BUILD_TRAP_HANDLER(breakpoint) { unsigned long ex = lookup_exception_vector(); - siginfo_t info; - int err; TRAP_HANDLER_DECL; - err = notify_die(DIE_BREAKPOINT, "breakpoint", regs, 0, ex, SIGTRAP); - if (err == NOTIFY_STOP) - return; - - /* Deliver the signal to userspace */ - info.si_signo = SIGTRAP; - info.si_errno = 0; - info.si_code = TRAP_HWBKPT; - force_sig_info(SIGTRAP, &info, current); + notify_die(DIE_BREAKPOINT, "breakpoint", regs, 0, ex, SIGTRAP); } /* @@ -417,8 +427,12 @@ int __kprobes hw_breakpoint_exceptions_notify(struct notifier_block *unused, /* * If the breakpoint hasn't been triggered by the UBC, it's * probably from a debugger, so don't do anything more here. + * + * This also permits the UBC interface clock to remain off for + * non-UBC breakpoints, as we don't need to check the triggered + * or active channel masks. */ - if (args->trapnr != 0x1e0) + if (args->trapnr != sh_ubc->trap_nr) return NOTIFY_DONE; return hw_breakpoint_handler(data); @@ -433,3 +447,17 @@ void hw_breakpoint_pmu_unthrottle(struct perf_event *bp) { /* TODO */ } + +int register_sh_ubc(struct sh_ubc *ubc) +{ + /* Bail if it's already assigned */ + if (sh_ubc != &ubc_dummy) + return -EBUSY; + sh_ubc = ubc; + + pr_info("HW Breakpoints: %s UBC support registered\n", ubc->name); + + WARN_ON(ubc->num_events > HBP_NUM); + + return 0; +} -- cgit v1.2.3