1 /**
2 * @file
3 * @section AUTHORS
4 *
5 * Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
6 *
7 * Authors:
8 * Rafal Wojtczuk <rafal@invisiblethingslab.com>
9 * Daniel De Graaf <dgdegra@tycho.nsa.gov>
10 *
11 * @section LICENSE
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2.1 of the License, or (at your option) any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; If not, see <http://www.gnu.org/licenses/>.
25 *
26 * @section DESCRIPTION
27 *
28 * This file contains the setup code used to establish the ring buffer.
29 */
30
31 #include <sys/types.h>
32 #include <sys/mman.h>
33 #include <sys/ioctl.h>
34 #include <sys/user.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <stdint.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41
42 #include <xenstore.h>
43 #include <xen/xen.h>
44 #include <xen/sys/evtchn.h>
45 #include <xen/sys/gntalloc.h>
46 #include <xen/sys/gntdev.h>
47 #include <libxenvchan.h>
48
49 #ifndef PAGE_SHIFT
50 #define PAGE_SHIFT 12
51 #endif
52
53 #ifndef PAGE_SIZE
54 #define PAGE_SIZE 4096
55 #endif
56
57 #define SMALL_RING_SHIFT 10
58 #define LARGE_RING_SHIFT 11
59
60 #define MAX_SMALL_RING (1 << SMALL_RING_SHIFT)
61 #define SMALL_RING_OFFSET 1024
62 #define MAX_LARGE_RING (1 << LARGE_RING_SHIFT)
63 #define LARGE_RING_OFFSET 2048
64
65 // if you go over this size, you'll have too many grants to fit in the shared page.
66 #define MAX_RING_SHIFT 20
67 #define MAX_RING_SIZE (1 << MAX_RING_SHIFT)
68
69 #ifndef offsetof
70 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
71 #endif
72
73 #define max(a,b) ((a > b) ? a : b)
74
init_gnt_srv(struct libxenvchan * ctrl,int domain)75 static int init_gnt_srv(struct libxenvchan *ctrl, int domain)
76 {
77 int pages_left = ctrl->read.order >= PAGE_SHIFT ? 1 << (ctrl->read.order - PAGE_SHIFT) : 0;
78 int pages_right = ctrl->write.order >= PAGE_SHIFT ? 1 << (ctrl->write.order - PAGE_SHIFT) : 0;
79 uint32_t ring_ref = -1;
80 void *ring;
81
82 ring = xengntshr_share_page_notify(ctrl->gntshr, domain,
83 &ring_ref, 1, offsetof(struct vchan_interface, srv_live),
84 ctrl->event_port);
85
86 if (!ring)
87 goto out;
88
89 memset(ring, 0, PAGE_SIZE);
90
91 ctrl->ring = ring;
92 ctrl->read.shr = &ctrl->ring->left;
93 ctrl->write.shr = &ctrl->ring->right;
94 ctrl->ring->left_order = ctrl->read.order;
95 ctrl->ring->right_order = ctrl->write.order;
96 ctrl->ring->cli_live = 2;
97 ctrl->ring->srv_live = 1;
98 ctrl->ring->cli_notify = VCHAN_NOTIFY_WRITE;
99
100 switch (ctrl->read.order) {
101 case SMALL_RING_SHIFT:
102 ctrl->read.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
103 break;
104 case LARGE_RING_SHIFT:
105 ctrl->read.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
106 break;
107 default:
108 ctrl->read.buffer = xengntshr_share_pages(ctrl->gntshr, domain,
109 pages_left, ctrl->ring->grants, 1);
110 if (!ctrl->read.buffer)
111 goto out_ring;
112 }
113
114 switch (ctrl->write.order) {
115 case SMALL_RING_SHIFT:
116 ctrl->write.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
117 break;
118 case LARGE_RING_SHIFT:
119 ctrl->write.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
120 break;
121 default:
122 ctrl->write.buffer = xengntshr_share_pages(ctrl->gntshr, domain,
123 pages_right, ctrl->ring->grants + pages_left, 1);
124 if (!ctrl->write.buffer)
125 goto out_unmap_left;
126 }
127
128 out:
129 return ring_ref;
130 out_unmap_left:
131 if (pages_left)
132 xengntshr_unshare(ctrl->gntshr, ctrl->read.buffer, pages_left);
133 out_ring:
134 xengntshr_unshare(ctrl->gntshr, ring, 1);
135 ring_ref = -1;
136 ctrl->ring = NULL;
137 ctrl->write.order = ctrl->read.order = 0;
138 goto out;
139 }
140
init_gnt_cli(struct libxenvchan * ctrl,int domain,uint32_t ring_ref)141 static int init_gnt_cli(struct libxenvchan *ctrl, int domain, uint32_t ring_ref)
142 {
143 int rv = -1;
144 uint32_t *grants;
145
146 ctrl->ring = xengnttab_map_grant_ref_notify(ctrl->gnttab,
147 domain, ring_ref, PROT_READ|PROT_WRITE,
148 offsetof(struct vchan_interface, cli_live), ctrl->event_port);
149
150 if (!ctrl->ring)
151 goto out;
152
153 ctrl->write.order = ctrl->ring->left_order;
154 ctrl->read.order = ctrl->ring->right_order;
155 ctrl->write.shr = &ctrl->ring->left;
156 ctrl->read.shr = &ctrl->ring->right;
157 if (ctrl->write.order < SMALL_RING_SHIFT || ctrl->write.order > MAX_RING_SHIFT)
158 goto out_unmap_ring;
159 if (ctrl->read.order < SMALL_RING_SHIFT || ctrl->read.order > MAX_RING_SHIFT)
160 goto out_unmap_ring;
161 if (ctrl->read.order == ctrl->write.order && ctrl->read.order < PAGE_SHIFT)
162 goto out_unmap_ring;
163
164 grants = ctrl->ring->grants;
165
166 switch (ctrl->write.order) {
167 case SMALL_RING_SHIFT:
168 ctrl->write.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
169 break;
170 case LARGE_RING_SHIFT:
171 ctrl->write.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
172 break;
173 default:
174 {
175 int pages_left = 1 << (ctrl->write.order - PAGE_SHIFT);
176 ctrl->write.buffer = xengnttab_map_domain_grant_refs(ctrl->gnttab,
177 pages_left, domain, grants, PROT_READ|PROT_WRITE);
178 if (!ctrl->write.buffer)
179 goto out_unmap_ring;
180 grants += pages_left;
181 }
182 }
183
184 switch (ctrl->read.order) {
185 case SMALL_RING_SHIFT:
186 ctrl->read.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
187 break;
188 case LARGE_RING_SHIFT:
189 ctrl->read.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
190 break;
191 default:
192 {
193 int pages_right = 1 << (ctrl->read.order - PAGE_SHIFT);
194 ctrl->read.buffer = xengnttab_map_domain_grant_refs(ctrl->gnttab,
195 pages_right, domain, grants, PROT_READ);
196 if (!ctrl->read.buffer)
197 goto out_unmap_left;
198 }
199 }
200
201 rv = 0;
202 out:
203 return rv;
204 out_unmap_left:
205 if (ctrl->write.order >= PAGE_SHIFT)
206 xengnttab_unmap(ctrl->gnttab, ctrl->write.buffer,
207 1 << (ctrl->write.order - PAGE_SHIFT));
208 out_unmap_ring:
209 xengnttab_unmap(ctrl->gnttab, ctrl->ring, 1);
210 ctrl->ring = 0;
211 ctrl->write.order = ctrl->read.order = 0;
212 rv = -1;
213 goto out;
214 }
215
init_evt_srv(struct libxenvchan * ctrl,int domain,struct xentoollog_logger * logger)216 static int init_evt_srv(struct libxenvchan *ctrl, int domain,
217 struct xentoollog_logger *logger)
218 {
219 xenevtchn_port_or_error_t port;
220
221 ctrl->event = xenevtchn_open(logger, 0);
222 if (!ctrl->event)
223 return -1;
224
225 port = xenevtchn_bind_unbound_port(ctrl->event, domain);
226 if (port < 0)
227 goto fail;
228 ctrl->event_port = port;
229
230 if (xenevtchn_unmask(ctrl->event, ctrl->event_port))
231 goto fail;
232
233 return 0;
234
235 fail:
236 if (port >= 0)
237 xenevtchn_unbind(ctrl->event, port);
238
239 xenevtchn_close(ctrl->event);
240 ctrl->event = NULL;
241
242 return -1;
243 }
244
init_xs_srv(struct libxenvchan * ctrl,int domain,const char * xs_base,int ring_ref)245 static int init_xs_srv(struct libxenvchan *ctrl, int domain, const char* xs_base, int ring_ref)
246 {
247 int ret = -1;
248 struct xs_handle *xs;
249 struct xs_permissions perms[2];
250 char buf[64];
251 char ref[16];
252 char* domid_str = NULL;
253 xs = xs_domain_open();
254 if (!xs)
255 goto fail;
256 domid_str = xs_read(xs, 0, "domid", NULL);
257 if (!domid_str)
258 goto fail_xs_open;
259
260 // owner domain is us
261 perms[0].id = atoi(domid_str);
262 // permissions for domains not listed = none
263 perms[0].perms = XS_PERM_NONE;
264 // other domains
265 perms[1].id = domain;
266 perms[1].perms = XS_PERM_READ;
267
268 snprintf(ref, sizeof ref, "%d", ring_ref);
269 snprintf(buf, sizeof buf, "%s/ring-ref", xs_base);
270 if (!xs_write(xs, 0, buf, ref, strlen(ref)))
271 goto fail_xs_open;
272 if (!xs_set_permissions(xs, 0, buf, perms, 2))
273 goto fail_xs_open;
274
275 snprintf(ref, sizeof ref, "%d", ctrl->event_port);
276 snprintf(buf, sizeof buf, "%s/event-channel", xs_base);
277 if (!xs_write(xs, 0, buf, ref, strlen(ref)))
278 goto fail_xs_open;
279 if (!xs_set_permissions(xs, 0, buf, perms, 2))
280 goto fail_xs_open;
281
282 ret = 0;
283 fail_xs_open:
284 free(domid_str);
285 xs_daemon_close(xs);
286 fail:
287 return ret;
288 }
289
min_order(size_t siz)290 static int min_order(size_t siz)
291 {
292 int rv = PAGE_SHIFT;
293 while (siz > (1 << rv))
294 rv++;
295 return rv;
296 }
297
libxenvchan_server_init(struct xentoollog_logger * logger,int domain,const char * xs_path,size_t left_min,size_t right_min)298 struct libxenvchan *libxenvchan_server_init(struct xentoollog_logger *logger,
299 int domain, const char* xs_path,
300 size_t left_min, size_t right_min)
301 {
302 struct libxenvchan *ctrl;
303 int ring_ref;
304 if (left_min > MAX_RING_SIZE || right_min > MAX_RING_SIZE)
305 return 0;
306
307 ctrl = malloc(sizeof(*ctrl));
308 if (!ctrl)
309 return 0;
310
311 ctrl->ring = NULL;
312 ctrl->event = NULL;
313 ctrl->is_server = 1;
314 ctrl->server_persist = 0;
315
316 ctrl->read.order = min_order(left_min);
317 ctrl->write.order = min_order(right_min);
318
319 // if we can avoid allocating extra pages by using in-page rings, do so
320 if (left_min <= MAX_SMALL_RING && right_min <= MAX_LARGE_RING) {
321 ctrl->read.order = SMALL_RING_SHIFT;
322 ctrl->write.order = LARGE_RING_SHIFT;
323 } else if (left_min <= MAX_LARGE_RING && right_min <= MAX_SMALL_RING) {
324 ctrl->read.order = LARGE_RING_SHIFT;
325 ctrl->write.order = SMALL_RING_SHIFT;
326 } else if (left_min <= MAX_LARGE_RING) {
327 ctrl->read.order = LARGE_RING_SHIFT;
328 } else if (right_min <= MAX_LARGE_RING) {
329 ctrl->write.order = LARGE_RING_SHIFT;
330 }
331
332 ctrl->gntshr = xengntshr_open(logger, 0);
333 if (!ctrl->gntshr) {
334 free(ctrl);
335 return 0;
336 }
337
338 if (init_evt_srv(ctrl, domain, logger))
339 goto out;
340 ring_ref = init_gnt_srv(ctrl, domain);
341 if (ring_ref < 0)
342 goto out;
343 if (init_xs_srv(ctrl, domain, xs_path, ring_ref))
344 goto out;
345 return ctrl;
346 out:
347 libxenvchan_close(ctrl);
348 return 0;
349 }
350
init_evt_cli(struct libxenvchan * ctrl,int domain,struct xentoollog_logger * logger)351 static int init_evt_cli(struct libxenvchan *ctrl, int domain,
352 struct xentoollog_logger *logger)
353 {
354 xenevtchn_port_or_error_t port;
355
356 ctrl->event = xenevtchn_open(logger, 0);
357 if (!ctrl->event)
358 return -1;
359
360 port = xenevtchn_bind_interdomain(ctrl->event,
361 domain, ctrl->event_port);
362 if (port < 0)
363 goto fail;
364 ctrl->event_port = port;
365
366 if (xenevtchn_unmask(ctrl->event, ctrl->event_port))
367 goto fail;
368
369 return 0;
370
371 fail:
372 if (port >= 0)
373 xenevtchn_unbind(ctrl->event, port);
374
375 xenevtchn_close(ctrl->event);
376 ctrl->event = NULL;
377
378 return -1;
379 }
380
381
libxenvchan_client_init(struct xentoollog_logger * logger,int domain,const char * xs_path)382 struct libxenvchan *libxenvchan_client_init(struct xentoollog_logger *logger,
383 int domain, const char* xs_path)
384 {
385 struct libxenvchan *ctrl = malloc(sizeof(struct libxenvchan));
386 struct xs_handle *xs = NULL;
387 char buf[64];
388 char *ref;
389 int ring_ref;
390 unsigned int len;
391
392 if (!ctrl)
393 return 0;
394 ctrl->ring = NULL;
395 ctrl->event = NULL;
396 ctrl->gnttab = NULL;
397 ctrl->write.order = ctrl->read.order = 0;
398 ctrl->is_server = 0;
399
400 xs = xs_daemon_open();
401 if (!xs)
402 xs = xs_domain_open();
403 if (!xs)
404 goto fail;
405
406 // find xenstore entry
407 snprintf(buf, sizeof buf, "%s/ring-ref", xs_path);
408 ref = xs_read(xs, 0, buf, &len);
409 if (!ref)
410 goto fail;
411 ring_ref = atoi(ref);
412 free(ref);
413 if (!ring_ref)
414 goto fail;
415 snprintf(buf, sizeof buf, "%s/event-channel", xs_path);
416 ref = xs_read(xs, 0, buf, &len);
417 if (!ref)
418 goto fail;
419 ctrl->event_port = atoi(ref);
420 free(ref);
421 if (!ctrl->event_port)
422 goto fail;
423
424 ctrl->gnttab = xengnttab_open(logger, 0);
425 if (!ctrl->gnttab)
426 goto fail;
427
428 // set up event channel
429 if (init_evt_cli(ctrl, domain, logger))
430 goto fail;
431
432 // set up shared page(s)
433 if (init_gnt_cli(ctrl, domain, ring_ref))
434 goto fail;
435
436 ctrl->ring->cli_live = 1;
437 ctrl->ring->srv_notify = VCHAN_NOTIFY_WRITE;
438
439 out:
440 if (xs)
441 xs_daemon_close(xs);
442 return ctrl;
443 fail:
444 libxenvchan_close(ctrl);
445 ctrl = NULL;
446 goto out;
447 }
448