xref: /openbmc/linux/arch/arm/kernel/perf_callchain.c (revision 3b1fff08038bd0792b1aa1e9703b2dd0512a3fd0)
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