commented early_printk patch because of rejects.
[linux-flexiantxendom0-3.2.10.git] / arch / h8300 / kernel / ptrace.c
1 /*
2  *  linux/arch/h8300/kernel/ptrace.c
3  *
4  *  Yoshinori Sato <qzb04471@nifty.ne.jp>
5  *
6  *  Based on:
7  *  linux/arch/m68k/kernel/ptrace.c
8  *
9  *  Copyright (C) 1994 by Hamish Macdonald
10  *  Taken from linux/kernel/ptrace.c and modified for M680x0.
11  *  linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds
12  *
13  * This file is subject to the terms and conditions of the GNU General
14  * Public License.  See the file COPYING in the main directory of
15  * this archive for more details.
16  */
17
18 #include <linux/kernel.h>
19 #include <linux/sched.h>
20 #include <linux/mm.h>
21 #include <linux/smp.h>
22 #include <linux/smp_lock.h>
23 #include <linux/errno.h>
24 #include <linux/ptrace.h>
25 #include <linux/user.h>
26 #include <linux/config.h>
27
28 #include <asm/uaccess.h>
29 #include <asm/page.h>
30 #include <asm/pgtable.h>
31 #include <asm/system.h>
32 #include <asm/processor.h>
33 #include <asm/signal.h>
34
35 /*
36  * does not yet catch signals sent when the child dies.
37  * in exit.c or in signal.c.
38  */
39
40 /* determines which bits in the SR the user has access to. */
41 /* 1 = access 0 = no access */
42 #define SR_MASK 0x001f
43
44 /* sets the trace bits. */
45 #define TRACE_BITS 0x8000
46
47 /* Find the stack offset for a register, relative to thread.esp0. */
48 #define PT_REG(reg)     ((long)&((struct pt_regs *)0)->reg)
49 /* Mapping from PT_xxx to the stack offset at which the register is
50    saved.  Notice that usp has no stack-slot and needs to be treated
51    specially (see get_reg/put_reg below). */
52 static const int regoff[] = {
53         PT_REG(er1), PT_REG(er2), PT_REG(er3), PT_REG(er4),
54         PT_REG(er5), PT_REG(er6), PT_REG(er0), PT_REG(orig_er0),
55         PT_REG(ccr), PT_REG(pc)
56 };
57
58 /*
59  * Get contents of register REGNO in task TASK.
60  */
61 static inline long get_reg(struct task_struct *task, int regno)
62 {
63         unsigned long *addr;
64
65         if (regno == PT_USP)
66                 addr = &task->thread.usp;
67         else if (regno < sizeof(regoff)/sizeof(regoff[0]))
68                 addr = (unsigned long *)(task->thread.esp0 + regoff[regno]);
69         else
70                 return 0;
71         return *addr;
72 }
73
74 /*
75  * Write contents of register REGNO in task TASK.
76  */
77 static inline int put_reg(struct task_struct *task, int regno,
78                           unsigned long data)
79 {
80         unsigned long *addr;
81
82         if (regno == PT_USP)
83                 addr = &task->thread.usp;
84         else if (regno < sizeof(regoff)/sizeof(regoff[0]))
85                 addr = (unsigned long *) (task->thread.esp0 + regoff[regno]);
86         else
87                 return -1;
88         *addr = data;
89         return 0;
90 }
91
92 /*
93  * Called by kernel/ptrace.c when detaching..
94  *
95  * Make sure the single step bit is not set.
96  */
97 int ptrace_cancel_bpt(struct task_struct *child)
98 {
99         int i,r=0;
100
101         for(i=0; i<4; i++) {
102                 if (child->thread.debugreg[i]) {
103                         if (child->thread.debugreg[i] != ~0)
104                                 put_user(child->thread.debugreg[i+4],
105                                          (unsigned short *)child->thread.debugreg[i]);
106                         r = 1;
107                         child->thread.debugreg[i] = 0;
108                 }
109         }
110         return r;
111 }
112
113 const static unsigned char opcode0[]={
114   0x04,0x02,0x04,0x02,0x04,0x02,0x04,0x02,  /* 0x58 */
115   0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,  /* 0x60 */
116   0x02,0x02,0x11,0x11,0x02,0x02,0x04,0x04,  /* 0x68 */
117   0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,  /* 0x70 */
118   0x08,0x04,0x06,0x04,0x04,0x04,0x04,0x04}; /* 0x78 */
119
120 const static int table_parser01(unsigned char *pc);
121 const static int table_parser02(unsigned char *pc);
122 const static int table_parser100(unsigned char *pc);
123 const static int table_parser101(unsigned char *pc);
124
125 const static int (*parsers[])(unsigned char *pc)={table_parser01,table_parser02};
126
127 static int insn_length(unsigned char *pc)
128 {
129   if (*pc == 0x01)
130     return table_parser01(pc+1);
131   if (*pc < 0x58 || *pc>=0x80) 
132     return 2;
133   else
134     if (opcode0[*pc-0x58]<0x10)
135       return opcode0[*pc-0x58];
136     else
137       return (*parsers[opcode0[*pc-0x58]-0x10])(pc+1);
138 }
139
140 const static int table_parser01(unsigned char *pc)
141 {
142   const unsigned char codelen[]={0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,
143                                  0x02,0x00,0x00,0x00,0x04,0x04,0x00,0x04};
144   const static int (*parsers[])(unsigned char *)={table_parser100,table_parser101};
145   unsigned char second_index;
146   second_index = (*pc) >> 4;
147   if (codelen[second_index]<0x10)
148     return codelen[second_index];
149   else
150     return parsers[codelen[second_index]-0x10](pc);
151 }
152
153 const static int table_parser02(unsigned char *pc)
154 {
155   return (*pc & 0x20)?0x06:0x04;
156 }
157
158 const static int table_parser100(unsigned char *pc)
159 {
160   return (*(pc+2) & 0x02)?0x08:0x06;
161 }
162
163 const static int table_parser101(unsigned char *pc)
164 {
165   return (*(pc+2) & 0x02)?0x08:0x06;
166 }
167
168 #define BREAK_INST 0x5730 /* TRAPA #3 */
169
170 int ptrace_set_bpt(struct task_struct *child)
171 {
172         unsigned long pc,next;
173         unsigned short insn;
174         pc = get_reg(child,PT_PC);
175         next = insn_length((unsigned char *)pc) + pc;
176         get_user(insn,(unsigned short *)pc);
177         if (insn == 0x5470) {
178                 /* rts */ 
179                 unsigned long sp;
180                 sp = get_reg(child,PT_USP);
181                 get_user(next,(unsigned long *)sp);
182         } else if ((insn & 0xfb00) != 0x5800) {
183                 /* jmp / jsr */
184                 int regs;
185                 const short reg_tbl[]={PT_ER0,PT_ER1,PT_ER2,PT_ER3,
186                                        PT_ER4,PT_ER5,PT_ER6,PT_USP};
187                 switch(insn & 0xfb00) {
188                         case 0x5900:
189                                regs = (insn & 0x0070) >> 8;
190                                next = get_reg(child,reg_tbl[regs]);
191                                break;
192                         case 0x5a00:
193                                get_user(next,(unsigned long *)(pc+2));
194                                next &= 0x00ffffff;
195                                break;
196                         case 0x5b00:
197                                /* unneccessary? */
198                                next = *(unsigned long *)(insn & 0xff);
199                                break;
200                 }
201         } else if (((insn & 0xf000) == 0x4000) || ((insn &0xff00) == 0x5500)) { 
202                 /* b**:8 */
203                 unsigned long dsp;
204                 dsp = (long)(insn && 0xff)+pc+2;
205                 child->thread.debugreg[1] = dsp;
206                 get_user(child->thread.debugreg[5],(unsigned short *)dsp);
207                 put_user(BREAK_INST,(unsigned short *)dsp);
208         } else if (((insn & 0xff00) == 0x5800) || ((insn &0xff00) == 0x5c00)) { 
209                 /* b**:16 */
210                 unsigned long dsp;
211                 get_user(dsp,(unsigned short *)(pc+2));
212                 dsp = (long)dsp+pc+4;
213                 child->thread.debugreg[1] = dsp;
214                 get_user(child->thread.debugreg[5],(unsigned short *)dsp);
215                 put_user(BREAK_INST,(unsigned short *)dsp);
216         }
217         child->thread.debugreg[0] = next;
218         get_user(child->thread.debugreg[4],(unsigned short *)next);
219         put_user(BREAK_INST,(unsigned short *)next);
220         return 0;
221 }
222
223 inline
224 static int read_long(struct task_struct * tsk, unsigned long addr,
225         unsigned long * result)
226 {
227         *result = *(unsigned long *)addr;
228         return 0;
229 }
230
231 void ptrace_disable(struct task_struct *child)
232 {
233         ptrace_cancel_bpt(child);
234 }
235
236 asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
237 {
238         struct task_struct *child;
239         int ret;
240
241         lock_kernel();
242         ret = -EPERM;
243         if (request == PTRACE_TRACEME) {
244                 /* are we already being traced? */
245                 if (current->ptrace & PT_PTRACED)
246                         goto out;
247                 /* set the ptrace bit in the process flags. */
248                 current->ptrace |= PT_PTRACED;
249                 ret = 0;
250                 goto out;
251         }
252         ret = -ESRCH;
253         read_lock(&tasklist_lock);
254         child = find_task_by_pid(pid);
255         if (child)
256                 get_task_struct(child);
257         read_unlock(&tasklist_lock);
258         if (!child)
259                 goto out;
260
261         ret = -EPERM;
262         if (pid == 1)           /* you may not mess with init */
263                 goto out_tsk;
264
265         if (request == PTRACE_ATTACH) {
266                 ret = ptrace_attach(child);
267                 goto out_tsk;
268         }
269         ret = -ESRCH;
270         if (!(child->ptrace & PT_PTRACED))
271                 goto out_tsk;
272         if (child->state != TASK_STOPPED) {
273                 if (request != PTRACE_KILL)
274                         goto out_tsk;
275         }
276         ret = ptrace_check_attach(child, request == PTRACE_KILL);
277         if (ret < 0)
278                 goto out_tsk;
279
280         switch (request) {
281                 case PTRACE_PEEKTEXT: /* read word at location addr. */ 
282                 case PTRACE_PEEKDATA: {
283                         unsigned long tmp;
284
285                         ret = read_long(child, addr, &tmp);
286                         if (ret < 0)
287                                 break ;
288                         ret = verify_area(VERIFY_WRITE, (void *) data, sizeof(long));
289                         if (!ret)
290                                 put_user(tmp, (unsigned long *) data);
291                         break ;
292                 }
293
294         /* read the word at location addr in the USER area. */
295                 case PTRACE_PEEKUSR: {
296                         unsigned long tmp;
297                         
298                         if ((addr & 3) || addr < 0 || addr >= sizeof(struct user))
299                                 ret = -EIO;
300                         
301                         ret = verify_area(VERIFY_WRITE, (void *) data,
302                                           sizeof(long));
303                         if (ret)
304                                 break ;
305                         tmp = 0;  /* Default return condition */
306                         addr = addr >> 2; /* temporary hack. */
307                         if (addr < 10)
308                                 tmp = get_reg(child, addr);
309                         else {
310                                 ret = -EIO;
311                                 break ;
312                         }
313                         put_user(tmp,(unsigned long *) data);
314                         ret = 0;
315                         break ;
316                 }
317
318       /* when I and D space are separate, this will have to be fixed. */
319                 case PTRACE_POKETEXT: /* write the word at location addr. */
320                 case PTRACE_POKEDATA:
321                         ret = 0;
322                         if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
323                                 break;
324                         ret = -EIO;
325                         break;
326
327                 case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
328                         if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) {
329                                 ret = -EIO;
330                                 break ;
331                         }
332                         addr = addr >> 2; /* temporary hack. */
333                             
334                         if (addr == PT_ORIG_ER0) {
335                                 ret = -EIO;
336                                 break ;
337                         }
338                         if (addr == PT_CCR) {
339                                 data &= SR_MASK;
340                         }
341                         if (addr < 10) {
342                                 if (put_reg(child, addr, data))
343                                         ret = -EIO;
344                                 else
345                                         ret = 0;
346                                 break ;
347                         }
348                         ret = -EIO;
349                         break ;
350                 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
351                 case PTRACE_CONT: { /* restart after signal. */
352                         ret = -EIO;
353                         if ((unsigned long) data >= _NSIG)
354                                 break ;
355                         if (request == PTRACE_SYSCALL)
356                                 set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
357                         else
358                                 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
359                         child->exit_code = data;
360                         wake_up_process(child);
361                         /* make sure the single step bit is not set. */
362                         ptrace_cancel_bpt(child);
363                         ret = 0;
364                 }
365
366 /*
367  * make the child exit.  Best I can do is send it a sigkill. 
368  * perhaps it should be put in the status that it wants to 
369  * exit.
370  */
371                 case PTRACE_KILL: {
372
373                         ret = 0;
374                         if (child->state == TASK_ZOMBIE) /* already dead */
375                                 break;
376                         child->exit_code = SIGKILL;
377                         ptrace_cancel_bpt(child);
378                         wake_up_process(child);
379                         break;
380                 }
381
382                 case PTRACE_SINGLESTEP: {  /* set the trap flag. */
383                         ret = -EIO;
384                         if ((unsigned long) data > _NSIG)
385                                 break;
386                         clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
387                         child->thread.debugreg[0]=-1;
388                         child->exit_code = data;
389                         wake_up_process(child);
390                         ret = 0;
391                         break;
392                 }
393
394                 case PTRACE_DETACH:     /* detach a process that was attached. */
395                         ret = ptrace_detach(child, data);
396                         break;
397
398                 case PTRACE_GETREGS: { /* Get all gp regs from the child. */
399                         int i;
400                         unsigned long tmp;
401                         for (i = 0; i < 19; i++) {
402                             tmp = get_reg(child, i);
403                             if (put_user(tmp, (unsigned long *) data)) {
404                                 ret = -EFAULT;
405                                 break;
406                             }
407                             data += sizeof(long);
408                         }
409                         ret = 0;
410                         break;
411                 }
412
413                 case PTRACE_SETREGS: { /* Set all gp regs in the child. */
414                         int i;
415                         unsigned long tmp;
416                         for (i = 0; i < 10; i++) {
417                             if (get_user(tmp, (unsigned long *) data)) {
418                                 ret = -EFAULT;
419                                 break;
420                             }
421                             put_reg(child, i, tmp);
422                             data += sizeof(long);
423                         }
424                         ret = 0;
425                         break;
426                 }
427
428                 default:
429                         ret = -EIO;
430                         break;
431         }
432 out_tsk:
433         put_task_struct(child);
434 out:
435         unlock_kernel();
436         return ret;
437 }
438
439 asmlinkage void syscall_trace(void)
440 {
441         if (!test_thread_flag(TIF_SYSCALL_TRACE))
442                 return;
443         if (!(current->ptrace & PT_PTRACED))
444                 return;
445         current->exit_code = SIGTRAP;
446         current->state = TASK_STOPPED;
447         notify_parent(current, SIGCHLD);
448         schedule();
449         /*
450          * this isn't the same as continuing with a signal, but it will do
451          * for normal use.  strace only continues with a signal if the
452          * stopping signal is not SIGTRAP.  -brl
453          */
454         if (current->exit_code) {
455                 send_sig(current->exit_code, current, 1);
456                 current->exit_code = 0;
457         }
458 }
459
460 asmlinkage void trace_trap(unsigned long bp)
461 {
462         if (current->thread.debugreg[0] == bp ||
463             current->thread.debugreg[1] == bp) {
464                 ptrace_cancel_bpt(current);
465                 force_sig(SIGTRAP,current);
466         } else
467                 force_sig(SIGILL,current);
468 }