1f04cf923SMarc-André Lureau /* 2f04cf923SMarc-André Lureau * memfd.c 3f04cf923SMarc-André Lureau * 4f04cf923SMarc-André Lureau * Copyright (c) 2015 Red Hat, Inc. 5f04cf923SMarc-André Lureau * 6f04cf923SMarc-André Lureau * QEMU library functions on POSIX which are shared between QEMU and 7f04cf923SMarc-André Lureau * the QEMU tools. 8f04cf923SMarc-André Lureau * 9f04cf923SMarc-André Lureau * Permission is hereby granted, free of charge, to any person obtaining a copy 10f04cf923SMarc-André Lureau * of this software and associated documentation files (the "Software"), to deal 11f04cf923SMarc-André Lureau * in the Software without restriction, including without limitation the rights 12f04cf923SMarc-André Lureau * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13f04cf923SMarc-André Lureau * copies of the Software, and to permit persons to whom the Software is 14f04cf923SMarc-André Lureau * furnished to do so, subject to the following conditions: 15f04cf923SMarc-André Lureau * 16f04cf923SMarc-André Lureau * The above copyright notice and this permission notice shall be included in 17f04cf923SMarc-André Lureau * all copies or substantial portions of the Software. 18f04cf923SMarc-André Lureau * 19f04cf923SMarc-André Lureau * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20f04cf923SMarc-André Lureau * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21f04cf923SMarc-André Lureau * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22f04cf923SMarc-André Lureau * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23f04cf923SMarc-André Lureau * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24f04cf923SMarc-André Lureau * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25f04cf923SMarc-André Lureau * THE SOFTWARE. 26f04cf923SMarc-André Lureau */ 27f04cf923SMarc-André Lureau 28f04cf923SMarc-André Lureau #include "qemu/osdep.h" 29f04cf923SMarc-André Lureau 30d3592199SMarc-André Lureau #include <glib/gprintf.h> 31d3592199SMarc-André Lureau 32f04cf923SMarc-André Lureau #include "qemu/memfd.h" 33f04cf923SMarc-André Lureau 3475e5b70eSPaolo Bonzini #if defined CONFIG_LINUX && !defined CONFIG_MEMFD 35f04cf923SMarc-André Lureau #include <sys/syscall.h> 36f04cf923SMarc-André Lureau #include <asm/unistd.h> 37f04cf923SMarc-André Lureau 38d3592199SMarc-André Lureau static int memfd_create(const char *name, unsigned int flags) 39f04cf923SMarc-André Lureau { 40f04cf923SMarc-André Lureau #ifdef __NR_memfd_create 41f04cf923SMarc-André Lureau return syscall(__NR_memfd_create, name, flags); 42f04cf923SMarc-André Lureau #else 43f04cf923SMarc-André Lureau return -1; 44f04cf923SMarc-André Lureau #endif 45f04cf923SMarc-André Lureau } 46f04cf923SMarc-André Lureau #endif 47f04cf923SMarc-André Lureau 48f04cf923SMarc-André Lureau #ifndef MFD_CLOEXEC 49f04cf923SMarc-André Lureau #define MFD_CLOEXEC 0x0001U 50f04cf923SMarc-André Lureau #endif 51f04cf923SMarc-André Lureau 52f04cf923SMarc-André Lureau #ifndef MFD_ALLOW_SEALING 53f04cf923SMarc-André Lureau #define MFD_ALLOW_SEALING 0x0002U 54f04cf923SMarc-André Lureau #endif 55d3592199SMarc-André Lureau 56*dcff1035SMarc-André Lureau int qemu_memfd_create(const char *name, size_t size, unsigned int seals) 57*dcff1035SMarc-André Lureau { 58*dcff1035SMarc-André Lureau int mfd = -1; 59*dcff1035SMarc-André Lureau 60*dcff1035SMarc-André Lureau #ifdef CONFIG_LINUX 61*dcff1035SMarc-André Lureau unsigned int flags = MFD_CLOEXEC; 62*dcff1035SMarc-André Lureau 63*dcff1035SMarc-André Lureau if (seals) { 64*dcff1035SMarc-André Lureau flags |= MFD_ALLOW_SEALING; 65*dcff1035SMarc-André Lureau } 66*dcff1035SMarc-André Lureau 67*dcff1035SMarc-André Lureau mfd = memfd_create(name, flags); 68*dcff1035SMarc-André Lureau if (mfd < 0) { 69*dcff1035SMarc-André Lureau return -1; 70*dcff1035SMarc-André Lureau } 71*dcff1035SMarc-André Lureau 72*dcff1035SMarc-André Lureau if (ftruncate(mfd, size) == -1) { 73*dcff1035SMarc-André Lureau perror("ftruncate"); 74*dcff1035SMarc-André Lureau close(mfd); 75*dcff1035SMarc-André Lureau return -1; 76*dcff1035SMarc-André Lureau } 77*dcff1035SMarc-André Lureau 78*dcff1035SMarc-André Lureau if (seals && fcntl(mfd, F_ADD_SEALS, seals) == -1) { 79*dcff1035SMarc-André Lureau perror("fcntl"); 80*dcff1035SMarc-André Lureau close(mfd); 81*dcff1035SMarc-André Lureau return -1; 82*dcff1035SMarc-André Lureau } 83*dcff1035SMarc-André Lureau #endif 84*dcff1035SMarc-André Lureau 85*dcff1035SMarc-André Lureau return mfd; 86*dcff1035SMarc-André Lureau } 87*dcff1035SMarc-André Lureau 88d3592199SMarc-André Lureau /* 89d3592199SMarc-André Lureau * This is a best-effort helper for shared memory allocation, with 90d3592199SMarc-André Lureau * optional sealing. The helper will do his best to allocate using 91d3592199SMarc-André Lureau * memfd with sealing, but may fallback on other methods without 92d3592199SMarc-André Lureau * sealing. 93d3592199SMarc-André Lureau */ 94d3592199SMarc-André Lureau void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals, 95d3592199SMarc-André Lureau int *fd) 96d3592199SMarc-André Lureau { 97d3592199SMarc-André Lureau void *ptr; 98*dcff1035SMarc-André Lureau int mfd = qemu_memfd_create(name, size, seals); 99d3592199SMarc-André Lureau 100*dcff1035SMarc-André Lureau /* some systems have memfd without sealing */ 101*dcff1035SMarc-André Lureau if (mfd == -1) { 102*dcff1035SMarc-André Lureau mfd = qemu_memfd_create(name, size, 0); 103d3592199SMarc-André Lureau } 104d3592199SMarc-André Lureau 105d3592199SMarc-André Lureau if (mfd == -1) { 10635f9b6efSMarc-André Lureau const char *tmpdir = g_get_tmp_dir(); 10735f9b6efSMarc-André Lureau gchar *fname; 10835f9b6efSMarc-André Lureau 10935f9b6efSMarc-André Lureau fname = g_strdup_printf("%s/memfd-XXXXXX", tmpdir); 11035f9b6efSMarc-André Lureau mfd = mkstemp(fname); 11135f9b6efSMarc-André Lureau unlink(fname); 11235f9b6efSMarc-André Lureau g_free(fname); 11335f9b6efSMarc-André Lureau 11435f9b6efSMarc-André Lureau if (mfd == -1) { 11535f9b6efSMarc-André Lureau perror("mkstemp"); 116d3592199SMarc-André Lureau return NULL; 117d3592199SMarc-André Lureau } 118d3592199SMarc-André Lureau 11935f9b6efSMarc-André Lureau if (ftruncate(mfd, size) == -1) { 12035f9b6efSMarc-André Lureau perror("ftruncate"); 12135f9b6efSMarc-André Lureau close(mfd); 12235f9b6efSMarc-André Lureau return NULL; 12335f9b6efSMarc-André Lureau } 12435f9b6efSMarc-André Lureau } 12535f9b6efSMarc-André Lureau 126d3592199SMarc-André Lureau ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0); 127d3592199SMarc-André Lureau if (ptr == MAP_FAILED) { 128d3592199SMarc-André Lureau perror("mmap"); 129d3592199SMarc-André Lureau close(mfd); 130d3592199SMarc-André Lureau return NULL; 131d3592199SMarc-André Lureau } 132d3592199SMarc-André Lureau 133d3592199SMarc-André Lureau *fd = mfd; 134d3592199SMarc-André Lureau return ptr; 135d3592199SMarc-André Lureau } 136d3592199SMarc-André Lureau 137d3592199SMarc-André Lureau void qemu_memfd_free(void *ptr, size_t size, int fd) 138d3592199SMarc-André Lureau { 139d3592199SMarc-André Lureau if (ptr) { 140d3592199SMarc-André Lureau munmap(ptr, size); 141d3592199SMarc-André Lureau } 142d3592199SMarc-André Lureau 143d3592199SMarc-André Lureau if (fd != -1) { 144d3592199SMarc-André Lureau close(fd); 145d3592199SMarc-André Lureau } 146d3592199SMarc-André Lureau } 14731190ed7SMarc-André Lureau 14831190ed7SMarc-André Lureau enum { 14931190ed7SMarc-André Lureau MEMFD_KO, 15031190ed7SMarc-André Lureau MEMFD_OK, 15131190ed7SMarc-André Lureau MEMFD_TODO 15231190ed7SMarc-André Lureau }; 15331190ed7SMarc-André Lureau 15431190ed7SMarc-André Lureau bool qemu_memfd_check(void) 15531190ed7SMarc-André Lureau { 15631190ed7SMarc-André Lureau static int memfd_check = MEMFD_TODO; 15731190ed7SMarc-André Lureau 15831190ed7SMarc-André Lureau if (memfd_check == MEMFD_TODO) { 15931190ed7SMarc-André Lureau int fd; 16031190ed7SMarc-André Lureau void *ptr; 16131190ed7SMarc-André Lureau 16231190ed7SMarc-André Lureau ptr = qemu_memfd_alloc("test", 4096, 0, &fd); 16331190ed7SMarc-André Lureau memfd_check = ptr ? MEMFD_OK : MEMFD_KO; 16431190ed7SMarc-André Lureau qemu_memfd_free(ptr, 4096, fd); 16531190ed7SMarc-André Lureau } 16631190ed7SMarc-André Lureau 16731190ed7SMarc-André Lureau return memfd_check == MEMFD_OK; 16831190ed7SMarc-André Lureau } 169