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