Merge branch 'linus' into cont_syslog
[safe/jmp/linux-2.6] / arch / mips / kernel / ptrace.c
index 362d172..c51b95f 100644 (file)
 #include <linux/mm.h>
 #include <linux/errno.h>
 #include <linux/ptrace.h>
-#include <linux/audit.h>
 #include <linux/smp.h>
-#include <linux/smp_lock.h>
 #include <linux/user.h>
 #include <linux/security.h>
-#include <linux/signal.h>
+#include <linux/audit.h>
+#include <linux/seccomp.h>
 
 #include <asm/byteorder.h>
 #include <asm/cpu.h>
@@ -47,7 +46,8 @@
  */
 void ptrace_disable(struct task_struct *child)
 {
-       /* Nothing to do.. */
+       /* Don't load the watchpoint registers for the ex-child. */
+       clear_tsk_thread_flag(child, TIF_LOAD_WATCH);
 }
 
 /*
@@ -55,7 +55,7 @@ void ptrace_disable(struct task_struct *child)
  * for 32-bit kernels and for 32-bit processes on a 64-bit kernel.
  * Registers are sign extended to fill the available space.
  */
-int ptrace_getregs (struct task_struct *child, __s64 __user *data)
+int ptrace_getregs(struct task_struct *child, __s64 __user *data)
 {
        struct pt_regs *regs;
        int i;
@@ -66,13 +66,13 @@ int ptrace_getregs (struct task_struct *child, __s64 __user *data)
        regs = task_pt_regs(child);
 
        for (i = 0; i < 32; i++)
-               __put_user (regs->regs[i], data + i);
-       __put_user (regs->lo, data + EF_LO - EF_R0);
-       __put_user (regs->hi, data + EF_HI - EF_R0);
-       __put_user (regs->cp0_epc, data + EF_CP0_EPC - EF_R0);
-       __put_user (regs->cp0_badvaddr, data + EF_CP0_BADVADDR - EF_R0);
-       __put_user (regs->cp0_status, data + EF_CP0_STATUS - EF_R0);
-       __put_user (regs->cp0_cause, data + EF_CP0_CAUSE - EF_R0);
+               __put_user((long)regs->regs[i], data + i);
+       __put_user((long)regs->lo, data + EF_LO - EF_R0);
+       __put_user((long)regs->hi, data + EF_HI - EF_R0);
+       __put_user((long)regs->cp0_epc, data + EF_CP0_EPC - EF_R0);
+       __put_user((long)regs->cp0_badvaddr, data + EF_CP0_BADVADDR - EF_R0);
+       __put_user((long)regs->cp0_status, data + EF_CP0_STATUS - EF_R0);
+       __put_user((long)regs->cp0_cause, data + EF_CP0_CAUSE - EF_R0);
 
        return 0;
 }
@@ -82,7 +82,7 @@ int ptrace_getregs (struct task_struct *child, __s64 __user *data)
  * the 64-bit format.  On a 32-bit kernel only the lower order half
  * (according to endianness) will be used.
  */
-int ptrace_setregs (struct task_struct *child, __s64 __user *data)
+int ptrace_setregs(struct task_struct *child, __s64 __user *data)
 {
        struct pt_regs *regs;
        int i;
@@ -93,19 +93,20 @@ int ptrace_setregs (struct task_struct *child, __s64 __user *data)
        regs = task_pt_regs(child);
 
        for (i = 0; i < 32; i++)
-               __get_user (regs->regs[i], data + i);
-       __get_user (regs->lo, data + EF_LO - EF_R0);
-       __get_user (regs->hi, data + EF_HI - EF_R0);
-       __get_user (regs->cp0_epc, data + EF_CP0_EPC - EF_R0);
+               __get_user(regs->regs[i], data + i);
+       __get_user(regs->lo, data + EF_LO - EF_R0);
+       __get_user(regs->hi, data + EF_HI - EF_R0);
+       __get_user(regs->cp0_epc, data + EF_CP0_EPC - EF_R0);
 
        /* badvaddr, status, and cause may not be written.  */
 
        return 0;
 }
 
