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