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/mmap-alloc.h> 13 #include <sys/types.h> 14 #include <sys/mman.h> 15 #include <assert.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 = QEMU_ALIGN_UP((uintptr_t)ptr, align) - (uintptr_t)ptr; 66 void *ptr1; 67 68 if (ptr == MAP_FAILED) { 69 return MAP_FAILED; 70 } 71 72 /* Make sure align is a power of 2 */ 73 assert(!(align & (align - 1))); 74 /* Always align to host page size */ 75 assert(align >= getpagesize()); 76 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 ptr += offset; 88 total -= offset; 89 90 if (offset > 0) { 91 munmap(ptr - offset, offset); 92 } 93 94 /* 95 * Leave a single PROT_NONE page allocated after the RAM block, to serve as 96 * a guard page guarding against potential buffer overflows. 97 */ 98 if (total > size + getpagesize()) { 99 munmap(ptr + size + getpagesize(), total - size - getpagesize()); 100 } 101 102 return ptr; 103 } 104 105 void qemu_ram_munmap(void *ptr, size_t size) 106 { 107 if (ptr) { 108 /* Unmap both the RAM block and the guard page */ 109 munmap(ptr, size + getpagesize()); 110 } 111 } 112