]>
Commit | Line | Data |
---|---|---|
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 | */ | |
33 | static 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 | ||
63 | static 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 |
69 | static inline |
70 | int 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 | */ | |
82 | static 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 | ®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); | |
b920de1b DH |
104 | } |
105 | ||
5d289964 DH |
106 | /* |
107 | * update the contents of the MN10300 userspace general registers | |
108 | */ | |
109 | static 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 | ®s->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 | ®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); | |
b920de1b DH |
153 | } |
154 | ||
5d289964 DH |
155 | /* |
156 | * retrieve the contents of MN10300 userspace FPU registers | |
157 | */ | |
158 | static 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 |
180 | static 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 | */ | |
204 | static 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 | */ | |
213 | enum mn10300_regset { | |
214 | REGSET_GENERAL, | |
215 | REGSET_FPU, | |
216 | }; | |
217 | ||
218 | static 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 | ||
248 | static 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 | ||
255 | const 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 | */ | |
263 | void 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 | 278 | void 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 |
290 | void 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 |
298 | long 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; | |
b920de1b DH |
303 | |
304 | switch (request) { | |
b920de1b | 305 | /* read the word at location addr in the USER area. */ |
5d289964 | 306 | case PTRACE_PEEKUSR: |
b920de1b DH |
307 | 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; | |
b920de1b DH |
318 | |
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 | ||
5d289964 DH |
332 | 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); | |
337 | ||
338 | 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); | |
343 | ||
344 | 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); | |
349 | ||
350 | 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); | |
b920de1b DH |
355 | |
356 | default: | |
5d289964 | 357 | ret = ptrace_request(child, request, addr, data); |
b920de1b DH |
358 | break; |
359 | } | |
360 | ||
361 | return ret; | |
362 | } | |
363 | ||
364 | /* | |
5d289964 DH |
365 | * handle tracing of system call entry |
366 | * - return the revised system call number or ULONG_MAX to cause ENOSYS | |
b920de1b | 367 | */ |
5d289964 | 368 | asmlinkage unsigned long syscall_trace_entry(struct pt_regs *regs) |
b920de1b | 369 | { |
5d289964 DH |
370 | 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; | |
b920de1b | 377 | |
5d289964 DH |
378 | return regs->orig_d0; |
379 | } | |
b920de1b | 380 | |
5d289964 DH |
381 | /* |
382 | * handle tracing of system call exit | |
383 | */ | |
384 | asmlinkage void syscall_trace_exit(struct pt_regs *regs) | |
385 | { | |
386 | tracehook_report_syscall_exit(regs, 0); | |
b920de1b | 387 | } |