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