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