1 /*
2 * Copyright (c) 2007-2008, D G Murray <Derek.Murray@cl.cam.ac.uk>
3 * Copyright (c) 2018, Oleksandr Andrushchenko, EPAM Systems Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation;
8 * version 2.1 of the License.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Split out from xc_linux_osdep.c
19 */
20
21 #include <fcntl.h>
22 #include <errno.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <stdint.h>
26 #include <string.h>
27 #include <stddef.h>
28
29 #include <sys/ioctl.h>
30 #include <sys/mman.h>
31
32 #include <xen/sys/gntdev.h>
33 #include <xen/sys/gntalloc.h>
34
35 #include <xenctrl.h>
36 #include <xen-tools/common-macros.h>
37
38 #include "private.h"
39
40 #define DEVXEN "/dev/xen/"
41
42 #ifndef O_CLOEXEC
43 #define O_CLOEXEC 0
44 #endif
45
osdep_gnttab_open(xengnttab_handle * xgt)46 int osdep_gnttab_open(xengnttab_handle *xgt)
47 {
48 int fd = open(DEVXEN "gntdev", O_RDWR|O_CLOEXEC);
49 if ( fd == -1 )
50 return -1;
51 xgt->fd = fd;
52 return 0;
53 }
54
osdep_gnttab_close(xengnttab_handle * xgt)55 int osdep_gnttab_close(xengnttab_handle *xgt)
56 {
57 if ( xgt->fd == -1 )
58 return 0;
59
60 return close(xgt->fd);
61 }
62
osdep_gnttab_set_max_grants(xengnttab_handle * xgt,uint32_t count)63 int osdep_gnttab_set_max_grants(xengnttab_handle *xgt, uint32_t count)
64 {
65 int fd = xgt->fd, rc;
66 struct ioctl_gntdev_set_max_grants max_grants = { .count = count };
67
68 rc = ioctl(fd, IOCTL_GNTDEV_SET_MAX_GRANTS, &max_grants);
69 if (rc) {
70 /*
71 * Newer (e.g. pv-ops) kernels don't implement this IOCTL,
72 * so ignore the resulting specific failure.
73 */
74 if (errno == ENOTTY)
75 rc = 0;
76 else
77 GTERROR(xgt->logger, "ioctl SET_MAX_GRANTS failed");
78 }
79
80 return rc;
81 }
82
osdep_gnttab_grant_map(xengnttab_handle * xgt,uint32_t count,int flags,int prot,uint32_t * domids,uint32_t * refs,uint32_t notify_offset,evtchn_port_t notify_port)83 void *osdep_gnttab_grant_map(xengnttab_handle *xgt,
84 uint32_t count, int flags, int prot,
85 uint32_t *domids, uint32_t *refs,
86 uint32_t notify_offset,
87 evtchn_port_t notify_port)
88 {
89 int fd = xgt->fd;
90 struct ioctl_gntdev_map_grant_ref *map;
91 unsigned int map_size = sizeof(*map) + (count - 1) * sizeof(map->refs[0]);
92 int os_page_size = sysconf(_SC_PAGESIZE);
93 void *addr = NULL;
94 int domids_stride = 1;
95 int i;
96
97 if (flags & XENGNTTAB_GRANT_MAP_SINGLE_DOMAIN)
98 domids_stride = 0;
99
100 if ( map_size <= os_page_size )
101 map = alloca(map_size);
102 else
103 {
104 map_size = ROUNDUP(map_size, XC_PAGE_SHIFT);
105 map = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
106 MAP_PRIVATE | MAP_ANON | MAP_POPULATE, -1, 0);
107 if ( map == MAP_FAILED )
108 {
109 GTERROR(xgt->logger, "mmap of map failed");
110 return NULL;
111 }
112 }
113
114 for ( i = 0; i < count; i++ )
115 {
116 map->refs[i].domid = domids[i * domids_stride];
117 map->refs[i].ref = refs[i];
118 }
119
120 map->count = count;
121
122 if ( ioctl(fd, IOCTL_GNTDEV_MAP_GRANT_REF, map) ) {
123 GTERROR(xgt->logger, "ioctl MAP_GRANT_REF failed");
124 goto out;
125 }
126
127 retry:
128 addr = mmap(NULL, XC_PAGE_SIZE * count, prot, MAP_SHARED, fd,
129 map->index);
130
131 if (addr == MAP_FAILED && errno == EAGAIN)
132 {
133 /*
134 * The grant hypercall can return EAGAIN if the granted page
135 * is swapped out. Since the paging daemon may be in the same
136 * domain, the hypercall cannot block without causing a
137 * deadlock.
138 *
139 * Because there are no notifications when the page is swapped
140 * in, wait a bit before retrying, and hope that the page will
141 * arrive eventually.
142 */
143 usleep(1000);
144 goto retry;
145 }
146
147 if (addr != MAP_FAILED)
148 {
149 int rv = 0;
150 struct ioctl_gntdev_unmap_notify notify;
151 notify.index = map->index;
152 notify.action = 0;
153 if (notify_offset < XC_PAGE_SIZE * count) {
154 notify.index += notify_offset;
155 notify.action |= UNMAP_NOTIFY_CLEAR_BYTE;
156 }
157 if (notify_port != -1) {
158 notify.event_channel_port = notify_port;
159 notify.action |= UNMAP_NOTIFY_SEND_EVENT;
160 }
161 if (notify.action)
162 rv = ioctl(fd, IOCTL_GNTDEV_SET_UNMAP_NOTIFY, ¬ify);
163 if (rv) {
164 GTERROR(xgt->logger, "ioctl SET_UNMAP_NOTIFY failed");
165 munmap(addr, count * XC_PAGE_SIZE);
166 addr = MAP_FAILED;
167 }
168 }
169
170 if (addr == MAP_FAILED)
171 {
172 int saved_errno = errno;
173 struct ioctl_gntdev_unmap_grant_ref unmap_grant;
174
175 /* Unmap the driver slots used to store the grant information. */
176 GTERROR(xgt->logger, "mmap failed");
177 unmap_grant.index = map->index;
178 unmap_grant.count = count;
179 ioctl(fd, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant);
180 errno = saved_errno;
181 addr = NULL;
182 }
183
184 out:
185 if ( map_size > os_page_size )
186 munmap(map, map_size);
187
188 return addr;
189 }
190
osdep_gnttab_unmap(xengnttab_handle * xgt,void * start_address,uint32_t count)191 int osdep_gnttab_unmap(xengnttab_handle *xgt,
192 void *start_address,
193 uint32_t count)
194 {
195 int fd = xgt->fd;
196 struct ioctl_gntdev_get_offset_for_vaddr get_offset;
197 struct ioctl_gntdev_unmap_grant_ref unmap_grant;
198 int rc;
199
200 if ( start_address == NULL )
201 {
202 errno = EINVAL;
203 return -1;
204 }
205
206 /* First, it is necessary to get the offset which was initially used to
207 * mmap() the pages.
208 */
209 get_offset.vaddr = (unsigned long)start_address;
210 if ( (rc = ioctl(fd, IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR,
211 &get_offset)) )
212 return rc;
213
214 if ( get_offset.count != count )
215 {
216 errno = EINVAL;
217 return -1;
218 }
219
220 /* Next, unmap the memory. */
221 if ( (rc = munmap(start_address, count * XC_PAGE_SIZE)) )
222 return rc;
223
224 /* Finally, unmap the driver slots used to store the grant information. */
225 unmap_grant.index = get_offset.offset;
226 unmap_grant.count = count;
227 if ( (rc = ioctl(fd, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant)) )
228 return rc;
229
230 return 0;
231 }
232
osdep_gnttab_grant_copy(xengnttab_handle * xgt,uint32_t count,xengnttab_grant_copy_segment_t * segs)233 int osdep_gnttab_grant_copy(xengnttab_handle *xgt,
234 uint32_t count,
235 xengnttab_grant_copy_segment_t *segs)
236 {
237 int rc;
238 int fd = xgt->fd;
239 struct ioctl_gntdev_grant_copy copy;
240
241 BUILD_BUG_ON(sizeof(struct ioctl_gntdev_grant_copy_segment) !=
242 sizeof(xengnttab_grant_copy_segment_t));
243
244 BUILD_BUG_ON(__alignof__(struct ioctl_gntdev_grant_copy_segment) !=
245 __alignof__(xengnttab_grant_copy_segment_t));
246
247 BUILD_BUG_ON(offsetof(struct ioctl_gntdev_grant_copy_segment,
248 source.virt) !=
249 offsetof(xengnttab_grant_copy_segment_t,
250 source.virt));
251 BUILD_BUG_ON(offsetof(struct ioctl_gntdev_grant_copy_segment,
252 source.foreign) !=
253 offsetof(xengnttab_grant_copy_segment_t,
254 source.foreign));
255 BUILD_BUG_ON(offsetof(struct ioctl_gntdev_grant_copy_segment,
256 source.foreign.ref) !=
257 offsetof(xengnttab_grant_copy_segment_t,
258 source.foreign));
259 BUILD_BUG_ON(offsetof(struct ioctl_gntdev_grant_copy_segment,
260 source.foreign.offset) !=
261 offsetof(xengnttab_grant_copy_segment_t,
262 source.foreign.offset));
263 BUILD_BUG_ON(offsetof(struct ioctl_gntdev_grant_copy_segment,
264 source.foreign.domid) !=
265 offsetof(xengnttab_grant_copy_segment_t,
266 source.foreign.domid));
267
268 BUILD_BUG_ON(offsetof(struct ioctl_gntdev_grant_copy_segment,
269 dest.virt) !=
270 offsetof(xengnttab_grant_copy_segment_t,
271 dest.virt));
272 BUILD_BUG_ON(offsetof(struct ioctl_gntdev_grant_copy_segment,
273 dest.foreign) !=
274 offsetof(xengnttab_grant_copy_segment_t,
275 dest.foreign));
276 BUILD_BUG_ON(offsetof(struct ioctl_gntdev_grant_copy_segment,
277 dest.foreign.ref) !=
278 offsetof(xengnttab_grant_copy_segment_t,
279 dest.foreign));
280 BUILD_BUG_ON(offsetof(struct ioctl_gntdev_grant_copy_segment,
281 dest.foreign.offset) !=
282 offsetof(xengnttab_grant_copy_segment_t,
283 dest.foreign.offset));
284 BUILD_BUG_ON(offsetof(struct ioctl_gntdev_grant_copy_segment,
285 dest.foreign.domid) !=
286 offsetof(xengnttab_grant_copy_segment_t,
287 dest.foreign.domid));
288
289 BUILD_BUG_ON(offsetof(struct ioctl_gntdev_grant_copy_segment,
290 len) !=
291 offsetof(xengnttab_grant_copy_segment_t, len));
292 BUILD_BUG_ON(offsetof(struct ioctl_gntdev_grant_copy_segment,
293 flags) !=
294 offsetof(xengnttab_grant_copy_segment_t, flags));
295 BUILD_BUG_ON(offsetof(struct ioctl_gntdev_grant_copy_segment,
296 status) !=
297 offsetof(xengnttab_grant_copy_segment_t, status));
298
299 copy.segments = (struct ioctl_gntdev_grant_copy_segment *)segs;
300 copy.count = count;
301
302 rc = ioctl(fd, IOCTL_GNTDEV_GRANT_COPY, ©);
303 if (rc)
304 GTERROR(xgt->logger, "ioctl GRANT COPY failed %d ", errno);
305
306 return rc;
307 }
308
osdep_gnttab_dmabuf_exp_from_refs(xengnttab_handle * xgt,uint32_t domid,uint32_t flags,uint32_t count,const uint32_t * refs,uint32_t * dmabuf_fd)309 int osdep_gnttab_dmabuf_exp_from_refs(xengnttab_handle *xgt, uint32_t domid,
310 uint32_t flags, uint32_t count,
311 const uint32_t *refs,
312 uint32_t *dmabuf_fd)
313 {
314 struct ioctl_gntdev_dmabuf_exp_from_refs *from_refs = NULL;
315 int rc = -1;
316
317 if ( !count )
318 {
319 errno = EINVAL;
320 goto out;
321 }
322
323 from_refs = malloc(sizeof(*from_refs) +
324 (count - 1) * sizeof(from_refs->refs[0]));
325 if ( !from_refs )
326 {
327 errno = ENOMEM;
328 goto out;
329 }
330
331 from_refs->flags = flags;
332 from_refs->count = count;
333 from_refs->domid = domid;
334
335 memcpy(from_refs->refs, refs, count * sizeof(from_refs->refs[0]));
336
337 if ( (rc = ioctl(xgt->fd, IOCTL_GNTDEV_DMABUF_EXP_FROM_REFS, from_refs)) )
338 {
339 GTERROR(xgt->logger, "ioctl DMABUF_EXP_FROM_REFS failed");
340 goto out;
341 }
342
343 *dmabuf_fd = from_refs->fd;
344 rc = 0;
345
346 out:
347 free(from_refs);
348 return rc;
349 }
350
osdep_gnttab_dmabuf_exp_wait_released(xengnttab_handle * xgt,uint32_t fd,uint32_t wait_to_ms)351 int osdep_gnttab_dmabuf_exp_wait_released(xengnttab_handle *xgt,
352 uint32_t fd, uint32_t wait_to_ms)
353 {
354 struct ioctl_gntdev_dmabuf_exp_wait_released wait;
355 int rc;
356
357 wait.fd = fd;
358 wait.wait_to_ms = wait_to_ms;
359
360 if ( (rc = ioctl(xgt->fd, IOCTL_GNTDEV_DMABUF_EXP_WAIT_RELEASED, &wait)) )
361 {
362 if ( errno == ENOENT )
363 {
364 /* The buffer may have already been released. */
365 errno = 0;
366 rc = 0;
367 } else
368 GTERROR(xgt->logger, "ioctl DMABUF_EXP_WAIT_RELEASED failed");
369 }
370
371 return rc;
372 }
373
osdep_gnttab_dmabuf_imp_to_refs(xengnttab_handle * xgt,uint32_t domid,uint32_t fd,uint32_t count,uint32_t * refs)374 int osdep_gnttab_dmabuf_imp_to_refs(xengnttab_handle *xgt, uint32_t domid,
375 uint32_t fd, uint32_t count, uint32_t *refs)
376 {
377 struct ioctl_gntdev_dmabuf_imp_to_refs *to_refs = NULL;
378 int rc = -1;
379
380 if ( !count )
381 {
382 errno = EINVAL;
383 goto out;
384 }
385
386 to_refs = malloc(sizeof(*to_refs) +
387 (count - 1) * sizeof(to_refs->refs[0]));
388 if ( !to_refs )
389 {
390 errno = ENOMEM;
391 goto out;
392 }
393
394 to_refs->fd = fd;
395 to_refs->count = count;
396 to_refs->domid = domid;
397
398 if ( (rc = ioctl(xgt->fd, IOCTL_GNTDEV_DMABUF_IMP_TO_REFS, to_refs)) )
399 {
400 GTERROR(xgt->logger, "ioctl DMABUF_IMP_TO_REFS failed");
401 goto out;
402 }
403
404 memcpy(refs, to_refs->refs, count * sizeof(*refs));
405 rc = 0;
406
407 out:
408 free(to_refs);
409 return rc;
410 }
411
osdep_gnttab_dmabuf_imp_release(xengnttab_handle * xgt,uint32_t fd)412 int osdep_gnttab_dmabuf_imp_release(xengnttab_handle *xgt, uint32_t fd)
413 {
414 struct ioctl_gntdev_dmabuf_imp_release release;
415 int rc;
416
417 release.fd = fd;
418
419 if ( (rc = ioctl(xgt->fd, IOCTL_GNTDEV_DMABUF_IMP_RELEASE, &release)) )
420 GTERROR(xgt->logger, "ioctl DMABUF_IMP_RELEASE failed");
421
422 return rc;
423 }
424
osdep_gntshr_open(xengntshr_handle * xgs)425 int osdep_gntshr_open(xengntshr_handle *xgs)
426 {
427 int fd = open(DEVXEN "gntalloc", O_RDWR);
428 if ( fd == -1 )
429 return -1;
430 xgs->fd = fd;
431 return 0;
432 }
433
osdep_gntshr_close(xengntshr_handle * xgs)434 int osdep_gntshr_close(xengntshr_handle *xgs)
435 {
436 if ( xgs->fd == -1 )
437 return 0;
438
439 return close(xgs->fd);
440 }
441
osdep_gntshr_share_pages(xengntshr_handle * xgs,uint32_t domid,int count,uint32_t * refs,int writable,uint32_t notify_offset,evtchn_port_t notify_port)442 void *osdep_gntshr_share_pages(xengntshr_handle *xgs,
443 uint32_t domid, int count,
444 uint32_t *refs, int writable,
445 uint32_t notify_offset,
446 evtchn_port_t notify_port)
447 {
448 struct ioctl_gntalloc_alloc_gref *gref_info = NULL;
449 struct ioctl_gntalloc_unmap_notify notify;
450 struct ioctl_gntalloc_dealloc_gref gref_drop;
451 int fd = xgs->fd;
452 int err;
453 void *area = NULL;
454 gref_info = malloc(sizeof(*gref_info) + count * sizeof(uint32_t));
455 if (!gref_info)
456 return NULL;
457 gref_info->domid = domid;
458 gref_info->flags = writable ? GNTALLOC_FLAG_WRITABLE : 0;
459 gref_info->count = count;
460
461 err = ioctl(fd, IOCTL_GNTALLOC_ALLOC_GREF, gref_info);
462 if (err) {
463 GSERROR(xgs->logger, "ioctl failed");
464 goto out;
465 }
466
467 area = mmap(NULL, count * XC_PAGE_SIZE, PROT_READ | PROT_WRITE,
468 MAP_SHARED, fd, gref_info->index);
469
470 if (area == MAP_FAILED) {
471 area = NULL;
472 GSERROR(xgs->logger, "mmap failed");
473 goto out_remove_fdmap;
474 }
475
476 notify.index = gref_info->index;
477 notify.action = 0;
478 if (notify_offset < XC_PAGE_SIZE * count) {
479 notify.index += notify_offset;
480 notify.action |= UNMAP_NOTIFY_CLEAR_BYTE;
481 }
482 if (notify_port != -1) {
483 notify.event_channel_port = notify_port;
484 notify.action |= UNMAP_NOTIFY_SEND_EVENT;
485 }
486 if (notify.action)
487 err = ioctl(fd, IOCTL_GNTALLOC_SET_UNMAP_NOTIFY, ¬ify);
488 if (err) {
489 GSERROR(xgs->logger, "ioctl SET_UNMAP_NOTIFY failed");
490 munmap(area, count * XC_PAGE_SIZE);
491 area = NULL;
492 }
493
494 memcpy(refs, gref_info->gref_ids, count * sizeof(uint32_t));
495
496 out_remove_fdmap:
497 /* Removing the mapping from the file descriptor does not cause the pages to
498 * be deallocated until the mapping is removed.
499 */
500 gref_drop.index = gref_info->index;
501 gref_drop.count = count;
502 ioctl(fd, IOCTL_GNTALLOC_DEALLOC_GREF, &gref_drop);
503 out:
504 free(gref_info);
505 return area;
506 }
507
osdep_gntshr_unshare(xengntshr_handle * xgs,void * start_address,uint32_t count)508 int osdep_gntshr_unshare(xengntshr_handle *xgs,
509 void *start_address, uint32_t count)
510 {
511 return munmap(start_address, count * XC_PAGE_SIZE);
512 }
513
514 /*
515 * Local variables:
516 * mode: C
517 * c-file-style: "BSD"
518 * c-basic-offset: 4
519 * tab-width: 4
520 * indent-tabs-mode: nil
521 * End:
522 */
523