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 #include "qemu/osdep.h" 13 #include <qemu/mmap-alloc.h> 14 15 #define HUGETLBFS_MAGIC 0x958458f6 16 17 #ifdef CONFIG_LINUX 18 #include <sys/vfs.h> 19 #endif 20 21 size_t qemu_fd_getpagesize(int fd) 22 { 23 #ifdef CONFIG_LINUX 24 struct statfs fs; 25 int ret; 26 27 if (fd != -1) { 28 do { 29 ret = fstatfs(fd, &fs); 30 } while (ret != 0 && errno == EINTR); 31 32 if (ret == 0 && fs.f_type == HUGETLBFS_MAGIC) { 33 return fs.f_bsize; 34 } 35 } 36 #endif 37 38 return getpagesize(); 39 } 40 41 void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) 42 { 43 /* 44 * Note: this always allocates at least one extra page of virtual address 45 * space, even if size is already aligned. 46 */ 47 size_t total = size + align; 48 #if defined(__powerpc64__) && defined(__linux__) 49 /* On ppc64 mappings in the same segment (aka slice) must share the same 50 * page size. Since we will be re-allocating part of this segment 51 * from the supplied fd, we should make sure to use the same page size, to 52 * this end we mmap the supplied fd. In this case, set MAP_NORESERVE to 53 * avoid allocating backing store memory. 54 * We do this unless we are using the system page size, in which case 55 * anonymous memory is OK. 56 */ 57 int anonfd = fd == -1 || qemu_fd_getpagesize(fd) == getpagesize() ? -1 : fd; 58 int flags = anonfd == -1 ? MAP_ANONYMOUS : MAP_NORESERVE; 59 void *ptr = mmap(0, total, PROT_NONE, flags | MAP_PRIVATE, anonfd, 0); 60 #else 61 void *ptr = mmap(0, total, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 62 #endif 63 size_t offset = QEMU_ALIGN_UP((uintptr_t)ptr, align) - (uintptr_t)ptr; 64 void *ptr1; 65 66 if (ptr == MAP_FAILED) { 67 return MAP_FAILED; 68 } 69 70 /* Make sure align is a power of 2 */ 71 assert(!(align & (align - 1))); 72 /* Always align to host page size */ 73 assert(align >= getpagesize()); 74 75 ptr1 = mmap(ptr + offset, size, PROT_READ | PROT_WRITE, 76 MAP_FIXED | 77 (fd == -1 ? MAP_ANONYMOUS : 0) | 78 (shared ? MAP_SHARED : MAP_PRIVATE), 79 fd, 0); 80 if (ptr1 == MAP_FAILED) { 81 munmap(ptr, total); 82 return MAP_FAILED; 83 } 84 85 ptr += offset; 86 total -= offset; 87 88 if (offset > 0) { 89 munmap(ptr - offset, offset); 90 } 91 92 /* 93 * Leave a single PROT_NONE page allocated after the RAM block, to serve as 94 * a guard page guarding against potential buffer overflows. 95 */ 96 if (total > size + getpagesize()) { 97 munmap(ptr + size + getpagesize(), total - size - getpagesize()); 98 } 99 100 return ptr; 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