[S390] ptrace changes
authorMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 10 Oct 2008 19:33:20 +0000 (21:33 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Fri, 10 Oct 2008 19:33:57 +0000 (21:33 +0200)
* System call parameter and result access functions
* Add tracehook calls
* Split syscall_trace into two functions do_syscall_trace_enter and
  do_syscall_trace_exit

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/Kconfig
arch/s390/include/asm/ptrace.h
arch/s390/include/asm/syscall.h [new file with mode: 0644]
arch/s390/include/asm/thread_info.h
arch/s390/kernel/entry.S
arch/s390/kernel/entry64.S
arch/s390/kernel/ptrace.c
arch/s390/kernel/signal.c

index 8d41908e251375ea3c5a96cbd8000a7b4a29df12..4c03049e7db9195586fe00b07a22c067dcc3f30a 100644 (file)
@@ -74,6 +74,7 @@ config S390
        select HAVE_KPROBES
        select HAVE_KRETPROBES
        select HAVE_KVM if 64BIT
+       select HAVE_ARCH_TRACEHOOK
 
 source "init/Kconfig"
 
index af2c9ac28a07945eee89ddc0e7fc2e3d592fd25b..a7226f8143fb9d34df06456f1759d0c2fa085e94 100644 (file)
@@ -490,6 +490,7 @@ extern void user_disable_single_step(struct task_struct *);
 
 #define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0)
 #define instruction_pointer(regs) ((regs)->psw.addr & PSW_ADDR_INSN)
+#define user_stack_pointer(regs)((regs)->gprs[15])
 #define regs_return_value(regs)((regs)->gprs[2])
 #define profile_pc(regs) instruction_pointer(regs)
 extern void show_regs(struct pt_regs * regs);
diff --git a/arch/s390/include/asm/syscall.h b/arch/s390/include/asm/syscall.h
new file mode 100644 (file)
index 0000000..6e62397
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Access to user system call parameters and results
+ *
+ *  Copyright IBM Corp. 2008
+ *  Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ */
+
+#ifndef _ASM_SYSCALL_H
+#define _ASM_SYSCALL_H 1
+
+#include <asm/ptrace.h>
+
+static inline long syscall_get_nr(struct task_struct *task,
+                                 struct pt_regs *regs)
+{
+       if (regs->trap != __LC_SVC_OLD_PSW)
+               return -1;
+       return regs->gprs[2];
+}
+
+static inline void syscall_rollback(struct task_struct *task,
+                                   struct pt_regs *regs)
+{
+       regs->gprs[2] = regs->orig_gpr2;
+}
+
+static inline long syscall_get_error(struct task_struct *task,
+                                    struct pt_regs *regs)
+{
+       return (regs->gprs[2] >= -4096UL) ? -regs->gprs[2] : 0;
+}
+
+static inline long syscall_get_return_value(struct task_struct *task,
+                                           struct pt_regs *regs)
+{
+       return regs->gprs[2];
+}
+
+static inline void syscall_set_return_value(struct task_struct *task,
+                                           struct pt_regs *regs,
+                                           int error, long val)
+{
+       regs->gprs[2] = error ? -error : val;
+}
+
+static inline void syscall_get_arguments(struct task_struct *task,
+                                        struct pt_regs *regs,
+                                        unsigned int i, unsigned int n,
+                                        unsigned long *args)
+{
+       BUG_ON(i + n > 6);
+#ifdef CONFIG_COMPAT
+       if (test_tsk_thread_flag(task, TIF_31BIT)) {
+               if (i + n == 6)
+                       args[--n] = (u32) regs->args[0];
+               while (n-- > 0)
+                       args[n] = (u32) regs->gprs[2 + i + n];
+       }
+#endif
+       if (i + n == 6)
+               args[--n] = regs->args[0];
+       memcpy(args, &regs->gprs[2 + i], n * sizeof(args[0]));
+}
+
+static inline void syscall_set_arguments(struct task_struct *task,
+                                        struct pt_regs *regs,
+                                        unsigned int i, unsigned int n,
+                                        const unsigned long *args)
+{
+       BUG_ON(i + n > 6);
+       if (i + n == 6)
+               regs->args[0] = args[--n];
+       memcpy(&regs->gprs[2 + i], args, n * sizeof(args[0]));
+}
+
+#endif /* _ASM_SYSCALL_H */
index 91a8f93ad355fcfd2e69a710bbc9efb2bba685f0..ea40a9d690fcc19407d8fd8a610d1af5b25a7a7d 100644 (file)
@@ -86,6 +86,7 @@ static inline struct thread_info *current_thread_info(void)
  * thread information flags bit numbers
  */
 #define TIF_SYSCALL_TRACE      0       /* syscall trace active */
+#define TIF_NOTIFY_RESUME      1       /* callback before returning to user */
 #define TIF_SIGPENDING         2       /* signal pending */
 #define TIF_NEED_RESCHED       3       /* rescheduling necessary */
 #define TIF_RESTART_SVC                4       /* restart svc with new svc number */
@@ -100,6 +101,7 @@ static inline struct thread_info *current_thread_info(void)
 #define TIF_RESTORE_SIGMASK    20      /* restore signal mask in do_signal() */
 
 #define _TIF_SYSCALL_TRACE     (1<<TIF_SYSCALL_TRACE)
+#define _TIF_NOTIFY_RESUME     (1<<TIF_NOTIFY_RESUME)
 #define _TIF_RESTORE_SIGMASK   (1<<TIF_RESTORE_SIGMASK)
 #define _TIF_SIGPENDING                (1<<TIF_SIGPENDING)
 #define _TIF_NEED_RESCHED      (1<<TIF_NEED_RESCHED)
index 708cf9cf9a355e175d5b1cf4fbdc2b96732e7084..ed500ef799b795a21f322ae0d8e818b8bd926a36 100644 (file)
@@ -49,9 +49,9 @@ SP_ILC             =  STACK_FRAME_OVERHEAD + __PT_ILC
 SP_TRAP      = STACK_FRAME_OVERHEAD + __PT_TRAP
 SP_SIZE      = STACK_FRAME_OVERHEAD + __PT_SIZE
 
-_TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
+_TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
                 _TIF_MCCK_PENDING | _TIF_RESTART_SVC | _TIF_SINGLE_STEP )
