License cleanup: add SPDX GPL-2.0 license identifier to files with no license
[sfrench/cifs-2.6.git] / arch / metag / kernel / perf_callchain.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Perf callchain handling code.
4  *
5  *   Based on the ARM perf implementation.
6  */
7
8 #include <linux/kernel.h>
9 #include <linux/sched.h>
10 #include <linux/perf_event.h>
11 #include <linux/uaccess.h>
12 #include <asm/ptrace.h>
13 #include <asm/stacktrace.h>
14
15 static bool is_valid_call(unsigned long calladdr)
16 {
17         unsigned int callinsn;
18
19         /* Check the possible return address is aligned. */
20         if (!(calladdr & 0x3)) {
21                 if (!get_user(callinsn, (unsigned int *)calladdr)) {
22                         /* Check for CALLR or SWAP PC,D1RtP. */
23                         if ((callinsn & 0xff000000) == 0xab000000 ||
24                             callinsn == 0xa3200aa0)
25                                 return true;
26                 }
27         }
28         return false;
29 }
30
31 static struct metag_frame __user *
32 user_backtrace(struct metag_frame __user *user_frame,
33                struct perf_callchain_entry_ctx *entry)
34 {
35         struct metag_frame frame;
36         unsigned long calladdr;
37
38         /* We cannot rely on having frame pointers in user code. */
39         while (1) {
40                 /* Also check accessibility of one struct frame beyond */
41                 if (!access_ok(VERIFY_READ, user_frame, sizeof(frame)))
42                         return 0;
43                 if (__copy_from_user_inatomic(&frame, user_frame,
44                                               sizeof(frame)))
45                         return 0;
46
47                 --user_frame;
48
49                 calladdr = frame.lr - 4;
50                 if (is_valid_call(calladdr)) {
51                         perf_callchain_store(entry, calladdr);
52                         return user_frame;
53                 }
54         }
55
56         return 0;
57 }
58
59 void
60 perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs)
61 {
62         unsigned long sp = regs->ctx.AX[0].U0;
63         struct metag_frame __user *frame;
64
65         frame = (struct metag_frame __user *)sp;
66
67         --frame;
68
69         while ((entry->nr < entry->max_stack) && frame)
70                 frame = user_backtrace(frame, entry);
71 }
72
73 /*
74  * Gets called by walk_stackframe() for every stackframe. This will be called
75  * whist unwinding the stackframe and is like a subroutine return so we use
76  * the PC.
77  */
78 static int
79 callchain_trace(struct stackframe *fr,
80                 void *data)
81 {
82         struct perf_callchain_entry_ctx *entry = data;
83         perf_callchain_store(entry, fr->pc);
84         return 0;
85 }
86
87 void
88 perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs)
89 {
90         struct stackframe fr;
91
92         fr.fp = regs->ctx.AX[1].U0;
93         fr.sp = regs->ctx.AX[0].U0;
94         fr.lr = regs->ctx.DX[4].U1;
95         fr.pc = regs->ctx.CurrPC;
96         walk_stackframe(&fr, callchain_trace, entry);
97 }