aboutsummaryrefslogtreecommitdiffstats
path: root/arch/blackfin/kernel/ptrace.c
diff options
context:
space:
mode:
authorBryan Wu <bryan.wu@analog.com>2007-05-06 17:50:22 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-07 15:12:58 -0400
commit1394f03221790a988afc3e4b3cb79f2e477246a9 (patch)
tree2c1963c9a4f2d84a5e021307fde240c5d567cf70 /arch/blackfin/kernel/ptrace.c
parent73243284463a761e04d69d22c7516b2be7de096c (diff)
blackfin architecture
This adds support for the Analog Devices Blackfin processor architecture, and currently supports the BF533, BF532, BF531, BF537, BF536, BF534, and BF561 (Dual Core) devices, with a variety of development platforms including those avaliable from Analog Devices (BF533-EZKit, BF533-STAMP, BF537-STAMP, BF561-EZKIT), and Bluetechnix! Tinyboards. The Blackfin architecture was jointly developed by Intel and Analog Devices Inc. (ADI) as the Micro Signal Architecture (MSA) core and introduced it in December of 2000. Since then ADI has put this core into its Blackfin processor family of devices. The Blackfin core has the advantages of a clean, orthogonal,RISC-like microprocessor instruction set. It combines a dual-MAC (Multiply/Accumulate), state-of-the-art signal processing engine and single-instruction, multiple-data (SIMD) multimedia capabilities into a single instruction-set architecture. The Blackfin architecture, including the instruction set, is described by the ADSP-BF53x/BF56x Blackfin Processor Programming Reference http://blackfin.uclinux.org/gf/download/frsrelease/29/2549/Blackfin_PRM.pdf The Blackfin processor is already supported by major releases of gcc, and there are binary and source rpms/tarballs for many architectures at: http://blackfin.uclinux.org/gf/project/toolchain/frs There is complete documentation, including "getting started" guides available at: http://docs.blackfin.uclinux.org/ which provides links to the sources and patches you will need in order to set up a cross-compiling environment for bfin-linux-uclibc This patch, as well as the other patches (toolchain, distribution, uClibc) are actively supported by Analog Devices Inc, at: http://blackfin.uclinux.org/ We have tested this on LTP, and our test plan (including pass/fails) can be found at: http://docs.blackfin.uclinux.org/doku.php?id=testing_the_linux_kernel [m.kozlowski@tuxland.pl: balance parenthesis in blackfin header files] Signed-off-by: Bryan Wu <bryan.wu@analog.com> Signed-off-by: Mariusz Kozlowski <m.kozlowski@tuxland.pl> Signed-off-by: Aubrey Li <aubrey.li@analog.com> Signed-off-by: Jie Zhang <jie.zhang@analog.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/blackfin/kernel/ptrace.c')
-rw-r--r--arch/blackfin/kernel/ptrace.c430
1 files changed, 430 insertions, 0 deletions
diff --git a/arch/blackfin/kernel/ptrace.c b/arch/blackfin/kernel/ptrace.c
new file mode 100644
index 000000000000..d7c8e514cb92
--- /dev/null
+++ b/arch/blackfin/kernel/ptrace.c
@@ -0,0 +1,430 @@
1/*
2 * File: arch/blackfin/kernel/ptrace.c
3 * Based on: Taken from linux/kernel/ptrace.c
4 * Author: linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
5 *
6 * Created: 1/23/92
7 * Description:
8 *
9 * Modified:
10 * Copyright 2004-2006 Analog Devices Inc.
11 *
12 * Bugs: Enter bugs at http://blackfin.uclinux.org/
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see the file COPYING, or write
26 * to the Free Software Foundation, Inc.,
27 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 */
29
30#include <linux/kernel.h>
31#include <linux/sched.h>
32#include <linux/mm.h>
33#include <linux/smp.h>
34#include <linux/smp_lock.h>
35#include <linux/errno.h>
36#include <linux/ptrace.h>
37#include <linux/user.h>
38#include <linux/signal.h>
39
40#include <asm/uaccess.h>
41#include <asm/page.h>
42#include <asm/pgtable.h>
43#include <asm/system.h>
44#include <asm/processor.h>
45#include <asm/asm-offsets.h>
46#include <asm/dma.h>
47
48#define MAX_SHARED_LIBS 3
49#define TEXT_OFFSET 0
50/*
51 * does not yet catch signals sent when the child dies.
52 * in exit.c or in signal.c.
53 */
54
55/* determines which bits in the SYSCFG reg the user has access to. */
56/* 1 = access 0 = no access */
57#define SYSCFG_MASK 0x0007 /* SYSCFG reg */
58/* sets the trace bits. */
59#define TRACE_BITS 0x0001
60
61/* Find the stack offset for a register, relative to thread.esp0. */
62#define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg)
63
64/*
65 * Get the address of the live pt_regs for the specified task.
66 * These are saved onto the top kernel stack when the process
67 * is not running.
68 *
69 * Note: if a user thread is execve'd from kernel space, the
70 * kernel stack will not be empty on entry to the kernel, so
71 * ptracing these tasks will fail.
72 */
73static inline struct pt_regs *get_user_regs(struct task_struct *task)
74{
75 return (struct pt_regs *)
76 ((unsigned long)task->thread_info +
77 (THREAD_SIZE - sizeof(struct pt_regs)));
78}
79
80/*
81 * Get all user integer registers.
82 */
83static inline int ptrace_getregs(struct task_struct *tsk, void __user * uregs)
84{
85 struct pt_regs *regs = get_user_regs(tsk);
86 return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0;
87}
88
89/* Mapping from PT_xxx to the stack offset at which the register is
90 * saved. Notice that usp has no stack-slot and needs to be treated
91 * specially (see get_reg/put_reg below).
92 */
93
94/*
95 * Get contents of register REGNO in task TASK.
96 */
97static inline long get_reg(struct task_struct *task, int regno)
98{
99 unsigned char *reg_ptr;
100
101 struct pt_regs *regs =
102 (struct pt_regs *)((unsigned long)task->thread_info +
103 (THREAD_SIZE - sizeof(struct pt_regs)));
104 reg_ptr = (char *)regs;
105
106 switch (regno) {
107 case PT_USP:
108 return task->thread.usp;
109 default:
110 if (regno <= 216)
111 return *(long *)(reg_ptr + regno);
112 }
113 /* slight mystery ... never seems to come here but kernel misbehaves without this code! */
114
115 printk(KERN_WARNING "Request to get for unknown register %d\n", regno);
116 return 0;
117}
118
119/*
120 * Write contents of register REGNO in task TASK.
121 */
122static inline int
123put_reg(struct task_struct *task, int regno, unsigned long data)
124{
125 char * reg_ptr;
126
127 struct pt_regs *regs =
128 (struct pt_regs *)((unsigned long)task->thread_info +
129 (THREAD_SIZE - sizeof(struct pt_regs)));
130 reg_ptr = (char *)regs;
131
132 switch (regno) {
133 case PT_PC:
134 /*********************************************************************/
135 /* At this point the kernel is most likely in exception. */
136 /* The RETX register will be used to populate the pc of the process. */
137 /*********************************************************************/
138 regs->retx = data;
139 regs->pc = data;
140 break;
141 case PT_RETX:
142 break; /* regs->retx = data; break; */
143 case PT_USP:
144 regs->usp = data;
145 task->thread.usp = data;
146 break;
147 default:
148 if (regno <= 216)
149 *(long *)(reg_ptr + regno) = data;
150 }
151 return 0;
152}
153
154/*
155 * check that an address falls within the bounds of the target process's memory mappings
156 */
157static inline int is_user_addr_valid(struct task_struct *child,
158 unsigned long start, unsigned long len)
159{
160 struct vm_list_struct *vml;
161 struct sram_list_struct *sraml;
162
163 for (vml = child->mm->context.vmlist; vml; vml = vml->next)
164 if (start >= vml->vma->vm_start && start + len <= vml->vma->vm_end)
165 return 0;
166
167 for (sraml = child->mm->context.sram_list; sraml; sraml = sraml->next)
168 if (start >= (unsigned long)sraml->addr
169 && start + len <= (unsigned long)sraml->addr + sraml->length)
170 return 0;
171
172 return -EIO;
173}
174
175/*
176 * Called by kernel/ptrace.c when detaching..
177 *
178 * Make sure the single step bit is not set.
179 */
180void ptrace_disable(struct task_struct *child)
181{
182 unsigned long tmp;
183 /* make sure the single step bit is not set. */
184 tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16);
185 put_reg(child, PT_SR, tmp);
186}
187
188long arch_ptrace(struct task_struct *child, long request, long addr, long data)
189{
190 int ret;
191 int add = 0;
192
193 switch (request) {
194 /* when I and D space are separate, these will need to be fixed. */
195 case PTRACE_PEEKDATA:
196 pr_debug("ptrace: PEEKDATA\n");
197 add = MAX_SHARED_LIBS * 4; /* space between text and data */
198 /* fall through */
199 case PTRACE_PEEKTEXT: /* read word at location addr. */
200 {
201 unsigned long tmp = 0;
202 int copied;
203
204 ret = -EIO;
205 pr_debug("ptrace: PEEKTEXT at addr 0x%08lx + add %d %ld\n", addr, add,
206 sizeof(data));
207 if (is_user_addr_valid(child, addr + add, sizeof(tmp)) < 0)
208 break;
209 pr_debug("ptrace: user address is valid\n");
210
211#if L1_CODE_LENGTH != 0
212 if (addr + add >= L1_CODE_START
213 && addr + add + sizeof(tmp) <= L1_CODE_START + L1_CODE_LENGTH) {
214 safe_dma_memcpy (&tmp, (const void *)(addr + add), sizeof(tmp));
215 copied = sizeof(tmp);
216 } else
217#endif
218 copied =
219 access_process_vm(child, addr + add, &tmp,
220 sizeof(tmp), 0);
221 pr_debug("ptrace: copied size %d [0x%08lx]\n", copied, tmp);
222 if (copied != sizeof(tmp))
223 break;
224 ret = put_user(tmp, (unsigned long *)data);
225 break;
226 }
227
228 /* read the word at location addr in the USER area. */
229 case PTRACE_PEEKUSR:
230 {
231 unsigned long tmp;
232 ret = -EIO;
233 tmp = 0;
234 if ((addr & 3) || (addr > (sizeof(struct pt_regs) + 16))) {
235 printk(KERN_WARNING "ptrace error : PEEKUSR : temporarily returning "
236 "0 - %x sizeof(pt_regs) is %lx\n",
237 (int)addr, sizeof(struct pt_regs));
238 break;
239 }
240 if (addr == sizeof(struct pt_regs)) {
241 /* PT_TEXT_ADDR */
242 tmp = child->mm->start_code + TEXT_OFFSET;
243 } else if (addr == (sizeof(struct pt_regs) + 4)) {
244 /* PT_TEXT_END_ADDR */
245 tmp = child->mm->end_code;
246 } else if (addr == (sizeof(struct pt_regs) + 8)) {
247 /* PT_DATA_ADDR */
248 tmp = child->mm->start_data;
249#ifdef CONFIG_BINFMT_ELF_FDPIC
250 } else if (addr == (sizeof(struct pt_regs) + 12)) {
251 tmp = child->mm->context.exec_fdpic_loadmap;
252 } else if (addr == (sizeof(struct pt_regs) + 16)) {
253 tmp = child->mm->context.interp_fdpic_loadmap;
254#endif
255 } else {
256 tmp = get_reg(child, addr);
257 }
258 ret = put_user(tmp, (unsigned long *)data);
259 break;
260 }
261
262 /* when I and D space are separate, this will have to be fixed. */
263 case PTRACE_POKEDATA:
264 printk(KERN_NOTICE "ptrace: PTRACE_PEEKDATA\n");
265 /* fall through */
266 case PTRACE_POKETEXT: /* write the word at location addr. */
267 {
268 int copied;
269
270 ret = -EIO;
271 pr_debug("ptrace: POKETEXT at addr 0x%08lx + add %d %ld bytes %lx\n",
272 addr, add, sizeof(data), data);
273 if (is_user_addr_valid(child, addr + add, sizeof(data)) < 0)
274 break;
275 pr_debug("ptrace: user address is valid\n");
276
277#if L1_CODE_LENGTH != 0
278 if (addr + add >= L1_CODE_START
279 && addr + add + sizeof(data) <= L1_CODE_START + L1_CODE_LENGTH) {
280 safe_dma_memcpy ((void *)(addr + add), &data, sizeof(data));
281 copied = sizeof(data);
282 } else
283#endif
284 copied =
285 access_process_vm(child, addr + add, &data,
286 sizeof(data), 1);
287 pr_debug("ptrace: copied size %d\n", copied);
288 if (copied != sizeof(data))
289 break;
290 ret = 0;
291 break;
292 }
293
294 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
295 ret = -EIO;
296 if ((addr & 3) || (addr > (sizeof(struct pt_regs) + 16))) {
297 printk(KERN_WARNING "ptrace error : POKEUSR: temporarily returning 0\n");
298 break;
299 }
300
301 if (addr >= (sizeof(struct pt_regs))) {
302 ret = 0;
303 break;
304 }
305 if (addr == PT_SYSCFG) {
306 data &= SYSCFG_MASK;
307 data |= get_reg(child, PT_SYSCFG);
308 }
309 ret = put_reg(child, addr, data);
310 break;
311
312 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
313 case PTRACE_CONT:
314 { /* restart after signal. */
315 long tmp;
316
317 pr_debug("ptrace_cont\n");
318
319 ret = -EIO;
320 if (!valid_signal(data))
321 break;
322 if (request == PTRACE_SYSCALL)
323 set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
324 else
325 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
326
327 child->exit_code = data;
328 /* make sure the single step bit is not set. */
329 tmp = get_reg(child, PT_SYSCFG) & ~(TRACE_BITS);
330 put_reg(child, PT_SYSCFG, tmp);
331 pr_debug("before wake_up_process\n");
332 wake_up_process(child);
333 ret = 0;
334 break;
335 }
336
337 /*
338 * make the child exit. Best I can do is send it a sigkill.
339 * perhaps it should be put in the status that it wants to
340 * exit.
341 */
342 case PTRACE_KILL:
343 {
344 long tmp;
345 ret = 0;
346 if (child->exit_state == EXIT_ZOMBIE) /* already dead */
347 break;
348 child->exit_code = SIGKILL;
349 /* make sure the single step bit is not set. */
350 tmp = get_reg(child, PT_SYSCFG) & ~(TRACE_BITS);
351 put_reg(child, PT_SYSCFG, tmp);
352 wake_up_process(child);
353 break;
354 }
355
356 case PTRACE_SINGLESTEP:
357 { /* set the trap flag. */
358 long tmp;
359
360 pr_debug("single step\n");
361 ret = -EIO;
362 if (!valid_signal(data))
363 break;
364 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
365
366 tmp = get_reg(child, PT_SYSCFG) | (TRACE_BITS);
367 put_reg(child, PT_SYSCFG, tmp);
368
369 child->exit_code = data;
370 /* give it a chance to run. */
371 wake_up_process(child);
372 ret = 0;
373 break;
374 }
375
376 case PTRACE_DETACH:
377 { /* detach a process that was attached. */
378 ret = ptrace_detach(child, data);
379 break;
380 }
381
382 case PTRACE_GETREGS:
383 {
384
385 /* Get all gp regs from the child. */
386 ret = ptrace_getregs(child, (void __user *)data);
387 break;
388 }
389
390 case PTRACE_SETREGS:
391 {
392 printk(KERN_NOTICE
393 "ptrace: SETREGS: **** NOT IMPLEMENTED ***\n");
394 /* Set all gp regs in the child. */
395 ret = 0;
396 break;
397 }
398 default:
399 ret = ptrace_request(child, request, addr, data);
400 break;
401 }
402
403 return ret;
404}
405
406asmlinkage void syscall_trace(void)
407{
408
409 if (!test_thread_flag(TIF_SYSCALL_TRACE))
410 return;
411
412 if (!(current->ptrace & PT_PTRACED))
413 return;
414
415 /* the 0x80 provides a way for the tracing parent to distinguish
416 * between a syscall stop and SIGTRAP delivery
417 */
418 ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
419 ? 0x80 : 0));
420
421 /*
422 * this isn't the same as continuing with a signal, but it will do
423 * for normal use. strace only continues with a signal if the
424 * stopping signal is not SIGTRAP. -brl
425 */
426 if (current->exit_code) {
427 send_sig(current->exit_code, current, 1);
428 current->exit_code = 0;
429 }
430}