Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / arch / mips / kernel / ptrace32.c
index eee2079..8704dc0 100644 (file)
 #include <linux/smp_lock.h>
 #include <linux/user.h>
 #include <linux/security.h>
-#include <linux/signal.h>
 
 #include <asm/cpu.h>
+#include <asm/dsp.h>
 #include <asm/fpu.h>
 #include <asm/mipsregs.h>
+#include <asm/mipsmtregs.h>
 #include <asm/pgtable.h>
 #include <asm/page.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/bootinfo.h>
 
+int ptrace_getregs (struct task_struct *child, __s64 __user *data);
+int ptrace_setregs (struct task_struct *child, __s64 __user *data);
+
+int ptrace_getfpregs (struct task_struct *child, __u32 __user *data);
+int ptrace_setfpregs (struct task_struct *child, __u32 __user *data);
+
 /*
  * Tracing a 32-bit process with a 64-bit strace and vice versa will not
  * work.  I don't know how to fix this.
@@ -50,30 +57,16 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
               (unsigned long) data);
 #endif
        lock_kernel();
-       ret = -EPERM;
        if (request == PTRACE_TRACEME) {
-               /* are we already being traced? */
-               if (current->ptrace & PT_PTRACED)
-                       goto out;
-               if ((ret = security_ptrace(current->parent, current)))
-                       goto out;
-               /* set the ptrace bit in the process flags. */
-               current->ptrace |= PT_PTRACED;
-               ret = 0;
+               ret = ptrace_traceme();
                goto out;
        }
-       ret = -ESRCH;
-       read_lock(&tasklist_lock);
-       child = find_task_by_pid(pid);
-       if (child)
-               get_task_struct(child);
-       read_unlock(&tasklist_lock);
-       if (!child)
-               goto out;
 
-       ret = -EPERM;
-       if (pid == 1)           /* you may not mess with init */
-               goto out_tsk;
+       child = ptrace_get_task_struct(pid);
+       if (IS_ERR(child)) {
+               ret = PTR_ERR(child);
+               goto out;
+       }
 
        if (request == PTRACE_ATTACH) {
                ret = ptrace_attach(child);
@@ -95,7 +88,36 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
                ret = -EIO;
                if (copied != sizeof(tmp))
                        break;
-               ret = put_user(tmp, (unsigned int *) (unsigned long) data);
+               ret = put_user(tmp, (unsigned int __user *) (unsigned long) data);
+               break;
+       }
+
+       /*
+        * Read 4 bytes of the other process' storage
+        *  data is a pointer specifying where the user wants the
+        *      4 bytes copied into
+        *  addr is a pointer in the user's storage that contains an 8 byte
+        *      address in the other process of the 4 bytes that is to be read
+        * (this is run in a 32-bit process looking at a 64-bit process)
+        * when I and D space are separate, these will need to be fixed.
+        */
+       case PTRACE_PEEKTEXT_3264:
+       case PTRACE_PEEKDATA_3264: {
+               u32 tmp;
+               int copied;
+               u32 __user * addrOthers;
+
+               ret = -EIO;
+
+               /* Get the addr in the other process that we want to read */
+               if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
+                       break;
+
+               copied = access_process_vm(child, (u64)addrOthers, &tmp,
+                               sizeof(tmp), 0);
+               if (copied != sizeof(tmp))
+                       break;
+               ret = put_user(tmp, (u32 __user *) (unsigned long) data);
                break;
        }
 
@@ -104,8 +126,7 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
                struct pt_regs *regs;
                unsigned int tmp;
 
-               regs = (struct pt_regs *) ((unsigned long) child->thread_info +
-                      THREAD_SIZE - 32 - sizeof(struct pt_regs));
+               regs = task_pt_regs(child);
                ret = 0;  /* Default return value. */
 
                switch (addr) {
@@ -152,22 +173,69 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
                        break;
                case FPC_EIR: { /* implementation / version register */
                        unsigned int flags;
+#ifdef CONFIG_MIPS_MT_SMTC
+                       unsigned int irqflags;
+                       unsigned int mtflags;
+#endif /* CONFIG_MIPS_MT_SMTC */
 
-                       if (!cpu_has_fpu)
+                       if (!cpu_has_fpu) {
+                               tmp = 0;
                                break;
+                       }
+
+#ifdef CONFIG_MIPS_MT_SMTC
+                       /* Read-modify-write of Status must be atomic */
+                       local_irq_save(irqflags);
+                       mtflags = dmt();
+#endif /* CONFIG_MIPS_MT_SMTC */
+
+                       preempt_disable();
+                       if (cpu_has_mipsmt) {
+                               unsigned int vpflags = dvpe();
+                               flags = read_c0_status();
+                               __enable_fpu();
+                               __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
+                               write_c0_status(flags);
+                               evpe(vpflags);
+                       } else {
+                               flags = read_c0_status();
+                               __enable_fpu();
+                               __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
+                               write_c0_status(flags);
+                       }
+#ifdef CONFIG_MIPS_MT_SMTC
+                       emt(mtflags);
+                       local_irq_restore(irqflags);
+#endif /* CONFIG_MIPS_MT_SMTC */
+                       preempt_enable();
+                       break;
+               }
+               case DSP_BASE ... DSP_BASE + 5: {
+                       dspreg_t *dregs;
 
-                       flags = read_c0_status();
-                       __enable_fpu();
-                       __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
-                       write_c0_status(flags);
+                       if (!cpu_has_dsp) {
+                               tmp = 0;
+                               ret = -EIO;
+                               goto out_tsk;
+                       }
+                       dregs = __get_dsp_regs(child);
+                       tmp = (unsigned long) (dregs[addr - DSP_BASE]);
                        break;
                }
+               case DSP_CONTROL:
+                       if (!cpu_has_dsp) {
+                               tmp = 0;
+                               ret = -EIO;
+                               goto out_tsk;
+                       }
+                       tmp = child->thread.dsp.dspcontrol;
+                       break;
                default:
                        tmp = 0;
                        ret = -EIO;
                        goto out_tsk;
                }
