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]; | |
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]; | |
ad4f9576 DM |
519 | if (target == current) { |
520 | for (; count > 0 && pos < 32; count--) { | |
521 | if (get_user(reg, ®_window[pos++]) || | |
522 | put_user(reg, u++)) | |
523 | return -EFAULT; | |
524 | } | |
525 | } else { | |
526 | for (; count > 0 && pos < 32; count--) { | |
527 | if (access_process_vm(target, | |
528 | (unsigned long) | |
529 | ®_window[pos], | |
530 | ®, sizeof(reg), 0) | |
531 | != sizeof(reg)) | |
532 | return -EFAULT; | |
533 | if (access_process_vm(target, | |
534 | (unsigned long) u, | |
535 | ®, sizeof(reg), 1) | |
536 | != sizeof(reg)) | |
537 | return -EFAULT; | |
538 | pos++; | |
539 | u++; | |
540 | } | |
d09c2a23 DM |
541 | } |
542 | } | |
543 | while (count > 0) { | |
544 | switch (pos) { | |
545 | case 32: /* PSR */ | |
546 | reg = tstate_to_psr(regs->tstate); | |
547 | break; | |
548 | case 33: /* PC */ | |
549 | reg = regs->tpc; | |
550 | break; | |
551 | case 34: /* NPC */ | |
552 | reg = regs->tnpc; | |
553 | break; | |
554 | case 35: /* Y */ | |
555 | reg = regs->y; | |
556 | break; | |
557 | case 36: /* WIM */ | |
558 | case 37: /* TBR */ | |
559 | reg = 0; | |
560 | break; | |
561 | default: | |
562 | goto finish; | |
563 | } | |
564 | ||
565 | if (kbuf) | |
566 | *k++ = reg; | |
567 | else if (put_user(reg, u++)) | |
568 | return -EFAULT; | |
569 | pos++; | |
570 | count--; | |
571 | } | |
572 | finish: | |
573 | pos *= sizeof(reg); | |
574 | count *= sizeof(reg); | |
575 | ||
576 | return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
577 | 38 * sizeof(reg), -1); | |
578 | } | |
579 | ||
580 | static int genregs32_set(struct task_struct *target, | |
581 | const struct user_regset *regset, | |
582 | unsigned int pos, unsigned int count, | |
583 | const void *kbuf, const void __user *ubuf) | |
584 | { | |
585 | struct pt_regs *regs = task_pt_regs(target); | |
586 | compat_ulong_t __user *reg_window; | |
587 | const compat_ulong_t *k = kbuf; | |
588 | const compat_ulong_t __user *u = ubuf; | |
589 | compat_ulong_t reg; | |
590 | ||
591 | if (target == current) | |
592 | flushw_user(); | |
593 | ||
594 | pos /= sizeof(reg); | |
595 | count /= sizeof(reg); | |
596 | ||
597 | if (kbuf) { | |
598 | for (; count > 0 && pos < 16; count--) | |
599 | regs->u_regs[pos++] = *k++; | |
600 | ||
601 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; | |
ad4f9576 DM |
602 | if (target == current) { |
603 | for (; count > 0 && pos < 32; count--) { | |
604 | if (put_user(*k++, ®_window[pos++])) | |
605 | return -EFAULT; | |
606 | } | |
607 | } else { | |
608 | for (; count > 0 && pos < 32; count--) { | |
609 | if (access_process_vm(target, | |
610 | (unsigned long) | |
611 | ®_window[pos], | |
612 | (void *) k, | |
613 | sizeof(*k), 1) | |
614 | != sizeof(*k)) | |
615 | return -EFAULT; | |
616 | k++; | |
617 | pos++; | |
618 | } | |
d09c2a23 DM |
619 | } |
620 | } else { | |
621 | for (; count > 0 && pos < 16; count--) { | |
622 | if (get_user(reg, u++)) | |
623 | return -EFAULT; | |
624 | regs->u_regs[pos++] = reg; | |
625 | } | |
626 | ||
627 | reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; | |
ad4f9576 DM |
628 | if (target == current) { |
629 | for (; count > 0 && pos < 32; count--) { | |
630 | if (get_user(reg, u++) || | |
631 | put_user(reg, ®_window[pos++])) | |
632 | return -EFAULT; | |
633 | } | |
634 | } else { | |
635 | for (; count > 0 && pos < 32; count--) { | |
636 | if (access_process_vm(target, | |
637 | (unsigned long) | |
638 | u, | |
639 | ®, sizeof(reg), 0) | |
640 | != sizeof(reg)) | |
641 | return -EFAULT; | |
642 | if (access_process_vm(target, | |
643 | (unsigned long) | |
644 | ®_window[pos], | |
645 | ®, sizeof(reg), 1) | |
646 | != sizeof(reg)) | |
647 | return -EFAULT; | |
648 | pos++; | |
649 | u++; | |
650 | } | |
d09c2a23 DM |
651 | } |
652 | } | |
653 | while (count > 0) { | |
654 | unsigned long tstate; | |
655 | ||
656 | if (kbuf) | |
657 | reg = *k++; | |
658 | else if (get_user(reg, u++)) | |
659 | return -EFAULT; | |
660 | ||
661 | switch (pos) { | |
662 | case 32: /* PSR */ | |
663 | tstate = regs->tstate; | |
28e61036 | 664 | tstate &= ~(TSTATE_ICC | TSTATE_XCC | TSTATE_SYSCALL); |
d09c2a23 | 665 | tstate |= psr_to_tstate_icc(reg); |
28e61036 DM |
666 | if (reg & PSR_SYSCALL) |
667 | tstate |= TSTATE_SYSCALL; | |
d09c2a23 DM |
668 | regs->tstate = tstate; |
669 | break; | |
670 | case 33: /* PC */ | |
671 | regs->tpc = reg; | |
672 | break; | |
673 | case 34: /* NPC */ | |
674 | regs->tnpc = reg; | |
675 | break; | |
676 | case 35: /* Y */ | |
677 | regs->y = reg; | |
678 | break; | |
679 | case 36: /* WIM */ | |
680 | case 37: /* TBR */ | |
681 | break; | |
682 | default: | |
683 | goto finish; | |
684 | } | |
685 | ||
686 | pos++; | |
687 | count--; | |
688 | } | |
689 | finish: | |
690 | pos *= sizeof(reg); | |
691 | count *= sizeof(reg); | |
692 | ||
693 | return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
694 | 38 * sizeof(reg), -1); | |
695 | } | |
696 | ||
697 | static int fpregs32_get(struct task_struct *target, | |
698 | const struct user_regset *regset, | |
699 | unsigned int pos, unsigned int count, | |
700 | void *kbuf, void __user *ubuf) | |
701 | { | |
702 | const unsigned long *fpregs = task_thread_info(target)->fpregs; | |
703 | compat_ulong_t enabled; | |
704 | unsigned long fprs; | |
705 | compat_ulong_t fsr; | |
706 | int ret = 0; | |
707 | ||
708 | if (target == current) | |
709 | save_and_clear_fpu(); | |
710 | ||
711 | fprs = task_thread_info(target)->fpsaved[0]; | |
712 | if (fprs & FPRS_FEF) { | |
713 | fsr = task_thread_info(target)->xfsr[0]; | |
714 | enabled = 1; | |
715 | } else { | |
716 | fsr = 0; | |
717 | enabled = 0; | |
718 | } | |
719 | ||
720 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
721 | fpregs, | |
722 | 0, 32 * sizeof(u32)); | |
723 | ||
724 | if (!ret) | |
725 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
726 | 32 * sizeof(u32), | |
727 | 33 * sizeof(u32)); | |
728 | if (!ret) | |
729 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
730 | &fsr, | |
731 | 33 * sizeof(u32), | |
732 | 34 * sizeof(u32)); | |
733 | ||
734 | if (!ret) { | |
735 | compat_ulong_t val; | |
736 | ||
737 | val = (enabled << 8) | (8 << 16); | |
738 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
739 | &val, | |
740 | 34 * sizeof(u32), | |
741 | 35 * sizeof(u32)); | |
742 | } | |
743 | ||
744 | if (!ret) | |
745 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
746 | 35 * sizeof(u32), -1); | |
747 | ||
748 | return ret; | |
749 | } | |
750 | ||
751 | static int fpregs32_set(struct task_struct *target, | |
752 | const struct user_regset *regset, | |
753 | unsigned int pos, unsigned int count, | |
754 | const void *kbuf, const void __user *ubuf) | |
755 | { | |
756 | unsigned long *fpregs = task_thread_info(target)->fpregs; | |
757 | unsigned long fprs; | |
758 | int ret; | |
759 | ||
760 | if (target == current) | |
761 | save_and_clear_fpu(); | |
762 | ||
763 | fprs = task_thread_info(target)->fpsaved[0]; | |
764 | ||
765 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
766 | fpregs, | |
767 | 0, 32 * sizeof(u32)); | |
768 | if (!ret) | |
769 | user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
770 | 32 * sizeof(u32), | |
771 | 33 * sizeof(u32)); | |
772 | if (!ret && count > 0) { | |
773 | compat_ulong_t fsr; | |
774 | unsigned long val; | |
775 | ||
776 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
777 | &fsr, | |
778 | 33 * sizeof(u32), | |
779 | 34 * sizeof(u32)); | |
780 | if (!ret) { | |
781 | val = task_thread_info(target)->xfsr[0]; | |
782 | val &= 0xffffffff00000000UL; | |
783 | val |= fsr; | |
784 | task_thread_info(target)->xfsr[0] = val; | |
785 | } | |
786 | } | |
787 | ||
788 | fprs |= (FPRS_FEF | FPRS_DL); | |
789 | task_thread_info(target)->fpsaved[0] = fprs; | |
790 | ||
791 | if (!ret) | |
792 | ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
793 | 34 * sizeof(u32), -1); | |
794 | return ret; | |
795 | } | |
796 | ||
797 | static const struct user_regset sparc32_regsets[] = { | |
798 | /* Format is: | |
799 | * G0 --> G7 | |
800 | * O0 --> O7 | |
801 | * L0 --> L7 | |
802 | * I0 --> I7 | |
803 | * PSR, PC, nPC, Y, WIM, TBR | |
804 | */ | |
805 | [REGSET_GENERAL] = { | |
806 | .core_note_type = NT_PRSTATUS, | |
3c503701 | 807 | .n = 38, |
d09c2a23 DM |
808 | .size = sizeof(u32), .align = sizeof(u32), |
809 | .get = genregs32_get, .set = genregs32_set | |
810 | }, | |
811 | /* Format is: | |
812 | * F0 --> F31 | |
813 | * empty 32-bit word | |
814 | * FSR (32--bit word) | |
815 | * FPU QUEUE COUNT (8-bit char) | |
816 | * FPU QUEUE ENTRYSIZE (8-bit char) | |
817 | * FPU ENABLED (8-bit char) | |
818 | * empty 8-bit char | |
819 | * FPU QUEUE (64 32-bit ints) | |
820 | */ | |
821 | [REGSET_FP] = { | |
822 | .core_note_type = NT_PRFPREG, | |
3c503701 | 823 | .n = 99, |
d09c2a23 DM |
824 | .size = sizeof(u32), .align = sizeof(u32), |
825 | .get = fpregs32_get, .set = fpregs32_set | |
826 | }, | |
827 | }; | |
828 | ||
829 | static const struct user_regset_view user_sparc32_view = { | |
830 | .name = "sparc", .e_machine = EM_SPARC, | |
831 | .regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) | |
832 | }; | |
11cc8a3a | 833 | #endif /* CONFIG_COMPAT */ |
d09c2a23 DM |
834 | |
835 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | |
836 | { | |
11cc8a3a | 837 | #ifdef CONFIG_COMPAT |
d09c2a23 DM |
838 | if (test_tsk_thread_flag(task, TIF_32BIT)) |
839 | return &user_sparc32_view; | |
11cc8a3a | 840 | #endif |
d09c2a23 DM |
841 | return &user_sparc64_view; |
842 | } | |
843 | ||
11cc8a3a | 844 | #ifdef CONFIG_COMPAT |
2ba85f3a DM |
845 | struct compat_fps { |
846 | unsigned int regs[32]; | |
847 | unsigned int fsr; | |
848 | unsigned int flags; | |
849 | unsigned int extra; | |
850 | unsigned int fpqd; | |
851 | struct compat_fq { | |
852 | unsigned int insnaddr; | |
853 | unsigned int insn; | |
854 | } fpq[16]; | |
855 | }; | |
856 | ||
857 | long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |
858 | compat_ulong_t caddr, compat_ulong_t cdata) | |
1da177e4 | 859 | { |
d786a4a6 | 860 | const struct user_regset_view *view = task_user_regset_view(current); |
2ba85f3a DM |
861 | compat_ulong_t caddr2 = task_pt_regs(current)->u_regs[UREG_I4]; |
862 | struct pt_regs32 __user *pregs; | |
863 | struct compat_fps __user *fps; | |
864 | unsigned long addr2 = caddr2; | |
865 | unsigned long addr = caddr; | |
866 | unsigned long data = cdata; | |
9473272a | 867 | int ret; |
1da177e4 | 868 | |
2ba85f3a DM |
869 | pregs = (struct pt_regs32 __user *) addr; |
870 | fps = (struct compat_fps __user *) addr; | |
1da177e4 | 871 | |
2ba85f3a | 872 | switch (request) { |
1759e58e | 873 | case PTRACE_PEEKUSR: |
9775369e DM |
874 | ret = (addr != 0) ? -EIO : 0; |
875 | break; | |
1759e58e | 876 | |
2ba85f3a | 877 | case PTRACE_GETREGS: |
9473272a DM |
878 | ret = copy_regset_to_user(child, view, REGSET_GENERAL, |
879 | 32 * sizeof(u32), | |
880 | 4 * sizeof(u32), | |
881 | &pregs->psr); | |
882 | if (!ret) | |
883 | ret = copy_regset_to_user(child, view, REGSET_GENERAL, | |
884 | 1 * sizeof(u32), | |
885 | 15 * sizeof(u32), | |
886 | &pregs->u_regs[0]); | |
9775369e | 887 | break; |
9473272a | 888 | |
2ba85f3a | 889 | case PTRACE_SETREGS: |
9473272a DM |
890 | ret = copy_regset_from_user(child, view, REGSET_GENERAL, |
891 | 32 * sizeof(u32), | |
892 | 4 * sizeof(u32), | |
893 | &pregs->psr); | |
894 | if (!ret) | |
895 | ret = copy_regset_from_user(child, view, REGSET_GENERAL, | |
896 | 1 * sizeof(u32), | |
897 | 15 * sizeof(u32), | |
898 | &pregs->u_regs[0]); | |
9775369e | 899 | break; |
9775369e | 900 | |
2ba85f3a | 901 | case PTRACE_GETFPREGS: |
9473272a DM |
902 | ret = copy_regset_to_user(child, view, REGSET_FP, |
903 | 0 * sizeof(u32), | |
904 | 32 * sizeof(u32), | |
905 | &fps->regs[0]); | |
906 | if (!ret) | |
907 | ret = copy_regset_to_user(child, view, REGSET_FP, | |
908 | 33 * sizeof(u32), | |
909 | 1 * sizeof(u32), | |
910 | &fps->fsr); | |
911 | if (!ret) { | |
912 | if (__put_user(0, &fps->flags) || | |
913 | __put_user(0, &fps->extra) || | |
914 | __put_user(0, &fps->fpqd) || | |
915 | clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) | |
916 | ret = -EFAULT; | |
917 | } | |
9775369e | 918 | break; |
9775369e | 919 | |
2ba85f3a | 920 | case PTRACE_SETFPREGS: |
9473272a DM |
921 | ret = copy_regset_from_user(child, view, REGSET_FP, |
922 | 0 * sizeof(u32), | |
923 | 32 * sizeof(u32), | |
924 | &fps->regs[0]); | |
925 | if (!ret) | |
926 | ret = copy_regset_from_user(child, view, REGSET_FP, | |
927 | 33 * sizeof(u32), | |
928 | 1 * sizeof(u32), | |
929 | &fps->fsr); | |
9775369e | 930 | break; |
2ba85f3a DM |
931 | |
932 | case PTRACE_READTEXT: | |
933 | case PTRACE_READDATA: | |
934 | ret = ptrace_readdata(child, addr, | |
935 | (char __user *)addr2, data); | |
936 | if (ret == data) | |
937 | ret = 0; | |
938 | else if (ret >= 0) | |
939 | ret = -EIO; | |
940 | break; | |
941 | ||
942 | case PTRACE_WRITETEXT: | |
943 | case PTRACE_WRITEDATA: | |
944 | ret = ptrace_writedata(child, (char __user *) addr2, | |
945 | addr, data); | |
946 | if (ret == data) | |
947 | ret = 0; | |
948 | else if (ret >= 0) | |
949 | ret = -EIO; | |
950 | break; | |
951 | ||
952 | default: | |
986bef85 DM |
953 | if (request == PTRACE_SPARC_DETACH) |
954 | request = PTRACE_DETACH; | |
2ba85f3a DM |
955 | ret = compat_ptrace_request(child, request, addr, data); |
956 | break; | |
1da177e4 LT |
957 | } |
958 | ||
2ba85f3a DM |
959 | return ret; |
960 | } | |
11cc8a3a | 961 | #endif /* CONFIG_COMPAT */ |
2ba85f3a DM |
962 | |
963 | struct fps { | |
964 | unsigned int regs[64]; | |
965 | unsigned long fsr; | |
966 | }; | |
967 | ||
968 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |
969 | { | |
d786a4a6 | 970 | const struct user_regset_view *view = task_user_regset_view(current); |
2ba85f3a | 971 | unsigned long addr2 = task_pt_regs(current)->u_regs[UREG_I4]; |
bfdf9ebc DM |
972 | struct pt_regs __user *pregs; |
973 | struct fps __user *fps; | |
2ba85f3a DM |
974 | int ret; |
975 | ||
bfdf9ebc DM |
976 | pregs = (struct pt_regs __user *) (unsigned long) addr; |
977 | fps = (struct fps __user *) (unsigned long) addr; | |
978 | ||
2ba85f3a DM |
979 | switch (request) { |
980 | case PTRACE_PEEKUSR: | |
981 | ret = (addr != 0) ? -EIO : 0; | |
982 | break; | |
9775369e | 983 | |
2ba85f3a DM |
984 | case PTRACE_GETREGS64: |
985 | ret = copy_regset_to_user(child, view, REGSET_GENERAL, | |
986 | 1 * sizeof(u64), | |
987 | 15 * sizeof(u64), | |
988 | &pregs->u_regs[0]); | |
989 | if (!ret) { | |
990 | /* XXX doesn't handle 'y' register correctly XXX */ | |
991 | ret = copy_regset_to_user(child, view, REGSET_GENERAL, | |
992 | 32 * sizeof(u64), | |
993 | 4 * sizeof(u64), | |
994 | &pregs->tstate); | |
995 | } | |
996 | break; | |
997 | ||
998 | case PTRACE_SETREGS64: | |
999 | ret = copy_regset_from_user(child, view, REGSET_GENERAL, | |
1000 | 1 * sizeof(u64), | |
1001 | 15 * sizeof(u64), | |
1002 | &pregs->u_regs[0]); | |
1003 | if (!ret) { | |
1004 | /* XXX doesn't handle 'y' register correctly XXX */ | |
1005 | ret = copy_regset_from_user(child, view, REGSET_GENERAL, | |
1006 | 32 * sizeof(u64), | |
1007 | 4 * sizeof(u64), | |
1008 | &pregs->tstate); | |
1009 | } | |
1010 | break; | |
1011 | ||
1012 | case PTRACE_GETFPREGS64: | |
1013 | ret = copy_regset_to_user(child, view, REGSET_FP, | |
1014 | 0 * sizeof(u64), | |
1015 | 33 * sizeof(u64), | |
1016 | fps); | |
1017 | break; | |
1018 | ||
1019 | case PTRACE_SETFPREGS64: | |
ee4ee527 | 1020 | ret = copy_regset_from_user(child, view, REGSET_FP, |
9473272a DM |
1021 | 0 * sizeof(u64), |
1022 | 33 * sizeof(u64), | |
1023 | fps); | |
9775369e | 1024 | break; |
1da177e4 LT |
1025 | |
1026 | case PTRACE_READTEXT: | |
9775369e DM |
1027 | case PTRACE_READDATA: |
1028 | ret = ptrace_readdata(child, addr, | |
1029 | (char __user *)addr2, data); | |
1030 | if (ret == data) | |
1031 | ret = 0; | |
1032 | else if (ret >= 0) | |
1033 | ret = -EIO; | |
1034 | break; | |
1da177e4 LT |
1035 | |
1036 | case PTRACE_WRITETEXT: | |
9775369e DM |
1037 | case PTRACE_WRITEDATA: |
1038 | ret = ptrace_writedata(child, (char __user *) addr2, | |
1039 | addr, data); | |
1040 | if (ret == data) | |
1041 | ret = 0; | |
1042 | else if (ret >= 0) | |
1043 | ret = -EIO; | |
1044 | break; | |
1da177e4 | 1045 | |
9775369e | 1046 | default: |
986bef85 DM |
1047 | if (request == PTRACE_SPARC_DETACH) |
1048 | request = PTRACE_DETACH; | |
9775369e DM |
1049 | ret = ptrace_request(child, request, addr, data); |
1050 | break; | |
1da177e4 | 1051 | } |
9775369e DM |
1052 | |
1053 | return ret; | |
1da177e4 LT |
1054 | } |
1055 | ||
fe06ccaa | 1056 | asmlinkage int syscall_trace_enter(struct pt_regs *regs) |
1da177e4 | 1057 | { |
73ccefab RM |
1058 | int ret = 0; |
1059 | ||
bb49bcda | 1060 | /* do the secure computing check first */ |
8d8a6479 | 1061 | secure_computing(regs->u_regs[UREG_G1]); |
bb49bcda | 1062 | |
fe06ccaa DM |
1063 | if (test_thread_flag(TIF_SYSCALL_TRACE)) |
1064 | ret = tracehook_report_syscall_entry(regs); | |
f7ceba36 | 1065 | |
c658ad1b DM |
1066 | if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) |
1067 | trace_sys_enter(regs, regs->u_regs[UREG_G1]); | |
1068 | ||
fe06ccaa | 1069 | if (unlikely(current->audit_context) && !ret) |
5411be59 | 1070 | audit_syscall_entry((test_thread_flag(TIF_32BIT) ? |
f7ceba36 DM |
1071 | AUDIT_ARCH_SPARC : |
1072 | AUDIT_ARCH_SPARC64), | |
1073 | regs->u_regs[UREG_G1], | |
1074 | regs->u_regs[UREG_I0], | |
1075 | regs->u_regs[UREG_I1], | |
1076 | regs->u_regs[UREG_I2], | |
1077 | regs->u_regs[UREG_I3]); | |
73ccefab RM |
1078 | |
1079 | return ret; | |
1da177e4 | 1080 | } |
fe06ccaa DM |
1081 | |
1082 | asmlinkage void syscall_trace_leave(struct pt_regs *regs) | |
1083 | { | |
1084 | if (unlikely(current->audit_context)) { | |
1085 | unsigned long tstate = regs->tstate; | |
1086 | int result = AUDITSC_SUCCESS; | |
1087 | ||
1088 | if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY))) | |
1089 | result = AUDITSC_FAILURE; | |
1090 | ||
1091 | audit_syscall_exit(result, regs->u_regs[UREG_I0]); | |
1092 | } | |
1093 | ||
c658ad1b DM |
1094 | if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) |
1095 | trace_sys_exit(regs, regs->u_regs[UREG_G1]); | |
1096 | ||
fe06ccaa DM |
1097 | if (test_thread_flag(TIF_SYSCALL_TRACE)) |
1098 | tracehook_report_syscall_exit(regs, 0); | |
1099 | } |