callchain_32.c (4630B)
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 "../kernel/ppc32.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, ®s)) 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, ®s)) 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}