Merge Linus' tree.
[pandora-kernel.git] / arch / s390 / kernel / ptrace.c
index 2688936..cc02232 100644 (file)
@@ -40,8 +40,9 @@
 #include <asm/pgalloc.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/unistd.h>
 
-#ifdef CONFIG_S390_SUPPORT
+#ifdef CONFIG_COMPAT
 #include "compat_ptrace.h"
 #endif
 
@@ -58,7 +59,7 @@ FixPerRegisters(struct task_struct *task)
        
        if (per_info->single_step) {
                per_info->control_regs.bits.starting_addr = 0;
-#ifdef CONFIG_S390_SUPPORT
+#ifdef CONFIG_COMPAT
                if (test_thread_flag(TIF_31BIT))
                        per_info->control_regs.bits.ending_addr = 0x7fffffffUL;
                else
@@ -111,7 +112,7 @@ ptrace_disable(struct task_struct *child)
        clear_single_step(child);
 }
 
-#ifndef CONFIG_ARCH_S390X
+#ifndef CONFIG_64BIT
 # define __ADDR_MASK 3
 #else
 # define __ADDR_MASK 7
@@ -130,13 +131,19 @@ static int
 peek_user(struct task_struct *child, addr_t addr, addr_t data)
 {
        struct user *dummy = NULL;
-       addr_t offset, tmp;
+       addr_t offset, tmp, mask;
 
        /*
         * Stupid gdb peeks/pokes the access registers in 64 bit with
         * an alignment of 4. Programmers from hell...
         */
-       if ((addr & 3) || addr > sizeof(struct user) - __ADDR_MASK)
+       mask = __ADDR_MASK;
+#ifdef CONFIG_64BIT
+       if (addr >= (addr_t) &dummy->regs.acrs &&
+           addr < (addr_t) &dummy->regs.orig_gpr2)
+               mask = 3;
+#endif
+       if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
                return -EIO;
 
        if (addr < (addr_t) &dummy->regs.acrs) {
@@ -153,6 +160,16 @@ peek_user(struct task_struct *child, addr_t addr, addr_t data)
                 * access registers are stored in the thread structure
                 */
                offset = addr - (addr_t) &dummy->regs.acrs;
+#ifdef CONFIG_64BIT
+               /*
+                * Very special case: old & broken 64 bit gdb reading
+                * from acrs[15]. Result is a 64 bit value. Read the
+                * 32 bit acrs[15] value and shift it by 32. Sick...
+                */
+               if (addr == (addr_t) &dummy->regs.acrs[15])
+                       tmp = ((unsigned long) child->thread.acrs[15]) << 32;
+               else
+#endif
                tmp = *(addr_t *)((addr_t) &child->thread.acrs + offset);
 
        } else if (addr == (addr_t) &dummy->regs.orig_gpr2) {
@@ -167,6 +184,9 @@ peek_user(struct task_struct *child, addr_t addr, addr_t data)
                 */
                offset = addr - (addr_t) &dummy->regs.fp_regs;
                tmp = *(addr_t *)((addr_t) &child->thread.fp_regs + offset);
+               if (addr == (addr_t) &dummy->regs.fp_regs.fpc)
+                       tmp &= (unsigned long) FPC_VALID_MASK
+                               << (BITS_PER_LONG - 32);
 
        } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) {
                /*
@@ -191,13 +211,19 @@ static int
 poke_user(struct task_struct *child, addr_t addr, addr_t data)
 {
        struct user *dummy = NULL;
-       addr_t offset;
+       addr_t offset, mask;
 
        /*
         * Stupid gdb peeks/pokes the access registers in 64 bit with
         * an alignment of 4. Programmers from hell indeed...
         */
-       if ((addr & 3) || addr > sizeof(struct user) - __ADDR_MASK)
+       mask = __ADDR_MASK;
+#ifdef CONFIG_64BIT
+       if (addr >= (addr_t) &dummy->regs.acrs &&
+           addr < (addr_t) &dummy->regs.orig_gpr2)
+               mask = 3;
+#endif
+       if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
                return -EIO;
 
        if (addr < (addr_t) &dummy->regs.acrs) {
@@ -205,13 +231,13 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data)
                 * psw and gprs are stored on the stack
                 */
                if (addr == (addr_t) &dummy->regs.psw.mask &&
-#ifdef CONFIG_S390_SUPPORT
+#ifdef CONFIG_COMPAT
                    data != PSW_MASK_MERGE(PSW_USER32_BITS, data) &&
 #endif
                    data != PSW_MASK_MERGE(PSW_USER_BITS, data))
                        /* Invalid psw mask. */
                        return -EINVAL;
-#ifndef CONFIG_ARCH_S390X
+#ifndef CONFIG_64BIT
                if (addr == (addr_t) &dummy->regs.psw.addr)
                        /* I'd like to reject addresses without the
                           high order bit but older gdb's rely on it */
@@ -224,6 +250,17 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data)
                 * access registers are stored in the thread structure
                 */
                offset = addr - (addr_t) &dummy->regs.acrs;
