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 227c0eda1aSMichal Suchanek #include "../kernel/ppc32.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 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 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; 62*91bf6955SChristophe Leroy if (current->mm->context.vdso && 63*91bf6955SChristophe Leroy nip == VDSO32_SYMBOL(current->mm->context.vdso, sigtramp32)) 647c0eda1aSMichal Suchanek return 1; 657c0eda1aSMichal Suchanek return 0; 667c0eda1aSMichal Suchanek } 677c0eda1aSMichal Suchanek 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; 73*91bf6955SChristophe Leroy if (current->mm->context.vdso && 74*91bf6955SChristophe Leroy nip == VDSO32_SYMBOL(current->mm->context.vdso, sigtramp_rt32)) 757c0eda1aSMichal Suchanek return 1; 767c0eda1aSMichal Suchanek return 0; 777c0eda1aSMichal Suchanek } 787c0eda1aSMichal Suchanek 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 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 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 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