microblaze: Support ptrace syscall tracing.
authorMichal Simek <monstr@monstr.eu>
Mon, 24 Aug 2009 11:26:04 +0000 (13:26 +0200)
committerMichal Simek <monstr@monstr.eu>
Tue, 22 Sep 2009 08:00:45 +0000 (10:00 +0200)
Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
Signed-off-by: Michal Simek <monstr@monstr.eu>
arch/microblaze/kernel/entry.S
arch/microblaze/kernel/ptrace.c

index c7353e7..acc1f05 100644 (file)
@@ -308,38 +308,69 @@ C_ENTRY(_user_exception):
        swi     r12, r1, PTO+PT_R0;
        tovirt(r1,r1)
 
-       la      r15, r0, ret_from_trap-8
 /* where the trap should return need -8 to adjust for rtsd r15, 8*/
 /* Jump to the appropriate function for the system call number in r12
  * (r12 is not preserved), or return an error if r12 is not valid. The LP
  * register should point to the location where
  * the called function should return.  [note that MAKE_SYS_CALL uses label 1] */
-       /* See if the system call number is valid.  */
+
+       # Step into virtual mode.
+       set_vms;
+       addik   r11, r0, 3f
+       rtid    r11, 0
+       nop
+3:
+       add     r11, r0, CURRENT_TASK    /* Get current task ptr into r11 */
+       lwi     r11, r11, TS_THREAD_INFO /* get thread info */
+       lwi     r11, r11, TI_FLAGS       /* get flags in thread info */
+       andi    r11, r11, _TIF_WORK_SYSCALL_MASK
+       beqi    r11, 4f
+
+       addik   r3, r0, -ENOSYS
+       swi     r3, r1, PTO + PT_R3
+       brlid   r15, do_syscall_trace_enter
+       addik   r5, r1, PTO + PT_R0
+
+       # do_syscall_trace_enter returns the new syscall nr.
+       addk    r12, r0, r3
+       lwi     r5, r1, PTO+PT_R5;
+       lwi     r6, r1, PTO+PT_R6;
+       lwi     r7, r1, PTO+PT_R7;
+       lwi     r8, r1, PTO+PT_R8;
+       lwi     r9, r1, PTO+PT_R9;
+       lwi     r10, r1, PTO+PT_R10;
+4:
+/* Jump to the appropriate function for the system call number in r12
+ * (r12 is not preserved), or return an error if r12 is not valid.
+ * The LP register should point to the location where the called function
+ * should return.  [note that MAKE_SYS_CALL uses label 1] */
+       /* See if the system call number is valid */
        addi    r11, r12, -__NR_syscalls;
-       bgei    r11,1f;
+       bgei    r11,5f;
        /* Figure out which function to use for this system call.  */
        /* Note Microblaze barrel shift is optional, so don't rely on it */
        add     r12, r12, r12;                  /* convert num -> ptr */
        add     r12, r12, r12;
 
        /* Trac syscalls and stored them to r0_ram */
-       lwi     r3, r12, 0x400 + TOPHYS(r0_ram)
+       lwi     r3, r12, 0x400 + r0_ram
        addi    r3, r3, 1
-       swi     r3, r12, 0x400 + TOPHYS(r0_ram)
+       swi     r3, r12, 0x400 + r0_ram
+
+       # Find and jump into the syscall handler.
+       lwi     r12, r12, sys_call_table
+       /* where the trap should return need -8 to adjust for rtsd r15, 8 */
+       la      r15, r0, ret_from_trap-8
+       bra     r12
 
-       lwi     r12, r12, TOPHYS(sys_call_table); /* Function ptr */
-       /* Make the system call.  to r12*/
-       set_vms;
-       rtid    r12, 0;
-       nop;
        /* The syscall number is invalid, return an error.  */
-1:     VM_ON;  /* RETURN() expects virtual mode*/
+5:
        addi    r3, r0, -ENOSYS;
        rtsd    r15,8;          /* looks like a normal subroutine return */
        or      r0, r0, r0
 
 
-/* Entry point used to return from a syscall/trap */
+/* Entry point used to return from a syscall/trap */
 /* We re-enable BIP bit before state restore */
 C_ENTRY(ret_from_trap):
        set_bip;                        /*  Ints masked for state restore*/
@@ -349,6 +380,23 @@ C_ENTRY(ret_from_trap):
 
        /* We're returning to user mode, so check for various conditions that
         * trigger rescheduling. */
+       # FIXME: Restructure all these flag checks.
+       add     r11, r0, CURRENT_TASK;  /* Get current task ptr into r11 */
+       lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
+       lwi     r11, r11, TI_FLAGS;             /* get flags in thread info */
+       andi    r11, r11, _TIF_WORK_SYSCALL_MASK
+       beqi    r11, 1f
+
+       swi     r3, r1, PTO + PT_R3
+       swi     r4, r1, PTO + PT_R4
+       brlid   r15, do_syscall_trace_leave
+       addik   r5, r1, PTO + PT_R0
+       lwi     r3, r1, PTO + PT_R3
+       lwi     r4, r1, PTO + PT_R4
+1:
+
+       /* We're returning to user mode, so check for various conditions that
+        * trigger rescheduling. */
        /* Get current task ptr into r11 */
        add     r11, r0, CURRENT_TASK;  /* Get current task ptr into r11 */
        lwi     r11, r11, TS_THREAD_INFO;       /* get thread info */
index 53ff39a..4b3ac32 100644 (file)
 #include <linux/sched.h>
 #include <linux/ptrace.h>
 #include <linux/signal.h>
+#include <linux/elf.h>
+#include <linux/audit.h>
+#include <linux/seccomp.h>
+#include <linux/tracehook.h>
 
 #include <linux/errno.h>
 #include <asm/processor.h>
@@ -174,6 +178,64 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
        return rval;
 }
 
+asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
+{
+       long ret = 0;
+
+       secure_computing(regs->r12);
+
+       if (test_thread_flag(TIF_SYSCALL_TRACE) &&
+           tracehook_report_syscall_entry(regs))
+               /*
+                * Tracing decided this syscall should not happen.
+                * We'll return a bogus call number to get an ENOSYS
+                * error, but leave the original number in regs->regs[0].
+                */
+               ret = -1L;
+
+       if (unlikely(current->audit_context))
+               audit_syscall_entry(EM_XILINX_MICROBLAZE, regs->r12,
+                                   regs->r5, regs->r6,
+                                   regs->r7, regs->r8);
+
+       return ret ?: regs->r12;
+}
+
+asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
+{
+       int step;
+
+       if (unlikely(current->audit_context))
+               audit_syscall_exit(AUDITSC_RESULT(regs->r3), regs->r3);
+
+       step = test_thread_flag(TIF_SINGLESTEP);
+       if (step || test_thread_flag(TIF_SYSCALL_TRACE))
+               tracehook_report_syscall_exit(regs, step);
+}
+
+#if 0
+static asmlinkage void syscall_trace(void)
+{
+       if (!test_thread_flag(TIF_SYSCALL_TRACE))
+               return;
+       if (!(current->ptrace & PT_PTRACED))
+               return;
+       /* The 0x80 provides a way for the tracing parent to distinguish
+        between a syscall stop and SIGTRAP delivery */
+       ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
+                               ? 0x80 : 0));
+       /*
+        * 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;
+       }
+}
+#endif
+
 void ptrace_disable(struct task_struct *child)
 {
        /* nothing to do */