]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - arch/xtensa/kernel/ptrace.c
[PATCH] unify sys_ptrace prototype
[mirror_ubuntu-bionic-kernel.git] / arch / xtensa / kernel / ptrace.c
CommitLineData
5a0015d6
CZ
1// TODO some minor issues
2/*
3 * This file is subject to the terms and conditions of the GNU General Public
4 * License. See the file "COPYING" in the main directory of this archive
5 * for more details.
6 *
7 * Copyright (C) 2001 - 2005 Tensilica Inc.
8 *
9 * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com>
10 * Chris Zankel <chris@zankel.net>
11 * Scott Foehner<sfoehner@yahoo.com>,
12 * Kevin Chea
13 * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca>
14 */
15
16#include <linux/config.h>
17#include <linux/kernel.h>
18#include <linux/sched.h>
19#include <linux/mm.h>
20#include <linux/errno.h>
21#include <linux/ptrace.h>
22#include <linux/smp.h>
23#include <linux/smp_lock.h>
24#include <linux/security.h>
0ee23b50 25#include <linux/signal.h>
5a0015d6
CZ
26
27#include <asm/pgtable.h>
28#include <asm/page.h>
29#include <asm/system.h>
30#include <asm/uaccess.h>
31#include <asm/ptrace.h>
32#include <asm/elf.h>
33
34#define TEST_KERNEL // verify kernel operations FIXME: remove
35
36
37/*
38 * Called by kernel/ptrace.c when detaching..
39 *
40 * Make sure single step bits etc are not set.
41 */
42
43void ptrace_disable(struct task_struct *child)
44{
45 /* Nothing to do.. */
46}
47
dfb7dac3 48long sys_ptrace(long request, long pid, long addr, long data)
5a0015d6
CZ
49{
50 struct task_struct *child;
51 int ret = -EPERM;
52
53 lock_kernel();
54
55#if 0
56 if ((int)request != 1)
57 printk("ptrace(r=%d,pid=%d,addr=%08lx,data=%08lx)\n",
58 (int) request, (int) pid, (unsigned long) addr,
59 (unsigned long) data);
60#endif
61
62 if (request == PTRACE_TRACEME) {
63
64 /* Are we already being traced? */
65
66 if (current->ptrace & PT_PTRACED)
67 goto out;
68
69 if ((ret = security_ptrace(current->parent, current)))
70 goto out;
71
72 /* Set the ptrace bit in the process flags. */
73
74 current->ptrace |= PT_PTRACED;
75 ret = 0;
76 goto out;
77 }
78
79 ret = -ESRCH;
80 read_lock(&tasklist_lock);
81 child = find_task_by_pid(pid);
82 if (child)
83 get_task_struct(child);
84 read_unlock(&tasklist_lock);
85 if (!child)
86 goto out;
87
88 ret = -EPERM;
89 if (pid == 1) /* you may not mess with init */
90 goto out;
91
92 if (request == PTRACE_ATTACH) {
93 ret = ptrace_attach(child);
94 goto out_tsk;
95 }
96
97 if ((ret = ptrace_check_attach(child, request == PTRACE_KILL)) < 0)
98 goto out_tsk;
99
100 switch (request) {
101 case PTRACE_PEEKTEXT: /* read word at location addr. */
102 case PTRACE_PEEKDATA:
103 {
104 unsigned long tmp;
105 int copied;
106
107 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
108 ret = -EIO;
109 if (copied != sizeof(tmp))
110 break;
111 ret = put_user(tmp,(unsigned long *) data);
112
113 goto out;
114 }
115
116 /* Read the word at location addr in the USER area. */
117
118 case PTRACE_PEEKUSR:
119 {
120 struct pt_regs *regs;
121 unsigned long tmp;
122
123 regs = xtensa_pt_regs(child);
124 tmp = 0; /* Default return value. */
125
126 switch(addr) {
127
128 case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
129 {
130 int ar = addr - REG_AR_BASE - regs->windowbase * 4;
131 ar &= (XCHAL_NUM_AREGS - 1);
132 if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0)
133 tmp = regs->areg[ar];
134 else
135 ret = -EIO;
136 break;
137 }
138 case REG_A_BASE ... REG_A_BASE + 15:
139 tmp = regs->areg[addr - REG_A_BASE];
140 break;
141 case REG_PC:
142 tmp = regs->pc;
143 break;
144 case REG_PS:
145 /* Note: PS.EXCM is not set while user task is running;
146 * its being set in regs is for exception handling
147 * convenience. */
148 tmp = (regs->ps & ~XCHAL_PS_EXCM_MASK);
149 break;
150 case REG_WB:
151 tmp = regs->windowbase;
152 break;
153 case REG_WS:
154 tmp = regs->windowstart;
155 break;
156 case REG_LBEG:
157 tmp = regs->lbeg;
158 break;
159 case REG_LEND:
160 tmp = regs->lend;
161 break;
162 case REG_LCOUNT:
163 tmp = regs->lcount;
164 break;
165 case REG_SAR:
166 tmp = regs->sar;
167 break;
168 case REG_DEPC:
169 tmp = regs->depc;
170 break;
171 case REG_EXCCAUSE:
172 tmp = regs->exccause;
173 break;
174 case REG_EXCVADDR:
175 tmp = regs->excvaddr;
176 break;
177 case SYSCALL_NR:
178 tmp = regs->syscall;
179 break;
180 default:
181 tmp = 0;
182 ret = -EIO;
183 goto out;
184 }
185 ret = put_user(tmp, (unsigned long *) data);
186 goto out;
187 }
188
189 case PTRACE_POKETEXT: /* write the word at location addr. */
190 case PTRACE_POKEDATA:
191 if (access_process_vm(child, addr, &data, sizeof(data), 1)
192 == sizeof(data))
193 break;
194 ret = -EIO;
195 goto out;
196
197 case PTRACE_POKEUSR:
198 {
199 struct pt_regs *regs;
200 regs = xtensa_pt_regs(child);
201
202 switch (addr) {
203 case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
204 {
205 int ar = addr - REG_AR_BASE - regs->windowbase * 4;
206 if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0)
207 regs->areg[ar & (XCHAL_NUM_AREGS - 1)] = data;
208 else
209 ret = -EIO;
210 break;
211 }
212 case REG_A_BASE ... REG_A_BASE + 15:
213 regs->areg[addr - REG_A_BASE] = data;
214 break;
215 case REG_PC:
216 regs->pc = data;
217 break;
218 case SYSCALL_NR:
219 regs->syscall = data;
220 break;
221#ifdef TEST_KERNEL
222 case REG_WB:
223 regs->windowbase = data;
224 break;
225 case REG_WS:
226 regs->windowstart = data;
227 break;
228#endif
229
230 default:
231 /* The rest are not allowed. */
232 ret = -EIO;
233 break;
234 }
235 break;
236 }
237
238 /* continue and stop at next (return from) syscall */
239 case PTRACE_SYSCALL:
240 case PTRACE_CONT: /* restart after signal. */
241 {
242 ret = -EIO;
0ee23b50 243 if (!valid_signal(data))
5a0015d6
CZ
244 break;
245 if (request == PTRACE_SYSCALL)
246 set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
247 else
248 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
249 child->exit_code = data;
250 /* Make sure the single step bit is not set. */
251 child->ptrace &= ~PT_SINGLESTEP;
252 wake_up_process(child);
253 ret = 0;
254 break;
255 }
256
257 /*
258 * make the child exit. Best I can do is send it a sigkill.
259 * perhaps it should be put in the status that it wants to
260 * exit.
261 */
262 case PTRACE_KILL:
263 ret = 0;
264 if (child->state == EXIT_ZOMBIE) /* already dead */
265 break;
266 child->exit_code = SIGKILL;
267 child->ptrace &= ~PT_SINGLESTEP;
268 wake_up_process(child);
269 break;
270
271 case PTRACE_SINGLESTEP:
272 ret = -EIO;
0ee23b50 273 if (!valid_signal(data))
5a0015d6
CZ
274 break;
275 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
276 child->ptrace |= PT_SINGLESTEP;
277 child->exit_code = data;
278 wake_up_process(child);
279 ret = 0;
280 break;
281
282 case PTRACE_GETREGS:
283 {
284 /* 'data' points to user memory in which to write.
285 * Mainly due to the non-live register values, we
286 * reformat the register values into something more
287 * standard. For convenience, we use the handy
288 * elf_gregset_t format. */
289
290 xtensa_gregset_t format;
291 struct pt_regs *regs = xtensa_pt_regs(child);
292
293 do_copy_regs (&format, regs, child);
294
295 /* Now, copy to user space nice and easy... */
296 ret = 0;
297 if (copy_to_user((void *)data, &format, sizeof(elf_gregset_t)))
298 ret = -EFAULT;
299 break;
300 }
301
302 case PTRACE_SETREGS:
303 {
304 /* 'data' points to user memory that contains the new
305 * values in the elf_gregset_t format. */
306
307 xtensa_gregset_t format;
308 struct pt_regs *regs = xtensa_pt_regs(child);
309
310 if (copy_from_user(&format,(void *)data,sizeof(elf_gregset_t))){
311 ret = -EFAULT;
312 break;
313 }
314
315 /* FIXME: Perhaps we want some sanity checks on
316 * these user-space values? See ARM version. Are
317 * debuggers a security concern? */
318
319 do_restore_regs (&format, regs, child);
320
321 ret = 0;
322 break;
323 }
324
325 case PTRACE_GETFPREGS:
326 {
327 /* 'data' points to user memory in which to write.
328 * For convenience, we use the handy
329 * elf_fpregset_t format. */
330
331 elf_fpregset_t fpregs;
332 struct pt_regs *regs = xtensa_pt_regs(child);
333
334 do_save_fpregs (&fpregs, regs, child);
335
336 /* Now, copy to user space nice and easy... */
337 ret = 0;
338 if (copy_to_user((void *)data, &fpregs, sizeof(elf_fpregset_t)))
339 ret = -EFAULT;
340
341 break;
342 }
343
344 case PTRACE_SETFPREGS:
345 {
346 /* 'data' points to user memory that contains the new
347 * values in the elf_fpregset_t format.
348 */
349 elf_fpregset_t fpregs;
350 struct pt_regs *regs = xtensa_pt_regs(child);
351
352 ret = 0;
353 if (copy_from_user(&fpregs, (void *)data, sizeof(elf_fpregset_t))) {
354 ret = -EFAULT;
355 break;
356 }
357
358 if (do_restore_fpregs (&fpregs, regs, child))
359 ret = -EIO;
360 break;
361 }
362
363 case PTRACE_GETFPREGSIZE:
364 /* 'data' points to 'unsigned long' set to the size
365 * of elf_fpregset_t
366 */
367 ret = put_user(sizeof(elf_fpregset_t), (unsigned long *) data);
368 break;
369
370 case PTRACE_DETACH: /* detach a process that was attached. */
371 ret = ptrace_detach(child, data);
372 break;
373
374 default:
375 ret = ptrace_request(child, request, addr, data);
376 goto out;
377 }
378out_tsk:
379 put_task_struct(child);
380out:
381 unlock_kernel();
382 return ret;
383}
384
385void do_syscall_trace(void)
386{
387 if (!test_thread_flag(TIF_SYSCALL_TRACE))
388 return;
389
390 if (!(current->ptrace & PT_PTRACED))
391 return;
392
393 /*
394 * The 0x80 provides a way for the tracing parent to distinguish
395 * between a syscall stop and SIGTRAP delivery
396 */
397 ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
398
399 /*
400 * this isn't the same as continuing with a signal, but it will do
401 * for normal use. strace only continues with a signal if the
402 * stopping signal is not SIGTRAP. -brl
403 */
404 if (current->exit_code) {
405 send_sig(current->exit_code, current, 1);
406 current->exit_code = 0;
407 }
408}