Commit 27985b2a authored by Ravi Bangoria's avatar Ravi Bangoria Committed by Michael Ellerman

powerpc/watchpoint: Don't ignore extraneous exceptions blindly

On powerpc, watchpoint match range is double-word granular. On a
watchpoint hit, DAR is set to the first byte of overlap between actual
access and watched range. And thus it's quite possible that DAR does
not point inside user specified range. Ex, say user creates a
watchpoint with address range 0x1004 to 0x1007. So hw would be
configured to watch from 0x1000 to 0x1007. If there is a 4 byte access
from 0x1002 to 0x1005, DAR will point to 0x1002 and thus interrupt
handler considers it as extraneous, but it's actually not, because
part of the access belongs to what user has asked.

Instead of blindly ignoring the exception, get actual address range by
analysing an instruction, and ignore only if actual range does not
overlap with user specified range.

Note: The behavior is unchanged for 8xx.
Signed-off-by: default avatarRavi Bangoria <ravi.bangoria@linux.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20191017093204.7511-5-ravi.bangoria@linux.ibm.com
parent c3f68b04
...@@ -222,33 +222,49 @@ void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs) ...@@ -222,33 +222,49 @@ void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs)
tsk->thread.last_hit_ubp = NULL; tsk->thread.last_hit_ubp = NULL;
} }
static bool is_larx_stcx_instr(struct pt_regs *regs, unsigned int instr) static bool dar_within_range(unsigned long dar, struct arch_hw_breakpoint *info)
{ {
int ret, type; return ((info->address <= dar) && (dar - info->address < info->len));
struct instruction_op op; }
ret = analyse_instr(&op, regs, instr); static bool
type = GETTYPE(op.type); dar_range_overlaps(unsigned long dar, int size, struct arch_hw_breakpoint *info)
return (!ret && (type == LARX || type == STCX)); {
return ((dar <= info->address + info->len - 1) &&
(dar + size - 1 >= info->address));
} }
/* /*
* Handle debug exception notifications. * Handle debug exception notifications.
*/ */
static bool stepping_handler(struct pt_regs *regs, struct perf_event *bp, static bool stepping_handler(struct pt_regs *regs, struct perf_event *bp,
unsigned long addr) struct arch_hw_breakpoint *info)
{ {
unsigned int instr = 0; unsigned int instr = 0;
int ret, type, size;
struct instruction_op op;
unsigned long addr = info->address;
if (__get_user_inatomic(instr, (unsigned int *)regs->nip)) if (__get_user_inatomic(instr, (unsigned int *)regs->nip))
goto fail; goto fail;
if (is_larx_stcx_instr(regs, instr)) { ret = analyse_instr(&op, regs, instr);
type = GETTYPE(op.type);
size = GETSIZE(op.type);
if (!ret && (type == LARX || type == STCX)) {
printk_ratelimited("Breakpoint hit on instruction that can't be emulated." printk_ratelimited("Breakpoint hit on instruction that can't be emulated."
" Breakpoint at 0x%lx will be disabled.\n", addr); " Breakpoint at 0x%lx will be disabled.\n", addr);
goto disable; goto disable;
} }
/*
* If it's extraneous event, we still need to emulate/single-
* step the instruction, but we don't generate an event.
*/
if (size && !dar_range_overlaps(regs->dar, size, info))
info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ;
/* Do not emulate user-space instructions, instead single-step them */ /* Do not emulate user-space instructions, instead single-step them */
if (user_mode(regs)) { if (user_mode(regs)) {
current->thread.last_hit_ubp = bp; current->thread.last_hit_ubp = bp;
...@@ -280,7 +296,6 @@ int hw_breakpoint_handler(struct die_args *args) ...@@ -280,7 +296,6 @@ int hw_breakpoint_handler(struct die_args *args)
struct perf_event *bp; struct perf_event *bp;
struct pt_regs *regs = args->regs; struct pt_regs *regs = args->regs;
struct arch_hw_breakpoint *info; struct arch_hw_breakpoint *info;
unsigned long dar = regs->dar;
/* Disable breakpoints during exception handling */ /* Disable breakpoints during exception handling */
hw_breakpoint_disable(); hw_breakpoint_disable();
...@@ -312,19 +327,14 @@ int hw_breakpoint_handler(struct die_args *args) ...@@ -312,19 +327,14 @@ int hw_breakpoint_handler(struct die_args *args)
goto out; goto out;
} }
/*
* Verify if dar lies within the address range occupied by the symbol
* being watched to filter extraneous exceptions. If it doesn't,
* we still need to single-step the instruction, but we don't
* generate an event.
*/
info->type &= ~HW_BRK_TYPE_EXTRANEOUS_IRQ; info->type &= ~HW_BRK_TYPE_EXTRANEOUS_IRQ;
if (!((bp->attr.bp_addr <= dar) && if (IS_ENABLED(CONFIG_PPC_8xx)) {
(dar - bp->attr.bp_addr < bp->attr.bp_len))) if (!dar_within_range(regs->dar, info))
info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ; info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ;
} else {
if (!IS_ENABLED(CONFIG_PPC_8xx) && !stepping_handler(regs, bp, info->address)) if (!stepping_handler(regs, bp, info))
goto out; goto out;
}
/* /*
* As a policy, the callback is invoked in a 'trigger-after-execute' * As a policy, the callback is invoked in a 'trigger-after-execute'
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment