Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
2 | /* ptrace.c: Sparc process tracing support. |
3 | * | |
8e3fe806 | 4 | * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net) |
1da177e4 LT |
5 | * |
6 | * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, | |
7 | * and David Mosberger. | |
8 | * | |
5b2afff2 | 9 | * Added Linux support -miguel (weird, eh?, the original code was meant |
1da177e4 LT |
10 | * to emulate SunOS). |
11 | */ | |
12 | ||
13 | #include <linux/kernel.h> | |
14 | #include <linux/sched.h> | |
15 | #include <linux/mm.h> | |
16 | #include <linux/errno.h> | |
17 | #include <linux/ptrace.h> | |
18 | #include <linux/user.h> | |
19 | #include <linux/smp.h> | |
1da177e4 | 20 | #include <linux/security.h> |
7ed20e1a | 21 | #include <linux/signal.h> |
8e3fe806 DM |
22 | #include <linux/regset.h> |
23 | #include <linux/elf.h> | |
1c133b4b | 24 | #include <linux/tracehook.h> |
1da177e4 | 25 | |
7c0f6ba6 | 26 | #include <linux/uaccess.h> |
d550bbd4 | 27 | #include <asm/cacheflush.h> |
1da177e4 | 28 | |
c8c8782d SR |
29 | #include "kernel.h" |
30 | ||
1da177e4 | 31 | /* #define ALLOW_INIT_TRACING */ |
1da177e4 LT |
32 | |
33 | /* | |
34 | * Called by kernel/ptrace.c when detaching.. | |
35 | * | |
36 | * Make sure single step bits etc are not set. | |
37 | */ | |
38 | void ptrace_disable(struct task_struct *child) | |
39 | { | |
40 | /* nothing to do */ | |
41 | } | |
42 | ||
8e3fe806 DM |
43 | enum sparc_regset { |
44 | REGSET_GENERAL, | |
45 | REGSET_FP, | |
46 | }; | |
47 | ||
cf51e129 AV |
48 | static int regwindow32_get(struct task_struct *target, |
49 | const struct pt_regs *regs, | |
50 | u32 *uregs) | |
51 | { | |
52 | unsigned long reg_window = regs->u_regs[UREG_I6]; | |
53 | int size = 16 * sizeof(u32); | |
54 | ||
55 | if (target == current) { | |
56 | if (copy_from_user(uregs, (void __user *)reg_window, size)) | |
57 | return -EFAULT; | |
58 | } else { | |
59 | if (access_process_vm(target, reg_window, uregs, size, | |
60 | FOLL_FORCE) != size) | |
61 | return -EFAULT; | |
62 | } | |
63 | return 0; | |
64 | } | |
65 | ||
66 | static int regwindow32_set(struct task_struct *target, | |
67 | const struct pt_regs *regs, | |
68 | u32 *uregs) | |
69 | { | |
70 | unsigned long reg_window = regs->u_regs[UREG_I6]; | |
71 | int size = 16 * sizeof(u32); | |
72 | ||
73 | if (target == current) { | |
74 | if (copy_to_user((void __user *)reg_window, uregs, size)) | |
75 | return -EFAULT; | |
76 | } else { | |
77 | if (access_process_vm(target, reg_window, uregs, size, | |
78 | FOLL_FORCE | FOLL_WRITE) != size) | |
79 | return -EFAULT; | |
80 | } | |
81 | return 0; | |
82 | } | |
83 | ||
8e3fe806 DM |
84 | static int genregs32_get(struct task_struct *target, |
85 | const struct user_regset *regset, | |
4d617aaa | 86 | struct membuf to) |
8e3fe806 DM |
87 | { |
88 | const struct pt_regs *regs = target->thread.kregs; | |
cf51e129 | 89 | u32 uregs[16]; |
8e3fe806 DM |
90 | |
91 | if (target == current) | |
92 | flush_user_windows(); | |
93 | ||
4d617aaa AV |
94 | membuf_write(&to, regs->u_regs, 16 * sizeof(u32)); |
95 | if (!to.left) | |
96 | return 0; | |
87d80533 AV |
97 | if (regwindow32_get(target, regs, uregs)) |
98 | return -EFAULT; | |
4d617aaa AV |
99 | membuf_write(&to, uregs, 16 * sizeof(u32)); |
100 | membuf_store(&to, regs->psr); | |
101 | membuf_store(&to, regs->pc); | |
102 | membuf_store(&to, regs->npc); | |
103 | membuf_store(&to, regs->y); | |
104 | return membuf_zero(&to, 2 * sizeof(u32)); | |
8e3fe806 DM |
105 | } |
106 | ||
107 | static int genregs32_set(struct task_struct *target, | |
108 | const struct user_regset *regset, | |
109 | unsigned int pos, unsigned int count, | |
110 | const void *kbuf, const void __user *ubuf) | |
111 | { | |
112 | struct pt_regs *regs = target->thread.kregs; | |
cf51e129 AV |
113 | u32 uregs[16]; |
114 | u32 psr; | |
115 | int ret; | |
8e3fe806 DM |
116 | |
117 | if (target == current) | |
118 | flush_user_windows(); | |
119 | ||
cf51e129 AV |
120 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
121 | regs->u_regs, | |
122 | 0, 16 * sizeof(u32)); | |
123 | if (ret || !count) | |
124 | return ret; | |
8e3fe806 | 125 | |
98a7fbf3 AV |
126 | if (regwindow32_get(target, regs, uregs)) |
127 | return -EFAULT; | |
128 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
129 | uregs, | |
130 | 16 * sizeof(u32), 32 * sizeof(u32)); | |
131 | if (ret) | |
132 | return ret; | |
133 | if (regwindow32_set(target, regs, uregs)) | |
134 | return -EFAULT; | |
135 | if (!count) | |
136 | return 0; | |
137 | ||
cf51e129 AV |
138 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
139 | &psr, | |
140 | 32 * sizeof(u32), 33 * sizeof(u32)); | |
141 | if (ret) | |
142 | return ret; | |
143 | regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) | | |
144 | (psr & (PSR_ICC | PSR_SYSCALL)); | |
145 | if (!count) | |
146 | return 0; | |
147 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
148 | ®s->pc, | |
149 | 33 * sizeof(u32), 34 * sizeof(u32)); | |
150 | if (ret || !count) | |
151 | return ret; | |
152 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
9d964e1b | 153 | ®s->npc, |
cf51e129 AV |
154 | 34 * sizeof(u32), 35 * sizeof(u32)); |
155 | if (ret || !count) | |
156 | return ret; | |
9d964e1b AV |
157 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
158 | ®s->y, | |
159 | 35 * sizeof(u32), 36 * sizeof(u32)); | |
160 | if (ret || !count) | |
161 | return ret; | |
8e3fe806 | 162 | return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
9d964e1b | 163 | 36 * sizeof(u32), 38 * sizeof(u32)); |
8e3fe806 DM |
164 | } |
165 | ||
166 | static int fpregs32_get(struct task_struct *target, | |
167 | const struct user_regset *regset, | |
4d617aaa | 168 | struct membuf to) |
8e3fe806 | 169 | { |
8e3fe806 DM |
170 | #if 0 |
171 | if (target == current) | |
172 | save_and_clear_fpu(); | |
173 | #endif | |
174 | ||
4d617aaa AV |
175 | membuf_write(&to, target->thread.float_regs, 32 * sizeof(u32)); |
176 | membuf_zero(&to, sizeof(u32)); | |
177 | membuf_write(&to, &target->thread.fsr, sizeof(u32)); | |
178 | membuf_store(&to, (u32)((1 << 8) | (8 << 16))); | |
179 | return membuf_zero(&to, 64 * sizeof(u32)); | |
8e3fe806 DM |
180 | } |
181 | ||
182 | static int fpregs32_set(struct task_struct *target, | |
183 | const struct user_regset *regset, | |
184 | unsigned int pos, unsigned int count, | |
185 | const void *kbuf, const void __user *ubuf) | |
186 | { | |
187 | unsigned long *fpregs = target->thread.float_regs; | |
188 | int ret; | |
189 | ||
190 | #if 0 | |
191 | if (target == current) | |
192 | save_and_clear_fpu(); | |
193 | #endif | |
194 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
195 | fpregs, | |
196 | 0, 32 * sizeof(u32)); | |
197 | if (!ret) | |
198 | user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
199 | 32 * sizeof(u32), | |
200 | 33 * sizeof(u32)); | |
98a7fbf3 | 201 | if (!ret) |
8e3fe806 DM |
202 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
203 | &target->thread.fsr, | |
204 | 33 * sizeof(u32), | |
205 | 34 * sizeof(u32)); | |
8e3fe806 DM |
206 | if (!ret) |
207 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
208 | 34 * sizeof(u32), -1); | |
209 | return ret; | |
210 | } | |
211 | ||
212 | static const struct user_regset sparc32_regsets[] = { | |
213 | /* Format is: | |
214 | * G0 --> G7 | |
215 | * O0 --> O7 | |
216 | * L0 --> L7 | |
217 | * I0 --> I7 | |
218 | * PSR, PC, nPC, Y, WIM, TBR | |
219 | */ | |
220 | [REGSET_GENERAL] = { | |
221 | .core_note_type = NT_PRSTATUS, | |
7d4ee289 | 222 | .n = 38, |
8e3fe806 | 223 | .size = sizeof(u32), .align = sizeof(u32), |
4d617aaa | 224 | .regset_get = genregs32_get, .set = genregs32_set |
8e3fe806 DM |
225 | }, |
226 | /* Format is: | |
227 | * F0 --> F31 | |
228 | * empty 32-bit word | |
229 | * FSR (32--bit word) | |
230 | * FPU QUEUE COUNT (8-bit char) | |
231 | * FPU QUEUE ENTRYSIZE (8-bit char) | |
232 | * FPU ENABLED (8-bit char) | |
233 | * empty 8-bit char | |
234 | * FPU QUEUE (64 32-bit ints) | |
235 | */ | |
236 | [REGSET_FP] = { | |
237 | .core_note_type = NT_PRFPREG, | |
7d4ee289 | 238 | .n = 99, |
8e3fe806 | 239 | .size = sizeof(u32), .align = sizeof(u32), |
4d617aaa | 240 | .regset_get = fpregs32_get, .set = fpregs32_set |
8e3fe806 DM |
241 | }, |
242 | }; | |
243 | ||
87d80533 AV |
244 | static int getregs_get(struct task_struct *target, |
245 | const struct user_regset *regset, | |
4d617aaa | 246 | struct membuf to) |
87d80533 AV |
247 | { |
248 | const struct pt_regs *regs = target->thread.kregs; | |
87d80533 AV |
249 | |
250 | if (target == current) | |
251 | flush_user_windows(); | |
252 | ||
4d617aaa AV |
253 | membuf_store(&to, regs->psr); |
254 | membuf_store(&to, regs->pc); | |
255 | membuf_store(&to, regs->npc); | |
256 | membuf_store(&to, regs->y); | |
257 | return membuf_write(&to, regs->u_regs + 1, 15 * sizeof(u32)); | |
87d80533 AV |
258 | } |
259 | ||
98a7fbf3 AV |
260 | static int setregs_set(struct task_struct *target, |
261 | const struct user_regset *regset, | |
262 | unsigned int pos, unsigned int count, | |
263 | const void *kbuf, const void __user *ubuf) | |
264 | { | |
265 | struct pt_regs *regs = target->thread.kregs; | |
266 | u32 v[4]; | |
267 | int ret; | |
268 | ||
269 | if (target == current) | |
270 | flush_user_windows(); | |
271 | ||
272 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
273 | v, | |
274 | 0, 4 * sizeof(u32)); | |
275 | if (ret) | |
276 | return ret; | |
277 | regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) | | |
278 | (v[0] & (PSR_ICC | PSR_SYSCALL)); | |
279 | regs->pc = v[1]; | |
280 | regs->npc = v[2]; | |
281 | regs->y = v[3]; | |
282 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
283 | regs->u_regs + 1, | |
284 | 4 * sizeof(u32) , 19 * sizeof(u32)); | |
285 | } | |
286 | ||
87d80533 AV |
287 | static int getfpregs_get(struct task_struct *target, |
288 | const struct user_regset *regset, | |
4d617aaa | 289 | struct membuf to) |
87d80533 | 290 | { |
87d80533 AV |
291 | #if 0 |
292 | if (target == current) | |
293 | save_and_clear_fpu(); | |
294 | #endif | |
4d617aaa AV |
295 | membuf_write(&to, &target->thread.float_regs, 32 * sizeof(u32)); |
296 | membuf_write(&to, &target->thread.fsr, sizeof(u32)); | |
297 | return membuf_zero(&to, 35 * sizeof(u32)); | |
87d80533 AV |
298 | } |
299 | ||
98a7fbf3 AV |
300 | static int setfpregs_set(struct task_struct *target, |
301 | const struct user_regset *regset, | |
302 | unsigned int pos, unsigned int count, | |
303 | const void *kbuf, const void __user *ubuf) | |
304 | { | |
305 | unsigned long *fpregs = target->thread.float_regs; | |
306 | int ret; | |
307 | ||
308 | #if 0 | |
309 | if (target == current) | |
310 | save_and_clear_fpu(); | |
311 | #endif | |
312 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
313 | fpregs, | |
314 | 0, 32 * sizeof(u32)); | |
315 | if (ret) | |
316 | return ret; | |
317 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
318 | &target->thread.fsr, | |
319 | 32 * sizeof(u32), | |
320 | 33 * sizeof(u32)); | |
321 | } | |
322 | ||
87d80533 AV |
323 | static const struct user_regset ptrace32_regsets[] = { |
324 | [REGSET_GENERAL] = { | |
98a7fbf3 | 325 | .n = 19, .size = sizeof(u32), |
4d617aaa | 326 | .regset_get = getregs_get, .set = setregs_set, |
87d80533 AV |
327 | }, |
328 | [REGSET_FP] = { | |
98a7fbf3 | 329 | .n = 68, .size = sizeof(u32), |
4d617aaa | 330 | .regset_get = getfpregs_get, .set = setfpregs_set, |
87d80533 AV |
331 | }, |
332 | }; | |
333 | ||
334 | static const struct user_regset_view ptrace32_view = { | |
335 | .regsets = ptrace32_regsets, .n = ARRAY_SIZE(ptrace32_regsets) | |
336 | }; | |
337 | ||
8e3fe806 DM |
338 | static const struct user_regset_view user_sparc32_view = { |
339 | .name = "sparc", .e_machine = EM_SPARC, | |
340 | .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) | |
341 | }; | |
342 | ||
343 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | |
344 | { | |
345 | return &user_sparc32_view; | |
346 | } | |
347 | ||
a9384e23 NK |
348 | struct fps { |
349 | unsigned long regs[32]; | |
350 | unsigned long fsr; | |
351 | unsigned long flags; | |
352 | unsigned long extra; | |
353 | unsigned long fpqd; | |
354 | struct fq { | |
355 | unsigned long *insnaddr; | |
356 | unsigned long insn; | |
357 | } fpq[16]; | |
358 | }; | |
359 | ||
9b05a69e NK |
360 | long arch_ptrace(struct task_struct *child, long request, |
361 | unsigned long addr, unsigned long data) | |
1da177e4 | 362 | { |
9775369e | 363 | unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4]; |
a9384e23 | 364 | void __user *addr2p; |
a9384e23 NK |
365 | struct pt_regs __user *pregs; |
366 | struct fps __user *fps; | |
d256eb8d DM |
367 | int ret; |
368 | ||
a9384e23 NK |
369 | addr2p = (void __user *) addr2; |
370 | pregs = (struct pt_regs __user *) addr; | |
371 | fps = (struct fps __user *) addr; | |
1da177e4 LT |
372 | |
373 | switch(request) { | |
1da177e4 | 374 | case PTRACE_GETREGS: { |
87d80533 AV |
375 | ret = copy_regset_to_user(child, &ptrace32_view, |
376 | REGSET_GENERAL, 0, | |
377 | 19 * sizeof(u32), | |
378 | pregs); | |
9775369e | 379 | break; |
1da177e4 LT |
380 | } |
381 | ||
382 | case PTRACE_SETREGS: { | |
98a7fbf3 AV |
383 | ret = copy_regset_from_user(child, &ptrace32_view, |
384 | REGSET_GENERAL, 0, | |
385 | 19 * sizeof(u32), | |
386 | pregs); | |
9775369e | 387 | break; |
1da177e4 LT |
388 | } |
389 | ||
390 | case PTRACE_GETFPREGS: { | |
87d80533 AV |
391 | ret = copy_regset_to_user(child, &ptrace32_view, |
392 | REGSET_FP, 0, | |
393 | 68 * sizeof(u32), | |
394 | fps); | |
9775369e | 395 | break; |
1da177e4 LT |
396 | } |
397 | ||
398 | case PTRACE_SETFPREGS: { | |
98a7fbf3 AV |
399 | ret = copy_regset_from_user(child, &ptrace32_view, |
400 | REGSET_FP, 0, | |
401 | 33 * sizeof(u32), | |
402 | fps); | |
9775369e | 403 | break; |
1da177e4 LT |
404 | } |
405 | ||
406 | case PTRACE_READTEXT: | |
9775369e | 407 | case PTRACE_READDATA: |
a9384e23 | 408 | ret = ptrace_readdata(child, addr, addr2p, data); |
9775369e DM |
409 | |
410 | if (ret == data) | |
411 | ret = 0; | |
412 | else if (ret >= 0) | |
413 | ret = -EIO; | |
414 | break; | |
1da177e4 LT |
415 | |
416 | case PTRACE_WRITETEXT: | |
9775369e | 417 | case PTRACE_WRITEDATA: |
a9384e23 | 418 | ret = ptrace_writedata(child, addr2p, addr, data); |
9775369e DM |
419 | |
420 | if (ret == data) | |
421 | ret = 0; | |
422 | else if (ret >= 0) | |
423 | ret = -EIO; | |
424 | break; | |
1da177e4 | 425 | |
9775369e | 426 | default: |
986bef85 DM |
427 | if (request == PTRACE_SPARC_DETACH) |
428 | request = PTRACE_DETACH; | |
9775369e DM |
429 | ret = ptrace_request(child, request, addr, data); |
430 | break; | |
1da177e4 LT |
431 | } |
432 | ||
9775369e | 433 | return ret; |
1da177e4 LT |
434 | } |
435 | ||
1c133b4b | 436 | asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p) |
1da177e4 | 437 | { |
1c133b4b DM |
438 | int ret = 0; |
439 | ||
440 | if (test_thread_flag(TIF_SYSCALL_TRACE)) { | |
441 | if (syscall_exit_p) | |
442 | tracehook_report_syscall_exit(regs, 0); | |
443 | else | |
444 | ret = tracehook_report_syscall_entry(regs); | |
1da177e4 | 445 | } |
1c133b4b DM |
446 | |
447 | return ret; | |
1da177e4 | 448 | } |