1/* 2 * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds 3 * these modifications are Copyright 2004-2010 Analog Devices Inc. 4 * 5 * Licensed under the GPL-2 6 */ 7 8#include <linux/kernel.h> 9#include <linux/sched.h> 10#include <linux/mm.h> 11#include <linux/smp.h> 12#include <linux/elf.h> 13#include <linux/errno.h> 14#include <linux/ptrace.h> 15#include <linux/user.h> 16#include <linux/regset.h> 17#include <linux/signal.h> 18#include <linux/tracehook.h> 19#include <linux/uaccess.h> 20 21#include <asm/page.h> 22#include <asm/pgtable.h> 23#include <asm/system.h> 24#include <asm/processor.h> 25#include <asm/asm-offsets.h> 26#include <asm/dma.h> 27#include <asm/fixed_code.h> 28#include <asm/cacheflush.h> 29#include <asm/mem_map.h> 30 31/* 32 * does not yet catch signals sent when the child dies. 33 * in exit.c or in signal.c. 34 */ 35 36/* 37 * Get contents of register REGNO in task TASK. 38 */ 39static inline long 40get_reg(struct task_struct *task, long regno, unsigned long __user *datap) 41{ 42 long tmp; 43 struct pt_regs *regs = task_pt_regs(task); 44 45 if (regno & 3 || regno > PT_LAST_PSEUDO || regno < 0) 46 return -EIO; 47 48 switch (regno) { 49 case PT_TEXT_ADDR: 50 tmp = task->mm->start_code; 51 break; 52 case PT_TEXT_END_ADDR: 53 tmp = task->mm->end_code; 54 break; 55 case PT_DATA_ADDR: 56 tmp = task->mm->start_data; 57 break; 58 case PT_USP: 59 tmp = task->thread.usp; 60 break; 61 default: 62 if (regno < sizeof(*regs)) { 63 void *reg_ptr = regs; 64 tmp = *(long *)(reg_ptr + regno); 65 } else 66 return -EIO; 67 } 68 69 return put_user(tmp, datap); 70} 71 72/* 73 * Write contents of register REGNO in task TASK. 74 */ 75static inline int 76put_reg(struct task_struct *task, long regno, unsigned long data) 77{ 78 struct pt_regs *regs = task_pt_regs(task); 79 80 if (regno & 3 || regno > PT_LAST_PSEUDO || regno < 0) 81 return -EIO; 82 83 switch (regno) { 84 case PT_PC: 85 /*********************************************************************/ 86 /* At this point the kernel is most likely in exception. */ 87 /* The RETX register will be used to populate the pc of the process. */ 88 /*********************************************************************/ 89 regs->retx = data; 90 regs->pc = data; 91 break; 92 case PT_RETX: 93 break; /* regs->retx = data; break; */ 94 case PT_USP: 95 regs->usp = data; 96 task->thread.usp = data; 97 break; 98 case PT_SYSCFG: /* don't let userspace screw with this */ 99 if ((data & ~1) != 0x6) 100 pr_warning("ptrace: ignore syscfg write of %#lx\n", data); 101 break; /* regs->syscfg = data; break; */ 102 default: 103 if (regno < sizeof(*regs)) { 104 void *reg_offset = regs; 105 *(long *)(reg_offset + regno) = data; 106 } 107 /* Ignore writes to pseudo registers */ 108 } 109 110 return 0; 111} 112 113/* 114 * check that an address falls within the bounds of the target process's memory mappings 115 */ 116static inline int is_user_addr_valid(struct task_struct *child, 117 unsigned long start, unsigned long len) 118{ 119 struct vm_area_struct *vma; 120 struct sram_list_struct *sraml; 121 122 /* overflow */ 123 if (start + len < start) 124 return -EIO; 125 126 vma = find_vma(child->mm, start); 127 if (vma && start >= vma->vm_start && start + len <= vma->vm_end) 128 return 0; 129 130 for (sraml = child->mm->context.sram_list; sraml; sraml = sraml->next) 131 if (start >= (unsigned long)sraml->addr 132 && start + len < (unsigned long)sraml->addr + sraml->length) 133 return 0; 134 135 if (start >= FIXED_CODE_START && start + len < FIXED_CODE_END) 136 return 0; 137 138 return -EIO; 139} 140 141/* 142 * retrieve the contents of Blackfin userspace general registers 143 */ 144static int genregs_get(struct task_struct *target, 145 const struct user_regset *regset, 146 unsigned int pos, unsigned int count, 147 void *kbuf, void __user *ubuf) 148{ 149 struct pt_regs *regs = task_pt_regs(target); 150 int ret; 151 152 /* This sucks ... */ 153 regs->usp = target->thread.usp; 154 155 ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, 156 regs, 0, sizeof(*regs)); 157 if (ret < 0) 158 return ret; 159 160 return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, 161 sizeof(*regs), -1); 162} 163 164/* 165 * update the contents of the Blackfin userspace general registers 166 */ 167static int genregs_set(struct task_struct *target, 168 const struct user_regset *regset, 169 unsigned int pos, unsigned int count, 170 const void *kbuf, const void __user *ubuf) 171{ 172 struct pt_regs *regs = task_pt_regs(target); 173 int ret; 174 175 /* Don't let people set SYSCFG (it's at the end of pt_regs) */ 176 ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, 177 regs, 0, PT_SYSCFG); 178 if (ret < 0) 179 return ret; 180 181 /* This sucks ... */ 182 target->thread.usp = regs->usp; 183 /* regs->retx = regs->pc; */ 184 185 return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 186 PT_SYSCFG, -1); 187} 188 189/* 190 * Define the register sets available on the Blackfin under Linux 191 */ 192enum bfin_regset { 193 REGSET_GENERAL, 194}; 195 196static const struct user_regset bfin_regsets[] = { 197 [REGSET_GENERAL] = { 198 .core_note_type = NT_PRSTATUS, 199 .n = sizeof(struct pt_regs) / sizeof(long), 200 .size = sizeof(long), 201 .align = sizeof(long), 202 .get = genregs_get, 203 .set = genregs_set, 204 }, 205}; 206 207static const struct user_regset_view user_bfin_native_view = { 208 .name = "Blackfin", 209 .e_machine = EM_BLACKFIN, 210 .regsets = bfin_regsets, 211 .n = ARRAY_SIZE(bfin_regsets), 212}; 213 214const struct user_regset_view *task_user_regset_view(struct task_struct *task) 215{ 216 return &user_bfin_native_view; 217} 218 219void user_enable_single_step(struct task_struct *child) 220{ 221 struct pt_regs *regs = task_pt_regs(child); 222 regs->syscfg |= SYSCFG_SSSTEP; 223 224 set_tsk_thread_flag(child, TIF_SINGLESTEP); 225} 226 227void user_disable_single_step(struct task_struct *child) 228{ 229 struct pt_regs *regs = task_pt_regs(child); 230 regs->syscfg &= ~SYSCFG_SSSTEP; 231 232 clear_tsk_thread_flag(child, TIF_SINGLESTEP); 233} 234 235long arch_ptrace(struct task_struct *child, long request, long addr, long data) 236{ 237 int ret; 238 unsigned long __user *datap = (unsigned long __user *)data; 239 void *paddr = (void *)addr; 240 241 switch (request) { 242 /* when I and D space are separate, these will need to be fixed. */ 243 case PTRACE_PEEKDATA: 244 pr_debug("ptrace: PEEKDATA\n"); 245 /* fall through */ 246 case PTRACE_PEEKTEXT: /* read word at location addr. */ 247 { 248 unsigned long tmp = 0; 249 int copied = 0, to_copy = sizeof(tmp); 250 251 ret = -EIO; 252 pr_debug("ptrace: PEEKTEXT at addr 0x%08lx + %i\n", addr, to_copy); 253 if (is_user_addr_valid(child, addr, to_copy) < 0) 254 break; 255 pr_debug("ptrace: user address is valid\n"); 256 257 switch (bfin_mem_access_type(addr, to_copy)) { 258 case BFIN_MEM_ACCESS_CORE: 259 case BFIN_MEM_ACCESS_CORE_ONLY: 260 copied = access_process_vm(child, addr, &tmp, 261 to_copy, 0); 262 if (copied) 263 break; 264 265 /* hrm, why didn't that work ... maybe no mapping */ 266 if (addr >= FIXED_CODE_START && 267 addr + to_copy <= FIXED_CODE_END) { 268 copy_from_user_page(0, 0, 0, &tmp, paddr, to_copy); 269 copied = to_copy; 270 } else if (addr >= BOOT_ROM_START) { 271 memcpy(&tmp, paddr, to_copy); 272 copied = to_copy; 273 } 274 275 break; 276 case BFIN_MEM_ACCESS_DMA: 277 if (safe_dma_memcpy(&tmp, paddr, to_copy)) 278 copied = to_copy; 279 break; 280 case BFIN_MEM_ACCESS_ITEST: 281 if (isram_memcpy(&tmp, paddr, to_copy)) 282 copied = to_copy; 283 break; 284 default: 285 copied = 0; 286 break; 287 } 288 289 pr_debug("ptrace: copied size %d [0x%08lx]\n", copied, tmp); 290 if (copied == to_copy) 291 ret = put_user(tmp, datap); 292 break; 293 } 294 295 /* when I and D space are separate, this will have to be fixed. */ 296 case PTRACE_POKEDATA: 297 pr_debug("ptrace: PTRACE_PEEKDATA\n"); 298 /* fall through */ 299 case PTRACE_POKETEXT: /* write the word at location addr. */ 300 { 301 int copied = 0, to_copy = sizeof(data); 302 303 ret = -EIO; 304 pr_debug("ptrace: POKETEXT at addr 0x%08lx + %i bytes %lx\n", 305 addr, to_copy, data); 306 if (is_user_addr_valid(child, addr, to_copy) < 0) 307 break; 308 pr_debug("ptrace: user address is valid\n"); 309 310 switch (bfin_mem_access_type(addr, to_copy)) { 311 case BFIN_MEM_ACCESS_CORE: 312 case BFIN_MEM_ACCESS_CORE_ONLY: 313 copied = access_process_vm(child, addr, &data, 314 to_copy, 1); 315 break; 316 case BFIN_MEM_ACCESS_DMA: 317 if (safe_dma_memcpy(paddr, &data, to_copy)) 318 copied = to_copy; 319 break; 320 case BFIN_MEM_ACCESS_ITEST: 321 if (isram_memcpy(paddr, &data, to_copy)) 322 copied = to_copy; 323 break; 324 default: 325 copied = 0; 326 break; 327 } 328 329 pr_debug("ptrace: copied size %d\n", copied); 330 if (copied == to_copy) 331 ret = 0; 332 break; 333 } 334 335 case PTRACE_PEEKUSR: 336 switch (addr) { 337#ifdef CONFIG_BINFMT_ELF_FDPIC /* backwards compat */ 338 case PT_FDPIC_EXEC: 339 request = PTRACE_GETFDPIC; 340 addr = PTRACE_GETFDPIC_EXEC; 341 goto case_default; 342 case PT_FDPIC_INTERP: 343 request = PTRACE_GETFDPIC; 344 addr = PTRACE_GETFDPIC_INTERP; 345 goto case_default; 346#endif 347 default: 348 ret = get_reg(child, addr, datap); 349 } 350 pr_debug("ptrace: PEEKUSR reg %li with %#lx = %i\n", addr, data, ret); 351 break; 352 353 case PTRACE_POKEUSR: 354 ret = put_reg(child, addr, data); 355 pr_debug("ptrace: POKEUSR reg %li with %li = %i\n", addr, data, ret); 356 break; 357 358 case PTRACE_GETREGS: 359 pr_debug("ptrace: PTRACE_GETREGS\n"); 360 return copy_regset_to_user(child, &user_bfin_native_view, 361 REGSET_GENERAL, 362 0, sizeof(struct pt_regs), 363 (void __user *)data); 364 365 case PTRACE_SETREGS: 366 pr_debug("ptrace: PTRACE_SETREGS\n"); 367 return copy_regset_from_user(child, &user_bfin_native_view, 368 REGSET_GENERAL, 369 0, sizeof(struct pt_regs), 370 (const void __user *)data); 371 372 case_default: 373 default: 374 ret = ptrace_request(child, request, addr, data); 375 break; 376 } 377 378 return ret; 379} 380 381asmlinkage int syscall_trace_enter(struct pt_regs *regs) 382{ 383 int ret = 0; 384 385 if (test_thread_flag(TIF_SYSCALL_TRACE)) 386 ret = tracehook_report_syscall_entry(regs); 387 388 return ret; 389} 390 391asmlinkage void syscall_trace_leave(struct pt_regs *regs) 392{ 393 int step; 394 395 step = test_thread_flag(TIF_SINGLESTEP); 396 if (step || test_thread_flag(TIF_SYSCALL_TRACE)) 397 tracehook_report_syscall_exit(regs, step); 398} 399