ia64/xen-unstable

view tools/xenfb/xenfb.c @ 12739:d57b0e2834d7

[PVFB][TOOLS] Don't unwatch the framebuffer frontend's state node just
because it want to state Closed. Otherwise, we don't notice when
the node gets deleted, and can't reliably shut the backend down.

Signed-off-by: Steven Smith <sos22@cam.ac.uk>
author Steven Smith <ssmith@xensource.com>
date Fri Dec 01 13:11:53 2006 +0000 (2006-12-01)
parents 0536dbde1562
children d54c8dab1e64
line source
1 #include <stdarg.h>
2 #include <stdlib.h>
3 #include <sys/types.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <xenctrl.h>
7 #include <xen/io/xenbus.h>
8 #include <xen/io/fbif.h>
9 #include <xen/io/kbdif.h>
10 #include <sys/select.h>
11 #include <stdbool.h>
12 #include <xen/linux/evtchn.h>
13 #include <xen/event_channel.h>
14 #include <sys/mman.h>
15 #include <errno.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <time.h>
19 #include <xs.h>
21 #include "xenfb.h"
23 // FIXME defend against malicious frontend?
25 struct xenfb_device {
26 const char *devicetype;
27 char nodename[64]; /* backend xenstore dir */
28 char otherend[64]; /* frontend xenstore dir */
29 int otherend_id; /* frontend domid */
30 enum xenbus_state state; /* backend state */
31 void *page; /* shared page */
32 evtchn_port_t port;
33 struct xenfb_private *xenfb;
34 };
36 struct xenfb_private {
37 struct xenfb pub;
38 int evt_xch; /* event channel driver handle */
39 int xc; /* hypervisor interface handle */
40 struct xs_handle *xsh; /* xs daemon handle */
41 struct xenfb_device fb, kbd;
42 size_t fb_len; /* size of framebuffer */
43 };
45 static void xenfb_detach_dom(struct xenfb_private *);
47 static char *xenfb_path_in_dom(struct xs_handle *xsh,
48 char *buf, size_t size,
49 unsigned domid, const char *fmt, ...)
50 {
51 va_list ap;
52 char *domp = xs_get_domain_path(xsh, domid);
53 int n;
55 if (domp == NULL)
56 return NULL;
58 n = snprintf(buf, size, "%s/", domp);
59 free(domp);
60 if (n >= size)
61 return NULL;
63 va_start(ap, fmt);
64 n += vsnprintf(buf + n, size - n, fmt, ap);
65 va_end(ap);
66 if (n >= size)
67 return NULL;
69 return buf;
70 }
72 static int xenfb_xs_scanf1(struct xs_handle *xsh,
73 const char *dir, const char *node,
74 const char *fmt, void *dest)
75 {
76 char buf[1024];
77 char *p;
78 int ret;
80 if (snprintf(buf, sizeof(buf), "%s/%s", dir, node) >= sizeof(buf)) {
81 errno = ENOENT;
82 return -1;
83 }
84 p = xs_read(xsh, XBT_NULL, buf, NULL);
85 if (!p) {
86 errno = ENOENT;
87 return -1;
88 }
89 ret = sscanf(p, fmt, dest);
90 free(p);
91 if (ret != 1) {
92 errno = EDOM;
93 return -1;
94 }
95 return ret;
96 }
98 static int xenfb_xs_printf(struct xs_handle *xsh,
99 const char *dir, const char *node, char *fmt, ...)
100 {
101 va_list ap;
102 char key[1024];
103 char val[1024];
104 int n;
106 if (snprintf(key, sizeof(key), "%s/%s", dir, node) >= sizeof(key)) {
107 errno = ENOENT;
108 return -1;
109 }
111 va_start(ap, fmt);
112 n = vsnprintf(val, sizeof(val), fmt, ap);
113 va_end(ap);
114 if (n >= sizeof(val)) {
115 errno = ENOSPC; /* close enough */
116 return -1;
117 }
119 if (!xs_write(xsh, XBT_NULL, key, val, n))
120 return -1;
121 return 0;
122 }
124 static void xenfb_device_init(struct xenfb_device *dev,
125 const char *type,
126 struct xenfb_private *xenfb)
127 {
128 dev->devicetype = type;
129 dev->otherend_id = -1;
130 dev->port = -1;
131 dev->xenfb = xenfb;
132 }
134 int xenfb_device_set_domain(struct xenfb_device *dev, int domid)
135 {
136 struct xenfb_private *xenfb = dev->xenfb;
138 dev->otherend_id = domid;
140 if (!xenfb_path_in_dom(xenfb->xsh,
141 dev->otherend, sizeof(dev->otherend),
142 domid, "device/%s/0", dev->devicetype)) {
143 errno = ENOENT;
144 return -1;
145 }
146 if (!xenfb_path_in_dom(xenfb->xsh,
147 dev->nodename, sizeof(dev->nodename),
148 0, "backend/%s/%d/0", dev->devicetype, domid)) {
149 errno = ENOENT;
150 return -1;
151 }
153 return 0;
154 }
156 struct xenfb *xenfb_new(void)
157 {
158 struct xenfb_private *xenfb = malloc(sizeof(*xenfb));
159 int serrno;
161 if (xenfb == NULL)
162 return NULL;
164 memset(xenfb, 0, sizeof(*xenfb));
165 xenfb->evt_xch = xenfb->xc = -1;
166 xenfb_device_init(&xenfb->fb, "vfb", xenfb);
167 xenfb_device_init(&xenfb->kbd, "vkbd", xenfb);
169 xenfb->evt_xch = xc_evtchn_open();
170 if (xenfb->evt_xch == -1)
171 goto fail;
173 xenfb->xc = xc_interface_open();
174 if (xenfb->xc == -1)
175 goto fail;
177 xenfb->xsh = xs_daemon_open();
178 if (!xenfb->xsh)
179 goto fail;
181 return &xenfb->pub;
183 fail:
184 serrno = errno;
185 xenfb_delete(&xenfb->pub);
186 errno = serrno;
187 return NULL;
188 }
190 /* Remove the backend area in xenbus since the framebuffer really is
191 going away. */
192 void xenfb_teardown(struct xenfb *xenfb_pub)
193 {
194 struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
196 xs_rm(xenfb->xsh, XBT_NULL, xenfb->fb.nodename);
197 xs_rm(xenfb->xsh, XBT_NULL, xenfb->kbd.nodename);
198 }
201 void xenfb_delete(struct xenfb *xenfb_pub)
202 {
203 struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
205 xenfb_detach_dom(xenfb);
206 if (xenfb->xc >= 0)
207 xc_interface_close(xenfb->xc);
208 if (xenfb->evt_xch >= 0)
209 xc_evtchn_close(xenfb->evt_xch);
210 if (xenfb->xsh)
211 xs_daemon_close(xenfb->xsh);
212 free(xenfb);
213 }
215 static enum xenbus_state xenfb_read_state(struct xs_handle *xsh,
216 const char *dir)
217 {
218 int ret, state;
220 ret = xenfb_xs_scanf1(xsh, dir, "state", "%d", &state);
221 if (ret < 0)
222 return XenbusStateUnknown;
224 if ((unsigned)state > XenbusStateClosed)
225 state = XenbusStateUnknown;
226 return state;
227 }
229 static int xenfb_switch_state(struct xenfb_device *dev,
230 enum xenbus_state state)
231 {
232 struct xs_handle *xsh = dev->xenfb->xsh;
234 if (xenfb_xs_printf(xsh, dev->nodename, "state", "%d", state) < 0)
235 return -1;
236 dev->state = state;
237 return 0;
238 }
240 static int xenfb_wait_for_state(struct xs_handle *xsh, const char *dir,
241 unsigned awaited)
242 {
243 unsigned state, dummy;
244 char **vec;
246 for (;;) {
247 state = xenfb_read_state(xsh, dir);
248 if (state < 0)
249 return -1;
251 if ((1 << state) & awaited)
252 return state;
254 vec = xs_read_watch(xsh, &dummy);
255 if (!vec)
256 return -1;
257 free(vec);
258 }
259 }
261 static int xenfb_wait_for_backend_creation(struct xenfb_device *dev)
262 {
263 struct xs_handle *xsh = dev->xenfb->xsh;
264 int state;
266 if (!xs_watch(xsh, dev->nodename, ""))
267 return -1;
268 state = xenfb_wait_for_state(xsh, dev->nodename,
269 (1 << XenbusStateInitialising)
270 | (1 << XenbusStateClosed)
271 #if 1 /* TODO fudging state to permit restarting; to be removed */
272 | (1 << XenbusStateInitWait)
273 | (1 << XenbusStateConnected)
274 | (1 << XenbusStateClosing)
275 #endif
276 );
277 xs_unwatch(xsh, dev->nodename, "");
279 switch (state) {
280 #if 1
281 case XenbusStateInitWait:
282 case XenbusStateConnected:
283 printf("Fudging state to %d\n", XenbusStateInitialising); /* FIXME */
284 #endif
285 case XenbusStateInitialising:
286 case XenbusStateClosing:
287 case XenbusStateClosed:
288 break;
289 default:
290 return -1;
291 }
293 return 0;
294 }
296 static int xenfb_hotplug(struct xenfb_device *dev)
297 {
298 if (xenfb_xs_printf(dev->xenfb->xsh, dev->nodename,
299 "hotplug-status", "connected"))
300 return -1;
301 return 0;
302 }
304 static int xenfb_wait_for_frontend_initialised(struct xenfb_device *dev)
305 {
306 switch (xenfb_wait_for_state(dev->xenfb->xsh, dev->otherend,
307 #if 1 /* TODO fudging state to permit restarting; to be removed */
308 (1 << XenbusStateInitialised)
309 | (1 << XenbusStateConnected)
310 #else
311 1 << XenbusStateInitialised,
312 #endif
313 )) {
314 #if 1
315 case XenbusStateConnected:
316 printf("Fudging state to %d\n", XenbusStateInitialised); /* FIXME */
317 #endif
318 case XenbusStateInitialised:
319 break;
320 default:
321 return -1;
322 }
324 return 0;
325 }
327 static int xenfb_map_fb(struct xenfb_private *xenfb, int domid)
328 {
329 struct xenfb_page *page = xenfb->fb.page;
330 int n_fbmfns;
331 int n_fbdirs;
332 unsigned long *fbmfns;
334 n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
335 n_fbdirs = n_fbmfns * sizeof(unsigned long);
336 n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
338 /*
339 * Bug alert: xc_map_foreign_batch() can fail partly and
340 * return a non-null value. This is a design flaw. When it
341 * happens, we happily continue here, and later crash on
342 * access.
343 */
344 fbmfns = xc_map_foreign_batch(xenfb->xc, domid,
345 PROT_READ, page->pd, n_fbdirs);
346 if (fbmfns == NULL)
347 return -1;
349 xenfb->pub.pixels = xc_map_foreign_batch(xenfb->xc, domid,
350 PROT_READ | PROT_WRITE, fbmfns, n_fbmfns);
351 if (xenfb->pub.pixels == NULL) {
352 munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE);
353 return -1;
354 }
356 return munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE);
357 }
359 static int xenfb_bind(struct xenfb_device *dev)
360 {
361 struct xenfb_private *xenfb = dev->xenfb;
362 unsigned long mfn;
363 evtchn_port_t evtchn;
365 if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "page-ref", "%lu",
366 &mfn) < 0)
367 return -1;
368 if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "event-channel", "%u",
369 &evtchn) < 0)
370 return -1;
372 dev->port = xc_evtchn_bind_interdomain(xenfb->evt_xch,
373 dev->otherend_id, evtchn);
374 if (dev->port == -1)
375 return -1;
377 dev->page = xc_map_foreign_range(xenfb->xc, dev->otherend_id,
378 XC_PAGE_SIZE, PROT_READ | PROT_WRITE, mfn);
379 if (dev->page == NULL)
380 return -1;
382 return 0;
383 }
385 static void xenfb_unbind(struct xenfb_device *dev)
386 {
387 if (dev->page) {
388 munmap(dev->page, XC_PAGE_SIZE);
389 dev->page = NULL;
390 }
391 if (dev->port >= 0) {
392 xc_evtchn_unbind(dev->xenfb->evt_xch, dev->port);
393 dev->port = -1;
394 }
395 }
397 static int xenfb_wait_for_frontend_connected(struct xenfb_device *dev)
398 {
399 switch (xenfb_wait_for_state(dev->xenfb->xsh, dev->otherend,
400 1 << XenbusStateConnected)) {
401 case XenbusStateConnected:
402 break;
403 default:
404 return -1;
405 }
407 return 0;
408 }
410 static void xenfb_dev_fatal(struct xenfb_device *dev, int err,
411 const char *fmt, ...)
412 {
413 struct xs_handle *xsh = dev->xenfb->xsh;
414 va_list ap;
415 char errdir[80];
416 char buf[1024];
417 int n;
419 fprintf(stderr, "%s ", dev->nodename); /* somewhat crude */
420 va_start(ap, fmt);
421 vfprintf(stderr, fmt, ap);
422 va_end(ap);
423 if (err)
424 fprintf(stderr, " (%s)", strerror(err));
425 putc('\n', stderr);
427 if (!xenfb_path_in_dom(xsh, errdir, sizeof(errdir), 0,
428 "error/%s", dev->nodename))
429 goto out; /* FIXME complain */
431 va_start(ap, fmt);
432 n = snprintf(buf, sizeof(buf), "%d ", err);
433 snprintf(buf + n, sizeof(buf) - n, fmt, ap);
434 va_end(ap);
436 if (xenfb_xs_printf(xsh, buf, "error", "%s", buf) < 0)
437 goto out; /* FIXME complain */
439 out:
440 xenfb_switch_state(dev, XenbusStateClosing);
441 }
443 int xenfb_attach_dom(struct xenfb *xenfb_pub, int domid)
444 {
445 struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
446 struct xs_handle *xsh = xenfb->xsh;
447 int val, serrno;
448 struct xenfb_page *fb_page;
450 xenfb_detach_dom(xenfb);
452 xenfb_device_set_domain(&xenfb->fb, domid);
453 xenfb_device_set_domain(&xenfb->kbd, domid);
455 if (xenfb_wait_for_backend_creation(&xenfb->fb) < 0)
456 goto error;
457 if (xenfb_wait_for_backend_creation(&xenfb->kbd) < 0)
458 goto error;
460 if (xenfb_xs_printf(xsh, xenfb->kbd.nodename, "feature-abs-pointer", "1"))
461 goto error;
462 if (xenfb_switch_state(&xenfb->fb, XenbusStateInitWait))
463 goto error;
464 if (xenfb_switch_state(&xenfb->kbd, XenbusStateInitWait))
465 goto error;
467 if (xenfb_hotplug(&xenfb->fb) < 0)
468 goto error;
469 if (xenfb_hotplug(&xenfb->kbd) < 0)
470 goto error;
472 if (!xs_watch(xsh, xenfb->fb.otherend, ""))
473 goto error;
474 if (!xs_watch(xsh, xenfb->kbd.otherend, ""))
475 goto error;
477 if (xenfb_wait_for_frontend_initialised(&xenfb->fb) < 0)
478 goto error;
479 if (xenfb_wait_for_frontend_initialised(&xenfb->kbd) < 0)
480 goto error;
482 if (xenfb_bind(&xenfb->fb) < 0)
483 goto error;
484 if (xenfb_bind(&xenfb->kbd) < 0)
485 goto error;
487 if (xenfb_xs_scanf1(xsh, xenfb->fb.otherend, "feature-update",
488 "%d", &val) < 0)
489 val = 0;
490 if (!val) {
491 errno = ENOTSUP;
492 goto error;
493 }
494 xenfb_xs_printf(xsh, xenfb->fb.nodename, "request-update", "1");
496 /* TODO check for permitted ranges */
497 fb_page = xenfb->fb.page;
498 xenfb->pub.depth = fb_page->depth;
499 xenfb->pub.width = fb_page->width;
500 xenfb->pub.height = fb_page->height;
501 /* TODO check for consistency with the above */
502 xenfb->fb_len = fb_page->mem_length;
503 xenfb->pub.row_stride = fb_page->line_length;
505 if (xenfb_map_fb(xenfb, domid) < 0)
506 goto error;
508 if (xenfb_switch_state(&xenfb->fb, XenbusStateConnected))
509 goto error;
510 if (xenfb_switch_state(&xenfb->kbd, XenbusStateConnected))
511 goto error;
513 if (xenfb_wait_for_frontend_connected(&xenfb->kbd) < 0)
514 goto error;
515 if (xenfb_xs_scanf1(xsh, xenfb->kbd.otherend, "request-abs-pointer",
516 "%d", &val) < 0)
517 val = 0;
518 xenfb->pub.abs_pointer_wanted = val;
520 return 0;
522 error:
523 serrno = errno;
524 xenfb_detach_dom(xenfb);
525 xenfb_dev_fatal(&xenfb->fb, serrno, "on fire");
526 xenfb_dev_fatal(&xenfb->kbd, serrno, "on fire");
527 errno = serrno;
528 return -1;
529 }
531 static void xenfb_detach_dom(struct xenfb_private *xenfb)
532 {
533 xenfb_unbind(&xenfb->fb);
534 xenfb_unbind(&xenfb->kbd);
535 if (xenfb->pub.pixels) {
536 munmap(xenfb->pub.pixels, xenfb->fb_len);
537 xenfb->pub.pixels = NULL;
538 }
539 }
541 static void xenfb_on_fb_event(struct xenfb_private *xenfb)
542 {
543 uint32_t prod, cons;
544 struct xenfb_page *page = xenfb->fb.page;
546 prod = page->out_prod;
547 if (prod == page->out_cons)
548 return;
549 rmb(); /* ensure we see ring contents up to prod */
550 for (cons = page->out_cons; cons != prod; cons++) {
551 union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
553 switch (event->type) {
554 case XENFB_TYPE_UPDATE:
555 if (xenfb->pub.update)
556 xenfb->pub.update(&xenfb->pub,
557 event->update.x, event->update.y,
558 event->update.width, event->update.height);
559 break;
560 }
561 }
562 mb(); /* ensure we're done with ring contents */
563 page->out_cons = cons;
564 xc_evtchn_notify(xenfb->evt_xch, xenfb->fb.port);
565 }
567 static void xenfb_on_kbd_event(struct xenfb_private *xenfb)
568 {
569 struct xenkbd_page *page = xenfb->kbd.page;
571 /* We don't understand any keyboard events, so just ignore them. */
572 if (page->out_prod == page->out_cons)
573 return;
574 page->out_cons = page->out_prod;
575 xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port);
576 }
578 static int xenfb_on_state_change(struct xenfb_device *dev)
579 {
580 enum xenbus_state state;
582 state = xenfb_read_state(dev->xenfb->xsh, dev->otherend);
584 switch (state) {
585 case XenbusStateUnknown:
586 /* There was an error reading the frontend state. The
587 domain has probably gone away; in any case, there's
588 not much point in us continuing. */
589 return -1;
590 case XenbusStateInitialising:
591 case XenbusStateInitWait:
592 case XenbusStateInitialised:
593 case XenbusStateConnected:
594 break;
595 case XenbusStateClosing:
596 xenfb_unbind(dev);
597 xenfb_switch_state(dev, state);
598 break;
599 case XenbusStateClosed:
600 xenfb_switch_state(dev, state);
601 }
602 return 0;
603 }
605 /* Returns 0 normally, -1 on error, or -2 if the domain went away. */
606 int xenfb_poll(struct xenfb *xenfb_pub, fd_set *readfds)
607 {
608 struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
609 evtchn_port_t port;
610 unsigned dummy;
611 char **vec;
612 int r;
614 if (FD_ISSET(xc_evtchn_fd(xenfb->evt_xch), readfds)) {
615 port = xc_evtchn_pending(xenfb->evt_xch);
616 if (port == -1)
617 return -1;
619 if (port == xenfb->fb.port)
620 xenfb_on_fb_event(xenfb);
621 else if (port == xenfb->kbd.port)
622 xenfb_on_kbd_event(xenfb);
624 if (xc_evtchn_unmask(xenfb->evt_xch, port) == -1)
625 return -1;
626 }
628 if (FD_ISSET(xs_fileno(xenfb->xsh), readfds)) {
629 vec = xs_read_watch(xenfb->xsh, &dummy);
630 free(vec);
631 r = xenfb_on_state_change(&xenfb->fb);
632 if (r == 0)
633 r = xenfb_on_state_change(&xenfb->kbd);
634 if (r == -1)
635 return -2;
636 }
638 return 0;
639 }
641 int xenfb_select_fds(struct xenfb *xenfb_pub, fd_set *readfds)
642 {
643 struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
644 int fd1 = xc_evtchn_fd(xenfb->evt_xch);
645 int fd2 = xs_fileno(xenfb->xsh);
647 FD_SET(fd1, readfds);
648 FD_SET(fd2, readfds);
649 return fd1 > fd2 ? fd1 + 1 : fd2 + 1;
650 }
652 static int xenfb_kbd_event(struct xenfb_private *xenfb,
653 union xenkbd_in_event *event)
654 {
655 uint32_t prod;
656 struct xenkbd_page *page = xenfb->kbd.page;
658 if (xenfb->kbd.state != XenbusStateConnected)
659 return 0;
661 prod = page->in_prod;
662 if (prod - page->in_cons == XENKBD_IN_RING_LEN) {
663 errno = EAGAIN;
664 return -1;
665 }
667 mb(); /* ensure ring space available */
668 XENKBD_IN_RING_REF(page, prod) = *event;
669 wmb(); /* ensure ring contents visible */
670 page->in_prod = prod + 1;
671 return xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port);
672 }
674 int xenfb_send_key(struct xenfb *xenfb_pub, bool down, int keycode)
675 {
676 struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
677 union xenkbd_in_event event;
679 memset(&event, 0, XENKBD_IN_EVENT_SIZE);
680 event.type = XENKBD_TYPE_KEY;
681 event.key.pressed = down ? 1 : 0;
682 event.key.keycode = keycode;
684 return xenfb_kbd_event(xenfb, &event);
685 }
687 int xenfb_send_motion(struct xenfb *xenfb_pub, int rel_x, int rel_y)
688 {
689 struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
690 union xenkbd_in_event event;
692 memset(&event, 0, XENKBD_IN_EVENT_SIZE);
693 event.type = XENKBD_TYPE_MOTION;
694 event.motion.rel_x = rel_x;
695 event.motion.rel_y = rel_y;
697 return xenfb_kbd_event(xenfb, &event);
698 }
700 int xenfb_send_position(struct xenfb *xenfb_pub, int abs_x, int abs_y)
701 {
702 struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
703 union xenkbd_in_event event;
705 memset(&event, 0, XENKBD_IN_EVENT_SIZE);
706 event.type = XENKBD_TYPE_POS;
707 event.pos.abs_x = abs_x;
708 event.pos.abs_y = abs_y;
710 return xenfb_kbd_event(xenfb, &event);
711 }