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 30*0f2956f9SMarc-André Lureau #include "qapi/error.h" 31f04cf923SMarc-André Lureau #include "qemu/memfd.h" 32f04cf923SMarc-André Lureau 3375e5b70eSPaolo Bonzini #if defined CONFIG_LINUX && !defined CONFIG_MEMFD 34f04cf923SMarc-André Lureau #include <sys/syscall.h> 35f04cf923SMarc-André Lureau #include <asm/unistd.h> 36f04cf923SMarc-André Lureau 37d3592199SMarc-André Lureau static int memfd_create(const char *name, unsigned int flags) 38f04cf923SMarc-André Lureau { 39f04cf923SMarc-André Lureau #ifdef __NR_memfd_create 40f04cf923SMarc-André Lureau return syscall(__NR_memfd_create, name, flags); 41f04cf923SMarc-André Lureau #else 42f04cf923SMarc-André Lureau return -1; 43f04cf923SMarc-André Lureau #endif 44f04cf923SMarc-André Lureau } 45f04cf923SMarc-André Lureau #endif 46f04cf923SMarc-André Lureau 47f04cf923SMarc-André Lureau #ifndef MFD_CLOEXEC 48f04cf923SMarc-André Lureau #define MFD_CLOEXEC 0x0001U 49f04cf923SMarc-André Lureau #endif 50f04cf923SMarc-André Lureau 51f04cf923SMarc-André Lureau #ifndef MFD_ALLOW_SEALING 52f04cf923SMarc-André Lureau #define MFD_ALLOW_SEALING 0x0002U 53f04cf923SMarc-André Lureau #endif 54d3592199SMarc-André Lureau 55*0f2956f9SMarc-André Lureau int qemu_memfd_create(const char *name, size_t size, 56*0f2956f9SMarc-André Lureau unsigned int seals, Error **errp) 57dcff1035SMarc-André Lureau { 58dcff1035SMarc-André Lureau #ifdef CONFIG_LINUX 59*0f2956f9SMarc-André Lureau int mfd = -1; 60dcff1035SMarc-André Lureau unsigned int flags = MFD_CLOEXEC; 61dcff1035SMarc-André Lureau 62dcff1035SMarc-André Lureau if (seals) { 63dcff1035SMarc-André Lureau flags |= MFD_ALLOW_SEALING; 64dcff1035SMarc-André Lureau } 65dcff1035SMarc-André Lureau 66dcff1035SMarc-André Lureau mfd = memfd_create(name, flags); 67dcff1035SMarc-André Lureau if (mfd < 0) { 68*0f2956f9SMarc-André Lureau goto err; 69dcff1035SMarc-André Lureau } 70dcff1035SMarc-André Lureau 71dcff1035SMarc-André Lureau if (ftruncate(mfd, size) == -1) { 72*0f2956f9SMarc-André Lureau goto err; 73dcff1035SMarc-André Lureau } 74dcff1035SMarc-André Lureau 75dcff1035SMarc-André Lureau if (seals && fcntl(mfd, F_ADD_SEALS, seals) == -1) { 76*0f2956f9SMarc-André Lureau goto err; 77dcff1035SMarc-André Lureau } 78dcff1035SMarc-André Lureau 79dcff1035SMarc-André Lureau return mfd; 80*0f2956f9SMarc-André Lureau 81*0f2956f9SMarc-André Lureau err: 82*0f2956f9SMarc-André Lureau if (mfd >= 0) { 83*0f2956f9SMarc-André Lureau close(mfd); 84*0f2956f9SMarc-André Lureau } 85*0f2956f9SMarc-André Lureau #endif 86*0f2956f9SMarc-André Lureau error_setg_errno(errp, errno, "failed to create memfd"); 87*0f2956f9SMarc-André Lureau return -1; 88dcff1035SMarc-André Lureau } 89dcff1035SMarc-André Lureau 90d3592199SMarc-André Lureau /* 91d3592199SMarc-André Lureau * This is a best-effort helper for shared memory allocation, with 92d3592199SMarc-André Lureau * optional sealing. The helper will do his best to allocate using 93d3592199SMarc-André Lureau * memfd with sealing, but may fallback on other methods without 94d3592199SMarc-André Lureau * sealing. 95d3592199SMarc-André Lureau */ 96d3592199SMarc-André Lureau void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals, 97*0f2956f9SMarc-André Lureau int *fd, Error **errp) 98d3592199SMarc-André Lureau { 99d3592199SMarc-André Lureau void *ptr; 100*0f2956f9SMarc-André Lureau int mfd = qemu_memfd_create(name, size, seals, NULL); 101d3592199SMarc-André Lureau 102dcff1035SMarc-André Lureau /* some systems have memfd without sealing */ 103dcff1035SMarc-André Lureau if (mfd == -1) { 104*0f2956f9SMarc-André Lureau mfd = qemu_memfd_create(name, size, 0, NULL); 105d3592199SMarc-André Lureau } 106d3592199SMarc-André Lureau 107d3592199SMarc-André Lureau if (mfd == -1) { 10835f9b6efSMarc-André Lureau const char *tmpdir = g_get_tmp_dir(); 10935f9b6efSMarc-André Lureau gchar *fname; 11035f9b6efSMarc-André Lureau 11135f9b6efSMarc-André Lureau fname = g_strdup_printf("%s/memfd-XXXXXX", tmpdir); 11235f9b6efSMarc-André Lureau mfd = mkstemp(fname); 11335f9b6efSMarc-André Lureau unlink(fname); 11435f9b6efSMarc-André Lureau g_free(fname); 11535f9b6efSMarc-André Lureau 116*0f2956f9SMarc-André Lureau if (mfd == -1 || 117*0f2956f9SMarc-André Lureau ftruncate(mfd, size) == -1) { 118*0f2956f9SMarc-André Lureau goto err; 11935f9b6efSMarc-André Lureau } 12035f9b6efSMarc-André Lureau } 12135f9b6efSMarc-André Lureau 122d3592199SMarc-André Lureau ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0); 123d3592199SMarc-André Lureau if (ptr == MAP_FAILED) { 124*0f2956f9SMarc-André Lureau goto err; 125d3592199SMarc-André Lureau } 126d3592199SMarc-André Lureau 127d3592199SMarc-André Lureau *fd = mfd; 128d3592199SMarc-André Lureau return ptr; 129*0f2956f9SMarc-André Lureau 130*0f2956f9SMarc-André Lureau err: 131*0f2956f9SMarc-André Lureau error_setg_errno(errp, errno, "failed to allocate shared memory"); 132*0f2956f9SMarc-André Lureau if (mfd >= 0) { 133*0f2956f9SMarc-André Lureau close(mfd); 134*0f2956f9SMarc-André Lureau } 135*0f2956f9SMarc-André Lureau return NULL; 136d3592199SMarc-André Lureau } 137d3592199SMarc-André Lureau 138d3592199SMarc-André Lureau void qemu_memfd_free(void *ptr, size_t size, int fd) 139d3592199SMarc-André Lureau { 140d3592199SMarc-André Lureau if (ptr) { 141d3592199SMarc-André Lureau munmap(ptr, size); 142d3592199SMarc-André Lureau } 143d3592199SMarc-André Lureau 144d3592199SMarc-André Lureau if (fd != -1) { 145d3592199SMarc-André Lureau close(fd); 146d3592199SMarc-André Lureau } 147d3592199SMarc-André Lureau } 14831190ed7SMarc-André Lureau 14931190ed7SMarc-André Lureau enum { 15031190ed7SMarc-André Lureau MEMFD_KO, 15131190ed7SMarc-André Lureau MEMFD_OK, 15231190ed7SMarc-André Lureau MEMFD_TODO 15331190ed7SMarc-André Lureau }; 15431190ed7SMarc-André Lureau 15531190ed7SMarc-André Lureau bool qemu_memfd_check(void) 15631190ed7SMarc-André Lureau { 15731190ed7SMarc-André Lureau static int memfd_check = MEMFD_TODO; 15831190ed7SMarc-André Lureau 15931190ed7SMarc-André Lureau if (memfd_check == MEMFD_TODO) { 16031190ed7SMarc-André Lureau int fd; 16131190ed7SMarc-André Lureau void *ptr; 16231190ed7SMarc-André Lureau 163*0f2956f9SMarc-André Lureau ptr = qemu_memfd_alloc("test", 4096, 0, &fd, NULL); 16431190ed7SMarc-André Lureau memfd_check = ptr ? MEMFD_OK : MEMFD_KO; 16531190ed7SMarc-André Lureau qemu_memfd_free(ptr, 4096, fd); 16631190ed7SMarc-André Lureau } 16731190ed7SMarc-André Lureau 16831190ed7SMarc-André Lureau return memfd_check == MEMFD_OK; 16931190ed7SMarc-André Lureau } 170