diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c --- a/sys/compat/linux/linux_misc.c +++ b/sys/compat/linux/linux_misc.c @@ -62,6 +62,7 @@ #include #include +#include #include #include @@ -324,6 +325,14 @@ args->old_len = round_page(args->old_len); if (args->new_len > args->old_len) { + vm_offset_t new_addr = args->addr; + + if (vm_mremap(td, &new_addr, args->new_len, args->addr, + args->old_len, args->flags) == KERN_SUCCESS) { + td->td_retval[0] = (uintptr_t)new_addr; + return 0; + } + td->td_retval[0] = 0; return (ENOMEM); } diff --git a/sys/vm/vm_map.h b/sys/vm/vm_map.h --- a/sys/vm/vm_map.h +++ b/sys/vm/vm_map.h @@ -534,5 +534,7 @@ int flags); long vmspace_swap_count(struct vmspace *vmspace); void vm_map_entry_set_vnode_text(vm_map_entry_t entry, bool add); +int vm_mremap(struct thread *td, vm_offset_t *naddr, + size_t nsize, uintptr_t oaddr, size_t osize, int flags); #endif /* _KERNEL */ #endif /* _VM_MAP_ */ diff --git a/sys/vm/vm_mmap.c b/sys/vm/vm_mmap.c --- a/sys/vm/vm_mmap.c +++ b/sys/vm/vm_mmap.c @@ -94,6 +94,8 @@ #include #include +#include + #ifdef HWPMC_HOOKS #include #endif @@ -1678,3 +1680,74 @@ return (EINVAL); } } + +int +vm_mremap(struct thread *td, vm_offset_t *naddr, + size_t nsize, uintptr_t oaddr, size_t osize, int flags) +{ + struct vmspace *vms; + vm_map_entry_t entry; + vm_offset_t kva, ofs; + vm_object_t obj; + vm_pindex_t pindex; + vm_prot_t prot; + boolean_t wired; + vm_map_t map; + size_t size; + int rv, vnode; + + vms = td->td_proc->p_vmspace; + + kva = (vm_offset_t)oaddr; + ofs = kva & PAGE_MASK; + kva = trunc_page(kva); + size = round_page(osize + ofs); + map = &vms->vm_map; + + rv = vm_map_lookup(&map, kva, VM_PROT_READ | VM_PROT_WRITE, &entry, + &obj, &pindex, &prot, &wired); + + if (rv != KERN_SUCCESS) + goto out; + + vm_map_lookup_done(map, entry); + + vnode = (obj->backing_object || obj->handle + || (obj->flags & OBJ_TMPFS)); + + if (vnode) { + vm_object_reference(obj); + rv = vm_mmap_object(map, naddr, nsize, prot, prot, MAP_SHARED, + obj, kva - entry->start, FALSE, td); + } else { + rv = vm_mmap_object(map, naddr, nsize, prot, prot, + MAP_PRIVATE | MAP_ANON, NULL, 0, FALSE, td); + if (rv == KERN_SUCCESS) { + size_t n = 0; + int save; + + save = vm_fault_disable_pagefaults(); + for (n = 0; n < osize; n += PAGE_SIZE) { + rv = vm_fault(map, (*naddr) + n, prot, + VM_FAULT_NORMAL, NULL); + if (rv != KERN_SUCCESS) { + vm_fault_enable_pagefaults(save); + goto out; + } + + rv = copyin((void *)(kva + n), + (void *)((*naddr) + n), PAGE_SIZE); + if (rv) { + vm_fault_enable_pagefaults(save); + return KERN_NO_ACCESS; + } + } + vm_fault_enable_pagefaults(save); + } + } + + vm_map_remove(map, kva, kva + size); + +out: + return rv; +}