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, | |
86 | unsigned int pos, unsigned int count, | |
87 | void *kbuf, void __user *ubuf) | |
88 | { | |
89 | const struct pt_regs *regs = target->thread.kregs; | |
cf51e129 AV |
90 | u32 uregs[16]; |
91 | int ret; | |
8e3fe806 DM |
92 | |
93 | if (target == current) | |
94 | flush_user_windows(); | |
95 | ||
cf51e129 AV |
96 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
97 | regs->u_regs, | |
98 | 0, 16 * sizeof(u32)); | |
99 | if (ret || !count) | |
100 | return ret; | |
8e3fe806 | 101 | |
87d80533 AV |
102 | if (regwindow32_get(target, regs, uregs)) |
103 | return -EFAULT; | |
104 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
105 | uregs, | |
106 | 16 * sizeof(u32), 32 * sizeof(u32)); | |
107 | if (ret) | |
108 | return ret; | |
8e3fe806 | 109 | |
cf51e129 AV |
110 | uregs[0] = regs->psr; |
111 | uregs[1] = regs->pc; | |
112 | uregs[2] = regs->npc; | |
113 | uregs[3] = regs->y; | |
114 | uregs[4] = 0; /* WIM */ | |
115 | uregs[5] = 0; /* TBR */ | |
116 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
117 | uregs, | |
118 | 32 * sizeof(u32), 38 * sizeof(u32)); | |
8e3fe806 DM |
119 | } |
120 | ||
121 | static int genregs32_set(struct task_struct *target, | |
122 | const struct user_regset *regset, | |
123 | unsigned int pos, unsigned int count, | |
124 | const void *kbuf, const void __user *ubuf) | |
125 | { | |
126 | struct pt_regs *regs = target->thread.kregs; | |
cf51e129 AV |
127 | u32 uregs[16]; |
128 | u32 psr; | |
129 | int ret; | |
8e3fe806 DM |
130 | |
131 | if (target == current) | |
132 | flush_user_windows(); | |
133 | ||
cf51e129 AV |
134 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
135 | regs->u_regs, | |
136 | 0, 16 * sizeof(u32)); | |
137 | if (ret || !count) | |
138 | return ret; | |
8e3fe806 | 139 | |
98a7fbf3 AV |
140 | if (regwindow32_get(target, regs, uregs)) |
141 | return -EFAULT; | |
142 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
143 | uregs, | |
144 | 16 * sizeof(u32), 32 * sizeof(u32)); | |
145 | if (ret) | |
146 | return ret; | |
147 | if (regwindow32_set(target, regs, uregs)) | |
148 | return -EFAULT; | |
149 | if (!count) | |
150 | return 0; | |
151 | ||
cf51e129 AV |
152 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
153 | &psr, | |
154 | 32 * sizeof(u32), 33 * sizeof(u32)); | |
155 | if (ret) | |
156 | return ret; | |
157 | regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) | | |
158 | (psr & (PSR_ICC | PSR_SYSCALL)); | |
159 | if (!count) | |
160 | return 0; | |
161 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
162 | ®s->pc, | |
163 | 33 * sizeof(u32), 34 * sizeof(u32)); | |
164 | if (ret || !count) | |
165 | return ret; | |
166 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
9d964e1b | 167 | ®s->npc, |
cf51e129 AV |
168 | 34 * sizeof(u32), 35 * sizeof(u32)); |
169 | if (ret || !count) | |
170 | return ret; | |
9d964e1b AV |
171 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
172 | ®s->y, | |
173 | 35 * sizeof(u32), 36 * sizeof(u32)); | |
174 | if (ret || !count) | |
175 | return ret; | |
8e3fe806 | 176 | return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
9d964e1b | 177 | 36 * sizeof(u32), 38 * sizeof(u32)); |
8e3fe806 DM |
178 | } |
179 | ||
180 | static int fpregs32_get(struct task_struct *target, | |
181 | const struct user_regset *regset, | |
182 | unsigned int pos, unsigned int count, | |
183 | void *kbuf, void __user *ubuf) | |
184 | { | |
185 | const unsigned long *fpregs = target->thread.float_regs; | |
186 | int ret = 0; | |
187 | ||
188 | #if 0 | |
189 | if (target == current) | |
190 | save_and_clear_fpu(); | |
191 | #endif | |
192 | ||
193 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
194 | fpregs, | |
195 | 0, 32 * sizeof(u32)); | |
196 | ||
197 | if (!ret) | |
198 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
199 | 32 * sizeof(u32), | |
200 | 33 * sizeof(u32)); | |
201 | if (!ret) | |
202 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
203 | &target->thread.fsr, | |
204 | 33 * sizeof(u32), | |
205 | 34 * sizeof(u32)); | |
206 | ||
207 | if (!ret) { | |
208 | unsigned long val; | |
209 | ||
210 | val = (1 << 8) | (8 << 16); | |
211 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
212 | &val, | |
213 | 34 * sizeof(u32), | |
214 | 35 * sizeof(u32)); | |
215 | } | |
216 | ||
217 | if (!ret) | |
218 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
219 | 35 * sizeof(u32), -1); | |
220 | ||
221 | return ret; | |
222 | } | |
223 | ||
224 | static int fpregs32_set(struct task_struct *target, | |
225 | const struct user_regset *regset, | |
226 | unsigned int pos, unsigned int count, | |
227 | const void *kbuf, const void __user *ubuf) | |
228 | { | |
229 | unsigned long *fpregs = target->thread.float_regs; | |
230 | int ret; | |
231 | ||
232 | #if 0 | |
233 | if (target == current) | |
234 | save_and_clear_fpu(); | |
235 | #endif | |
236 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
237 | fpregs, | |
238 | 0, 32 * sizeof(u32)); | |
239 | if (!ret) | |
240 | user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
241 | 32 * sizeof(u32), | |
242 | 33 * sizeof(u32)); | |
98a7fbf3 | 243 | if (!ret) |
8e3fe806 DM |
244 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
245 | &target->thread.fsr, | |
246 | 33 * sizeof(u32), | |
247 | 34 * sizeof(u32)); | |
8e3fe806 DM |
248 | if (!ret) |
249 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
250 | 34 * sizeof(u32), -1); | |
251 | return ret; | |
252 | } | |
253 | ||
254 | static const struct user_regset sparc32_regsets[] = { | |
255 | /* Format is: | |
256 | * G0 --> G7 | |
257 | * O0 --> O7 | |
258 | * L0 --> L7 | |
259 | * I0 --> I7 | |
260 | * PSR, PC, nPC, Y, WIM, TBR | |
261 | */ | |
262 | [REGSET_GENERAL] = { | |
263 | .core_note_type = NT_PRSTATUS, | |
7d4ee289 | 264 | .n = 38, |
8e3fe806 DM |
265 | .size = sizeof(u32), .align = sizeof(u32), |
266 | .get = genregs32_get, .set = genregs32_set | |
267 | }, | |
268 | /* Format is: | |
269 | * F0 --> F31 | |
270 | * empty 32-bit word | |
271 | * FSR (32--bit word) | |
272 | * FPU QUEUE COUNT (8-bit char) | |
273 | * FPU QUEUE ENTRYSIZE (8-bit char) | |
274 | * FPU ENABLED (8-bit char) | |
275 | * empty 8-bit char | |
276 | * FPU QUEUE (64 32-bit ints) | |
277 | */ | |
278 | [REGSET_FP] = { | |
279 | .core_note_type = NT_PRFPREG, | |
7d4ee289 | 280 | .n = 99, |
8e3fe806 DM |
281 | .size = sizeof(u32), .align = sizeof(u32), |
282 | .get = fpregs32_get, .set = fpregs32_set | |
283 | }, | |
284 | }; | |
285 | ||
87d80533 AV |
286 | static int getregs_get(struct task_struct *target, |
287 | const struct user_regset *regset, | |
288 | unsigned int pos, unsigned int count, | |
289 | void *kbuf, void __user *ubuf) | |
290 | { | |
291 | const struct pt_regs *regs = target->thread.kregs; | |
292 | u32 v[4]; | |
293 | int ret; | |
294 | ||
295 | if (target == current) | |
296 | flush_user_windows(); | |
297 | ||
298 | v[0] = regs->psr; | |
299 | v[1] = regs->pc; | |
300 | v[2] = regs->npc; | |
301 | v[3] = regs->y; | |
302 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
303 | v, | |
304 | 0 * sizeof(u32), 4 * sizeof(u32)); | |
305 | if (ret) | |
306 | return ret; | |
307 | ||
308 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
309 | regs->u_regs + 1, | |
310 | 4 * sizeof(u32), 19 * sizeof(u32)); | |
311 | } | |
312 | ||
98a7fbf3 AV |
313 | static int setregs_set(struct task_struct *target, |
314 | const struct user_regset *regset, | |
315 | unsigned int pos, unsigned int count, | |
316 | const void *kbuf, const void __user *ubuf) | |
317 | { | |
318 | struct pt_regs *regs = target->thread.kregs; | |
319 | u32 v[4]; | |
320 | int ret; | |
321 | ||
322 | if (target == current) | |
323 | flush_user_windows(); | |
324 | ||
325 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
326 | v, | |
327 | 0, 4 * sizeof(u32)); | |
328 | if (ret) | |
329 | return ret; | |
330 | regs->psr = (regs->psr & ~(PSR_ICC | PSR_SYSCALL)) | | |
331 | (v[0] & (PSR_ICC | PSR_SYSCALL)); | |
332 | regs->pc = v[1]; | |
333 | regs->npc = v[2]; | |
334 | regs->y = v[3]; | |
335 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
336 | regs->u_regs + 1, | |
337 | 4 * sizeof(u32) , 19 * sizeof(u32)); | |
338 | } | |
339 | ||
87d80533 AV |
340 | static int getfpregs_get(struct task_struct *target, |
341 | const struct user_regset *regset, | |
342 | unsigned int pos, unsigned int count, | |
343 | void *kbuf, void __user *ubuf) | |
344 | { | |
345 | const unsigned long *fpregs = target->thread.float_regs; | |
346 | int ret = 0; | |
347 | ||
348 | #if 0 | |
349 | if (target == current) | |
350 | save_and_clear_fpu(); | |
351 | #endif | |
352 | ||
353 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
354 | fpregs, | |
355 | 0, 32 * sizeof(u32)); | |
356 | if (ret) | |
357 | return ret; | |
358 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
359 | &target->thread.fsr, | |
360 | 32 * sizeof(u32), 33 * sizeof(u32)); | |
361 | if (ret) | |
362 | return ret; | |
363 | return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
364 | 33 * sizeof(u32), 68 * sizeof(u32)); | |
365 | } | |
366 | ||
98a7fbf3 AV |
367 | static int setfpregs_set(struct task_struct *target, |
368 | const struct user_regset *regset, | |
369 | unsigned int pos, unsigned int count, | |
370 | const void *kbuf, const void __user *ubuf) | |
371 | { | |
372 | unsigned long *fpregs = target->thread.float_regs; | |
373 | int ret; | |
374 | ||
375 | #if 0 | |
376 | if (target == current) | |
377 | save_and_clear_fpu(); | |
378 | #endif | |
379 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
380 | fpregs, | |
381 | 0, 32 * sizeof(u32)); | |
382 | if (ret) | |
383 | return ret; | |
384 | return user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
385 | &target->thread.fsr, | |
386 | 32 * sizeof(u32), | |
387 | 33 * sizeof(u32)); | |
388 | } | |
389 | ||
87d80533 AV |
390 | static const struct user_regset ptrace32_regsets[] = { |
391 | [REGSET_GENERAL] = { | |
98a7fbf3 AV |
392 | .n = 19, .size = sizeof(u32), |
393 | .get = getregs_get, .set = setregs_set, | |
87d80533 AV |
394 | }, |
395 | [REGSET_FP] = { | |
98a7fbf3 AV |
396 | .n = 68, .size = sizeof(u32), |
397 | .get = getfpregs_get, .set = setfpregs_set, | |
87d80533 AV |
398 | }, |
399 | }; | |
400 | ||
401 | static const struct user_regset_view ptrace32_view = { | |
402 | .regsets = ptrace32_regsets, .n = ARRAY_SIZE(ptrace32_regsets) | |
403 | }; | |
404 | ||
8e3fe806 DM |
405 | static const struct user_regset_view user_sparc32_view = { |
406 | .name = "sparc", .e_machine = EM_SPARC, | |
407 | .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) | |
408 | }; | |
409 | ||
410 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | |
411 | { | |
412 | return &user_sparc32_view; | |
413 | } | |
414 | ||
a9384e23 NK |
415 | struct fps { |
416 | unsigned long regs[32]; | |
417 | unsigned long fsr; | |
418 | unsigned long flags; | |
419 | unsigned long extra; | |
420 | unsigned long fpqd; | |
421 | struct fq { | |
422 | unsigned long *insnaddr; | |
423 | unsigned long insn; | |
424 | } fpq[16]; | |
425 | }; | |
426 | ||
9b05a69e NK |
427 | long arch_ptrace(struct task_struct *child, long request, |
428 | unsigned long addr, unsigned long data) | |
1da177e4 | 429 | { |
9775369e | 430 | unsigned long addr2 = current->thread.kregs->u_regs[UREG_I4]; |
a9384e23 | 431 | void __user *addr2p; |
a9384e23 NK |
432 | struct pt_regs __user *pregs; |
433 | struct fps __user *fps; | |
d256eb8d DM |
434 | int ret; |
435 | ||
a9384e23 NK |
436 | addr2p = (void __user *) addr2; |
437 | pregs = (struct pt_regs __user *) addr; | |
438 | fps = (struct fps __user *) addr; | |
1da177e4 LT |
439 | |
440 | switch(request) { | |
1da177e4 | 441 | case PTRACE_GETREGS: { |
87d80533 AV |
442 | ret = copy_regset_to_user(child, &ptrace32_view, |
443 | REGSET_GENERAL, 0, | |
444 | 19 * sizeof(u32), | |
445 | pregs); | |
9775369e | 446 | break; |
1da177e4 LT |
447 | } |
448 | ||
449 | case PTRACE_SETREGS: { | |
98a7fbf3 AV |
450 | ret = copy_regset_from_user(child, &ptrace32_view, |
451 | REGSET_GENERAL, 0, | |
452 | 19 * sizeof(u32), | |
453 | pregs); | |
9775369e | 454 | break; |
1da177e4 LT |
455 | } |
456 | ||
457 | case PTRACE_GETFPREGS: { | |
87d80533 AV |
458 | ret = copy_regset_to_user(child, &ptrace32_view, |
459 | REGSET_FP, 0, | |
460 | 68 * sizeof(u32), | |
461 | fps); | |
9775369e | 462 | break; |
1da177e4 LT |
463 | } |
464 | ||
465 | case PTRACE_SETFPREGS: { | |
98a7fbf3 AV |
466 | ret = copy_regset_from_user(child, &ptrace32_view, |
467 | REGSET_FP, 0, | |
468 | 33 * sizeof(u32), | |
469 | fps); | |
9775369e | 470 | break; |
1da177e4 LT |
471 | } |
472 | ||
473 | case PTRACE_READTEXT: | |
9775369e | 474 | case PTRACE_READDATA: |
a9384e23 | 475 | ret = ptrace_readdata(child, addr, addr2p, data); |
9775369e DM |
476 | |
477 | if (ret == data) | |
478 | ret = 0; | |
479 | else if (ret >= 0) | |
480 | ret = -EIO; | |
481 | break; | |
1da177e4 LT |
482 | |
483 | case PTRACE_WRITETEXT: | |
9775369e | 484 | case PTRACE_WRITEDATA: |
a9384e23 | 485 | ret = ptrace_writedata(child, addr2p, addr, data); |
9775369e DM |
486 | |
487 | if (ret == data) | |
488 | ret = 0; | |
489 | else if (ret >= 0) | |
490 | ret = -EIO; | |
491 | break; | |
1da177e4 | 492 | |
9775369e | 493 | default: |
986bef85 DM |
494 | if (request == PTRACE_SPARC_DETACH) |
495 | request = PTRACE_DETACH; | |
9775369e DM |
496 | ret = ptrace_request(child, request, addr, data); |
497 | break; | |
1da177e4 LT |
498 | } |
499 | ||
9775369e | 500 | return ret; |
1da177e4 LT |
501 | } |
502 | ||
1c133b4b | 503 | asmlinkage int syscall_trace(struct pt_regs *regs, int syscall_exit_p) |
1da177e4 | 504 | { |
1c133b4b DM |
505 | int ret = 0; |
506 | ||
507 | if (test_thread_flag(TIF_SYSCALL_TRACE)) { | |
508 | if (syscall_exit_p) | |
509 | tracehook_report_syscall_exit(regs, 0); | |
510 | else | |
511 | ret = tracehook_report_syscall_entry(regs); | |
1da177e4 | 512 | } |
1c133b4b DM |
513 | |
514 | return ret; | |
1da177e4 | 515 | } |