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