1 /* 2 * Copyright (C) 2014 Altera Corporation 3 * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch> 4 * 5 * This file is subject to the terms and conditions of the GNU General 6 * Public License. See the file COPYING in the main directory of this 7 * archive for more details. 8 */ 9 10 #include <linux/elf.h> 11 #include <linux/errno.h> 12 #include <linux/kernel.h> 13 #include <linux/mm.h> 14 #include <linux/ptrace.h> 15 #include <linux/regset.h> 16 #include <linux/sched.h> 17 #include <linux/sched/task_stack.h> 18 #include <linux/tracehook.h> 19 #include <linux/uaccess.h> 20 #include <linux/user.h> 21 22 static int genregs_get(struct task_struct *target, 23 const struct user_regset *regset, 24 struct membuf to) 25 { 26 const struct pt_regs *regs = task_pt_regs(target); 27 const struct switch_stack *sw = (struct switch_stack *)regs - 1; 28 29 membuf_zero(&to, 4); // R0 30 membuf_write(&to, ®s->r1, 7 * 4); // R1..R7 31 membuf_write(&to, ®s->r8, 8 * 4); // R8..R15 32 membuf_write(&to, sw, 8 * 4); // R16..R23 33 membuf_zero(&to, 2 * 4); /* et and bt */ 34 membuf_store(&to, regs->gp); 35 membuf_store(&to, regs->sp); 36 membuf_store(&to, regs->fp); 37 membuf_store(&to, regs->ea); 38 membuf_zero(&to, 4); // PTR_BA 39 membuf_store(&to, regs->ra); 40 membuf_store(&to, regs->ea); /* use ea for PC */ 41 return membuf_zero(&to, (NUM_PTRACE_REG - PTR_PC) * 4); 42 } 43 44 /* 45 * Set the thread state from a regset passed in via ptrace 46 */ 47 static int genregs_set(struct task_struct *target, 48 const struct user_regset *regset, 49 unsigned int pos, unsigned int count, 50 const void *kbuf, const void __user *ubuf) 51 { 52 struct pt_regs *regs = task_pt_regs(target); 53 const struct switch_stack *sw = (struct switch_stack *)regs - 1; 54 int ret = 0; 55 56 #define REG_IGNORE_RANGE(START, END) \ 57 if (!ret) \ 58 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, \ 59 START * 4, (END * 4) + 4); 60 61 #define REG_IN_ONE(PTR, LOC) \ 62 if (!ret) \ 63 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ 64 (void *)(PTR), LOC * 4, (LOC * 4) + 4); 65 66 #define REG_IN_RANGE(PTR, START, END) \ 67 if (!ret) \ 68 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ 69 (void *)(PTR), START * 4, (END * 4) + 4); 70 71 REG_IGNORE_RANGE(PTR_R0, PTR_R0); 72 REG_IN_RANGE(®s->r1, PTR_R1, PTR_R7); 73 REG_IN_RANGE(®s->r8, PTR_R8, PTR_R15); 74 REG_IN_RANGE(sw, PTR_R16, PTR_R23); 75 REG_IGNORE_RANGE(PTR_R24, PTR_R25); /* et and bt */ 76 REG_IN_ONE(®s->gp, PTR_GP); 77 REG_IN_ONE(®s->sp, PTR_SP); 78 REG_IN_ONE(®s->fp, PTR_FP); 79 REG_IN_ONE(®s->ea, PTR_EA); 80 REG_IGNORE_RANGE(PTR_BA, PTR_BA); 81 REG_IN_ONE(®s->ra, PTR_RA); 82 REG_IN_ONE(®s->ea, PTR_PC); /* use ea for PC */ 83 if (!ret) 84 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 85 PTR_STATUS * 4, -1); 86 87 return ret; 88 } 89 90 /* 91 * Define the register sets available on Nios2 under Linux 92 */ 93 enum nios2_regset { 94 REGSET_GENERAL, 95 }; 96 97 static const struct user_regset nios2_regsets[] = { 98 [REGSET_GENERAL] = { 99 .core_note_type = NT_PRSTATUS, 100 .n = NUM_PTRACE_REG, 101 .size = sizeof(unsigned long), 102 .align = sizeof(unsigned long), 103 .regset_get = genregs_get, 104 .set = genregs_set, 105 } 106 }; 107 108 static const struct user_regset_view nios2_user_view = { 109 .name = "nios2", 110 .e_machine = ELF_ARCH, 111 .ei_osabi = ELF_OSABI, 112 .regsets = nios2_regsets, 113 .n = ARRAY_SIZE(nios2_regsets) 114 }; 115 116 const struct user_regset_view *task_user_regset_view(struct task_struct *task) 117 { 118 return &nios2_user_view; 119 } 120 121 void ptrace_disable(struct task_struct *child) 122 { 123 124 } 125 126 long arch_ptrace(struct task_struct *child, long request, unsigned long addr, 127 unsigned long data) 128 { 129 return ptrace_request(child, request, addr, data); 130 } 131 132 asmlinkage int do_syscall_trace_enter(void) 133 { 134 int ret = 0; 135 136 if (test_thread_flag(TIF_SYSCALL_TRACE)) 137 ret = tracehook_report_syscall_entry(task_pt_regs(current)); 138 139 return ret; 140 } 141 142 asmlinkage void do_syscall_trace_exit(void) 143 { 144 if (test_thread_flag(TIF_SYSCALL_TRACE)) 145 tracehook_report_syscall_exit(task_pt_regs(current), 0); 146 } 147