diff options
Diffstat (limited to 'sys/vm/vm_map.c')
-rw-r--r-- | sys/vm/vm_map.c | 124 |
1 files changed, 97 insertions, 27 deletions
diff --git a/sys/vm/vm_map.c b/sys/vm/vm_map.c index b56e896..26effdb 100644 --- a/sys/vm/vm_map.c +++ b/sys/vm/vm_map.c @@ -35,8 +35,10 @@ #include <sys/syscall.h> #include <sys/syslog.h> #include <sys/mman.h> +#include <sys/filedesc.h> #include <vm/dynalloc.h> #include <vm/vm_pager.h> +#include <vm/vm_device.h> #include <vm/pmap.h> #include <vm/map.h> #include <vm/vm.h> @@ -157,51 +159,113 @@ vm_map_modify(struct vas vas, vaddr_t va, paddr_t pa, vm_prot_t prot, bool unmap * crashes. */ void * -mmap_at(void *addr, size_t len, int prot, int flags, int fildes, off_t off) +mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off) { - struct vm_object *map_obj; + struct vm_object *map_obj = NULL; + struct cdevsw *cdevp; struct vm_page *pg; struct mmap_entry *ep; + struct vnode *vp; + struct filedesc *fdp; struct proc *td; struct vas vas; int error, npgs; paddr_t pa; vaddr_t va; size_t misalign; + off_t page_off; misalign = len & (DEFAULT_PAGESIZE - 1); len = ALIGN_UP(len + misalign, DEFAULT_PAGESIZE); npgs = len / DEFAULT_PAGESIZE; - - if (addr == NULL) { - pr_error("mmap: NULL addr not supported\n"); - return NULL; - } + vas = pmap_read_vas(); /* Validate flags */ - if (ISSET(flags, MAP_FIXED | MAP_SHARED)) { - pr_error("mmap: fixed/shared mappings not yet supported\n"); + if (ISSET(flags, MAP_FIXED)) { + pr_error("mmap: fixed mappings not yet supported\n"); mmap_dbg(addr, len, prot, flags, fildes, off); return NULL; } - map_obj = dynalloc(sizeof(*map_obj)); - if (map_obj == NULL) { - kprintf("mmap: failed to allocate map object\n"); - return NULL; + + /* + * Attempt to open the file if mapping + * is shared. + */ + if (ISSET(flags, MAP_SHARED)) { + fdp = fd_get(fildes); + if (fdp == NULL) { + pr_error("mmap: no such fd (fd=%d)\n", fildes); + return NULL; + } + + vp = fdp->vp; + if (vp->type != VCHR) { + /* TODO */ + pr_error("mmap: only device files supported\n"); + return NULL; + } + + map_obj = dv_attach(vp->major, vp->dev, prot); + if (map_obj == NULL) { + kprintf("mmap: dv_attach() failure\n"); + return NULL; + } + + cdevp = map_obj->data; + if ((pa = cdevp->mmap(vp->dev, len, off, 0)) == 0) { + kprintf("mmap: dev mmap() gave 0\n"); + return NULL; + } + + /* + * If the address passed is NULL, just identity + * map everything. + * + * XXX: This is why the bounds check done in the + * cdev mmap() *must* be correct. + * + * TODO: Use copy-on-write for this instead. Since mapping + * certain devices may required a lot of memory to + * be referenced anyways, we could use a buffered + * copy-on-write technique where only a window of + * pages can be mapped on-demand and other pages + * freed when that window is exceeded. + */ + if (addr == NULL) { + addr = (void *)pa; + } + + va = ALIGN_DOWN((vaddr_t)addr, DEFAULT_PAGESIZE); + error = vm_map(vas, va, pa, prot, len); + if (error != 0) { + kprintf("mmap: map failed (error=%d)\n", error); + return NULL; + } + + goto done; } - error = vm_obj_init(map_obj, &vm_anonops, 1); - if (error < 0) { - kprintf("mmap: vm_obj_init() returned %d\n", error); - kprintf("mmap: failed to init object\n"); - return NULL; + + /* Only allocate new obj if needed */ + if (map_obj == NULL) { + map_obj = dynalloc(sizeof(*map_obj)); + if (map_obj == NULL) { + kprintf("mmap: failed to allocate map object\n"); + return NULL; + } + error = vm_obj_init(map_obj, &vm_anonops, 1); + if (error < 0) { + kprintf("mmap: vm_obj_init() returned %d\n", error); + kprintf("mmap: failed to init object\n"); + return NULL; + } } /* XXX: Assuming private */ - vas = pmap_read_vas(); va = ALIGN_DOWN((vaddr_t)addr, DEFAULT_PAGESIZE); for (int i = 0; i < npgs; ++i) { pg = vm_pagealloc(map_obj, PALLOC_ZERO); + page_off = i * DEFAULT_PAGESIZE; if (pg == NULL) { /* TODO */ @@ -209,15 +273,21 @@ mmap_at(void *addr, size_t len, int prot, int flags, int fildes, off_t off) return NULL; } + /* TODO: copy-on-write */ + if (addr == NULL) { + va = pg->phys_addr; + addr = (void *)va; + } + pa = pg->phys_addr; - error = vm_map(vas, va, pa, prot, len); - pr_trace("va=%p, len=%d\n", va, len); + error = vm_map(vas, va + page_off, pa, prot, len); if (error < 0) { pr_error("mmap: failed to map page (retval=%x)\n", error); return NULL; } } +done: /* Add entry to ledger */ td = this_td(); ep = dynalloc(sizeof(*ep)); @@ -243,7 +313,7 @@ mmap_at(void *addr, size_t len, int prot, int flags, int fildes, off_t off) * multiple of the machine page size. */ int -munmap_at(void *addr, size_t len) +munmap(void *addr, size_t len) { int pgno; vaddr_t va; @@ -299,7 +369,7 @@ munmap_at(void *addr, size_t len) * arg5 -> off */ scret_t -mmap(struct syscall_args *scargs) +sys_mmap(struct syscall_args *scargs) { void *addr; size_t len; @@ -308,11 +378,11 @@ mmap(struct syscall_args *scargs) addr = (void *)scargs->arg0; len = scargs->arg1; - prot = scargs->arg2; + prot = scargs->arg2 | PROT_USER; flags = scargs->arg3; fildes = scargs->arg4; off = scargs->arg5; - return (scret_t)mmap_at(addr, len, prot, flags, fildes, off); + return (scret_t)mmap(addr, len, prot, flags, fildes, off); } /* @@ -322,14 +392,14 @@ mmap(struct syscall_args *scargs) * arg1 -> len */ scret_t -munmap(struct syscall_args *scargs) +sys_munmap(struct syscall_args *scargs) { void *addr; size_t len; addr = (void *)scargs->arg0; len = scargs->arg1; - return (scret_t)munmap_at(addr, len); + return (scret_t)munmap(addr, len); } /* |