ia64/xen-unstable

view tools/ioemu/hw/xenfb.c @ 17662:9044705960cb

ioemu: Fix PVFB backend to limit frame buffer size

The recent fix to validate the frontend's frame buffer description
neglected to limit the frame buffer size correctly. This lets a
malicious frontend make the backend attempt to map an arbitrary amount
of guest memory, which could be useful for a denial of service attack
against dom0.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
author Keir Fraser <keir.fraser@citrix.com>
date Thu May 15 09:36:38 2008 +0100 (2008-05-15)
parents 53195719f762
children 13dda75739f2
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 <xen/io/protocols.h>
11 #include <stdbool.h>
12 #include <xen/event_channel.h>
13 #include <sys/mman.h>
14 #include <errno.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <time.h>
18 #include <xs.h>
20 #include "xenfb.h"
22 #ifdef CONFIG_STUBDOM
23 #include <semaphore.h>
24 #include <sched.h>
25 #include <fbfront.h>
26 #endif
28 #ifndef BTN_LEFT
29 #define BTN_LEFT 0x110 /* from <linux/input.h> */
30 #endif
32 struct xenfb;
34 struct xenfb_device {
35 const char *devicetype;
36 char nodename[64]; /* backend xenstore dir */
37 char otherend[64]; /* frontend xenstore dir */
38 int otherend_id; /* frontend domid */
39 enum xenbus_state state; /* backend state */
40 void *page; /* shared page */
41 evtchn_port_t port;
42 struct xenfb *xenfb;
43 };
45 struct xenfb {
46 DisplayState *ds; /* QEMU graphical console state */
47 int evt_xch; /* event channel driver handle */
48 int xc; /* hypervisor interface handle */
49 struct xs_handle *xsh; /* xs daemon handle */
50 struct xenfb_device fb, kbd;
51 void *pixels; /* guest framebuffer data */
52 size_t fb_len; /* size of framebuffer */
53 int row_stride; /* width of one row in framebuffer */
54 int depth; /* colour depth of guest framebuffer */
55 int width; /* pixel width of guest framebuffer */
56 int height; /* pixel height of guest framebuffer */
57 int offset; /* offset of the framebuffer */
58 int abs_pointer_wanted; /* Whether guest supports absolute pointer */
59 int button_state; /* Last seen pointer button state */
60 int refresh_period; /* The refresh period we have advised */
61 char protocol[64]; /* frontend protocol */
62 };
64 /* Functions for frontend/backend state machine*/
65 static int xenfb_wait_for_frontend(struct xenfb_device *dev, IOHandler *handler);
66 static int xenfb_wait_for_backend(struct xenfb_device *dev, IOHandler *handler);
67 static void xenfb_backend_created_kbd(void *opaque);
68 static void xenfb_backend_created_fb(void *opaque);
69 static void xenfb_frontend_initialized_kbd(void *opaque);
70 static void xenfb_frontend_initialized_fb(void *opaque);
71 static void xenfb_frontend_connected_kbd(void *opaque);
73 /* Helper functions for checking state of frontend/backend devices */
74 static int xenfb_frontend_connected(struct xenfb_device *dev);
75 static int xenfb_frontend_initialized(struct xenfb_device *dev);
76 static int xenfb_backend_created(struct xenfb_device *dev);
78 /* Functions which tie the PVFB into the QEMU device model */
79 static void xenfb_key_event(void *opaque, int keycode);
80 static void xenfb_mouse_event(void *opaque,
81 int dx, int dy, int dz, int button_state);
82 static void xenfb_guest_copy(struct xenfb *xenfb, int x, int y, int w, int h);
83 static void xenfb_update(void *opaque);
84 static void xenfb_invalidate(void *opaque);
85 static void xenfb_screen_dump(void *opaque, const char *name);
86 static int xenfb_register_console(struct xenfb *xenfb);
88 /*
89 * Tables to map from scancode to Linux input layer keycode.
90 * Scancodes are hardware-specific. These maps assumes a
91 * standard AT or PS/2 keyboard which is what QEMU feeds us.
92 */
93 static const unsigned char atkbd_set2_keycode[512] = {
95 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117,
96 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0,
97 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183,
98 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185,
99 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0,
100 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85,
101 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0,
102 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
104 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
105 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125,
106 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127,
107 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142,
108 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0,
109 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0,
110 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112,
111 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0,
113 };
115 static const unsigned char atkbd_unxlate_table[128] = {
117 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
118 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
119 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
120 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3,
121 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105,
122 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63,
123 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
124 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
126 };
128 static unsigned char scancode2linux[512];
130 static int xenfb_xs_scanf1(struct xs_handle *xsh,
131 const char *dir, const char *node,
132 const char *fmt, void *dest)
133 {
134 char buf[1024];
135 char *p;
136 int ret;
138 if (snprintf(buf, sizeof(buf), "%s/%s", dir, node) >= sizeof(buf)) {
139 errno = ENOENT;
140 return -1;
141 }
142 p = xs_read(xsh, XBT_NULL, buf, NULL);
143 if (!p) {
144 errno = ENOENT;
145 return -1;
146 }
147 ret = sscanf(p, fmt, dest);
148 free(p);
149 if (ret != 1) {
150 errno = EDOM;
151 return -1;
152 }
153 return ret;
154 }
156 static int xenfb_xs_printf(struct xs_handle *xsh,
157 const char *dir, const char *node, char *fmt, ...)
158 {
159 va_list ap;
160 char key[1024];
161 char val[1024];
162 int n;
164 if (snprintf(key, sizeof(key), "%s/%s", dir, node) >= sizeof(key)) {
165 errno = ENOENT;
166 return -1;
167 }
169 va_start(ap, fmt);
170 n = vsnprintf(val, sizeof(val), fmt, ap);
171 va_end(ap);
172 if (n >= sizeof(val)) {
173 errno = ENOSPC; /* close enough */
174 return -1;
175 }
177 if (!xs_write(xsh, XBT_NULL, key, val, n))
178 return -1;
179 return 0;
180 }
182 static void xenfb_device_init(struct xenfb_device *dev,
183 const char *type,
184 struct xenfb *xenfb)
185 {
186 dev->devicetype = type;
187 dev->otherend_id = -1;
188 dev->port = -1;
189 dev->xenfb = xenfb;
190 }
192 static char *xenfb_path_in_dom(struct xs_handle *xsh,
193 char *buf, size_t size,
194 unsigned domid, const char *fmt, ...)
195 {
196 va_list ap;
197 char *domp = xs_get_domain_path(xsh, domid);
198 int n;
200 if (domp == NULL)
201 return NULL;
203 n = snprintf(buf, size, "%s/", domp);
204 free(domp);
205 if (n >= size)
206 return NULL;
208 va_start(ap, fmt);
209 n += vsnprintf(buf + n, size - n, fmt, ap);
210 va_end(ap);
211 if (n >= size)
212 return NULL;
214 return buf;
215 }
217 static int xenfb_device_set_domain(struct xenfb_device *dev, int domid)
218 {
219 dev->otherend_id = domid;
221 if (!xenfb_path_in_dom(dev->xenfb->xsh,
222 dev->otherend, sizeof(dev->otherend),
223 domid, "device/%s/0", dev->devicetype)) {
224 errno = ENOENT;
225 return -1;
226 }
227 if (!xenfb_path_in_dom(dev->xenfb->xsh,
228 dev->nodename, sizeof(dev->nodename),
229 0, "backend/%s/%d/0", dev->devicetype, domid)) {
230 errno = ENOENT;
231 return -1;
232 }
234 return 0;
235 }
237 struct xenfb *xenfb_new(int domid, DisplayState *ds)
238 {
239 struct xenfb *xenfb = qemu_malloc(sizeof(struct xenfb));
240 int serrno;
241 int i;
243 if (xenfb == NULL)
244 return NULL;
246 /* Prepare scancode mapping table */
247 for (i = 0; i < 128; i++) {
248 scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]];
249 scancode2linux[i | 0x80] =
250 atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80];
251 }
253 memset(xenfb, 0, sizeof(*xenfb));
254 xenfb->evt_xch = xenfb->xc = -1;
255 xenfb_device_init(&xenfb->fb, "vfb", xenfb);
256 xenfb_device_init(&xenfb->kbd, "vkbd", xenfb);
258 xenfb->evt_xch = xc_evtchn_open();
259 if (xenfb->evt_xch == -1)
260 goto fail;
262 xenfb->xc = xc_interface_open();
263 if (xenfb->xc == -1)
264 goto fail;
266 xenfb->xsh = xs_daemon_open();
267 if (!xenfb->xsh)
268 goto fail;
270 xenfb->ds = ds;
271 xenfb_device_set_domain(&xenfb->fb, domid);
272 xenfb_device_set_domain(&xenfb->kbd, domid);
274 fprintf(stderr, "FB: Waiting for KBD backend creation\n");
275 xenfb_wait_for_backend(&xenfb->kbd, xenfb_backend_created_kbd);
277 return xenfb;
279 fail:
280 serrno = errno;
281 xenfb_shutdown(xenfb);
282 errno = serrno;
283 return NULL;
284 }
287 static enum xenbus_state xenfb_read_state(struct xs_handle *xsh,
288 const char *dir)
289 {
290 int ret, state;
292 ret = xenfb_xs_scanf1(xsh, dir, "state", "%d", &state);
293 if (ret < 0)
294 return XenbusStateUnknown;
296 if ((unsigned)state > XenbusStateClosed)
297 state = XenbusStateUnknown;
298 return state;
299 }
301 static int xenfb_switch_state(struct xenfb_device *dev,
302 enum xenbus_state state)
303 {
304 struct xs_handle *xsh = dev->xenfb->xsh;
306 if (xenfb_xs_printf(xsh, dev->nodename, "state", "%d", state) < 0)
307 return -1;
308 dev->state = state;
309 return 0;
310 }
313 static int xenfb_hotplug(struct xenfb_device *dev)
314 {
315 if (xenfb_xs_printf(dev->xenfb->xsh, dev->nodename,
316 "hotplug-status", "connected"))
317 return -1;
318 return 0;
319 }
321 static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src)
322 {
323 uint32_t *src32 = src;
324 uint64_t *src64 = src;
325 int i;
327 for (i = 0; i < count; i++)
328 dst[i] = (mode == 32) ? src32[i] : src64[i];
329 }
331 static int xenfb_map_fb(struct xenfb *xenfb, int domid)
332 {
333 struct xenfb_page *page = xenfb->fb.page;
334 int n_fbmfns;
335 int n_fbdirs;
336 unsigned long *pgmfns = NULL;
337 unsigned long *fbmfns = NULL;
338 void *map, *pd;
339 int mode, ret = -1;
341 /* default to native */
342 pd = page->pd;
343 mode = sizeof(unsigned long) * 8;
345 if (0 == strlen(xenfb->protocol)) {
346 /*
347 * Undefined protocol, some guesswork needed.
348 *
349 * Old frontends which don't set the protocol use
350 * one page directory only, thus pd[1] must be zero.
351 * pd[1] of the 32bit struct layout and the lower
352 * 32 bits of pd[0] of the 64bit struct layout have
353 * the same location, so we can check that ...
354 */
355 uint32_t *ptr32 = NULL;
356 uint32_t *ptr64 = NULL;
357 #if defined(__i386__)
358 ptr32 = (void*)page->pd;
359 ptr64 = ((void*)page->pd) + 4;
360 #elif defined(__x86_64__)
361 ptr32 = ((void*)page->pd) - 4;
362 ptr64 = (void*)page->pd;
363 #endif
364 if (ptr32) {
365 if (0 == ptr32[1]) {
366 mode = 32;
367 pd = ptr32;
368 } else {
369 mode = 64;
370 pd = ptr64;
371 }
372 }
373 #if defined(__x86_64__)
374 } else if (0 == strcmp(xenfb->protocol, XEN_IO_PROTO_ABI_X86_32)) {
375 /* 64bit dom0, 32bit domU */
376 mode = 32;
377 pd = ((void*)page->pd) - 4;
378 #elif defined(__i386__)
379 } else if (0 == strcmp(xenfb->protocol, XEN_IO_PROTO_ABI_X86_64)) {
380 /* 32bit dom0, 64bit domU */
381 mode = 64;
382 pd = ((void*)page->pd) + 4;
383 #endif
384 }
386 n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
387 n_fbdirs = n_fbmfns * mode / 8;
388 n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
390 pgmfns = malloc(sizeof(unsigned long) * n_fbdirs);
391 fbmfns = malloc(sizeof(unsigned long) * n_fbmfns);
392 if (!pgmfns || !fbmfns)
393 goto out;
395 xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd);
396 map = xc_map_foreign_pages(xenfb->xc, domid,
397 PROT_READ, pgmfns, n_fbdirs);
398 if (map == NULL)
399 goto out;
400 xenfb_copy_mfns(mode, n_fbmfns, fbmfns, map);
401 munmap(map, n_fbdirs * XC_PAGE_SIZE);
403 xenfb->pixels = xc_map_foreign_pages(xenfb->xc, domid,
404 PROT_READ | PROT_WRITE, fbmfns, n_fbmfns);
405 if (xenfb->pixels == NULL)
406 goto out;
408 ret = 0; /* all is fine */
410 out:
411 if (pgmfns)
412 free(pgmfns);
413 if (fbmfns)
414 free(fbmfns);
415 return ret;
416 }
418 static int xenfb_bind(struct xenfb_device *dev)
419 {
420 struct xenfb *xenfb = dev->xenfb;
421 unsigned long mfn;
422 evtchn_port_t evtchn;
424 if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "page-ref", "%lu",
425 &mfn) < 0)
426 return -1;
427 if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "event-channel", "%u",
428 &evtchn) < 0)
429 return -1;
431 dev->port = xc_evtchn_bind_interdomain(xenfb->evt_xch,
432 dev->otherend_id, evtchn);
433 if (dev->port == -1)
434 return -1;
436 dev->page = xc_map_foreign_range(xenfb->xc, dev->otherend_id,
437 XC_PAGE_SIZE, PROT_READ | PROT_WRITE, mfn);
438 if (dev->page == NULL)
439 return -1;
441 return 0;
442 }
444 static void xenfb_unbind(struct xenfb_device *dev)
445 {
446 if (dev->page) {
447 munmap(dev->page, XC_PAGE_SIZE);
448 dev->page = NULL;
449 }
450 if (dev->port >= 0) {
451 xc_evtchn_unbind(dev->xenfb->evt_xch, dev->port);
452 dev->port = -1;
453 }
454 }
457 static void xenfb_detach_dom(struct xenfb *xenfb)
458 {
459 xenfb_unbind(&xenfb->fb);
460 xenfb_unbind(&xenfb->kbd);
461 if (xenfb->pixels) {
462 munmap(xenfb->pixels, xenfb->fb_len);
463 xenfb->pixels = NULL;
464 }
465 }
467 /* Remove the backend area in xenbus since the framebuffer really is
468 going away. */
469 void xenfb_shutdown(struct xenfb *xenfb)
470 {
471 fprintf(stderr, "FB: Shutting down backend\n");
472 xs_rm(xenfb->xsh, XBT_NULL, xenfb->fb.nodename);
473 xs_rm(xenfb->xsh, XBT_NULL, xenfb->kbd.nodename);
475 xenfb_detach_dom(xenfb);
476 if (xenfb->xc >= 0)
477 xc_interface_close(xenfb->xc);
478 if (xenfb->evt_xch >= 0)
479 xc_evtchn_close(xenfb->evt_xch);
480 if (xenfb->xsh)
481 xs_daemon_close(xenfb->xsh);
482 free(xenfb);
483 }
485 static int xenfb_configure_fb(struct xenfb *xenfb, size_t fb_len_lim,
486 int width, int height, int depth,
487 size_t fb_len, int offset, int row_stride)
488 {
489 size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd);
490 size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz;
491 size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz;
492 size_t fb_len_max = fb_pages * XC_PAGE_SIZE;
493 int max_width, max_height;
495 if (fb_len_lim > fb_len_max) {
496 fprintf(stderr,
497 "FB: fb size limit %zu exceeds %zu, corrected\n",
498 fb_len_lim, fb_len_max);
499 fb_len_lim = fb_len_max;
500 }
501 if (fb_len > fb_len_lim) {
502 fprintf(stderr,
503 "FB: frontend fb size %zu limited to %zu\n",
504 fb_len, fb_len_lim);
505 fb_len = fb_len_lim;
506 }
507 if (depth != 8 && depth != 16 && depth != 24 && depth != 32) {
508 fprintf(stderr,
509 "FB: can't handle frontend fb depth %d\n",
510 depth);
511 return -1;
512 }
513 if (row_stride < 0 || row_stride > fb_len) {
514 fprintf(stderr,
515 "FB: invalid frontend stride %d\n", row_stride);
516 return -1;
517 }
518 max_width = row_stride / (depth / 8);
519 if (width < 0 || width > max_width) {
520 fprintf(stderr,
521 "FB: invalid frontend width %d limited to %d\n",
522 width, max_width);
523 width = max_width;
524 }
525 if (offset < 0 || offset >= fb_len) {
526 fprintf(stderr,
527 "FB: invalid frontend offset %d (max %zu)\n",
528 offset, fb_len - 1);
529 return -1;
530 }
531 max_height = (fb_len - offset) / row_stride;
532 if (height < 0 || height > max_height) {
533 fprintf(stderr,
534 "FB: invalid frontend height %d limited to %d\n",
535 height, max_height);
536 height = max_height;
537 }
538 xenfb->fb_len = fb_len;
539 xenfb->row_stride = row_stride;
540 xenfb->depth = depth;
541 xenfb->width = width;
542 xenfb->height = height;
543 xenfb->offset = offset;
544 fprintf(stderr, "Framebuffer %dx%dx%d offset %d stride %d\n",
545 width, height, depth, offset, row_stride);
546 return 0;
547 }
549 static void xenfb_on_fb_event(struct xenfb *xenfb)
550 {
551 uint32_t prod, cons;
552 struct xenfb_page *page = xenfb->fb.page;
554 prod = page->out_prod;
555 if (prod == page->out_cons)
556 return;
557 xen_rmb(); /* ensure we see ring contents up to prod */
558 for (cons = page->out_cons; cons != prod; cons++) {
559 union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
560 int x, y, w, h;
562 switch (event->type) {
563 case XENFB_TYPE_UPDATE:
564 x = MAX(event->update.x, 0);
565 y = MAX(event->update.y, 0);
566 w = MIN(event->update.width, xenfb->width - x);
567 h = MIN(event->update.height, xenfb->height - y);
568 if (w < 0 || h < 0) {
569 fprintf(stderr, "%s bogus update ignored\n",
570 xenfb->fb.nodename);
571 break;
572 }
573 if (x != event->update.x || y != event->update.y
574 || w != event->update.width
575 || h != event->update.height) {
576 fprintf(stderr, "%s bogus update clipped\n",
577 xenfb->fb.nodename);
578 }
579 xenfb_guest_copy(xenfb, x, y, w, h);
580 break;
581 case XENFB_TYPE_RESIZE:
582 if (xenfb_configure_fb(xenfb, xenfb->fb_len,
583 event->resize.width,
584 event->resize.height,
585 event->resize.depth,
586 xenfb->fb_len,
587 event->resize.offset,
588 event->resize.stride) < 0)
589 break;
590 dpy_colourdepth(xenfb->ds, xenfb->depth);
591 dpy_resize(xenfb->ds, xenfb->width, xenfb->height, xenfb->row_stride);
592 if (xenfb->ds->shared_buf)
593 dpy_setdata(xenfb->ds, xenfb->pixels + xenfb->offset);
594 xenfb_invalidate(xenfb);
595 break;
596 }
597 }
598 xen_mb(); /* ensure we're done with ring contents */
599 page->out_cons = cons;
600 xc_evtchn_notify(xenfb->evt_xch, xenfb->fb.port);
601 }
603 static int xenfb_queue_full(struct xenfb *xenfb)
604 {
605 struct xenfb_page *page = xenfb->fb.page;
606 uint32_t cons, prod;
608 prod = page->in_prod;
609 cons = page->in_cons;
610 return prod - cons == XENFB_IN_RING_LEN;
611 }
613 static void xenfb_send_event(struct xenfb *xenfb, union xenfb_in_event *event)
614 {
615 uint32_t prod;
616 struct xenfb_page *page = xenfb->fb.page;
618 prod = page->in_prod;
619 /* caller ensures !xenfb_queue_full() */
620 xen_mb(); /* ensure ring space available */
621 XENFB_IN_RING_REF(page, prod) = *event;
622 xen_wmb(); /* ensure ring contents visible */
623 page->in_prod = prod + 1;
625 xc_evtchn_notify(xenfb->evt_xch, xenfb->fb.port);
626 }
628 static void xenfb_send_refresh_period(struct xenfb *xenfb, int period)
629 {
630 union xenfb_in_event event;
632 memset(&event, 0, sizeof(event));
633 event.type = XENFB_TYPE_REFRESH_PERIOD;
634 event.refresh_period.period = period;
635 xenfb_send_event(xenfb, &event);
636 }
638 static void xenfb_on_kbd_event(struct xenfb *xenfb)
639 {
640 struct xenkbd_page *page = xenfb->kbd.page;
642 /* We don't understand any keyboard events, so just ignore them. */
643 if (page->out_prod == page->out_cons)
644 return;
645 page->out_cons = page->out_prod;
646 xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port);
647 }
649 static int xenfb_on_state_change(struct xenfb_device *dev)
650 {
651 enum xenbus_state state;
653 state = xenfb_read_state(dev->xenfb->xsh, dev->otherend);
655 switch (state) {
656 case XenbusStateUnknown:
657 /* There was an error reading the frontend state. The
658 domain has probably gone away; in any case, there's
659 not much point in us continuing. */
660 return -1;
661 case XenbusStateInitialising:
662 case XenbusStateInitWait:
663 case XenbusStateInitialised:
664 case XenbusStateConnected:
665 break;
666 case XenbusStateClosing:
667 xenfb_unbind(dev);
668 xenfb_switch_state(dev, state);
669 break;
670 case XenbusStateClosed:
671 xenfb_switch_state(dev, state);
672 }
673 return 0;
674 }
676 /* Send an event to the keyboard frontend driver */
677 static int xenfb_kbd_event(struct xenfb *xenfb,
678 union xenkbd_in_event *event)
679 {
680 uint32_t prod;
681 struct xenkbd_page *page = xenfb->kbd.page;
683 if (xenfb->kbd.state != XenbusStateConnected)
684 return 0;
686 prod = page->in_prod;
687 if (prod - page->in_cons == XENKBD_IN_RING_LEN) {
688 errno = EAGAIN;
689 return -1;
690 }
692 xen_mb(); /* ensure ring space available */
693 XENKBD_IN_RING_REF(page, prod) = *event;
694 xen_wmb(); /* ensure ring contents visible */
695 page->in_prod = prod + 1;
696 return xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port);
697 }
699 /* Send a keyboard (or mouse button) event */
700 static int xenfb_send_key(struct xenfb *xenfb, bool down, int keycode)
701 {
702 union xenkbd_in_event event;
704 memset(&event, 0, XENKBD_IN_EVENT_SIZE);
705 event.type = XENKBD_TYPE_KEY;
706 event.key.pressed = down ? 1 : 0;
707 event.key.keycode = keycode;
709 return xenfb_kbd_event(xenfb, &event);
710 }
712 /* Send a relative mouse movement event */
713 static int xenfb_send_motion(struct xenfb *xenfb,
714 int rel_x, int rel_y, int rel_z)
715 {
716 union xenkbd_in_event event;
718 memset(&event, 0, XENKBD_IN_EVENT_SIZE);
719 event.type = XENKBD_TYPE_MOTION;
720 event.motion.rel_x = rel_x;
721 event.motion.rel_y = rel_y;
722 event.motion.rel_z = rel_z;
724 return xenfb_kbd_event(xenfb, &event);
725 }
727 /* Send an absolute mouse movement event */
728 static int xenfb_send_position(struct xenfb *xenfb,
729 int abs_x, int abs_y, int rel_z)
730 {
731 union xenkbd_in_event event;
733 memset(&event, 0, XENKBD_IN_EVENT_SIZE);
734 event.type = XENKBD_TYPE_POS;
735 event.pos.abs_x = abs_x;
736 event.pos.abs_y = abs_y;
737 event.pos.rel_z = rel_z;
739 return xenfb_kbd_event(xenfb, &event);
740 }
742 /* Process events from the frontend event channel */
743 static void xenfb_dispatch_channel(void *opaque)
744 {
745 struct xenfb *xenfb = (struct xenfb *)opaque;
746 evtchn_port_t port;
747 port = xc_evtchn_pending(xenfb->evt_xch);
748 if (port == -1) {
749 xenfb_shutdown(xenfb);
750 exit(1);
751 }
753 if (port == xenfb->fb.port)
754 xenfb_on_fb_event(xenfb);
755 else if (port == xenfb->kbd.port)
756 xenfb_on_kbd_event(xenfb);
758 if (xc_evtchn_unmask(xenfb->evt_xch, port) == -1) {
759 xenfb_shutdown(xenfb);
760 exit(1);
761 }
762 }
764 /* Process ongoing events from the frontend devices */
765 static void xenfb_dispatch_store(void *opaque)
766 {
767 struct xenfb *xenfb = (struct xenfb *)opaque;
768 unsigned dummy;
769 char **vec;
770 int r;
772 vec = xs_read_watch(xenfb->xsh, &dummy);
773 free(vec);
774 r = xenfb_on_state_change(&xenfb->fb);
775 if (r == 0)
776 r = xenfb_on_state_change(&xenfb->kbd);
777 if (r < 0) {
778 xenfb_shutdown(xenfb);
779 exit(1);
780 }
781 }
784 /****************************************************************
785 *
786 * Functions for processing frontend config
787 *
788 ****************************************************************/
791 /* Process the frontend framebuffer config */
792 static int xenfb_read_frontend_fb_config(struct xenfb *xenfb) {
793 struct xenfb_page *fb_page;
794 int val;
795 int videoram;
797 if (xenfb_xs_scanf1(xenfb->xsh, xenfb->fb.otherend, "feature-update",
798 "%d", &val) < 0)
799 val = 0;
800 if (!val) {
801 fprintf(stderr, "feature-update not supported\n");
802 errno = ENOTSUP;
803 return -1;
804 }
805 if (xenfb_xs_scanf1(xenfb->xsh, xenfb->fb.otherend, "protocol", "%63s",
806 xenfb->protocol) < 0)
807 xenfb->protocol[0] = '\0';
808 xenfb_xs_printf(xenfb->xsh, xenfb->fb.nodename, "request-update", "1");
809 xenfb->refresh_period = -1;
811 if (xenfb_xs_scanf1(xenfb->xsh, xenfb->fb.nodename, "videoram", "%d",
812 &videoram) < 0)
813 videoram = 0;
814 fb_page = xenfb->fb.page;
815 if (xenfb_configure_fb(xenfb, videoram * 1024 * 1024U,
816 fb_page->width, fb_page->height, fb_page->depth,
817 fb_page->mem_length, 0, fb_page->line_length)
818 < 0) {
819 errno = EINVAL;
820 return -1;
821 }
823 if (xenfb_map_fb(xenfb, xenfb->fb.otherend_id) < 0)
824 return -1;
826 /* Indicate we have the frame buffer resize feature */
827 xenfb_xs_printf(xenfb->xsh, xenfb->fb.nodename, "feature-resize", "1");
829 /* Tell kbd pointer the screen geometry */
830 xenfb_xs_printf(xenfb->xsh, xenfb->kbd.nodename, "width", "%d", xenfb->width);
831 xenfb_xs_printf(xenfb->xsh, xenfb->kbd.nodename, "height", "%d", xenfb->height);
833 if (xenfb_switch_state(&xenfb->fb, XenbusStateConnected))
834 return -1;
835 if (xenfb_switch_state(&xenfb->kbd, XenbusStateConnected))
836 return -1;
838 return 0;
839 }
841 /* Process the frontend keyboard config */
842 static int xenfb_read_frontend_kbd_config(struct xenfb *xenfb)
843 {
844 int val;
846 if (xenfb_xs_scanf1(xenfb->xsh, xenfb->kbd.otherend, "request-abs-pointer",
847 "%d", &val) < 0)
848 val = 0;
849 xenfb->abs_pointer_wanted = val;
851 return 0;
852 }
855 /****************************************************************
856 *
857 * Functions for frontend/backend state machine
858 *
859 ****************************************************************/
861 /* Register a watch against a frontend device, and setup
862 * QEMU event loop to poll the xenstore FD for notification */
863 static int xenfb_wait_for_frontend(struct xenfb_device *dev, IOHandler *handler)
864 {
865 fprintf(stderr, "Doing frontend watch on %s\n", dev->otherend);
866 if (!xs_watch(dev->xenfb->xsh, dev->otherend, "")) {
867 fprintf(stderr, "Watch for dev failed\n");
868 return -1;
869 }
871 if (qemu_set_fd_handler2(xs_fileno(dev->xenfb->xsh), NULL, handler, NULL, dev) < 0)
872 return -1;
874 return 0;
875 }
877 /* Register a watch against a backend device, and setup
878 * QEMU event loop to poll the xenstore FD for notification */
879 static int xenfb_wait_for_backend(struct xenfb_device *dev, IOHandler *handler)
880 {
881 fprintf(stderr, "Doing backend watch on %s\n", dev->nodename);
882 if (!xs_watch(dev->xenfb->xsh, dev->nodename, "")) {
883 fprintf(stderr, "Watch for dev failed\n");
884 return -1;
885 }
887 if (qemu_set_fd_handler2(xs_fileno(dev->xenfb->xsh), NULL, handler, NULL, dev) < 0)
888 return -1;
890 return 0;
891 }
893 /* Callback invoked while waiting for KBD backend to change
894 * to the created state */
895 static void xenfb_backend_created_kbd(void *opaque)
896 {
897 struct xenfb_device *dev = (struct xenfb_device *)opaque;
898 int ret = xenfb_backend_created(dev);
899 if (ret < 0) {
900 xenfb_shutdown(dev->xenfb);
901 exit(1);
902 }
903 if (ret)
904 return; /* Still waiting */
906 if (xenfb_xs_printf(dev->xenfb->xsh, dev->nodename, "feature-abs-pointer", "1")) {
907 xenfb_shutdown(dev->xenfb);
908 exit(1);
909 }
911 fprintf(stderr, "FB: Waiting for FB backend creation\n");
912 xenfb_wait_for_backend(&dev->xenfb->fb, xenfb_backend_created_fb);
913 }
915 /* Callback invoked while waiting for FB backend to change
916 * to the created state */
917 static void xenfb_backend_created_fb(void *opaque)
918 {
919 struct xenfb_device *dev = (struct xenfb_device *)opaque;
920 int ret = xenfb_backend_created(dev);
921 if (ret < 0) {
922 xenfb_shutdown(dev->xenfb);
923 exit(1);
924 }
925 if (ret)
926 return; /* Still waiting */
928 fprintf(stderr, "FB: Waiting for KBD frontend initialization\n");
929 xenfb_wait_for_frontend(&dev->xenfb->kbd, xenfb_frontend_initialized_kbd);
930 }
932 /* Callback invoked while waiting for KBD frontend to change
933 * to the initialized state */
934 static void xenfb_frontend_initialized_kbd(void *opaque)
935 {
936 struct xenfb_device *dev = (struct xenfb_device *)opaque;
937 int ret = xenfb_frontend_initialized(dev);
938 if (ret < 0) {
939 xenfb_shutdown(dev->xenfb);
940 exit(1);
941 }
942 if (ret)
943 return; /* Still waiting */
946 fprintf(stderr, "FB: Waiting for FB frontend initialization\n");
947 xenfb_wait_for_frontend(&dev->xenfb->fb, xenfb_frontend_initialized_fb);
948 }
950 /* Callback invoked while waiting for FB frontend to change
951 * to the initialized state */
952 static void xenfb_frontend_initialized_fb(void *opaque)
953 {
954 struct xenfb_device *dev = (struct xenfb_device *)opaque;
955 int ret = xenfb_frontend_initialized(dev);
956 if (ret < 0) {
957 xenfb_shutdown(dev->xenfb);
958 exit(1);
959 }
960 if (ret)
961 return; /* Still waiting */
964 if (xenfb_read_frontend_fb_config(dev->xenfb)) {
965 xenfb_shutdown(dev->xenfb);
966 exit(1);
967 }
969 fprintf(stderr, "FB: Waiting for KBD frontend connection\n");
970 xenfb_wait_for_frontend(&dev->xenfb->kbd, xenfb_frontend_connected_kbd);
971 }
973 /* Callback invoked while waiting for KBD frontend to change
974 * to the connected state */
975 static void xenfb_frontend_connected_kbd(void *opaque)
976 {
977 struct xenfb_device *dev = (struct xenfb_device *)opaque;
978 int ret = xenfb_frontend_connected(dev);
979 if (ret < 0) {
980 xenfb_shutdown(dev->xenfb);
981 exit(1);
982 }
983 if (ret)
984 return; /* Still waiting */
986 if (xenfb_read_frontend_kbd_config(dev->xenfb) < 0) {
987 xenfb_shutdown(dev->xenfb);
988 exit(1);
989 }
991 xenfb_register_console(dev->xenfb);
992 }
995 /****************************************************************
996 *
997 * Helper functions for checking state of frontend/backend devices
998 *
999 ****************************************************************/
1001 /* Helper to determine if a frontend device is in Connected state */
1002 static int xenfb_frontend_connected(struct xenfb_device *dev)
1004 unsigned int state;
1005 unsigned int dummy;
1006 char **vec;
1007 vec = xs_read_watch(dev->xenfb->xsh, &dummy);
1008 if (!vec)
1009 return -1;
1010 free(vec);
1012 state = xenfb_read_state(dev->xenfb->xsh, dev->otherend);
1013 if (!((1 <<state) & ((1 << XenbusStateUnknown) |
1014 (1 << XenbusStateConnected)))) {
1015 fprintf(stderr, "FB: Carry on waiting\n");
1016 return 1;
1019 /* Don't unwatch frontend - we need to detect shutdown */
1020 /*xs_unwatch(dev->xenfb->xsh, dev->otherend, "");*/
1022 switch (state) {
1023 case XenbusStateConnected:
1024 break;
1025 default:
1026 return -1;
1028 return 0;
1032 /* Helper to determine if a frontend device is in Initialized state */
1033 static int xenfb_frontend_initialized(struct xenfb_device *dev)
1035 unsigned int state;
1036 unsigned int dummy;
1037 char **vec;
1038 vec = xs_read_watch(dev->xenfb->xsh, &dummy);
1039 if (!vec)
1040 return -1;
1041 free(vec);
1043 state = xenfb_read_state(dev->xenfb->xsh, dev->otherend);
1045 if (!((1 << state) & ((1 << XenbusStateUnknown)
1046 | (1 << XenbusStateInitialised)
1047 #if 1 /* TODO fudging state to permit restarting; to be removed */
1048 | (1 << XenbusStateConnected)
1049 #endif
1050 ))) {
1051 fprintf(stderr, "FB: Carry on waiting\n");
1052 return 1;
1055 xs_unwatch(dev->xenfb->xsh, dev->otherend, "");
1057 switch (state) {
1058 #if 1
1059 case XenbusStateConnected:
1060 printf("Fudging state to %d\n", XenbusStateInitialised); /* FIXME */
1061 #endif
1062 case XenbusStateInitialised:
1063 break;
1064 default:
1065 return -1;
1068 if (xenfb_bind(dev) < 0)
1069 return -1;
1071 return 0;
1074 /* Helper to determine if a backend device is in Created state */
1075 static int xenfb_backend_created(struct xenfb_device *dev)
1077 unsigned int state;
1078 unsigned int dummy;
1079 char **vec;
1080 vec = xs_read_watch(dev->xenfb->xsh, &dummy);
1081 if (!vec)
1082 return -1;
1083 free(vec);
1085 state = xenfb_read_state(dev->xenfb->xsh, dev->nodename);
1087 if (!((1 <<state) & ((1 << XenbusStateUnknown)
1088 | (1 << XenbusStateInitialising)
1089 | (1 << XenbusStateClosed)
1090 #if 1 /* TODO fudging state to permit restarting; to be removed */
1091 | (1 << XenbusStateInitWait)
1092 | (1 << XenbusStateConnected)
1093 | (1 << XenbusStateClosing)
1094 #endif
1095 ))) {
1096 fprintf(stderr, "FB: Carry on waiting\n");
1097 return 1;
1100 xs_unwatch(dev->xenfb->xsh, dev->nodename, "");
1102 switch (state) {
1103 #if 1
1104 case XenbusStateInitWait:
1105 case XenbusStateConnected:
1106 printf("Fudging state to %d\n", XenbusStateInitialising); /* FIXME */
1107 #endif
1108 case XenbusStateInitialising:
1109 case XenbusStateClosing:
1110 case XenbusStateClosed:
1111 break;
1112 default:
1113 fprintf(stderr, "Wrong state %d\n", state);
1114 return -1;
1116 xenfb_switch_state(dev, XenbusStateInitWait);
1117 if (xenfb_hotplug(dev) < 0)
1118 return -1;
1120 return 0;
1124 /****************************************************************
1126 * QEMU device model integration functions
1128 ****************************************************************/
1130 /*
1131 * Send a key event from the client to the guest OS
1132 * QEMU gives us a raw scancode from an AT / PS/2 style keyboard.
1133 * We have to turn this into a Linux Input layer keycode.
1135 * Extra complexity from the fact that with extended scancodes
1136 * (like those produced by arrow keys) this method gets called
1137 * twice, but we only want to send a single event. So we have to
1138 * track the '0xe0' scancode state & collapse the extended keys
1139 * as needed.
1141 * Wish we could just send scancodes straight to the guest which
1142 * already has code for dealing with this...
1143 */
1144 static void xenfb_key_event(void *opaque, int scancode)
1146 static int extended = 0;
1147 int down = 1;
1148 if (scancode == 0xe0) {
1149 extended = 1;
1150 return;
1151 } else if (scancode & 0x80) {
1152 scancode &= 0x7f;
1153 down = 0;
1155 if (extended) {
1156 scancode |= 0x80;
1157 extended = 0;
1159 xenfb_send_key(opaque, down, scancode2linux[scancode]);
1162 /*
1163 * Send a mouse event from the client to the guest OS
1165 * The QEMU mouse can be in either relative, or absolute mode.
1166 * Movement is sent separately from button state, which has to
1167 * be encoded as virtual key events. We also don't actually get
1168 * given any button up/down events, so have to track changes in
1169 * the button state.
1170 */
1171 static void xenfb_mouse_event(void *opaque,
1172 int dx, int dy, int dz, int button_state)
1174 int i;
1175 struct xenfb *xenfb = opaque;
1176 if (xenfb->abs_pointer_wanted)
1177 xenfb_send_position(xenfb,
1178 dx * (xenfb->ds->width - 1) / 0x7fff,
1179 dy * (xenfb->ds->height - 1) / 0x7fff,
1180 dz);
1181 else
1182 xenfb_send_motion(xenfb, dx, dy, dz);
1184 for (i = 0 ; i < 8 ; i++) {
1185 int lastDown = xenfb->button_state & (1 << i);
1186 int down = button_state & (1 << i);
1187 if (down == lastDown)
1188 continue;
1190 if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0)
1191 return;
1193 xenfb->button_state = button_state;
1196 /* A convenient function for munging pixels between different depths */
1197 #define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \
1198 for (line = y ; line < (y+h) ; line++) { \
1199 SRC_T *src = (SRC_T *)(xenfb->pixels \
1200 + xenfb->offset \
1201 + (line * xenfb->row_stride) \
1202 + (x * xenfb->depth / 8)); \
1203 DST_T *dst = (DST_T *)(xenfb->ds->data \
1204 + (line * xenfb->ds->linesize) \
1205 + (x * xenfb->ds->depth / 8)); \
1206 int col; \
1207 const int RSS = 32 - (RSB + GSB + BSB); \
1208 const int GSS = 32 - (GSB + BSB); \
1209 const int BSS = 32 - (BSB); \
1210 const uint32_t RSM = (~0U) << (32 - RSB); \
1211 const uint32_t GSM = (~0U) << (32 - GSB); \
1212 const uint32_t BSM = (~0U) << (32 - BSB); \
1213 const int RDS = 32 - (RDB + GDB + BDB); \
1214 const int GDS = 32 - (GDB + BDB); \
1215 const int BDS = 32 - (BDB); \
1216 const uint32_t RDM = (~0U) << (32 - RDB); \
1217 const uint32_t GDM = (~0U) << (32 - GDB); \
1218 const uint32_t BDM = (~0U) << (32 - BDB); \
1219 for (col = x ; col < (x+w) ; col++) { \
1220 uint32_t spix = *src; \
1221 *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \
1222 (((spix << GSS) & GSM & GDM) >> GDS) | \
1223 (((spix << BSS) & BSM & BDM) >> BDS); \
1224 src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \
1225 dst = (DST_T *) ((unsigned long) dst + xenfb->ds->depth / 8); \
1226 } \
1230 /* This copies data from the guest framebuffer region, into QEMU's copy
1231 * NB. QEMU's copy is stored in the pixel format of a) the local X
1232 * server (SDL case) or b) the current VNC client pixel format.
1233 * When shifting between colour depths we preserve the MSB.
1234 */
1235 static void xenfb_guest_copy(struct xenfb *xenfb, int x, int y, int w, int h)
1237 int line;
1239 if (!xenfb->ds->shared_buf) {
1240 if (xenfb->depth == xenfb->ds->depth) { /* Perfect match can use fast path */
1241 for (line = y ; line < (y+h) ; line++) {
1242 memcpy(xenfb->ds->data + (line * xenfb->ds->linesize) + (x * xenfb->ds->depth / 8),
1243 xenfb->pixels + xenfb->offset + (line * xenfb->row_stride) + (x * xenfb->depth / 8),
1244 w * xenfb->depth / 8);
1246 } else { /* Mismatch requires slow pixel munging */
1247 /* 8 bit == r:3 g:3 b:2 */
1248 /* 16 bit == r:5 g:6 b:5 */
1249 /* 24 bit == r:8 g:8 b:8 */
1250 /* 32 bit == r:8 g:8 b:8 (padding:8) */
1251 if (xenfb->depth == 8) {
1252 if (xenfb->ds->depth == 16) {
1253 BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5);
1254 } else if (xenfb->ds->depth == 32) {
1255 BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8);
1257 } else if (xenfb->depth == 16) {
1258 if (xenfb->ds->depth == 8) {
1259 BLT(uint16_t, uint8_t, 5, 6, 5, 3, 3, 2);
1260 } else if (xenfb->ds->depth == 32) {
1261 BLT(uint16_t, uint32_t, 5, 6, 5, 8, 8, 8);
1263 } else if (xenfb->depth == 24 || xenfb->depth == 32) {
1264 if (xenfb->ds->depth == 8) {
1265 BLT(uint32_t, uint8_t, 8, 8, 8, 3, 3, 2);
1266 } else if (xenfb->ds->depth == 16) {
1267 BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5);
1268 } else if (xenfb->ds->depth == 32) {
1269 BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8);
1274 dpy_update(xenfb->ds, x, y, w, h);
1277 /* Periodic update of display, transmit the refresh interval to the frontend */
1278 static void xenfb_update(void *opaque)
1280 struct xenfb *xenfb = opaque;
1281 int period;
1283 if (xenfb_queue_full(xenfb))
1284 return;
1286 if (xenfb->ds->idle)
1287 period = XENFB_NO_REFRESH;
1288 else {
1289 period = xenfb->ds->gui_timer_interval;
1290 if (!period)
1291 period = GUI_REFRESH_INTERVAL;
1294 /* Will have to be disabled for frontends without feature-update */
1295 if (xenfb->refresh_period != period) {
1296 xenfb_send_refresh_period(xenfb, period);
1297 xenfb->refresh_period = period;
1301 /* QEMU display state changed, so refresh the framebuffer copy */
1302 static void xenfb_invalidate(void *opaque)
1304 struct xenfb *xenfb = opaque;
1305 xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height);
1308 /* Screen dump is not used in Xen, so no need to impl this....yet */
1309 static void xenfb_screen_dump(void *opaque, const char *name) { }
1312 /* Register a QEMU graphical console, and key/mouse handler,
1313 * connecting up their events to the frontend */
1314 static int xenfb_register_console(struct xenfb *xenfb) {
1315 /* Register our keyboard & mouse handlers */
1316 qemu_add_kbd_event_handler(xenfb_key_event, xenfb);
1317 qemu_add_mouse_event_handler(xenfb_mouse_event, xenfb,
1318 xenfb->abs_pointer_wanted,
1319 "Xen PVFB Mouse");
1321 /* Tell QEMU to allocate a graphical console */
1322 graphic_console_init(xenfb->ds,
1323 xenfb_update,
1324 xenfb_invalidate,
1325 xenfb_screen_dump,
1326 xenfb);
1327 dpy_colourdepth(xenfb->ds, xenfb->depth);
1328 dpy_resize(xenfb->ds, xenfb->width, xenfb->height, xenfb->row_stride);
1329 if (xenfb->ds->shared_buf)
1330 dpy_setdata(xenfb->ds, xenfb->pixels);
1332 if (qemu_set_fd_handler2(xc_evtchn_fd(xenfb->evt_xch), NULL, xenfb_dispatch_channel, NULL, xenfb) < 0)
1333 return -1;
1334 if (qemu_set_fd_handler2(xs_fileno(xenfb->xsh), NULL, xenfb_dispatch_store, NULL, xenfb) < 0)
1335 return -1;
1337 fprintf(stderr, "Xen Framebuffer registered\n");
1338 return 0;
1341 #ifdef CONFIG_STUBDOM
1342 typedef struct XenFBState {
1343 struct semaphore kbd_sem;
1344 struct kbdfront_dev *kbd_dev;
1345 struct fbfront_dev *fb_dev;
1346 void *vga_vram, *nonshared_vram;
1347 DisplayState *ds;
1348 } XenFBState;
1350 XenFBState *xs;
1352 static char *kbd_path, *fb_path;
1354 static unsigned char linux2scancode[KEY_MAX + 1];
1356 int xenfb_connect_vkbd(const char *path)
1358 kbd_path = strdup(path);
1359 return 0;
1362 int xenfb_connect_vfb(const char *path)
1364 fb_path = strdup(path);
1365 return 0;
1368 static void xenfb_pv_update(DisplayState *ds, int x, int y, int w, int h)
1370 XenFBState *xs = ds->opaque;
1371 struct fbfront_dev *fb_dev = xs->fb_dev;
1372 if (!fb_dev)
1373 return;
1374 fbfront_update(fb_dev, x, y, w, h);
1377 static void xenfb_pv_resize(DisplayState *ds, int w, int h, int linesize)
1379 XenFBState *xs = ds->opaque;
1380 struct fbfront_dev *fb_dev = xs->fb_dev;
1381 fprintf(stderr,"resize to %dx%d, %d required\n", w, h, linesize);
1382 ds->width = w;
1383 ds->height = h;
1384 if (!linesize)
1385 ds->shared_buf = 0;
1386 if (!ds->shared_buf)
1387 linesize = w * 4;
1388 ds->linesize = linesize;
1389 if (!fb_dev)
1390 return;
1391 if (ds->shared_buf) {
1392 ds->data = NULL;
1393 } else {
1394 ds->data = xs->nonshared_vram;
1395 fbfront_resize(fb_dev, w, h, linesize, ds->depth, VGA_RAM_SIZE);
1399 static void xenfb_pv_colourdepth(DisplayState *ds, int depth)
1401 XenFBState *xs = ds->opaque;
1402 struct fbfront_dev *fb_dev = xs->fb_dev;
1403 static int lastdepth = -1;
1404 if (!depth) {
1405 ds->shared_buf = 0;
1406 ds->depth = 32;
1407 } else {
1408 ds->shared_buf = 1;
1409 ds->depth = depth;
1411 if (depth != lastdepth) {
1412 fprintf(stderr,"redepth to %d required\n", depth);
1413 lastdepth = depth;
1414 } else return;
1415 if (!fb_dev)
1416 return;
1417 if (ds->shared_buf) {
1418 ds->data = NULL;
1419 } else {
1420 ds->data = xs->nonshared_vram;
1421 fbfront_resize(fb_dev, ds->width, ds->height, ds->linesize, ds->depth, VGA_RAM_SIZE);
1425 static void xenfb_pv_setdata(DisplayState *ds, void *pixels)
1427 XenFBState *xs = ds->opaque;
1428 struct fbfront_dev *fb_dev = xs->fb_dev;
1429 int offset = pixels - xs->vga_vram;
1430 ds->data = pixels;
1431 if (!fb_dev)
1432 return;
1433 fbfront_resize(fb_dev, ds->width, ds->height, ds->linesize, ds->depth, offset);
1436 static void xenfb_pv_refresh(DisplayState *ds)
1438 vga_hw_update();
1441 static void xenfb_fb_handler(void *opaque)
1443 #define FB_NUM_BATCH 4
1444 union xenfb_in_event buf[FB_NUM_BATCH];
1445 int n, i;
1446 XenFBState *xs = opaque;
1447 DisplayState *ds = xs->ds;
1449 n = fbfront_receive(xs->fb_dev, buf, FB_NUM_BATCH);
1450 for (i = 0; i < n; i++) {
1451 switch (buf[i].type) {
1452 case XENFB_TYPE_REFRESH_PERIOD:
1453 if (buf[i].refresh_period.period == XENFB_NO_REFRESH) {
1454 /* Sleeping interval */
1455 ds->idle = 1;
1456 ds->gui_timer_interval = 500;
1457 } else {
1458 /* Set interval */
1459 ds->idle = 0;
1460 ds->gui_timer_interval = buf[i].refresh_period.period;
1462 default:
1463 /* ignore unknown events */
1464 break;
1469 static void xenfb_kbd_handler(void *opaque)
1471 #define KBD_NUM_BATCH 64
1472 union xenkbd_in_event buf[KBD_NUM_BATCH];
1473 int n, i;
1474 XenFBState *xs = opaque;
1475 DisplayState *s = xs->ds;
1476 static int buttons;
1477 static int x, y;
1479 n = kbdfront_receive(xs->kbd_dev, buf, KBD_NUM_BATCH);
1480 for (i = 0; i < n; i++) {
1481 switch (buf[i].type) {
1483 case XENKBD_TYPE_MOTION:
1484 fprintf(stderr, "FB backend sent us relative mouse motion event!\n");
1485 break;
1487 case XENKBD_TYPE_POS:
1489 int new_x = buf[i].pos.abs_x;
1490 int new_y = buf[i].pos.abs_y;
1491 if (new_x >= s->width)
1492 new_x = s->width - 1;
1493 if (new_y >= s->height)
1494 new_y = s->height - 1;
1495 if (kbd_mouse_is_absolute()) {
1496 kbd_mouse_event(
1497 new_x * 0x7FFF / (s->width - 1),
1498 new_y * 0x7FFF / (s->height - 1),
1499 buf[i].pos.rel_z,
1500 buttons);
1501 } else {
1502 kbd_mouse_event(
1503 new_x - x,
1504 new_y - y,
1505 buf[i].pos.rel_z,
1506 buttons);
1508 x = new_x;
1509 y = new_y;
1510 break;
1513 case XENKBD_TYPE_KEY:
1515 int keycode = buf[i].key.keycode;
1516 int button = 0;
1518 if (keycode == BTN_LEFT)
1519 button = MOUSE_EVENT_LBUTTON;
1520 else if (keycode == BTN_RIGHT)
1521 button = MOUSE_EVENT_RBUTTON;
1522 else if (keycode == BTN_MIDDLE)
1523 button = MOUSE_EVENT_MBUTTON;
1525 if (button) {
1526 if (buf[i].key.pressed)
1527 buttons |= button;
1528 else
1529 buttons &= ~button;
1530 if (kbd_mouse_is_absolute())
1531 kbd_mouse_event(
1532 x * 0x7FFF / (s->width - 1),
1533 y * 0x7FFF / (s->height - 1),
1534 0,
1535 buttons);
1536 else
1537 kbd_mouse_event(0, 0, 0, buttons);
1538 } else {
1539 int scancode = linux2scancode[keycode];
1540 if (!scancode) {
1541 fprintf(stderr, "Can't convert keycode %x to scancode\n", keycode);
1542 break;
1544 if (scancode & 0x80) {
1545 kbd_put_keycode(0xe0);
1546 scancode &= 0x7f;
1548 if (!buf[i].key.pressed)
1549 scancode |= 0x80;
1550 kbd_put_keycode(scancode);
1552 break;
1558 static void kbdfront_thread(void *p)
1560 int scancode, keycode;
1561 XenFBState *xs = p;
1562 xs->kbd_dev = init_kbdfront(kbd_path, 1);
1563 if (!xs->kbd_dev) {
1564 fprintf(stderr,"can't open keyboard\n");
1565 exit(1);
1567 up(&xs->kbd_sem);
1568 for (scancode = 0; scancode < 128; scancode++) {
1569 keycode = atkbd_set2_keycode[atkbd_unxlate_table[scancode]];
1570 linux2scancode[keycode] = scancode;
1571 keycode = atkbd_set2_keycode[atkbd_unxlate_table[scancode] | 0x80];
1572 linux2scancode[keycode] = scancode | 0x80;
1576 int xenfb_pv_display_init(DisplayState *ds)
1578 if (!fb_path || !kbd_path)
1579 return -1;
1581 xs = qemu_mallocz(sizeof(XenFBState));
1582 if (!xs)
1583 return -1;
1585 init_SEMAPHORE(&xs->kbd_sem, 0);
1586 xs->ds = ds;
1588 create_thread("kbdfront", kbdfront_thread, (void*) xs);
1590 ds->data = xs->nonshared_vram = qemu_memalign(PAGE_SIZE, VGA_RAM_SIZE);
1591 memset(ds->data, 0, VGA_RAM_SIZE);
1592 ds->opaque = xs;
1593 ds->depth = 32;
1594 ds->bgr = 0;
1595 ds->width = 640;
1596 ds->height = 400;
1597 ds->linesize = 640 * 4;
1598 ds->dpy_update = xenfb_pv_update;
1599 ds->dpy_resize = xenfb_pv_resize;
1600 ds->dpy_colourdepth = xenfb_pv_colourdepth;
1601 ds->dpy_setdata = xenfb_pv_setdata;
1602 ds->dpy_refresh = xenfb_pv_refresh;
1603 return 0;
1606 int xenfb_pv_display_start(void *data)
1608 DisplayState *ds;
1609 struct fbfront_dev *fb_dev;
1610 int kbd_fd, fb_fd;
1611 int offset = 0;
1612 unsigned long *mfns;
1613 int n = VGA_RAM_SIZE / PAGE_SIZE;
1614 int i;
1616 if (!fb_path || !kbd_path)
1617 return 0;
1619 ds = xs->ds;
1620 xs->vga_vram = data;
1621 mfns = malloc(2 * n * sizeof(*mfns));
1622 for (i = 0; i < n; i++)
1623 mfns[i] = virtual_to_mfn(xs->vga_vram + i * PAGE_SIZE);
1624 for (i = 0; i < n; i++)
1625 mfns[n + i] = virtual_to_mfn(xs->nonshared_vram + i * PAGE_SIZE);
1627 fb_dev = init_fbfront(fb_path, mfns, ds->width, ds->height, ds->depth, ds->linesize, 2 * n);
1628 free(mfns);
1629 if (!fb_dev) {
1630 fprintf(stderr,"can't open frame buffer\n");
1631 exit(1);
1633 free(fb_path);
1635 if (ds->shared_buf) {
1636 offset = (void*) ds->data - xs->vga_vram;
1637 } else {
1638 offset = VGA_RAM_SIZE;
1639 ds->data = xs->nonshared_vram;
1641 if (offset)
1642 fbfront_resize(fb_dev, ds->width, ds->height, ds->linesize, ds->depth, offset);
1644 down(&xs->kbd_sem);
1645 free(kbd_path);
1647 kbd_fd = kbdfront_open(xs->kbd_dev);
1648 qemu_set_fd_handler(kbd_fd, xenfb_kbd_handler, NULL, xs);
1650 fb_fd = fbfront_open(fb_dev);
1651 qemu_set_fd_handler(fb_fd, xenfb_fb_handler, NULL, xs);
1653 xs->fb_dev = fb_dev;
1654 return 0;
1656 #endif
1658 /*
1659 * Local variables:
1660 * c-indent-level: 8
1661 * c-basic-offset: 8
1662 * tab-width: 8
1663 * End:
1664 */