xref: /openbmc/linux/arch/powerpc/perf/callchain_32.c (revision 7c0eda1a04340a1de09bdf6521853e3bc0637c3b)
1*7c0eda1aSMichal Suchanek // SPDX-License-Identifier: GPL-2.0-or-later
2*7c0eda1aSMichal Suchanek /*
3*7c0eda1aSMichal Suchanek  * Performance counter callchain support - powerpc architecture code
4*7c0eda1aSMichal Suchanek  *
5*7c0eda1aSMichal Suchanek  * Copyright © 2009 Paul Mackerras, IBM Corporation.
6*7c0eda1aSMichal Suchanek  */
7*7c0eda1aSMichal Suchanek #include <linux/kernel.h>
8*7c0eda1aSMichal Suchanek #include <linux/sched.h>
9*7c0eda1aSMichal Suchanek #include <linux/perf_event.h>
10*7c0eda1aSMichal Suchanek #include <linux/percpu.h>
11*7c0eda1aSMichal Suchanek #include <linux/uaccess.h>
12*7c0eda1aSMichal Suchanek #include <linux/mm.h>
13*7c0eda1aSMichal Suchanek #include <asm/ptrace.h>
14*7c0eda1aSMichal Suchanek #include <asm/pgtable.h>
15*7c0eda1aSMichal Suchanek #include <asm/sigcontext.h>
16*7c0eda1aSMichal Suchanek #include <asm/ucontext.h>
17*7c0eda1aSMichal Suchanek #include <asm/vdso.h>
18*7c0eda1aSMichal Suchanek #include <asm/pte-walk.h>
19*7c0eda1aSMichal Suchanek 
20*7c0eda1aSMichal Suchanek #include "callchain.h"
21*7c0eda1aSMichal Suchanek 
22*7c0eda1aSMichal Suchanek #ifdef CONFIG_PPC64
23*7c0eda1aSMichal Suchanek #include "../kernel/ppc32.h"
24*7c0eda1aSMichal Suchanek #else  /* CONFIG_PPC64 */
25*7c0eda1aSMichal Suchanek 
26*7c0eda1aSMichal Suchanek #define __SIGNAL_FRAMESIZE32	__SIGNAL_FRAMESIZE
27*7c0eda1aSMichal Suchanek #define sigcontext32		sigcontext
28*7c0eda1aSMichal Suchanek #define mcontext32		mcontext
29*7c0eda1aSMichal Suchanek #define ucontext32		ucontext
30*7c0eda1aSMichal Suchanek #define compat_siginfo_t	struct siginfo
31*7c0eda1aSMichal Suchanek 
32*7c0eda1aSMichal Suchanek #endif /* CONFIG_PPC64 */
33*7c0eda1aSMichal Suchanek 
34*7c0eda1aSMichal Suchanek /*
35*7c0eda1aSMichal Suchanek  * On 32-bit we just access the address and let hash_page create a
36*7c0eda1aSMichal Suchanek  * HPTE if necessary, so there is no need to fall back to reading
37*7c0eda1aSMichal Suchanek  * the page tables.  Since this is called at interrupt level,
38*7c0eda1aSMichal Suchanek  * do_page_fault() won't treat a DSI as a page fault.
39*7c0eda1aSMichal Suchanek  */
40*7c0eda1aSMichal Suchanek static int read_user_stack_32(unsigned int __user *ptr, unsigned int *ret)
41*7c0eda1aSMichal Suchanek {
42*7c0eda1aSMichal Suchanek 	int rc;
43*7c0eda1aSMichal Suchanek 
44*7c0eda1aSMichal Suchanek 	if ((unsigned long)ptr > TASK_SIZE - sizeof(unsigned int) ||
45*7c0eda1aSMichal Suchanek 	    ((unsigned long)ptr & 3))
46*7c0eda1aSMichal Suchanek 		return -EFAULT;
47*7c0eda1aSMichal Suchanek 
48*7c0eda1aSMichal Suchanek 	rc = probe_user_read(ret, ptr, sizeof(*ret));
49*7c0eda1aSMichal Suchanek 
50*7c0eda1aSMichal Suchanek 	if (IS_ENABLED(CONFIG_PPC64) && rc)
51*7c0eda1aSMichal Suchanek 		return read_user_stack_slow(ptr, ret, 4);
52*7c0eda1aSMichal Suchanek 
53*7c0eda1aSMichal Suchanek 	return rc;
54*7c0eda1aSMichal Suchanek }
55*7c0eda1aSMichal Suchanek 
56*7c0eda1aSMichal Suchanek /*
57*7c0eda1aSMichal Suchanek  * Layout for non-RT signal frames
58*7c0eda1aSMichal Suchanek  */
59*7c0eda1aSMichal Suchanek struct signal_frame_32 {
60*7c0eda1aSMichal Suchanek 	char			dummy[__SIGNAL_FRAMESIZE32];
61*7c0eda1aSMichal Suchanek 	struct sigcontext32	sctx;
62*7c0eda1aSMichal Suchanek 	struct mcontext32	mctx;
63*7c0eda1aSMichal Suchanek 	int			abigap[56];
64*7c0eda1aSMichal Suchanek };
65*7c0eda1aSMichal Suchanek 
66*7c0eda1aSMichal Suchanek /*
67*7c0eda1aSMichal Suchanek  * Layout for RT signal frames
68*7c0eda1aSMichal Suchanek  */
69*7c0eda1aSMichal Suchanek struct rt_signal_frame_32 {
70*7c0eda1aSMichal Suchanek 	char			dummy[__SIGNAL_FRAMESIZE32 + 16];
71*7c0eda1aSMichal Suchanek 	compat_siginfo_t	info;
72*7c0eda1aSMichal Suchanek 	struct ucontext32	uc;
73*7c0eda1aSMichal Suchanek 	int			abigap[56];
74*7c0eda1aSMichal Suchanek };
75*7c0eda1aSMichal Suchanek 
76*7c0eda1aSMichal Suchanek static int is_sigreturn_32_address(unsigned int nip, unsigned int fp)
77*7c0eda1aSMichal Suchanek {
78*7c0eda1aSMichal Suchanek 	if (nip == fp + offsetof(struct signal_frame_32, mctx.mc_pad))
79*7c0eda1aSMichal Suchanek 		return 1;
80*7c0eda1aSMichal Suchanek 	if (vdso32_sigtramp && current->mm->context.vdso_base &&
81*7c0eda1aSMichal Suchanek 	    nip == current->mm->context.vdso_base + vdso32_sigtramp)
82*7c0eda1aSMichal Suchanek 		return 1;
83*7c0eda1aSMichal Suchanek 	return 0;
84*7c0eda1aSMichal Suchanek }
85*7c0eda1aSMichal Suchanek 
86*7c0eda1aSMichal Suchanek static int is_rt_sigreturn_32_address(unsigned int nip, unsigned int fp)
87*7c0eda1aSMichal Suchanek {
88*7c0eda1aSMichal Suchanek 	if (nip == fp + offsetof(struct rt_signal_frame_32,
89*7c0eda1aSMichal Suchanek 				 uc.uc_mcontext.mc_pad))
90*7c0eda1aSMichal Suchanek 		return 1;
91*7c0eda1aSMichal Suchanek 	if (vdso32_rt_sigtramp && current->mm->context.vdso_base &&
92*7c0eda1aSMichal Suchanek 	    nip == current->mm->context.vdso_base + vdso32_rt_sigtramp)
93*7c0eda1aSMichal Suchanek 		return 1;
94*7c0eda1aSMichal Suchanek 	return 0;
95*7c0eda1aSMichal Suchanek }
96*7c0eda1aSMichal Suchanek 
97*7c0eda1aSMichal Suchanek static int sane_signal_32_frame(unsigned int sp)
98*7c0eda1aSMichal Suchanek {
99*7c0eda1aSMichal Suchanek 	struct signal_frame_32 __user *sf;
100*7c0eda1aSMichal Suchanek 	unsigned int regs;
101*7c0eda1aSMichal Suchanek 
102*7c0eda1aSMichal Suchanek 	sf = (struct signal_frame_32 __user *) (unsigned long) sp;
103*7c0eda1aSMichal Suchanek 	if (read_user_stack_32((unsigned int __user *) &sf->sctx.regs, &regs))
104*7c0eda1aSMichal Suchanek 		return 0;
105*7c0eda1aSMichal Suchanek 	return regs == (unsigned long) &sf->mctx;
106*7c0eda1aSMichal Suchanek }
107*7c0eda1aSMichal Suchanek 
108*7c0eda1aSMichal Suchanek static int sane_rt_signal_32_frame(unsigned int sp)
109*7c0eda1aSMichal Suchanek {
110*7c0eda1aSMichal Suchanek 	struct rt_signal_frame_32 __user *sf;
111*7c0eda1aSMichal Suchanek 	unsigned int regs;
112*7c0eda1aSMichal Suchanek 
113*7c0eda1aSMichal Suchanek 	sf = (struct rt_signal_frame_32 __user *) (unsigned long) sp;
114*7c0eda1aSMichal Suchanek 	if (read_user_stack_32((unsigned int __user *) &sf->uc.uc_regs, &regs))
115*7c0eda1aSMichal Suchanek 		return 0;
116*7c0eda1aSMichal Suchanek 	return regs == (unsigned long) &sf->uc.uc_mcontext;
117*7c0eda1aSMichal Suchanek }
118*7c0eda1aSMichal Suchanek 
119*7c0eda1aSMichal Suchanek static unsigned int __user *signal_frame_32_regs(unsigned int sp,
120*7c0eda1aSMichal Suchanek 				unsigned int next_sp, unsigned int next_ip)
121*7c0eda1aSMichal Suchanek {
122*7c0eda1aSMichal Suchanek 	struct mcontext32 __user *mctx = NULL;
123*7c0eda1aSMichal Suchanek 	struct signal_frame_32 __user *sf;
124*7c0eda1aSMichal Suchanek 	struct rt_signal_frame_32 __user *rt_sf;
125*7c0eda1aSMichal Suchanek 
126*7c0eda1aSMichal Suchanek 	/*
127*7c0eda1aSMichal Suchanek 	 * Note: the next_sp - sp >= signal frame size check
128*7c0eda1aSMichal Suchanek 	 * is true when next_sp < sp, for example, when
129*7c0eda1aSMichal Suchanek 	 * transitioning from an alternate signal stack to the
130*7c0eda1aSMichal Suchanek 	 * normal stack.
131*7c0eda1aSMichal Suchanek 	 */
132*7c0eda1aSMichal Suchanek 	if (next_sp - sp >= sizeof(struct signal_frame_32) &&
133*7c0eda1aSMichal Suchanek 	    is_sigreturn_32_address(next_ip, sp) &&
134*7c0eda1aSMichal Suchanek 	    sane_signal_32_frame(sp)) {
135*7c0eda1aSMichal Suchanek 		sf = (struct signal_frame_32 __user *) (unsigned long) sp;
136*7c0eda1aSMichal Suchanek 		mctx = &sf->mctx;
137*7c0eda1aSMichal Suchanek 	}
138*7c0eda1aSMichal Suchanek 
139*7c0eda1aSMichal Suchanek 	if (!mctx && next_sp - sp >= sizeof(struct rt_signal_frame_32) &&
140*7c0eda1aSMichal Suchanek 	    is_rt_sigreturn_32_address(next_ip, sp) &&
141*7c0eda1aSMichal Suchanek 	    sane_rt_signal_32_frame(sp)) {
142*7c0eda1aSMichal Suchanek 		rt_sf = (struct rt_signal_frame_32 __user *) (unsigned long) sp;
143*7c0eda1aSMichal Suchanek 		mctx = &rt_sf->uc.uc_mcontext;
144*7c0eda1aSMichal Suchanek 	}
145*7c0eda1aSMichal Suchanek 
146*7c0eda1aSMichal Suchanek 	if (!mctx)
147*7c0eda1aSMichal Suchanek 		return NULL;
148*7c0eda1aSMichal Suchanek 	return mctx->mc_gregs;
149*7c0eda1aSMichal Suchanek }
150*7c0eda1aSMichal Suchanek 
151*7c0eda1aSMichal Suchanek void perf_callchain_user_32(struct perf_callchain_entry_ctx *entry,
152*7c0eda1aSMichal Suchanek 			    struct pt_regs *regs)
153*7c0eda1aSMichal Suchanek {
154*7c0eda1aSMichal Suchanek 	unsigned int sp, next_sp;
155*7c0eda1aSMichal Suchanek 	unsigned int next_ip;
156*7c0eda1aSMichal Suchanek 	unsigned int lr;
157*7c0eda1aSMichal Suchanek 	long level = 0;
158*7c0eda1aSMichal Suchanek 	unsigned int __user *fp, *uregs;
159*7c0eda1aSMichal Suchanek 
160*7c0eda1aSMichal Suchanek 	next_ip = perf_instruction_pointer(regs);
161*7c0eda1aSMichal Suchanek 	lr = regs->link;
162*7c0eda1aSMichal Suchanek 	sp = regs->gpr[1];
163*7c0eda1aSMichal Suchanek 	perf_callchain_store(entry, next_ip);
164*7c0eda1aSMichal Suchanek 
165*7c0eda1aSMichal Suchanek 	while (entry->nr < entry->max_stack) {
166*7c0eda1aSMichal Suchanek 		fp = (unsigned int __user *) (unsigned long) sp;
167*7c0eda1aSMichal Suchanek 		if (invalid_user_sp(sp) || read_user_stack_32(fp, &next_sp))
168*7c0eda1aSMichal Suchanek 			return;
169*7c0eda1aSMichal Suchanek 		if (level > 0 && read_user_stack_32(&fp[1], &next_ip))
170*7c0eda1aSMichal Suchanek 			return;
171*7c0eda1aSMichal Suchanek 
172*7c0eda1aSMichal Suchanek 		uregs = signal_frame_32_regs(sp, next_sp, next_ip);
173*7c0eda1aSMichal Suchanek 		if (!uregs && level <= 1)
174*7c0eda1aSMichal Suchanek 			uregs = signal_frame_32_regs(sp, next_sp, lr);
175*7c0eda1aSMichal Suchanek 		if (uregs) {
176*7c0eda1aSMichal Suchanek 			/*
177*7c0eda1aSMichal Suchanek 			 * This looks like an signal frame, so restart
178*7c0eda1aSMichal Suchanek 			 * the stack trace with the values in it.
179*7c0eda1aSMichal Suchanek 			 */
180*7c0eda1aSMichal Suchanek 			if (read_user_stack_32(&uregs[PT_NIP], &next_ip) ||
181*7c0eda1aSMichal Suchanek 			    read_user_stack_32(&uregs[PT_LNK], &lr) ||
182*7c0eda1aSMichal Suchanek 			    read_user_stack_32(&uregs[PT_R1], &sp))
183*7c0eda1aSMichal Suchanek 				return;
184*7c0eda1aSMichal Suchanek 			level = 0;
185*7c0eda1aSMichal Suchanek 			perf_callchain_store_context(entry, PERF_CONTEXT_USER);
186*7c0eda1aSMichal Suchanek 			perf_callchain_store(entry, next_ip);
187*7c0eda1aSMichal Suchanek 			continue;
188*7c0eda1aSMichal Suchanek 		}
189*7c0eda1aSMichal Suchanek 
190*7c0eda1aSMichal Suchanek 		if (level == 0)
191*7c0eda1aSMichal Suchanek 			next_ip = lr;
192*7c0eda1aSMichal Suchanek 		perf_callchain_store(entry, next_ip);
193*7c0eda1aSMichal Suchanek 		++level;
194*7c0eda1aSMichal Suchanek 		sp = next_sp;
195*7c0eda1aSMichal Suchanek 	}
196*7c0eda1aSMichal Suchanek }
197