diff options
Diffstat (limited to 'arch/mips/kernel/ptrace.c')
-rw-r--r-- | arch/mips/kernel/ptrace.c | 242 |
1 files changed, 219 insertions, 23 deletions
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 0b571a5b4b83..fcceab8f2e00 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c | |||
@@ -28,14 +28,18 @@ | |||
28 | #include <linux/security.h> | 28 | #include <linux/security.h> |
29 | #include <linux/signal.h> | 29 | #include <linux/signal.h> |
30 | 30 | ||
31 | #include <asm/byteorder.h> | ||
31 | #include <asm/cpu.h> | 32 | #include <asm/cpu.h> |
33 | #include <asm/dsp.h> | ||
32 | #include <asm/fpu.h> | 34 | #include <asm/fpu.h> |
33 | #include <asm/mipsregs.h> | 35 | #include <asm/mipsregs.h> |
36 | #include <asm/mipsmtregs.h> | ||
34 | #include <asm/pgtable.h> | 37 | #include <asm/pgtable.h> |
35 | #include <asm/page.h> | 38 | #include <asm/page.h> |
36 | #include <asm/system.h> | 39 | #include <asm/system.h> |
37 | #include <asm/uaccess.h> | 40 | #include <asm/uaccess.h> |
38 | #include <asm/bootinfo.h> | 41 | #include <asm/bootinfo.h> |
42 | #include <asm/reg.h> | ||
39 | 43 | ||
40 | /* | 44 | /* |
41 | * Called by kernel/ptrace.c when detaching.. | 45 | * Called by kernel/ptrace.c when detaching.. |
@@ -47,6 +51,129 @@ void ptrace_disable(struct task_struct *child) | |||
47 | /* Nothing to do.. */ | 51 | /* Nothing to do.. */ |
48 | } | 52 | } |
49 | 53 | ||
54 | /* | ||
55 | * Read a general register set. We always use the 64-bit format, even | ||
56 | * for 32-bit kernels and for 32-bit processes on a 64-bit kernel. | ||
57 | * Registers are sign extended to fill the available space. | ||
58 | */ | ||
59 | int ptrace_getregs (struct task_struct *child, __s64 __user *data) | ||
60 | { | ||
61 | struct pt_regs *regs; | ||
62 | int i; | ||
63 | |||
64 | if (!access_ok(VERIFY_WRITE, data, 38 * 8)) | ||
65 | return -EIO; | ||
66 | |||
67 | regs = (struct pt_regs *) ((unsigned long) child->thread_info + | ||
68 | THREAD_SIZE - 32 - sizeof(struct pt_regs)); | ||
69 | |||
70 | for (i = 0; i < 32; i++) | ||
71 | __put_user (regs->regs[i], data + i); | ||
72 | __put_user (regs->lo, data + EF_LO - EF_R0); | ||
73 | __put_user (regs->hi, data + EF_HI - EF_R0); | ||
74 | __put_user (regs->cp0_epc, data + EF_CP0_EPC - EF_R0); | ||
75 | __put_user (regs->cp0_badvaddr, data + EF_CP0_BADVADDR - EF_R0); | ||
76 | __put_user (regs->cp0_status, data + EF_CP0_STATUS - EF_R0); | ||
77 | __put_user (regs->cp0_cause, data + EF_CP0_CAUSE - EF_R0); | ||
78 | |||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | /* | ||
83 | * Write a general register set. As for PTRACE_GETREGS, we always use | ||
84 | * the 64-bit format. On a 32-bit kernel only the lower order half | ||
85 | * (according to endianness) will be used. | ||
86 | */ | ||
87 | int ptrace_setregs (struct task_struct *child, __s64 __user *data) | ||
88 | { | ||
89 | struct pt_regs *regs; | ||
90 | int i; | ||
91 | |||
92 | if (!access_ok(VERIFY_READ, data, 38 * 8)) | ||
93 | return -EIO; | ||
94 | |||
95 | regs = (struct pt_regs *) ((unsigned long) child->thread_info + | ||
96 | THREAD_SIZE - 32 - sizeof(struct pt_regs)); | ||
97 | |||
98 | for (i = 0; i < 32; i++) | ||
99 | __get_user (regs->regs[i], data + i); | ||
100 | __get_user (regs->lo, data + EF_LO - EF_R0); | ||
101 | __get_user (regs->hi, data + EF_HI - EF_R0); | ||
102 | __get_user (regs->cp0_epc, data + EF_CP0_EPC - EF_R0); | ||
103 | |||
104 | /* badvaddr, status, and cause may not be written. */ | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | int ptrace_getfpregs (struct task_struct *child, __u32 __user *data) | ||
110 | { | ||
111 | int i; | ||
112 | |||
113 | if (!access_ok(VERIFY_WRITE, data, 33 * 8)) | ||
114 | return -EIO; | ||
115 | |||
116 | if (tsk_used_math(child)) { | ||
117 | fpureg_t *fregs = get_fpu_regs(child); | ||
118 | for (i = 0; i < 32; i++) | ||
119 | __put_user (fregs[i], i + (__u64 __user *) data); | ||
120 | } else { | ||
121 | for (i = 0; i < 32; i++) | ||
122 | __put_user ((__u64) -1, i + (__u64 __user *) data); | ||
123 | } | ||
124 | |||
125 | if (cpu_has_fpu) { | ||
126 | unsigned int flags, tmp; | ||
127 | |||
128 | __put_user (child->thread.fpu.hard.fcr31, data + 64); | ||
129 | |||
130 | preempt_disable(); | ||
131 | if (cpu_has_mipsmt) { | ||
132 | unsigned int vpflags = dvpe(); | ||
133 | flags = read_c0_status(); | ||
134 | __enable_fpu(); | ||
135 | __asm__ __volatile__("cfc1\t%0,$0" : "=r" (tmp)); | ||
136 | write_c0_status(flags); | ||
137 | evpe(vpflags); | ||
138 | } else { | ||
139 | flags = read_c0_status(); | ||
140 | __enable_fpu(); | ||
141 | __asm__ __volatile__("cfc1\t%0,$0" : "=r" (tmp)); | ||
142 | write_c0_status(flags); | ||
143 | } | ||
144 | preempt_enable(); | ||
145 | __put_user (tmp, data + 65); | ||
146 | } else { | ||
147 | __put_user (child->thread.fpu.soft.fcr31, data + 64); | ||
148 | __put_user ((__u32) 0, data + 65); | ||
149 | } | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | int ptrace_setfpregs (struct task_struct *child, __u32 __user *data) | ||
155 | { | ||
156 | fpureg_t *fregs; | ||
157 | int i; | ||
158 | |||
159 | if (!access_ok(VERIFY_READ, data, 33 * 8)) | ||
160 | return -EIO; | ||
161 | |||
162 | fregs = get_fpu_regs(child); | ||
163 | |||
164 | for (i = 0; i < 32; i++) | ||
165 | __get_user (fregs[i], i + (__u64 __user *) data); | ||
166 | |||
167 | if (cpu_has_fpu) | ||
168 | __get_user (child->thread.fpu.hard.fcr31, data + 64); | ||
169 | else | ||
170 | __get_user (child->thread.fpu.soft.fcr31, data + 64); | ||
171 | |||
172 | /* FIR may not be written. */ | ||
173 | |||
174 | return 0; | ||
175 | } | ||
176 | |||
50 | asmlinkage int sys_ptrace(long request, long pid, long addr, long data) | 177 | asmlinkage int sys_ptrace(long request, long pid, long addr, long data) |
51 | { | 178 | { |
52 | struct task_struct *child; | 179 | struct task_struct *child; |
@@ -103,7 +230,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) | |||
103 | ret = -EIO; | 230 | ret = -EIO; |
104 | if (copied != sizeof(tmp)) | 231 | if (copied != sizeof(tmp)) |
105 | break; | 232 | break; |
106 | ret = put_user(tmp,(unsigned long *) data); | 233 | ret = put_user(tmp,(unsigned long __user *) data); |
107 | break; | 234 | break; |
108 | } | 235 | } |
109 | 236 | ||
@@ -169,18 +296,53 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) | |||
169 | if (!cpu_has_fpu) | 296 | if (!cpu_has_fpu) |
170 | break; | 297 | break; |
171 | 298 | ||
172 | flags = read_c0_status(); | 299 | preempt_disable(); |
173 | __enable_fpu(); | 300 | if (cpu_has_mipsmt) { |
174 | __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp)); | 301 | unsigned int vpflags = dvpe(); |
175 | write_c0_status(flags); | 302 | flags = read_c0_status(); |
303 | __enable_fpu(); | ||
304 | __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp)); | ||
305 | write_c0_status(flags); | ||
306 | evpe(vpflags); | ||
307 | } else { | ||
308 | flags = read_c0_status(); | ||
309 | __enable_fpu(); | ||
310 | __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp)); | ||
311 | write_c0_status(flags); | ||
312 | } | ||
313 | preempt_enable(); | ||
314 | break; | ||
315 | } | ||
316 | case DSP_BASE ... DSP_BASE + 5: { | ||
317 | dspreg_t *dregs; | ||
318 | |||
319 | if (!cpu_has_dsp) { | ||
320 | tmp = 0; | ||
321 | ret = -EIO; | ||
322 | goto out_tsk; | ||
323 | } | ||
324 | if (child->thread.dsp.used_dsp) { | ||
325 | dregs = __get_dsp_regs(child); | ||
326 | tmp = (unsigned long) (dregs[addr - DSP_BASE]); | ||
327 | } else { | ||
328 | tmp = -1; /* DSP registers yet used */ | ||
329 | } | ||
176 | break; | 330 | break; |
177 | } | 331 | } |
332 | case DSP_CONTROL: | ||
333 | if (!cpu_has_dsp) { | ||
334 | tmp = 0; | ||
335 | ret = -EIO; | ||
336 | goto out_tsk; | ||
337 | } | ||
338 | tmp = child->thread.dsp.dspcontrol; | ||
339 | break; | ||
178 | default: | 340 | default: |
179 | tmp = 0; | 341 | tmp = 0; |
180 | ret = -EIO; | 342 | ret = -EIO; |
181 | goto out_tsk; | 343 | goto out_tsk; |
182 | } | 344 | } |
183 | ret = put_user(tmp, (unsigned long *) data); | 345 | ret = put_user(tmp, (unsigned long __user *) data); |
184 | break; | 346 | break; |
185 | } | 347 | } |
186 | 348 | ||
@@ -247,6 +409,25 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) | |||
247 | else | 409 | else |
248 | child->thread.fpu.soft.fcr31 = data; | 410 | child->thread.fpu.soft.fcr31 = data; |
249 | break; | 411 | break; |
412 | case DSP_BASE ... DSP_BASE + 5: { | ||
413 | dspreg_t *dregs; | ||
414 | |||
415 | if (!cpu_has_dsp) { | ||
416 | ret = -EIO; | ||
417 | break; | ||
418 | } | ||
419 | |||
420 | dregs = __get_dsp_regs(child); | ||
421 | dregs[addr - DSP_BASE] = data; | ||
422 | break; | ||
423 | } | ||
424 | case DSP_CONTROL: | ||
425 | if (!cpu_has_dsp) { | ||
426 | ret = -EIO; | ||
427 | break; | ||
428 | } | ||
429 | child->thread.dsp.dspcontrol = data; | ||
430 | break; | ||
250 | default: | 431 | default: |
251 | /* The rest are not allowed. */ | 432 | /* The rest are not allowed. */ |
252 | ret = -EIO; | 433 | ret = -EIO; |
@@ -255,6 +436,22 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) | |||
255 | break; | 436 | break; |
256 | } | 437 | } |
257 | 438 | ||
439 | case PTRACE_GETREGS: | ||
440 | ret = ptrace_getregs (child, (__u64 __user *) data); | ||
441 | break; | ||
442 | |||
443 | case PTRACE_SETREGS: | ||
444 | ret = ptrace_setregs (child, (__u64 __user *) data); | ||
445 | break; | ||
446 | |||
447 | case PTRACE_GETFPREGS: | ||
448 | ret = ptrace_getfpregs (child, (__u32 __user *) data); | ||
449 | break; | ||
450 | |||
451 | case PTRACE_SETFPREGS: | ||
452 | ret = ptrace_setfpregs (child, (__u32 __user *) data); | ||
453 | break; | ||
454 | |||
258 | case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ | 455 | case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ |
259 | case PTRACE_CONT: { /* restart after signal. */ | 456 | case PTRACE_CONT: { /* restart after signal. */ |
260 | ret = -EIO; | 457 | ret = -EIO; |
@@ -289,6 +486,11 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) | |||
289 | ret = ptrace_detach(child, data); | 486 | ret = ptrace_detach(child, data); |
290 | break; | 487 | break; |
291 | 488 | ||
489 | case PTRACE_GET_THREAD_AREA: | ||
490 | ret = put_user(child->thread_info->tp_value, | ||
491 | (unsigned long __user *) data); | ||
492 | break; | ||
493 | |||
292 | default: | 494 | default: |
293 | ret = ptrace_request(child, request, addr, data); | 495 | ret = ptrace_request(child, request, addr, data); |
294 | break; | 496 | break; |
@@ -303,21 +505,14 @@ out: | |||
303 | 505 | ||
304 | static inline int audit_arch(void) | 506 | static inline int audit_arch(void) |
305 | { | 507 | { |
306 | #ifdef CONFIG_CPU_LITTLE_ENDIAN | 508 | int arch = EM_MIPS; |
307 | #ifdef CONFIG_64BIT | ||
308 | if (!(current->thread.mflags & MF_32BIT_REGS)) | ||
309 | return AUDIT_ARCH_MIPSEL64; | ||
310 | #endif /* MIPS64 */ | ||
311 | return AUDIT_ARCH_MIPSEL; | ||
312 | |||
313 | #else /* big endian... */ | ||
314 | #ifdef CONFIG_64BIT | 509 | #ifdef CONFIG_64BIT |
315 | if (!(current->thread.mflags & MF_32BIT_REGS)) | 510 | arch |= __AUDIT_ARCH_64BIT; |
316 | return AUDIT_ARCH_MIPS64; | 511 | #endif |
317 | #endif /* MIPS64 */ | 512 | #if defined(__LITTLE_ENDIAN) |
318 | return AUDIT_ARCH_MIPS; | 513 | arch |= __AUDIT_ARCH_LE; |
319 | 514 | #endif | |
320 | #endif /* endian */ | 515 | return arch; |
321 | } | 516 | } |
322 | 517 | ||
323 | /* | 518 | /* |
@@ -327,12 +522,13 @@ static inline int audit_arch(void) | |||
327 | asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit) | 522 | asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit) |
328 | { | 523 | { |
329 | if (unlikely(current->audit_context) && entryexit) | 524 | if (unlikely(current->audit_context) && entryexit) |
330 | audit_syscall_exit(current, AUDITSC_RESULT(regs->regs[2]), regs->regs[2]); | 525 | audit_syscall_exit(current, AUDITSC_RESULT(regs->regs[2]), |
526 | regs->regs[2]); | ||
331 | 527 | ||
332 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | ||
333 | goto out; | ||
334 | if (!(current->ptrace & PT_PTRACED)) | 528 | if (!(current->ptrace & PT_PTRACED)) |
335 | goto out; | 529 | goto out; |
530 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | ||
531 | goto out; | ||
336 | 532 | ||
337 | /* The 0x80 provides a way for the tracing parent to distinguish | 533 | /* The 0x80 provides a way for the tracing parent to distinguish |
338 | between a syscall stop and SIGTRAP delivery */ | 534 | between a syscall stop and SIGTRAP delivery */ |