]>
Commit | Line | Data |
---|---|---|
bc3966bf JH |
1 | /* |
2 | * Copyright (C) 2005-2012 Imagination Technologies Ltd. | |
3 | * | |
4 | * This file is subject to the terms and conditions of the GNU General | |
5 | * Public License. See the file COPYING in the main directory of | |
6 | * this archive for more details. | |
7 | */ | |
8 | ||
9 | #include <linux/kernel.h> | |
10 | #include <linux/mm.h> | |
11 | #include <linux/errno.h> | |
12 | #include <linux/ptrace.h> | |
13 | #include <linux/user.h> | |
14 | #include <linux/regset.h> | |
15 | #include <linux/tracehook.h> | |
16 | #include <linux/elf.h> | |
17 | #include <linux/uaccess.h> | |
68db0cf1 IM |
18 | #include <linux/sched/task_stack.h> |
19 | ||
bc3966bf JH |
20 | #include <trace/syscall.h> |
21 | ||
22 | #define CREATE_TRACE_POINTS | |
23 | #include <trace/events/syscalls.h> | |
24 | ||
25 | /* | |
26 | * user_regset definitions. | |
27 | */ | |
28 | ||
29 | int metag_gp_regs_copyout(const struct pt_regs *regs, | |
30 | unsigned int pos, unsigned int count, | |
31 | void *kbuf, void __user *ubuf) | |
32 | { | |
33 | const void *ptr; | |
34 | unsigned long data; | |
35 | int ret; | |
36 | ||
37 | /* D{0-1}.{0-7} */ | |
38 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
39 | regs->ctx.DX, 0, 4*16); | |
40 | if (ret) | |
41 | goto out; | |
42 | /* A{0-1}.{0-1} */ | |
43 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
44 | regs->ctx.AX, 4*16, 4*20); | |
45 | if (ret) | |
46 | goto out; | |
47 | /* A{0-1}.2 */ | |
48 | if (regs->ctx.SaveMask & TBICTX_XEXT_BIT) | |
49 | ptr = regs->ctx.Ext.Ctx.pExt; | |
50 | else | |
51 | ptr = ®s->ctx.Ext.AX2; | |
52 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
53 | ptr, 4*20, 4*22); | |
54 | if (ret) | |
55 | goto out; | |
56 | /* A{0-1}.3 */ | |
57 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
58 | ®s->ctx.AX3, 4*22, 4*24); | |
59 | if (ret) | |
60 | goto out; | |
61 | /* PC */ | |
62 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
63 | ®s->ctx.CurrPC, 4*24, 4*25); | |
64 | if (ret) | |
65 | goto out; | |
66 | /* TXSTATUS */ | |
67 | data = (unsigned long)regs->ctx.Flags; | |
68 | if (regs->ctx.SaveMask & TBICTX_CBUF_BIT) | |
69 | data |= USER_GP_REGS_STATUS_CATCH_BIT; | |
70 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
71 | &data, 4*25, 4*26); | |
72 | if (ret) | |
73 | goto out; | |
74 | /* TXRPT, TXBPOBITS, TXMODE */ | |
75 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
76 | ®s->ctx.CurrRPT, 4*26, 4*29); | |
77 | if (ret) | |
78 | goto out; | |
79 | /* Padding */ | |
80 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
81 | 4*29, 4*30); | |
82 | out: | |
83 | return ret; | |
84 | } | |
85 | ||
86 | int metag_gp_regs_copyin(struct pt_regs *regs, | |
87 | unsigned int pos, unsigned int count, | |
88 | const void *kbuf, const void __user *ubuf) | |
89 | { | |
90 | void *ptr; | |
91 | unsigned long data; | |
92 | int ret; | |
93 | ||
94 | /* D{0-1}.{0-7} */ | |
95 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
96 | regs->ctx.DX, 0, 4*16); | |
97 | if (ret) | |
98 | goto out; | |
99 | /* A{0-1}.{0-1} */ | |
100 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
101 | regs->ctx.AX, 4*16, 4*20); | |
102 | if (ret) | |
103 | goto out; | |
104 | /* A{0-1}.2 */ | |
105 | if (regs->ctx.SaveMask & TBICTX_XEXT_BIT) | |
106 | ptr = regs->ctx.Ext.Ctx.pExt; | |
107 | else | |
108 | ptr = ®s->ctx.Ext.AX2; | |
109 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
110 | ptr, 4*20, 4*22); | |
111 | if (ret) | |
112 | goto out; | |
113 | /* A{0-1}.3 */ | |
114 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
115 | ®s->ctx.AX3, 4*22, 4*24); | |
116 | if (ret) | |
117 | goto out; | |
118 | /* PC */ | |
119 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
120 | ®s->ctx.CurrPC, 4*24, 4*25); | |
121 | if (ret) | |
122 | goto out; | |
123 | /* TXSTATUS */ | |
124 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
125 | &data, 4*25, 4*26); | |
126 | if (ret) | |
127 | goto out; | |
128 | regs->ctx.Flags = data & 0xffff; | |
129 | if (data & USER_GP_REGS_STATUS_CATCH_BIT) | |
130 | regs->ctx.SaveMask |= TBICTX_XCBF_BIT | TBICTX_CBUF_BIT; | |
131 | else | |
132 | regs->ctx.SaveMask &= ~TBICTX_CBUF_BIT; | |
133 | /* TXRPT, TXBPOBITS, TXMODE */ | |
134 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
135 | ®s->ctx.CurrRPT, 4*26, 4*29); | |
136 | out: | |
137 | return ret; | |
138 | } | |
139 | ||
140 | static int metag_gp_regs_get(struct task_struct *target, | |
141 | const struct user_regset *regset, | |
142 | unsigned int pos, unsigned int count, | |
143 | void *kbuf, void __user *ubuf) | |
144 | { | |
145 | const struct pt_regs *regs = task_pt_regs(target); | |
146 | return metag_gp_regs_copyout(regs, pos, count, kbuf, ubuf); | |
147 | } | |
148 | ||
149 | static int metag_gp_regs_set(struct task_struct *target, | |
150 | const struct user_regset *regset, | |
151 | unsigned int pos, unsigned int count, | |
152 | const void *kbuf, const void __user *ubuf) | |
153 | { | |
154 | struct pt_regs *regs = task_pt_regs(target); | |
155 | return metag_gp_regs_copyin(regs, pos, count, kbuf, ubuf); | |
156 | } | |
157 | ||
158 | int metag_cb_regs_copyout(const struct pt_regs *regs, | |
159 | unsigned int pos, unsigned int count, | |
160 | void *kbuf, void __user *ubuf) | |
161 | { | |
162 | int ret; | |
163 | ||
164 | /* TXCATCH{0-3} */ | |
165 | if (regs->ctx.SaveMask & TBICTX_XCBF_BIT) | |
166 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
167 | regs->extcb0, 0, 4*4); | |
168 | else | |
169 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
170 | 0, 4*4); | |
171 | return ret; | |
172 | } | |
173 | ||
174 | int metag_cb_regs_copyin(struct pt_regs *regs, | |
175 | unsigned int pos, unsigned int count, | |
176 | const void *kbuf, const void __user *ubuf) | |
177 | { | |
178 | int ret; | |
179 | ||
180 | /* TXCATCH{0-3} */ | |
181 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
182 | regs->extcb0, 0, 4*4); | |
183 | return ret; | |
184 | } | |
185 | ||
186 | static int metag_cb_regs_get(struct task_struct *target, | |
187 | const struct user_regset *regset, | |
188 | unsigned int pos, unsigned int count, | |
189 | void *kbuf, void __user *ubuf) | |
190 | { | |
191 | const struct pt_regs *regs = task_pt_regs(target); | |
192 | return metag_cb_regs_copyout(regs, pos, count, kbuf, ubuf); | |
193 | } | |
194 | ||
195 | static int metag_cb_regs_set(struct task_struct *target, | |
196 | const struct user_regset *regset, | |
197 | unsigned int pos, unsigned int count, | |
198 | const void *kbuf, const void __user *ubuf) | |
199 | { | |
200 | struct pt_regs *regs = task_pt_regs(target); | |
201 | return metag_cb_regs_copyin(regs, pos, count, kbuf, ubuf); | |
202 | } | |
203 | ||
204 | int metag_rp_state_copyout(const struct pt_regs *regs, | |
205 | unsigned int pos, unsigned int count, | |
206 | void *kbuf, void __user *ubuf) | |
207 | { | |
208 | unsigned long mask; | |
209 | u64 *ptr; | |
210 | int ret, i; | |
211 | ||
212 | /* Empty read pipeline */ | |
213 | if (!(regs->ctx.SaveMask & TBICTX_CBRP_BIT)) { | |
214 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, | |
215 | 0, 4*13); | |
216 | goto out; | |
217 | } | |
218 | ||
219 | mask = (regs->ctx.CurrDIVTIME & TXDIVTIME_RPMASK_BITS) >> | |
220 | TXDIVTIME_RPMASK_S; | |
221 | ||
222 | /* Read pipeline entries */ | |
223 | ptr = (void *)®s->extcb0[1]; | |
224 | for (i = 0; i < 6; ++i, ++ptr) { | |
225 | if (mask & (1 << i)) | |
226 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
227 | ptr, 8*i, 8*(i + 1)); | |
228 | else | |
229 | ret = user_regset_copyout_zero(&pos, &count, &kbuf, | |
230 | &ubuf, 8*i, 8*(i + 1)); | |
231 | if (ret) | |
232 | goto out; | |
233 | } | |
234 | /* Mask of entries */ | |
235 | ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, | |
236 | &mask, 4*12, 4*13); | |
237 | out: | |
238 | return ret; | |
239 | } | |
240 | ||
241 | int metag_rp_state_copyin(struct pt_regs *regs, | |
242 | unsigned int pos, unsigned int count, | |
243 | const void *kbuf, const void __user *ubuf) | |
244 | { | |
245 | struct user_rp_state rp; | |
246 | unsigned long long *ptr; | |
247 | int ret, i; | |
248 | ||
249 | /* Read the entire pipeline before making any changes */ | |
250 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | |
251 | &rp, 0, 4*13); | |
252 | if (ret) | |
253 | goto out; | |
254 | ||
255 | /* Write pipeline entries */ | |
256 | ptr = (void *)®s->extcb0[1]; | |
257 | for (i = 0; i < 6; ++i, ++ptr) | |
258 | if (rp.mask & (1 << i)) | |
259 | *ptr = rp.entries[i]; | |
260 | ||
261 | /* Update RPMask in TXDIVTIME */ | |
262 | regs->ctx.CurrDIVTIME &= ~TXDIVTIME_RPMASK_BITS; | |
263 | regs->ctx.CurrDIVTIME |= (rp.mask << TXDIVTIME_RPMASK_S) | |
264 | & TXDIVTIME_RPMASK_BITS; | |
265 | ||
266 | /* Set/clear flags to indicate catch/read pipeline state */ | |
267 | if (rp.mask) | |
268 | regs->ctx.SaveMask |= TBICTX_XCBF_BIT | TBICTX_CBRP_BIT; | |
269 | else | |
270 | regs->ctx.SaveMask &= ~TBICTX_CBRP_BIT; | |
271 | out: | |
272 | return ret; | |
273 | } | |
274 | ||
275 | static int metag_rp_state_get(struct task_struct *target, | |
276 | const struct user_regset *regset, | |
277 | unsigned int pos, unsigned int count, | |
278 | void *kbuf, void __user *ubuf) | |
279 | { | |
280 | const struct pt_regs *regs = task_pt_regs(target); | |
281 | return metag_rp_state_copyout(regs, pos, count, kbuf, ubuf); | |
282 | } | |
283 | ||
284 | static int metag_rp_state_set(struct task_struct *target, | |
285 | const struct user_regset *regset, | |
286 | unsigned int pos, unsigned int count, | |
287 | const void *kbuf, const void __user *ubuf) | |
288 | { | |
289 | struct pt_regs *regs = task_pt_regs(target); | |
290 | return metag_rp_state_copyin(regs, pos, count, kbuf, ubuf); | |
291 | } | |
292 | ||
876d6dcd PC |
293 | static int metag_tls_get(struct task_struct *target, |
294 | const struct user_regset *regset, | |
295 | unsigned int pos, unsigned int count, | |
296 | void *kbuf, void __user *ubuf) | |
297 | { | |
298 | void __user *tls = target->thread.tls_ptr; | |
299 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, &tls, 0, -1); | |
300 | } | |
301 | ||
302 | static int metag_tls_set(struct task_struct *target, | |
303 | const struct user_regset *regset, | |
304 | unsigned int pos, unsigned int count, | |
305 | const void *kbuf, const void __user *ubuf) | |
306 | { | |
307 | int ret; | |
308 | void __user *tls; | |
309 | ||
310 | ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1); | |
311 | if (ret) | |
312 | return ret; | |
313 | ||
314 | target->thread.tls_ptr = tls; | |
315 | return ret; | |
316 | } | |
317 | ||
bc3966bf JH |
318 | enum metag_regset { |
319 | REGSET_GENERAL, | |
320 | REGSET_CBUF, | |
321 | REGSET_READPIPE, | |
876d6dcd | 322 | REGSET_TLS, |
bc3966bf JH |
323 | }; |
324 | ||
325 | static const struct user_regset metag_regsets[] = { | |
326 | [REGSET_GENERAL] = { | |
327 | .core_note_type = NT_PRSTATUS, | |
328 | .n = ELF_NGREG, | |
329 | .size = sizeof(long), | |
330 | .align = sizeof(long long), | |
331 | .get = metag_gp_regs_get, | |
332 | .set = metag_gp_regs_set, | |
333 | }, | |
334 | [REGSET_CBUF] = { | |
335 | .core_note_type = NT_METAG_CBUF, | |
336 | .n = sizeof(struct user_cb_regs) / sizeof(long), | |
337 | .size = sizeof(long), | |
338 | .align = sizeof(long long), | |
339 | .get = metag_cb_regs_get, | |
340 | .set = metag_cb_regs_set, | |
341 | }, | |
342 | [REGSET_READPIPE] = { | |
343 | .core_note_type = NT_METAG_RPIPE, | |
344 | .n = sizeof(struct user_rp_state) / sizeof(long), | |
345 | .size = sizeof(long), | |
346 | .align = sizeof(long long), | |
347 | .get = metag_rp_state_get, | |
348 | .set = metag_rp_state_set, | |
349 | }, | |
876d6dcd PC |
350 | [REGSET_TLS] = { |
351 | .core_note_type = NT_METAG_TLS, | |
352 | .n = 1, | |
353 | .size = sizeof(void *), | |
354 | .align = sizeof(void *), | |
355 | .get = metag_tls_get, | |
356 | .set = metag_tls_set, | |
357 | }, | |
bc3966bf JH |
358 | }; |
359 | ||
360 | static const struct user_regset_view user_metag_view = { | |
361 | .name = "metag", | |
362 | .e_machine = EM_METAG, | |
363 | .regsets = metag_regsets, | |
364 | .n = ARRAY_SIZE(metag_regsets) | |
365 | }; | |
366 | ||
367 | const struct user_regset_view *task_user_regset_view(struct task_struct *task) | |
368 | { | |
369 | return &user_metag_view; | |
370 | } | |
371 | ||
372 | /* | |
373 | * Called by kernel/ptrace.c when detaching.. | |
374 | * | |
375 | * Make sure single step bits etc are not set. | |
376 | */ | |
377 | void ptrace_disable(struct task_struct *child) | |
378 | { | |
379 | /* nothing to do.. */ | |
380 | } | |
381 | ||
382 | long arch_ptrace(struct task_struct *child, long request, unsigned long addr, | |
383 | unsigned long data) | |
384 | { | |
385 | int ret; | |
386 | ||
387 | switch (request) { | |
388 | default: | |
389 | ret = ptrace_request(child, request, addr, data); | |
390 | break; | |
391 | } | |
392 | ||
393 | return ret; | |
394 | } | |
395 | ||
396 | int syscall_trace_enter(struct pt_regs *regs) | |
397 | { | |
398 | int ret = 0; | |
399 | ||
400 | if (test_thread_flag(TIF_SYSCALL_TRACE)) | |
401 | ret = tracehook_report_syscall_entry(regs); | |
402 | ||
403 | if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) | |
404 | trace_sys_enter(regs, regs->ctx.DX[0].U1); | |
405 | ||
406 | return ret ? -1 : regs->ctx.DX[0].U1; | |
407 | } | |
408 | ||
409 | void syscall_trace_leave(struct pt_regs *regs) | |
410 | { | |
411 | if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) | |
412 | trace_sys_exit(regs, regs->ctx.DX[0].U1); | |
413 | ||
414 | if (test_thread_flag(TIF_SYSCALL_TRACE)) | |
415 | tracehook_report_syscall_exit(regs, 0); | |
416 | } |