aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mn10300/kernel/ptrace.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2008-02-08 07:19:31 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-08 12:22:30 -0500
commitb920de1b77b72ca9432ac3f97edb26541e65e5dd (patch)
tree40fa9be1470e929c47927dea7eddf184c0204229 /arch/mn10300/kernel/ptrace.c
parentef3d534754f31fed9c3b976fee1ece1b3bc38282 (diff)
mn10300: add the MN10300/AM33 architecture to the kernel
Add architecture support for the MN10300/AM33 CPUs produced by MEI to the kernel. This patch also adds board support for the ASB2303 with the ASB2308 daughter board, and the ASB2305. The only processor supported is the MN103E010, which is an AM33v2 core plus on-chip devices. [akpm@linux-foundation.org: nuke cvs control strings] Signed-off-by: Masakazu Urade <urade.masakazu@jp.panasonic.com> Signed-off-by: Koichi Yasutake <yasutake.koichi@jp.panasonic.com> Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/mn10300/kernel/ptrace.c')
-rw-r--r--arch/mn10300/kernel/ptrace.c379
1 files changed, 379 insertions, 0 deletions
diff --git a/arch/mn10300/kernel/ptrace.c b/arch/mn10300/kernel/ptrace.c
new file mode 100644
index 000000000000..d6d6cdc75c52
--- /dev/null
+++ b/arch/mn10300/kernel/ptrace.c
@@ -0,0 +1,379 @@
1/* MN10300 Process tracing
2 *
3 * Copyright (C) 2007 Matsushita Electric Industrial Co., Ltd.
4 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
5 * Modified by David Howells (dhowells@redhat.com)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public Licence
9 * as published by the Free Software Foundation; either version
10 * 2 of the Licence, or (at your option) any later version.
11 */
12#include <linux/kernel.h>
13#include <linux/sched.h>
14#include <linux/mm.h>
15#include <linux/smp.h>
16#include <linux/smp_lock.h>
17#include <linux/errno.h>
18#include <linux/ptrace.h>
19#include <linux/user.h>
20#include <asm/uaccess.h>
21#include <asm/pgtable.h>
22#include <asm/system.h>
23#include <asm/processor.h>
24#include <asm/cacheflush.h>
25#include <asm/fpu.h>
26#include <asm/asm-offsets.h>
27
28/*
29 * translate ptrace register IDs into struct pt_regs offsets
30 */
31static const u8 ptrace_regid_to_frame[] = {
32 [PT_A3 << 2] = REG_A3,
33 [PT_A2 << 2] = REG_A2,
34 [PT_D3 << 2] = REG_D3,
35 [PT_D2 << 2] = REG_D2,
36 [PT_MCVF << 2] = REG_MCVF,
37 [PT_MCRL << 2] = REG_MCRL,
38 [PT_MCRH << 2] = REG_MCRH,
39 [PT_MDRQ << 2] = REG_MDRQ,
40 [PT_E1 << 2] = REG_E1,
41 [PT_E0 << 2] = REG_E0,
42 [PT_E7 << 2] = REG_E7,
43 [PT_E6 << 2] = REG_E6,
44 [PT_E5 << 2] = REG_E5,
45 [PT_E4 << 2] = REG_E4,
46 [PT_E3 << 2] = REG_E3,
47 [PT_E2 << 2] = REG_E2,
48 [PT_SP << 2] = REG_SP,
49 [PT_LAR << 2] = REG_LAR,
50 [PT_LIR << 2] = REG_LIR,
51 [PT_MDR << 2] = REG_MDR,
52 [PT_A1 << 2] = REG_A1,
53 [PT_A0 << 2] = REG_A0,
54 [PT_D1 << 2] = REG_D1,
55 [PT_D0 << 2] = REG_D0,
56 [PT_ORIG_D0 << 2] = REG_ORIG_D0,
57 [PT_EPSW << 2] = REG_EPSW,
58 [PT_PC << 2] = REG_PC,
59};
60
61static inline int get_stack_long(struct task_struct *task, int offset)
62{
63 return *(unsigned long *)
64 ((unsigned long) task->thread.uregs + offset);
65}
66
67/*
68 * this routine will put a word on the processes privileged stack.
69 * the offset is how far from the base addr as stored in the TSS.
70 * this routine assumes that all the privileged stacks are in our
71 * data space.
72 */
73static inline
74int put_stack_long(struct task_struct *task, int offset, unsigned long data)
75{
76 unsigned long stack;
77
78 stack = (unsigned long) task->thread.uregs + offset;
79 *(unsigned long *) stack = data;
80 return 0;
81}
82
83static inline unsigned long get_fpregs(struct fpu_state_struct *buf,
84 struct task_struct *tsk)
85{
86 return __copy_to_user(buf, &tsk->thread.fpu_state,
87 sizeof(struct fpu_state_struct));
88}
89
90static inline unsigned long set_fpregs(struct task_struct *tsk,
91 struct fpu_state_struct *buf)
92{
93 return __copy_from_user(&tsk->thread.fpu_state, buf,
94 sizeof(struct fpu_state_struct));
95}
96
97static inline void fpsave_init(struct task_struct *task)
98{
99 memset(&task->thread.fpu_state, 0, sizeof(struct fpu_state_struct));
100}
101
102/*
103 * make sure the single step bit is not set
104 */
105void ptrace_disable(struct task_struct *child)
106{
107#ifndef CONFIG_MN10300_USING_JTAG
108 struct user *dummy = NULL;
109 long tmp;
110
111 tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw);
112 tmp &= ~EPSW_T;
113 put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp);
114#endif
115}
116
117/*
118 * set the single step bit
119 */
120void ptrace_enable(struct task_struct *child)
121{
122#ifndef CONFIG_MN10300_USING_JTAG
123 struct user *dummy = NULL;
124 long tmp;
125
126 tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw);
127 tmp |= EPSW_T;
128 put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp);
129#endif
130}
131
132/*
133 * handle the arch-specific side of process tracing
134 */
135long arch_ptrace(struct task_struct *child, long request, long addr, long data)
136{
137 struct fpu_state_struct fpu_state;
138 int i, ret;
139
140 switch (request) {
141 /* read the word at location addr. */
142 case PTRACE_PEEKTEXT: {
143 unsigned long tmp;
144 int copied;
145
146 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
147 ret = -EIO;
148 if (copied != sizeof(tmp))
149 break;
150 ret = put_user(tmp, (unsigned long *) data);
151 break;
152 }
153
154 /* read the word at location addr. */
155 case PTRACE_PEEKDATA: {
156 unsigned long tmp;
157 int copied;
158
159 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
160 ret = -EIO;
161 if (copied != sizeof(tmp))
162 break;
163 ret = put_user(tmp, (unsigned long *) data);
164 break;
165 }
166
167 /* read the word at location addr in the USER area. */
168 case PTRACE_PEEKUSR: {
169 unsigned long tmp;
170
171 ret = -EIO;
172 if ((addr & 3) || addr < 0 ||
173 addr > sizeof(struct user) - 3)
174 break;
175
176 tmp = 0; /* Default return condition */
177 if (addr < NR_PTREGS << 2)
178 tmp = get_stack_long(child,
179 ptrace_regid_to_frame[addr]);
180 ret = put_user(tmp, (unsigned long *) data);
181 break;
182 }
183
184 /* write the word at location addr. */
185 case PTRACE_POKETEXT:
186 case PTRACE_POKEDATA:
187 if (access_process_vm(child, addr, &data, sizeof(data), 1) ==
188 sizeof(data))
189 ret = 0;
190 else
191 ret = -EIO;
192 break;
193
194 /* write the word at location addr in the USER area */
195 case PTRACE_POKEUSR:
196 ret = -EIO;
197 if ((addr & 3) || addr < 0 ||
198 addr > sizeof(struct user) - 3)
199 break;
200
201 ret = 0;
202 if (addr < NR_PTREGS << 2)
203 ret = put_stack_long(child, ptrace_regid_to_frame[addr],
204 data);
205 break;
206
207 /* continue and stop at next (return from) syscall */
208 case PTRACE_SYSCALL:
209 /* restart after signal. */
210 case PTRACE_CONT:
211 ret = -EIO;
212 if ((unsigned long) data > _NSIG)
213 break;
214 if (request == PTRACE_SYSCALL)
215 set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
216 else
217 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
218 child->exit_code = data;
219 ptrace_disable(child);
220 wake_up_process(child);
221 ret = 0;
222 break;
223
224 /*
225 * make the child exit
226 * - the best I can do is send it a sigkill
227 * - perhaps it should be put in the status that it wants to
228 * exit
229 */
230 case PTRACE_KILL:
231 ret = 0;
232 if (child->exit_state == EXIT_ZOMBIE) /* already dead */
233 break;
234 child->exit_code = SIGKILL;
235 clear_tsk_thread_flag(child, TIF_SINGLESTEP);
236 ptrace_disable(child);
237 wake_up_process(child);
238 break;
239
240 case PTRACE_SINGLESTEP: /* set the trap flag. */
241#ifndef CONFIG_MN10300_USING_JTAG
242 ret = -EIO;
243 if ((unsigned long) data > _NSIG)
244 break;
245 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
246 ptrace_enable(child);
247 child->exit_code = data;
248 wake_up_process(child);
249 ret = 0;
250#else
251 ret = -EINVAL;
252#endif
253 break;
254
255 case PTRACE_DETACH: /* detach a process that was attached. */
256 ret = ptrace_detach(child, data);
257 break;
258
259 /* Get all gp regs from the child. */
260 case PTRACE_GETREGS: {
261 unsigned long tmp;
262
263 if (!access_ok(VERIFY_WRITE, (unsigned *) data, NR_PTREGS << 2)) {
264 ret = -EIO;
265 break;
266 }
267
268 for (i = 0; i < NR_PTREGS << 2; i += 4) {
269 tmp = get_stack_long(child, ptrace_regid_to_frame[i]);
270 __put_user(tmp, (unsigned long *) data);
271 data += sizeof(tmp);
272 }
273 ret = 0;
274 break;
275 }
276
277 case PTRACE_SETREGS: { /* Set all gp regs in the child. */
278 unsigned long tmp;
279
280 if (!access_ok(VERIFY_READ, (unsigned long *)data,
281 sizeof(struct pt_regs))) {
282 ret = -EIO;
283 break;
284 }
285
286 for (i = 0; i < NR_PTREGS << 2; i += 4) {
287 __get_user(tmp, (unsigned long *) data);
288 put_stack_long(child, ptrace_regid_to_frame[i], tmp);
289 data += sizeof(tmp);
290 }
291 ret = 0;
292 break;
293 }
294
295 case PTRACE_GETFPREGS: { /* Get the child FPU state. */
296 if (is_using_fpu(child)) {
297 unlazy_fpu(child);
298 fpu_state = child->thread.fpu_state;
299 } else {
300 memset(&fpu_state, 0, sizeof(fpu_state));
301 }
302
303 ret = -EIO;
304 if (copy_to_user((void *) data, &fpu_state,
305 sizeof(fpu_state)) == 0)
306 ret = 0;
307 break;
308 }
309
310 case PTRACE_SETFPREGS: { /* Set the child FPU state. */
311 ret = -EFAULT;
312 if (copy_from_user(&fpu_state, (const void *) data,
313 sizeof(fpu_state)) == 0) {
314 fpu_kill_state(child);
315 child->thread.fpu_state = fpu_state;
316 set_using_fpu(child);
317 ret = 0;
318 }
319 break;
320 }
321
322 case PTRACE_SETOPTIONS: {
323 if (data & PTRACE_O_TRACESYSGOOD)
324 child->ptrace |= PT_TRACESYSGOOD;
325 else
326 child->ptrace &= ~PT_TRACESYSGOOD;
327 ret = 0;
328 break;
329 }
330
331 default:
332 ret = -EIO;
333 break;
334 }
335
336 return ret;
337}
338
339/*
340 * notification of system call entry/exit
341 * - triggered by current->work.syscall_trace
342 */
343asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
344{
345#if 0
346 /* just in case... */
347 printk(KERN_DEBUG "[%d] syscall_%lu(%lx,%lx,%lx,%lx) = %lx\n",
348 current->pid,
349 regs->orig_d0,
350 regs->a0,
351 regs->d1,
352 regs->a3,
353 regs->a2,
354 regs->d0);
355 return;
356#endif
357
358 if (!test_thread_flag(TIF_SYSCALL_TRACE) &&
359 !test_thread_flag(TIF_SINGLESTEP))
360 return;
361 if (!(current->ptrace & PT_PTRACED))
362 return;
363
364 /* the 0x80 provides a way for the tracing parent to distinguish
365 between a syscall stop and SIGTRAP delivery */
366 ptrace_notify(SIGTRAP |
367 ((current->ptrace & PT_TRACESYSGOOD) &&
368 !test_thread_flag(TIF_SINGLESTEP) ? 0x80 : 0));
369
370 /*
371 * this isn't the same as continuing with a signal, but it will do
372 * for normal use. strace only continues with a signal if the
373 * stopping signal is not SIGTRAP. -brl
374 */
375 if (current->exit_code) {
376 send_sig(current->exit_code, current, 1);
377 current->exit_code = 0;
378 }
379}