misc: tegra-profiler: support eh_frame sections
[linux-3.10.git] / drivers / misc / tegra-profiler / backtrace.c
1 /*
2  * drivers/misc/tegra-profiler/backtrace.c
3  *
4  * Copyright (c) 2015, NVIDIA CORPORATION.  All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  */
16
17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18
19 #include <linux/uaccess.h>
20 #include <linux/sched.h>
21 #include <linux/mm.h>
22
23 #include <linux/tegra_profiler.h>
24
25 #include "quadd.h"
26 #include "backtrace.h"
27 #include "eh_unwind.h"
28 #include "dwarf_unwind.h"
29 #include "hrt.h"
30
31 static inline int
32 is_thumb_mode(struct pt_regs *regs)
33 {
34 #ifdef CONFIG_ARM64
35         return compat_thumb_mode(regs);
36 #else
37         return thumb_mode(regs);
38 #endif
39 }
40
41 unsigned long
42 quadd_user_stack_pointer(struct pt_regs *regs)
43 {
44 #ifdef CONFIG_ARM64
45         if (compat_user_mode(regs))
46                 return regs->compat_sp;
47 #endif
48
49         return user_stack_pointer(regs);
50 }
51
52 unsigned long
53 quadd_get_user_frame_pointer(struct pt_regs *regs)
54 {
55         unsigned long fp;
56
57 #ifdef CONFIG_ARM64
58         if (compat_user_mode(regs))
59                 fp = is_thumb_mode(regs) ?
60                         regs->compat_usr(7) : regs->compat_usr(11);
61         else
62                 fp = regs->regs[29];
63 #else
64         fp = is_thumb_mode(regs) ? regs->ARM_r7 : regs->ARM_fp;
65 #endif
66         return fp;
67 }
68
69 unsigned long
70 quadd_user_link_register(struct pt_regs *regs)
71 {
72 #ifdef CONFIG_ARM64
73         return compat_user_mode(regs) ?
74                 regs->compat_lr : regs->regs[30];
75 #else
76         return regs->ARM_lr;
77 #endif
78 }
79
80 static inline void
81 put_unw_type(u32 *p, int bt_idx, unsigned int type)
82 {
83         int word_idx, shift;
84
85         word_idx = bt_idx / 8;
86         shift = (bt_idx % 8) * 4;
87
88         *(p + word_idx) &= ~(0x0f << shift);
89         *(p + word_idx) |= (type & 0x0f) << shift;
90 }
91
92 int
93 quadd_callchain_store(struct quadd_callchain *cc,
94                       unsigned long ip, unsigned int type)
95 {
96         unsigned long low_addr = cc->hrt->low_addr;
97
98         if (ip < low_addr || !validate_pc_addr(ip, sizeof(unsigned long))) {
99                 cc->unw_rc = QUADD_URC_PC_INCORRECT;
100                 return 0;
101         }
102
103         if (cc->nr >= QUADD_MAX_STACK_DEPTH) {
104                 cc->unw_rc = QUADD_URC_LEVEL_TOO_DEEP;
105                 return 0;
106         }
107
108         put_unw_type(cc->types, cc->nr, type);
109
110         if (cc->cs_64)
111                 cc->ip_64[cc->nr++] = ip;
112         else
113                 cc->ip_32[cc->nr++] = ip;
114
115         return 1;
116 }
117
118 static int
119 is_ex_entry_exist(struct pt_regs *regs,
120                   unsigned long addr,
121                   struct task_struct *task)
122 {
123         return quadd_is_ex_entry_exist_dwarf(regs, addr, task) ||
124                quadd_is_ex_entry_exist_arm32_ehabi(regs, addr, task);
125 }
126
127 static unsigned long __user *
128 user_backtrace(struct pt_regs *regs,
129                unsigned long __user *tail,
130                struct quadd_callchain *cc,
131                struct vm_area_struct *stack_vma,
132                struct task_struct *task)
133 {
134         int nr_added;
135         unsigned long value, value_lr = 0, value_fp = 0;
136         unsigned long __user *fp_prev = NULL;
137
138         if (!is_vma_addr((unsigned long)tail, stack_vma, sizeof(*tail)))
139                 return NULL;
140
141         if (__copy_from_user_inatomic(&value, tail, sizeof(unsigned long))) {
142                 cc->unw_rc = QUADD_URC_EACCESS;
143                 return NULL;
144         }
145
146         if (is_vma_addr(value, stack_vma, sizeof(value))) {
147                 /* gcc thumb/clang frame */
148                 value_fp = value;
149
150                 if (!is_vma_addr((unsigned long)(tail + 1), stack_vma,
151                     sizeof(*tail)))
152                         return NULL;
153
154                 if (__copy_from_user_inatomic(&value_lr, tail + 1,
155                                               sizeof(value_lr))) {
156                         cc->unw_rc = QUADD_URC_EACCESS;
157                         return NULL;
158                 }
159
160                 cc->curr_fp = value_fp;
161                 cc->curr_sp = (unsigned long)tail + sizeof(value_fp) * 2;
162                 cc->curr_pc = value_lr;
163         } else {
164                 /* gcc arm frame */
165                 if (__copy_from_user_inatomic(&value_fp, tail - 1,
166                                               sizeof(value_fp))) {
167                         cc->unw_rc = QUADD_URC_EACCESS;
168                         return NULL;
169                 }
170
171                 cc->curr_fp = value_fp;
172                 cc->curr_sp = (unsigned long)tail + sizeof(value_fp);
173                 cc->curr_pc = value_lr = value;
174
175                 if (!is_vma_addr(value_fp, stack_vma, sizeof(value_fp)))
176                         return NULL;
177         }
178
179         fp_prev = (unsigned long __user *)value_fp;
180
181         nr_added = quadd_callchain_store(cc, value_lr, QUADD_UNW_TYPE_FP);
182         if (nr_added == 0)
183                 return NULL;
184
185         if (cc->unw_method == QUADD_UNW_METHOD_MIXED &&
186             is_ex_entry_exist(regs, value_lr, task))
187                 return NULL;
188
189         if (fp_prev <= tail)
190                 return NULL;
191
192         return fp_prev;
193 }
194
195 static unsigned int
196 get_user_callchain_fp(struct pt_regs *regs,
197                       struct quadd_callchain *cc,
198                       struct task_struct *task)
199 {
200         unsigned long fp, sp, pc, reg;
201         struct vm_area_struct *vma, *vma_pc = NULL;
202         unsigned long __user *tail = NULL;
203         struct mm_struct *mm = task->mm;
204
205         cc->nr = 0;
206         cc->unw_rc = QUADD_URC_FP_INCORRECT;
207
208         if (!regs || !mm) {
209                 cc->unw_rc = QUADD_URC_FAILURE;
210                 return 0;
211         }
212
213         sp = quadd_user_stack_pointer(regs);
214         pc = instruction_pointer(regs);
215         fp = quadd_get_user_frame_pointer(regs);
216
217         if (fp == 0 || fp < sp || fp & 0x3)
218                 return 0;
219
220         vma = find_vma(mm, sp);
221         if (!vma) {
222                 cc->unw_rc = QUADD_URC_SP_INCORRECT;
223                 return 0;
224         }
225
226         if (!is_vma_addr(fp, vma, sizeof(fp)))
227                 return 0;
228
229         if (probe_kernel_address(fp, reg)) {
230                 pr_warn_once("%s: failed for address: %#lx\n", __func__, fp);
231                 cc->unw_rc = QUADD_URC_EACCESS;
232                 return 0;
233         }
234
235         pr_debug("sp/fp: %#lx/%#lx, pc/lr: %#lx/%#lx, *fp: %#lx, stack: %#lx-%#lx\n",
236                  sp, fp, pc, quadd_user_link_register(regs), reg,
237                  vma->vm_start, vma->vm_end);
238
239         if (is_thumb_mode(regs)) {
240                 if (reg <= fp || !is_vma_addr(reg, vma, sizeof(reg)))
241                         return 0;
242         } else if (reg > fp && is_vma_addr(reg, vma, sizeof(reg))) {
243                 /* fp --> fp prev */
244                 unsigned long value;
245                 int read_lr = 0;
246
247                 if (is_vma_addr(fp + sizeof(unsigned long), vma, sizeof(fp))) {
248                         if (__copy_from_user_inatomic(
249                                         &value,
250                                         (unsigned long __user *)fp + 1,
251                                         sizeof(unsigned long))) {
252                                 cc->unw_rc = QUADD_URC_EACCESS;
253                                 return 0;
254                         }
255
256                         vma_pc = find_vma(mm, pc);
257                         read_lr = 1;
258                 }
259
260                 if (!read_lr || !is_vma_addr(value, vma_pc, sizeof(value))) {
261                         /* gcc: fp --> short frame tail (fp) */
262                         int nr_added;
263                         unsigned long lr = quadd_user_link_register(regs);
264
265                         nr_added = quadd_callchain_store(cc, lr,
266                                                          QUADD_UNW_TYPE_LR_FP);
267                         if (nr_added == 0)
268                                 return cc->nr;
269
270                         tail = (unsigned long __user *)reg;
271                 }
272         }
273
274         if (!tail)
275                 tail = (unsigned long __user *)fp;
276
277         while (tail && !((unsigned long)tail & 0x3))
278                 tail = user_backtrace(regs, tail, cc, vma, task);
279
280         return cc->nr;
281 }
282
283 static unsigned int
284 __user_backtrace(struct pt_regs *regs,
285                  struct quadd_callchain *cc,
286                  struct task_struct *task)
287 {
288         struct mm_struct *mm = task->mm;
289         struct vm_area_struct *vma;
290         unsigned long __user *tail;
291
292         cc->unw_rc = QUADD_URC_FP_INCORRECT;
293
294         if (!mm) {
295                 cc->unw_rc = QUADD_URC_FAILURE;
296                 return cc->nr;
297         }
298
299         vma = find_vma(mm, cc->curr_sp);
300         if (!vma) {
301                 cc->unw_rc = QUADD_URC_SP_INCORRECT;
302                 return cc->nr;
303         }
304
305         tail = (unsigned long __user *)cc->curr_fp;
306
307         while (tail && !((unsigned long)tail & 0x3))
308                 tail = user_backtrace(regs, tail, cc, vma, task);
309
310         return cc->nr;
311 }
312
313 #ifdef CONFIG_ARM64
314 static u32 __user *
315 user_backtrace_compat(struct pt_regs *regs,
316                       u32 __user *tail,
317                       struct quadd_callchain *cc,
318                       struct vm_area_struct *stack_vma,
319                       struct task_struct *task)
320 {
321         int nr_added;
322         u32 value, value_lr = 0, value_fp = 0;
323         u32 __user *fp_prev = NULL;
324
325         if (!is_vma_addr((unsigned long)tail, stack_vma, sizeof(*tail)))
326                 return NULL;
327
328         if (__copy_from_user_inatomic(&value, tail, sizeof(value))) {
329                 cc->unw_rc = QUADD_URC_EACCESS;
330                 return NULL;
331         }
332
333         if (is_vma_addr(value, stack_vma, sizeof(value))) {
334                 /* gcc thumb/clang frame */
335                 value_fp = value;
336
337                 if (!is_vma_addr((unsigned long)(tail + 1), stack_vma,
338                     sizeof(*tail)))
339                         return NULL;
340
341                 if (__copy_from_user_inatomic(&value_lr, tail + 1,
342                                               sizeof(value_lr))) {
343                         cc->unw_rc = QUADD_URC_EACCESS;
344                         return NULL;
345                 }
346
347                 cc->curr_fp = value_fp;
348                 cc->curr_sp = (unsigned long)tail + sizeof(value_fp) * 2;
349                 cc->curr_pc = value_lr;
350         } else {
351                 /* gcc arm frame */
352                 if (__copy_from_user_inatomic(&value_fp, tail - 1,
353                                               sizeof(value_fp))) {
354                         cc->unw_rc = QUADD_URC_EACCESS;
355                         return NULL;
356                 }
357
358                 cc->curr_fp = value_fp;
359                 cc->curr_sp = (unsigned long)tail + sizeof(value_fp);
360                 cc->curr_pc = value_lr = value;
361
362                 if (!is_vma_addr(value_fp, stack_vma, sizeof(value_fp)))
363                         return NULL;
364         }
365
366         fp_prev = (u32 __user *)(unsigned long)value_fp;
367
368         nr_added = quadd_callchain_store(cc, value_lr, QUADD_UNW_TYPE_FP);
369         if (nr_added == 0)
370                 return NULL;
371
372         if (cc->unw_method == QUADD_UNW_METHOD_MIXED &&
373             is_ex_entry_exist(regs, value_lr, task))
374                 return NULL;
375
376         if (fp_prev <= tail)
377                 return NULL;
378
379         return fp_prev;
380 }
381
382 static unsigned int
383 get_user_callchain_fp_compat(struct pt_regs *regs,
384                              struct quadd_callchain *cc,
385                              struct task_struct *task)
386 {
387         u32 fp, sp, pc, reg;
388         struct vm_area_struct *vma, *vma_pc = NULL;
389         u32 __user *tail = NULL;
390         struct mm_struct *mm = task->mm;
391
392         cc->nr = 0;
393         cc->unw_rc = QUADD_URC_FP_INCORRECT;
394
395         if (!regs || !mm) {
396                 cc->unw_rc = QUADD_URC_FAILURE;
397                 return 0;
398         }
399
400         sp = quadd_user_stack_pointer(regs);
401         pc = instruction_pointer(regs);
402         fp = quadd_get_user_frame_pointer(regs);
403
404         if (fp == 0 || fp < sp || fp & 0x3)
405                 return 0;
406
407         vma = find_vma(mm, sp);
408         if (!vma) {
409                 cc->unw_rc = QUADD_URC_SP_INCORRECT;
410                 return 0;
411         }
412
413         if (!is_vma_addr(fp, vma, sizeof(fp)))
414                 return 0;
415
416         if (probe_kernel_address((unsigned long)fp, reg)) {
417                 pr_warn_once("%s: failed for address: %#x\n", __func__, fp);
418                 cc->unw_rc = QUADD_URC_EACCESS;
419                 return 0;
420         }
421
422         pr_debug("sp/fp: %#x/%#x, pc/lr: %#x/%#x, *fp: %#x, stack: %#lx-%#lx\n",
423                  sp, fp, pc, (u32)quadd_user_link_register(regs), reg,
424                  vma->vm_start, vma->vm_end);
425
426         if (is_thumb_mode(regs)) {
427                 if (reg <= fp || !is_vma_addr(reg, vma, sizeof(reg)))
428                         return 0;
429         } else if (reg > fp && is_vma_addr(reg, vma, sizeof(reg))) {
430                 /* fp --> fp prev */
431                 u32 value;
432                 int read_lr = 0;
433
434                 if (is_vma_addr(fp + sizeof(u32), vma, sizeof(fp))) {
435                         if (__copy_from_user_inatomic(
436                                         &value,
437                                         (u32 __user *)(fp + sizeof(u32)),
438                                         sizeof(value))) {
439                                 cc->unw_rc = QUADD_URC_EACCESS;
440                                 return 0;
441                         }
442
443                         vma_pc = find_vma(mm, pc);
444                         read_lr = 1;
445                 }
446
447                 if (!read_lr || !is_vma_addr(value, vma_pc, sizeof(value))) {
448                         /* gcc: fp --> short frame tail (fp) */
449                         int nr_added;
450                         u32 lr = quadd_user_link_register(regs);
451
452                         nr_added = quadd_callchain_store(cc, lr,
453                                                          QUADD_UNW_TYPE_LR_FP);
454                         if (nr_added == 0)
455                                 return cc->nr;
456
457                         tail = (u32 __user *)(unsigned long)reg;
458                 }
459         }
460
461         if (!tail)
462                 tail = (u32 __user *)(unsigned long)fp;
463
464         while (tail && !((unsigned long)tail & 0x3))
465                 tail = user_backtrace_compat(regs, tail, cc, vma, task);
466
467         return cc->nr;
468 }
469
470 static unsigned int
471 __user_backtrace_compat(struct pt_regs *regs,
472                         struct quadd_callchain *cc,
473                         struct task_struct *task)
474 {
475         struct mm_struct *mm = task->mm;
476         struct vm_area_struct *vma;
477         u32 __user *tail;
478
479         cc->unw_rc = QUADD_URC_FP_INCORRECT;
480
481         if (!mm) {
482                 cc->unw_rc = QUADD_URC_FAILURE;
483                 return cc->nr;
484         }
485
486         vma = find_vma(mm, cc->curr_sp);
487         if (!vma) {
488                 cc->unw_rc = QUADD_URC_SP_INCORRECT;
489                 return cc->nr;
490         }
491
492         tail = (u32 __user *)cc->curr_fp;
493
494         while (tail && !((unsigned long)tail & 0x3))
495                 tail = user_backtrace_compat(regs, tail, cc, vma, task);
496
497         return cc->nr;
498 }
499
500 #endif  /* CONFIG_ARM64 */
501
502 static unsigned int
503 __get_user_callchain_fp(struct pt_regs *regs,
504                       struct quadd_callchain *cc,
505                       struct task_struct *task)
506 {
507         if (cc->nr > 0) {
508                 if (cc->unw_rc == QUADD_URC_LEVEL_TOO_DEEP)
509                         return cc->nr;
510
511 #ifdef CONFIG_ARM64
512                 if (compat_user_mode(regs))
513                         __user_backtrace_compat(regs, cc, task);
514                 else
515                         __user_backtrace(regs, cc, task);
516 #else
517                 __user_backtrace(regs, cc, task);
518 #endif
519
520                 return cc->nr;
521         }
522
523 #ifdef CONFIG_ARM64
524         if (compat_user_mode(regs))
525                 return get_user_callchain_fp_compat(regs, cc, task);
526 #endif
527
528         return get_user_callchain_fp(regs, cc, task);
529 }
530
531 static unsigned int
532 get_user_callchain_ut(struct pt_regs *regs,
533                       struct quadd_callchain *cc,
534                       struct task_struct *task)
535 {
536         int nr_prev;
537
538         do {
539                 nr_prev = cc->nr;
540
541                 quadd_get_user_cc_dwarf(regs, cc, task);
542                 if (nr_prev > 0 && cc->nr == nr_prev)
543                         break;
544
545                 nr_prev = cc->nr;
546
547                 quadd_get_user_cc_arm32_ehabi(regs, cc, task);
548         } while (nr_prev != cc->nr);
549
550         return cc->nr;
551 }
552
553 static unsigned int
554 get_user_callchain_mixed(struct pt_regs *regs,
555                       struct quadd_callchain *cc,
556                       struct task_struct *task)
557 {
558         int nr_prev;
559
560         do {
561                 nr_prev = cc->nr;
562
563                 quadd_get_user_cc_dwarf(regs, cc, task);
564
565                 quadd_get_user_cc_arm32_ehabi(regs, cc, task);
566
567                 __get_user_callchain_fp(regs, cc, task);
568         } while (nr_prev != cc->nr);
569
570         return cc->nr;
571 }
572
573 unsigned int
574 quadd_get_user_callchain(struct pt_regs *regs,
575                          struct quadd_callchain *cc,
576                          struct quadd_ctx *ctx,
577                          struct task_struct *task)
578 {
579         unsigned int method = cc->unw_method;
580
581         cc->nr = 0;
582
583         if (!regs) {
584                 cc->unw_rc = QUADD_URC_FAILURE;
585                 return 0;
586         }
587
588         cc->curr_sp = 0;
589         cc->curr_fp = 0;
590         cc->curr_pc = 0;
591
592 #ifdef CONFIG_ARM64
593         cc->cs_64 = compat_user_mode(regs) ? 0 : 1;
594 #else
595         cc->cs_64 = 0;
596 #endif
597
598         cc->unw_rc = 0;
599
600         switch (method) {
601         case QUADD_UNW_METHOD_FP:
602                 __get_user_callchain_fp(regs, cc, task);
603                 break;
604
605         case QUADD_UNW_METHOD_EHT:
606                 get_user_callchain_ut(regs, cc, task);
607                 break;
608
609         case QUADD_UNW_METHOD_MIXED:
610                 get_user_callchain_mixed(regs, cc, task);
611                 break;
612
613         case QUADD_UNW_METHOD_NONE:
614         default:
615                 break;
616         }
617
618         return cc->nr;
619 }