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