xref: /linux/arch/xtensa/kernel/ptrace.c (revision 858259cf7d1c443c836a2022b78cb281f0a9b95e)
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>
25 #include <linux/signal.h>
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 
43 void ptrace_disable(struct task_struct *child)
44 {
45 	/* Nothing to do.. */
46 }
47 
48 long sys_ptrace(long request, long pid, long addr, long data)
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;
243 		if (!valid_signal(data))
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;
273 		if (!valid_signal(data))
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 	}
378 out_tsk:
379 	put_task_struct(child);
380 out:
381 	unlock_kernel();
382 	return ret;
383 }
384 
385 void 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 }
409