1/* $NetBSD: uvm_mremap.c,v 1.21 2020/11/27 22:32:43 yhardy Exp $ */ 2 3/*- 4 * Copyright (c)2006,2007,2009 YAMAMOTO Takashi, 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: uvm_mremap.c,v 1.21 2020/11/27 22:32:43 yhardy Exp $"); 31 32#include <sys/param.h> 33#include <sys/mman.h> 34#include <sys/sched.h> 35#include <sys/syscallargs.h> 36#include <sys/proc.h> 37#include <sys/atomic.h> 38 39#include <uvm/uvm.h> 40 41static int 42uvm_mapent_extend(struct vm_map *map, vaddr_t endva, vsize_t size) 43{ 44 struct vm_map_entry *entry; 45 struct vm_map_entry *reserved_entry; 46 struct uvm_object *uobj; 47 int error = 0; 48 49 vm_map_lock(map); 50 if (!uvm_map_lookup_entry(map, endva, &reserved_entry)) { 51 error = ENOENT; 52 goto done; 53 } 54 if (reserved_entry->start != endva || 55 reserved_entry->end != endva + size || 56 reserved_entry->object.uvm_obj != NULL || 57 reserved_entry->aref.ar_amap != NULL || 58 reserved_entry->protection != VM_PROT_NONE) { 59 error = EINVAL; 60 goto done; 61 } 62 entry = reserved_entry->prev; 63 if (&map->header == entry || entry->end != endva) { 64 error = EINVAL; 65 goto done; 66 } 67 68 /* 69 * now, make reserved_entry compatible with entry, and then 70 * try to merge. 71 */ 72 73 uobj = entry->object.uvm_obj; 74 if (uobj) { 75 voff_t offset = entry->offset; 76 voff_t newoffset; 77 78 newoffset = offset + entry->end - entry->start; 79 if (newoffset <= offset) { 80 error = E2BIG; /* XXX */ 81 goto done; 82 } 83 if (uobj->pgops->pgo_reference) 84 uobj->pgops->pgo_reference(uobj); 85 reserved_entry->object.uvm_obj = uobj; 86 reserved_entry->offset = newoffset; 87 } 88 reserved_entry->etype = entry->etype; 89 if (UVM_ET_ISCOPYONWRITE(entry)) { 90 reserved_entry->etype |= UVM_ET_NEEDSCOPY; 91 } 92 reserved_entry->flags &= ~UVM_MAP_NOMERGE; 93 reserved_entry->protection = entry->protection; 94 reserved_entry->max_protection = entry->max_protection; 95 reserved_entry->inheritance = entry->inheritance; 96 reserved_entry->advice = entry->advice; 97 reserved_entry->wired_count = 0; /* XXX should inherit? */ 98 uvm_mapent_trymerge(map, reserved_entry, 0); 99done: 100 vm_map_unlock(map); 101 102 return error; 103} 104 105/* 106 * uvm_mremap: move and/or resize existing mappings. 107 */ 108 109int 110uvm_mremap(struct vm_map *oldmap, vaddr_t oldva, vsize_t oldsize, 111 struct vm_map *newmap, vaddr_t *newvap, vsize_t newsize, 112 struct proc *newproc, int flags) 113{ 114 vaddr_t dstva; 115 vsize_t movesize; 116 vaddr_t newva; 117 int alignshift; 118 vaddr_t align = 0; 119 int error = 0; 120 const bool fixed = (flags & MAP_FIXED) != 0; 121 const bool duplicate = (flags & MAP_REMAPDUP) != 0; 122 123 if (fixed) { 124 newva = *newvap; 125 } else { 126 newva = 0; 127 } 128 if ((oldva & PAGE_MASK) != 0 || 129 (newva & PAGE_MASK) != 0 || 130 (oldsize & PAGE_MASK) != 0 || 131 (newsize & PAGE_MASK) != 0) { 132 return EINVAL; 133 } 134 /* XXX zero-size should be allowed? */ 135 if (oldva + oldsize <= oldva || newva + newsize <= newva) { 136 return EINVAL; 137 } 138 139 /* 140 * Try to see if any requested alignment can even be attempted. 141 * Make sure we can express the alignment (asking for a >= 4GB 142 * alignment on an ILP32 architecure make no sense) and the 143 * alignment is at least for a page sized quanitiy. If the 144 * request was for a fixed mapping, make sure supplied address 145 * adheres to the request alignment. 146 */ 147 alignshift = (flags & MAP_ALIGNMENT_MASK) >> MAP_ALIGNMENT_SHIFT; 148 if (alignshift != 0) { 149 if (alignshift >= sizeof(vaddr_t) * NBBY) 150 return EINVAL; 151 align = 1L << alignshift; 152 if (align < PAGE_SIZE) 153 return EINVAL; 154 if (align >= vm_map_max(oldmap)) 155 return ENOMEM; 156 if ((flags & MAP_FIXED) != 0) { 157 if ((*newvap & (align - 1)) != 0) 158 return EINVAL; 159 align = 0; 160 } 161 } 162 163 /* 164 * check the easy cases first. 165 */ 166 167 if (!duplicate && 168 (!fixed || newva == oldva) && newmap == oldmap && 169 (align == 0 || (oldva & (align - 1)) == 0)) { 170 vaddr_t va; 171 172 if (newsize == oldsize) { 173 newva = oldva; 174 goto done; 175 } 176 if (newsize < oldsize) { 177 uvm_unmap(oldmap, oldva + newsize, oldva + oldsize); 178 newva = oldva; 179 goto done; 180 } 181 va = oldva + oldsize; 182 if (uvm_map_reserve(oldmap, newsize - oldsize, 0, 0, &va, 183 UVM_FLAG_FIXED)) { 184 newva = oldva; 185 goto extend; 186 } 187 if (fixed) { 188 return ENOMEM; 189 } 190 } 191 192 /* 193 * we need to move mappings. 194 */ 195 196 if (!fixed) { 197 KASSERT(&newproc->p_vmspace->vm_map == newmap); 198 newva = newproc->p_emul->e_vm_default_addr(newproc, 199 (vaddr_t)newproc->p_vmspace->vm_daddr, newsize, 200 newproc->p_vmspace->vm_map.flags & VM_MAP_TOPDOWN); 201 } 202 dstva = newva; 203 if (!uvm_map_reserve(newmap, newsize, oldva, align, &dstva, 204 fixed ? UVM_FLAG_FIXED : 0)) { 205 return ENOMEM; 206 } 207 KASSERT(!fixed || dstva == newva); 208 newva = dstva; 209 movesize = MIN(oldsize, newsize); 210 error = uvm_map_extract(oldmap, oldva, movesize, newmap, &dstva, 211 UVM_EXTRACT_RESERVED); 212 KASSERT(dstva == newva); 213 if (error != 0) { 214 /* 215 * undo uvm_map_reserve. 216 */ 217 uvm_unmap(newmap, newva, newva + newsize); 218 return error; 219 } 220 if (newsize > oldsize) { 221extend: 222 error = uvm_mapent_extend(newmap, newva + oldsize, 223 newsize - oldsize); 224 if (error != 0) { 225 /* 226 * undo uvm_map_reserve and uvm_map_extract. 227 */ 228 if (newva == oldva && newmap == oldmap) { 229 uvm_unmap(newmap, newva + oldsize, 230 newva + newsize); 231 } else { 232 uvm_unmap(newmap, newva, newva + newsize); 233 } 234 return error; 235 } 236 } 237 238 /* 239 * now we won't fail. 240 * remove original entries unless we did in-place extend. 241 */ 242 243 if (!duplicate && (oldva != newva || oldmap != newmap)) { 244 uvm_unmap(oldmap, oldva, oldva + oldsize); 245 } 246done: 247 *newvap = newva; 248 return 0; 249} 250 251/* 252 * sys_mremap: mremap system call. 253 */ 254 255int 256sys_mremap(struct lwp *l, const struct sys_mremap_args *uap, register_t *retval) 257{ 258 /* { 259 syscallarg(void *) old_address; 260 syscallarg(size_t) old_size; 261 syscallarg(void *) new_address; 262 syscallarg(size_t) new_size; 263 syscallarg(int) flags; 264 } */ 265 266 struct proc *p; 267 struct vm_map *map; 268 vaddr_t oldva; 269 vaddr_t newva; 270 size_t oldsize; 271 size_t newsize; 272 int flags; 273 int error; 274 275 flags = SCARG(uap, flags); 276 oldva = (vaddr_t)SCARG(uap, old_address); 277 oldsize = (vsize_t)(SCARG(uap, old_size)); 278 newva = (vaddr_t)SCARG(uap, new_address); 279 newsize = (vsize_t)(SCARG(uap, new_size)); 280 281 if ((flags & ~(MAP_FIXED | MAP_REMAPDUP | MAP_ALIGNMENT_MASK)) != 0) { 282 error = EINVAL; 283 goto done; 284 } 285 286 oldsize = round_page(oldsize); 287 newsize = round_page(newsize); 288 289 p = l->l_proc; 290 map = &p->p_vmspace->vm_map; 291 error = uvm_mremap(map, oldva, oldsize, map, &newva, newsize, p, flags); 292 293done: 294 *retval = (error != 0) ? 0 : (register_t)newva; 295 return error; 296} 297