| /** | |
| * @file common.c | |
| * | |
| * @remark Copyright 2004 Oprofile Authors | |
| * @remark Copyright 2010 ARM Ltd. | |
| * @remark Read the file COPYING | |
| * | |
| * @author Zwane Mwaikambo | |
| * @author Will Deacon [move to perf] | |
| */ | |
| #include <linux/cpumask.h> | |
| #include <linux/init.h> | |
| #include <linux/mutex.h> | |
| #include <linux/oprofile.h> | |
| #include <linux/perf_event.h> | |
| #include <linux/platform_device.h> | |
| #include <linux/slab.h> | |
| #include <asm/stacktrace.h> | |
| #include <linux/uaccess.h> | |
| #include <asm/perf_event.h> | |
| #include <asm/ptrace.h> | |
| #ifdef CONFIG_HW_PERF_EVENTS | |
| /* | |
| * OProfile has a curious naming scheme for the ARM PMUs, but they are | |
| * part of the user ABI so we need to map from the perf PMU name for | |
| * supported PMUs. | |
| */ | |
| static struct op_perf_name { | |
| char *perf_name; | |
| char *op_name; | |
| } op_perf_name_map[] = { | |
| { "armv5_xscale1", "arm/xscale1" }, | |
| { "armv5_xscale2", "arm/xscale2" }, | |
| { "armv6_1136", "arm/armv6" }, | |
| { "armv6_1156", "arm/armv6" }, | |
| { "armv6_1176", "arm/armv6" }, | |
| { "armv6_11mpcore", "arm/mpcore" }, | |
| { "armv7_cortex_a8", "arm/armv7" }, | |
| { "armv7_cortex_a9", "arm/armv7-ca9" }, | |
| }; | |
| char *op_name_from_perf_id(void) | |
| { | |
| int i; | |
| struct op_perf_name names; | |
| const char *perf_name = perf_pmu_name(); | |
| for (i = 0; i < ARRAY_SIZE(op_perf_name_map); ++i) { | |
| names = op_perf_name_map[i]; | |
| if (!strcmp(names.perf_name, perf_name)) | |
| return names.op_name; | |
| } | |
| return NULL; | |
| } | |
| #endif | |
| static int report_trace(struct stackframe *frame, void *d) | |
| { | |
| unsigned int *depth = d; | |
| if (*depth) { | |
| oprofile_add_trace(frame->pc); | |
| (*depth)--; | |
| } | |
| return *depth == 0; | |
| } | |
| /* | |
| * The registers we're interested in are at the end of the variable | |
| * length saved register structure. The fp points at the end of this | |
| * structure so the address of this struct is: | |
| * (struct frame_tail *)(xxx->fp)-1 | |
| */ | |
| struct frame_tail { | |
| struct frame_tail *fp; | |
| unsigned long sp; | |
| unsigned long lr; | |
| } __attribute__((packed)); | |
| static struct frame_tail* user_backtrace(struct frame_tail *tail) | |
| { | |
| struct frame_tail buftail[2]; | |
| /* Also check accessibility of one struct frame_tail beyond */ | |
| if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) | |
| return NULL; | |
| if (__copy_from_user_inatomic(buftail, tail, sizeof(buftail))) | |
| return NULL; | |
| oprofile_add_trace(buftail[0].lr); | |
| /* frame pointers should strictly progress back up the stack | |
| * (towards higher addresses) */ | |
| if (tail + 1 >= buftail[0].fp) | |
| return NULL; | |
| return buftail[0].fp-1; | |
| } | |
| static void arm_backtrace(struct pt_regs * const regs, unsigned int depth) | |
| { | |
| struct frame_tail *tail = ((struct frame_tail *) regs->ARM_fp) - 1; | |
| if (!user_mode(regs)) { | |
| struct stackframe frame; | |
| arm_get_current_stackframe(regs, &frame); | |
| walk_stackframe(&frame, report_trace, &depth); | |
| return; | |
| } | |
| while (depth-- && tail && !((unsigned long) tail & 3)) | |
| tail = user_backtrace(tail); | |
| } | |
| int __init oprofile_arch_init(struct oprofile_operations *ops) | |
| { | |
| /* provide backtrace support also in timer mode: */ | |
| ops->backtrace = arm_backtrace; | |
| return oprofile_perf_init(ops); | |
| } | |
| void oprofile_arch_exit(void) | |
| { | |
| oprofile_perf_exit(); | |
| } |