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>
8ed876d35SMadhavan T. Venkataraman #include <linux/stacktrace.h>
952da443eSMark Rutland #include <linux/uaccess.h>
1052da443eSMark Rutland
11ccc43810SMark Rutland #include <asm/pointer_auth.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 *
user_backtrace(struct frame_tail __user * tail,struct perf_callchain_entry_ctx * entry)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
41*ca708599SMark Rutland lr = ptrauth_strip_user_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 *
compat_user_backtrace(struct compat_frame_tail __user * tail,struct perf_callchain_entry_ctx * entry)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
perf_callchain_user(struct perf_callchain_entry_ctx * entry,struct pt_regs * regs)102cfbcf468SArnaldo Carvalho de Melo void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
10352da443eSMark Rutland struct pt_regs *regs)
10452da443eSMark Rutland {
1051c343051SSean Christopherson if (perf_guest_state()) {
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 &&
11933c222aeSPeter 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
callchain_trace(void * data,unsigned long pc)135baa2cd41SMark Brown static bool callchain_trace(void *data, unsigned long pc)
13652da443eSMark Rutland {
137cfbcf468SArnaldo Carvalho de Melo struct perf_callchain_entry_ctx *entry = data;
138ed876d35SMadhavan T. Venkataraman return perf_callchain_store(entry, pc) == 0;
13952da443eSMark Rutland }
14052da443eSMark Rutland
perf_callchain_kernel(struct perf_callchain_entry_ctx * entry,struct pt_regs * regs)141cfbcf468SArnaldo Carvalho de Melo void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
14252da443eSMark Rutland struct pt_regs *regs)
14352da443eSMark Rutland {
1441c343051SSean Christopherson if (perf_guest_state()) {
14552da443eSMark Rutland /* We don't support guest os callchain now */
14652da443eSMark Rutland return;
14752da443eSMark Rutland }
14852da443eSMark Rutland
149ed876d35SMadhavan T. Venkataraman arch_stack_walk(callchain_trace, entry, current, regs);
15052da443eSMark Rutland }
15152da443eSMark Rutland
perf_instruction_pointer(struct pt_regs * regs)15252da443eSMark Rutland unsigned long perf_instruction_pointer(struct pt_regs *regs)
15352da443eSMark Rutland {
1541c343051SSean Christopherson if (perf_guest_state())
1551c343051SSean Christopherson return perf_guest_get_ip();
15652da443eSMark Rutland
15752da443eSMark Rutland return instruction_pointer(regs);
15852da443eSMark Rutland }
15952da443eSMark Rutland
perf_misc_flags(struct pt_regs * regs)16052da443eSMark Rutland unsigned long perf_misc_flags(struct pt_regs *regs)
16152da443eSMark Rutland {
1621c343051SSean Christopherson unsigned int guest_state = perf_guest_state();
16352da443eSMark Rutland int misc = 0;
16452da443eSMark Rutland
165b9f5621cSLike Xu if (guest_state) {
166b9f5621cSLike Xu if (guest_state & PERF_GUEST_USER)
16752da443eSMark Rutland misc |= PERF_RECORD_MISC_GUEST_USER;
16852da443eSMark Rutland else
16952da443eSMark Rutland misc |= PERF_RECORD_MISC_GUEST_KERNEL;
17052da443eSMark Rutland } else {
17152da443eSMark Rutland if (user_mode(regs))
17252da443eSMark Rutland misc |= PERF_RECORD_MISC_USER;
17352da443eSMark Rutland else
17452da443eSMark Rutland misc |= PERF_RECORD_MISC_KERNEL;
17552da443eSMark Rutland }
17652da443eSMark Rutland
17752da443eSMark Rutland return misc;
17852da443eSMark Rutland }
179