]> git.openfabrics.org - ~shefty/rdma-dev.git/blob - arch/tile/kernel/ptrace.c
b32bc3f9d631b5ad23ae85d980e610b21333d0f3
[~shefty/rdma-dev.git] / arch / tile / kernel / ptrace.c
1 /*
2  * Copyright 2010 Tilera Corporation. All Rights Reserved.
3  *
4  *   This program is free software; you can redistribute it and/or
5  *   modify it under the terms of the GNU General Public License
6  *   as published by the Free Software Foundation, version 2.
7  *
8  *   This program is distributed in the hope that it will be useful, but
9  *   WITHOUT ANY WARRANTY; without even the implied warranty of
10  *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
11  *   NON INFRINGEMENT.  See the GNU General Public License for
12  *   more details.
13  *
14  * Copied from i386: Ross Biro 1/23/92
15  */
16
17 #include <linux/kernel.h>
18 #include <linux/ptrace.h>
19 #include <linux/kprobes.h>
20 #include <linux/compat.h>
21 #include <linux/uaccess.h>
22 #include <asm/traps.h>
23
24 void user_enable_single_step(struct task_struct *child)
25 {
26         set_tsk_thread_flag(child, TIF_SINGLESTEP);
27 }
28
29 void user_disable_single_step(struct task_struct *child)
30 {
31         clear_tsk_thread_flag(child, TIF_SINGLESTEP);
32 }
33
34 /*
35  * Called by kernel/ptrace.c when detaching..
36  */
37 void ptrace_disable(struct task_struct *child)
38 {
39         clear_tsk_thread_flag(child, TIF_SINGLESTEP);
40
41         /*
42          * These two are currently unused, but will be set by arch_ptrace()
43          * and used in the syscall assembly when we do support them.
44          */
45         clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
46 }
47
48 /*
49  * Get registers from task and ready the result for userspace.
50  * Note that we localize the API issues to getregs() and putregs() at
51  * some cost in performance, e.g. we need a full pt_regs copy for
52  * PEEKUSR, and two copies for POKEUSR.  But in general we expect
53  * GETREGS/PUTREGS to be the API of choice anyway.
54  */
55 static char *getregs(struct task_struct *child, struct pt_regs *uregs)
56 {
57         *uregs = *task_pt_regs(child);
58
59         /* Set up flags ABI bits. */
60         uregs->flags = 0;
61 #ifdef CONFIG_COMPAT
62         if (task_thread_info(child)->status & TS_COMPAT)
63                 uregs->flags |= PT_FLAGS_COMPAT;
64 #endif
65
66         return (char *)uregs;
67 }
68
69 /* Put registers back to task. */
70 static void putregs(struct task_struct *child, struct pt_regs *uregs)
71 {
72         struct pt_regs *regs = task_pt_regs(child);
73
74         /* Don't allow overwriting the kernel-internal flags word. */
75         uregs->flags = regs->flags;
76
77         /* Only allow setting the ICS bit in the ex1 word. */
78         uregs->ex1 = PL_ICS_EX1(USER_PL, EX1_ICS(uregs->ex1));
79
80         *regs = *uregs;
81 }
82
83 long arch_ptrace(struct task_struct *child, long request,
84                  unsigned long addr, unsigned long data)
85 {
86         unsigned long __user *datap = (long __user __force *)data;
87         unsigned long tmp;
88         long ret = -EIO;
89         char *childreg;
90         struct pt_regs copyregs;
91
92         switch (request) {
93
94         case PTRACE_PEEKUSR:  /* Read register from pt_regs. */
95                 if (addr >= PTREGS_SIZE)
96                         break;
97                 childreg = getregs(child, &copyregs) + addr;
98 #ifdef CONFIG_COMPAT
99                 if (is_compat_task()) {
100                         if (addr & (sizeof(compat_long_t)-1))
101                                 break;
102                         ret = put_user(*(compat_long_t *)childreg,
103                                        (compat_long_t __user *)datap);
104                 } else
105 #endif
106                 {
107                         if (addr & (sizeof(long)-1))
108                                 break;
109                         ret = put_user(*(long *)childreg, datap);
110                 }
111                 break;
112
113         case PTRACE_POKEUSR:  /* Write register in pt_regs. */
114                 if (addr >= PTREGS_SIZE)
115                         break;
116                 childreg = getregs(child, &copyregs) + addr;
117 #ifdef CONFIG_COMPAT
118                 if (is_compat_task()) {
119                         if (addr & (sizeof(compat_long_t)-1))
120                                 break;
121                         *(compat_long_t *)childreg = data;
122                 } else
123 #endif
124                 {
125                         if (addr & (sizeof(long)-1))
126                                 break;
127                         *(long *)childreg = data;
128                 }
129                 putregs(child, &copyregs);
130                 ret = 0;
131                 break;
132
133         case PTRACE_GETREGS:  /* Get all registers from the child. */
134                 if (copy_to_user(datap, getregs(child, &copyregs),
135                                  sizeof(struct pt_regs)) == 0) {
136                         ret = 0;
137                 }
138                 break;
139
140         case PTRACE_SETREGS:  /* Set all registers in the child. */
141                 if (copy_from_user(&copyregs, datap,
142                                    sizeof(struct pt_regs)) == 0) {
143                         putregs(child, &copyregs);
144                         ret = 0;
145                 }
146                 break;
147
148         case PTRACE_GETFPREGS:  /* Get the child FPU state. */
149         case PTRACE_SETFPREGS:  /* Set the child FPU state. */
150                 break;
151
152         case PTRACE_SETOPTIONS:
153                 /* Support TILE-specific ptrace options. */
154                 BUILD_BUG_ON(PTRACE_O_MASK_TILE & PTRACE_O_MASK);
155                 tmp = data & PTRACE_O_MASK_TILE;
156                 data &= ~PTRACE_O_MASK_TILE;
157                 ret = ptrace_request(child, request, addr, data);
158                 if (ret == 0) {
159                         unsigned int flags = child->ptrace;
160                         flags &= ~(PTRACE_O_MASK_TILE << PT_OPT_FLAG_SHIFT);
161                         flags |= (tmp << PT_OPT_FLAG_SHIFT);
162                         child->ptrace = flags;
163                 }
164                 break;
165
166         default:
167 #ifdef CONFIG_COMPAT
168                 if (task_thread_info(current)->status & TS_COMPAT) {
169                         ret = compat_ptrace_request(child, request,
170                                                     addr, data);
171                         break;
172                 }
173 #endif
174                 ret = ptrace_request(child, request, addr, data);
175                 break;
176         }
177
178         return ret;
179 }
180
181 #ifdef CONFIG_COMPAT
182 /* Not used; we handle compat issues in arch_ptrace() directly. */
183 long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
184                                compat_ulong_t addr, compat_ulong_t data)
185 {
186         BUG();
187 }
188 #endif
189
190 void do_syscall_trace(void)
191 {
192         if (!test_thread_flag(TIF_SYSCALL_TRACE))
193                 return;
194
195         if (!(current->ptrace & PT_PTRACED))
196                 return;
197
198         /*
199          * The 0x80 provides a way for the tracing parent to distinguish
200          * between a syscall stop and SIGTRAP delivery
201          */
202         ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
203
204         /*
205          * this isn't the same as continuing with a signal, but it will do
206          * for normal use.  strace only continues with a signal if the
207          * stopping signal is not SIGTRAP.  -brl
208          */
209         if (current->exit_code) {
210                 send_sig(current->exit_code, current, 1);
211                 current->exit_code = 0;
212         }
213 }
214
215 void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
216 {
217         struct siginfo info;
218
219         memset(&info, 0, sizeof(info));
220         info.si_signo = SIGTRAP;
221         info.si_code  = TRAP_BRKPT;
222         info.si_addr  = (void __user *) regs->pc;
223
224         /* Send us the fakey SIGTRAP */
225         force_sig_info(SIGTRAP, &info, tsk);
226 }
227
228 /* Handle synthetic interrupt delivered only by the simulator. */
229 void __kprobes do_breakpoint(struct pt_regs* regs, int fault_num)
230 {
231         send_sigtrap(current, regs, fault_num);
232 }