xref: /openbmc/linux/arch/arm64/kernel/perf_callchain.c (revision 33c222aeda14596ca5b9a1a3002858c6c3565ddd)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
252da443eSMark Rutland /*
352da443eSMark Rutland  * arm64 callchain support
452da443eSMark Rutland  *
552da443eSMark Rutland  * Copyright (C) 2015 ARM Limited
652da443eSMark Rutland  */
752da443eSMark Rutland #include <linux/perf_event.h>
852da443eSMark Rutland #include <linux/uaccess.h>
952da443eSMark Rutland 
10ccc43810SMark Rutland #include <asm/pointer_auth.h>
1152da443eSMark Rutland #include <asm/stacktrace.h>
1252da443eSMark Rutland 
1352da443eSMark Rutland struct frame_tail {
1452da443eSMark Rutland 	struct frame_tail	__user *fp;
1552da443eSMark Rutland 	unsigned long		lr;
1652da443eSMark Rutland } __attribute__((packed));
1752da443eSMark Rutland 
1852da443eSMark Rutland /*
1952da443eSMark Rutland  * Get the return address for a single stackframe and return a pointer to the
2052da443eSMark Rutland  * next frame tail.
2152da443eSMark Rutland  */
2252da443eSMark Rutland static struct frame_tail __user *
2352da443eSMark Rutland user_backtrace(struct frame_tail __user *tail,
24cfbcf468SArnaldo Carvalho de Melo 	       struct perf_callchain_entry_ctx *entry)
2552da443eSMark Rutland {
2652da443eSMark Rutland 	struct frame_tail buftail;
2752da443eSMark Rutland 	unsigned long err;
28ccc43810SMark Rutland 	unsigned long lr;
2952da443eSMark Rutland 
3052da443eSMark Rutland 	/* Also check accessibility of one struct frame_tail beyond */
3196d4f267SLinus Torvalds 	if (!access_ok(tail, sizeof(buftail)))
3252da443eSMark Rutland 		return NULL;
3352da443eSMark Rutland 
3452da443eSMark Rutland 	pagefault_disable();
3552da443eSMark Rutland 	err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
3652da443eSMark Rutland 	pagefault_enable();
3752da443eSMark Rutland 
3852da443eSMark Rutland 	if (err)
3952da443eSMark Rutland 		return NULL;
4052da443eSMark Rutland 
41ccc43810SMark Rutland 	lr = ptrauth_strip_insn_pac(buftail.lr);
42ccc43810SMark Rutland 
43ccc43810SMark Rutland 	perf_callchain_store(entry, lr);
4452da443eSMark Rutland 
4552da443eSMark Rutland 	/*
4652da443eSMark Rutland 	 * Frame pointers should strictly progress back up the stack
4752da443eSMark Rutland 	 * (towards higher addresses).
4852da443eSMark Rutland 	 */
4952da443eSMark Rutland 	if (tail >= buftail.fp)
5052da443eSMark Rutland 		return NULL;
5152da443eSMark Rutland 
5252da443eSMark Rutland 	return buftail.fp;
5352da443eSMark Rutland }
5452da443eSMark Rutland 
5552da443eSMark Rutland #ifdef CONFIG_COMPAT
5652da443eSMark Rutland /*
5752da443eSMark Rutland  * The registers we're interested in are at the end of the variable
5852da443eSMark Rutland  * length saved register structure. The fp points at the end of this
5952da443eSMark Rutland  * structure so the address of this struct is:
6052da443eSMark Rutland  * (struct compat_frame_tail *)(xxx->fp)-1
6152da443eSMark Rutland  *
6252da443eSMark Rutland  * This code has been adapted from the ARM OProfile support.
6352da443eSMark Rutland  */
6452da443eSMark Rutland struct compat_frame_tail {
6552da443eSMark Rutland 	compat_uptr_t	fp; /* a (struct compat_frame_tail *) in compat mode */
6652da443eSMark Rutland 	u32		sp;
6752da443eSMark Rutland 	u32		lr;
6852da443eSMark Rutland } __attribute__((packed));
6952da443eSMark Rutland 
7052da443eSMark Rutland static struct compat_frame_tail __user *
7152da443eSMark Rutland compat_user_backtrace(struct compat_frame_tail __user *tail,
72cfbcf468SArnaldo Carvalho de Melo 		      struct perf_callchain_entry_ctx *entry)
7352da443eSMark Rutland {
7452da443eSMark Rutland 	struct compat_frame_tail buftail;
7552da443eSMark Rutland 	unsigned long err;
7652da443eSMark Rutland 
7752da443eSMark Rutland 	/* Also check accessibility of one struct frame_tail beyond */
7896d4f267SLinus Torvalds 	if (!access_ok(tail, sizeof(buftail)))
7952da443eSMark Rutland 		return NULL;
8052da443eSMark Rutland 
8152da443eSMark Rutland 	pagefault_disable();
8252da443eSMark Rutland 	err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
8352da443eSMark Rutland 	pagefault_enable();
8452da443eSMark Rutland 
8552da443eSMark Rutland 	if (err)
8652da443eSMark Rutland 		return NULL;
8752da443eSMark Rutland 
8852da443eSMark Rutland 	perf_callchain_store(entry, buftail.lr);
8952da443eSMark Rutland 
9052da443eSMark Rutland 	/*
9152da443eSMark Rutland 	 * Frame pointers should strictly progress back up the stack
9252da443eSMark Rutland 	 * (towards higher addresses).
9352da443eSMark Rutland 	 */
9452da443eSMark Rutland 	if (tail + 1 >= (struct compat_frame_tail __user *)
9552da443eSMark Rutland 			compat_ptr(buftail.fp))
9652da443eSMark Rutland 		return NULL;
9752da443eSMark Rutland 
9852da443eSMark Rutland 	return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1;
9952da443eSMark Rutland }
10052da443eSMark Rutland #endif /* CONFIG_COMPAT */
10152da443eSMark Rutland 
102cfbcf468SArnaldo Carvalho de Melo void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
10352da443eSMark Rutland 			 struct pt_regs *regs)
10452da443eSMark Rutland {
10552da443eSMark Rutland 	if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
10652da443eSMark Rutland 		/* We don't support guest os callchain now */
10752da443eSMark Rutland 		return;
10852da443eSMark Rutland 	}
10952da443eSMark Rutland 
11052da443eSMark Rutland 	perf_callchain_store(entry, regs->pc);
11152da443eSMark Rutland 
11252da443eSMark Rutland 	if (!compat_user_mode(regs)) {
11352da443eSMark Rutland 		/* AARCH64 mode */
11452da443eSMark Rutland 		struct frame_tail __user *tail;
11552da443eSMark Rutland 
11652da443eSMark Rutland 		tail = (struct frame_tail __user *)regs->regs[29];
11752da443eSMark Rutland 
1183b1fff08SArnaldo Carvalho de Melo 		while (entry->nr < entry->max_stack &&
119*33c222aeSPeter Collingbourne 		       tail && !((unsigned long)tail & 0x7))
12052da443eSMark Rutland 			tail = user_backtrace(tail, entry);
12152da443eSMark Rutland 	} else {
12252da443eSMark Rutland #ifdef CONFIG_COMPAT
12352da443eSMark Rutland 		/* AARCH32 compat mode */
12452da443eSMark Rutland 		struct compat_frame_tail __user *tail;
12552da443eSMark Rutland 
12652da443eSMark Rutland 		tail = (struct compat_frame_tail __user *)regs->compat_fp - 1;
12752da443eSMark Rutland 
1283b1fff08SArnaldo Carvalho de Melo 		while ((entry->nr < entry->max_stack) &&
12952da443eSMark Rutland 			tail && !((unsigned long)tail & 0x3))
13052da443eSMark Rutland 			tail = compat_user_backtrace(tail, entry);
13152da443eSMark Rutland #endif
13252da443eSMark Rutland 	}
13352da443eSMark Rutland }
13452da443eSMark Rutland 
13552da443eSMark Rutland /*
13652da443eSMark Rutland  * Gets called by walk_stackframe() for every stackframe. This will be called
13752da443eSMark Rutland  * whist unwinding the stackframe and is like a subroutine return so we use
13852da443eSMark Rutland  * the PC.
13952da443eSMark Rutland  */
140baa2cd41SMark Brown static bool callchain_trace(void *data, unsigned long pc)
14152da443eSMark Rutland {
142cfbcf468SArnaldo Carvalho de Melo 	struct perf_callchain_entry_ctx *entry = data;
143baa2cd41SMark Brown 	perf_callchain_store(entry, pc);
144baa2cd41SMark Brown 	return true;
14552da443eSMark Rutland }
14652da443eSMark Rutland 
147cfbcf468SArnaldo Carvalho de Melo void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
14852da443eSMark Rutland 			   struct pt_regs *regs)
14952da443eSMark Rutland {
15052da443eSMark Rutland 	struct stackframe frame;
15152da443eSMark Rutland 
15252da443eSMark Rutland 	if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
15352da443eSMark Rutland 		/* We don't support guest os callchain now */
15452da443eSMark Rutland 		return;
15552da443eSMark Rutland 	}
15652da443eSMark Rutland 
157f3dcbe67SDave Martin 	start_backtrace(&frame, regs->regs[29], regs->pc);
158fe13f95bSAKASHI Takahiro 	walk_stackframe(current, &frame, callchain_trace, entry);
15952da443eSMark Rutland }
16052da443eSMark Rutland 
16152da443eSMark Rutland unsigned long perf_instruction_pointer(struct pt_regs *regs)
16252da443eSMark Rutland {
16352da443eSMark Rutland 	if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
16452da443eSMark Rutland 		return perf_guest_cbs->get_guest_ip();
16552da443eSMark Rutland 
16652da443eSMark Rutland 	return instruction_pointer(regs);
16752da443eSMark Rutland }
16852da443eSMark Rutland 
16952da443eSMark Rutland unsigned long perf_misc_flags(struct pt_regs *regs)
17052da443eSMark Rutland {
17152da443eSMark Rutland 	int misc = 0;
17252da443eSMark Rutland 
17352da443eSMark Rutland 	if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
17452da443eSMark Rutland 		if (perf_guest_cbs->is_user_mode())
17552da443eSMark Rutland 			misc |= PERF_RECORD_MISC_GUEST_USER;
17652da443eSMark Rutland 		else
17752da443eSMark Rutland 			misc |= PERF_RECORD_MISC_GUEST_KERNEL;
17852da443eSMark Rutland 	} else {
17952da443eSMark Rutland 		if (user_mode(regs))
18052da443eSMark Rutland 			misc |= PERF_RECORD_MISC_USER;
18152da443eSMark Rutland 		else
18252da443eSMark Rutland 			misc |= PERF_RECORD_MISC_KERNEL;
18352da443eSMark Rutland 	}
18452da443eSMark Rutland 
18552da443eSMark Rutland 	return misc;
18652da443eSMark Rutland }
187