diff options
Diffstat (limited to 'target/linux/realtek/files/arch/rlx/kernel/ptrace.c')
-rw-r--r-- | target/linux/realtek/files/arch/rlx/kernel/ptrace.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/target/linux/realtek/files/arch/rlx/kernel/ptrace.c b/target/linux/realtek/files/arch/rlx/kernel/ptrace.c new file mode 100644 index 000000000..85d370f4d --- /dev/null +++ b/target/linux/realtek/files/arch/rlx/kernel/ptrace.c @@ -0,0 +1,282 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1992 Ross Biro + * Copyright (C) Linus Torvalds + * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle + * Copyright (C) 1996 David S. Miller + * Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com + * Copyright (C) 1999 MIPS Technologies, Inc. + * Copyright (C) 2000 Ulf Carlsson + * + * At this time Linux/MIPS64 only supports syscall tracing, even for 32-bit + * binaries. + */ +#include <linux/compiler.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/smp.h> +#include <linux/user.h> +#include <linux/security.h> +#include <linux/audit.h> +#include <linux/seccomp.h> + +#include <asm/byteorder.h> +#include <asm/cpu.h> +#include <asm/rlxregs.h> +#include <asm/pgtable.h> +#include <asm/page.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/bootinfo.h> +#include <asm/reg.h> + +/* + * Called by kernel/ptrace.c when detaching.. + * + * Make sure single step bits etc are not set. + */ +void ptrace_disable(struct task_struct *child) +{ + /* Don't load the watchpoint registers for the ex-child. */ + //clear_tsk_thread_flag(child, TIF_LOAD_WATCH); +} + +/* + * Read a general register set. We always use the 64-bit format, even + * for 32-bit kernels and for 32-bit processes on a 64-bit kernel. + * Registers are sign extended to fill the available space. + */ +int ptrace_getregs(struct task_struct *child, __s64 __user *data) +{ + struct pt_regs *regs; + int i; + + if (!access_ok(VERIFY_WRITE, data, 38 * 8)) + return -EIO; + + regs = task_pt_regs(child); + + for (i = 0; i < 32; i++) + __put_user((long)regs->regs[i], data + i); + __put_user((long)regs->lo, data + EF_LO - EF_R0); + __put_user((long)regs->hi, data + EF_HI - EF_R0); + __put_user((long)regs->cp0_epc, data + EF_CP0_EPC - EF_R0); + __put_user((long)regs->cp0_badvaddr, data + EF_CP0_BADVADDR - EF_R0); + __put_user((long)regs->cp0_status, data + EF_CP0_STATUS - EF_R0); + __put_user((long)regs->cp0_cause, data + EF_CP0_CAUSE - EF_R0); + + return 0; +} + +/* + * Write a general register set. As for PTRACE_GETREGS, we always use + * the 64-bit format. On a 32-bit kernel only the lower order half + * (according to endianness) will be used. + */ +int ptrace_setregs(struct task_struct *child, __s64 __user *data) +{ + struct pt_regs *regs; + int i; + + if (!access_ok(VERIFY_READ, data, 38 * 8)) + return -EIO; + + regs = task_pt_regs(child); + + for (i = 0; i < 32; i++) + __get_user(regs->regs[i], data + i); + __get_user(regs->lo, data + EF_LO - EF_R0); + __get_user(regs->hi, data + EF_HI - EF_R0); + __get_user(regs->cp0_epc, data + EF_CP0_EPC - EF_R0); + + /* badvaddr, status, and cause may not be written. */ + + return 0; +} + +long arch_ptrace(struct task_struct *child, long request, long addr, long data) +{ + int ret; + + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: + ret = generic_ptrace_peekdata(child, addr, data); + break; + + /* Read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + struct pt_regs *regs; + unsigned long tmp = 0; + + regs = task_pt_regs(child); + ret = 0; /* Default return value. */ + + switch (addr) { + case 0 ... 31: + tmp = regs->regs[addr]; + break; + case PC: + tmp = regs->cp0_epc; + break; + case CAUSE: + tmp = regs->cp0_cause; + break; + case BADVADDR: + tmp = regs->cp0_badvaddr; + break; + case MMHI: + tmp = regs->hi; + break; + case MMLO: + tmp = regs->lo; + break; + default: + tmp = 0; + ret = -EIO; + goto out; + } + ret = put_user(tmp, (unsigned long __user *) data); + break; + } + + /* when I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = generic_ptrace_pokedata(child, addr, data); + break; + + case PTRACE_POKEUSR: { + struct pt_regs *regs; + ret = 0; + regs = task_pt_regs(child); + + switch (addr) { + case 0 ... 31: + regs->regs[addr] = data; + break; + case PC: + regs->cp0_epc = data; + break; + case MMHI: + regs->hi = data; + break; + case MMLO: + regs->lo = data; + break; + default: + /* The rest are not allowed. */ + ret = -EIO; + break; + } + break; + } + + case PTRACE_GETREGS: + ret = ptrace_getregs(child, (__s64 __user *) data); + break; + + case PTRACE_SETREGS: + ret = ptrace_setregs(child, (__s64 __user *) data); + break; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + 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; + 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) /* already dead */ + break; + child->exit_code = SIGKILL; + wake_up_process(child); + break; + + case PTRACE_GET_THREAD_AREA: + ret = put_user(task_thread_info(child)->tp_value, + (unsigned long __user *) data); + break; + + default: + ret = ptrace_request(child, request, addr, data); + break; + } + out: + return ret; +} + +static inline int audit_arch(void) +{ + int arch = EM_MIPS; +#ifdef CONFIG_CPU_LITTLE_ENDIAN + arch |= __AUDIT_ARCH_LE; +#endif + return arch; +} + +/* + * Notification of system call entry/exit + * - triggered by current->work.syscall_trace + */ +asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit) +{ + /* do the secure computing check first */ + if (!entryexit) + secure_computing(regs->regs[0]); + + if (unlikely(current->audit_context) && entryexit) + audit_syscall_exit(AUDITSC_RESULT(regs->regs[2]), + regs->regs[2]); + + if (!(current->ptrace & PT_PTRACED)) + goto out; + + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + goto out; + + /* 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) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } + +out: + if (unlikely(current->audit_context) && !entryexit) + audit_syscall_entry(audit_arch(), regs->regs[0], + regs->regs[4], regs->regs[5], + regs->regs[6], regs->regs[7]); +} |