1*d39976f0SMark Rutland /* 2*d39976f0SMark Rutland * ARM callchain support 3*d39976f0SMark Rutland * 4*d39976f0SMark Rutland * Copyright (C) 2009 picoChip Designs, Ltd., Jamie Iles 5*d39976f0SMark Rutland * Copyright (C) 2010 ARM Ltd., Will Deacon <will.deacon@arm.com> 6*d39976f0SMark Rutland * 7*d39976f0SMark Rutland * This code is based on the ARM OProfile backtrace code. 8*d39976f0SMark Rutland */ 9*d39976f0SMark Rutland #include <linux/perf_event.h> 10*d39976f0SMark Rutland #include <linux/uaccess.h> 11*d39976f0SMark Rutland 12*d39976f0SMark Rutland #include <asm/stacktrace.h> 13*d39976f0SMark Rutland 14*d39976f0SMark Rutland /* 15*d39976f0SMark Rutland * The registers we're interested in are at the end of the variable 16*d39976f0SMark Rutland * length saved register structure. The fp points at the end of this 17*d39976f0SMark Rutland * structure so the address of this struct is: 18*d39976f0SMark Rutland * (struct frame_tail *)(xxx->fp)-1 19*d39976f0SMark Rutland * 20*d39976f0SMark Rutland * This code has been adapted from the ARM OProfile support. 21*d39976f0SMark Rutland */ 22*d39976f0SMark Rutland struct frame_tail { 23*d39976f0SMark Rutland struct frame_tail __user *fp; 24*d39976f0SMark Rutland unsigned long sp; 25*d39976f0SMark Rutland unsigned long lr; 26*d39976f0SMark Rutland } __attribute__((packed)); 27*d39976f0SMark Rutland 28*d39976f0SMark Rutland /* 29*d39976f0SMark Rutland * Get the return address for a single stackframe and return a pointer to the 30*d39976f0SMark Rutland * next frame tail. 31*d39976f0SMark Rutland */ 32*d39976f0SMark Rutland static struct frame_tail __user * 33*d39976f0SMark Rutland user_backtrace(struct frame_tail __user *tail, 34*d39976f0SMark Rutland struct perf_callchain_entry *entry) 35*d39976f0SMark Rutland { 36*d39976f0SMark Rutland struct frame_tail buftail; 37*d39976f0SMark Rutland unsigned long err; 38*d39976f0SMark Rutland 39*d39976f0SMark Rutland if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) 40*d39976f0SMark Rutland return NULL; 41*d39976f0SMark Rutland 42*d39976f0SMark Rutland pagefault_disable(); 43*d39976f0SMark Rutland err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); 44*d39976f0SMark Rutland pagefault_enable(); 45*d39976f0SMark Rutland 46*d39976f0SMark Rutland if (err) 47*d39976f0SMark Rutland return NULL; 48*d39976f0SMark Rutland 49*d39976f0SMark Rutland perf_callchain_store(entry, buftail.lr); 50*d39976f0SMark Rutland 51*d39976f0SMark Rutland /* 52*d39976f0SMark Rutland * Frame pointers should strictly progress back up the stack 53*d39976f0SMark Rutland * (towards higher addresses). 54*d39976f0SMark Rutland */ 55*d39976f0SMark Rutland if (tail + 1 >= buftail.fp) 56*d39976f0SMark Rutland return NULL; 57*d39976f0SMark Rutland 58*d39976f0SMark Rutland return buftail.fp - 1; 59*d39976f0SMark Rutland } 60*d39976f0SMark Rutland 61*d39976f0SMark Rutland void 62*d39976f0SMark Rutland perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs) 63*d39976f0SMark Rutland { 64*d39976f0SMark Rutland struct frame_tail __user *tail; 65*d39976f0SMark Rutland 66*d39976f0SMark Rutland if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { 67*d39976f0SMark Rutland /* We don't support guest os callchain now */ 68*d39976f0SMark Rutland return; 69*d39976f0SMark Rutland } 70*d39976f0SMark Rutland 71*d39976f0SMark Rutland perf_callchain_store(entry, regs->ARM_pc); 72*d39976f0SMark Rutland 73*d39976f0SMark Rutland if (!current->mm) 74*d39976f0SMark Rutland return; 75*d39976f0SMark Rutland 76*d39976f0SMark Rutland tail = (struct frame_tail __user *)regs->ARM_fp - 1; 77*d39976f0SMark Rutland 78*d39976f0SMark Rutland while ((entry->nr < PERF_MAX_STACK_DEPTH) && 79*d39976f0SMark Rutland tail && !((unsigned long)tail & 0x3)) 80*d39976f0SMark Rutland tail = user_backtrace(tail, entry); 81*d39976f0SMark Rutland } 82*d39976f0SMark Rutland 83*d39976f0SMark Rutland /* 84*d39976f0SMark Rutland * Gets called by walk_stackframe() for every stackframe. This will be called 85*d39976f0SMark Rutland * whist unwinding the stackframe and is like a subroutine return so we use 86*d39976f0SMark Rutland * the PC. 87*d39976f0SMark Rutland */ 88*d39976f0SMark Rutland static int 89*d39976f0SMark Rutland callchain_trace(struct stackframe *fr, 90*d39976f0SMark Rutland void *data) 91*d39976f0SMark Rutland { 92*d39976f0SMark Rutland struct perf_callchain_entry *entry = data; 93*d39976f0SMark Rutland perf_callchain_store(entry, fr->pc); 94*d39976f0SMark Rutland return 0; 95*d39976f0SMark Rutland } 96*d39976f0SMark Rutland 97*d39976f0SMark Rutland void 98*d39976f0SMark Rutland perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs) 99*d39976f0SMark Rutland { 100*d39976f0SMark Rutland struct stackframe fr; 101*d39976f0SMark Rutland 102*d39976f0SMark Rutland if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { 103*d39976f0SMark Rutland /* We don't support guest os callchain now */ 104*d39976f0SMark Rutland return; 105*d39976f0SMark Rutland } 106*d39976f0SMark Rutland 107*d39976f0SMark Rutland arm_get_current_stackframe(regs, &fr); 108*d39976f0SMark Rutland walk_stackframe(&fr, callchain_trace, entry); 109*d39976f0SMark Rutland } 110*d39976f0SMark Rutland 111*d39976f0SMark Rutland unsigned long perf_instruction_pointer(struct pt_regs *regs) 112*d39976f0SMark Rutland { 113*d39976f0SMark Rutland if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) 114*d39976f0SMark Rutland return perf_guest_cbs->get_guest_ip(); 115*d39976f0SMark Rutland 116*d39976f0SMark Rutland return instruction_pointer(regs); 117*d39976f0SMark Rutland } 118*d39976f0SMark Rutland 119*d39976f0SMark Rutland unsigned long perf_misc_flags(struct pt_regs *regs) 120*d39976f0SMark Rutland { 121*d39976f0SMark Rutland int misc = 0; 122*d39976f0SMark Rutland 123*d39976f0SMark Rutland if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) { 124*d39976f0SMark Rutland if (perf_guest_cbs->is_user_mode()) 125*d39976f0SMark Rutland misc |= PERF_RECORD_MISC_GUEST_USER; 126*d39976f0SMark Rutland else 127*d39976f0SMark Rutland misc |= PERF_RECORD_MISC_GUEST_KERNEL; 128*d39976f0SMark Rutland } else { 129*d39976f0SMark Rutland if (user_mode(regs)) 130*d39976f0SMark Rutland misc |= PERF_RECORD_MISC_USER; 131*d39976f0SMark Rutland else 132*d39976f0SMark Rutland misc |= PERF_RECORD_MISC_KERNEL; 133*d39976f0SMark Rutland } 134*d39976f0SMark Rutland 135*d39976f0SMark Rutland return misc; 136*d39976f0SMark Rutland } 137