ARM: hw_breakpoint: updates for 3.2
Subsystems (2)
cover ARM: hw_breakpoint: updates for 3.2
Hello,
I finally got my paws on a Cortex-A15 FPGA and updated the hw_breakpoint code to make use of the shiny new features in debug architecture 7.1.
This patch series adds support for:
1.) Debug architecture 7.1 detection and initialisation 2.) Reading of the DFAR on watchpoint exceptions, so now we can handle
more than just a single watchpoint.
3.) Graceful failure when a debug-hating implementor decides to tie
DBGSWENABLE low.
I've taken it for a spin with perf on Cortex-A9 and A15 platforms and it seems to work. I've also booted it on my 1176 to ensure that the new initialisation code doesn't barf there.
All feedback welcome.
Will
Will Deacon (5):
ARM: hw_breakpoint: add initial Cortex-A15 (debug v7.1) support
ARM: hw_breakpoint: reserve one breakpoint for watchpoint stepping
ARM: hw_breakpoint: add support for multiple watchpoints
ARM: hw_breakpoint: reduce the number of WARN_ONCE invocations
ARM: hw_breakpoint: trap undef instruction exceptions in
reset_ctrl_regs
arch/arm/include/asm/hw_breakpoint.h | 2 + arch/arm/kernel/hw_breakpoint.c | 267 ++++++++++++++++++++------------- 2 files changed, 164 insertions(+), 105 deletions(-)
PATCH 1/5 ARM: hw_breakpoint: add initial Cortex-A15 (debug v7.1) support
This patch adds initial support for Cortex-A15 (debug architecture v7.1) to the hw_breakpoint ARM backend.
--- arch/arm/include/asm/hw_breakpoint.h | 1 + arch/arm/kernel/hw_breakpoint.c | 52 +++++++++++++++++++++------------ 2 files changed, 34 insertions(+), 19 deletions(-)
arch/arm/include/asm/hw_breakpoint.h +1 -0
@@ -50,6 +50,7 @@ static inline void decode_ctrl_reg(u32 reg,#define ARM_DEBUG_ARCH_V6_1 2#define ARM_DEBUG_ARCH_V7_ECP14 3#define ARM_DEBUG_ARCH_V7_MM 4 +#define ARM_DEBUG_ARCH_V7_1 5/* Breakpoint */ #define ARM_BREAKPOINT_EXECUTE 0
arch/arm/kernel/hw_breakpoint.c +33 -19
86 lines not on this page · raw message
Signature · 1 line
1.7.0.4
PATCH 1/5 ARM: hw_breakpoint: add initial Cortex-A15 (debug v7.1) support
Quoted 17 lines · Will Deacon
arch/arm/include/asm/hw_breakpoint.h +1
-0@@ -50,6 +50,7 @@ static inline void decode_ctrl_reg(u32 reg,#define ARM_DEBUG_ARCH_V6_1 2#define ARM_DEBUG_ARCH_V7_ECP14 3#define ARM_DEBUG_ARCH_V7_MM 4 +#define ARM_DEBUG_ARCH_V7_1 5/* Breakpoint */ #define ARM_BREAKPOINT_EXECUTE 0arch/arm/kernel/hw_breakpoint.c +0
-0
arch/arm/kernel/hw_breakpoint.c u8 arch_get_debug_arch(void) · line 157
Quoted 5 lines
arch/arm/kernel/hw_breakpoint.c +1
-1@@ -154,7 +154,7 @@ u8 arch_get_debug_arch(void)static int debug_arch_supported (void ) {u8 arch =get_debug_arch (); -return arch >=ARM_DEBUG_ARCH_V6 && arch <=ARM_DEBUG_ARCH_V7_ECP14 ; +return arch >=ARM_DEBUG_ARCH_V6 && arch <=ARM_DEBUG_ARCH_V7_1 ;
Is ARM_DEBUG_ARCH_V7_MM supported now too?
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
PATCH 1/5 ARM: hw_breakpoint: add initial Cortex-A15 (debug v7.1) support
Hi Stephen,
Quoted 11 lines · Stephen Boyd
Quoted 10 lines · Will Deacon
arch/arm/include/asm/hw_breakpoint.h +1
-0@@ -50,6 +50,7 @@ static inline void decode_ctrl_reg(u32 reg,#define ARM_DEBUG_ARCH_V6_1 2#define ARM_DEBUG_ARCH_V7_ECP14 3#define ARM_DEBUG_ARCH_V7_MM 4 +#define ARM_DEBUG_ARCH_V7_1 5
Quoted 14 lines
arch/arm/kernel/hw_breakpoint.c u8 arch_get_debug_arch(void) · line 157
Quoted 12 lines
arch/arm/include/asm/hw_breakpoint.h +0
-0/* Breakpoint */ #define ARM_BREAKPOINT_EXECUTE 0arch/arm/kernel/hw_breakpoint.c +1
-1@@ -154,7 +154,7 @@ u8 arch_get_debug_arch(void)static int debug_arch_supported (void ) {u8 arch =get_debug_arch (); -return arch >=ARM_DEBUG_ARCH_V6 && arch <=ARM_DEBUG_ARCH_V7_ECP14 ; +return arch >=ARM_DEBUG_ARCH_V6 && arch <=ARM_DEBUG_ARCH_V7_1 ;Is
ARM_DEBUG_ARCH_V7_MMsupported now too?
Ah, well spotted. I'd better fix that check before somebody thinks I'm committing to supporting the memory-mapped interface!
Thanks,
Will
PATCH 2/5 ARM: hw_breakpoint: reserve one breakpoint for watchpoint stepping
In reply to Will DeaconThe current hw_breakpoint code on ARM reserves 1 breakpoint for each watchpoint that is available. Since debug architectures prior to 7.1 are restricted to 1 watchpoint anyway, only one breakpoint was ever reserved.
This patch changes the reservation strategy so that a single breakpoint is reserved, regardless of the number of watchpoints. This is in preparation for multiple-watchpoint support on debug architectures from 7.1 onwards.
--- arch/arm/kernel/hw_breakpoint.c | 63 +++++++++++++++------------------------ 1 files changed, 24 insertions(+), 39 deletions(-)
arch/arm/kernel/hw_breakpoint.c +24 -39
@@ -45,7 +45,6 @@ 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. */
@@ -157,7 +156,15 @@ static int debug_arch_supported(void)return arch >=ARM_DEBUG_ARCH_V6 && arch <=ARM_DEBUG_ARCH_V7_1 ; } -/* Determine number of BRP register available. */ +/* Determine number of WRP registers available. */ +static int get_num_wrp_resources (void ) +{ +u32 didr; +ARM_DBG_READ (c0,0 , didr); +return ((didr >>28 ) &0xf ) +1 ; +} + +/* Determine number of BRP registers available. */ static int get_num_brp_resources (void ) {u32 didr;
@@ -176,9 +183,10 @@ static int core_has_mismatch_brps(void)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. +* On debug architectures prior to 7.1, 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
@@ -192,36 +200,17 @@ static int get_num_wrps(void)* [the ARM ARM states that the DFAR is UNKNOWN, but experience shows * that it is set on some implementations]. */ +if (get_debug_arch () <ARM_DEBUG_ARCH_V7_1 ) +return 1 ; -#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 ; +return get_num_wrp_resources (); }/* 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; +return core_has_mismatch_brps () ? brps -1 : brps; }/*
@@ -718,7 +707,7 @@ static void watchpoint_single_step_handler(unsigned long pc) slots = (struct perf_event **)__get_cpu_var (wp_on_reg); -for (i =0 ; i < core_num_reserved_brps; ++i) { +for (i =0 ; i < core_num_wrps; ++i) {rcu_read_lock (); wp = slots[i];
@@ -837,7 +826,7 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr,*/ static void reset_ctrl_regs (void *info) { -int i, err =0 , cpu =smp_processor_id (); +int i, raw_num_brps, err =0 , cpu =smp_processor_id ();u32 dbg_power;cpumask_t *cpumask = info;
@@ -893,7 +882,8 @@ static void reset_ctrl_regs(void *info)return ;/* We must also reset any reserved registers. */ -for (i =0 ; i < core_num_brps + core_num_reserved_brps; ++i) { + raw_num_brps =get_num_brp_resources (); +for (i =0 ; i < raw_num_brps; ++i) {write_wb_reg (ARM_BASE_BCR + i,0UL );write_wb_reg (ARM_BASE_BVR + i,0UL ); }
@@ -930,15 +920,11 @@ 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_reserved_brps, core_num_wrps); - -if (core_num_reserved_brps) -pr_info ("%d breakpoint(s) reserved for watchpoint " -"single-step.\n" , core_num_reserved_brps); +pr_info ("found %d " "%s" "breakpoint and %d watchpoint registers.\n" , + core_num_brps,core_has_mismatch_brps () ?"(+1 reserved) " : +"" , core_num_wrps);/* * Reset the breakpoint resources. We assume that a halting
@@ -947,7 +933,6 @@ static int __init arch_hw_breakpoint_init(void)on_each_cpu (reset_ctrl_regs, &cpumask,1 );if (!cpumask_empty (&cpumask)) { core_num_brps =0 ; - core_num_reserved_brps =0 ; core_num_wrps =0 ;return 0 ; }
Signature · 1 line
1.7.0.4
PATCH 3/5 ARM: hw_breakpoint: add support for multiple watchpoints
In reply to Will DeaconARM debug architecture 7.1 mandates that the DFAR is updated on a watchpoint debug exception to contain the faulting virtual address of the memory access. This allows us to determine which watchpoints have fired and therefore report useful information to userspace.
This patch adds support for using the DFAR in the watchpoint handler, which allows us to support multiple watchpoints on CPUs implementing the new debug architecture.
--- arch/arm/include/asm/hw_breakpoint.h | 1 + arch/arm/kernel/hw_breakpoint.c | 100 ++++++++++++++++++++++------------ 2 files changed, 66 insertions(+), 35 deletions(-)
arch/arm/include/asm/hw_breakpoint.h +1 -0
@@ -58,6 +58,7 @@ static inline void decode_ctrl_reg(u32 reg,/* Watchpoints */ #define ARM_BREAKPOINT_LOAD 1#define ARM_BREAKPOINT_STORE 2 +#define ARM_FSR_ACCESS_MASK (1 << 11)/* Privilege Levels */ #define ARM_BREAKPOINT_PRIV 1
arch/arm/kernel/hw_breakpoint.c +65 -35
158 lines not on this page · raw message
Signature · 1 line
1.7.0.4
PATCH 4/5 ARM: hw_breakpoint: reduce the number of WARN_ONCE invocations
In reply to Will DeaconThe ARM hw_breakpoint backend is currently a bit too noisy when things start to go awry.
This patch removes a couple of over-zealous WARN_ONCE invocations and replaces then with pr_warnings instead.
--- arch/arm/kernel/hw_breakpoint.c | 16 ++++++++-------- 1 files changed, 8 insertions(+), 8 deletions(-)
arch/arm/kernel/hw_breakpoint.c +8 -8
@@ -136,10 +136,11 @@ static u8 get_debug_arch(void)u32 didr;/* Do we implement the extended CPUID interface? */ -if (WARN_ONCE ((((read_cpuid_id () >>16 ) &0xf ) !=0xf ), -"CPUID feature registers not supported. " -"Assuming v6 debug is present.\n" )) +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 ;
@@ -228,7 +229,7 @@ static int enable_monitor_mode(void)/* Ensure that halting mode is disabled. */ if (WARN_ONCE (dscr &ARM_DSCR_HDBGEN , -"halting debug mode enabled. Unable to access hardware resources.\n" )) { +"halting debug mode enabled. Unable to access hardware resources.\n" )) { ret = -EPERM ; goto out; }
@@ -623,10 +624,9 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)* 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 () - || !bp->hw.bp_target), -"overflow handler required but none found\n" )) { +if (!bp->overflow_handler && (arch_check_bp_in_kernelspace (bp) || + !core_has_mismatch_brps () || !bp->hw.bp_target)) { +pr_warning ("overflow handler required but none found\n" ); ret = -EINVAL ; } out:
Signature · 1 line
1.7.0.4
PATCH 5/5 ARM: hw_breakpoint: trap undef instruction exceptions in reset_ctrl_regs
In reply to Will DeaconThe ARM debug registers can only be accessed if the DBGSWENABLE signal to the core is driven HIGH by the DAP. The architecture does not provide a way to detect the value of this signal, so the best we can do is register an undef_hook to trap debug register co-processor accesses and then fail if the trap is taken.
--- arch/arm/kernel/hw_breakpoint.c | 46 +++++++++++++++++++++++++++++++------- 1 files changed, 37 insertions(+), 9 deletions(-)
arch/arm/kernel/hw_breakpoint.c +37 -9
@@ -854,11 +854,31 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr,/* * One-time initialisation. */ -static void reset_ctrl_regs (void *info) +static cpumask_t debug_err_mask; + +static int debug_reg_trap (struct pt_regs *regs,unsigned int instr) +{ +int cpu =smp_processor_id (); + +pr_warning ("Debug register access (0x%x) caused undefined instruction on CPU %d\n" , + instr, cpu); + +/* Set the error flag for this CPU and skip the faulting instruction. */ +cpumask_set_cpu (cpu, &debug_err_mask); +instruction_pointer (regs) +=4 ; +return 0 ; +} + +static struct undef_hook debug_reg_hook = { + .instr_mask =0x0fe80f10 , + .instr_val =0x0e000e10 , + .fn = debug_reg_trap, +}; + +static void reset_ctrl_regs (void *unused) {int i, raw_num_brps, err =0 , cpu =smp_processor_id ();u32 dbg_power; -cpumask_t *cpumask = info;/* * v7 debug contains save and restore registers so that debug state
@@ -890,7 +910,7 @@ static void reset_ctrl_regs(void *info)if (err) {pr_warning ("CPU %d debug is powered down!\n" , cpu); -cpumask_or (cpumask, cpumask,cpumask_of (cpu)); +cpumask_or (&debug_err_mask, &debug_err_mask,cpumask_of (cpu));return ; }
@@ -929,6 +949,7 @@ static int __cpuinit dbg_reset_notify(struct notifier_block *self, {if (action ==CPU_ONLINE )smp_call_function_single ((int )cpu, reset_ctrl_regs,NULL ,1 ); +return NOTIFY_OK ; }
@@ -939,7 +960,6 @@ static struct notifier_block __cpuinitdata dbg_reset_nb = {static int __init arch_hw_breakpoint_init (void ) {u32 dscr; -cpumask_t cpumask = {CPU_BITS_NONE }; debug_arch =get_debug_arch ();
@@ -952,21 +972,29 @@ static int __init arch_hw_breakpoint_init(void) core_num_brps =get_num_brps (); core_num_wrps =get_num_wrps (); -pr_info ("found %d " "%s" "breakpoint and %d watchpoint registers.\n" , - core_num_brps,core_has_mismatch_brps () ?"(+1 reserved) " : -"" , core_num_wrps); +/* +* We need to tread carefully here because DBGSWENABLE may be +* driven low on this core and there isn't an architected way to +* determine that. +*/ +register_undef_hook (&debug_reg_hook);/* * Reset the breakpoint resources. We assume that a halting * debugger will leave the world in a nice state for us. */ -on_each_cpu (reset_ctrl_regs, &cpumask,1 ); -if (!cpumask_empty (&cpumask)) { +on_each_cpu (reset_ctrl_regs,NULL ,1 ); +unregister_undef_hook (&debug_reg_hook); +if (!cpumask_empty (&debug_err_mask)) { core_num_brps =0 ; core_num_wrps =0 ;return 0 ; } +pr_info ("found %d " "%s" "breakpoint and %d watchpoint registers.\n" , + core_num_brps,core_has_mismatch_brps () ?"(+1 reserved) " : +"" , core_num_wrps); +ARM_DBG_READ (c1,0 , dscr);if (dscr &ARM_DSCR_HDBGEN ) { max_watchpoint_len =4 ;
Signature · 1 line
1.7.0.4