1 /* ptrace.c: FRV specific parts of process tracing
3 * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 * - Derived from arch/m68k/kernel/ptrace.c
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
13 #include <linux/kernel.h>
14 #include <linux/sched.h>
16 #include <linux/smp.h>
17 #include <linux/errno.h>
18 #include <linux/ptrace.h>
19 #include <linux/user.h>
20 #include <linux/security.h>
21 #include <linux/signal.h>
23 #include <asm/uaccess.h>
25 #include <asm/pgtable.h>
26 #include <asm/system.h>
27 #include <asm/processor.h>
28 #include <asm/unistd.h>
31 * does not yet catch signals sent when the child dies.
32 * in exit.c or in signal.c.
36 * Get contents of register REGNO in task TASK.
38 static inline long get_reg(struct task_struct
*task
, int regno
)
40 struct user_context
*user
= task
->thread
.user
;
42 if (regno
< 0 || regno
>= PT__END
)
45 return ((unsigned long *) user
)[regno
];
49 * Write contents of register REGNO in task TASK.
51 static inline int put_reg(struct task_struct
*task
, int regno
,
54 struct user_context
*user
= task
->thread
.user
;
56 if (regno
< 0 || regno
>= PT__END
)
66 ((unsigned long *) user
)[regno
] = data
;
72 * check that an address falls within the bounds of the target process's memory
75 static inline int is_user_addr_valid(struct task_struct
*child
,
76 unsigned long start
, unsigned long len
)
79 if (start
>= PAGE_OFFSET
|| len
> PAGE_OFFSET
- start
)
83 struct vm_area_struct
*vma
;
85 vma
= find_vma(child
->mm
, start
);
86 if (vma
&& start
>= vma
->vm_start
&& start
+ len
<= vma
->vm_end
)
94 * Called by kernel/ptrace.c when detaching..
96 * Control h/w single stepping
98 void ptrace_disable(struct task_struct
*child
)
100 child
->thread
.frame0
->__status
&= ~REG__STATUS_STEP
;
103 void ptrace_enable(struct task_struct
*child
)
105 child
->thread
.frame0
->__status
|= REG__STATUS_STEP
;
108 long arch_ptrace(struct task_struct
*child
, long request
, long addr
, long data
)
114 /* when I and D space are separate, these will need to be fixed. */
115 case PTRACE_PEEKTEXT
: /* read word at location addr. */
116 case PTRACE_PEEKDATA
:
118 if (is_user_addr_valid(child
, addr
, sizeof(tmp
)) < 0)
120 ret
= generic_ptrace_peekdata(child
, addr
, data
);
123 /* read the word at location addr in the USER area. */
124 case PTRACE_PEEKUSR
: {
127 if ((addr
& 3) || addr
< 0)
132 case 0 ... PT__END
- 1:
133 tmp
= get_reg(child
, addr
>> 2);
137 tmp
= child
->mm
->end_code
- child
->mm
->start_code
;
141 tmp
= child
->mm
->end_data
- child
->mm
->start_data
;
145 tmp
= child
->mm
->start_stack
- child
->mm
->start_brk
;
149 tmp
= child
->mm
->start_code
;
153 tmp
= child
->mm
->start_stack
;
162 ret
= put_user(tmp
, (unsigned long *) data
);
166 /* when I and D space are separate, this will have to be fixed. */
167 case PTRACE_POKETEXT
: /* write the word at location addr. */
168 case PTRACE_POKEDATA
:
170 if (is_user_addr_valid(child
, addr
, sizeof(tmp
)) < 0)
172 ret
= generic_ptrace_pokedata(child
, addr
, data
);
175 case PTRACE_POKEUSR
: /* write the word at location addr in the USER area */
177 if ((addr
& 3) || addr
< 0)
182 case 0 ... PT__END
-1:
183 ret
= put_reg(child
, addr
>> 2, data
);
192 case PTRACE_SYSCALL
: /* continue and stop at next (return from) syscall */
193 case PTRACE_CONT
: /* restart after signal. */
195 if (!valid_signal(data
))
197 if (request
== PTRACE_SYSCALL
)
198 set_tsk_thread_flag(child
, TIF_SYSCALL_TRACE
);
200 clear_tsk_thread_flag(child
, TIF_SYSCALL_TRACE
);
201 child
->exit_code
= data
;
202 ptrace_disable(child
);
203 wake_up_process(child
);
207 /* make the child exit. Best I can do is send it a sigkill.
208 * perhaps it should be put in the status that it wants to
213 if (child
->exit_state
== EXIT_ZOMBIE
) /* already dead */
215 child
->exit_code
= SIGKILL
;
216 clear_tsk_thread_flag(child
, TIF_SINGLESTEP
);
217 ptrace_disable(child
);
218 wake_up_process(child
);
221 case PTRACE_SINGLESTEP
: /* set the trap flag. */
223 if (!valid_signal(data
))
225 clear_tsk_thread_flag(child
, TIF_SYSCALL_TRACE
);
226 ptrace_enable(child
);
227 child
->exit_code
= data
;
228 wake_up_process(child
);
232 case PTRACE_DETACH
: /* detach a process that was attached. */
233 ret
= ptrace_detach(child
, data
);
236 case PTRACE_GETREGS
: { /* Get all integer regs from the child. */
238 for (i
= 0; i
< PT__GPEND
; i
++) {
239 tmp
= get_reg(child
, i
);
240 if (put_user(tmp
, (unsigned long *) data
)) {
244 data
+= sizeof(long);
250 case PTRACE_SETREGS
: { /* Set all integer regs in the child. */
252 for (i
= 0; i
< PT__GPEND
; i
++) {
253 if (get_user(tmp
, (unsigned long *) data
)) {
257 put_reg(child
, i
, tmp
);
258 data
+= sizeof(long);
264 case PTRACE_GETFPREGS
: { /* Get the child FP/Media state. */
266 if (copy_to_user((void *) data
,
267 &child
->thread
.user
->f
,
268 sizeof(child
->thread
.user
->f
)))
273 case PTRACE_SETFPREGS
: { /* Set the child FP/Media state. */
275 if (copy_from_user(&child
->thread
.user
->f
,
277 sizeof(child
->thread
.user
->f
)))
282 case PTRACE_GETFDPIC
:
285 case PTRACE_GETFDPIC_EXEC
:
286 tmp
= child
->mm
->context
.exec_fdpic_loadmap
;
288 case PTRACE_GETFDPIC_INTERP
:
289 tmp
= child
->mm
->context
.interp_fdpic_loadmap
;
296 if (put_user(tmp
, (unsigned long *) data
)) {
309 asmlinkage
void do_syscall_trace(int leaving
)
311 if (!test_thread_flag(TIF_SYSCALL_TRACE
))
314 if (!(current
->ptrace
& PT_PTRACED
))
317 /* we need to indicate entry or exit to strace */
319 __frame
->__status
|= REG__STATUS_SYSC_EXIT
;
321 __frame
->__status
|= REG__STATUS_SYSC_ENTRY
;
323 ptrace_notify(SIGTRAP
);
326 * this isn't the same as continuing with a signal, but it will do
327 * for normal use. strace only continues with a signal if the
328 * stopping signal is not SIGTRAP. -brl
330 if (current
->exit_code
) {
331 send_sig(current
->exit_code
, current
, 1);
332 current
->exit_code
= 0;