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, ®s))
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, ®s))
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