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