1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */ 3 4 #include <linux/perf_event.h> 5 #include <linux/uaccess.h> 6 7 /* Kernel callchain */ 8 struct stackframe { 9 unsigned long fp; 10 unsigned long ra; 11 }; 12 13 /* 14 * Get the return address for a single stackframe and return a pointer to the 15 * next frame tail. 16 */ 17 static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry, 18 unsigned long fp, unsigned long reg_ra) 19 { 20 struct stackframe buftail; 21 unsigned long ra = 0; 22 unsigned long *user_frame_tail = 23 (unsigned long *)(fp - sizeof(struct stackframe)); 24 25 /* Check accessibility of one struct frame_tail beyond */ 26 if (!access_ok(user_frame_tail, sizeof(buftail))) 27 return 0; 28 if (__copy_from_user_inatomic(&buftail, user_frame_tail, 29 sizeof(buftail))) 30 return 0; 31 32 if (reg_ra != 0) 33 ra = reg_ra; 34 else 35 ra = buftail.ra; 36 37 fp = buftail.fp; 38 if (ra != 0) 39 perf_callchain_store(entry, ra); 40 else 41 return 0; 42 43 return fp; 44 } 45 46 /* 47 * This will be called when the target is in user mode 48 * This function will only be called when we use 49 * "PERF_SAMPLE_CALLCHAIN" in 50 * kernel/events/core.c:perf_prepare_sample() 51 * 52 * How to trigger perf_callchain_[user/kernel] : 53 * $ perf record -e cpu-clock --call-graph fp ./program 54 * $ perf report --call-graph 55 * 56 * On RISC-V platform, the program being sampled and the C library 57 * need to be compiled with -fno-omit-frame-pointer, otherwise 58 * the user stack will not contain function frame. 59 */ 60 void perf_callchain_user(struct perf_callchain_entry_ctx *entry, 61 struct pt_regs *regs) 62 { 63 unsigned long fp = 0; 64 65 /* RISC-V does not support perf in guest mode. */ 66 if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) 67 return; 68 69 fp = regs->s0; 70 perf_callchain_store(entry, regs->epc); 71 72 fp = user_backtrace(entry, fp, regs->ra); 73 while (fp && !(fp & 0x3) && entry->nr < entry->max_stack) 74 fp = user_backtrace(entry, fp, 0); 75 } 76 77 bool fill_callchain(unsigned long pc, void *entry) 78 { 79 return perf_callchain_store(entry, pc); 80 } 81 82 void notrace walk_stackframe(struct task_struct *task, 83 struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg); 84 void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, 85 struct pt_regs *regs) 86 { 87 /* RISC-V does not support perf in guest mode. */ 88 if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { 89 pr_warn("RISC-V does not support perf in guest mode!"); 90 return; 91 } 92 93 walk_stackframe(NULL, regs, fill_callchain, entry); 94 } 95