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 */ |
