more changes on original files
[linux-2.4.git] / arch / ppc64 / kernel / ptrace.c
1 /*
2  *  linux/arch/ppc64/kernel/ptrace.c
3  *
4  *  PowerPC version
5  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
6  *
7  *  Derived from "arch/m68k/kernel/ptrace.c"
8  *  Copyright (C) 1994 by Hamish Macdonald
9  *  Taken from linux/kernel/ptrace.c and modified for M680x0.
10  *  linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
11  *
12  * Modified by Cort Dougan (cort@hq.fsmlabs.com)
13  * and Paul Mackerras (paulus@linuxcare.com.au).
14  *
15  * This file is subject to the terms and conditions of the GNU General
16  * Public License.  See the file README.legal in the main directory of
17  * this archive for more details.
18  */
19
20 #include <linux/kernel.h>
21 #include <linux/sched.h>
22 #include <linux/mm.h>
23 #include <linux/smp.h>
24 #include <linux/smp_lock.h>
25 #include <linux/errno.h>
26 #include <linux/ptrace.h>
27 #include <linux/user.h>
28
29 #include <asm/uaccess.h>
30 #include <asm/page.h>
31 #include <asm/pgtable.h>
32 #include <asm/system.h>
33
34 /*
35  * Set of msr bits that gdb can change on behalf of a process.
36  */
37 #define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1)
38
39 /*
40  * does not yet catch signals sent when the child dies.
41  * in exit.c or in signal.c.
42  */
43
44 /*
45  * Get contents of register REGNO in task TASK.
46  */
47 static inline unsigned long get_reg(struct task_struct *task, int regno)
48 {
49         if (regno < sizeof(struct pt_regs) / sizeof(unsigned long))
50                 return ((unsigned long *)task->thread.regs)[regno];
51         return (0);
52 }
53
54 /*
55  * Write contents of register REGNO in task TASK.
56  */
57 static inline int put_reg(struct task_struct *task, int regno,
58                           unsigned long data)
59 {
60         if (regno < PT_SOFTE) {
61                 if (regno == PT_MSR)
62                         data = (data & MSR_DEBUGCHANGE)
63                                 | (task->thread.regs->msr & ~MSR_DEBUGCHANGE);
64                 ((unsigned long *)task->thread.regs)[regno] = data;
65                 return 0;
66         }
67         return -EIO;
68 }
69
70 #ifdef CONFIG_ALTIVEC
71 /*
72  * Get contents of AltiVec register state in task TASK
73  */
74 static inline int get_vrregs(unsigned long data, struct task_struct *task)
75 {
76         return (copy_to_user((void *)data,&task->thread.vr[0],
77                         offsetof(struct thread_struct,vrsave[2])-
78                              offsetof(struct thread_struct,vr[0])) ? -EFAULT : 0 );
79 }
80
81 /*
82  * Write contents of AltiVec register state into task TASK.
83  */
84 static inline int set_vrregs(struct task_struct *task, unsigned long data)
85 {
86         return (copy_from_user(&task->thread.vr[0],(void *)data,
87                         offsetof(struct thread_struct,vrsave[2])-
88                              offsetof(struct thread_struct,vr[0])) ? -EFAULT : 0 );
89 }
90 #endif
91
92 static inline void
93 set_single_step(struct task_struct *task)
94 {
95         struct pt_regs *regs = task->thread.regs;
96         if (regs != NULL)
97                 regs->msr |= MSR_SE;
98 }
99
100 static inline void
101 clear_single_step(struct task_struct *task)
102 {
103         struct pt_regs *regs = task->thread.regs;
104         if (regs != NULL)
105                 regs->msr &= ~MSR_SE;
106 }
107
108 /*
109  * Called by kernel/ptrace.c when detaching..
110  *
111  * Make sure single step bits etc are not set.
112  */
113 void ptrace_disable(struct task_struct *child)
114 {
115         /* make sure the single step bit is not set. */
116         clear_single_step(child);
117 }
118
119 int sys_ptrace(long request, long pid, long addr, long data)
120 {
121         struct task_struct *child;
122         int ret = -EPERM;
123
124         lock_kernel();
125         if (request == PTRACE_TRACEME) {
126                 /* are we already being traced? */
127                 if (current->ptrace & PT_PTRACED)
128                         goto out;
129                 /* set the ptrace bit in the process flags. */
130                 current->ptrace |= PT_PTRACED;
131                 ret = 0;
132                 goto out;
133         }
134         ret = -ESRCH;
135         read_lock(&tasklist_lock);
136         child = find_task_by_pid(pid);
137         if (child)
138                 get_task_struct(child);
139         read_unlock(&tasklist_lock);
140         if (!child)
141                 goto out;
142
143         ret = -EPERM;
144         if (pid == 1)           /* you may not mess with init */
145                 goto out_tsk;
146
147         if (request == PTRACE_ATTACH) {
148                 ret = ptrace_attach(child);
149                 goto out_tsk;
150         }
151
152         ret = ptrace_check_attach(child, request == PTRACE_KILL);
153         if (ret < 0)
154                 goto out_tsk;
155
156         switch (request) {
157         /* when I and D space are separate, these will need to be fixed. */
158         case PTRACE_PEEKTEXT: /* read word at location addr. */ 
159         case PTRACE_PEEKDATA: {
160                 unsigned long tmp;
161                 int copied;
162
163                 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
164                 ret = -EIO;
165                 if (copied != sizeof(tmp))
166                         break;
167                 ret = put_user(tmp,(unsigned long *) data);
168                 break;
169         }
170
171         /* read the word at location addr in the USER area. */
172         case PTRACE_PEEKUSR: {
173                 unsigned long index;
174                 unsigned long tmp;
175
176                 ret = -EIO;
177                 /* convert to index and check */
178                 index = (unsigned long) addr >> 3;
179                 if ((addr & 7) || (index > PT_FPSCR))
180                         break;
181
182                 if (index < PT_FPR0) {
183                         tmp = get_reg(child, (int)index);
184                 } else {
185                         if (child->thread.regs->msr & MSR_FP)
186                                 giveup_fpu(child);
187                         tmp = ((unsigned long *)child->thread.fpr)[index - PT_FPR0];
188                 }
189                 ret = put_user(tmp,(unsigned long *) data);
190                 break;
191         }
192
193         /* If I and D space are separate, this will have to be fixed. */
194         case PTRACE_POKETEXT: /* write the word at location addr. */
195         case PTRACE_POKEDATA:
196                 ret = 0;
197                 if (access_process_vm(child, addr, &data, sizeof(data), 1)
198                                 == sizeof(data))
199                         break;
200                 ret = -EIO;
201                 break;
202
203         /* write the word at location addr in the USER area */
204         case PTRACE_POKEUSR: {
205                 unsigned long index;
206
207                 ret = -EIO;
208                 /* convert to index and check */
209                 index = (unsigned long) addr >> 3;
210                 if ((addr & 7) || (index > PT_FPSCR))
211                         break;
212
213                 if (index == PT_ORIG_R3)
214                         break;
215                 if (index < PT_FPR0) {
216                         ret = put_reg(child, index, data);
217                 } else {
218                         if (child->thread.regs->msr & MSR_FP)
219                                 giveup_fpu(child);
220                         ((unsigned long *)child->thread.fpr)[index - PT_FPR0] = data;
221                         ret = 0;
222                 }
223                 break;
224         }
225
226         case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
227         case PTRACE_CONT: { /* restart after signal. */
228                 ret = -EIO;
229                 if ((unsigned long) data > _NSIG)
230                         break;
231                 if (request == PTRACE_SYSCALL)
232                         child->ptrace |= PT_TRACESYS;
233                 else
234                         child->ptrace &= ~PT_TRACESYS;
235                 child->exit_code = data;
236                 /* make sure the single step bit is not set. */
237                 clear_single_step(child);
238                 wake_up_process(child);
239                 ret = 0;
240                 break;
241         }
242
243         /*
244          * make the child exit.  Best I can do is send it a sigkill.
245          * perhaps it should be put in the status that it wants to
246          * exit.
247          */
248         case PTRACE_KILL: {
249                 ret = 0;
250                 if (child->state == TASK_ZOMBIE)        /* already dead */
251                         break;
252                 child->exit_code = SIGKILL;
253                 /* make sure the single step bit is not set. */
254                 clear_single_step(child);
255                 wake_up_process(child);
256                 break;
257         }
258
259         case PTRACE_SINGLESTEP: {  /* set the trap flag. */
260                 ret = -EIO;
261                 if ((unsigned long) data > _NSIG)
262                         break;
263                 child->ptrace &= ~PT_TRACESYS;
264                 set_single_step(child);
265                 child->exit_code = data;
266                 /* give it a chance to run. */
267                 wake_up_process(child);
268                 ret = 0;
269                 break;
270         }
271
272         case PTRACE_DETACH:
273                 ret = ptrace_detach(child, data);
274                 break;
275
276         case PPC_PTRACE_GETREGS: { /* Get GPRs 0 - 31. */
277                 int i;
278                 unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
279                 unsigned long *tmp = (unsigned long *)addr;
280
281                 for (i = 0; i < 32; i++) {
282                         ret = put_user(*reg, tmp);
283                         if (ret)
284                                 break;
285                         reg++;
286                         tmp++;
287                 }
288                 break;
289         }
290
291         case PPC_PTRACE_SETREGS: { /* Set GPRs 0 - 31. */
292                 int i;
293                 unsigned long *reg = &((unsigned long *)child->thread.regs)[0];
294                 unsigned long *tmp = (unsigned long *)addr;
295
296                 for (i = 0; i < 32; i++) {
297                         ret = get_user(*reg, tmp);
298                         if (ret)
299                                 break;
300                         reg++;
301                         tmp++;
302                 }
303                 break;
304         }
305
306         case PPC_PTRACE_GETFPREGS: { /* Get FPRs 0 - 31. */
307                 int i;
308                 unsigned long *reg = &((unsigned long *)child->thread.fpr)[0];
309                 unsigned long *tmp = (unsigned long *)addr;
310
311                 if (child->thread.regs->msr & MSR_FP)
312                         giveup_fpu(child);
313
314                 for (i = 0; i < 32; i++) {
315                         ret = put_user(*reg, tmp);
316                         if (ret)
317                                 break;
318                         reg++;
319                         tmp++;
320                 }
321                 break;
322         }
323
324         case PPC_PTRACE_SETFPREGS: { /* Get FPRs 0 - 31. */
325                 int i;
326                 unsigned long *reg = &((unsigned long *)child->thread.fpr)[0];
327                 unsigned long *tmp = (unsigned long *)addr;
328
329                 if (child->thread.regs->msr & MSR_FP)
330                         giveup_fpu(child);
331
332                 for (i = 0; i < 32; i++) {
333                         ret = get_user(*reg, tmp);
334                         if (ret)
335                                 break;
336                         reg++;
337                         tmp++;
338                 }
339                 break;
340         }
341 #ifdef CONFIG_ALTIVEC
342         case PTRACE_GETVRREGS:
343                 /* Get the child altivec register state. */
344                 if (child->thread.regs->msr & MSR_VEC)
345                         giveup_altivec(child);
346                 ret = get_vrregs(data, child);
347                 break;
348
349         case PTRACE_SETVRREGS:
350                 /* Set the child altivec register state. */
351                 /* this is to clear the MSR_VEC bit to force a reload
352                  * of register state from memory */
353                 if (child->thread.regs->msr & MSR_VEC)
354                         giveup_altivec(child);
355                 ret = set_vrregs(child,data);
356                 break;
357 #endif
358
359         default:
360                 ret = -EIO;
361                 break;
362         }
363 out_tsk:
364         free_task_struct(child);
365 out:
366         unlock_kernel();
367         return ret;
368 }
369
370 void syscall_trace(void)
371 {
372   if ((current->ptrace & (PT_PTRACED|PT_TRACESYS))
373                         != (PT_PTRACED|PT_TRACESYS))
374                 return;
375         current->exit_code = SIGTRAP;
376         current->state = TASK_STOPPED;
377         notify_parent(current, SIGCHLD);
378         schedule();
379         /*
380          * this isn't the same as continuing with a signal, but it will do
381          * for normal use.  strace only continues with a signal if the
382          * stopping signal is not SIGTRAP.  -brl
383          */
384         if (current->exit_code) {
385                 send_sig(current->exit_code, current, 1);
386                 current->exit_code = 0;
387         }
388 }