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