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 <stddef.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <stdint.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42
43 #include <xenstore.h>
44 #include <xen/xen.h>
45 #include <xen/sys/evtchn.h>
46 #include <xen/sys/gntalloc.h>
47 #include <xen/sys/gntdev.h>
48 #include <libxenvchan.h>
49 #include <xen-tools/common-macros.h>
50
51 #include "vchan.h"
52
53 #ifndef PAGE_SHIFT
54 #define PAGE_SHIFT 12
55 #endif
56
57 #ifndef PAGE_SIZE
58 #define PAGE_SIZE 4096
59 #endif
60
61 #define SMALL_RING_SHIFT 10
62 #define LARGE_RING_SHIFT 11
63
64 #define MAX_SMALL_RING (1 << SMALL_RING_SHIFT)
65 #define SMALL_RING_OFFSET 1024
66 #define MAX_LARGE_RING (1 << LARGE_RING_SHIFT)
67 #define LARGE_RING_OFFSET 2048
68
69 // if you go over this size, you'll have too many grants to fit in the shared page.
70 #define MAX_RING_SHIFT 20
71 #define MAX_RING_SIZE (1 << MAX_RING_SHIFT)
72
init_gnt_srv(struct libxenvchan * ctrl,int domain)73 static int init_gnt_srv(struct libxenvchan *ctrl, int domain)
74 {
75 int pages_left = ctrl->read.order >= PAGE_SHIFT ? 1 << (ctrl->read.order - PAGE_SHIFT) : 0;
76 int pages_right = ctrl->write.order >= PAGE_SHIFT ? 1 << (ctrl->write.order - PAGE_SHIFT) : 0;
77 uint32_t ring_ref = -1;
78 void *ring;
79
80 ring = xengntshr_share_page_notify(ctrl->gntshr, domain,
81 &ring_ref, 1, offsetof(struct vchan_interface, srv_live),
82 ctrl->event_port);
83
84 if (!ring)
85 goto out;
86
87 memset(ring, 0, PAGE_SIZE);
88
89 ctrl->ring = ring;
90 ctrl->read.shr = &ctrl->ring->left;
91 ctrl->write.shr = &ctrl->ring->right;
92 ctrl->ring->left_order = ctrl->read.order;
93 ctrl->ring->right_order = ctrl->write.order;
94 ctrl->ring->cli_live = 2;
95 ctrl->ring->srv_live = 1;
96 ctrl->ring->cli_notify = VCHAN_NOTIFY_WRITE;
97
98 switch (ctrl->read.order) {
99 case SMALL_RING_SHIFT:
100 ctrl->read.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
101 break;
102 case LARGE_RING_SHIFT:
103 ctrl->read.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
104 break;
105 default:
106 ctrl->read.buffer = xengntshr_share_pages(ctrl->gntshr, domain,
107 pages_left, ctrl->ring->grants, 1);
108 if (!ctrl->read.buffer)
109 goto out_ring;
110 }
111
112 switch (ctrl->write.order) {
113 case SMALL_RING_SHIFT:
114 ctrl->write.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
115 break;
116 case LARGE_RING_SHIFT:
117 ctrl->write.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
118 break;
119 default:
120 ctrl->write.buffer = xengntshr_share_pages(ctrl->gntshr, domain,
121 pages_right, ctrl->ring->grants + pages_left, 1);
122 if (!ctrl->write.buffer)
123 goto out_unmap_left;
124 }
125
126 out:
127 return ring_ref;
128 out_unmap_left:
129 if (pages_left)
130 xengntshr_unshare(ctrl->gntshr, ctrl->read.buffer, pages_left);
131 out_ring:
132 xengntshr_unshare(ctrl->gntshr, ring, 1);
133 ring_ref = -1;
134 ctrl->ring = NULL;
135 ctrl->write.order = ctrl->read.order = 0;
136 goto out;
137 }
138
init_gnt_cli(struct libxenvchan * ctrl,int domain,uint32_t ring_ref)139 static int init_gnt_cli(struct libxenvchan *ctrl, int domain, uint32_t ring_ref)
140 {
141 int rv = -1;
142 uint32_t *grants;
143
144 ctrl->ring = xengnttab_map_grant_ref_notify(ctrl->gnttab,
145 domain, ring_ref, PROT_READ|PROT_WRITE,
146 offsetof(struct vchan_interface, cli_live), ctrl->event_port);
147
148 if (!ctrl->ring)
149 goto out;
150
151 ctrl->write.order = ctrl->ring->left_order;
152 ctrl->read.order = ctrl->ring->right_order;
153 ctrl->write.shr = &ctrl->ring->left;
154 ctrl->read.shr = &ctrl->ring->right;
155 if (ctrl->write.order < SMALL_RING_SHIFT || ctrl->write.order > MAX_RING_SHIFT)
156 goto out_unmap_ring;
157 if (ctrl->read.order < SMALL_RING_SHIFT || ctrl->read.order > MAX_RING_SHIFT)
158 goto out_unmap_ring;
159 if (ctrl->read.order == ctrl->write.order && ctrl->read.order < PAGE_SHIFT)
160 goto out_unmap_ring;
161
162 grants = ctrl->ring->grants;
163
164 switch (ctrl->write.order) {
165 case SMALL_RING_SHIFT:
166 ctrl->write.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
167 break;
168 case LARGE_RING_SHIFT:
169 ctrl->write.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
170 break;
171 default:
172 {
173 int pages_left = 1 << (ctrl->write.order - PAGE_SHIFT);
174 ctrl->write.buffer = xengnttab_map_domain_grant_refs(ctrl->gnttab,
175 pages_left, domain, grants, PROT_READ|PROT_WRITE);
176 if (!ctrl->write.buffer)
177 goto out_unmap_ring;
178 grants += pages_left;
179 }
180 }
181
182 switch (ctrl->read.order) {
183 case SMALL_RING_SHIFT:
184 ctrl->read.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
185 break;
186 case LARGE_RING_SHIFT:
187 ctrl->read.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
188 break;
189 default:
190 {
191 int pages_right = 1 << (ctrl->read.order - PAGE_SHIFT);
192 ctrl->read.buffer = xengnttab_map_domain_grant_refs(ctrl->gnttab,
193 pages_right, domain, grants, PROT_READ);
194 if (!ctrl->read.buffer)
195 goto out_unmap_left;
196 }
197 }
198
199 rv = 0;
200 out:
201 return rv;
202 out_unmap_left:
203 if (ctrl->write.order >= PAGE_SHIFT)
204 xengnttab_unmap(ctrl->gnttab, ctrl->write.buffer,
205 1 << (ctrl->write.order - PAGE_SHIFT));
206 out_unmap_ring:
207 xengnttab_unmap(ctrl->gnttab, ctrl->ring, 1);
208 ctrl->ring = 0;
209 ctrl->write.order = ctrl->read.order = 0;
210 rv = -1;
211 goto out;
212 }
213
init_evt_srv(struct libxenvchan * ctrl,int domain,struct xentoollog_logger * logger)214 static int init_evt_srv(struct libxenvchan *ctrl, int domain,
215 struct xentoollog_logger *logger)
216 {
217 xenevtchn_port_or_error_t port;
218
219 ctrl->event = xenevtchn_open(logger, 0);
220 if (!ctrl->event)
221 return -1;
222
223 port = xenevtchn_bind_unbound_port(ctrl->event, domain);
224 if (port < 0)
225 goto fail;
226 ctrl->event_port = port;
227
228 if (xenevtchn_unmask(ctrl->event, ctrl->event_port))
229 goto fail;
230
231 return 0;
232
233 fail:
234 if (port >= 0)
235 xenevtchn_unbind(ctrl->event, port);
236
237 xenevtchn_close(ctrl->event);
238 ctrl->event = NULL;
239
240 return -1;
241 }
242
init_xs_srv(struct libxenvchan * ctrl,int domain,const char * xs_base,int ring_ref)243 static int init_xs_srv(struct libxenvchan *ctrl, int domain, const char* xs_base, int ring_ref)
244 {
245 int ret = -1;
246 struct xs_handle *xs;
247 struct xs_permissions perms[2];
248 char buf[64];
249 char ref[16];
250 char* domid_str = NULL;
251 xs_transaction_t xs_trans = XBT_NULL;
252
253 /* store the base path so we can clean up on server closure */
254 ctrl->xs_path = strdup(xs_base);
255 if (!ctrl->xs_path)
256 return -1;
257
258 xs = xs_open(0);
259 if (!xs)
260 goto fail;
261 domid_str = xs_read(xs, 0, "domid", NULL);
262 if (!domid_str)
263 goto fail_xs_open;
264
265 // owner domain is us
266 perms[0].id = atoi(domid_str);
267 // permissions for domains not listed = none
268 perms[0].perms = XS_PERM_NONE;
269 // other domains
270 perms[1].id = domain;
271 perms[1].perms = XS_PERM_READ;
272
273 retry_transaction:
274 xs_trans = xs_transaction_start(xs);
275 if (!xs_trans)
276 goto fail_xs_open;
277
278 snprintf(ref, sizeof ref, "%d", ring_ref);
279 snprintf(buf, sizeof buf, "%s/ring-ref", xs_base);
280 if (!xs_write(xs, xs_trans, buf, ref, strlen(ref)))
281 goto fail_xs_open;
282 if (!xs_set_permissions(xs, xs_trans, buf, perms, 2))
283 goto fail_xs_open;
284
285 snprintf(ref, sizeof ref, "%d", ctrl->event_port);
286 snprintf(buf, sizeof buf, "%s/event-channel", xs_base);
287 if (!xs_write(xs, xs_trans, buf, ref, strlen(ref)))
288 goto fail_xs_open;
289 if (!xs_set_permissions(xs, xs_trans, buf, perms, 2))
290 goto fail_xs_open;
291
292 if (!xs_transaction_end(xs, xs_trans, 0)) {
293 if (errno == EAGAIN)
294 goto retry_transaction;
295 } else {
296 ret = 0;
297 }
298 fail_xs_open:
299 free(domid_str);
300 xs_close(xs);
301 fail:
302 return ret;
303 }
304
close_xs_srv(struct libxenvchan * ctrl)305 void close_xs_srv(struct libxenvchan *ctrl)
306 {
307 struct xs_handle *xs;
308
309 if (!ctrl->xs_path)
310 return;
311
312 xs = xs_open(0);
313 if (xs) {
314 xs_rm(xs, XBT_NULL, ctrl->xs_path);
315 xs_close(xs);
316 }
317
318 free(ctrl->xs_path);
319 }
320
min_order(size_t siz)321 static int min_order(size_t siz)
322 {
323 int rv = PAGE_SHIFT;
324 while (siz > (1 << rv))
325 rv++;
326 return rv;
327 }
328
libxenvchan_server_init(struct xentoollog_logger * logger,int domain,const char * xs_path,size_t left_min,size_t right_min)329 struct libxenvchan *libxenvchan_server_init(struct xentoollog_logger *logger,
330 int domain, const char* xs_path,
331 size_t left_min, size_t right_min)
332 {
333 struct libxenvchan *ctrl;
334 int ring_ref;
335 if (left_min > MAX_RING_SIZE || right_min > MAX_RING_SIZE)
336 return 0;
337
338 ctrl = malloc(sizeof(*ctrl));
339 if (!ctrl)
340 return 0;
341
342 ctrl->ring = NULL;
343 ctrl->event = NULL;
344 ctrl->is_server = 1;
345 ctrl->server_persist = 0;
346
347 ctrl->read.order = min_order(left_min);
348 ctrl->write.order = min_order(right_min);
349
350 // if we can avoid allocating extra pages by using in-page rings, do so
351 if (left_min <= MAX_SMALL_RING && right_min <= MAX_LARGE_RING) {
352 ctrl->read.order = SMALL_RING_SHIFT;
353 ctrl->write.order = LARGE_RING_SHIFT;
354 } else if (left_min <= MAX_LARGE_RING && right_min <= MAX_SMALL_RING) {
355 ctrl->read.order = LARGE_RING_SHIFT;
356 ctrl->write.order = SMALL_RING_SHIFT;
357 } else if (left_min <= MAX_LARGE_RING) {
358 ctrl->read.order = LARGE_RING_SHIFT;
359 } else if (right_min <= MAX_LARGE_RING) {
360 ctrl->write.order = LARGE_RING_SHIFT;
361 }
362
363 ctrl->gntshr = xengntshr_open(logger, 0);
364 if (!ctrl->gntshr) {
365 free(ctrl);
366 return 0;
367 }
368
369 if (init_evt_srv(ctrl, domain, logger))
370 goto out;
371 ring_ref = init_gnt_srv(ctrl, domain);
372 if (ring_ref < 0)
373 goto out;
374 if (init_xs_srv(ctrl, domain, xs_path, ring_ref))
375 goto out;
376 return ctrl;
377 out:
378 libxenvchan_close(ctrl);
379 return 0;
380 }
381
init_evt_cli(struct libxenvchan * ctrl,int domain,struct xentoollog_logger * logger)382 static int init_evt_cli(struct libxenvchan *ctrl, int domain,
383 struct xentoollog_logger *logger)
384 {
385 xenevtchn_port_or_error_t port;
386
387 ctrl->event = xenevtchn_open(logger, 0);
388 if (!ctrl->event)
389 return -1;
390
391 port = xenevtchn_bind_interdomain(ctrl->event,
392 domain, ctrl->event_port);
393 if (port < 0)
394 goto fail;
395 ctrl->event_port = port;
396
397 if (xenevtchn_unmask(ctrl->event, ctrl->event_port))
398 goto fail;
399
400 return 0;
401
402 fail:
403 if (port >= 0)
404 xenevtchn_unbind(ctrl->event, port);
405
406 xenevtchn_close(ctrl->event);
407 ctrl->event = NULL;
408
409 return -1;
410 }
411
412
libxenvchan_client_init(struct xentoollog_logger * logger,int domain,const char * xs_path)413 struct libxenvchan *libxenvchan_client_init(struct xentoollog_logger *logger,
414 int domain, const char* xs_path)
415 {
416 struct libxenvchan *ctrl = malloc(sizeof(struct libxenvchan));
417 struct xs_handle *xs = NULL;
418 char buf[64];
419 char *ref;
420 int ring_ref;
421 unsigned int len;
422
423 if (!ctrl)
424 return 0;
425 ctrl->ring = NULL;
426 ctrl->event = NULL;
427 ctrl->gnttab = NULL;
428 ctrl->write.order = ctrl->read.order = 0;
429 ctrl->is_server = 0;
430
431 xs = xs_open(0);
432 if (!xs)
433 goto fail;
434
435 // find xenstore entry
436 snprintf(buf, sizeof buf, "%s/ring-ref", xs_path);
437 ref = xs_read(xs, 0, buf, &len);
438 if (!ref)
439 goto fail;
440 ring_ref = atoi(ref);
441 free(ref);
442 if (!ring_ref)
443 goto fail;
444 snprintf(buf, sizeof buf, "%s/event-channel", xs_path);
445 ref = xs_read(xs, 0, buf, &len);
446 if (!ref)
447 goto fail;
448 ctrl->event_port = atoi(ref);
449 free(ref);
450 if (!ctrl->event_port)
451 goto fail;
452
453 ctrl->gnttab = xengnttab_open(logger, 0);
454 if (!ctrl->gnttab)
455 goto fail;
456
457 // set up event channel
458 if (init_evt_cli(ctrl, domain, logger))
459 goto fail;
460
461 // set up shared page(s)
462 if (init_gnt_cli(ctrl, domain, ring_ref))
463 goto fail;
464
465 ctrl->ring->cli_live = 1;
466 ctrl->ring->srv_notify = VCHAN_NOTIFY_WRITE;
467
468 /* wake up the server */
469 xenevtchn_notify(ctrl->event, ctrl->event_port);
470
471 out:
472 if (xs)
473 xs_close(xs);
474 return ctrl;
475 fail:
476 libxenvchan_close(ctrl);
477 ctrl = NULL;
478 goto out;
479 }
480