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: Ravi Bangoria <ravi.bangoria@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20191017093204.7511-5-ravi.bangoria@linux.ibm.com
This commit is contained in:
Ravi Bangoria 2019-10-17 15:02:01 +05:30 committed by Michael Ellerman
parent c3f68b0478
commit 27985b2a64

View File

@ -222,33 +222,49 @@ void thread_change_pc(struct task_struct *tsk, struct pt_regs *regs)
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;
struct instruction_op op;
return ((info->address <= dar) && (dar - info->address < info->len));
}
ret = analyse_instr(&op, regs, instr);
type = GETTYPE(op.type);
return (!ret && (type == LARX || type == STCX));
static bool
dar_range_overlaps(unsigned long dar, int size, struct arch_hw_breakpoint *info)
{
return ((dar <= info->address + info->len - 1) &&
(dar + size - 1 >= info->address));
}
/*
* Handle debug exception notifications.
*/
static bool stepping_handler(struct pt_regs *regs, struct perf_event *bp,
unsigned long addr)
struct arch_hw_breakpoint *info)
{
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))
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."
" Breakpoint at 0x%lx will be disabled.\n", addr);
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 */
if (user_mode(regs)) {
current->thread.last_hit_ubp = bp;
@ -280,7 +296,6 @@ int hw_breakpoint_handler(struct die_args *args)
struct perf_event *bp;
struct pt_regs *regs = args->regs;
struct arch_hw_breakpoint *info;
unsigned long dar = regs->dar;
/* Disable breakpoints during exception handling */
hw_breakpoint_disable();
@ -312,19 +327,14 @@ int hw_breakpoint_handler(struct die_args *args)
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;
if (!((bp->attr.bp_addr <= dar) &&
(dar - bp->attr.bp_addr < bp->attr.bp_len)))
info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ;
if (!IS_ENABLED(CONFIG_PPC_8xx) && !stepping_handler(regs, bp, info->address))
goto out;
if (IS_ENABLED(CONFIG_PPC_8xx)) {
if (!dar_within_range(regs->dar, info))
info->type |= HW_BRK_TYPE_EXTRANEOUS_IRQ;
} else {
if (!stepping_handler(regs, bp, info))
goto out;
}
/*
* As a policy, the callback is invoked in a 'trigger-after-execute'