Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8c449753 authored by Frederic Weisbecker's avatar Frederic Weisbecker Committed by Ingo Molnar
Browse files

perf/arch/arm64: Implement hw_breakpoint_arch_parse()



Migrate to the new API in order to remove arch_validate_hwbkpt_settings()
that clumsily mixes up architecture validation and commit.

Signed-off-by: default avatarFrederic Weisbecker <frederic@kernel.org>
Acked-by: default avatarWill Deacon <will.deacon@arm.com>
Acked-by: default avatarMark Rutland <mark.rutland@arm.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Chris Zankel <chris@zankel.net>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Joel Fernandes <joel.opensrc@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Max Filippov <jcmvbkbc@gmail.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Rich Felker <dalias@libc.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Yoshinori Sato <ysato@users.sourceforge.jp>
Link: http://lkml.kernel.org/r/1529981939-8231-7-git-send-email-frederic@kernel.org


Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 9d52718c
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -119,13 +119,17 @@ static inline void decode_ctrl_reg(u32 reg,

struct task_struct;
struct notifier_block;
struct perf_event_attr;
struct perf_event;
struct pmu;

extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
				  int *gen_len, int *gen_type, int *offset);
extern int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw);
extern int arch_validate_hwbkpt_settings(struct perf_event *bp);
extern int hw_breakpoint_arch_parse(struct perf_event *bp,
				    const struct perf_event_attr *attr,
				    struct arch_hw_breakpoint *hw);
#define hw_breakpoint_arch_parse hw_breakpoint_arch_parse
extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
					   unsigned long val, void *data);

+40 −39
Original line number Diff line number Diff line
@@ -420,53 +420,53 @@ int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
/*
 * Construct an arch_hw_breakpoint from a perf_event.
 */
static int arch_build_bp_info(struct perf_event *bp)
static int arch_build_bp_info(struct perf_event *bp,
			      const struct perf_event_attr *attr,
			      struct arch_hw_breakpoint *hw)
{
	struct arch_hw_breakpoint *info = counter_arch_bp(bp);