-_TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
+_TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
                 _TIF_MCCK_PENDING)
 
 STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER
@@ -318,6 +318,8 @@ sysc_work:
        bo      BASED(sysc_reschedule)
        tm      __TI_flags+3(%r9),_TIF_SIGPENDING
        bnz     BASED(sysc_sigpending)
+       tm      __TI_flags+3(%r9),_TIF_NOTIFY_RESUME
+       bnz     BASED(sysc_notify_resume)
        tm      __TI_flags+3(%r9),_TIF_RESTART_SVC
        bo      BASED(sysc_restart)
        tm      __TI_flags+3(%r9),_TIF_SINGLE_STEP
@@ -355,6 +357,16 @@ sysc_sigpending:
        bo      BASED(sysc_singlestep)
        b       BASED(sysc_work_loop)
 
+#
+# _TIF_NOTIFY_RESUME is set, call do_notify_resume
+#
+sysc_notify_resume:
+       la      %r2,SP_PTREGS(%r15)     # load pt_regs
+       l       %r1,BASED(.Ldo_notify_resume)
+       la      %r14,BASED(sysc_work_loop)
+       br      %r1                     # call do_notify_resume
+
+
 #
 # _TIF_RESTART_SVC is set, set up registers and restart svc
 #
@@ -378,20 +390,21 @@ sysc_singlestep:
        br      %r1                     # branch to do_single_step
 
 #
-# call trace before and after sys_call
+# call tracehook_report_syscall_entry/tracehook_report_syscall_exit before
+# and after the system call
 #
 sysc_tracesys:
