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 refs_size = ROUNDUP(count *
77                                      sizeof(struct ioctl_gntdev_grant_ref),
78                                      PAGE_SHIFT);
79 
80     domids_stride = (flags & XENGNTTAB_GRANT_MAP_SINGLE_DOMAIN) ? 0 : 1;
81     if ( refs_size <= PAGE_SIZE )
82         map.refs = malloc(refs_size);
83     else
84     {
85         map.refs = mmap(NULL, refs_size, PROT_READ | PROT_WRITE,
86                         MAP_PRIVATE | MAP_ANON, -1, 0);
87         if ( map.refs == MAP_FAILED )
88         {
89             GTERROR(xgt->logger, "anon mmap of map failed");
90             return NULL;
91         }
92     }
93 
94     for ( i = 0; i < count; i++ )
95     {
96         map.refs[i].domid = domids[i * domids_stride];
97         map.refs[i].ref = refs[i];
98     }
99 
100     map.count = count;
101 
102     if ( ioctl(fd, IOCTL_GNTDEV_MAP_GRANT_REF, &map) )
103     {
104         GTERROR(xgt->logger, "ioctl MAP_GRANT_REF failed");
105         goto out;
106     }
107 
108     addr = mmap(NULL, PAGE_SIZE * count, prot, MAP_SHARED, fd,
109                 map.index);
110     if ( addr != MAP_FAILED )
111     {
112         int rv = 0;
113         struct ioctl_gntdev_unmap_notify notify;
114 
115         notify.index = map.index;
116         notify.action = 0;
117         if ( notify_offset < PAGE_SIZE * count )
118         {
119             notify.index += notify_offset;
120             notify.action |= UNMAP_NOTIFY_CLEAR_BYTE;
121         }
122         if ( notify_port != -1 )
123         {
124             notify.event_channel_port = notify_port;
125             notify.action |= UNMAP_NOTIFY_SEND_EVENT;
126         }
127         if ( notify.action )
128             rv = ioctl(fd, IOCTL_GNTDEV_SET_UNMAP_NOTIFY, &notify);
129         if ( rv )
130         {
131             GTERROR(xgt->logger, "ioctl SET_UNMAP_NOTIFY failed");
132             munmap(addr, count * PAGE_SIZE);
133             addr = MAP_FAILED;
134         }
135     }
136     if ( addr == MAP_FAILED )
137     {
138         int saved_errno = errno;
139         struct ioctl_gntdev_unmap_grant_ref unmap_grant;
140 
141         /* Unmap the driver slots used to store the grant information. */
142         GTERROR(xgt->logger, "mmap failed");
143         unmap_grant.index = map.index;
144         unmap_grant.count = count;
145         ioctl(fd, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant);
146         errno = saved_errno;
147         addr = NULL;
148     }
149 
150  out:
151     if ( refs_size > PAGE_SIZE )
152         munmap(map.refs, refs_size);
153     else
154         free(map.refs);
155 
156     return addr;
157 }
158 
osdep_gnttab_unmap(xengnttab_handle * xgt,void * start_address,uint32_t count)159 int osdep_gnttab_unmap(xengnttab_handle *xgt,
160                        void *start_address,
161                        uint32_t count)
162 {
163     int rc;
164     int fd = xgt->fd;
165     struct ioctl_gntdev_unmap_grant_ref unmap_grant;
166     struct ioctl_gntdev_get_offset_for_vaddr get_offset;
167 
168     if ( start_address == NULL )
169     {
170         errno = EINVAL;
171         return -1;
172     }
173 
174     /*
175      * First, it is necessary to get the offset which was initially used to
176      * mmap() the pages.
177      */
178     get_offset.vaddr = (unsigned long)start_address;
179     if ( (rc = ioctl(fd, IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR,
180                      &get_offset)) )
181         return rc;
182 
183     if ( get_offset.count != count )
184     {
185         errno = EINVAL;
186         return -1;
187     }
188 
189     /* Next, unmap the memory. */
190     if ( (rc = munmap(start_address, count * PAGE_SIZE)) )
191         return rc;
192 
193     /* Finally, unmap the driver slots used to store the grant information. */
194     unmap_grant.index = get_offset.offset;
195     unmap_grant.count = count;
196     if ( (rc = ioctl(fd, IOCTL_GNTDEV_UNMAP_GRANT_REF, &unmap_grant)) )
197         return rc;
198 
199     return 0;
200 }
201 
osdep_gnttab_grant_copy(xengnttab_handle * xgt,uint32_t count,xengnttab_grant_copy_segment_t * segs)202 int osdep_gnttab_grant_copy(xengnttab_handle *xgt,
203                             uint32_t count,
204                             xengnttab_grant_copy_segment_t *segs)
205 {
206     errno = ENOSYS;
207     return -1;
208 }
209 
osdep_gntshr_open(xengntshr_handle * xgs)210 int osdep_gntshr_open(xengntshr_handle *xgs)
211 {
212 
213     int fd = open(DEVXEN, O_RDWR);
214 
215     if ( fd == -1 )
216         return -1;
217     xgs->fd = fd;
218 
219     return 0;
220 }
221 
osdep_gntshr_close(xengntshr_handle * xgs)222 int osdep_gntshr_close(xengntshr_handle *xgs)
223 {
224     if ( xgs->fd == -1 )
225         return 0;
226 
227     return close(xgs->fd);
228 }
229 
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)230 void *osdep_gntshr_share_pages(xengntshr_handle *xgs,
231                                uint32_t domid, int count,
232                                uint32_t *refs, int writable,
233                                uint32_t notify_offset,
234                                evtchn_port_t notify_port)
235 {
236     int err;
237     int fd = xgs->fd;
238     void *area = NULL;
239     struct ioctl_gntdev_unmap_notify notify;
240     struct ioctl_gntdev_dealloc_gref gref_drop;
241     struct ioctl_gntdev_alloc_gref gref_info;
242 
243     gref_info.gref_ids = malloc(count * sizeof(uint32_t));
244     if ( gref_info.gref_ids == NULL )
245         return NULL;
246     gref_info.domid = domid;
247     gref_info.flags = writable ? GNTDEV_ALLOC_FLAG_WRITABLE : 0;
248     gref_info.count = count;
249 
250     err = ioctl(fd, IOCTL_GNTDEV_ALLOC_GREF, &gref_info);
251     if ( err )
252     {
253         GSERROR(xgs->logger, "ioctl failed");
254         goto out;
255     }
256 
257     area = mmap(NULL, count * PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
258                 fd, gref_info.index);
259 
260     if ( area == MAP_FAILED )
261     {
262         area = NULL;
263         GSERROR(xgs->logger, "mmap failed");
264         goto out_remove_fdmap;
265     }
266 
267     notify.index = gref_info.index;
268     notify.action = 0;
269     if ( notify_offset < PAGE_SIZE * count )
270     {
271         notify.index += notify_offset;
272         notify.action |= UNMAP_NOTIFY_CLEAR_BYTE;
273     }
274     if ( notify_port != -1 )
275     {
276         notify.event_channel_port = notify_port;
277         notify.action |= UNMAP_NOTIFY_SEND_EVENT;
278     }
279     if ( notify.action )
280         err = ioctl(fd, IOCTL_GNTDEV_SET_UNMAP_NOTIFY, &notify);
281     if ( err )
282     {
283         GSERROR(xgs->logger, "ioctl SET_UNMAP_NOTIFY failed");
284         munmap(area, count * PAGE_SIZE);
285         area = NULL;
286     }
287 
288     memcpy(refs, gref_info.gref_ids, count * sizeof(uint32_t));
289 
290  out_remove_fdmap:
291     /*
292      * Removing the mapping from the file descriptor does not cause the
293      * pages to be deallocated until the mapping is removed.
294      */
295     gref_drop.index = gref_info.index;
296     gref_drop.count = count;
297     ioctl(fd, IOCTL_GNTDEV_DEALLOC_GREF, &gref_drop);
298  out:
299     free(gref_info.gref_ids);
300 
301     return area;
302 }
303 
osdep_gntshr_unshare(xengntshr_handle * xgs,void * start_address,uint32_t count)304 int osdep_gntshr_unshare(xengntshr_handle *xgs,
305                          void *start_address, uint32_t count)
306 {
307     return munmap(start_address, count * PAGE_SIZE);
308 }
309 
310 /*
311  * The functions below are Linux-isms that will likely never be implemented
312  * on FreeBSD unless FreeBSD also implements something akin to Linux dmabuf.
313  */
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)314 int osdep_gnttab_dmabuf_exp_from_refs(xengnttab_handle *xgt, uint32_t domid,
315                                       uint32_t flags, uint32_t count,
316                                       const uint32_t *refs,
317                                       uint32_t *dmabuf_fd)
318 {
319     abort();
320 }
321 
osdep_gnttab_dmabuf_exp_wait_released(xengnttab_handle * xgt,uint32_t fd,uint32_t wait_to_ms)322 int osdep_gnttab_dmabuf_exp_wait_released(xengnttab_handle *xgt,
323                                           uint32_t fd, uint32_t wait_to_ms)
324 {
325     abort();
326 }
327 
osdep_gnttab_dmabuf_imp_to_refs(xengnttab_handle * xgt,uint32_t domid,uint32_t fd,uint32_t count,uint32_t * refs)328 int osdep_gnttab_dmabuf_imp_to_refs(xengnttab_handle *xgt, uint32_t domid,
329                                     uint32_t fd, uint32_t count, uint32_t *refs)
330 {
331     abort();
332 }
333 
osdep_gnttab_dmabuf_imp_release(xengnttab_handle * xgt,uint32_t fd)334 int osdep_gnttab_dmabuf_imp_release(xengnttab_handle *xgt, uint32_t fd)
335 {
336     abort();
337 }
338 
339 /*
340  * Local variables:
341  * mode: C
342  * c-file-style: "BSD"
343  * c-basic-offset: 4
344  * tab-width: 4
345  * indent-tabs-mode: nil
346  * End:
347  */
348