|  | /* | 
|  | * 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/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 <linux/kdebug.h> | 
|  |  | 
|  | static struct pt_regs *get_user_regs(struct task_struct *tsk) | 
|  | { | 
|  | return (struct pt_regs *)((unsigned long)task_stack_page(tsk) + | 
|  | THREAD_SIZE - sizeof(struct pt_regs)); | 
|  | } | 
|  |  | 
|  | void user_enable_single_step(struct task_struct *tsk) | 
|  | { | 
|  | pr_debug("user_enable_single_step: pid=%u, PC=0x%08lx, SR=0x%08lx\n", | 
|  | tsk->pid, task_pt_regs(tsk)->pc, task_pt_regs(tsk)->sr); | 
|  |  | 
|  | /* | 
|  | * We can't schedule in Debug mode, so when TIF_BREAKPOINT is | 
|  | * set, the system call or exception handler will do a | 
|  | * breakpoint to enter monitor mode before returning to | 
|  | * userspace. | 
|  | * | 
|  | * The monitor code will then notice that TIF_SINGLE_STEP is | 
|  | * set and return to userspace with single stepping enabled. | 
|  | * The CPU will then enter monitor mode again after exactly | 
|  | * one instruction has been executed, and the monitor code | 
|  | * will then send a SIGTRAP to the process. | 
|  | */ | 
|  | set_tsk_thread_flag(tsk, TIF_BREAKPOINT); | 
|  | set_tsk_thread_flag(tsk, TIF_SINGLE_STEP); | 
|  | } | 
|  |  | 
|  | void user_disable_single_step(struct task_struct *child) | 
|  | { | 
|  | /* XXX(hch): a no-op here seems wrong.. */ | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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); | 
|  | clear_tsk_thread_flag(child, TIF_BREAKPOINT); | 
|  | ocd_disable(child); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * 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; | 
|  |  | 
|  | 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])]; | 
|  |  | 
|  | pr_debug("ptrace_read_user(%s[%u], %#lx, %p) -> %#lx\n", | 
|  | tsk->comm, tsk->pid, offset, data, value); | 
|  |  | 
|  | 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; | 
|  |  | 
|  | pr_debug("ptrace_write_user(%s[%u], %#lx, %#lx)\n", | 
|  | tsk->comm, tsk->pid, offset, value); | 
|  |  | 
|  | if (offset & 3 || offset >= sizeof(struct user)) { | 
|  | pr_debug("  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, | 
|  | unsigned long addr, unsigned long data) | 
|  | { | 
|  | int ret; | 
|  | void __user *datap = (void __user *) data; | 
|  |  | 
|  | switch (request) { | 
|  | /* Read the word at location addr in the child process */ | 
|  | case PTRACE_PEEKTEXT: | 
|  | case PTRACE_PEEKDATA: | 
|  | ret = generic_ptrace_peekdata(child, addr, data); | 
|  | break; | 
|  |  | 
|  | case PTRACE_PEEKUSR: | 
|  | ret = ptrace_read_user(child, addr, datap); | 
|  | break; | 
|  |  | 
|  | /* Write the word in data at location addr */ | 
|  | case PTRACE_POKETEXT: | 
|  | case PTRACE_POKEDATA: | 
|  | ret = generic_ptrace_pokedata(child, addr, data); | 
|  | break; | 
|  |  | 
|  | case PTRACE_POKEUSR: | 
|  | ret = ptrace_write_user(child, addr, data); | 
|  | break; | 
|  |  | 
|  | case PTRACE_GETREGS: | 
|  | ret = ptrace_getregs(child, datap); | 
|  | break; | 
|  |  | 
|  | case PTRACE_SETREGS: | 
|  | ret = ptrace_setregs(child, datap); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | ret = ptrace_request(child, request, addr, data); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | asmlinkage void syscall_trace(void) | 
|  | { | 
|  | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | 
|  | return; | 
|  | if (!(current->ptrace & PT_PTRACED)) | 
|  | return; | 
|  |  | 
|  | /* 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * debug_trampoline() is an assembly stub which will store all user | 
|  | * registers on the stack and execute a breakpoint instruction. | 
|  | * | 
|  | * If we single-step into an exception handler which runs with | 
|  | * interrupts disabled the whole time so it doesn't have to check for | 
|  | * pending work, its return address will be modified so that it ends | 
|  | * up returning to debug_trampoline. | 
|  | * | 
|  | * If the exception handler decides to store the user context and | 
|  | * enable interrupts after all, it will restore the original return | 
|  | * address and status register value. Before it returns, it will | 
|  | * notice that TIF_BREAKPOINT is set and execute a breakpoint | 
|  | * instruction. | 
|  | */ | 
|  | extern void debug_trampoline(void); | 
|  |  | 
|  | asmlinkage struct pt_regs *do_debug(struct pt_regs *regs) | 
|  | { | 
|  | struct thread_info	*ti; | 
|  | unsigned long		trampoline_addr; | 
|  | u32			status; | 
|  | u32			ctrl; | 
|  | int			code; | 
|  |  | 
|  | status = ocd_read(DS); | 
|  | ti = current_thread_info(); | 
|  | code = TRAP_BRKPT; | 
|  |  | 
|  | pr_debug("do_debug: status=0x%08x PC=0x%08lx SR=0x%08lx tif=0x%08lx\n", | 
|  | status, regs->pc, regs->sr, ti->flags); | 
|  |  | 
|  | if (!user_mode(regs)) { | 
|  | unsigned long	die_val = DIE_BREAKPOINT; | 
|  |  | 
|  | if (status & (1 << OCD_DS_SSS_BIT)) | 
|  | die_val = DIE_SSTEP; | 
|  |  | 
|  | if (notify_die(die_val, "ptrace", regs, 0, 0, SIGTRAP) | 
|  | == NOTIFY_STOP) | 
|  | return regs; | 
|  |  | 
|  | if ((status & (1 << OCD_DS_SWB_BIT)) | 
|  | && test_and_clear_ti_thread_flag( | 
|  | ti, TIF_BREAKPOINT)) { | 
|  | /* | 
|  | * Explicit breakpoint from trampoline or | 
|  | * exception/syscall/interrupt handler. | 
|  | * | 
|  | * The real saved regs are on the stack right | 
|  | * after the ones we saved on entry. | 
|  | */ | 
|  | regs++; | 
|  | pr_debug("  -> TIF_BREAKPOINT done, adjusted regs:" | 
|  | "PC=0x%08lx SR=0x%08lx\n", | 
|  | regs->pc, regs->sr); | 
|  | BUG_ON(!user_mode(regs)); | 
|  |  | 
|  | if (test_thread_flag(TIF_SINGLE_STEP)) { | 
|  | pr_debug("Going to do single step...\n"); | 
|  | return regs; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * No TIF_SINGLE_STEP means we're done | 
|  | * stepping over a syscall. Do the trap now. | 
|  | */ | 
|  | code = TRAP_TRACE; | 
|  | } else if ((status & (1 << OCD_DS_SSS_BIT)) | 
|  | && test_ti_thread_flag(ti, TIF_SINGLE_STEP)) { | 
|  |  | 
|  | pr_debug("Stepped into something, " | 
|  | "setting TIF_BREAKPOINT...\n"); | 
|  | set_ti_thread_flag(ti, TIF_BREAKPOINT); | 
|  |  | 
|  | /* | 
|  | * We stepped into an exception, interrupt or | 
|  | * syscall handler. Some exception handlers | 
|  | * don't check for pending work, so we need to | 
|  | * set up a trampoline just in case. | 
|  | * | 
|  | * The exception entry code will undo the | 
|  | * trampoline stuff if it does a full context | 
|  | * save (which also means that it'll check for | 
|  | * pending work later.) | 
|  | */ | 
|  | if ((regs->sr & MODE_MASK) == MODE_EXCEPTION) { | 
|  | trampoline_addr | 
|  | = (unsigned long)&debug_trampoline; | 
|  |  | 
|  | pr_debug("Setting up trampoline...\n"); | 
|  | ti->rar_saved = sysreg_read(RAR_EX); | 
|  | ti->rsr_saved = sysreg_read(RSR_EX); | 
|  | sysreg_write(RAR_EX, trampoline_addr); | 
|  | sysreg_write(RSR_EX, (MODE_EXCEPTION | 
|  | | SR_EM | SR_GM)); | 
|  | BUG_ON(ti->rsr_saved & MODE_MASK); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * If we stepped into a system call, we | 
|  | * shouldn't do a single step after we return | 
|  | * since the return address is right after the | 
|  | * "scall" instruction we were told to step | 
|  | * over. | 
|  | */ | 
|  | if ((regs->sr & MODE_MASK) == MODE_SUPERVISOR) { | 
|  | pr_debug("Supervisor; no single step\n"); | 
|  | clear_ti_thread_flag(ti, TIF_SINGLE_STEP); | 
|  | } | 
|  |  | 
|  | ctrl = ocd_read(DC); | 
|  | ctrl &= ~(1 << OCD_DC_SS_BIT); | 
|  | ocd_write(DC, ctrl); | 
|  |  | 
|  | return regs; | 
|  | } else { | 
|  | printk(KERN_ERR "Unexpected OCD_DS value: 0x%08x\n", | 
|  | status); | 
|  | printk(KERN_ERR "Thread flags: 0x%08lx\n", ti->flags); | 
|  | die("Unhandled debug trap in kernel mode", | 
|  | regs, SIGTRAP); | 
|  | } | 
|  | } else if (status & (1 << OCD_DS_SSS_BIT)) { | 
|  | /* Single step in user mode */ | 
|  | code = TRAP_TRACE; | 
|  |  | 
|  | ctrl = ocd_read(DC); | 
|  | ctrl &= ~(1 << OCD_DC_SS_BIT); | 
|  | ocd_write(DC, ctrl); | 
|  | } | 
|  |  | 
|  | pr_debug("Sending SIGTRAP: code=%d PC=0x%08lx SR=0x%08lx\n", | 
|  | code, regs->pc, regs->sr); | 
|  |  | 
|  | clear_thread_flag(TIF_SINGLE_STEP); | 
|  | _exception(SIGTRAP, regs, code, instruction_pointer(regs)); | 
|  |  | 
|  | return regs; | 
|  | } |