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 size_t qemu_mempath_getpagesize(const char *mem_path) 44 { 45 #ifdef CONFIG_LINUX 46 struct statfs fs; 47 int ret; 48 49 do { 50 ret = statfs(mem_path, &fs); 51 } while (ret != 0 && errno == EINTR); 52 53 if (ret != 0) { 54 fprintf(stderr, "Couldn't statfs() memory path: %s\n", 55 strerror(errno)); 56 exit(1); 57 } 58 59 if (fs.f_type == HUGETLBFS_MAGIC) { 60 /* It's hugepage, return the huge page size */ 61 return fs.f_bsize; 62 } 63 #endif 64 65 return getpagesize(); 66 } 67 68 void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) 69 { 70 /* 71 * Note: this always allocates at least one extra page of virtual address 72 * space, even if size is already aligned. 73 */ 74 size_t total = size + align; 75 #if defined(__powerpc64__) && defined(__linux__) 76 /* On ppc64 mappings in the same segment (aka slice) must share the same 77 * page size. Since we will be re-allocating part of this segment 78 * from the supplied fd, we should make sure to use the same page size, to 79 * this end we mmap the supplied fd. In this case, set MAP_NORESERVE to 80 * avoid allocating backing store memory. 81 * We do this unless we are using the system page size, in which case 82 * anonymous memory is OK. 83 */ 84 int anonfd = fd == -1 || qemu_fd_getpagesize(fd) == getpagesize() ? -1 : fd; 85 int flags = anonfd == -1 ? MAP_ANONYMOUS : MAP_NORESERVE; 86 void *ptr = mmap(0, total, PROT_NONE, flags | MAP_PRIVATE, anonfd, 0); 87 #else 88 void *ptr = mmap(0, total, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); 89 #endif 90 size_t offset; 91 void *ptr1; 92 93 if (ptr == MAP_FAILED) { 94 return MAP_FAILED; 95 } 96 97 assert(is_power_of_2(align)); 98 /* Always align to host page size */ 99 assert(align >= getpagesize()); 100 101 offset = QEMU_ALIGN_UP((uintptr_t)ptr, align) - (uintptr_t)ptr; 102 ptr1 = mmap(ptr + offset, size, PROT_READ | PROT_WRITE, 103 MAP_FIXED | 104 (fd == -1 ? MAP_ANONYMOUS : 0) | 105 (shared ? MAP_SHARED : MAP_PRIVATE), 106 fd, 0); 107 if (ptr1 == MAP_FAILED) { 108 munmap(ptr, total); 109 return MAP_FAILED; 110 } 111 112 if (offset > 0) { 113 munmap(ptr, offset); 114 } 115 116 /* 117 * Leave a single PROT_NONE page allocated after the RAM block, to serve as 118 * a guard page guarding against potential buffer overflows. 119 */ 120 total -= offset; 121 if (total > size + getpagesize()) { 122 munmap(ptr1 + size + getpagesize(), total - size - getpagesize()); 123 } 124 125 return ptr1; 126 } 127 128 void qemu_ram_munmap(void *ptr, size_t size) 129 { 130 if (ptr) { 131 /* Unmap both the RAM block and the guard page */ 132 munmap(ptr, size + getpagesize()); 133 } 134 } 135