-       l       %r1,BASED(.Ltrace)
+       l       %r1,BASED(.Ltrace_entry)
        la      %r2,SP_PTREGS(%r15)     # load pt_regs
        la      %r3,0
        srl     %r7,2
        st      %r7,SP_R2(%r15)
        basr    %r14,%r1
-       clc     SP_R2(4,%r15),BASED(.Lnr_syscalls)
+       cl      %r2,BASED(.Lnr_syscalls)
        bnl     BASED(sysc_tracenogo)
        l       %r8,BASED(.Lsysc_table)
-       l       %r7,SP_R2(%r15)         # strace might have changed the
-       sll     %r7,2                   #  system call
+       lr      %r7,%r2
+       sll     %r7,2                   # *4
        l       %r8,0(%r7,%r8)
 sysc_tracego:
        lm      %r3,%r6,SP_R3(%r15)
@@ -401,9 +414,8 @@ sysc_tracego:
 sysc_tracenogo:
        tm      __TI_flags+3(%r9),(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT)
        bz      BASED(sysc_return)
-       l       %r1,BASED(.Ltrace)
+       l       %r1,BASED(.Ltrace_exit)
        la      %r2,SP_PTREGS(%r15)     # load pt_regs
-       la      %r3,1
        la      %r14,BASED(sysc_return)
        br      %r1
 
@@ -666,6 +678,8 @@ io_work_loop:
        bo      BASED(io_reschedule)
        tm      __TI_flags+3(%r9),_TIF_SIGPENDING
        bnz     BASED(io_sigpending)
+       tm      __TI_flags+3(%r9),_TIF_NOTIFY_RESUME
+       bnz     BASED(io_notify_resume)
        b       BASED(io_restore)
 io_work_done:
 
@@ -704,6 +718,19 @@ io_sigpending:
        TRACE_IRQS_OFF
        b       BASED(io_work_loop)
 
+#
+# _TIF_SIGPENDING is set, call do_signal
+#
+io_notify_resume:
+       TRACE_IRQS_ON
+       stosm   __SF_EMPTY(%r15),0x03   # reenable interrupts
+       la      %r2,SP_PTREGS(%r15)     # load pt_regs
+       l       %r1,BASED(.Ldo_notify_resume)
+       basr    %r14,%r1                # call do_signal
+       stnsm   __SF_EMPTY(%r15),0xfc   # disable I/O and ext. interrupts
+       TRACE_IRQS_OFF
+       b       BASED(io_work_loop)
+
 /*
  * External interrupt handler routine
  */
@@ -1070,6 +1097,8 @@ cleanup_io_leave_insn:
 .Ldo_IRQ:      .long   do_IRQ
 .Ldo_extint:   .long   do_extint
 .Ldo_signal:   .long   do_signal
+.Ldo_notify_resume:
+               .long   do_notify_resume
 .Lhandle_per:  .long   do_single_step
 .Ldo_execve:   .long   do_execve
 .Lexecve_tail: .long   execve_tail
@@ -1079,7 +1108,8 @@ cleanup_io_leave_insn:
 .Lpreempt_schedule_irq:
                .long   preempt_schedule_irq
 #endif
-.Ltrace:       .long   syscall_trace
+.Ltrace_entry: .long   do_syscall_trace_enter
+.Ltrace_exit:  .long   do_syscall_trace_exit
 .Lschedtail:   .long   schedule_tail
 .Lsysc_table:  .long   sys_call_table
 #ifdef CONFIG_TRACE_IRQFLAGS
index fee10177dbfcf67a175c7d82f1839886ef509ff4..d7ce150453f2865bfa4c9e99f53c03cda198442d 100644 (file)
@@ -52,9 +52,9 @@ SP_SIZE      =        STACK_FRAME_OVERHEAD + __PT_SIZE
 STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER
 STACK_SIZE  = 1 << STACK_SHIFT
 
-_TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
+_TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
                 _TIF_MCCK_PENDING | _TIF_RESTART_SVC | _TIF_SINGLE_STEP )
