xref: /openbmc/linux/arch/sh/kernel/ptrace.c (revision 87c2ce3b)
1 /*
2  * linux/arch/sh/kernel/ptrace.c
3  *
4  * Original x86 implementation:
5  *	By Ross Biro 1/23/92
6  *	edited by Linus Torvalds
7  *
8  * SuperH version:   Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
9  *
10  */
11 
12 #include <linux/config.h>
13 #include <linux/kernel.h>
14 #include <linux/sched.h>
15 #include <linux/mm.h>
16 #include <linux/smp.h>
17 #include <linux/smp_lock.h>
18 #include <linux/errno.h>
19 #include <linux/ptrace.h>
20 #include <linux/user.h>
21 #include <linux/slab.h>
22 #include <linux/security.h>
23 #include <linux/signal.h>
24 
25 #include <asm/io.h>
26 #include <asm/uaccess.h>
27 #include <asm/pgtable.h>
28 #include <asm/system.h>
29 #include <asm/processor.h>
30 #include <asm/mmu_context.h>
31 
32 /*
33  * does not yet catch signals sent when the child dies.
34  * in exit.c or in signal.c.
35  */
36 
37 /*
38  * This routine will get a word off of the process kernel stack.
39  */
40 static inline int get_stack_long(struct task_struct *task, int offset)
41 {
42 	unsigned char *stack;
43 
44 	stack = (unsigned char *)
45 		task->thread_info + THREAD_SIZE	- sizeof(struct pt_regs)
46 #ifdef CONFIG_SH_DSP
47 		- sizeof(struct pt_dspregs)
48 #endif
49 		- sizeof(unsigned long);
50 	stack += offset;
51 	return (*((int *)stack));
52 }
53 
54 /*
55  * This routine will put a word on the process kernel stack.
56  */
57 static inline int put_stack_long(struct task_struct *task, int offset,
58 				 unsigned long data)
59 {
60 	unsigned char *stack;
61 
62 	stack = (unsigned char *)
63 		task->thread_info + THREAD_SIZE - sizeof(struct pt_regs)
64 #ifdef CONFIG_SH_DSP
65 		- sizeof(struct pt_dspregs)
66 #endif
67 		- sizeof(unsigned long);
68 	stack += offset;
69 	*(unsigned long *) stack = data;
70 	return 0;
71 }
72 
73 /*
74  * Called by kernel/ptrace.c when detaching..
75  *
76  * Make sure single step bits etc are not set.
77  */
78 void ptrace_disable(struct task_struct *child)
79 {
80 	/* nothing to do.. */
81 }
82 
83 long arch_ptrace(struct task_struct *child, long request, long addr, long data)
84 {
85 	struct user * dummy = NULL;
86 	int ret;
87 
88 	switch (request) {
89 	/* when I and D space are separate, these will need to be fixed. */
90 	case PTRACE_PEEKTEXT: /* read word at location addr. */
91 	case PTRACE_PEEKDATA: {
92 		unsigned long tmp;
93 		int copied;
94 
95 		copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
96 		ret = -EIO;
97 		if (copied != sizeof(tmp))
98 			break;
99 		ret = put_user(tmp,(unsigned long *) data);
100 		break;
101 	}
102 
103 	/* read the word at location addr in the USER area. */
104 	case PTRACE_PEEKUSR: {
105 		unsigned long tmp;
106 
107 		ret = -EIO;
108 		if ((addr & 3) || addr < 0 ||
109 		    addr > sizeof(struct user) - 3)
110 			break;
111 
112 		if (addr < sizeof(struct pt_regs))
113 			tmp = get_stack_long(child, addr);
114 		else if (addr >= (long) &dummy->fpu &&
115 			 addr < (long) &dummy->u_fpvalid) {
116 			if (!tsk_used_math(child)) {
117 				if (addr == (long)&dummy->fpu.fpscr)
118 					tmp = FPSCR_INIT;
119 				else
120 					tmp = 0;
121 			} else
122 				tmp = ((long *)&child->thread.fpu)
123 					[(addr - (long)&dummy->fpu) >> 2];
124 		} else if (addr == (long) &dummy->u_fpvalid)
125 			tmp = !!tsk_used_math(child);
126 		else
127 			tmp = 0;
128 		ret = put_user(tmp, (unsigned long *)data);
129 		break;
130 	}
131 
132 	/* when I and D space are separate, this will have to be fixed. */
133 	case PTRACE_POKETEXT: /* write the word at location addr. */
134 	case PTRACE_POKEDATA:
135 		ret = 0;
136 		if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
137 			break;
138 		ret = -EIO;
139 		break;
140 
141 	case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
142 		ret = -EIO;
143 		if ((addr & 3) || addr < 0 ||
144 		    addr > sizeof(struct user) - 3)
145 			break;
146 
147 		if (addr < sizeof(struct pt_regs))
148 			ret = put_stack_long(child, addr, data);
149 		else if (addr >= (long) &dummy->fpu &&
150 			 addr < (long) &dummy->u_fpvalid) {
151 			set_stopped_child_used_math(child);
152 			((long *)&child->thread.fpu)
153 				[(addr - (long)&dummy->fpu) >> 2] = data;
154 			ret = 0;
155 		} else if (addr == (long) &dummy->u_fpvalid) {
156 			conditional_stopped_child_used_math(data, child);
157 			ret = 0;
158 		}
159 		break;
160 
161 	case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
162 	case PTRACE_CONT: { /* restart after signal. */
163 		ret = -EIO;
164 		if (!valid_signal(data))
165 			break;
166 		if (request == PTRACE_SYSCALL)
167 			set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
168 		else
169 			clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
170 		child->exit_code = data;
171 		wake_up_process(child);
172 		ret = 0;
173 		break;
174 	}
175 
176 /*
177  * make the child exit.  Best I can do is send it a sigkill.
178  * perhaps it should be put in the status that it wants to
179  * exit.
180  */
181 	case PTRACE_KILL: {
182 		ret = 0;
183 		if (child->exit_state == EXIT_ZOMBIE)	/* already dead */
184 			break;
185 		child->exit_code = SIGKILL;
186 		wake_up_process(child);
187 		break;
188 	}
189 
190 	case PTRACE_SINGLESTEP: {  /* set the trap flag. */
191 		long pc;
192 		struct pt_regs *dummy = NULL;
193 
194 		ret = -EIO;
195 		if (!valid_signal(data))
196 			break;
197 		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
198 		if ((child->ptrace & PT_DTRACE) == 0) {
199 			/* Spurious delayed TF traps may occur */
200 			child->ptrace |= PT_DTRACE;
201 		}
202 
203 		pc = get_stack_long(child, (long)&dummy->pc);
204 
205 		/* Next scheduling will set up UBC */
206 		if (child->thread.ubc_pc == 0)
207 			ubc_usercnt += 1;
208 		child->thread.ubc_pc = pc;
209 
210 		child->exit_code = data;
211 		/* give it a chance to run. */
212 		wake_up_process(child);
213 		ret = 0;
214 		break;
215 	}
216 
217 	case PTRACE_DETACH: /* detach a process that was attached. */
218 		ret = ptrace_detach(child, data);
219 		break;
220 
221 #ifdef CONFIG_SH_DSP
222 	case PTRACE_GETDSPREGS: {
223 		unsigned long dp;
224 
225 		ret = -EIO;
226 		dp = ((unsigned long) child) + THREAD_SIZE -
227 			 sizeof(struct pt_dspregs);
228 		if (*((int *) (dp - 4)) == SR_FD) {
229 			copy_to_user(addr, (void *) dp,
230 				sizeof(struct pt_dspregs));
231 			ret = 0;
232 		}
233 		break;
234 	}
235 
236 	case PTRACE_SETDSPREGS: {
237 		unsigned long dp;
238 		int i;
239 
240 		ret = -EIO;
241 		dp = ((unsigned long) child) + THREAD_SIZE -
242 			 sizeof(struct pt_dspregs);
243 		if (*((int *) (dp - 4)) == SR_FD) {
244 			copy_from_user((void *) dp, addr,
245 				sizeof(struct pt_dspregs));
246 			ret = 0;
247 		}
248 		break;
249 	}
250 #endif
251 	default:
252 		ret = ptrace_request(child, request, addr, data);
253 		break;
254 	}
255 
256 	return ret;
257 }
258 
259 asmlinkage void do_syscall_trace(void)
260 {
261 	struct task_struct *tsk = current;
262 
263 	if (!test_thread_flag(TIF_SYSCALL_TRACE))
264 		return;
265 	if (!(tsk->ptrace & PT_PTRACED))
266 		return;
267 	/* the 0x80 provides a way for the tracing parent to distinguish
268 	   between a syscall stop and SIGTRAP delivery */
269 	ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
270 				 ? 0x80 : 0));
271 
272 	/*
273 	 * this isn't the same as continuing with a signal, but it will do
274 	 * for normal use.  strace only continues with a signal if the
275 	 * stopping signal is not SIGTRAP.  -brl
276 	 */
277 	if (tsk->exit_code) {
278 		send_sig(tsk->exit_code, tsk, 1);
279 		tsk->exit_code = 0;
280 	}
281 }
282