int uvm_mmap_dev(struct proc *p, void **addrp, size_t len, dev_t dev, off_t off) { struct uvm_object *uobj; int error, flags, prot; flags = MAP_SHARED; prot = VM_PROT_READ | VM_PROT_WRITE; if (*addrp) flags |= MAP_FIXED; else *addrp = (void *)p->p_emul->e_vm_default_addr(p, (vaddr_t)p->p_vmspace->vm_daddr, len, p->p_vmspace->vm_map.flags & VM_MAP_TOPDOWN); uobj = udv_attach(dev, prot, off, len); if (uobj == NULL) return EINVAL; error = uvm_mmap(&p->p_vmspace->vm_map, (vaddr_t *)addrp, (vsize_t)len, prot, prot, flags, UVM_ADV_RANDOM, uobj, off, p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur); return error; }
int uvm_mmap(vm_map_t map, vaddr_t *addr, vsize_t size, vm_prot_t prot, vm_prot_t maxprot, int flags, caddr_t handle, voff_t foff, vsize_t locklimit, struct proc *p) { struct uvm_object *uobj; struct vnode *vp; int error; int advice = UVM_ADV_NORMAL; uvm_flag_t uvmflag = 0; vsize_t align = 0; /* userland page size */ /* * check params */ if (size == 0) return(0); if (foff & PAGE_MASK) return(EINVAL); if ((prot & maxprot) != prot) return(EINVAL); /* * for non-fixed mappings, round off the suggested address. * for fixed mappings, check alignment and zap old mappings. */ if ((flags & MAP_FIXED) == 0) { *addr = round_page(*addr); /* round */ } else { if (*addr & PAGE_MASK) return(EINVAL); uvmflag |= UVM_FLAG_FIXED; uvm_unmap_p(map, *addr, *addr + size, p); /* zap! */ } /* * handle anon vs. non-anon mappings. for non-anon mappings attach * to underlying vm object. */ if (flags & MAP_ANON) { if ((flags & MAP_FIXED) == 0 && size >= __LDPGSZ) align = __LDPGSZ; foff = UVM_UNKNOWN_OFFSET; uobj = NULL; if ((flags & MAP_SHARED) == 0) /* XXX: defer amap create */ uvmflag |= UVM_FLAG_COPYONW; else /* shared: create amap now */ uvmflag |= UVM_FLAG_OVERLAY; } else { vp = (struct vnode *) handle; /* get vnode */ if (vp->v_type != VCHR) { uobj = uvn_attach((void *) vp, (flags & MAP_SHARED) ? maxprot : (maxprot & ~VM_PROT_WRITE)); if (uobj) { assert((void*)uobj == vp); if (flags & MAP_DENYWRITE) uvmflag |= UVM_FLAG_DENYWRITE; if ((flags & MAP_SHARED) && (maxprot & VM_PROT_WRITE)) uvmflag |= UVM_FLAG_WRITECOUNT; } #ifndef UBC /* * XXXCDC: hack from old code * don't allow vnodes which have been mapped * shared-writeable to persist [forces them to be * flushed out when last reference goes]. * XXXCDC: interesting side effect: avoids a bug. * note that in WRITE [ufs_readwrite.c] that we * allocate buffer, uncache, and then do the write. * the problem with this is that if the uncache causes * VM data to be flushed to the same area of the file * we are writing to... in that case we've got the * buffer locked and our process goes to sleep forever. * * XXXCDC: checking maxprot protects us from the * "persistbug" program but this is not a long term * solution. * * XXXCDC: we don't bother calling uncache with the vp * VOP_LOCKed since we know that we are already * holding a valid reference to the uvn (from the * uvn_attach above), and thus it is impossible for * the uncache to kill the uvn and trigger I/O. */ if (flags & MAP_SHARED) { if ((prot & VM_PROT_WRITE) || (maxprot & VM_PROT_WRITE)) { uvm_vnp_uncache(vp); } } #else /* XXX for now, attach doesn't gain a ref */ VREF(vp); #endif } else { uobj = udv_attach((void *) &vp->v_rdev, (flags & MAP_SHARED) ? maxprot : (maxprot & ~VM_PROT_WRITE), foff, size); /* * XXX Some devices don't like to be mapped with * XXX PROT_EXEC, but we don't really have a * XXX better way of handling this, right now */ if (uobj == NULL && (prot & PROT_EXEC) == 0) { maxprot &= ~VM_PROT_EXECUTE; uobj = udv_attach((void *) &vp->v_rdev, (flags & MAP_SHARED) ? maxprot : (maxprot & ~VM_PROT_WRITE), foff, size); } advice = UVM_ADV_RANDOM; } if (uobj == NULL) return((vp->v_type == VREG) ? ENOMEM : EINVAL); if ((flags & MAP_SHARED) == 0) uvmflag |= UVM_FLAG_COPYONW; } /* * set up mapping flags */ uvmflag = UVM_MAPFLAG(prot, maxprot, (flags & MAP_SHARED) ? UVM_INH_SHARE : UVM_INH_COPY, advice, uvmflag); error = uvm_map_p(map, addr, size, uobj, foff, align, uvmflag, p); if (error == 0) { /* * POSIX 1003.1b -- if our address space was configured * to lock all future mappings, wire the one we just made. */ if (prot == VM_PROT_NONE) { /* * No more work to do in this case. */ return (0); } vm_map_lock(map); if (map->flags & VM_MAP_WIREFUTURE) { if ((atop(size) + uvmexp.wired) > uvmexp.wiredmax #ifdef pmap_wired_count || (locklimit != 0 && (size + ptoa(pmap_wired_count(vm_map_pmap(map)))) > locklimit) #endif ) { error = ENOMEM; vm_map_unlock(map); /* unmap the region! */ uvm_unmap(map, *addr, *addr + size); goto bad; } /* * uvm_map_pageable() always returns the map * unlocked. */ error = uvm_map_pageable(map, *addr, *addr + size, FALSE, UVM_LK_ENTER); if (error != 0) { /* unmap the region! */ uvm_unmap(map, *addr, *addr + size); goto bad; } return (0); } vm_map_unlock(map); return (0); } /* * errors: first detach from the uobj, if any. */ if (uobj) uobj->pgops->pgo_detach(uobj); bad: return (error); }