Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Performance counter callchain support - powerpc architecture code
0004  *
0005  * Copyright © 2009 Paul Mackerras, IBM Corporation.
0006  */
0007 #include <linux/kernel.h>
0008 #include <linux/sched.h>
0009 #include <linux/perf_event.h>
0010 #include <linux/percpu.h>
0011 #include <linux/uaccess.h>
0012 #include <linux/mm.h>
0013 #include <asm/ptrace.h>
0014 #include <asm/sigcontext.h>
0015 #include <asm/ucontext.h>
0016 #include <asm/vdso.h>
0017 #include <asm/pte-walk.h>
0018 
0019 #include "callchain.h"
0020 
0021 static int read_user_stack_64(const unsigned long __user *ptr, unsigned long *ret)
0022 {
0023     return __read_user_stack(ptr, ret, sizeof(*ret));
0024 }
0025 
0026 /*
0027  * 64-bit user processes use the same stack frame for RT and non-RT signals.
0028  */
0029 struct signal_frame_64 {
0030     char        dummy[__SIGNAL_FRAMESIZE];
0031     struct ucontext uc;
0032     unsigned long   unused[2];
0033     unsigned int    tramp[6];
0034     struct siginfo  *pinfo;
0035     void        *puc;
0036     struct siginfo  info;
0037     char        abigap[288];
0038 };
0039 
0040 static int is_sigreturn_64_address(unsigned long nip, unsigned long fp)
0041 {
0042     if (nip == fp + offsetof(struct signal_frame_64, tramp))
0043         return 1;
0044     if (current->mm->context.vdso &&
0045         nip == VDSO64_SYMBOL(current->mm->context.vdso, sigtramp_rt64))
0046         return 1;
0047     return 0;
0048 }
0049 
0050 /*
0051  * Do some sanity checking on the signal frame pointed to by sp.
0052  * We check the pinfo and puc pointers in the frame.
0053  */
0054 static int sane_signal_64_frame(unsigned long sp)
0055 {
0056     struct signal_frame_64 __user *sf;
0057     unsigned long pinfo, puc;
0058 
0059     sf = (struct signal_frame_64 __user *) sp;
0060     if (read_user_stack_64((unsigned long __user *) &sf->pinfo, &pinfo) ||
0061         read_user_stack_64((unsigned long __user *) &sf->puc, &puc))
0062         return 0;
0063     return pinfo == (unsigned long) &sf->info &&
0064         puc == (unsigned long) &sf->uc;
0065 }
0066 
0067 void perf_callchain_user_64(struct perf_callchain_entry_ctx *entry,
0068                 struct pt_regs *regs)
0069 {
0070     unsigned long sp, next_sp;
0071     unsigned long next_ip;
0072     unsigned long lr;
0073     long level = 0;
0074     struct signal_frame_64 __user *sigframe;
0075     unsigned long __user *fp, *uregs;
0076 
0077     next_ip = perf_instruction_pointer(regs);
0078     lr = regs->link;
0079     sp = regs->gpr[1];
0080     perf_callchain_store(entry, next_ip);
0081 
0082     while (entry->nr < entry->max_stack) {
0083         fp = (unsigned long __user *) sp;
0084         if (invalid_user_sp(sp) || read_user_stack_64(fp, &next_sp))
0085             return;
0086         if (level > 0 && read_user_stack_64(&fp[2], &next_ip))
0087             return;
0088 
0089         /*
0090          * Note: the next_sp - sp >= signal frame size check
0091          * is true when next_sp < sp, which can happen when
0092          * transitioning from an alternate signal stack to the
0093          * normal stack.
0094          */
0095         if (next_sp - sp >= sizeof(struct signal_frame_64) &&
0096             (is_sigreturn_64_address(next_ip, sp) ||
0097              (level <= 1 && is_sigreturn_64_address(lr, sp))) &&
0098             sane_signal_64_frame(sp)) {
0099             /*
0100              * This looks like an signal frame
0101              */
0102             sigframe = (struct signal_frame_64 __user *) sp;
0103             uregs = sigframe->uc.uc_mcontext.gp_regs;
0104             if (read_user_stack_64(&uregs[PT_NIP], &next_ip) ||
0105                 read_user_stack_64(&uregs[PT_LNK], &lr) ||
0106                 read_user_stack_64(&uregs[PT_R1], &sp))
0107                 return;
0108             level = 0;
0109             perf_callchain_store_context(entry, PERF_CONTEXT_USER);
0110             perf_callchain_store(entry, next_ip);
0111             continue;
0112         }
0113 
0114         if (level == 0)
0115             next_ip = lr;
0116         perf_callchain_store(entry, next_ip);
0117         ++level;
0118         sp = next_sp;
0119     }
0120 }