1 /*- 2 * Copyright (c) 1982, 1986, 1989 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This software was developed by the Computer Systems Engineering group 6 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 7 * contributed to Berkeley. 8 * 9 * %sccs.include.proprietary.c% 10 * 11 * @(#)sys_process.c 7.1 (Berkeley) 07/13/92 12 */ 13 14 #include "sys/param.h" 15 #include "sys/proc.h" 16 #include "sys/vnode.h" 17 #include "sys/buf.h" 18 #include "sys/malloc.h" 19 #include "sys/ptrace.h" 20 #include "sys/user.h" 21 22 #include "vm/vm.h" 23 #include "vm/vm_page.h" 24 25 #include "machine/psl.h" 26 #include "machine/reg.h" 27 28 /* 29 * Priority for tracing 30 */ 31 #define IPCPRI PZERO 32 33 /* 34 * Tracing variables. 35 * Used to pass trace command from 36 * parent to child being traced. 37 * This data base cannot be 38 * shared and is locked 39 * per user. 40 */ 41 struct { 42 int ip_lock; 43 int ip_req; 44 int ip_error; 45 union { 46 char un_any[4]; 47 struct { 48 int wd_data; 49 caddr_t wd_addr; 50 } un_wd; 51 struct trapframe un_tf; 52 struct fpstate un_f; 53 } ip_un; 54 } ipc; 55 #define ip_any ip_un.un_any 56 #define ip_data ip_un.un_wd.wd_data 57 #define ip_addr ip_un.un_wd.wd_addr 58 #define ip_tf ip_un.un_tf 59 #define ip_f ip_un.un_f 60 61 /* 62 * Process debugging system call. 63 */ 64 struct ptrace_args { 65 int req; 66 int pid; 67 caddr_t addr; 68 int data; 69 }; 70 ptrace(curp, uap, retval) 71 struct proc *curp; 72 register struct ptrace_args *uap; 73 int *retval; 74 { 75 register struct proc *p; 76 register enum { t_oneword, t_regin, t_regout } type; 77 register size_t size; 78 register int error; 79 80 if (uap->req == PT_TRACE_ME) { 81 curp->p_flag |= STRC; 82 curp->p_oppid = 0; /* XXX put in the zeroed section */ 83 return (0); 84 } 85 if ((p = pfind(uap->pid)) == NULL) 86 return (ESRCH); 87 switch (uap->req) { 88 89 case PT_READ_I: 90 case PT_READ_D: 91 case PT_WRITE_I: 92 case PT_WRITE_D: 93 case PT_CONTINUE: 94 case PT_KILL: 95 case PT_DETACH: 96 type = t_oneword; 97 size = 0; 98 break; 99 100 case PT_ATTACH: 101 /* 102 * Must be root if the process has used set user or 103 * group privileges or does not belong to the real 104 * user. Must not be already traced. 105 */ 106 if ((p->p_flag & SUGID || 107 p->p_cred->p_ruid != curp->p_cred->p_ruid) && 108 (error = suser(p->p_ucred, &p->p_acflag)) != 0) 109 return (error); 110 if (p->p_flag & STRC) 111 return (EALREADY); /* ??? */ 112 /* 113 * It would be nice if the tracing relationship was separate 114 * from the parent relationship but that would require 115 * another set of links in the proc struct or for "wait" 116 * to scan the entire proc table. To make life easier, 117 * we just re-parent the process we're trying to trace. 118 * The old parent is remembered so we can put things back 119 * on a "detach". 120 */ 121 p->p_flag |= STRC; 122 p->p_oppid = p->p_pptr->p_pid; 123 proc_reparent(p, curp); 124 psignal(p, SIGSTOP); 125 return (0); 126 127 case PT_GETREGS: 128 type = t_regout; 129 size = sizeof(struct trapframe); 130 break; 131 132 case PT_SETREGS: 133 type = t_regin; 134 size = sizeof(struct trapframe); 135 break; 136 137 case PT_GETFPREGS: 138 type = t_regout; 139 size = sizeof(struct fpstate); 140 break; 141 142 case PT_SETFPREGS: 143 type = t_regin; 144 size = sizeof(struct fpstate); 145 break; 146 147 default: 148 return (EINVAL); 149 } 150 if (p->p_stat != SSTOP || p->p_pptr != curp || !(p->p_flag & STRC)) 151 return (ESRCH); 152 while (ipc.ip_lock) 153 sleep((caddr_t)&ipc, IPCPRI); 154 ipc.ip_lock = p->p_pid; 155 ipc.ip_req = uap->req; 156 ipc.ip_error = 0; 157 switch (type) { 158 159 case t_oneword: 160 ipc.ip_addr = uap->addr; 161 ipc.ip_data = uap->data; 162 break; 163 164 case t_regin: 165 if ((error = copyin(uap->addr, ipc.ip_any, size)) != 0) 166 return (error); 167 break; 168 169 case t_regout: 170 break; 171 172 default: 173 panic("ptrace"); 174 } 175 p->p_flag &= ~SWTED; 176 do { 177 if (p->p_stat == SSTOP) 178 setrun(p); 179 sleep((caddr_t)&ipc, IPCPRI); 180 } while (ipc.ip_req > 0); 181 if ((error = ipc.ip_error) == 0) { 182 if (type == t_oneword) 183 *retval = ipc.ip_data; 184 else if (type == t_regout) 185 error = copyout(ipc.ip_any, uap->addr, size); 186 } 187 ipc.ip_lock = 0; 188 wakeup((caddr_t)&ipc); 189 return (error); 190 } 191 192 /* 193 * Write text space by unprotecting, writing, and reprotecting. 194 */ 195 static int 196 writetext(p, addr, data, len) 197 struct proc *p; 198 caddr_t addr, data; 199 int len; 200 { 201 vm_offset_t sa, ea; 202 vm_map_t map; 203 int error; 204 205 map = &p->p_vmspace->vm_map; 206 sa = trunc_page((vm_offset_t)addr); 207 ea = round_page((vm_offset_t)addr + len - 1); 208 if (vm_map_protect(map, sa, ea, VM_PROT_DEFAULT, 0) != KERN_SUCCESS) 209 return (-1); 210 error = copyout(data, addr, len); 211 (void) vm_map_protect(map, sa, ea, VM_PROT_READ|VM_PROT_EXECUTE, 0); 212 return (error); 213 } 214 215 /* 216 * Transmit a tracing request from the parent to the child process 217 * being debugged. This code runs in the context of the child process 218 * to fulfill the command requested by the parent. 219 */ 220 procxmt(p) 221 register struct proc *p; 222 { 223 register int req, error, sig, pc, psr; 224 register caddr_t addr; 225 register struct trapframe *tf, *utf; 226 register struct fpstate *fs, *oldfs; 227 extern struct fpstate initfpstate; 228 229 if (ipc.ip_lock != p->p_pid) 230 return (0); 231 p->p_slptime = 0; 232 req = ipc.ip_req; 233 ipc.ip_req = 0; 234 error = 0; 235 switch (req) { 236 237 case PT_READ_I: /* read the child's text space */ 238 case PT_READ_D: /* read the child's data space */ 239 write_user_windows(); 240 (void) rwindow_save(p); /* ignore unwritable windows */ 241 error = copyin(ipc.ip_addr, ipc.ip_any, sizeof(int)); 242 break; 243 244 case PT_WRITE_I: /* write the child's text space */ 245 case PT_WRITE_D: /* write the child's data space */ 246 addr = ipc.ip_addr; 247 write_user_windows(); 248 (void) rwindow_save(p); 249 error = copyout(ipc.ip_any, addr, sizeof(int)); 250 if (error && req == PT_WRITE_I) 251 error = writetext(p, addr, ipc.ip_any, sizeof(int)); 252 break; 253 254 case PT_CONTINUE: /* continue the child */ 255 sig = ipc.ip_data; 256 if ((unsigned)sig >= NSIG) { 257 error = EINVAL; 258 break; 259 } 260 pc = (int)ipc.ip_addr; 261 if (pc & 3) { 262 if (pc != 1) { 263 error = EINVAL; 264 break; 265 } 266 } else { 267 tf = p->p_md.md_tf; 268 tf->tf_pc = pc; 269 tf->tf_npc = pc + 4; 270 } 271 p->p_xstat = sig; /* see issig */ 272 wakeup((caddr_t)&ipc); 273 return (1); 274 275 case PT_KILL: /* kill the child process */ 276 wakeup((caddr_t)&ipc); 277 exit(p, (int)p->p_xstat); 278 279 case PT_DETACH: /* stop tracing the child */ 280 sig = ipc.ip_data; 281 if ((unsigned)sig >= NSIG) { 282 error = EINVAL; 283 break; 284 } 285 pc = (int)ipc.ip_addr; 286 if (pc & 3) { 287 if (pc != 1) { 288 error = EINVAL; 289 break; 290 } 291 } else { 292 tf = p->p_md.md_tf; 293 tf->tf_pc = pc; 294 tf->tf_npc = pc + 4; 295 } 296 p->p_xstat = sig; /* see issig */ 297 p->p_flag &= ~STRC; 298 if (p->p_oppid != p->p_pptr->p_pid) { 299 register struct proc *pp = pfind(p->p_oppid); 300 301 if (pp) 302 proc_reparent(p, pp); 303 } 304 p->p_oppid = 0; 305 wakeup((caddr_t)&ipc); 306 return (1); 307 308 case PT_GETREGS: 309 copywords((caddr_t)p->p_md.md_tf, (caddr_t)&ipc.ip_tf, 310 sizeof(struct trapframe)); 311 ipc.ip_tf.tf_global[0] = 0; /* XXX */ 312 break; 313 314 case PT_SETREGS: 315 tf = p->p_md.md_tf; 316 utf = &ipc.ip_tf; 317 if ((utf->tf_pc | utf->tf_npc) & 3) { 318 error = EINVAL; 319 break; 320 } 321 psr = (tf->tf_psr & ~PSR_ICC) | (utf->tf_psr & PSR_ICC); 322 copywords((caddr_t)utf, (caddr_t)tf, sizeof(*tf)); 323 tf->tf_psr = psr; 324 break; 325 326 case PT_GETFPREGS: 327 if ((fs = p->p_md.md_fpstate) == NULL) 328 fs = &initfpstate; 329 else if (p == fpproc) 330 savefpstate(fs); 331 copywords((caddr_t)fs, (caddr_t)&ipc.ip_f, sizeof *fs); 332 break; 333 334 case PT_SETFPREGS: 335 fs = &ipc.ip_f; 336 if ((fs->fs_fsr & FSR_MBZ) != 0 || fs->fs_qsize) { 337 error = EINVAL; 338 break; 339 } 340 oldfs = p->p_md.md_fpstate; 341 if (oldfs == NULL) 342 p->p_md.md_fpstate = oldfs = malloc(sizeof *oldfs, 343 M_SUBPROC, M_WAITOK); 344 else if (p == fpproc) { 345 savefpstate(oldfs); 346 fpproc = NULL; 347 p->p_md.md_tf->tf_psr &= ~PSR_EF; 348 } 349 copywords((caddr_t)fs, (caddr_t)oldfs, sizeof *oldfs); 350 break; 351 352 default: 353 panic("procxmt"); 354 } 355 ipc.ip_error = error; 356 wakeup((caddr_t)&ipc); 357 return (0); 358 } 359