Linux-2.6.12-rc2
[linux-flexiantxendom0-natty.git] / arch / mips / kernel / ptrace32.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  * Copyright (C) 2000 Ulf Carlsson
13  *
14  * At this time Linux/MIPS64 only supports syscall tracing, even for 32-bit
15  * binaries.
16  */
17 #include <linux/compiler.h>
18 #include <linux/kernel.h>
19 #include <linux/sched.h>
20 #include <linux/mm.h>
21 #include <linux/errno.h>
22 #include <linux/ptrace.h>
23 #include <linux/smp.h>
24 #include <linux/smp_lock.h>
25 #include <linux/user.h>
26 #include <linux/security.h>
27
28 #include <asm/cpu.h>
29 #include <asm/fpu.h>
30 #include <asm/mipsregs.h>
31 #include <asm/pgtable.h>
32 #include <asm/page.h>
33 #include <asm/system.h>
34 #include <asm/uaccess.h>
35 #include <asm/bootinfo.h>
36
37 /*
38  * Tracing a 32-bit process with a 64-bit strace and vice versa will not
39  * work.  I don't know how to fix this.
40  */
41 asmlinkage int sys32_ptrace(int request, int pid, int addr, int data)
42 {
43         struct task_struct *child;
44         int ret;
45
46 #if 0
47         printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n",
48                (int) request, (int) pid, (unsigned long) addr,
49                (unsigned long) data);
50 #endif
51         lock_kernel();
52         ret = -EPERM;
53         if (request == PTRACE_TRACEME) {
54                 /* are we already being traced? */
55                 if (current->ptrace & PT_PTRACED)
56                         goto out;
57                 if ((ret = security_ptrace(current->parent, current)))
58                         goto out;
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         /* when I and D space are separate, these will need to be fixed. */
88         case PTRACE_PEEKTEXT: /* read word at location addr. */
89         case PTRACE_PEEKDATA: {
90                 unsigned int tmp;
91                 int copied;
92
93                 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
94                 ret = -EIO;
95                 if (copied != sizeof(tmp))
96                         break;
97                 ret = put_user(tmp, (unsigned int *) (unsigned long) data);
98                 break;
99         }
100
101         /* Read the word at location addr in the USER area. */
102         case PTRACE_PEEKUSR: {
103                 struct pt_regs *regs;
104                 unsigned int tmp;
105
106                 regs = (struct pt_regs *) ((unsigned long) child->thread_info +
107                        THREAD_SIZE - 32 - sizeof(struct pt_regs));
108                 ret = 0;  /* Default return value. */
109
110                 switch (addr) {
111                 case 0 ... 31:
112                         tmp = regs->regs[addr];
113                         break;
114                 case FPR_BASE ... FPR_BASE + 31:
115                         if (tsk_used_math(child)) {
116                                 fpureg_t *fregs = get_fpu_regs(child);
117
118                                 /*
119                                  * The odd registers are actually the high
120                                  * order bits of the values stored in the even
121                                  * registers - unless we're using r2k_switch.S.
122                                  */
123                                 if (addr & 1)
124                                         tmp = (unsigned long) (fregs[((addr & ~1) - 32)] >> 32);
125                                 else
126                                         tmp = (unsigned long) (fregs[(addr - 32)] & 0xffffffff);
127                         } else {
128                                 tmp = -1;       /* FP not yet used  */
129                         }
130                         break;
131                 case PC:
132                         tmp = regs->cp0_epc;
133                         break;
134                 case CAUSE:
135                         tmp = regs->cp0_cause;
136                         break;
137                 case BADVADDR:
138                         tmp = regs->cp0_badvaddr;
139                         break;
140                 case MMHI:
141                         tmp = regs->hi;
142                         break;
143                 case MMLO:
144                         tmp = regs->lo;
145                         break;
146                 case FPC_CSR:
147                         if (cpu_has_fpu)
148                                 tmp = child->thread.fpu.hard.fcr31;
149                         else
150                                 tmp = child->thread.fpu.soft.fcr31;
151                         break;
152                 case FPC_EIR: { /* implementation / version register */
153                         unsigned int flags;
154
155                         if (!cpu_has_fpu)
156                                 break;
157
158                         flags = read_c0_status();
159                         __enable_fpu();
160                         __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
161                         write_c0_status(flags);
162                         break;
163                 }
164                 default:
165                         tmp = 0;
166                         ret = -EIO;
167                         goto out_tsk;
168                 }
169                 ret = put_user(tmp, (unsigned *) (unsigned long) data);
170                 break;
171         }
172
173         /* when I and D space are separate, this will have to be fixed. */
174         case PTRACE_POKETEXT: /* write the word at location addr. */
175         case PTRACE_POKEDATA:
176                 ret = 0;
177                 if (access_process_vm(child, addr, &data, sizeof(data), 1)
178                     == sizeof(data))
179                         break;
180                 ret = -EIO;
181                 break;
182
183         case PTRACE_POKEUSR: {
184                 struct pt_regs *regs;
185                 ret = 0;
186                 regs = (struct pt_regs *) ((unsigned long) child->thread_info +
187                        THREAD_SIZE - 32 - sizeof(struct pt_regs));
188
189                 switch (addr) {
190                 case 0 ... 31:
191                         regs->regs[addr] = data;
192                         break;
193                 case FPR_BASE ... FPR_BASE + 31: {
194                         fpureg_t *fregs = get_fpu_regs(child);
195
196                         if (!tsk_used_math(child)) {
197                                 /* FP not yet used  */
198                                 memset(&child->thread.fpu.hard, ~0,
199                                        sizeof(child->thread.fpu.hard));
200                                 child->thread.fpu.hard.fcr31 = 0;
201                         }
202                         /*
203                          * The odd registers are actually the high order bits
204                          * of the values stored in the even registers - unless
205                          * we're using r2k_switch.S.
206                          */
207                         if (addr & 1) {
208                                 fregs[(addr & ~1) - FPR_BASE] &= 0xffffffff;
209                                 fregs[(addr & ~1) - FPR_BASE] |= ((unsigned long long) data) << 32;
210                         } else {
211                                 fregs[addr - FPR_BASE] &= ~0xffffffffLL;
212                                 /* Must cast, lest sign extension fill upper
213                                    bits!  */
214                                 fregs[addr - FPR_BASE] |= (unsigned int)data;
215                         }
216                         break;
217                 }
218                 case PC:
219                         regs->cp0_epc = data;
220                         break;
221                 case MMHI:
222                         regs->hi = data;
223                         break;
224                 case MMLO:
225                         regs->lo = data;
226                         break;
227                 case FPC_CSR:
228                         if (cpu_has_fpu)
229                                 child->thread.fpu.hard.fcr31 = data;
230                         else
231                                 child->thread.fpu.soft.fcr31 = data;
232                         break;
233                 default:
234                         /* The rest are not allowed. */
235                         ret = -EIO;
236                         break;
237                 }
238                 break;
239                 }
240
241         case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
242         case PTRACE_CONT: { /* restart after signal. */
243                 ret = -EIO;
244                 if ((unsigned int) data > _NSIG)
245                         break;
246                 if (request == PTRACE_SYSCALL) {
247                         set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
248                 }
249                 else {
250                         clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
251                 }
252                 child->exit_code = data;
253                 wake_up_process(child);
254                 ret = 0;
255                 break;
256         }
257
258         /*
259          * make the child exit.  Best I can do is send it a sigkill.
260          * perhaps it should be put in the status that it wants to
261          * exit.
262          */
263         case PTRACE_KILL:
264                 ret = 0;
265                 if (child->exit_state == EXIT_ZOMBIE)   /* already dead */
266                         break;
267                 child->exit_code = SIGKILL;
268                 wake_up_process(child);
269                 break;
270
271         case PTRACE_DETACH: /* detach a process that was attached. */
272                 ret = ptrace_detach(child, data);
273                 break;
274
275         default:
276                 ret = ptrace_request(child, request, addr, data);
277                 break;
278         }
279
280 out_tsk:
281         put_task_struct(child);
282 out:
283         unlock_kernel();
284         return ret;
285 }