* SUCH DAMAGE.
*
* from: @(#)sys_process.c 7.22 (Berkeley) 5/11/91
- * $Id$
+ * $Id: sys_process.c,v 1.9 1993/12/19 00:51:35 wollman Exp $
*/
-#include <stddef.h>
-
-#define IPCREG
#include "param.h"
+#include "systm.h"
#include "proc.h"
#include "vnode.h"
#include "buf.h"
#include "ptrace.h"
-#include "machine/eflags.h"
#include "machine/reg.h"
#include "machine/psl.h"
#include "vm/vm.h"
#include "vm/vm_page.h"
+#include "vm/vm_kern.h"
#include "user.h"
#define SFTRC 0
#endif
-/*
- * `ipcreg' defined in <machine/reg.h>
- * Should we define a structure with all regs?
- */
-int sipcreg[NIPCREG] =
- { 0,0,sEDI,sESI,sEBP,sEBX,sEDX,sECX,sEAX,sEIP,sCS,sEFLAGS,sESP,sSS };
-
-struct {
- int flag;
-#define IPC_BUSY 1
-#define IPC_WANT 2
-#define IPC_DONE 4
- int req; /* copy of ptrace request */
- int *addr; /* copy of ptrace address */
- int data; /* copy of ptrace data */
- int error; /* errno from `procxmt' */
- int regs[NIPCREG]; /* PT_[GS]ETREG */
- caddr_t buf; /* PT_BREAD/WRITE */
- int buflen; /* " */
-} ipc;
+int
+pread (struct proc *procp, unsigned int addr, unsigned int *retval) {
+ int rv;
+ vm_map_t map, tmap;
+ vm_object_t object;
+ vm_offset_t kva = 0;
+ int page_offset; /* offset into page */
+ vm_offset_t pageno; /* page number */
+ vm_map_entry_t out_entry;
+ vm_prot_t out_prot;
+ boolean_t wired, single_use;
+ vm_offset_t off;
+
+ /* Map page into kernel space */
+
+ map = &procp->p_vmspace->vm_map;
+
+ page_offset = addr - trunc_page(addr);
+ pageno = trunc_page(addr);
+
+ tmap = map;
+ rv = vm_map_lookup (&tmap, pageno, VM_PROT_READ, &out_entry,
+ &object, &off, &out_prot, &wired, &single_use);
+
+ if (rv != KERN_SUCCESS)
+ return EINVAL;
+
+ vm_map_lookup_done (tmap, out_entry);
+
+ /* Find space in kernel_map for the page we're interested in */
+ rv = vm_map_find (kernel_map, object, off, &kva, PAGE_SIZE, 1);
+
+ if (!rv) {
+ vm_object_reference (object);
+
+ rv = vm_map_pageable (kernel_map, kva, kva + PAGE_SIZE, 0);
+ if (!rv) {
+ *retval = 0;
+ bcopy ((caddr_t)(kva + page_offset), retval,
+ sizeof *retval);
+ }
+ vm_map_remove (kernel_map, kva, kva + PAGE_SIZE);
+ }
-/*
- * Process debugging system call.
- */
+ return rv;
+}
+
+int
+pwrite (struct proc *procp, unsigned int addr, unsigned int datum) {
+ int rv;
+ vm_map_t map, tmap;
+ vm_object_t object;
+ vm_offset_t kva = 0;
+ int page_offset; /* offset into page */
+ vm_offset_t pageno; /* page number */
+ vm_map_entry_t out_entry;
+ vm_prot_t out_prot;
+ boolean_t wired, single_use;
+ vm_offset_t off;
+ boolean_t fix_prot = 0;
+
+ /* Map page into kernel space */
+
+ map = &procp->p_vmspace->vm_map;
+
+ page_offset = addr - trunc_page(addr);
+ pageno = trunc_page(addr);
+
+ /*
+ * Check the permissions for the area we're interested in.
+ */
+
+ if (vm_map_check_protection (map, pageno, pageno + PAGE_SIZE,
+ VM_PROT_WRITE) == FALSE) {
+ /*
+ * If the page was not writable, we make it so.
+ * XXX It is possible a page may *not* be read/executable,
+ * if a process changes that!
+ */
+ fix_prot = 1;
+ /* The page isn't writable, so let's try making it so... */
+ if ((rv = vm_map_protect (map, pageno, pageno + PAGE_SIZE,
+ VM_PROT_ALL, 0)) != KERN_SUCCESS)
+ return EFAULT; /* I guess... */
+ }
+
+ /*
+ * Now we need to get the page. out_entry, out_prot, wired, and
+ * single_use aren't used. One would think the vm code would be
+ * a *bit* nicer... We use tmap because vm_map_lookup() can
+ * change the map argument.
+ */
+
+ tmap = map;
+ rv = vm_map_lookup (&tmap, pageno, VM_PROT_WRITE, &out_entry,
+ &object, &off, &out_prot, &wired, &single_use);
+ if (rv != KERN_SUCCESS) {
+ return EINVAL;
+ }
+
+ /*
+ * Okay, we've got the page. Let's release tmap.
+ */
+
+ vm_map_lookup_done (tmap, out_entry);
+
+ /*
+ * Fault the page-table-page in...
+ */
+ vm_map_pageable(map, trunc_page(vtopte(pageno)),
+ trunc_page(vtopte(pageno)) + NBPG, FALSE);
+ /*
+ * Fault the page in...
+ */
+
+ rv = vm_fault (map, pageno, VM_PROT_WRITE, FALSE);
+ if (rv != KERN_SUCCESS) {
+ /*
+ * release the page table page
+ */
+ vm_map_pageable(map, trunc_page(vtopte(pageno)),
+ trunc_page(vtopte(pageno)) + NBPG, TRUE);
+ return EFAULT;
+ }
+
+ /*
+ * The page may need to be faulted in again, it seems.
+ * This covers COW pages, I believe.
+ */
+
+ if (!rv)
+ rv = vm_fault (map, pageno, VM_PROT_WRITE, 0);
+
+ /* Find space in kernel_map for the page we're interested in */
+ rv = vm_map_find (kernel_map, object, off, (vm_offset_t *)&kva,
+ PAGE_SIZE, 1);
+
+ if (!rv) {
+ vm_object_reference (object);
+
+ rv = vm_map_pageable (kernel_map, kva, kva + PAGE_SIZE, FALSE);
+ if (!rv) {
+ bcopy (&datum, (caddr_t)(kva + page_offset), sizeof datum);
+ }
+ vm_map_remove (kernel_map, kva, kva + PAGE_SIZE);
+ }
+
+ if (fix_prot)
+ vm_map_protect (map, pageno, pageno + PAGE_SIZE,
+ VM_PROT_READ|VM_PROT_EXECUTE, 0);
+
+ /*
+ * release the page table page
+ */
+ vm_map_pageable(map, trunc_page(vtopte(pageno)),
+ trunc_page(vtopte(pageno)) + NBPG, TRUE);
+
+ return rv;
+}
struct ptrace_args {
int req;
int data;
};
+/*
+ * Process debugging system call.
+ */
+int
ptrace(curp, uap, retval)
struct proc *curp;
register struct ptrace_args *uap;
*retval = 0;
if (uap->req == PT_TRACE_ME) {
curp->p_flag |= STRC;
- /*p->p_tptr = p->p_pptr; * What shall we do here ? */
return 0;
}
if ((p = pfind(uap->pid)) == NULL) {
return ESRCH;
}
-#ifdef notyet
+#ifdef PT_ATTACH
if (uap->req != PT_ATTACH && (
(p->p_flag & STRC) == 0 ||
(p->p_tptr && curp != p->p_tptr) ||
return ESRCH;
#endif
-
-
#ifdef PT_ATTACH
+ if (uap->req != PT_ATTACH) {
+#endif
+ if ((p->p_flag & STRC) == 0)
+ return EPERM;
+ if (p->p_stat != SSTOP || (p->p_flag & SWTED) == 0)
+ return EBUSY;
+#ifdef PT_ATTACH
+ }
+#endif
+ /*
+ * XXX The PT_ATTACH code is completely broken. It will
+ * be obsoleted by a /proc filesystem, so is it worth it
+ * to fix it? (Answer, probably. So that'll be next,
+ * I guess.)
+ */
+
switch (uap->req) {
+#ifdef PT_ATTACH
case PT_ATTACH:
if (curp->p_ucred->cr_uid != 0 && (
curp->p_ucred->cr_uid != p->p_ucred->cr_uid ||
p->p_tptr = curp;
p->p_flag |= STRC;
- psignal(p, SIGTRAP);
+ psignal(p, SIGSTOP);
return 0;
case PT_DETACH:
splx(s);
return 0;
-#ifdef PT_INHERIT
+# ifdef PT_INHERIT
case PT_INHERIT:
if ((p->p_flag & STRC) == 0)
return ESRCH;
p->p_flag |= SFTRC;
return 0;
-#endif
-
- default:
- break;
- }
-#endif
-
- /* Other ptrace calls require target process to be in stopped state */
- if ((p->p_flag & STRC) == 0 || p->p_stat != SSTOP) {
- return ESRCH;
- }
-
- /* Acquire the ipc structure */
- while (ipc.flag & IPC_BUSY) {
- ipc.flag |= IPC_WANT;
- error = tsleep((caddr_t)&ipc, PWAIT|PCATCH, "ipc", 0);
- if (error)
- goto out;
- }
-
- /* Got it, fill it */
- ipc.flag = IPC_BUSY;
- ipc.error = 0;
- ipc.req = uap->req;
- ipc.addr = uap->addr;
- ipc.data = uap->data;
-
-#ifdef PT_GETREGS
- switch (uap->req) {
- case PT_SETREGS:
- error = copyin((char *)ipc.addr, (char *)ipc.regs, sizeof(ipc.regs));
- if (error)
- goto out;
- break;
-
-#ifdef notyet /* requires change in number of args to ptrace syscall */
- case PT_BWRITE_I:
- case PT_BWRITE_D:
- ipc.buflen = uap->data;
- ipc.buf = kmem_alloc_wait(kernelmap, uap->data);
- error = copyin((char *)ipc.addr, (char *)ipc.buf, ipc.buflen);
- if (error) {
- kmem_free_wakeup(kernelmap, ipc.buf, ipc.buflen);
- goto out;
- }
-#endif
- default:
- break;
- }
-#endif
-
- setrun(p);
- while ((ipc.flag & IPC_DONE) == 0) {
- error = tsleep((caddr_t)&ipc, PWAIT|PCATCH, "ipc", 0);
- if (error)
- goto out;
- }
-
- *retval = ipc.data;
- if (error = ipc.error)
- goto out;
-
-#ifdef PT_GETREGS
- switch (uap->req) {
- case PT_GETREGS:
- error = copyout((char *)ipc.regs, (char *)ipc.addr, sizeof(ipc.regs));
- break;
-
- case PT_BREAD_I:
- case PT_BREAD_D:
- /* Not yet */
- default:
- break;
- }
-#endif
-
-out:
- /* Release ipc structure */
- ipc.flag &= ~IPC_BUSY;
- if (ipc.flag & IPC_WANT) {
- ipc.flag &= ~IPC_WANT;
- wakeup((caddr_t)&ipc);
- }
- return error;
-}
-
-procxmt(p)
- register struct proc *p;
-{
- int i, *xreg, rv = 0;
-#ifdef i386
- int new_eflags, old_cs, old_ds, old_es, old_ss, old_eflags;
- int *regs;
-#endif
+# endif /* PT_INHERIT */
+#endif /* PT_ATTACH */
- /* Are we still being traced? */
- if ((p->p_flag & STRC) == 0)
- return 1;
-
- p->p_addr->u_kproc.kp_proc = *p;
- fill_eproc(p, &p->p_addr->u_kproc.kp_eproc);
-
- switch (ipc.req) {
case PT_READ_I:
case PT_READ_D:
- if (!useracc(ipc.addr, sizeof(ipc.data), B_READ)) {
- ipc.error = EFAULT;
- break;
- }
- ipc.error = copyin((char *)ipc.addr, (char *)&ipc.data, sizeof(ipc.data));
- break;
-
- case PT_READ_U:
- if ((u_int)ipc.addr > UPAGES * NBPG - sizeof(int)) {
- ipc.error = EFAULT;
- break;
- }
- ipc.data = *(int *)((u_int)p->p_addr + (u_int)ipc.addr);
- break;
-
+ if (error = pread (p, (unsigned int)uap->addr, retval))
+ return error;
+ return 0;
case PT_WRITE_I:
- case PT_WRITE_D: { /* 04 Sep 92*/
- vm_prot_t prot; /* current protection of region */
- int cow; /* ensure copy-on-write happens */
-
- if (cow = (useracc(ipc.addr, sizeof(ipc.data), B_WRITE) == 0)) {
- vm_offset_t addr = (vm_offset_t)ipc.addr;
- vm_size_t size;
- vm_prot_t max_prot;
- vm_inherit_t inh;
- boolean_t shared;
- vm_object_t object;
- vm_offset_t objoff;
-
- /*
- * XXX - the useracc check is stronger than the vm
- * checks because the user page tables are in the map.
- * Anyway, most of this can be removed now that COW
- * works.
- */
- if (!useracc(ipc.addr, sizeof(ipc.data), B_READ) ||
- vm_region(&p->p_vmspace->vm_map, &addr, &size,
- &prot, &max_prot, &inh, &shared,
- &object, &objoff) != KERN_SUCCESS ||
- vm_protect(&p->p_vmspace->vm_map, ipc.addr,
- sizeof(ipc.data), FALSE,
- prot|VM_PROT_WRITE) != KERN_SUCCESS ||
- vm_fault(&p->p_vmspace->vm_map,trunc_page(ipc.addr),
- VM_PROT_WRITE, FALSE) != KERN_SUCCESS) {
-
- ipc.error = EFAULT;
- break;
- }
- }
- ipc.error = copyout((char *)&ipc.data,
- (char *)ipc.addr, sizeof(ipc.data));
- if (cow)
- if (vm_protect(&p->p_vmspace->vm_map, ipc.addr,
- sizeof(ipc.data), FALSE,
- prot) != KERN_SUCCESS)
- printf("ptrace: oops\n");
- break;
- }
-
- case PT_WRITE_U:
-#ifdef i386
- regs = p->p_regs;
- /*
- * XXX - privileged kernel state is scattered all over the
- * user area. Only allow write access to areas known to
- * be safe.
- */
-#define GO_IF_SAFE(min, size) \
- if ((u_int)ipc.addr >= (min) \
- && (u_int)ipc.addr <= (min) + (size) - sizeof(int)) \
- goto pt_write_u
- /*
- * Allow writing entire FPU state.
- */
- GO_IF_SAFE(offsetof(struct user, u_pcb)
- + offsetof(struct pcb, pcb_savefpu),
- sizeof(struct save87));
- /*
- * Allow writing ordinary registers. Changes to segment
- * registers and to some bits in %eflags will be silently
- * ignored. Such changes ought to be an error.
- */
-/*
- * XXX - there is no define for the base of the user area except USRSTACK.
- * XXX - USRSTACK is not the base of the user stack. It is the base of the
- * user area.
- */
-#define USER_OFF(va) ((u_int)(va) - USRSTACK)
- GO_IF_SAFE(USER_OFF(regs),
- (curpcb->pcb_flags & FM_TRAP ? tSS + 1 : sSS + 1)
- * sizeof *regs);
- ipc.error = EFAULT;
- break;
-#else
- if ((u_int)ipc.addr > UPAGES * NBPG - sizeof(int)) {
- ipc.error = EFAULT;
- break;
- }
-#endif
- pt_write_u:
-#ifdef i386
- if (curpcb->pcb_flags & FM_TRAP) {
- old_cs = regs[tCS];
- old_ds = regs[tES];
- old_es = regs[tES];
- old_ss = regs[tSS];
- old_eflags = regs[tEFLAGS];
- } else {
- old_cs = regs[sCS];
- old_ss = regs[sSS];
- old_eflags = regs[sEFLAGS];
- }
-#endif
- *(int *)((u_int)p->p_addr + (u_int)ipc.addr) = ipc.data;
-#ifdef i386
+ case PT_WRITE_D:
+ if (error = pwrite (p, (unsigned int)uap->addr,
+ (unsigned int)uap->data))
+ return error;
+ return 0;
+ case PT_STEP:
+ if (error = ptrace_single_step (p))
+ return error;
+ /* fallthrough */
+ case PT_CONTINUE:
/*
- * Don't allow segment registers to change (although they can
- * be changed directly to certain values).
- * Don't allow privileged bits in %eflags to change. Users
- * have privilege to change TF and NT although although they
- * usually shouldn't.
- * XXX - fix PT_SETREGS.
- * XXX - simplify. Maybe copy through a temporary struct.
- * Watch out for problems when ipc.addr is not a multiple
- * of the register size.
+ * Continue at addr uap->addr with signal
+ * uap->data; if uap->addr is 1, then we just
+ * let the chips fall where they may.
+ *
+ * The only check I'll make right now is for
+ * uap->data to be larger than NSIG; if so, we return
+ * EINVAL.
*/
-#define EFL_UNPRIVILEGED (EFL_CF | EFL_PF | EFL_AF | EFL_ZF | EFL_SF \
- | EFL_TF | EFL_DF | EFL_OF | EFL_NT)
- if (curpcb->pcb_flags & FM_TRAP) {
- regs[tCS] = old_cs;
- regs[tDS] = old_ds;
- regs[tES] = old_es;
- regs[tSS] = old_es;
- new_eflags = regs[tEFLAGS];
- regs[tEFLAGS]
- = (new_eflags & EFL_UNPRIVILEGED)
- | (old_eflags & ~EFL_UNPRIVILEGED);
- } else {
- regs[sCS] = old_cs;
- regs[sSS] = old_ss;
- new_eflags = regs[sEFLAGS];
- regs[sEFLAGS]
- = (new_eflags & EFL_UNPRIVILEGED)
- | (old_eflags & ~EFL_UNPRIVILEGED);
- }
-#endif
- break;
+ if (uap->data >= NSIG)
+ return EINVAL;
- case PT_CONTINUE:
- if (ipc.addr != (int *)1) {
-#ifdef i386
- p->p_regs[(curpcb->pcb_flags&FM_TRAP)?tEIP:sEIP] = (int)ipc.addr;
-#endif
- }
- p->p_flag &= ~SSTRC; /* Only set by PT_SYSCALL */
- if ((unsigned)ipc.data >= NSIG) {
- ipc.error = EINVAL;
- } else {
- p->p_xstat = ipc.data;
- rv = 1;
+ if (uap->addr != (int*)1) {
+ fill_eproc (p, &p->p_addr->u_kproc.kp_eproc);
+ if (error = ptrace_set_pc (p, uap->addr))
+ return error;
}
- break;
- case PT_KILL:
- p->p_flag &= ~SSTRC; /* Only set by PT_SYSCALL */
- rv = 2;
- break;
+ p->p_xstat = uap->data;
- case PT_STEP:
-#ifdef i386
- if (ipc.addr != (int *)1) {
- p->p_regs[(curpcb->pcb_flags&FM_TRAP)?tEIP:sEIP] = (int)ipc.addr;
+/* if (p->p_stat == SSTOP) */
+ setrun (p);
+ return 0;
+ case PT_READ_U:
+ if ((u_int)uap->addr > (UPAGES * NBPG - sizeof(int))) {
+ return EFAULT;
}
- p->p_regs[(curpcb->pcb_flags&FM_TRAP)?tEFLAGS:sEFLAGS] |= PSL_T;
-#endif
- p->p_flag &= ~SSTRC; /* Only set by PT_SYSCALL */
- p->p_xstat = 0;
- rv = 1;
- break;
-
-#ifdef PT_SYSCALL
- case PT_SYSCALL:
- if (ipc.addr != (int *)1) {
-#ifdef i386
- p->p_regs[(curpcb->pcb_flags&FM_TRAP)?tEIP:sEIP] = (int)ipc.addr;
-#endif
+ p->p_addr->u_kproc.kp_proc = *p;
+ fill_eproc (p, &p->p_addr->u_kproc.kp_eproc);
+ *retval = *(int*)((u_int)p->p_addr + (u_int)uap->addr);
+ return 0;
+ case PT_WRITE_U:
+ if ((u_int)uap->addr > (UPAGES * NBPG - sizeof(int))) {
+ return EFAULT;
}
- p->p_flag |= SSTRC;
- p->p_xstat = 0;
- rv = 1;
- break;
-#endif
+ p->p_addr->u_kproc.kp_proc = *p;
+ fill_eproc (p, &p->p_addr->u_kproc.kp_eproc);
+ *(int*)((u_int)p->p_addr + (u_int)uap->addr) = uap->data;
+ return 0;
+ case PT_KILL:
+ p->p_xstat = SIGKILL;
+ setrun(p);
+ return 0;
#ifdef PT_GETREGS
case PT_GETREGS:
-#ifdef i386
- xreg = (curpcb->pcb_flags&FM_TRAP)?ipcreg:sipcreg;
-#endif
-
- for (i = 0; i < NIPCREG; i++)
- ipc.regs[i] = p->p_regs[xreg[i]];
- break;
-
+ /*
+ * copyout the registers into addr. There's no
+ * size constraint!!! *GRRR*
+ */
+ return ptrace_getregs(p, uap->addr);
case PT_SETREGS:
-#ifdef i386
- xreg = (curpcb->pcb_flags&FM_TRAP)?ipcreg:sipcreg;
-#endif
-
- for (i = 0; i < NIPCREG; i++)
- p->p_regs[xreg[i]] = ipc.regs[i];
- break;
-#endif
-
-#ifdef PT_DUMP
- case PT_DUMP:
- /* Should be able to specify core file name */
- ipc.error = coredump(p);
- break;
-#endif
-
+ /*
+ * copyin the registers from addr. Again, no
+ * size constraint!!! *GRRRR*
+ */
+ return ptrace_setregs (p, uap->addr);
+#endif /* PT_GETREGS */
default:
- ipc.error = EINVAL;
+ break;
}
- ipc.flag |= IPC_DONE;
- wakeup((caddr_t)&ipc);
- if (rv == 2)
- kexit(p, 0); /*???*/
+ return 0;
+}
- return rv;
+int
+procxmt(p)
+ register struct proc *p;
+{
+ return 1;
}
/*
};
/* ARGSUSED */
+int
profil(p, uap, retval)
struct proc *p;
register struct profil_args *uap;
* addupc is set right... it's gotta be writable by the user...
*/
- if (useracc(uap->bufbase,uap->bufsize*sizeof(short),B_WRITE) == 0)
+ if (useracc((caddr_t)uap->bufbase, uap->bufsize * sizeof(short),
+ B_WRITE) == 0)
return EFAULT;
p->p_stats->p_prof.pr_base = uap->bufbase;