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/tracehook.h> 18 #include <linux/uaccess.h> 19 #include <linux/user.h> 20 21 static int genregs_get(struct task_struct *target, 22 const struct user_regset *regset, 23 unsigned int pos, unsigned int count, 24 void *kbuf, void __user *ubuf) 25 { 26 const struct pt_regs *regs = task_pt_regs(target); 27 const struct switch_stack *sw = (struct switch_stack *)regs - 1; 28 int ret = 0; 29 30 #define REG_O_ZERO_RANGE(START, END) \ 31 if (!ret) \ 32 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, \ 33 START * 4, (END * 4) + 4); 34 35 #define REG_O_ONE(PTR, LOC) \ 36 if (!ret) \ 37 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \ 38 LOC * 4, (LOC * 4) + 4); 39 40 #define REG_O_RANGE(PTR, START, END) \ 41 if (!ret) \ 42 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, PTR, \ 43 START * 4, (END * 4) + 4); 44 45 REG_O_ZERO_RANGE(PTR_R0, PTR_R0); 46 REG_O_RANGE(®s->r1, PTR_R1, PTR_R7); 47 REG_O_RANGE(®s->r8, PTR_R8, PTR_R15); 48 REG_O_RANGE(sw, PTR_R16, PTR_R23); 49 REG_O_ZERO_RANGE(PTR_R24, PTR_R25); /* et and bt */ 50 REG_O_ONE(®s->gp, PTR_GP); 51 REG_O_ONE(®s->sp, PTR_SP); 52 REG_O_ONE(®s->fp, PTR_FP); 53 REG_O_ONE(®s->ea, PTR_EA); 54 REG_O_ZERO_RANGE(PTR_BA, PTR_BA); 55 REG_O_ONE(®s->ra, PTR_RA); 56 REG_O_ONE(®s->ea, PTR_PC); /* use ea for PC */ 57 if (!ret) 58 ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 59 PTR_STATUS * 4, -1); 60 61 return ret; 62 } 63 64 /* 65 * Set the thread state from a regset passed in via ptrace 66 */ 67 static int genregs_set(struct task_struct *target, 68 const struct user_regset *regset, 69 unsigned int pos, unsigned int count, 70 const void *kbuf, const void __user *ubuf) 71 { 72 struct pt_regs *regs = task_pt_regs(target); 73 const struct switch_stack *sw = (struct switch_stack *)regs - 1; 74 int ret = 0; 75 76 #define REG_IGNORE_RANGE(START, END) \ 77 if (!ret) \ 78 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, \ 79 START * 4, (END * 4) + 4); 80 81 #define REG_IN_ONE(PTR, LOC) \ 82 if (!ret) \ 83 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ 84 (void *)(PTR), LOC * 4, (LOC * 4) + 4); 85 86 #define REG_IN_RANGE(PTR, START, END) \ 87 if (!ret) \ 88 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, \ 89 (void *)(PTR), START * 4, (END * 4) + 4); 90 91 REG_IGNORE_RANGE(PTR_R0, PTR_R0); 92 REG_IN_RANGE(®s->r1, PTR_R1, PTR_R7); 93 REG_IN_RANGE(®s->r8, PTR_R8, PTR_R15); 94 REG_IN_RANGE(sw, PTR_R16, PTR_R23); 95 REG_IGNORE_RANGE(PTR_R24, PTR_R25); /* et and bt */ 96 REG_IN_ONE(®s->gp, PTR_GP); 97 REG_IN_ONE(®s->sp, PTR_SP); 98 REG_IN_ONE(®s->fp, PTR_FP); 99 REG_IN_ONE(®s->ea, PTR_EA); 100 REG_IGNORE_RANGE(PTR_BA, PTR_BA); 101 REG_IN_ONE(®s->ra, PTR_RA); 102 REG_IN_ONE(®s->ea, PTR_PC); /* use ea for PC */ 103 if (!ret) 104 ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 105 PTR_STATUS * 4, -1); 106 107 return ret; 108 } 109 110 /* 111 * Define the register sets available on Nios2 under Linux 112 */ 113 enum nios2_regset { 114 REGSET_GENERAL, 115 }; 116 117 static const struct user_regset nios2_regsets[] = { 118 [REGSET_GENERAL] = { 119 .core_note_type = NT_PRSTATUS, 120 .n = NUM_PTRACE_REG, 121 .size = sizeof(unsigned long), 122 .align = sizeof(unsigned long), 123 .get = genregs_get, 124 .set = genregs_set, 125 } 126 }; 127 128 static const struct user_regset_view nios2_user_view = { 129 .name = "nios2", 130 .e_machine = ELF_ARCH, 131 .ei_osabi = ELF_OSABI, 132 .regsets = nios2_regsets, 133 .n = ARRAY_SIZE(nios2_regsets) 134 }; 135 136 const struct user_regset_view *task_user_regset_view(struct task_struct *task) 137 { 138 return &nios2_user_view; 139 } 140 141 void ptrace_disable(struct task_struct *child) 142 { 143 144 } 145 146 long arch_ptrace(struct task_struct *child, long request, unsigned long addr, 147 unsigned long data) 148 { 149 return ptrace_request(child, request, addr, data); 150 } 151 152 asmlinkage int do_syscall_trace_enter(void) 153 { 154 int ret = 0; 155 156 if (test_thread_flag(TIF_SYSCALL_TRACE)) 157 ret = tracehook_report_syscall_entry(task_pt_regs(current)); 158 159 return ret; 160 } 161 162 asmlinkage void do_syscall_trace_exit(void) 163 { 164 if (test_thread_flag(TIF_SYSCALL_TRACE)) 165 tracehook_report_syscall_exit(task_pt_regs(current), 0); 166 } 167