1 /*
2  *  linux/arch/cris/kernel/ptrace.c
3  *
4  * Parts taken from the m68k port.
5  *
6  * Copyright (c) 2000, 2001 Axis Communications AB
7  *
8  * Authors:   Bjorn Wesen
9  *
10  * $Log: ptrace.c,v $
11  * Revision 1.9  2003/10/01 11:34:23  aurer
12  * * Allow PTRACE_PEEKUSR and PTRACE_POKEUSR to access USP.
13  * * Removed nonsensical comment about ptrace behavior.
14  *
15  * Revision 1.8  2001/11/12 18:26:21  pkj
16  * Fixed compiler warnings.
17  *
18  * Revision 1.7  2001/09/26 11:53:49  bjornw
19  * PTRACE_DETACH works more simple in 2.4.10
20  *
21  * Revision 1.6  2001/07/25 16:08:47  bjornw
22  * PTRACE_ATTACH bulk moved into arch-independant code in 2.4.7
23  *
24  * Revision 1.5  2001/03/26 14:24:28  orjanf
25  * * Changed loop condition.
26  * * Added comment documenting non-standard ptrace behaviour.
27  *
28  * Revision 1.4  2001/03/20 19:44:41  bjornw
29  * Use the user_regs macro instead of thread.esp0
30  *
31  * Revision 1.3  2000/12/18 23:45:25  bjornw
32  * Linux/CRIS first version
33  *
34  *
35  */
36 
37 #include <linux/kernel.h>
38 #include <linux/sched.h>
39 #include <linux/mm.h>
40 #include <linux/smp.h>
41 #include <linux/smp_lock.h>
42 #include <linux/errno.h>
43 #include <linux/ptrace.h>
44 #include <linux/user.h>
45 
46 #include <asm/uaccess.h>
47 #include <asm/page.h>
48 #include <asm/pgtable.h>
49 #include <asm/system.h>
50 #include <asm/processor.h>
51 
52 /*
53  * does not yet catch signals sent when the child dies.
54  * in exit.c or in signal.c.
55  */
56 
57 /* determines which bits in DCCR the user has access to. */
58 /* 1 = access 0 = no access */
59 #define DCCR_MASK 0x0000001f     /* XNZVC */
60 
61 /*
62  * Get contents of register REGNO in task TASK.
63  */
get_reg(struct task_struct * task,unsigned int regno)64 static inline long get_reg(struct task_struct *task, unsigned int regno)
65 {
66 	/* USP is a special case, it's not in the pt_regs struct but
67 	 * in the tasks thread struct
68 	 */
69 
70 	if (regno == PT_USP)
71 		return task->thread.usp;
72 	else if (regno < PT_MAX)
73 		return ((unsigned long *)user_regs(task))[regno];
74 	else
75 		return 0;
76 }
77 
78 /*
79  * Write contents of register REGNO in task TASK.
80  */
put_reg(struct task_struct * task,unsigned int regno,unsigned long data)81 static inline int put_reg(struct task_struct *task, unsigned int regno,
82 			  unsigned long data)
83 {
84 	if (regno == PT_USP)
85 		task->thread.usp = data;
86 	else if (regno < PT_MAX)
87 		((unsigned long *)user_regs(task))[regno] = data;
88 	else
89 		return -1;
90 	return 0;
91 }
92 
93 /*
94  * Called by kernel/ptrace.c when detaching..
95  *
96  * Make sure the single step bit is not set.
97  */
ptrace_disable(struct task_struct * child)98 void ptrace_disable(struct task_struct *child)
99 {
100        /* Todo - pending singlesteps? */
101 }
102 
sys_ptrace(long request,long pid,long addr,long data)103 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
104 {
105 	struct task_struct *child;
106 	int ret;
107 
108 	lock_kernel();
109 	ret = -EPERM;
110 	if (request == PTRACE_TRACEME) {
111 		/* are we already being traced? */
112 		if (current->ptrace & PT_PTRACED)
113 			goto out;
114 		/* set the ptrace bit in the process flags. */
115 		current->ptrace |= PT_PTRACED;
116 		ret = 0;
117 		goto out;
118 	}
119 	ret = -ESRCH;
120 	read_lock(&tasklist_lock);
121 	child = find_task_by_pid(pid);
122 	if (child)
123 		get_task_struct(child);
124 	read_unlock(&tasklist_lock);
125 	if (!child)
126 		goto out;
127 	ret = -EPERM;
128 	if (pid == 1)		/* you may not mess with init */
129 		goto out_tsk;
130 	if (request == PTRACE_ATTACH) {
131 		ret = ptrace_attach(child);
132 		goto out_tsk;
133 	}
134 	ret = -ESRCH;
135 	if (!(child->ptrace & PT_PTRACED))
136 		goto out_tsk;
137 	if (child->state != TASK_STOPPED) {
138 		if (request != PTRACE_KILL)
139 			goto out_tsk;
140 	}
141 	if (child->p_pptr != current)
142 		goto out_tsk;
143 
144 	switch (request) {
145 	/* when I and D space are separate, these will need to be fixed. */
146 		case PTRACE_PEEKTEXT: /* read word at location addr. */
147 		case PTRACE_PEEKDATA: {
148 			unsigned long tmp;
149 			int copied;
150 
151 			copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
152 			ret = -EIO;
153 			if (copied != sizeof(tmp))
154 				break;
155 			ret = put_user(tmp,(unsigned long *) data);
156 			break;
157 		}
158 
159 		/* read the word at location addr in the USER area. */
160 		case PTRACE_PEEKUSR: {
161 			unsigned long tmp;
162 
163 			ret = -EIO;
164 			if ((addr & 3) || addr < 0 || addr > PT_MAX << 2)
165 				break;
166 
167 			tmp = get_reg(child, addr >> 2);
168 			ret = put_user(tmp, (unsigned long *)data);
169 			break;
170 		}
171 
172 		/* when I and D space are separate, this will have to be fixed. */
173 		case PTRACE_POKETEXT: /* write the word at location addr. */
174 		case PTRACE_POKEDATA:
175 			ret = 0;
176 			if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
177 				break;
178 			ret = -EIO;
179 			break;
180 
181 		case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
182 			ret = -EIO;
183 			if ((addr & 3) || addr < 0 || addr > PT_MAX << 2)
184 				break;
185 
186 			addr >>= 2;
187 
188 			if (addr == PT_DCCR) {
189 				/* don't allow the tracing process to change stuff like
190 				 * interrupt enable, kernel/user bit, dma enables etc.
191 				 */
192 				data &= DCCR_MASK;
193 				data |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
194 			}
195 			if (put_reg(child, addr, data))
196 				break;
197 			ret = 0;
198 			break;
199 
200 		case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
201 		case PTRACE_CONT: /* restart after signal. */
202 			ret = -EIO;
203 			if ((unsigned long) data > _NSIG)
204 				break;
205 			if (request == PTRACE_SYSCALL)
206 				child->ptrace |= PT_TRACESYS;
207 			else
208 				child->ptrace &= ~PT_TRACESYS;
209 			child->exit_code = data;
210 			/* TODO: make sure any pending breakpoint is killed */
211 			wake_up_process(child);
212 			ret = 0;
213 			break;
214 
215 /*
216  * make the child exit.  Best I can do is send it a sigkill.
217  * perhaps it should be put in the status that it wants to
218  * exit.
219  */
220 		case PTRACE_KILL:
221 			ret = 0;
222 			if (child->state == TASK_ZOMBIE) /* already dead */
223 				break;
224 			child->exit_code = SIGKILL;
225 			/* TODO: make sure any pending breakpoint is killed */
226 			wake_up_process(child);
227 			break;
228 
229 		case PTRACE_SINGLESTEP: /* set the trap flag. */
230 			ret = -EIO;
231 			if ((unsigned long) data > _NSIG)
232 				break;
233 			child->ptrace &= ~PT_TRACESYS;
234 
235 			/* TODO: set some clever breakpoint mechanism... */
236 
237 			child->exit_code = data;
238 			/* give it a chance to run. */
239 			wake_up_process(child);
240 			ret = 0;
241 			break;
242 
243 		case PTRACE_DETACH:
244 			ret = ptrace_detach(child, data);
245 			break;
246 
247 		case PTRACE_GETREGS: { /* Get all gp regs from the child. */
248 		  	int i;
249 			unsigned long tmp;
250 			for (i = 0; i <= PT_MAX; i++) {
251 				tmp = get_reg(child, i);
252 				if (put_user(tmp, (unsigned long *) data)) {
253 					ret = -EFAULT;
254 					break;
255 				}
256 				data += sizeof(long);
257 			}
258 			ret = 0;
259 			break;
260 		}
261 
262 		case PTRACE_SETREGS: { /* Set all gp regs in the child. */
263 			int i;
264 			unsigned long tmp;
265 			for (i = 0; i <= PT_MAX; i++) {
266 				if (get_user(tmp, (unsigned long *) data)) {
267 					ret = -EFAULT;
268 					break;
269 				}
270 				if (i == PT_DCCR) {
271 					tmp &= DCCR_MASK;
272 					tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK;
273 				}
274 				put_reg(child, i, tmp);
275 				data += sizeof(long);
276 			}
277 			ret = 0;
278 			break;
279 		}
280 
281 		default:
282 			ret = -EIO;
283 			break;
284 	}
285 out_tsk:
286 	free_task_struct(child);
287 out:
288 	unlock_kernel();
289 	return ret;
290 }
291 
syscall_trace(void)292 asmlinkage void syscall_trace(void)
293 {
294 	if ((current->ptrace & (PT_PTRACED | PT_TRACESYS)) !=
295 	    (PT_PTRACED | PT_TRACESYS))
296 		return;
297 	/* TODO: make a way to distinguish between a syscall stop and SIGTRAP
298 	 * delivery like in the i386 port ?
299 	 */
300 	current->exit_code = SIGTRAP;
301 	current->state = TASK_STOPPED;
302 	notify_parent(current, SIGCHLD);
303 	schedule();
304 	/*
305 	 * this isn't the same as continuing with a signal, but it will do
306 	 * for normal use.  strace only continues with a signal if the
307 	 * stopping signal is not SIGTRAP.  -brl
308 	 */
309 	if (current->exit_code) {
310 		send_sig(current->exit_code, current, 1);
311 		current->exit_code = 0;
312 	}
313 }
314