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