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