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