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_transaction_t xs_trans = XBT_NULL;
254 xs = xs_domain_open();
255 if (!xs)
256 goto fail;
257 domid_str = xs_read(xs, 0, "domid", NULL);
258 if (!domid_str)
259 goto fail_xs_open;
260
261 // owner domain is us
262 perms[0].id = atoi(domid_str);
263 // permissions for domains not listed = none
264 perms[0].perms = XS_PERM_NONE;
265 // other domains
266 perms[1].id = domain;
267 perms[1].perms = XS_PERM_READ;
268
269 retry_transaction:
270 xs_trans = xs_transaction_start(xs);
271 if (!xs_trans)
272 goto fail_xs_open;
273
274 snprintf(ref, sizeof ref, "%d", ring_ref);
275 snprintf(buf, sizeof buf, "%s/ring-ref", xs_base);
276 if (!xs_write(xs, xs_trans, buf, ref, strlen(ref)))
277 goto fail_xs_open;
278 if (!xs_set_permissions(xs, xs_trans, buf, perms, 2))
279 goto fail_xs_open;
280
281 snprintf(ref, sizeof ref, "%d", ctrl->event_port);
282 snprintf(buf, sizeof buf, "%s/event-channel", xs_base);
283 if (!xs_write(xs, xs_trans, buf, ref, strlen(ref)))
284 goto fail_xs_open;
285 if (!xs_set_permissions(xs, xs_trans, buf, perms, 2))
286 goto fail_xs_open;
287
288 if (!xs_transaction_end(xs, xs_trans, 0)) {
289 if (errno == EAGAIN)
290 goto retry_transaction;
291 } else {
292 ret = 0;
293 }
294 fail_xs_open:
295 free(domid_str);
296 xs_daemon_close(xs);
297 fail:
298 return ret;
299 }
300
min_order(size_t siz)301 static int min_order(size_t siz)
302 {
303 int rv = PAGE_SHIFT;
304 while (siz > (1 << rv))
305 rv++;
306 return rv;
307 }
308
libxenvchan_server_init(struct xentoollog_logger * logger,int domain,const char * xs_path,size_t left_min,size_t right_min)309 struct libxenvchan *libxenvchan_server_init(struct xentoollog_logger *logger,
310 int domain, const char* xs_path,
311 size_t left_min, size_t right_min)
312 {
313 struct libxenvchan *ctrl;
314 int ring_ref;
315 if (left_min > MAX_RING_SIZE || right_min > MAX_RING_SIZE)
316 return 0;
317
318 ctrl = malloc(sizeof(*ctrl));
319 if (!ctrl)
320 return 0;
321
322 ctrl->ring = NULL;
323 ctrl->event = NULL;
324 ctrl->is_server = 1;
325 ctrl->server_persist = 0;
326
327 ctrl->read.order = min_order(left_min);
328 ctrl->write.order = min_order(right_min);
329
330 // if we can avoid allocating extra pages by using in-page rings, do so
331 if (left_min <= MAX_SMALL_RING && right_min <= MAX_LARGE_RING) {
332 ctrl->read.order = SMALL_RING_SHIFT;
333 ctrl->write.order = LARGE_RING_SHIFT;
334 } else if (left_min <= MAX_LARGE_RING && right_min <= MAX_SMALL_RING) {
335 ctrl->read.order = LARGE_RING_SHIFT;
336 ctrl->write.order = SMALL_RING_SHIFT;
337 } else if (left_min <= MAX_LARGE_RING) {
338 ctrl->read.order = LARGE_RING_SHIFT;
339 } else if (right_min <= MAX_LARGE_RING) {
340 ctrl->write.order = LARGE_RING_SHIFT;
341 }
342
343 ctrl->gntshr = xengntshr_open(logger, 0);
344 if (!ctrl->gntshr) {
345 free(ctrl);
346 return 0;
347 }
348
349 if (init_evt_srv(ctrl, domain, logger))
350 goto out;
351 ring_ref = init_gnt_srv(ctrl, domain);
352 if (ring_ref < 0)
353 goto out;
354 if (init_xs_srv(ctrl, domain, xs_path, ring_ref))
355 goto out;
356 return ctrl;
357 out:
358 libxenvchan_close(ctrl);
359 return 0;
360 }
361
init_evt_cli(struct libxenvchan * ctrl,int domain,struct xentoollog_logger * logger)362 static int init_evt_cli(struct libxenvchan *ctrl, int domain,
363 struct xentoollog_logger *logger)
364 {
365 xenevtchn_port_or_error_t port;
366
367 ctrl->event = xenevtchn_open(logger, 0);
368 if (!ctrl->event)
369 return -1;
370
371 port = xenevtchn_bind_interdomain(ctrl->event,
372 domain, ctrl->event_port);
373 if (port < 0)
374 goto fail;
375 ctrl->event_port = port;
376
377 if (xenevtchn_unmask(ctrl->event, ctrl->event_port))
378 goto fail;
379
380 return 0;
381
382 fail:
383 if (port >= 0)
384 xenevtchn_unbind(ctrl->event, port);
385
386 xenevtchn_close(ctrl->event);
387 ctrl->event = NULL;
388
389 return -1;
390 }
391
392
libxenvchan_client_init(struct xentoollog_logger * logger,int domain,const char * xs_path)393 struct libxenvchan *libxenvchan_client_init(struct xentoollog_logger *logger,
394 int domain, const char* xs_path)
395 {
396 struct libxenvchan *ctrl = malloc(sizeof(struct libxenvchan));
397 struct xs_handle *xs = NULL;
398 char buf[64];
399 char *ref;
400 int ring_ref;
401 unsigned int len;
402
403 if (!ctrl)
404 return 0;
405 ctrl->ring = NULL;
406 ctrl->event = NULL;
407 ctrl->gnttab = NULL;
408 ctrl->write.order = ctrl->read.order = 0;
409 ctrl->is_server = 0;
410
411 xs = xs_daemon_open();
412 if (!xs)
413 xs = xs_domain_open();
414 if (!xs)
415 goto fail;
416
417 // find xenstore entry
418 snprintf(buf, sizeof buf, "%s/ring-ref", xs_path);
419 ref = xs_read(xs, 0, buf, &len);
420 if (!ref)
421 goto fail;
422 ring_ref = atoi(ref);
423 free(ref);
424 if (!ring_ref)
425 goto fail;
426 snprintf(buf, sizeof buf, "%s/event-channel", xs_path);
427 ref = xs_read(xs, 0, buf, &len);
428 if (!ref)
429 goto fail;
430 ctrl->event_port = atoi(ref);
431 free(ref);
432 if (!ctrl->event_port)
433 goto fail;
434
435 ctrl->gnttab = xengnttab_open(logger, 0);
436 if (!ctrl->gnttab)
437 goto fail;
438
439 // set up event channel
440 if (init_evt_cli(ctrl, domain, logger))
441 goto fail;
442
443 // set up shared page(s)
444 if (init_gnt_cli(ctrl, domain, ring_ref))
445 goto fail;
446
447 ctrl->ring->cli_live = 1;
448 ctrl->ring->srv_notify = VCHAN_NOTIFY_WRITE;
449
450 /* wake up the server */
451 xenevtchn_notify(ctrl->event, ctrl->event_port);
452
453 out:
454 if (xs)
455 xs_daemon_close(xs);
456 return ctrl;
457 fail:
458 libxenvchan_close(ctrl);
459 ctrl = NULL;
460 goto out;
461 }
462