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, &notify);
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, &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, &notify);
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