]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - arch/s390/kernel/ptrace.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / arch / s390 / kernel / ptrace.c
index 019bb714db49f758d4fef00f5671c218cab171a4..ef86ad2439868ffd4889b56f378f19e5c1f11968 100644 (file)
@@ -1,25 +1,9 @@
 /*
- *  arch/s390/kernel/ptrace.c
+ *  Ptrace user space interface.
  *
- *  S390 version
- *    Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
+ *    Copyright IBM Corp. 1999,2010
+ *    Author(s): Denis Joseph Barrow
  *               Martin Schwidefsky (schwidefsky@de.ibm.com)
- *
- *  Based on PowerPC version 
- *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
- *
- *  Derived from "arch/m68k/kernel/ptrace.c"
- *  Copyright (C) 1994 by Hamish Macdonald
- *  Taken from linux/kernel/ptrace.c and modified for M680x0.
- *  linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
- *
- * Modified by Cort Dougan (cort@cs.nmt.edu) 
- *
- *
- * This file is subject to the terms and conditions of the GNU General
- * Public License.  See the file README.legal in the main directory of
- * this archive for more details.
  */
 
 #include <linux/kernel.h>
@@ -61,76 +45,58 @@ enum s390_regset {
        REGSET_GENERAL_EXTENDED,
 };
 
