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, 54 * unless we are using the system page size, in which case anonymous memory 55 * is OK. Use align as a hint for the page size. 56 * In this case, set MAP_NORESERVE to avoid allocating backing store memory. 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