sh: Fix up broken 32-bit initrd support.
[linux-2.6] / arch / sh / kernel / ptrace_32.c
1 /*
2  * linux/arch/sh/kernel/ptrace.c
3  *
4  * Original x86 implementation:
5  *      By Ross Biro 1/23/92
6  *      edited by Linus Torvalds
7  *
8  * SuperH version:   Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
9  * Audit support: Yuichi Nakamura <ynakam@hitachisoft.jp>
10  */
11 #include <linux/kernel.h>
12 #include <linux/sched.h>
13 #include <linux/mm.h>
14 #include <linux/smp.h>
15 #include <linux/errno.h>
16 #include <linux/ptrace.h>
17 #include <linux/user.h>
18 #include <linux/slab.h>
19 #include <linux/security.h>
20 #include <linux/signal.h>
21 #include <linux/io.h>
22 #include <linux/audit.h>
23 #include <linux/seccomp.h>
24 #include <linux/tracehook.h>
25 #include <asm/uaccess.h>
26 #include <asm/pgtable.h>
27 #include <asm/system.h>
28 #include <asm/processor.h>
29 #include <asm/mmu_context.h>
30 #include <asm/syscalls.h>
31
32 /*
33  * does not yet catch signals sent when the child dies.
34  * in exit.c or in signal.c.
35  */
36
37 /*
38  * This routine will get a word off of the process kernel stack.
39  */
40 static inline int get_stack_long(struct task_struct *task, int offset)
41 {
42         unsigned char *stack;
43
44         stack = (unsigned char *)task_pt_regs(task);
45         stack += offset;
46         return (*((int *)stack));
47 }
48
49 /*
50  * This routine will put a word on the process kernel stack.
51  */
52 static inline int put_stack_long(struct task_struct *task, int offset,
53                                  unsigned long data)
54 {
55         unsigned char *stack;
56
57         stack = (unsigned char *)task_pt_regs(task);
58         stack += offset;
59         *(unsigned long *) stack = data;
60         return 0;
61 }
62
63 void user_enable_single_step(struct task_struct *child)
64 {
65         struct pt_regs *regs = task_pt_regs(child);
66         long pc;
67
68         pc = get_stack_long(child, (long)&regs->pc);
69
70         /* Next scheduling will set up UBC */
71         if (child->thread.ubc_pc == 0)
72                 ubc_usercnt += 1;
73
74         child->thread.ubc_pc = pc;
75
76         set_tsk_thread_flag(child, TIF_SINGLESTEP);
77 }
78
79 void user_disable_single_step(struct task_struct *child)
80 {
81         clear_tsk_thread_flag(child, TIF_SINGLESTEP);
82
83         /*
84          * Ensure the UBC is not programmed at the next context switch.
85          *
86          * Normally this is not needed but there are sequences such as
87          * singlestep, signal delivery, and continue that leave the
88          * ubc_pc non-zero leading to spurious SIGTRAPs.
89          */
90         if (child->thread.ubc_pc != 0) {
91                 ubc_usercnt -= 1;
92                 child->thread.ubc_pc = 0;
93         }
94 }
95
96 /*
97  * Called by kernel/ptrace.c when detaching..
98  *
99  * Make sure single step bits etc are not set.
100  */
101 void ptrace_disable(struct task_struct *child)
102 {
103         user_disable_single_step(child);
104 }
105
106 long arch_ptrace(struct task_struct *child, long request, long addr, long data)
107 {
108         struct user * dummy = NULL;
109         unsigned long __user *datap = (unsigned long __user *)data;
110         int ret;
111
112         switch (request) {
113         /* read the word at location addr in the USER area. */
114         case PTRACE_PEEKUSR: {
115                 unsigned long tmp;
116
117                 ret = -EIO;
118                 if ((addr & 3) || addr < 0 ||
119                     addr > sizeof(struct user) - 3)
120                         break;
121
122                 if (addr < sizeof(struct pt_regs))
123                         tmp = get_stack_long(child, addr);
124                 else if (addr >= (long) &dummy->fpu &&
125                          addr < (long) &dummy->u_fpvalid) {
126                         if (!tsk_used_math(child)) {
127                                 if (addr == (long)&dummy->fpu.fpscr)
128                                         tmp = FPSCR_INIT;
129                                 else
130                                         tmp = 0;
131                         } else
132                                 tmp = ((long *)&child->thread.fpu)
133                                         [(addr - (long)&dummy->fpu) >> 2];
134                 } else if (addr == (long) &dummy->u_fpvalid)
135                         tmp = !!tsk_used_math(child);
136                 else
137                         tmp = 0;
138                 ret = put_user(tmp, datap);
139                 break;
140         }
141
142         case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
143                 ret = -EIO;
144                 if ((addr & 3) || addr < 0 ||
145                     addr > sizeof(struct user) - 3)
146                         break;
147
148                 if (addr < sizeof(struct pt_regs))
149                         ret = put_stack_long(child, addr, data);
150                 else if (addr >= (long) &dummy->fpu &&
151                          addr < (long) &dummy->u_fpvalid) {
152                         set_stopped_child_used_math(child);
153                         ((long *)&child->thread.fpu)
154                                 [(addr - (long)&dummy->fpu) >> 2] = data;
155                         ret = 0;
156                 } else if (addr == (long) &dummy->u_fpvalid) {
157                         conditional_stopped_child_used_math(data, child);
158                         ret = 0;
159                 }
160                 break;
161
162 #ifdef CONFIG_SH_DSP
163         case PTRACE_GETDSPREGS: {
164                 unsigned long dp;
165
166                 ret = -EIO;
167                 dp = ((unsigned long) child) + THREAD_SIZE -
168                          sizeof(struct pt_dspregs);
169                 if (*((int *) (dp - 4)) == SR_FD) {
170                         copy_to_user((void *)addr, (void *) dp,
171                                 sizeof(struct pt_dspregs));
172                         ret = 0;
173                 }
174                 break;
175         }
176
177         case PTRACE_SETDSPREGS: {
178                 unsigned long dp;
179
180                 ret = -EIO;
181                 dp = ((unsigned long) child) + THREAD_SIZE -
182                          sizeof(struct pt_dspregs);
183                 if (*((int *) (dp - 4)) == SR_FD) {
184                         copy_from_user((void *) dp, (void *)addr,
185                                 sizeof(struct pt_dspregs));
186                         ret = 0;
187                 }
188                 break;
189         }
190 #endif
191 #ifdef CONFIG_BINFMT_ELF_FDPIC
192         case PTRACE_GETFDPIC: {
193                 unsigned long tmp = 0;
194
195                 switch (addr) {
196                 case PTRACE_GETFDPIC_EXEC:
197                         tmp = child->mm->context.exec_fdpic_loadmap;
198                         break;
199                 case PTRACE_GETFDPIC_INTERP:
200                         tmp = child->mm->context.interp_fdpic_loadmap;
201                         break;
202                 default:
203                         break;
204                 }
205
206                 ret = 0;
207                 if (put_user(tmp, datap)) {
208                         ret = -EFAULT;
209                         break;
210                 }
211                 break;
212         }
213 #endif
214         default:
215                 ret = ptrace_request(child, request, addr, data);
216                 break;
217         }
218
219         return ret;
220 }
221
222 static inline int audit_arch(void)
223 {
224         int arch = EM_SH;
225
226 #ifdef CONFIG_CPU_LITTLE_ENDIAN
227         arch |= __AUDIT_ARCH_LE;
228 #endif
229
230         return arch;
231 }
232
233 asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
234 {
235         long ret = 0;
236
237         secure_computing(regs->regs[0]);
238
239         if (test_thread_flag(TIF_SYSCALL_TRACE) &&
240             tracehook_report_syscall_entry(regs))
241                 /*
242                  * Tracing decided this syscall should not happen.
243                  * We'll return a bogus call number to get an ENOSYS
244                  * error, but leave the original number in regs->regs[0].
245                  */
246                 ret = -1L;
247
248         if (unlikely(current->audit_context))
249                 audit_syscall_entry(audit_arch(), regs->regs[3],
250                                     regs->regs[4], regs->regs[5],
251                                     regs->regs[6], regs->regs[7]);
252
253         return ret ?: regs->regs[0];
254 }
255
256 asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
257 {
258         int step;
259
260         if (unlikely(current->audit_context))
261                 audit_syscall_exit(AUDITSC_RESULT(regs->regs[0]),
262                                    regs->regs[0]);
263
264         step = test_thread_flag(TIF_SINGLESTEP);
265         if (step || test_thread_flag(TIF_SYSCALL_TRACE))
266                 tracehook_report_syscall_exit(regs, step);
267 }