-static void
-FixPerRegisters(struct task_struct *task)
+void update_per_regs(struct task_struct *task)
 {
-       struct pt_regs *regs;
-       per_struct *per_info;
-       per_cr_words cr_words;
-
-       regs = task_pt_regs(task);
-       per_info = (per_struct *) &task->thread.per_info;
-       per_info->control_regs.bits.em_instruction_fetch =
-               per_info->single_step | per_info->instruction_fetch;
-       
-       if (per_info->single_step) {
-               per_info->control_regs.bits.starting_addr = 0;
-#ifdef CONFIG_COMPAT
-               if (is_compat_task())
-                       per_info->control_regs.bits.ending_addr = 0x7fffffffUL;
-               else
-#endif
-                       per_info->control_regs.bits.ending_addr = PSW_ADDR_INSN;
-       } else {
-               per_info->control_regs.bits.starting_addr =
-                       per_info->starting_addr;
-               per_info->control_regs.bits.ending_addr =
-                       per_info->ending_addr;
-       }
-       /*
-        * if any of the control reg tracing bits are on 
-        * we switch on per in the psw
-        */
-       if (per_info->control_regs.words.cr[0] & PER_EM_MASK)
-               regs->psw.mask |= PSW_MASK_PER;
-       else
+       static const struct per_regs per_single_step = {
+               .control = PER_EVENT_IFETCH,
+               .start = 0,
+               .end = PSW_ADDR_INSN,
+       };
+       struct pt_regs *regs = task_pt_regs(task);
+       struct thread_struct *thread = &task->thread;
+       const struct per_regs *new;
+       struct per_regs old;
+
+       /* TIF_SINGLE_STEP overrides the user specified PER registers. */
+       new = test_tsk_thread_flag(task, TIF_SINGLE_STEP) ?
+               &per_single_step : &thread->per_user;
+
+       /* Take care of the PER enablement bit in the PSW. */
+       if (!(new->control & PER_EVENT_MASK)) {
                regs->psw.mask &= ~PSW_MASK_PER;
-
-       if (per_info->control_regs.bits.em_storage_alteration)
-               per_info->control_regs.bits.storage_alt_space_ctl = 1;
-       else
-               per_info->control_regs.bits.storage_alt_space_ctl = 0;
-
-       if (task == current) {
-               __ctl_store(cr_words, 9, 11);
-               if (memcmp(&cr_words, &per_info->control_regs.words,
-                          sizeof(cr_words)) != 0)
-                       __ctl_load(per_info->control_regs.words, 9, 11);
+               return;
        }
+       regs->psw.mask |= PSW_MASK_PER;
+       __ctl_store(old, 9, 11);
+       if (memcmp(new, &old, sizeof(struct per_regs)) != 0)
+               __ctl_load(*new, 9, 11);
 }
 
 void user_enable_single_step(struct task_struct *task)
 {
-       task->thread.per_info.single_step = 1;
-       FixPerRegisters(task);
+       set_tsk_thread_flag(task, TIF_SINGLE_STEP);
+       if (task == current)
+               update_per_regs(task);
 }
 
 void user_disable_single_step(struct task_struct *task)
 {
-       task->thread.per_info.single_step = 0;
-       FixPerRegisters(task);
+       clear_tsk_thread_flag(task, TIF_SINGLE_STEP);
+       if (task == current)
+               update_per_regs(task);
 }
 
 /*
  * Called by kernel/ptrace.c when detaching..
  *
- * Make sure single step bits etc are not set.
+ * Clear all debugging related fields.
  */
-void
-ptrace_disable(struct task_struct *child)
+void ptrace_disable(struct task_struct *task)
 {
-       /* make sure the single step bit is not set. */
-       user_disable_single_step(child);
+       memset(&task->thread.per_user, 0, sizeof(task->thread.per_user));
+       memset(&task->thread.per_event, 0, sizeof(task->thread.per_event));
+       clear_tsk_thread_flag(task, TIF_SINGLE_STEP);
+       clear_tsk_thread_flag(task, TIF_PER_TRAP);
 }
 
 #ifndef CONFIG_64BIT
@@ -139,6 +105,47 @@ ptrace_disable(struct task_struct *child)
 # define __ADDR_MASK 7
 #endif
 
+static inline unsigned long __peek_user_per(struct task_struct *child,
+                                           addr_t addr)
+{
+       struct per_struct_kernel *dummy = NULL;
+
+       if (addr == (addr_t) &dummy->cr9)
+               /* Control bits of the active per set. */
+               return test_thread_flag(TIF_SINGLE_STEP) ?
+                       PER_EVENT_IFETCH : child->thread.per_user.control;
+       else if (addr == (addr_t) &dummy->cr10)
+               /* Start address of the active per set. */
+               return test_thread_flag(TIF_SINGLE_STEP) ?
+                       0 : child->thread.per_user.start;
+       else if (addr == (addr_t) &dummy->cr11)
+               /* End address of the active per set. */
+               return test_thread_flag(TIF_SINGLE_STEP) ?
+                       PSW_ADDR_INSN : child->thread.per_user.end;
+       else if (addr == (addr_t) &dummy->bits)
+               /* Single-step bit. */
+               return test_thread_flag(TIF_SINGLE_STEP) ?
+                       (1UL << (BITS_PER_LONG - 1)) : 0;
+       else if (addr == (addr_t) &dummy->starting_addr)
+               /* Start address of the user specified per set. */
+               return child->thread.per_user.start;
+       else if (addr == (addr_t) &dummy->ending_addr)
+               /* End address of the user specified per set. */
+               return child->thread.per_user.end;
+       else if (addr == (addr_t) &dummy->perc_atmid)
+               /* PER code, ATMID and AI of the last PER trap */
+               return (unsigned long)
+                       child->thread.per_event.cause << (BITS_PER_LONG - 16);
+       else if (addr == (addr_t) &dummy->address)
+               /* Address of the last PER trap */
+               return child->thread.per_event.address;
+       else if (addr == (addr_t) &dummy->access_id)
+               /* Access id of the last PER trap */
+               return (unsigned long)
+                       child->thread.per_event.paid << (BITS_PER_LONG - 8);
+       return 0;
+}
+
 /*
  * Read the word at offset addr from the user area of a process. The
  * trouble here is that the information is littered over different
@@ -204,10 +211,10 @@ static unsigned long __peek_user(struct task_struct *child, addr_t addr)
 
        } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) {
                /*
-                * per_info is found in the thread structure
+                * Handle access to the per_info structure.
                 */
-               offset = addr - (addr_t) &dummy->regs.per_info;
-               tmp = *(addr_t *)((addr_t) &child->thread.per_info + offset);
+               addr -= (addr_t) &dummy->regs.per_info;
+               tmp = __peek_user_per(child, addr);
 
        } else
                tmp = 0;
@@ -237,6 +244,35 @@ peek_user(struct task_struct *child, addr_t addr, addr_t data)
        return put_user(tmp, (addr_t __user *) data);
 }
 
