ARM: hw_breakpoint: fix ordering of debug register reset sequence
The debug register reset sequence for v7 and v7.1 is congruent with tap-dancing through a minefield. Rather than wait until we've blown ourselves to pieces, this patch instead checks the debug_err_mask after each potentially faulting operation. We also move the enabling of monitor_mode to the end of the sequence in order to prevent spurious debug events generated by UNKNOWN register values. Reported-by: Stephen Boyd <sboyd@codeaurora.org> Tested-by: Stephen Boyd <sboyd@codeaurora.org> Signed-off-by: Will Deacon <will.deacon@arm.com>
This commit is contained in:
parent
b59a540ca9
commit
614bea500a
1 changed files with 26 additions and 10 deletions
|
@ -231,8 +231,6 @@ static int get_num_brps(void)
|
||||||
static int enable_monitor_mode(void)
|
static int enable_monitor_mode(void)
|
||||||
{
|
{
|
||||||
u32 dscr;
|
u32 dscr;
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
ARM_DBG_READ(c1, 0, dscr);
|
ARM_DBG_READ(c1, 0, dscr);
|
||||||
|
|
||||||
/* If monitor mode is already enabled, just return. */
|
/* If monitor mode is already enabled, just return. */
|
||||||
|
@ -251,17 +249,18 @@ static int enable_monitor_mode(void)
|
||||||
isb();
|
isb();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -ENODEV;
|
return -ENODEV;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check that the write made it through. */
|
/* Check that the write made it through. */
|
||||||
ARM_DBG_READ(c1, 0, dscr);
|
ARM_DBG_READ(c1, 0, dscr);
|
||||||
if (!(dscr & ARM_DSCR_MDBGEN))
|
if (WARN_ONCE(!(dscr & ARM_DSCR_MDBGEN),
|
||||||
ret = -EPERM;
|
"Failed to enable monitor mode on CPU %d.\n",
|
||||||
|
smp_processor_id()))
|
||||||
|
return -EPERM;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hw_breakpoint_slots(int type)
|
int hw_breakpoint_slots(int type)
|
||||||
|
@ -962,11 +961,16 @@ clear_vcr:
|
||||||
asm volatile("mcr p14, 0, %0, c0, c7, 0" : : "r" (0));
|
asm volatile("mcr p14, 0, %0, c0, c7, 0" : : "r" (0));
|
||||||
isb();
|
isb();
|
||||||
|
|
||||||
reset_regs:
|
if (cpumask_intersects(&debug_err_mask, cpumask_of(cpu))) {
|
||||||
if (enable_monitor_mode())
|
pr_warning("CPU %d failed to disable vector catch\n", cpu);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* We must also reset any reserved registers. */
|
reset_regs:
|
||||||
|
/*
|
||||||
|
* The control/value register pairs are UNKNOWN out of reset so
|
||||||
|
* clear them to avoid spurious debug events.
|
||||||
|
*/
|
||||||
raw_num_brps = get_num_brp_resources();
|
raw_num_brps = get_num_brp_resources();
|
||||||
for (i = 0; i < raw_num_brps; ++i) {
|
for (i = 0; i < raw_num_brps; ++i) {
|
||||||
write_wb_reg(ARM_BASE_BCR + i, 0UL);
|
write_wb_reg(ARM_BASE_BCR + i, 0UL);
|
||||||
|
@ -977,6 +981,18 @@ reset_regs:
|
||||||
write_wb_reg(ARM_BASE_WCR + i, 0UL);
|
write_wb_reg(ARM_BASE_WCR + i, 0UL);
|
||||||
write_wb_reg(ARM_BASE_WVR + i, 0UL);
|
write_wb_reg(ARM_BASE_WVR + i, 0UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cpumask_intersects(&debug_err_mask, cpumask_of(cpu))) {
|
||||||
|
pr_warning("CPU %d failed to clear debug register pairs\n", cpu);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Have a crack at enabling monitor mode. We don't actually need
|
||||||
|
* it yet, but reporting an error early is useful if it fails.
|
||||||
|
*/
|
||||||
|
if (enable_monitor_mode())
|
||||||
|
cpumask_or(&debug_err_mask, &debug_err_mask, cpumask_of(cpu));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __cpuinit dbg_reset_notify(struct notifier_block *self,
|
static int __cpuinit dbg_reset_notify(struct notifier_block *self,
|
||||||
|
|
Loading…
Add table
Reference in a new issue