* [PATCH 00/11] ARM: hw_breakpoint: fixes and improvements (v3)
@ 2010-12-02 13:45 Will Deacon
2010-12-02 13:45 ` [PATCH 01/11] ARM: hw_breakpoint: ensure OS lock is clear before writing to debug registers Will Deacon
` (10 more replies)
0 siblings, 11 replies; 14+ messages in thread
From: Will Deacon @ 2010-12-02 13:45 UTC (permalink / raw)
To: linux-arm-kernel
Hello again,
This is version 3 of the patchset originally posted here:
[v1]: http://lists.infradead.org/pipermail/linux-arm-kernel/2010-November/032717.html
[v2]: http://lists.infradead.org/pipermail/linux-arm-kernel/2010-November/032962.html
It's grown a bit since v2, but I think I've caught all of the corner cases now.
Thanks to the people who have highlighted any problems.
Changes since v2 include:
* Unified single-stepping mechanism for watchpoints and breakpoints
* Corrected misdiagnosis of the problem fixed in patch 6 (`do not allocate...')
* For cores where monitor mode cannot be enabled due to hardware restrictions,
we no longer fail the initcall or WARN_ONCE.
* Per-cpu breakpoints are disallowed for the time being because they cannot
be stepped reliably (since we may take an interrupt before the step has
completed).
* Updated the comment describing why only a single watchpoint is available.
As per usual, all feedback is welcome.
Will Deacon (11):
ARM: hw_breakpoint: ensure OS lock is clear before writing to debug
registers
ARM: hw_breakpoint: reset control registers in hotplug path
ARM: hw_breakpoint: correct and simplify alignment fixup code
ARM: hw_breakpoint: disable preemption during debug exception
handling
ARM: hw_breakpoint: don't advertise reserved breakpoints
ARM: hw_breakpoint: do not allocate new breakpoints with preemption
disabled
ARM: hw_breakpoint: unify single-stepping code for watchpoints and
breakpoints
ARM: hw_breakpoint: disallow per-cpu breakpoints without overflow
handler
ARM: ptrace: fix style issue with hw_breakpoint interface
ARM: hw_breakpoint: fix warnings generated by sparse
ARM: hw_breakpoint: do not fail initcall if monitor mode is disabled
arch/arm/include/asm/hw_breakpoint.h | 4 +-
arch/arm/kernel/entry-armv.S | 4 +
arch/arm/kernel/entry-header.S | 19 ++
arch/arm/kernel/hw_breakpoint.c | 545 ++++++++++++++++++++--------------
arch/arm/kernel/ptrace.c | 4 +-
5 files changed, 349 insertions(+), 227 deletions(-)
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 01/11] ARM: hw_breakpoint: ensure OS lock is clear before writing to debug registers
2010-12-02 13:45 [PATCH 00/11] ARM: hw_breakpoint: fixes and improvements (v3) Will Deacon
@ 2010-12-02 13:45 ` Will Deacon
2010-12-02 13:45 ` [PATCH 02/11] ARM: hw_breakpoint: reset control registers in hotplug path Will Deacon
` (9 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Will Deacon @ 2010-12-02 13:45 UTC (permalink / raw)
To: linux-arm-kernel
ARMv7 architects a system for saving and restoring the debug registers
across low-power modes. At the heart of this system is a lock register
which, when set, forbids writes to the debug registers. While locked,
writes to debug registers via the co-processor interface will result
in undefined instruction traps. Linux currently doesn't make use of
this feature because we update the debug registers on context switch
anyway, however the status of the lock is IMPLEMENTATION DEFINED on
reset.
This patch ensures that the lock is cleared during boot so that we
can write to the debug registers safely.
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm/kernel/hw_breakpoint.c | 27 ++++++++++++++++++++++-----
1 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index 21e3a4a..793959e 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -768,6 +768,23 @@ static void __init reset_ctrl_regs(void *unused)
{
int i;
+ /*
+ * v7 debug contains save and restore registers so that debug state
+ * can be maintained across low-power modes without leaving
+ * the debug logic powered up. It is IMPLEMENTATION DEFINED whether
+ * we can write to the debug registers out of reset, so we must
+ * unlock the OS Lock Access Register to avoid taking undefined
+ * instruction exceptions later on.
+ */
+ if (debug_arch >= ARM_DEBUG_ARCH_V7_ECP14) {
+ /*
+ * Unconditionally clear the lock by writing a value
+ * other than 0xC5ACCE55 to the access register.
+ */
+ asm volatile("mcr p14, 0, %0, c1, c0, 4" : : "r" (0));
+ isb();
+ }
+
if (enable_monitor_mode())
return;
@@ -810,17 +827,17 @@ static int __init arch_hw_breakpoint_init(void)
pr_warning("halting debug mode enabled. Assuming maximum "
"watchpoint size of 4 bytes.");
} else {
- /* Work out the maximum supported watchpoint length. */
- max_watchpoint_len = get_max_wp_len();
- pr_info("maximum watchpoint size is %u bytes.\n",
- max_watchpoint_len);
-
/*
* Reset the breakpoint resources. We assume that a halting
* debugger will leave the world in a nice state for us.
*/
smp_call_function(reset_ctrl_regs, NULL, 1);
reset_ctrl_regs(NULL);
+
+ /* Work out the maximum supported watchpoint length. */
+ max_watchpoint_len = get_max_wp_len();
+ pr_info("maximum watchpoint size is %u bytes.\n",
+ max_watchpoint_len);
}
/* Register debug fault handler. */
--
1.7.0.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 02/11] ARM: hw_breakpoint: reset control registers in hotplug path
2010-12-02 13:45 [PATCH 00/11] ARM: hw_breakpoint: fixes and improvements (v3) Will Deacon
2010-12-02 13:45 ` [PATCH 01/11] ARM: hw_breakpoint: ensure OS lock is clear before writing to debug registers Will Deacon
@ 2010-12-02 13:45 ` Will Deacon
2010-12-02 13:45 ` [PATCH 03/11] ARM: hw_breakpoint: correct and simplify alignment fixup code Will Deacon
` (8 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Will Deacon @ 2010-12-02 13:45 UTC (permalink / raw)
To: linux-arm-kernel
The ARMv7 debug architecture doesn't make any guarantees about the
contents of debug control registers following a debug logic reset.
This patch ensures that we reset the control registers when a cpu
comes ONLINE (for example, with hotplug) so that when we enable
monitor mode while inserting a breakpoint we won't exhibit random
behaviour.
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm/kernel/hw_breakpoint.c | 16 +++++++++++++++-
1 files changed, 15 insertions(+), 1 deletions(-)
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index 793959e..515a3c4 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -764,7 +764,7 @@ out:
/*
* One-time initialisation.
*/
-static void __init reset_ctrl_regs(void *unused)
+static void reset_ctrl_regs(void *unused)
{
int i;
@@ -799,6 +799,18 @@ static void __init reset_ctrl_regs(void *unused)
}
}
+static int __cpuinit dbg_reset_notify(struct notifier_block *self,
+ unsigned long action, void *cpu)
+{
+ if (action == CPU_ONLINE)
+ smp_call_function_single((int)cpu, reset_ctrl_regs, NULL, 1);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata dbg_reset_nb = {
+ .notifier_call = dbg_reset_notify,
+};
+
static int __init arch_hw_breakpoint_init(void)
{
int ret = 0;
@@ -846,6 +858,8 @@ static int __init arch_hw_breakpoint_init(void)
hook_ifault_code(2, hw_breakpoint_pending, SIGTRAP, TRAP_HWBKPT,
"breakpoint debug exception");
+ /* Register hotplug notifier. */
+ register_cpu_notifier(&dbg_reset_nb);
out:
return ret;
}
--
1.7.0.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 03/11] ARM: hw_breakpoint: correct and simplify alignment fixup code
2010-12-02 13:45 [PATCH 00/11] ARM: hw_breakpoint: fixes and improvements (v3) Will Deacon
2010-12-02 13:45 ` [PATCH 01/11] ARM: hw_breakpoint: ensure OS lock is clear before writing to debug registers Will Deacon
2010-12-02 13:45 ` [PATCH 02/11] ARM: hw_breakpoint: reset control registers in hotplug path Will Deacon
@ 2010-12-02 13:45 ` Will Deacon
2010-12-02 13:45 ` [PATCH 04/11] ARM: hw_breakpoint: disable preemption during debug exception handling Will Deacon
` (7 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Will Deacon @ 2010-12-02 13:45 UTC (permalink / raw)
To: linux-arm-kernel
The current hw_breakpoint code tries to fix up the alignment of
breakpoints so that we can make use of sparse byte-address-select
bits in the control register and give the illusion that we can
set breakpoints on unaligned addresses.
Although this works on v6 cores, v7 forbids this behaviour, instead
requiring breakpoints to be set on aligned addresses and have contiguous
byte-address-select ranges depending on the instruction set in use.
For ARM the only supported size is 4 bytes, whilst Thumb-2 also permits
2 byte breakpoints (watchpoints can be of 1, 2, 4 or 8 bytes long).
This patch simplifies the alignment fixup code so that we require
addresses to be aligned to the size of the corresponding breakpoint.
This allows us to handle the common case of breaking on a half-word
aligned Thumb-2 instruction and also allows us to set byte watchpoints
on arbitrary addresses.
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm/kernel/hw_breakpoint.c | 57 +++++++++++++++++++++-----------------
1 files changed, 31 insertions(+), 26 deletions(-)
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index 515a3c4..d37ed35 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -537,6 +537,17 @@ static int arch_build_bp_info(struct perf_event *bp)
return -EINVAL;
}
+ /*
+ * Breakpoints must be of length 2 (thumb) or 4 (ARM) bytes.
+ * Watchpoints can be of length 1, 2, 4 or 8 bytes if supported
+ * by the hardware and must be aligned to the appropriate number of
+ * bytes.
+ */
+ if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE &&
+ info->ctrl.len != ARM_BREAKPOINT_LEN_2 &&
+ info->ctrl.len != ARM_BREAKPOINT_LEN_4)
+ return -EINVAL;
+
/* Address */
info->address = bp->attr.bp_addr;
@@ -561,7 +572,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
{
struct arch_hw_breakpoint *info = counter_arch_bp(bp);
int ret = 0;
- u32 bytelen, max_len, offset, alignment_mask = 0x3;
+ u32 offset, alignment_mask = 0x3;
/* Build the arch_hw_breakpoint. */
ret = arch_build_bp_info(bp);
@@ -571,32 +582,27 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
/* Check address alignment. */
if (info->ctrl.len == ARM_BREAKPOINT_LEN_8)
alignment_mask = 0x7;
- if (info->address & alignment_mask) {
- /*
- * Try to fix the alignment. This may result in a length
- * that is too large, so we must check for that.
- */
- bytelen = get_hbp_len(info->ctrl.len);
- max_len = info->ctrl.type == ARM_BREAKPOINT_EXECUTE ? 4 :
- max_watchpoint_len;
-
- if (max_len >= 8)
- offset = info->address & 0x7;
- else
- offset = info->address & 0x3;
-
- if (bytelen > (1 << ((max_len - (offset + 1)) >> 1))) {
- ret = -EFBIG;
- goto out;
- }
-
- info->ctrl.len <<= offset;
- info->address &= ~offset;
-
- pr_debug("breakpoint alignment fixup: length = 0x%x, "
- "address = 0x%x\n", info->ctrl.len, info->address);
+ offset = info->address & alignment_mask;
+ switch (offset) {
+ case 0:
+ /* Aligned */
+ break;
+ case 1:
+ /* Allow single byte watchpoint. */
+ if (info->ctrl.len == ARM_BREAKPOINT_LEN_1)
+ break;
+ case 2:
+ /* Allow halfword watchpoints and breakpoints. */
+ if (info->ctrl.len == ARM_BREAKPOINT_LEN_2)
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
}
+ info->address &= ~alignment_mask;
+ info->ctrl.len <<= offset;
+
/*
* Currently we rely on an overflow handler to take
* care of single-stepping the breakpoint when it fires.
@@ -607,7 +613,6 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
(arch_check_bp_in_kernelspace(bp) || !core_has_mismatch_bps()),
"overflow handler required but none found")) {
ret = -EINVAL;
- goto out;
}
out:
return ret;
--
1.7.0.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 04/11] ARM: hw_breakpoint: disable preemption during debug exception handling
2010-12-02 13:45 [PATCH 00/11] ARM: hw_breakpoint: fixes and improvements (v3) Will Deacon
` (2 preceding siblings ...)
2010-12-02 13:45 ` [PATCH 03/11] ARM: hw_breakpoint: correct and simplify alignment fixup code Will Deacon
@ 2010-12-02 13:45 ` Will Deacon
2010-12-02 13:45 ` [PATCH 05/11] ARM: hw_breakpoint: don't advertise reserved breakpoints Will Deacon
` (6 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Will Deacon @ 2010-12-02 13:45 UTC (permalink / raw)
To: linux-arm-kernel
On ARM, debug exceptions occur in the form of data or prefetch aborts.
One difference is that debug exceptions require access to per-cpu banked
registers and data structures which are not saved in the low-level exception
code. For kernels built with CONFIG_PREEMPT, there is an unlikely scenario
that the debug handler ends up running on a different CPU from the one
that originally signalled the event, resulting in random data being read
from the wrong registers.
This patch adds a debug_entry macro to the low-level exception handling
code which checks whether the taken exception is a debug exception. If
it is, the preempt count for the faulting process is incremented. After
the debug handler has finished, the count is decremented.
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm/kernel/entry-armv.S | 4 ++++
| 19 +++++++++++++++++++
arch/arm/kernel/hw_breakpoint.c | 18 +++++++++++++-----
3 files changed, 36 insertions(+), 5 deletions(-)
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index c09e357..34bbef0 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -198,6 +198,7 @@ __dabt_svc:
@
@ set desired IRQ state, then call main handler
@
+ debug_entry r1
msr cpsr_c, r9
mov r2, sp
bl do_DataAbort
@@ -324,6 +325,7 @@ __pabt_svc:
#else
bl CPU_PABORT_HANDLER
#endif
+ debug_entry r1
msr cpsr_c, r9 @ Maybe enable interrupts
mov r2, sp @ regs
bl do_PrefetchAbort @ call abort handler
@@ -439,6 +441,7 @@ __dabt_usr:
@
@ IRQs on, then call the main handler
@
+ debug_entry r1
enable_irq
mov r2, sp
adr lr, BSYM(ret_from_exception)
@@ -703,6 +706,7 @@ __pabt_usr:
#else
bl CPU_PABORT_HANDLER
#endif
+ debug_entry r1
enable_irq @ Enable interrupts
mov r2, sp @ regs
bl do_PrefetchAbort @ call abort handler
--git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S
index d93f976..ae94649 100644
--- a/arch/arm/kernel/entry-header.S
+++ b/arch/arm/kernel/entry-header.S
@@ -165,6 +165,25 @@
.endm
#endif /* !CONFIG_THUMB2_KERNEL */
+ @
+ @ Debug exceptions are taken as prefetch or data aborts.
+ @ We must disable preemption during the handler so that
+ @ we can access the debug registers safely.
+ @
+ .macro debug_entry, fsr
+#if defined(CONFIG_HAVE_HW_BREAKPOINT) && defined(CONFIG_PREEMPT)
+ ldr r4, =0x40f @ mask out fsr.fs
+ and r5, r4, \fsr
+ cmp r5, #2 @ debug exception
+ bne 1f
+ get_thread_info r10
+ ldr r6, [r10, #TI_PREEMPT] @ get preempt count
+ add r11, r6, #1 @ increment it
+ str r11, [r10, #TI_PREEMPT]
+1:
+#endif
+ .endm
+
/*
* These are the registers used in the syscall handler, and allow us to
* have in theory up to 7 arguments to a function - r0 to r6.
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index d37ed35..eeba380 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -24,6 +24,7 @@
#define pr_fmt(fmt) "hw-breakpoint: " fmt
#include <linux/errno.h>
+#include <linux/hardirq.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include <linux/smp.h>
@@ -736,14 +737,17 @@ unlock:
/*
* Called from either the Data Abort Handler [watchpoint] or the
- * Prefetch Abort Handler [breakpoint].
+ * Prefetch Abort Handler [breakpoint] with preemption disabled.
*/
static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr,
struct pt_regs *regs)
{
- int ret = 1; /* Unhandled fault. */
+ int ret = 0;
u32 dscr;
+ /* We must be called with preemption disabled. */
+ WARN_ON(preemptible());
+
/* We only handle watchpoints and hardware breakpoints. */
ARM_DBG_READ(c1, 0, dscr);
@@ -758,11 +762,15 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr,
watchpoint_handler(addr, regs);
break;
default:
- goto out;
+ ret = 1; /* Unhandled fault. */
}
- ret = 0;
-out:
+ /*
+ * Re-enable preemption after it was disabled in the
+ * low-level exception handling code.
+ */
+ preempt_enable();
+
return ret;
}
--
1.7.0.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 05/11] ARM: hw_breakpoint: don't advertise reserved breakpoints
2010-12-02 13:45 [PATCH 00/11] ARM: hw_breakpoint: fixes and improvements (v3) Will Deacon
` (3 preceding siblings ...)
2010-12-02 13:45 ` [PATCH 04/11] ARM: hw_breakpoint: disable preemption during debug exception handling Will Deacon
@ 2010-12-02 13:45 ` Will Deacon
2010-12-02 13:45 ` [PATCH 06/11] ARM: hw_breakpoint: do not allocate new breakpoints with preemption disabled Will Deacon
` (5 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Will Deacon @ 2010-12-02 13:45 UTC (permalink / raw)
To: linux-arm-kernel
To permit handling of watchpoint exceptions without signalling a
debugger, it is necessary to reserve breakpoint registers for in-kernel
use only.
This patch ensures that we record and subtract the number of reserved
breakpoints from the number of usable breakpoint registers that we
advertise to userspace via the ptrace API.
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm/kernel/hw_breakpoint.c | 206 ++++++++++++++++++++++-----------------
1 files changed, 117 insertions(+), 89 deletions(-)
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index eeba380..b16c456 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -45,6 +45,7 @@ static DEFINE_PER_CPU(struct perf_event *, wp_on_reg[ARM_MAX_WRP]);
/* Number of BRP/WRP registers on this CPU. */
static int core_num_brps;
+static int core_num_reserved_brps;
static int core_num_wrps;
/* Debug architecture version. */
@@ -53,87 +54,6 @@ static u8 debug_arch;
/* Maximum supported watchpoint length. */
static u8 max_watchpoint_len;
-/* Determine number of BRP registers available. */
-static int get_num_brps(void)
-{
- u32 didr;
- ARM_DBG_READ(c0, 0, didr);
- return ((didr >> 24) & 0xf) + 1;
-}
-
-/* Determine number of WRP registers available. */
-static int get_num_wrps(void)
-{
- /*
- * FIXME: When a watchpoint fires, the only way to work out which
- * watchpoint it was is by disassembling the faulting instruction
- * and working out the address of the memory access.
- *
- * Furthermore, we can only do this if the watchpoint was precise
- * since imprecise watchpoints prevent us from calculating register
- * based addresses.
- *
- * For the time being, we only report 1 watchpoint register so we
- * always know which watchpoint fired. In the future we can either
- * add a disassembler and address generation emulator, or we can
- * insert a check to see if the DFAR is set on watchpoint exception
- * entry [the ARM ARM states that the DFAR is UNKNOWN, but
- * experience shows that it is set on some implementations].
- */
-
-#if 0
- u32 didr, wrps;
- ARM_DBG_READ(c0, 0, didr);
- return ((didr >> 28) & 0xf) + 1;
-#endif
-
- return 1;
-}
-
-int hw_breakpoint_slots(int type)
-{
- /*
- * We can be called early, so don't rely on
- * our static variables being initialised.
- */
- switch (type) {
- case TYPE_INST:
- return get_num_brps();
- case TYPE_DATA:
- return get_num_wrps();
- default:
- pr_warning("unknown slot type: %d\n", type);
- return 0;
- }
-}
-
-/* Determine debug architecture. */
-static u8 get_debug_arch(void)
-{
- u32 didr;
-
- /* Do we implement the extended CPUID interface? */
- if (((read_cpuid_id() >> 16) & 0xf) != 0xf) {
- pr_warning("CPUID feature registers not supported. "
- "Assuming v6 debug is present.\n");
- return ARM_DEBUG_ARCH_V6;
- }
-
- ARM_DBG_READ(c0, 0, didr);
- return (didr >> 16) & 0xf;
-}
-
-/* Does this core support mismatch breakpoints? */
-static int core_has_mismatch_bps(void)
-{
- return debug_arch >= ARM_DEBUG_ARCH_V7_ECP14 && core_num_brps > 1;
-}
-
-u8 arch_get_debug_arch(void)
-{
- return debug_arch;
-}
-
#define READ_WB_REG_CASE(OP2, M, VAL) \
case ((OP2 << 4) + M): \
ARM_DBG_READ(c ## M, OP2, VAL); \
@@ -211,6 +131,111 @@ static void write_wb_reg(int n, u32 val)
isb();
}
+/* Determine debug architecture. */
+static u8 get_debug_arch(void)
+{
+ u32 didr;
+
+ /* Do we implement the extended CPUID interface? */
+ if (((read_cpuid_id() >> 16) & 0xf) != 0xf) {
+ pr_warning("CPUID feature registers not supported. "
+ "Assuming v6 debug is present.\n");
+ return ARM_DEBUG_ARCH_V6;
+ }
+
+ ARM_DBG_READ(c0, 0, didr);
+ return (didr >> 16) & 0xf;
+}
+
+u8 arch_get_debug_arch(void)
+{
+ return debug_arch;
+}
+
+/* Determine number of BRP register available. */
+static int get_num_brp_resources(void)
+{
+ u32 didr;
+ ARM_DBG_READ(c0, 0, didr);
+ return ((didr >> 24) & 0xf) + 1;
+}
+
+/* Does this core support mismatch breakpoints? */
+static int core_has_mismatch_brps(void)
+{
+ return (get_debug_arch() >= ARM_DEBUG_ARCH_V7_ECP14 &&
+ get_num_brp_resources() > 1);
+}
+
+/* Determine number of usable WRPs available. */
+static int get_num_wrps(void)
+{
+ /*
+ * FIXME: When a watchpoint fires, the only way to work out which
+ * watchpoint it was is by disassembling the faulting instruction
+ * and working out the address of the memory access.
+ *
+ * Furthermore, we can only do this if the watchpoint was precise
+ * since imprecise watchpoints prevent us from calculating register
+ * based addresses.
+ *
+ * Providing we have more than 1 breakpoint register, we only report
+ * a single watchpoint register for the time being. This way, we always
+ * know which watchpoint fired. In the future we can either add a
+ * disassembler and address generation emulator, or we can insert a
+ * check to see if the DFAR is set on watchpoint exception entry
+ * [the ARM ARM states that the DFAR is UNKNOWN, but experience shows
+ * that it is set on some implementations].
+ */
+
+#if 0
+ int wrps;
+ u32 didr;
+ ARM_DBG_READ(c0, 0, didr);
+ wrps = ((didr >> 28) & 0xf) + 1;
+#endif
+ int wrps = 1;
+
+ if (core_has_mismatch_brps() && wrps >= get_num_brp_resources())
+ wrps = get_num_brp_resources() - 1;
+
+ return wrps;
+}
+
+/* We reserve one breakpoint for each watchpoint. */
+static int get_num_reserved_brps(void)
+{
+ if (core_has_mismatch_brps())
+ return get_num_wrps();
+ return 0;
+}
+
+/* Determine number of usable BRPs available. */
+static int get_num_brps(void)
+{
+ int brps = get_num_brp_resources();
+ if (core_has_mismatch_brps())
+ brps -= get_num_reserved_brps();
+ return brps;
+}
+
+int hw_breakpoint_slots(int type)
+{
+ /*
+ * We can be called early, so don't rely on
+ * our static variables being initialised.
+ */
+ switch (type) {
+ case TYPE_INST:
+ return get_num_brps();
+ case TYPE_DATA:
+ return get_num_wrps();
+ default:
+ pr_warning("unknown slot type: %d\n", type);
+ return 0;
+ }
+}
+
/*
* In order to access the breakpoint/watchpoint control registers,
* we must be running in debug monitor mode. Unfortunately, we can
@@ -326,7 +351,7 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
ctrl_base = ARM_BASE_BCR;
val_base = ARM_BASE_BVR;
slots = __get_cpu_var(bp_on_reg);
- max_slots = core_num_brps - 1;
+ max_slots = core_num_brps;
if (bp_is_single_step(bp)) {
info->ctrl.mismatch = 1;
@@ -377,7 +402,7 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
/* Breakpoint */
base = ARM_BASE_BCR;
slots = __get_cpu_var(bp_on_reg);
- max_slots = core_num_brps - 1;
+ max_slots = core_num_brps;
if (bp_is_single_step(bp)) {
i = max_slots;
@@ -611,7 +636,7 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
* we can use the mismatch feature as a poor-man's hardware single-step.
*/
if (WARN_ONCE(!bp->overflow_handler &&
- (arch_check_bp_in_kernelspace(bp) || !core_has_mismatch_bps()),
+ (arch_check_bp_in_kernelspace(bp) || !core_has_mismatch_brps()),
"overflow handler required but none found")) {
ret = -EINVAL;
}
@@ -698,7 +723,7 @@ static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs)
/* The exception entry code places the amended lr in the PC. */
addr = regs->ARM_pc;
- for (i = 0; i < core_num_brps; ++i) {
+ for (i = 0; i < core_num_brps + core_num_reserved_brps; ++i) {
rcu_read_lock();
bp = slots[i];
@@ -801,7 +826,8 @@ static void reset_ctrl_regs(void *unused)
if (enable_monitor_mode())
return;
- for (i = 0; i < core_num_brps; ++i) {
+ /* We must also reset any reserved registers. */
+ for (i = 0; i < core_num_brps + core_num_reserved_brps; ++i) {
write_wb_reg(ARM_BASE_BCR + i, 0UL);
write_wb_reg(ARM_BASE_BVR + i, 0UL);
}
@@ -839,13 +865,15 @@ static int __init arch_hw_breakpoint_init(void)
/* Determine how many BRPs/WRPs are available. */
core_num_brps = get_num_brps();
+ core_num_reserved_brps = get_num_reserved_brps();
core_num_wrps = get_num_wrps();
pr_info("found %d breakpoint and %d watchpoint registers.\n",
- core_num_brps, core_num_wrps);
+ core_num_brps + core_num_reserved_brps, core_num_wrps);
- if (core_has_mismatch_bps())
- pr_info("1 breakpoint reserved for watchpoint single-step.\n");
+ if (core_num_reserved_brps)
+ pr_info("%d breakpoint(s) reserved for watchpoint "
+ "single-step.\n", core_num_reserved_brps);
ARM_DBG_READ(c1, 0, dscr);
if (dscr & ARM_DSCR_HDBGEN) {
--
1.7.0.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 06/11] ARM: hw_breakpoint: do not allocate new breakpoints with preemption disabled
2010-12-02 13:45 [PATCH 00/11] ARM: hw_breakpoint: fixes and improvements (v3) Will Deacon
` (4 preceding siblings ...)
2010-12-02 13:45 ` [PATCH 05/11] ARM: hw_breakpoint: don't advertise reserved breakpoints Will Deacon
@ 2010-12-02 13:45 ` Will Deacon
2010-12-02 13:45 ` [PATCH 07/11] ARM: hw_breakpoint: unify single-stepping code for watchpoints and breakpoints Will Deacon
` (4 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Will Deacon @ 2010-12-02 13:45 UTC (permalink / raw)
To: linux-arm-kernel
The watchpoint single-stepping code calls register_user_hw_breakpoint to
register a mismatch breakpoint for stepping over the watchpoint. This is
performed with preemption disabled, which is unsafe as we may end up scheduling
whilst in_atomic(). Furthermore, using the perf API is rather overkill since
we are already in the hw-breakpoint backend and only require access to reserved
breakpoints anyway.
This patch reworks the watchpoint stepping code so that we don't require
another perf_event for the mismatch breakpoint. Instead, we hold a separate
arch_hw_breakpoint_ctrl struct inside the watchpoint which is used exclusively
for stepping. We can check whether or not stepping is enabled when installing
or uninstalling the watchpoint and operate on the breakpoint accordingly.
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm/include/asm/hw_breakpoint.h | 2 +-
arch/arm/kernel/hw_breakpoint.c | 134 +++++++++++++++++++--------------
2 files changed, 78 insertions(+), 58 deletions(-)
diff --git a/arch/arm/include/asm/hw_breakpoint.h b/arch/arm/include/asm/hw_breakpoint.h
index 4d8ae9d..881429d 100644
--- a/arch/arm/include/asm/hw_breakpoint.h
+++ b/arch/arm/include/asm/hw_breakpoint.h
@@ -20,7 +20,7 @@ struct arch_hw_breakpoint_ctrl {
struct arch_hw_breakpoint {
u32 address;
u32 trigger;
- struct perf_event *suspended_wp;
+ struct arch_hw_breakpoint_ctrl step_ctrl;
struct arch_hw_breakpoint_ctrl ctrl;
};
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index b16c456..81b63e9 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -316,23 +316,6 @@ u8 arch_get_max_wp_len(void)
}
/*
- * Handler for reactivating a suspended watchpoint when the single
- * step `mismatch' breakpoint is triggered.
- */
-static void wp_single_step_handler(struct perf_event *bp, int unused,
- struct perf_sample_data *data,
- struct pt_regs *regs)
-{
- perf_event_enable(counter_arch_bp(bp)->suspended_wp);
- unregister_hw_breakpoint(bp);
-}
-
-static int bp_is_single_step(struct perf_event *bp)
-{
- return bp->overflow_handler == wp_single_step_handler;
-}
-
-/*
* Install a perf counter breakpoint.
*/
int arch_install_hw_breakpoint(struct perf_event *bp)
@@ -340,29 +323,35 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
struct arch_hw_breakpoint *info = counter_arch_bp(bp);
struct perf_event **slot, **slots;
int i, max_slots, ctrl_base, val_base, ret = 0;
+ u32 addr, ctrl;
/* Ensure that we are in monitor mode and halting mode is disabled. */
ret = enable_monitor_mode();
if (ret)
goto out;
+ addr = info->address;
+ ctrl = encode_ctrl_reg(info->ctrl) | 0x1;
+
if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) {
/* Breakpoint */
ctrl_base = ARM_BASE_BCR;
val_base = ARM_BASE_BVR;
slots = __get_cpu_var(bp_on_reg);
max_slots = core_num_brps;
-
- if (bp_is_single_step(bp)) {
- info->ctrl.mismatch = 1;
- i = max_slots;
- slots[i] = bp;
- goto setup;
- }
} else {
/* Watchpoint */
- ctrl_base = ARM_BASE_WCR;
- val_base = ARM_BASE_WVR;
+ if (info->step_ctrl.enabled) {
+ /* Install into the reserved breakpoint region. */
+ ctrl_base = ARM_BASE_BCR + core_num_brps;
+ val_base = ARM_BASE_BVR + core_num_brps;
+ /* Override the watchpoint data with the step data. */
+ addr = info->trigger & ~0x3;
+ ctrl = encode_ctrl_reg(info->step_ctrl);
+ } else {
+ ctrl_base = ARM_BASE_WCR;
+ val_base = ARM_BASE_WVR;
+ }
slots = __get_cpu_var(wp_on_reg);
max_slots = core_num_wrps;
}
@@ -381,12 +370,11 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
goto out;
}
-setup:
/* Setup the address register. */
- write_wb_reg(val_base + i, info->address);
+ write_wb_reg(val_base + i, addr);
/* Setup the control register. */
- write_wb_reg(ctrl_base + i, encode_ctrl_reg(info->ctrl) | 0x1);
+ write_wb_reg(ctrl_base + i, ctrl);
out:
return ret;
@@ -403,15 +391,12 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
base = ARM_BASE_BCR;
slots = __get_cpu_var(bp_on_reg);
max_slots = core_num_brps;
-
- if (bp_is_single_step(bp)) {
- i = max_slots;
- slots[i] = NULL;
- goto reset;
- }
} else {
/* Watchpoint */
- base = ARM_BASE_WCR;
+ if (info->step_ctrl.enabled)
+ base = ARM_BASE_BCR + core_num_brps;
+ else
+ base = ARM_BASE_WCR;
slots = __get_cpu_var(wp_on_reg);
max_slots = core_num_wrps;
}
@@ -429,7 +414,6 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot"))
return;
-reset:
/* Reset the control register. */
write_wb_reg(base + i, 0);
}
@@ -579,7 +563,7 @@ static int arch_build_bp_info(struct perf_event *bp)
/* Privilege */
info->ctrl.privilege = ARM_BREAKPOINT_USER;
- if (arch_check_bp_in_kernelspace(bp) && !bp_is_single_step(bp))
+ if (arch_check_bp_in_kernelspace(bp))
info->ctrl.privilege |= ARM_BREAKPOINT_PRIV;
/* Enabled? */
@@ -664,22 +648,18 @@ static void update_mismatch_flag(int idx, int flag)
static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs)
{
int i;
- struct perf_event *bp, **slots = __get_cpu_var(wp_on_reg);
+ struct perf_event *wp, **slots = __get_cpu_var(wp_on_reg);
struct arch_hw_breakpoint *info;
- struct perf_event_attr attr;
/* Without a disassembler, we can only handle 1 watchpoint. */
BUG_ON(core_num_wrps > 1);
- hw_breakpoint_init(&attr);
- attr.bp_addr = regs->ARM_pc & ~0x3;
- attr.bp_len = HW_BREAKPOINT_LEN_4;
- attr.bp_type = HW_BREAKPOINT_X;
-
for (i = 0; i < core_num_wrps; ++i) {
rcu_read_lock();
- if (slots[i] == NULL) {
+ wp = slots[i];
+
+ if (wp == NULL) {
rcu_read_unlock();
continue;
}
@@ -689,24 +669,60 @@ static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs)
* single watchpoint, we can set the trigger to the lowest
* possible faulting address.
*/
- info = counter_arch_bp(slots[i]);
- info->trigger = slots[i]->attr.bp_addr;
+ info = counter_arch_bp(wp);
+ info->trigger = wp->attr.bp_addr;
pr_debug("watchpoint fired: address = 0x%x\n", info->trigger);
- perf_bp_event(slots[i], regs);
+ perf_bp_event(wp, regs);
/*
* If no overflow handler is present, insert a temporary
* mismatch breakpoint so we can single-step over the
* watchpoint trigger.
*/
- if (!slots[i]->overflow_handler) {
- bp = register_user_hw_breakpoint(&attr,
- wp_single_step_handler,
- current);
- counter_arch_bp(bp)->suspended_wp = slots[i];
- perf_event_disable(slots[i]);
+ if (!wp->overflow_handler) {
+ arch_uninstall_hw_breakpoint(wp);
+ info->step_ctrl.mismatch = 1;
+ info->step_ctrl.len = ARM_BREAKPOINT_LEN_4;
+ info->step_ctrl.type = ARM_BREAKPOINT_EXECUTE;
+ info->step_ctrl.privilege = info->ctrl.privilege;
+ info->step_ctrl.enabled = 1;
+ info->trigger = regs->ARM_pc;
+ arch_install_hw_breakpoint(wp);
+ }
+
+ rcu_read_unlock();
+ }
+}
+
+static void watchpoint_single_step_handler(unsigned long pc)
+{
+ int i;
+ struct perf_event *wp, **slots = __get_cpu_var(wp_on_reg);
+ struct arch_hw_breakpoint *info;
+
+ for (i = 0; i < core_num_reserved_brps; ++i) {
+ rcu_read_lock();
+
+ wp = slots[i];
+
+ if (wp == NULL)
+ goto unlock;
+
+ info = counter_arch_bp(wp);
+ if (!info->step_ctrl.enabled)
+ goto unlock;
+
+ /*
+ * Restore the original watchpoint if we've completed the
+ * single-step.
+ */
+ if (info->trigger != pc) {
+ arch_uninstall_hw_breakpoint(wp);
+ info->step_ctrl.enabled = 0;
+ arch_install_hw_breakpoint(wp);
}
+unlock:
rcu_read_unlock();
}
}
@@ -723,7 +739,8 @@ static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs)
/* The exception entry code places the amended lr in the PC. */
addr = regs->ARM_pc;
- for (i = 0; i < core_num_brps + core_num_reserved_brps; ++i) {
+ /* Check the currently installed breakpoints first. */
+ for (i = 0; i < core_num_brps; ++i) {
rcu_read_lock();
bp = slots[i];
@@ -750,7 +767,7 @@ static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs)
}
unlock:
- if ((mismatch && !info->ctrl.mismatch) || bp_is_single_step(bp)) {
+ if (mismatch && !info->ctrl.mismatch) {
pr_debug("breakpoint fired: address = 0x%x\n", addr);
perf_bp_event(bp, regs);
}
@@ -758,6 +775,9 @@ unlock:
update_mismatch_flag(i, mismatch);
rcu_read_unlock();
}
+
+ /* Handle any pending watchpoint single-step breakpoints. */
+ watchpoint_single_step_handler(addr);
}
/*
--
1.7.0.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 07/11] ARM: hw_breakpoint: unify single-stepping code for watchpoints and breakpoints
2010-12-02 13:45 [PATCH 00/11] ARM: hw_breakpoint: fixes and improvements (v3) Will Deacon
` (5 preceding siblings ...)
2010-12-02 13:45 ` [PATCH 06/11] ARM: hw_breakpoint: do not allocate new breakpoints with preemption disabled Will Deacon
@ 2010-12-02 13:45 ` Will Deacon
2010-12-02 13:45 ` [PATCH 08/11] ARM: hw_breakpoint: disallow per-cpu breakpoints without overflow handler Will Deacon
` (3 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Will Deacon @ 2010-12-02 13:45 UTC (permalink / raw)
To: linux-arm-kernel
The single-stepping code is currently different depending on whether
we are stepping over a breakpoint or a watchpoint. There is no good
reason for this, so let's sort it out.
This patch adds functions for enabling/disabling single-step for
a particular hw_breakpoint and integrates this with the exception
handling code.
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm/include/asm/hw_breakpoint.h | 4 +-
arch/arm/kernel/hw_breakpoint.c | 81 +++++++++++++++++-----------------
2 files changed, 42 insertions(+), 43 deletions(-)
diff --git a/arch/arm/include/asm/hw_breakpoint.h b/arch/arm/include/asm/hw_breakpoint.h
index 881429d..f389b27 100644
--- a/arch/arm/include/asm/hw_breakpoint.h
+++ b/arch/arm/include/asm/hw_breakpoint.h
@@ -20,8 +20,8 @@ struct arch_hw_breakpoint_ctrl {
struct arch_hw_breakpoint {
u32 address;
u32 trigger;
- struct arch_hw_breakpoint_ctrl step_ctrl;
- struct arch_hw_breakpoint_ctrl ctrl;
+ struct arch_hw_breakpoint_ctrl step_ctrl;
+ struct arch_hw_breakpoint_ctrl ctrl;
};
static inline u32 encode_ctrl_reg(struct arch_hw_breakpoint_ctrl ctrl)
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index 81b63e9..36cd768 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -339,6 +339,11 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
val_base = ARM_BASE_BVR;
slots = __get_cpu_var(bp_on_reg);
max_slots = core_num_brps;
+ if (info->step_ctrl.enabled) {
+ /* Override the breakpoint data with the step data. */
+ addr = info->trigger & ~0x3;
+ ctrl = encode_ctrl_reg(info->step_ctrl);
+ }
} else {
/* Watchpoint */
if (info->step_ctrl.enabled) {
@@ -628,21 +633,28 @@ out:
return ret;
}
-static void update_mismatch_flag(int idx, int flag)
+/*
+ * Enable/disable single-stepping over the breakpoint bp at address addr.
+ */
+static void enable_single_step(struct perf_event *bp, u32 addr)
{
- struct perf_event *bp = __get_cpu_var(bp_on_reg[idx]);
- struct arch_hw_breakpoint *info;
-
- if (bp == NULL)
- return;
+ struct arch_hw_breakpoint *info = counter_arch_bp(bp);
- info = counter_arch_bp(bp);
+ arch_uninstall_hw_breakpoint(bp);
+ info->step_ctrl.mismatch = 1;
+ info->step_ctrl.len = ARM_BREAKPOINT_LEN_4;
+ info->step_ctrl.type = ARM_BREAKPOINT_EXECUTE;
+ info->step_ctrl.privilege = info->ctrl.privilege;
+ info->step_ctrl.enabled = 1;
+ info->trigger = addr;
+ arch_install_hw_breakpoint(bp);
+}
- /* Update the mismatch field to enter/exit `single-step' mode */
- if (!bp->overflow_handler && info->ctrl.mismatch != flag) {
- info->ctrl.mismatch = flag;
- write_wb_reg(ARM_BASE_BCR + idx, encode_ctrl_reg(info->ctrl) | 0x1);
- }
+static void disable_single_step(struct perf_event *bp)
+{
+ arch_uninstall_hw_breakpoint(bp);
+ counter_arch_bp(bp)->step_ctrl.enabled = 0;
+ arch_install_hw_breakpoint(bp);
}
static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs)
@@ -679,16 +691,8 @@ static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs)
* mismatch breakpoint so we can single-step over the
* watchpoint trigger.
*/
- if (!wp->overflow_handler) {
- arch_uninstall_hw_breakpoint(wp);
- info->step_ctrl.mismatch = 1;
- info->step_ctrl.len = ARM_BREAKPOINT_LEN_4;
- info->step_ctrl.type = ARM_BREAKPOINT_EXECUTE;
- info->step_ctrl.privilege = info->ctrl.privilege;
- info->step_ctrl.enabled = 1;
- info->trigger = regs->ARM_pc;
- arch_install_hw_breakpoint(wp);
- }
+ if (!wp->overflow_handler)
+ enable_single_step(wp, instruction_pointer(regs));
rcu_read_unlock();
}
@@ -716,11 +720,8 @@ static void watchpoint_single_step_handler(unsigned long pc)
* Restore the original watchpoint if we've completed the
* single-step.
*/
- if (info->trigger != pc) {
- arch_uninstall_hw_breakpoint(wp);
- info->step_ctrl.enabled = 0;
- arch_install_hw_breakpoint(wp);
- }
+ if (info->trigger != pc)
+ disable_single_step(wp);
unlock:
rcu_read_unlock();
@@ -730,7 +731,6 @@ unlock:
static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs)
{
int i;
- int mismatch;
u32 ctrl_reg, val, addr;
struct perf_event *bp, **slots = __get_cpu_var(bp_on_reg);
struct arch_hw_breakpoint *info;
@@ -745,34 +745,33 @@ static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs)
bp = slots[i];
- if (bp == NULL) {
- rcu_read_unlock();
- continue;
- }
+ if (bp == NULL)
+ goto unlock;
- mismatch = 0;
+ info = counter_arch_bp(bp);
/* Check if the breakpoint value matches. */
val = read_wb_reg(ARM_BASE_BVR + i);
if (val != (addr & ~0x3))
- goto unlock;
+ goto mismatch;
/* Possible match, check the byte address select to confirm. */
ctrl_reg = read_wb_reg(ARM_BASE_BCR + i);
decode_ctrl_reg(ctrl_reg, &ctrl);
if ((1 << (addr & 0x3)) & ctrl.len) {
- mismatch = 1;
- info = counter_arch_bp(bp);
info->trigger = addr;
- }
-
-unlock:
- if (mismatch && !info->ctrl.mismatch) {
pr_debug("breakpoint fired: address = 0x%x\n", addr);
perf_bp_event(bp, regs);
+ if (!bp->overflow_handler)
+ enable_single_step(bp, addr);
+ goto unlock;
}
- update_mismatch_flag(i, mismatch);
+mismatch:
+ /* If we're stepping a breakpoint, it can now be restored. */
+ if (info->step_ctrl.enabled)
+ disable_single_step(bp);
+unlock:
rcu_read_unlock();
}
--
1.7.0.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 08/11] ARM: hw_breakpoint: disallow per-cpu breakpoints without overflow handler
2010-12-02 13:45 [PATCH 00/11] ARM: hw_breakpoint: fixes and improvements (v3) Will Deacon
` (6 preceding siblings ...)
2010-12-02 13:45 ` [PATCH 07/11] ARM: hw_breakpoint: unify single-stepping code for watchpoints and breakpoints Will Deacon
@ 2010-12-02 13:45 ` Will Deacon
2010-12-02 13:46 ` [PATCH 09/11] ARM: ptrace: fix style issue with hw_breakpoint interface Will Deacon
` (2 subsequent siblings)
10 siblings, 0 replies; 14+ messages in thread
From: Will Deacon @ 2010-12-02 13:45 UTC (permalink / raw)
To: linux-arm-kernel
Single-stepping a breakpoint requires us to disable it temporarily so that
we don't get stuck in a recursive debug trap. With per-cpu breakpoints this
presents a problem where an interrupt can be taken before the single-step has
completed and a new task is eventually scheduled. This new task will not
hit the breakpoint because it will have been disabled during the previous
handling code.
This patch disallows per-cpu breakpoints on ARM when an overflow handler
is not present. A similar effect can be created by placing breakpoints on
a shell and then running applications there.
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm/kernel/hw_breakpoint.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index 36cd768..eef1b1e 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -622,10 +622,12 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
* Currently we rely on an overflow handler to take
* care of single-stepping the breakpoint when it fires.
* In the case of userspace breakpoints on a core with V7 debug,
- * we can use the mismatch feature as a poor-man's hardware single-step.
+ * we can use the mismatch feature as a poor-man's hardware
+ * single-step, but this only works for per-task breakpoints.
*/
if (WARN_ONCE(!bp->overflow_handler &&
- (arch_check_bp_in_kernelspace(bp) || !core_has_mismatch_brps()),
+ (arch_check_bp_in_kernelspace(bp) || !core_has_mismatch_brps()
+ || !bp->hw.bp_target),
"overflow handler required but none found")) {
ret = -EINVAL;
}
--
1.7.0.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 09/11] ARM: ptrace: fix style issue with hw_breakpoint interface
2010-12-02 13:45 [PATCH 00/11] ARM: hw_breakpoint: fixes and improvements (v3) Will Deacon
` (7 preceding siblings ...)
2010-12-02 13:45 ` [PATCH 08/11] ARM: hw_breakpoint: disallow per-cpu breakpoints without overflow handler Will Deacon
@ 2010-12-02 13:46 ` Will Deacon
2010-12-02 13:46 ` [PATCH 10/11] ARM: hw_breakpoint: fix warnings generated by sparse Will Deacon
2010-12-02 13:46 ` [PATCH 11/11] ARM: hw_breakpoint: do not fail initcall if monitor mode is disabled Will Deacon
10 siblings, 0 replies; 14+ messages in thread
From: Will Deacon @ 2010-12-02 13:46 UTC (permalink / raw)
To: linux-arm-kernel
This patch fixes a trivial style issue in ptrace.c.
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm/kernel/ptrace.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index 3e97483..19c6816 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -1060,8 +1060,8 @@ static int ptrace_sethbpregs(struct task_struct *tsk, long num,
goto out;
if ((gen_type & implied_type) != gen_type) {
- ret = -EINVAL;
- goto out;
+ ret = -EINVAL;
+ goto out;
}
attr.bp_len = gen_len;
--
1.7.0.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 10/11] ARM: hw_breakpoint: fix warnings generated by sparse
2010-12-02 13:45 [PATCH 00/11] ARM: hw_breakpoint: fixes and improvements (v3) Will Deacon
` (8 preceding siblings ...)
2010-12-02 13:46 ` [PATCH 09/11] ARM: ptrace: fix style issue with hw_breakpoint interface Will Deacon
@ 2010-12-02 13:46 ` Will Deacon
2010-12-02 14:30 ` Russell King - ARM Linux
2010-12-02 13:46 ` [PATCH 11/11] ARM: hw_breakpoint: do not fail initcall if monitor mode is disabled Will Deacon
10 siblings, 1 reply; 14+ messages in thread
From: Will Deacon @ 2010-12-02 13:46 UTC (permalink / raw)
To: linux-arm-kernel
sparse doesn't like per-cpu accesses such as:
static DEFINE_PER_CPU(struct perf_event *, foo[MAXLEN]);
struct perf_event **bar = __get_cpu_var(foo);
and shouts quite loudly about it:
| warning: incorrect type in assignment (different modifiers)
| expected struct perf_event **slots
| got struct perf_event *[noderef] *<noident>
This patch adds casts to these sorts of assignments in hw_breakpoint.c
in order to silence the warnings.
Reported-by: Russell King - ARM Linux <linux@arm.linux.org.uk>
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm/kernel/hw_breakpoint.c | 20 +++++++++++++-------
1 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index eef1b1e..56ed9a6 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -337,7 +337,7 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
/* Breakpoint */
ctrl_base = ARM_BASE_BCR;
val_base = ARM_BASE_BVR;
- slots = __get_cpu_var(bp_on_reg);
+ slots = (struct perf_event **)__get_cpu_var(bp_on_reg);
max_slots = core_num_brps;
if (info->step_ctrl.enabled) {
/* Override the breakpoint data with the step data. */
@@ -357,7 +357,7 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
ctrl_base = ARM_BASE_WCR;
val_base = ARM_BASE_WVR;
}
- slots = __get_cpu_var(wp_on_reg);
+ slots = (struct perf_event **)__get_cpu_var(wp_on_reg);
max_slots = core_num_wrps;
}
@@ -394,7 +394,7 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) {
/* Breakpoint */
base = ARM_BASE_BCR;
- slots = __get_cpu_var(bp_on_reg);
+ slots = (struct perf_event **)__get_cpu_var(bp_on_reg);
max_slots = core_num_brps;
} else {
/* Watchpoint */
@@ -402,7 +402,7 @@ void arch_uninstall_hw_breakpoint(struct perf_event *bp)
base = ARM_BASE_BCR + core_num_brps;
else
base = ARM_BASE_WCR;
- slots = __get_cpu_var(wp_on_reg);
+ slots = (struct perf_event **)__get_cpu_var(wp_on_reg);
max_slots = core_num_wrps;
}
@@ -662,9 +662,11 @@ static void disable_single_step(struct perf_event *bp)
static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs)
{
int i;
- struct perf_event *wp, **slots = __get_cpu_var(wp_on_reg);
+ struct perf_event *wp, **slots;
struct arch_hw_breakpoint *info;
+ slots = (struct perf_event **)__get_cpu_var(wp_on_reg);
+
/* Without a disassembler, we can only handle 1 watchpoint. */
BUG_ON(core_num_wrps > 1);
@@ -703,9 +705,11 @@ static void watchpoint_handler(unsigned long unknown, struct pt_regs *regs)
static void watchpoint_single_step_handler(unsigned long pc)
{
int i;
- struct perf_event *wp, **slots = __get_cpu_var(wp_on_reg);
+ struct perf_event *wp, **slots;
struct arch_hw_breakpoint *info;
+ slots = (struct perf_event **)__get_cpu_var(wp_on_reg);
+
for (i = 0; i < core_num_reserved_brps; ++i) {
rcu_read_lock();
@@ -734,10 +738,12 @@ static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs)
{
int i;
u32 ctrl_reg, val, addr;
- struct perf_event *bp, **slots = __get_cpu_var(bp_on_reg);
+ struct perf_event *bp, **slots;
struct arch_hw_breakpoint *info;
struct arch_hw_breakpoint_ctrl ctrl;
+ slots = (struct perf_event **)__get_cpu_var(bp_on_reg);
+
/* The exception entry code places the amended lr in the PC. */
addr = regs->ARM_pc;
--
1.7.0.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 11/11] ARM: hw_breakpoint: do not fail initcall if monitor mode is disabled
2010-12-02 13:45 [PATCH 00/11] ARM: hw_breakpoint: fixes and improvements (v3) Will Deacon
` (9 preceding siblings ...)
2010-12-02 13:46 ` [PATCH 10/11] ARM: hw_breakpoint: fix warnings generated by sparse Will Deacon
@ 2010-12-02 13:46 ` Will Deacon
10 siblings, 0 replies; 14+ messages in thread
From: Will Deacon @ 2010-12-02 13:46 UTC (permalink / raw)
To: linux-arm-kernel
The debug registers can only be manipulated from software if monitor
debug mode is enabled. On some cores, this can never be enabled (i.e.
the corresponding bit the the DSCR is RAZ/WI).
This patch ensures we can handle this hardware configuration and fail
gracefully, rather than blow up the kernel during boot.
Reported-by: Cyril Chemparathy <cyril@ti.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
arch/arm/kernel/hw_breakpoint.c | 22 +++++++++++-----------
1 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c
index 56ed9a6..da647d9 100644
--- a/arch/arm/kernel/hw_breakpoint.c
+++ b/arch/arm/kernel/hw_breakpoint.c
@@ -225,6 +225,9 @@ int hw_breakpoint_slots(int type)
* We can be called early, so don't rely on
* our static variables being initialised.
*/
+ if (enable_monitor_mode())
+ return 0;
+
switch (type) {
case TYPE_INST:
return get_num_brps();
@@ -272,10 +275,8 @@ static int enable_monitor_mode(void)
/* Check that the write made it through. */
ARM_DBG_READ(c1, 0, dscr);
- if (WARN_ONCE(!(dscr & ARM_DSCR_MDBGEN),
- "failed to enable monitor mode.")) {
+ if (!(dscr & ARM_DSCR_MDBGEN))
ret = -EPERM;
- }
out:
return ret;
@@ -294,9 +295,6 @@ static u8 get_max_wp_len(void)
if (debug_arch < ARM_DEBUG_ARCH_V7_ECP14)
goto out;
- if (enable_monitor_mode())
- goto out;
-
memset(&ctrl, 0, sizeof(ctrl));
ctrl.len = ARM_BREAKPOINT_LEN_8;
ctrl_reg = encode_ctrl_reg(ctrl);
@@ -879,15 +877,18 @@ static struct notifier_block __cpuinitdata dbg_reset_nb = {
static int __init arch_hw_breakpoint_init(void)
{
- int ret = 0;
u32 dscr;
debug_arch = get_debug_arch();
if (debug_arch > ARM_DEBUG_ARCH_V7_ECP14) {
pr_info("debug architecture 0x%x unsupported.\n", debug_arch);
- ret = -ENODEV;
- goto out;
+ return 0;
+ }
+
+ if (enable_monitor_mode()) {
+ pr_warning("unable to activate monitor debug mode.\n");
+ return 0;
}
/* Determine how many BRPs/WRPs are available. */
@@ -928,8 +929,7 @@ static int __init arch_hw_breakpoint_init(void)
/* Register hotplug notifier. */
register_cpu_notifier(&dbg_reset_nb);
-out:
- return ret;
+ return 0;
}
arch_initcall(arch_hw_breakpoint_init);
--
1.7.0.4
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 10/11] ARM: hw_breakpoint: fix warnings generated by sparse
2010-12-02 13:46 ` [PATCH 10/11] ARM: hw_breakpoint: fix warnings generated by sparse Will Deacon
@ 2010-12-02 14:30 ` Russell King - ARM Linux
2010-12-02 14:33 ` Will Deacon
0 siblings, 1 reply; 14+ messages in thread
From: Russell King - ARM Linux @ 2010-12-02 14:30 UTC (permalink / raw)
To: linux-arm-kernel
On Thu, Dec 02, 2010 at 01:46:01PM +0000, Will Deacon wrote:
> This patch adds casts to these sorts of assignments in hw_breakpoint.c
> in order to silence the warnings.
>
> Reported-by: Russell King - ARM Linux <linux@arm.linux.org.uk>
Please change this to:
Reported-by: Russell King <rmk+kernel@arm.linux.org.uk>
I don't need more - and different - email addresses in git for
get_maintainer.pl etc to spam me with.
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 10/11] ARM: hw_breakpoint: fix warnings generated by sparse
2010-12-02 14:30 ` Russell King - ARM Linux
@ 2010-12-02 14:33 ` Will Deacon
0 siblings, 0 replies; 14+ messages in thread
From: Will Deacon @ 2010-12-02 14:33 UTC (permalink / raw)
To: linux-arm-kernel
Hi Russell,
> On Thu, Dec 02, 2010 at 01:46:01PM +0000, Will Deacon wrote:
> > This patch adds casts to these sorts of assignments in hw_breakpoint.c
> > in order to silence the warnings.
> >
> > Reported-by: Russell King - ARM Linux <linux@arm.linux.org.uk>
>
> Please change this to:
>
> Reported-by: Russell King <rmk+kernel@arm.linux.org.uk>
>
> I don't need more - and different - email addresses in git for
> get_maintainer.pl etc to spam me with.
Sure thing, I'll update it. I just used the email address from
which you reported the problem.
Will
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2010-12-02 14:33 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-12-02 13:45 [PATCH 00/11] ARM: hw_breakpoint: fixes and improvements (v3) Will Deacon
2010-12-02 13:45 ` [PATCH 01/11] ARM: hw_breakpoint: ensure OS lock is clear before writing to debug registers Will Deacon
2010-12-02 13:45 ` [PATCH 02/11] ARM: hw_breakpoint: reset control registers in hotplug path Will Deacon
2010-12-02 13:45 ` [PATCH 03/11] ARM: hw_breakpoint: correct and simplify alignment fixup code Will Deacon
2010-12-02 13:45 ` [PATCH 04/11] ARM: hw_breakpoint: disable preemption during debug exception handling Will Deacon
2010-12-02 13:45 ` [PATCH 05/11] ARM: hw_breakpoint: don't advertise reserved breakpoints Will Deacon
2010-12-02 13:45 ` [PATCH 06/11] ARM: hw_breakpoint: do not allocate new breakpoints with preemption disabled Will Deacon
2010-12-02 13:45 ` [PATCH 07/11] ARM: hw_breakpoint: unify single-stepping code for watchpoints and breakpoints Will Deacon
2010-12-02 13:45 ` [PATCH 08/11] ARM: hw_breakpoint: disallow per-cpu breakpoints without overflow handler Will Deacon
2010-12-02 13:46 ` [PATCH 09/11] ARM: ptrace: fix style issue with hw_breakpoint interface Will Deacon
2010-12-02 13:46 ` [PATCH 10/11] ARM: hw_breakpoint: fix warnings generated by sparse Will Deacon
2010-12-02 14:30 ` Russell King - ARM Linux
2010-12-02 14:33 ` Will Deacon
2010-12-02 13:46 ` [PATCH 11/11] ARM: hw_breakpoint: do not fail initcall if monitor mode is disabled Will Deacon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).