1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Performance counter callchain support - powerpc architecture code
5 * Copyright © 2009 Paul Mackerras, IBM Corporation.
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>
13 #include <asm/ptrace.h>
14 #include <asm/sigcontext.h>
15 #include <asm/ucontext.h>
17 #include <asm/pte-walk.h>
19 #include "callchain.h"
22 #include "../kernel/ppc32.h"
23 #else /* CONFIG_PPC64 */
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
31 #endif /* CONFIG_PPC64 */
33 static int read_user_stack_32(const unsigned int __user
*ptr
, unsigned int *ret
)
35 return __read_user_stack(ptr
, ret
, sizeof(*ret
));
39 * Layout for non-RT signal frames
41 struct signal_frame_32
{
42 char dummy
[__SIGNAL_FRAMESIZE32
];
43 struct sigcontext32 sctx
;
44 struct mcontext32 mctx
;
49 * Layout for RT signal frames
51 struct rt_signal_frame_32
{
52 char dummy
[__SIGNAL_FRAMESIZE32
+ 16];
53 compat_siginfo_t info
;
58 static int is_sigreturn_32_address(unsigned int nip
, unsigned int fp
)
60 if (nip
== fp
+ offsetof(struct signal_frame_32
, mctx
.mc_pad
))
62 if (current
->mm
->context
.vdso
&&
63 nip
== VDSO32_SYMBOL(current
->mm
->context
.vdso
, sigtramp32
))
68 static int is_rt_sigreturn_32_address(unsigned int nip
, unsigned int fp
)
70 if (nip
== fp
+ offsetof(struct rt_signal_frame_32
,
71 uc
.uc_mcontext
.mc_pad
))
73 if (current
->mm
->context
.vdso
&&
74 nip
== VDSO32_SYMBOL(current
->mm
->context
.vdso
, sigtramp_rt32
))
79 static int sane_signal_32_frame(unsigned int sp
)
81 struct signal_frame_32 __user
*sf
;
84 sf
= (struct signal_frame_32 __user
*) (unsigned long) sp
;
85 if (read_user_stack_32((unsigned int __user
*) &sf
->sctx
.regs
, ®s
))
87 return regs
== (unsigned long) &sf
->mctx
;
90 static int sane_rt_signal_32_frame(unsigned int sp
)
92 struct rt_signal_frame_32 __user
*sf
;
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
))
98 return regs
== (unsigned long) &sf
->uc
.uc_mcontext
;
101 static unsigned int __user
*signal_frame_32_regs(unsigned int sp
,
102 unsigned int next_sp
, unsigned int next_ip
)
104 struct mcontext32 __user
*mctx
= NULL
;
105 struct signal_frame_32 __user
*sf
;
106 struct rt_signal_frame_32 __user
*rt_sf
;
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
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
;
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
;
130 return mctx
->mc_gregs
;
133 void perf_callchain_user_32(struct perf_callchain_entry_ctx
*entry
,
134 struct pt_regs
*regs
)
136 unsigned int sp
, next_sp
;
137 unsigned int next_ip
;
140 unsigned int __user
*fp
, *uregs
;
142 next_ip
= perf_instruction_pointer(regs
);
145 perf_callchain_store(entry
, next_ip
);
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
))
151 if (level
> 0 && read_user_stack_32(&fp
[1], &next_ip
))
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
);
159 * This looks like an signal frame, so restart
160 * the stack trace with the values in it.
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
))
167 perf_callchain_store_context(entry
, PERF_CONTEXT_USER
);
168 perf_callchain_store(entry
, next_ip
);
174 perf_callchain_store(entry
, next_ip
);