/* * Copyright (C) 2004-2006 Atmel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #undef DEBUG #include <linux/kernel.h> #include <linux/sched.h> #include <linux/mm.h> #include <linux/smp_lock.h> #include <linux/ptrace.h> #include <linux/errno.h> #include <linux/user.h> #include <linux/security.h> #include <linux/unistd.h> #include <linux/notifier.h> #include <asm/traps.h> #include <asm/uaccess.h> #include <asm/ocd.h> #include <asm/mmu_context.h> #include <asm/kdebug.h> static struct pt_regs *get_user_regs(struct task_struct *tsk) { return (struct pt_regs *)((unsigned long) tsk->thread_info + THREAD_SIZE - sizeof(struct pt_regs)); } static void ptrace_single_step(struct task_struct *tsk) { pr_debug("ptrace_single_step: pid=%u, SR=0x%08lx\n", tsk->pid, tsk->thread.cpu_context.sr); if (!(tsk->thread.cpu_context.sr & SR_D)) { /* * Set a breakpoint at the current pc to force the * process into debug mode. The syscall/exception * exit code will set a breakpoint at the return * address when this flag is set. */ pr_debug("ptrace_single_step: Setting TIF_BREAKPOINT\n"); set_tsk_thread_flag(tsk, TIF_BREAKPOINT); } /* The monitor code will do the actual step for us */ set_tsk_thread_flag(tsk, TIF_SINGLE_STEP); } /* * Called by kernel/ptrace.c when detaching * * Make sure any single step bits, etc. are not set */ void ptrace_disable(struct task_struct *child) { clear_tsk_thread_flag(child, TIF_SINGLE_STEP); } /* * Handle hitting a breakpoint */ static void ptrace_break(struct task_struct *tsk, struct pt_regs *regs) { siginfo_t info; info.si_signo = SIGTRAP; info.si_errno = 0; info.si_code = TRAP_BRKPT; info.si_addr = (void __user *)instruction_pointer(regs); pr_debug("ptrace_break: Sending SIGTRAP to PID %u (pc = 0x%p)\n", tsk->pid, info.si_addr); force_sig_info(SIGTRAP, &info, tsk); } /* * Read the word at offset "offset" into the task's "struct user". We * actually access the pt_regs struct stored on the kernel stack. */ static int ptrace_read_user(struct task_struct *tsk, unsigned long offset, unsigned long __user *data) { unsigned long *regs; unsigned long value; pr_debug("ptrace_read_user(%p, %#lx, %p)\n", tsk, offset, data); if (offset & 3 || offset >= sizeof(struct user)) { printk("ptrace_read_user: invalid offset 0x%08lx\n", offset); return -EIO; } regs = (unsigned long *)get_user_regs(tsk); value = 0; if (offset < sizeof(struct pt_regs)) value = regs[offset / sizeof(regs[0])]; return put_user(value, data); } /* * Write the word "value" to offset "offset" into the task's "struct * user". We actually access the pt_regs struct stored on the kernel * stack. */ static int ptrace_write_user(struct task_struct *tsk, unsigned long offset, unsigned long value) { unsigned long *regs; if (offset & 3 || offset >= sizeof(struct user)) { printk("ptrace_write_user: invalid offset 0x%08lx\n", offset); return -EIO; } if (offset >= sizeof(struct pt_regs)) return 0; regs = (unsigned long *)get_user_regs(tsk); regs[offset / sizeof(regs[0])] = value; return 0; } static int ptrace_getregs(struct task_struct *tsk, void __user *uregs) { struct pt_regs *regs = get_user_regs(tsk); return copy_to_user(uregs, regs, sizeof(*regs)) ? -EFAULT : 0; } static int ptrace_setregs(struct task_struct *tsk, const void __user *uregs) { struct pt_regs newregs; int ret; ret = -EFAULT; if (copy_from_user(&newregs, uregs, sizeof(newregs)) == 0) { struct pt_regs *regs = get_user_regs(tsk); ret = -EINVAL; if (valid_user_regs(&newregs)) { *regs = newregs; ret = 0; } } return ret; } long arch_ptrace(struct task_struct *child, long request, long addr, long data) { unsigned long tmp; int ret; pr_debug("arch_ptrace(%ld, %d, %#lx, %#lx)\n", request, child->pid, addr, data); pr_debug("ptrace: Enabling monitor mode...\n"); __mtdr(DBGREG_DC, __mfdr(DBGREG_DC) | DC_MM | DC_DBE); switch (request) { /* Read the word at location addr in the child process */ case PTRACE_PEEKTEXT: case PTRACE_PEEKDATA: ret = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); if (ret == sizeof(tmp)) ret = put_user(tmp, (unsigned long __user *)data); else ret = -EIO; break; case PTRACE_PEEKUSR: ret = ptrace_read_user(child, addr, (unsigned long __user *)data); break; /* Write the word in data at location addr */ case PTRACE_POKETEXT: case PTRACE_POKEDATA: ret = access_process_vm(child, addr, &data, sizeof(data), 1); if (ret == sizeof(data)) ret = 0; else ret = -EIO; break; case PTRACE_POKEUSR: ret = ptrace_write_user(child, addr, data); break; /* continue and stop at next (return from) syscall */ case PTRACE_SYSCALL: /* restart after signal */ case PTRACE_CONT: ret = -EIO; if (!valid_signal(data)) break; if (request == PTRACE_SYSCALL) set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); else clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); child->exit_code = data; /* XXX: Are we sure no breakpoints are active here? */ wake_up_process(child); ret = 0; break; /* * Make the child exit. Best I can do is send it a * SIGKILL. Perhaps it should be put in the status that it * wants to exit. */ case PTRACE_KILL: ret = 0; if (child->exit_state == EXIT_ZOMBIE) break; child->exit_code = SIGKILL; wake_up_process(child); break; /* * execute single instruction. */ case PTRACE_SINGLESTEP: ret = -EIO; if (!valid_signal(data)) break; clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); ptrace_single_step(child); child->exit_code = data; wake_up_process(child); ret = 0; break; /* Detach a process that was attached */ case PTRACE_DETACH: ret = ptrace_detach(child, data); break; case PTRACE_GETREGS: ret = ptrace_getregs(child, (void __user *)data); break; case PTRACE_SETREGS: ret = ptrace_setregs(child, (const void __user *)data); break; default: ret = ptrace_request(child, request, addr, data); break; } pr_debug("sys_ptrace returning %d (DC = 0x%08lx)\n", ret, __mfdr(DBGREG_DC)); return ret; } asmlinkage void syscall_trace(void) { pr_debug("syscall_trace called\n"); if (!test_thread_flag(TIF_SYSCALL_TRACE)) return; if (!(current->ptrace & PT_PTRACED)) return; pr_debug("syscall_trace: notifying parent\n"); /* The 0x80 provides a way for the tracing parent to * distinguish between a syscall stop and SIGTRAP delivery */ ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); /* * this isn't the same as continuing with a signal, but it * will do for normal use. strace only continues with a * signal if the stopping signal is not SIGTRAP. -brl */ if (current->exit_code) { pr_debug("syscall_trace: sending signal %d to PID %u\n", current->exit_code, current->pid); send_sig(current->exit_code, current, 1); current->exit_code = 0; } } asmlinkage void do_debug_priv(struct pt_regs *regs) { unsigned long dc, ds; unsigned long die_val; ds = __mfdr(DBGREG_DS); pr_debug("do_debug_priv: pc = %08lx, ds = %08lx\n", regs->pc, ds); if (ds & DS_SSS) die_val = DIE_SSTEP; else die_val = DIE_BREAKPOINT; if (notify_die(die_val, regs, 0, SIGTRAP) == NOTIFY_STOP) return; if (likely(ds & DS_SSS)) { extern void itlb_miss(void); extern void tlb_miss_common(void); struct thread_info *ti; dc = __mfdr(DBGREG_DC); dc &= ~DC_SS; __mtdr(DBGREG_DC, dc); ti = current_thread_info(); ti->flags |= _TIF_BREAKPOINT; /* The TLB miss handlers don't check thread flags */ if ((regs->pc >= (unsigned long)&itlb_miss) && (regs->pc <= (unsigned long)&tlb_miss_common)) { __mtdr(DBGREG_BWA2A, sysreg_read(RAR_EX)); __mtdr(DBGREG_BWC2A, 0x40000001 | (get_asid() << 1)); } /* * If we're running in supervisor mode, the breakpoint * will take us where we want directly, no need to * single step. */ if ((regs->sr & MODE_MASK) != MODE_SUPERVISOR) ti->flags |= TIF_SINGLE_STEP; } else { panic("Unable to handle debug trap at pc = %08lx\n", regs->pc); } } /* * Handle breakpoints, single steps and other debuggy things. To keep * things simple initially, we run with interrupts and exceptions * disabled all the time. */ asmlinkage void do_debug(struct pt_regs *regs) { unsigned long dc, ds; ds = __mfdr(DBGREG_DS); pr_debug("do_debug: pc = %08lx, ds = %08lx\n", regs->pc, ds); if (test_thread_flag(TIF_BREAKPOINT)) { pr_debug("TIF_BREAKPOINT set\n"); /* We're taking care of it */ clear_thread_flag(TIF_BREAKPOINT); __mtdr(DBGREG_BWC2A, 0); } if (test_thread_flag(TIF_SINGLE_STEP)) { pr_debug("TIF_SINGLE_STEP set, ds = 0x%08lx\n", ds); if (ds & DS_SSS) { dc = __mfdr(DBGREG_DC); dc &= ~DC_SS; __mtdr(DBGREG_DC, dc); clear_thread_flag(TIF_SINGLE_STEP); ptrace_break(current, regs); } } else { /* regular breakpoint */ ptrace_break(current, regs); } }