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 14static 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 30static struct metag_frame __user * 31user_backtrace(struct metag_frame __user *user_frame, 32 struct perf_callchain_entry *entry) 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 58void 59perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs) 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 68 while ((entry->nr < PERF_MAX_STACK_DEPTH) && frame) 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 */ 77static int 78callchain_trace(struct stackframe *fr, 79 void *data) 80{ 81 struct perf_callchain_entry *entry = data; 82 perf_callchain_store(entry, fr->pc); 83 return 0; 84} 85 86void 87perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs) 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} 97