xref: /qemu/util/memfd.c (revision 4f938cbd)
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 
300f2956f9SMarc-André Lureau #include "qapi/error.h"
31f04cf923SMarc-André Lureau #include "qemu/memfd.h"
322ef8c0c9SMarc-André Lureau #include "qemu/host-utils.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 
56c5b2a9e0SMarc-André Lureau #ifndef MFD_HUGETLB
57c5b2a9e0SMarc-André Lureau #define MFD_HUGETLB 0x0004U
58c5b2a9e0SMarc-André Lureau #endif
59c5b2a9e0SMarc-André Lureau 
602ef8c0c9SMarc-André Lureau #ifndef MFD_HUGE_SHIFT
612ef8c0c9SMarc-André Lureau #define MFD_HUGE_SHIFT 26
622ef8c0c9SMarc-André Lureau #endif
632ef8c0c9SMarc-André Lureau 
64c5b2a9e0SMarc-André Lureau int qemu_memfd_create(const char *name, size_t size, bool hugetlb,
652ef8c0c9SMarc-André Lureau                       uint64_t hugetlbsize, unsigned int seals, Error **errp)
66dcff1035SMarc-André Lureau {
672ef8c0c9SMarc-André Lureau     int htsize = hugetlbsize ? ctz64(hugetlbsize) : 0;
682ef8c0c9SMarc-André Lureau 
69*4f938cbdSPeter Maydell     if (htsize && 1ULL << htsize != hugetlbsize) {
702ef8c0c9SMarc-André Lureau         error_setg(errp, "Hugepage size must be a power of 2");
712ef8c0c9SMarc-André Lureau         return -1;
722ef8c0c9SMarc-André Lureau     }
732ef8c0c9SMarc-André Lureau 
742ef8c0c9SMarc-André Lureau     htsize = htsize << MFD_HUGE_SHIFT;
752ef8c0c9SMarc-André Lureau 
76dcff1035SMarc-André Lureau #ifdef CONFIG_LINUX
770f2956f9SMarc-André Lureau     int mfd = -1;
78dcff1035SMarc-André Lureau     unsigned int flags = MFD_CLOEXEC;
79dcff1035SMarc-André Lureau 
80dcff1035SMarc-André Lureau     if (seals) {
81dcff1035SMarc-André Lureau         flags |= MFD_ALLOW_SEALING;
82dcff1035SMarc-André Lureau     }
83c5b2a9e0SMarc-André Lureau     if (hugetlb) {
84c5b2a9e0SMarc-André Lureau         flags |= MFD_HUGETLB;
852ef8c0c9SMarc-André Lureau         flags |= htsize;
86c5b2a9e0SMarc-André Lureau     }
87dcff1035SMarc-André Lureau     mfd = memfd_create(name, flags);
88dcff1035SMarc-André Lureau     if (mfd < 0) {
890f2956f9SMarc-André Lureau         goto err;
90dcff1035SMarc-André Lureau     }
91dcff1035SMarc-André Lureau 
92dcff1035SMarc-André Lureau     if (ftruncate(mfd, size) == -1) {
930f2956f9SMarc-André Lureau         goto err;
94dcff1035SMarc-André Lureau     }
95dcff1035SMarc-André Lureau 
96dcff1035SMarc-André Lureau     if (seals && fcntl(mfd, F_ADD_SEALS, seals) == -1) {
970f2956f9SMarc-André Lureau         goto err;
98dcff1035SMarc-André Lureau     }
99dcff1035SMarc-André Lureau 
100dcff1035SMarc-André Lureau     return mfd;
1010f2956f9SMarc-André Lureau 
1020f2956f9SMarc-André Lureau err:
1030f2956f9SMarc-André Lureau     if (mfd >= 0) {
1040f2956f9SMarc-André Lureau         close(mfd);
1050f2956f9SMarc-André Lureau     }
1060f2956f9SMarc-André Lureau #endif
1070f2956f9SMarc-André Lureau     error_setg_errno(errp, errno, "failed to create memfd");
1080f2956f9SMarc-André Lureau     return -1;
109dcff1035SMarc-André Lureau }
110dcff1035SMarc-André Lureau 
111d3592199SMarc-André Lureau /*
112d3592199SMarc-André Lureau  * This is a best-effort helper for shared memory allocation, with
113d3592199SMarc-André Lureau  * optional sealing. The helper will do his best to allocate using
114d3592199SMarc-André Lureau  * memfd with sealing, but may fallback on other methods without
115d3592199SMarc-André Lureau  * sealing.
116d3592199SMarc-André Lureau  */
117d3592199SMarc-André Lureau void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals,
1180f2956f9SMarc-André Lureau                        int *fd, Error **errp)
119d3592199SMarc-André Lureau {
120d3592199SMarc-André Lureau     void *ptr;
1212ef8c0c9SMarc-André Lureau     int mfd = qemu_memfd_create(name, size, false, 0, seals, NULL);
122d3592199SMarc-André Lureau 
123dcff1035SMarc-André Lureau     /* some systems have memfd without sealing */
124dcff1035SMarc-André Lureau     if (mfd == -1) {
1252ef8c0c9SMarc-André Lureau         mfd = qemu_memfd_create(name, size, false, 0, 0, NULL);
126d3592199SMarc-André Lureau     }
127d3592199SMarc-André Lureau 
128d3592199SMarc-André Lureau     if (mfd == -1) {
12935f9b6efSMarc-André Lureau         const char *tmpdir = g_get_tmp_dir();
13035f9b6efSMarc-André Lureau         gchar *fname;
13135f9b6efSMarc-André Lureau 
13235f9b6efSMarc-André Lureau         fname = g_strdup_printf("%s/memfd-XXXXXX", tmpdir);
13335f9b6efSMarc-André Lureau         mfd = mkstemp(fname);
13435f9b6efSMarc-André Lureau         unlink(fname);
13535f9b6efSMarc-André Lureau         g_free(fname);
13635f9b6efSMarc-André Lureau 
1370f2956f9SMarc-André Lureau         if (mfd == -1 ||
1380f2956f9SMarc-André Lureau             ftruncate(mfd, size) == -1) {
1390f2956f9SMarc-André Lureau             goto err;
14035f9b6efSMarc-André Lureau         }
14135f9b6efSMarc-André Lureau     }
14235f9b6efSMarc-André Lureau 
143d3592199SMarc-André Lureau     ptr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, mfd, 0);
144d3592199SMarc-André Lureau     if (ptr == MAP_FAILED) {
1450f2956f9SMarc-André Lureau         goto err;
146d3592199SMarc-André Lureau     }
147d3592199SMarc-André Lureau 
148d3592199SMarc-André Lureau     *fd = mfd;
149d3592199SMarc-André Lureau     return ptr;
1500f2956f9SMarc-André Lureau 
1510f2956f9SMarc-André Lureau err:
1520f2956f9SMarc-André Lureau     error_setg_errno(errp, errno, "failed to allocate shared memory");
1530f2956f9SMarc-André Lureau     if (mfd >= 0) {
1540f2956f9SMarc-André Lureau         close(mfd);
1550f2956f9SMarc-André Lureau     }
1560f2956f9SMarc-André Lureau     return NULL;
157d3592199SMarc-André Lureau }
158d3592199SMarc-André Lureau 
159d3592199SMarc-André Lureau void qemu_memfd_free(void *ptr, size_t size, int fd)
160d3592199SMarc-André Lureau {
161d3592199SMarc-André Lureau     if (ptr) {
162d3592199SMarc-André Lureau         munmap(ptr, size);
163d3592199SMarc-André Lureau     }
164d3592199SMarc-André Lureau 
165d3592199SMarc-André Lureau     if (fd != -1) {
166d3592199SMarc-André Lureau         close(fd);
167d3592199SMarc-André Lureau     }
168d3592199SMarc-André Lureau }
16931190ed7SMarc-André Lureau 
17031190ed7SMarc-André Lureau enum {
17131190ed7SMarc-André Lureau     MEMFD_KO,
17231190ed7SMarc-André Lureau     MEMFD_OK,
17331190ed7SMarc-André Lureau     MEMFD_TODO
17431190ed7SMarc-André Lureau };
17531190ed7SMarc-André Lureau 
176648abbfbSMarc-André Lureau /**
177648abbfbSMarc-André Lureau  * qemu_memfd_alloc_check():
178648abbfbSMarc-André Lureau  *
179648abbfbSMarc-André Lureau  * Check if qemu_memfd_alloc() can allocate, including using a
180648abbfbSMarc-André Lureau  * fallback implementation when host doesn't support memfd.
181648abbfbSMarc-André Lureau  */
182648abbfbSMarc-André Lureau bool qemu_memfd_alloc_check(void)
18331190ed7SMarc-André Lureau {
18431190ed7SMarc-André Lureau     static int memfd_check = MEMFD_TODO;
18531190ed7SMarc-André Lureau 
18631190ed7SMarc-André Lureau     if (memfd_check == MEMFD_TODO) {
18731190ed7SMarc-André Lureau         int fd;
18831190ed7SMarc-André Lureau         void *ptr;
18931190ed7SMarc-André Lureau 
1900f2956f9SMarc-André Lureau         ptr = qemu_memfd_alloc("test", 4096, 0, &fd, NULL);
19131190ed7SMarc-André Lureau         memfd_check = ptr ? MEMFD_OK : MEMFD_KO;
19231190ed7SMarc-André Lureau         qemu_memfd_free(ptr, 4096, fd);
19331190ed7SMarc-André Lureau     }
19431190ed7SMarc-André Lureau 
19531190ed7SMarc-André Lureau     return memfd_check == MEMFD_OK;
19631190ed7SMarc-André Lureau }
197648abbfbSMarc-André Lureau 
198648abbfbSMarc-André Lureau /**
199648abbfbSMarc-André Lureau  * qemu_memfd_check():
200648abbfbSMarc-André Lureau  *
201648abbfbSMarc-André Lureau  * Check if host supports memfd.
202648abbfbSMarc-André Lureau  */
203648abbfbSMarc-André Lureau bool qemu_memfd_check(void)
204648abbfbSMarc-André Lureau {
205648abbfbSMarc-André Lureau #ifdef CONFIG_LINUX
206648abbfbSMarc-André Lureau     static int memfd_check = MEMFD_TODO;
207648abbfbSMarc-André Lureau 
208648abbfbSMarc-André Lureau     if (memfd_check == MEMFD_TODO) {
209648abbfbSMarc-André Lureau         int mfd = memfd_create("test", 0);
210648abbfbSMarc-André Lureau         if (mfd >= 0) {
211648abbfbSMarc-André Lureau             memfd_check = MEMFD_OK;
212648abbfbSMarc-André Lureau             close(mfd);
213648abbfbSMarc-André Lureau         } else {
214648abbfbSMarc-André Lureau             memfd_check = MEMFD_KO;
215648abbfbSMarc-André Lureau         }
216648abbfbSMarc-André Lureau     }
217648abbfbSMarc-André Lureau 
218648abbfbSMarc-André Lureau     return memfd_check == MEMFD_OK;
219648abbfbSMarc-André Lureau #else
220648abbfbSMarc-André Lureau     return false;
221648abbfbSMarc-André Lureau #endif
222648abbfbSMarc-André Lureau }
223