]> bbs.cooldavid.org Git - net-next-2.6.git/blame - arch/mn10300/kernel/ptrace.c
ptrace: cleanup arch_ptrace() on mn10300
[net-next-2.6.git] / arch / mn10300 / kernel / ptrace.c
CommitLineData
b920de1b
DH
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>
b920de1b
DH
16#include <linux/errno.h>
17#include <linux/ptrace.h>
18#include <linux/user.h>
5d289964
DH
19#include <linux/regset.h>
20#include <linux/elf.h>
21#include <linux/tracehook.h>
b920de1b
DH
22#include <asm/uaccess.h>
23#include <asm/pgtable.h>
24#include <asm/system.h>
25#include <asm/processor.h>
26#include <asm/cacheflush.h>
27#include <asm/fpu.h>
28#include <asm/asm-offsets.h>
29
30/*
31 * translate ptrace register IDs into struct pt_regs offsets
32 */
33static const u8 ptrace_regid_to_frame[] = {
34 [PT_A3 << 2] = REG_A3,
35 [PT_A2 << 2] = REG_A2,
36 [PT_D3 << 2] = REG_D3,
37 [PT_D2 << 2] = REG_D2,
38 [PT_MCVF << 2] = REG_MCVF,
39 [PT_MCRL << 2] = REG_MCRL,
40 [PT_MCRH << 2] = REG_MCRH,
41 [PT_MDRQ << 2] = REG_MDRQ,
42 [PT_E1 << 2] = REG_E1,
43 [PT_E0 << 2] = REG_E0,
44 [PT_E7 << 2] = REG_E7,
45 [PT_E6 << 2] = REG_E6,
46 [PT_E5 << 2] = REG_E5,
47 [PT_E4 << 2] = REG_E4,
48 [PT_E3 << 2] = REG_E3,
49 [PT_E2 << 2] = REG_E2,
50 [PT_SP << 2] = REG_SP,
51 [PT_LAR << 2] = REG_LAR,
52 [PT_LIR << 2] = REG_LIR,
53 [PT_MDR << 2] = REG_MDR,
54 [PT_A1 << 2] = REG_A1,
55 [PT_A0 << 2] = REG_A0,
56 [PT_D1 << 2] = REG_D1,
57 [PT_D0 << 2] = REG_D0,
58 [PT_ORIG_D0 << 2] = REG_ORIG_D0,
59 [PT_EPSW << 2] = REG_EPSW,
60 [PT_PC << 2] = REG_PC,
61};
62
63static inline int get_stack_long(struct task_struct *task, int offset)
64{
65 return *(unsigned long *)
66 ((unsigned long) task->thread.uregs + offset);
67}
68
b920de1b
DH
69static inline
70int put_stack_long(struct task_struct *task, int offset, unsigned long data)
71{
72 unsigned long stack;
73
74 stack = (unsigned long) task->thread.uregs + offset;
75 *(unsigned long *) stack = data;
76 return 0;
77}
78
5d289964
DH
79/*
80 * retrieve the contents of MN10300 userspace general registers
81 */
82static int genregs_get(struct task_struct *target,
83 const struct user_regset *regset,
84 unsigned int pos, unsigned int count,
85 void *kbuf, void __user *ubuf)
b920de1b 86{
5d289964
DH
87 const struct pt_regs *regs = task_pt_regs(target);
88 int ret;
89
90 /* we need to skip regs->next */
91 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
92 regs, 0, PT_ORIG_D0 * sizeof(long));
93 if (ret < 0)
94 return ret;
95
96 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
97 &regs->orig_d0, PT_ORIG_D0 * sizeof(long),
98 NR_PTREGS * sizeof(long));
99 if (ret < 0)
100 return ret;
101
102 return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
103 NR_PTREGS * sizeof(long), -1);
b920de1b
DH
104}
105
5d289964
DH
106/*
107 * update the contents of the MN10300 userspace general registers
108 */
109static int genregs_set(struct task_struct *target,
110 const struct user_regset *regset,
111 unsigned int pos, unsigned int count,
112 const void *kbuf, const void __user *ubuf)
b920de1b 113{
5d289964
DH
114 struct pt_regs *regs = task_pt_regs(target);
115 unsigned long tmp;
116 int ret;
117
118 /* we need to skip regs->next */
119 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
120 regs, 0, PT_ORIG_D0 * sizeof(long));
121 if (ret < 0)
122 return ret;
123
124 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
125 &regs->orig_d0, PT_ORIG_D0 * sizeof(long),
126 PT_EPSW * sizeof(long));
127 if (ret < 0)
128 return ret;
129
130 /* we need to mask off changes to EPSW */
131 tmp = regs->epsw;
132 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
133 &tmp, PT_EPSW * sizeof(long),
134 PT_PC * sizeof(long));
135 tmp &= EPSW_FLAG_V | EPSW_FLAG_C | EPSW_FLAG_N | EPSW_FLAG_Z;
136 tmp |= regs->epsw & ~(EPSW_FLAG_V | EPSW_FLAG_C | EPSW_FLAG_N |
137 EPSW_FLAG_Z);
138 regs->epsw = tmp;
139
140 if (ret < 0)
141 return ret;
142
143 /* and finally load the PC */
144 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
145 &regs->pc, PT_PC * sizeof(long),
146 NR_PTREGS * sizeof(long));
147
148 if (ret < 0)
149 return ret;
150
151 return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
152 NR_PTREGS * sizeof(long), -1);
b920de1b
DH
153}
154
5d289964
DH
155/*
156 * retrieve the contents of MN10300 userspace FPU registers
157 */
158static int fpuregs_get(struct task_struct *target,
159 const struct user_regset *regset,
160 unsigned int pos, unsigned int count,
161 void *kbuf, void __user *ubuf)
b920de1b 162{
5d289964
DH
163 const struct fpu_state_struct *fpregs = &target->thread.fpu_state;
164 int ret;
165
166 unlazy_fpu(target);
167
168 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
169 fpregs, 0, sizeof(*fpregs));
170 if (ret < 0)
171 return ret;
172
173 return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf,
174 sizeof(*fpregs), -1);
b920de1b
DH
175}
176
177/*
5d289964 178 * update the contents of the MN10300 userspace FPU registers
b920de1b 179 */
5d289964
DH
180static int fpuregs_set(struct task_struct *target,
181 const struct user_regset *regset,
182 unsigned int pos, unsigned int count,
183 const void *kbuf, const void __user *ubuf)
184{
185 struct fpu_state_struct fpu_state = target->thread.fpu_state;
186 int ret;
187
188 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
189 &fpu_state, 0, sizeof(fpu_state));
190 if (ret < 0)
191 return ret;
192
193 fpu_kill_state(target);
194 target->thread.fpu_state = fpu_state;
195 set_using_fpu(target);
196
197 return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf,
198 sizeof(fpu_state), -1);
199}
200
201/*
202 * determine if the FPU registers have actually been used
203 */
204static int fpuregs_active(struct task_struct *target,
205 const struct user_regset *regset)
206{
207 return is_using_fpu(target) ? regset->n : 0;
208}
209
210/*
211 * Define the register sets available on the MN10300 under Linux
212 */
213enum mn10300_regset {
214 REGSET_GENERAL,
215 REGSET_FPU,
216};
217
218static const struct user_regset mn10300_regsets[] = {
219 /*
220 * General register format is:
221 * A3, A2, D3, D2, MCVF, MCRL, MCRH, MDRQ
222 * E1, E0, E7...E2, SP, LAR, LIR, MDR
223 * A1, A0, D1, D0, ORIG_D0, EPSW, PC
224 */
225 [REGSET_GENERAL] = {
226 .core_note_type = NT_PRSTATUS,
227 .n = ELF_NGREG,
228 .size = sizeof(long),
229 .align = sizeof(long),
230 .get = genregs_get,
231 .set = genregs_set,
232 },
233 /*
234 * FPU register format is:
235 * FS0-31, FPCR
236 */
237 [REGSET_FPU] = {
238 .core_note_type = NT_PRFPREG,
239 .n = sizeof(struct fpu_state_struct) / sizeof(long),
240 .size = sizeof(long),
241 .align = sizeof(long),
242 .get = fpuregs_get,
243 .set = fpuregs_set,
244 .active = fpuregs_active,
245 },
246};
247
248static const struct user_regset_view user_mn10300_native_view = {
249 .name = "mn10300",
250 .e_machine = EM_MN10300,
251 .regsets = mn10300_regsets,
252 .n = ARRAY_SIZE(mn10300_regsets),
253};
254
255const struct user_regset_view *task_user_regset_view(struct task_struct *task)
256{
257 return &user_mn10300_native_view;
258}
259
260/*
261 * set the single-step bit
262 */
263void user_enable_single_step(struct task_struct *child)
b920de1b
DH
264{
265#ifndef CONFIG_MN10300_USING_JTAG
266 struct user *dummy = NULL;
267 long tmp;
268
269 tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw);
5d289964 270 tmp |= EPSW_T;
b920de1b
DH
271 put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp);
272#endif
273}
274
275/*
5d289964 276 * make sure the single-step bit is not set
b920de1b 277 */
5d289964 278void user_disable_single_step(struct task_struct *child)
b920de1b
DH
279{
280#ifndef CONFIG_MN10300_USING_JTAG
281 struct user *dummy = NULL;
282 long tmp;
283
284 tmp = get_stack_long(child, (unsigned long) &dummy->regs.epsw);
5d289964 285 tmp &= ~EPSW_T;
b920de1b
DH
286 put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp);
287#endif
288}
289
5d289964
DH
290void ptrace_disable(struct task_struct *child)
291{
292 user_disable_single_step(child);
293}
294
b920de1b
DH
295/*
296 * handle the arch-specific side of process tracing
297 */
9b05a69e
NK
298long arch_ptrace(struct task_struct *child, long request,
299 unsigned long addr, unsigned long data)
b920de1b 300{
5d289964
DH
301 unsigned long tmp;
302 int ret;
261bb920 303 unsigned long __user *datap = (unsigned long __user *) data;
b920de1b
DH
304
305 switch (request) {
b920de1b 306 /* read the word at location addr in the USER area. */
5d289964 307 case PTRACE_PEEKUSR:
b920de1b 308 ret = -EIO;
261bb920 309 if ((addr & 3) || addr > sizeof(struct user) - 3)
b920de1b
DH
310 break;
311
312 tmp = 0; /* Default return condition */
313 if (addr < NR_PTREGS << 2)
314 tmp = get_stack_long(child,
315 ptrace_regid_to_frame[addr]);
261bb920 316 ret = put_user(tmp, datap);
b920de1b 317 break;
b920de1b
DH
318
319 /* write the word at location addr in the USER area */
320 case PTRACE_POKEUSR:
321 ret = -EIO;
261bb920 322 if ((addr & 3) || addr > sizeof(struct user) - 3)
b920de1b
DH
323 break;
324
325 ret = 0;
326 if (addr < NR_PTREGS << 2)
327 ret = put_stack_long(child, ptrace_regid_to_frame[addr],
328 data);
329 break;
330
5d289964
DH
331 case PTRACE_GETREGS: /* Get all integer regs from the child. */
332 return copy_regset_to_user(child, &user_mn10300_native_view,
333 REGSET_GENERAL,
334 0, NR_PTREGS * sizeof(long),
261bb920 335 datap);
5d289964
DH
336
337 case PTRACE_SETREGS: /* Set all integer regs in the child. */
338 return copy_regset_from_user(child, &user_mn10300_native_view,
339 REGSET_GENERAL,
340 0, NR_PTREGS * sizeof(long),
261bb920 341 datap);
5d289964
DH
342
343 case PTRACE_GETFPREGS: /* Get the child FPU state. */
344 return copy_regset_to_user(child, &user_mn10300_native_view,
345 REGSET_FPU,
346 0, sizeof(struct fpu_state_struct),
261bb920 347 datap);
5d289964
DH
348
349 case PTRACE_SETFPREGS: /* Set the child FPU state. */
350 return copy_regset_from_user(child, &user_mn10300_native_view,
351 REGSET_FPU,
352 0, sizeof(struct fpu_state_struct),
261bb920 353 datap);
b920de1b
DH
354
355 default:
5d289964 356 ret = ptrace_request(child, request, addr, data);
b920de1b
DH
357 break;
358 }
359
360 return ret;
361}
362
363/*
5d289964
DH
364 * handle tracing of system call entry
365 * - return the revised system call number or ULONG_MAX to cause ENOSYS
b920de1b 366 */
5d289964 367asmlinkage unsigned long syscall_trace_entry(struct pt_regs *regs)
b920de1b 368{
5d289964
DH
369 if (tracehook_report_syscall_entry(regs))
370 /* tracing decided this syscall should not happen, so
371 * We'll return a bogus call number to get an ENOSYS
372 * error, but leave the original number in
373 * regs->orig_d0
374 */
375 return ULONG_MAX;
b920de1b 376
5d289964
DH
377 return regs->orig_d0;
378}
b920de1b 379
5d289964
DH
380/*
381 * handle tracing of system call exit
382 */
383asmlinkage void syscall_trace_exit(struct pt_regs *regs)
384{
385 tracehook_report_syscall_exit(regs, 0);
b920de1b 386}