-int ptrace_getfpregs (struct task_struct *child, __u32 __user *data)
+int ptrace_getfpregs(struct task_struct *child, __u32 __user *data)
 {
        int i;
+       unsigned int tmp;
 
        if (!access_ok(VERIFY_WRITE, data, 33 * 8))
                return -EIO;
@@ -113,18 +114,18 @@ int ptrace_getfpregs (struct task_struct *child, __u32 __user *data)
        if (tsk_used_math(child)) {
                fpureg_t *fregs = get_fpu_regs(child);
                for (i = 0; i < 32; i++)
-                       __put_user (fregs[i], i + (__u64 __user *) data);
+                       __put_user(fregs[i], i + (__u64 __user *) data);
        } else {
                for (i = 0; i < 32; i++)
-                       __put_user ((__u64) -1, i + (__u64 __user *) data);
+                       __put_user((__u64) -1, i + (__u64 __user *) data);
        }
 
-       __put_user (child->thread.fpu.fcr31, data + 64);
+       __put_user(child->thread.fpu.fcr31, data + 64);
 
+       preempt_disable();
        if (cpu_has_fpu) {
-               unsigned int flags, tmp;
+               unsigned int flags;
 
-               preempt_disable();
                if (cpu_has_mipsmt) {
                        unsigned int vpflags = dvpe();
                        flags = read_c0_status();
@@ -138,16 +139,16 @@ int ptrace_getfpregs (struct task_struct *child, __u32 __user *data)
                        __asm__ __volatile__("cfc1\t%0,$0" : "=r" (tmp));
                        write_c0_status(flags);
                }
-               preempt_enable();
-               __put_user (tmp, data + 65);
        } else {
-               __put_user ((__u32) 0, data + 65);
+               tmp = 0;
        }
+       preempt_enable();
+       __put_user(tmp, data + 65);
 
        return 0;
 }
 
-int ptrace_setfpregs (struct task_struct *child, __u32 __user *data)
+int ptrace_setfpregs(struct task_struct *child, __u32 __user *data)
 {
        fpureg_t *fregs;
        int i;
@@ -158,15 +159,102 @@ int ptrace_setfpregs (struct task_struct *child, __u32 __user *data)
        fregs = get_fpu_regs(child);
 
        for (i = 0; i < 32; i++)
-               __get_user (fregs[i], i + (__u64 __user *) data);
+               __get_user(fregs[i], i + (__u64 __user *) data);
 
-       __get_user (child->thread.fpu.fcr31, data + 64);
+       __get_user(child->thread.fpu.fcr31, data + 64);
 
        /* FIR may not be written.  */
 
        return 0;
 }
 
