1 /*
2  * Copyright (c) 2007-2008, D G Murray <Derek.Murray@cl.cam.ac.uk>
3  * Copyright (c) 2016-2017, Akshay Jaggi <jaggi@FreeBSD.org>
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 linux.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 
28 #include <sys/ioctl.h>
29 #include <sys/mman.h>
30 
31 #include <xen/sys/gntdev.h>
32 
33 #include "private.h"
34 
35 #define PAGE_SHIFT           12
36 #define PAGE_SIZE            (1UL << PAGE_SHIFT)
37 #define PAGE_MASK            (~(PAGE_SIZE-1))
38 
39 #define DEVXEN "/dev/xen/gntdev"
40 
osdep_gnttab_open(xengnttab_handle * xgt)41 int osdep_gnttab_open(xengnttab_handle *xgt)
42 {
43     int fd = open(DEVXEN, O_RDWR|O_CLOEXEC);
44 
45     if ( fd == -1 )
46         return -1;
47     xgt->fd = fd;
48 
49     return 0;
50 }
51 
osdep_gnttab_close(xengnttab_handle * xgt)52 int osdep_gnttab_close(xengnttab_handle *xgt)
53 {
54     if ( xgt->fd == -1 )
55         return 0;
56 
57     return close(xgt->fd);
58 }
59 
osdep_gnttab_set_max_grants(xengnttab_handle * xgt,uint32_t count)60 int osdep_gnttab_set_max_grants(xengnttab_handle *xgt, uint32_t count)
61 {
62     return 0;
63 }
64 
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)65 void *osdep_gnttab_grant_map(xengnttab_handle *xgt,
66                              uint32_t count, int flags, int prot,
67                              uint32_t *domids, uint32_t *refs,
68                              uint32_t notify_offset,
69                              evtchn_port_t notify_port)
70 {
71     uint32_t i;
72     int fd = xgt->fd;
73     struct ioctl_gntdev_map_grant_ref *map;
74     void *addr = NULL;
75     int domids_stride;
76     unsigned int map_size = ROUNDUP((sizeof(*map) + (count - 1) *
77                                     sizeof(struct ioctl_gntdev_map_grant_ref)),
78                                     PAGE_SHIFT);
79 
80     domids_stride = (flags & XENGNTTAB_GRANT_MAP_SINGLE_DOMAIN) ? 0 : 1;
81     if ( map_size <= PAGE_SIZE )
82         map = malloc(sizeof(*map) +
83                      (count - 1) * sizeof(struct ioctl_gntdev_map_grant_ref));
84     else
85     {
86         map = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
87                    MAP_PRIVATE | MAP_ANON, -1, 0);
88         if ( map == MAP_FAILED )
89         {
90             GTERROR(xgt->logger, "anon mmap of map failed");
91             return NULL;
92         }
93     }
94 
95     for ( i = 0; i < count; i++ )
96     {
97         map->refs[i].domid = domids[i * domids_stride];
98         map->refs[i].ref = refs[i];
99     }
100 
101     map->count = count;
102 
103     if ( ioctl(fd, IOCTL_GNTDEV_MAP_GRANT_REF, map) )
104     {
105         GTERROR(xgt->logger, "ioctl MAP_GRANT_REF failed");
106         goto out;
107     }
108 
109     addr = mmap(NULL, PAGE_SIZE * count, prot, MAP_SHARED, fd,
110                 map->index);
111     if ( addr != MAP_FAILED )
112     {
113         int rv = 0;
114         struct ioctl_gntdev_unmap_notify notify;
115 
116         notify.index = map->index;
117         notify.action = 0;
118         if ( notify_offset < PAGE_SIZE * count )
119         {
120             notify.index += notify_offset;
121             notify.action |= UNMAP_NOTIFY_CLEAR_BYTE;
122         }
123         if ( notify_port != -1 )
124         {
125             notify.event_channel_port = notify_port;
126             notify.action |= UNMAP_NOTIFY_SEND_EVENT;
127         }
128         if ( notify.action )
129             rv = ioctl(fd, IOCTL_GNTDEV_SET_UNMAP_NOTIFY, &notify);
130         if ( rv )
131         {
132             GTERROR(xgt->logger, "ioctl SET_UNMAP_NOTIFY failed");
133             munmap(addr, count * PAGE_SIZE);
134             addr = MAP_FAILED;
135         }
136     }
137     if ( addr == MAP_FAILED )
138     {
139         int saved_errno = errno;
140         struct ioctl_gntdev_unmap_grant_ref unmap_grant;
141 
142         /* Unmap the driver slots used to store the grant information. */
143         GTERROR(xgt->logger, "mmap failed");
144         unmap_grant.index = map->index;
145         unmap_grant.count = count;
146         ioctl(fd, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant);
147         errno = saved_errno;
148         addr = NULL;
149     }
150 
151  out:
152     if ( map_size > PAGE_SIZE )
153         munmap(map, map_size);
154     else
155         free(map);
156 
157     return addr;
158 }
159 
osdep_gnttab_unmap(xengnttab_handle * xgt,void * start_address,uint32_t count)160 int osdep_gnttab_unmap(xengnttab_handle *xgt,
161                        void *start_address,
162                        uint32_t count)
163 {
164     int rc;
165     int fd = xgt->fd;
166     struct ioctl_gntdev_unmap_grant_ref unmap_grant;
167     struct ioctl_gntdev_get_offset_for_vaddr get_offset;
168 
169     if ( start_address == NULL )
170     {
171         errno = EINVAL;
172         return -1;
173     }
174 
175     /*
176      * First, it is necessary to get the offset which was initially used to
177      * mmap() the pages.
178      */
179     get_offset.vaddr = (unsigned long)start_address;
180     if ( (rc = ioctl(fd, IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR,
181                      &get_offset)) )
182         return rc;
183 
184     if ( get_offset.count != count )
185     {
186         errno = EINVAL;
187         return -1;
188     }
189 
190     /* Next, unmap the memory. */
191     if ( (rc = munmap(start_address, count * PAGE_SIZE)) )
192         return rc;
193 
194     /* Finally, unmap the driver slots used to store the grant information. */
195     unmap_grant.index = get_offset.offset;
196     unmap_grant.count = count;
197     if ( (rc = ioctl(fd, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant)) )
198         return rc;
199 
200     return 0;
201 }
202 
osdep_gnttab_grant_copy(xengnttab_handle * xgt,uint32_t count,xengnttab_grant_copy_segment_t * segs)203 int osdep_gnttab_grant_copy(xengnttab_handle *xgt,
204                             uint32_t count,
205                             xengnttab_grant_copy_segment_t *segs)
206 {
207     errno = ENOSYS;
208     return -1;
209 }
210 
osdep_gntshr_open(xengntshr_handle * xgs)211 int osdep_gntshr_open(xengntshr_handle *xgs)
212 {
213 
214     int fd = open(DEVXEN, O_RDWR);
215 
216     if ( fd == -1 )
217         return -1;
218     xgs->fd = fd;
219 
220     return 0;
221 }
222 
osdep_gntshr_close(xengntshr_handle * xgs)223 int osdep_gntshr_close(xengntshr_handle *xgs)
224 {
225     if ( xgs->fd == -1 )
226         return 0;
227 
228     return close(xgs->fd);
229 }
230 
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)231 void *osdep_gntshr_share_pages(xengntshr_handle *xgs,
232                                uint32_t domid, int count,
233                                uint32_t *refs, int writable,
234                                uint32_t notify_offset,
235                                evtchn_port_t notify_port)
236 {
237     int err;
238     int fd = xgs->fd;
239     void *area = NULL;
240     struct ioctl_gntdev_unmap_notify notify;
241     struct ioctl_gntdev_dealloc_gref gref_drop;
242     struct ioctl_gntdev_alloc_gref *gref_info = NULL;
243 
244     gref_info = malloc(sizeof(*gref_info) + count * sizeof(uint32_t));
245     if ( gref_info == NULL )
246         return NULL;
247     gref_info->domid = domid;
248     gref_info->flags = writable ? GNTDEV_ALLOC_FLAG_WRITABLE : 0;
249     gref_info->count = count;
250 
251     err = ioctl(fd, IOCTL_GNTDEV_ALLOC_GREF, gref_info);
252     if ( err )
253     {
254         GSERROR(xgs->logger, "ioctl failed");
255         goto out;
256     }
257 
258     area = mmap(NULL, count * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
259                 fd, gref_info->index);
260 
261     if ( area == MAP_FAILED )
262     {
263         area = NULL;
264         GSERROR(xgs->logger, "mmap failed");
265         goto out_remove_fdmap;
266     }
267 
268     notify.index = gref_info->index;
269     notify.action = 0;
270     if ( notify_offset < PAGE_SIZE * count )
271     {
272         notify.index += notify_offset;
273         notify.action |= UNMAP_NOTIFY_CLEAR_BYTE;
274     }
275     if ( notify_port != -1 )
276     {
277         notify.event_channel_port = notify_port;
278         notify.action |= UNMAP_NOTIFY_SEND_EVENT;
279     }
280     if ( notify.action )
281         err = ioctl(fd, IOCTL_GNTDEV_SET_UNMAP_NOTIFY, &notify);
282     if ( err )
283     {
284         GSERROR(xgs->logger, "ioctl SET_UNMAP_NOTIFY failed");
285         munmap(area, count * PAGE_SIZE);
286         area = NULL;
287     }
288 
289     memcpy(refs, gref_info->gref_ids, count * sizeof(uint32_t));
290 
291  out_remove_fdmap:
292     /*
293      * Removing the mapping from the file descriptor does not cause the
294      * pages to be deallocated until the mapping is removed.
295      */
296     gref_drop.index = gref_info->index;
297     gref_drop.count = count;
298     ioctl(fd, IOCTL_GNTDEV_DEALLOC_GREF, &gref_drop);
299  out:
300     free(gref_info);
301 
302     return area;
303 }
304 
osdep_gntshr_unshare(xengntshr_handle * xgs,void * start_address,uint32_t count)305 int osdep_gntshr_unshare(xengntshr_handle *xgs,
306                          void *start_address, uint32_t count)
307 {
308     return munmap(start_address, count * PAGE_SIZE);
309 }
310 
311 /*
312  * Local variables:
313  * mode: C
314  * c-file-style: "BSD"
315  * c-basic-offset: 4
316  * tab-width: 4
317  * indent-tabs-mode: nil
318  * End:
319  */
320