]>
Commit | Line | Data |
---|---|---|
52da443e MR |
1 | /* |
2 | * arm64 callchain support | |
3 | * | |
4 | * Copyright (C) 2015 ARM Limited | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | #include <linux/perf_event.h> | |
19 | #include <linux/uaccess.h> | |
20 | ||
ccc43810 | 21 | #include <asm/pointer_auth.h> |
52da443e MR |
22 | #include <asm/stacktrace.h> |
23 | ||
24 | struct frame_tail { | |
25 | struct frame_tail __user *fp; | |
26 | unsigned long lr; | |
27 | } __attribute__((packed)); | |
28 | ||
29 | /* | |
30 | * Get the return address for a single stackframe and return a pointer to the | |
31 | * next frame tail. | |
32 | */ | |
33 | static struct frame_tail __user * | |
34 | user_backtrace(struct frame_tail __user *tail, | |
cfbcf468 | 35 | struct perf_callchain_entry_ctx *entry) |
52da443e MR |
36 | { |
37 | struct frame_tail buftail; | |
38 | unsigned long err; | |
ccc43810 | 39 | unsigned long lr; |
52da443e MR |
40 | |
41 | /* Also check accessibility of one struct frame_tail beyond */ | |
96d4f267 | 42 | if (!access_ok(tail, sizeof(buftail))) |
52da443e MR |
43 | return NULL; |
44 | ||
45 | pagefault_disable(); | |
46 | err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); | |
47 | pagefault_enable(); | |
48 | ||
49 | if (err) | |
50 | return NULL; | |
51 | ||
ccc43810 MR |
52 | lr = ptrauth_strip_insn_pac(buftail.lr); |
53 | ||
54 | perf_callchain_store(entry, lr); | |
52da443e MR |
55 | |
56 | /* | |
57 | * Frame pointers should strictly progress back up the stack | |
58 | * (towards higher addresses). | |
59 | */ | |
60 | if (tail >= buftail.fp) | |
61 | return NULL; | |
62 | ||
63 | return buftail.fp; | |
64 | } | |
65 | ||
66 | #ifdef CONFIG_COMPAT | |
67 | /* | |
68 | * The registers we're interested in are at the end of the variable | |
69 | * length saved register structure. The fp points at the end of this | |
70 | * structure so the address of this struct is: | |
71 | * (struct compat_frame_tail *)(xxx->fp)-1 | |
72 | * | |
73 | * This code has been adapted from the ARM OProfile support. | |
74 | */ | |
75 | struct compat_frame_tail { | |
76 | compat_uptr_t fp; /* a (struct compat_frame_tail *) in compat mode */ | |
77 | u32 sp; | |
78 | u32 lr; | |
79 | } __attribute__((packed)); | |
80 | ||
81 | static struct compat_frame_tail __user * | |
82 | compat_user_backtrace(struct compat_frame_tail __user *tail, | |
cfbcf468 | 83 | struct perf_callchain_entry_ctx *entry) |
52da443e MR |
84 | { |
85 | struct compat_frame_tail buftail; | |
86 | unsigned long err; | |
87 | ||
88 | /* Also check accessibility of one struct frame_tail beyond */ | |
96d4f267 | 89 | if (!access_ok(tail, sizeof(buftail))) |
52da443e MR |
90 | return NULL; |
91 | ||
92 | pagefault_disable(); | |
93 | err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); | |
94 | pagefault_enable(); | |
95 | ||
96 | if (err) | |
97 | return NULL; | |
98 | ||
99 | perf_callchain_store(entry, buftail.lr); | |
100 | ||
101 | /* | |
102 | * Frame pointers should strictly progress back up the stack | |
103 | * (towards higher addresses). | |
104 | */ | |
105 | if (tail + 1 >= (struct compat_frame_tail __user *) | |
106 | compat_ptr(buftail.fp)) | |
107 | return NULL; | |
108 | ||
109 | return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1; | |
110 | } | |
111 | #endif /* CONFIG_COMPAT */ | |
112 | ||
cfbcf468 | 113 | void perf_callchain_user(struct perf_callchain_entry_ctx *entry, |
52da443e MR |
114 | struct pt_regs *regs) |
115 | { | |
116 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | |
117 | /* We don't support guest os callchain now */ | |
118 | return; | |
119 | } | |
120 | ||
121 | perf_callchain_store(entry, regs->pc); | |
122 | ||
123 | if (!compat_user_mode(regs)) { | |
124 | /* AARCH64 mode */ | |
125 | struct frame_tail __user *tail; | |
126 | ||
127 | tail = (struct frame_tail __user *)regs->regs[29]; | |
128 | ||
3b1fff08 | 129 | while (entry->nr < entry->max_stack && |
52da443e MR |
130 | tail && !((unsigned long)tail & 0xf)) |
131 | tail = user_backtrace(tail, entry); | |
132 | } else { | |
133 | #ifdef CONFIG_COMPAT | |
134 | /* AARCH32 compat mode */ | |
135 | struct compat_frame_tail __user *tail; | |
136 | ||
137 | tail = (struct compat_frame_tail __user *)regs->compat_fp - 1; | |
138 | ||
3b1fff08 | 139 | while ((entry->nr < entry->max_stack) && |
52da443e MR |
140 | tail && !((unsigned long)tail & 0x3)) |
141 | tail = compat_user_backtrace(tail, entry); | |
142 | #endif | |
143 | } | |
144 | } | |
145 | ||
146 | /* | |
147 | * Gets called by walk_stackframe() for every stackframe. This will be called | |
148 | * whist unwinding the stackframe and is like a subroutine return so we use | |
149 | * the PC. | |
150 | */ | |
151 | static int callchain_trace(struct stackframe *frame, void *data) | |
152 | { | |
cfbcf468 | 153 | struct perf_callchain_entry_ctx *entry = data; |
52da443e MR |
154 | perf_callchain_store(entry, frame->pc); |
155 | return 0; | |
156 | } | |
157 | ||
cfbcf468 | 158 | void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, |
52da443e MR |
159 | struct pt_regs *regs) |
160 | { | |
161 | struct stackframe frame; | |
162 | ||
163 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | |
164 | /* We don't support guest os callchain now */ | |
165 | return; | |
166 | } | |
167 | ||
168 | frame.fp = regs->regs[29]; | |
52da443e | 169 | frame.pc = regs->pc; |
20380bb3 | 170 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
a448276c | 171 | frame.graph = 0; |
20380bb3 | 172 | #endif |
52da443e | 173 | |
fe13f95b | 174 | walk_stackframe(current, &frame, callchain_trace, entry); |
52da443e MR |
175 | } |
176 | ||
177 | unsigned long perf_instruction_pointer(struct pt_regs *regs) | |
178 | { | |
179 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) | |
180 | return perf_guest_cbs->get_guest_ip(); | |
181 | ||
182 | return instruction_pointer(regs); | |
183 | } | |
184 | ||
185 | unsigned long perf_misc_flags(struct pt_regs *regs) | |
186 | { | |
187 | int misc = 0; | |
188 | ||
189 | if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { | |
190 | if (perf_guest_cbs->is_user_mode()) | |
191 | misc |= PERF_RECORD_MISC_GUEST_USER; | |
192 | else | |
193 | misc |= PERF_RECORD_MISC_GUEST_KERNEL; | |
194 | } else { | |
195 | if (user_mode(regs)) | |
196 | misc |= PERF_RECORD_MISC_USER; | |
197 | else | |
198 | misc |= PERF_RECORD_MISC_KERNEL; | |
199 | } | |
200 | ||
201 | return misc; | |
202 | } |