+#ifdef CONFIG_64BIT
+               /*
+                * Very special case: old & broken 64 bit gdb writing
+                * to acrs[15] with a 64 bit value. Ignore the lower
+                * half of the value and write the upper 32 bit to
+                * acrs[15]. Sick...
+                */
+               if (addr == (addr_t) &dummy->regs.acrs[15])
+                       child->thread.acrs[15] = (unsigned int) (data >> 32);
+               else
+#endif
                *(addr_t *)((addr_t) &child->thread.acrs + offset) = data;
 
        } else if (addr == (addr_t) &dummy->regs.orig_gpr2) {
@@ -237,7 +274,8 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data)
                 * floating point regs. are stored in the thread structure
                 */
                if (addr == (addr_t) &dummy->regs.fp_regs.fpc &&
-                   (data & ~FPC_VALID_MASK) != 0)
+                   (data & ~((unsigned long) FPC_VALID_MASK
+                             << (BITS_PER_LONG - 32))) != 0)
                        return -EINVAL;
                offset = addr - (addr_t) &dummy->regs.fp_regs;
                *(addr_t *)((addr_t) &child->thread.fp_regs + offset) = data;
@@ -319,7 +357,7 @@ do_ptrace_normal(struct task_struct *child, long request, long addr, long data)
        return ptrace_request(child, request, addr, data);
 }
 
-#ifdef CONFIG_S390_SUPPORT
+#ifdef CONFIG_COMPAT
 /*
  * Now the fun part starts... a 31 bit program running in the
  * 31 bit emulation tracing another program. PTRACE_PEEKTEXT,
@@ -591,7 +629,7 @@ do_ptrace(struct task_struct *child, long request, long addr, long data)
                        return peek_user(child, addr, data);
                if (request == PTRACE_POKEUSR && addr == PT_IEEE_IP)
                        return poke_user(child, addr, data);
-#ifdef CONFIG_S390_SUPPORT
+#ifdef CONFIG_COMPAT
                if (request == PTRACE_PEEKUSR &&
                    addr == PT32_IEEE_IP && test_thread_flag(TIF_31BIT))
                        return peek_user_emu31(child, addr, data);
@@ -657,7 +695,7 @@ do_ptrace(struct task_struct *child, long request, long addr, long data)
 
        /* Do requests that differ for 31/64 bit */
        default:
-#ifdef CONFIG_S390_SUPPORT
+#ifdef CONFIG_COMPAT
                if (test_thread_flag(TIF_31BIT))
                        return do_ptrace_emu31(child, request, addr, data);
 #endif
@@ -674,35 +712,18 @@ sys_ptrace(long request, long pid, long addr, long data)
        int ret;
 
        lock_kernel();
-
        if (request == PTRACE_TRACEME) {
-               /* are we already being traced? */
-               ret = -EPERM;
-               if (current->ptrace & PT_PTRACED)
-                       goto out;
-               ret = security_ptrace(current->parent, current);
-               if (ret)
-                       goto out;
-               /* set the ptrace bit in the process flags. */
-               current->ptrace |= PT_PTRACED;
-               goto out;
+                ret = ptrace_traceme();
+                goto out;
        }
 
-       ret = -EPERM;
-       if (pid == 1)           /* you may not mess with init */
-               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)
+       child = ptrace_get_task_struct(pid);
+       if (IS_ERR(child)) {
+               ret = PTR_ERR(child);
                goto out;
+       }
 
        ret = do_ptrace(child, request, addr, data);
-
        put_task_struct(child);
 out:
        unlock_kernel();
@@ -722,6 +743,13 @@ syscall_trace(struct pt_regs *regs, int entryexit)
        ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
                                 ? 0x80 : 0));
 
+       /*
+        * If the debuffer has set an invalid system call number,
+        * we prepare to skip the system call restart handling.
+        */
+       if (!entryexit && regs->gprs[2] >= NR_syscalls)
+               regs->trap = -1;
+
        /*
         * this isn't the same as continuing with a signal, but it will do
         * for normal use.  strace only continues with a signal if the