]> git.ipfire.org Git - people/arne_f/kernel.git/blob - arch/metag/kernel/perf_callchain.c
License cleanup: add SPDX GPL-2.0 license identifier to files with no license
[people/arne_f/kernel.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 }