File: /usr/src/linux/arch/cris/kernel/ptrace.c
1 /*
2 * linux/arch/cris/kernel/ptrace.c
3 *
4 * Parts taken from the m68k port.
5 *
6 * Copyright (c) 2000, 2001 Axis Communications AB
7 *
8 * Authors: Bjorn Wesen
9 *
10 * $Log: ptrace.c,v $
11 * Revision 1.6 2001/07/25 16:08:47 bjornw
12 * PTRACE_ATTACH bulk moved into arch-independant code in 2.4.7
13 *
14 * Revision 1.5 2001/03/26 14:24:28 orjanf
15 * * Changed loop condition.
16 * * Added comment documenting non-standard ptrace behaviour.
17 *
18 * Revision 1.4 2001/03/20 19:44:41 bjornw
19 * Use the user_regs macro instead of thread.esp0
20 *
21 * Revision 1.3 2000/12/18 23:45:25 bjornw
22 * Linux/CRIS first version
23 *
24 *
25 */
26
27 #include <linux/kernel.h>
28 #include <linux/sched.h>
29 #include <linux/mm.h>
30 #include <linux/smp.h>
31 #include <linux/smp_lock.h>
32 #include <linux/errno.h>
33 #include <linux/ptrace.h>
34 #include <linux/user.h>
35
36 #include <asm/uaccess.h>
37 #include <asm/page.h>
38 #include <asm/pgtable.h>
39 #include <asm/system.h>
40 #include <asm/processor.h>
41
42 /*
43 * does not yet catch signals sent when the child dies.
44 * in exit.c or in signal.c.
45 */
46
47 /* determines which bits in DCCR the user has access to. */
48 /* 1 = access 0 = no access */
49 #define DCCR_MASK 0x0000001f /* XNZVC */
50
51 /*
52 * Get contents of register REGNO in task TASK.
53 */
54 static inline long get_reg(struct task_struct *task, unsigned int regno)
55 {
56 /* USP is a special case, it's not in the pt_regs struct but
57 * in the tasks thread struct
58 */
59
60 if (regno == PT_USP)
61 return task->thread.usp;
62 else if (regno < PT_MAX)
63 return ((unsigned long *)user_regs(task))[regno];
64 else
65 return 0;
66 }
67
68 /*
69 * Write contents of register REGNO in task TASK.
70 */
71 static inline int put_reg(struct task_struct *task, unsigned int regno,
72 unsigned long data)
73 {
74 unsigned long *addr;
75
76 if (regno == PT_USP)
77 task->thread.usp = data;
78 else if (regno < PT_MAX)
79 ((unsigned long *)user_regs(task))[regno] = data;
80 else
81 return -1;
82 return 0;
83 }
84
85 /*
86 * Called by kernel/ptrace.c when detaching..
87 *
88 * Make sure the single step bit is not set.
89 */
90 void ptrace_disable(struct task_struct *child)
91 {
92 /* Todo - pending singlesteps? */
93 }
94
95 /* Note that this implementation of ptrace behaves differently from vanilla
96 * ptrace. Contrary to what the man page says, in the PTRACE_PEEKTEXT,
97 * PTRACE_PEEKDATA, and PTRACE_PEEKUSER requests the data variable is not
98 * ignored. Instead, the data variable is expected to point at a location
99 * (in user space) where the result of the ptrace call is written (instead of
100 * being returned).
101 */
102
103 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
104 {
105 struct task_struct *child;
106 int ret;
107
108 lock_kernel();
109 ret = -EPERM;
110 if (request == PTRACE_TRACEME) {
111 /* are we already being traced? */
112 if (current->ptrace & PT_PTRACED)
113 goto out;
114 /* set the ptrace bit in the process flags. */
115 current->ptrace |= PT_PTRACED;
116 ret = 0;
117 goto out;
118 }
119 ret = -ESRCH;
120 read_lock(&tasklist_lock);
121 child = find_task_by_pid(pid);
122 if (child)
123 get_task_struct(child);
124 read_unlock(&tasklist_lock);
125 if (!child)
126 goto out;
127 ret = -EPERM;
128 if (pid == 1) /* you may not mess with init */
129 goto out_tsk;
130 if (request == PTRACE_ATTACH) {
131 ret = ptrace_attach(child);
132 goto out_tsk;
133 }
134 ret = -ESRCH;
135 if (!(child->ptrace & PT_PTRACED))
136 goto out_tsk;
137 if (child->state != TASK_STOPPED) {
138 if (request != PTRACE_KILL)
139 goto out_tsk;
140 }
141 if (child->p_pptr != current)
142 goto out_tsk;
143
144 switch (request) {
145 /* when I and D space are separate, these will need to be fixed. */
146 case PTRACE_PEEKTEXT: /* read word at location addr. */
147 case PTRACE_PEEKDATA: {
148 unsigned long tmp;
149 int copied;
150
151 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
152 ret = -EIO;
153 if (copied != sizeof(tmp))
154 break;
155 ret = put_user(tmp,(unsigned long *) data);
156 break;
157 }
158
159 /* read the word at location addr in the USER area. */
160 case PTRACE_PEEKUSR: {
161 unsigned long tmp;
162
163 ret = -EIO;
164 if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
165 break;
166
167 tmp = 0; /* Default return condition */
168 ret = -EIO;
169 if (addr < sizeof(struct pt_regs)) {
170 tmp = get_reg(child, addr >> 2);
171 ret = put_user(tmp, (unsigned long *)data);
172 }
173 break;
174 }
175
176 /* when I and D space are separate, this will have to be fixed. */
177 case PTRACE_POKETEXT: /* write the word at location addr. */
178 case PTRACE_POKEDATA:
179 ret = 0;
180 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
181 break;
182 ret = -EIO;
183 break;
184
185 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
186 ret = -EIO;
187 if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
188 break;
189
190 if (addr < sizeof(struct pt_regs)) {
191 addr >>= 2;
192
193 if (addr == PT_DCCR) {
194 /* don't allow the tracing process to change stuff like
195 * interrupt enable, kernel/user bit, dma enables etc.
196 */
197 data &= DCCR_MASK;
198 data |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
199 }
200 if (put_reg(child, addr, data))
201 break;
202 ret = 0;
203 }
204 break;
205
206 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
207 case PTRACE_CONT: { /* restart after signal. */
208 long tmp;
209
210 ret = -EIO;
211 if ((unsigned long) data > _NSIG)
212 break;
213 if (request == PTRACE_SYSCALL)
214 child->ptrace |= PT_TRACESYS;
215 else
216 child->ptrace &= ~PT_TRACESYS;
217 child->exit_code = data;
218 /* TODO: make sure any pending breakpoint is killed */
219 wake_up_process(child);
220 ret = 0;
221 break;
222 }
223
224 /*
225 * make the child exit. Best I can do is send it a sigkill.
226 * perhaps it should be put in the status that it wants to
227 * exit.
228 */
229 case PTRACE_KILL: {
230 long tmp;
231
232 ret = 0;
233 if (child->state == TASK_ZOMBIE) /* already dead */
234 break;
235 child->exit_code = SIGKILL;
236 /* TODO: make sure any pending breakpoint is killed */
237 wake_up_process(child);
238 break;
239 }
240
241 case PTRACE_SINGLESTEP: { /* set the trap flag. */
242 long tmp;
243
244 ret = -EIO;
245 if ((unsigned long) data > _NSIG)
246 break;
247 child->ptrace &= ~PT_TRACESYS;
248
249 /* TODO: set some clever breakpoint mechanism... */
250
251 child->exit_code = data;
252 /* give it a chance to run. */
253 wake_up_process(child);
254 ret = 0;
255 break;
256 }
257
258 case PTRACE_DETACH:
259 ret = ptrace_detach(child, data);
260 break;
261
262 case PTRACE_GETREGS: { /* Get all gp regs from the child. */
263 int i;
264 unsigned long tmp;
265 for (i = 0; i <= PT_MAX; i++) {
266 tmp = get_reg(child, i);
267 if (put_user(tmp, (unsigned long *) data)) {
268 ret = -EFAULT;
269 break;
270 }
271 data += sizeof(long);
272 }
273 ret = 0;
274 break;
275 }
276
277 case PTRACE_SETREGS: { /* Set all gp regs in the child. */
278 int i;
279 unsigned long tmp;
280 for (i = 0; i <= PT_MAX; i++) {
281 if (get_user(tmp, (unsigned long *) data)) {
282 ret = -EFAULT;
283 break;
284 }
285 if (i == PT_DCCR) {
286 tmp &= DCCR_MASK;
287 tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
288 }
289 put_reg(child, i, tmp);
290 data += sizeof(long);
291 }
292 ret = 0;
293 break;
294 }
295
296 default:
297 ret = -EIO;
298 break;
299 }
300 out_tsk:
301 free_task_struct(child);
302 out:
303 unlock_kernel();
304 return ret;
305 }
306
307 asmlinkage void syscall_trace(void)
308 {
309 if ((current->ptrace & (PT_PTRACED | PT_TRACESYS)) !=
310 (PT_PTRACED | PT_TRACESYS))
311 return;
312 /* TODO: make a way to distinguish between a syscall stop and SIGTRAP
313 * delivery like in the i386 port ?
314 */
315 current->exit_code = SIGTRAP;
316 current->state = TASK_STOPPED;
317 notify_parent(current, SIGCHLD);
318 schedule();
319 /*
320 * this isn't the same as continuing with a signal, but it will do
321 * for normal use. strace only continues with a signal if the
322 * stopping signal is not SIGTRAP. -brl
323 */
324 if (current->exit_code) {
325 send_sig(current->exit_code, current, 1);
326 current->exit_code = 0;
327 }
328 }
329