• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/arch/xtensa/kernel/
1// TODO some minor issues
2/*
3 * This file is subject to the terms and conditions of the GNU General Public
4 * License.  See the file "COPYING" in the main directory of this archive
5 * for more details.
6 *
7 * Copyright (C) 2001 - 2007  Tensilica Inc.
8 *
9 * Joe Taylor	<joe@tensilica.com, joetylr@yahoo.com>
10 * Chris Zankel <chris@zankel.net>
11 * Scott Foehner<sfoehner@yahoo.com>,
12 * Kevin Chea
13 * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca>
14 */
15
16#include <linux/kernel.h>
17#include <linux/sched.h>
18#include <linux/mm.h>
19#include <linux/errno.h>
20#include <linux/ptrace.h>
21#include <linux/smp.h>
22#include <linux/security.h>
23#include <linux/signal.h>
24
25#include <asm/pgtable.h>
26#include <asm/page.h>
27#include <asm/system.h>
28#include <asm/uaccess.h>
29#include <asm/ptrace.h>
30#include <asm/elf.h>
31#include <asm/coprocessor.h>
32
33
34void user_enable_single_step(struct task_struct *child)
35{
36	child->ptrace |= PT_SINGLESTEP;
37}
38
39void user_disable_single_step(struct task_struct *child)
40{
41	child->ptrace &= ~PT_SINGLESTEP;
42}
43
44/*
45 * Called by kernel/ptrace.c when detaching to disable single stepping.
46 */
47
48void ptrace_disable(struct task_struct *child)
49{
50	/* Nothing to do.. */
51}
52
53int ptrace_getregs(struct task_struct *child, void __user *uregs)
54{
55	struct pt_regs *regs = task_pt_regs(child);
56	xtensa_gregset_t __user *gregset = uregs;
57	unsigned long wm = regs->wmask;
58	unsigned long wb = regs->windowbase;
59	int live, i;
60
61	if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t)))
62		return -EIO;
63
64	__put_user(regs->pc, &gregset->pc);
65	__put_user(regs->ps & ~(1 << PS_EXCM_BIT), &gregset->ps);
66	__put_user(regs->lbeg, &gregset->lbeg);
67	__put_user(regs->lend, &gregset->lend);
68	__put_user(regs->lcount, &gregset->lcount);
69	__put_user(regs->windowstart, &gregset->windowstart);
70	__put_user(regs->windowbase, &gregset->windowbase);
71
72	live = (wm & 2) ? 4 : (wm & 4) ? 8 : (wm & 8) ? 12 : 16;
73
74	for (i = 0; i < live; i++)
75		__put_user(regs->areg[i],gregset->a+((wb*4+i)%XCHAL_NUM_AREGS));
76	for (i = XCHAL_NUM_AREGS - (wm >> 4) * 4; i < XCHAL_NUM_AREGS; i++)
77		__put_user(regs->areg[i],gregset->a+((wb*4+i)%XCHAL_NUM_AREGS));
78
79	return 0;
80}
81
82int ptrace_setregs(struct task_struct *child, void __user *uregs)
83{
84	struct pt_regs *regs = task_pt_regs(child);
85	xtensa_gregset_t *gregset = uregs;
86	const unsigned long ps_mask = PS_CALLINC_MASK | PS_OWB_MASK;
87	unsigned long ps;
88	unsigned long wb;
89
90	if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t)))
91		return -EIO;
92
93	__get_user(regs->pc, &gregset->pc);
94	__get_user(ps, &gregset->ps);
95	__get_user(regs->lbeg, &gregset->lbeg);
96	__get_user(regs->lend, &gregset->lend);
97	__get_user(regs->lcount, &gregset->lcount);
98	__get_user(regs->windowstart, &gregset->windowstart);
99	__get_user(wb, &gregset->windowbase);
100
101	regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT);
102
103	if (wb >= XCHAL_NUM_AREGS / 4)
104		return -EFAULT;
105
106	regs->windowbase = wb;
107
108	if (wb != 0 &&  __copy_from_user(regs->areg + XCHAL_NUM_AREGS - wb * 4,
109					 gregset->a, wb * 16))
110		return -EFAULT;
111
112	if (__copy_from_user(regs->areg, gregset->a + wb*4, (WSBITS-wb) * 16))
113		return -EFAULT;
114
115	return 0;
116}
117
118
119int ptrace_getxregs(struct task_struct *child, void __user *uregs)
120{
121	struct pt_regs *regs = task_pt_regs(child);
122	struct thread_info *ti = task_thread_info(child);
123	elf_xtregs_t __user *xtregs = uregs;
124	int ret = 0;
125
126	if (!access_ok(VERIFY_WRITE, uregs, sizeof(elf_xtregs_t)))
127		return -EIO;
128
129#if XTENSA_HAVE_COPROCESSORS
130	/* Flush all coprocessor registers to memory. */
131	coprocessor_flush_all(ti);
132	ret |= __copy_to_user(&xtregs->cp0, &ti->xtregs_cp,
133			      sizeof(xtregs_coprocessor_t));
134#endif
135	ret |= __copy_to_user(&xtregs->opt, &regs->xtregs_opt,
136			      sizeof(xtregs->opt));
137	ret |= __copy_to_user(&xtregs->user,&ti->xtregs_user,
138			      sizeof(xtregs->user));
139
140	return ret ? -EFAULT : 0;
141}
142
143int ptrace_setxregs(struct task_struct *child, void __user *uregs)
144{
145	struct thread_info *ti = task_thread_info(child);
146	struct pt_regs *regs = task_pt_regs(child);
147	elf_xtregs_t *xtregs = uregs;
148	int ret = 0;
149
150#if XTENSA_HAVE_COPROCESSORS
151	/* Flush all coprocessors before we overwrite them. */
152	coprocessor_flush_all(ti);
153	coprocessor_release_all(ti);
154
155	ret |= __copy_from_user(&ti->xtregs_cp, &xtregs->cp0,
156				sizeof(xtregs_coprocessor_t));
157#endif
158	ret |= __copy_from_user(&regs->xtregs_opt, &xtregs->opt,
159				sizeof(xtregs->opt));
160	ret |= __copy_from_user(&ti->xtregs_user, &xtregs->user,
161				sizeof(xtregs->user));
162
163	return ret ? -EFAULT : 0;
164}
165
166int ptrace_peekusr(struct task_struct *child, long regno, long __user *ret)
167{
168	struct pt_regs *regs;
169	unsigned long tmp;
170
171	regs = task_pt_regs(child);
172	tmp = 0;  /* Default return value. */
173
174	switch(regno) {
175
176		case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
177			tmp = regs->areg[regno - REG_AR_BASE];
178			break;
179
180		case REG_A_BASE ... REG_A_BASE + 15:
181			tmp = regs->areg[regno - REG_A_BASE];
182			break;
183
184		case REG_PC:
185			tmp = regs->pc;
186			break;
187
188		case REG_PS:
189			/* Note:  PS.EXCM is not set while user task is running;
190			 * its being set in regs is for exception handling
191			 * convenience.  */
192			tmp = (regs->ps & ~(1 << PS_EXCM_BIT));
193			break;
194
195		case REG_WB:
196			break;		/* tmp = 0 */
197
198		case REG_WS:
199		{
200			unsigned long wb = regs->windowbase;
201			unsigned long ws = regs->windowstart;
202			tmp = ((ws>>wb) | (ws<<(WSBITS-wb))) & ((1<<WSBITS)-1);
203			break;
204		}
205		case REG_LBEG:
206			tmp = regs->lbeg;
207			break;
208
209		case REG_LEND:
210			tmp = regs->lend;
211			break;
212
213		case REG_LCOUNT:
214			tmp = regs->lcount;
215			break;
216
217		case REG_SAR:
218			tmp = regs->sar;
219			break;
220
221		case SYSCALL_NR:
222			tmp = regs->syscall;
223			break;
224
225		default:
226			return -EIO;
227	}
228	return put_user(tmp, ret);
229}
230
231int ptrace_pokeusr(struct task_struct *child, long regno, long val)
232{
233	struct pt_regs *regs;
234	regs = task_pt_regs(child);
235
236	switch (regno) {
237		case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1:
238			regs->areg[regno - REG_AR_BASE] = val;
239			break;
240
241		case REG_A_BASE ... REG_A_BASE + 15:
242			regs->areg[regno - REG_A_BASE] = val;
243			break;
244
245		case REG_PC:
246			regs->pc = val;
247			break;
248
249		case SYSCALL_NR:
250			regs->syscall = val;
251			break;
252
253		default:
254			return -EIO;
255	}
256	return 0;
257}
258
259long arch_ptrace(struct task_struct *child, long request, long addr, long data)
260{
261	int ret = -EPERM;
262
263	switch (request) {
264	case PTRACE_PEEKTEXT:	/* read word at location addr. */
265	case PTRACE_PEEKDATA:
266		ret = generic_ptrace_peekdata(child, addr, data);
267		break;
268
269	case PTRACE_PEEKUSR:	/* read register specified by addr. */
270		ret = ptrace_peekusr(child, addr, (void __user *) data);
271		break;
272
273	case PTRACE_POKETEXT:	/* write the word at location addr. */
274	case PTRACE_POKEDATA:
275		ret = generic_ptrace_pokedata(child, addr, data);
276		break;
277
278	case PTRACE_POKEUSR:	/* write register specified by addr. */
279		ret = ptrace_pokeusr(child, addr, data);
280		break;
281
282	case PTRACE_GETREGS:
283		ret = ptrace_getregs(child, (void __user *) data);
284		break;
285
286	case PTRACE_SETREGS:
287		ret = ptrace_setregs(child, (void __user *) data);
288		break;
289
290	case PTRACE_GETXTREGS:
291		ret = ptrace_getxregs(child, (void __user *) data);
292		break;
293
294	case PTRACE_SETXTREGS:
295		ret = ptrace_setxregs(child, (void __user *) data);
296		break;
297
298	default:
299		ret = ptrace_request(child, request, addr, data);
300		break;
301	}
302
303	return ret;
304}
305
306void do_syscall_trace(void)
307{
308	/*
309	 * The 0x80 provides a way for the tracing parent to distinguish
310	 * between a syscall stop and SIGTRAP delivery
311	 */
312	ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
313
314	/*
315	 * this isn't the same as continuing with a signal, but it will do
316	 * for normal use.  strace only continues with a signal if the
317	 * stopping signal is not SIGTRAP.  -brl
318	 */
319	if (current->exit_code) {
320		send_sig(current->exit_code, current, 1);
321		current->exit_code = 0;
322	}
323}
324
325void do_syscall_trace_enter(struct pt_regs *regs)
326{
327	if (test_thread_flag(TIF_SYSCALL_TRACE)
328			&& (current->ptrace & PT_PTRACED))
329		do_syscall_trace();
330
331}
332
333void do_syscall_trace_leave(struct pt_regs *regs)
334{
335	if ((test_thread_flag(TIF_SYSCALL_TRACE))
336			&& (current->ptrace & PT_PTRACED))
337		do_syscall_trace();
338}
339