1
2
3
4
5
6
7
8
9
10
11
12#include <linux/kernel.h>
13#include <linux/sched.h>
14#include <linux/sched/task_stack.h>
15#include <linux/mm.h>
16#include <linux/smp.h>
17#include <linux/errno.h>
18#include <linux/ptrace.h>
19#include <linux/user.h>
20#include <linux/regset.h>
21#include <linux/elf.h>
22#include <linux/tracehook.h>
23#include <linux/uaccess.h>
24#include <asm/pgtable.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
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
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
79
80
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)
86{
87 const struct pt_regs *regs = task_pt_regs(target);
88 int ret;
89
90
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 ®s->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);
104}
105
106
107
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)
113{
114 struct pt_regs *regs = task_pt_regs(target);
115 unsigned long tmp;
116 int ret;
117
118
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 ®s->orig_d0, PT_ORIG_D0 * sizeof(long),
126 PT_EPSW * sizeof(long));
127 if (ret < 0)
128 return ret;
129
130
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
144 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
145 ®s->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);
153}
154
155
156
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)
162{
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);
175}
176
177
178
179
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
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
212
213enum mn10300_regset {
214 REGSET_GENERAL,
215 REGSET_FPU,
216};
217
218static const struct user_regset mn10300_regsets[] = {
219
220
221
222
223
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
235
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
262
263void user_enable_single_step(struct task_struct *child)
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);
270 tmp |= EPSW_T;
271 put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp);
272#endif
273}
274
275
276
277
278void user_disable_single_step(struct task_struct *child)
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);
285 tmp &= ~EPSW_T;
286 put_stack_long(child, (unsigned long) &dummy->regs.epsw, tmp);
287#endif
288}
289
290void ptrace_disable(struct task_struct *child)
291{
292 user_disable_single_step(child);
293}
294
295
296
297
298long arch_ptrace(struct task_struct *child, long request,
299 unsigned long addr, unsigned long data)
300{
301 unsigned long tmp;
302 int ret;
303 unsigned long __user *datap = (unsigned long __user *) data;
304
305 switch (request) {
306
307 case PTRACE_PEEKUSR:
308 ret = -EIO;
309 if ((addr & 3) || addr > sizeof(struct user) - 3)
310 break;
311
312 tmp = 0;
313 if (addr < NR_PTREGS << 2)
314 tmp = get_stack_long(child,
315 ptrace_regid_to_frame[addr]);
316 ret = put_user(tmp, datap);
317 break;
318
319
320 case PTRACE_POKEUSR:
321 ret = -EIO;
322 if ((addr & 3) || addr > sizeof(struct user) - 3)
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
331 case PTRACE_GETREGS:
332 return copy_regset_to_user(child, &user_mn10300_native_view,
333 REGSET_GENERAL,
334 0, NR_PTREGS * sizeof(long),
335 datap);
336
337 case PTRACE_SETREGS:
338 return copy_regset_from_user(child, &user_mn10300_native_view,
339 REGSET_GENERAL,
340 0, NR_PTREGS * sizeof(long),
341 datap);
342
343 case PTRACE_GETFPREGS:
344 return copy_regset_to_user(child, &user_mn10300_native_view,
345 REGSET_FPU,
346 0, sizeof(struct fpu_state_struct),
347 datap);
348
349 case PTRACE_SETFPREGS:
350 return copy_regset_from_user(child, &user_mn10300_native_view,
351 REGSET_FPU,
352 0, sizeof(struct fpu_state_struct),
353 datap);
354
355 default:
356 ret = ptrace_request(child, request, addr, data);
357 break;
358 }
359
360 return ret;
361}
362
363
364
365
366
367asmlinkage unsigned long syscall_trace_entry(struct pt_regs *regs)
368{
369 if (tracehook_report_syscall_entry(regs))
370
371
372
373
374
375 return ULONG_MAX;
376
377 return regs->orig_d0;
378}
379
380
381
382
383asmlinkage void syscall_trace_exit(struct pt_regs *regs)
384{
385 tracehook_report_syscall_exit(regs, 0);
386}
387