+static inline void __poke_user_per(struct task_struct *child,
+                                  addr_t addr, addr_t data)
+{
+       struct per_struct_kernel *dummy = NULL;
+
+       /*
+        * There are only three fields in the per_info struct that the
+        * debugger user can write to.
+        * 1) cr9: the debugger wants to set a new PER event mask
+        * 2) starting_addr: the debugger wants to set a new starting
+        *    address to use with the PER event mask.
+        * 3) ending_addr: the debugger wants to set a new ending
+        *    address to use with the PER event mask.
+        * The user specified PER event mask and the start and end
+        * addresses are used only if single stepping is not in effect.
+        * Writes to any other field in per_info are ignored.
+        */
+       if (addr == (addr_t) &dummy->cr9)
+               /* PER event mask of the user specified per set. */
+               child->thread.per_user.control =
+                       data & (PER_EVENT_MASK | PER_CONTROL_MASK);
+       else if (addr == (addr_t) &dummy->starting_addr)
+               /* Starting address of the user specified per set. */
+               child->thread.per_user.start = data;
+       else if (addr == (addr_t) &dummy->ending_addr)
+               /* Ending address of the user specified per set. */
+               child->thread.per_user.end = data;
+}
+
 /*
  * Write a word to the user area of a process at location addr. This
  * operation does have an additional problem compared to peek_user.
@@ -311,19 +347,17 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
 
        } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) {
                /*
-                * per_info is found in the thread structure 
+                * Handle access to the per_info structure.
                 */
-               offset = addr - (addr_t) &dummy->regs.per_info;
-               *(addr_t *)((addr_t) &child->thread.per_info + offset) = data;
+               addr -= (addr_t) &dummy->regs.per_info;
+               __poke_user_per(child, addr, data);
 
        }
 
-       FixPerRegisters(child);
        return 0;
 }
 
