1// TODO some minor issues 2/* 3 * This file is subject to the terms and conditions of the GNU General Public 4 * License. See the file "COPYING" in the main directory of this archive 5 * for more details. 6 * 7 * Copyright (C) 2001 - 2005 Tensilica Inc. 8 * 9 * Joe Taylor <joe@tensilica.com, joetylr@yahoo.com> 10 * Chris Zankel <chris@zankel.net> 11 * Scott Foehner<sfoehner@yahoo.com>, 12 * Kevin Chea 13 * Marc Gauthier<marc@tensilica.com> <marc@alumni.uwaterloo.ca> 14 */ 15 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/security.h> 23#include <linux/signal.h> 24 25#include <asm/pgtable.h> 26#include <asm/page.h> 27#include <asm/system.h> 28#include <asm/uaccess.h> 29#include <asm/ptrace.h> 30#include <asm/elf.h> 31 32#define TEST_KERNEL 33 34 35/* 36 * Called by kernel/ptrace.c when detaching.. 37 * 38 * Make sure single step bits etc are not set. 39 */ 40 41void ptrace_disable(struct task_struct *child) 42{ 43 /* Nothing to do.. */ 44} 45 46long arch_ptrace(struct task_struct *child, long request, long addr, long data) 47{ 48 int ret = -EPERM; 49 50 switch (request) { 51 case PTRACE_PEEKTEXT: /* read word at location addr. */ 52 case PTRACE_PEEKDATA: 53 { 54 unsigned long tmp; 55 int copied; 56 57 copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); 58 ret = -EIO; 59 if (copied != sizeof(tmp)) 60 break; 61 ret = put_user(tmp,(unsigned long *) data); 62 63 goto out; 64 } 65 66 /* Read the word at location addr in the USER area. */ 67 68 case PTRACE_PEEKUSR: 69 { 70 struct pt_regs *regs; 71 unsigned long tmp; 72 73 regs = task_pt_regs(child); 74 tmp = 0; /* Default return value. */ 75 76 switch(addr) { 77 78 case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: 79 { 80 int ar = addr - REG_AR_BASE - regs->windowbase * 4; 81 ar &= (XCHAL_NUM_AREGS - 1); 82 if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0) 83 tmp = regs->areg[ar]; 84 else 85 ret = -EIO; 86 break; 87 } 88 case REG_A_BASE ... REG_A_BASE + 15: 89 tmp = regs->areg[addr - REG_A_BASE]; 90 break; 91 case REG_PC: 92 tmp = regs->pc; 93 break; 94 case REG_PS: 95 /* Note: PS.EXCM is not set while user task is running; 96 * its being set in regs is for exception handling 97 * convenience. */ 98 tmp = (regs->ps & ~(1 << PS_EXCM_BIT)); 99 break; 100 case REG_WB: 101 tmp = regs->windowbase; 102 break; 103 case REG_WS: 104 tmp = regs->windowstart; 105 break; 106 case REG_LBEG: 107 tmp = regs->lbeg; 108 break; 109 case REG_LEND: 110 tmp = regs->lend; 111 break; 112 case REG_LCOUNT: 113 tmp = regs->lcount; 114 break; 115 case REG_SAR: 116 tmp = regs->sar; 117 break; 118 case REG_DEPC: 119 tmp = regs->depc; 120 break; 121 case REG_EXCCAUSE: 122 tmp = regs->exccause; 123 break; 124 case REG_EXCVADDR: 125 tmp = regs->excvaddr; 126 break; 127 case SYSCALL_NR: 128 tmp = regs->syscall; 129 break; 130 default: 131 tmp = 0; 132 ret = -EIO; 133 goto out; 134 } 135 ret = put_user(tmp, (unsigned long *) data); 136 goto out; 137 } 138 139 case PTRACE_POKETEXT: /* write the word at location addr. */ 140 case PTRACE_POKEDATA: 141 if (access_process_vm(child, addr, &data, sizeof(data), 1) 142 == sizeof(data)) 143 break; 144 ret = -EIO; 145 goto out; 146 147 case PTRACE_POKEUSR: 148 { 149 struct pt_regs *regs; 150 regs = task_pt_regs(child); 151 152 switch (addr) { 153 case REG_AR_BASE ... REG_AR_BASE + XCHAL_NUM_AREGS - 1: 154 { 155 int ar = addr - REG_AR_BASE - regs->windowbase * 4; 156 if (ar < 16 && ar + (regs->wmask >> 4) * 4 >= 0) 157 regs->areg[ar & (XCHAL_NUM_AREGS - 1)] = data; 158 else 159 ret = -EIO; 160 break; 161 } 162 case REG_A_BASE ... REG_A_BASE + 15: 163 regs->areg[addr - REG_A_BASE] = data; 164 break; 165 case REG_PC: 166 regs->pc = data; 167 break; 168 case SYSCALL_NR: 169 regs->syscall = data; 170 break; 171#ifdef TEST_KERNEL 172 case REG_WB: 173 regs->windowbase = data; 174 break; 175 case REG_WS: 176 regs->windowstart = data; 177 break; 178#endif 179 180 default: 181 /* The rest are not allowed. */ 182 ret = -EIO; 183 break; 184 } 185 break; 186 } 187 188 /* continue and stop at next (return from) syscall */ 189 case PTRACE_SYSCALL: 190 case PTRACE_CONT: /* restart after signal. */ 191 { 192 ret = -EIO; 193 if (!valid_signal(data)) 194 break; 195 if (request == PTRACE_SYSCALL) 196 set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 197 else 198 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 199 child->exit_code = data; 200 /* Make sure the single step bit is not set. */ 201 child->ptrace &= ~PT_SINGLESTEP; 202 wake_up_process(child); 203 ret = 0; 204 break; 205 } 206 207 /* 208 * make the child exit. Best I can do is send it a sigkill. 209 * perhaps it should be put in the status that it wants to 210 * exit. 211 */ 212 case PTRACE_KILL: 213 ret = 0; 214 if (child->exit_state == EXIT_ZOMBIE) /* already dead */ 215 break; 216 child->exit_code = SIGKILL; 217 child->ptrace &= ~PT_SINGLESTEP; 218 wake_up_process(child); 219 break; 220 221 case PTRACE_SINGLESTEP: 222 ret = -EIO; 223 if (!valid_signal(data)) 224 break; 225 clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); 226 child->ptrace |= PT_SINGLESTEP; 227 child->exit_code = data; 228 wake_up_process(child); 229 ret = 0; 230 break; 231 232 case PTRACE_GETREGS: 233 { 234 /* 'data' points to user memory in which to write. 235 * Mainly due to the non-live register values, we 236 * reformat the register values into something more 237 * standard. For convenience, we use the handy 238 * elf_gregset_t format. */ 239 240 xtensa_gregset_t format; 241 struct pt_regs *regs = task_pt_regs(child); 242 243 do_copy_regs (&format, regs, child); 244 245 /* Now, copy to user space nice and easy... */ 246 ret = 0; 247 if (copy_to_user((void *)data, &format, sizeof(elf_gregset_t))) 248 ret = -EFAULT; 249 break; 250 } 251 252 case PTRACE_SETREGS: 253 { 254 /* 'data' points to user memory that contains the new 255 * values in the elf_gregset_t format. */ 256 257 xtensa_gregset_t format; 258 struct pt_regs *regs = task_pt_regs(child); 259 260 if (copy_from_user(&format,(void *)data,sizeof(elf_gregset_t))){ 261 ret = -EFAULT; 262 break; 263 } 264 265 266 do_restore_regs (&format, regs, child); 267 268 ret = 0; 269 break; 270 } 271 272 case PTRACE_GETFPREGS: 273 { 274 /* 'data' points to user memory in which to write. 275 * For convenience, we use the handy 276 * elf_fpregset_t format. */ 277 278 elf_fpregset_t fpregs; 279 struct pt_regs *regs = task_pt_regs(child); 280 281 do_save_fpregs (&fpregs, regs, child); 282 283 /* Now, copy to user space nice and easy... */ 284 ret = 0; 285 if (copy_to_user((void *)data, &fpregs, sizeof(elf_fpregset_t))) 286 ret = -EFAULT; 287 288 break; 289 } 290 291 case PTRACE_SETFPREGS: 292 { 293 /* 'data' points to user memory that contains the new 294 * values in the elf_fpregset_t format. 295 */ 296 elf_fpregset_t fpregs; 297 struct pt_regs *regs = task_pt_regs(child); 298 299 ret = 0; 300 if (copy_from_user(&fpregs, (void *)data, sizeof(elf_fpregset_t))) { 301 ret = -EFAULT; 302 break; 303 } 304 305 if (do_restore_fpregs (&fpregs, regs, child)) 306 ret = -EIO; 307 break; 308 } 309 310 case PTRACE_GETFPREGSIZE: 311 /* 'data' points to 'unsigned long' set to the size 312 * of elf_fpregset_t 313 */ 314 ret = put_user(sizeof(elf_fpregset_t), (unsigned long *) data); 315 break; 316 317 case PTRACE_DETACH: /* detach a process that was attached. */ 318 ret = ptrace_detach(child, data); 319 break; 320 321 default: 322 ret = ptrace_request(child, request, addr, data); 323 goto out; 324 } 325 out: 326 return ret; 327} 328 329void do_syscall_trace(void) 330{ 331 /* 332 * The 0x80 provides a way for the tracing parent to distinguish 333 * between a syscall stop and SIGTRAP delivery 334 */ 335 ptrace_notify(SIGTRAP|((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); 336 337 /* 338 * this isn't the same as continuing with a signal, but it will do 339 * for normal use. strace only continues with a signal if the 340 * stopping signal is not SIGTRAP. -brl 341 */ 342 if (current->exit_code) { 343 send_sig(current->exit_code, current, 1); 344 current->exit_code = 0; 345 } 346} 347 348void do_syscall_trace_enter(struct pt_regs *regs) 349{ 350 if (test_thread_flag(TIF_SYSCALL_TRACE) 351 && (current->ptrace & PT_PTRACED)) 352 do_syscall_trace(); 353 354} 355 356void do_syscall_trace_leave(struct pt_regs *regs) 357{ 358 if ((test_thread_flag(TIF_SYSCALL_TRACE)) 359 && (current->ptrace & PT_PTRACED)) 360 do_syscall_trace(); 361} 362