Commit | Line | Data |
---|---|---|
7c0eda1a MS |
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> | |
7c0eda1a MS |
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 | |
8cd1def4 | 22 | #include <asm/syscalls_32.h> |
7c0eda1a MS |
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 | ||
d3a133aa | 33 | static int read_user_stack_32(const unsigned int __user *ptr, unsigned int *ret) |
7c0eda1a | 34 | { |
d3a133aa | 35 | return __read_user_stack(ptr, ret, sizeof(*ret)); |
7c0eda1a MS |
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; | |
91bf6955 CL |
62 | if (current->mm->context.vdso && |
63 | nip == VDSO32_SYMBOL(current->mm->context.vdso, sigtramp32)) | |
7c0eda1a MS |
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; | |
91bf6955 CL |
73 | if (current->mm->context.vdso && |
74 | nip == VDSO32_SYMBOL(current->mm->context.vdso, sigtramp_rt32)) | |
7c0eda1a MS |
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, ®s)) | |
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, ®s)) | |
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_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 | } |