Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/perf/callchain_32.c
26439 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
#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, &regs))
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, &regs))
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_arch_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
180