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