	/* Type */
	switch (bp->attr.bp_type) {
	switch (attr->bp_type) {
	case HW_BREAKPOINT_X:
		info->ctrl.type = ARM_BREAKPOINT_EXECUTE;
		hw->ctrl.type = ARM_BREAKPOINT_EXECUTE;
		break;
	case HW_BREAKPOINT_R:
		info->ctrl.type = ARM_BREAKPOINT_LOAD;
		hw->ctrl.type = ARM_BREAKPOINT_LOAD;
		break;
	case HW_BREAKPOINT_W:
		info->ctrl.type = ARM_BREAKPOINT_STORE;
		hw->ctrl.type = ARM_BREAKPOINT_STORE;
		break;
	case HW_BREAKPOINT_RW:
		info->ctrl.type = ARM_BREAKPOINT_LOAD | ARM_BREAKPOINT_STORE;
		hw->ctrl.type = ARM_BREAKPOINT_LOAD | ARM_BREAKPOINT_STORE;
		break;
	default:
		return -EINVAL;
	}

	/* Len */
	switch (bp->attr.bp_len) {
	switch (attr->bp_len) {
	case HW_BREAKPOINT_LEN_1:
		info->ctrl.len = ARM_BREAKPOINT_LEN_1;
		hw->ctrl.len = ARM_BREAKPOINT_LEN_1;
		break;
	case HW_BREAKPOINT_LEN_2:
		info->ctrl.len = ARM_BREAKPOINT_LEN_2;
		hw->ctrl.len = ARM_BREAKPOINT_LEN_2;
		break;
	case HW_BREAKPOINT_LEN_3:
		info->ctrl.len = ARM_BREAKPOINT_LEN_3;
		hw->ctrl.len = ARM_BREAKPOINT_LEN_3;
		break;
	case HW_BREAKPOINT_LEN_4:
		info->ctrl.len = ARM_BREAKPOINT_LEN_4;
		hw->ctrl.len = ARM_BREAKPOINT_LEN_4;
		break;
	case HW_BREAKPOINT_LEN_5:
		info->ctrl.len = ARM_BREAKPOINT_LEN_5;
		hw->ctrl.len = ARM_BREAKPOINT_LEN_5;
		break;
	case HW_BREAKPOINT_LEN_6:
		info->ctrl.len = ARM_BREAKPOINT_LEN_6;
		hw->ctrl.len = ARM_BREAKPOINT_LEN_6;
		break;
	case HW_BREAKPOINT_LEN_7:
		info->ctrl.len = ARM_BREAKPOINT_LEN_7;
		hw->ctrl.len = ARM_BREAKPOINT_LEN_7;
		break;
	case HW_BREAKPOINT_LEN_8:
		info->ctrl.len = ARM_BREAKPOINT_LEN_8;
		hw->ctrl.len = ARM_BREAKPOINT_LEN_8;
		break;
	default:
		return -EINVAL;
@@ -477,37 +477,37 @@ static int arch_build_bp_info(struct perf_event *bp)
	 * AArch32 also requires breakpoints of length 2 for Thumb.
	 * Watchpoints can be of length 1, 2, 4 or 8 bytes.
	 */
	if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) {
	if (hw->ctrl.type == ARM_BREAKPOINT_EXECUTE) {
		if (is_compat_bp(bp)) {
			if (info->ctrl.len != ARM_BREAKPOINT_LEN_2 &&
			    info->ctrl.len != ARM_BREAKPOINT_LEN_4)
			if (hw->ctrl.len != ARM_BREAKPOINT_LEN_2 &&
			    hw->ctrl.len != ARM_BREAKPOINT_LEN_4)
				return -EINVAL;
		} else if (info->ctrl.len != ARM_BREAKPOINT_LEN_4) {
		} else if (hw->ctrl.len != ARM_BREAKPOINT_LEN_4) {
			/*
			 * FIXME: Some tools (I'm looking at you perf) assume
			 *	  that breakpoints should be sizeof(long). This
			 *	  is nonsense. For now, we fix up the parameter
			 *	  but we should probably return -EINVAL instead.
			 */
			info->ctrl.len = ARM_BREAKPOINT_LEN_4;
			hw->ctrl.len = ARM_BREAKPOINT_LEN_4;
		}
	}

	/* Address */
	info->address = bp->attr.bp_addr;
	hw->address = attr->bp_addr;

	/*
	 * Privilege
	 * Note that we disallow combined EL0/EL1 breakpoints because
	 * that would complicate the stepping code.
	 */
	if (arch_check_bp_in_kernelspace(info))
		info->ctrl.privilege = AARCH64_BREAKPOINT_EL1;
	if (arch_check_bp_in_kernelspace(hw))
		hw->ctrl.privilege = AARCH64_BREAKPOINT_EL1;
	else
		info->ctrl.privilege = AARCH64_BREAKPOINT_EL0;
		hw->ctrl.privilege = AARCH64_BREAKPOINT_EL0;

	/* Enabled? */
	info->ctrl.enabled = !bp->attr.disabled;
	hw->ctrl.enabled = !attr->disabled;

	return 0;
}
@@ -515,14 +515,15 @@ static int arch_build_bp_info(struct perf_event *bp)
/*
 * Validate the arch-specific HW Breakpoint register settings.
 */
int arch_validate_hwbkpt_settings(struct perf_event *bp)
int hw_breakpoint_arch_parse(struct perf_event *bp,
			     const struct perf_event_attr *attr,
			     struct arch_hw_breakpoint *hw)
{
	struct arch_hw_breakpoint *info = counter_arch_bp(bp);
	int ret;
	u64 alignment_mask, offset;

	/* Build the arch_hw_breakpoint. */
	ret = arch_build_bp_info(bp);
	ret = arch_build_bp_info(bp, attr, hw);
	if (ret)
		return ret;

@@ -536,42 +537,42 @@ int arch_validate_hwbkpt_settings(struct perf_event *bp)
	 * that here.
	 */
	if (is_compat_bp(bp)) {
		if (info->ctrl.len == ARM_BREAKPOINT_LEN_8)
		if (hw->ctrl.len == ARM_BREAKPOINT_LEN_8)
			alignment_mask = 0x7;
		else
			alignment_mask = 0x3;
		offset = info->address & alignment_mask;
		offset = hw->address & alignment_mask;
		switch (offset) {
		case 0:
			/* Aligned */
			break;
		case 1:
			/* Allow single byte watchpoint. */
			if (info->ctrl.len == ARM_BREAKPOINT_LEN_1)
			if (hw->ctrl.len == ARM_BREAKPOINT_LEN_1)
				break;
		case 2:
			/* Allow halfword watchpoints and breakpoints. */
			if (info->ctrl.len == ARM_BREAKPOINT_LEN_2)
			if (hw->ctrl.len == ARM_BREAKPOINT_LEN_2)
				break;
		default:
			return -EINVAL;
		}
	} else {
		if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE)
		if (hw->ctrl.type == ARM_BREAKPOINT_EXECUTE)
			alignment_mask = 0x3;
		else
			alignment_mask = 0x7;
		offset = info->address & alignment_mask;
		offset = hw->address & alignment_mask;
	}

	info->address &= ~alignment_mask;
	info->ctrl.len <<= offset;
	hw->address &= ~alignment_mask;
	hw->ctrl.len <<= offset;

	/*
	 * Disallow per-task kernel breakpoints since these would
	 * complicate the stepping code.
	 */
	if (info->ctrl.privilege == AARCH64_BREAKPOINT_EL1 && bp->hw.target)
	if (hw->ctrl.privilege == AARCH64_BREAKPOINT_EL1 && bp->hw.target)
		return -EINVAL;

	return 0;