Linux Audio

Check our new training course

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