diff options
Diffstat (limited to 'arch/arm/kernel/hw_breakpoint.c')
| -rw-r--r-- | arch/arm/kernel/hw_breakpoint.c | 43 | 
1 files changed, 39 insertions, 4 deletions
diff --git a/arch/arm/kernel/hw_breakpoint.c b/arch/arm/kernel/hw_breakpoint.c index dc0fb7a81371..a12efd0f43e8 100644 --- a/arch/arm/kernel/hw_breakpoint.c +++ b/arch/arm/kernel/hw_breakpoint.c @@ -17,6 +17,7 @@  #include <linux/perf_event.h>  #include <linux/hw_breakpoint.h>  #include <linux/smp.h> +#include <linux/cfi.h>  #include <linux/cpu_pm.h>  #include <linux/coresight.h> @@ -626,7 +627,7 @@ int hw_breakpoint_arch_parse(struct perf_event *bp,  	hw->address &= ~alignment_mask;  	hw->ctrl.len <<= offset; -	if (uses_default_overflow_handler(bp)) { +	if (is_default_overflow_handler(bp)) {  		/*  		 * Mismatch breakpoints are required for single-stepping  		 * breakpoints. @@ -798,7 +799,7 @@ static void watchpoint_handler(unsigned long addr, unsigned int fsr,  		 * Otherwise, insert a temporary mismatch breakpoint so that  		 * we can single-step over the watchpoint trigger.  		 */ -		if (!uses_default_overflow_handler(wp)) +		if (!is_default_overflow_handler(wp))  			continue;  step:  		enable_single_step(wp, instruction_pointer(regs)); @@ -811,7 +812,7 @@ step:  		info->trigger = addr;  		pr_debug("watchpoint fired: address = 0x%x\n", info->trigger);  		perf_bp_event(wp, regs); -		if (uses_default_overflow_handler(wp)) +		if (is_default_overflow_handler(wp))  			enable_single_step(wp, instruction_pointer(regs));  	} @@ -886,7 +887,7 @@ static void breakpoint_handler(unsigned long unknown, struct pt_regs *regs)  			info->trigger = addr;  			pr_debug("breakpoint fired: address = 0x%x\n", addr);  			perf_bp_event(bp, regs); -			if (uses_default_overflow_handler(bp)) +			if (is_default_overflow_handler(bp))  				enable_single_step(bp, addr);  			goto unlock;  		} @@ -903,6 +904,37 @@ unlock:  	watchpoint_single_step_handler(addr);  } +#ifdef CONFIG_CFI_CLANG +static void hw_breakpoint_cfi_handler(struct pt_regs *regs) +{ +	/* +	 * TODO: implementing target and type to pass to CFI using the more +	 * elaborate report_cfi_failure() requires compiler work. To be able +	 * to properly extract target information the compiler needs to +	 * emit a stable instructions sequence for the CFI checks so we can +	 * decode the instructions preceding the trap and figure out which +	 * registers were used. +	 */ + +	switch (report_cfi_failure_noaddr(regs, instruction_pointer(regs))) { +	case BUG_TRAP_TYPE_BUG: +		die("Oops - CFI", regs, 0); +		break; +	case BUG_TRAP_TYPE_WARN: +		/* Skip the breaking instruction */ +		instruction_pointer(regs) += 4; +		break; +	default: +		die("Unknown CFI error", regs, 0); +		break; +	} +} +#else +static void hw_breakpoint_cfi_handler(struct pt_regs *regs) +{ +} +#endif +  /*   * Called from either the Data Abort Handler [watchpoint] or the   * Prefetch Abort Handler [breakpoint] with interrupts disabled. @@ -932,6 +964,9 @@ static int hw_breakpoint_pending(unsigned long addr, unsigned int fsr,  	case ARM_ENTRY_SYNC_WATCHPOINT:  		watchpoint_handler(addr, fsr, regs);  		break; +	case ARM_ENTRY_CFI_BREAKPOINT: +		hw_breakpoint_cfi_handler(regs); +		break;  	default:  		ret = 1; /* Unhandled fault. */  	}  |