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