import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / arch / mips / kernel / ptrace.c
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 1992 Ross Biro
7  * Copyright (C) Linus Torvalds
8  * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle
9  * Copyright (C) 1996 David S. Miller
10  * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
11  * Copyright (C) 1999 MIPS Technologies, Inc.
12  */
13 #include <linux/config.h>
14 #include <linux/kernel.h>
15 #include <linux/sched.h>
16 #include <linux/mm.h>
17 #include <linux/errno.h>
18 #include <linux/ptrace.h>
19 #include <linux/smp.h>
20 #include <linux/smp_lock.h>
21 #include <linux/user.h>
22
23 #include <asm/mipsregs.h>
24 #include <asm/pgtable.h>
25 #include <asm/page.h>
26 #include <asm/system.h>
27 #include <asm/uaccess.h>
28 #include <asm/bootinfo.h>
29 #include <asm/cpu.h>
30 #include <asm/fpu.h>
31
32 /*
33  * Called by kernel/ptrace.c when detaching..
34  *
35  * Make sure single step bits etc are not set.
36  */
37 void ptrace_disable(struct task_struct *child)
38 {
39         /* Nothing to do.. */
40 }
41
42 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
43 {
44         struct task_struct *child;
45         int ret;
46
47         lock_kernel();
48 #if 0
49         printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n",
50                (int) request, (int) pid, (unsigned long) addr,
51                (unsigned long) data);
52 #endif
53         if (request == PTRACE_TRACEME) {
54                 /* are we already being traced? */
55                 if (current->ptrace & PT_PTRACED) {
56                         ret = -EPERM;
57                         goto out;
58                 }
59                 /* set the ptrace bit in the process flags. */
60                 current->ptrace |= PT_PTRACED;
61                 ret = 0;
62                 goto out;
63         }
64         ret = -ESRCH;
65         read_lock(&tasklist_lock);
66         child = find_task_by_pid(pid);
67         if (child)
68                 get_task_struct(child);
69         read_unlock(&tasklist_lock);
70         if (!child)
71                 goto out;
72
73         ret = -EPERM;
74         if (pid == 1)           /* you may not mess with init */
75                 goto out_tsk;
76
77         if (request == PTRACE_ATTACH) {
78                 ret = ptrace_attach(child);
79                 goto out_tsk;
80         }
81
82         ret = ptrace_check_attach(child, request == PTRACE_KILL);
83         if (ret < 0)
84                 goto out_tsk;
85
86         switch (request) {
87         case PTRACE_PEEKTEXT: /* read word at location addr. */
88         case PTRACE_PEEKDATA: {
89                 unsigned long tmp;
90                 int copied;
91
92                 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
93                 ret = -EIO;
94                 if (copied != sizeof(tmp))
95                         break;
96                 ret = put_user(tmp,(unsigned long *) data);
97                 break;
98                 }
99
100         /* Read the word at location addr in the USER area.  */
101         case PTRACE_PEEKUSR: {
102                 struct pt_regs *regs;
103                 unsigned long tmp;
104
105                 regs = (struct pt_regs *) ((unsigned long) child +
106                        KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs));
107                 tmp = 0;  /* Default return value. */
108
109                 switch(addr) {
110                 case 0 ... 31:
111                         tmp = regs->regs[addr];
112                         break;
113                 case FPR_BASE ... FPR_BASE + 31:
114                         if (child->used_math) {
115                                 unsigned long long *fregs = get_fpu_regs(child);
116                                 /*
117                                  * The odd registers are actually the high
118                                  * order bits of the values stored in the even
119                                  * registers - unless we're using r2k_switch.S.
120                                  */
121                                 if (addr & 1)
122                                         tmp = (unsigned long) (fregs[((addr & ~1) - 32)] >> 32);
123                                 else
124                                         tmp = (unsigned long) (fregs[(addr - 32)] & 0xffffffff);
125                         } else {
126                                 tmp = -1;       /* FP not yet used  */
127                         }
128                         break;
129                 case PC:
130                         tmp = regs->cp0_epc;
131                         break;
132                 case CAUSE:
133                         tmp = regs->cp0_cause;
134                         break;
135                 case BADVADDR:
136                         tmp = regs->cp0_badvaddr;
137                         break;
138                 case MMHI:
139                         tmp = regs->hi;
140                         break;
141                 case MMLO:
142                         tmp = regs->lo;
143                         break;
144                 case FPC_CSR:
145                         if (!cpu_has_fpu)
146                                 tmp = child->thread.fpu.soft.sr;
147                         else
148                                 tmp = child->thread.fpu.hard.control;
149                         break;
150                 case FPC_EIR: { /* implementation / version register */
151                         unsigned long flags;
152
153                         if (!cpu_has_fpu)
154                                 break;
155
156                         __save_flags(flags);
157                         __enable_fpu();
158                         __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
159                         __restore_flags(flags);
160                         break;
161                 }
162                 default:
163                         tmp = 0;
164                         ret = -EIO;
165                         goto out_tsk;
166                 }
167                 ret = put_user(tmp, (unsigned long *) data);
168                 break;
169                 }
170
171         case PTRACE_POKETEXT: /* write the word at location addr. */
172         case PTRACE_POKEDATA:
173                 ret = 0;
174                 if (access_process_vm(child, addr, &data, sizeof(data), 1)
175                     == sizeof(data))
176                         break;
177                 ret = -EIO;
178                 break;
179
180         case PTRACE_POKEUSR: {
181                 struct pt_regs *regs;
182                 ret = 0;
183                 regs = (struct pt_regs *) ((unsigned long) child +
184                        KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs));
185
186                 switch (addr) {
187                 case 0 ... 31:
188                         regs->regs[addr] = data;
189                         break;
190                 case FPR_BASE ... FPR_BASE + 31: {
191                         unsigned long long *fregs;
192                         fregs = (unsigned long long *)get_fpu_regs(child);
193                         if (!child->used_math) {
194                                 /* FP not yet used  */
195                                 memset(&child->thread.fpu.hard, ~0,
196                                        sizeof(child->thread.fpu.hard));
197                                 child->thread.fpu.hard.control = 0;
198                         }
199                         /*
200                          * The odd registers are actually the high order bits
201                          * of the values stored in the even registers - unless
202                          * we're using r2k_switch.S.
203                          */
204                         if (addr & 1) {
205                                 fregs[(addr & ~1) - FPR_BASE] &= 0xffffffff;
206                                 fregs[(addr & ~1) - FPR_BASE] |= ((unsigned long long) data) << 32;
207                         } else {
208                                 fregs[addr - FPR_BASE] &= ~0xffffffffLL;
209                                 fregs[addr - FPR_BASE] |= data;
210                         }
211                         break;
212                 }
213                 case PC:
214                         regs->cp0_epc = data;
215                         break;
216                 case MMHI:
217                         regs->hi = data;
218                         break;
219                 case MMLO:
220                         regs->lo = data;
221                         break;
222                 case FPC_CSR:
223                         if (!cpu_has_fpu)
224                                 child->thread.fpu.soft.sr = data;
225                         else
226                                 child->thread.fpu.hard.control = data;
227                         break;
228                 default:
229                         /* The rest are not allowed. */
230                         ret = -EIO;
231                         break;
232                 }
233                 break;
234                 }
235
236         case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
237         case PTRACE_CONT: { /* restart after signal. */
238                 ret = -EIO;
239                 if ((unsigned long) data > _NSIG)
240                         break;
241                 if (request == PTRACE_SYSCALL)
242                         child->ptrace |= PT_TRACESYS;
243                 else
244                         child->ptrace &= ~PT_TRACESYS;
245                 child->exit_code = data;
246                 wake_up_process(child);
247                 ret = 0;
248                 break;
249                 }
250
251         /*
252          * make the child exit.  Best I can do is send it a sigkill.
253          * perhaps it should be put in the status that it wants to
254          * exit.
255          */
256         case PTRACE_KILL:
257                 ret = 0;
258                 if (child->state == TASK_ZOMBIE)        /* already dead */
259                         break;
260                 child->exit_code = SIGKILL;
261                 wake_up_process(child);
262                 break;
263
264         case PTRACE_DETACH: /* detach a process that was attached. */
265                 ret = ptrace_detach(child, data);
266                 break;
267
268         case PTRACE_SETOPTIONS:
269                 if (data & PTRACE_O_TRACESYSGOOD)
270                         child->ptrace |= PT_TRACESYSGOOD;
271                 else
272                         child->ptrace &= ~PT_TRACESYSGOOD;
273                 ret = 0;
274                 break;
275
276         default:
277                 ret = -EIO;
278                 break;
279         }
280 out_tsk:
281         free_task_struct(child);
282 out:
283         unlock_kernel();
284         return ret;
285 }
286
287 asmlinkage void syscall_trace(void)
288 {
289         if ((current->ptrace & (PT_PTRACED|PT_TRACESYS))
290                         != (PT_PTRACED|PT_TRACESYS))
291                 return;
292         /* The 0x80 provides a way for the tracing parent to distinguish
293            between a syscall stop and SIGTRAP delivery */
294         current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
295                                         ? 0x80 : 0);
296         current->state = TASK_STOPPED;
297         notify_parent(current, SIGCHLD);
298         schedule();
299         /*
300          * this isn't the same as continuing with a signal, but it will do
301          * for normal use.  strace only continues with a signal if the
302          * stopping signal is not SIGTRAP.  -brl
303          */
304         if (current->exit_code) {
305                 send_sig(current->exit_code, current, 1);
306                 current->exit_code = 0;
307         }
308 }