Commit | Line | Data |
---|---|---|
903b20ad JH |
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, | |
cfbcf468 | 32 | struct perf_callchain_entry_ctx *entry) |
903b20ad JH |
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 | |
cfbcf468 | 59 | perf_callchain_user(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs) |
903b20ad JH |
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 | ||
3b1fff08 | 68 | while ((entry->nr < entry->max_stack) && frame) |
903b20ad JH |
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 | { | |
cfbcf468 | 81 | struct perf_callchain_entry_ctx *entry = data; |
903b20ad JH |
82 | perf_callchain_store(entry, fr->pc); |
83 | return 0; | |
84 | } | |
85 | ||
86 | void | |
cfbcf468 | 87 | perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, struct pt_regs *regs) |
903b20ad JH |
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 | } |