diff options
author | des <des@FreeBSD.org> | 2001-10-07 20:08:42 +0000 |
---|---|---|
committer | des <des@FreeBSD.org> | 2001-10-07 20:08:42 +0000 |
commit | 29c5c858fce0c57b6ec8d88ecac8c437e7eb7f31 (patch) | |
tree | 619938768546b5f4413bb8dc40f32a9f6cff02e7 /sys/kern/sys_process.c | |
parent | 6eea6dc1d4f5db40f3dc480af266ecd685f624a2 (diff) | |
download | FreeBSD-src-29c5c858fce0c57b6ec8d88ecac8c437e7eb7f31.zip FreeBSD-src-29c5c858fce0c57b6ec8d88ecac8c437e7eb7f31.tar.gz |
Dissociate ptrace from procfs.
Until now, the ptrace syscall was implemented as a wrapper that called
various functions in procfs depending on which ptrace operation was
requested. Most of these functions were themselves wrappers around
procfs_{read,write}_{,db,fp}regs(), with only some extra error checks,
which weren't necessary in the ptrace case anyway.
This commit moves procfs_rwmem() from procfs_mem.c into sys_process.c
(renaming it to proc_rwmem() in the process), and implements ptrace()
directly in terms of procfs_{read,write}_{,db,fp}regs() instead of
having it fake up a struct uio and then call procfs_do{,db,fp}regs().
It also moves the prototypes for procfs_{read,write}_{,db,fp}regs()
and proc_rwmem() from proc.h to ptrace.h, and marks all procfs files
except procfs_machdep.c as "optional procfs" instead of "standard".
Diffstat (limited to 'sys/kern/sys_process.c')
-rw-r--r-- | sys/kern/sys_process.c | 391 |
1 files changed, 205 insertions, 186 deletions
diff --git a/sys/kern/sys_process.c b/sys/kern/sys_process.c index 4627b2d..7408c6c 100644 --- a/sys/kern/sys_process.c +++ b/sys/kern/sys_process.c @@ -45,151 +45,176 @@ #include <machine/reg.h> #include <vm/vm.h> +#include <vm/vm_param.h> #include <vm/pmap.h> +#include <vm/vm_extern.h> #include <vm/vm_map.h> +#include <vm/vm_kern.h> +#include <vm/vm_object.h> #include <vm/vm_page.h> -#include <fs/procfs/procfs.h> - -/* use the equivalent procfs code */ -#if 0 -static int -pread(struct proc *procp, unsigned int addr, unsigned int *retval) +int +proc_rwmem(struct proc *p, struct uio *uio) { - 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; - vm_pindex_t pindex; + struct vmspace *vm; + vm_map_t map; + vm_object_t object = NULL; + vm_offset_t pageno = 0; /* page number */ + vm_prot_t reqprot; + vm_offset_t kva; + int error; + int writing; - /* Map page into kernel space */ + GIANT_REQUIRED; - map = &procp->p_vmspace->vm_map; + /* + * if the vmspace is in the midst of being deallocated or the + * process is exiting, don't try to grab anything. The page table + * usage in that process can be messed up. + */ + vm = p->p_vmspace; + if ((p->p_flag & P_WEXIT)) + return (EFAULT); + if (vm->vm_refcnt < 1) + return (EFAULT); + ++vm->vm_refcnt; + /* + * The map we want... + */ + map = &vm->vm_map; - page_offset = addr - trunc_page(addr); - pageno = trunc_page(addr); + writing = uio->uio_rw == UIO_WRITE; + reqprot = writing ? (VM_PROT_WRITE | VM_PROT_OVERRIDE_WRITE) : + VM_PROT_READ; - tmap = map; - rv = vm_map_lookup(&tmap, pageno, VM_PROT_READ, &out_entry, - &object, &pindex, &out_prot, &wired); + kva = kmem_alloc_pageable(kernel_map, PAGE_SIZE); - if (rv != KERN_SUCCESS) - return (EINVAL); + /* + * Only map in one page at a time. We don't have to, but it + * makes things easier. This way is trivial - right? + */ + do { + vm_map_t tmap; + vm_offset_t uva; + int page_offset; /* offset into page */ + vm_map_entry_t out_entry; + vm_prot_t out_prot; + boolean_t wired; + vm_pindex_t pindex; + u_int len; + vm_page_t m; - vm_map_lookup_done(tmap, out_entry); + object = NULL; - /* Find space in kernel_map for the page we're interested in */ - rv = vm_map_find(kernel_map, object, IDX_TO_OFF(pindex), - &kva, PAGE_SIZE, 0, VM_PROT_ALL, VM_PROT_ALL, 0); + uva = (vm_offset_t)uio->uio_offset; - if (!rv) { - vm_object_reference(object); + /* + * Get the page number of this segment. + */ + pageno = trunc_page(uva); + page_offset = uva - pageno; - rv = vm_map_pageable(kernel_map, kva, kva + PAGE_SIZE, 0); - if (!rv) { - *retval = 0; - bcopy((caddr_t)kva + page_offset, - retval, sizeof *retval); + /* + * How many bytes to copy + */ + len = min(PAGE_SIZE - page_offset, uio->uio_resid); + + /* + * Fault the page on behalf of the process + */ + error = vm_fault(map, pageno, reqprot, VM_FAULT_NORMAL); + if (error) { + error = EFAULT; + break; } - vm_map_remove(kernel_map, kva, kva + PAGE_SIZE); - } - return (rv); -} + /* + * 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; + error = vm_map_lookup(&tmap, pageno, reqprot, &out_entry, + &object, &pindex, &out_prot, &wired); -static 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; - vm_pindex_t pindex; - boolean_t fix_prot = 0; + if (error) { + error = EFAULT; + + /* + * Make sure that there is no residue in 'object' from + * an error return on vm_map_lookup. + */ + object = NULL; - /* Map page into kernel space */ + break; + } - map = &procp->p_vmspace->vm_map; + m = vm_page_lookup(object, pindex); - page_offset = addr - trunc_page(addr); - pageno = trunc_page(addr); + /* Allow fallback to backing objects if we are reading */ - /* - * Check the permissions for the area we're interested in. - */ + while (m == NULL && !writing && object->backing_object) { + + pindex += OFF_TO_IDX(object->backing_object_offset); + object = object->backing_object; + + m = vm_page_lookup(object, pindex); + } + + if (m == NULL) { + error = EFAULT; + + /* + * Make sure that there is no residue in 'object' from + * an error return on vm_map_lookup. + */ + object = NULL; + + vm_map_lookup_done(tmap, out_entry); + + break; + } - 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! + * Wire the page into memory */ - 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... */ - } + vm_page_wire(m); - /* - * 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. - */ + /* + * We're done with tmap now. + * But reference the object first, so that we won't loose + * it. + */ + vm_object_reference(object); + vm_map_lookup_done(tmap, out_entry); - tmap = map; - rv = vm_map_lookup(&tmap, pageno, VM_PROT_WRITE, &out_entry, - &object, &pindex, &out_prot, &wired); - if (rv != KERN_SUCCESS) { - return (EINVAL); - } + pmap_kenter(kva, VM_PAGE_TO_PHYS(m)); - /* - * Okay, we've got the page. Let's release tmap. - */ + /* + * Now do the i/o move. + */ + error = uiomove((caddr_t)(kva + page_offset), len, uio); - vm_map_lookup_done(tmap, out_entry); + pmap_kremove(kva); - /* - * Fault the page in... - */ + /* + * release the page and the object + */ + vm_page_unwire(m, 1); + vm_object_deallocate(object); - rv = vm_fault(map, pageno, VM_PROT_WRITE|VM_PROT_READ, FALSE); - if (rv != KERN_SUCCESS) - return (EFAULT); + object = NULL; - /* Find space in kernel_map for the page we're interested in */ - rv = vm_map_find(kernel_map, object, IDX_TO_OFF(pindex), - &kva, PAGE_SIZE, 0, - VM_PROT_ALL, VM_PROT_ALL, 0); - if (!rv) { - vm_object_reference(object); + } while (error == 0 && uio->uio_resid > 0); - rv = vm_map_pageable(kernel_map, kva, kva + PAGE_SIZE, 0); - if (!rv) { - bcopy(&datum, (caddr_t)kva + page_offset, sizeof datum); - } - vm_map_remove(kernel_map, kva, kva + PAGE_SIZE); - } + if (object) + vm_object_deallocate(object); - if (fix_prot) - vm_map_protect(map, pageno, pageno + PAGE_SIZE, - VM_PROT_READ|VM_PROT_EXECUTE, 0); - return (rv); + kmem_free(kernel_map, kva, PAGE_SIZE); + vmspace_free(vm); + return (error); } -#endif /* * Process debugging system call. @@ -204,14 +229,17 @@ struct ptrace_args { #endif int -ptrace(td, uap) - struct thread *td; - struct ptrace_args *uap; +ptrace(struct thread *td, struct ptrace_args *uap) { struct proc *curp = td->td_proc; struct proc *p; struct iovec iov; struct uio uio; + union { + struct reg reg; + struct dbreg dbreg; + struct fpreg fpreg; + } r; int error = 0; int write; @@ -228,6 +256,19 @@ ptrace(td, uap) return (ESRCH); } + if ((error = p_candebug(curp, p)) != 0) { + PROC_UNLOCK(p); + return (error); + } + + /* + * Don't debug system processes! + */ + if ((p->p_flag & P_SYSTEM) != 0) { + PROC_UNLOCK(p); + return (EINVAL); + } + /* * Permissions check */ @@ -249,11 +290,6 @@ ptrace(td, uap) return (EBUSY); } - if ((error = p_candebug(curp, p))) { - PROC_UNLOCK(p); - return (error); - } - /* OK */ break; @@ -434,14 +470,14 @@ ptrace(td, uap) uio.uio_segflg = UIO_SYSSPACE; /* ie: the uap */ uio.uio_rw = write ? UIO_WRITE : UIO_READ; uio.uio_td = td; - error = procfs_domem(curp, p, NULL, &uio); + error = proc_rwmem(p, &uio); if (uio.uio_resid != 0) { /* - * XXX procfs_domem() doesn't currently return ENOSPC, + * XXX proc_rwmem() doesn't currently return ENOSPC, * so I think write() can bogusly return 0. * XXX what happens for short writes? We don't want * to write partial data. - * XXX procfs_domem() returns EPERM for other invalid + * XXX proc_rwmem() returns EPERM for other invalid * addresses. Convert this to EINVAL. Does this * clobber returns of EPERM for other reasons? */ @@ -456,99 +492,85 @@ ptrace(td, uap) #ifdef PT_SETREGS case PT_SETREGS: - write = 1; - /* fallthrough */ + error = copyin(uap->addr, &r.reg, sizeof r.reg); + if (error == 0) { + PHOLD(p); + error = procfs_write_regs(&p->p_thread, &r.reg); + PRELE(p); + } + return (error); #endif /* PT_SETREGS */ + #ifdef PT_GETREGS case PT_GETREGS: - /* write = 0 above */ + PHOLD(p); + error = procfs_read_regs(&p->p_thread, &r.reg); + PRELE(p); + if (error == 0) + error = copyout(&r.reg, uap->addr, sizeof r.reg); + return (error); #endif /* PT_SETREGS */ -#if defined(PT_SETREGS) || defined(PT_GETREGS) - if (!procfs_validregs(td)) /* no P_SYSTEM procs please */ - return (EINVAL); - else { - iov.iov_base = uap->addr; - iov.iov_len = sizeof(struct reg); - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - uio.uio_offset = 0; - uio.uio_resid = sizeof(struct reg); - uio.uio_segflg = UIO_USERSPACE; - uio.uio_rw = write ? UIO_WRITE : UIO_READ; - uio.uio_td = td; - return (procfs_doregs(curp, p, NULL, &uio)); - } -#endif /* defined(PT_SETREGS) || defined(PT_GETREGS) */ #ifdef PT_SETFPREGS case PT_SETFPREGS: - write = 1; - /* fallthrough */ + error = copyin(uap->addr, &r.fpreg, sizeof r.fpreg); + if (error == 0) { + PHOLD(p); + error = procfs_write_fpregs(&p->p_thread, &r.fpreg); + PRELE(p); + } + return (error); #endif /* PT_SETFPREGS */ + #ifdef PT_GETFPREGS case PT_GETFPREGS: - /* write = 0 above */ + PHOLD(p); + error = procfs_read_fpregs(&p->p_thread, &r.fpreg); + PRELE(p); + if (error == 0) + error = copyout(&r.fpreg, uap->addr, sizeof r.fpreg); + return (error); #endif /* PT_SETFPREGS */ -#if defined(PT_SETFPREGS) || defined(PT_GETFPREGS) - if (!procfs_validfpregs(td)) /* no P_SYSTEM procs please */ - return (EINVAL); - else { - iov.iov_base = uap->addr; - iov.iov_len = sizeof(struct fpreg); - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - uio.uio_offset = 0; - uio.uio_resid = sizeof(struct fpreg); - uio.uio_segflg = UIO_USERSPACE; - uio.uio_rw = write ? UIO_WRITE : UIO_READ; - uio.uio_td = td; - return (procfs_dofpregs(curp, p, NULL, &uio)); - } -#endif /* defined(PT_SETFPREGS) || defined(PT_GETFPREGS) */ #ifdef PT_SETDBREGS case PT_SETDBREGS: - write = 1; - /* fallthrough */ + error = copyin(uap->addr, &r.dbreg, sizeof r.dbreg); + if (error == 0) { + PHOLD(p); + error = procfs_write_dbregs(&p->p_thread, &r.dbreg); + PRELE(p); + } + return (error); #endif /* PT_SETDBREGS */ + #ifdef PT_GETDBREGS case PT_GETDBREGS: - /* write = 0 above */ + PHOLD(p); + error = procfs_read_dbregs(&p->p_thread, &r.dbreg); + PRELE(p); + if (error == 0) + error = copyout(&r.dbreg, uap->addr, sizeof r.dbreg); + return (error); #endif /* PT_SETDBREGS */ -#if defined(PT_SETDBREGS) || defined(PT_GETDBREGS) - if (!procfs_validdbregs(td)) /* no P_SYSTEM procs please */ - return (EINVAL); - else { - iov.iov_base = uap->addr; - iov.iov_len = sizeof(struct dbreg); - uio.uio_iov = &iov; - uio.uio_iovcnt = 1; - uio.uio_offset = 0; - uio.uio_resid = sizeof(struct dbreg); - uio.uio_segflg = UIO_USERSPACE; - uio.uio_rw = write ? UIO_WRITE : UIO_READ; - uio.uio_td = td; - return (procfs_dodbregs(curp, p, NULL, &uio)); - } -#endif /* defined(PT_SETDBREGS) || defined(PT_GETDBREGS) */ default: + KASSERT(0, ("unreachable code\n")); break; } + KASSERT(0, ("unreachable code\n")); return (0); } int -trace_req(p) - struct proc *p; +trace_req(struct proc *p) { return (1); } /* * stopevent() - * Stop a process because of a procfs event; + * Stop a process because of a debugging event; * stay stopped until p->p_step is cleared * (cleared by PIOCCONT in procfs). * @@ -556,10 +578,7 @@ trace_req(p) */ void -stopevent(p, event, val) - struct proc *p; - unsigned int event; - unsigned int val; +stopevent(struct proc *p, unsigned int event, unsigned int val) { PROC_LOCK_ASSERT(p, MA_OWNED | MA_NOTRECURSED); |