/* * minherit system call handler * * minherit_args(void *addr, size_t len, int inherit) * * No requirements. */ int sys_minherit(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) /* wrap */ return(EINVAL); tmpaddr = addr + size; /* workaround gcc4 opt */ if (tmpaddr < addr) /* wrap */ 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); }
DECLHIDDEN(int) rtR0MemObjNativeMapUser(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, RTR3PTR R3PtrFixed, size_t uAlignment, unsigned fProt, RTR0PROCESS R0Process) { /* * Check for unsupported stuff. */ AssertMsgReturn(R0Process == RTR0ProcHandleSelf(), ("%p != %p\n", R0Process, RTR0ProcHandleSelf()), VERR_NOT_SUPPORTED); if (uAlignment > PAGE_SIZE) return VERR_NOT_SUPPORTED; int rc; PRTR0MEMOBJFREEBSD pMemToMapFreeBSD = (PRTR0MEMOBJFREEBSD)pMemToMap; struct proc *pProc = (struct proc *)R0Process; struct vm_map *pProcMap = &pProc->p_vmspace->vm_map; /* calc protection */ vm_prot_t ProtectionFlags = 0; if ((fProt & RTMEM_PROT_NONE) == RTMEM_PROT_NONE) ProtectionFlags = VM_PROT_NONE; if ((fProt & RTMEM_PROT_READ) == RTMEM_PROT_READ) ProtectionFlags |= VM_PROT_READ; if ((fProt & RTMEM_PROT_WRITE) == RTMEM_PROT_WRITE) ProtectionFlags |= VM_PROT_WRITE; if ((fProt & RTMEM_PROT_EXEC) == RTMEM_PROT_EXEC) ProtectionFlags |= VM_PROT_EXECUTE; /* calc mapping address */ vm_offset_t AddrR3; if (R3PtrFixed == (RTR3PTR)-1) { /** @todo: is this needed?. */ PROC_LOCK(pProc); AddrR3 = round_page((vm_offset_t)pProc->p_vmspace->vm_daddr + lim_max(pProc, RLIMIT_DATA)); PROC_UNLOCK(pProc); } else AddrR3 = (vm_offset_t)R3PtrFixed; /* Insert the pObject in the map. */ vm_object_reference(pMemToMapFreeBSD->pObject); rc = vm_map_find(pProcMap, /* Map to insert the object in */ pMemToMapFreeBSD->pObject, /* Object to map */ 0, /* Start offset in the object */ &AddrR3, /* Start address IN/OUT */ pMemToMap->cb, /* Size of the mapping */ #if __FreeBSD_version >= 1000055 0, /* Upper bound of the mapping */ #endif R3PtrFixed == (RTR3PTR)-1 ? VMFS_ANY_SPACE : VMFS_NO_SPACE, /* Whether a suitable address should be searched for first */ ProtectionFlags, /* protection flags */ VM_PROT_ALL, /* Maximum protection flags */ 0); /* copy-on-write and similar flags */ if (rc == KERN_SUCCESS) { rc = vm_map_wire(pProcMap, AddrR3, AddrR3 + pMemToMap->cb, VM_MAP_WIRE_USER|VM_MAP_WIRE_NOHOLES); AssertMsg(rc == KERN_SUCCESS, ("%#x\n", rc)); rc = vm_map_inherit(pProcMap, AddrR3, AddrR3 + pMemToMap->cb, VM_INHERIT_SHARE); AssertMsg(rc == KERN_SUCCESS, ("%#x\n", rc)); /* * Create a mapping object for it. */ PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(RTR0MEMOBJFREEBSD), RTR0MEMOBJTYPE_MAPPING, (void *)AddrR3, pMemToMap->cb); if (pMemFreeBSD) { Assert((vm_offset_t)pMemFreeBSD->Core.pv == AddrR3); pMemFreeBSD->Core.u.Mapping.R0Process = R0Process; *ppMem = &pMemFreeBSD->Core; return VINF_SUCCESS; } rc = vm_map_remove(pProcMap, AddrR3, AddrR3 + pMemToMap->cb); AssertMsg(rc == KERN_SUCCESS, ("Deleting mapping failed\n")); } else vm_object_deallocate(pMemToMapFreeBSD->pObject); return VERR_NO_MEMORY; }
/* * Internal version of mmap. * Currently used by mmap, exec, and sys5 shared memory. * Handle is either a vnode pointer or NULL for MAP_ANON. * * No requirements */ 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) { boolean_t fitit; vm_object_t object; vm_offset_t eaddr; vm_size_t esize; vm_size_t align; int (*uksmap)(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); /* * XXX messy code, fixme * * NOTE: Overflow checks require discrete statements or GCC4 * will optimize it out. */ if ((p = curproc) != NULL && map == &p->p_vmspace->vm_map) { esize = map->size + size; /* workaround gcc4 opt */ if (esize < map->size || esize > p->p_rlimit[RLIMIT_VMEM].rlim_cur) { lwkt_reltoken(&map->token); return(ENOMEM); } } /* * We currently can only deal with page aligned file offsets. * The check is here rather than in the syscall because the * kernel calls this function internally for other mmaping * operations (such as in exec) and non-aligned offsets will * cause pmap inconsistencies...so we want to be sure to * disallow this in all cases. * * NOTE: Overflow checks require discrete statements or GCC4 * will optimize it out. */ if (foff & PAGE_MASK) { lwkt_reltoken(&map->token); return (EINVAL); } /* * Handle alignment. For large memory maps it is possible * that the MMU can optimize the page table so align anything * that is a multiple of SEG_SIZE to SEG_SIZE. * * Also align any large mapping (bigger than 16x SG_SIZE) to a * SEG_SIZE address boundary. */ 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; /* * Lookup/allocate object. */ if (flags & MAP_ANON) { /* * Unnamed anonymous regions always start at 0. */ if (handle) { /* * Default memory object */ object = default_pager_alloc(handle, objsize, prot, foff); if (object == NULL) { lwkt_reltoken(&map->token); return(ENOMEM); } docow = MAP_PREFAULT_PARTIAL; } else { /* * Implicit single instance of a default memory * object, so we don't need a VM object yet. */ foff = 0; object = NULL; docow = 0; } vp = NULL; } else { vp = (struct vnode *)handle; /* * Non-anonymous mappings of VCHR (aka not /dev/zero) * cannot specify MAP_STACK or MAP_VPAGETABLE. */ if (vp->v_type == VCHR) { if (flags & (MAP_STACK | MAP_VPAGETABLE)) { lwkt_reltoken(&map->token); return(EINVAL); } } if (vp->v_type == VCHR && vp->v_rdev->si_ops->d_uksmap) { /* * Device mappings without a VM object, typically * sharing permanently allocated kernel memory or * process-context-specific (per-process) data. * * Force them to be shared. */ uksmap = vp->v_rdev->si_ops->d_uksmap; object = NULL; docow = MAP_PREFAULT_PARTIAL; flags &= ~(MAP_PRIVATE|MAP_COPY); flags |= MAP_SHARED; } else if (vp->v_type == VCHR) { /* * Device mappings (device size unknown?). * Force them to be shared. */ error = dev_dmmap_single(vp->v_rdev, &foff, objsize, &object, prot, NULL); 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 = MAP_PREFAULT_PARTIAL; flags &= ~(MAP_PRIVATE|MAP_COPY); flags |= MAP_SHARED; } else { /* * Regular file mapping (typically). The attribute * check is for the link count test only. mmapable * vnodes must already have a VM object assigned. */ struct vattr vat; int error; error = VOP_GETATTR(vp, &vat); if (error) { lwkt_reltoken(&map->token); return (error); } docow = MAP_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 it is a regular file without any references * we do not need to sync it. */ if (vp->v_type == VREG && vat.va_nlink == 0) { flags |= MAP_NOSYNC; } } } /* * Deal with the adjusted flags */ if ((flags & (MAP_ANON|MAP_SHARED)) == 0) docow |= MAP_COPY_ON_WRITE; if (flags & MAP_NOSYNC) docow |= MAP_DISABLE_SYNCER; if (flags & MAP_NOCORE) docow |= MAP_DISABLE_COREDUMP; #if defined(VM_PROT_READ_IS_EXEC) if (prot & VM_PROT_READ) prot |= VM_PROT_EXECUTE; if (maxprot & VM_PROT_READ) maxprot |= VM_PROT_EXECUTE; #endif /* * This may place the area in its own page directory if (size) is * large enough, otherwise it typically returns its argument. * * (object can be NULL) */ if (fitit) { *addr = pmap_addr_hint(object, *addr, size); } /* * Stack mappings need special attention. * * Mappings that use virtual page tables will default to storing * the page table at offset 0. */ if (uksmap) { rv = vm_map_find(map, uksmap, vp->v_rdev, foff, addr, size, align, fitit, VM_MAPTYPE_UKSMAP, prot, maxprot, docow); } else if (flags & MAP_STACK) { rv = vm_map_stack(map, *addr, size, flags, prot, maxprot, docow); } else if (flags & MAP_VPAGETABLE) { rv = vm_map_find(map, object, NULL, foff, addr, size, align, fitit, VM_MAPTYPE_VPAGETABLE, prot, maxprot, docow); } else { rv = vm_map_find(map, object, NULL, foff, addr, size, align, fitit, VM_MAPTYPE_NORMAL, prot, maxprot, docow); } if (rv != KERN_SUCCESS) { /* * Lose the object reference. Will destroy the * object if it's an unnamed anonymous mapping * or named anonymous without other references. * * (NOTE: object can be NULL) */ vm_object_deallocate(object); goto out; } /* * Shared memory is also shared with children. */ 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 a process has marked all future mappings for wiring, do so */ if ((rv == KERN_SUCCESS) && (map->flags & MAP_WIREFUTURE)) vm_map_unwire(map, *addr, *addr + size, FALSE); /* * Set the access time on the vnode */ 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); } }
/** * Worker for the two virtual address space reservers. * * We're leaning on the examples provided by mmap and vm_mmap in vm_mmap.c here. */ static int rtR0MemObjNativeReserveInMap(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment, RTR0PROCESS R0Process, vm_map_t pMap) { int rc; /* * The pvFixed address range must be within the VM space when specified. */ if ( pvFixed != (void *)-1 && ( (vm_offset_t)pvFixed < vm_map_min(pMap) || (vm_offset_t)pvFixed + cb > vm_map_max(pMap))) return VERR_INVALID_PARAMETER; /* * Check that the specified alignment is supported. */ if (uAlignment > PAGE_SIZE) return VERR_NOT_SUPPORTED; /* * Create the object. */ PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), RTR0MEMOBJTYPE_RES_VIRT, NULL, cb); if (!pMemFreeBSD) return VERR_NO_MEMORY; vm_offset_t MapAddress = pvFixed != (void *)-1 ? (vm_offset_t)pvFixed : vm_map_min(pMap); if (pvFixed != (void *)-1) vm_map_remove(pMap, MapAddress, MapAddress + cb); rc = vm_map_find(pMap, /* map */ NULL, /* object */ 0, /* offset */ &MapAddress, /* addr (IN/OUT) */ cb, /* length */ #if __FreeBSD_version >= 1000055 0, /* max addr */ #endif pvFixed == (void *)-1 ? VMFS_ANY_SPACE : VMFS_NO_SPACE, /* find_space */ VM_PROT_NONE, /* protection */ VM_PROT_ALL, /* max(_prot) ?? */ 0); /* cow (copy-on-write) */ if (rc == KERN_SUCCESS) { if (R0Process != NIL_RTR0PROCESS) { rc = vm_map_inherit(pMap, MapAddress, MapAddress + cb, VM_INHERIT_SHARE); AssertMsg(rc == KERN_SUCCESS, ("%#x\n", rc)); } pMemFreeBSD->Core.pv = (void *)MapAddress; pMemFreeBSD->Core.u.ResVirt.R0Process = R0Process; *ppMem = &pMemFreeBSD->Core; return VINF_SUCCESS; } rc = VERR_NO_MEMORY; /** @todo fix translation (borrow from darwin) */ rtR0MemObjDelete(&pMemFreeBSD->Core); return rc; }