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