]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* ptrace.c: Sparc process tracing support. |
2 | * | |
d09c2a23 | 3 | * Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net) |
1da177e4 LT |
4 | * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) |
5 | * | |
6 | * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, | |
7 | * and David Mosberger. | |
8 | * | |
9 | * Added Linux support -miguel (weird, eh?, the original code was meant | |
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> |
f7ceba36 DM |
21 | #include <linux/seccomp.h> |
22 | #include <linux/audit.h> | |
7ed20e1a | 23 | #include <linux/signal.h> |
d09c2a23 | 24 | #include <linux/regset.h> |
73ccefab | 25 | #include <linux/tracehook.h> |
c658ad1b | 26 | #include <trace/syscall.h> |
d09c2a23 DM |
27 | #include <linux/compat.h> |
28 | #include <linux/elf.h> | |
1da177e4 LT |
29 | |
30 | #include <asm/asi.h> | |
31 | #include <asm/pgtable.h> | |
1da177e4 LT |
32 | #include <asm/uaccess.h> |
33 | #include <asm/psrcompat.h> | |
34 | #include <asm/visasm.h> | |
35 | #include <asm/spitfire.h> | |
6a9b490d | 36 | #include <asm/page.h> |
717463d8 | 37 | #include <asm/cpudata.h> |
bfdf9ebc DM |
38 | #include <asm/cacheflush.h> |
39 | ||
c658ad1b DM |
40 | #define CREATE_TRACE_POINTS |
41 | #include <trace/events/syscalls.h> | |
42 | ||
bfdf9ebc | 43 | #include "entry.h" |
1da177e4 | 44 | |
1da177e4 | 45 | /* #define ALLOW_INIT_TRACING */ |
1da177e4 LT |
46 | |
47 | /* | |
48 | * Called by kernel/ptrace.c when detaching.. | |
49 | * | |
50 | * Make sure single step bits etc are not set. | |
51 | */ | |
52 | void ptrace_disable(struct task_struct *child) | |
53 | { | |
54 | /* nothing to do */ | |
55 | } | |
56 | ||
dadeafdf DM |
57 | /* To get the necessary page struct, access_process_vm() first calls |
58 | * get_user_pages(). This has done a flush_dcache_page() on the | |
59 | * accessed page. Then our caller (copy_{to,from}_user_page()) did | |
60 | * to memcpy to read/write the data from that page. | |
61 | * | |
62 | * Now, the only thing we have to do is: | |
63 | * 1) flush the D-cache if it's possible than an illegal alias | |
64 | * has been created | |
65 | * 2) flush the I-cache if this is pre-cheetah and we did a write | |
66 | */ | |
67 | void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, | |
68 | unsigned long uaddr, void *kaddr, | |
69 | unsigned long len, int write) | |
70 | { | |
71 | BUG_ON(len > PAGE_SIZE); | |
72 | ||
7adb37fe DM |
73 | if (tlb_type == hypervisor) |
74 | return; | |
75 | ||
f6a843d9 DM |
76 | preempt_disable(); |
77 | ||
dadeafdf DM |
78 | #ifdef DCACHE_ALIASING_POSSIBLE |
79 | /* If bit 13 of the kernel address we used to access the | |
80 | * user page is the same as the virtual address that page | |
81 | * is mapped to in the user's address space, we can skip the | |
82 | * D-cache flush. | |
83 | */ | |
6a9b490d | 84 | if ((uaddr ^ (unsigned long) kaddr) & (1UL << 13)) { |
dadeafdf DM |
85 | unsigned long start = __pa(kaddr); |
86 | unsigned long end = start + len; | |
717463d8 DM |
87 | unsigned long dcache_line_size; |
88 | ||
89 | dcache_line_size = local_cpu_data().dcache_line_size; | |
dadeafdf DM |
90 | |
91 | if (tlb_type == spitfire) { | |
717463d8 | 92 | for (; start < end; start += dcache_line_size) |
6a9b490d | 93 | spitfire_put_dcache_tag(start & 0x3fe0, 0x0); |
dadeafdf | 94 | } else { |
717463d8 DM |
95 | start &= ~(dcache_line_size - 1); |
96 | for (; start < end; start += dcache_line_size) | |
dadeafdf DM |
97 | __asm__ __volatile__( |
98 | "stxa %%g0, [%0] %1\n\t" | |
99 | "membar #Sync" | |
100 | : /* no outputs */ | |
6a9b490d | 101 | : "r" (start), |
dadeafdf DM |
102 | "i" (ASI_DCACHE_INVALIDATE)); |
103 | } | |
104 | } | |
105 | #endif | |
106 | if (write && tlb_type == spitfire) { | |
107 | unsigned long start = (unsigned long) kaddr; | |
108 | unsigned long end = start + len; | |
717463d8 DM |
109 | unsigned long icache_line_size; |
110 | ||
111 | icache_line_size = local_cpu_data().icache_line_size; | |
dadeafdf | 112 | |
717463d8 | 113 | for (; start < end; start += icache_line_size) |
dadeafdf DM |
114 | flushi(start); |
115 | } | |
f6a843d9 DM |
116 | |
117 | preempt_enable(); | |
dadeafdf DM |
118 | } |
119 | ||
d786a4a6 DM |
120 | static int get_from_target(struct task_struct *target, unsigned long uaddr, |
121 | void *kbuf, int len) | |
122 | { | |
123 | if (target == current) { | |
124 | if (copy_from_user(kbuf, (void __user *) uaddr, len)) | |
125 | return -EFAULT; | |
126 | } else { | |
127 | int len2 = access_process_vm(target, uaddr, kbuf, len, 0); | |
128 | if (len2 != len) | |
129 | return -EFAULT; | |
130 | } | |
131 | return 0; | |
132 | } | |
133 | ||
134 | static int set_to_target(struct task_struct *target, unsigned long uaddr, | |
135 | void *kbuf, int len) | |
136 | { | |
137 | if (target == current) { | |
138 | if (copy_to_user((void __user *) uaddr, kbuf, len)) | |
139 | return -EFAULT; | |
140 | } else { | |
141 | int len2 = access_process_vm(target, uaddr, kbuf, len, 1); | |
142 | if (len2 != len) | |
143 | return -EFAULT; | |
144 | } | |
145 | return 0; | |
146 | } | |
147 | ||
148 | static int regwindow64_get(struct task_struct *target, | |
149 | const struct pt_regs *regs, | |
150 | struct reg_window *wbuf) | |
151 | { | |
152 | unsigned long rw_addr = regs->u_regs[UREG_I6]; | |
153 | ||
517ffce4 | 154 | if (!test_thread_64bit_stack(rw_addr)) { |
d786a4a6 DM |
155 | struct reg_window32 win32; |
156 | int i; | |
157 | ||
158 | if (get_from_target(target, rw_addr, &win32, sizeof(win32))) | |
159 | return -EFAULT; | |
160 | for (i = 0; i < 8; i++) | |
161 | wbuf->locals[i] = win32.locals[i]; | |
162 | for (i = 0; i < 8; i++) | |
163 | wbuf->ins[i] = win32.ins[i]; | |
164 | } else { | |
165 | rw_addr += STACK_BIAS; | |
166 | if (get_from_target(target, rw_addr, wbuf, sizeof(*wbuf))) | |
167 | return -EFAULT; | |
168 | } | |
169 | ||
170 | return 0; | |
171 | } | |
172 | ||
173 | static int regwindow64_set(struct task_struct *target, | |
174 | const struct pt_regs *regs, | |
175 | struct reg_window *wbuf) | |
176 | { | |
177 | unsigned long rw_addr = regs->u_regs[UREG_I6]; | |
178 | ||
517ffce4 | 179 | if (!test_thread_64bit_stack(rw_addr)) { |
d786a4a6 DM |
180 | struct reg_window32 win32; |
181 | int i; | |
182 | ||
183 | for (i = 0; i < 8; i++) | |
184 | win32.locals[i] = wbuf->locals[i]; | |
185 | for (i = 0; i < 8; i++) | |
186 | win32.ins[i] = wbuf->ins[i]; | |
187 | ||
188 | if (set_to_target(target, rw_addr, &win32, sizeof(win32))) | |
189 | return -EFAULT; | |
190 | } else { | |
191 | rw_addr += STACK_BIAS; | |
192 | if (set_to_target(target, rw_addr, wbuf, sizeof(*wbuf))) | |
193 | return -EFAULT; | |
194 | } | |
195 | ||
196 | return 0; | |
197 | } | |
198 | ||
d09c2a23 DM |
199 | enum sparc_regset { |
200 | REGSET_GENERAL, | |
201 | REGSET_FP, | |
202 | }; | |
203 | ||
204 | static int genregs64_get(struct task_struct *target, | |
205 | const struct user_regset *regset, | |
206 | unsigned int pos, unsigned int count, | |
207 | void *kbuf, void __user *ubuf) | |
208 | { | |
209 | const struct pt_regs *regs = task_pt_regs(target); | |
210 | int ret; | |
211 | ||
212 | if (target == current) | |
213 | flushw_user(); | |
214 | ||
215 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
216 | regs->u_regs, | |
217 | 0, 16 * sizeof(u64)); | |
d786a4a6 DM |
218 | if (!ret && count && pos < (32 * sizeof(u64))) { |
219 | struct reg_window window; | |
d09c2a23 | 220 | |
d786a4a6 DM |
221 | if (regwindow64_get(target, regs, &window)) |
222 | return -EFAULT; | |
d09c2a23 | 223 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
d786a4a6 | 224 | &window, |
d09c2a23 DM |
225 | 16 * sizeof(u64), |
226 | 32 * sizeof(u64)); | |
227 | } | |
228 | ||
229 | if (!ret) { | |
230 | /* TSTATE, TPC, TNPC */ | |
231 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
232 | ®s->tstate, | |
233 | 32 * sizeof(u64), | |
234 | 35 * sizeof(u64)); | |
235 | } | |
236 | ||
237 | if (!ret) { | |
238 | unsigned long y = regs->y; | |
239 | ||
240 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
241 | &y, | |
242 | 35 * sizeof(u64), | |
243 | 36 * sizeof(u64)); | |
244 | } | |
245 | ||
d786a4a6 | 246 | if (!ret) { |
d09c2a23 DM |
247 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, |
248 | 36 * sizeof(u64), -1); | |
249 | ||
d786a4a6 | 250 | } |
d09c2a23 DM |
251 | return ret; |
252 | } | |
253 | ||
254 | static int genregs64_set(struct task_struct *target, | |
255 | const struct user_regset *regset, | |
256 | unsigned int pos, unsigned int count, | |
257 | const void *kbuf, const void __user *ubuf) | |
258 | { | |
259 | struct pt_regs *regs = task_pt_regs(target); | |
260 | int ret; | |
261 | ||
262 | if (target == current) | |
263 | flushw_user(); | |
264 | ||
265 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
266 | regs->u_regs, | |
267 | 0, 16 * sizeof(u64)); | |
d786a4a6 DM |
268 | if (!ret && count && pos < (32 * sizeof(u64))) { |
269 | struct reg_window window; | |
d09c2a23 | 270 | |
d786a4a6 DM |
271 | if (regwindow64_get(target, regs, &window)) |
272 | return -EFAULT; | |
d09c2a23 DM |
273 | |
274 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
d786a4a6 | 275 | &window, |
d09c2a23 DM |
276 | 16 * sizeof(u64), |
277 | 32 * sizeof(u64)); | |
d786a4a6 DM |
278 | |
279 | if (!ret && | |
280 | regwindow64_set(target, regs, &window)) | |
281 | return -EFAULT; | |
d09c2a23 DM |
282 | } |
283 | ||
284 | if (!ret && count > 0) { | |
285 | unsigned long tstate; | |
286 | ||
287 | /* TSTATE */ | |
288 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
289 | &tstate, | |
290 | 32 * sizeof(u64), | |
291 | 33 * sizeof(u64)); | |
292 | if (!ret) { | |
28e61036 DM |
293 | /* Only the condition codes and the "in syscall" |
294 | * state can be modified in the %tstate register. | |
d09c2a23 | 295 | */ |
28e61036 DM |
296 | tstate &= (TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL); |
297 | regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL); | |
d09c2a23 DM |
298 | regs->tstate |= tstate; |
299 | } | |
300 | } | |
301 | ||
302 | if (!ret) { | |
303 | /* TPC, TNPC */ | |
304 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
305 | ®s->tpc, | |
306 | 33 * sizeof(u64), | |
307 | 35 * sizeof(u64)); | |
308 | } | |
309 | ||
310 | if (!ret) { | |
311 | unsigned long y; | |
312 | ||
313 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
314 | &y, | |
315 | 35 * sizeof(u64), | |
316 | 36 * sizeof(u64)); | |
317 | if (!ret) | |
318 | regs->y = y; | |
319 | } | |
320 | ||
321 | if (!ret) | |
322 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
323 | 36 * sizeof(u64), -1); | |
324 | ||
325 | return ret; | |
326 | } | |
327 | ||
328 | static int fpregs64_get(struct task_struct *target, | |
329 | const struct user_regset *regset, | |
330 | unsigned int pos, unsigned int count, | |
331 | void *kbuf, void __user *ubuf) | |
332 | { | |
333 | const unsigned long *fpregs = task_thread_info(target)->fpregs; | |
334 | unsigned long fprs, fsr, gsr; | |
335 | int ret; | |
336 | ||
337 | if (target == current) | |
338 | save_and_clear_fpu(); | |
339 | ||
340 | fprs = task_thread_info(target)->fpsaved[0]; | |
341 | ||
342 | if (fprs & FPRS_DL) | |
343 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
344 | fpregs, | |
345 | 0, 16 * sizeof(u64)); | |
346 | else | |
347 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
348 | 0, | |
349 | 16 * sizeof(u64)); | |
350 | ||
351 | if (!ret) { | |
352 | if (fprs & FPRS_DU) | |
353 | ret = user_regset_copyout(&pos, &count, | |
354 | &kbuf, &ubuf, | |
355 | fpregs + 16, | |
356 | 16 * sizeof(u64), | |
357 | 32 * sizeof(u64)); | |
358 | else | |
359 | ret = user_regset_copyout_zero(&pos, &count, | |
360 | &kbuf, &ubuf, | |
361 | 16 * sizeof(u64), | |
362 | 32 * sizeof(u64)); | |
363 | } | |
364 | ||
365 | if (fprs & FPRS_FEF) { | |
366 | fsr = task_thread_info(target)->xfsr[0]; | |
367 | gsr = task_thread_info(target)->gsr[0]; | |
368 | } else { | |
369 | fsr = gsr = 0; | |
370 | } | |
371 | ||
372 | if (!ret) | |
373 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
374 | &fsr, | |
375 | 32 * sizeof(u64), | |
376 | 33 * sizeof(u64)); | |
377 | if (!ret) | |
378 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
379 | &gsr, | |
380 | 33 * sizeof(u64), | |
381 | 34 * sizeof(u64)); | |
382 | if (!ret) | |
383 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
384 | &fprs, | |
385 | 34 * sizeof(u64), | |
386 | 35 * sizeof(u64)); | |
387 | ||
388 | if (!ret) | |
389 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
390 | 35 * sizeof(u64), -1); | |
391 | ||
392 | return ret; | |
393 | } | |
394 | ||
395 | static int fpregs64_set(struct task_struct *target, | |
396 | const struct user_regset *regset, | |
397 | unsigned int pos, unsigned int count, | |
398 | const void *kbuf, const void __user *ubuf) | |
399 | { | |
400 | unsigned long *fpregs = task_thread_info(target)->fpregs; | |
401 | unsigned long fprs; | |
402 | int ret; | |
403 | ||
404 | if (target == current) | |
405 | save_and_clear_fpu(); | |
406 | ||
407 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
408 | fpregs, | |
409 | 0, 32 * sizeof(u64)); | |
410 | if (!ret) | |
411 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
412 | task_thread_info(target)->xfsr, | |
413 | 32 * sizeof(u64), | |
414 | 33 * sizeof(u64)); | |
415 | if (!ret) | |
416 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
417 | task_thread_info(target)->gsr, | |
418 | 33 * sizeof(u64), | |
419 | 34 * sizeof(u64)); | |
420 | ||
421 | fprs = task_thread_info(target)->fpsaved[0]; | |
422 | if (!ret && count > 0) { | |
423 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
424 | &fprs, | |
425 | 34 * sizeof(u64), | |
426 | 35 * sizeof(u64)); | |
427 | } | |
428 | ||
429 | fprs |= (FPRS_FEF | FPRS_DL | FPRS_DU); | |
430 | task_thread_info(target)->fpsaved[0] = fprs; | |
431 | ||
432 | if (!ret) | |
433 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
434 | 35 * sizeof(u64), -1); | |
435 | return ret; | |
436 | } | |
437 | ||
438 | static const struct user_regset sparc64_regsets[] = { | |
439 | /* Format is: | |
440 | * G0 --> G7 | |
441 | * O0 --> O7 | |
442 | * L0 --> L7 | |
443 | * I0 --> I7 | |
444 | * TSTATE, TPC, TNPC, Y | |
445 | */ | |
446 | [REGSET_GENERAL] = { | |
447 | .core_note_type = NT_PRSTATUS, | |
3c503701 | 448 | .n = 36, |
d09c2a23 DM |
449 | .size = sizeof(u64), .align = sizeof(u64), |
450 | .get = genregs64_get, .set = genregs64_set | |
451 | }, | |
452 | /* Format is: | |
453 | * F0 --> F63 | |
454 | * FSR | |
455 | * GSR | |
456 | * FPRS | |
457 | */ | |
458 | [REGSET_FP] = { | |
459 | .core_note_type = NT_PRFPREG, | |
3c503701 | 460 | .n = 35, |
d09c2a23 DM |
461 | .size = sizeof(u64), .align = sizeof(u64), |
462 | .get = fpregs64_get, .set = fpregs64_set | |
463 | }, | |
464 | }; | |
465 | ||
466 | static const struct user_regset_view user_sparc64_view = { | |
467 | .name = "sparc64", .e_machine = EM_SPARCV9, | |
468 | .regsets = sparc64_regsets, .n = ARRAY_SIZE(sparc64_regsets) | |
469 | }; | |
470 | ||
11cc8a3a | 471 | #ifdef CONFIG_COMPAT |
d09c2a23 DM |
472 | static int genregs32_get(struct task_struct *target, |
473 | const struct user_regset *regset, | |
474 | unsigned int pos, unsigned int count, | |
475 | void *kbuf, void __user *ubuf) | |
476 | { | |
477 | const struct pt_regs *regs = task_pt_regs(target); | |
478 | compat_ulong_t __user *reg_window; | |
479 | compat_ulong_t *k = kbuf; | |
480 | compat_ulong_t __user *u = ubuf; | |
481 | compat_ulong_t reg; | |
482 | ||
483 | if (target == current) | |
484 | flushw_user(); | |
485 | ||
486 | pos /= sizeof(reg); | |
487 | count /= sizeof(reg); | |
488 | ||
489 | if (kbuf) { | |
490 | for (; count > 0 && pos < 16; count--) | |
491 | *k++ = regs->u_regs[pos++]; | |
492 | ||
493 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; | |
b857bd29 | 494 | reg_window -= 16; |
ad4f9576 DM |
495 | if (target == current) { |
496 | for (; count > 0 && pos < 32; count--) { | |
497 | if (get_user(*k++, ®_window[pos++])) | |
498 | return -EFAULT; | |
499 | } | |
500 | } else { | |
501 | for (; count > 0 && pos < 32; count--) { | |
502 | if (access_process_vm(target, | |
503 | (unsigned long) | |
504 | ®_window[pos], | |
505 | k, sizeof(*k), 0) | |
506 | != sizeof(*k)) | |
507 | return -EFAULT; | |
508 | k++; | |
509 | pos++; | |
510 | } | |
d09c2a23 DM |
511 | } |
512 | } else { | |
513 | for (; count > 0 && pos < 16; count--) { | |
514 | if (put_user((compat_ulong_t) regs->u_regs[pos++], u++)) | |
515 | return -EFAULT; | |
516 | } | |
517 | ||
518 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; | |
b857bd29 | 519 | reg_window -= 16; |
ad4f9576 DM |
520 | if (target == current) { |
521 | for (; count > 0 && pos < 32; count--) { | |
522 | if (get_user(reg, ®_window[pos++]) || | |
523 | put_user(reg, u++)) | |
524 | return -EFAULT; | |
525 | } | |
526 | } else { | |
527 | for (; count > 0 && pos < 32; count--) { | |
528 | if (access_process_vm(target, | |
529 | (unsigned long) | |
530 | ®_window[pos], | |
531 | ®, sizeof(reg), 0) | |
532 | != sizeof(reg)) | |
533 | return -EFAULT; | |
534 | if (access_process_vm(target, | |
535 | (unsigned long) u, | |
536 | ®, sizeof(reg), 1) | |
537 | != sizeof(reg)) | |
538 | return -EFAULT; | |
539 | pos++; | |
540 | u++; | |
541 | } | |
d09c2a23 DM |
542 | } |
543 | } | |
544 | while (count > 0) { | |
545 | switch (pos) { | |
546 | case 32: /* PSR */ | |
547 | reg = tstate_to_psr(regs->tstate); | |
548 | break; | |
549 | case 33: /* PC */ | |
550 | reg = regs->tpc; | |
551 | break; | |
552 | case 34: /* NPC */ | |
553 | reg = regs->tnpc; | |
554 | break; | |
555 | case 35: /* Y */ | |
556 | reg = regs->y; | |
557 | break; | |
558 | case 36: /* WIM */ | |
559 | case 37: /* TBR */ | |
560 | reg = 0; | |
561 | break; | |
562 | default: | |
563 | goto finish; | |
564 | } | |
565 | ||
566 | if (kbuf) | |
567 | *k++ = reg; | |
568 | else if (put_user(reg, u++)) | |
569 | return -EFAULT; | |
570 | pos++; | |
571 | count--; | |
572 | } | |
573 | finish: | |
574 | pos *= sizeof(reg); | |
575 | count *= sizeof(reg); | |
576 | ||
577 | return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
578 | 38 * sizeof(reg), -1); | |
579 | } | |
580 | ||
581 | static int genregs32_set(struct task_struct *target, | |
582 | const struct user_regset *regset, | |
583 | unsigned int pos, unsigned int count, | |
584 | const void *kbuf, const void __user *ubuf) | |
585 | { | |
586 | struct pt_regs *regs = task_pt_regs(target); | |
587 | compat_ulong_t __user *reg_window; | |
588 | const compat_ulong_t *k = kbuf; | |
589 | const compat_ulong_t __user *u = ubuf; | |
590 | compat_ulong_t reg; | |
591 | ||
592 | if (target == current) | |
593 | flushw_user(); | |
594 | ||
595 | pos /= sizeof(reg); | |
596 | count /= sizeof(reg); | |
597 | ||
598 | if (kbuf) { | |
599 | for (; count > 0 && pos < 16; count--) | |
600 | regs->u_regs[pos++] = *k++; | |
601 | ||
602 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; | |
b857bd29 | 603 | reg_window -= 16; |
ad4f9576 DM |
604 | if (target == current) { |
605 | for (; count > 0 && pos < 32; count--) { | |
606 | if (put_user(*k++, ®_window[pos++])) | |
607 | return -EFAULT; | |
608 | } | |
609 | } else { | |
610 | for (; count > 0 && pos < 32; count--) { | |
611 | if (access_process_vm(target, | |
612 | (unsigned long) | |
613 | ®_window[pos], | |
614 | (void *) k, | |
615 | sizeof(*k), 1) | |
616 | != sizeof(*k)) | |
617 | return -EFAULT; | |
618 | k++; | |
619 | pos++; | |
620 | } | |
d09c2a23 DM |
621 | } |
622 | } else { | |
623 | for (; count > 0 && pos < 16; count--) { | |
624 | if (get_user(reg, u++)) | |
625 | return -EFAULT; | |
626 | regs->u_regs[pos++] = reg; | |
627 | } | |
628 | ||
629 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; | |
b857bd29 | 630 | reg_window -= 16; |
ad4f9576 DM |
631 | if (target == current) { |
632 | for (; count > 0 && pos < 32; count--) { | |
633 | if (get_user(reg, u++) || | |
634 | put_user(reg, ®_window[pos++])) | |
635 | return -EFAULT; | |
636 | } | |
637 | } else { | |
638 | for (; count > 0 && pos < 32; count--) { | |
639 | if (access_process_vm(target, | |
640 | (unsigned long) | |
641 | u, | |
642 | ®, sizeof(reg), 0) | |
643 | != sizeof(reg)) | |
644 | return -EFAULT; | |
645 | if (access_process_vm(target, | |
646 | (unsigned long) | |
647 | ®_window[pos], | |
648 | ®, sizeof(reg), 1) | |
649 | != sizeof(reg)) | |
650 | return -EFAULT; | |
651 | pos++; | |
652 | u++; | |
653 | } | |
d09c2a23 DM |
654 | } |
655 | } | |
656 | while (count > 0) { | |
657 | unsigned long tstate; | |
658 | ||
659 | if (kbuf) | |
660 | reg = *k++; | |
661 | else if (get_user(reg, u++)) | |
662 | return -EFAULT; | |
663 | ||
664 | switch (pos) { | |
665 | case 32: /* PSR */ | |
666 | tstate = regs->tstate; | |
28e61036 | 667 | tstate &= ~(TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL); |
d09c2a23 | 668 | tstate |= psr_to_tstate_icc(reg); |
28e61036 DM |
669 | if (reg & PSR_SYSCALL) |
670 | tstate |= TSTATE_SYSCALL; | |
d09c2a23 DM |
671 | regs->tstate = tstate; |
672 | break; | |
673 | case 33: /* PC */ | |
674 | regs->tpc = reg; | |
675 | break; | |
676 | case 34: /* NPC */ | |
677 | regs->tnpc = reg; | |
678 | break; | |
679 | case 35: /* Y */ | |
680 | regs->y = reg; | |
681 | break; | |
682 | case 36: /* WIM */ | |
683 | case 37: /* TBR */ | |
684 | break; | |
685 | default: | |
686 | goto finish; | |
687 | } | |
688 | ||
689 | pos++; | |
690 | count--; | |
691 | } | |
692 | finish: | |
693 | pos *= sizeof(reg); | |
694 | count *= sizeof(reg); | |
695 | ||
696 | return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
697 | 38 * sizeof(reg), -1); | |
698 | } | |
699 | ||
700 | static int fpregs32_get(struct task_struct *target, | |
701 | const struct user_regset *regset, | |
702 | unsigned int pos, unsigned int count, | |
703 | void *kbuf, void __user *ubuf) | |
704 | { | |
705 | const unsigned long *fpregs = task_thread_info(target)->fpregs; | |
706 | compat_ulong_t enabled; | |
707 | unsigned long fprs; | |
708 | compat_ulong_t fsr; | |
709 | int ret = 0; | |
710 | ||
711 | if (target == current) | |
712 | save_and_clear_fpu(); | |
713 | ||
714 | fprs = task_thread_info(target)->fpsaved[0]; | |
715 | if (fprs & FPRS_FEF) { | |
716 | fsr = task_thread_info(target)->xfsr[0]; | |
717 | enabled = 1; | |
718 | } else { | |
719 | fsr = 0; | |
720 | enabled = 0; | |
721 | } | |
722 | ||
723 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
724 | fpregs, | |
725 | 0, 32 * sizeof(u32)); | |
726 | ||
727 | if (!ret) | |
728 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
729 | 32 * sizeof(u32), | |
730 | 33 * sizeof(u32)); | |
731 | if (!ret) | |
732 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
733 | &fsr, | |
734 | 33 * sizeof(u32), | |
735 | 34 * sizeof(u32)); | |
736 | ||
737 | if (!ret) { | |
738 | compat_ulong_t val; | |
739 | ||
740 | val = (enabled << 8) | (8 << 16); | |
741 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
742 | &val, | |
743 | 34 * sizeof(u32), | |
744 | 35 * sizeof(u32)); | |
745 | } | |
746 | ||
747 | if (!ret) | |
748 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
749 | 35 * sizeof(u32), -1); | |
750 | ||
751 | return ret; | |
752 | } | |
753 | ||
754 | static int fpregs32_set(struct task_struct *target, | |
755 | const struct user_regset *regset, | |
756 | unsigned int pos, unsigned int count, | |
757 | const void *kbuf, const void __user *ubuf) | |
758 | { | |
759 | unsigned long *fpregs = task_thread_info(target)->fpregs; | |
760 | unsigned long fprs; | |
761 | int ret; | |
762 | ||
763 | if (target == current) | |
764 | save_and_clear_fpu(); | |
765 | ||
766 | fprs = task_thread_info(target)->fpsaved[0]; | |
767 | ||
768 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
769 | fpregs, | |
770 | 0, 32 * sizeof(u32)); | |
771 | if (!ret) | |
772 | user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
773 | 32 * sizeof(u32), | |
774 | 33 * sizeof(u32)); | |
775 | if (!ret && count > 0) { | |
776 | compat_ulong_t fsr; | |
777 | unsigned long val; | |
778 | ||
779 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
780 | &fsr, | |
781 | 33 * sizeof(u32), | |
782 | 34 * sizeof(u32)); | |
783 | if (!ret) { | |
784 | val = task_thread_info(target)->xfsr[0]; | |
785 | val &= 0xffffffff00000000UL; | |
786 | val |= fsr; | |
787 | task_thread_info(target)->xfsr[0] = val; | |
788 | } | |
789 | } | |
790 | ||
791 | fprs |= (FPRS_FEF | FPRS_DL); | |
792 | task_thread_info(target)->fpsaved[0] = fprs; | |
793 | ||
794 | if (!ret) | |
795 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
796 | 34 * sizeof(u32), -1); | |
797 | return ret; | |
798 | } | |
799 | ||
800 | static const struct user_regset sparc32_regsets[] = { | |
801 | /* Format is: | |
802 | * G0 --> G7 | |
803 | * O0 --> O7 | |
804 | * L0 --> L7 | |
805 | * I0 --> I7 | |
806 | * PSR, PC, nPC, Y, WIM, TBR | |
807 | */ | |
808 | [REGSET_GENERAL] = { | |
809 | .core_note_type = NT_PRSTATUS, | |
3c503701 | 810 | .n = 38, |
d09c2a23 DM |
811 | .size = sizeof(u32), .align = sizeof(u32), |
812 | .get = genregs32_get, .set = genregs32_set | |
813 | }, | |
814 | /* Format is: | |
815 | * F0 --> F31 | |
816 | * empty 32-bit word | |
817 | * FSR (32--bit word) | |
818 | * FPU QUEUE COUNT (8-bit char) | |
819 | * FPU QUEUE ENTRYSIZE (8-bit char) | |
820 | * FPU ENABLED (8-bit char) | |
821 | * empty 8-bit char | |
822 | * FPU QUEUE (64 32-bit ints) | |
823 | */ | |
824 | [REGSET_FP] = { | |
825 | .core_note_type = NT_PRFPREG, | |
3c503701 | 826 | .n = 99, |
d09c2a23 DM |
827 | .size = sizeof(u32), .align = sizeof(u32), |
828 | .get = fpregs32_get, .set = fpregs32_set | |
829 | }, | |
830 | }; | |
831 | ||
832 | static const struct user_regset_view user_sparc32_view = { | |
833 | .name = "sparc", .e_machine = EM_SPARC, | |
834 | .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) | |
835 | }; | |
11cc8a3a | 836 | #endif /* CONFIG_COMPAT */ |
d09c2a23 DM |
837 | |
838 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | |
839 | { | |
11cc8a3a | 840 | #ifdef CONFIG_COMPAT |
d09c2a23 DM |
841 | if (test_tsk_thread_flag(task, TIF_32BIT)) |
842 | return &user_sparc32_view; | |
11cc8a3a | 843 | #endif |
d09c2a23 DM |
844 | return &user_sparc64_view; |
845 | } | |
846 | ||
11cc8a3a | 847 | #ifdef CONFIG_COMPAT |
2ba85f3a DM |
848 | struct compat_fps { |
849 | unsigned int regs[32]; | |
850 | unsigned int fsr; | |
851 | unsigned int flags; | |
852 | unsigned int extra; | |
853 | unsigned int fpqd; | |
854 | struct compat_fq { | |
855 | unsigned int insnaddr; | |
856 | unsigned int insn; | |
857 | } fpq[16]; | |
858 | }; | |
859 | ||
860 | long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |
861 | compat_ulong_t caddr, compat_ulong_t cdata) | |
1da177e4 | 862 | { |
d786a4a6 | 863 | const struct user_regset_view *view = task_user_regset_view(current); |
2ba85f3a DM |
864 | compat_ulong_t caddr2 = task_pt_regs(current)->u_regs[UREG_I4]; |
865 | struct pt_regs32 __user *pregs; | |
866 | struct compat_fps __user *fps; | |
867 | unsigned long addr2 = caddr2; | |
868 | unsigned long addr = caddr; | |
869 | unsigned long data = cdata; | |
9473272a | 870 | int ret; |
1da177e4 | 871 | |
2ba85f3a DM |
872 | pregs = (struct pt_regs32 __user *) addr; |
873 | fps = (struct compat_fps __user *) addr; | |
1da177e4 | 874 | |
2ba85f3a | 875 | switch (request) { |
1759e58e | 876 | case PTRACE_PEEKUSR: |
9775369e DM |
877 | ret = (addr != 0) ? -EIO : 0; |
878 | break; | |
1759e58e | 879 | |
2ba85f3a | 880 | case PTRACE_GETREGS: |
9473272a DM |
881 | ret = copy_regset_to_user(child, view, REGSET_GENERAL, |
882 | 32 * sizeof(u32), | |
883 | 4 * sizeof(u32), | |
884 | &pregs->psr); | |
885 | if (!ret) | |
886 | ret = copy_regset_to_user(child, view, REGSET_GENERAL, | |
887 | 1 * sizeof(u32), | |
888 | 15 * sizeof(u32), | |
889 | &pregs->u_regs[0]); | |
9775369e | 890 | break; |
9473272a | 891 | |
2ba85f3a | 892 | case PTRACE_SETREGS: |
9473272a DM |
893 | ret = copy_regset_from_user(child, view, REGSET_GENERAL, |
894 | 32 * sizeof(u32), | |
895 | 4 * sizeof(u32), | |
896 | &pregs->psr); | |
897 | if (!ret) | |
898 | ret = copy_regset_from_user(child, view, REGSET_GENERAL, | |
899 | 1 * sizeof(u32), | |
900 | 15 * sizeof(u32), | |
901 | &pregs->u_regs[0]); | |
9775369e | 902 | break; |
9775369e | 903 | |
2ba85f3a | 904 | case PTRACE_GETFPREGS: |
9473272a DM |
905 | ret = copy_regset_to_user(child, view, REGSET_FP, |
906 | 0 * sizeof(u32), | |
907 | 32 * sizeof(u32), | |
908 | &fps->regs[0]); | |
909 | if (!ret) | |
910 | ret = copy_regset_to_user(child, view, REGSET_FP, | |
911 | 33 * sizeof(u32), | |
912 | 1 * sizeof(u32), | |
913 | &fps->fsr); | |
914 | if (!ret) { | |
915 | if (__put_user(0, &fps->flags) || | |
916 | __put_user(0, &fps->extra) || | |
917 | __put_user(0, &fps->fpqd) || | |
918 | clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) | |
919 | ret = -EFAULT; | |
920 | } | |
9775369e | 921 | break; |
9775369e | 922 | |
2ba85f3a | 923 | case PTRACE_SETFPREGS: |
9473272a DM |
924 | ret = copy_regset_from_user(child, view, REGSET_FP, |
925 | 0 * sizeof(u32), | |
926 | 32 * sizeof(u32), | |
927 | &fps->regs[0]); | |
928 | if (!ret) | |
929 | ret = copy_regset_from_user(child, view, REGSET_FP, | |
930 | 33 * sizeof(u32), | |
931 | 1 * sizeof(u32), | |
932 | &fps->fsr); | |
9775369e | 933 | break; |
2ba85f3a DM |
934 | |
935 | case PTRACE_READTEXT: | |
936 | case PTRACE_READDATA: | |
937 | ret = ptrace_readdata(child, addr, | |
938 | (char __user *)addr2, data); | |
939 | if (ret == data) | |
940 | ret = 0; | |
941 | else if (ret >= 0) | |
942 | ret = -EIO; | |
943 | break; | |
944 | ||
945 | case PTRACE_WRITETEXT: | |
946 | case PTRACE_WRITEDATA: | |
947 | ret = ptrace_writedata(child, (char __user *) addr2, | |
948 | addr, data); | |
949 | if (ret == data) | |
950 | ret = 0; | |
951 | else if (ret >= 0) | |
952 | ret = -EIO; | |
953 | break; | |
954 | ||
955 | default: | |
986bef85 DM |
956 | if (request == PTRACE_SPARC_DETACH) |
957 | request = PTRACE_DETACH; | |
2ba85f3a DM |
958 | ret = compat_ptrace_request(child, request, addr, data); |
959 | break; | |
1da177e4 LT |
960 | } |
961 | ||
2ba85f3a DM |
962 | return ret; |
963 | } | |
11cc8a3a | 964 | #endif /* CONFIG_COMPAT */ |
2ba85f3a DM |
965 | |
966 | struct fps { | |
967 | unsigned int regs[64]; | |
968 | unsigned long fsr; | |
969 | }; | |
970 | ||
9b05a69e NK |
971 | long arch_ptrace(struct task_struct *child, long request, |
972 | unsigned long addr, unsigned long data) | |
2ba85f3a | 973 | { |
d786a4a6 | 974 | const struct user_regset_view *view = task_user_regset_view(current); |
2ba85f3a | 975 | unsigned long addr2 = task_pt_regs(current)->u_regs[UREG_I4]; |
bfdf9ebc DM |
976 | struct pt_regs __user *pregs; |
977 | struct fps __user *fps; | |
a9384e23 | 978 | void __user *addr2p; |
2ba85f3a DM |
979 | int ret; |
980 | ||
9b05a69e NK |
981 | pregs = (struct pt_regs __user *) addr; |
982 | fps = (struct fps __user *) addr; | |
a9384e23 | 983 | addr2p = (void __user *) addr2; |
bfdf9ebc | 984 | |
2ba85f3a DM |
985 | switch (request) { |
986 | case PTRACE_PEEKUSR: | |
987 | ret = (addr != 0) ? -EIO : 0; | |
988 | break; | |
9775369e | 989 | |
2ba85f3a DM |
990 | case PTRACE_GETREGS64: |
991 | ret = copy_regset_to_user(child, view, REGSET_GENERAL, | |
992 | 1 * sizeof(u64), | |
993 | 15 * sizeof(u64), | |
994 | &pregs->u_regs[0]); | |
995 | if (!ret) { | |
996 | /* XXX doesn't handle 'y' register correctly XXX */ | |
997 | ret = copy_regset_to_user(child, view, REGSET_GENERAL, | |
998 | 32 * sizeof(u64), | |
999 | 4 * sizeof(u64), | |
1000 | &pregs->tstate); | |
1001 | } | |
1002 | break; | |
1003 | ||
1004 | case PTRACE_SETREGS64: | |
1005 | ret = copy_regset_from_user(child, view, REGSET_GENERAL, | |
1006 | 1 * sizeof(u64), | |
1007 | 15 * sizeof(u64), | |
1008 | &pregs->u_regs[0]); | |
1009 | if (!ret) { | |
1010 | /* XXX doesn't handle 'y' register correctly XXX */ | |
1011 | ret = copy_regset_from_user(child, view, REGSET_GENERAL, | |
1012 | 32 * sizeof(u64), | |
1013 | 4 * sizeof(u64), | |
1014 | &pregs->tstate); | |
1015 | } | |
1016 | break; | |
1017 | ||
1018 | case PTRACE_GETFPREGS64: | |
1019 | ret = copy_regset_to_user(child, view, REGSET_FP, | |
1020 | 0 * sizeof(u64), | |
1021 | 33 * sizeof(u64), | |
1022 | fps); | |
1023 | break; | |
1024 | ||
1025 | case PTRACE_SETFPREGS64: | |
ee4ee527 | 1026 | ret = copy_regset_from_user(child, view, REGSET_FP, |
9473272a DM |
1027 | 0 * sizeof(u64), |
1028 | 33 * sizeof(u64), | |
1029 | fps); | |
9775369e | 1030 | break; |
1da177e4 LT |
1031 | |
1032 | case PTRACE_READTEXT: | |
9775369e | 1033 | case PTRACE_READDATA: |
a9384e23 | 1034 | ret = ptrace_readdata(child, addr, addr2p, data); |
9775369e DM |
1035 | if (ret == data) |
1036 | ret = 0; | |
1037 | else if (ret >= 0) | |
1038 | ret = -EIO; | |
1039 | break; | |
1da177e4 LT |
1040 | |
1041 | case PTRACE_WRITETEXT: | |
9775369e | 1042 | case PTRACE_WRITEDATA: |
a9384e23 | 1043 | ret = ptrace_writedata(child, addr2p, addr, data); |
9775369e DM |
1044 | if (ret == data) |
1045 | ret = 0; | |
1046 | else if (ret >= 0) | |
1047 | ret = -EIO; | |
1048 | break; | |
1da177e4 | 1049 | |
9775369e | 1050 | default: |
986bef85 DM |
1051 | if (request == PTRACE_SPARC_DETACH) |
1052 | request = PTRACE_DETACH; | |
9775369e DM |
1053 | ret = ptrace_request(child, request, addr, data); |
1054 | break; | |
1da177e4 | 1055 | } |
9775369e DM |
1056 | |
1057 | return ret; | |
1da177e4 LT |
1058 | } |
1059 | ||
fe06ccaa | 1060 | asmlinkage int syscall_trace_enter(struct pt_regs *regs) |
1da177e4 | 1061 | { |
73ccefab RM |
1062 | int ret = 0; |
1063 | ||
bb49bcda | 1064 | /* do the secure computing check first */ |
e4da89d0 | 1065 | secure_computing_strict(regs->u_regs[UREG_G1]); |
bb49bcda | 1066 | |
fe06ccaa DM |
1067 | if (test_thread_flag(TIF_SYSCALL_TRACE)) |
1068 | ret = tracehook_report_syscall_entry(regs); | |
f7ceba36 | 1069 | |
c658ad1b DM |
1070 | if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) |
1071 | trace_sys_enter(regs, regs->u_regs[UREG_G1]); | |
1072 | ||
b05d8447 EP |
1073 | audit_syscall_entry((test_thread_flag(TIF_32BIT) ? |
1074 | AUDIT_ARCH_SPARC : | |
1075 | AUDIT_ARCH_SPARC64), | |
1076 | regs->u_regs[UREG_G1], | |
1077 | regs->u_regs[UREG_I0], | |
1078 | regs->u_regs[UREG_I1], | |
1079 | regs->u_regs[UREG_I2], | |
1080 | regs->u_regs[UREG_I3]); | |
73ccefab RM |
1081 | |
1082 | return ret; | |
1da177e4 | 1083 | } |
fe06ccaa DM |
1084 | |
1085 | asmlinkage void syscall_trace_leave(struct pt_regs *regs) | |
1086 | { | |
d7e7528b | 1087 | audit_syscall_exit(regs); |
fe06ccaa | 1088 | |
c658ad1b | 1089 | if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) |
04001552 | 1090 | trace_sys_exit(regs, regs->u_regs[UREG_I0]); |
c658ad1b | 1091 | |
fe06ccaa DM |
1092 | if (test_thread_flag(TIF_SYSCALL_TRACE)) |
1093 | tracehook_report_syscall_exit(regs, 0); | |
1094 | } |