-_TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
+_TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
                 _TIF_MCCK_PENDING)
 
 #define BASED(name) name-system_call(%r13)
@@ -310,6 +310,8 @@ sysc_work:
        jo      sysc_reschedule
        tm      __TI_flags+7(%r9),_TIF_SIGPENDING
        jnz     sysc_sigpending
+       tm      __TI_flags+7(%r9),_TIF_NOTIFY_RESUME
+       jnz     sysc_notify_resume
        tm      __TI_flags+7(%r9),_TIF_RESTART_SVC
        jo      sysc_restart
        tm      __TI_flags+7(%r9),_TIF_SINGLE_STEP
@@ -344,6 +346,14 @@ sysc_sigpending:
        jo      sysc_singlestep
        j       sysc_work_loop
 
+#
+# _TIF_NOTIFY_RESUME is set, call do_notify_resume
+#
+sysc_notify_resume:
+       la      %r2,SP_PTREGS(%r15)     # load pt_regs
+       larl    %r14,sysc_work_loop
+       jg      do_notify_resume        # call do_notify_resume
+
 #
 # _TIF_RESTART_SVC is set, set up registers and restart svc
 #
@@ -367,20 +377,19 @@ sysc_singlestep:
        jg      do_single_step          # branch to do_sigtrap
 
 #
-# call syscall_trace before and after system call
-# special linkage: %r12 contains the return address for trace_svc
+# call tracehook_report_syscall_entry/tracehook_report_syscall_exit before
+# and after the system call
 #
 sysc_tracesys:
        la      %r2,SP_PTREGS(%r15)     # load pt_regs
        la      %r3,0
        srl     %r7,2
        stg     %r7,SP_R2(%r15)
-       brasl   %r14,syscall_trace
+       brasl   %r14,do_syscall_trace_enter
        lghi    %r0,NR_syscalls
-       clg     %r0,SP_R2(%r15)
+       clgr    %r0,%r2
        jnh     sysc_tracenogo
-       lg      %r7,SP_R2(%r15)         # strace might have changed the
-       sll     %r7,2                   # system call
+       slag    %r7,%r2,2               # *4
        lgf     %r8,0(%r7,%r10)
 sysc_tracego:
        lmg     %r3,%r6,SP_R3(%r15)
@@ -391,9 +400,8 @@ sysc_tracenogo:
        tm      __TI_flags+7(%r9),(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT)
        jz      sysc_return
        la      %r2,SP_PTREGS(%r15)     # load pt_regs
-       la      %r3,1
        larl    %r14,sysc_return        # return point is sysc_return
-       jg      syscall_trace
+       jg      do_syscall_trace_exit
 
 #
 # a new process exits the kernel with ret_from_fork
@@ -672,6 +680,8 @@ io_work_loop:
        jo      io_reschedule
        tm      __TI_flags+7(%r9),_TIF_SIGPENDING
        jnz     io_sigpending
+       tm      __TI_flags+7(%r9),_TIF_NOTIFY_RESUME
+       jnz     io_notify_resume
        j       io_restore
 io_work_done:
 
@@ -712,6 +722,18 @@ io_sigpending:
        TRACE_IRQS_OFF
        j       io_work_loop
 
+#
+# _TIF_NOTIFY_RESUME or is set, call do_notify_resume
+#
+io_notify_resume:
+       TRACE_IRQS_ON
+       stosm   __SF_EMPTY(%r15),0x03   # reenable interrupts
+       la      %r2,SP_PTREGS(%r15)     # load pt_regs
+       brasl   %r14,do_notify_resume   # call do_notify_resume
+       stnsm   __SF_EMPTY(%r15),0xfc   # disable I/O and ext. interrupts
+       TRACE_IRQS_OFF
+       j       io_work_loop
+
 /*
  * External interrupt handler routine
  */
index c8b08289eb87e1d78c9c92996fe7bbfe30c16645..1f31be1ecc4bd57bd3af99ef004f00df4f24987b 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/signal.h>
 #include <linux/elf.h>
 #include <linux/regset.h>