-               ret = put_user(tmp, (unsigned *) (unsigned long) data);
+               ret = put_user(tmp, (unsigned __user *) (unsigned long) data);
                break;
        }
 
@@ -181,11 +249,35 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
                ret = -EIO;
                break;
 
+       /*
+        * Write 4 bytes into the other process' storage
+        *  data is the 4 bytes that the user wants written
+        *  addr is a pointer in the user's storage that contains an
+        *      8 byte address in the other process where the 4 bytes
+        *      that is to be written
+        * (this is run in a 32-bit process looking at a 64-bit process)
+        * when I and D space are separate, these will need to be fixed.
+        */
+       case PTRACE_POKETEXT_3264:
+       case PTRACE_POKEDATA_3264: {
+               u32 __user * addrOthers;
+
+               /* Get the addr in the other process that we want to write into */
+               ret = -EIO;
+               if (get_user(addrOthers, (u32 __user * __user *) (unsigned long) addr) != 0)
+                       break;
+               ret = 0;
+               if (access_process_vm(child, (u64)addrOthers, &data,
+                                       sizeof(data), 1) == sizeof(data))
+                       break;
+               ret = -EIO;
+               break;
+       }
+
        case PTRACE_POKEUSR: {
                struct pt_regs *regs;
                ret = 0;
-               regs = (struct pt_regs *) ((unsigned long) child->thread_info +
-                      THREAD_SIZE - 32 - sizeof(struct pt_regs));
+               regs = task_pt_regs(child);
 
                switch (addr) {
                case 0 ... 31:
@@ -231,6 +323,25 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
                        else
                                child->thread.fpu.soft.fcr31 = data;
                        break;
+               case DSP_BASE ... DSP_BASE + 5: {
+                       dspreg_t *dregs;
+
+                       if (!cpu_has_dsp) {
+                               ret = -EIO;
+                               break;
+                       }
+
+                       dregs = __get_dsp_regs(child);
+                       dregs[addr - DSP_BASE] = data;
+                       break;
+               }
+               case DSP_CONTROL:
+                       if (!cpu_has_dsp) {
+                               ret = -EIO;
+                               break;
+                       }
+                       child->thread.dsp.dspcontrol = data;
+                       break;
                default:
                        /* The rest are not allowed. */
                        ret = -EIO;
@@ -239,6 +350,22 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
                break;
                }
 
+       case PTRACE_GETREGS:
+               ret = ptrace_getregs (child, (__u64 __user *) (__u64) data);
+               break;
+
+       case PTRACE_SETREGS:
+               ret = ptrace_setregs (child, (__u64 __user *) (__u64) data);
+               break;
+
+       case PTRACE_GETFPREGS:
+               ret = ptrace_getfpregs (child, (__u32 __user *) (__u64) data);
+               break;
+
+       case PTRACE_SETFPREGS:
+               ret = ptrace_setfpregs (child, (__u32 __user *) (__u64) data);
+               break;
+
        case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
        case PTRACE_CONT: { /* restart after signal. */
                ret = -EIO;
@@ -269,10 +396,25 @@ asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
                wake_up_process(child);
                break;
 
+       case PTRACE_GET_THREAD_AREA:
+               ret = put_user(task_thread_info(child)->tp_value,
+                               (unsigned int __user *) (unsigned long) data);
+               break;
+
        case PTRACE_DETACH: /* detach a process that was attached. */
                ret = ptrace_detach(child, data);
                break;
 
+       case PTRACE_GETEVENTMSG:
+               ret = put_user(child->ptrace_message,
+                              (unsigned int __user *) (unsigned long) data);
+               break;
+
+       case PTRACE_GET_THREAD_AREA_3264:
+               ret = put_user(task_thread_info(child)->tp_value,
+                               (unsigned long __user *) (unsigned long) data);
+               break;
+
        default:
                ret = ptrace_request(child, request, addr, data);
                break;