]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public | |
3 | * License. See the file "COPYING" in the main directory of this archive | |
4 | * for more details. | |
5 | * | |
6 | * Copyright (C) 1992 Ross Biro | |
7 | * Copyright (C) Linus Torvalds | |
8 | * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle | |
9 | * Copyright (C) 1996 David S. Miller | |
10 | * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com | |
11 | * Copyright (C) 1999 MIPS Technologies, Inc. | |
12 | * Copyright (C) 2000 Ulf Carlsson | |
13 | * | |
14 | * At this time Linux/MIPS64 only supports syscall tracing, even for 32-bit | |
15 | * binaries. | |
16 | */ | |
1da177e4 | 17 | #include <linux/compiler.h> |
c3fc5cd5 | 18 | #include <linux/context_tracking.h> |
7aeb753b | 19 | #include <linux/elf.h> |
1da177e4 LT |
20 | #include <linux/kernel.h> |
21 | #include <linux/sched.h> | |
68db0cf1 | 22 | #include <linux/sched/task_stack.h> |
1da177e4 LT |
23 | #include <linux/mm.h> |
24 | #include <linux/errno.h> | |
25 | #include <linux/ptrace.h> | |
7aeb753b | 26 | #include <linux/regset.h> |
1da177e4 | 27 | #include <linux/smp.h> |
1da177e4 | 28 | #include <linux/security.h> |
40e084a5 | 29 | #include <linux/stddef.h> |
bc3d22c1 | 30 | #include <linux/tracehook.h> |
293c5bd1 RB |
31 | #include <linux/audit.h> |
32 | #include <linux/seccomp.h> | |
1d7bf993 | 33 | #include <linux/ftrace.h> |
1da177e4 | 34 | |
f8280c8d | 35 | #include <asm/byteorder.h> |
1da177e4 | 36 | #include <asm/cpu.h> |
9b26616c | 37 | #include <asm/cpu-info.h> |
e50c0a8f | 38 | #include <asm/dsp.h> |
1da177e4 LT |
39 | #include <asm/fpu.h> |
40 | #include <asm/mipsregs.h> | |
101b3531 | 41 | #include <asm/mipsmtregs.h> |
1da177e4 LT |
42 | #include <asm/pgtable.h> |
43 | #include <asm/page.h> | |
44109c60 | 44 | #include <asm/processor.h> |
bec9b2b2 | 45 | #include <asm/syscall.h> |
7c0f6ba6 | 46 | #include <linux/uaccess.h> |
1da177e4 | 47 | #include <asm/bootinfo.h> |
ea3d710f | 48 | #include <asm/reg.h> |
1da177e4 | 49 | |
1d7bf993 RB |
50 | #define CREATE_TRACE_POINTS |
51 | #include <trace/events/syscalls.h> | |
52 | ||
1da177e4 LT |
53 | /* |
54 | * Called by kernel/ptrace.c when detaching.. | |
55 | * | |
56 | * Make sure single step bits etc are not set. | |
57 | */ | |
58 | void ptrace_disable(struct task_struct *child) | |
59 | { | |
0926bf95 DD |
60 | /* Don't load the watchpoint registers for the ex-child. */ |
61 | clear_tsk_thread_flag(child, TIF_LOAD_WATCH); | |
1da177e4 LT |
62 | } |
63 | ||
abf378be | 64 | /* |
5a1aca44 MR |
65 | * Poke at FCSR according to its mask. Set the Cause bits even |
66 | * if a corresponding Enable bit is set. This will be noticed at | |
67 | * the time the thread is switched to and SIGFPE thrown accordingly. | |
abf378be MR |
68 | */ |
69 | static void ptrace_setfcr31(struct task_struct *child, u32 value) | |
70 | { | |
71 | u32 fcr31; | |
72 | u32 mask; | |
73 | ||
abf378be MR |
74 | fcr31 = child->thread.fpu.fcr31; |
75 | mask = boot_cpu_data.fpu_msk31; | |
76 | child->thread.fpu.fcr31 = (value & ~mask) | (fcr31 & mask); | |
77 | } | |
78 | ||
ea3d710f | 79 | /* |
70342287 | 80 | * Read a general register set. We always use the 64-bit format, even |
ea3d710f DJ |
81 | * for 32-bit kernels and for 32-bit processes on a 64-bit kernel. |
82 | * Registers are sign extended to fill the available space. | |
83 | */ | |
a79ebea6 | 84 | int ptrace_getregs(struct task_struct *child, struct user_pt_regs __user *data) |
ea3d710f DJ |
85 | { |
86 | struct pt_regs *regs; | |
87 | int i; | |
88 | ||
89 | if (!access_ok(VERIFY_WRITE, data, 38 * 8)) | |
90 | return -EIO; | |
91 | ||
40bc9c67 | 92 | regs = task_pt_regs(child); |
ea3d710f DJ |
93 | |
94 | for (i = 0; i < 32; i++) | |
a79ebea6 AS |
95 | __put_user((long)regs->regs[i], (__s64 __user *)&data->regs[i]); |
96 | __put_user((long)regs->lo, (__s64 __user *)&data->lo); | |
97 | __put_user((long)regs->hi, (__s64 __user *)&data->hi); | |
98 | __put_user((long)regs->cp0_epc, (__s64 __user *)&data->cp0_epc); | |
99 | __put_user((long)regs->cp0_badvaddr, (__s64 __user *)&data->cp0_badvaddr); | |
100 | __put_user((long)regs->cp0_status, (__s64 __user *)&data->cp0_status); | |
101 | __put_user((long)regs->cp0_cause, (__s64 __user *)&data->cp0_cause); | |
ea3d710f DJ |
102 | |
103 | return 0; | |
104 | } | |
105 | ||
106 | /* | |
107 | * Write a general register set. As for PTRACE_GETREGS, we always use | |
108 | * the 64-bit format. On a 32-bit kernel only the lower order half | |
109 | * (according to endianness) will be used. | |
110 | */ | |
a79ebea6 | 111 | int ptrace_setregs(struct task_struct *child, struct user_pt_regs __user *data) |
ea3d710f DJ |
112 | { |
113 | struct pt_regs *regs; | |
114 | int i; | |
115 | ||
116 | if (!access_ok(VERIFY_READ, data, 38 * 8)) | |
117 | return -EIO; | |
118 | ||
40bc9c67 | 119 | regs = task_pt_regs(child); |
ea3d710f DJ |
120 | |
121 | for (i = 0; i < 32; i++) | |
a79ebea6 AS |
122 | __get_user(regs->regs[i], (__s64 __user *)&data->regs[i]); |
123 | __get_user(regs->lo, (__s64 __user *)&data->lo); | |
124 | __get_user(regs->hi, (__s64 __user *)&data->hi); | |
125 | __get_user(regs->cp0_epc, (__s64 __user *)&data->cp0_epc); | |
ea3d710f DJ |
126 | |
127 | /* badvaddr, status, and cause may not be written. */ | |
128 | ||
de8cd0dc JH |
129 | /* System call number may have been changed */ |
130 | mips_syscall_update_nr(child, regs); | |
131 | ||
ea3d710f DJ |
132 | return 0; |
133 | } | |
134 | ||
49a89efb | 135 | int ptrace_getfpregs(struct task_struct *child, __u32 __user *data) |
ea3d710f DJ |
136 | { |
137 | int i; | |
138 | ||
139 | if (!access_ok(VERIFY_WRITE, data, 33 * 8)) | |
140 | return -EIO; | |
141 | ||
142 | if (tsk_used_math(child)) { | |
bbd426f5 | 143 | union fpureg *fregs = get_fpu_regs(child); |
ea3d710f | 144 | for (i = 0; i < 32; i++) |
bbd426f5 PB |
145 | __put_user(get_fpr64(&fregs[i], 0), |
146 | i + (__u64 __user *)data); | |
ea3d710f DJ |
147 | } else { |
148 | for (i = 0; i < 32; i++) | |
49a89efb | 149 | __put_user((__u64) -1, i + (__u64 __user *) data); |
ea3d710f DJ |
150 | } |
151 | ||
49a89efb | 152 | __put_user(child->thread.fpu.fcr31, data + 64); |
656ff9be | 153 | __put_user(boot_cpu_data.fpu_id, data + 65); |
ea3d710f DJ |
154 | |
155 | return 0; | |
156 | } | |
157 | ||
49a89efb | 158 | int ptrace_setfpregs(struct task_struct *child, __u32 __user *data) |
ea3d710f | 159 | { |
bbd426f5 PB |
160 | union fpureg *fregs; |
161 | u64 fpr_val; | |
9b26616c | 162 | u32 value; |
ea3d710f DJ |
163 | int i; |
164 | ||
165 | if (!access_ok(VERIFY_READ, data, 33 * 8)) | |
166 | return -EIO; | |
167 | ||
ac9ad83b | 168 | init_fp_ctx(child); |
ea3d710f DJ |
169 | fregs = get_fpu_regs(child); |
170 | ||
bbd426f5 PB |
171 | for (i = 0; i < 32; i++) { |
172 | __get_user(fpr_val, i + (__u64 __user *)data); | |
173 | set_fpr64(&fregs[i], 0, fpr_val); | |
174 | } | |
ea3d710f | 175 | |
9b26616c | 176 | __get_user(value, data + 64); |
abf378be | 177 | ptrace_setfcr31(child, value); |
ea3d710f DJ |
178 | |
179 | /* FIR may not be written. */ | |
180 | ||
181 | return 0; | |
182 | } | |
183 | ||
0926bf95 DD |
184 | int ptrace_get_watch_regs(struct task_struct *child, |
185 | struct pt_watch_regs __user *addr) | |
186 | { | |
187 | enum pt_watch_style style; | |
188 | int i; | |
189 | ||
57c7ea51 | 190 | if (!cpu_has_watch || boot_cpu_data.watch_reg_use_cnt == 0) |
0926bf95 DD |
191 | return -EIO; |
192 | if (!access_ok(VERIFY_WRITE, addr, sizeof(struct pt_watch_regs))) | |
193 | return -EIO; | |
194 | ||
195 | #ifdef CONFIG_32BIT | |
196 | style = pt_watch_style_mips32; | |
197 | #define WATCH_STYLE mips32 | |
198 | #else | |
199 | style = pt_watch_style_mips64; | |
200 | #define WATCH_STYLE mips64 | |
201 | #endif | |
202 | ||
203 | __put_user(style, &addr->style); | |
57c7ea51 | 204 | __put_user(boot_cpu_data.watch_reg_use_cnt, |
0926bf95 | 205 | &addr->WATCH_STYLE.num_valid); |
57c7ea51 | 206 | for (i = 0; i < boot_cpu_data.watch_reg_use_cnt; i++) { |
0926bf95 DD |
207 | __put_user(child->thread.watch.mips3264.watchlo[i], |
208 | &addr->WATCH_STYLE.watchlo[i]); | |
50af501c JH |
209 | __put_user(child->thread.watch.mips3264.watchhi[i] & |
210 | (MIPS_WATCHHI_MASK | MIPS_WATCHHI_IRW), | |
0926bf95 | 211 | &addr->WATCH_STYLE.watchhi[i]); |
57c7ea51 | 212 | __put_user(boot_cpu_data.watch_reg_masks[i], |
0926bf95 DD |
213 | &addr->WATCH_STYLE.watch_masks[i]); |
214 | } | |
215 | for (; i < 8; i++) { | |
216 | __put_user(0, &addr->WATCH_STYLE.watchlo[i]); | |
217 | __put_user(0, &addr->WATCH_STYLE.watchhi[i]); | |
218 | __put_user(0, &addr->WATCH_STYLE.watch_masks[i]); | |
219 | } | |
220 | ||
221 | return 0; | |
222 | } | |
223 | ||
224 | int ptrace_set_watch_regs(struct task_struct *child, | |
225 | struct pt_watch_regs __user *addr) | |
226 | { | |
227 | int i; | |
228 | int watch_active = 0; | |
229 | unsigned long lt[NUM_WATCH_REGS]; | |
230 | u16 ht[NUM_WATCH_REGS]; | |
231 | ||
57c7ea51 | 232 | if (!cpu_has_watch || boot_cpu_data.watch_reg_use_cnt == 0) |
0926bf95 DD |
233 | return -EIO; |
234 | if (!access_ok(VERIFY_READ, addr, sizeof(struct pt_watch_regs))) | |
235 | return -EIO; | |
236 | /* Check the values. */ | |
57c7ea51 | 237 | for (i = 0; i < boot_cpu_data.watch_reg_use_cnt; i++) { |
0926bf95 DD |
238 | __get_user(lt[i], &addr->WATCH_STYLE.watchlo[i]); |
239 | #ifdef CONFIG_32BIT | |
240 | if (lt[i] & __UA_LIMIT) | |
241 | return -EINVAL; | |
242 | #else | |
243 | if (test_tsk_thread_flag(child, TIF_32BIT_ADDR)) { | |
244 | if (lt[i] & 0xffffffff80000000UL) | |
245 | return -EINVAL; | |
246 | } else { | |
247 | if (lt[i] & __UA_LIMIT) | |
248 | return -EINVAL; | |
249 | } | |
250 | #endif | |
251 | __get_user(ht[i], &addr->WATCH_STYLE.watchhi[i]); | |
50af501c | 252 | if (ht[i] & ~MIPS_WATCHHI_MASK) |
0926bf95 DD |
253 | return -EINVAL; |
254 | } | |
255 | /* Install them. */ | |
57c7ea51 | 256 | for (i = 0; i < boot_cpu_data.watch_reg_use_cnt; i++) { |
50af501c | 257 | if (lt[i] & MIPS_WATCHLO_IRW) |
0926bf95 DD |
258 | watch_active = 1; |
259 | child->thread.watch.mips3264.watchlo[i] = lt[i]; | |
260 | /* Set the G bit. */ | |
261 | child->thread.watch.mips3264.watchhi[i] = ht[i]; | |
262 | } | |
263 | ||
264 | if (watch_active) | |
265 | set_tsk_thread_flag(child, TIF_LOAD_WATCH); | |
266 | else | |
267 | clear_tsk_thread_flag(child, TIF_LOAD_WATCH); | |
268 | ||
269 | return 0; | |
270 | } | |
271 | ||
7aeb753b RB |
272 | /* regset get/set implementations */ |
273 | ||
c23b3d1a AS |
274 | #if defined(CONFIG_32BIT) || defined(CONFIG_MIPS32_O32) |
275 | ||
276 | static int gpr32_get(struct task_struct *target, | |
277 | const struct user_regset *regset, | |
278 | unsigned int pos, unsigned int count, | |
279 | void *kbuf, void __user *ubuf) | |
7aeb753b RB |
280 | { |
281 | struct pt_regs *regs = task_pt_regs(target); | |
c23b3d1a | 282 | u32 uregs[ELF_NGREG] = {}; |
7aeb753b | 283 | |
08c941bf | 284 | mips_dump_regs32(uregs, regs); |
c23b3d1a AS |
285 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, |
286 | sizeof(uregs)); | |
7aeb753b RB |
287 | } |
288 | ||
c23b3d1a AS |
289 | static int gpr32_set(struct task_struct *target, |
290 | const struct user_regset *regset, | |
291 | unsigned int pos, unsigned int count, | |
292 | const void *kbuf, const void __user *ubuf) | |
7aeb753b | 293 | { |
c23b3d1a AS |
294 | struct pt_regs *regs = task_pt_regs(target); |
295 | u32 uregs[ELF_NGREG]; | |
296 | unsigned start, num_regs, i; | |
297 | int err; | |
298 | ||
299 | start = pos / sizeof(u32); | |
300 | num_regs = count / sizeof(u32); | |
301 | ||
302 | if (start + num_regs > ELF_NGREG) | |
303 | return -EIO; | |
304 | ||
305 | err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, uregs, 0, | |
306 | sizeof(uregs)); | |
307 | if (err) | |
308 | return err; | |
309 | ||
310 | for (i = start; i < num_regs; i++) { | |
311 | /* | |
312 | * Cast all values to signed here so that if this is a 64-bit | |
313 | * kernel, the supplied 32-bit values will be sign extended. | |
314 | */ | |
315 | switch (i) { | |
316 | case MIPS32_EF_R1 ... MIPS32_EF_R25: | |
317 | /* k0/k1 are ignored. */ | |
318 | case MIPS32_EF_R28 ... MIPS32_EF_R31: | |
319 | regs->regs[i - MIPS32_EF_R0] = (s32)uregs[i]; | |
320 | break; | |
321 | case MIPS32_EF_LO: | |
322 | regs->lo = (s32)uregs[i]; | |
323 | break; | |
324 | case MIPS32_EF_HI: | |
325 | regs->hi = (s32)uregs[i]; | |
326 | break; | |
327 | case MIPS32_EF_CP0_EPC: | |
328 | regs->cp0_epc = (s32)uregs[i]; | |
329 | break; | |
330 | } | |
331 | } | |
332 | ||
de8cd0dc JH |
333 | /* System call number may have been changed */ |
334 | mips_syscall_update_nr(target, regs); | |
335 | ||
c23b3d1a AS |
336 | return 0; |
337 | } | |
338 | ||
339 | #endif /* CONFIG_32BIT || CONFIG_MIPS32_O32 */ | |
340 | ||
341 | #ifdef CONFIG_64BIT | |
342 | ||
343 | static int gpr64_get(struct task_struct *target, | |
344 | const struct user_regset *regset, | |
345 | unsigned int pos, unsigned int count, | |
346 | void *kbuf, void __user *ubuf) | |
347 | { | |
348 | struct pt_regs *regs = task_pt_regs(target); | |
349 | u64 uregs[ELF_NGREG] = {}; | |
c23b3d1a | 350 | |
08c941bf | 351 | mips_dump_regs64(uregs, regs); |
c23b3d1a AS |
352 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, |
353 | sizeof(uregs)); | |
354 | } | |
7aeb753b | 355 | |
c23b3d1a AS |
356 | static int gpr64_set(struct task_struct *target, |
357 | const struct user_regset *regset, | |
358 | unsigned int pos, unsigned int count, | |
359 | const void *kbuf, const void __user *ubuf) | |
360 | { | |
361 | struct pt_regs *regs = task_pt_regs(target); | |
362 | u64 uregs[ELF_NGREG]; | |
363 | unsigned start, num_regs, i; | |
364 | int err; | |
365 | ||
366 | start = pos / sizeof(u64); | |
367 | num_regs = count / sizeof(u64); | |
368 | ||
369 | if (start + num_regs > ELF_NGREG) | |
370 | return -EIO; | |
7aeb753b | 371 | |
c23b3d1a AS |
372 | err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, uregs, 0, |
373 | sizeof(uregs)); | |
374 | if (err) | |
375 | return err; | |
376 | ||
377 | for (i = start; i < num_regs; i++) { | |
378 | switch (i) { | |
379 | case MIPS64_EF_R1 ... MIPS64_EF_R25: | |
380 | /* k0/k1 are ignored. */ | |
381 | case MIPS64_EF_R28 ... MIPS64_EF_R31: | |
382 | regs->regs[i - MIPS64_EF_R0] = uregs[i]; | |
383 | break; | |
384 | case MIPS64_EF_LO: | |
385 | regs->lo = uregs[i]; | |
386 | break; | |
387 | case MIPS64_EF_HI: | |
388 | regs->hi = uregs[i]; | |
389 | break; | |
390 | case MIPS64_EF_CP0_EPC: | |
391 | regs->cp0_epc = uregs[i]; | |
392 | break; | |
393 | } | |
394 | } | |
7aeb753b | 395 | |
de8cd0dc JH |
396 | /* System call number may have been changed */ |
397 | mips_syscall_update_nr(target, regs); | |
398 | ||
7aeb753b RB |
399 | return 0; |
400 | } | |
401 | ||
c23b3d1a AS |
402 | #endif /* CONFIG_64BIT */ |
403 | ||
a03fe725 MR |
404 | /* |
405 | * Copy the floating-point context to the supplied NT_PRFPREG buffer, | |
406 | * !CONFIG_CPU_HAS_MSA variant. FP context's general register slots | |
be07a6a1 | 407 | * correspond 1:1 to buffer slots. Only general registers are copied. |
a03fe725 MR |
408 | */ |
409 | static int fpr_get_fpa(struct task_struct *target, | |
410 | unsigned int *pos, unsigned int *count, | |
411 | void **kbuf, void __user **ubuf) | |
7aeb753b | 412 | { |
a03fe725 MR |
413 | return user_regset_copyout(pos, count, kbuf, ubuf, |
414 | &target->thread.fpu, | |
be07a6a1 | 415 | 0, NUM_FPU_REGS * sizeof(elf_fpreg_t)); |
a03fe725 | 416 | } |
72b22bba | 417 | |
a03fe725 MR |
418 | /* |
419 | * Copy the floating-point context to the supplied NT_PRFPREG buffer, | |
420 | * CONFIG_CPU_HAS_MSA variant. Only lower 64 bits of FP context's | |
be07a6a1 MR |
421 | * general register slots are copied to buffer slots. Only general |
422 | * registers are copied. | |
a03fe725 MR |
423 | */ |
424 | static int fpr_get_msa(struct task_struct *target, | |
425 | unsigned int *pos, unsigned int *count, | |
426 | void **kbuf, void __user **ubuf) | |
427 | { | |
428 | unsigned int i; | |
429 | u64 fpr_val; | |
430 | int err; | |
72b22bba | 431 | |
006501e0 | 432 | BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t)); |
72b22bba PB |
433 | for (i = 0; i < NUM_FPU_REGS; i++) { |
434 | fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0); | |
a03fe725 | 435 | err = user_regset_copyout(pos, count, kbuf, ubuf, |
72b22bba PB |
436 | &fpr_val, i * sizeof(elf_fpreg_t), |
437 | (i + 1) * sizeof(elf_fpreg_t)); | |
438 | if (err) | |
439 | return err; | |
440 | } | |
441 | ||
442 | return 0; | |
7aeb753b RB |
443 | } |
444 | ||
be07a6a1 MR |
445 | /* |
446 | * Copy the floating-point context to the supplied NT_PRFPREG buffer. | |
447 | * Choose the appropriate helper for general registers, and then copy | |
71e909c0 | 448 | * the FCSR and FIR registers separately. |
be07a6a1 | 449 | */ |
a03fe725 | 450 | static int fpr_get(struct task_struct *target, |
7aeb753b RB |
451 | const struct user_regset *regset, |
452 | unsigned int pos, unsigned int count, | |
a03fe725 | 453 | void *kbuf, void __user *ubuf) |
7aeb753b | 454 | { |
be07a6a1 | 455 | const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); |
71e909c0 | 456 | const int fir_pos = fcr31_pos + sizeof(u32); |
72b22bba | 457 | int err; |
72b22bba | 458 | |
a03fe725 MR |
459 | if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) |
460 | err = fpr_get_fpa(target, &pos, &count, &kbuf, &ubuf); | |
461 | else | |
462 | err = fpr_get_msa(target, &pos, &count, &kbuf, &ubuf); | |
be07a6a1 MR |
463 | if (err) |
464 | return err; | |
465 | ||
466 | err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
467 | &target->thread.fpu.fcr31, | |
468 | fcr31_pos, fcr31_pos + sizeof(u32)); | |
71e909c0 MR |
469 | if (err) |
470 | return err; | |
471 | ||
472 | err = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
473 | &boot_cpu_data.fpu_id, | |
474 | fir_pos, fir_pos + sizeof(u32)); | |
a03fe725 MR |
475 | |
476 | return err; | |
477 | } | |
ac9ad83b | 478 | |
a03fe725 MR |
479 | /* |
480 | * Copy the supplied NT_PRFPREG buffer to the floating-point context, | |
481 | * !CONFIG_CPU_HAS_MSA variant. Buffer slots correspond 1:1 to FP | |
be07a6a1 | 482 | * context's general register slots. Only general registers are copied. |
a03fe725 MR |
483 | */ |
484 | static int fpr_set_fpa(struct task_struct *target, | |
485 | unsigned int *pos, unsigned int *count, | |
486 | const void **kbuf, const void __user **ubuf) | |
487 | { | |
488 | return user_regset_copyin(pos, count, kbuf, ubuf, | |
489 | &target->thread.fpu, | |
be07a6a1 | 490 | 0, NUM_FPU_REGS * sizeof(elf_fpreg_t)); |
a03fe725 MR |
491 | } |
492 | ||
493 | /* | |
494 | * Copy the supplied NT_PRFPREG buffer to the floating-point context, | |
495 | * CONFIG_CPU_HAS_MSA variant. Buffer slots are copied to lower 64 | |
be07a6a1 MR |
496 | * bits only of FP context's general register slots. Only general |
497 | * registers are copied. | |
a03fe725 MR |
498 | */ |
499 | static int fpr_set_msa(struct task_struct *target, | |
500 | unsigned int *pos, unsigned int *count, | |
501 | const void **kbuf, const void __user **ubuf) | |
502 | { | |
503 | unsigned int i; | |
504 | u64 fpr_val; | |
505 | int err; | |
72b22bba | 506 | |
d614fd58 | 507 | BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t)); |
80b3ffce | 508 | for (i = 0; i < NUM_FPU_REGS && *count > 0; i++) { |
a03fe725 | 509 | err = user_regset_copyin(pos, count, kbuf, ubuf, |
72b22bba PB |
510 | &fpr_val, i * sizeof(elf_fpreg_t), |
511 | (i + 1) * sizeof(elf_fpreg_t)); | |
512 | if (err) | |
513 | return err; | |
514 | set_fpr64(&target->thread.fpu.fpr[i], 0, fpr_val); | |
515 | } | |
516 | ||
517 | return 0; | |
7aeb753b RB |
518 | } |
519 | ||
dc24d0ed MR |
520 | /* |
521 | * Copy the supplied NT_PRFPREG buffer to the floating-point context. | |
be07a6a1 | 522 | * Choose the appropriate helper for general registers, and then copy |
71e909c0 MR |
523 | * the FCSR register separately. Ignore the incoming FIR register |
524 | * contents though, as the register is read-only. | |
dc24d0ed MR |
525 | * |
526 | * We optimize for the case where `count % sizeof(elf_fpreg_t) == 0', | |
527 | * which is supposed to have been guaranteed by the kernel before | |
528 | * calling us, e.g. in `ptrace_regset'. We enforce that requirement, | |
529 | * so that we can safely avoid preinitializing temporaries for | |
530 | * partial register writes. | |
531 | */ | |
a03fe725 MR |
532 | static int fpr_set(struct task_struct *target, |
533 | const struct user_regset *regset, | |
534 | unsigned int pos, unsigned int count, | |
535 | const void *kbuf, const void __user *ubuf) | |
536 | { | |
be07a6a1 | 537 | const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t); |
71e909c0 | 538 | const int fir_pos = fcr31_pos + sizeof(u32); |
be07a6a1 | 539 | u32 fcr31; |
a03fe725 MR |
540 | int err; |
541 | ||
dc24d0ed MR |
542 | BUG_ON(count % sizeof(elf_fpreg_t)); |
543 | ||
c8c5a3a2 MR |
544 | if (pos + count > sizeof(elf_fpregset_t)) |
545 | return -EIO; | |
546 | ||
a03fe725 MR |
547 | init_fp_ctx(target); |
548 | ||
549 | if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t)) | |
550 | err = fpr_set_fpa(target, &pos, &count, &kbuf, &ubuf); | |
551 | else | |
552 | err = fpr_set_msa(target, &pos, &count, &kbuf, &ubuf); | |
be07a6a1 MR |
553 | if (err) |
554 | return err; | |
555 | ||
556 | if (count > 0) { | |
557 | err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
558 | &fcr31, | |
559 | fcr31_pos, fcr31_pos + sizeof(u32)); | |
560 | if (err) | |
561 | return err; | |
562 | ||
563 | ptrace_setfcr31(target, fcr31); | |
564 | } | |
a03fe725 | 565 | |
71e909c0 MR |
566 | if (count > 0) |
567 | err = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, | |
568 | fir_pos, | |
569 | fir_pos + sizeof(u32)); | |
570 | ||
a03fe725 MR |
571 | return err; |
572 | } | |
573 | ||
44109c60 MR |
574 | #if defined(CONFIG_32BIT) || defined(CONFIG_MIPS32_O32) |
575 | ||
576 | /* | |
577 | * Copy the DSP context to the supplied 32-bit NT_MIPS_DSP buffer. | |
578 | */ | |
579 | static int dsp32_get(struct task_struct *target, | |
580 | const struct user_regset *regset, | |
581 | unsigned int pos, unsigned int count, | |
582 | void *kbuf, void __user *ubuf) | |
583 | { | |
584 | unsigned int start, num_regs, i; | |
585 | u32 dspregs[NUM_DSP_REGS + 1]; | |
586 | ||
587 | BUG_ON(count % sizeof(u32)); | |
588 | ||
589 | if (!cpu_has_dsp) | |
590 | return -EIO; | |
591 | ||
592 | start = pos / sizeof(u32); | |
593 | num_regs = count / sizeof(u32); | |
594 | ||
595 | if (start + num_regs > NUM_DSP_REGS + 1) | |
596 | return -EIO; | |
597 | ||
598 | for (i = start; i < num_regs; i++) | |
599 | switch (i) { | |
600 | case 0 ... NUM_DSP_REGS - 1: | |
601 | dspregs[i] = target->thread.dsp.dspr[i]; | |
602 | break; | |
603 | case NUM_DSP_REGS: | |
604 | dspregs[i] = target->thread.dsp.dspcontrol; | |
605 | break; | |
606 | } | |
607 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, dspregs, 0, | |
608 | sizeof(dspregs)); | |
609 | } | |
610 | ||
611 | /* | |
612 | * Copy the supplied 32-bit NT_MIPS_DSP buffer to the DSP context. | |
613 | */ | |
614 | static int dsp32_set(struct task_struct *target, | |
615 | const struct user_regset *regset, | |
616 | unsigned int pos, unsigned int count, | |
617 | const void *kbuf, const void __user *ubuf) | |
618 | { | |
619 | unsigned int start, num_regs, i; | |
620 | u32 dspregs[NUM_DSP_REGS + 1]; | |
621 | int err; | |
622 | ||
623 | BUG_ON(count % sizeof(u32)); | |
624 | ||
625 | if (!cpu_has_dsp) | |
626 | return -EIO; | |
627 | ||
628 | start = pos / sizeof(u32); | |
629 | num_regs = count / sizeof(u32); | |
630 | ||
631 | if (start + num_regs > NUM_DSP_REGS + 1) | |
632 | return -EIO; | |
633 | ||
634 | err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, dspregs, 0, | |
635 | sizeof(dspregs)); | |
636 | if (err) | |
637 | return err; | |
638 | ||
639 | for (i = start; i < num_regs; i++) | |
640 | switch (i) { | |
641 | case 0 ... NUM_DSP_REGS - 1: | |
642 | target->thread.dsp.dspr[i] = (s32)dspregs[i]; | |
643 | break; | |
644 | case NUM_DSP_REGS: | |
645 | target->thread.dsp.dspcontrol = (s32)dspregs[i]; | |
646 | break; | |
647 | } | |
648 | ||
649 | return 0; | |
650 | } | |
651 | ||
652 | #endif /* CONFIG_32BIT || CONFIG_MIPS32_O32 */ | |
653 | ||
654 | #ifdef CONFIG_64BIT | |
655 | ||
656 | /* | |
657 | * Copy the DSP context to the supplied 64-bit NT_MIPS_DSP buffer. | |
658 | */ | |
659 | static int dsp64_get(struct task_struct *target, | |
660 | const struct user_regset *regset, | |
661 | unsigned int pos, unsigned int count, | |
662 | void *kbuf, void __user *ubuf) | |
663 | { | |
664 | unsigned int start, num_regs, i; | |
665 | u64 dspregs[NUM_DSP_REGS + 1]; | |
666 | ||
667 | BUG_ON(count % sizeof(u64)); | |
668 | ||
669 | if (!cpu_has_dsp) | |
670 | return -EIO; | |
671 | ||
672 | start = pos / sizeof(u64); | |
673 | num_regs = count / sizeof(u64); | |
674 | ||
675 | if (start + num_regs > NUM_DSP_REGS + 1) | |
676 | return -EIO; | |
677 | ||
678 | for (i = start; i < num_regs; i++) | |
679 | switch (i) { | |
680 | case 0 ... NUM_DSP_REGS - 1: | |
681 | dspregs[i] = target->thread.dsp.dspr[i]; | |
682 | break; | |
683 | case NUM_DSP_REGS: | |
684 | dspregs[i] = target->thread.dsp.dspcontrol; | |
685 | break; | |
686 | } | |
687 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, dspregs, 0, | |
688 | sizeof(dspregs)); | |
689 | } | |
690 | ||
691 | /* | |
692 | * Copy the supplied 64-bit NT_MIPS_DSP buffer to the DSP context. | |
693 | */ | |
694 | static int dsp64_set(struct task_struct *target, | |
695 | const struct user_regset *regset, | |
696 | unsigned int pos, unsigned int count, | |
697 | const void *kbuf, const void __user *ubuf) | |
698 | { | |
699 | unsigned int start, num_regs, i; | |
700 | u64 dspregs[NUM_DSP_REGS + 1]; | |
701 | int err; | |
702 | ||
703 | BUG_ON(count % sizeof(u64)); | |
704 | ||
705 | if (!cpu_has_dsp) | |
706 | return -EIO; | |
707 | ||
708 | start = pos / sizeof(u64); | |
709 | num_regs = count / sizeof(u64); | |
710 | ||
711 | if (start + num_regs > NUM_DSP_REGS + 1) | |
712 | return -EIO; | |
713 | ||
714 | err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, dspregs, 0, | |
715 | sizeof(dspregs)); | |
716 | if (err) | |
717 | return err; | |
718 | ||
719 | for (i = start; i < num_regs; i++) | |
720 | switch (i) { | |
721 | case 0 ... NUM_DSP_REGS - 1: | |
722 | target->thread.dsp.dspr[i] = dspregs[i]; | |
723 | break; | |
724 | case NUM_DSP_REGS: | |
725 | target->thread.dsp.dspcontrol = dspregs[i]; | |
726 | break; | |
727 | } | |
728 | ||
729 | return 0; | |
730 | } | |
731 | ||
732 | #endif /* CONFIG_64BIT */ | |
733 | ||
734 | /* | |
735 | * Determine whether the DSP context is present. | |
736 | */ | |
737 | static int dsp_active(struct task_struct *target, | |
738 | const struct user_regset *regset) | |
739 | { | |
740 | return cpu_has_dsp ? NUM_DSP_REGS + 1 : -ENODEV; | |
741 | } | |
742 | ||
1ae22a0e MR |
743 | /* Copy the FP mode setting to the supplied NT_MIPS_FP_MODE buffer. */ |
744 | static int fp_mode_get(struct task_struct *target, | |
745 | const struct user_regset *regset, | |
746 | unsigned int pos, unsigned int count, | |
747 | void *kbuf, void __user *ubuf) | |
748 | { | |
749 | int fp_mode; | |
750 | ||
751 | fp_mode = mips_get_process_fp_mode(target); | |
752 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &fp_mode, 0, | |
753 | sizeof(fp_mode)); | |
754 | } | |
755 | ||
756 | /* | |
757 | * Copy the supplied NT_MIPS_FP_MODE buffer to the FP mode setting. | |
758 | * | |
759 | * We optimize for the case where `count % sizeof(int) == 0', which | |
760 | * is supposed to have been guaranteed by the kernel before calling | |
761 | * us, e.g. in `ptrace_regset'. We enforce that requirement, so | |
762 | * that we can safely avoid preinitializing temporaries for partial | |
763 | * mode writes. | |
764 | */ | |
765 | static int fp_mode_set(struct task_struct *target, | |
766 | const struct user_regset *regset, | |
767 | unsigned int pos, unsigned int count, | |
768 | const void *kbuf, const void __user *ubuf) | |
769 | { | |
770 | int fp_mode; | |
771 | int err; | |
772 | ||
773 | BUG_ON(count % sizeof(int)); | |
774 | ||
775 | if (pos + count > sizeof(fp_mode)) | |
776 | return -EIO; | |
777 | ||
778 | err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fp_mode, 0, | |
779 | sizeof(fp_mode)); | |
780 | if (err) | |
781 | return err; | |
782 | ||
783 | if (count > 0) | |
784 | err = mips_set_process_fp_mode(target, fp_mode); | |
785 | ||
786 | return err; | |
787 | } | |
788 | ||
7aeb753b RB |
789 | enum mips_regset { |
790 | REGSET_GPR, | |
791 | REGSET_FPR, | |
44109c60 | 792 | REGSET_DSP, |
1ae22a0e | 793 | REGSET_FP_MODE, |
7aeb753b RB |
794 | }; |
795 | ||
40e084a5 RB |
796 | struct pt_regs_offset { |
797 | const char *name; | |
798 | int offset; | |
799 | }; | |
800 | ||
801 | #define REG_OFFSET_NAME(reg, r) { \ | |
802 | .name = #reg, \ | |
803 | .offset = offsetof(struct pt_regs, r) \ | |
804 | } | |
805 | ||
806 | #define REG_OFFSET_END { \ | |
807 | .name = NULL, \ | |
808 | .offset = 0 \ | |
809 | } | |
810 | ||
811 | static const struct pt_regs_offset regoffset_table[] = { | |
812 | REG_OFFSET_NAME(r0, regs[0]), | |
813 | REG_OFFSET_NAME(r1, regs[1]), | |
814 | REG_OFFSET_NAME(r2, regs[2]), | |
815 | REG_OFFSET_NAME(r3, regs[3]), | |
816 | REG_OFFSET_NAME(r4, regs[4]), | |
817 | REG_OFFSET_NAME(r5, regs[5]), | |
818 | REG_OFFSET_NAME(r6, regs[6]), | |
819 | REG_OFFSET_NAME(r7, regs[7]), | |
820 | REG_OFFSET_NAME(r8, regs[8]), | |
821 | REG_OFFSET_NAME(r9, regs[9]), | |
822 | REG_OFFSET_NAME(r10, regs[10]), | |
823 | REG_OFFSET_NAME(r11, regs[11]), | |
824 | REG_OFFSET_NAME(r12, regs[12]), | |
825 | REG_OFFSET_NAME(r13, regs[13]), | |
826 | REG_OFFSET_NAME(r14, regs[14]), | |
827 | REG_OFFSET_NAME(r15, regs[15]), | |
828 | REG_OFFSET_NAME(r16, regs[16]), | |
829 | REG_OFFSET_NAME(r17, regs[17]), | |
830 | REG_OFFSET_NAME(r18, regs[18]), | |
831 | REG_OFFSET_NAME(r19, regs[19]), | |
832 | REG_OFFSET_NAME(r20, regs[20]), | |
833 | REG_OFFSET_NAME(r21, regs[21]), | |
834 | REG_OFFSET_NAME(r22, regs[22]), | |
835 | REG_OFFSET_NAME(r23, regs[23]), | |
836 | REG_OFFSET_NAME(r24, regs[24]), | |
837 | REG_OFFSET_NAME(r25, regs[25]), | |
838 | REG_OFFSET_NAME(r26, regs[26]), | |
839 | REG_OFFSET_NAME(r27, regs[27]), | |
840 | REG_OFFSET_NAME(r28, regs[28]), | |
841 | REG_OFFSET_NAME(r29, regs[29]), | |
842 | REG_OFFSET_NAME(r30, regs[30]), | |
843 | REG_OFFSET_NAME(r31, regs[31]), | |
844 | REG_OFFSET_NAME(c0_status, cp0_status), | |
845 | REG_OFFSET_NAME(hi, hi), | |
846 | REG_OFFSET_NAME(lo, lo), | |
847 | #ifdef CONFIG_CPU_HAS_SMARTMIPS | |
848 | REG_OFFSET_NAME(acx, acx), | |
849 | #endif | |
850 | REG_OFFSET_NAME(c0_badvaddr, cp0_badvaddr), | |
851 | REG_OFFSET_NAME(c0_cause, cp0_cause), | |
852 | REG_OFFSET_NAME(c0_epc, cp0_epc), | |
40e084a5 RB |
853 | #ifdef CONFIG_CPU_CAVIUM_OCTEON |
854 | REG_OFFSET_NAME(mpl0, mpl[0]), | |
855 | REG_OFFSET_NAME(mpl1, mpl[1]), | |
856 | REG_OFFSET_NAME(mpl2, mpl[2]), | |
857 | REG_OFFSET_NAME(mtp0, mtp[0]), | |
858 | REG_OFFSET_NAME(mtp1, mtp[1]), | |
859 | REG_OFFSET_NAME(mtp2, mtp[2]), | |
860 | #endif | |
861 | REG_OFFSET_END, | |
862 | }; | |
863 | ||
864 | /** | |
865 | * regs_query_register_offset() - query register offset from its name | |
866 | * @name: the name of a register | |
867 | * | |
868 | * regs_query_register_offset() returns the offset of a register in struct | |
869 | * pt_regs from its name. If the name is invalid, this returns -EINVAL; | |
870 | */ | |
871 | int regs_query_register_offset(const char *name) | |
872 | { | |
873 | const struct pt_regs_offset *roff; | |
874 | for (roff = regoffset_table; roff->name != NULL; roff++) | |
875 | if (!strcmp(roff->name, name)) | |
876 | return roff->offset; | |
877 | return -EINVAL; | |
878 | } | |
879 | ||
c23b3d1a AS |
880 | #if defined(CONFIG_32BIT) || defined(CONFIG_MIPS32_O32) |
881 | ||
7aeb753b RB |
882 | static const struct user_regset mips_regsets[] = { |
883 | [REGSET_GPR] = { | |
884 | .core_note_type = NT_PRSTATUS, | |
885 | .n = ELF_NGREG, | |
886 | .size = sizeof(unsigned int), | |
887 | .align = sizeof(unsigned int), | |
c23b3d1a AS |
888 | .get = gpr32_get, |
889 | .set = gpr32_set, | |
7aeb753b RB |
890 | }, |
891 | [REGSET_FPR] = { | |
892 | .core_note_type = NT_PRFPREG, | |
893 | .n = ELF_NFPREG, | |
894 | .size = sizeof(elf_fpreg_t), | |
895 | .align = sizeof(elf_fpreg_t), | |
896 | .get = fpr_get, | |
897 | .set = fpr_set, | |
898 | }, | |
44109c60 MR |
899 | [REGSET_DSP] = { |
900 | .core_note_type = NT_MIPS_DSP, | |
901 | .n = NUM_DSP_REGS + 1, | |
902 | .size = sizeof(u32), | |
903 | .align = sizeof(u32), | |
904 | .get = dsp32_get, | |
905 | .set = dsp32_set, | |
906 | .active = dsp_active, | |
907 | }, | |
1ae22a0e MR |
908 | [REGSET_FP_MODE] = { |
909 | .core_note_type = NT_MIPS_FP_MODE, | |
910 | .n = 1, | |
911 | .size = sizeof(int), | |
912 | .align = sizeof(int), | |
913 | .get = fp_mode_get, | |
914 | .set = fp_mode_set, | |
915 | }, | |
7aeb753b RB |
916 | }; |
917 | ||
918 | static const struct user_regset_view user_mips_view = { | |
919 | .name = "mips", | |
920 | .e_machine = ELF_ARCH, | |
921 | .ei_osabi = ELF_OSABI, | |
922 | .regsets = mips_regsets, | |
923 | .n = ARRAY_SIZE(mips_regsets), | |
924 | }; | |
925 | ||
c23b3d1a AS |
926 | #endif /* CONFIG_32BIT || CONFIG_MIPS32_O32 */ |
927 | ||
928 | #ifdef CONFIG_64BIT | |
929 | ||
7aeb753b RB |
930 | static const struct user_regset mips64_regsets[] = { |
931 | [REGSET_GPR] = { | |
932 | .core_note_type = NT_PRSTATUS, | |
933 | .n = ELF_NGREG, | |
934 | .size = sizeof(unsigned long), | |
935 | .align = sizeof(unsigned long), | |
c23b3d1a AS |
936 | .get = gpr64_get, |
937 | .set = gpr64_set, | |
7aeb753b RB |
938 | }, |
939 | [REGSET_FPR] = { | |
940 | .core_note_type = NT_PRFPREG, | |
941 | .n = ELF_NFPREG, | |
942 | .size = sizeof(elf_fpreg_t), | |
943 | .align = sizeof(elf_fpreg_t), | |
944 | .get = fpr_get, | |
945 | .set = fpr_set, | |
946 | }, | |
44109c60 MR |
947 | [REGSET_DSP] = { |
948 | .core_note_type = NT_MIPS_DSP, | |
949 | .n = NUM_DSP_REGS + 1, | |
950 | .size = sizeof(u64), | |
951 | .align = sizeof(u64), | |
952 | .get = dsp64_get, | |
953 | .set = dsp64_set, | |
954 | .active = dsp_active, | |
955 | }, | |
1ae22a0e MR |
956 | [REGSET_FP_MODE] = { |
957 | .core_note_type = NT_MIPS_FP_MODE, | |
958 | .n = 1, | |
959 | .size = sizeof(int), | |
960 | .align = sizeof(int), | |
961 | .get = fp_mode_get, | |
962 | .set = fp_mode_set, | |
963 | }, | |
7aeb753b RB |
964 | }; |
965 | ||
966 | static const struct user_regset_view user_mips64_view = { | |
c23b3d1a | 967 | .name = "mips64", |
7aeb753b RB |
968 | .e_machine = ELF_ARCH, |
969 | .ei_osabi = ELF_OSABI, | |
970 | .regsets = mips64_regsets, | |
c23b3d1a | 971 | .n = ARRAY_SIZE(mips64_regsets), |
7aeb753b RB |
972 | }; |
973 | ||
547da673 MR |
974 | #ifdef CONFIG_MIPS32_N32 |
975 | ||
976 | static const struct user_regset_view user_mipsn32_view = { | |
977 | .name = "mipsn32", | |
978 | .e_flags = EF_MIPS_ABI2, | |
979 | .e_machine = ELF_ARCH, | |
980 | .ei_osabi = ELF_OSABI, | |
981 | .regsets = mips64_regsets, | |
982 | .n = ARRAY_SIZE(mips64_regsets), | |
983 | }; | |
984 | ||
985 | #endif /* CONFIG_MIPS32_N32 */ | |
986 | ||
c23b3d1a AS |
987 | #endif /* CONFIG_64BIT */ |
988 | ||
7aeb753b RB |
989 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
990 | { | |
991 | #ifdef CONFIG_32BIT | |
992 | return &user_mips_view; | |
c23b3d1a | 993 | #else |
7aeb753b | 994 | #ifdef CONFIG_MIPS32_O32 |
c23b3d1a AS |
995 | if (test_tsk_thread_flag(task, TIF_32BIT_REGS)) |
996 | return &user_mips_view; | |
547da673 MR |
997 | #endif |
998 | #ifdef CONFIG_MIPS32_N32 | |
999 | if (test_tsk_thread_flag(task, TIF_32BIT_ADDR)) | |
1000 | return &user_mipsn32_view; | |
7aeb753b | 1001 | #endif |
7aeb753b | 1002 | return &user_mips64_view; |
c23b3d1a | 1003 | #endif |
7aeb753b RB |
1004 | } |
1005 | ||
9b05a69e NK |
1006 | long arch_ptrace(struct task_struct *child, long request, |
1007 | unsigned long addr, unsigned long data) | |
1da177e4 | 1008 | { |
1da177e4 | 1009 | int ret; |
fb671139 NK |
1010 | void __user *addrp = (void __user *) addr; |
1011 | void __user *datavp = (void __user *) data; | |
1012 | unsigned long __user *datalp = (void __user *) data; | |
1da177e4 | 1013 | |
1da177e4 LT |
1014 | switch (request) { |
1015 | /* when I and D space are separate, these will need to be fixed. */ | |
1016 | case PTRACE_PEEKTEXT: /* read word at location addr. */ | |
76647323 AD |
1017 | case PTRACE_PEEKDATA: |
1018 | ret = generic_ptrace_peekdata(child, addr, data); | |
1da177e4 | 1019 | break; |
1da177e4 LT |
1020 | |
1021 | /* Read the word at location addr in the USER area. */ | |
1022 | case PTRACE_PEEKUSR: { | |
1023 | struct pt_regs *regs; | |
bbd426f5 | 1024 | union fpureg *fregs; |
1da177e4 LT |
1025 | unsigned long tmp = 0; |
1026 | ||
40bc9c67 | 1027 | regs = task_pt_regs(child); |
1da177e4 LT |
1028 | ret = 0; /* Default return value. */ |
1029 | ||
1030 | switch (addr) { | |
1031 | case 0 ... 31: | |
1032 | tmp = regs->regs[addr]; | |
1033 | break; | |
1034 | case FPR_BASE ... FPR_BASE + 31: | |
597ce172 PB |
1035 | if (!tsk_used_math(child)) { |
1036 | /* FP not yet used */ | |
1037 | tmp = -1; | |
1038 | break; | |
1039 | } | |
1040 | fregs = get_fpu_regs(child); | |
1da177e4 | 1041 | |
875d43e7 | 1042 | #ifdef CONFIG_32BIT |
9a3a92cc | 1043 | if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) { |
1da177e4 LT |
1044 | /* |
1045 | * The odd registers are actually the high | |
1046 | * order bits of the values stored in the even | |
d1157b10 | 1047 | * registers. |
1da177e4 | 1048 | */ |
bbd426f5 PB |
1049 | tmp = get_fpr32(&fregs[(addr & ~1) - FPR_BASE], |
1050 | addr & 1); | |
597ce172 | 1051 | break; |
1da177e4 | 1052 | } |
597ce172 | 1053 | #endif |
c7e81462 | 1054 | tmp = get_fpr64(&fregs[addr - FPR_BASE], 0); |
1da177e4 LT |
1055 | break; |
1056 | case PC: | |
1057 | tmp = regs->cp0_epc; | |
1058 | break; | |
1059 | case CAUSE: | |
1060 | tmp = regs->cp0_cause; | |
1061 | break; | |
1062 | case BADVADDR: | |
1063 | tmp = regs->cp0_badvaddr; | |
1064 | break; | |
1065 | case MMHI: | |
1066 | tmp = regs->hi; | |
1067 | break; | |
1068 | case MMLO: | |
1069 | tmp = regs->lo; | |
1070 | break; | |
9693a853 FBH |
1071 | #ifdef CONFIG_CPU_HAS_SMARTMIPS |
1072 | case ACX: | |
1073 | tmp = regs->acx; | |
1074 | break; | |
1075 | #endif | |
1da177e4 | 1076 | case FPC_CSR: |
eae89076 | 1077 | tmp = child->thread.fpu.fcr31; |
1da177e4 | 1078 | break; |
3351047f PB |
1079 | case FPC_EIR: |
1080 | /* implementation / version register */ | |
656ff9be | 1081 | tmp = boot_cpu_data.fpu_id; |
1da177e4 | 1082 | break; |
c134a5ec RB |
1083 | case DSP_BASE ... DSP_BASE + 5: { |
1084 | dspreg_t *dregs; | |
1085 | ||
e50c0a8f RB |
1086 | if (!cpu_has_dsp) { |
1087 | tmp = 0; | |
1088 | ret = -EIO; | |
481bed45 | 1089 | goto out; |
e50c0a8f | 1090 | } |
6c355852 | 1091 | dregs = __get_dsp_regs(child); |
f5958b4c | 1092 | tmp = dregs[addr - DSP_BASE]; |
e50c0a8f | 1093 | break; |
c134a5ec | 1094 | } |
e50c0a8f RB |
1095 | case DSP_CONTROL: |
1096 | if (!cpu_has_dsp) { | |
1097 | tmp = 0; | |
1098 | ret = -EIO; | |
481bed45 | 1099 | goto out; |
e50c0a8f RB |
1100 | } |
1101 | tmp = child->thread.dsp.dspcontrol; | |
1102 | break; | |
1da177e4 LT |
1103 | default: |
1104 | tmp = 0; | |
1105 | ret = -EIO; | |
481bed45 | 1106 | goto out; |
1da177e4 | 1107 | } |
fb671139 | 1108 | ret = put_user(tmp, datalp); |
1da177e4 LT |
1109 | break; |
1110 | } | |
1111 | ||
1112 | /* when I and D space are separate, this will have to be fixed. */ | |
1113 | case PTRACE_POKETEXT: /* write the word at location addr. */ | |
1114 | case PTRACE_POKEDATA: | |
f284ce72 | 1115 | ret = generic_ptrace_pokedata(child, addr, data); |
1da177e4 LT |
1116 | break; |
1117 | ||
1118 | case PTRACE_POKEUSR: { | |
1119 | struct pt_regs *regs; | |
1120 | ret = 0; | |
40bc9c67 | 1121 | regs = task_pt_regs(child); |
1da177e4 LT |
1122 | |
1123 | switch (addr) { | |
1124 | case 0 ... 31: | |
1125 | regs->regs[addr] = data; | |
de8cd0dc JH |
1126 | /* System call number may have been changed */ |
1127 | if (addr == 2) | |
1128 | mips_syscall_update_nr(child, regs); | |
1129 | else if (addr == 4 && | |
1130 | mips_syscall_is_indirect(child, regs)) | |
1131 | mips_syscall_update_nr(child, regs); | |
1da177e4 LT |
1132 | break; |
1133 | case FPR_BASE ... FPR_BASE + 31: { | |
bbd426f5 | 1134 | union fpureg *fregs = get_fpu_regs(child); |
1da177e4 | 1135 | |
ac9ad83b | 1136 | init_fp_ctx(child); |
875d43e7 | 1137 | #ifdef CONFIG_32BIT |
9a3a92cc | 1138 | if (test_tsk_thread_flag(child, TIF_32BIT_FPREGS)) { |
597ce172 PB |
1139 | /* |
1140 | * The odd registers are actually the high | |
1141 | * order bits of the values stored in the even | |
d1157b10 | 1142 | * registers. |
597ce172 | 1143 | */ |
bbd426f5 PB |
1144 | set_fpr32(&fregs[(addr & ~1) - FPR_BASE], |
1145 | addr & 1, data); | |
597ce172 | 1146 | break; |
1da177e4 LT |
1147 | } |
1148 | #endif | |
bbd426f5 | 1149 | set_fpr64(&fregs[addr - FPR_BASE], 0, data); |
1da177e4 LT |
1150 | break; |
1151 | } | |
1152 | case PC: | |
1153 | regs->cp0_epc = data; | |
1154 | break; | |
1155 | case MMHI: | |
1156 | regs->hi = data; | |
1157 | break; | |
1158 | case MMLO: | |
1159 | regs->lo = data; | |
1160 | break; | |
9693a853 FBH |
1161 | #ifdef CONFIG_CPU_HAS_SMARTMIPS |
1162 | case ACX: | |
1163 | regs->acx = data; | |
1164 | break; | |
1165 | #endif | |
1da177e4 | 1166 | case FPC_CSR: |
c9e56039 | 1167 | init_fp_ctx(child); |
abf378be | 1168 | ptrace_setfcr31(child, data); |
1da177e4 | 1169 | break; |
c134a5ec RB |
1170 | case DSP_BASE ... DSP_BASE + 5: { |
1171 | dspreg_t *dregs; | |
1172 | ||
e50c0a8f RB |
1173 | if (!cpu_has_dsp) { |
1174 | ret = -EIO; | |
1175 | break; | |
1176 | } | |
1177 | ||
c134a5ec | 1178 | dregs = __get_dsp_regs(child); |
e50c0a8f RB |
1179 | dregs[addr - DSP_BASE] = data; |
1180 | break; | |
c134a5ec | 1181 | } |
e50c0a8f RB |
1182 | case DSP_CONTROL: |
1183 | if (!cpu_has_dsp) { | |
1184 | ret = -EIO; | |
1185 | break; | |
1186 | } | |
1187 | child->thread.dsp.dspcontrol = data; | |
1188 | break; | |
1da177e4 LT |
1189 | default: |
1190 | /* The rest are not allowed. */ | |
1191 | ret = -EIO; | |
1192 | break; | |
1193 | } | |
1194 | break; | |
1195 | } | |
1196 | ||
ea3d710f | 1197 | case PTRACE_GETREGS: |
fb671139 | 1198 | ret = ptrace_getregs(child, datavp); |
ea3d710f DJ |
1199 | break; |
1200 | ||
1201 | case PTRACE_SETREGS: | |
fb671139 | 1202 | ret = ptrace_setregs(child, datavp); |
ea3d710f DJ |
1203 | break; |
1204 | ||
1205 | case PTRACE_GETFPREGS: | |
fb671139 | 1206 | ret = ptrace_getfpregs(child, datavp); |
ea3d710f DJ |
1207 | break; |
1208 | ||
1209 | case PTRACE_SETFPREGS: | |
fb671139 | 1210 | ret = ptrace_setfpregs(child, datavp); |
ea3d710f DJ |
1211 | break; |
1212 | ||
3c37026d | 1213 | case PTRACE_GET_THREAD_AREA: |
fb671139 | 1214 | ret = put_user(task_thread_info(child)->tp_value, datalp); |
3c37026d RB |
1215 | break; |
1216 | ||
0926bf95 | 1217 | case PTRACE_GET_WATCH_REGS: |
fb671139 | 1218 | ret = ptrace_get_watch_regs(child, addrp); |
0926bf95 DD |
1219 | break; |
1220 | ||
1221 | case PTRACE_SET_WATCH_REGS: | |
fb671139 | 1222 | ret = ptrace_set_watch_regs(child, addrp); |
0926bf95 DD |
1223 | break; |
1224 | ||
1da177e4 LT |
1225 | default: |
1226 | ret = ptrace_request(child, request, addr, data); | |
1227 | break; | |
1228 | } | |
481bed45 | 1229 | out: |
1da177e4 LT |
1230 | return ret; |
1231 | } | |
1232 | ||
1233 | /* | |
1234 | * Notification of system call entry/exit | |
1235 | * - triggered by current->work.syscall_trace | |
1236 | */ | |
4c21b8fd | 1237 | asmlinkage long syscall_trace_enter(struct pt_regs *regs, long syscall) |
1da177e4 | 1238 | { |
c3fc5cd5 RB |
1239 | user_exit(); |
1240 | ||
c2d9f177 LP |
1241 | current_thread_info()->syscall = syscall; |
1242 | ||
b6318a90 JH |
1243 | if (test_thread_flag(TIF_SYSCALL_TRACE)) { |
1244 | if (tracehook_report_syscall_entry(regs)) | |
1245 | return -1; | |
1246 | syscall = current_thread_info()->syscall; | |
1247 | } | |
2ac3c8d1 | 1248 | |
669c4092 DD |
1249 | #ifdef CONFIG_SECCOMP |
1250 | if (unlikely(test_thread_flag(TIF_SECCOMP))) { | |
1251 | int ret, i; | |
1252 | struct seccomp_data sd; | |
3d729dea | 1253 | unsigned long args[6]; |
669c4092 DD |
1254 | |
1255 | sd.nr = syscall; | |
1256 | sd.arch = syscall_get_arch(); | |
3d729dea JH |
1257 | syscall_get_arguments(current, regs, 0, 6, args); |
1258 | for (i = 0; i < 6; i++) | |
1259 | sd.args[i] = args[i]; | |
669c4092 DD |
1260 | sd.instruction_pointer = KSTK_EIP(current); |
1261 | ||
1262 | ret = __secure_computing(&sd); | |
1263 | if (ret == -1) | |
1264 | return ret; | |
b6318a90 | 1265 | syscall = current_thread_info()->syscall; |
669c4092 DD |
1266 | } |
1267 | #endif | |
293c5bd1 | 1268 | |
1d7bf993 RB |
1269 | if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) |
1270 | trace_sys_enter(regs, regs->regs[2]); | |
1271 | ||
91397401 | 1272 | audit_syscall_entry(syscall, regs->regs[4], regs->regs[5], |
b05d8447 | 1273 | regs->regs[6], regs->regs[7]); |
828db212 JH |
1274 | |
1275 | /* | |
1276 | * Negative syscall numbers are mistaken for rejected syscalls, but | |
1277 | * won't have had the return value set appropriately, so we do so now. | |
1278 | */ | |
1279 | if (syscall < 0) | |
1280 | syscall_set_return_value(current, regs, -ENOSYS, 0); | |
1225eb82 | 1281 | return syscall; |
1da177e4 | 1282 | } |
8b659a39 RB |
1283 | |
1284 | /* | |
1285 | * Notification of system call entry/exit | |
1286 | * - triggered by current->work.syscall_trace | |
1287 | */ | |
1288 | asmlinkage void syscall_trace_leave(struct pt_regs *regs) | |
1289 | { | |
c3fc5cd5 RB |
1290 | /* |
1291 | * We may come here right after calling schedule_user() | |
1292 | * or do_notify_resume(), in which case we can be in RCU | |
1293 | * user mode. | |
1294 | */ | |
1295 | user_exit(); | |
1296 | ||
d7e7528b | 1297 | audit_syscall_exit(regs); |
8b659a39 | 1298 | |
1d7bf993 | 1299 | if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) |
4f32a39d | 1300 | trace_sys_exit(regs, regs_return_value(regs)); |
1d7bf993 | 1301 | |
bc3d22c1 RB |
1302 | if (test_thread_flag(TIF_SYSCALL_TRACE)) |
1303 | tracehook_report_syscall_exit(regs, 0); | |
c3fc5cd5 RB |
1304 | |
1305 | user_enter(); | |
8b659a39 | 1306 | } |