uvm_mmap.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,306 行 · 第 1/2 页
C
1,306 行
/* $NetBSD: uvm_mmap.c,v 1.46 2000/11/27 08:40:04 chs Exp $ *//* * Copyright (c) 1997 Charles D. Cranor and Washington University. * Copyright (c) 1991, 1993 The Regents of the University of California. * Copyright (c) 1988 University of Utah. * * 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 Charles D. Cranor, * Washington University, 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.5 (Berkeley) 5/19/94 * from: Id: uvm_mmap.c,v 1.1.2.14 1998/01/05 21:04:26 chuck Exp *//* * uvm_mmap.c: system call interface into VM system, plus kernel vm_mmap * function. */#include <sys/param.h>#include <sys/systm.h>#include <sys/file.h>#include <sys/filedesc.h>#include <sys/resourcevar.h>#include <sys/mman.h>#include <sys/mount.h>#include <sys/proc.h>#include <sys/malloc.h>#include <sys/vnode.h>#include <sys/conf.h>#include <sys/stat.h>#include <miscfs/specfs/specdev.h>#include <sys/syscallargs.h>#include <uvm/uvm.h>#include <uvm/uvm_device.h>#include <uvm/uvm_vnode.h>#ifdef OSKIT#include "oskit_uvm_internal.h"#endif#ifndef OSKIT/* * unimplemented VM system calls: *//* * sys_sbrk: sbrk system call. *//* ARGSUSED */intsys_sbrk(p, v, retval) struct proc *p; void *v; register_t *retval;{#if 0 struct sys_sbrk_args /* { syscallarg(intptr_t) incr; } */ *uap = v;#endif return (ENOSYS);}/* * sys_sstk: sstk system call. *//* ARGSUSED */intsys_sstk(p, v, retval) struct proc *p; void *v; register_t *retval;{#if 0 struct sys_sstk_args /* { syscallarg(int) incr; } */ *uap = v;#endif return (ENOSYS);}/* * sys_mincore: determine if pages are in core or not. *//* ARGSUSED */intsys_mincore(p, v, retval) struct proc *p; void *v; register_t *retval;{ struct sys_mincore_args /* { syscallarg(void *) addr; syscallarg(size_t) len; syscallarg(char *) vec; } */ *uap = v; vm_page_t m; char *vec, pgi; struct uvm_object *uobj; struct vm_amap *amap; struct vm_anon *anon; vm_map_entry_t entry; vaddr_t start, end, lim; vm_map_t map; vsize_t len; int error = 0, npgs; map = &p->p_vmspace->vm_map; start = (vaddr_t)SCARG(uap, addr); len = SCARG(uap, len); vec = SCARG(uap, vec); if (start & PAGE_MASK) return (EINVAL); len = round_page(len); end = start + len; if (end <= start) return (EINVAL); npgs = len >> PAGE_SHIFT; if (uvm_useracc(vec, npgs, B_WRITE) == FALSE) return (EFAULT); /* * Lock down vec, so our returned status isn't outdated by * storing the status byte for a page. */ uvm_vslock(p, vec, npgs, VM_PROT_WRITE); vm_map_lock_read(map); if (uvm_map_lookup_entry(map, start, &entry) == FALSE) { error = ENOMEM; goto out; } for (/* nothing */; entry != &map->header && entry->start < end; entry = entry->next) {#ifdef DIAGNOSTIC if (UVM_ET_ISSUBMAP(entry)) panic("mincore: user map has submap"); if (start < entry->start) panic("mincore: hole");#endif /* Make sure there are no holes. */ if (entry->end < end && (entry->next == &map->header || entry->next->start > entry->end)) { error = ENOMEM; goto out; } lim = end < entry->end ? end : entry->end; /* * Special case for objects with no "real" pages. Those * are always considered resident (mapped devices). */ if (UVM_ET_ISOBJ(entry)) {#ifdef DIAGNOSTIC if (UVM_OBJ_IS_KERN_OBJECT(entry->object.uvm_obj)) panic("mincore: user map has kernel object");#endif if (entry->object.uvm_obj->pgops->pgo_releasepg == NULL) { for (/* nothing */; start < lim; start += PAGE_SIZE, vec++) subyte(vec, 1); continue; } } amap = entry->aref.ar_amap; /* top layer */ uobj = entry->object.uvm_obj; /* bottom layer */ if (amap != NULL) amap_lock(amap); if (uobj != NULL) simple_lock(&uobj->vmobjlock); for (/* nothing */; start < lim; start += PAGE_SIZE, vec++) { pgi = 0; if (amap != NULL) { /* Check the top layer first. */ anon = amap_lookup(&entry->aref, start - entry->start); /* Don't need to lock anon here. */ if (anon != NULL && anon->u.an_page != NULL) { /* * Anon has the page for this entry * offset. */ pgi = 1; } } if (uobj != NULL && pgi == 0) { /* Check the bottom layer. */ m = uvm_pagelookup(uobj, entry->offset + (start - entry->start)); if (m != NULL) { /* * Object has the page for this entry * offset. */ pgi = 1; } } (void) subyte(vec, pgi); } if (uobj != NULL) simple_unlock(&uobj->vmobjlock); if (amap != NULL) amap_unlock(amap); } out: vm_map_unlock_read(map); uvm_vsunlock(p, SCARG(uap, vec), npgs); return (error);}#endif /*OSKIT*//* * sys_mmap: mmap system call. * * => file offest and address may not be page aligned * - if MAP_FIXED, offset and address must have remainder mod PAGE_SIZE * - if address isn't page aligned the mapping starts at trunc_page(addr) * and the return value is adjusted up by the page offset. */intsys_mmap(p, v, retval) struct proc *p; void *v; register_t *retval;{ struct sys_mmap_args /* { syscallarg(caddr_t) addr; syscallarg(size_t) len; syscallarg(int) prot; syscallarg(int) flags; syscallarg(int) fd; syscallarg(long) pad; syscallarg(off_t) pos; } */ *uap = v; vaddr_t addr;#ifndef OSKIT struct vattr va;#endif off_t pos; vsize_t size, pageoff; vm_prot_t prot, maxprot;#ifdef OSKIT int flags;#else int flags, fd;#endif vaddr_t vm_min_address = VM_MIN_ADDRESS;#ifndef OSKIT struct filedesc *fdp = p->p_fd; struct file *fp; struct vnode *vp;#else struct file xfp; struct file *fp = &xfp; struct vnode xvp; struct vnode *vp = &xvp;#endif caddr_t handle; int error;#ifdef OSKIT oskit_iunknown_t *iunknown; vp->v_type = VREG; fp->f_flag = FREAD|FWRITE;#endif /* * first, extract syscall args from the uap. */ addr = (vaddr_t) SCARG(uap, addr); size = (vsize_t) SCARG(uap, len); prot = SCARG(uap, prot) & VM_PROT_ALL; flags = SCARG(uap, flags);#ifdef OSKIT iunknown = (oskit_iunknown_t*)SCARG(uap, fd);#else fd = SCARG(uap, fd);#endif pos = SCARG(uap, pos); /* * Fixup the old deprecated MAP_COPY into MAP_PRIVATE, and * validate the flags. */ if (flags & MAP_COPY) flags = (flags & ~MAP_COPY) | MAP_PRIVATE; if ((flags & (MAP_SHARED|MAP_PRIVATE)) == (MAP_SHARED|MAP_PRIVATE)) return (EINVAL); /* * align file position and save offset. adjust size. */ pageoff = (pos & PAGE_MASK); pos -= pageoff; size += pageoff; /* add offset */ size = (vsize_t) round_page(size); /* round up */ if ((ssize_t) size < 0) return (EINVAL); /* don't allow wrap */ /* * now check (MAP_FIXED) or get (!MAP_FIXED) the "addr" */ if (flags & MAP_FIXED) { /* ensure address and file offset are aligned properly */ addr -= pageoff; if (addr & PAGE_MASK) return (EINVAL); if (VM_MAXUSER_ADDRESS > 0 && (addr + size) > VM_MAXUSER_ADDRESS) return (EINVAL); if (vm_min_address > 0 && addr < vm_min_address) return (EINVAL); if (addr > addr + size) return (EINVAL); /* no wrapping! */ } else { /* * not fixed: make sure we skip over the largest possible heap. * we will refine our guess later (e.g. to account for VAC, etc) */ if (addr < round_page((vaddr_t)p->p_vmspace->vm_daddr + MAXDSIZ)) addr = round_page((vaddr_t)p->p_vmspace->vm_daddr + MAXDSIZ); } /* * check for file mappings (i.e. not anonymous) and verify file. */ if ((flags & MAP_ANON) == 0) {#ifndef OSKIT if (fd < 0 || fd >= fdp->fd_nfiles) return(EBADF); /* failed range check? */ fp = fdp->fd_ofiles[fd]; /* convert to file pointer */ if (fp == NULL) return(EBADF); if (fp->f_type != DTYPE_VNODE) return (ENODEV); /* only mmap vnodes! */ vp = (struct vnode *)fp->f_data; /* convert to vnode */ if (vp->v_type != VREG && vp->v_type != VCHR && vp->v_type != VBLK) return (ENODEV); /* only REG/CHR/BLK support mmap */ if (vp->v_type == VREG && (pos + size) < pos) return (EOVERFLOW); /* no offset wrapping */ /* special case: catch SunOS style /dev/zero */ if (vp->v_type == VCHR && iszerodev(vp->v_rdev)) { flags |= MAP_ANON; goto is_anon; }#endif /*OSKIT*/ /* * Old programs may not select a specific sharing type, so * default to an appropriate one. * * XXX: how does MAP_ANON fit in the picture? */ if ((flags & (MAP_SHARED|MAP_PRIVATE)) == 0) {#if defined(DEBUG) printf("WARNING: defaulted mmap() share type to " "%s (pid %d comm %s)\n", vp->v_type == VCHR ? "MAP_SHARED" : "MAP_PRIVATE", p->p_pid, p->p_comm);#endif if (vp->v_type == VCHR) flags |= MAP_SHARED; /* for a device */ else flags |= MAP_PRIVATE; /* for a file */ } /* * MAP_PRIVATE device mappings don't make sense (and aren't * supported anyway). However, some programs rely on this, * so just change it to MAP_SHARED. */ if (vp->v_type == VCHR && (flags & MAP_PRIVATE) != 0) {#if defined(DIAGNOSTIC) printf("WARNING: converted MAP_PRIVATE device mapping " "to MAP_SHARED (pid %d comm %s)\n", p->p_pid, p->p_comm);#endif flags = (flags & ~MAP_PRIVATE) | MAP_SHARED; } /* * now check protection */ maxprot = VM_PROT_EXECUTE; /* check read access */ if (fp->f_flag & FREAD) maxprot |= VM_PROT_READ; else if (prot & PROT_READ) return (EACCES); /* check write access, shared case first */ if (flags & MAP_SHARED) { /* * if the file is writable, only add PROT_WRITE to * maxprot if the file is not immutable, append-only. * otherwise, if we have asked for PROT_WRITE, return * EPERM. */ if (fp->f_flag & FWRITE) {#ifdef OSKIT maxprot |= VM_PROT_WRITE;#else if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p))) return (error); if ((va.va_flags & (IMMUTABLE|APPEND)) == 0) maxprot |= VM_PROT_WRITE; else if (prot & PROT_WRITE) return (EPERM);#endif } else if (prot & PROT_WRITE) return (EACCES); } else { /* MAP_PRIVATE mappings can always write to */ maxprot |= VM_PROT_WRITE; }#ifdef OSKIT handle = (caddr_t)iunknown;#else /* * set handle to vnode */ handle = (caddr_t)vp;#endif } else { /* MAP_ANON case */ /* * XXX What do we do about (MAP_SHARED|MAP_PRIVATE) == 0? */#ifndef OSKIT if (fd != -1) return (EINVAL); is_anon: /* label for SunOS style /dev/zero */#endif handle = NULL; maxprot = VM_PROT_ALL; pos = 0; } /* * XXX (in)sanity check. We don't do proper datasize checking * XXX for anonymous (or private writable) mmap(). However, * XXX know that if we're trying to allocate more than the amount * XXX remaining under our current data size limit, _that_ should * XXX be disallowed. */ if ((flags & MAP_ANON) != 0 || ((flags & MAP_PRIVATE) != 0 && (prot & PROT_WRITE) != 0)) { if (size > (p->p_rlimit[RLIMIT_DATA].rlim_cur - ctob(p->p_vmspace->vm_dsize))) { return (ENOMEM); } } /* * now let kernel internal function uvm_mmap do the work. */ error = uvm_mmap(&p->p_vmspace->vm_map, &addr, size, prot, maxprot, flags, handle, pos, p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur); if (error == 0) /* remember to add offset */ *retval = (register_t)(addr + pageoff); return (error);}/* * sys___msync13: the msync system call (a front-end for flush) */intsys___msync13(p, v, retval) struct proc *p; void *v; register_t *retval;{ struct sys___msync13_args /* { syscallarg(caddr_t) addr; syscallarg(size_t) len; syscallarg(int) flags; } */ *uap = v; vaddr_t addr; vsize_t size, pageoff; vm_map_t map; int rv, flags, uvmflags; /* * extract syscall args from the uap */ addr = (vaddr_t)SCARG(uap, addr); size = (vsize_t)SCARG(uap, len); flags = SCARG(uap, flags); /* sanity check flags */ if ((flags & ~(MS_ASYNC | MS_SYNC | MS_INVALIDATE)) != 0 || (flags & (MS_ASYNC | MS_SYNC | MS_INVALIDATE)) == 0 || (flags & (MS_ASYNC | MS_SYNC)) == (MS_ASYNC | MS_SYNC)) return (EINVAL); if ((flags & (MS_ASYNC | MS_SYNC)) == 0) flags |= MS_SYNC; /* * align the address to a page boundary, and adjust the size accordingly */ pageoff = (addr & PAGE_MASK); addr -= pageoff; size += pageoff; size = (vsize_t) round_page(size); /* disallow wrap-around. */ if (addr + size < addr) return (EINVAL); /* * get map */ map = &p->p_vmspace->vm_map; /* * XXXCDC: do we really need this semantic? * * XXX Gak! If size is zero we are supposed to sync "all modified * pages with the region containing addr". Unfortunately, we * don't really keep track of individual mmaps so we approximate * by flushing the range of the map entry containing addr. * This can be incorrect if the region splits or is coalesced * with a neighbor. */ if (size == 0) { vm_map_entry_t entry; vm_map_lock_read(map); rv = uvm_map_lookup_entry(map, addr, &entry); if (rv == TRUE) { addr = entry->start; size = entry->end - entry->start; } vm_map_unlock_read(map); if (rv == FALSE) return (EINVAL); } /* * translate MS_ flags into PGO_ flags */ uvmflags = PGO_CLEANIT; if (flags & MS_INVALIDATE) uvmflags |= PGO_FREE; if (flags & MS_SYNC) uvmflags |= PGO_SYNCIO; else uvmflags |= PGO_SYNCIO; /* XXXCDC: force sync for now! */ /* * doit! */ rv = uvm_map_clean(map, addr, addr+size, uvmflags); /* * and return... */ switch (rv) { case KERN_SUCCESS: return(0); case KERN_INVALID_ADDRESS: return (ENOMEM); case KERN_FAILURE: return (EIO);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?