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, ¬ify);
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, ¬ify);
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