Implement the perf register sampling interface for Alpha. This enables perf to capture register state and user stack dumps with samples, supporting --call-graph dwarf.
The perf_regs enum exposes the registers available in pt_regs: r0-r8, r16-r28, gp, pc, and ps. Registers r9-r15 are callee-saved and live in switch_stack, not pt_regs, so they are not included. A switch statement maps perf register indices to pt_regs fields since Alpha's pt_regs layout is non-contiguous (unlike architectures where direct array indexing works). Assisted-by: Claude:claude-opus-4-6 Signed-off-by: Matt Turner <[email protected]> --- arch/alpha/Kconfig | 2 + arch/alpha/include/asm/perf_regs.h | 7 +++ arch/alpha/include/uapi/asm/perf_regs.h | 34 +++++++++++++ arch/alpha/kernel/Makefile | 1 + arch/alpha/kernel/perf_regs.c | 86 +++++++++++++++++++++++++++++++++ 5 files changed, 130 insertions(+) diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index e5611766b6d6..96b38d320eb8 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -33,6 +33,8 @@ config ALPHA select HAVE_PAGE_SIZE_8KB select HAVE_PCSPKR_PLATFORM select HAVE_PERF_EVENTS + select HAVE_PERF_REGS + select HAVE_PERF_USER_STACK_DUMP select NEED_DMA_MAP_STATE select NEED_SG_DMA_LENGTH select GENERIC_IRQ_PROBE diff --git a/arch/alpha/include/asm/perf_regs.h b/arch/alpha/include/asm/perf_regs.h new file mode 100644 index 000000000000..6045da1a2a4f --- /dev/null +++ b/arch/alpha/include/asm/perf_regs.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_ALPHA_PERF_REGS_H +#define _ASM_ALPHA_PERF_REGS_H + +#include <uapi/asm/perf_regs.h> + +#endif /* _ASM_ALPHA_PERF_REGS_H */ diff --git a/arch/alpha/include/uapi/asm/perf_regs.h b/arch/alpha/include/uapi/asm/perf_regs.h new file mode 100644 index 000000000000..afeb6a1584cd --- /dev/null +++ b/arch/alpha/include/uapi/asm/perf_regs.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_ALPHA_PERF_REGS_H +#define _UAPI_ASM_ALPHA_PERF_REGS_H + +enum perf_event_alpha_regs { + PERF_REG_ALPHA_R0, + PERF_REG_ALPHA_R1, + PERF_REG_ALPHA_R2, + PERF_REG_ALPHA_R3, + PERF_REG_ALPHA_R4, + PERF_REG_ALPHA_R5, + PERF_REG_ALPHA_R6, + PERF_REG_ALPHA_R7, + PERF_REG_ALPHA_R8, + PERF_REG_ALPHA_R16, + PERF_REG_ALPHA_R17, + PERF_REG_ALPHA_R18, + PERF_REG_ALPHA_R19, + PERF_REG_ALPHA_R20, + PERF_REG_ALPHA_R21, + PERF_REG_ALPHA_R22, + PERF_REG_ALPHA_R23, + PERF_REG_ALPHA_R24, + PERF_REG_ALPHA_R25, + PERF_REG_ALPHA_R26, + PERF_REG_ALPHA_R27, + PERF_REG_ALPHA_R28, + PERF_REG_ALPHA_GP, + PERF_REG_ALPHA_PC, + PERF_REG_ALPHA_PS, + PERF_REG_ALPHA_MAX, +}; + +#endif /* _UAPI_ASM_ALPHA_PERF_REGS_H */ diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile index 4ea5c189e60e..0c04b89676ed 100644 --- a/arch/alpha/kernel/Makefile +++ b/arch/alpha/kernel/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_PCI) += pci.o pci_iommu.o pci-sysfs.o obj-$(CONFIG_SRM_ENV) += srm_env.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o +obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o obj-$(CONFIG_RTC_DRV_ALPHA) += rtc.o obj-$(CONFIG_AUDIT) += audit.o diff --git a/arch/alpha/kernel/perf_regs.c b/arch/alpha/kernel/perf_regs.c new file mode 100644 index 000000000000..e15081ac95e0 --- /dev/null +++ b/arch/alpha/kernel/perf_regs.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/perf_event.h> +#include <asm/perf_regs.h> +#include <asm/ptrace.h> + +u64 perf_reg_value(struct pt_regs *regs, int idx) +{ + switch (idx) { + case PERF_REG_ALPHA_R0: + return regs->r0; + case PERF_REG_ALPHA_R1: + return regs->r1; + case PERF_REG_ALPHA_R2: + return regs->r2; + case PERF_REG_ALPHA_R3: + return regs->r3; + case PERF_REG_ALPHA_R4: + return regs->r4; + case PERF_REG_ALPHA_R5: + return regs->r5; + case PERF_REG_ALPHA_R6: + return regs->r6; + case PERF_REG_ALPHA_R7: + return regs->r7; + case PERF_REG_ALPHA_R8: + return regs->r8; + case PERF_REG_ALPHA_R16: + return regs->r16; + case PERF_REG_ALPHA_R17: + return regs->r17; + case PERF_REG_ALPHA_R18: + return regs->r18; + case PERF_REG_ALPHA_R19: + return regs->r19; + case PERF_REG_ALPHA_R20: + return regs->r20; + case PERF_REG_ALPHA_R21: + return regs->r21; + case PERF_REG_ALPHA_R22: + return regs->r22; + case PERF_REG_ALPHA_R23: + return regs->r23; + case PERF_REG_ALPHA_R24: + return regs->r24; + case PERF_REG_ALPHA_R25: + return regs->r25; + case PERF_REG_ALPHA_R26: + return regs->r26; + case PERF_REG_ALPHA_R27: + return regs->r27; + case PERF_REG_ALPHA_R28: + return regs->r28; + case PERF_REG_ALPHA_GP: + return regs->gp; + case PERF_REG_ALPHA_PC: + return regs->pc; + case PERF_REG_ALPHA_PS: + return regs->ps; + default: + WARN_ON_ONCE(1); + return 0; + } +} + +#define REG_RESERVED (~((1ULL << PERF_REG_ALPHA_MAX) - 1)) + +int perf_reg_validate(u64 mask) +{ + if (!mask || mask & REG_RESERVED) + return -EINVAL; + + return 0; +} + +u64 perf_reg_abi(struct task_struct *task) +{ + return PERF_SAMPLE_REGS_ABI_64; +} + +void perf_get_regs_user(struct perf_regs *regs_user, + struct pt_regs *regs) +{ + regs_user->regs = task_pt_regs(current); + regs_user->abi = perf_reg_abi(current); +} -- 2.54.0

