xref: /openbmc/linux/arch/powerpc/perf/callchain_32.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
17c0eda1aSMichal Suchanek // SPDX-License-Identifier: GPL-2.0-or-later
27c0eda1aSMichal Suchanek /*
37c0eda1aSMichal Suchanek  * Performance counter callchain support - powerpc architecture code
47c0eda1aSMichal Suchanek  *
57c0eda1aSMichal Suchanek  * Copyright © 2009 Paul Mackerras, IBM Corporation.
67c0eda1aSMichal Suchanek  */
77c0eda1aSMichal Suchanek #include <linux/kernel.h>
87c0eda1aSMichal Suchanek #include <linux/sched.h>
97c0eda1aSMichal Suchanek #include <linux/perf_event.h>
107c0eda1aSMichal Suchanek #include <linux/percpu.h>
117c0eda1aSMichal Suchanek #include <linux/uaccess.h>
127c0eda1aSMichal Suchanek #include <linux/mm.h>
137c0eda1aSMichal Suchanek #include <asm/ptrace.h>
147c0eda1aSMichal Suchanek #include <asm/sigcontext.h>
157c0eda1aSMichal Suchanek #include <asm/ucontext.h>
167c0eda1aSMichal Suchanek #include <asm/vdso.h>
177c0eda1aSMichal Suchanek #include <asm/pte-walk.h>
187c0eda1aSMichal Suchanek 
197c0eda1aSMichal Suchanek #include "callchain.h"
207c0eda1aSMichal Suchanek 
217c0eda1aSMichal Suchanek #ifdef CONFIG_PPC64
22*8cd1def4SRohan McLure #include <asm/syscalls_32.h>
237c0eda1aSMichal Suchanek #else  /* CONFIG_PPC64 */
247c0eda1aSMichal Suchanek 
257c0eda1aSMichal Suchanek #define __SIGNAL_FRAMESIZE32	__SIGNAL_FRAMESIZE
267c0eda1aSMichal Suchanek #define sigcontext32		sigcontext
277c0eda1aSMichal Suchanek #define mcontext32		mcontext
287c0eda1aSMichal Suchanek #define ucontext32		ucontext
297c0eda1aSMichal Suchanek #define compat_siginfo_t	struct siginfo
307c0eda1aSMichal Suchanek 
317c0eda1aSMichal Suchanek #endif /* CONFIG_PPC64 */
327c0eda1aSMichal Suchanek 
read_user_stack_32(const unsigned int __user * ptr,unsigned int * ret)33d3a133aaSMichal Suchanek static int read_user_stack_32(const unsigned int __user *ptr, unsigned int *ret)
347c0eda1aSMichal Suchanek {
35d3a133aaSMichal Suchanek 	return __read_user_stack(ptr, ret, sizeof(*ret));
367c0eda1aSMichal Suchanek }
377c0eda1aSMichal Suchanek 
387c0eda1aSMichal Suchanek /*
397c0eda1aSMichal Suchanek  * Layout for non-RT signal frames
407c0eda1aSMichal Suchanek  */
417c0eda1aSMichal Suchanek struct signal_frame_32 {
427c0eda1aSMichal Suchanek 	char			dummy[__SIGNAL_FRAMESIZE32];
437c0eda1aSMichal Suchanek 	struct sigcontext32	sctx;
447c0eda1aSMichal Suchanek 	struct mcontext32	mctx;
457c0eda1aSMichal Suchanek 	int			abigap[56];
467c0eda1aSMichal Suchanek };
477c0eda1aSMichal Suchanek 
487c0eda1aSMichal Suchanek /*
497c0eda1aSMichal Suchanek  * Layout for RT signal frames
507c0eda1aSMichal Suchanek  */
517c0eda1aSMichal Suchanek struct rt_signal_frame_32 {
527c0eda1aSMichal Suchanek 	char			dummy[__SIGNAL_FRAMESIZE32 + 16];
537c0eda1aSMichal Suchanek 	compat_siginfo_t	info;
547c0eda1aSMichal Suchanek 	struct ucontext32	uc;
557c0eda1aSMichal Suchanek 	int			abigap[56];
567c0eda1aSMichal Suchanek };
577c0eda1aSMichal Suchanek 
is_sigreturn_32_address(unsigned int nip,unsigned int fp)587c0eda1aSMichal Suchanek static int is_sigreturn_32_address(unsigned int nip, unsigned int fp)
597c0eda1aSMichal Suchanek {
607c0eda1aSMichal Suchanek 	if (nip == fp + offsetof(struct signal_frame_32, mctx.mc_pad))
617c0eda1aSMichal Suchanek 		return 1;
6291bf6955SChristophe Leroy 	if (current->mm->context.vdso &&
6391bf6955SChristophe Leroy 	    nip == VDSO32_SYMBOL(current->mm->context.vdso, sigtramp32))
647c0eda1aSMichal Suchanek 		return 1;
657c0eda1aSMichal Suchanek 	return 0;
667c0eda1aSMichal Suchanek }
677c0eda1aSMichal Suchanek 
is_rt_sigreturn_32_address(unsigned int nip,unsigned int fp)687c0eda1aSMichal Suchanek static int is_rt_sigreturn_32_address(unsigned int nip, unsigned int fp)
697c0eda1aSMichal Suchanek {
707c0eda1aSMichal Suchanek 	if (nip == fp + offsetof(struct rt_signal_frame_32,
717c0eda1aSMichal Suchanek 				 uc.uc_mcontext.mc_pad))
727c0eda1aSMichal Suchanek 		return 1;
7391bf6955SChristophe Leroy 	if (current->mm->context.vdso &&
7491bf6955SChristophe Leroy 	    nip == VDSO32_SYMBOL(current->mm->context.vdso, sigtramp_rt32))
757c0eda1aSMichal Suchanek 		return 1;
767c0eda1aSMichal Suchanek 	return 0;
777c0eda1aSMichal Suchanek }
787c0eda1aSMichal Suchanek 
sane_signal_32_frame(unsigned int sp)797c0eda1aSMichal Suchanek static int sane_signal_32_frame(unsigned int sp)
807c0eda1aSMichal Suchanek {
817c0eda1aSMichal Suchanek 	struct signal_frame_32 __user *sf;
827c0eda1aSMichal Suchanek 	unsigned int regs;
837c0eda1aSMichal Suchanek 
847c0eda1aSMichal Suchanek 	sf = (struct signal_frame_32 __user *) (unsigned long) sp;
857c0eda1aSMichal Suchanek 	if (read_user_stack_32((unsigned int __user *) &sf->sctx.regs, &regs))
867c0eda1aSMichal Suchanek 		return 0;
877c0eda1aSMichal Suchanek 	return regs == (unsigned long) &sf->mctx;
887c0eda1aSMichal Suchanek }
897c0eda1aSMichal Suchanek 
sane_rt_signal_32_frame(unsigned int sp)907c0eda1aSMichal Suchanek static int sane_rt_signal_32_frame(unsigned int sp)
917c0eda1aSMichal Suchanek {
927c0eda1aSMichal Suchanek 	struct rt_signal_frame_32 __user *sf;
937c0eda1aSMichal Suchanek 	unsigned int regs;
947c0eda1aSMichal Suchanek 
957c0eda1aSMichal Suchanek 	sf = (struct rt_signal_frame_32 __user *) (unsigned long) sp;
967c0eda1aSMichal Suchanek 	if (read_user_stack_32((unsigned int __user *) &sf->uc.uc_regs, &regs))
977c0eda1aSMichal Suchanek 		return 0;
987c0eda1aSMichal Suchanek 	return regs == (unsigned long) &sf->uc.uc_mcontext;
997c0eda1aSMichal Suchanek }
1007c0eda1aSMichal Suchanek 
signal_frame_32_regs(unsigned int sp,unsigned int next_sp,unsigned int next_ip)1017c0eda1aSMichal Suchanek static unsigned int __user *signal_frame_32_regs(unsigned int sp,
1027c0eda1aSMichal Suchanek 				unsigned int next_sp, unsigned int next_ip)
1037c0eda1aSMichal Suchanek {
1047c0eda1aSMichal Suchanek 	struct mcontext32 __user *mctx = NULL;
1057c0eda1aSMichal Suchanek 	struct signal_frame_32 __user *sf;
1067c0eda1aSMichal Suchanek 	struct rt_signal_frame_32 __user *rt_sf;
1077c0eda1aSMichal Suchanek 
1087c0eda1aSMichal Suchanek 	/*
1097c0eda1aSMichal Suchanek 	 * Note: the next_sp - sp >= signal frame size check
1107c0eda1aSMichal Suchanek 	 * is true when next_sp < sp, for example, when
1117c0eda1aSMichal Suchanek 	 * transitioning from an alternate signal stack to the
1127c0eda1aSMichal Suchanek 	 * normal stack.
1137c0eda1aSMichal Suchanek 	 */
1147c0eda1aSMichal Suchanek 	if (next_sp - sp >= sizeof(struct signal_frame_32) &&
1157c0eda1aSMichal Suchanek 	    is_sigreturn_32_address(next_ip, sp) &&
1167c0eda1aSMichal Suchanek 	    sane_signal_32_frame(sp)) {
1177c0eda1aSMichal Suchanek 		sf = (struct signal_frame_32 __user *) (unsigned long) sp;
1187c0eda1aSMichal Suchanek 		mctx = &sf->mctx;
1197c0eda1aSMichal Suchanek 	}
1207c0eda1aSMichal Suchanek 
1217c0eda1aSMichal Suchanek 	if (!mctx && next_sp - sp >= sizeof(struct rt_signal_frame_32) &&
1227c0eda1aSMichal Suchanek 	    is_rt_sigreturn_32_address(next_ip, sp) &&
1237c0eda1aSMichal Suchanek 	    sane_rt_signal_32_frame(sp)) {
1247c0eda1aSMichal Suchanek 		rt_sf = (struct rt_signal_frame_32 __user *) (unsigned long) sp;
1257c0eda1aSMichal Suchanek 		mctx = &rt_sf->uc.uc_mcontext;
1267c0eda1aSMichal Suchanek 	}
1277c0eda1aSMichal Suchanek 
1287c0eda1aSMichal Suchanek 	if (!mctx)
1297c0eda1aSMichal Suchanek 		return NULL;
1307c0eda1aSMichal Suchanek 	return mctx->mc_gregs;
1317c0eda1aSMichal Suchanek }
1327c0eda1aSMichal Suchanek 
perf_callchain_user_32(struct perf_callchain_entry_ctx * entry,struct pt_regs * regs)1337c0eda1aSMichal Suchanek void perf_callchain_user_32(struct perf_callchain_entry_ctx *entry,
1347c0eda1aSMichal Suchanek 			    struct pt_regs *regs)
1357c0eda1aSMichal Suchanek {
1367c0eda1aSMichal Suchanek 	unsigned int sp, next_sp;
1377c0eda1aSMichal Suchanek 	unsigned int next_ip;
1387c0eda1aSMichal Suchanek 	unsigned int lr;
1397c0eda1aSMichal Suchanek 	long level = 0;
1407c0eda1aSMichal Suchanek 	unsigned int __user *fp, *uregs;
1417c0eda1aSMichal Suchanek 
1427c0eda1aSMichal Suchanek 	next_ip = perf_instruction_pointer(regs);
1437c0eda1aSMichal Suchanek 	lr = regs->link;
1447c0eda1aSMichal Suchanek 	sp = regs->gpr[1];
1457c0eda1aSMichal Suchanek 	perf_callchain_store(entry, next_ip);
1467c0eda1aSMichal Suchanek 
1477c0eda1aSMichal Suchanek 	while (entry->nr < entry->max_stack) {
1487c0eda1aSMichal Suchanek 		fp = (unsigned int __user *) (unsigned long) sp;
1497c0eda1aSMichal Suchanek 		if (invalid_user_sp(sp) || read_user_stack_32(fp, &next_sp))
1507c0eda1aSMichal Suchanek 			return;
1517c0eda1aSMichal Suchanek 		if (level > 0 && read_user_stack_32(&fp[1], &next_ip))
1527c0eda1aSMichal Suchanek 			return;
1537c0eda1aSMichal Suchanek 
1547c0eda1aSMichal Suchanek 		uregs = signal_frame_32_regs(sp, next_sp, next_ip);
1557c0eda1aSMichal Suchanek 		if (!uregs && level <= 1)
1567c0eda1aSMichal Suchanek 			uregs = signal_frame_32_regs(sp, next_sp, lr);
1577c0eda1aSMichal Suchanek 		if (uregs) {
1587c0eda1aSMichal Suchanek 			/*
1597c0eda1aSMichal Suchanek 			 * This looks like an signal frame, so restart
1607c0eda1aSMichal Suchanek 			 * the stack trace with the values in it.
1617c0eda1aSMichal Suchanek 			 */
1627c0eda1aSMichal Suchanek 			if (read_user_stack_32(&uregs[PT_NIP], &next_ip) ||
1637c0eda1aSMichal Suchanek 			    read_user_stack_32(&uregs[PT_LNK], &lr) ||
1647c0eda1aSMichal Suchanek 			    read_user_stack_32(&uregs[PT_R1], &sp))
1657c0eda1aSMichal Suchanek 				return;
1667c0eda1aSMichal Suchanek 			level = 0;
1677c0eda1aSMichal Suchanek 			perf_callchain_store_context(entry, PERF_CONTEXT_USER);
1687c0eda1aSMichal Suchanek 			perf_callchain_store(entry, next_ip);
1697c0eda1aSMichal Suchanek 			continue;
1707c0eda1aSMichal Suchanek 		}
1717c0eda1aSMichal Suchanek 
1727c0eda1aSMichal Suchanek 		if (level == 0)
1737c0eda1aSMichal Suchanek 			next_ip = lr;
1747c0eda1aSMichal Suchanek 		perf_callchain_store(entry, next_ip);
1757c0eda1aSMichal Suchanek 		++level;
1767c0eda1aSMichal Suchanek 		sp = next_sp;
1777c0eda1aSMichal Suchanek 	}
1787c0eda1aSMichal Suchanek }
179