File: /usr/src/linux/arch/s390/kernel/ptrace.c
1 /*
2 * arch/s390/kernel/ptrace.c
3 *
4 * S390 version
5 * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
6 * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
7 *
8 * Based on PowerPC version
9 * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
10 *
11 * Derived from "arch/m68k/kernel/ptrace.c"
12 * Copyright (C) 1994 by Hamish Macdonald
13 * Taken from linux/kernel/ptrace.c and modified for M680x0.
14 * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
15 *
16 * Modified by Cort Dougan (cort@cs.nmt.edu)
17 *
18 *
19 * This file is subject to the terms and conditions of the GNU General
20 * Public License. See the file README.legal in the main directory of
21 * this archive for more details.
22 */
23
24 #include <stddef.h>
25 #include <linux/kernel.h>
26 #include <linux/sched.h>
27 #include <linux/mm.h>
28 #include <linux/smp.h>
29 #include <linux/smp_lock.h>
30 #include <linux/errno.h>
31 #include <linux/ptrace.h>
32 #include <linux/user.h>
33
34 #include <asm/segment.h>
35 #include <asm/page.h>
36 #include <asm/pgtable.h>
37 #include <asm/pgalloc.h>
38 #include <asm/system.h>
39 #include <asm/uaccess.h>
40
41
42 void FixPerRegisters(struct task_struct *task)
43 {
44 struct pt_regs *regs = task->thread.regs;
45 per_struct *per_info=
46 (per_struct *)&task->thread.per_info;
47
48 per_info->control_regs.bits.em_instruction_fetch=
49 per_info->single_step|per_info->instruction_fetch;
50
51 if(per_info->single_step)
52 {
53 per_info->control_regs.bits.starting_addr=0;
54 per_info->control_regs.bits.ending_addr=0x7fffffffUL;
55 }
56 else
57 {
58 per_info->control_regs.bits.starting_addr=
59 per_info->starting_addr;
60 per_info->control_regs.bits.ending_addr=
61 per_info->ending_addr;
62 }
63 /* if any of the control reg tracing bits are on
64 we switch on per in the psw */
65 if(per_info->control_regs.words.cr[0]&PER_EM_MASK)
66 regs->psw.mask |=PSW_PER_MASK;
67 else
68 regs->psw.mask &= ~PSW_PER_MASK;
69 if (per_info->control_regs.bits.em_storage_alteration)
70 {
71 per_info->control_regs.bits.storage_alt_space_ctl=1;
72 //((pgd_t *)__pa(task->mm->pgd))->pgd |= USER_STD_MASK;
73 }
74 else
75 {
76 per_info->control_regs.bits.storage_alt_space_ctl=0;
77 //((pgd_t *)__pa(task->mm->pgd))->pgd &= ~USER_STD_MASK;
78 }
79 }
80
81 void set_single_step(struct task_struct *task)
82 {
83 per_struct *per_info=
84 (per_struct *)&task->thread.per_info;
85
86 per_info->single_step=1; /* Single step */
87 FixPerRegisters(task);
88 }
89
90 void clear_single_step(struct task_struct *task)
91 {
92 per_struct *per_info=
93 (per_struct *)&task->thread.per_info;
94
95 per_info->single_step=0;
96 FixPerRegisters(task);
97 }
98
99 int ptrace_usercopy(addr_t realuseraddr,addr_t copyaddr,int len,int tofromuser,int writeuser,u32 mask)
100 {
101 u32 tempuser;
102 int retval=0;
103
104 if(writeuser&&realuseraddr==(addr_t)NULL)
105 return(0);
106 if(mask!=0xffffffff)
107 {
108 tempuser=*((u32 *)realuseraddr);
109 if(!writeuser)
110 {
111 tempuser&=mask;
112 realuseraddr=(addr_t)&tempuser;
113 }
114 }
115 if(tofromuser)
116 {
117 if(writeuser)
118 {
119 retval=copy_from_user((void *)realuseraddr,(void *)copyaddr,len);
120 }
121 else
122 {
123 if(realuseraddr==(addr_t)NULL)
124 retval=(clear_user((void *)copyaddr,len)==-EFAULT ? -EIO:0);
125 else
126 retval=(copy_to_user((void *)copyaddr,(void *)realuseraddr,len)==-EFAULT ? -EIO:0);
127 }
128 }
129 else
130 {
131 if(writeuser)
132 memcpy((void *)realuseraddr,(void *)copyaddr,len);
133 else
134 memcpy((void *)copyaddr,(void *)realuseraddr,len);
135 }
136 if(mask!=0xffffffff&&writeuser)
137 (*((u32 *)realuseraddr))=(((*((u32 *)realuseraddr))&mask)|(tempuser&~mask));
138 return(retval);
139 }
140
141 int copy_user(struct task_struct *task,saddr_t useraddr,addr_t copyaddr,int len,int tofromuser,int writingtouser)
142 {
143 int copylen=0,copymax;
144 addr_t realuseraddr;
145 saddr_t enduseraddr=useraddr+len;
146
147 u32 mask;
148
149 if (useraddr < 0 || enduseraddr > sizeof(struct user)||
150 (useraddr < PT_ENDREGS && (useraddr&3))||
151 (enduseraddr < PT_ENDREGS && (enduseraddr&3)))
152 return (-EIO);
153 while(len>0)
154 {
155 mask=0xffffffff;
156 if(useraddr<PT_FPC)
157 {
158 realuseraddr=(addr_t)&(((u8 *)task->thread.regs)[useraddr]);
159 if(useraddr<PT_PSWMASK)
160 {
161 copymax=PT_PSWMASK;
162 }
163 else if(useraddr<(PT_PSWMASK+4))
164 {
165 copymax=(PT_PSWMASK+4);
166 if(writingtouser)
167 mask=PSW_MASK_DEBUGCHANGE;
168 }
169 else if(useraddr<(PT_PSWADDR+4))
170 {
171 copymax=PT_PSWADDR+4;
172 mask=PSW_ADDR_DEBUGCHANGE;
173 }
174 else
175 copymax=PT_FPC;
176
177 }
178 else if(useraddr<(PT_FPR15_LO+4))
179 {
180 copymax=(PT_FPR15_LO+4);
181 realuseraddr=(addr_t)&(((u8 *)&task->thread.fp_regs)[useraddr-PT_FPC]);
182 }
183 else if(useraddr<sizeof(struct user_regs_struct))
184 {
185 copymax=sizeof(struct user_regs_struct);
186 realuseraddr=(addr_t)&(((u8 *)&task->thread.per_info)[useraddr-PT_CR_9]);
187 }
188 else
189 {
190 copymax=sizeof(struct user);
191 realuseraddr=(addr_t)NULL;
192 }
193 copylen=copymax-useraddr;
194 copylen=(copylen>len ? len:copylen);
195 if(ptrace_usercopy(realuseraddr,copyaddr,copylen,tofromuser,writingtouser,mask))
196 return (-EIO);
197 copyaddr+=copylen;
198 len-=copylen;
199 useraddr+=copylen;
200 }
201 FixPerRegisters(task);
202 return(0);
203 }
204
205 /*
206 * Called by kernel/ptrace.c when detaching..
207 *
208 * Make sure single step bits etc are not set.
209 */
210 void ptrace_disable(struct task_struct *child)
211 {
212 /* make sure the single step bit is not set. */
213 clear_single_step(child);
214 }
215
216 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
217 {
218 struct task_struct *child;
219 int ret = -EPERM;
220 unsigned long flags;
221 unsigned long tmp;
222 int copied;
223 ptrace_area parea;
224
225 lock_kernel();
226 if (request == PTRACE_TRACEME)
227 {
228 /* are we already being traced? */
229 if (current->ptrace & PT_PTRACED)
230 goto out;
231 /* set the ptrace bit in the process flags. */
232 current->ptrace |= PT_PTRACED;
233 ret = 0;
234 goto out;
235 }
236 ret = -ESRCH;
237 read_lock(&tasklist_lock);
238 child = find_task_by_pid(pid);
239 read_unlock(&tasklist_lock);
240 if (!child)
241 goto out;
242 ret = -EPERM;
243 if (pid == 1) /* you may not mess with init */
244 goto out;
245 if (request == PTRACE_ATTACH)
246 {
247 ret = ptrace_attach(child);
248 goto out;
249 }
250 ret = -ESRCH;
251 // printk("child=%lX child->flags=%lX",child,child->flags);
252 /* I added child!=current line so we can get the */
253 /* ieee_instruction_pointer from the user structure DJB */
254 if(child!=current)
255 {
256 if (!(child->ptrace & PT_PTRACED))
257 goto out;
258 if (child->state != TASK_STOPPED)
259 {
260 if (request != PTRACE_KILL)
261 goto out;
262 }
263 if (child->p_pptr != current)
264 goto out;
265 }
266 switch (request)
267 {
268 /* If I and D space are separate, these will need to be fixed. */
269 case PTRACE_PEEKTEXT: /* read word at location addr. */
270 case PTRACE_PEEKDATA:
271 copied = access_process_vm(child,ADDR_BITS_REMOVE(addr), &tmp, sizeof(tmp), 0);
272 ret = -EIO;
273 if (copied != sizeof(tmp))
274 goto out;
275 ret = put_user(tmp,(unsigned long *) data);
276 goto out;
277
278 /* read the word at location addr in the USER area. */
279 case PTRACE_PEEKUSR:
280 ret=copy_user(child,addr,data,sizeof(unsigned long),1,0);
281 break;
282
283 /* If I and D space are separate, this will have to be fixed. */
284 case PTRACE_POKETEXT: /* write the word at location addr. */
285 case PTRACE_POKEDATA:
286 ret = 0;
287 if (access_process_vm(child,ADDR_BITS_REMOVE(addr), &data, sizeof(data), 1) == sizeof(data))
288 goto out;
289 ret = -EIO;
290 goto out;
291 break;
292
293 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
294 ret=copy_user(child,addr,(addr_t)&data,sizeof(unsigned long),0,1);
295 break;
296
297 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
298 case PTRACE_CONT: /* restart after signal. */
299 ret = -EIO;
300 if ((unsigned long) data >= _NSIG)
301 break;
302 if (request == PTRACE_SYSCALL)
303 child->ptrace |= PT_TRACESYS;
304 else
305 child->ptrace &= ~PT_TRACESYS;
306 child->exit_code = data;
307 /* make sure the single step bit is not set. */
308 clear_single_step(child);
309 wake_up_process(child);
310 ret = 0;
311 break;
312
313 /*
314 * make the child exit. Best I can do is send it a sigkill.
315 * perhaps it should be put in the status that it wants to
316 * exit.
317 */
318 case PTRACE_KILL:
319 ret = 0;
320 if (child->state == TASK_ZOMBIE) /* already dead */
321 break;
322 child->exit_code = SIGKILL;
323 clear_single_step(child);
324 wake_up_process(child);
325 /* make sure the single step bit is not set. */
326 break;
327
328 case PTRACE_SINGLESTEP: /* set the trap flag. */
329 ret = -EIO;
330 if ((unsigned long) data >= _NSIG)
331 break;
332 child->ptrace &= ~PT_TRACESYS;
333 child->exit_code = data;
334 set_single_step(child);
335 /* give it a chance to run. */
336 wake_up_process(child);
337 ret = 0;
338 break;
339
340 case PTRACE_DETACH: /* detach a process that was attached. */
341 ret = ptrace_detach(child, data);
342 break;
343 case PTRACE_PEEKUSR_AREA:
344 case PTRACE_POKEUSR_AREA:
345 if((ret=copy_from_user(&parea,(void *)addr,sizeof(parea)))==0)
346 ret=copy_user(child,parea.kernel_addr,parea.process_addr,
347 parea.len,1,(request==PTRACE_POKEUSR_AREA));
348 break;
349 default:
350 ret = -EIO;
351 break;
352 }
353 out:
354 unlock_kernel();
355 return ret;
356 }
357
358 asmlinkage void syscall_trace(void)
359 {
360 lock_kernel();
361 if ((current->ptrace & (PT_PTRACED|PT_TRACESYS))
362 != (PT_PTRACED|PT_TRACESYS))
363 goto out;
364 current->exit_code = SIGTRAP;
365 set_current_state(TASK_STOPPED);
366 notify_parent(current, SIGCHLD);
367 schedule();
368 /*
369 * this isn't the same as continuing with a signal, but it will do
370 * for normal use. strace only continues with a signal if the
371 * stopping signal is not SIGTRAP. -brl
372 */
373 if (current->exit_code) {
374 send_sig(current->exit_code, current, 1);
375 current->exit_code = 0;
376 }
377 out:
378 unlock_kernel();
379 }
380