1 /* 2 * memory management system call shims and definitions 3 * 4 * Copyright (c) 2013-15 Stacey D. Son 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 /* 21 * Copyright (c) 1982, 1986, 1993 22 * The Regents of the University of California. All rights reserved. 23 * 24 * Redistribution and use in source and binary forms, with or without 25 * modification, are permitted provided that the following conditions 26 * are met: 27 * 1. Redistributions of source code must retain the above copyright 28 * notice, this list of conditions and the following disclaimer. 29 * 2. Redistributions in binary form must reproduce the above copyright 30 * notice, this list of conditions and the following disclaimer in the 31 * documentation and/or other materials provided with the distribution. 32 * 4. Neither the name of the University nor the names of its contributors 33 * may be used to endorse or promote products derived from this software 34 * without specific prior written permission. 35 * 36 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 37 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 38 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 39 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 40 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 41 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 42 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 43 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 44 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 45 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 46 * SUCH DAMAGE. 47 */ 48 49 #ifndef BSD_USER_BSD_MEM_H 50 #define BSD_USER_BSD_MEM_H 51 52 #include <sys/types.h> 53 #include <sys/ipc.h> 54 #include <sys/mman.h> 55 #include <sys/shm.h> 56 #include <fcntl.h> 57 58 #include "qemu-bsd.h" 59 #include "exec/page-protection.h" 60 #include "user/page-protection.h" 61 62 extern struct bsd_shm_regions bsd_shm_regions[]; 63 extern abi_ulong target_brk; 64 extern abi_ulong initial_target_brk; 65 66 /* mmap(2) */ 67 static inline abi_long do_bsd_mmap(void *cpu_env, abi_long arg1, abi_long arg2, 68 abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6, abi_long arg7, 69 abi_long arg8) 70 { 71 if (regpairs_aligned(cpu_env) != 0) { 72 arg6 = arg7; 73 arg7 = arg8; 74 } 75 return get_errno(target_mmap(arg1, arg2, arg3, 76 target_to_host_bitmask(arg4, mmap_flags_tbl), 77 arg5, target_arg64(arg6, arg7))); 78 } 79 80 /* munmap(2) */ 81 static inline abi_long do_bsd_munmap(abi_long arg1, abi_long arg2) 82 { 83 return get_errno(target_munmap(arg1, arg2)); 84 } 85 86 /* mprotect(2) */ 87 static inline abi_long do_bsd_mprotect(abi_long arg1, abi_long arg2, 88 abi_long arg3) 89 { 90 return get_errno(target_mprotect(arg1, arg2, arg3)); 91 } 92 93 /* msync(2) */ 94 static inline abi_long do_bsd_msync(abi_long addr, abi_long len, abi_long flags) 95 { 96 if (!guest_range_valid_untagged(addr, len)) { 97 /* It seems odd, but POSIX wants this to be ENOMEM */ 98 return -TARGET_ENOMEM; 99 } 100 101 return get_errno(msync(g2h_untagged(addr), len, flags)); 102 } 103 104 /* mlock(2) */ 105 static inline abi_long do_bsd_mlock(abi_long arg1, abi_long arg2) 106 { 107 if (!guest_range_valid_untagged(arg1, arg2)) { 108 return -TARGET_EINVAL; 109 } 110 return get_errno(mlock(g2h_untagged(arg1), arg2)); 111 } 112 113 /* munlock(2) */ 114 static inline abi_long do_bsd_munlock(abi_long arg1, abi_long arg2) 115 { 116 if (!guest_range_valid_untagged(arg1, arg2)) { 117 return -TARGET_EINVAL; 118 } 119 return get_errno(munlock(g2h_untagged(arg1), arg2)); 120 } 121 122 /* mlockall(2) */ 123 static inline abi_long do_bsd_mlockall(abi_long arg1) 124 { 125 return get_errno(mlockall(arg1)); 126 } 127 128 /* munlockall(2) */ 129 static inline abi_long do_bsd_munlockall(void) 130 { 131 return get_errno(munlockall()); 132 } 133 134 /* madvise(2) */ 135 static inline abi_long do_bsd_madvise(abi_long arg1, abi_long arg2, 136 abi_long arg3) 137 { 138 abi_ulong len; 139 int ret = 0; 140 abi_long start = arg1; 141 abi_long len_in = arg2; 142 abi_long advice = arg3; 143 144 if (start & ~TARGET_PAGE_MASK) { 145 return -TARGET_EINVAL; 146 } 147 if (len_in == 0) { 148 return 0; 149 } 150 len = TARGET_PAGE_ALIGN(len_in); 151 if (len == 0 || !guest_range_valid_untagged(start, len)) { 152 return -TARGET_EINVAL; 153 } 154 155 /* 156 * Most advice values are hints, so ignoring and returning success is ok. 157 * 158 * However, some advice values such as MADV_DONTNEED, are not hints and 159 * need to be emulated. 160 * 161 * A straight passthrough for those may not be safe because qemu sometimes 162 * turns private file-backed mappings into anonymous mappings. 163 * If all guest pages have PAGE_PASSTHROUGH set, mappings have the 164 * same semantics for the host as for the guest. 165 * 166 * MADV_DONTNEED is passed through, if possible. 167 * If passthrough isn't possible, we nevertheless (wrongly!) return 168 * success, which is broken but some userspace programs fail to work 169 * otherwise. Completely implementing such emulation is quite complicated 170 * though. 171 */ 172 mmap_lock(); 173 switch (advice) { 174 case MADV_DONTNEED: 175 if (page_check_range(start, len, PAGE_PASSTHROUGH)) { 176 ret = get_errno(madvise(g2h_untagged(start), len, advice)); 177 if (ret == 0) { 178 page_reset_target_data(start, start + len - 1); 179 } 180 } 181 } 182 mmap_unlock(); 183 184 return ret; 185 } 186 187 /* minherit(2) */ 188 static inline abi_long do_bsd_minherit(abi_long addr, abi_long len, 189 abi_long inherit) 190 { 191 return get_errno(minherit(g2h_untagged(addr), len, inherit)); 192 } 193 194 /* mincore(2) */ 195 static inline abi_long do_bsd_mincore(abi_ulong target_addr, abi_ulong len, 196 abi_ulong target_vec) 197 { 198 abi_long ret; 199 void *p; 200 abi_ulong vec_len = DIV_ROUND_UP(len, TARGET_PAGE_SIZE); 201 202 if (!guest_range_valid_untagged(target_addr, len) 203 || !page_check_range(target_addr, len, PAGE_VALID)) { 204 return -TARGET_EFAULT; 205 } 206 207 p = lock_user(VERIFY_WRITE, target_vec, vec_len, 0); 208 if (p == NULL) { 209 return -TARGET_EFAULT; 210 } 211 ret = get_errno(mincore(g2h_untagged(target_addr), len, p)); 212 unlock_user(p, target_vec, vec_len); 213 214 return ret; 215 } 216 217 /* do_brk() must return target values and target errnos. */ 218 static inline abi_long do_obreak(abi_ulong brk_val) 219 { 220 abi_long mapped_addr; 221 abi_ulong new_brk; 222 abi_ulong old_brk; 223 224 /* brk pointers are always untagged */ 225 226 /* do not allow to shrink below initial brk value */ 227 if (brk_val < initial_target_brk) { 228 return target_brk; 229 } 230 231 new_brk = TARGET_PAGE_ALIGN(brk_val); 232 old_brk = TARGET_PAGE_ALIGN(target_brk); 233 234 /* new and old target_brk might be on the same page */ 235 if (new_brk == old_brk) { 236 target_brk = brk_val; 237 return target_brk; 238 } 239 240 /* Release heap if necessary */ 241 if (new_brk < old_brk) { 242 target_munmap(new_brk, old_brk - new_brk); 243 244 target_brk = brk_val; 245 return target_brk; 246 } 247 248 mapped_addr = target_mmap(old_brk, new_brk - old_brk, 249 PROT_READ | PROT_WRITE, 250 MAP_FIXED | MAP_EXCL | MAP_ANON | MAP_PRIVATE, 251 -1, 0); 252 253 if (mapped_addr == old_brk) { 254 target_brk = brk_val; 255 return target_brk; 256 } 257 258 /* For everything else, return the previous break. */ 259 return target_brk; 260 } 261 262 /* shm_open(2) */ 263 static inline abi_long do_bsd_shm_open(abi_ulong arg1, abi_long arg2, 264 abi_long arg3) 265 { 266 int ret; 267 void *p; 268 269 if (arg1 == (uintptr_t)SHM_ANON) { 270 p = SHM_ANON; 271 } else { 272 p = lock_user_string(arg1); 273 if (p == NULL) { 274 return -TARGET_EFAULT; 275 } 276 } 277 ret = get_errno(shm_open(p, target_to_host_bitmask(arg2, fcntl_flags_tbl), 278 arg3)); 279 280 if (p != SHM_ANON) { 281 unlock_user(p, arg1, 0); 282 } 283 284 return ret; 285 } 286 287 /* shm_unlink(2) */ 288 static inline abi_long do_bsd_shm_unlink(abi_ulong arg1) 289 { 290 int ret; 291 void *p; 292 293 p = lock_user_string(arg1); 294 if (p == NULL) { 295 return -TARGET_EFAULT; 296 } 297 ret = get_errno(shm_unlink(p)); /* XXX path(p)? */ 298 unlock_user(p, arg1, 0); 299 300 return ret; 301 } 302 303 /* shmget(2) */ 304 static inline abi_long do_bsd_shmget(abi_long arg1, abi_ulong arg2, 305 abi_long arg3) 306 { 307 return get_errno(shmget(arg1, arg2, arg3)); 308 } 309 310 /* shmctl(2) */ 311 static inline abi_long do_bsd_shmctl(abi_long shmid, abi_long cmd, 312 abi_ulong buff) 313 { 314 struct shmid_ds dsarg; 315 abi_long ret = -TARGET_EINVAL; 316 317 cmd &= 0xff; 318 319 switch (cmd) { 320 case IPC_STAT: 321 if (target_to_host_shmid_ds(&dsarg, buff)) { 322 return -TARGET_EFAULT; 323 } 324 ret = get_errno(shmctl(shmid, cmd, &dsarg)); 325 if (host_to_target_shmid_ds(buff, &dsarg)) { 326 return -TARGET_EFAULT; 327 } 328 break; 329 330 case IPC_SET: 331 if (target_to_host_shmid_ds(&dsarg, buff)) { 332 return -TARGET_EFAULT; 333 } 334 ret = get_errno(shmctl(shmid, cmd, &dsarg)); 335 break; 336 337 case IPC_RMID: 338 ret = get_errno(shmctl(shmid, cmd, NULL)); 339 break; 340 341 default: 342 ret = -TARGET_EINVAL; 343 break; 344 } 345 346 return ret; 347 } 348 349 /* shmat(2) */ 350 static inline abi_long do_bsd_shmat(int shmid, abi_ulong shmaddr, int shmflg) 351 { 352 abi_ulong raddr; 353 abi_long ret; 354 struct shmid_ds shm_info; 355 356 /* Find out the length of the shared memory segment. */ 357 ret = get_errno(shmctl(shmid, IPC_STAT, &shm_info)); 358 if (is_error(ret)) { 359 /* Can't get the length */ 360 return ret; 361 } 362 363 if (!guest_range_valid_untagged(shmaddr, shm_info.shm_segsz)) { 364 return -TARGET_EINVAL; 365 } 366 367 WITH_MMAP_LOCK_GUARD() { 368 void *host_raddr; 369 370 if (shmaddr) { 371 host_raddr = shmat(shmid, (void *)g2h_untagged(shmaddr), shmflg); 372 } else { 373 abi_ulong mmap_start; 374 375 mmap_start = mmap_find_vma(0, shm_info.shm_segsz); 376 377 if (mmap_start == -1) { 378 return -TARGET_ENOMEM; 379 } 380 host_raddr = shmat(shmid, g2h_untagged(mmap_start), 381 shmflg | SHM_REMAP); 382 } 383 384 if (host_raddr == (void *)-1) { 385 return get_errno(-1); 386 } 387 raddr = h2g(host_raddr); 388 389 page_set_flags(raddr, raddr + shm_info.shm_segsz - 1, 390 PAGE_VALID | PAGE_RESET | PAGE_READ | 391 (shmflg & SHM_RDONLY ? 0 : PAGE_WRITE)); 392 393 for (int i = 0; i < N_BSD_SHM_REGIONS; i++) { 394 if (bsd_shm_regions[i].start == 0) { 395 bsd_shm_regions[i].start = raddr; 396 bsd_shm_regions[i].size = shm_info.shm_segsz; 397 break; 398 } 399 } 400 } 401 402 return raddr; 403 } 404 405 /* shmdt(2) */ 406 static inline abi_long do_bsd_shmdt(abi_ulong shmaddr) 407 { 408 abi_long ret; 409 410 WITH_MMAP_LOCK_GUARD() { 411 int i; 412 413 for (i = 0; i < N_BSD_SHM_REGIONS; ++i) { 414 if (bsd_shm_regions[i].start == shmaddr) { 415 break; 416 } 417 } 418 419 if (i == N_BSD_SHM_REGIONS) { 420 return -TARGET_EINVAL; 421 } 422 423 ret = get_errno(shmdt(g2h_untagged(shmaddr))); 424 if (ret == 0) { 425 abi_ulong size = bsd_shm_regions[i].size; 426 427 bsd_shm_regions[i].start = 0; 428 page_set_flags(shmaddr, shmaddr + size - 1, 0); 429 mmap_reserve(shmaddr, size); 430 } 431 } 432 433 return ret; 434 } 435 436 static inline abi_long do_bsd_vadvise(void) 437 { 438 /* See sys_ovadvise() in vm_unix.c */ 439 return -TARGET_EINVAL; 440 } 441 442 static inline abi_long do_bsd_sbrk(void) 443 { 444 /* see sys_sbrk() in vm_mmap.c */ 445 return -TARGET_EOPNOTSUPP; 446 } 447 448 static inline abi_long do_bsd_sstk(void) 449 { 450 /* see sys_sstk() in vm_mmap.c */ 451 return -TARGET_EOPNOTSUPP; 452 } 453 454 #endif /* BSD_USER_BSD_MEM_H */ 455