+#include <linux/tracehook.h>
 
 #include <asm/segment.h>
 #include <asm/page.h>
@@ -639,40 +640,44 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 }
 #endif
 
-asmlinkage void
-syscall_trace(struct pt_regs *regs, int entryexit)
+asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
 {
-       if (unlikely(current->audit_context) && entryexit)
-               audit_syscall_exit(AUDITSC_RESULT(regs->gprs[2]), regs->gprs[2]);
-
-       if (!test_thread_flag(TIF_SYSCALL_TRACE))
-               goto out;
-       if (!(current->ptrace & PT_PTRACED))
-               goto out;
-       ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
-                                ? 0x80 : 0));
+       long ret;
 
        /*
-        * If the debuffer has set an invalid system call number,
-        * we prepare to skip the system call restart handling.
+        * The sysc_tracesys code in entry.S stored the system
+        * call number to gprs[2].
         */
-       if (!entryexit && regs->gprs[2] >= NR_syscalls)
+       ret = regs->gprs[2];
+       if (test_thread_flag(TIF_SYSCALL_TRACE) &&
+           (tracehook_report_syscall_entry(regs) ||
+            regs->gprs[2] >= NR_syscalls)) {
+               /*
+                * Tracing decided this syscall should not happen or the
+                * debugger stored an invalid system call number. Skip
+                * the system call and the system call restart handling.
+                */
                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
-        * stopping signal is not SIGTRAP.  -brl
-        */
-       if (current->exit_code) {
-               send_sig(current->exit_code, current, 1);
-               current->exit_code = 0;
+               ret = -1;
        }
- out:
-       if (unlikely(current->audit_context) && !entryexit)
-               audit_syscall_entry(test_thread_flag(TIF_31BIT)?AUDIT_ARCH_S390:AUDIT_ARCH_S390X,
-                                   regs->gprs[2], regs->orig_gpr2, regs->gprs[3],
-                                   regs->gprs[4], regs->gprs[5]);
+
+       if (unlikely(current->audit_context))
+               audit_syscall_entry(test_thread_flag(TIF_31BIT) ?
+                                       AUDIT_ARCH_S390 : AUDIT_ARCH_S390X,
+                                   regs->gprs[2], regs->orig_gpr2,
+                                   regs->gprs[3], regs->gprs[4],
+                                   regs->gprs[5]);
+       return ret;
+}
+
+asmlinkage void do_syscall_trace_exit(struct pt_regs *regs)
+{
+       if (unlikely(current->audit_context))
+               audit_syscall_exit(AUDITSC_RESULT(regs->gprs[2]),
+                                  regs->gprs[2]);
+
+       if (test_thread_flag(TIF_SYSCALL_TRACE))
+               tracehook_report_syscall_exit(regs, 0);
 }
 
 /*
index b976820402154a49b3792e7f8c07d7c803416358..4f7fc3059a8e8a1e1e937d976d1c962febcdbd7e 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/tty.h>
 #include <linux/personality.h>
 #include <linux/binfmts.h>
+#include <linux/tracehook.h>
 #include <asm/ucontext.h>
 #include <asm/uaccess.h>
 #include <asm/lowcore.h>
@@ -507,6 +508,12 @@ void do_signal(struct pt_regs *regs)
                         */
                        if (current->thread.per_info.single_step)
                                set_thread_flag(TIF_SINGLE_STEP);
+
+                       /*
+                        * Let tracing know that we've done the handler setup.
+                        */
+                       tracehook_signal_handler(signr, &info, &ka, regs,
+                                        test_thread_flag(TIF_SINGLE_STEP));
                }
                return;
        }
@@ -526,3 +533,9 @@ void do_signal(struct pt_regs *regs)
                set_thread_flag(TIF_RESTART_SVC);
        }
 }
+
+void do_notify_resume(struct pt_regs *regs)
+{
+       clear_thread_flag(TIF_NOTIFY_RESUME);
+       tracehook_notify_resume(regs);
+}