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