Merge master.kernel.org:/pub/scm/linux/kernel/git/lethal/sh-2.6
[linux-2.6] / arch / sh / kernel / ptrace.c
1 /*
2  * linux/arch/sh/kernel/ptrace.c
3  *
4  * Original x86 implementation:
5  *      By Ross Biro 1/23/92
6  *      edited by Linus Torvalds
7  *
8  * SuperH version:   Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
9  *
10  */
11
12 #include <linux/kernel.h>
13 #include <linux/sched.h>
14 #include <linux/mm.h>
15 #include <linux/smp.h>
16 #include <linux/smp_lock.h>
17 #include <linux/errno.h>
18 #include <linux/ptrace.h>
19 #include <linux/user.h>
20 #include <linux/slab.h>
21 #include <linux/security.h>
22 #include <linux/signal.h>
23
24 #include <asm/io.h>
25 #include <asm/uaccess.h>
26 #include <asm/pgtable.h>
27 #include <asm/system.h>
28 #include <asm/processor.h>
29 #include <asm/mmu_context.h>
30
31 /*
32  * does not yet catch signals sent when the child dies.
33  * in exit.c or in signal.c.
34  */
35
36 /*
37  * This routine will get a word off of the process kernel stack.
38  */
39 static inline int get_stack_long(struct task_struct *task, int offset)
40 {
41         unsigned char *stack;
42
43         stack = (unsigned char *)task_pt_regs(task);
44         stack += offset;
45         return (*((int *)stack));
46 }
47
48 /*
49  * This routine will put a word on the process kernel stack.
50  */
51 static inline int put_stack_long(struct task_struct *task, int offset,
52                                  unsigned long data)
53 {
54         unsigned char *stack;
55
56         stack = (unsigned char *)task_pt_regs(task);
57         stack += offset;
58         *(unsigned long *) stack = data;
59         return 0;
60 }
61
62 /*
63  * Called by kernel/ptrace.c when detaching..
64  *
65  * Make sure single step bits etc are not set.
66  */
67 void ptrace_disable(struct task_struct *child)
68 {
69         /* nothing to do.. */
70 }
71
72 long arch_ptrace(struct task_struct *child, long request, long addr, long data)
73 {
74         struct user * dummy = NULL;
75         int ret;
76
77         switch (request) {
78         /* when I and D space are separate, these will need to be fixed. */
79         case PTRACE_PEEKTEXT: /* read word at location addr. */ 
80         case PTRACE_PEEKDATA: {
81                 unsigned long tmp;
82                 int copied;
83
84                 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
85                 ret = -EIO;
86                 if (copied != sizeof(tmp))
87                         break;
88                 ret = put_user(tmp,(unsigned long *) data);
89                 break;
90         }
91
92         /* read the word at location addr in the USER area. */
93         case PTRACE_PEEKUSR: {
94                 unsigned long tmp;
95
96                 ret = -EIO;
97                 if ((addr & 3) || addr < 0 || 
98                     addr > sizeof(struct user) - 3)
99                         break;
100
101                 if (addr < sizeof(struct pt_regs))
102                         tmp = get_stack_long(child, addr);
103                 else if (addr >= (long) &dummy->fpu &&
104                          addr < (long) &dummy->u_fpvalid) {
105                         if (!tsk_used_math(child)) {
106                                 if (addr == (long)&dummy->fpu.fpscr)
107                                         tmp = FPSCR_INIT;
108                                 else
109                                         tmp = 0;
110                         } else
111                                 tmp = ((long *)&child->thread.fpu)
112                                         [(addr - (long)&dummy->fpu) >> 2];
113                 } else if (addr == (long) &dummy->u_fpvalid)
114                         tmp = !!tsk_used_math(child);
115                 else
116                         tmp = 0;
117                 ret = put_user(tmp, (unsigned long *)data);
118                 break;
119         }
120
121         /* when I and D space are separate, this will have to be fixed. */
122         case PTRACE_POKETEXT: /* write the word at location addr. */
123         case PTRACE_POKEDATA:
124                 ret = 0;
125                 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
126                         break;
127                 ret = -EIO;
128                 break;
129
130         case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
131                 ret = -EIO;
132                 if ((addr & 3) || addr < 0 || 
133                     addr > sizeof(struct user) - 3)
134                         break;
135
136                 if (addr < sizeof(struct pt_regs))
137                         ret = put_stack_long(child, addr, data);
138                 else if (addr >= (long) &dummy->fpu &&
139                          addr < (long) &dummy->u_fpvalid) {
140                         set_stopped_child_used_math(child);
141                         ((long *)&child->thread.fpu)
142                                 [(addr - (long)&dummy->fpu) >> 2] = data;
143                         ret = 0;
144                 } else if (addr == (long) &dummy->u_fpvalid) {
145                         conditional_stopped_child_used_math(data, child);
146                         ret = 0;
147                 }
148                 break;
149
150         case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
151         case PTRACE_CONT: { /* restart after signal. */
152                 ret = -EIO;
153                 if (!valid_signal(data))
154                         break;
155                 if (request == PTRACE_SYSCALL)
156                         set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
157                 else
158                         clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
159                 child->exit_code = data;
160                 wake_up_process(child);
161                 ret = 0;
162                 break;
163         }
164
165 /*
166  * make the child exit.  Best I can do is send it a sigkill. 
167  * perhaps it should be put in the status that it wants to 
168  * exit.
169  */
170         case PTRACE_KILL: {
171                 ret = 0;
172                 if (child->exit_state == EXIT_ZOMBIE)   /* already dead */
173                         break;
174                 child->exit_code = SIGKILL;
175                 wake_up_process(child);
176                 break;
177         }
178
179         case PTRACE_SINGLESTEP: {  /* set the trap flag. */
180                 long pc;
181                 struct pt_regs *dummy = NULL;
182
183                 ret = -EIO;
184                 if (!valid_signal(data))
185                         break;
186                 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
187                 if ((child->ptrace & PT_DTRACE) == 0) {
188                         /* Spurious delayed TF traps may occur */
189                         child->ptrace |= PT_DTRACE;
190                 }
191
192                 pc = get_stack_long(child, (long)&dummy->pc);
193
194                 /* Next scheduling will set up UBC */
195                 if (child->thread.ubc_pc == 0)
196                         ubc_usercnt += 1;
197                 child->thread.ubc_pc = pc;
198
199                 child->exit_code = data;
200                 /* give it a chance to run. */
201                 wake_up_process(child);
202                 ret = 0;
203                 break;
204         }
205
206         case PTRACE_DETACH: /* detach a process that was attached. */
207                 ret = ptrace_detach(child, data);
208                 break;
209
210 #ifdef CONFIG_SH_DSP
211         case PTRACE_GETDSPREGS: {
212                 unsigned long dp;
213
214                 ret = -EIO;
215                 dp = ((unsigned long) child) + THREAD_SIZE -
216                          sizeof(struct pt_dspregs);
217                 if (*((int *) (dp - 4)) == SR_FD) {
218                         copy_to_user(addr, (void *) dp,
219                                 sizeof(struct pt_dspregs));
220                         ret = 0;
221                 }
222                 break;
223         }
224
225         case PTRACE_SETDSPREGS: {
226                 unsigned long dp;
227
228                 ret = -EIO;
229                 dp = ((unsigned long) child) + THREAD_SIZE -
230                          sizeof(struct pt_dspregs);
231                 if (*((int *) (dp - 4)) == SR_FD) {
232                         copy_from_user((void *) dp, addr,
233                                 sizeof(struct pt_dspregs));
234                         ret = 0;
235                 }
236                 break;
237         }
238 #endif
239         default:
240                 ret = ptrace_request(child, request, addr, data);
241                 break;
242         }
243
244         return ret;
245 }
246
247 asmlinkage void do_syscall_trace(void)
248 {
249         struct task_struct *tsk = current;
250
251         if (!test_thread_flag(TIF_SYSCALL_TRACE))
252                 return;
253         if (!(tsk->ptrace & PT_PTRACED))
254                 return;
255         /* the 0x80 provides a way for the tracing parent to distinguish
256            between a syscall stop and SIGTRAP delivery */
257         ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
258                                  ? 0x80 : 0));
259
260         /*
261          * this isn't the same as continuing with a signal, but it will do
262          * for normal use.  strace only continues with a signal if the
263          * stopping signal is not SIGTRAP.  -brl
264          */
265         if (tsk->exit_code) {
266                 send_sig(tsk->exit_code, tsk, 1);
267                 tsk->exit_code = 0;
268         }
269 }