1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 1992 Ross Biro 7 * Copyright (C) Linus Torvalds 8 * Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle 9 * Copyright (C) 1996 David S. Miller 10 * Copyright (C) 2000 Ulf Carlsson 11 * 12 * At this time Linux/MIPS64 only supports syscall tracing, even for 32-bit 13 * binaries. 14 */ 15#include <linux/config.h> 16#include <linux/kernel.h> 17#include <linux/sched.h> 18#include <linux/mm.h> 19#include <linux/errno.h> 20#include <linux/ptrace.h> 21#include <linux/smp.h> 22#include <linux/smp_lock.h> 23#include <linux/user.h> 24 25#include <asm/cpu.h> 26#include <asm/fpu.h> 27#include <asm/mipsregs.h> 28#include <asm/pgtable.h> 29#include <asm/page.h> 30#include <asm/system.h> 31#include <asm/uaccess.h> 32#include <asm/bootinfo.h> 33 34/* 35 * Called by kernel/ptrace.c when detaching.. 36 * 37 * Make sure single step bits etc are not set. 38 */ 39void ptrace_disable(struct task_struct *child) 40{ 41 /* Nothing to do.. */ 42} 43 44/* 45 * Tracing a 32-bit process with a 64-bit strace and vice versa will not 46 * work. I don't know how to fix this. 47 */ 48asmlinkage int sys32_ptrace(int request, int pid, int addr, int data) 49{ 50 struct task_struct *child; 51 int ret; 52 53 lock_kernel(); 54 ret = -EPERM; 55 if (request == PTRACE_TRACEME) { 56 /* are we already being traced? */ 57 if (current->ptrace & PT_PTRACED) 58 goto out; 59 /* set the ptrace bit in the process flags. */ 60 current->ptrace |= PT_PTRACED; 61 ret = 0; 62 goto out; 63 } 64 ret = -ESRCH; 65 read_lock(&tasklist_lock); 66 child = find_task_by_pid(pid); 67 if (child) 68 get_task_struct(child); 69 read_unlock(&tasklist_lock); 70 if (!child) 71 goto out; 72 73 ret = -EPERM; 74 if (pid == 1) /* you may not mess with init */ 75 goto out_tsk; 76 77 if (request == PTRACE_ATTACH) { 78 ret = ptrace_attach(child); 79 goto out_tsk; 80 } 81 82 ret = ptrace_check_attach(child, request == PTRACE_KILL); 83 if (ret < 0) 84 goto out_tsk; 85 86 switch (request) { 87 /* when I and D space are separate, these will need to be fixed. */ 88 case PTRACE_PEEKTEXT: /* read word at location addr. */ 89 case PTRACE_PEEKDATA: { 90 unsigned int tmp; 91 int copied; 92 93 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); 94 ret = -EIO; 95 if (copied != sizeof(tmp)) 96 break; 97 ret = put_user(tmp, (unsigned int *) (unsigned long) data); 98 break; 99 } 100 101 /* read the word at location addr in the USER area. */ 102 case PTRACE_PEEKUSR: { 103 struct pt_regs *regs; 104 unsigned int tmp; 105 106 regs = (struct pt_regs *) ((unsigned long) child + 107 KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs)); 108 ret = 0; 109 110 switch (addr) { 111 case 0 ... 31: 112 tmp = regs->regs[addr]; 113 break; 114 case FPR_BASE ... FPR_BASE + 31: 115 if (child->used_math) { 116 unsigned long *fregs = get_fpu_regs(child); 117 tmp = fregs[addr - FPR_BASE]; 118 } else { 119 tmp = -EIO; 120 } 121 break; 122 case PC: 123 tmp = regs->cp0_epc; 124 break; 125 case CAUSE: 126 tmp = regs->cp0_cause; 127 break; 128 case BADVADDR: 129 tmp = regs->cp0_badvaddr; 130 break; 131 case MMHI: 132 tmp = regs->hi; 133 break; 134 case MMLO: 135 tmp = regs->lo; 136 break; 137 case FPC_CSR: 138 if (mips_cpu.options & MIPS_CPU_FPU) 139 tmp = child->thread.fpu.hard.control; 140 else 141 tmp = child->thread.fpu.soft.sr; 142 break; 143 case FPC_EIR: { /* implementation / version register */ 144 unsigned int flags; 145 __save_flags(flags); 146 __enable_fpu(); 147 __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp)); 148 __restore_flags(flags); 149 break; 150 } 151 default: 152 tmp = 0; 153 ret = -EIO; 154 goto out_tsk; 155 } 156 ret = put_user(tmp, (unsigned *) (unsigned long) data); 157 break; 158 } 159 /* when I and D space are separate, this will have to be fixed. */ 160 case PTRACE_POKETEXT: /* write the word at location addr. */ 161 case PTRACE_POKEDATA: 162 ret = 0; 163 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) 164 break; 165 ret = -EIO; 166 break; 167 168 case PTRACE_POKEUSR: { 169 struct pt_regs *regs; 170 ret = 0; 171 regs = (struct pt_regs *) ((unsigned long) child + 172 KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs)); 173 174 switch (addr) { 175 case 0 ... 31: 176 regs->regs[addr] = data; 177 break; 178 case FPR_BASE ... FPR_BASE + 31: { 179 unsigned long *fregs = get_fpu_regs(child); 180 if (!child->used_math) { 181 /* FP not yet used */ 182 memset(&child->thread.fpu.hard, ~0, 183 sizeof(child->thread.fpu.hard)); 184 child->thread.fpu.hard.control = 0; 185 } 186 fregs[addr - FPR_BASE] = data; 187 break; 188 } 189 case PC: 190 regs->cp0_epc = data; 191 break; 192 case MMHI: 193 regs->hi = data; 194 break; 195 case MMLO: 196 regs->lo = data; 197 break; 198 case FPC_CSR: 199 if (mips_cpu.options & MIPS_CPU_FPU) 200 child->thread.fpu.hard.control = data; 201 else 202 child->thread.fpu.soft.sr = data; 203 break; 204 default: 205 /* The rest are not allowed. */ 206 ret = -EIO; 207 break; 208 } 209 goto out; 210 } 211 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ 212 case PTRACE_CONT: { /* restart after signal. */ 213 ret = -EIO; 214 if ((unsigned int) data > _NSIG) 215 break; 216 if (request == PTRACE_SYSCALL) 217 child->ptrace |= PT_TRACESYS; 218 else 219 child->ptrace &= ~PT_TRACESYS; 220 child->exit_code = data; 221 wake_up_process(child); 222 ret = 0; 223 break; 224 } 225 226/* 227 * make the child exit. Best I can do is send it a sigkill. 228 * perhaps it should be put in the status that it wants to 229 * exit. 230 */ 231 case PTRACE_KILL: { 232 if (child->state == TASK_ZOMBIE) /* already dead */ 233 break; 234 child->exit_code = SIGKILL; 235 wake_up_process(child); 236 break; 237 } 238 239 case PTRACE_DETACH: /* detach a process that was attached. */ 240 ret = ptrace_detach(child, data); 241 break; 242 243 case PTRACE_SETOPTIONS: { 244 if (data & PTRACE_O_TRACESYSGOOD) 245 child->ptrace |= PT_TRACESYSGOOD; 246 else 247 child->ptrace &= ~PT_TRACESYSGOOD; 248 ret = 0; 249 break; 250 } 251 252 default: 253 ret = -EIO; 254 break; 255 } 256 257out_tsk: 258 free_task_struct(child); 259out: 260 unlock_kernel(); 261 return ret; 262} 263 264asmlinkage int sys_ptrace(long request, long pid, long addr, long data) 265{ 266 struct task_struct *child; 267 int ret; 268 269 lock_kernel(); 270 ret = -EPERM; 271 if (request == PTRACE_TRACEME) { 272 /* are we already being traced? */ 273 if (current->ptrace & PT_PTRACED) 274 goto out; 275 /* set the ptrace bit in the process flags. */ 276 current->ptrace |= PT_PTRACED; 277 ret = 0; 278 goto out; 279 } 280 ret = -ESRCH; 281 read_lock(&tasklist_lock); 282 child = find_task_by_pid(pid); 283 if (child) 284 get_task_struct(child); 285 read_unlock(&tasklist_lock); 286 if (!child) 287 goto out; 288 289 ret = -EPERM; 290 if (pid == 1) /* you may not mess with init */ 291 goto out; 292 293 if (request == PTRACE_ATTACH) { 294 ret = ptrace_attach(child); 295 goto out_tsk; 296 } 297 298 ret = ptrace_check_attach(child, request == PTRACE_KILL); 299 if (ret < 0) 300 goto out_tsk; 301 302 switch (request) { 303 /* when I and D space are separate, these will need to be fixed. */ 304 case PTRACE_PEEKTEXT: /* read word at location addr. */ 305 case PTRACE_PEEKDATA: { 306 unsigned long tmp; 307 int copied; 308 309 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); 310 ret = -EIO; 311 if (copied != sizeof(tmp)) 312 break; 313 ret = put_user(tmp,(unsigned long *) data); 314 break; 315 } 316 317 /* read the word at location addr in the USER area. */ 318 case PTRACE_PEEKUSR: { 319 struct pt_regs *regs; 320 unsigned long tmp; 321 322 regs = (struct pt_regs *) ((unsigned long) child + 323 KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs)); 324 ret = 0; 325 326 switch (addr) { 327 case 0 ... 31: 328 tmp = regs->regs[addr]; 329 break; 330 case FPR_BASE ... FPR_BASE + 31: 331 if (child->used_math) { 332 unsigned long *fregs = get_fpu_regs(child); 333 tmp = fregs[addr - FPR_BASE]; 334 } else { 335 tmp = -EIO; 336 } 337 break; 338 case PC: 339 tmp = regs->cp0_epc; 340 break; 341 case CAUSE: 342 tmp = regs->cp0_cause; 343 break; 344 case BADVADDR: 345 tmp = regs->cp0_badvaddr; 346 break; 347 case MMHI: 348 tmp = regs->hi; 349 break; 350 case MMLO: 351 tmp = regs->lo; 352 break; 353 case FPC_CSR: 354 if (mips_cpu.options & MIPS_CPU_FPU) 355 tmp = child->thread.fpu.hard.control; 356 else 357 tmp = child->thread.fpu.soft.sr; 358 break; 359 case FPC_EIR: { /* implementation / version register */ 360 unsigned int flags; 361 __save_flags(flags); 362 __enable_fpu(); 363 __asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp)); 364 __restore_flags(flags); 365 break; 366 } 367 default: 368 tmp = 0; 369 ret = -EIO; 370 goto out_tsk; 371 } 372 ret = put_user(tmp, (unsigned long *) data); 373 break; 374 } 375 /* when I and D space are separate, this will have to be fixed. */ 376 case PTRACE_POKETEXT: /* write the word at location addr. */ 377 case PTRACE_POKEDATA: 378 ret = 0; 379 if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) 380 break; 381 ret = -EIO; 382 break; 383 384 case PTRACE_POKEUSR: { 385 struct pt_regs *regs; 386 ret = 0; 387 regs = (struct pt_regs *) ((unsigned long) child + 388 KERNEL_STACK_SIZE - 32 - sizeof(struct pt_regs)); 389 390 switch (addr) { 391 case 0 ... 31: 392 regs->regs[addr] = data; 393 break; 394 case FPR_BASE ... FPR_BASE + 31: { 395 unsigned long *fregs = get_fpu_regs(child); 396 if (!child->used_math) { 397 /* FP not yet used */ 398 memset(&child->thread.fpu.hard, ~0, 399 sizeof(child->thread.fpu.hard)); 400 child->thread.fpu.hard.control = 0; 401 } 402 fregs[addr - FPR_BASE] = data; 403 break; 404 } 405 case PC: 406 regs->cp0_epc = data; 407 break; 408 case MMHI: 409 regs->hi = data; 410 break; 411 case MMLO: 412 regs->lo = data; 413 break; 414 case FPC_CSR: 415 if (mips_cpu.options & MIPS_CPU_FPU) 416 child->thread.fpu.hard.control = data; 417 else 418 child->thread.fpu.soft.sr = data; 419 break; 420 default: 421 /* The rest are not allowed. */ 422 ret = -EIO; 423 break; 424 } 425 goto out; 426 } 427 case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ 428 case PTRACE_CONT: { /* restart after signal. */ 429 ret = -EIO; 430 if ((unsigned long) data > _NSIG) 431 break; 432 if (request == PTRACE_SYSCALL) 433 child->ptrace |= PT_TRACESYS; 434 else 435 child->ptrace &= ~PT_TRACESYS; 436 child->exit_code = data; 437 wake_up_process(child); 438 ret = 0; 439 break; 440 } 441 442/* 443 * make the child exit. Best I can do is send it a sigkill. 444 * perhaps it should be put in the status that it wants to 445 * exit. 446 */ 447 case PTRACE_KILL: { 448 if (child->state == TASK_ZOMBIE) /* already dead */ 449 break; 450 child->exit_code = SIGKILL; 451 wake_up_process(child); 452 break; 453 } 454 455 case PTRACE_DETACH: /* detach a process that was attached. */ 456 ret = ptrace_detach(child, data); 457 break; 458 459 case PTRACE_SETOPTIONS: { 460 if (data & PTRACE_O_TRACESYSGOOD) 461 child->ptrace |= PT_TRACESYSGOOD; 462 else 463 child->ptrace &= ~PT_TRACESYSGOOD; 464 ret = 0; 465 break; 466 } 467 468 default: 469 ret = -EIO; 470 break; 471 } 472 473out_tsk: 474 free_task_struct(child); 475out: 476 unlock_kernel(); 477 return ret; 478} 479 480asmlinkage void syscall_trace(void) 481{ 482 if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) 483 != (PT_PTRACED|PT_TRACESYS)) 484 return; 485 486 /* The 0x80 provides a way for the tracing parent to distinguish 487 between a syscall stop and SIGTRAP delivery */ 488 current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) 489 ? 0x80 : 0); 490 current->state = TASK_STOPPED; 491 notify_parent(current, SIGCHLD); 492 schedule(); 493 /* 494 * this isn't the same as continuing with a signal, but it will do 495 * for normal use. strace only continues with a signal if the 496 * stopping signal is not SIGTRAP. -brl 497 */ 498 if (current->exit_code) { 499 send_sig(current->exit_code, current, 1); 500 current->exit_code = 0; 501 } 502} 503