1 /* 2 * Support for RAM backed by mmaped host memory. 3 * 4 * Copyright (c) 2015 Red Hat, Inc. 5 * 6 * Authors: 7 * Michael S. Tsirkin <mst@redhat.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2 or 10 * later. See the COPYING file in the top-level directory. 11 */ 12 13 #include "qemu/osdep.h" 14 #include "qemu/mmap-alloc.h" 15 #include "qemu/host-utils.h" 16 17 #define HUGETLBFS_MAGIC 0x958458f6 18 19 #ifdef CONFIG_LINUX 20 #include <sys/vfs.h> 21 #endif 22 23 size_t qemu_fd_getpagesize(int fd) 24 { 25 #ifdef CONFIG_LINUX 26 struct statfs fs; 27 int ret; 28 29 if (fd != -1) { 30 do { 31 ret = fstatfs(fd, &fs); 32 } while (ret != 0 && errno == EINTR); 33 34 if (ret == 0 && fs.f_type == HUGETLBFS_MAGIC) { 35 return fs.f_bsize; 36 } 37 } 38 #endif 39 40 return getpagesize(); 41 } 42 43 void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) 44 { 45 /* 46 * Note: this always allocates at least one extra page of virtual address 47 * space, even if size is already aligned. 48 */ 49 size_t total = size + align; 50 #if defined(__powerpc64__) && defined(__linux__) 51 /* On ppc64 mappings in the same segment (aka slice) must share the same 52 * page size. Since we will be re-allocating part of this segment 53 * from the supplied fd, we should make sure to use the same page size, to 54 * this end we mmap the supplied fd. In this case, set MAP_NORESERVE to 55 * avoid allocating backing store memory. 56 * We do this unless we are using the system page size, in which case 57 * anonymous memory is OK. 58 */ 59 int anonfd = fd == -1 || qemu_fd_getpagesize(fd) == getpagesize() ? -1 : fd; 60 int flags = anonfd == -1 ? MAP_ANONYMOUS : MAP_NORESERVE; 61 void *ptr = mmap(0, total, PROT_NONE, flags | MAP_PRIVATE, anonfd, 0); 62 #else 63 void *ptr = mmap(0, total, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 64 #endif 65 size_t offset; 66 void *ptr1; 67 68 if (ptr == MAP_FAILED) { 69 return MAP_FAILED; 70 } 71 72 assert(is_power_of_2(align)); 73 /* Always align to host page size */ 74 assert(align >= getpagesize()); 75 76 offset = QEMU_ALIGN_UP((uintptr_t)ptr, align) - (uintptr_t)ptr; 77 ptr1 = mmap(ptr + offset, size, PROT_READ | PROT_WRITE, 78 MAP_FIXED | 79 (fd == -1 ? MAP_ANONYMOUS : 0) | 80 (shared ? MAP_SHARED : MAP_PRIVATE), 81 fd, 0); 82 if (ptr1 == MAP_FAILED) { 83 munmap(ptr, total); 84 return MAP_FAILED; 85 } 86 87 if (offset > 0) { 88 munmap(ptr, offset); 89 } 90 91 /* 92 * Leave a single PROT_NONE page allocated after the RAM block, to serve as 93 * a guard page guarding against potential buffer overflows. 94 */ 95 total -= offset; 96 if (total > size + getpagesize()) { 97 munmap(ptr1 + size + getpagesize(), total - size - getpagesize()); 98 } 99 100 return ptr1; 101 } 102 103 void qemu_ram_munmap(void *ptr, size_t size) 104 { 105 if (ptr) { 106 /* Unmap both the RAM block and the guard page */ 107 munmap(ptr, size + getpagesize()); 108 } 109 } 110