File: /usr/src/linux/arch/parisc/kernel/ptrace.c
1 /*
2 * Kernel support for the ptrace() and syscall tracing interfaces.
3 *
4 * Copyright (C) 2000 Hewlett-Packard Co, Linuxcare Inc.
5 * Copyright (C) 2000 Matthew Wilcox <matthew@wil.cx>
6 * Copyright (C) 2000 David Huggins-Daines <dhd@debian.org>
7 */
8
9 #include <linux/kernel.h>
10 #include <linux/sched.h>
11 #include <linux/mm.h>
12 #include <linux/smp.h>
13 #include <linux/smp_lock.h>
14 #include <linux/errno.h>
15 #include <linux/ptrace.h>
16 #include <linux/user.h>
17
18 #include <asm/uaccess.h>
19 #include <asm/pgtable.h>
20 #include <asm/system.h>
21 #include <asm/processor.h>
22 #include <asm/offset.h>
23
24 /* These are used in entry.S, syscall_restore_rfi. We need to record the
25 * current stepping mode somewhere other than in PSW, because there is no
26 * concept of saving and restoring the users PSW over a syscall. We choose
27 * to use these two bits in task->ptrace. These bits must not clash with
28 * any PT_* defined in include/linux/sched.h, and must match with the bit
29 * tests in entry.S
30 */
31 #define PT_SINGLESTEP 0x10000
32 #define PT_BLOCKSTEP 0x20000
33
34 /*
35 * Called by kernel/ptrace.c when detaching..
36 *
37 * Make sure single step bits etc are not set.
38 */
39 void ptrace_disable(struct task_struct *child)
40 {
41 /* make sure the trap bits are not set */
42 pa_psw(child)->r = 0;
43 pa_psw(child)->t = 0;
44 pa_psw(child)->h = 0;
45 pa_psw(child)->l = 0;
46 }
47
48 long sys_ptrace(long request, pid_t pid, long addr, long data)
49 {
50 struct task_struct *child;
51 long ret;
52
53 lock_kernel();
54 ret = -EPERM;
55 if (request == PTRACE_TRACEME) {
56 /* are we already being traced? */
57 if (current->ptrace & PT_PTRACED)
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
65 ret = -ESRCH;
66 read_lock(&tasklist_lock);
67 child = find_task_by_pid(pid);
68 if (child)
69 get_task_struct(child);
70 read_unlock(&tasklist_lock);
71 if (!child)
72 goto out;
73 ret = -EPERM;
74 if (pid == 1) /* no messing around with init! */
75 goto out_tsk;
76
77 if (request == PTRACE_ATTACH) {
78 ret = ptrace_attach(child);
79 goto out_tsk;
80 }
81 ret = -ESRCH;
82 if (!(child->ptrace & PT_PTRACED))
83 goto out_tsk;
84 if (child->state != TASK_STOPPED) {
85 if (request != PTRACE_KILL)
86 goto out_tsk;
87 }
88 if (child->p_pptr != current)
89 goto out_tsk;
90
91 switch (request) {
92 case PTRACE_PEEKTEXT: /* read word at location addr. */
93 case PTRACE_PEEKDATA: {
94 unsigned long tmp;
95 int copied;
96
97 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
98 ret = -EIO;
99 if (copied != sizeof(tmp))
100 goto out_tsk;
101 ret = put_user(tmp,(unsigned long *) data);
102 goto out_tsk;
103 }
104
105 /* when I and D space are separate, this will have to be fixed. */
106 case PTRACE_POKETEXT: /* write the word at location addr. */
107 case PTRACE_POKEDATA:
108 ret = 0;
109 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
110 goto out_tsk;
111 ret = -EIO;
112 goto out_tsk;
113
114 /* Read the word at location addr in the USER area. This will need
115 to change when the kernel no longer saves all regs on a syscall. */
116 case PTRACE_PEEKUSR: {
117 unsigned long tmp;
118
119 ret = -EIO;
120 if ((addr & 3) || (unsigned long) addr >= sizeof(struct pt_regs))
121 goto out_tsk;
122
123 tmp = *(unsigned long *) ((char *) task_regs(child) + addr);
124 ret = put_user(tmp, (unsigned long *) data);
125 goto out_tsk;
126 }
127
128 /* Write the word at location addr in the USER area. This will need
129 to change when the kernel no longer saves all regs on a syscall.
130 FIXME. There is a problem at the moment in that r3-r18 are only
131 saved if the process is ptraced on syscall entry, and even then
132 those values are overwritten by actual register values on syscall
133 exit. */
134 case PTRACE_POKEUSR:
135 ret = -EIO;
136 if ((addr & 3) || (unsigned long) addr >= sizeof(struct pt_regs))
137 goto out_tsk;
138 /* XXX This test probably needs adjusting. We probably want to
139 * allow writes to some bits of PSW, and may want to block writes
140 * to (some) space registers. Some register values written here
141 * may be ignored in entry.S:syscall_restore_rfi; e.g. iaoq is
142 * written with r31/r31+4, and not with the values in pt_regs.
143 */
144 /* Allow writing of gr1-gr31, fr*, sr*, iasq*, iaoq*, sar */
145 if (addr == PT_PSW || (addr > PT_IAOQ1 && addr != PT_SAR))
146 goto out_tsk;
147
148 *(unsigned long *) ((char *) task_regs(child) + addr) = data;
149 ret = 0;
150 goto out_tsk;
151
152 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
153 case PTRACE_CONT:
154 ret = -EIO;
155 if ((unsigned long) data > _NSIG)
156 goto out_tsk;
157 child->ptrace &= ~(PT_SINGLESTEP|PT_BLOCKSTEP);
158 if (request == PTRACE_SYSCALL)
159 child->ptrace |= PT_TRACESYS;
160 else
161 child->ptrace &= ~PT_TRACESYS;
162 child->exit_code = data;
163 goto out_wake_notrap;
164
165 case PTRACE_KILL:
166 /*
167 * make the child exit. Best I can do is send it a
168 * sigkill. perhaps it should be put in the status
169 * that it wants to exit.
170 */
171 if (child->state == TASK_ZOMBIE) /* already dead */
172 goto out_tsk;
173 child->exit_code = SIGKILL;
174 goto out_wake_notrap;
175
176 case PTRACE_SINGLEBLOCK:
177 ret = -EIO;
178 if ((unsigned long) data > _NSIG)
179 goto out_tsk;
180 child->ptrace &= ~(PT_TRACESYS|PT_SINGLESTEP);
181 child->ptrace |= PT_BLOCKSTEP;
182 child->exit_code = data;
183
184 /* Enable taken branch trap. */
185 pa_psw(child)->r = 0;
186 pa_psw(child)->t = 1;
187 pa_psw(child)->h = 0;
188 pa_psw(child)->l = 0;
189 goto out_wake;
190
191 case PTRACE_SINGLESTEP:
192 ret = -EIO;
193 if ((unsigned long) data > _NSIG)
194 goto out_tsk;
195 child->ptrace &= ~(PT_TRACESYS|PT_BLOCKSTEP);
196 child->ptrace |= PT_SINGLESTEP;
197 child->exit_code = data;
198
199 if (pa_psw(child)->n) {
200 struct siginfo si;
201
202 /* Nullified, just crank over the queue. */
203 task_regs(child)->iaoq[0] = task_regs(child)->iaoq[1];
204 task_regs(child)->iasq[0] = task_regs(child)->iasq[1];
205 task_regs(child)->iaoq[1] = task_regs(child)->iaoq[0] + 4;
206 pa_psw(child)->n = 0;
207 pa_psw(child)->x = 0;
208 pa_psw(child)->y = 0;
209 pa_psw(child)->z = 0;
210 pa_psw(child)->b = 0;
211 pa_psw(child)->r = 0;
212 pa_psw(child)->t = 0;
213 pa_psw(child)->h = 0;
214 pa_psw(child)->l = 0;
215 /* Don't wake up the child, but let the
216 parent know something happened. */
217 si.si_code = TRAP_TRACE;
218 si.si_addr = (void *) (task_regs(child)->iaoq[0] & ~3);
219 si.si_signo = SIGTRAP;
220 si.si_errno = 0;
221 force_sig_info(SIGTRAP, &si, child);
222 //notify_parent(child, SIGCHLD);
223 //ret = 0;
224 goto out_wake;
225 }
226
227 /* Enable recovery counter traps. The recovery counter
228 * itself will be set to zero on a task switch. If the
229 * task is suspended on a syscall then the syscall return
230 * path will overwrite the recovery counter with a suitable
231 * value such that it traps once back in user space. We
232 * disable interrupts in the childs PSW here also, to avoid
233 * interrupts while the recovery counter is decrementing.
234 */
235 pa_psw(child)->r = 1;
236 pa_psw(child)->t = 0;
237 pa_psw(child)->h = 0;
238 pa_psw(child)->l = 0;
239 /* give it a chance to run. */
240 goto out_wake;
241
242 case PTRACE_DETACH:
243 ret = ptrace_detach(child, data);
244 goto out_tsk;
245
246 default:
247 ret = -EIO;
248 goto out_tsk;
249 }
250
251 out_wake_notrap:
252 /* make sure the trap bits are not set */
253 pa_psw(child)->r = 0;
254 pa_psw(child)->t = 0;
255 pa_psw(child)->h = 0;
256 pa_psw(child)->l = 0;
257 out_wake:
258 wake_up_process(child);
259 ret = 0;
260 out_tsk:
261 free_task_struct(child);
262 out:
263 unlock_kernel();
264 return ret;
265 }
266
267 void syscall_trace(void)
268 {
269 if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) !=
270 (PT_PTRACED|PT_TRACESYS))
271 return;
272 current->exit_code = SIGTRAP;
273 current->state = TASK_STOPPED;
274 notify_parent(current, SIGCHLD);
275 schedule();
276 /*
277 * this isn't the same as continuing with a signal, but it will do
278 * for normal use. strace only continues with a signal if the
279 * stopping signal is not SIGTRAP. -brl
280 */
281 if (current->exit_code) {
282 send_sig(current->exit_code, current, 1);
283 current->exit_code = 0;
284 }
285 }
286