1 // SPDX-License-Identifier: GPL-2.0 !! 1 /* >> 2 * linux/arch/m68k/kernel/ptrace.c >> 3 * >> 4 * Copyright (C) 1994 by Hamish Macdonald >> 5 * Taken from linux/kernel/ptrace.c and modified for M680x0. >> 6 * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds >> 7 * >> 8 * This file is subject to the terms and conditions of the GNU General >> 9 * Public License. See the file COPYING in the main directory of >> 10 * this archive for more details. >> 11 */ >> 12 >> 13 #include <linux/kernel.h> >> 14 #include <linux/sched.h> >> 15 #include <linux/sched/task_stack.h> >> 16 #include <linux/mm.h> >> 17 #include <linux/smp.h> >> 18 #include <linux/errno.h> 2 #include <linux/ptrace.h> 19 #include <linux/ptrace.h> >> 20 #include <linux/user.h> >> 21 #include <linux/signal.h> >> 22 #include <linux/regset.h> >> 23 #include <linux/elf.h> >> 24 #include <linux/seccomp.h> >> 25 #include <linux/uaccess.h> >> 26 #include <asm/page.h> >> 27 #include <asm/processor.h> 3 28 4 /** !! 29 #include "ptrace.h" 5 * regs_query_register_offset() - query regist !! 30 6 * @name: the name of a register !! 31 /* 7 * !! 32 * does not yet catch signals sent when the child dies. 8 * regs_query_register_offset() returns the of !! 33 * in exit.c or in signal.c. 9 * pt_regs from its name. If the name is inval << 10 */ 34 */ 11 int regs_query_register_offset(const char *nam !! 35 >> 36 /* determines which bits in the SR the user has access to. */ >> 37 /* 1 = access 0 = no access */ >> 38 #define SR_MASK 0x001f >> 39 >> 40 /* sets the trace bits. */ >> 41 #define TRACE_BITS 0xC000 >> 42 #define T1_BIT 0x8000 >> 43 #define T0_BIT 0x4000 >> 44 >> 45 /* Find the stack offset for a register, relative to thread.esp0. */ >> 46 #define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) >> 47 #define SW_REG(reg) ((long)&((struct switch_stack *)0)->reg \ >> 48 - sizeof(struct switch_stack)) >> 49 /* Mapping from PT_xxx to the stack offset at which the register is >> 50 saved. Notice that usp has no stack-slot and needs to be treated >> 51 specially (see get_reg/put_reg below). */ >> 52 static const int regoff[] = { >> 53 [0] = PT_REG(d1), >> 54 [1] = PT_REG(d2), >> 55 [2] = PT_REG(d3), >> 56 [3] = PT_REG(d4), >> 57 [4] = PT_REG(d5), >> 58 [5] = SW_REG(d6), >> 59 [6] = SW_REG(d7), >> 60 [7] = PT_REG(a0), >> 61 [8] = PT_REG(a1), >> 62 [9] = PT_REG(a2), >> 63 [10] = SW_REG(a3), >> 64 [11] = SW_REG(a4), >> 65 [12] = SW_REG(a5), >> 66 [13] = SW_REG(a6), >> 67 [14] = PT_REG(d0), >> 68 [15] = -1, >> 69 [16] = PT_REG(orig_d0), >> 70 [17] = PT_REG(sr), >> 71 [18] = PT_REG(pc), >> 72 }; >> 73 >> 74 /* >> 75 * Get contents of register REGNO in task TASK. >> 76 */ >> 77 static inline long get_reg(struct task_struct *task, int regno) 12 { 78 { 13 const struct pt_regs_offset *roff; !! 79 unsigned long *addr; 14 for (roff = regoffset_table; roff->nam !! 80 15 if (!strcmp(roff->name, name)) !! 81 if (regno == PT_USP) 16 return roff->offset; !! 82 addr = &task->thread.usp; 17 return -EINVAL; !! 83 else if (regno < ARRAY_SIZE(regoff)) >> 84 addr = (unsigned long *)(task->thread.esp0 + regoff[regno]); >> 85 else >> 86 return 0; >> 87 /* Need to take stkadj into account. */ >> 88 if (regno == PT_SR || regno == PT_PC) { >> 89 long stkadj = *(long *)(task->thread.esp0 + PT_REG(stkadj)); >> 90 addr = (unsigned long *) ((unsigned long)addr + stkadj); >> 91 /* The sr is actually a 16 bit register. */ >> 92 if (regno == PT_SR) >> 93 return *(unsigned short *)addr; >> 94 } >> 95 return *addr; 18 } 96 } 19 97 20 /** !! 98 /* 21 * regs_query_register_name() - query register !! 99 * Write contents of register REGNO in task TASK. 22 * @offset: the offset of a register in st << 23 * << 24 * regs_query_register_name() returns the name << 25 * offset in struct pt_regs. If the @offset is << 26 */ 100 */ 27 const char *regs_query_register_name(unsigned !! 101 static inline int put_reg(struct task_struct *task, int regno, >> 102 unsigned long data) >> 103 { >> 104 unsigned long *addr; >> 105 >> 106 if (regno == PT_USP) >> 107 addr = &task->thread.usp; >> 108 else if (regno < ARRAY_SIZE(regoff)) >> 109 addr = (unsigned long *)(task->thread.esp0 + regoff[regno]); >> 110 else >> 111 return -1; >> 112 /* Need to take stkadj into account. */ >> 113 if (regno == PT_SR || regno == PT_PC) { >> 114 long stkadj = *(long *)(task->thread.esp0 + PT_REG(stkadj)); >> 115 addr = (unsigned long *) ((unsigned long)addr + stkadj); >> 116 /* The sr is actually a 16 bit register. */ >> 117 if (regno == PT_SR) { >> 118 *(unsigned short *)addr = data; >> 119 return 0; >> 120 } >> 121 } >> 122 *addr = data; >> 123 return 0; >> 124 } >> 125 >> 126 /* >> 127 * Make sure the single step bit is not set. >> 128 */ >> 129 static inline void singlestep_disable(struct task_struct *child) >> 130 { >> 131 unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; >> 132 put_reg(child, PT_SR, tmp); >> 133 clear_tsk_thread_flag(child, TIF_DELAYED_TRACE); >> 134 } >> 135 >> 136 /* >> 137 * Called by kernel/ptrace.c when detaching.. >> 138 */ >> 139 void ptrace_disable(struct task_struct *child) >> 140 { >> 141 singlestep_disable(child); >> 142 } >> 143 >> 144 void user_enable_single_step(struct task_struct *child) >> 145 { >> 146 unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; >> 147 put_reg(child, PT_SR, tmp | T1_BIT); >> 148 set_tsk_thread_flag(child, TIF_DELAYED_TRACE); >> 149 } >> 150 >> 151 #ifdef CONFIG_MMU >> 152 void user_enable_block_step(struct task_struct *child) >> 153 { >> 154 unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; >> 155 put_reg(child, PT_SR, tmp | T0_BIT); >> 156 } >> 157 #endif >> 158 >> 159 void user_disable_single_step(struct task_struct *child) >> 160 { >> 161 singlestep_disable(child); >> 162 } >> 163 >> 164 long arch_ptrace(struct task_struct *child, long request, >> 165 unsigned long addr, unsigned long data) >> 166 { >> 167 unsigned long tmp; >> 168 int i, ret = 0; >> 169 int regno = addr >> 2; /* temporary hack. */ >> 170 unsigned long __user *datap = (unsigned long __user *) data; >> 171 >> 172 switch (request) { >> 173 /* read the word at location addr in the USER area. */ >> 174 case PTRACE_PEEKUSR: >> 175 if (addr & 3) >> 176 goto out_eio; >> 177 >> 178 if (regno >= 0 && regno < 19) { >> 179 tmp = get_reg(child, regno); >> 180 } else if (regno >= 21 && regno < 49) { >> 181 tmp = child->thread.fp[regno - 21]; >> 182 /* Convert internal fpu reg representation >> 183 * into long double format >> 184 */ >> 185 if (FPU_IS_EMU && (regno < 45) && !(regno % 3)) >> 186 tmp = ((tmp & 0xffff0000) << 15) | >> 187 ((tmp & 0x0000ffff) << 16); >> 188 #ifndef CONFIG_MMU >> 189 } else if (regno == 49) { >> 190 tmp = child->mm->start_code; >> 191 } else if (regno == 50) { >> 192 tmp = child->mm->start_data; >> 193 } else if (regno == 51) { >> 194 tmp = child->mm->end_code; >> 195 #endif >> 196 } else >> 197 goto out_eio; >> 198 ret = put_user(tmp, datap); >> 199 break; >> 200 >> 201 case PTRACE_POKEUSR: >> 202 /* write the word at location addr in the USER area */ >> 203 if (addr & 3) >> 204 goto out_eio; >> 205 >> 206 if (regno == PT_SR) { >> 207 data &= SR_MASK; >> 208 data |= get_reg(child, PT_SR) & ~SR_MASK; >> 209 } >> 210 if (regno >= 0 && regno < 19) { >> 211 if (put_reg(child, regno, data)) >> 212 goto out_eio; >> 213 } else if (regno >= 21 && regno < 48) { >> 214 /* Convert long double format >> 215 * into internal fpu reg representation >> 216 */ >> 217 if (FPU_IS_EMU && (regno < 45) && !(regno % 3)) { >> 218 data <<= 15; >> 219 data = (data & 0xffff0000) | >> 220 ((data & 0x0000ffff) >> 1); >> 221 } >> 222 child->thread.fp[regno - 21] = data; >> 223 } else >> 224 goto out_eio; >> 225 break; >> 226 >> 227 case PTRACE_GETREGS: /* Get all gp regs from the child. */ >> 228 for (i = 0; i < 19; i++) { >> 229 tmp = get_reg(child, i); >> 230 ret = put_user(tmp, datap); >> 231 if (ret) >> 232 break; >> 233 datap++; >> 234 } >> 235 break; >> 236 >> 237 case PTRACE_SETREGS: /* Set all gp regs in the child. */ >> 238 for (i = 0; i < 19; i++) { >> 239 ret = get_user(tmp, datap); >> 240 if (ret) >> 241 break; >> 242 if (i == PT_SR) { >> 243 tmp &= SR_MASK; >> 244 tmp |= get_reg(child, PT_SR) & ~SR_MASK; >> 245 } >> 246 put_reg(child, i, tmp); >> 247 datap++; >> 248 } >> 249 break; >> 250 >> 251 case PTRACE_GETFPREGS: /* Get the child FPU state. */ >> 252 if (copy_to_user(datap, &child->thread.fp, >> 253 sizeof(struct user_m68kfp_struct))) >> 254 ret = -EFAULT; >> 255 break; >> 256 >> 257 case PTRACE_SETFPREGS: /* Set the child FPU state. */ >> 258 if (copy_from_user(&child->thread.fp, datap, >> 259 sizeof(struct user_m68kfp_struct))) >> 260 ret = -EFAULT; >> 261 break; >> 262 >> 263 case PTRACE_GET_THREAD_AREA: >> 264 ret = put_user(task_thread_info(child)->tp_value, datap); >> 265 break; >> 266 >> 267 default: >> 268 ret = ptrace_request(child, request, addr, data); >> 269 break; >> 270 } >> 271 >> 272 return ret; >> 273 out_eio: >> 274 return -EIO; >> 275 } >> 276 >> 277 asmlinkage int syscall_trace_enter(void) >> 278 { >> 279 int ret = 0; >> 280 >> 281 if (test_thread_flag(TIF_SYSCALL_TRACE)) >> 282 ret = ptrace_report_syscall_entry(task_pt_regs(current)); >> 283 >> 284 if (secure_computing() == -1) >> 285 return -1; >> 286 >> 287 return ret; >> 288 } >> 289 >> 290 asmlinkage void syscall_trace_leave(void) >> 291 { >> 292 if (test_thread_flag(TIF_SYSCALL_TRACE)) >> 293 ptrace_report_syscall_exit(task_pt_regs(current), 0); >> 294 } >> 295 >> 296 #if defined(CONFIG_BINFMT_ELF_FDPIC) && defined(CONFIG_ELF_CORE) >> 297 /* >> 298 * Currently the only thing that needs to use regsets for m68k is the >> 299 * coredump support of the elf_fdpic loader. Implement the minimum >> 300 * definitions required for that. >> 301 */ >> 302 static int m68k_regset_get(struct task_struct *target, >> 303 const struct user_regset *regset, >> 304 struct membuf to) >> 305 { >> 306 struct pt_regs *ptregs = task_pt_regs(target); >> 307 u32 uregs[ELF_NGREG]; >> 308 >> 309 ELF_CORE_COPY_REGS(uregs, ptregs); >> 310 return membuf_write(&to, uregs, sizeof(uregs)); >> 311 } >> 312 >> 313 enum m68k_regset { >> 314 REGSET_GPR, >> 315 #ifdef CONFIG_FPU >> 316 REGSET_FPU, >> 317 #endif >> 318 }; >> 319 >> 320 static const struct user_regset m68k_user_regsets[] = { >> 321 [REGSET_GPR] = { >> 322 .core_note_type = NT_PRSTATUS, >> 323 .n = ELF_NGREG, >> 324 .size = sizeof(u32), >> 325 .align = sizeof(u16), >> 326 .regset_get = m68k_regset_get, >> 327 }, >> 328 #ifdef CONFIG_FPU >> 329 [REGSET_FPU] = { >> 330 .core_note_type = NT_PRFPREG, >> 331 .n = sizeof(struct user_m68kfp_struct) / sizeof(u32), >> 332 .size = sizeof(u32), >> 333 .align = sizeof(u32), >> 334 } >> 335 #endif /* CONFIG_FPU */ >> 336 }; >> 337 >> 338 static const struct user_regset_view user_m68k_view = { >> 339 .name = "m68k", >> 340 .e_machine = EM_68K, >> 341 .ei_osabi = ELF_OSABI, >> 342 .regsets = m68k_user_regsets, >> 343 .n = ARRAY_SIZE(m68k_user_regsets) >> 344 }; >> 345 >> 346 const struct user_regset_view *task_user_regset_view(struct task_struct *task) 28 { 347 { 29 const struct pt_regs_offset *roff; !! 348 return &user_m68k_view; 30 for (roff = regoffset_table; roff->nam << 31 if (roff->offset == offset) << 32 return roff->name; << 33 return NULL; << 34 } 349 } >> 350 #endif /* CONFIG_BINFMT_ELF_FDPIC && CONFIG_ELF_CORE */ 35 351
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.