1
2
3
4#include <linux/perf_event.h>
5#include <linux/uaccess.h>
6
7#include <asm/stacktrace.h>
8
9
10
11
12
13static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
14 unsigned long fp, unsigned long reg_ra)
15{
16 struct stackframe buftail;
17 unsigned long ra = 0;
18 unsigned long *user_frame_tail =
19 (unsigned long *)(fp - sizeof(struct stackframe));
20
21
22 if (!access_ok(user_frame_tail, sizeof(buftail)))
23 return 0;
24 if (__copy_from_user_inatomic(&buftail, user_frame_tail,
25 sizeof(buftail)))
26 return 0;
27
28 if (reg_ra != 0)
29 ra = reg_ra;
30 else
31 ra = buftail.ra;
32
33 fp = buftail.fp;
34 if (ra != 0)
35 perf_callchain_store(entry, ra);
36 else
37 return 0;
38
39 return fp;
40}
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
57 struct pt_regs *regs)
58{
59 unsigned long fp = 0;
60
61
62 if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
63 return;
64
65 fp = regs->s0;
66 perf_callchain_store(entry, regs->epc);
67
68 fp = user_backtrace(entry, fp, regs->ra);
69 while (fp && !(fp & 0x3) && entry->nr < entry->max_stack)
70 fp = user_backtrace(entry, fp, 0);
71}
72
73static bool fill_callchain(void *entry, unsigned long pc)
74{
75 return perf_callchain_store(entry, pc);
76}
77
78void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
79 struct pt_regs *regs)
80{
81
82 if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
83 pr_warn("RISC-V does not support perf in guest mode!");
84 return;
85 }
86
87 walk_stackframe(NULL, regs, fill_callchain, entry);
88}
89