File: /usr/src/linux/arch/s390x/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/config.h>
26 #include <linux/kernel.h>
27 #include <linux/sched.h>
28 #include <linux/mm.h>
29 #include <linux/smp.h>
30 #include <linux/smp_lock.h>
31 #include <linux/errno.h>
32 #include <linux/ptrace.h>
33 #include <linux/user.h>
34
35 #include <asm/segment.h>
36 #include <asm/page.h>
37 #include <asm/pgtable.h>
38 #include <asm/pgalloc.h>
39 #include <asm/system.h>
40 #include <asm/uaccess.h>
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 per_info->control_regs.bits.starting_addr=0;
53 #ifdef CONFIG_S390_SUPPORT
54 if (current->thread.flags & S390_FLAG_31BIT) {
55 per_info->control_regs.bits.ending_addr=0x7fffffffUL;
56 }
57 else
58 #endif
59 {
60 per_info->control_regs.bits.ending_addr=-1L;
61 }
62 } else {
63 per_info->control_regs.bits.starting_addr=
64 per_info->starting_addr;
65 per_info->control_regs.bits.ending_addr=
66 per_info->ending_addr;
67 }
68 /* if any of the control reg tracing bits are on
69 we switch on per in the psw */
70 if (per_info->control_regs.words.cr[0] & PER_EM_MASK)
71 regs->psw.mask |= PSW_PER_MASK;
72 else
73 regs->psw.mask &= ~PSW_PER_MASK;
74 if (per_info->control_regs.bits.storage_alt_space_ctl)
75 task->thread.user_seg |= USER_STD_MASK;
76 else
77 task->thread.user_seg &= ~USER_STD_MASK;
78 }
79
80 void set_single_step(struct task_struct *task)
81 {
82 per_struct *per_info= (per_struct *) &task->thread.per_info;
83
84 per_info->single_step = 1; /* Single step */
85 FixPerRegisters (task);
86 }
87
88 void clear_single_step(struct task_struct *task)
89 {
90 per_struct *per_info= (per_struct *) &task->thread.per_info;
91
92 per_info->single_step = 0;
93 FixPerRegisters (task);
94 }
95
96 int ptrace_usercopy(addr_t realuseraddr, addr_t copyaddr, int len,
97 int tofromuser, int writeuser, unsigned long mask)
98 {
99 unsigned long *realuserptr, *copyptr;
100 unsigned long tempuser;
101 int retval;
102
103 retval = 0;
104 realuserptr = (unsigned long *) realuseraddr;
105 copyptr = (unsigned long *) copyaddr;
106
107 if (writeuser && realuserptr == NULL)
108 return 0;
109
110 if (mask != -1L) {
111 tempuser = *realuserptr;
112 if (!writeuser) {
113 tempuser &= mask;
114 realuserptr = &tempuser;
115 }
116 }
117 if (tofromuser) {
118 if (writeuser) {
119 retval = copy_from_user(realuserptr, copyptr, len);
120 } else {
121 if (realuserptr == NULL)
122 retval = clear_user(copyptr, len);
123 else
124 retval = copy_to_user(copyptr,realuserptr,len);
125 retval = (retval == -EFAULT) ? -EIO : 0;
126 }
127 } else {
128 if (writeuser)
129 memcpy(realuserptr, copyptr, len);
130 else
131 memcpy(copyptr, realuserptr, len);
132 }
133 if (mask != -1L && writeuser)
134 *realuserptr = (*realuserptr & mask) | (tempuser & ~mask);
135 return retval;
136 }
137
138 int copy_user(struct task_struct *task,saddr_t useraddr, addr_t copyaddr,
139 int len, int tofromuser, int writingtouser)
140 {
141 int copylen=0,copymax;
142 addr_t realuseraddr;
143 saddr_t enduseraddr;
144
145 unsigned long mask;
146
147 #ifdef CONFIG_S390_SUPPORT
148 if (current->thread.flags & S390_FLAG_31BIT) {
149 /* adjust user offsets to 64 bit structure */
150 if (useraddr < PT_PSWADDR / 2)
151 useraddr = 2 * useraddr;
152 else if(useraddr < PT_ACR0 / 2)
153 useraddr = 2 * useraddr + sizeof(addr_t) / 2;
154 else if(useraddr < PT_ACR0 / 2 + (PT_ORIGGPR2 - PT_ACR0))
155 useraddr = useraddr + PT_ACR0 / 2;
156 else if(useraddr < PT_ACR0 / 2 + (sizeof(struct user_regs_struct) - sizeof(addr_t) / 2 - PT_ACR0))
157 useraddr = useraddr + PT_ACR0 / 2 + sizeof(addr_t) / 2;
158 }
159 #endif
160
161 enduseraddr=useraddr+len;
162
163 if (useraddr < 0 || enduseraddr > sizeof(struct user)||
164 (useraddr < PT_ENDREGS && (useraddr&3))||
165 (enduseraddr < PT_ENDREGS && (enduseraddr&3)))
166 return (-EIO);
167 while(len>0)
168 {
169 mask=PSW_ADDR_MASK;
170 if(useraddr<PT_FPC)
171 {
172 realuseraddr=(addr_t)&(((u8 *)task->thread.regs)[useraddr]);
173 if(useraddr<PT_PSWMASK)
174 {
175 copymax=PT_PSWMASK;
176 }
177 else if(useraddr<(PT_PSWMASK+8))
178 {
179 copymax=(PT_PSWMASK+8);
180 if(writingtouser)
181 mask=PSW_MASK_DEBUGCHANGE;
182 }
183 else if(useraddr<(PT_PSWADDR+8))
184 {
185 copymax=PT_PSWADDR+8;
186 mask=PSW_ADDR_DEBUGCHANGE;
187 }
188 else
189 copymax=PT_FPC;
190
191 }
192 else if(useraddr<(PT_FPR15+sizeof(freg_t)))
193 {
194 copymax=(PT_FPR15+sizeof(freg_t));
195 realuseraddr=(addr_t)&(((u8 *)&task->thread.fp_regs)[useraddr-PT_FPC]);
196 }
197 else if(useraddr<sizeof(struct user_regs_struct))
198 {
199 copymax=sizeof(struct user_regs_struct);
200 realuseraddr=(addr_t)&(((u8 *)&task->thread.per_info)[useraddr-PT_CR_9]);
201 }
202 else
203 {
204 copymax=sizeof(struct user);
205 realuseraddr=(addr_t)NULL;
206 }
207 copylen=copymax-useraddr;
208 copylen=(copylen>len ? len:copylen);
209 if(ptrace_usercopy(realuseraddr,copyaddr,copylen,tofromuser,writingtouser,mask))
210 return (-EIO);
211 copyaddr+=copylen;
212 len-=copylen;
213 useraddr+=copylen;
214 }
215 FixPerRegisters(task);
216 return(0);
217 }
218
219 /*
220 * Called by kernel/ptrace.c when detaching..
221 *
222 * Make sure single step bits etc are not set.
223 */
224 void ptrace_disable(struct task_struct *child)
225 {
226 /* make sure the single step bit is not set. */
227 clear_single_step(child);
228 }
229
230 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
231 {
232 struct task_struct *child;
233 int ret = -EPERM;
234 unsigned long flags;
235 unsigned long tmp;
236 int copied;
237 ptrace_area parea;
238
239 lock_kernel();
240 if (request == PTRACE_TRACEME)
241 {
242 /* are we already being traced? */
243 if (current->ptrace & PT_PTRACED)
244 goto out;
245 /* set the ptrace bit in the process flags. */
246 current->ptrace |= PT_PTRACED;
247 ret = 0;
248 goto out;
249 }
250 ret = -ESRCH;
251 read_lock(&tasklist_lock);
252 child = find_task_by_pid(pid);
253 read_unlock(&tasklist_lock);
254 if (!child)
255 goto out;
256 ret = -EPERM;
257 if (pid == 1) /* you may not mess with init */
258 goto out;
259 if (request == PTRACE_ATTACH)
260 {
261 ret = ptrace_attach(child);
262 goto out;
263 }
264 ret = -ESRCH;
265 // printk("child=%lX child->flags=%lX",child,child->flags);
266 /* I added child!=current line so we can get the */
267 /* ieee_instruction_pointer from the user structure DJB */
268 if(child!=current)
269 {
270 if (!(child->ptrace & PT_PTRACED))
271 goto out;
272 if (child->state != TASK_STOPPED)
273 {
274 if (request != PTRACE_KILL)
275 goto out;
276 }
277 if (child->p_pptr != current)
278 goto out;
279 }
280 switch (request)
281 {
282 /* If I and D space are separate, these will need to be fixed. */
283 case PTRACE_PEEKTEXT: /* read word at location addr. */
284 case PTRACE_PEEKDATA:
285 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
286 ret = -EIO;
287 if (copied != sizeof(tmp))
288 goto out;
289 ret = put_user(tmp,(unsigned long *) data);
290 goto out;
291
292 /* read the word at location addr in the USER area. */
293 case PTRACE_PEEKUSR:
294 ret=copy_user(child,addr,data,sizeof(unsigned long),1,0);
295 break;
296
297 /* If I and D space are separate, this will have to be fixed. */
298 case PTRACE_POKETEXT: /* write the word at location addr. */
299 case PTRACE_POKEDATA:
300 ret = 0;
301 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
302 goto out;
303 ret = -EIO;
304 goto out;
305 break;
306
307 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
308 ret=copy_user(child,addr,(addr_t)&data,sizeof(unsigned long),0,1);
309 break;
310
311 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
312 case PTRACE_CONT: /* restart after signal. */
313 ret = -EIO;
314 if ((unsigned long) data >= _NSIG)
315 break;
316 if (request == PTRACE_SYSCALL)
317 child->ptrace |= PT_TRACESYS;
318 else
319 child->ptrace &= ~PT_TRACESYS;
320 child->exit_code = data;
321 /* make sure the single step bit is not set. */
322 clear_single_step(child);
323 wake_up_process(child);
324 ret = 0;
325 break;
326
327 /*
328 * make the child exit. Best I can do is send it a sigkill.
329 * perhaps it should be put in the status that it wants to
330 * exit.
331 */
332 case PTRACE_KILL:
333 ret = 0;
334 if (child->state == TASK_ZOMBIE) /* already dead */
335 break;
336 child->exit_code = SIGKILL;
337 clear_single_step(child);
338 wake_up_process(child);
339 /* make sure the single step bit is not set. */
340 break;
341
342 case PTRACE_SINGLESTEP: /* set the trap flag. */
343 ret = -EIO;
344 if ((unsigned long) data >= _NSIG)
345 break;
346 child->ptrace &= ~PT_TRACESYS;
347 child->exit_code = data;
348 set_single_step(child);
349 /* give it a chance to run. */
350 wake_up_process(child);
351 ret = 0;
352 break;
353
354 case PTRACE_DETACH: /* detach a process that was attached. */
355 ret = ptrace_detach(child, data);
356 break;
357
358 case PTRACE_PEEKUSR_AREA:
359 case PTRACE_POKEUSR_AREA:
360 if((ret=copy_from_user(&parea,(void *)addr,sizeof(parea)))==0)
361 ret=copy_user(child,parea.kernel_addr,parea.process_addr,
362 parea.len,1,(request==PTRACE_POKEUSR_AREA));
363 break;
364 default:
365 ret = -EIO;
366 break;
367 }
368 out:
369 unlock_kernel();
370 return ret;
371 }
372
373 typedef struct
374 {
375 __u32 len;
376 __u32 kernel_addr;
377 __u32 process_addr;
378 } ptrace_area_emu31;
379
380 asmlinkage int sys32_ptrace(long request, long pid, long addr, s32 data)
381 {
382 struct task_struct *child;
383 int ret = -EPERM;
384 unsigned long flags;
385 u32 tmp;
386 int copied;
387 ptrace_area parea;
388
389 lock_kernel();
390 if (request == PTRACE_TRACEME)
391 {
392 /* are we already being traced? */
393 if (current->ptrace & PT_PTRACED)
394 goto out;
395 /* set the ptrace bit in the process flags. */
396 current->ptrace |= PT_PTRACED;
397 ret = 0;
398 goto out;
399 }
400 ret = -ESRCH;
401 read_lock(&tasklist_lock);
402 child = find_task_by_pid(pid);
403 read_unlock(&tasklist_lock);
404 if (!child)
405 goto out;
406 ret = -EPERM;
407 if (pid == 1) /* you may not mess with init */
408 goto out;
409 if (request == PTRACE_ATTACH)
410 {
411 ret = ptrace_attach(child);
412 goto out;
413 }
414 ret = -ESRCH;
415 // printk("child=%lX child->flags=%lX",child,child->flags);
416 /* I added child!=current line so we can get the */
417 /* ieee_instruction_pointer from the user structure DJB */
418 if(child!=current)
419 {
420 if (!(child->ptrace & PT_PTRACED))
421 goto out;
422 if (child->state != TASK_STOPPED)
423 {
424 if (request != PTRACE_KILL)
425 goto out;
426 }
427 if (child->p_pptr != current)
428 goto out;
429 }
430 switch (request)
431 {
432 /* If I and D space are separate, these will need to be fixed. */
433 case PTRACE_PEEKTEXT: /* read word at location addr. */
434 case PTRACE_PEEKDATA:
435 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
436 ret = -EIO;
437 if (copied != sizeof(tmp))
438 goto out;
439 ret = put_user(tmp,(u32 *)(unsigned long)data);
440 goto out;
441
442 /* read the word at location addr in the USER area. */
443 case PTRACE_PEEKUSR:
444 ret=copy_user(child,addr,data,sizeof(u32),1,0);
445 break;
446
447 /* If I and D space are separate, this will have to be fixed. */
448 case PTRACE_POKETEXT: /* write the word at location addr. */
449 case PTRACE_POKEDATA:
450 ret = 0;
451 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
452 goto out;
453 ret = -EIO;
454 goto out;
455 break;
456
457 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
458 ret=copy_user(child,addr,(addr_t)&data,sizeof(u32),0,1);
459 break;
460
461 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
462 case PTRACE_CONT: /* restart after signal. */
463 ret = -EIO;
464 if ((unsigned long) data >= _NSIG)
465 break;
466 if (request == PTRACE_SYSCALL)
467 child->ptrace |= PT_TRACESYS;
468 else
469 child->ptrace &= ~PT_TRACESYS;
470 child->exit_code = data;
471 /* make sure the single step bit is not set. */
472 clear_single_step(child);
473 wake_up_process(child);
474 ret = 0;
475 break;
476
477 /*
478 * make the child exit. Best I can do is send it a sigkill.
479 * perhaps it should be put in the status that it wants to
480 * exit.
481 */
482 case PTRACE_KILL:
483 ret = 0;
484 if (child->state == TASK_ZOMBIE) /* already dead */
485 break;
486 child->exit_code = SIGKILL;
487 clear_single_step(child);
488 wake_up_process(child);
489 /* make sure the single step bit is not set. */
490 break;
491
492 case PTRACE_SINGLESTEP: /* set the trap flag. */
493 ret = -EIO;
494 if ((unsigned long) data >= _NSIG)
495 break;
496 child->ptrace &= ~PT_TRACESYS;
497 child->exit_code = data;
498 set_single_step(child);
499 /* give it a chance to run. */
500 wake_up_process(child);
501 ret = 0;
502 break;
503
504 case PTRACE_DETACH: /* detach a process that was attached. */
505 ret = -EIO;
506 if ((unsigned long) data >= _NSIG)
507 break;
508 child->ptrace &= ~(PT_PTRACED|PT_TRACESYS);
509 child->exit_code = data;
510 write_lock_irqsave(&tasklist_lock, flags);
511 REMOVE_LINKS(child);
512 child->p_pptr = child->p_opptr;
513 SET_LINKS(child);
514 write_unlock_irqrestore(&tasklist_lock, flags);
515 /* make sure the single step bit is not set. */
516 clear_single_step(child);
517 wake_up_process(child);
518 ret = 0;
519 break;
520 case PTRACE_PEEKUSR_AREA:
521 case PTRACE_POKEUSR_AREA:
522 {
523 ptrace_area_emu31 * parea31 = (void *)addr;
524 if (!access_ok(VERIFY_READ, parea31, sizeof(*parea31)))
525 return(-EFAULT);
526 ret = __get_user(parea.len, &parea31->len);
527 ret |= __get_user(parea.kernel_addr, &parea31->kernel_addr);
528 ret |= __get_user(parea.process_addr, &parea31->process_addr);
529 if(ret==0)
530 ret=copy_user(child,parea.kernel_addr,parea.process_addr,
531 parea.len,1,(request==PTRACE_POKEUSR_AREA));
532 break;
533 }
534 default:
535 ret = -EIO;
536 break;
537 }
538 out:
539 unlock_kernel();
540 return ret;
541 }
542
543 asmlinkage void syscall_trace(void)
544 {
545 lock_kernel();
546 if ((current->ptrace & (PT_PTRACED|PT_TRACESYS))
547 != (PT_PTRACED|PT_TRACESYS))
548 goto out;
549 current->exit_code = SIGTRAP;
550 set_current_state(TASK_STOPPED);
551 notify_parent(current, SIGCHLD);
552 schedule();
553 /*
554 * this isn't the same as continuing with a signal, but it will do
555 * for normal use. strace only continues with a signal if the
556 * stopping signal is not SIGTRAP. -brl
557 */
558 if (current->exit_code) {
559 send_sig(current->exit_code, current, 1);
560 current->exit_code = 0;
561 }
562 out:
563 unlock_kernel();
564 }
565