]> bbs.cooldavid.org Git - net-next-2.6.git/blob - arch/tile/kernel/ptrace.c
468054928e7d4f62a517d88a2cc4100c452dca5a
[net-next-2.6.git] / arch / tile / kernel / ptrace.c
1 /*
2  * Copyright 2010 Tilera Corporation. All Rights Reserved.
3  *
4  *   This program is free software; you can redistribute it and/or
5  *   modify it under the terms of the GNU General Public License
6  *   as published by the Free Software Foundation, version 2.
7  *
8  *   This program is distributed in the hope that it will be useful, but
9  *   WITHOUT ANY WARRANTY; without even the implied warranty of
10  *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
11  *   NON INFRINGEMENT.  See the GNU General Public License for
12  *   more details.
13  *
14  * Copied from i386: Ross Biro 1/23/92
15  */
16
17 #include <linux/kernel.h>
18 #include <linux/ptrace.h>
19 #include <linux/kprobes.h>
20 #include <linux/compat.h>
21 #include <linux/uaccess.h>
22
23 void user_enable_single_step(struct task_struct *child)
24 {
25         set_tsk_thread_flag(child, TIF_SINGLESTEP);
26 }
27
28 void user_disable_single_step(struct task_struct *child)
29 {
30         clear_tsk_thread_flag(child, TIF_SINGLESTEP);
31 }
32
33 /*
34  * This routine will put a word on the process's privileged stack.
35  */
36 static void putreg(struct task_struct *task,
37                    unsigned long addr, unsigned long value)
38 {
39         unsigned int regno = addr / sizeof(unsigned long);
40         struct pt_regs *childregs = task_pt_regs(task);
41         childregs->regs[regno] = value;
42         childregs->flags |= PT_FLAGS_RESTORE_REGS;
43 }
44
45 static unsigned long getreg(struct task_struct *task, unsigned long addr)
46 {
47         unsigned int regno = addr / sizeof(unsigned long);
48         struct pt_regs *childregs = task_pt_regs(task);
49         return childregs->regs[regno];
50 }
51
52 /*
53  * Called by kernel/ptrace.c when detaching..
54  */
55 void ptrace_disable(struct task_struct *child)
56 {
57         clear_tsk_thread_flag(child, TIF_SINGLESTEP);
58
59         /*
60          * These two are currently unused, but will be set by arch_ptrace()
61          * and used in the syscall assembly when we do support them.
62          */
63         clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
64 }
65
66 long arch_ptrace(struct task_struct *child, long request, long addr, long data)
67 {
68         unsigned long __user *datap;
69         unsigned long tmp;
70         int i;
71         long ret = -EIO;
72
73 #ifdef CONFIG_COMPAT
74         if (task_thread_info(current)->status & TS_COMPAT)
75                 data = (u32)data;
76         if (task_thread_info(child)->status & TS_COMPAT)
77                 addr = (u32)addr;
78 #endif
79         datap = (unsigned long __user *)data;
80
81         switch (request) {
82
83         case PTRACE_PEEKUSR:  /* Read register from pt_regs. */
84                 if (addr & (sizeof(data)-1))
85                         break;
86                 if (addr < 0 || addr >= PTREGS_SIZE)
87                         break;
88                 tmp = getreg(child, addr);   /* Read register */
89                 ret = put_user(tmp, datap);
90                 break;
91
92         case PTRACE_POKEUSR:  /* Write register in pt_regs. */
93                 if (addr & (sizeof(data)-1))
94                         break;
95                 if (addr < 0 || addr >= PTREGS_SIZE)
96                         break;
97                 putreg(child, addr, data);   /* Write register */
98                 break;
99
100         case PTRACE_GETREGS:  /* Get all registers from the child. */
101                 if (!access_ok(VERIFY_WRITE, datap, PTREGS_SIZE))
102                         break;
103                 for (i = 0; i < PTREGS_SIZE; i += sizeof(long)) {
104                         ret = __put_user(getreg(child, i), datap);
105                         if (ret != 0)
106                                 break;
107                         datap++;
108                 }
109                 break;
110
111         case PTRACE_SETREGS:  /* Set all registers in the child. */
112                 if (!access_ok(VERIFY_READ, datap, PTREGS_SIZE))
113                         break;
114                 for (i = 0; i < PTREGS_SIZE; i += sizeof(long)) {
115                         ret = __get_user(tmp, datap);
116                         if (ret != 0)
117                                 break;
118                         putreg(child, i, tmp);
119                         datap++;
120                 }
121                 break;
122
123         case PTRACE_GETFPREGS:  /* Get the child FPU state. */
124         case PTRACE_SETFPREGS:  /* Set the child FPU state. */
125                 break;
126
127         case PTRACE_SETOPTIONS:
128                 /* Support TILE-specific ptrace options. */
129                 child->ptrace &= ~PT_TRACE_MASK_TILE;
130                 tmp = data & PTRACE_O_MASK_TILE;
131                 data &= ~PTRACE_O_MASK_TILE;
132                 ret = ptrace_request(child, request, addr, data);
133                 if (tmp & PTRACE_O_TRACEMIGRATE)
134                         child->ptrace |= PT_TRACE_MIGRATE;
135                 break;
136
137         default:
138 #ifdef CONFIG_COMPAT
139                 if (task_thread_info(current)->status & TS_COMPAT) {
140                         ret = compat_ptrace_request(child, request,
141                                                     addr, data);
142                         break;
143                 }
144 #endif
145                 ret = ptrace_request(child, request, addr, data);
146                 break;
147         }
148
149         return ret;
150 }
151
152 #ifdef CONFIG_COMPAT
153 /* Not used; we handle compat issues in arch_ptrace() directly. */
154 long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
155                                compat_ulong_t addr, compat_ulong_t data)
156 {
157         BUG();
158 }
159 #endif
160
161 void do_syscall_trace(void)
162 {
163         if (!test_thread_flag(TIF_SYSCALL_TRACE))
164                 return;
165
166         if (!(current->ptrace & PT_PTRACED))
167                 return;
168
169         /*
170          * The 0x80 provides a way for the tracing parent to distinguish
171          * between a syscall stop and SIGTRAP delivery
172          */
173         ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
174
175         /*
176          * this isn't the same as continuing with a signal, but it will do
177          * for normal use.  strace only continues with a signal if the
178          * stopping signal is not SIGTRAP.  -brl
179          */
180         if (current->exit_code) {
181                 send_sig(current->exit_code, current, 1);
182                 current->exit_code = 0;
183         }
184 }
185
186 void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code)
187 {
188         struct siginfo info;
189
190         memset(&info, 0, sizeof(info));
191         info.si_signo = SIGTRAP;
192         info.si_code  = TRAP_BRKPT;
193         info.si_addr  = (void __user *) regs->pc;
194
195         /* Send us the fakey SIGTRAP */
196         force_sig_info(SIGTRAP, &info, tsk);
197 }
198
199 /* Handle synthetic interrupt delivered only by the simulator. */
200 void __kprobes do_breakpoint(struct pt_regs* regs, int fault_num)
201 {
202         send_sigtrap(current, regs, fault_num);
203 }