+int ptrace_get_watch_regs(struct task_struct *child,
+                         struct pt_watch_regs __user *addr)
+{
+       enum pt_watch_style style;
+       int i;
+
+       if (!cpu_has_watch || current_cpu_data.watch_reg_use_cnt == 0)
+               return -EIO;
+       if (!access_ok(VERIFY_WRITE, addr, sizeof(struct pt_watch_regs)))
+               return -EIO;
+
+#ifdef CONFIG_32BIT
+       style = pt_watch_style_mips32;
+#define WATCH_STYLE mips32
+#else
+       style = pt_watch_style_mips64;
+#define WATCH_STYLE mips64
+#endif
+
+       __put_user(style, &addr->style);
+       __put_user(current_cpu_data.watch_reg_use_cnt,
+                  &addr->WATCH_STYLE.num_valid);
+       for (i = 0; i < current_cpu_data.watch_reg_use_cnt; i++) {
+               __put_user(child->thread.watch.mips3264.watchlo[i],
+                          &addr->WATCH_STYLE.watchlo[i]);
+               __put_user(child->thread.watch.mips3264.watchhi[i] & 0xfff,
+                          &addr->WATCH_STYLE.watchhi[i]);
+               __put_user(current_cpu_data.watch_reg_masks[i],
+                          &addr->WATCH_STYLE.watch_masks[i]);
+       }
+       for (; i < 8; i++) {
+               __put_user(0, &addr->WATCH_STYLE.watchlo[i]);
+               __put_user(0, &addr->WATCH_STYLE.watchhi[i]);
+               __put_user(0, &addr->WATCH_STYLE.watch_masks[i]);
+       }
+
+       return 0;
+}
+
+int ptrace_set_watch_regs(struct task_struct *child,
+                         struct pt_watch_regs __user *addr)
+{
+       int i;
+       int watch_active = 0;
+       unsigned long lt[NUM_WATCH_REGS];
+       u16 ht[NUM_WATCH_REGS];
+
+       if (!cpu_has_watch || current_cpu_data.watch_reg_use_cnt == 0)
+               return -EIO;
+       if (!access_ok(VERIFY_READ, addr, sizeof(struct pt_watch_regs)))
+               return -EIO;
+       /* Check the values. */
+       for (i = 0; i < current_cpu_data.watch_reg_use_cnt; i++) {
+               __get_user(lt[i], &addr->WATCH_STYLE.watchlo[i]);
+#ifdef CONFIG_32BIT
+               if (lt[i] & __UA_LIMIT)
+                       return -EINVAL;
+#else
+               if (test_tsk_thread_flag(child, TIF_32BIT_ADDR)) {
+                       if (lt[i] & 0xffffffff80000000UL)
+                               return -EINVAL;
+               } else {
+                       if (lt[i] & __UA_LIMIT)
+                               return -EINVAL;
+               }
+#endif
+               __get_user(ht[i], &addr->WATCH_STYLE.watchhi[i]);
+               if (ht[i] & ~0xff8)
+                       return -EINVAL;
+       }
+       /* Install them. */
+       for (i = 0; i < current_cpu_data.watch_reg_use_cnt; i++) {
+               if (lt[i] & 7)
+                       watch_active = 1;
+               child->thread.watch.mips3264.watchlo[i] = lt[i];
+               /* Set the G bit. */
+               child->thread.watch.mips3264.watchhi[i] = ht[i];
+       }
+
+       if (watch_active)
+               set_tsk_thread_flag(child, TIF_LOAD_WATCH);
+       else
+               clear_tsk_thread_flag(child, TIF_LOAD_WATCH);
+
+       return 0;
+}
+
 long arch_ptrace(struct task_struct *child, long request, long addr, long data)
 {
        int ret;
@@ -174,17 +262,9 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
        switch (request) {
        /* when I and D space are separate, these will need to be fixed. */
        case PTRACE_PEEKTEXT: /* read word at location addr. */
-       case PTRACE_PEEKDATA: {
-               unsigned long tmp;
-               int copied;
-
-               copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
-               ret = -EIO;
-               if (copied != sizeof(tmp))
-                       break;
-               ret = put_user(tmp,(unsigned long __user *) data);
+       case PTRACE_PEEKDATA:
+               ret = generic_ptrace_peekdata(child, addr, data);
                break;
-       }
 
        /* Read the word at location addr in the USER area. */
        case PTRACE_PEEKUSR: {
@@ -235,26 +315,32 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
                case MMLO:
                        tmp = regs->lo;
                        break;
+#ifdef CONFIG_CPU_HAS_SMARTMIPS
+               case ACX:
+                       tmp = regs->acx;
+                       break;
+#endif
                case FPC_CSR:
                        tmp = child->thread.fpu.fcr31;
                        break;
                case FPC_EIR: { /* implementation / version register */
                        unsigned int flags;
 #ifdef CONFIG_MIPS_MT_SMTC
-                       unsigned int irqflags;
+                       unsigned long irqflags;
                        unsigned int mtflags;
 #endif /* CONFIG_MIPS_MT_SMTC */
 
-                       if (!cpu_has_fpu)
+                       preempt_disable();
+                       if (!cpu_has_fpu) {
+                               preempt_enable();
                                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();
@@ -307,11 +393,7 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
        /* when I and D space are separate, this will have to be fixed. */
        case PTRACE_POKETEXT: /* write the word at location addr. */
        case PTRACE_POKEDATA:
-               ret = 0;
-               if (access_process_vm(child, addr, &data, sizeof(data), 1)
-                   == sizeof(data))
-                       break;
-               ret = -EIO;
+               ret = generic_ptrace_pokedata(child, addr, data);
                break;
 
        case PTRACE_POKEUSR: {
@@ -360,6 +442,11 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
                case MMLO:
                        regs->lo = data;
                        break;
+#ifdef CONFIG_CPU_HAS_SMARTMIPS
+               case ACX:
+                       regs->acx = data;
+                       break;
+#endif
                case FPC_CSR:
                        child->thread.fpu.fcr31 = data;
                        break;
@@ -391,58 +478,34 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
                }
 
        case PTRACE_GETREGS:
-               ret = ptrace_getregs (child, (__u64 __user *) data);
+               ret = ptrace_getregs(child, (__s64 __user *) data);
                break;
 
        case PTRACE_SETREGS:
-               ret = ptrace_setregs (child, (__u64 __user *) data);
+               ret = ptrace_setregs(child, (__s64 __user *) data);
                break;
 
        case PTRACE_GETFPREGS:
-               ret = ptrace_getfpregs (child, (__u32 __user *) data);
+               ret = ptrace_getfpregs(child, (__u32 __user *) data);
                break;
 
        case PTRACE_SETFPREGS:
-               ret = ptrace_setfpregs (child, (__u32 __user *) data);
+               ret = ptrace_setfpregs(child, (__u32 __user *) data);
                break;
 
-       case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
-       case PTRACE_CONT: { /* restart after signal. */
-               ret = -EIO;
-               if (!valid_signal(data))
-                       break;
-               if (request == PTRACE_SYSCALL) {
-                       set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
-               }
-               else {
-                       clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
-               }
-               child->exit_code = data;
-               wake_up_process(child);
-               ret = 0;
-               break;
-       }
-
-       /*
-        * make the child exit.  Best I can do is send it a sigkill.
-        * perhaps it should be put in the status that it wants to
-        * exit.
-        */
-       case PTRACE_KILL:
-               ret = 0;
-               if (child->exit_state == EXIT_ZOMBIE)   /* already dead */
-                       break;
-               child->exit_code = SIGKILL;
-               wake_up_process(child);
+       case PTRACE_GET_THREAD_AREA:
+               ret = put_user(task_thread_info(child)->tp_value,
+                               (unsigned long __user *) data);
                break;
 
-       case PTRACE_DETACH: /* detach a process that was attached. */
-               ret = ptrace_detach(child, data);
+       case PTRACE_GET_WATCH_REGS:
+               ret = ptrace_get_watch_regs(child,
+                                       (struct pt_watch_regs __user *) addr);
                break;
 
-       case PTRACE_GET_THREAD_AREA:
-               ret = put_user(task_thread_info(child)->tp_value,
-                               (unsigned long __user *) data);
+       case PTRACE_SET_WATCH_REGS:
+               ret = ptrace_set_watch_regs(child,
+                                       (struct pt_watch_regs __user *) addr);
                break;
 
        default:
@@ -471,12 +534,17 @@ static inline int audit_arch(void)
  */
 asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
 {
+       /* do the secure computing check first */
+       if (!entryexit)
+               secure_computing(regs->regs[0]);
+
        if (unlikely(current->audit_context) && entryexit)
                audit_syscall_exit(AUDITSC_RESULT(regs->regs[2]),
                                   regs->regs[2]);
 
        if (!(current->ptrace & PT_PTRACED))
                goto out;
+
        if (!test_thread_flag(TIF_SYSCALL_TRACE))
                goto out;
 
@@ -494,9 +562,10 @@ asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
                send_sig(current->exit_code, current, 1);
                current->exit_code = 0;
        }
- out:
+
+out:
        if (unlikely(current->audit_context) && !entryexit)
-               audit_syscall_entry(audit_arch(), regs->regs[2],
+               audit_syscall_entry(audit_arch(), regs->regs[0],
                                    regs->regs[4], regs->regs[5],
                                    regs->regs[6], regs->regs[7]);
 }