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 33 static 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 */ 41 struct 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 */ 51 struct 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 58 static 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 68 static 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 79 static 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 90 static 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 101 static 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 133 void 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 } 179
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.