Source to bsd/kern/kern_mman.c
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
* Reserved. This file contains Original Code and/or Modifications of
* Original Code as defined in and that are subject to the Apple Public
* Source License Version 1.0 (the 'License'). You may not use this file
* except in compliance with the License. Please obtain a copy of the
* License at http://www.apple.com/publicsource and read it before using
* this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License."
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1988 University of Utah.
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Science Department.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: Utah $Hdr: vm_mmap.c 1.6 91/10/21$
*
* @(#)vm_mmap.c 8.10 (Berkeley) 2/19/95
*/
/*
* Mapped file (mmap) interface to VM
*/
#import <sys/param.h>
#import <sys/systm.h>
#import <sys/filedesc.h>
#import <sys/proc.h>
#import <sys/resourcevar.h>
#import <sys/buf.h>
#import <sys/vnode.h>
#import <sys/acct.h>
#import <sys/wait.h>
#import <sys/file.h>
#import <sys/vadvise.h>
#import <sys/trace.h>
#import <sys/mman.h>
#import <sys/conf.h>
#import <mach/kern_return.h>
#import <kern/task.h>
#import <mach/vm_param.h>
#import <vm/vm_map.h>
#import <vm/vm_pager.h>
#import <vm/vnode_pager.h>
#import <kern/mapfs.h>
#import <miscfs/specfs/specdev.h>
struct sbrk_args {
int incr;
};
/* ARGSUSED */
int
sbrk(p, uap, retval)
struct proc *p;
struct sbrk_args *uap;
register_t *retval;
{
/* Not yet implemented */
kprintf("sbrk(): not supported!\n");
return (EOPNOTSUPP);
}
struct sstk_args {
int incr;
} *uap;
/* ARGSUSED */
int
sstk(p, uap, retval)
struct proc *p;
struct sstk_args *uap;
register_t *retval;
{
/* Not yet implemented */
kprintf("sstk(): not supported!\n");
return (EOPNOTSUPP);
}
#if COMPAT_43
/* ARGSUSED */
int
ogetpagesize(p, uap, retval)
struct proc *p;
void *uap;
register_t *retval;
{
*retval = PAGE_SIZE;
return (0);
}
#endif /* COMPAT_43 */
struct osmmap_args {
caddr_t addr;
int len;
int prot;
int share;
int fd;
long pos;
};
osmmap(curp, uap, retval)
struct proc *curp;
register struct osmmap_args *uap;
register_t *retval;
{
struct mmap_args {
caddr_t addr;
size_t len;
int prot;
int flags;
int fd;
#ifdef DOUBLE_ALIGN_PARAMS
long pad;
#endif
off_t pos;
} newargs;
if ((uap->share == MAP_SHARED )|| (uap->share == MAP_PRIVATE )) {
newargs.addr = uap->addr;
newargs.len = (size_t)uap->len;
newargs.prot = uap->prot;
newargs.flags = uap->share;
newargs.fd = uap->fd;
newargs.pos = (off_t)uap->pos;
return(mmap(curp,&newargs, retval));
} else
return(EINVAL);
}
struct mmap_args {
caddr_t addr;
size_t len;
int prot;
int flags;
int fd;
#ifdef DOUBLE_ALIGN_PARAMS
long pad;
#endif
off_t pos;
};
mmap(p, uap, retval)
struct proc *p;
struct mmap_args *uap;
register_t *retval;
{
/*
* Map in special device (must be SHARED) or file
*/
struct file *fp;
register struct vnode *vp;
int flags;
int prot;
int err=0;
vm_map_t user_map;
kern_return_t result;
vm_offset_t user_addr;
vm_size_t user_size;
vm_offset_t file_pos;
boolean_t find_space;
extern vm_object_t vm_object_special();
user_addr = (vm_offset_t)uap->addr;
user_size = (vm_size_t) uap->len;
prot = (uap->prot & VM_PROT_ALL);
flags = uap->flags;
/*
* The vm code does not have prototypes & compileser doesn't do the'
* the right thing when you cast 64bit value and pass it in function
* call. So here it is.
*/
file_pos = (vm_offset_t)uap->pos;
/* Anonymous mapping not supported, for now. */
if (((flags & MAP_FIXED) && !page_aligned(user_addr)) ||
(user_size < 0) || (flags & MAP_ANON)) {
return(EINVAL);
}
err = fdgetf(p, uap->fd, &fp);
if (err)
return(err);
if (fp->f_type != DTYPE_VNODE)
return(EBADF);
vp = (struct vnode *)fp->f_data;
/*
* We bend a little - round the start and end addresses
* to the nearest page boundary.
*/
user_addr = trunc_page(user_addr);
user_size = round_page(user_size);
/*
* File can be COPIED at an arbitrary offset.
* File can only be SHARED if the offset is at a
* page boundary.
*/
if ((flags & MAP_SHARED) && ((vm_offset_t)file_pos & page_mask)) {
return(EINVAL);
}
/*
* File must be writable if memory will be.
*/
if ((prot & PROT_WRITE) && (fp->f_flag&FWRITE) == 0) {
if ((flags & MAP_PRIVATE) == 0) {
/* #2211802 */
/* If private map, its okay to have write access
as allocated space is both read and write
*/
return(EACCES);
}
}
if ((prot & PROT_READ) && (fp->f_flag&FREAD) == 0) {
return(EACCES);
}
/*
* Bug# 2203998 -> zero sized length is a valid arg
* in 4.4; for ex, copy of zero sized file
* will call mmap with length 0
*/
if (user_size == 0)
return(0);
/*
* memory must exist and be writable (even if we're
* just reading)
*/
user_map = current_task()->map;
#if 0
if (!vm_map_check_protection(user_map, user_addr,
(vm_offset_t)(user_addr + user_size),
VM_PROT_READ|VM_PROT_WRITE)) {
return(EINVAL);
}
#endif
if (flags & MAP_FIXED) {
find_space = FALSE;
} else {
find_space = TRUE;
}
if (vp->v_type == VCHR || vp->v_type == VSTR) {
return(EOPNOTSUPP);
}
else {
/*
* Map in a file. May be PRIVATE (copy-on-write)
* or SHARED (changes go back to file)
*/
vm_pager_t pager;
vm_map_t copy_map;
vm_offset_t off;
struct vm_info *vmp;
/*
* Only allow regular files for the moment.
*/
if (vp->v_type != VREG) {
return(EBADF);
}
pager = vnode_pager_setup(vp, FALSE, TRUE);
/*
* Set credentials:
* FIXME: if we're writing the file we need a way to
* ensure that someone doesn't replace our R/W creds
* with ones that only work for read.
*/
vmp = vp->v_vm_info;
if (vmp->cred == NULL) {
crhold(p->p_ucred);
vmp->cred = p->p_ucred;
}
/* Find space if not MAP_FIXED; otherwise validate */
/* This also fixes 2215605 */
result = vm_map_find(user_map, NULL, (vm_offset_t) 0,
&user_addr, user_size, find_space);
if (result != KERN_SUCCESS) {
goto out;
}
if (flags & MAP_SHARED) {
/*
* Map it directly, allowing modifications
* to go out to the inode.
*/
(void) vm_deallocate(user_map, user_addr, user_size);
result = vm_allocate_with_pager(user_map,
&user_addr, user_size, find_space,
pager,
(vm_offset_t)file_pos);
if (result != KERN_SUCCESS) {
goto out;
}
}
else {
#if 0
/* Moved to cover both shared and private cases */
result = vm_map_find(user_map, NULL, (vm_offset_t) 0,
&user_addr, user_size, find_space);
if (result != KERN_SUCCESS) {
goto out;
}
#endif /* 0 */
/*
* Copy-on-write of file. Map into private
* map, then copy into our address space.
*/
copy_map = vm_map_create(pmap_create(user_size),
0, user_size, TRUE);
off = 0;
result = vm_allocate_with_pager(copy_map,
&off, user_size, find_space,
pager,
(vm_offset_t)file_pos);
if (result != KERN_SUCCESS) {
vm_map_deallocate(copy_map);
goto out;
}
result = vm_map_copy(user_map, copy_map,
user_addr, user_size,
0, FALSE, FALSE);
if (result != KERN_SUCCESS) {
vm_map_deallocate(copy_map);
goto out;
}
vm_map_deallocate(copy_map);
}
}
/*
* Our memory defaults to read-write. If it shouldn't
* be readable, protect it.
*/
if ((prot & PROT_WRITE) == 0) {
result = vm_protect(user_map, user_addr, user_size,
FALSE, VM_PROT_READ);
if (result != KERN_SUCCESS) {
(void) vm_deallocate(user_map, user_addr, user_size);
goto out;
}
}
/*
* Shared memory is also shared with children
*/
/*
* HACK HACK HACK
* Since this memory CAN'T be made copy-on-write, and since
* its users fork, we must change its inheritance to SHARED.
* HACK HACK HACK
*/
if (flags & MAP_SHARED) {
result = vm_inherit(user_map, user_addr, user_size,
VM_INHERIT_SHARE);
if (result != KERN_SUCCESS) {
(void) vm_deallocate(user_map, user_addr, user_size);
goto out;
}
}
*fdflags(p, uap->fd) |= UF_MAPPED;
if (result == KERN_SUCCESS)
*retval = (register_t)user_addr;
/* FALL THROUGH */
out:
switch (result) {
case KERN_SUCCESS:
return (0);
case KERN_INVALID_ADDRESS:
case KERN_NO_SPACE:
return (ENOMEM);
case KERN_PROTECTION_FAILURE:
return (EACCES);
default:
return (EINVAL);
}
}
struct msync_args {
caddr_t addr;
int len;
};
int
msync(p, uap, retval)
struct proc *p;
struct msync_args *uap;
register_t *retval;
{
/* Not yet implemented */
return (EOPNOTSUPP);
}
mremap()
{
/* Not yet implemented */
return (EOPNOTSUPP);
}
struct munmap_args {
caddr_t addr;
int len;
};
munmap(p, uap, retval)
struct proc *p;
struct munmap_args *uap;
register_t *retval;
{
vm_offset_t user_addr;
vm_size_t user_size;
kern_return_t result;
user_addr = (vm_offset_t) uap->addr;
user_size = (vm_size_t) uap->len;
/*
* user size is allocated to page boundary and size from
* the user land is not necessarily in multiple of page size
*/
user_size = round_page(user_size);
if ((user_addr & page_mask) ||
(user_size & page_mask)) {
return(EINVAL);
}
result = vm_deallocate(current_task()->map, user_addr, user_size);
if (result != KERN_SUCCESS) {
return(EINVAL);
}
return(0);
}
void
munmapfd(p, fd)
struct proc *p;
int fd;
{
/*
* XXX should vm_deallocate any regions mapped to this file
*/
*fdflags(p, fd) &= ~UF_MAPPED;
}
struct mprotect_args {
caddr_t addr;
int len;
int prot;
};
int
mprotect(p, uap, retval)
struct proc *p;
struct mprotect_args *uap;
register_t *retval;
{
vm_size_t size;
register vm_prot_t prot;
caddr_t addr;
int len;
vm_map_t user_map;
addr = uap->addr;
len = uap->len;
prot = (vm_prot_t)(uap->prot & VM_PROT_ALL);
if (((unsigned long)addr & page_mask) || len < 0)
return(EINVAL);
size = (vm_size_t)len;
user_map = current_task()->map;
switch (vm_map_protect(user_map, addr, addr+size, prot,
FALSE)) {
case KERN_SUCCESS:
return (0);
case KERN_PROTECTION_FAILURE:
return (EACCES);
}
return (EINVAL);
}
struct madvise_args {
caddr_t addr;
int len;
int behav;
};
/* ARGSUSED */
int
madvise(p, uap, retval)
struct proc *p;
struct madvise_args *uap;
register_t *retval;
{
/* Not yet implemented */
return (EOPNOTSUPP);
}
struct mincore_args {
caddr_t addr;
int len;
char * vec;
};
/* ARGSUSED */
int
mincore(p, uap, retval)
struct proc *p;
struct mincore_args *uap;
register_t *retval;
{
/* Not yet implemented */
return (EOPNOTSUPP);
}
struct mlock_args {
caddr_t addr;
size_t len;
};
int
mlock(p, uap, retval)
struct proc *p;
struct mlock_args *uap;
register_t *retval;
{
/* Not yet implemented */
return (EOPNOTSUPP);
}
struct munlock_args {
caddr_t addr;
size_t len;
};
int
munlock(p, uap, retval)
struct proc *p;
struct munlock_args *uap;
register_t *retval;
{
/* Not yet implemented */
return (EOPNOTSUPP);
}
int
map_fd(fd, offset, va, findspace, size)
int fd;
vm_offset_t offset;
vm_offset_t *va;
boolean_t findspace;
vm_size_t size;
{
struct file *fp;
register struct vnode *vp;
vm_map_t user_map;
kern_return_t result;
vm_offset_t user_addr;
vm_size_t user_size;
vm_pager_t pager;
vm_map_t copy_map;
vm_offset_t off;
user_map = current_task()->map;
/*
* Find the inode; verify that it's a regular file.
*/
if (getvnode(current_proc(), fd, &fp))
return (KERN_INVALID_ARGUMENT);
vp = (struct vnode *)fp->f_data;
if (vp->v_type != VREG)
return(KERN_INVALID_ARGUMENT);
user_size = round_page(size);
if (findspace) {
/*
* Allocate dummy memory.
*/
result = vm_allocate(user_map, &user_addr, size, TRUE);
if (result != KERN_SUCCESS)
return(result);
if (copyout(&user_addr, va, sizeof(vm_offset_t))) {
(void) vm_deallocate(user_map, user_addr, size);
return(KERN_INVALID_ADDRESS);
}
}
else {
/*
* Get user's address, and verify that it's
* page-aligned and writable.
*/
if (copyin(va, &user_addr, sizeof(vm_offset_t)))
return(KERN_INVALID_ADDRESS);
if ((trunc_page(user_addr) != user_addr))
return(KERN_INVALID_ARGUMENT);
if (!vm_map_check_protection(user_map, user_addr,
(vm_offset_t)(user_addr + user_size),
VM_PROT_READ|VM_PROT_WRITE))
return(KERN_INVALID_ARGUMENT);
}
/*
* Allow user to map in a zero length file.
*/
if (size == 0)
return KERN_SUCCESS;
/*
* Map in the file.
*/
pager = vnode_pager_setup(vp, FALSE, TRUE);
/*
* Map into private map, then copy into our address space.
*/
copy_map = vm_map_create(pmap_create(user_size), 0, user_size, TRUE);
off = 0;
result = vm_allocate_with_pager(copy_map, &off, user_size, FALSE,
pager,
offset);
if (result == KERN_SUCCESS)
result = vm_map_copy(user_map, copy_map, user_addr, user_size,
0, FALSE, FALSE);
if ((result != KERN_SUCCESS) && findspace)
(void) vm_deallocate(user_map, user_addr, user_size);
vm_map_deallocate(copy_map);
/*
* Set credentials.
*/
if (vp->v_vm_info->cred == NULL) {
crhold(current_proc()->p_ucred);
vp->v_vm_info->cred = current_proc()->p_ucred;
}
return(result);
}
/* BEGIN DEFUNCT */
struct obreak_args {
char *nsiz;
};
obreak(p, uap, retval)
struct proc *p;
struct obreak_args *uap;
register_t *retval;
{
/* Not implemented, obsolete */
return (ENOMEM);
}
int both;
ovadvise()
{
#ifdef lint
both = 0;
#endif
}
/* END DEFUNCT */