/* * Port on Texas Instruments TMS320C6x architecture * * Copyright (C) 2004, 2006, 2009, 2010, 2011 Texas Instruments Incorporated * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com) * * Updated for 2.6.34: Mark Salter <msalter@redhat.com> * * 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. */ #include <linux/ptrace.h> #include <linux/tracehook.h> #include <linux/regset.h> #include <linux/elf.h> #include <asm/cacheflush.h> #define PT_REG_SIZE (sizeof(struct pt_regs)) /* * Called by kernel/ptrace.c when detaching. */ void ptrace_disable(struct task_struct *child) { /* nothing to do */ } /* * Get a register number from live pt_regs for the specified task. */ static inline long get_reg(struct task_struct *task, int regno) { long *addr = (long *)task_pt_regs(task); if (regno == PT_TSR || regno == PT_CSR) return 0; return addr[regno]; } /* * Write contents of register REGNO in task TASK. */ static inline int put_reg(struct task_struct *task, int regno, unsigned long data) { unsigned long *addr = (unsigned long *)task_pt_regs(task); if (regno != PT_TSR && regno != PT_CSR) addr[regno] = data; return 0; } /* regset get/set implementations */ static int gpr_get(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf) { struct pt_regs *regs = task_pt_regs(target); return user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs, 0, sizeof(*regs)); } static int gpr_set(struct task_struct *target, const struct user_regset *regset, unsigned int pos, unsigned int count, const void *kbuf, const void __user *ubuf) { int ret; struct pt_regs *regs = task_pt_regs(target); /* Don't copyin TSR or CSR */ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®s, 0, PT_TSR * sizeof(long)); if (ret) return ret; ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, PT_TSR * sizeof(long), (PT_TSR + 1) * sizeof(long)); if (ret) return ret; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®s, (PT_TSR + 1) * sizeof(long), PT_CSR * sizeof(long)); if (ret) return ret; ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, PT_CSR * sizeof(long), (PT_CSR + 1) * sizeof(long)); if (ret) return ret; ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, ®s, (PT_CSR + 1) * sizeof(long), -1); return ret; } enum c6x_regset { REGSET_GPR, }; static const struct user_regset c6x_regsets[] = { [REGSET_GPR] = { .core_note_type = NT_PRSTATUS, .n = ELF_NGREG, .size = sizeof(u32), .align = sizeof(u32), .get = gpr_get, .set = gpr_set }, }; static const struct user_regset_view user_c6x_native_view = { .name = "tic6x", .e_machine = EM_TI_C6000, .regsets = c6x_regsets, .n = ARRAY_SIZE(c6x_regsets), }; const struct user_regset_view *task_user_regset_view(struct task_struct *task) { return &user_c6x_native_view; } /* * Perform ptrace request */ long arch_ptrace(struct task_struct *child, long request, unsigned long addr, unsigned long data) { int ret = 0; switch (request) { /* * write the word at location addr. */ case PTRACE_POKETEXT: ret = generic_ptrace_pokedata(child, addr, data); if (ret == 0 && request == PTRACE_POKETEXT) flush_icache_range(addr, addr + 4); break; default: ret = ptrace_request(child, request, addr, data); break; } return ret; } /* * handle tracing of system call entry * - return the revised system call number or ULONG_MAX to cause ENOSYS */ asmlinkage unsigned long syscall_trace_entry(struct pt_regs *regs) { if (tracehook_report_syscall_entry(regs)) /* tracing decided this syscall should not happen, so * We'll return a bogus call number to get an ENOSYS * error, but leave the original number in * regs->orig_a4 */ return ULONG_MAX; return regs->b0; } /* * handle tracing of system call exit */ asmlinkage void syscall_trace_exit(struct pt_regs *regs) { tracehook_report_syscall_exit(regs, 0); }