Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/perf/callchain_64.c
26424 views
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
static int read_user_stack_64(const unsigned long __user *ptr, unsigned long *ret)
22
{
23
return __read_user_stack(ptr, ret, sizeof(*ret));
24
}
25
26
/*
27
* 64-bit user processes use the same stack frame for RT and non-RT signals.
28
*/
29
struct signal_frame_64 {
30
char dummy[__SIGNAL_FRAMESIZE];
31
struct ucontext uc;
32
unsigned long unused[2];
33
unsigned int tramp[6];
34
struct siginfo *pinfo;
35
void *puc;
36
struct siginfo info;
37
char abigap[288];
38
};
39
40
static int is_sigreturn_64_address(unsigned long nip, unsigned long fp)
41
{
42
if (nip == fp + offsetof(struct signal_frame_64, tramp))
43
return 1;
44
if (current->mm->context.vdso &&
45
nip == VDSO64_SYMBOL(current->mm->context.vdso, sigtramp_rt64))
46
return 1;
47
return 0;
48
}
49
50
/*
51
* Do some sanity checking on the signal frame pointed to by sp.
52
* We check the pinfo and puc pointers in the frame.
53
*/
54
static int sane_signal_64_frame(unsigned long sp)
55
{
56
struct signal_frame_64 __user *sf;
57
unsigned long pinfo, puc;
58
59
sf = (struct signal_frame_64 __user *) sp;
60
if (read_user_stack_64((unsigned long __user *) &sf->pinfo, &pinfo) ||
61
read_user_stack_64((unsigned long __user *) &sf->puc, &puc))
62
return 0;
63
return pinfo == (unsigned long) &sf->info &&
64
puc == (unsigned long) &sf->uc;
65
}
66
67
void perf_callchain_user_64(struct perf_callchain_entry_ctx *entry,
68
struct pt_regs *regs)
69
{
70
unsigned long sp, next_sp;
71
unsigned long next_ip;
72
unsigned long lr;
73
long level = 0;
74
struct signal_frame_64 __user *sigframe;
75
unsigned long __user *fp, *uregs;
76
77
next_ip = perf_arch_instruction_pointer(regs);
78
lr = regs->link;
79
sp = regs->gpr[1];
80
perf_callchain_store(entry, next_ip);
81
82
while (entry->nr < entry->max_stack) {
83
fp = (unsigned long __user *) sp;
84
if (invalid_user_sp(sp) || read_user_stack_64(fp, &next_sp))
85
return;
86
if (level > 0 && read_user_stack_64(&fp[2], &next_ip))
87
return;
88
89
/*
90
* Note: the next_sp - sp >= signal frame size check
91
* is true when next_sp < sp, which can happen when
92
* transitioning from an alternate signal stack to the
93
* normal stack.
94
*/
95
if (next_sp - sp >= sizeof(struct signal_frame_64) &&
96
(is_sigreturn_64_address(next_ip, sp) ||
97
(level <= 1 && is_sigreturn_64_address(lr, sp))) &&
98
sane_signal_64_frame(sp)) {
99
/*
100
* This looks like an signal frame
101
*/
102
sigframe = (struct signal_frame_64 __user *) sp;
103
uregs = sigframe->uc.uc_mcontext.gp_regs;
104
if (read_user_stack_64(&uregs[PT_NIP], &next_ip) ||
105
read_user_stack_64(&uregs[PT_LNK], &lr) ||
106
read_user_stack_64(&uregs[PT_R1], &sp))
107
return;
108
level = 0;
109
perf_callchain_store_context(entry, PERF_CONTEXT_USER);
110
perf_callchain_store(entry, next_ip);
111
continue;
112
}
113
114
if (level == 0)
115
next_ip = lr;
116
perf_callchain_store(entry, next_ip);
117
++level;
118
sp = next_sp;
119
}
120
}
121
122