18c2ecf20Sopenharmony_ci/** 28c2ecf20Sopenharmony_ci * @file common.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * @remark Copyright 2004 Oprofile Authors 58c2ecf20Sopenharmony_ci * @remark Copyright 2010 ARM Ltd. 68c2ecf20Sopenharmony_ci * @remark Read the file COPYING 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * @author Zwane Mwaikambo 98c2ecf20Sopenharmony_ci * @author Will Deacon [move to perf] 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/cpumask.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/oprofile.h> 168c2ecf20Sopenharmony_ci#include <linux/perf_event.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <asm/stacktrace.h> 208c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <asm/perf_event.h> 238c2ecf20Sopenharmony_ci#include <asm/ptrace.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#ifdef CONFIG_HW_PERF_EVENTS 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* 288c2ecf20Sopenharmony_ci * OProfile has a curious naming scheme for the ARM PMUs, but they are 298c2ecf20Sopenharmony_ci * part of the user ABI so we need to map from the perf PMU name for 308c2ecf20Sopenharmony_ci * supported PMUs. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_cistatic struct op_perf_name { 338c2ecf20Sopenharmony_ci char *perf_name; 348c2ecf20Sopenharmony_ci char *op_name; 358c2ecf20Sopenharmony_ci} op_perf_name_map[] = { 368c2ecf20Sopenharmony_ci { "armv5_xscale1", "arm/xscale1" }, 378c2ecf20Sopenharmony_ci { "armv5_xscale2", "arm/xscale2" }, 388c2ecf20Sopenharmony_ci { "armv6_1136", "arm/armv6" }, 398c2ecf20Sopenharmony_ci { "armv6_1156", "arm/armv6" }, 408c2ecf20Sopenharmony_ci { "armv6_1176", "arm/armv6" }, 418c2ecf20Sopenharmony_ci { "armv6_11mpcore", "arm/mpcore" }, 428c2ecf20Sopenharmony_ci { "armv7_cortex_a8", "arm/armv7" }, 438c2ecf20Sopenharmony_ci { "armv7_cortex_a9", "arm/armv7-ca9" }, 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cichar *op_name_from_perf_id(void) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci int i; 498c2ecf20Sopenharmony_ci struct op_perf_name names; 508c2ecf20Sopenharmony_ci const char *perf_name = perf_pmu_name(); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(op_perf_name_map); ++i) { 538c2ecf20Sopenharmony_ci names = op_perf_name_map[i]; 548c2ecf20Sopenharmony_ci if (!strcmp(names.perf_name, perf_name)) 558c2ecf20Sopenharmony_ci return names.op_name; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return NULL; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci#endif 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int report_trace(struct stackframe *frame, void *d) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci unsigned int *depth = d; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (*depth) { 678c2ecf20Sopenharmony_ci oprofile_add_trace(frame->pc); 688c2ecf20Sopenharmony_ci (*depth)--; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return *depth == 0; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* 758c2ecf20Sopenharmony_ci * The registers we're interested in are at the end of the variable 768c2ecf20Sopenharmony_ci * length saved register structure. The fp points at the end of this 778c2ecf20Sopenharmony_ci * structure so the address of this struct is: 788c2ecf20Sopenharmony_ci * (struct frame_tail *)(xxx->fp)-1 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_cistruct frame_tail { 818c2ecf20Sopenharmony_ci struct frame_tail *fp; 828c2ecf20Sopenharmony_ci unsigned long sp; 838c2ecf20Sopenharmony_ci unsigned long lr; 848c2ecf20Sopenharmony_ci} __attribute__((packed)); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic struct frame_tail* user_backtrace(struct frame_tail *tail) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct frame_tail buftail[2]; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Also check accessibility of one struct frame_tail beyond */ 918c2ecf20Sopenharmony_ci if (!access_ok(tail, sizeof(buftail))) 928c2ecf20Sopenharmony_ci return NULL; 938c2ecf20Sopenharmony_ci if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) 948c2ecf20Sopenharmony_ci return NULL; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci oprofile_add_trace(buftail[0].lr); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* frame pointers should strictly progress back up the stack 998c2ecf20Sopenharmony_ci * (towards higher addresses) */ 1008c2ecf20Sopenharmony_ci if (tail + 1 >= buftail[0].fp) 1018c2ecf20Sopenharmony_ci return NULL; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return buftail[0].fp-1; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic void arm_backtrace(struct pt_regs * const regs, unsigned int depth) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct frame_tail *tail = ((struct frame_tail *) regs->ARM_fp) - 1; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (!user_mode(regs)) { 1118c2ecf20Sopenharmony_ci struct stackframe frame; 1128c2ecf20Sopenharmony_ci arm_get_current_stackframe(regs, &frame); 1138c2ecf20Sopenharmony_ci walk_stackframe(&frame, report_trace, &depth); 1148c2ecf20Sopenharmony_ci return; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci while (depth-- && tail && !((unsigned long) tail & 3)) 1188c2ecf20Sopenharmony_ci tail = user_backtrace(tail); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ciint __init oprofile_arch_init(struct oprofile_operations *ops) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci /* provide backtrace support also in timer mode: */ 1248c2ecf20Sopenharmony_ci ops->backtrace = arm_backtrace; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return oprofile_perf_init(ops); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_civoid oprofile_arch_exit(void) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci oprofile_perf_exit(); 1328c2ecf20Sopenharmony_ci} 133