-static int
-poke_user(struct task_struct *child, addr_t addr, addr_t data)
+static int poke_user(struct task_struct *child, addr_t addr, addr_t data)
 {
        addr_t mask;
 
@@ -409,13 +443,54 @@ long arch_ptrace(struct task_struct *child, long request,
  * a 64 bit program is a no-no.
  */
 
+/*
+ * Same as peek_user_per but for a 31 bit program.
+ */
+static inline __u32 __peek_user_per_compat(struct task_struct *child,
+                                          addr_t addr)
+{
+       struct compat_per_struct_kernel *dummy32 = NULL;
+
+       if (addr == (addr_t) &dummy32->cr9)
+               /* Control bits of the active per set. */
+               return (__u32) test_thread_flag(TIF_SINGLE_STEP) ?
+                       PER_EVENT_IFETCH : child->thread.per_user.control;
+       else if (addr == (addr_t) &dummy32->cr10)
+               /* Start address of the active per set. */
+               return (__u32) test_thread_flag(TIF_SINGLE_STEP) ?
+                       0 : child->thread.per_user.start;
+       else if (addr == (addr_t) &dummy32->cr11)
+               /* End address of the active per set. */
+               return test_thread_flag(TIF_SINGLE_STEP) ?
+                       PSW32_ADDR_INSN : child->thread.per_user.end;
+       else if (addr == (addr_t) &dummy32->bits)
+               /* Single-step bit. */
+               return (__u32) test_thread_flag(TIF_SINGLE_STEP) ?
+                       0x80000000 : 0;
+       else if (addr == (addr_t) &dummy32->starting_addr)
+               /* Start address of the user specified per set. */
+               return (__u32) child->thread.per_user.start;
+       else if (addr == (addr_t) &dummy32->ending_addr)
+               /* End address of the user specified per set. */
+               return (__u32) child->thread.per_user.end;
+       else if (addr == (addr_t) &dummy32->perc_atmid)
+               /* PER code, ATMID and AI of the last PER trap */
+               return (__u32) child->thread.per_event.cause << 16;
+       else if (addr == (addr_t) &dummy32->address)
+               /* Address of the last PER trap */
+               return (__u32) child->thread.per_event.address;
+       else if (addr == (addr_t) &dummy32->access_id)
+               /* Access id of the last PER trap */
+               return (__u32) child->thread.per_event.paid << 24;
+       return 0;
+}
+
 /*
  * Same as peek_user but for a 31 bit program.
  */
 static u32 __peek_user_compat(struct task_struct *child, addr_t addr)
 {
-       struct user32 *dummy32 = NULL;
-       per_struct32 *dummy_per32 = NULL;
+       struct compat_user *dummy32 = NULL;
        addr_t offset;
        __u32 tmp;
 
@@ -465,19 +540,10 @@ static u32 __peek_user_compat(struct task_struct *child, addr_t addr)
 
        } else if (addr < (addr_t) (&dummy32->regs.per_info + 1)) {
                /*
-                * per_info is found in the thread structure
+                * Handle access to the per_info structure.
                 */
-               offset = addr - (addr_t) &dummy32->regs.per_info;
-               /* This is magic. See per_struct and per_struct32. */
-               if ((offset >= (addr_t) &dummy_per32->control_regs &&
-                    offset < (addr_t) (&dummy_per32->control_regs + 1)) ||
-                   (offset >= (addr_t) &dummy_per32->starting_addr &&
-                    offset <= (addr_t) &dummy_per32->ending_addr) ||
-                   offset == (addr_t) &dummy_per32->lowcore.words.address)
-                       offset = offset*2 + 4;
-               else
-                       offset = offset*2;
-               tmp = *(__u32 *)((addr_t) &child->thread.per_info + offset);
+               addr -= (addr_t) &dummy32->regs.per_info;
+               tmp = __peek_user_per_compat(child, addr);
 
        } else
                tmp = 0;
@@ -497,14 +563,33 @@ static int peek_user_compat(struct task_struct *child,
        return put_user(tmp, (__u32 __user *) data);
 }
 
+/*
+ * Same as poke_user_per but for a 31 bit program.
+ */
+static inline void __poke_user_per_compat(struct task_struct *child,
+                                         addr_t addr, __u32 data)
+{
+       struct compat_per_struct_kernel *dummy32 = NULL;
+
+       if (addr == (addr_t) &dummy32->cr9)
+               /* PER event mask of the user specified per set. */
+               child->thread.per_user.control =
+                       data & (PER_EVENT_MASK | PER_CONTROL_MASK);
+       else if (addr == (addr_t) &dummy32->starting_addr)
+               /* Starting address of the user specified per set. */
+               child->thread.per_user.start = data;
+       else if (addr == (addr_t) &dummy32->ending_addr)
+               /* Ending address of the user specified per set. */
+               child->thread.per_user.end = data;
+}
+
 /*
  * Same as poke_user but for a 31 bit program.
  */
 static int __poke_user_compat(struct task_struct *child,
                              addr_t addr, addr_t data)
 {
-       struct user32 *dummy32 = NULL;
-       per_struct32 *dummy_per32 = NULL;
+       struct compat_user *dummy32 = NULL;
        __u32 tmp = (__u32) data;
        addr_t offset;
 
@@ -561,37 +646,20 @@ static int __poke_user_compat(struct task_struct *child,
 
        } else if (addr < (addr_t) (&dummy32->regs.per_info + 1)) {
                /*
-                * per_info is found in the thread structure.
-                */
-               offset = addr - (addr_t) &dummy32->regs.per_info;
-               /*
-                * This is magic. See per_struct and per_struct32.
-                * By incident the offsets in per_struct are exactly
-                * twice the offsets in per_struct32 for all fields.
-                * The 8 byte fields need special handling though,
-                * because the second half (bytes 4-7) is needed and
-                * not the first half.
+                * Handle access to the per_info structure.
                 */
-               if ((offset >= (addr_t) &dummy_per32->control_regs &&
-                    offset < (addr_t) (&dummy_per32->control_regs + 1)) ||
-                   (offset >= (addr_t) &dummy_per32->starting_addr &&
-                    offset <= (addr_t) &dummy_per32->ending_addr) ||
-                   offset == (addr_t) &dummy_per32->lowcore.words.address)
-                       offset = offset*2 + 4;
-               else
-                       offset = offset*2;
-               *(__u32 *)((addr_t) &child->thread.per_info + offset) = tmp;
-
+               addr -= (addr_t) &dummy32->regs.per_info;
+               __poke_user_per_compat(child, addr, data);
        }
 
-       FixPerRegisters(child);
        return 0;
 }
 
 static int poke_user_compat(struct task_struct *child,
                            addr_t addr, addr_t data)
 {
-       if (!is_compat_task() || (addr & 3) || addr > sizeof(struct user32) - 3)
+       if (!is_compat_task() || (addr & 3) ||
+           addr > sizeof(struct compat_user) - 3)
                return -EIO;
 
        return __poke_user_compat(child, addr, data);
@@ -602,7 +670,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 {
        unsigned long addr = caddr;
        unsigned long data = cdata;
-       ptrace_area_emu31 parea; 
+       compat_ptrace_area parea;
        int copied, ret;
 
        switch (request) {