#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/sysmsg.h>
#include <sys/filedesc.h>
#include <sys/kern_syscall.h>
#include <sys/proc.h>
#include <sys/caps.h>
#include <sys/resource.h>
#include <sys/resourcevar.h>
#include <sys/vnode.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/conf.h>
#include <sys/stat.h>
#include <sys/vmmeter.h>
#include <sys/sysctl.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <sys/lock.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_object.h>
#include <vm/vm_page.h>
#include <vm/vm_pager.h>
#include <vm/vm_pageout.h>
#include <vm/vm_extern.h>
#include <vm/vm_kern.h>
#include <sys/file2.h>
#include <sys/thread.h>
#include <vm/vm_page2.h>
static int max_proc_mmap = 1000000;
SYSCTL_INT(_vm, OID_AUTO, max_proc_mmap, CTLFLAG_RW, &max_proc_mmap, 0, "");
int vkernel_enable;
SYSCTL_INT(_vm, OID_AUTO, vkernel_enable, CTLFLAG_RW, &vkernel_enable, 0, "");
static long vpagetable_mmap_count = 0;
SYSCTL_LONG(_vm, OID_AUTO, vpagetable_mmap, CTLFLAG_RW,
&vpagetable_mmap_count, 0, "Number of MAP_VPAGETABLE mappings created");
extern int debug_vpagetable;
int
sys_sstk(struct sysmsg *sysmsg, const struct sstk_args *uap)
{
return (EOPNOTSUPP);
}
int
kern_mmap(struct vmspace *vms, caddr_t uaddr, size_t ulen,
int uprot, int uflags, int fd, off_t upos, void **res)
{
struct thread *td = curthread;
struct proc *p = td->td_proc;
struct file *fp = NULL;
struct vnode *vp;
vm_offset_t addr;
vm_offset_t tmpaddr;
vm_size_t size, pageoff;
vm_prot_t prot, maxprot;
void *handle;
int flags, error;
off_t pos;
vm_object_t obj;
KKASSERT(p);
addr = (vm_offset_t) uaddr;
size = ulen;
prot = uprot & VM_PROT_ALL;
flags = uflags;
pos = upos;
if (((flags & MAP_ANON) && (fd != -1 || pos != 0)))
return (EINVAL);
if (size == 0)
return (EINVAL);
if (flags & MAP_STACK) {
if (fd != -1)
return (EINVAL);
if ((prot & (PROT_READ|PROT_WRITE)) != (PROT_READ|PROT_WRITE))
return (EINVAL);
flags |= MAP_ANON;
pos = 0;
}
if (flags & MAP_VPAGETABLE) {
if (vkernel_enable == 0)
return (EOPNOTSUPP);
if (flags & MAP_STACK)
return (EINVAL);
if ((prot & (PROT_READ|PROT_WRITE)) != (PROT_READ|PROT_WRITE))
return (EINVAL);
}
pageoff = (pos & PAGE_MASK);
pos -= pageoff;
size += pageoff;
size = (vm_size_t) round_page(size);
if (size < ulen)
return(EINVAL);
if (flags & (MAP_FIXED | MAP_TRYFIXED)) {
addr -= pageoff;
if (addr & PAGE_MASK)
return (EINVAL);
tmpaddr = addr + size;
if (tmpaddr < addr)
return (EINVAL);
if (VM_MAX_USER_ADDRESS > 0 && tmpaddr > VM_MAX_USER_ADDRESS)
return (EINVAL);
if (VM_MIN_USER_ADDRESS > 0 && addr < VM_MIN_USER_ADDRESS)
return (EINVAL);
} else {
addr = vm_map_hint(p, addr, prot, flags);
}
if (flags & MAP_ANON) {
handle = NULL;
maxprot = VM_PROT_ALL;
} else {
fp = holdfp(td, fd, -1);
if (fp == NULL)
return (EBADF);
if (fp->f_type != DTYPE_VNODE) {
error = EINVAL;
if (fp->f_type == DTYPE_DMABUF) {
kprintf("dmabuf-mmap-attempt\n");
}
goto done;
}
if (fp->f_flag & FPOSIXSHM)
flags |= MAP_NOSYNC;
vp = (struct vnode *) fp->f_data;
switch(vp->v_type) {
case VREG:
if ((obj = vp->v_object) == NULL) {
error = EINVAL;
goto done;
}
KKASSERT((struct vnode *)obj->handle == vp);
break;
case VCHR:
if (vp->v_rdev == NULL) {
error = EBADF;
goto done;
}
break;
default:
error = EINVAL;
goto done;
}
if (vp->v_type == VCHR && iszerodev(vp->v_rdev)) {
handle = NULL;
maxprot = VM_PROT_ALL;
flags |= MAP_ANON;
pos = 0;
} else {
if (vp->v_type == VCHR &&
(flags & (MAP_PRIVATE|MAP_COPY))) {
error = EINVAL;
goto done;
}
maxprot = VM_PROT_EXECUTE;
if (fp->f_flag & FREAD) {
maxprot |= VM_PROT_READ;
} else if (prot & PROT_READ) {
error = EACCES;
goto done;
}
if ((flags & MAP_SHARED) != 0 || vp->v_type == VCHR) {
if ((fp->f_flag & FWRITE) != 0) {
struct vattr va;
if ((error = VOP_GETATTR(vp, &va))) {
goto done;
}
if ((va.va_flags &
(IMMUTABLE|APPEND)) == 0) {
maxprot |= VM_PROT_WRITE;
if ((prot & PROT_WRITE) &&
vp->v_type == VREG &&
vn_lock(vp, LK_EXCLUSIVE | LK_RETRY) == 0) {
vfs_timestamp(&vp->v_lastwrite_ts);
vsetflags(vp, VLASTWRITETS);
vn_unlock(vp);
}
} else if (prot & PROT_WRITE) {
error = EPERM;
goto done;
}
} else if ((prot & PROT_WRITE) != 0) {
error = EACCES;
goto done;
}
} else {
maxprot |= VM_PROT_WRITE;
}
handle = (void *)vp;
}
}
lwkt_gettoken(&vms->vm_map.token);
if (max_proc_mmap && vms->vm_map.nentries >= max_proc_mmap) {
error = ENOMEM;
lwkt_reltoken(&vms->vm_map.token);
goto done;
}
error = vm_mmap(&vms->vm_map, &addr, size, prot, maxprot,
flags, handle, pos, fp);
if (error == 0)
*res = (void *)(addr + pageoff);
lwkt_reltoken(&vms->vm_map.token);
done:
if (fp)
dropfp(td, fd, fp);
return (error);
}
int
sys_mmap(struct sysmsg *sysmsg, const struct mmap_args *uap)
{
int error;
int flags = uap->flags;
off_t upos = uap->pos;
if (flags & MAP_STACK) {
if (uap->fd != -1)
return (EINVAL);
if ((uap->prot & (PROT_READ|PROT_WRITE)) !=
(PROT_READ|PROT_WRITE)) {
return (EINVAL);
}
flags &= ~MAP_STACK;
flags |= MAP_ANON;
upos = 0;
}
error = kern_mmap(curproc->p_vmspace, uap->addr, uap->len,
uap->prot, flags,
uap->fd, upos, &sysmsg->sysmsg_resultp);
return (error);
}
int
sys_msync(struct sysmsg *sysmsg, const struct msync_args *uap)
{
struct proc *p = curproc;
vm_offset_t addr;
vm_offset_t tmpaddr;
vm_size_t size, pageoff;
int flags;
vm_map_t map;
int rv;
addr = (vm_offset_t) uap->addr;
size = uap->len;
flags = uap->flags;
pageoff = (addr & PAGE_MASK);
addr -= pageoff;
size += pageoff;
size = (vm_size_t) round_page(size);
if (size < uap->len)
return(EINVAL);
tmpaddr = addr + size;
if (tmpaddr < addr)
return(EINVAL);
if ((flags & (MS_ASYNC|MS_INVALIDATE)) == (MS_ASYNC|MS_INVALIDATE))
return (EINVAL);
map = &p->p_vmspace->vm_map;
lwkt_gettoken(&map->token);
if (size == 0) {
vm_map_entry_t entry;
vm_map_lock_read(map);
rv = vm_map_lookup_entry(map, addr, &entry);
if (rv == FALSE) {
vm_map_unlock_read(map);
rv = KERN_INVALID_ADDRESS;
goto done;
}
addr = entry->ba.start;
size = entry->ba.end - entry->ba.start;
vm_map_unlock_read(map);
}
rv = vm_map_clean(map, addr, addr + size, (flags & MS_ASYNC) == 0,
(flags & MS_INVALIDATE) != 0);
done:
lwkt_reltoken(&map->token);
switch (rv) {
case KERN_SUCCESS:
break;
case KERN_INVALID_ADDRESS:
return (EINVAL);
case KERN_FAILURE:
return (EIO);
default:
return (EINVAL);
}
return (0);
}
int
sys_munmap(struct sysmsg *sysmsg, const struct munmap_args *uap)
{
struct proc *p = curproc;
vm_offset_t addr;
vm_offset_t tmpaddr;
vm_size_t size, pageoff;
vm_map_t map;
addr = (vm_offset_t) uap->addr;
size = uap->len;
pageoff = (addr & PAGE_MASK);
addr -= pageoff;
size += pageoff;
size = (vm_size_t) round_page(size);
if (size < uap->len)
return(EINVAL);
tmpaddr = addr + size;
if (tmpaddr < addr)
return(EINVAL);
if (size == 0)
return (0);
if (VM_MAX_USER_ADDRESS > 0 && tmpaddr > VM_MAX_USER_ADDRESS)
return (EINVAL);
if (VM_MIN_USER_ADDRESS > 0 && addr < VM_MIN_USER_ADDRESS)
return (EINVAL);
map = &p->p_vmspace->vm_map;
lwkt_gettoken(&map->token);
if (!vm_map_check_protection(map, addr, addr + size,
VM_PROT_NONE, FALSE)) {
lwkt_reltoken(&map->token);
return (EINVAL);
}
vm_map_remove(map, addr, addr + size);
lwkt_reltoken(&map->token);
return (0);
}
int
sys_mprotect(struct sysmsg *sysmsg, const struct mprotect_args *uap)
{
struct proc *p = curproc;
vm_offset_t addr;
vm_offset_t tmpaddr;
vm_size_t size, pageoff;
vm_prot_t prot;
int error;
addr = (vm_offset_t) uap->addr;
size = uap->len;
prot = uap->prot & VM_PROT_ALL;
pageoff = (addr & PAGE_MASK);
addr -= pageoff;
size += pageoff;
size = (vm_size_t) round_page(size);
if (size < uap->len)
return(EINVAL);
tmpaddr = addr + size;
if (tmpaddr < addr)
return(EINVAL);
switch (vm_map_protect(&p->p_vmspace->vm_map, addr, addr + size,
prot, FALSE)) {
case KERN_SUCCESS:
error = 0;
break;
case KERN_PROTECTION_FAILURE:
error = EACCES;
break;
default:
error = EINVAL;
break;
}
return (error);
}
int
sys_minherit(struct sysmsg *sysmsg, const struct minherit_args *uap)
{
struct proc *p = curproc;
vm_offset_t addr;
vm_offset_t tmpaddr;
vm_size_t size, pageoff;
vm_inherit_t inherit;
int error;
addr = (vm_offset_t)uap->addr;
size = uap->len;
inherit = uap->inherit;
pageoff = (addr & PAGE_MASK);
addr -= pageoff;
size += pageoff;
size = (vm_size_t) round_page(size);
if (size < uap->len)
return(EINVAL);
tmpaddr = addr + size;
if (tmpaddr < addr)
return(EINVAL);
switch (vm_map_inherit(&p->p_vmspace->vm_map, addr,
addr + size, inherit)) {
case KERN_SUCCESS:
error = 0;
break;
case KERN_PROTECTION_FAILURE:
error = EACCES;
break;
default:
error = EINVAL;
break;
}
return (error);
}
int
sys_madvise(struct sysmsg *sysmsg, const struct madvise_args *uap)
{
struct proc *p = curproc;
vm_offset_t start, end;
vm_offset_t tmpaddr = (vm_offset_t)uap->addr + uap->len;
int error;
if (uap->behav < 0 || uap->behav >= MADV_CONTROL_END)
return (EINVAL);
if (tmpaddr < (vm_offset_t)uap->addr)
return (EINVAL);
if (VM_MAX_USER_ADDRESS > 0 && tmpaddr > VM_MAX_USER_ADDRESS)
return (EINVAL);
if (VM_MIN_USER_ADDRESS > 0 && uap->addr < VM_MIN_USER_ADDRESS)
return (EINVAL);
start = trunc_page((vm_offset_t)uap->addr);
end = round_page(tmpaddr);
error = vm_map_madvise(&p->p_vmspace->vm_map, start, end,
uap->behav, 0);
return (error);
}
int
sys_mcontrol(struct sysmsg *sysmsg, const struct mcontrol_args *uap)
{
struct proc *p = curproc;
vm_offset_t start, end;
vm_offset_t tmpaddr = (vm_offset_t)uap->addr + uap->len;
int error;
if (uap->behav < 0 || uap->behav > MADV_CONTROL_END)
return (EINVAL);
if (tmpaddr < (vm_offset_t) uap->addr)
return (EINVAL);
if (VM_MAX_USER_ADDRESS > 0 && tmpaddr > VM_MAX_USER_ADDRESS)
return (EINVAL);
if (VM_MIN_USER_ADDRESS > 0 && uap->addr < VM_MIN_USER_ADDRESS)
return (EINVAL);
start = trunc_page((vm_offset_t)uap->addr);
end = round_page(tmpaddr);
error = vm_map_madvise(&p->p_vmspace->vm_map, start, end,
uap->behav, uap->value);
return (error);
}
int
sys_mincore(struct sysmsg *sysmsg, const struct mincore_args *uap)
{
struct proc *p = curproc;
vm_offset_t addr, first_addr;
vm_offset_t end, cend;
pmap_t pmap;
vm_map_t map;
char *vec;
int error;
int vecindex, lastvecindex;
vm_map_entry_t current;
vm_map_entry_t entry;
int mincoreinfo;
unsigned int timestamp;
first_addr = addr = trunc_page((vm_offset_t) uap->addr);
end = addr + (vm_size_t)round_page(uap->len);
if (end < addr)
return (EINVAL);
if (VM_MAX_USER_ADDRESS > 0 && end > VM_MAX_USER_ADDRESS)
return (EINVAL);
vec = uap->vec;
map = &p->p_vmspace->vm_map;
pmap = vmspace_pmap(p->p_vmspace);
lwkt_gettoken(&map->token);
vm_map_lock_read(map);
RestartScan:
timestamp = map->timestamp;
if (!vm_map_lookup_entry(map, addr, &entry))
entry = RB_MIN(vm_map_rb_tree, &map->rb_root);
lastvecindex = -1;
for (current = entry;
current && current->ba.start < end;
current = vm_map_rb_tree_RB_NEXT(current)) {
if (current->maptype != VM_MAPTYPE_NORMAL)
continue;
if (current->ba.object == NULL)
continue;
if (addr < current->ba.start)
addr = current->ba.start;
cend = current->ba.end;
if (cend > end)
cend = end;
while (addr < cend) {
mincoreinfo = pmap_mincore(pmap, addr);
if (mincoreinfo == 0 &&
current->maptype == VM_MAPTYPE_NORMAL) {
vm_pindex_t pindex;
vm_ooffset_t offset;
vm_page_t m;
offset = current->ba.offset +
(addr - current->ba.start);
pindex = OFF_TO_IDX(offset);
lwkt_gettoken(&vm_token);
vm_object_hold(current->ba.object);
m = vm_page_lookup(current->ba.object, pindex);
if (m && m->valid) {
mincoreinfo = MINCORE_INCORE;
if (m->dirty || pmap_is_modified(m))
mincoreinfo |= MINCORE_MODIFIED_OTHER;
if ((m->flags & PG_REFERENCED) ||
pmap_ts_referenced(m)) {
vm_page_flag_set(m, PG_REFERENCED);
mincoreinfo |= MINCORE_REFERENCED_OTHER;
}
}
vm_object_drop(current->ba.object);
lwkt_reltoken(&vm_token);
}
vm_map_unlock_read(map);
vecindex = OFF_TO_IDX(addr - first_addr);
while((lastvecindex + 1) < vecindex) {
error = subyte( vec + lastvecindex, 0);
if (error) {
error = EFAULT;
goto done;
}
++lastvecindex;
}
error = subyte(vec + vecindex, mincoreinfo);
if (error) {
error = EFAULT;
goto done;
}
vm_map_lock_read(map);
if (timestamp != map->timestamp)
goto RestartScan;
lastvecindex = vecindex;
addr += PAGE_SIZE;
}
}
vm_map_unlock_read(map);
vecindex = OFF_TO_IDX(end - first_addr);
while((lastvecindex + 1) < vecindex) {
error = subyte( vec + lastvecindex, 0);
if (error) {
error = EFAULT;
goto done;
}
++lastvecindex;
}
vm_map_lock_read(map);
if (timestamp != map->timestamp)
goto RestartScan;
vm_map_unlock_read(map);
error = 0;
done:
lwkt_reltoken(&map->token);
return (error);
}
int
sys_mlock(struct sysmsg *sysmsg, const struct mlock_args *uap)
{
vm_offset_t addr;
vm_offset_t tmpaddr;
vm_size_t size, pageoff;
struct thread *td = curthread;
struct proc *p = td->td_proc;
int error;
addr = (vm_offset_t) uap->addr;
size = uap->len;
pageoff = (addr & PAGE_MASK);
addr -= pageoff;
size += pageoff;
size = (vm_size_t) round_page(size);
if (size < uap->len)
return (EINVAL);
if (size == 0)
return (0);
tmpaddr = addr + size;
if (tmpaddr < addr)
return (EINVAL);
if (atop(size) + vmstats.v_wire_count > vm_page_max_wired)
return (EAGAIN);
#ifdef pmap_wired_count
if (size + ptoa(pmap_wired_count(vm_map_pmap(&p->p_vmspace->vm_map))) >
p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur) {
return (ENOMEM);
}
#else
error = caps_priv_check_td(td, SYSCAP_RESTRICTEDROOT);
if (error) {
return (error);
}
#endif
error = vm_map_user_wiring(&p->p_vmspace->vm_map,
addr, addr + size, FALSE);
return (error == KERN_SUCCESS ? 0 : ENOMEM);
}
int
sys_mlockall(struct sysmsg *sysmsg, const struct mlockall_args *uap)
{
struct thread *td = curthread;
struct proc *p = td->td_proc;
vm_map_t map = &p->p_vmspace->vm_map;
vm_map_entry_t entry;
int how = uap->how;
int rc = KERN_SUCCESS;
if (((how & MCL_CURRENT) == 0) && ((how & MCL_FUTURE) == 0))
return (EINVAL);
rc = caps_priv_check_td(td, SYSCAP_RESTRICTEDROOT);
if (rc)
return (rc);
vm_map_lock(map);
do {
if (how & MCL_CURRENT) {
RB_FOREACH(entry, vm_map_rb_tree, &map->rb_root) {
if ((entry->eflags & MAP_ENTRY_USER_WIRED) ||
entry->maptype != VM_MAPTYPE_NORMAL ||
(entry->max_protection & VM_PROT_WRITE) == 0) {
continue;
}
if (entry->wired_count != 0) {
entry->wired_count++;
entry->eflags |= MAP_ENTRY_USER_WIRED;
continue;
}
entry->wired_count++;
rc = vm_fault_wire(map, entry, TRUE, 0);
if (rc)
goto done;
entry->eflags |= MAP_ENTRY_USER_WIRED;
}
}
if (how & MCL_FUTURE)
map->flags |= MAP_WIREFUTURE;
} while(0);
done:
RB_FOREACH(entry, vm_map_rb_tree, &map->rb_root) {
if (entry->eflags & MAP_ENTRY_USER_WIRED) {
entry->eflags &= ~MAP_ENTRY_USER_WIRED;
vm_fault_unwire(map, entry);
}
}
vm_map_unlock(map);
return (rc);
}
int
sys_munlockall(struct sysmsg *sysmsg, const struct munlockall_args *uap)
{
struct thread *td = curthread;
struct proc *p = td->td_proc;
vm_map_t map = &p->p_vmspace->vm_map;
vm_map_entry_t entry;
int rc = KERN_SUCCESS;
vm_map_lock(map);
map->flags &= ~MAP_WIREFUTURE;
retry:
RB_FOREACH(entry, vm_map_rb_tree, &map->rb_root) {
if ((entry->eflags & MAP_ENTRY_USER_WIRED) == 0)
continue;
if (entry->eflags & MAP_ENTRY_IN_TRANSITION) {
entry->eflags |= MAP_ENTRY_NEEDS_WAKEUP;
++mycpu->gd_cnt.v_intrans_coll;
++mycpu->gd_cnt.v_intrans_wait;
vm_map_transition_wait(map, 1);
goto retry;
}
KASSERT(entry->wired_count > 0,
("wired_count was 0 with USER_WIRED set! %p", entry));
entry->eflags &= ~MAP_ENTRY_USER_WIRED;
vm_fault_unwire(map, entry);
}
vm_map_unlock(map);
return (rc);
}
int
sys_munlock(struct sysmsg *sysmsg, const struct munlock_args *uap)
{
struct thread *td = curthread;
struct proc *p = td->td_proc;
vm_offset_t addr;
vm_offset_t tmpaddr;
vm_size_t size, pageoff;
int error;
addr = (vm_offset_t) uap->addr;
size = uap->len;
pageoff = (addr & PAGE_MASK);
addr -= pageoff;
size += pageoff;
size = (vm_size_t) round_page(size);
tmpaddr = addr + size;
if (tmpaddr < addr)
return (EINVAL);
if (size == 0)
return (0);
#ifndef pmap_wired_count
error = caps_priv_check_td(td, SYSCAP_RESTRICTEDROOT);
if (error)
return (error);
#endif
error = vm_map_user_wiring(&p->p_vmspace->vm_map,
addr, addr + size, TRUE);
return (error == KERN_SUCCESS ? 0 : ENOMEM);
}
int
vm_mmap(vm_map_t map, vm_offset_t *addr, vm_size_t size, vm_prot_t prot,
vm_prot_t maxprot, int flags, void *handle, vm_ooffset_t foff,
struct file *fp)
{
boolean_t fitit;
vm_object_t object;
vm_offset_t eaddr;
vm_size_t esize;
vm_size_t align;
int (*uksmap)(vm_map_backing_t ba, int op, cdev_t dev, vm_page_t fake);
struct vnode *vp;
struct thread *td = curthread;
struct proc *p;
int rv = KERN_SUCCESS;
off_t objsize;
int docow;
int error;
if (size == 0)
return (0);
objsize = round_page(size);
if (objsize < size)
return (EINVAL);
size = objsize;
lwkt_gettoken(&map->token);
if ((p = curproc) != NULL && map == &p->p_vmspace->vm_map) {
esize = map->size + size;
if (esize < map->size ||
esize > p->p_rlimit[RLIMIT_VMEM].rlim_cur) {
lwkt_reltoken(&map->token);
return(ENOMEM);
}
}
if (foff & PAGE_MASK) {
lwkt_reltoken(&map->token);
return (EINVAL);
}
if (flags & MAP_SIZEALIGN) {
align = size;
if ((align ^ (align - 1)) != (align << 1) - 1) {
lwkt_reltoken(&map->token);
return (EINVAL);
}
} else if ((flags & MAP_FIXED) == 0 &&
((size & SEG_MASK) == 0 || size > SEG_SIZE * 16)) {
align = SEG_SIZE;
} else {
align = PAGE_SIZE;
}
if ((flags & (MAP_FIXED | MAP_TRYFIXED)) == 0) {
fitit = TRUE;
*addr = round_page(*addr);
} else {
if (*addr != trunc_page(*addr)) {
lwkt_reltoken(&map->token);
return (EINVAL);
}
eaddr = *addr + size;
if (eaddr < *addr) {
lwkt_reltoken(&map->token);
return (EINVAL);
}
fitit = FALSE;
if ((flags & MAP_TRYFIXED) == 0)
vm_map_remove(map, *addr, *addr + size);
}
uksmap = NULL;
if (flags & MAP_ANON) {
if (handle) {
object = default_pager_alloc(handle, objsize,
prot, foff);
if (object == NULL) {
lwkt_reltoken(&map->token);
return(ENOMEM);
}
docow = COWF_PREFAULT_PARTIAL;
} else {
foff = 0;
object = NULL;
docow = 0;
}
vp = NULL;
} else {
vp = (struct vnode *)handle;
if (vp->v_type == VCHR) {
if (flags & MAP_STACK) {
lwkt_reltoken(&map->token);
return(EINVAL);
}
}
if (vp->v_type == VCHR && vp->v_rdev->si_ops->d_uksmap) {
uksmap = vp->v_rdev->si_ops->d_uksmap;
object = NULL;
docow = COWF_PREFAULT_PARTIAL;
flags &= ~(MAP_PRIVATE|MAP_COPY);
flags |= MAP_SHARED;
} else if (vp->v_type == VCHR) {
error = dev_dmmap_single(vp->v_rdev, &foff, objsize,
&object, prot, fp);
if (error == ENODEV) {
handle = (void *)(intptr_t)vp->v_rdev;
object = dev_pager_alloc(handle, objsize, prot, foff);
if (object == NULL) {
lwkt_reltoken(&map->token);
return(EINVAL);
}
} else if (error) {
lwkt_reltoken(&map->token);
return(error);
}
docow = COWF_PREFAULT_PARTIAL;
flags &= ~(MAP_PRIVATE|MAP_COPY);
flags |= MAP_SHARED;
} else {
struct vattr vat;
int error;
error = VOP_GETATTR(vp, &vat);
if (error) {
lwkt_reltoken(&map->token);
return (error);
}
docow = COWF_PREFAULT_PARTIAL;
object = vnode_pager_reference(vp);
if (object == NULL && vp->v_type == VREG) {
lwkt_reltoken(&map->token);
kprintf("Warning: cannot mmap vnode %p, no "
"object\n", vp);
return(EINVAL);
}
if (vp->v_type == VREG && vat.va_nlink == 0) {
flags |= MAP_NOSYNC;
}
}
}
if ((flags & (MAP_ANON|MAP_SHARED)) == 0)
docow |= COWF_COPY_ON_WRITE;
if (flags & MAP_NOSYNC)
docow |= COWF_DISABLE_SYNCER;
if (flags & MAP_NOCORE)
docow |= COWF_DISABLE_COREDUMP;
if (flags & MAP_32BIT)
docow |= COWF_32BIT;
if (fitit) {
*addr = pmap_addr_hint(object, *addr, size);
}
if (uksmap) {
rv = vm_map_find(map, uksmap, vp->v_rdev,
foff, addr, size,
align, fitit,
VM_MAPTYPE_UKSMAP, VM_SUBSYS_MMAP,
prot, maxprot, docow);
} else if (flags & MAP_STACK) {
rv = vm_map_stack(map, addr, size, flags,
prot, maxprot, docow);
} else if (flags & MAP_VPAGETABLE) {
++vpagetable_mmap_count;
if (debug_vpagetable) {
kprintf("MAP_VPAGETABLE: addr=%lx size=%lx pid=%d\n",
*addr, size,
(curproc ? curproc->p_pid : -1));
}
rv = vm_map_find(map, object, NULL,
foff, addr, size,
align, fitit,
VM_MAPTYPE_VPAGETABLE, VM_SUBSYS_MMAP,
prot, maxprot, docow);
} else {
rv = vm_map_find(map, object, NULL,
foff, addr, size,
align, fitit,
VM_MAPTYPE_NORMAL, VM_SUBSYS_MMAP,
prot, maxprot, docow);
}
if (rv != KERN_SUCCESS) {
vm_object_deallocate(object);
goto out;
}
if (flags & (MAP_SHARED|MAP_INHERIT)) {
rv = vm_map_inherit(map, *addr, *addr + size, VM_INHERIT_SHARE);
if (rv != KERN_SUCCESS) {
vm_map_remove(map, *addr, *addr + size);
goto out;
}
}
if ((rv == KERN_SUCCESS) && (map->flags & MAP_WIREFUTURE))
vm_map_user_wiring(map, *addr, *addr + size, FALSE);
if (vp != NULL)
vn_mark_atime(vp, td);
out:
lwkt_reltoken(&map->token);
switch (rv) {
case KERN_SUCCESS:
return (0);
case KERN_INVALID_ADDRESS:
case KERN_NO_SPACE:
return (ENOMEM);
case KERN_PROTECTION_FAILURE:
return (EACCES);
default:
return (EINVAL);
}
}
int
vm_mmap_to_errno(int rv)
{
switch (rv) {
case KERN_SUCCESS:
return (0);
case KERN_INVALID_ADDRESS:
case KERN_NO_SPACE:
return (ENOMEM);
case KERN_PROTECTION_FAILURE:
return (EACCES);
default:
return (EINVAL);
}
}