PV framebuffer frontend. Derived from http://hg.codemonkey.ws/vncfb Extensive changes based on feedback from xen-devel. Signed-off-by: Markus Armbruster <armbru@redhat.com> --- tools/Makefile | 1 tools/python/xen/xend/XendDevices.py | 4 tools/python/xen/xend/server/vfbif.py | 29 + tools/xenfb/Makefile | 33 + tools/xenfb/sdlfb.c | 337 ++++++++++++++++++++ tools/xenfb/vncfb.c | 396 +++++++++++++++++++++++ tools/xenfb/xenfb.c | 571 ++++++++++++++++++++++++++++++++++ tools/xenfb/xenfb.h | 34 ++ 8 files changed, 1404 insertions(+), 1 deletion(-) diff -r 2e35cf028ff0 tools/Makefile --- a/tools/Makefile Thu Nov 09 15:43:24 2006 +0000 +++ b/tools/Makefile Fri Nov 10 08:01:00 2006 +0100 @@ -19,6 +19,7 @@ SUBDIRS-y += libaio SUBDIRS-y += libaio SUBDIRS-y += blktap SUBDIRS-y += libfsimage +SUBDIRS-y += xenfb # These don''t cross-compile ifeq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH)) diff -r 2e35cf028ff0 tools/python/xen/xend/XendDevices.py --- a/tools/python/xen/xend/XendDevices.py Thu Nov 09 15:43:24 2006 +0000 +++ b/tools/python/xen/xend/XendDevices.py Thu Nov 09 17:58:26 2006 +0100 @@ -19,7 +19,7 @@ # A collection of DevControllers # -from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif +from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif, vfbif from xen.xend.server.BlktapController import BlktapController class XendDevices: @@ -41,6 +41,8 @@ class XendDevices: ''irq'': irqif.IRQController, ''usb'': usbif.UsbifController, ''tap'': BlktapController, + ''vfb'': vfbif.VfbifController, + ''vkbd'': vfif.VkbdifController, } #@classmethod diff -r 2e35cf028ff0 tools/python/xen/xend/server/vfbif.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/python/xen/xend/server/vfbif.py Tue Nov 07 14:46:46 2006 +0100 @@ -0,0 +1,29 @@ +from xen.xend.server.DevController import DevController + +class VfbifController(DevController): + """Virtual frame buffer controller. Handles all vfb devices for a domain. + """ + + def __init__(self, vm): + DevController.__init__(self, vm) + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + devid = 0 + back = {} + front = {} + return (devid, back, front) + +class VkbdifController(DevController): + """Virtual keyboard controller. Handles all vkbd devices for a domain. + """ + + def __init__(self, vm): + DevController.__init__(self, vm) + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + devid = 0 + back = {} + front = {} + return (devid, back, front) diff -r 2e35cf028ff0 tools/xenfb/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/Makefile Thu Nov 09 10:19:26 2006 +0100 @@ -0,0 +1,33 @@ +XEN_ROOT=../.. +include $(XEN_ROOT)/tools/Rules.mk + +CFLAGS += -I$(XEN_LIBXC) -I$(XEN_XENSTORE) -I$(XEN_ROOT)/linux-2.6-xen-sparse/include +LDFLAGS += -L$(XEN_LIBXC) -L$(XEN_XENSTORE) + +INSTALL = install +INSTALL_PROG = $(INSTALL) -m0755 +INSTALL_DIR = $(INSTALL) -d -m0755 + +.PHONY: all +all: build + +.PHONY: build +build: mk-symlinks + $(MAKE) vncfb sdlfb + +install: all + $(INSTALL_DIR) -p $(DESTDIR)/usr/$(LIBDIR)/xen/bin + $(INSTALL_PROG) vncfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-vncfb + $(INSTALL_PROG) sdlfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-sdlfb + +sdlfb: sdlfb.o xenfb.o + +sdlfb.o: CFLAGS += $(shell sdl-config --cflags) +sdlfb: LDLIBS += $(shell sdl-config --libs) -lxenctrl -lxenstore + +clean: + $(RM) *.o *~ vncfb sdlfb + +vncfb: vncfb.o xenfb.o +vncfb.o: CFLAGS += $(shell libvncserver-config --cflags) +vncfb: LDLIBS += $(shell libvncserver-config --libs) -lxenctrl -lxenstore diff -r 2e35cf028ff0 tools/xenfb/sdlfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/sdlfb.c Wed Nov 08 11:35:54 2006 +0100 @@ -0,0 +1,337 @@ +#include <SDL.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/select.h> +#include <stdlib.h> +#include <linux/input.h> +#include <getopt.h> +#include <string.h> +#include "xenfb.h" + +struct SDLFBData +{ + SDL_Surface *dst; + SDL_Surface *src; +}; + +/* + * Map from scancode to Linux input layer keycode. Scancodes are + * hardware-specific. This map assumes a standard AT or PS/2 + * keyboard. + * + * Why use scancodes? We can''t use key symbols, because they don''t + * identify keys --- they''re what keys are mapped to. The standard + * German keymap, for instance, maps both KEY_COMMA and KEY_102ND to + * SDLK_LESS. + */ +static int keymap[256] = { + [9] = KEY_ESC, + [10] = KEY_1, + [11] = KEY_2, + [12] = KEY_3, + [13] = KEY_4, + [14] = KEY_5, + [15] = KEY_6, + [16] = KEY_7, + [17] = KEY_8, + [18] = KEY_9, + [19] = KEY_0, + [20] = KEY_MINUS, + [21] = KEY_EQUAL, + [22] = KEY_BACKSPACE, + [23] = KEY_TAB, + [24] = KEY_Q, + [25] = KEY_W, + [26] = KEY_E, + [27] = KEY_R, + [28] = KEY_T, + [29] = KEY_Y, + [30] = KEY_U, + [31] = KEY_I, + [32] = KEY_O, + [33] = KEY_P, + [34] = KEY_LEFTBRACE, + [35] = KEY_RIGHTBRACE, + [36] = KEY_ENTER, + [37] = KEY_LEFTCTRL, + [38] = KEY_A, + [39] = KEY_S, + [40] = KEY_D, + [41] = KEY_F, + [42] = KEY_G, + [43] = KEY_H, + [44] = KEY_J, + [45] = KEY_K, + [46] = KEY_L, + [47] = KEY_SEMICOLON, + [48] = KEY_APOSTROPHE, + [49] = KEY_GRAVE, + [50] = KEY_LEFTSHIFT, + [51] = KEY_BACKSLASH, + [52] = KEY_Z, + [53] = KEY_X, + [54] = KEY_C, + [55] = KEY_V, + [56] = KEY_B, + [57] = KEY_N, + [58] = KEY_M, + [59] = KEY_COMMA, + [60] = KEY_DOT, + [61] = KEY_SLASH, + [62] = KEY_RIGHTSHIFT, + [63] = KEY_KPASTERISK, + [64] = KEY_LEFTALT, + [65] = KEY_SPACE, + [66] = KEY_CAPSLOCK, + [67] = KEY_F1, + [68] = KEY_F2, + [69] = KEY_F3, + [70] = KEY_F4, + [71] = KEY_F5, + [72] = KEY_F6, + [73] = KEY_F7, + [74] = KEY_F8, + [75] = KEY_F9, + [76] = KEY_F10, + [77] = KEY_NUMLOCK, + [78] = KEY_SCROLLLOCK, + [79] = KEY_KP7, + [80] = KEY_KP8, + [81] = KEY_KP9, + [82] = KEY_KPMINUS, + [83] = KEY_KP4, + [84] = KEY_KP5, + [85] = KEY_KP6, + [86] = KEY_KPPLUS, + [87] = KEY_KP1, + [88] = KEY_KP2, + [89] = KEY_KP3, + [90] = KEY_KP0, + [91] = KEY_KPDOT, + [94] = KEY_102ND, /* FIXME is this correct? */ + [95] = KEY_F11, + [96] = KEY_F12, + [108] = KEY_KPENTER, + [109] = KEY_RIGHTCTRL, + [112] = KEY_KPSLASH, + [111] = KEY_SYSRQ, + [113] = KEY_RIGHTALT, + [97] = KEY_HOME, + [98] = KEY_UP, + [99] = KEY_PAGEUP, + [100] = KEY_LEFT, + [102] = KEY_RIGHT, + [103] = KEY_END, + [104] = KEY_DOWN, + [105] = KEY_PAGEDOWN, + [106] = KEY_INSERT, + [107] = KEY_DELETE, + [110] = KEY_PAUSE, + [115] = KEY_LEFTMETA, + [116] = KEY_RIGHTMETA, + [117] = KEY_MENU, +}; + +static int btnmap[] = { + [SDL_BUTTON_LEFT] = BTN_LEFT, + [SDL_BUTTON_MIDDLE] = BTN_MIDDLE, + [SDL_BUTTON_RIGHT] = BTN_RIGHT, + /* FIXME not 100% sure about these: */ + [SDL_BUTTON_WHEELUP] = BTN_FORWARD, + [SDL_BUTTON_WHEELDOWN] BTN_BACK +}; + +static void sdl_update(struct xenfb *xenfb, int x, int y, int width, int height) +{ + struct SDLFBData *data = xenfb->user_data; + SDL_Rect r = { x, y, width, height }; + SDL_BlitSurface(data->src, &r, data->dst, &r); + SDL_UpdateRect(data->dst, x, y, width, height); +} + +static int sdl_on_event(struct xenfb *xenfb, SDL_Event *event) +{ + int x, y, ret; + + switch (event->type) { + case SDL_KEYDOWN: + case SDL_KEYUP: + if (keymap[event->key.keysym.scancode] == 0) + break; + ret = xenfb_send_key(xenfb, + event->type == SDL_KEYDOWN, + keymap[event->key.keysym.scancode]); + if (ret < 0) + fprintf(stderr, "Key %d %s lost (%s)\n", + keymap[event->key.keysym.scancode], + event->type == SDL_KEYDOWN ? "down" : "up", + strerror(errno)); + break; + case SDL_MOUSEMOTION: + if (xenfb->abs_pointer_wanted) { + SDL_GetMouseState(&x, &y); + ret = xenfb_send_position(xenfb, x, y); + } else { + SDL_GetRelativeMouseState(&x, &y); + ret = xenfb_send_motion(xenfb, x, y); + } + if (ret < 0) + fprintf(stderr, "Pointer to %d,%d lost (%s)\n", + x, y, strerror(errno)); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + if (event->button.button >= sizeof(btnmap) / sizeof(*btnmap)) + break; + if (btnmap[event->button.button] == 0) + break; + ret = xenfb_send_key(xenfb, + event->type == SDL_MOUSEBUTTONDOWN, + btnmap[event->button.button]); + if (ret < 0) + fprintf(stderr, "Button %d %s lost (%s)\n", + btnmap[event->button.button] - BTN_MOUSE, + event->type == SDL_MOUSEBUTTONDOWN ? "down" : "up", + strerror(errno)); + break; + case SDL_QUIT: + return 0; + } + + return 1; +} + +static struct option options[] = { + { "domid", 1, NULL, ''d'' }, + { "title", 1, NULL, ''t'' }, +}; + +int main(int argc, char **argv) +{ + struct xenfb *xenfb; + int fd; + int domid = -1; + char * title = NULL; + fd_set readfds; + struct SDLFBData data; + SDL_Rect r; + struct timeval tv; + SDL_Event event; + int do_quit = 0; + int opt; + char *endp; + + while ((opt = getopt_long(argc, argv, "d:t:", options, + NULL)) != -1) { + switch (opt) { + case ''d'': + domid = strtol(optarg, &endp, 10); + if (endp == optarg || *endp) { + fprintf(stderr, "Invalid domain id specified\n"); + exit(1); + } + break; + case ''t'': + title = strdup(optarg); + break; + } + } + if (optind != argc) { + fprintf(stderr, "Invalid options!\n"); + exit(1); + } + if (domid <= 0) { + fprintf(stderr, "Domain ID must be specified!\n"); + exit(1); + } + + xenfb = xenfb_new(); + if (xenfb == NULL) { + fprintf(stderr, "Could not create framebuffer (%s)\n", + strerror(errno)); + exit(1); + } + + if (!xenfb_attach_dom(xenfb, domid)) { + fprintf(stderr, "Could not connect to domain (%s)\n", + strerror(errno)); + exit(1); + } + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "Could not initialize SDL\n"); + exit(1); + } + + fd = xenfb_get_fileno(xenfb); + + data.dst = SDL_SetVideoMode(xenfb->width, xenfb->height, xenfb->depth, + SDL_SWSURFACE); + if (!data.dst) { + fprintf(stderr, "SDL_SetVideoMode failed\n"); + exit(1); + } + + data.src = SDL_CreateRGBSurfaceFrom(xenfb->pixels, + xenfb->width, xenfb->height, + xenfb->depth, xenfb->row_stride, + 0xFF0000, 0xFF00, 0xFF, 0); + + if (!data.src) { + fprintf(stderr, "SDL_CreateRGBSurfaceFrom failed\n"); + exit(1); + } + + if (title == NULL) + title = strdup("xen-sdlfb"); + SDL_WM_SetCaption(title, title); + + r.x = r.y = 0; + r.w = xenfb->width; + r.h = xenfb->height; + SDL_BlitSurface(data.src, &r, data.dst, &r); + SDL_UpdateRect(data.dst, 0, 0, xenfb->width, xenfb->height); + + xenfb->update = sdl_update; + xenfb->user_data = &data; + + SDL_ShowCursor(0); + + /* + * We need to wait for fd becoming ready or SDL events to + * arrive. We time out the select after 10ms to poll for SDL + * events. Clunky, but works. Could avoid the clunkiness + * with a separate thread. + */ + for (;;) { + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + tv = (struct timeval){0, 10000}; + + if (select(fd + 1, &readfds, NULL, NULL, &tv) < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, + "Can''t select() on event channel (%s)\n", + strerror(errno)); + break; + } + + while (SDL_PollEvent(&event)) { + if (!sdl_on_event(xenfb, &event)) + do_quit = 1; + } + + if (do_quit) + break; + + if (FD_ISSET(fd, &readfds)) + xenfb_on_incoming(xenfb); + } + + xenfb_delete(xenfb); + + SDL_Quit(); + + return 0; +} diff -r 2e35cf028ff0 tools/xenfb/vncfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/vncfb.c Thu Nov 09 10:18:58 2006 +0100 @@ -0,0 +1,396 @@ +#define _GNU_SOURCE +#include <errno.h> +#include <getopt.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <malloc.h> +#include <rfb/rfb.h> +#include <rfb/keysym.h> +#include <linux/input.h> +#include <xs.h> +#include "xenfb.h" + +static int xk2linux[0x10000] = { + [XK_a] = KEY_A, + [XK_b] = KEY_B, + [XK_c] = KEY_C, + [XK_d] = KEY_D, + [XK_e] = KEY_E, + [XK_f] = KEY_F, + [XK_g] = KEY_G, + [XK_h] = KEY_H, + [XK_i] = KEY_I, + [XK_j] = KEY_J, + [XK_k] = KEY_K, + [XK_l] = KEY_L, + [XK_m] = KEY_M, + [XK_n] = KEY_N, + [XK_o] = KEY_O, + [XK_p] = KEY_P, + [XK_q] = KEY_Q, + [XK_r] = KEY_R, + [XK_s] = KEY_S, + [XK_t] = KEY_T, + [XK_u] = KEY_U, + [XK_v] = KEY_V, + [XK_w] = KEY_W, + [XK_x] = KEY_X, + [XK_y] = KEY_Y, + [XK_z] = KEY_Z, + [XK_A] = KEY_A, + [XK_B] = KEY_B, + [XK_C] = KEY_C, + [XK_D] = KEY_D, + [XK_E] = KEY_E, + [XK_F] = KEY_F, + [XK_G] = KEY_G, + [XK_H] = KEY_H, + [XK_I] = KEY_I, + [XK_J] = KEY_J, + [XK_K] = KEY_K, + [XK_L] = KEY_L, + [XK_M] = KEY_M, + [XK_N] = KEY_N, + [XK_O] = KEY_O, + [XK_P] = KEY_P, + [XK_Q] = KEY_Q, + [XK_R] = KEY_R, + [XK_S] = KEY_S, + [XK_T] = KEY_T, + [XK_U] = KEY_U, + [XK_V] = KEY_V, + [XK_W] = KEY_W, + [XK_X] = KEY_X, + [XK_Y] = KEY_Y, + [XK_Z] = KEY_Z, + [XK_0] = KEY_0, + [XK_1] = KEY_1, + [XK_2] = KEY_2, + [XK_3] = KEY_3, + [XK_4] = KEY_4, + [XK_5] = KEY_5, + [XK_6] = KEY_6, + [XK_7] = KEY_7, + [XK_8] = KEY_8, + [XK_9] = KEY_9, + [XK_Return] = KEY_ENTER, + [XK_BackSpace] = KEY_BACKSPACE, + [XK_Tab] = KEY_TAB, + [XK_Pause] = KEY_PAUSE, + [XK_Delete] = KEY_DELETE, + [XK_slash] = KEY_SLASH, + [XK_minus] = KEY_MINUS, + [XK_equal] = KEY_EQUAL, + [XK_Escape] = KEY_ESC, + [XK_braceleft] = KEY_LEFTBRACE, + [XK_braceright] = KEY_RIGHTBRACE, + [XK_bracketleft] = KEY_LEFTMETA, + [XK_bracketright] = KEY_RIGHTMETA, + [XK_Control_L] = KEY_LEFTCTRL, + [XK_Control_R] = KEY_RIGHTCTRL, + [XK_Shift_L] = KEY_LEFTSHIFT, + [XK_Shift_R] = KEY_RIGHTSHIFT, + [XK_Alt_L] = KEY_LEFTALT, + [XK_Alt_R] = KEY_RIGHTALT, + [XK_semicolon] = KEY_SEMICOLON, + [XK_apostrophe] = KEY_APOSTROPHE, + [XK_grave] = KEY_GRAVE, + [XK_backslash] = KEY_BACKSLASH, + [XK_comma] = KEY_COMMA, + [XK_period] = KEY_DOT, + [XK_space] = KEY_SPACE, + [XK_Caps_Lock] = KEY_CAPSLOCK, + [XK_Num_Lock] = KEY_NUMLOCK, + [XK_Scroll_Lock] = KEY_SCROLLLOCK, + [XK_Sys_Req] = KEY_SYSRQ, + [XK_Linefeed] = KEY_LINEFEED, + [XK_Home] = KEY_HOME, + [XK_Pause] = KEY_PAUSE, + [XK_F1] = KEY_F1, + [XK_F2] = KEY_F2, + [XK_F3] = KEY_F3, + [XK_F4] = KEY_F4, + [XK_F5] = KEY_F5, + [XK_F6] = KEY_F6, + [XK_F7] = KEY_F7, + [XK_F8] = KEY_F8, + [XK_F9] = KEY_F9, + [XK_F10] = KEY_F10, + [XK_F11] = KEY_F11, + [XK_F12] = KEY_F12, + [XK_Up] = KEY_UP, + [XK_Page_Up] = KEY_PAGEUP, + [XK_Left] = KEY_LEFT, + [XK_Right] = KEY_RIGHT, + [XK_End] = KEY_END, + [XK_Down] = KEY_DOWN, + [XK_Page_Down] = KEY_PAGEDOWN, + [XK_Insert] = KEY_INSERT, + [XK_colon] = KEY_SEMICOLON, + [XK_quotedbl] = KEY_APOSTROPHE, + [XK_less] = KEY_COMMA, + [XK_greater] = KEY_DOT, + [XK_question] = KEY_SLASH, + [XK_bar] = KEY_BACKSLASH, + [XK_asciitilde] = KEY_GRAVE, + [XK_exclam] = KEY_1, + [XK_at] = KEY_2, + [XK_numbersign] = KEY_3, + [XK_dollar] = KEY_4, + [XK_percent] = KEY_5, + [XK_asciicircum] = KEY_6, + [XK_ampersand] = KEY_7, + [XK_asterisk] = KEY_8, + [XK_parenleft] = KEY_9, + [XK_parenright] = KEY_0, + [XK_underscore] = KEY_MINUS, + [XK_plus] = KEY_EQUAL, +}; + +static void on_kbd_event(rfbBool down, rfbKeySym keycode, rfbClientPtr cl) +{ + /* + * We need to map to the key''s Linux input layer keycode. + * Unfortunately, we don''t get the key here, only the + * rfbKeySym, which is what the key is mapped to. Mapping + * back to the key is impossible in general, even when you + * know the keymap. For instance, the standard German keymap + * maps both KEY_COMMA and KEY_102ND to XK_less. We simply + * assume standard US layout. This sucks. + */ + rfbScreenInfoPtr server = cl->screen; + struct xenfb *xenfb = server->screenData; + if (keycode >= sizeof(xk2linux) / sizeof(*xk2linux)) + return; + if (xk2linux[keycode] == 0) + return; + if (xenfb_send_key(xenfb, down, xk2linux[keycode]) < 0) + fprintf(stderr, "Key %d %s lost (%s)\n", + xk2linux[keycode], down ? "down" : "up", + strerror(errno)); +} + +static void on_ptr_event(int buttonMask, int x, int y, rfbClientPtr cl) +{ + /* initial pointer state: at (0,0), buttons up */ + static int last_x, last_y, last_button; + rfbScreenInfoPtr server = cl->screen; + struct xenfb *xenfb = server->screenData; + int i, last_down, down, ret; + + for (i = 0; i < 8; i++) { + last_down = last_button & (1 << i); + down = buttonMask & (1 << i); + if (down == last_down) + continue; + /* FIXME this assumes buttons are numbered the same; verify they are */ + if (xenfb_send_key(xenfb, down != 0, BTN_MOUSE + i) < 0) + fprintf(stderr, "Button %d %s lost (%s)\n", + i, down ? "down" : "up", strerror(errno)); + } + + if (x != last_x || y != last_y) { + if (xenfb->abs_pointer_wanted) + ret = xenfb_send_position(xenfb, x, y); + else + ret = xenfb_send_motion(xenfb, x - last_x, y - last_y); + if (ret < 0) + fprintf(stderr, "Pointer to %d,%d lost (%s)\n", + x, y, strerror(errno)); + } + + last_button = buttonMask; + last_x = x; + last_y = y; +} + +static void xenstore_write_vncport(int port, int domid) +{ + char *buf = NULL, *path; + char portstr[10]; + struct xs_handle *xsh = NULL; + + xsh = xs_daemon_open(); + if (xsh == NULL) + return; + + path = xs_get_domain_path(xsh, domid); + if (path == NULL) { + fprintf(stderr, "Can''t get domain path (%s)\n", + strerror(errno)); + goto out; + } + + if (asprintf(&buf, "%s/console/vnc-port", path) == -1) { + fprintf(stderr, "Can''t make vncport path\n"); + goto out; + } + + if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) { + fprintf(stderr, "Can''t make vncport value\n"); + goto out; + } + + if (!xs_write(xsh, XBT_NULL, buf, portstr, strlen(portstr))) + fprintf(stderr, "Can''t set vncport (%s)\n", + strerror(errno)); + + out: + free(buf); +} + + +static void vnc_update(struct xenfb *xenfb, int x, int y, int w, int h) +{ + rfbScreenInfoPtr server = xenfb->user_data; + rfbMarkRectAsModified(server, x, y, x + w, y + h); +} + +static struct option options[] = { + { "domid", 1, NULL, ''d'' }, + { "vncport", 1, NULL, ''p'' }, + { "title", 1, NULL, ''t'' }, + { "unused", 0, NULL, ''u'' }, + { "listen", 1, NULL, ''l'' }, +}; + +int main(int argc, char **argv) +{ + rfbScreenInfoPtr server; + char *fake_argv[7] = { "vncfb", "-rfbport", "5901", + "-desktop", "xen-vncfb", + "-listen", "0.0.0.0" }; + int fake_argc = sizeof(fake_argv) / sizeof(fake_argv[0]); + int domid = -1, port = -1; + char *title = NULL; + char *listen = NULL; + bool unused = false; + int opt; + struct xenfb *xenfb; + fd_set readfds; + int fd; + char portstr[10]; + char *endp; + + while ((opt = getopt_long(argc, argv, "d:p:t:u", options, + NULL)) != -1) { + switch (opt) { + case ''d'': + errno = 0; + domid = strtol(optarg, &endp, 10); + if (endp == optarg || *endp || errno) { + fprintf(stderr, "Invalid domain id specified\n"); + exit(1); + } + break; + case ''p'': + errno = 0; + port = strtol(optarg, &endp, 10); + if (endp == optarg || *endp || errno) { + fprintf(stderr, "Invalid port specified\n"); + exit(1); + } + break; + case ''t'': + title = strdup(optarg); + break; + case ''u'': + unused = true; + break; + case ''l'': + listen = strdup(optarg); + break; + } + } + if (optind != argc) { + fprintf(stderr, "Invalid options!\n"); + exit(1); + } + if (domid <= 0) { + fprintf(stderr, "Domain ID must be specified!\n"); + exit(1); + } + + if (port <= 0) + port = 5900 + domid; + if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) { + fprintf(stderr, "Invalid port specified\n"); + exit(1); + } + + fake_argv[2] = portstr; + + if (title != NULL) + fake_argv[4] = title; + + if (listen != NULL) + fake_argv[6] = listen; + + signal(SIGPIPE, SIG_IGN); + + xenfb = xenfb_new(); + if (xenfb == NULL) { + fprintf(stderr, "Could not create framebuffer (%s)\n", + strerror(errno)); + exit(1); + } + + if (!xenfb_attach_dom(xenfb, domid)) { + fprintf(stderr, "Could not connect to domain (%s)\n", + strerror(errno)); + exit(1); + } + + server = rfbGetScreen(&fake_argc, fake_argv, + xenfb->width, xenfb->height, + 8, 3, xenfb->depth / 8); + if (server == NULL) { + fprintf(stderr, "Could not create VNC server\n"); + exit(1); + } + + xenfb->user_data = server; + xenfb->update = vnc_update; + + if (unused) + server->autoPort = true; + + server->serverFormat.redShift = 16; + server->serverFormat.greenShift = 8; + server->serverFormat.blueShift = 0; + server->kbdAddEvent = on_kbd_event; + server->ptrAddEvent = on_ptr_event; + server->frameBuffer = (char *)xenfb->pixels; + server->screenData = xenfb; + server->cursor = NULL; + rfbInitServer(server); + + rfbRunEventLoop(server, -1, true); + + fd = xenfb_get_fileno(xenfb); + + xenstore_write_vncport(server->port, domid); + + for (;;) { + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + if (select(fd + 1, &readfds, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, + "Can''t select() on event channel (%s)\n", + strerror(errno)); + break; + } + + if (FD_ISSET(fd, &readfds)) + xenfb_on_incoming(xenfb); + } + + rfbScreenCleanup(server); + xenfb_delete(xenfb); + + return 0; +} diff -r 2e35cf028ff0 tools/xenfb/xenfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/xenfb.c Thu Nov 09 10:22:36 2006 +0100 @@ -0,0 +1,571 @@ +#include <stdarg.h> +#include <stdlib.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> +#include <xen/io/xenfb.h> +#include <xen/io/xenkbd.h> +#include <sys/select.h> +#include <stdbool.h> +#include <xen/linux/evtchn.h> +#include <xen/event_channel.h> +#include <sys/mman.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <xs.h> + +#include "xenfb.h" + +// FIXME defend against malicious frontend? + +struct xenfb_private +{ + struct xenfb pub; + int domid; + evtchn_port_t fbdev_port, kbd_port; + int evt_xch; + int xc; + unsigned char *fb; + size_t fb_len; + struct xenfb_page *fb_info; + struct xenkbd_page *kbd_info; +}; + +struct xenfb *xenfb_new(void) +{ + struct xenfb_private *xenfb = malloc(sizeof(*xenfb)); + + if (xenfb == NULL) + return NULL; + + memset(xenfb, 0, sizeof(*xenfb)); + + xenfb->domid = -1; + + xenfb->evt_xch = xc_evtchn_open(); + if (xenfb->evt_xch == -1) { + int serrno = errno; + free(xenfb); + errno = serrno; + return NULL; + } + + xenfb->xc = xc_interface_open(); + if (xenfb->xc == -1) { + int serrno = errno; + xc_evtchn_close(xenfb->evt_xch); + free(xenfb); + errno = serrno; + return NULL; + } + + return &xenfb->pub; +} + +int xenfb_get_fileno(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + + return xc_evtchn_fd(xenfb->evt_xch); +} + +static void xenfb_detach_dom(struct xenfb_private *xenfb) +{ + xenfb->domid = -1; + munmap(xenfb->fb, xenfb->fb_len); + munmap(xenfb->fb_info, XC_PAGE_SIZE); + munmap(xenfb->kbd_info, XC_PAGE_SIZE); + xc_evtchn_unbind(xenfb->evt_xch, xenfb->fbdev_port); + xc_evtchn_unbind(xenfb->evt_xch, xenfb->kbd_port); +} + +void xenfb_delete(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + if (xenfb->domid != -1) + xenfb_detach_dom(xenfb); + free(xenfb); +} + +static int xenfb_kbd_event(struct xenfb_private *xenfb, + union xenkbd_in_event *event) +{ + uint32_t prod; + struct xenkbd_page *info = xenfb->kbd_info; + + prod = info->in_prod; + if (prod - info->in_cons == XENKBD_IN_RING_LEN) { + errno = EAGAIN; + return -1; + } + + mb(); /* ensure ring space available */ + XENKBD_IN_RING_REF(info, prod) = *event; + wmb(); /* ensure ring contents visible */ + info->in_prod = prod + 1; + return xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd_port); +} + +static char *xenfb_path_in_dom(struct xs_handle *xsh, + char *buf, size_t size, + unsigned domid, const char *fmt, ...) +{ + va_list ap; + char *domp = xs_get_domain_path(xsh, domid); + int n; + + if (domp == NULL) + return NULL; + + n = snprintf(buf, size, "%s/", domp); + free(domp); + if (n >= size) + return NULL; + + va_start(ap, fmt); + n += vsnprintf(buf + n, size - n, fmt, ap); + va_end(ap); + if (n >= size) + return NULL; + + return buf; +} + +static int xenfb_xs_scanf1(struct xs_handle *xsh, + const char *dir, const char *node, + const char *fmt, void *dest) +{ + char buf[1024]; + char *p; + int ret; + + if (snprintf(buf, sizeof(buf), "%s/%s", dir, node) >= sizeof(buf)) { + errno = -ENOENT; + return -1; + } + p = xs_read(xsh, XBT_NULL, buf, NULL); + if (!p) { + errno = -ENOENT; + return -1; + } + ret = sscanf(p, fmt, dest); + free(p); + if (ret != 1) { + errno = -EDOM; + return -1; + } + return ret; +} + +static int xenfb_xs_printf(struct xs_handle *xsh, + const char *dir, const char *node, char *fmt, ...) +{ + va_list ap; + char key[1024]; + char val[1024]; + int n; + + if (snprintf(key, sizeof(key), "%s/%s", dir, node) >= sizeof(key)) { + errno = -ENOENT; + return -1; + } + + va_start(ap, fmt); + n = vsnprintf(val, sizeof(val), fmt, ap); + va_end(ap); + if (n >= sizeof(val)) { + errno = -ENOSPC; /* close enough */ + return -1; + } + + if (!xs_write(xsh, XBT_NULL, key, val, n)) + return -1; + return 0; +} + +static int xenfb_wait_for_state(struct xs_handle *xsh, const char *dir, + unsigned awaited) +{ + int ret; + unsigned state, dummy; + char **vec; + + for (;;) { + ret = xenfb_xs_scanf1(xsh, dir, "state", "%u", &state); + if (ret < 0 && errno != -ENOENT) + return ret; + if (state > XenbusStateClosed) + state = XenbusStateUnknown; + if ((1 << state) & awaited) + return state; + + vec = xs_read_watch(xsh, &dummy); + if (!vec) + return -1; + free(vec); + } +} + +static int xenfb_hotplug(struct xs_handle *xsh, const char *backdir) +{ + if (xenfb_xs_printf(xsh, backdir, "hotplug-status", "connected")) + return -1; + + if (!xs_watch(xsh, backdir, "")) + return -1; + + switch (xenfb_wait_for_state(xsh, backdir, +#if 1 /* TODO fudging state to permit restarting; to be removed */ + (1 << XenbusStateInitialising) + | (1 << XenbusStateInitWait) + | (1 << XenbusStateConnected) +#else + 1 << XenbusStateInitialising, +#endif + )) { +#if 1 + case XenbusStateInitWait: + case XenbusStateConnected: + printf("Fudging state to %d\n", XenbusStateInitialising); /* FIXME */ +#endif + case XenbusStateInitialising: + break; + default: + return -1; + } + + xs_unwatch(xsh, backdir, ""); + return 0; +} + +static int xenfb_wait_for_frontend_initialised(struct xs_handle *xsh, + const char *frontdir) +{ + switch (xenfb_wait_for_state(xsh, frontdir, +#if 1 /* TODO fudging state to permit restarting; to be removed */ + (1 << XenbusStateInitialised) + | (1 << XenbusStateConnected) +#else + 1 << XenbusStateInitialised, +#endif + )) { +#if 1 + case XenbusStateConnected: + printf("Fudging state to %d\n", XenbusStateInitialised); /* FIXME */ +#endif + case XenbusStateInitialised: + break; + default: + return -1; + } + + return 0; +} + +static int xenfb_wait_for_frontend_connected(struct xs_handle *xsh, + const char *frontdir) +{ + switch (xenfb_wait_for_state(xsh, frontdir, + 1 << XenbusStateConnected)) { + case XenbusStateConnected: + break; + default: + return -1; + } + + return 0; +} + +static int xenfb_get_connection(struct xs_handle *xsh, const char *frontdir, + unsigned long *mfn, int *evtchn) +{ + if (xenfb_xs_scanf1(xsh, frontdir, "page-ref", "%lu", mfn) < 0) + return -1; + if (xenfb_xs_scanf1(xsh, frontdir, "event-channel", "%u", evtchn) + < 0) + return -1; + return 0; +} + +bool xenfb_attach_dom(struct xenfb *xenfb_pub, int domid) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + char vfb_frontdir[64], vfb_backdir[64]; + char vkbd_frontdir[64], vkbd_backdir[64]; + struct xs_handle *xsh; + int ret, val; + int n_fbmfns; + int n_fbdirs = 0; + unsigned long *fbmfns = NULL; + int serrno; + int fbdev_evtchn, kbd_evtchn; + unsigned long fbdev_mfn, kbd_mfn; + + if (xenfb->domid != -1) { + xenfb_detach_dom(xenfb); + if (domid == -1) + return true; + } + + xsh = xs_daemon_open(); + if (!xsh) + goto error; + + if (!xenfb_path_in_dom(xsh, vfb_frontdir, sizeof(vfb_frontdir), + domid, "device/vfb/0")) { + errno = -ENOENT; + goto error; + } + if (!xenfb_path_in_dom(xsh, vfb_backdir, sizeof(vfb_backdir), + 0, "backend/vfb/%d/0", domid)) { + errno = -ENOENT; + goto error; + } + if (!xenfb_path_in_dom(xsh, vkbd_frontdir, sizeof(vkbd_frontdir), + domid, "device/vkbd/0")) { + errno = -ENOENT; + goto error; + } + if (!xenfb_path_in_dom(xsh, vkbd_backdir, sizeof(vkbd_backdir), + 0, "backend/vkbd/%d/0", domid)) { + errno = -ENOENT; + goto error; + } + + if (xenfb_hotplug(xsh, vfb_backdir) < 0) + goto error; + if (xenfb_hotplug(xsh, vkbd_backdir) < 0) + goto error; + + if (xenfb_xs_printf(xsh, vkbd_backdir, "feature-abs-pointer", "1")) + goto error; + if (xenfb_xs_printf(xsh, vfb_backdir, "state", "%d", + XenbusStateInitWait)) + goto error; + if (xenfb_xs_printf(xsh, vkbd_backdir, "state", "%d", + XenbusStateInitWait)) + goto error; + + if (!xs_watch(xsh, vfb_frontdir, "")) + goto error; + if (!xs_watch(xsh, vkbd_frontdir, "")) + goto error; + + if (xenfb_wait_for_frontend_initialised(xsh, vfb_frontdir) < 0) + goto error; + if (xenfb_wait_for_frontend_initialised(xsh, vkbd_frontdir) < 0) + goto error; + + if (xenfb_get_connection(xsh, vfb_frontdir, &fbdev_mfn, &fbdev_evtchn) + < 0) + goto error; + if (xenfb_get_connection(xsh, vkbd_frontdir, &kbd_mfn, &kbd_evtchn) + < 0) + goto error; + + if (xenfb_xs_scanf1(xsh, vfb_frontdir, "feature-update", + "%d", &val) < 0) + val = 0; + if (!val) { + errno = ENOTSUP; + goto error; + } + xenfb_xs_printf(xsh, vfb_backdir, "request-update", "1"); + + xenfb->fbdev_port = xc_evtchn_bind_interdomain(xenfb->evt_xch, domid, + fbdev_evtchn); + if (xenfb->fbdev_port == -1) + goto error; + + xenfb->kbd_port = xc_evtchn_bind_interdomain(xenfb->evt_xch, domid, + kbd_evtchn); + if (xenfb->kbd_port == -1) + goto error; + + ret = xc_domain_translate_gpfn_list(xenfb->xc, domid, 1, + &fbdev_mfn, &fbdev_mfn); + if (ret < 0 && errno != EINVAL) + goto error; + xenfb->fb_info = xc_map_foreign_range(xenfb->xc, domid, XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, + fbdev_mfn); + if (xenfb->fb_info == NULL) + goto error; + + xenfb->kbd_info = xc_map_foreign_range(xenfb->xc, domid, XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, + kbd_mfn); + if (xenfb->kbd_info == NULL) + goto error; + + /* TODO check for permitted ranges */ + xenfb->pub.depth = xenfb->fb_info->depth; + xenfb->pub.width = xenfb->fb_info->width; + xenfb->pub.height = xenfb->fb_info->height; + /* TODO check for consistency with the above */ + xenfb->fb_len = xenfb->fb_info->mem_length; + xenfb->pub.row_stride = xenfb->fb_info->line_length; + + n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + n_fbdirs = n_fbmfns * sizeof(unsigned long); + n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + + /* + * Bug alert: xc_map_foreign_batch() can fail partly and + * return a non-null value. This is a design flaw. When it + * happens, we happily continue here, and later crash on + * access. + */ + fbmfns = xc_map_foreign_batch(xenfb->xc, domid, PROT_READ, xenfb->fb_info->pd, n_fbdirs); + if (fbmfns == NULL) + goto error; + + xenfb->fb = xc_map_foreign_batch(xenfb->xc, domid, PROT_READ | PROT_WRITE, fbmfns, n_fbmfns); + if (xenfb->fb == NULL) + goto error; + + if (xenfb_xs_printf(xsh, vfb_backdir, "state", "%d", + XenbusStateConnected)) + goto error; + if (xenfb_xs_printf(xsh, vkbd_backdir, "state", "%d", + XenbusStateConnected)) + goto error; + + if (xenfb_wait_for_frontend_connected(xsh, vkbd_frontdir) < 0) + goto error; + if (xenfb_xs_scanf1(xsh, vkbd_frontdir, "request-abs-pointer", + "%d", &val) < 0) + val = 0; + xenfb->pub.abs_pointer_wanted = val; + + xs_daemon_close(xsh); + xsh = NULL; + + munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE); + + xenfb->domid = domid; + + xenfb->pub.pixels = xenfb->fb; + + return true; + + error: + serrno = errno; + if (xenfb->fb) + munmap(xenfb->fb, xenfb->fb_len); + if (fbmfns) + munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE); + if (xenfb->kbd_info) + munmap(xenfb->kbd_info, XC_PAGE_SIZE); + if (xenfb->fb_info) + munmap(xenfb->fb_info, XC_PAGE_SIZE); + if (xenfb->kbd_port); + xc_evtchn_unbind(xenfb->evt_xch, xenfb->kbd_port); + if (xenfb->fbdev_port) + xc_evtchn_unbind(xenfb->evt_xch, xenfb->fbdev_port); + if (xsh) + xs_daemon_close(xsh); + errno = serrno; + return false; +} + +static void xenfb_on_fb_event(struct xenfb_private *xenfb) +{ + uint32_t prod, cons; + struct xenfb_page *info = xenfb->fb_info; + + prod = info->out_prod; + if (prod == info->out_cons) + return; + rmb(); /* ensure we see ring contents up to prod */ + for (cons = info->out_cons; cons != prod; cons++) { + union xenfb_out_event *event = &XENFB_OUT_RING_REF(info, cons); + + switch (event->type) { + case XENFB_TYPE_UPDATE: + if (xenfb->pub.update) + xenfb->pub.update(&xenfb->pub, + event->update.x, event->update.y, + event->update.width, event->update.height); + break; + } + } + mb(); /* ensure we''re done with ring contents */ + info->out_cons = cons; + xc_evtchn_notify(xenfb->evt_xch, xenfb->fbdev_port); +} + +static void xenfb_on_kbd_event(struct xenfb_private *xenfb) +{ + struct xenkbd_page *info = xenfb->kbd_info; + + /* We don''t understand any keyboard events, so just ignore them. */ + if (info->out_prod == info->out_cons) + return; + info->out_cons = info->out_prod; + xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd_port); +} + +int xenfb_on_incoming(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + evtchn_port_t port; + + port = xc_evtchn_pending(xenfb->evt_xch); + if (port == -1) + return -1; + + if (port == xenfb->fbdev_port) { + xenfb_on_fb_event(xenfb); + } else if (port == xenfb->kbd_port) { + xenfb_on_kbd_event(xenfb); + } + + if (xc_evtchn_unmask(xenfb->evt_xch, port) == -1) + return -1; + + return 0; +} + +int xenfb_send_key(struct xenfb *xenfb_pub, bool down, int keycode) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_KEY; + event.key.pressed = down ? 1 : 0; + event.key.keycode = keycode; + + return xenfb_kbd_event(xenfb, &event); +} + +int xenfb_send_motion(struct xenfb *xenfb_pub, int rel_x, int rel_y) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_MOTION; + event.motion.rel_x = rel_x; + event.motion.rel_y = rel_y; + + return xenfb_kbd_event(xenfb, &event); +} + +int xenfb_send_position(struct xenfb *xenfb_pub, int abs_x, int abs_y) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_POS; + event.pos.abs_x = abs_x; + event.pos.abs_y = abs_y; + + return xenfb_kbd_event(xenfb, &event); +} diff -r 2e35cf028ff0 tools/xenfb/xenfb.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/xenfb.h Wed Nov 08 09:26:08 2006 +0100 @@ -0,0 +1,34 @@ +#ifndef _XENFB_H_ +#define _XENFB_H_ + +#include <stdbool.h> +#include <stdint.h> + +struct xenfb +{ + uint8_t *pixels; + + int row_stride; + int depth; + int width; + int height; + int abs_pointer_wanted; + + void *user_data; + + void (*update)(struct xenfb *xenfb, int x, int y, int width, int height); +}; + +struct xenfb *xenfb_new(void); +void xenfb_delete(struct xenfb *xenfb); + +bool xenfb_attach_dom(struct xenfb *xenfb, int domid); + +int xenfb_get_fileno(struct xenfb *xenfb); +int xenfb_on_incoming(struct xenfb *xenfb); + +int xenfb_send_key(struct xenfb *xenfb, bool down, int keycode); +int xenfb_send_motion(struct xenfb *xenfb, int rel_x, int rel_y); +int xenfb_send_position(struct xenfb *xenfb, int abs_x, int abs_y); + +#endif _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
> PV framebuffer frontend. Derived from http://hg.codemonkey.ws/vncfbThis is the backend, isn''t it?> diff -r 2e35cf028ff0 tools/python/xen/xend/XendDevices.py > --- a/tools/python/xen/xend/XendDevices.py Thu Nov 09 15:43:24 2006 +0000 > +++ b/tools/python/xen/xend/XendDevices.py Thu Nov 09 17:58:26 2006 +0100 > @@ -41,6 +41,8 @@ class XendDevices: > ''irq'': irqif.IRQController, > ''usb'': usbif.UsbifController, > ''tap'': BlktapController, > + ''vfb'': vfbif.VfbifController, > + ''vkbd'': vfif.VkbdifController,Typo? Should this be: + ''vkbd'': vfbif.VkbdifController, ?> diff -r 2e35cf028ff0 tools/xenfb/vncfb.c > --- /dev/null Thu Jan 01 00:00:00 1970 +0000 > +++ b/tools/xenfb/vncfb.c Thu Nov 09 10:18:58 2006 +0100> +int main(int argc, char **argv) > +{ > + rfbScreenInfoPtr server; > + char *fake_argv[7] = { "vncfb", "-rfbport", "5901", > + "-desktop", "xen-vncfb", > + "-listen", "0.0.0.0" };HVM VNC server defaults to listening on 127.0.0.1 now. You might want to make this do the same thing.> diff -r 2e35cf028ff0 tools/xenfb/xenfb.c > --- /dev/null Thu Jan 01 00:00:00 1970 +0000 > +++ b/tools/xenfb/xenfb.c Thu Nov 09 10:22:36 2006 +0100> +static int xenfb_hotplug(struct xs_handle *xsh, const char *backdir) > +{ > + if (xenfb_xs_printf(xsh, backdir, "hotplug-status", "connected")) > + return -1; > + > + if (!xs_watch(xsh, backdir, "")) > + return -1; > + > + switch (xenfb_wait_for_state(xsh, backdir, > +#if 1 /* TODO fudging state to permit restarting; to be removed */ > + (1 << XenbusStateInitialising) > + | (1 << XenbusStateInitWait) > + | (1 << XenbusStateConnected) > +#else > + 1 << XenbusStateInitialising, > +#endifI''m not sure what you''re trying to do here. It looks like you''re waiting for the backend to go to state Initialising (or InitWait, or Connected), but this is the backend, so if it isn''t there already you''re going to wait forever.> +bool xenfb_attach_dom(struct xenfb *xenfb_pub, int domid) > +{...> + if (xenfb_hotplug(xsh, vfb_backdir) < 0) > + goto error; > + if (xenfb_hotplug(xsh, vkbd_backdir) < 0) > + goto error; > + > + if (xenfb_xs_printf(xsh, vkbd_backdir, "feature-abs-pointer", "1")) > + goto error; > + if (xenfb_xs_printf(xsh, vfb_backdir, "state", "%d", > + XenbusStateInitWait)) > + goto error; > + if (xenfb_xs_printf(xsh, vkbd_backdir, "state", "%d", > + XenbusStateInitWait)) > + goto error; > +I''d probably reorder this a little to look more like this: (1) Set feature-abs-pointer (2) Set state to InitWait (3) Set hotplug status The only actual *required* constraint is (1) before (2), so that the frontend doesn''t initialise before we''ve set the feature and potentially miss it. (2) before (3) is kind of nice, in that it makes sure that the backend is ready before xend unpauses the domain, and so the frontend''ll be able to connect the first time it tries, but that''s a lot less important here than for e.g. block devices.> + error: > + serrno = errno; > + if (xenfb->fb) > + munmap(xenfb->fb, xenfb->fb_len); > + if (fbmfns) > + munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE); > + if (xenfb->kbd_info) > + munmap(xenfb->kbd_info, XC_PAGE_SIZE); > + if (xenfb->fb_info) > + munmap(xenfb->fb_info, XC_PAGE_SIZE); > + if (xenfb->kbd_port); > + xc_evtchn_unbind(xenfb->evt_xch, xenfb->kbd_port); > + if (xenfb->fbdev_port) > + xc_evtchn_unbind(xenfb->evt_xch, xenfb->fbdev_port); > + if (xsh) > + xs_daemon_close(xsh); > + errno = serrno;It''d be good to set an error in xenbus when this fails. Look at xenbus_dev_fatal and _dev_error in drivers/xen/xenbus/xenbus_client.c to see how to do this.> + return false; > +} > +Steven. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Steven Smith <sos22-xen@srcf.ucam.org> writes:>> PV framebuffer frontend. Derived from http://hg.codemonkey.ws/vncfb > This is the backend, isn''t it? > >> diff -r 2e35cf028ff0 tools/python/xen/xend/XendDevices.py >> --- a/tools/python/xen/xend/XendDevices.py Thu Nov 09 15:43:24 2006 +0000 >> +++ b/tools/python/xen/xend/XendDevices.py Thu Nov 09 17:58:26 2006 +0100 >> @@ -41,6 +41,8 @@ class XendDevices: >> ''irq'': irqif.IRQController, >> ''usb'': usbif.UsbifController, >> ''tap'': BlktapController, >> + ''vfb'': vfbif.VfbifController, >> + ''vkbd'': vfif.VkbdifController, > Typo? Should this be: > > + ''vkbd'': vfbif.VkbdifController, > > ?Yes. Thanks!>> diff -r 2e35cf028ff0 tools/xenfb/vncfb.c >> --- /dev/null Thu Jan 01 00:00:00 1970 +0000 >> +++ b/tools/xenfb/vncfb.c Thu Nov 09 10:18:58 2006 +0100 > >> +int main(int argc, char **argv) >> +{ >> + rfbScreenInfoPtr server; >> + char *fake_argv[7] = { "vncfb", "-rfbport", "5901", >> + "-desktop", "xen-vncfb", >> + "-listen", "0.0.0.0" }; > HVM VNC server defaults to listening on 127.0.0.1 now. You might want > to make this do the same thing.Okay.>> diff -r 2e35cf028ff0 tools/xenfb/xenfb.c >> --- /dev/null Thu Jan 01 00:00:00 1970 +0000 >> +++ b/tools/xenfb/xenfb.c Thu Nov 09 10:22:36 2006 +0100 > >> +static int xenfb_hotplug(struct xs_handle *xsh, const char *backdir) >> +{ >> + if (xenfb_xs_printf(xsh, backdir, "hotplug-status", "connected")) >> + return -1; >> + >> + if (!xs_watch(xsh, backdir, "")) >> + return -1; >> + >> + switch (xenfb_wait_for_state(xsh, backdir, >> +#if 1 /* TODO fudging state to permit restarting; to be removed */ >> + (1 << XenbusStateInitialising) >> + | (1 << XenbusStateInitWait) >> + | (1 << XenbusStateConnected) >> +#else >> + 1 << XenbusStateInitialising, >> +#endif > I''m not sure what you''re trying to do here. It looks like you''re > waiting for the backend to go to state Initialising (or InitWait, or > Connected), but this is the backend, so if it isn''t there already > you''re going to wait forever.Part of the crude hacks that let me kill and restart the backend without disturbing the front end. Here we wait for xend to create the initial xenstore nodes for us, by waiting for the transition to XenbusStateInitialising. If we reconnect, we''re already past that state. Should go away when we implement reconnect cleanly.>> +bool xenfb_attach_dom(struct xenfb *xenfb_pub, int domid) >> +{ > ... >> + if (xenfb_hotplug(xsh, vfb_backdir) < 0) >> + goto error; >> + if (xenfb_hotplug(xsh, vkbd_backdir) < 0) >> + goto error; >> + >> + if (xenfb_xs_printf(xsh, vkbd_backdir, "feature-abs-pointer", "1")) >> + goto error; >> + if (xenfb_xs_printf(xsh, vfb_backdir, "state", "%d", >> + XenbusStateInitWait)) >> + goto error; >> + if (xenfb_xs_printf(xsh, vkbd_backdir, "state", "%d", >> + XenbusStateInitWait)) >> + goto error; >> + > I''d probably reorder this a little to look more like this: > > (1) Set feature-abs-pointer > (2) Set state to InitWait > (3) Set hotplug status > > The only actual *required* constraint is (1) before (2), so that the > frontend doesn''t initialise before we''ve set the feature and > potentially miss it. > > (2) before (3) is kind of nice, in that it makes sure that the backend > is ready before xend unpauses the domain, and so the frontend''ll be > able to connect the first time it tries, but that''s a lot less > important here than for e.g. block devices.Based on our previous discussions, I designed the startup protocol this way: backend frontend ------------------------------------------------------------------ hotplug_status = connected [makes xend populate xenstore, set fe and be state = Initialising] wait for be state = Initialising [i.e. wait for xend] write xs: feature-abs-pointer write xs: feature-update be state = InitWait fe state = Initialised ------------------------------ sync ------------------------------ wait for fe state = Initialised wait for be state = InitWait ------------------------------ sync ------------------------------ read xs: feature-update read xs: feature-abs-pointer write xs: request-update write xs: request-abs-pointer be state = Connected fe state = Connected ------------------------------ sync ------------------------------ wait for fe state = Connected wait for be state = Connected ------------------------------ sync ------------------------------ read xs: request-abs-pointer read xs: request-update The symmetry made sense to me. If this is not the right way to do it, please explain it to me once again. A diagram like the one I provided above might be more precise and thus useful than prose, even if it''s just as crude as mine.>> + error: >> + serrno = errno; >> + if (xenfb->fb) >> + munmap(xenfb->fb, xenfb->fb_len); >> + if (fbmfns) >> + munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE); >> + if (xenfb->kbd_info) >> + munmap(xenfb->kbd_info, XC_PAGE_SIZE); >> + if (xenfb->fb_info) >> + munmap(xenfb->fb_info, XC_PAGE_SIZE); >> + if (xenfb->kbd_port); >> + xc_evtchn_unbind(xenfb->evt_xch, xenfb->kbd_port); >> + if (xenfb->fbdev_port) >> + xc_evtchn_unbind(xenfb->evt_xch, xenfb->fbdev_port); >> + if (xsh) >> + xs_daemon_close(xsh); >> + errno = serrno; > It''d be good to set an error in xenbus when this fails. Look at > xenbus_dev_fatal and _dev_error in drivers/xen/xenbus/xenbus_client.c > to see how to do this.Thanks, I''ll look into that.>> + return false; >> +} >> + > > Steven._______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
> >> diff -r 2e35cf028ff0 tools/xenfb/vncfb.c > >> --- /dev/null Thu Jan 01 00:00:00 1970 +0000 > >> +++ b/tools/xenfb/vncfb.c Thu Nov 09 10:18:58 2006 +0100 > > > >> +int main(int argc, char **argv) > >> +{ > >> + rfbScreenInfoPtr server; > >> + char *fake_argv[7] = { "vncfb", "-rfbport", "5901", > >> + "-desktop", "xen-vncfb", > >> + "-listen", "0.0.0.0" }; > > HVM VNC server defaults to listening on 127.0.0.1 now. You might want > > to make this do the same thing. > Okay.Thanks.> > ... > >> + if (xenfb_hotplug(xsh, vfb_backdir) < 0) > >> + goto error; > >> + if (xenfb_hotplug(xsh, vkbd_backdir) < 0) > >> + goto error; > >> + > >> + if (xenfb_xs_printf(xsh, vkbd_backdir, "feature-abs-pointer", "1")) > >> + goto error; > >> + if (xenfb_xs_printf(xsh, vfb_backdir, "state", "%d", > >> + XenbusStateInitWait)) > >> + goto error; > >> + if (xenfb_xs_printf(xsh, vkbd_backdir, "state", "%d", > >> + XenbusStateInitWait)) > >> + goto error; > >> + > > I''d probably reorder this a little to look more like this: > > > > (1) Set feature-abs-pointer > > (2) Set state to InitWait > > (3) Set hotplug status > > > > The only actual *required* constraint is (1) before (2), so that the > > frontend doesn''t initialise before we''ve set the feature and > > potentially miss it. > > > > (2) before (3) is kind of nice, in that it makes sure that the backend > > is ready before xend unpauses the domain, and so the frontend''ll be > > able to connect the first time it tries, but that''s a lot less > > important here than for e.g. block devices. > Based on our previous discussions, I designed the startup protocol > this way: > > backend frontend > ------------------------------------------------------------------ > hotplug_status = connected > [makes xend populate xenstore, set fe and be state = Initialising] > wait for be state = Initialising > [i.e. wait for xend] > write xs: feature-abs-pointer write xs: feature-update > be state = InitWait fe state = Initialised > ------------------------------ sync ------------------------------ > wait for fe state = Initialised wait for be state = InitWait > ------------------------------ sync ------------------------------ > read xs: feature-update read xs: feature-abs-pointer > write xs: request-update write xs: request-abs-pointer > be state = Connected fe state = Connected > ------------------------------ sync ------------------------------ > wait for fe state = Connected wait for be state = Connected > ------------------------------ sync ------------------------------ > read xs: request-abs-pointer read xs: request-update > > The symmetry made sense to me.Ah, sorry, I wasn''t clear enough. You''ve got everything right after the first sync line, but the bit before that isn''t quite right. Xend creates xenstore area when the domain is created, and then waits until hotplug-status is set before starting the domain running. The idea is that the backend driver should be watching its area of xenbus (so /local/domain/0/backend/vif, say), so that it notices when the area is created and creates the appropriate backend devices. Creating the backend devices triggers the hotplug scripts via some udev magic which I''ve never quite understood, and they then e.g. connect vifs up to the bridge. Once they''ve finished, the backends are all ready, and so it''s safe to start the guest. If you start the guest before the backends are ready, you potentially have issues like your root fileystem not becoming available until after the guest has booted, which tends to make Linux unhappy. xend backend driver hotplug scripts Creates a new domain paused Creates backend area Notices new backend area, creates backend device Does some basic setup on backend device Go to state InitWait Kicks udev Does a bit more setup on backend device Sets hotplug-status Notices hotplug status, unpauses domain Now, the obvious mapping of this protocol onto the PVFB would have xend create the xenstore area when the guest is created and spawn the backend itself. The backend could then set hotplug-status to indicate that it''s ready, which would unpause the guest and things would then proceed in the usual way. This would work, and I''d be quite happy with it, but it does have the slight disadvantage that you can''t start a domain with a framebuffer configured and no backend attached. If this worries you, you could have xend not wait for the hotplug status but instead start the domain immediately (there''s a waitForBackend method on class DevController which you''d have to override. I don''t remember the details, but I don''t think it''s very hard). If you go this path, you need to consider the possibility that the backend area isn''t set up in time when the _probe function is run in the frontend, but I think that works already.> >> + error: > >> + serrno = errno; > >> + if (xenfb->fb) > >> + munmap(xenfb->fb, xenfb->fb_len); > >> + if (fbmfns) > >> + munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE); > >> + if (xenfb->kbd_info) > >> + munmap(xenfb->kbd_info, XC_PAGE_SIZE); > >> + if (xenfb->fb_info) > >> + munmap(xenfb->fb_info, XC_PAGE_SIZE); > >> + if (xenfb->kbd_port); > >> + xc_evtchn_unbind(xenfb->evt_xch, xenfb->kbd_port); > >> + if (xenfb->fbdev_port) > >> + xc_evtchn_unbind(xenfb->evt_xch, xenfb->fbdev_port); > >> + if (xsh) > >> + xs_daemon_close(xsh); > >> + errno = serrno; > > It''d be good to set an error in xenbus when this fails. Look at > > xenbus_dev_fatal and _dev_error in drivers/xen/xenbus/xenbus_client.c > > to see how to do this. > Thanks, I''ll look into that.Thanks. Steven. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Steven Smith <sos22@cam.ac.uk> writes: [...]>> >> + if (xenfb_hotplug(xsh, vfb_backdir) < 0) >> >> + goto error; >> >> + if (xenfb_hotplug(xsh, vkbd_backdir) < 0) >> >> + goto error; >> >> + >> >> + if (xenfb_xs_printf(xsh, vkbd_backdir, "feature-abs-pointer", "1")) >> >> + goto error; >> >> + if (xenfb_xs_printf(xsh, vfb_backdir, "state", "%d", >> >> + XenbusStateInitWait)) >> >> + goto error; >> >> + if (xenfb_xs_printf(xsh, vkbd_backdir, "state", "%d", >> >> + XenbusStateInitWait)) >> >> + goto error; >> >> + >> > I''d probably reorder this a little to look more like this: >> > >> > (1) Set feature-abs-pointer >> > (2) Set state to InitWait >> > (3) Set hotplug status >> > >> > The only actual *required* constraint is (1) before (2), so that the >> > frontend doesn''t initialise before we''ve set the feature and >> > potentially miss it. >> > >> > (2) before (3) is kind of nice, in that it makes sure that the backend >> > is ready before xend unpauses the domain, and so the frontend''ll be >> > able to connect the first time it tries, but that''s a lot less >> > important here than for e.g. block devices. >> Based on our previous discussions, I designed the startup protocol >> this way: >> >> backend frontend >> ------------------------------------------------------------------ >> hotplug_status = connected >> [makes xend populate xenstore, set fe and be state = Initialising] >> wait for be state = Initialising >> [i.e. wait for xend] >> write xs: feature-abs-pointer write xs: feature-update >> be state = InitWait fe state = Initialised >> ------------------------------ sync ------------------------------ >> wait for fe state = Initialised wait for be state = InitWait >> ------------------------------ sync ------------------------------ >> read xs: feature-update read xs: feature-abs-pointer >> write xs: request-update write xs: request-abs-pointer >> be state = Connected fe state = Connected >> ------------------------------ sync ------------------------------ >> wait for fe state = Connected wait for be state = Connected >> ------------------------------ sync ------------------------------ >> read xs: request-abs-pointer read xs: request-update >> >> The symmetry made sense to me. > Ah, sorry, I wasn''t clear enough. You''ve got everything right after > the first sync line, but the bit before that isn''t quite right. Xend > creates xenstore area when the domain is created, and then waits until > hotplug-status is set before starting the domain running. The idea is > that the backend driver should be watching its area of xenbus (so > /local/domain/0/backend/vif, say), so that it notices when the area is > created and creates the appropriate backend devices. Creating the > backend devices triggers the hotplug scripts via some udev magic which > I''ve never quite understood, and they then e.g. connect vifs up to the > bridge. Once they''ve finished, the backends are all ready, and so > it''s safe to start the guest. If you start the guest before the > backends are ready, you potentially have issues like your root > fileystem not becoming available until after the guest has booted, > which tends to make Linux unhappy. > > xend backend driver hotplug scripts > Creates a new domain > paused > Creates backend area > Notices new backend > area, creates > backend device > Does some basic setup > on backend device > Go to state InitWait > Kicks udev > Does a bit more setup > on backend > device > Sets hotplug-status > Notices hotplug status, > unpauses domain > > Now, the obvious mapping of this protocol onto the PVFB would have > xend create the xenstore area when the guest is created and spawn the > backend itself. The backend could then set hotplug-status to indicate > that it''s ready, which would unpause the guest and things would then > proceed in the usual way.I coded this, and it works.> This would work, and I''d be quite happy with it, but it does have the > slight disadvantage that you can''t start a domain with a framebuffer > configured and no backend attached. If this worries you, you could > have xend not wait for the hotplug status but instead start the domain > immediately (there''s a waitForBackend method on class DevController > which you''d have to override. I don''t remember the details, but I > don''t think it''s very hard). If you go this path, you need to > consider the possibility that the backend area isn''t set up in time > when the _probe function is run in the frontend, but I think that > works already.I like this, but I''m saving it for another day. Thanks again for your help! [...] _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Markus Armbruster <armbru@redhat.com> writes:> Steven Smith <sos22@cam.ac.uk> writes: > > [...] >>> >> + if (xenfb_hotplug(xsh, vfb_backdir) < 0) >>> >> + goto error; >>> >> + if (xenfb_hotplug(xsh, vkbd_backdir) < 0) >>> >> + goto error; >>> >> + >>> >> + if (xenfb_xs_printf(xsh, vkbd_backdir, "feature-abs-pointer", "1")) >>> >> + goto error; >>> >> + if (xenfb_xs_printf(xsh, vfb_backdir, "state", "%d", >>> >> + XenbusStateInitWait)) >>> >> + goto error; >>> >> + if (xenfb_xs_printf(xsh, vkbd_backdir, "state", "%d", >>> >> + XenbusStateInitWait)) >>> >> + goto error; >>> >> + >>> > I''d probably reorder this a little to look more like this: >>> > >>> > (1) Set feature-abs-pointer >>> > (2) Set state to InitWait >>> > (3) Set hotplug status >>> > >>> > The only actual *required* constraint is (1) before (2), so that the >>> > frontend doesn''t initialise before we''ve set the feature and >>> > potentially miss it. >>> > >>> > (2) before (3) is kind of nice, in that it makes sure that the backend >>> > is ready before xend unpauses the domain, and so the frontend''ll be >>> > able to connect the first time it tries, but that''s a lot less >>> > important here than for e.g. block devices. >>> Based on our previous discussions, I designed the startup protocol >>> this way: >>> >>> backend frontend >>> ------------------------------------------------------------------ >>> hotplug_status = connected >>> [makes xend populate xenstore, set fe and be state = Initialising] >>> wait for be state = Initialising >>> [i.e. wait for xend] >>> write xs: feature-abs-pointer write xs: feature-update >>> be state = InitWait fe state = Initialised >>> ------------------------------ sync ------------------------------ >>> wait for fe state = Initialised wait for be state = InitWait >>> ------------------------------ sync ------------------------------ >>> read xs: feature-update read xs: feature-abs-pointer >>> write xs: request-update write xs: request-abs-pointer >>> be state = Connected fe state = Connected >>> ------------------------------ sync ------------------------------ >>> wait for fe state = Connected wait for be state = Connected >>> ------------------------------ sync ------------------------------ >>> read xs: request-abs-pointer read xs: request-update >>> >>> The symmetry made sense to me. >> Ah, sorry, I wasn''t clear enough. You''ve got everything right after >> the first sync line, but the bit before that isn''t quite right. Xend >> creates xenstore area when the domain is created, and then waits until >> hotplug-status is set before starting the domain running. The idea is >> that the backend driver should be watching its area of xenbus (so >> /local/domain/0/backend/vif, say), so that it notices when the area is >> created and creates the appropriate backend devices. Creating the >> backend devices triggers the hotplug scripts via some udev magic which >> I''ve never quite understood, and they then e.g. connect vifs up to the >> bridge. Once they''ve finished, the backends are all ready, and so >> it''s safe to start the guest. If you start the guest before the >> backends are ready, you potentially have issues like your root >> fileystem not becoming available until after the guest has booted, >> which tends to make Linux unhappy. >> >> xend backend driver hotplug scripts >> Creates a new domain >> paused >> Creates backend area >> Notices new backend >> area, creates >> backend device >> Does some basic setup >> on backend device >> Go to state InitWait >> Kicks udev >> Does a bit more setup >> on backend >> device >> Sets hotplug-status >> Notices hotplug status, >> unpauses domain >> >> Now, the obvious mapping of this protocol onto the PVFB would have >> xend create the xenstore area when the guest is created and spawn the >> backend itself. The backend could then set hotplug-status to indicate >> that it''s ready, which would unpause the guest and things would then >> proceed in the usual way. > > I coded this, and it works.Not quite. I fear there''s a race. My xenkbd otherend_changed callback runs twice with backend_state =XenbusStateConnected instead of first with XenbusStateInitWait and then with XenbusStateConnected. Here''s what I think happens. First how backend, xend and frontend synchronize: backend xend frontend ------------------------------------------------------------------ create domain paused create xenstore, fe and be state Initialising ________ sync _______/ / wait for be state Initialising write xs: feature-* be state = InitWait hotplug_status = connected \________ sync _______ \ wait for hotplug_status connected unpause domain \________ sync _______ \ write xs: feature-* fe state = Initialised \___________________ sync __________________/ / \ wait for fe state wait for be state Initialised InitWait read xs: feature-* read xs: feature-* write xs: request-* write xs: request-* be state = Connected fe state = Connected \___________________ sync __________________/ / \ wait for fe state wait for be state Connected Connected read xs: request-* read xs: request-* Except the frontend doesn''t actually wait for be state, it runs a callback (otherend_changed) when the state changes. Now, when the this callback in the frontend is delayed sufficiently, the order of events could be: frontend backend ------------------------------------------------------------------ [...] be state = InitWait hotplug_status connected [...] fe state = Initialised wait for fe state Initialised completes read xs: feature-* write xs: request-* be state = Connected otherend_changed runs and sees be state Connected (actual trigger was InitWait) otherend_changed runs and sees be state Connected How do you want me to deal with this? Note the funny code in drivers/xen/blkback/xenbus.c''s frontend_changed: case XenbusStateInitialised: case XenbusStateConnected: /* Ensure we connect even when two watches fire in close successsion and we miss the intermediate value of frontend_state. */ If this is the right thing to do there, why isn''t it done elsewhere? _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
PV framebuffer backend. Derived from http://hg.codemonkey.ws/vncfb Extensive changes based on feedback from xen-devel. Signed-off-by: Markus Armbruster <armbru@redhat.com> --- tools/Makefile | 1 tools/python/xen/xend/XendDevices.py | 4 tools/python/xen/xend/server/vfbif.py | 29 + tools/python/xen/xm/create.py | 17 tools/xenfb/Makefile | 33 + tools/xenfb/sdlfb.c | 337 ++++++++++++++++++ tools/xenfb/vncfb.c | 396 +++++++++++++++++++++ tools/xenfb/xenfb.c | 619 ++++++++++++++++++++++++++++++++++ tools/xenfb/xenfb.h | 34 + 9 files changed, 1469 insertions(+), 1 deletion(-) diff -r c677f4e75608 tools/Makefile --- a/tools/Makefile Thu Nov 16 11:11:17 2006 +0000 +++ b/tools/Makefile Fri Nov 10 08:01:00 2006 +0100 @@ -19,6 +19,7 @@ SUBDIRS-y += libaio SUBDIRS-y += libaio SUBDIRS-y += blktap SUBDIRS-y += libfsimage +SUBDIRS-y += xenfb # These don''t cross-compile ifeq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH)) diff -r c677f4e75608 tools/python/xen/xend/XendDevices.py --- a/tools/python/xen/xend/XendDevices.py Thu Nov 16 11:11:17 2006 +0000 +++ b/tools/python/xen/xend/XendDevices.py Mon Nov 13 15:00:34 2006 +0100 @@ -19,7 +19,7 @@ # A collection of DevControllers # -from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif +from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif, vfbif from xen.xend.server.BlktapController import BlktapController class XendDevices: @@ -41,6 +41,8 @@ class XendDevices: ''irq'': irqif.IRQController, ''usb'': usbif.UsbifController, ''tap'': BlktapController, + ''vfb'': vfbif.VfbifController, + ''vkbd'': vfbif.VkbdifController, } #@classmethod diff -r c677f4e75608 tools/python/xen/xm/create.py --- a/tools/python/xen/xm/create.py Thu Nov 16 11:11:17 2006 +0000 +++ b/tools/python/xen/xm/create.py Tue Nov 14 14:51:37 2006 +0100 @@ -280,6 +280,14 @@ gopts.var(''usbport'', val=''PATH'', use="""Add a physical USB port to a domain, as specified by the path to that port. This option may be repeated to add more than one port.""") +gopts.var(''vfb'', val="no|yes''", + fn=set_bool, default=0, + use="Make the domain a framebuffer backend.") + +gopts.var(''vkbd'', val="no|yes''", + fn=set_bool, default=0, + use="Make the domain a keyboard backend.") + gopts.var(''vif'', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,backend=DOM,vifname=NAME", fn=append_value, default=[], use="""Add a network interface with the given MAC address and bridge. @@ -556,6 +564,13 @@ def configure_usb(config_devs, vals): config_usb = [''usbport'', [''path'', path]] config_devs.append([''device'', config_usb]) +def configure_vfbs(config_devs, vals): + if vals.vfb: + config_devs.append([''device'', [''vfb'', []]]) + +def configure_vkbds(config_devs, vals): + if vals.vkbd: + config_devs.append([''device'', [''vkbd'', []]]) def configure_security(config, vals): """Create the config for ACM security labels. @@ -734,6 +749,8 @@ def make_config(vals): configure_vifs(config_devs, vals) configure_usb(config_devs, vals) configure_vtpm(config_devs, vals) + configure_vfbs(config_devs, vals) + configure_vkbds(config_devs, vals) configure_security(config, vals) config += config_devs diff -r c677f4e75608 tools/python/xen/xend/server/vfbif.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/python/xen/xend/server/vfbif.py Tue Nov 07 14:46:46 2006 +0100 @@ -0,0 +1,29 @@ +from xen.xend.server.DevController import DevController + +class VfbifController(DevController): + """Virtual frame buffer controller. Handles all vfb devices for a domain. + """ + + def __init__(self, vm): + DevController.__init__(self, vm) + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + devid = 0 + back = {} + front = {} + return (devid, back, front) + +class VkbdifController(DevController): + """Virtual keyboard controller. Handles all vkbd devices for a domain. + """ + + def __init__(self, vm): + DevController.__init__(self, vm) + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + devid = 0 + back = {} + front = {} + return (devid, back, front) diff -r c677f4e75608 tools/xenfb/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/Makefile Thu Nov 09 10:19:26 2006 +0100 @@ -0,0 +1,33 @@ +XEN_ROOT=../.. +include $(XEN_ROOT)/tools/Rules.mk + +CFLAGS += -I$(XEN_LIBXC) -I$(XEN_XENSTORE) -I$(XEN_ROOT)/linux-2.6-xen-sparse/include +LDFLAGS += -L$(XEN_LIBXC) -L$(XEN_XENSTORE) + +INSTALL = install +INSTALL_PROG = $(INSTALL) -m0755 +INSTALL_DIR = $(INSTALL) -d -m0755 + +.PHONY: all +all: build + +.PHONY: build +build: mk-symlinks + $(MAKE) vncfb sdlfb + +install: all + $(INSTALL_DIR) -p $(DESTDIR)/usr/$(LIBDIR)/xen/bin + $(INSTALL_PROG) vncfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-vncfb + $(INSTALL_PROG) sdlfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-sdlfb + +sdlfb: sdlfb.o xenfb.o + +sdlfb.o: CFLAGS += $(shell sdl-config --cflags) +sdlfb: LDLIBS += $(shell sdl-config --libs) -lxenctrl -lxenstore + +clean: + $(RM) *.o *~ vncfb sdlfb + +vncfb: vncfb.o xenfb.o +vncfb.o: CFLAGS += $(shell libvncserver-config --cflags) +vncfb: LDLIBS += $(shell libvncserver-config --libs) -lxenctrl -lxenstore diff -r c677f4e75608 tools/xenfb/sdlfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/sdlfb.c Wed Nov 08 11:35:54 2006 +0100 @@ -0,0 +1,337 @@ +#include <SDL.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/select.h> +#include <stdlib.h> +#include <linux/input.h> +#include <getopt.h> +#include <string.h> +#include "xenfb.h" + +struct SDLFBData +{ + SDL_Surface *dst; + SDL_Surface *src; +}; + +/* + * Map from scancode to Linux input layer keycode. Scancodes are + * hardware-specific. This map assumes a standard AT or PS/2 + * keyboard. + * + * Why use scancodes? We can''t use key symbols, because they don''t + * identify keys --- they''re what keys are mapped to. The standard + * German keymap, for instance, maps both KEY_COMMA and KEY_102ND to + * SDLK_LESS. + */ +static int keymap[256] = { + [9] = KEY_ESC, + [10] = KEY_1, + [11] = KEY_2, + [12] = KEY_3, + [13] = KEY_4, + [14] = KEY_5, + [15] = KEY_6, + [16] = KEY_7, + [17] = KEY_8, + [18] = KEY_9, + [19] = KEY_0, + [20] = KEY_MINUS, + [21] = KEY_EQUAL, + [22] = KEY_BACKSPACE, + [23] = KEY_TAB, + [24] = KEY_Q, + [25] = KEY_W, + [26] = KEY_E, + [27] = KEY_R, + [28] = KEY_T, + [29] = KEY_Y, + [30] = KEY_U, + [31] = KEY_I, + [32] = KEY_O, + [33] = KEY_P, + [34] = KEY_LEFTBRACE, + [35] = KEY_RIGHTBRACE, + [36] = KEY_ENTER, + [37] = KEY_LEFTCTRL, + [38] = KEY_A, + [39] = KEY_S, + [40] = KEY_D, + [41] = KEY_F, + [42] = KEY_G, + [43] = KEY_H, + [44] = KEY_J, + [45] = KEY_K, + [46] = KEY_L, + [47] = KEY_SEMICOLON, + [48] = KEY_APOSTROPHE, + [49] = KEY_GRAVE, + [50] = KEY_LEFTSHIFT, + [51] = KEY_BACKSLASH, + [52] = KEY_Z, + [53] = KEY_X, + [54] = KEY_C, + [55] = KEY_V, + [56] = KEY_B, + [57] = KEY_N, + [58] = KEY_M, + [59] = KEY_COMMA, + [60] = KEY_DOT, + [61] = KEY_SLASH, + [62] = KEY_RIGHTSHIFT, + [63] = KEY_KPASTERISK, + [64] = KEY_LEFTALT, + [65] = KEY_SPACE, + [66] = KEY_CAPSLOCK, + [67] = KEY_F1, + [68] = KEY_F2, + [69] = KEY_F3, + [70] = KEY_F4, + [71] = KEY_F5, + [72] = KEY_F6, + [73] = KEY_F7, + [74] = KEY_F8, + [75] = KEY_F9, + [76] = KEY_F10, + [77] = KEY_NUMLOCK, + [78] = KEY_SCROLLLOCK, + [79] = KEY_KP7, + [80] = KEY_KP8, + [81] = KEY_KP9, + [82] = KEY_KPMINUS, + [83] = KEY_KP4, + [84] = KEY_KP5, + [85] = KEY_KP6, + [86] = KEY_KPPLUS, + [87] = KEY_KP1, + [88] = KEY_KP2, + [89] = KEY_KP3, + [90] = KEY_KP0, + [91] = KEY_KPDOT, + [94] = KEY_102ND, /* FIXME is this correct? */ + [95] = KEY_F11, + [96] = KEY_F12, + [108] = KEY_KPENTER, + [109] = KEY_RIGHTCTRL, + [112] = KEY_KPSLASH, + [111] = KEY_SYSRQ, + [113] = KEY_RIGHTALT, + [97] = KEY_HOME, + [98] = KEY_UP, + [99] = KEY_PAGEUP, + [100] = KEY_LEFT, + [102] = KEY_RIGHT, + [103] = KEY_END, + [104] = KEY_DOWN, + [105] = KEY_PAGEDOWN, + [106] = KEY_INSERT, + [107] = KEY_DELETE, + [110] = KEY_PAUSE, + [115] = KEY_LEFTMETA, + [116] = KEY_RIGHTMETA, + [117] = KEY_MENU, +}; + +static int btnmap[] = { + [SDL_BUTTON_LEFT] = BTN_LEFT, + [SDL_BUTTON_MIDDLE] = BTN_MIDDLE, + [SDL_BUTTON_RIGHT] = BTN_RIGHT, + /* FIXME not 100% sure about these: */ + [SDL_BUTTON_WHEELUP] = BTN_FORWARD, + [SDL_BUTTON_WHEELDOWN] BTN_BACK +}; + +static void sdl_update(struct xenfb *xenfb, int x, int y, int width, int height) +{ + struct SDLFBData *data = xenfb->user_data; + SDL_Rect r = { x, y, width, height }; + SDL_BlitSurface(data->src, &r, data->dst, &r); + SDL_UpdateRect(data->dst, x, y, width, height); +} + +static int sdl_on_event(struct xenfb *xenfb, SDL_Event *event) +{ + int x, y, ret; + + switch (event->type) { + case SDL_KEYDOWN: + case SDL_KEYUP: + if (keymap[event->key.keysym.scancode] == 0) + break; + ret = xenfb_send_key(xenfb, + event->type == SDL_KEYDOWN, + keymap[event->key.keysym.scancode]); + if (ret < 0) + fprintf(stderr, "Key %d %s lost (%s)\n", + keymap[event->key.keysym.scancode], + event->type == SDL_KEYDOWN ? "down" : "up", + strerror(errno)); + break; + case SDL_MOUSEMOTION: + if (xenfb->abs_pointer_wanted) { + SDL_GetMouseState(&x, &y); + ret = xenfb_send_position(xenfb, x, y); + } else { + SDL_GetRelativeMouseState(&x, &y); + ret = xenfb_send_motion(xenfb, x, y); + } + if (ret < 0) + fprintf(stderr, "Pointer to %d,%d lost (%s)\n", + x, y, strerror(errno)); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + if (event->button.button >= sizeof(btnmap) / sizeof(*btnmap)) + break; + if (btnmap[event->button.button] == 0) + break; + ret = xenfb_send_key(xenfb, + event->type == SDL_MOUSEBUTTONDOWN, + btnmap[event->button.button]); + if (ret < 0) + fprintf(stderr, "Button %d %s lost (%s)\n", + btnmap[event->button.button] - BTN_MOUSE, + event->type == SDL_MOUSEBUTTONDOWN ? "down" : "up", + strerror(errno)); + break; + case SDL_QUIT: + return 0; + } + + return 1; +} + +static struct option options[] = { + { "domid", 1, NULL, ''d'' }, + { "title", 1, NULL, ''t'' }, +}; + +int main(int argc, char **argv) +{ + struct xenfb *xenfb; + int fd; + int domid = -1; + char * title = NULL; + fd_set readfds; + struct SDLFBData data; + SDL_Rect r; + struct timeval tv; + SDL_Event event; + int do_quit = 0; + int opt; + char *endp; + + while ((opt = getopt_long(argc, argv, "d:t:", options, + NULL)) != -1) { + switch (opt) { + case ''d'': + domid = strtol(optarg, &endp, 10); + if (endp == optarg || *endp) { + fprintf(stderr, "Invalid domain id specified\n"); + exit(1); + } + break; + case ''t'': + title = strdup(optarg); + break; + } + } + if (optind != argc) { + fprintf(stderr, "Invalid options!\n"); + exit(1); + } + if (domid <= 0) { + fprintf(stderr, "Domain ID must be specified!\n"); + exit(1); + } + + xenfb = xenfb_new(); + if (xenfb == NULL) { + fprintf(stderr, "Could not create framebuffer (%s)\n", + strerror(errno)); + exit(1); + } + + if (!xenfb_attach_dom(xenfb, domid)) { + fprintf(stderr, "Could not connect to domain (%s)\n", + strerror(errno)); + exit(1); + } + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "Could not initialize SDL\n"); + exit(1); + } + + fd = xenfb_get_fileno(xenfb); + + data.dst = SDL_SetVideoMode(xenfb->width, xenfb->height, xenfb->depth, + SDL_SWSURFACE); + if (!data.dst) { + fprintf(stderr, "SDL_SetVideoMode failed\n"); + exit(1); + } + + data.src = SDL_CreateRGBSurfaceFrom(xenfb->pixels, + xenfb->width, xenfb->height, + xenfb->depth, xenfb->row_stride, + 0xFF0000, 0xFF00, 0xFF, 0); + + if (!data.src) { + fprintf(stderr, "SDL_CreateRGBSurfaceFrom failed\n"); + exit(1); + } + + if (title == NULL) + title = strdup("xen-sdlfb"); + SDL_WM_SetCaption(title, title); + + r.x = r.y = 0; + r.w = xenfb->width; + r.h = xenfb->height; + SDL_BlitSurface(data.src, &r, data.dst, &r); + SDL_UpdateRect(data.dst, 0, 0, xenfb->width, xenfb->height); + + xenfb->update = sdl_update; + xenfb->user_data = &data; + + SDL_ShowCursor(0); + + /* + * We need to wait for fd becoming ready or SDL events to + * arrive. We time out the select after 10ms to poll for SDL + * events. Clunky, but works. Could avoid the clunkiness + * with a separate thread. + */ + for (;;) { + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + tv = (struct timeval){0, 10000}; + + if (select(fd + 1, &readfds, NULL, NULL, &tv) < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, + "Can''t select() on event channel (%s)\n", + strerror(errno)); + break; + } + + while (SDL_PollEvent(&event)) { + if (!sdl_on_event(xenfb, &event)) + do_quit = 1; + } + + if (do_quit) + break; + + if (FD_ISSET(fd, &readfds)) + xenfb_on_incoming(xenfb); + } + + xenfb_delete(xenfb); + + SDL_Quit(); + + return 0; +} diff -r c677f4e75608 tools/xenfb/vncfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/vncfb.c Mon Nov 13 15:00:47 2006 +0100 @@ -0,0 +1,396 @@ +#define _GNU_SOURCE +#include <errno.h> +#include <getopt.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <malloc.h> +#include <rfb/rfb.h> +#include <rfb/keysym.h> +#include <linux/input.h> +#include <xs.h> +#include "xenfb.h" + +static int xk2linux[0x10000] = { + [XK_a] = KEY_A, + [XK_b] = KEY_B, + [XK_c] = KEY_C, + [XK_d] = KEY_D, + [XK_e] = KEY_E, + [XK_f] = KEY_F, + [XK_g] = KEY_G, + [XK_h] = KEY_H, + [XK_i] = KEY_I, + [XK_j] = KEY_J, + [XK_k] = KEY_K, + [XK_l] = KEY_L, + [XK_m] = KEY_M, + [XK_n] = KEY_N, + [XK_o] = KEY_O, + [XK_p] = KEY_P, + [XK_q] = KEY_Q, + [XK_r] = KEY_R, + [XK_s] = KEY_S, + [XK_t] = KEY_T, + [XK_u] = KEY_U, + [XK_v] = KEY_V, + [XK_w] = KEY_W, + [XK_x] = KEY_X, + [XK_y] = KEY_Y, + [XK_z] = KEY_Z, + [XK_A] = KEY_A, + [XK_B] = KEY_B, + [XK_C] = KEY_C, + [XK_D] = KEY_D, + [XK_E] = KEY_E, + [XK_F] = KEY_F, + [XK_G] = KEY_G, + [XK_H] = KEY_H, + [XK_I] = KEY_I, + [XK_J] = KEY_J, + [XK_K] = KEY_K, + [XK_L] = KEY_L, + [XK_M] = KEY_M, + [XK_N] = KEY_N, + [XK_O] = KEY_O, + [XK_P] = KEY_P, + [XK_Q] = KEY_Q, + [XK_R] = KEY_R, + [XK_S] = KEY_S, + [XK_T] = KEY_T, + [XK_U] = KEY_U, + [XK_V] = KEY_V, + [XK_W] = KEY_W, + [XK_X] = KEY_X, + [XK_Y] = KEY_Y, + [XK_Z] = KEY_Z, + [XK_0] = KEY_0, + [XK_1] = KEY_1, + [XK_2] = KEY_2, + [XK_3] = KEY_3, + [XK_4] = KEY_4, + [XK_5] = KEY_5, + [XK_6] = KEY_6, + [XK_7] = KEY_7, + [XK_8] = KEY_8, + [XK_9] = KEY_9, + [XK_Return] = KEY_ENTER, + [XK_BackSpace] = KEY_BACKSPACE, + [XK_Tab] = KEY_TAB, + [XK_Pause] = KEY_PAUSE, + [XK_Delete] = KEY_DELETE, + [XK_slash] = KEY_SLASH, + [XK_minus] = KEY_MINUS, + [XK_equal] = KEY_EQUAL, + [XK_Escape] = KEY_ESC, + [XK_braceleft] = KEY_LEFTBRACE, + [XK_braceright] = KEY_RIGHTBRACE, + [XK_bracketleft] = KEY_LEFTMETA, + [XK_bracketright] = KEY_RIGHTMETA, + [XK_Control_L] = KEY_LEFTCTRL, + [XK_Control_R] = KEY_RIGHTCTRL, + [XK_Shift_L] = KEY_LEFTSHIFT, + [XK_Shift_R] = KEY_RIGHTSHIFT, + [XK_Alt_L] = KEY_LEFTALT, + [XK_Alt_R] = KEY_RIGHTALT, + [XK_semicolon] = KEY_SEMICOLON, + [XK_apostrophe] = KEY_APOSTROPHE, + [XK_grave] = KEY_GRAVE, + [XK_backslash] = KEY_BACKSLASH, + [XK_comma] = KEY_COMMA, + [XK_period] = KEY_DOT, + [XK_space] = KEY_SPACE, + [XK_Caps_Lock] = KEY_CAPSLOCK, + [XK_Num_Lock] = KEY_NUMLOCK, + [XK_Scroll_Lock] = KEY_SCROLLLOCK, + [XK_Sys_Req] = KEY_SYSRQ, + [XK_Linefeed] = KEY_LINEFEED, + [XK_Home] = KEY_HOME, + [XK_Pause] = KEY_PAUSE, + [XK_F1] = KEY_F1, + [XK_F2] = KEY_F2, + [XK_F3] = KEY_F3, + [XK_F4] = KEY_F4, + [XK_F5] = KEY_F5, + [XK_F6] = KEY_F6, + [XK_F7] = KEY_F7, + [XK_F8] = KEY_F8, + [XK_F9] = KEY_F9, + [XK_F10] = KEY_F10, + [XK_F11] = KEY_F11, + [XK_F12] = KEY_F12, + [XK_Up] = KEY_UP, + [XK_Page_Up] = KEY_PAGEUP, + [XK_Left] = KEY_LEFT, + [XK_Right] = KEY_RIGHT, + [XK_End] = KEY_END, + [XK_Down] = KEY_DOWN, + [XK_Page_Down] = KEY_PAGEDOWN, + [XK_Insert] = KEY_INSERT, + [XK_colon] = KEY_SEMICOLON, + [XK_quotedbl] = KEY_APOSTROPHE, + [XK_less] = KEY_COMMA, + [XK_greater] = KEY_DOT, + [XK_question] = KEY_SLASH, + [XK_bar] = KEY_BACKSLASH, + [XK_asciitilde] = KEY_GRAVE, + [XK_exclam] = KEY_1, + [XK_at] = KEY_2, + [XK_numbersign] = KEY_3, + [XK_dollar] = KEY_4, + [XK_percent] = KEY_5, + [XK_asciicircum] = KEY_6, + [XK_ampersand] = KEY_7, + [XK_asterisk] = KEY_8, + [XK_parenleft] = KEY_9, + [XK_parenright] = KEY_0, + [XK_underscore] = KEY_MINUS, + [XK_plus] = KEY_EQUAL, +}; + +static void on_kbd_event(rfbBool down, rfbKeySym keycode, rfbClientPtr cl) +{ + /* + * We need to map to the key''s Linux input layer keycode. + * Unfortunately, we don''t get the key here, only the + * rfbKeySym, which is what the key is mapped to. Mapping + * back to the key is impossible in general, even when you + * know the keymap. For instance, the standard German keymap + * maps both KEY_COMMA and KEY_102ND to XK_less. We simply + * assume standard US layout. This sucks. + */ + rfbScreenInfoPtr server = cl->screen; + struct xenfb *xenfb = server->screenData; + if (keycode >= sizeof(xk2linux) / sizeof(*xk2linux)) + return; + if (xk2linux[keycode] == 0) + return; + if (xenfb_send_key(xenfb, down, xk2linux[keycode]) < 0) + fprintf(stderr, "Key %d %s lost (%s)\n", + xk2linux[keycode], down ? "down" : "up", + strerror(errno)); +} + +static void on_ptr_event(int buttonMask, int x, int y, rfbClientPtr cl) +{ + /* initial pointer state: at (0,0), buttons up */ + static int last_x, last_y, last_button; + rfbScreenInfoPtr server = cl->screen; + struct xenfb *xenfb = server->screenData; + int i, last_down, down, ret; + + for (i = 0; i < 8; i++) { + last_down = last_button & (1 << i); + down = buttonMask & (1 << i); + if (down == last_down) + continue; + /* FIXME this assumes buttons are numbered the same; verify they are */ + if (xenfb_send_key(xenfb, down != 0, BTN_MOUSE + i) < 0) + fprintf(stderr, "Button %d %s lost (%s)\n", + i, down ? "down" : "up", strerror(errno)); + } + + if (x != last_x || y != last_y) { + if (xenfb->abs_pointer_wanted) + ret = xenfb_send_position(xenfb, x, y); + else + ret = xenfb_send_motion(xenfb, x - last_x, y - last_y); + if (ret < 0) + fprintf(stderr, "Pointer to %d,%d lost (%s)\n", + x, y, strerror(errno)); + } + + last_button = buttonMask; + last_x = x; + last_y = y; +} + +static void xenstore_write_vncport(int port, int domid) +{ + char *buf = NULL, *path; + char portstr[10]; + struct xs_handle *xsh = NULL; + + xsh = xs_daemon_open(); + if (xsh == NULL) + return; + + path = xs_get_domain_path(xsh, domid); + if (path == NULL) { + fprintf(stderr, "Can''t get domain path (%s)\n", + strerror(errno)); + goto out; + } + + if (asprintf(&buf, "%s/console/vnc-port", path) == -1) { + fprintf(stderr, "Can''t make vncport path\n"); + goto out; + } + + if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) { + fprintf(stderr, "Can''t make vncport value\n"); + goto out; + } + + if (!xs_write(xsh, XBT_NULL, buf, portstr, strlen(portstr))) + fprintf(stderr, "Can''t set vncport (%s)\n", + strerror(errno)); + + out: + free(buf); +} + + +static void vnc_update(struct xenfb *xenfb, int x, int y, int w, int h) +{ + rfbScreenInfoPtr server = xenfb->user_data; + rfbMarkRectAsModified(server, x, y, x + w, y + h); +} + +static struct option options[] = { + { "domid", 1, NULL, ''d'' }, + { "vncport", 1, NULL, ''p'' }, + { "title", 1, NULL, ''t'' }, + { "unused", 0, NULL, ''u'' }, + { "listen", 1, NULL, ''l'' }, +}; + +int main(int argc, char **argv) +{ + rfbScreenInfoPtr server; + char *fake_argv[7] = { "vncfb", "-rfbport", "5901", + "-desktop", "xen-vncfb", + "-listen", "127.0.0.1" }; + int fake_argc = sizeof(fake_argv) / sizeof(fake_argv[0]); + int domid = -1, port = -1; + char *title = NULL; + char *listen = NULL; + bool unused = false; + int opt; + struct xenfb *xenfb; + fd_set readfds; + int fd; + char portstr[10]; + char *endp; + + while ((opt = getopt_long(argc, argv, "d:p:t:u", options, + NULL)) != -1) { + switch (opt) { + case ''d'': + errno = 0; + domid = strtol(optarg, &endp, 10); + if (endp == optarg || *endp || errno) { + fprintf(stderr, "Invalid domain id specified\n"); + exit(1); + } + break; + case ''p'': + errno = 0; + port = strtol(optarg, &endp, 10); + if (endp == optarg || *endp || errno) { + fprintf(stderr, "Invalid port specified\n"); + exit(1); + } + break; + case ''t'': + title = strdup(optarg); + break; + case ''u'': + unused = true; + break; + case ''l'': + listen = strdup(optarg); + break; + } + } + if (optind != argc) { + fprintf(stderr, "Invalid options!\n"); + exit(1); + } + if (domid <= 0) { + fprintf(stderr, "Domain ID must be specified!\n"); + exit(1); + } + + if (port <= 0) + port = 5900 + domid; + if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) { + fprintf(stderr, "Invalid port specified\n"); + exit(1); + } + + fake_argv[2] = portstr; + + if (title != NULL) + fake_argv[4] = title; + + if (listen != NULL) + fake_argv[6] = listen; + + signal(SIGPIPE, SIG_IGN); + + xenfb = xenfb_new(); + if (xenfb == NULL) { + fprintf(stderr, "Could not create framebuffer (%s)\n", + strerror(errno)); + exit(1); + } + + if (!xenfb_attach_dom(xenfb, domid)) { + fprintf(stderr, "Could not connect to domain (%s)\n", + strerror(errno)); + exit(1); + } + + server = rfbGetScreen(&fake_argc, fake_argv, + xenfb->width, xenfb->height, + 8, 3, xenfb->depth / 8); + if (server == NULL) { + fprintf(stderr, "Could not create VNC server\n"); + exit(1); + } + + xenfb->user_data = server; + xenfb->update = vnc_update; + + if (unused) + server->autoPort = true; + + server->serverFormat.redShift = 16; + server->serverFormat.greenShift = 8; + server->serverFormat.blueShift = 0; + server->kbdAddEvent = on_kbd_event; + server->ptrAddEvent = on_ptr_event; + server->frameBuffer = (char *)xenfb->pixels; + server->screenData = xenfb; + server->cursor = NULL; + rfbInitServer(server); + + rfbRunEventLoop(server, -1, true); + + fd = xenfb_get_fileno(xenfb); + + xenstore_write_vncport(server->port, domid); + + for (;;) { + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + if (select(fd + 1, &readfds, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, + "Can''t select() on event channel (%s)\n", + strerror(errno)); + break; + } + + if (FD_ISSET(fd, &readfds)) + xenfb_on_incoming(xenfb); + } + + rfbScreenCleanup(server); + xenfb_delete(xenfb); + + return 0; +} diff -r c677f4e75608 tools/xenfb/xenfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/xenfb.c Fri Nov 17 11:16:32 2006 +0100 @@ -0,0 +1,619 @@ +#include <stdarg.h> +#include <stdlib.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> +#include <xen/io/xenfb.h> +#include <xen/io/xenkbd.h> +#include <sys/select.h> +#include <stdbool.h> +#include <xen/linux/evtchn.h> +#include <xen/event_channel.h> +#include <sys/mman.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <xs.h> + +#include "xenfb.h" + +// FIXME defend against malicious frontend? + +struct xenfb_private +{ + struct xenfb pub; + int domid; + evtchn_port_t fbdev_port, kbd_port; + int evt_xch; + int xc; + unsigned char *fb; + size_t fb_len; + struct xenfb_page *fb_info; + struct xenkbd_page *kbd_info; +}; + +struct xenfb *xenfb_new(void) +{ + struct xenfb_private *xenfb = malloc(sizeof(*xenfb)); + + if (xenfb == NULL) + return NULL; + + memset(xenfb, 0, sizeof(*xenfb)); + + xenfb->domid = -1; + + xenfb->evt_xch = xc_evtchn_open(); + if (xenfb->evt_xch == -1) { + int serrno = errno; + free(xenfb); + errno = serrno; + return NULL; + } + + xenfb->xc = xc_interface_open(); + if (xenfb->xc == -1) { + int serrno = errno; + xc_evtchn_close(xenfb->evt_xch); + free(xenfb); + errno = serrno; + return NULL; + } + + return &xenfb->pub; +} + +int xenfb_get_fileno(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + + return xc_evtchn_fd(xenfb->evt_xch); +} + +static void xenfb_detach_dom(struct xenfb_private *xenfb) +{ + xenfb->domid = -1; + munmap(xenfb->fb, xenfb->fb_len); + munmap(xenfb->fb_info, XC_PAGE_SIZE); + munmap(xenfb->kbd_info, XC_PAGE_SIZE); + xc_evtchn_unbind(xenfb->evt_xch, xenfb->fbdev_port); + xc_evtchn_unbind(xenfb->evt_xch, xenfb->kbd_port); +} + +void xenfb_delete(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + if (xenfb->domid != -1) + xenfb_detach_dom(xenfb); + free(xenfb); +} + +static int xenfb_kbd_event(struct xenfb_private *xenfb, + union xenkbd_in_event *event) +{ + uint32_t prod; + struct xenkbd_page *info = xenfb->kbd_info; + + prod = info->in_prod; + if (prod - info->in_cons == XENKBD_IN_RING_LEN) { + errno = EAGAIN; + return -1; + } + + mb(); /* ensure ring space available */ + XENKBD_IN_RING_REF(info, prod) = *event; + wmb(); /* ensure ring contents visible */ + info->in_prod = prod + 1; + return xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd_port); +} + +static char *xenfb_path_in_dom(struct xs_handle *xsh, + char *buf, size_t size, + unsigned domid, const char *fmt, ...) +{ + va_list ap; + char *domp = xs_get_domain_path(xsh, domid); + int n; + + if (domp == NULL) + return NULL; + + n = snprintf(buf, size, "%s/", domp); + free(domp); + if (n >= size) + return NULL; + + va_start(ap, fmt); + n += vsnprintf(buf + n, size - n, fmt, ap); + va_end(ap); + if (n >= size) + return NULL; + + return buf; +} + +static int xenfb_xs_scanf1(struct xs_handle *xsh, + const char *dir, const char *node, + const char *fmt, void *dest) +{ + char buf[1024]; + char *p; + int ret; + + if (snprintf(buf, sizeof(buf), "%s/%s", dir, node) >= sizeof(buf)) { + errno = -ENOENT; + return -1; + } + p = xs_read(xsh, XBT_NULL, buf, NULL); + if (!p) { + errno = -ENOENT; + return -1; + } + ret = sscanf(p, fmt, dest); + free(p); + if (ret != 1) { + errno = -EDOM; + return -1; + } + return ret; +} + +static int xenfb_xs_printf(struct xs_handle *xsh, + const char *dir, const char *node, char *fmt, ...) +{ + va_list ap; + char key[1024]; + char val[1024]; + int n; + + if (snprintf(key, sizeof(key), "%s/%s", dir, node) >= sizeof(key)) { + errno = -ENOENT; + return -1; + } + + va_start(ap, fmt); + n = vsnprintf(val, sizeof(val), fmt, ap); + va_end(ap); + if (n >= sizeof(val)) { + errno = -ENOSPC; /* close enough */ + return -1; + } + + if (!xs_write(xsh, XBT_NULL, key, val, n)) + return -1; + return 0; +} + +static void xenfb_dev_fatal(struct xs_handle *xsh, unsigned domid, + const char *dir, int err, const char *fmt, ...) +{ + va_list ap; + char errdir[80]; + char buf[1024]; + int n; + + fprintf(stderr, "%s ", dir); /* somewhat crude */ + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + if (err) + fprintf(stderr, " (%s)", strerror(err)); + putc(''\n'', stderr); + + if (!xenfb_path_in_dom(xsh, errdir, sizeof(errdir), domid, + "error/%s", dir)) + goto out; /* FIXME complain */ + + va_start(ap, fmt); + n = snprintf(buf, sizeof(buf), "%d ", err); + snprintf(buf + n, sizeof(buf) - n, fmt, ap); + va_end(ap); + + if (xenfb_xs_printf(xsh, buf, "error", "%s", buf) < 0) + goto out; /* FIXME complain */ + + out: + xenfb_xs_printf(xsh, dir, "state", "%d", XenbusStateClosing); +} + +static int xenfb_wait_for_state(struct xs_handle *xsh, const char *dir, + unsigned awaited) +{ + int ret; + unsigned state, dummy; + char **vec; + + for (;;) { + ret = xenfb_xs_scanf1(xsh, dir, "state", "%u", &state); + if (ret < 0 && errno != -ENOENT) + return ret; + if (state > XenbusStateClosed) + state = XenbusStateUnknown; + if ((1 << state) & awaited) + return state; + + vec = xs_read_watch(xsh, &dummy); + if (!vec) + return -1; + free(vec); + } +} + +static int xenfb_wait_for_backend_creation(struct xs_handle *xsh, + const char *backdir) +{ + if (!xs_watch(xsh, backdir, "")) + return -1; + + switch (xenfb_wait_for_state(xsh, backdir, + (1 << XenbusStateInitialising) + | (1 << XenbusStateClosed) +#if 1 /* TODO fudging state to permit restarting; to be removed */ + | (1 << XenbusStateInitWait) + | (1 << XenbusStateConnected) + | (1 << XenbusStateClosing) +#endif + )) { +#if 1 + case XenbusStateInitWait: + case XenbusStateConnected: + printf("Fudging state to %d\n", XenbusStateInitialising); /* FIXME */ +#endif + case XenbusStateInitialising: + case XenbusStateClosing: + case XenbusStateClosed: + break; + default: + return -1; + } + + xs_unwatch(xsh, backdir, ""); + return 0; +} + +static int xenfb_hotplug(struct xs_handle *xsh, const char *backdir) +{ + printf("Hotplugging %s\n", backdir); + if (xenfb_xs_printf(xsh, backdir, "hotplug-status", "connected")) + return -1; + return 0; +} + +static int xenfb_wait_for_frontend_initialised(struct xs_handle *xsh, + const char *frontdir) +{ + switch (xenfb_wait_for_state(xsh, frontdir, +#if 1 /* TODO fudging state to permit restarting; to be removed */ + (1 << XenbusStateInitialised) + | (1 << XenbusStateConnected) +#else + 1 << XenbusStateInitialised, +#endif + )) { +#if 1 + case XenbusStateConnected: + printf("Fudging state to %d\n", XenbusStateInitialised); /* FIXME */ +#endif + case XenbusStateInitialised: + break; + default: + return -1; + } + + return 0; +} + +static int xenfb_wait_for_frontend_connected(struct xs_handle *xsh, + const char *frontdir) +{ + switch (xenfb_wait_for_state(xsh, frontdir, + 1 << XenbusStateConnected)) { + case XenbusStateConnected: + break; + default: + return -1; + } + + return 0; +} + +static int xenfb_get_connection(struct xs_handle *xsh, const char *frontdir, + unsigned long *mfn, int *evtchn) +{ + if (xenfb_xs_scanf1(xsh, frontdir, "page-ref", "%lu", mfn) < 0) + return -1; + if (xenfb_xs_scanf1(xsh, frontdir, "event-channel", "%u", evtchn) + < 0) + return -1; + return 0; +} + +bool xenfb_attach_dom(struct xenfb *xenfb_pub, int domid) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + char vfb_frontdir[64], vfb_backdir[64]; + char vkbd_frontdir[64], vkbd_backdir[64]; + struct xs_handle *xsh; + int ret, val; + int n_fbmfns; + int n_fbdirs = 0; + unsigned long *fbmfns = NULL; + int serrno; + int fbdev_evtchn, kbd_evtchn; + unsigned long fbdev_mfn, kbd_mfn; + + if (xenfb->domid != -1) { + xenfb_detach_dom(xenfb); + if (domid == -1) + return true; + } + + xsh = xs_daemon_open(); + if (!xsh) + goto error; + + if (!xenfb_path_in_dom(xsh, vfb_frontdir, sizeof(vfb_frontdir), + domid, "device/vfb/0")) { + errno = -ENOENT; + goto error; + } + if (!xenfb_path_in_dom(xsh, vfb_backdir, sizeof(vfb_backdir), + 0, "backend/vfb/%d/0", domid)) { + errno = -ENOENT; + goto error; + } + if (!xenfb_path_in_dom(xsh, vkbd_frontdir, sizeof(vkbd_frontdir), + domid, "device/vkbd/0")) { + errno = -ENOENT; + goto error; + } + if (!xenfb_path_in_dom(xsh, vkbd_backdir, sizeof(vkbd_backdir), + 0, "backend/vkbd/%d/0", domid)) { + errno = -ENOENT; + goto error; + } + + if (xenfb_wait_for_backend_creation(xsh, vfb_backdir) < 0) + goto error; + if (xenfb_wait_for_backend_creation(xsh, vkbd_backdir) < 0) + goto error; + + if (xenfb_xs_printf(xsh, vkbd_backdir, "feature-abs-pointer", "1")) + goto error; + + if (xenfb_xs_printf(xsh, vfb_backdir, "state", "%d", + XenbusStateInitWait)) + goto error; + if (xenfb_xs_printf(xsh, vkbd_backdir, "state", "%d", + XenbusStateInitWait)) + goto error; + + if (xenfb_hotplug(xsh, vfb_backdir) < 0) + goto error; + if (xenfb_hotplug(xsh, vkbd_backdir) < 0) + goto error; + + if (!xs_watch(xsh, vfb_frontdir, "")) + goto error; + if (!xs_watch(xsh, vkbd_frontdir, "")) + goto error; + + if (xenfb_wait_for_frontend_initialised(xsh, vfb_frontdir) < 0) + goto error; + if (xenfb_wait_for_frontend_initialised(xsh, vkbd_frontdir) < 0) + goto error; + + if (xenfb_get_connection(xsh, vfb_frontdir, &fbdev_mfn, &fbdev_evtchn) + < 0) + goto error; + if (xenfb_get_connection(xsh, vkbd_frontdir, &kbd_mfn, &kbd_evtchn) + < 0) + goto error; + + if (xenfb_xs_scanf1(xsh, vfb_frontdir, "feature-update", + "%d", &val) < 0) + val = 0; + if (!val) { + errno = ENOTSUP; + goto error; + } + xenfb_xs_printf(xsh, vfb_backdir, "request-update", "1"); + + xenfb->fbdev_port = xc_evtchn_bind_interdomain(xenfb->evt_xch, domid, + fbdev_evtchn); + if (xenfb->fbdev_port == -1) + goto error; + + xenfb->kbd_port = xc_evtchn_bind_interdomain(xenfb->evt_xch, domid, + kbd_evtchn); + if (xenfb->kbd_port == -1) + goto error; + + ret = xc_domain_translate_gpfn_list(xenfb->xc, domid, 1, + &fbdev_mfn, &fbdev_mfn); + if (ret < 0 && errno != EINVAL) + goto error; + xenfb->fb_info = xc_map_foreign_range(xenfb->xc, domid, XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, + fbdev_mfn); + if (xenfb->fb_info == NULL) + goto error; + + xenfb->kbd_info = xc_map_foreign_range(xenfb->xc, domid, XC_PAGE_SIZE, + PROT_READ | PROT_WRITE, + kbd_mfn); + if (xenfb->kbd_info == NULL) + goto error; + + /* TODO check for permitted ranges */ + xenfb->pub.depth = xenfb->fb_info->depth; + xenfb->pub.width = xenfb->fb_info->width; + xenfb->pub.height = xenfb->fb_info->height; + /* TODO check for consistency with the above */ + xenfb->fb_len = xenfb->fb_info->mem_length; + xenfb->pub.row_stride = xenfb->fb_info->line_length; + + n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + n_fbdirs = n_fbmfns * sizeof(unsigned long); + n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + + /* + * Bug alert: xc_map_foreign_batch() can fail partly and + * return a non-null value. This is a design flaw. When it + * happens, we happily continue here, and later crash on + * access. + */ + fbmfns = xc_map_foreign_batch(xenfb->xc, domid, PROT_READ, xenfb->fb_info->pd, n_fbdirs); + if (fbmfns == NULL) + goto error; + + xenfb->fb = xc_map_foreign_batch(xenfb->xc, domid, PROT_READ | PROT_WRITE, fbmfns, n_fbmfns); + if (xenfb->fb == NULL) + goto error; + + if (xenfb_xs_printf(xsh, vfb_backdir, "state", "%d", + XenbusStateConnected)) + goto error; + if (xenfb_xs_printf(xsh, vkbd_backdir, "state", "%d", + XenbusStateConnected)) + goto error; + + if (xenfb_wait_for_frontend_connected(xsh, vkbd_frontdir) < 0) + goto error; + if (xenfb_xs_scanf1(xsh, vkbd_frontdir, "request-abs-pointer", + "%d", &val) < 0) + val = 0; + xenfb->pub.abs_pointer_wanted = val; + + xs_daemon_close(xsh); + xsh = NULL; + + munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE); + + xenfb->domid = domid; + + xenfb->pub.pixels = xenfb->fb; + + return true; + + error: + serrno = errno; + if (xenfb->fb) + munmap(xenfb->fb, xenfb->fb_len); + if (fbmfns) + munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE); + if (xenfb->kbd_info) + munmap(xenfb->kbd_info, XC_PAGE_SIZE); + if (xenfb->fb_info) + munmap(xenfb->fb_info, XC_PAGE_SIZE); + if (xenfb->kbd_port); + xc_evtchn_unbind(xenfb->evt_xch, xenfb->kbd_port); + if (xenfb->fbdev_port) + xc_evtchn_unbind(xenfb->evt_xch, xenfb->fbdev_port); + xenfb_dev_fatal(xsh, domid, vfb_backdir, serrno, "on fire"); + xenfb_dev_fatal(xsh, domid, vkbd_backdir, serrno, "on fire"); + if (xsh) + xs_daemon_close(xsh); + errno = serrno; + return false; +} + +static void xenfb_on_fb_event(struct xenfb_private *xenfb) +{ + uint32_t prod, cons; + struct xenfb_page *info = xenfb->fb_info; + + prod = info->out_prod; + if (prod == info->out_cons) + return; + rmb(); /* ensure we see ring contents up to prod */ + for (cons = info->out_cons; cons != prod; cons++) { + union xenfb_out_event *event = &XENFB_OUT_RING_REF(info, cons); + + switch (event->type) { + case XENFB_TYPE_UPDATE: + if (xenfb->pub.update) + xenfb->pub.update(&xenfb->pub, + event->update.x, event->update.y, + event->update.width, event->update.height); + break; + } + } + mb(); /* ensure we''re done with ring contents */ + info->out_cons = cons; + xc_evtchn_notify(xenfb->evt_xch, xenfb->fbdev_port); +} + +static void xenfb_on_kbd_event(struct xenfb_private *xenfb) +{ + struct xenkbd_page *info = xenfb->kbd_info; + + /* We don''t understand any keyboard events, so just ignore them. */ + if (info->out_prod == info->out_cons) + return; + info->out_cons = info->out_prod; + xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd_port); +} + +int xenfb_on_incoming(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + evtchn_port_t port; + + port = xc_evtchn_pending(xenfb->evt_xch); + if (port == -1) + return -1; + + if (port == xenfb->fbdev_port) { + xenfb_on_fb_event(xenfb); + } else if (port == xenfb->kbd_port) { + xenfb_on_kbd_event(xenfb); + } + + if (xc_evtchn_unmask(xenfb->evt_xch, port) == -1) + return -1; + + return 0; +} + +int xenfb_send_key(struct xenfb *xenfb_pub, bool down, int keycode) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_KEY; + event.key.pressed = down ? 1 : 0; + event.key.keycode = keycode; + + return xenfb_kbd_event(xenfb, &event); +} + +int xenfb_send_motion(struct xenfb *xenfb_pub, int rel_x, int rel_y) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_MOTION; + event.motion.rel_x = rel_x; + event.motion.rel_y = rel_y; + + return xenfb_kbd_event(xenfb, &event); +} + +int xenfb_send_position(struct xenfb *xenfb_pub, int abs_x, int abs_y) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_POS; + event.pos.abs_x = abs_x; + event.pos.abs_y = abs_y; + + return xenfb_kbd_event(xenfb, &event); +} diff -r c677f4e75608 tools/xenfb/xenfb.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/xenfb.h Wed Nov 08 09:26:08 2006 +0100 @@ -0,0 +1,34 @@ +#ifndef _XENFB_H_ +#define _XENFB_H_ + +#include <stdbool.h> +#include <stdint.h> + +struct xenfb +{ + uint8_t *pixels; + + int row_stride; + int depth; + int width; + int height; + int abs_pointer_wanted; + + void *user_data; + + void (*update)(struct xenfb *xenfb, int x, int y, int width, int height); +}; + +struct xenfb *xenfb_new(void); +void xenfb_delete(struct xenfb *xenfb); + +bool xenfb_attach_dom(struct xenfb *xenfb, int domid); + +int xenfb_get_fileno(struct xenfb *xenfb); +int xenfb_on_incoming(struct xenfb *xenfb); + +int xenfb_send_key(struct xenfb *xenfb, bool down, int keycode); +int xenfb_send_motion(struct xenfb *xenfb, int rel_x, int rel_y); +int xenfb_send_position(struct xenfb *xenfb, int abs_x, int abs_y); + +#endif _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Diffs since last iteration: diff -rupN -x ''*.orig'' xen-unstable.old/tools/python/xen/xend/XendDevices.py xen-unstable.new/tools/python/xen/xend/XendDevices.py --- xen-unstable.old/tools/python/xen/xend/XendDevices.py 2006-11-17 13:56:52.000000000 +0100 +++ xen-unstable.new/tools/python/xen/xend/XendDevices.py 2006-11-17 13:56:21.000000000 +0100 @@ -42,7 +42,7 @@ class XendDevices: ''usb'': usbif.UsbifController, ''tap'': BlktapController, ''vfb'': vfbif.VfbifController, - ''vkbd'': vfif.VkbdifController, + ''vkbd'': vfbif.VkbdifController, } #@classmethod diff -rupN -x ''*.orig'' xen-unstable.old/tools/python/xen/xm/create.py xen-unstable.new/tools/python/xen/xm/create.py --- xen-unstable.old/tools/python/xen/xm/create.py 2006-11-17 13:53:39.000000000 +0100 +++ xen-unstable.new/tools/python/xen/xm/create.py 2006-11-17 13:56:21.000000000 +0100 @@ -280,6 +280,14 @@ gopts.var(''usbport'', val=''PATH'', use="""Add a physical USB port to a domain, as specified by the path to that port. This option may be repeated to add more than one port.""") +gopts.var(''vfb'', val="no|yes''", + fn=set_bool, default=0, + use="Make the domain a framebuffer backend.") + +gopts.var(''vkbd'', val="no|yes''", + fn=set_bool, default=0, + use="Make the domain a keyboard backend.") + gopts.var(''vif'', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,backend=DOM,vifname=NAME", fn=append_value, default=[], use="""Add a network interface with the given MAC address and bridge. @@ -560,6 +568,13 @@ def configure_usb(config_devs, vals): config_usb = [''usbport'', [''path'', path]] config_devs.append([''device'', config_usb]) +def configure_vfbs(config_devs, vals): + if vals.vfb: + config_devs.append([''device'', [''vfb'', []]]) + +def configure_vkbds(config_devs, vals): + if vals.vkbd: + config_devs.append([''device'', [''vkbd'', []]]) def configure_security(config, vals): """Create the config for ACM security labels. @@ -738,6 +753,8 @@ def make_config(vals): configure_vifs(config_devs, vals) configure_usb(config_devs, vals) configure_vtpm(config_devs, vals) + configure_vfbs(config_devs, vals) + configure_vkbds(config_devs, vals) configure_security(config, vals) config += config_devs diff -rupN -x ''*.orig'' xen-unstable.old/tools/xenfb/vncfb.c xen-unstable.new/tools/xenfb/vncfb.c --- xen-unstable.old/tools/xenfb/vncfb.c 2006-11-17 13:56:52.000000000 +0100 +++ xen-unstable.new/tools/xenfb/vncfb.c 2006-11-17 13:56:21.000000000 +0100 @@ -260,7 +260,7 @@ int main(int argc, char **argv) rfbScreenInfoPtr server; char *fake_argv[7] = { "vncfb", "-rfbport", "5901", "-desktop", "xen-vncfb", - "-listen", "0.0.0.0" }; + "-listen", "127.0.0.1" }; int fake_argc = sizeof(fake_argv) / sizeof(fake_argv[0]); int domid = -1, port = -1; char *title = NULL; diff -rupN -x ''*.orig'' xen-unstable.old/tools/xenfb/xenfb.c xen-unstable.new/tools/xenfb/xenfb.c --- xen-unstable.old/tools/xenfb/xenfb.c 2006-11-17 13:56:52.000000000 +0100 +++ xen-unstable.new/tools/xenfb/xenfb.c 2006-11-17 13:56:21.000000000 +0100 @@ -187,6 +187,38 @@ static int xenfb_xs_printf(struct xs_han return 0; } +static void xenfb_dev_fatal(struct xs_handle *xsh, unsigned domid, + const char *dir, int err, const char *fmt, ...) +{ + va_list ap; + char errdir[80]; + char buf[1024]; + int n; + + fprintf(stderr, "%s ", dir); /* somewhat crude */ + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + if (err) + fprintf(stderr, " (%s)", strerror(err)); + putc(''\n'', stderr); + + if (!xenfb_path_in_dom(xsh, errdir, sizeof(errdir), domid, + "error/%s", dir)) + goto out; /* FIXME complain */ + + va_start(ap, fmt); + n = snprintf(buf, sizeof(buf), "%d ", err); + snprintf(buf + n, sizeof(buf) - n, fmt, ap); + va_end(ap); + + if (xenfb_xs_printf(xsh, buf, "error", "%s", buf) < 0) + goto out; /* FIXME complain */ + + out: + xenfb_xs_printf(xsh, dir, "state", "%d", XenbusStateClosing); +} + static int xenfb_wait_for_state(struct xs_handle *xsh, const char *dir, unsigned awaited) { @@ -210,21 +242,19 @@ static int xenfb_wait_for_state(struct x } } -static int xenfb_hotplug(struct xs_handle *xsh, const char *backdir) +static int xenfb_wait_for_backend_creation(struct xs_handle *xsh, + const char *backdir) { - if (xenfb_xs_printf(xsh, backdir, "hotplug-status", "connected")) - return -1; - if (!xs_watch(xsh, backdir, "")) return -1; switch (xenfb_wait_for_state(xsh, backdir, -#if 1 /* TODO fudging state to permit restarting; to be removed */ (1 << XenbusStateInitialising) + | (1 << XenbusStateClosed) +#if 1 /* TODO fudging state to permit restarting; to be removed */ | (1 << XenbusStateInitWait) | (1 << XenbusStateConnected) -#else - 1 << XenbusStateInitialising, + | (1 << XenbusStateClosing) #endif )) { #if 1 @@ -233,6 +263,8 @@ static int xenfb_hotplug(struct xs_handl printf("Fudging state to %d\n", XenbusStateInitialising); /* FIXME */ #endif case XenbusStateInitialising: + case XenbusStateClosing: + case XenbusStateClosed: break; default: return -1; @@ -241,7 +273,15 @@ static int xenfb_hotplug(struct xs_handl xs_unwatch(xsh, backdir, ""); return 0; } - + +static int xenfb_hotplug(struct xs_handle *xsh, const char *backdir) +{ + printf("Hotplugging %s\n", backdir); + if (xenfb_xs_printf(xsh, backdir, "hotplug-status", "connected")) + return -1; + return 0; +} + static int xenfb_wait_for_frontend_initialised(struct xs_handle *xsh, const char *frontdir) { @@ -336,13 +376,13 @@ bool xenfb_attach_dom(struct xenfb *xenf goto error; } - if (xenfb_hotplug(xsh, vfb_backdir) < 0) + if (xenfb_wait_for_backend_creation(xsh, vfb_backdir) < 0) goto error; - if (xenfb_hotplug(xsh, vkbd_backdir) < 0) + if (xenfb_wait_for_backend_creation(xsh, vkbd_backdir) < 0) goto error; if (xenfb_xs_printf(xsh, vkbd_backdir, "feature-abs-pointer", "1")) goto error; if (xenfb_xs_printf(xsh, vfb_backdir, "state", "%d", XenbusStateInitWait)) goto error; @@ -350,6 +391,11 @@ bool xenfb_attach_dom(struct xenfb *xenf XenbusStateInitWait)) goto error; + if (xenfb_hotplug(xsh, vfb_backdir) < 0) + goto error; + if (xenfb_hotplug(xsh, vkbd_backdir) < 0) + goto error; + if (!xs_watch(xsh, vfb_frontdir, "")) goto error; if (!xs_watch(xsh, vkbd_frontdir, "")) @@ -453,7 +499,7 @@ bool xenfb_attach_dom(struct xenfb *xenf return true; - error: + error: serrno = errno; if (xenfb->fb) munmap(xenfb->fb, xenfb->fb_len); @@ -467,6 +513,8 @@ bool xenfb_attach_dom(struct xenfb *xenf xc_evtchn_unbind(xenfb->evt_xch, xenfb->kbd_port); if (xenfb->fbdev_port) xc_evtchn_unbind(xenfb->evt_xch, xenfb->fbdev_port); + xenfb_dev_fatal(xsh, domid, vfb_backdir, serrno, "on fire"); + xenfb_dev_fatal(xsh, domid, vkbd_backdir, serrno, "on fire"); if (xsh) xs_daemon_close(xsh); errno = serrno; _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Atsushi SAKAI
2006-Nov-22 11:49 UTC
[Xen-ia64-devel] [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Hi, Markus This is a patch for work PV frame buffer on IA64. We also prepare the patch for FC6 and RHEL5B2 and its confirmed working on IA64 and x86. In this patch code, #ifdef __ia64__ exists, but it does not need. The purpose is avoid the unnecessary error handling for x86. Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp> Signed-off-by: Masami Watanabe <masami.watanabe@jp.fujitsu.com> Signed-off-by: Atsushi SAKAI <sakaia@jp.fujitsu.com>>From seeing your patches policy,We do memory address translation in Dom0 application side. Thanks Atsushi SAKAI>PV framebuffer backend. Derived from http://hg.codemonkey.ws/vncfb > >Extensive changes based on feedback from xen-devel. > >Signed-off-by: Markus Armbruster <armbru@redhat.com> >--- > tools/Makefile | 1 > tools/python/xen/xend/XendDevices.py | 4 > tools/python/xen/xend/server/vfbif.py | 29 + > tools/python/xen/xm/create.py | 17 > tools/xenfb/Makefile | 33 + > tools/xenfb/sdlfb.c | 337 ++++++++++++++++++ > tools/xenfb/vncfb.c | 396 +++++++++++++++++++++ > tools/xenfb/xenfb.c | 619 ++++++++++++++++++++++++++++++++++ > tools/xenfb/xenfb.h | 34 + > 9 files changed, 1469 insertions(+), 1 deletion(-) >_______________________________________________ Xen-ia64-devel mailing list Xen-ia64-devel@lists.xensource.com http://lists.xensource.com/xen-ia64-devel
Markus Armbruster
2006-Nov-22 13:46 UTC
Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Atsushi SAKAI <sakaia@jp.fujitsu.com> writes:> Hi, Markus > > This is a patch for work PV frame buffer on IA64. > We also prepare the patch for FC6 and RHEL5B2 and its confirmed working > on IA64 and x86. > > In this patch code, > #ifdef __ia64__ exists, but it does not need. > The purpose is avoid the unnecessary error handling for x86.Do you mean your __ia64__ code works fine everywhere?> Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp> > Signed-off-by: Masami Watanabe <masami.watanabe@jp.fujitsu.com> > Signed-off-by: Atsushi SAKAI <sakaia@jp.fujitsu.com> > > >>From seeing your patches policy, > We do memory address translation in Dom0 application side.Looks like this is support for shadow translate mode guests. My current code has that, but I didn''t catch all places. Cool.> Thanks > Atsushi SAKAI[...]> diff -r 92fddc5af648 tools/xenfb/xenfb.c > --- a/tools/xenfb/xenfb.c Wed Nov 22 20:34:45 2006 +0900 > +++ b/tools/xenfb/xenfb.c Wed Nov 22 20:36:35 2006 +0900 > @@ -344,6 +344,9 @@ bool xenfb_attach_dom(struct xenfb *xenf > int serrno; > int fbdev_evtchn, kbd_evtchn; > unsigned long fbdev_mfn, kbd_mfn; > +#ifdef __ia64__ > + xen_pfn_t *page_array; > +#endif > > if (xenfb->domid != -1) { > xenfb_detach_dom(xenfb); > @@ -432,16 +435,24 @@ bool xenfb_attach_dom(struct xenfb *xenf > if (xenfb->kbd_port == -1) > goto error; > > +#ifdef __ia64__ > ret = xc_domain_translate_gpfn_list(xenfb->xc, domid, 1, > &fbdev_mfn, &fbdev_mfn); > if (ret < 0 && errno != EINVAL) > goto error; > +#endif > xenfb->fb_info = xc_map_foreign_range(xenfb->xc, domid, XC_PAGE_SIZE, > PROT_READ | PROT_WRITE, > fbdev_mfn); > if (xenfb->fb_info == NULL) > goto error; > > +#ifdef __ia64__ > + ret = xc_domain_translate_gpfn_list(xenfb->xc, domid, 1, > + &kbd_mfn, &kbd_mfn); > + if (ret < 0 && errno != EINVAL) > + goto error; > +#endif > xenfb->kbd_info = xc_map_foreign_range(xenfb->xc, domid, XC_PAGE_SIZE, > PROT_READ | PROT_WRITE, > kbd_mfn); > @@ -466,10 +477,36 @@ bool xenfb_attach_dom(struct xenfb *xenf > * happens, we happily continue here, and later crash on > * access. > */ > - fbmfns = xc_map_foreign_batch(xenfb->xc, domid, PROT_READ, xenfb->fb_info->pd, n_fbdirs); > + > +#ifdef __ia64__ > + page_array = (xen_pfn_t *)malloc(n_fbdirs * sizeof(xen_pfn_t)); > + ret = xc_domain_translate_gpfn_list(xenfb->xc, domid, > + n_fbdirs, xenfb->fb_info->pd, page_array); > + if (ret == 0) { > + memcpy(xenfb->fb_info->pd, page_array, > + n_fbdirs * sizeof(xen_pfn_t)); > + } else { > + if (ret < 0 && errno != EINVAL) > + goto error; > + }Stupid question: why can''t you translate in place here instead of bouncing through page_array?> + free(page_array); > +#endif > + fbmfns = xc_map_foreign_batch(xenfb->xc, domid, PROT_READ | PROT_WRITE, xenfb->fb_info->pd, n_fbdirs); > if (fbmfns == NULL) > goto error; > > +#ifdef __ia64__ > + page_array = (xen_pfn_t *)malloc(n_fbmfns * sizeof(xen_pfn_t)); > + ret = xc_domain_translate_gpfn_list(xenfb->xc, domid, > + n_fbmfns, fbmfns, page_array); > + if (ret == 0) { > + memcpy(fbmfns, page_array, n_fbmfns * sizeof(xen_pfn_t)); > + } else { > + if (ret < 0 && errno != EINVAL) > + goto error; > + } > + free(page_array); > +#endif > xenfb->fb = xc_map_foreign_batch(xenfb->xc, domid, PROT_READ | PROT_WRITE, fbmfns, n_fbmfns); > if (xenfb->fb == NULL) > goto error;_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Atsushi SAKAI
2006-Nov-24 05:00 UTC
[Xen-ia64-devel] Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Hi, Markus Good question! If I do xc_domain_translate_gpfn_list to mapped area by xc_map_foreign_range/batch as 5th paramater, This makes Dom0 hungs. To avoid this, I use page array just mapped by malloc in xen-vncfb.>Stupid question: why can''t you translate in place here instead of >bouncing through page_array?Thanks Atsushi SAKAI _______________________________________________ Xen-ia64-devel mailing list Xen-ia64-devel@lists.xensource.com http://lists.xensource.com/xen-ia64-devel
PV framebuffer backend. Derived from http://hg.codemonkey.ws/vncfb Extensive changes based on feedback from xen-devel. Signed-off-by: Markus Armbruster <armbru@redhat.com> --- tools/Makefile | 1 tools/python/xen/xend/XendDevices.py | 4 tools/python/xen/xend/XendDomainInfo.py | 19 tools/python/xen/xend/image.py | 74 +++ tools/python/xen/xend/server/vfbif.py | 29 + tools/python/xen/xm/create.py | 34 + tools/xenfb/Makefile | 35 + tools/xenfb/sdlfb.c | 334 ++++++++++++++ tools/xenfb/vncfb.c | 393 +++++++++++++++++ tools/xenfb/xenfb.c | 727 ++++++++++++++++++++++++++++++++ tools/xenfb/xenfb.h | 34 + 11 files changed, 1678 insertions(+), 6 deletions(-) diff -r c98a8e2c62d1 tools/Makefile --- a/tools/Makefile Thu Nov 23 15:06:35 2006 +0000 +++ b/tools/Makefile Fri Nov 10 08:01:00 2006 +0100 @@ -19,6 +19,7 @@ SUBDIRS-y += libaio SUBDIRS-y += libaio SUBDIRS-y += blktap SUBDIRS-y += libfsimage +SUBDIRS-y += xenfb # These don''t cross-compile ifeq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH)) diff -r c98a8e2c62d1 tools/python/xen/xend/XendDevices.py --- a/tools/python/xen/xend/XendDevices.py Thu Nov 23 15:06:35 2006 +0000 +++ b/tools/python/xen/xend/XendDevices.py Thu Nov 23 18:54:35 2006 +0100 @@ -19,7 +19,7 @@ # A collection of DevControllers # -from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif +from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif, vfbif from xen.xend.server.BlktapController import BlktapController class XendDevices: @@ -41,6 +41,8 @@ class XendDevices: ''irq'': irqif.IRQController, ''usb'': usbif.UsbifController, ''tap'': BlktapController, + ''vfb'': vfbif.VfbifController, + ''vkbd'': vfbif.VkbdifController, } #@classmethod diff -r c98a8e2c62d1 tools/python/xen/xend/XendDomainInfo.py --- a/tools/python/xen/xend/XendDomainInfo.py Thu Nov 23 15:06:35 2006 +0000 +++ b/tools/python/xen/xend/XendDomainInfo.py Thu Nov 23 18:54:35 2006 +0100 @@ -459,7 +459,7 @@ class XendDomainInfo: try: self._constructDomain() self._storeVmDetails() - self._createDevices() + self._restoreDomain() self._createChannels() self._storeDomDetails() self._endRestore() @@ -1331,6 +1331,23 @@ class XendDomainInfo: self.image.cleanupBootloading() raise VmError(str(exn)) + + def _restoreDomain(self): + log.debug(''XendDomainInfo.restoreDomain: %s %s'', + self.domid, + self.info[''cpu_weight'']) + + if not self.infoIsSet(''image''): + raise VmError(''Missing image in configuration'') + + try: + self.image = image.create(self, + self.info[''image''], + self.info[''device'']) + + self._createDevices() + except RuntimeError, exn: + raise VmError(str(exn)) def cleanupDomain(self): """Cleanup domain resources; release devices. Idempotent. Nothrow diff -r c98a8e2c62d1 tools/python/xen/xend/image.py --- a/tools/python/xen/xend/image.py Thu Nov 23 15:06:35 2006 +0000 +++ b/tools/python/xen/xend/image.py Wed Nov 22 18:59:11 2006 +0100 @@ -23,6 +23,7 @@ import signal import signal import xen.lowlevel.xc +import xen.util.auxbin from xen.xend import sxp from xen.xend.XendError import VmError, XendError from xen.xend.XendLogging import log @@ -209,6 +210,79 @@ class LinuxImageHandler(ImageHandler): cmdline = self.cmdline, ramdisk = self.ramdisk, features = self.vm.getFeatures()) + + def configure(self, imageConfig, deviceConfig): + ImageHandler.configure(self, imageConfig, deviceConfig) + + self.pid = 0 + log.info("configuring linux guest") + + # set up the graphics bits. + # FIXME: this is much like what we do for HVM, should it be + # for all image types now? + self.display = sxp.child_value(imageConfig, ''display'') + self.xauthority = sxp.child_value(imageConfig, ''xauthority'') + self.vncconsole = sxp.child_value(imageConfig, ''vncconsole'') + vncpasswd = sxp.child_value(imageConfig, ''vncpasswd'') + self.vncpasswd = vncpasswd + + self.vnc = sxp.child_value(imageConfig, ''vnc'') + self.sdl = sxp.child_value(imageConfig, ''sdl'') + if self.vnc: + self.vncdisplay = int(sxp.child_value(imageConfig, ''vncdisplay'', + self.vm.getDomid())) + self.vncunused = sxp.child_value(imageConfig, ''vncunused'') + self.vnclisten = sxp.child_value(imageConfig, ''vnclisten'') + if not(self.vnclisten): + self.vnclisten = xen.xend.XendRoot.instance().get_vnclisten_address() + + def createDeviceModel(self): + if self.pid: + return + # Execute device model (for us, it''s just the fb frontend) + if not self.vnc and not self.sdl: + return + + if self.vnc: + args = [xen.util.auxbin.pathTo("xen-vncfb")] + if self.vncunused: + args += [''--unused''] + elif self.vncdisplay: + args += [ "--vncport", "%d" %(5900 + self.vncdisplay,) ] + if self.vnclisten: + args += [ "--listen", self.vnclisten ] + + # password check + if self.vncpasswd is None: + # get password from xend-config(if password omitted, None) + self.vncpasswd = xen.xend.XendRoot.instance().get_vncpasswd_default() + + if self.vncpasswd is None: + raise VmError(''vncpasswd is not setup in the guest config or xend-config.'') + if self.vncpasswd != '''': + self.vm.storeVm("vncpasswd", self.vncpasswd) + log.info("vncpassword set to ''%s''", self.vncpasswd) + + elif self.sdl: + args = [xen.util.auxbin.pathTo("xen-sdlfb")] + args = args + [ "--domid", "%d" % self.vm.getDomid(), + "--title", self.vm.info[''name''] ] + + env = dict(os.environ) + if self.display: + env[''DISPLAY''] = self.display + if self.xauthority: + env[''XAUTHORITY''] = self.xauthority + log.info("spawning video: %s", args) + self.pid = os.spawnve(os.P_NOWAIT, args[0], args, env) + log.info("device model pid: %d", self.pid) + + def destroy(self): + if not self.pid: + return + os.kill(self.pid, signal.SIGKILL) + os.waitpid(self.pid, 0) + self.pid = 0 class PPC_LinuxImageHandler(LinuxImageHandler): diff -r c98a8e2c62d1 tools/python/xen/xm/create.py --- a/tools/python/xen/xm/create.py Thu Nov 23 15:06:35 2006 +0000 +++ b/tools/python/xen/xm/create.py Fri Nov 17 16:02:28 2006 +0100 @@ -280,6 +280,14 @@ gopts.var(''usbport'', val=''PATH'', use="""Add a physical USB port to a domain, as specified by the path to that port. This option may be repeated to add more than one port.""") +gopts.var(''vfb'', val="no|yes''", + fn=set_bool, default=0, + use="Make the domain a framebuffer backend.") + +gopts.var(''vkbd'', val="no|yes''", + fn=set_bool, default=0, + use="Make the domain a keyboard backend.") + gopts.var(''vif'', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,backend=DOM,vifname=NAME", fn=append_value, default=[], use="""Add a network interface with the given MAC address and bridge. @@ -508,8 +516,10 @@ def configure_image(vals): config_image.append([''args'', vals.extra]) if vals.builder == ''hvm'': - configure_hvm(config_image, vals) - + configure_hvm(config_image, vals) + + configure_graphics(config_image, vals) + return config_image def configure_disks(config_devs, vals): @@ -560,6 +570,13 @@ def configure_usb(config_devs, vals): config_usb = [''usbport'', [''path'', path]] config_devs.append([''device'', config_usb]) +def configure_vfbs(config_devs, vals): + if vals.vfb: + config_devs.append([''device'', [''vfb'', []]]) + +def configure_vkbds(config_devs, vals): + if vals.vkbd: + config_devs.append([''device'', [''vkbd'', []]]) def configure_security(config, vals): """Create the config for ACM security labels. @@ -657,13 +674,20 @@ def configure_vifs(config_devs, vals): config_devs.append([''device'', config_vif]) +def configure_graphics(config_image, vals): + """Create the config for graphic consoles. + """ + args = [ ''vnc'', ''vncdisplay'', ''vncconsole'', ''vncunused'', + ''sdl'', ''display'', ''xauthority'', ''vnclisten'', ''vncpasswd''] + for a in args: + if (vals.__dict__[a]): + config_image.append([a, vals.__dict__[a]]) + def configure_hvm(config_image, vals): """Create the config for HVM devices. """ args = [ ''device_model'', ''pae'', ''vcpus'', ''boot'', ''fda'', ''fdb'', ''localtime'', ''serial'', ''stdvga'', ''isa'', ''nographic'', ''soundhw'', - ''vnc'', ''vncdisplay'', ''vncunused'', ''vncconsole'', ''vnclisten'', - ''sdl'', ''display'', ''xauthority'', ''acpi'', ''usb'', ''usbdevice'', ''keymap'' ] for a in args: if (vals.__dict__[a]): @@ -738,6 +762,8 @@ def make_config(vals): configure_vifs(config_devs, vals) configure_usb(config_devs, vals) configure_vtpm(config_devs, vals) + configure_vfbs(config_devs, vals) + configure_vkbds(config_devs, vals) configure_security(config, vals) config += config_devs diff -r c98a8e2c62d1 tools/python/xen/xend/server/vfbif.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/python/xen/xend/server/vfbif.py Thu Nov 23 09:18:03 2006 +0100 @@ -0,0 +1,29 @@ +from xen.xend.server.DevController import DevController + +class VfbifController(DevController): + """Virtual frame buffer controller. Handles all vfb devices for a domain. + """ + + def __init__(self, vm): + DevController.__init__(self, vm) + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + devid = 0 + back = {} + front = {} + return (devid, back, front) + +class VkbdifController(DevController): + """Virtual keyboard controller. Handles all vkbd devices for a domain. + """ + + def __init__(self, vm): + DevController.__init__(self, vm) + + def getDeviceDetails(self, config): + """@see DevController.getDeviceDetails""" + devid = 0 + back = {} + front = {} + return (devid, back, front) diff -r c98a8e2c62d1 tools/xenfb/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/Makefile Thu Nov 23 11:29:49 2006 +0100 @@ -0,0 +1,35 @@ +XEN_ROOT=../.. +include $(XEN_ROOT)/tools/Rules.mk + +CFLAGS += -I$(XEN_LIBXC) -I$(XEN_XENSTORE) -I$(XEN_ROOT)/linux-2.6-xen-sparse/include +LDFLAGS += -L$(XEN_LIBXC) -L$(XEN_XENSTORE) + +INSTALL = install +INSTALL_PROG = $(INSTALL) -m0755 +INSTALL_DIR = $(INSTALL) -d -m0755 + +.PHONY: all +all: build + +.PHONY: build +build: mk-symlinks + $(MAKE) vncfb sdlfb + +install: all + $(INSTALL_DIR) -p $(DESTDIR)/usr/$(LIBDIR)/xen/bin + $(INSTALL_PROG) vncfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-vncfb + $(INSTALL_PROG) sdlfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-sdlfb + +sdlfb: sdlfb.o xenfb.o + +sdlfb.o: CFLAGS += $(shell sdl-config --cflags) +sdlfb: LDLIBS += $(shell sdl-config --libs) -lxenctrl -lxenstore + +clean: + $(RM) *.o *~ vncfb sdlfb + +vncfb: vncfb.o xenfb.o +vncfb.o: CFLAGS += $(shell libvncserver-config --cflags) +vncfb: LDLIBS += $(shell libvncserver-config --libs) -lxenctrl -lxenstore + +sdlfb.o xenfb.o vncfb.o: xenfb.h diff -r c98a8e2c62d1 tools/xenfb/sdlfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/sdlfb.c Thu Nov 23 11:09:50 2006 +0100 @@ -0,0 +1,334 @@ +#include <SDL.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/select.h> +#include <stdlib.h> +#include <linux/input.h> +#include <getopt.h> +#include <string.h> +#include "xenfb.h" + +struct SDLFBData +{ + SDL_Surface *dst; + SDL_Surface *src; +}; + +/* + * Map from scancode to Linux input layer keycode. Scancodes are + * hardware-specific. This map assumes a standard AT or PS/2 + * keyboard. + * + * Why use scancodes? We can''t use key symbols, because they don''t + * identify keys --- they''re what keys are mapped to. The standard + * German keymap, for instance, maps both KEY_COMMA and KEY_102ND to + * SDLK_LESS. + */ +static int keymap[256] = { + [9] = KEY_ESC, + [10] = KEY_1, + [11] = KEY_2, + [12] = KEY_3, + [13] = KEY_4, + [14] = KEY_5, + [15] = KEY_6, + [16] = KEY_7, + [17] = KEY_8, + [18] = KEY_9, + [19] = KEY_0, + [20] = KEY_MINUS, + [21] = KEY_EQUAL, + [22] = KEY_BACKSPACE, + [23] = KEY_TAB, + [24] = KEY_Q, + [25] = KEY_W, + [26] = KEY_E, + [27] = KEY_R, + [28] = KEY_T, + [29] = KEY_Y, + [30] = KEY_U, + [31] = KEY_I, + [32] = KEY_O, + [33] = KEY_P, + [34] = KEY_LEFTBRACE, + [35] = KEY_RIGHTBRACE, + [36] = KEY_ENTER, + [37] = KEY_LEFTCTRL, + [38] = KEY_A, + [39] = KEY_S, + [40] = KEY_D, + [41] = KEY_F, + [42] = KEY_G, + [43] = KEY_H, + [44] = KEY_J, + [45] = KEY_K, + [46] = KEY_L, + [47] = KEY_SEMICOLON, + [48] = KEY_APOSTROPHE, + [49] = KEY_GRAVE, + [50] = KEY_LEFTSHIFT, + [51] = KEY_BACKSLASH, + [52] = KEY_Z, + [53] = KEY_X, + [54] = KEY_C, + [55] = KEY_V, + [56] = KEY_B, + [57] = KEY_N, + [58] = KEY_M, + [59] = KEY_COMMA, + [60] = KEY_DOT, + [61] = KEY_SLASH, + [62] = KEY_RIGHTSHIFT, + [63] = KEY_KPASTERISK, + [64] = KEY_LEFTALT, + [65] = KEY_SPACE, + [66] = KEY_CAPSLOCK, + [67] = KEY_F1, + [68] = KEY_F2, + [69] = KEY_F3, + [70] = KEY_F4, + [71] = KEY_F5, + [72] = KEY_F6, + [73] = KEY_F7, + [74] = KEY_F8, + [75] = KEY_F9, + [76] = KEY_F10, + [77] = KEY_NUMLOCK, + [78] = KEY_SCROLLLOCK, + [79] = KEY_KP7, + [80] = KEY_KP8, + [81] = KEY_KP9, + [82] = KEY_KPMINUS, + [83] = KEY_KP4, + [84] = KEY_KP5, + [85] = KEY_KP6, + [86] = KEY_KPPLUS, + [87] = KEY_KP1, + [88] = KEY_KP2, + [89] = KEY_KP3, + [90] = KEY_KP0, + [91] = KEY_KPDOT, + [94] = KEY_102ND, /* FIXME is this correct? */ + [95] = KEY_F11, + [96] = KEY_F12, + [108] = KEY_KPENTER, + [109] = KEY_RIGHTCTRL, + [112] = KEY_KPSLASH, + [111] = KEY_SYSRQ, + [113] = KEY_RIGHTALT, + [97] = KEY_HOME, + [98] = KEY_UP, + [99] = KEY_PAGEUP, + [100] = KEY_LEFT, + [102] = KEY_RIGHT, + [103] = KEY_END, + [104] = KEY_DOWN, + [105] = KEY_PAGEDOWN, + [106] = KEY_INSERT, + [107] = KEY_DELETE, + [110] = KEY_PAUSE, + [115] = KEY_LEFTMETA, + [116] = KEY_RIGHTMETA, + [117] = KEY_MENU, +}; + +static int btnmap[] = { + [SDL_BUTTON_LEFT] = BTN_LEFT, + [SDL_BUTTON_MIDDLE] = BTN_MIDDLE, + [SDL_BUTTON_RIGHT] = BTN_RIGHT, + /* FIXME not 100% sure about these: */ + [SDL_BUTTON_WHEELUP] = BTN_FORWARD, + [SDL_BUTTON_WHEELDOWN] BTN_BACK +}; + +static void sdl_update(struct xenfb *xenfb, int x, int y, int width, int height) +{ + struct SDLFBData *data = xenfb->user_data; + SDL_Rect r = { x, y, width, height }; + SDL_BlitSurface(data->src, &r, data->dst, &r); + SDL_UpdateRect(data->dst, x, y, width, height); +} + +static int sdl_on_event(struct xenfb *xenfb, SDL_Event *event) +{ + int x, y, ret; + + switch (event->type) { + case SDL_KEYDOWN: + case SDL_KEYUP: + if (keymap[event->key.keysym.scancode] == 0) + break; + ret = xenfb_send_key(xenfb, + event->type == SDL_KEYDOWN, + keymap[event->key.keysym.scancode]); + if (ret < 0) + fprintf(stderr, "Key %d %s lost (%s)\n", + keymap[event->key.keysym.scancode], + event->type == SDL_KEYDOWN ? "down" : "up", + strerror(errno)); + break; + case SDL_MOUSEMOTION: + if (xenfb->abs_pointer_wanted) { + SDL_GetMouseState(&x, &y); + ret = xenfb_send_position(xenfb, x, y); + } else { + SDL_GetRelativeMouseState(&x, &y); + ret = xenfb_send_motion(xenfb, x, y); + } + if (ret < 0) + fprintf(stderr, "Pointer to %d,%d lost (%s)\n", + x, y, strerror(errno)); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + if (event->button.button >= sizeof(btnmap) / sizeof(*btnmap)) + break; + if (btnmap[event->button.button] == 0) + break; + ret = xenfb_send_key(xenfb, + event->type == SDL_MOUSEBUTTONDOWN, + btnmap[event->button.button]); + if (ret < 0) + fprintf(stderr, "Button %d %s lost (%s)\n", + btnmap[event->button.button] - BTN_MOUSE, + event->type == SDL_MOUSEBUTTONDOWN ? "down" : "up", + strerror(errno)); + break; + case SDL_QUIT: + return 0; + } + + return 1; +} + +static struct option options[] = { + { "domid", 1, NULL, ''d'' }, + { "title", 1, NULL, ''t'' }, +}; + +int main(int argc, char **argv) +{ + struct xenfb *xenfb; + int domid = -1; + char * title = NULL; + fd_set readfds; + int nfds; + struct SDLFBData data; + SDL_Rect r; + struct timeval tv; + SDL_Event event; + int do_quit = 0; + int opt; + char *endp; + + while ((opt = getopt_long(argc, argv, "d:t:", options, + NULL)) != -1) { + switch (opt) { + case ''d'': + domid = strtol(optarg, &endp, 10); + if (endp == optarg || *endp) { + fprintf(stderr, "Invalid domain id specified\n"); + exit(1); + } + break; + case ''t'': + title = strdup(optarg); + break; + } + } + if (optind != argc) { + fprintf(stderr, "Invalid options!\n"); + exit(1); + } + if (domid <= 0) { + fprintf(stderr, "Domain ID must be specified!\n"); + exit(1); + } + + xenfb = xenfb_new(); + if (xenfb == NULL) { + fprintf(stderr, "Could not create framebuffer (%s)\n", + strerror(errno)); + exit(1); + } + + if (xenfb_attach_dom(xenfb, domid) < 0) { + fprintf(stderr, "Could not connect to domain (%s)\n", + strerror(errno)); + exit(1); + } + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "Could not initialize SDL\n"); + exit(1); + } + + data.dst = SDL_SetVideoMode(xenfb->width, xenfb->height, xenfb->depth, + SDL_SWSURFACE); + if (!data.dst) { + fprintf(stderr, "SDL_SetVideoMode failed\n"); + exit(1); + } + + data.src = SDL_CreateRGBSurfaceFrom(xenfb->pixels, + xenfb->width, xenfb->height, + xenfb->depth, xenfb->row_stride, + 0xFF0000, 0xFF00, 0xFF, 0); + + if (!data.src) { + fprintf(stderr, "SDL_CreateRGBSurfaceFrom failed\n"); + exit(1); + } + + if (title == NULL) + title = strdup("xen-sdlfb"); + SDL_WM_SetCaption(title, title); + + r.x = r.y = 0; + r.w = xenfb->width; + r.h = xenfb->height; + SDL_BlitSurface(data.src, &r, data.dst, &r); + SDL_UpdateRect(data.dst, 0, 0, xenfb->width, xenfb->height); + + xenfb->update = sdl_update; + xenfb->user_data = &data; + + SDL_ShowCursor(0); + + /* + * We need to wait for fds becoming ready or SDL events to + * arrive. We time out the select after 10ms to poll for SDL + * events. Clunky, but works. Could avoid the clunkiness + * with a separate thread. + */ + for (;;) { + FD_ZERO(&readfds); + nfds = xenfb_select_fds(xenfb, &readfds); + tv = (struct timeval){0, 10000}; + + if (select(nfds, &readfds, NULL, NULL, &tv) < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, + "Can''t select() on event channel (%s)\n", + strerror(errno)); + break; + } + + while (SDL_PollEvent(&event)) { + if (!sdl_on_event(xenfb, &event)) + do_quit = 1; + } + + if (do_quit) + break; + + xenfb_poll(xenfb, &readfds); + } + + xenfb_delete(xenfb); + + SDL_Quit(); + + return 0; +} diff -r c98a8e2c62d1 tools/xenfb/vncfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/vncfb.c Thu Nov 23 11:32:35 2006 +0100 @@ -0,0 +1,393 @@ +#define _GNU_SOURCE +#include <errno.h> +#include <getopt.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <malloc.h> +#include <rfb/rfb.h> +#include <rfb/keysym.h> +#include <linux/input.h> +#include <xs.h> +#include "xenfb.h" + +static int xk2linux[0x10000] = { + [XK_a] = KEY_A, + [XK_b] = KEY_B, + [XK_c] = KEY_C, + [XK_d] = KEY_D, + [XK_e] = KEY_E, + [XK_f] = KEY_F, + [XK_g] = KEY_G, + [XK_h] = KEY_H, + [XK_i] = KEY_I, + [XK_j] = KEY_J, + [XK_k] = KEY_K, + [XK_l] = KEY_L, + [XK_m] = KEY_M, + [XK_n] = KEY_N, + [XK_o] = KEY_O, + [XK_p] = KEY_P, + [XK_q] = KEY_Q, + [XK_r] = KEY_R, + [XK_s] = KEY_S, + [XK_t] = KEY_T, + [XK_u] = KEY_U, + [XK_v] = KEY_V, + [XK_w] = KEY_W, + [XK_x] = KEY_X, + [XK_y] = KEY_Y, + [XK_z] = KEY_Z, + [XK_A] = KEY_A, + [XK_B] = KEY_B, + [XK_C] = KEY_C, + [XK_D] = KEY_D, + [XK_E] = KEY_E, + [XK_F] = KEY_F, + [XK_G] = KEY_G, + [XK_H] = KEY_H, + [XK_I] = KEY_I, + [XK_J] = KEY_J, + [XK_K] = KEY_K, + [XK_L] = KEY_L, + [XK_M] = KEY_M, + [XK_N] = KEY_N, + [XK_O] = KEY_O, + [XK_P] = KEY_P, + [XK_Q] = KEY_Q, + [XK_R] = KEY_R, + [XK_S] = KEY_S, + [XK_T] = KEY_T, + [XK_U] = KEY_U, + [XK_V] = KEY_V, + [XK_W] = KEY_W, + [XK_X] = KEY_X, + [XK_Y] = KEY_Y, + [XK_Z] = KEY_Z, + [XK_0] = KEY_0, + [XK_1] = KEY_1, + [XK_2] = KEY_2, + [XK_3] = KEY_3, + [XK_4] = KEY_4, + [XK_5] = KEY_5, + [XK_6] = KEY_6, + [XK_7] = KEY_7, + [XK_8] = KEY_8, + [XK_9] = KEY_9, + [XK_Return] = KEY_ENTER, + [XK_BackSpace] = KEY_BACKSPACE, + [XK_Tab] = KEY_TAB, + [XK_Pause] = KEY_PAUSE, + [XK_Delete] = KEY_DELETE, + [XK_slash] = KEY_SLASH, + [XK_minus] = KEY_MINUS, + [XK_equal] = KEY_EQUAL, + [XK_Escape] = KEY_ESC, + [XK_braceleft] = KEY_LEFTBRACE, + [XK_braceright] = KEY_RIGHTBRACE, + [XK_bracketleft] = KEY_LEFTMETA, + [XK_bracketright] = KEY_RIGHTMETA, + [XK_Control_L] = KEY_LEFTCTRL, + [XK_Control_R] = KEY_RIGHTCTRL, + [XK_Shift_L] = KEY_LEFTSHIFT, + [XK_Shift_R] = KEY_RIGHTSHIFT, + [XK_Alt_L] = KEY_LEFTALT, + [XK_Alt_R] = KEY_RIGHTALT, + [XK_semicolon] = KEY_SEMICOLON, + [XK_apostrophe] = KEY_APOSTROPHE, + [XK_grave] = KEY_GRAVE, + [XK_backslash] = KEY_BACKSLASH, + [XK_comma] = KEY_COMMA, + [XK_period] = KEY_DOT, + [XK_space] = KEY_SPACE, + [XK_Caps_Lock] = KEY_CAPSLOCK, + [XK_Num_Lock] = KEY_NUMLOCK, + [XK_Scroll_Lock] = KEY_SCROLLLOCK, + [XK_Sys_Req] = KEY_SYSRQ, + [XK_Linefeed] = KEY_LINEFEED, + [XK_Home] = KEY_HOME, + [XK_Pause] = KEY_PAUSE, + [XK_F1] = KEY_F1, + [XK_F2] = KEY_F2, + [XK_F3] = KEY_F3, + [XK_F4] = KEY_F4, + [XK_F5] = KEY_F5, + [XK_F6] = KEY_F6, + [XK_F7] = KEY_F7, + [XK_F8] = KEY_F8, + [XK_F9] = KEY_F9, + [XK_F10] = KEY_F10, + [XK_F11] = KEY_F11, + [XK_F12] = KEY_F12, + [XK_Up] = KEY_UP, + [XK_Page_Up] = KEY_PAGEUP, + [XK_Left] = KEY_LEFT, + [XK_Right] = KEY_RIGHT, + [XK_End] = KEY_END, + [XK_Down] = KEY_DOWN, + [XK_Page_Down] = KEY_PAGEDOWN, + [XK_Insert] = KEY_INSERT, + [XK_colon] = KEY_SEMICOLON, + [XK_quotedbl] = KEY_APOSTROPHE, + [XK_less] = KEY_COMMA, + [XK_greater] = KEY_DOT, + [XK_question] = KEY_SLASH, + [XK_bar] = KEY_BACKSLASH, + [XK_asciitilde] = KEY_GRAVE, + [XK_exclam] = KEY_1, + [XK_at] = KEY_2, + [XK_numbersign] = KEY_3, + [XK_dollar] = KEY_4, + [XK_percent] = KEY_5, + [XK_asciicircum] = KEY_6, + [XK_ampersand] = KEY_7, + [XK_asterisk] = KEY_8, + [XK_parenleft] = KEY_9, + [XK_parenright] = KEY_0, + [XK_underscore] = KEY_MINUS, + [XK_plus] = KEY_EQUAL, +}; + +static void on_kbd_event(rfbBool down, rfbKeySym keycode, rfbClientPtr cl) +{ + /* + * We need to map to the key''s Linux input layer keycode. + * Unfortunately, we don''t get the key here, only the + * rfbKeySym, which is what the key is mapped to. Mapping + * back to the key is impossible in general, even when you + * know the keymap. For instance, the standard German keymap + * maps both KEY_COMMA and KEY_102ND to XK_less. We simply + * assume standard US layout. This sucks. + */ + rfbScreenInfoPtr server = cl->screen; + struct xenfb *xenfb = server->screenData; + if (keycode >= sizeof(xk2linux) / sizeof(*xk2linux)) + return; + if (xk2linux[keycode] == 0) + return; + if (xenfb_send_key(xenfb, down, xk2linux[keycode]) < 0) + fprintf(stderr, "Key %d %s lost (%s)\n", + xk2linux[keycode], down ? "down" : "up", + strerror(errno)); +} + +static void on_ptr_event(int buttonMask, int x, int y, rfbClientPtr cl) +{ + /* initial pointer state: at (0,0), buttons up */ + static int last_x, last_y, last_button; + rfbScreenInfoPtr server = cl->screen; + struct xenfb *xenfb = server->screenData; + int i, last_down, down, ret; + + for (i = 0; i < 8; i++) { + last_down = last_button & (1 << i); + down = buttonMask & (1 << i); + if (down == last_down) + continue; + /* FIXME this assumes buttons are numbered the same; verify they are */ + if (xenfb_send_key(xenfb, down != 0, BTN_MOUSE + i) < 0) + fprintf(stderr, "Button %d %s lost (%s)\n", + i, down ? "down" : "up", strerror(errno)); + } + + if (x != last_x || y != last_y) { + if (xenfb->abs_pointer_wanted) + ret = xenfb_send_position(xenfb, x, y); + else + ret = xenfb_send_motion(xenfb, x - last_x, y - last_y); + if (ret < 0) + fprintf(stderr, "Pointer to %d,%d lost (%s)\n", + x, y, strerror(errno)); + } + + last_button = buttonMask; + last_x = x; + last_y = y; +} + +static void xenstore_write_vncport(int port, int domid) +{ + char *buf = NULL, *path; + char portstr[10]; + struct xs_handle *xsh = NULL; + + xsh = xs_daemon_open(); + if (xsh == NULL) + return; + + path = xs_get_domain_path(xsh, domid); + if (path == NULL) { + fprintf(stderr, "Can''t get domain path (%s)\n", + strerror(errno)); + goto out; + } + + if (asprintf(&buf, "%s/console/vnc-port", path) == -1) { + fprintf(stderr, "Can''t make vncport path\n"); + goto out; + } + + if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) { + fprintf(stderr, "Can''t make vncport value\n"); + goto out; + } + + if (!xs_write(xsh, XBT_NULL, buf, portstr, strlen(portstr))) + fprintf(stderr, "Can''t set vncport (%s)\n", + strerror(errno)); + + out: + free(buf); +} + + +static void vnc_update(struct xenfb *xenfb, int x, int y, int w, int h) +{ + rfbScreenInfoPtr server = xenfb->user_data; + rfbMarkRectAsModified(server, x, y, x + w, y + h); +} + +static struct option options[] = { + { "domid", 1, NULL, ''d'' }, + { "vncport", 1, NULL, ''p'' }, + { "title", 1, NULL, ''t'' }, + { "unused", 0, NULL, ''u'' }, + { "listen", 1, NULL, ''l'' }, +}; + +int main(int argc, char **argv) +{ + rfbScreenInfoPtr server; + char *fake_argv[7] = { "vncfb", "-rfbport", "5901", + "-desktop", "xen-vncfb", + "-listen", "127.0.0.1" }; + int fake_argc = sizeof(fake_argv) / sizeof(fake_argv[0]); + int domid = -1, port = -1; + char *title = NULL; + char *listen = NULL; + bool unused = false; + int opt; + struct xenfb *xenfb; + fd_set readfds; + int nfds; + char portstr[10]; + char *endp; + + while ((opt = getopt_long(argc, argv, "d:p:t:u", options, + NULL)) != -1) { + switch (opt) { + case ''d'': + errno = 0; + domid = strtol(optarg, &endp, 10); + if (endp == optarg || *endp || errno) { + fprintf(stderr, "Invalid domain id specified\n"); + exit(1); + } + break; + case ''p'': + errno = 0; + port = strtol(optarg, &endp, 10); + if (endp == optarg || *endp || errno) { + fprintf(stderr, "Invalid port specified\n"); + exit(1); + } + break; + case ''t'': + title = strdup(optarg); + break; + case ''u'': + unused = true; + break; + case ''l'': + listen = strdup(optarg); + break; + } + } + if (optind != argc) { + fprintf(stderr, "Invalid options!\n"); + exit(1); + } + if (domid <= 0) { + fprintf(stderr, "Domain ID must be specified!\n"); + exit(1); + } + + if (port <= 0) + port = 5900 + domid; + if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) { + fprintf(stderr, "Invalid port specified\n"); + exit(1); + } + + fake_argv[2] = portstr; + + if (title != NULL) + fake_argv[4] = title; + + if (listen != NULL) + fake_argv[6] = listen; + + signal(SIGPIPE, SIG_IGN); + + xenfb = xenfb_new(); + if (xenfb == NULL) { + fprintf(stderr, "Could not create framebuffer (%s)\n", + strerror(errno)); + exit(1); + } + + if (xenfb_attach_dom(xenfb, domid) < 0) { + fprintf(stderr, "Could not connect to domain (%s)\n", + strerror(errno)); + exit(1); + } + + server = rfbGetScreen(&fake_argc, fake_argv, + xenfb->width, xenfb->height, + 8, 3, xenfb->depth / 8); + if (server == NULL) { + fprintf(stderr, "Could not create VNC server\n"); + exit(1); + } + + xenfb->user_data = server; + xenfb->update = vnc_update; + + if (unused) + server->autoPort = true; + + server->serverFormat.redShift = 16; + server->serverFormat.greenShift = 8; + server->serverFormat.blueShift = 0; + server->kbdAddEvent = on_kbd_event; + server->ptrAddEvent = on_ptr_event; + server->frameBuffer = xenfb->pixels; + server->screenData = xenfb; + server->cursor = NULL; + rfbInitServer(server); + + rfbRunEventLoop(server, -1, true); + + xenstore_write_vncport(server->port, domid); + + for (;;) { + FD_ZERO(&readfds); + nfds = xenfb_select_fds(xenfb, &readfds); + + if (select(nfds, &readfds, NULL, NULL, NULL) < 0) { + if (errno == EINTR) + continue; + fprintf(stderr, + "Can''t select() on event channel (%s)\n", + strerror(errno)); + break; + } + + xenfb_poll(xenfb, &readfds); + } + + rfbScreenCleanup(server); + xenfb_delete(xenfb); + + return 0; +} diff -r c98a8e2c62d1 tools/xenfb/xenfb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/xenfb.c Thu Nov 23 18:49:10 2006 +0100 @@ -0,0 +1,727 @@ +#include <stdarg.h> +#include <stdlib.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <xenctrl.h> +#include <xen/io/xenbus.h> +#include <xen/io/xenfb.h> +#include <xen/io/xenkbd.h> +#include <sys/select.h> +#include <stdbool.h> +#include <xen/linux/evtchn.h> +#include <xen/event_channel.h> +#include <sys/mman.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <xs.h> + +#include "xenfb.h" + +// FIXME defend against malicious frontend? + +struct xenfb_device { + const char *devicetype; + char nodename[64]; /* backend xenstore dir */ + char otherend[64]; /* frontend xenstore dir */ + int otherend_id; /* frontend domid */ + enum xenbus_state state; /* backend state */ + void *page; /* shared page */ + evtchn_port_t port; + struct xenfb_private *xenfb; +}; + +struct xenfb_private { + struct xenfb pub; + int evt_xch; /* event channel driver handle */ + int xc; /* hypervisor interface handle */ + struct xs_handle *xsh; /* xs daemon handle */ + struct xenfb_device fb, kbd; + size_t fb_len; /* size of framebuffer */ +}; + +static void xenfb_detach_dom(struct xenfb_private *); + +static char *xenfb_path_in_dom(struct xs_handle *xsh, + char *buf, size_t size, + unsigned domid, const char *fmt, ...) +{ + va_list ap; + char *domp = xs_get_domain_path(xsh, domid); + int n; + + if (domp == NULL) + return NULL; + + n = snprintf(buf, size, "%s/", domp); + free(domp); + if (n >= size) + return NULL; + + va_start(ap, fmt); + n += vsnprintf(buf + n, size - n, fmt, ap); + va_end(ap); + if (n >= size) + return NULL; + + return buf; +} + +static int xenfb_xs_scanf1(struct xs_handle *xsh, + const char *dir, const char *node, + const char *fmt, void *dest) +{ + char buf[1024]; + char *p; + int ret; + + if (snprintf(buf, sizeof(buf), "%s/%s", dir, node) >= sizeof(buf)) { + errno = ENOENT; + return -1; + } + p = xs_read(xsh, XBT_NULL, buf, NULL); + if (!p) { + errno = ENOENT; + return -1; + } + ret = sscanf(p, fmt, dest); + free(p); + if (ret != 1) { + errno = EDOM; + return -1; + } + return ret; +} + +static int xenfb_xs_printf(struct xs_handle *xsh, + const char *dir, const char *node, char *fmt, ...) +{ + va_list ap; + char key[1024]; + char val[1024]; + int n; + + if (snprintf(key, sizeof(key), "%s/%s", dir, node) >= sizeof(key)) { + errno = ENOENT; + return -1; + } + + va_start(ap, fmt); + n = vsnprintf(val, sizeof(val), fmt, ap); + va_end(ap); + if (n >= sizeof(val)) { + errno = ENOSPC; /* close enough */ + return -1; + } + + if (!xs_write(xsh, XBT_NULL, key, val, n)) + return -1; + return 0; +} + +static void xenfb_device_init(struct xenfb_device *dev, + const char *type, + struct xenfb_private *xenfb) +{ + dev->devicetype = type; + dev->otherend_id = -1; + dev->port = -1; + dev->xenfb = xenfb; +} + +int xenfb_device_set_domain(struct xenfb_device *dev, int domid) +{ + struct xenfb_private *xenfb = dev->xenfb; + + dev->otherend_id = domid; + + if (!xenfb_path_in_dom(xenfb->xsh, + dev->otherend, sizeof(dev->otherend), + domid, "device/%s/0", dev->devicetype)) { + errno = ENOENT; + return -1; + } + if (!xenfb_path_in_dom(xenfb->xsh, + dev->nodename, sizeof(dev->nodename), + 0, "backend/%s/%d/0", dev->devicetype, domid)) { + errno = ENOENT; + return -1; + } + + return 0; +} + +struct xenfb *xenfb_new(void) +{ + struct xenfb_private *xenfb = malloc(sizeof(*xenfb)); + int serrno; + + if (xenfb == NULL) + return NULL; + + memset(xenfb, 0, sizeof(*xenfb)); + xenfb->evt_xch = xenfb->xc = -1; + xenfb_device_init(&xenfb->fb, "vfb", xenfb); + xenfb_device_init(&xenfb->kbd, "vkbd", xenfb); + + xenfb->evt_xch = xc_evtchn_open(); + if (xenfb->evt_xch == -1) + goto fail; + + xenfb->xc = xc_interface_open(); + if (xenfb->xc == -1) + goto fail; + + xenfb->xsh = xs_daemon_open(); + if (!xenfb->xsh) + goto fail; + + return &xenfb->pub; + + fail: + serrno = errno; + xenfb_delete(&xenfb->pub); + errno = serrno; + return NULL; +} + +void xenfb_delete(struct xenfb *xenfb_pub) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + + xenfb_detach_dom(xenfb); + if (xenfb->xc >= 0) + xc_interface_close(xenfb->xc); + if (xenfb->evt_xch >= 0) + xc_evtchn_close(xenfb->evt_xch); + if (xenfb->xsh) + xs_daemon_close(xenfb->xsh); + free(xenfb); +} + +static enum xenbus_state xenfb_read_state(struct xs_handle *xsh, + const char *dir) +{ + int ret, state; + + ret = xenfb_xs_scanf1(xsh, dir, "state", "%d", &state); + if (ret < 0) + return XenbusStateUnknown; + + if ((unsigned)state > XenbusStateClosed) + state = XenbusStateUnknown; + return state; +} + +static int xenfb_switch_state(struct xenfb_device *dev, + enum xenbus_state state) +{ + struct xs_handle *xsh = dev->xenfb->xsh; + + if (xenfb_xs_printf(xsh, dev->nodename, "state", "%d", state) < 0) + return -1; + dev->state = state; + return 0; +} + +static int xenfb_wait_for_state(struct xs_handle *xsh, const char *dir, + unsigned awaited) +{ + unsigned state, dummy; + char **vec; + + for (;;) { + state = xenfb_read_state(xsh, dir); + if (state < 0) + return -1; + + if ((1 << state) & awaited) + return state; + + vec = xs_read_watch(xsh, &dummy); + if (!vec) + return -1; + free(vec); + } +} + +static int xenfb_wait_for_backend_creation(struct xenfb_device *dev) +{ + struct xs_handle *xsh = dev->xenfb->xsh; + int state; + + if (!xs_watch(xsh, dev->nodename, "")) + return -1; + state = xenfb_wait_for_state(xsh, dev->nodename, + (1 << XenbusStateInitialising) + | (1 << XenbusStateClosed) +#if 1 /* TODO fudging state to permit restarting; to be removed */ + | (1 << XenbusStateInitWait) + | (1 << XenbusStateConnected) + | (1 << XenbusStateClosing) +#endif + ); + xs_unwatch(xsh, dev->nodename, ""); + + switch (state) { +#if 1 + case XenbusStateInitWait: + case XenbusStateConnected: + printf("Fudging state to %d\n", XenbusStateInitialising); /* FIXME */ +#endif + case XenbusStateInitialising: + case XenbusStateClosing: + case XenbusStateClosed: + break; + default: + return -1; + } + + return 0; +} + +static int xenfb_hotplug(struct xenfb_device *dev) +{ + if (xenfb_xs_printf(dev->xenfb->xsh, dev->nodename, + "hotplug-status", "connected")) + return -1; + return 0; +} + +static int xenfb_wait_for_frontend_initialised(struct xenfb_device *dev) +{ + switch (xenfb_wait_for_state(dev->xenfb->xsh, dev->otherend, +#if 1 /* TODO fudging state to permit restarting; to be removed */ + (1 << XenbusStateInitialised) + | (1 << XenbusStateConnected) +#else + 1 << XenbusStateInitialised, +#endif + )) { +#if 1 + case XenbusStateConnected: + printf("Fudging state to %d\n", XenbusStateInitialised); /* FIXME */ +#endif + case XenbusStateInitialised: + break; + default: + return -1; + } + + return 0; +} + +static void *xenfb_map_foreign_range(int xc_handle, uint32_t dom, + int size, int prot, + unsigned long mfn) +{ + int rc; + + rc = xc_domain_translate_gpfn_list(xc_handle, dom, 1, &mfn, &mfn); + if (rc < 0 && errno != EINVAL) + return NULL; + return xc_map_foreign_range(xc_handle, dom, size, prot, mfn); +} + +static void *xenfb_map_foreign_batch(int xc_handle, uint32_t dom, int prot, + xen_pfn_t *arr, int num) +{ + xen_pfn_t *buf; + int rc; + void *ret; + + /* make a copy to avoid clobbering arr[] */ + buf = malloc(num * sizeof(*buf)); + if (!buf) + return NULL; + memcpy(buf, arr, num * sizeof(*buf)); + + rc = xc_domain_translate_gpfn_list(xc_handle, dom, num, buf, buf); + if (rc < 0 && errno != EINVAL) { + free(buf); + return NULL; + } + + /* + * Bug alert: xc_map_foreign_batch() can fail partly and + * return a non-null value. This is a design flaw. When it + * happens, we happily continue here, and later crash on + * access. + */ + ret = xc_map_foreign_batch(xc_handle, dom, prot, buf, num); + free(buf); + return ret; +} + +static int xenfb_map_fb(struct xenfb_private *xenfb, int domid) +{ + struct xenfb_page *page = xenfb->fb.page; + int n_fbmfns; + int n_fbdirs; + unsigned long *fbmfns; + + n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + n_fbdirs = n_fbmfns * sizeof(unsigned long); + n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE; + + fbmfns = xenfb_map_foreign_batch(xenfb->xc, domid, + PROT_READ, page->pd, n_fbdirs); + if (fbmfns == NULL) + return -1; + + xenfb->pub.pixels = xenfb_map_foreign_batch(xenfb->xc, domid, + PROT_READ | PROT_WRITE, fbmfns, n_fbmfns); + if (xenfb->pub.pixels == NULL) { + munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE); + return -1; + } + + return munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE); +} + +static int xenfb_bind(struct xenfb_device *dev) +{ + struct xenfb_private *xenfb = dev->xenfb; + unsigned long mfn; + evtchn_port_t evtchn; + + if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "page-ref", "%lu", + &mfn) < 0) + return -1; + if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "event-channel", "%u", + &evtchn) < 0) + return -1; + + dev->port = xc_evtchn_bind_interdomain(xenfb->evt_xch, + dev->otherend_id, evtchn); + if (dev->port == -1) + return -1; + + dev->page = xenfb_map_foreign_range(xenfb->xc, dev->otherend_id, + XC_PAGE_SIZE, PROT_READ | PROT_WRITE, mfn); + if (dev->page == NULL) + return -1; + + return 0; +} + +static void xenfb_unbind(struct xenfb_device *dev) +{ + if (dev->page) { + munmap(dev->page, XC_PAGE_SIZE); + dev->page = NULL; + } + if (dev->port >= 0) { + xc_evtchn_unbind(dev->xenfb->evt_xch, dev->port); + dev->port = -1; + } +} + +static int xenfb_wait_for_frontend_connected(struct xenfb_device *dev) +{ + switch (xenfb_wait_for_state(dev->xenfb->xsh, dev->otherend, + 1 << XenbusStateConnected)) { + case XenbusStateConnected: + break; + default: + return -1; + } + + return 0; +} + +static void xenfb_dev_fatal(struct xenfb_device *dev, int err, + const char *fmt, ...) +{ + struct xs_handle *xsh = dev->xenfb->xsh; + va_list ap; + char errdir[80]; + char buf[1024]; + int n; + + fprintf(stderr, "%s ", dev->nodename); /* somewhat crude */ + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + if (err) + fprintf(stderr, " (%s)", strerror(err)); + putc(''\n'', stderr); + + if (!xenfb_path_in_dom(xsh, errdir, sizeof(errdir), 0, + "error/%s", dev->nodename)) + goto out; /* FIXME complain */ + + va_start(ap, fmt); + n = snprintf(buf, sizeof(buf), "%d ", err); + snprintf(buf + n, sizeof(buf) - n, fmt, ap); + va_end(ap); + + if (xenfb_xs_printf(xsh, buf, "error", "%s", buf) < 0) + goto out; /* FIXME complain */ + + out: + xenfb_switch_state(dev, XenbusStateClosing); +} + +int xenfb_attach_dom(struct xenfb *xenfb_pub, int domid) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + struct xs_handle *xsh = xenfb->xsh; + int val, serrno; + struct xenfb_page *fb_page; + + xenfb_detach_dom(xenfb); + + xenfb_device_set_domain(&xenfb->fb, domid); + xenfb_device_set_domain(&xenfb->kbd, domid); + + if (xenfb_wait_for_backend_creation(&xenfb->fb) < 0) + goto error; + if (xenfb_wait_for_backend_creation(&xenfb->kbd) < 0) + goto error; + + if (xenfb_xs_printf(xsh, xenfb->kbd.nodename, "feature-abs-pointer", "1")) + goto error; + if (xenfb_switch_state(&xenfb->fb, XenbusStateInitWait)) + goto error; + if (xenfb_switch_state(&xenfb->kbd, XenbusStateInitWait)) + goto error; + + if (xenfb_hotplug(&xenfb->fb) < 0) + goto error; + if (xenfb_hotplug(&xenfb->kbd) < 0) + goto error; + + if (!xs_watch(xsh, xenfb->fb.otherend, "")) + goto error; + if (!xs_watch(xsh, xenfb->kbd.otherend, "")) + goto error; + + if (xenfb_wait_for_frontend_initialised(&xenfb->fb) < 0) + goto error; + if (xenfb_wait_for_frontend_initialised(&xenfb->kbd) < 0) + goto error; + + if (xenfb_bind(&xenfb->fb) < 0) + goto error; + if (xenfb_bind(&xenfb->kbd) < 0) + goto error; + + if (xenfb_xs_scanf1(xsh, xenfb->fb.otherend, "feature-update", + "%d", &val) < 0) + val = 0; + if (!val) { + errno = ENOTSUP; + goto error; + } + xenfb_xs_printf(xsh, xenfb->fb.nodename, "request-update", "1"); + + /* TODO check for permitted ranges */ + fb_page = xenfb->fb.page; + xenfb->pub.depth = fb_page->depth; + xenfb->pub.width = fb_page->width; + xenfb->pub.height = fb_page->height; + /* TODO check for consistency with the above */ + xenfb->fb_len = fb_page->mem_length; + xenfb->pub.row_stride = fb_page->line_length; + + if (xenfb_map_fb(xenfb, domid) < 0) + goto error; + + if (xenfb_switch_state(&xenfb->fb, XenbusStateConnected)) + goto error; + if (xenfb_switch_state(&xenfb->kbd, XenbusStateConnected)) + goto error; + + if (xenfb_wait_for_frontend_connected(&xenfb->kbd) < 0) + goto error; + if (xenfb_xs_scanf1(xsh, xenfb->kbd.otherend, "request-abs-pointer", + "%d", &val) < 0) + val = 0; + xenfb->pub.abs_pointer_wanted = val; + + return 0; + + error: + serrno = errno; + xenfb_detach_dom(xenfb); + xenfb_dev_fatal(&xenfb->fb, serrno, "on fire"); + xenfb_dev_fatal(&xenfb->kbd, serrno, "on fire"); + errno = serrno; + return -1; +} + +static void xenfb_detach_dom(struct xenfb_private *xenfb) +{ + xenfb_unbind(&xenfb->fb); + xenfb_unbind(&xenfb->kbd); + if (xenfb->pub.pixels) { + munmap(xenfb->pub.pixels, xenfb->fb_len); + xenfb->pub.pixels = NULL; + } +} + +static void xenfb_on_fb_event(struct xenfb_private *xenfb) +{ + uint32_t prod, cons; + struct xenfb_page *page = xenfb->fb.page; + + prod = page->out_prod; + if (prod == page->out_cons) + return; + rmb(); /* ensure we see ring contents up to prod */ + for (cons = page->out_cons; cons != prod; cons++) { + union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons); + + switch (event->type) { + case XENFB_TYPE_UPDATE: + if (xenfb->pub.update) + xenfb->pub.update(&xenfb->pub, + event->update.x, event->update.y, + event->update.width, event->update.height); + break; + } + } + mb(); /* ensure we''re done with ring contents */ + page->out_cons = cons; + xc_evtchn_notify(xenfb->evt_xch, xenfb->fb.port); +} + +static void xenfb_on_kbd_event(struct xenfb_private *xenfb) +{ + struct xenkbd_page *page = xenfb->kbd.page; + + /* We don''t understand any keyboard events, so just ignore them. */ + if (page->out_prod == page->out_cons) + return; + page->out_cons = page->out_prod; + xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port); +} + +static void xenfb_on_state_change(struct xenfb_device *dev) +{ + enum xenbus_state state; + + state = xenfb_read_state(dev->xenfb->xsh, dev->otherend); + + switch (state) { + case XenbusStateUnknown: + case XenbusStateInitialising: + case XenbusStateInitWait: + case XenbusStateInitialised: + case XenbusStateConnected: + break; + case XenbusStateClosing: + xenfb_unbind(dev); + xenfb_switch_state(dev, state); + break; + case XenbusStateClosed: + xs_unwatch(dev->xenfb->xsh, dev->otherend, ""); + xenfb_switch_state(dev, state); + } +} + +int xenfb_poll(struct xenfb *xenfb_pub, fd_set *readfds) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + evtchn_port_t port; + unsigned dummy; + char **vec; + + if (FD_ISSET(xc_evtchn_fd(xenfb->evt_xch), readfds)) { + port = xc_evtchn_pending(xenfb->evt_xch); + if (port == -1) + return -1; + + if (port == xenfb->fb.port) + xenfb_on_fb_event(xenfb); + else if (port == xenfb->kbd.port) + xenfb_on_kbd_event(xenfb); + + if (xc_evtchn_unmask(xenfb->evt_xch, port) == -1) + return -1; + } + + if (FD_ISSET(xs_fileno(xenfb->xsh), readfds)) { + vec = xs_read_watch(xenfb->xsh, &dummy); + free(vec); + xenfb_on_state_change(&xenfb->fb); + xenfb_on_state_change(&xenfb->kbd); + } + + return 0; +} + +int xenfb_select_fds(struct xenfb *xenfb_pub, fd_set *readfds) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + int fd1 = xc_evtchn_fd(xenfb->evt_xch); + int fd2 = xs_fileno(xenfb->xsh); + + FD_SET(fd1, readfds); + FD_SET(fd2, readfds); + return fd1 > fd2 ? fd1 + 1 : fd2 + 1; +} + +static int xenfb_kbd_event(struct xenfb_private *xenfb, + union xenkbd_in_event *event) +{ + uint32_t prod; + struct xenkbd_page *page = xenfb->kbd.page; + + if (xenfb->kbd.state != XenbusStateConnected) + return 0; + + prod = page->in_prod; + if (prod - page->in_cons == XENKBD_IN_RING_LEN) { + errno = EAGAIN; + return -1; + } + + mb(); /* ensure ring space available */ + XENKBD_IN_RING_REF(page, prod) = *event; + wmb(); /* ensure ring contents visible */ + page->in_prod = prod + 1; + return xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port); +} + +int xenfb_send_key(struct xenfb *xenfb_pub, bool down, int keycode) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_KEY; + event.key.pressed = down ? 1 : 0; + event.key.keycode = keycode; + + return xenfb_kbd_event(xenfb, &event); +} + +int xenfb_send_motion(struct xenfb *xenfb_pub, int rel_x, int rel_y) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_MOTION; + event.motion.rel_x = rel_x; + event.motion.rel_y = rel_y; + + return xenfb_kbd_event(xenfb, &event); +} + +int xenfb_send_position(struct xenfb *xenfb_pub, int abs_x, int abs_y) +{ + struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub; + union xenkbd_in_event event; + + memset(&event, 0, XENKBD_IN_EVENT_SIZE); + event.type = XENKBD_TYPE_POS; + event.pos.abs_x = abs_x; + event.pos.abs_y = abs_y; + + return xenfb_kbd_event(xenfb, &event); +} diff -r c98a8e2c62d1 tools/xenfb/xenfb.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/xenfb/xenfb.h Thu Nov 23 11:32:28 2006 +0100 @@ -0,0 +1,34 @@ +#ifndef _XENFB_H_ +#define _XENFB_H_ + +#include <stdbool.h> +#include <sys/types.h> + +struct xenfb +{ + void *pixels; + + int row_stride; + int depth; + int width; + int height; + int abs_pointer_wanted; + + void *user_data; + + void (*update)(struct xenfb *xenfb, int x, int y, int width, int height); +}; + +struct xenfb *xenfb_new(void); +void xenfb_delete(struct xenfb *xenfb); + +int xenfb_attach_dom(struct xenfb *xenfb, int domid); + +int xenfb_select_fds(struct xenfb *xenfb, fd_set *readfds); +int xenfb_poll(struct xenfb *xenfb, fd_set *readfds); + +int xenfb_send_key(struct xenfb *xenfb, bool down, int keycode); +int xenfb_send_motion(struct xenfb *xenfb, int rel_x, int rel_y); +int xenfb_send_position(struct xenfb *xenfb, int abs_x, int abs_y); + +#endif _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Markus Armbruster
2006-Nov-24 08:10 UTC
Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Hi Atsushi, Could you give my latest patch a try on IA-64? I didn''t merge your patch literally, so I might have broken it. Cheers, Markus _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Atsushi SAKAI
2006-Nov-24 08:48 UTC
[Xen-ia64-devel] Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Hi, Markus I checked your fixes. It seems fine in source code base. I will run the test next week. Thanks Atsushi SAKAI>Hi Atsushi, > >Could you give my latest patch a try on IA-64? I didn''t merge your >patch literally, so I might have broken it. > >Cheers, > >Markus > >_______________________________________________ >Xen-devel mailing list >Xen-devel@lists.xensource.com >http://lists.xensource.com/xen-devel >_______________________________________________ Xen-ia64-devel mailing list Xen-ia64-devel@lists.xensource.com http://lists.xensource.com/xen-ia64-devel
Atsushi SAKAI
2006-Nov-28 13:04 UTC
[Xen-ia64-devel] Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Hi, Markus This is just a status. I tested on this. It has still in serial console problem. (I think this problem is common issue.) I use xen-ia64-unstable:12523, DomU create works fine. But adding your patch, DomU login console does not appeared. This problem occures even in off the CONFIG_XEN_FRAMEBUFFER=n.>From this environment,I guess this problem is in serial console. Anyway xen-vncfb is running (not defunct). Thanks Atsushi SAKAI>Hi Atsushi, > >Could you give my latest patch a try on IA-64? I didn''t merge your >patch literally, so I might have broken it. > >Cheers, > >Markus >_______________________________________________ Xen-ia64-devel mailing list Xen-ia64-devel@lists.xensource.com http://lists.xensource.com/xen-ia64-devel
Markus Armbruster
2006-Nov-28 13:55 UTC
Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Atsushi SAKAI <sakaia@jp.fujitsu.com> writes:> Hi, Markus > > This is just a status. > I tested on this. > It has still in serial console problem. > (I think this problem is common issue.) > > I use xen-ia64-unstable:12523, DomU create works fine. > But adding your patch, DomU login console does not appeared. > This problem occures even in off the CONFIG_XEN_FRAMEBUFFER=n. > >>From this environment, > I guess this problem is in serial console. > > Anyway xen-vncfb is running (not defunct).I guess by `login console'' you mean getty asking for a user name. Where does it fail to appear? Text console (the one you get with xm console)? Framebuffer window? Does the framebuffer window work otherwise? Do console messages appear there? Does it have a mouse cursor? _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Stephen C. Tweedie
2006-Nov-28 15:36 UTC
Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Hi, On Tue, 2006-11-28 at 22:04 +0900, Atsushi SAKAI wrote:> I use xen-ia64-unstable:12523, DomU create works fine. > But adding your patch, DomU login console does not appeared.I''m using xencons=xvc console=xvc on all my PV FB Xen kernels, both dom0 and domU, and serial/tty console works just fine with that. Does it help for you? Thanks, Stephen _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Atsushi SAKAI
2006-Nov-29 13:08 UTC
[Xen-ia64-devel] Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Hi, Markus At this point, I cannot connect vnc. The serial console did not appear with following messages for DomU side. "Warning: unable to open an initial console" And use gdb attach to xen-vncfb, it is in wait state at main =>xenfb_attach_dom =>xenfb_wait_for_backend_creation =>xenfb_wait_for_state =>xs_read_watch this point in xen-vncfb is initialize phase of vfb. So vfb should not be appeared. Thanks Atsushi SAKAI>Atsushi SAKAI <sakaia@jp.fujitsu.com> writes: > >> Hi, Markus >> >> This is just a status. >> I tested on this. >> It has still in serial console problem. >> (I think this problem is common issue.) >> >> I use xen-ia64-unstable:12523, DomU create works fine. >> But adding your patch, DomU login console does not appeared. >> This problem occures even in off the CONFIG_XEN_FRAMEBUFFER=n. >> >>>From this environment, >> I guess this problem is in serial console. >> >> Anyway xen-vncfb is running (not defunct). > >I guess by `login console'' you mean getty asking for a user name. >Where does it fail to appear? Text console (the one you get with xm >console)? Framebuffer window? > >Does the framebuffer window work otherwise? Do console messages >appear there? Does it have a mouse cursor? >_______________________________________________ Xen-ia64-devel mailing list Xen-ia64-devel@lists.xensource.com http://lists.xensource.com/xen-ia64-devel
Atsushi SAKAI
2006-Dec-01 12:35 UTC
[Xen-ia64-devel] Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Hi, Markus Finally Booting DomU is succesful with xenfb driver. But xen-vncfb is waiting in prhread_cond_wait. main xenfb_attach_dom xenfb_wait_for_backend_creation xenfb_wait_for_state xs_read_watch pthread_cond_wait And By doing xensore-ls, the keyword "vfb" is not appleared.>From this consideration, I guess xen-vncfb has a problem.Is there any suggestion? c.f. During the survey, I found large difference your xenfn patch and FC6 xenfb code. Thanks Atsushi SAKAI>Atsushi SAKAI <sakaia@jp.fujitsu.com> writes: > >> Hi, Markus >> >> This is just a status. >> I tested on this. >> It has still in serial console problem. >> (I think this problem is common issue.) >> >> I use xen-ia64-unstable:12523, DomU create works fine. >> But adding your patch, DomU login console does not appeared. >> This problem occures even in off the CONFIG_XEN_FRAMEBUFFER=n. >> >>>From this environment, >> I guess this problem is in serial console. >> >> Anyway xen-vncfb is running (not defunct). > >I guess by `login console'' you mean getty asking for a user name. >Where does it fail to appear? Text console (the one you get with xm >console)? Framebuffer window? > >Does the framebuffer window work otherwise? Do console messages >appear there? Does it have a mouse cursor? >_______________________________________________ Xen-ia64-devel mailing list Xen-ia64-devel@lists.xensource.com http://lists.xensource.com/xen-ia64-devel
Markus Armbruster
2006-Dec-01 18:03 UTC
Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Atsushi SAKAI <sakaia@jp.fujitsu.com> writes:> Hi, Markus > > Finally Booting DomU is succesful with xenfb driver. > > But xen-vncfb is waiting in prhread_cond_wait. > main > xenfb_attach_dom > xenfb_wait_for_backend_creation > xenfb_wait_for_state > xs_read_watch > pthread_cond_wait > > And By doing xensore-ls, > the keyword "vfb" is not appleared. > >>From this consideration, I guess xen-vncfb has a problem.This waits for xend to create the xenstore directory.> Is there any suggestion?Do you have vfb and vkbd configured in your /etc/xen/DOMNAME file? Could you post your config file here?> c.f. > During the survey, > I found large difference your xenfn patch and FC6 xenfb code.Yes, FC-6 has an old version of the patch. Really old.> > Thanks > Atsushi SAKAI_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Atsushi SAKAI
2006-Dec-04 08:46 UTC
[Xen-ia64-devel] Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Hi, Markus Thank you for your suggestion. My config is attached as follows(see below) Anyway, I cannot find vfb and vkbd definition in RHEL5 package. Please give me appropriate config. kernel = "/boot/efi/efi/xen/vmlinuz-2.6.16.29-xen-sakaia" ramdisk = "/boot/efi/efi/xen/initrd-2.6.16.29-xenU.img" memory = 1024 name = "rhel4-sakaia0" disk = [ ''file:/xen/image/fc6t3.img,hda1,w'' ] vif = [ '''' ] root = "/dev/hda1 ro" extra = "nomca ide0=noprobe ide1=noprobe ide2=noprobe ide3=noprobe xencons=xvc" vnc = 1 vncpasswd='' '' Thanks Atsushi SAKAI>Atsushi SAKAI <sakaia@jp.fujitsu.com> writes: > >> Hi, Markus >> >> Finally Booting DomU is succesful with xenfb driver. >> >> But xen-vncfb is waiting in prhread_cond_wait. >> main >> xenfb_attach_dom >> xenfb_wait_for_backend_creation >> xenfb_wait_for_state >> xs_read_watch >> pthread_cond_wait >> >> And By doing xensore-ls, >> the keyword "vfb" is not appleared. >> >>>From this consideration, I guess xen-vncfb has a problem. > >This waits for xend to create the xenstore directory. > >> Is there any suggestion? > >Do you have vfb and vkbd configured in your /etc/xen/DOMNAME file? >Could you post your config file here? > >> c.f. >> During the survey, >> I found large difference your xenfn patch and FC6 xenfb code. > >Yes, FC-6 has an old version of the patch. Really old. > >> >> Thanks >> Atsushi SAKAI_______________________________________________ Xen-ia64-devel mailing list Xen-ia64-devel@lists.xensource.com http://lists.xensource.com/xen-ia64-devel
Markus Armbruster
2006-Dec-04 19:44 UTC
Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Atsushi SAKAI <sakaia@jp.fujitsu.com> writes:> Hi, Markus > > Thank you for your suggestion. > My config is attached as follows(see below) > Anyway, I cannot find vfb and vkbd definition in RHEL5 package. > Please give me appropriate config. > > > > kernel = "/boot/efi/efi/xen/vmlinuz-2.6.16.29-xen-sakaia" > ramdisk = "/boot/efi/efi/xen/initrd-2.6.16.29-xenU.img" > memory = 1024 > name = "rhel4-sakaia0" > disk = [ ''file:/xen/image/fc6t3.img,hda1,w'' ] > vif = [ '''' ] > root = "/dev/hda1 ro" > extra = "nomca ide0=noprobe ide1=noprobe ide2=noprobe ide3=noprobe xencons=xvc" > vnc = 1 > vncpasswd='' ''You don''t have the devices configured. For the latest version in xen-unstable.hg, try replacing vnc = 1 with something like vfb = [ ''type=vnc'' ] _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Atsushi SAKAI
2006-Dec-05 12:01 UTC
[Xen-ia64-devel] Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Hi, Markus Thank you for your suggestion. Would you please post sample vfb config file? Or any document for latest version. Anyway, your vfb patch makes large changes compared to previous post for example xencons in config is erased in latest version. Other example, your following suggestion in config file seems to be vfbif = xx not vfb = xx Thanks Atsushi SAKAI>Atsushi SAKAI <sakaia@jp.fujitsu.com> writes: > >> Hi, Markus >> >> Thank you for your suggestion. >> My config is attached as follows(see below) >> Anyway, I cannot find vfb and vkbd definition in RHEL5 package. >> Please give me appropriate config. >> >> >> >> kernel = "/boot/efi/efi/xen/vmlinuz-2.6.16.29-xen-sakaia" >> ramdisk = "/boot/efi/efi/xen/initrd-2.6.16.29-xenU.img" >> memory = 1024 >> name = "rhel4-sakaia0" >> disk = [ ''file:/xen/image/fc6t3.img,hda1,w'' ] >> vif = [ '''' ] >> root = "/dev/hda1 ro" >> extra = "nomca ide0=noprobe ide1=noprobe ide2=noprobe ide3=noprobe xencons=xvc" >> vnc = 1 >> vncpasswd='' '' > >You don''t have the devices configured. > >For the latest version in xen-unstable.hg, try replacing > >vnc = 1 > >with something like > >vfb = [ ''type=vnc'' ] >_______________________________________________ Xen-ia64-devel mailing list Xen-ia64-devel@lists.xensource.com http://lists.xensource.com/xen-ia64-devel
Markus Armbruster
2006-Dec-05 17:32 UTC
Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Atsushi SAKAI <sakaia@jp.fujitsu.com> writes:> Hi, Markus > > Thank you for your suggestion. > Would you please post sample vfb config file? > Or any document for latest version. > > Anyway, your vfb patch makes large changes compared to previous post > for example xencons in config is erased in latest version. > > Other example, your following suggestion in config file > seems to be vfbif = xx not vfb = xx > > Thanks > Atsushi SAKAIname = "parafat" memory = "384" disk = [ ''file:/spare/parafat,xvda,w'', ] vif = [ ''mac=00:16:3e:34:a2:c0, bridge=xenbr0'', ] vfb = [ ''type=sdl'' ] uuid = "535c7fc5-3de0-370c-05ea-e6b91383ffa1" bootloader="/usr/bin/pygrub" vcpus=1 on_reboot = ''restart'' on_crash = ''restart'' _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Atsushi SAKAI
2006-Dec-07 00:55 UTC
[Xen-ia64-devel] Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Hi, Markus Thank you I confirmed to run on IA64. Thanks Atsushi SAKAI>Atsushi SAKAI <sakaia@jp.fujitsu.com> writes: > >> Hi, Markus >> >> Thank you for your suggestion. >> Would you please post sample vfb config file? >> Or any document for latest version. >> >> Anyway, your vfb patch makes large changes compared to previous post >> for example xencons in config is erased in latest version. >> >> Other example, your following suggestion in config file >> seems to be vfbif = xx not vfb = xx >> >> Thanks >> Atsushi SAKAI > >name = "parafat" >memory = "384" >disk = [ ''file:/spare/parafat,xvda,w'', ] >vif = [ ''mac=00:16:3e:34:a2:c0, bridge=xenbr0'', ] >vfb = [ ''type=sdl'' ] >uuid = "535c7fc5-3de0-370c-05ea-e6b91383ffa1" >bootloader="/usr/bin/pygrub" >vcpus=1 >on_reboot = ''restart'' >on_crash = ''restart'' >_______________________________________________ Xen-ia64-devel mailing list Xen-ia64-devel@lists.xensource.com http://lists.xensource.com/xen-ia64-devel
Markus Armbruster
2006-Dec-07 07:58 UTC
Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Atsushi SAKAI <sakaia@jp.fujitsu.com> writes:> Hi, Markus > > Thank you > I confirmed to run on IA64. > > Thanks > Atsushi SAKAICongrats! And thanks for the info. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Atsushi SAKAI
2006-Dec-12 11:54 UTC
[Xen-ia64-devel] Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Hi, Markus I am checking VFB with SMP with xen-ia64-unstable cset 12886. But It seems strange behavior. (call trace appeared from DomU xm console output) Did you test it on SMP DomU? Of course UP DomU seems fine at this moment. Thanks Atsushi SAKAI _______________________________________________ Xen-ia64-devel mailing list Xen-ia64-devel@lists.xensource.com http://lists.xensource.com/xen-ia64-devel
Markus Armbruster
2006-Dec-12 12:23 UTC
Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Atsushi SAKAI <sakaia@jp.fujitsu.com> writes:> Hi, Markus > > I am checking VFB with SMP > with xen-ia64-unstable cset 12886. > > But It seems strange behavior. > (call trace appeared from DomU xm console output) > Did you test it on SMP DomU? > Of course UP DomU seems fine at this moment. > > Thanks > Atsushi SAKAIPlease provide details, including xm console output. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Atsushi SAKAI
2006-Dec-13 02:18 UTC
Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Hi, Markus Thank you I will sending a vcpus=2 and vcpus=1 cases. Thanks Atsushi SAKAI> >Please provide details, including xm console output. >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Atsushi SAKAI
2006-Dec-14 10:58 UTC
[Xen-ia64-devel] Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Hi, Markus About SMP DomU VFB call trace problem. I searched the code and found the code in smp_call_function line337@linux/arch/ia64/kernel/smp.c WARN_ON(irqs_disabled()); The call trace problem caused by spin_lock_irqsave() in xenfb_update_screen@linux/drivers/xen/fbfront/xenfb.c Why you need to use spin_lock_irqsave in xenfb_update()? I understand the locking but why irqsave is required at this point? Thanks Atsushi SAKAI>Atsushi SAKAI <sakaia@jp.fujitsu.com> writes: > >> Hi, Markus >> >> I am checking VFB with SMP >> with xen-ia64-unstable cset 12886. >> >> But It seems strange behavior. >> (call trace appeared from DomU xm console output) >> Did you test it on SMP DomU? >> Of course UP DomU seems fine at this moment. >> >> Thanks >> Atsushi SAKAI > >Please provide details, including xm console output. >_______________________________________________ Xen-ia64-devel mailing list Xen-ia64-devel@lists.xensource.com http://lists.xensource.com/xen-ia64-devel
Keir Fraser
2006-Dec-14 11:30 UTC
Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
On 14/12/06 10:58, "Atsushi SAKAI" <sakaia@jp.fujitsu.com> wrote:> I searched the code and found > the code in smp_call_function line337@linux/arch/ia64/kernel/smp.c > WARN_ON(irqs_disabled()); > > The call trace problem caused by spin_lock_irqsave() > in xenfb_update_screen@linux/drivers/xen/fbfront/xenfb.c > > Why you need to use spin_lock_irqsave in xenfb_update()? > I understand the locking but why irqsave is required at this point?The lock is used in fb_ops, vm_ops and kthread contexts. None of these are executed in IRQ context so irqsave is not needed. I''ll remove it. I''m not sure whether you can call zap_page_range() even with a spinlock held though... -- Keir _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Markus Armbruster
2006-Dec-14 12:37 UTC
Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
Keir Fraser <keir@xensource.com> writes:> On 14/12/06 10:58, "Atsushi SAKAI" <sakaia@jp.fujitsu.com> wrote: > >> I searched the code and found >> the code in smp_call_function line337@linux/arch/ia64/kernel/smp.c >> WARN_ON(irqs_disabled()); >> >> The call trace problem caused by spin_lock_irqsave() >> in xenfb_update_screen@linux/drivers/xen/fbfront/xenfb.cThanks for debugging this, Atsushi!>> Why you need to use spin_lock_irqsave in xenfb_update()? >> I understand the locking but why irqsave is required at this point? > > The lock is used in fb_ops, vm_ops and kthread contexts. None of these are > executed in IRQ context so irqsave is not needed. I''ll remove it. I''m not > sure whether you can call zap_page_range() even with a spinlock held > though... > > -- KeirYou can''t. This was spotted just yesterday in our internal review. I''m working on a fix. If you could hold the removal of irqsave for a bit, that would avoid merge conflicts. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Keir Fraser
2006-Dec-14 13:30 UTC
Re: [PATCH] Re: [Xen-devel] Re: [PATCH 2/2] PV framebuffer
On 14/12/06 12:37, "Markus Armbruster" <armbru@redhat.com> wrote:>> The lock is used in fb_ops, vm_ops and kthread contexts. None of these are >> executed in IRQ context so irqsave is not needed. I''ll remove it. I''m not >> sure whether you can call zap_page_range() even with a spinlock held >> though... >> >> -- Keir > > You can''t. This was spotted just yesterday in our internal review. > I''m working on a fix. If you could hold the removal of irqsave for a > bit, that would avoid merge conflicts.Too late I''m afraid. If you come up with a reasonable fix before 3.0.4 goes out it''d be nice to slip a fix in if it''s not too big. -- Keir _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Markus Armbruster
2006-Dec-15 16:38 UTC
[Xen-devel] [PATCH][PVFB][LINUX] Fix possible sleep while holding spinlock
xenfb_update_screen() calls zap_page_range() while holding spinlock mm_lock. Big no-no. Changeset 13018:c98ca86138a7422cdf9b15d87c95619b7277bb6a merely sweeps the bug under the carpet: it silences zap_page_range()''s cries for help by keeping interrupts enabled. That doesn''t fix the bug, and it''s also wrong: if a critical region gets interrupted, and the interrupt printk()s, xenfb_refresh() gets executed and promptly deadlocks. This patch fixes the locking, but leaves open a race between xenfb_update_screen() and do_no_page(). See the source code for a detailed explanation of how it works, and where it fails. Signed-off-by: Markus Armbruster <armbru@redhat.com> diff -r c2fe2635e68b linux-2.6-xen-sparse/drivers/xen/fbfront/xenfb.c --- a/linux-2.6-xen-sparse/drivers/xen/fbfront/xenfb.c Fri Dec 15 09:52:19 2006 +0000 +++ b/linux-2.6-xen-sparse/drivers/xen/fbfront/xenfb.c Fri Dec 15 15:52:39 2006 +0100 @@ -49,8 +51,9 @@ struct xenfb_info struct timer_list refresh; int dirty; int x1, y1, x2, y2; /* dirty rectangle, - protected by mm_lock */ - spinlock_t mm_lock; + protected by dirty_lock */ + spinlock_t dirty_lock; + struct mutex mm_lock; int nr_pages; struct page **pages; struct list_head mappings; /* protected by mm_lock */ @@ -63,6 +66,70 @@ struct xenfb_info struct xenbus_device *xbdev; }; + +/* + * How the locks work together + * + * There are two locks: spinlock dirty_lock protecting the dirty + * rectangle, and mutex mm_lock protecting mappings. + * + * The problem is that dirty rectangle and mappings aren''t + * independent: the dirty rectangle must cover all faulted pages in + * mappings. We need to prove that our locking maintains this + * invariant. + * + * There are several kinds of critical regions: + * + * 1. Holding only dirty_lock: xenfb_refresh(). May run in + * interrupts. Extends the dirty rectangle. Trivially preserves + * invariant. + * + * 2. Holding only mm_lock: xenfb_mmap() and xenfb_vm_close(). Touch + * only mappings. The former creates unfaulted pages. Preserves + * invariant. The latter removes pages. Preserves invariant. + * + * 3. Holding both locks: xenfb_vm_nopage(). Extends the dirty + * rectangle and updates mappings consistently. Preserves + * invariant. + * + * 4. The ugliest one: xenfb_update_screen(). Clear the dirty + * rectangle and update mappings consistently. + * + * We can''t simply hold both locks, because zap_page_range() cannot + * be called with a spinlock held. + * + * Therefore, we first clear the dirty rectangle with both locks + * held. Then we unlock dirty_lock and update the mappings. + * Critical regions that hold only dirty_lock may interfere with + * that. This can only be region 1: xenfb_refresh(). But that + * just extends the dirty rectangle, which can''t harm the + * invariant. + * + * But FIXME: the invariant is too weak. It misses that the fault + * record in mappings must be consistent with the mapping of pages in + * the associated address space! do_no_page() updates the PTE after + * xenfb_vm_nopage() returns, i.e. outside the critical region. This + * allows the following race: + * + * X writes to some address in the Xen frame buffer + * Fault - call do_no_page() + * call xenfb_vm_nopage() + * grab mm_lock + * map->faults++; + * release mm_lock + * return back to do_no_page() + * (preempted, or SMP) + * Xen worker thread runs. + * grab mm_lock + * look at mappings + * find this mapping, zaps its pages (but page not in pte yet) + * clear map->faults + * releases mm_lock + * (back to X process) + * put page in X''s pte + * + * Oh well, we wont be updating the writes to this page anytime soon. + */ static int xenfb_fps = 20; static unsigned long xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8; @@ -105,6 +172,7 @@ static int xenfb_queue_full(struct xenfb static void xenfb_update_screen(struct xenfb_info *info) { + unsigned long flags; int y1, y2, x1, x2; struct xenfb_mapping *map; @@ -113,14 +181,16 @@ static void xenfb_update_screen(struct x if (xenfb_queue_full(info)) return; - spin_lock(&info->mm_lock); - + mutex_lock(&info->mm_lock); + + spin_lock_irqsave(&info->dirty_lock, flags); y1 = info->y1; y2 = info->y2; x1 = info->x1; x2 = info->x2; info->x1 = info->y1 = INT_MAX; info->x2 = info->y2 = 0; + spin_unlock_irqrestore(&info->dirty_lock, flags); list_for_each_entry(map, &info->mappings, link) { if (!map->faults) @@ -130,7 +200,7 @@ static void xenfb_update_screen(struct x map->faults = 0; } - spin_unlock(&info->mm_lock); + mutex_unlock(&info->mm_lock); xenfb_do_update(info, x1, y1, x2 - x1, y2 - y1); } @@ -213,9 +283,11 @@ static void xenfb_refresh(struct xenfb_i static void xenfb_refresh(struct xenfb_info *info, int x1, int y1, int w, int h) { - spin_lock(&info->mm_lock); + unsigned long flags; + + spin_lock_irqsave(&info->dirty_lock, flags); __xenfb_refresh(info, x1, y1, w, h); - spin_unlock(&info->mm_lock); + spin_unlock_irqrestore(&info->dirty_lock, flags); } static void xenfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) @@ -253,12 +325,12 @@ static void xenfb_vm_close(struct vm_are struct xenfb_mapping *map = vma->vm_private_data; struct xenfb_info *info = map->info; - spin_lock(&info->mm_lock); + mutex_lock(&info->mm_lock); if (atomic_dec_and_test(&map->map_refs)) { list_del(&map->link); kfree(map); } - spin_unlock(&info->mm_lock); + mutex_unlock(&info->mm_lock); } static struct page *xenfb_vm_nopage(struct vm_area_struct *vma, @@ -267,13 +339,15 @@ static struct page *xenfb_vm_nopage(stru struct xenfb_mapping *map = vma->vm_private_data; struct xenfb_info *info = map->info; int pgnr = (vaddr - vma->vm_start) >> PAGE_SHIFT; + unsigned long flags; struct page *page; int y1, y2; if (pgnr >= info->nr_pages) return NOPAGE_SIGBUS; - spin_lock(&info->mm_lock); + mutex_lock(&info->mm_lock); + spin_lock_irqsave(&info->dirty_lock, flags); page = info->pages[pgnr]; get_page(page); map->faults++; @@ -283,7 +357,8 @@ static struct page *xenfb_vm_nopage(stru if (y2 > info->fb_info->var.yres) y2 = info->fb_info->var.yres; __xenfb_refresh(info, 0, y1, info->fb_info->var.xres, y2 - y1); - spin_unlock(&info->mm_lock); + spin_unlock_irqrestore(&info->dirty_lock, flags); + mutex_unlock(&info->mm_lock); if (type) *type = VM_FAULT_MINOR; @@ -323,9 +398,9 @@ static int xenfb_mmap(struct fb_info *fb map->info = info; atomic_set(&map->map_refs, 1); - spin_lock(&info->mm_lock); + mutex_lock(&info->mm_lock); list_add(&map->link, &info->mappings); - spin_unlock(&info->mm_lock); + mutex_unlock(&info->mm_lock); vma->vm_ops = &xenfb_vm_ops; vma->vm_flags |= (VM_DONTEXPAND | VM_RESERVED); @@ -382,7 +459,8 @@ static int __devinit xenfb_probe(struct info->xbdev = dev; info->irq = -1; info->x1 = info->y1 = INT_MAX; - spin_lock_init(&info->mm_lock); + spin_lock_init(&info->dirty_lock); + mutex_init(&info->mm_lock); init_waitqueue_head(&info->wq); init_timer(&info->refresh); info->refresh.function = xenfb_timer; _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Keir Fraser
2006-Dec-15 18:06 UTC
Re: [Xen-devel] [PATCH][PVFB][LINUX] Fix possible sleep while holding spinlock
On 15/12/06 16:38, "Markus Armbruster" <armbru@redhat.com> wrote:> This patch fixes the locking, but leaves open a race between > xenfb_update_screen() and do_no_page(). See the source code for a > detailed explanation of how it works, and where it fails.That''s a bit of a shame. Is there any way we could use do_file_page() and have a populate hook (which gets to install the PTE itself)? Or are there lots of assumptions about VMAs that would take this path, assert VM_NONLINEAR, etc? -- Keir _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Markus Armbruster
2006-Dec-15 18:35 UTC
Re: [Xen-devel] [PATCH][PVFB][LINUX] Fix possible sleep while holding spinlock
Keir Fraser <keir@xensource.com> writes:> On 15/12/06 16:38, "Markus Armbruster" <armbru@redhat.com> wrote: > >> This patch fixes the locking, but leaves open a race between >> xenfb_update_screen() and do_no_page(). See the source code for a >> detailed explanation of how it works, and where it fails. > > That''s a bit of a shame. Is there any way we could use do_file_page() and > have a populate hook (which gets to install the PTE itself)? Or are there > lots of assumptions about VMAs that would take this path, assert > VM_NONLINEAR, etc? > > -- KeirMy colleages and I suspect we need a deep think about the problem. But right now we can''t spare the time for that. _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Atsushi SAKAI
2006-Dec-19 02:52 UTC
[Xen-ia64-devel] Re: [Xen-devel] [PATCH][PVFB][LINUX] Fix possible sleep while holding spinlock
Hi, Markus I tested on SMP DomU at xen-ia64-unstable 13083. It seems works fine. Thanks Atsushi SAKAI>xenfb_update_screen() calls zap_page_range() while holding spinlock >mm_lock. Big no-no. > >Changeset 13018:c98ca86138a7422cdf9b15d87c95619b7277bb6a merely sweeps >the bug under the carpet: it silences zap_page_range()''s cries for >help by keeping interrupts enabled. That doesn''t fix the bug, and >it''s also wrong: if a critical region gets interrupted, and the >interrupt printk()s, xenfb_refresh() gets executed and promptly >deadlocks. > >This patch fixes the locking, but leaves open a race between >xenfb_update_screen() and do_no_page(). See the source code for a >detailed explanation of how it works, and where it fails. > >Signed-off-by: Markus Armbruster <armbru@redhat.com> >_______________________________________________ Xen-ia64-devel mailing list Xen-ia64-devel@lists.xensource.com http://lists.xensource.com/xen-ia64-devel
Markus Armbruster
2006-Dec-19 07:49 UTC
Re: [Xen-devel] [PATCH][PVFB][LINUX] Fix possible sleep while holding spinlock
Atsushi SAKAI <sakaia@jp.fujitsu.com> writes:> Hi, Markus > > I tested on SMP DomU at xen-ia64-unstable 13083. > It seems works fine. > > Thanks > Atsushi SAKAIGood news, thanks a lot! _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Atsushi SAKAI
2006-Dec-19 08:17 UTC
Re: [Xen-devel] [PATCH][PVFB][LINUX] Fix possible sleep while holding spinlock
Hi, Markus I have a request of config. Please add vfb config example to files (xmexample1/2/3) which is located in tools/example/ Thanks Atsushi SAKAI>Atsushi SAKAI <sakaia@jp.fujitsu.com> writes: > >> Hi, Markus >> >> I tested on SMP DomU at xen-ia64-unstable 13083. >> It seems works fine. >> >> Thanks >> Atsushi SAKAI > >Good news, thanks a lot! >_______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel
Markus Armbruster
2007-Jan-10 08:50 UTC
Re: [Xen-devel] [PATCH][PVFB][LINUX] Fix possible sleep while holding spinlock
Atsushi SAKAI <sakaia@jp.fujitsu.com> writes:> Hi, Markus > > I have a request of config. > Please add vfb config example to files (xmexample1/2/3) > which is located in tools/example/ > > Thanks > Atsushi SAKAIQuick shot from the hip, hope this helps. diff -r 9865145e53eb tools/examples/xmexample1 --- a/tools/examples/xmexample1 Fri Jan 05 18:17:36 2007 +0000 +++ b/tools/examples/xmexample1 Wed Jan 10 09:43:30 2007 +0100 @@ -64,6 +64,40 @@ vif = [ '''' ] # and MODE is r for read-only, w for read-write. disk = [ ''phy:hda1,hda1,w'' ] + +#---------------------------------------------------------------------------- +# Define frame buffer device. +# +# By default, no frame buffer device is configured. +# +# To create one using the SDL backend and sensible defaults: +# +# vfb = [ ''type=sdl'' ] +# +# This uses environment variables XAUTHORITY and DISPLAY. You +# can override that: +# +# vfb = [ ''type=sdl,xauthority=/home/bozo/.Xauthority,display=:1'' ] +# +# To create one using the VNC backend and sensible defaults: +# +# vfb = [ ''type=vnc'' ] +# +# The backend listens on 127.0.0.1 port 5900+N by default, where N is +# the domain ID. You can override both address and N: +# +# vfb = [ ''type=vnc,vnclisten=127.0.0.1,vncdisplay=1'' ] +# +# Or you can bind the first unused port above 5900: +# +# vfb = [ ''type=vnc,vnclisten=0.0.0.0,vnunused=1'' ] +# +# You can override the password: +# +# vfb = [ ''type=vnc,vncpasswd=MYPASSWD'' ] +# +# Empty password disables authentication. Defaults to the vncpasswd +# configured in xend-config.sxp. #---------------------------------------------------------------------------- # Define to which TPM instance the user domain should communicate. diff -r 9865145e53eb tools/examples/xmexample2 --- a/tools/examples/xmexample2 Fri Jan 05 18:17:36 2007 +0000 +++ b/tools/examples/xmexample2 Wed Jan 10 09:48:44 2007 +0100 @@ -100,6 +100,40 @@ vif = [ '''' ] # All domains get sda6 read-only (to use for /usr, see below). disk = [ ''phy:sda%d,sda1,w'' % (7+vmid), ''phy:sda6,sda6,r'' ] + +#---------------------------------------------------------------------------- +# Define frame buffer device. +# +# By default, no frame buffer device is configured. +# +# To create one using the SDL backend and sensible defaults: +# +# vfb = [ ''type=sdl'' ] +# +# This uses environment variables XAUTHORITY and DISPLAY. You +# can override that: +# +# vfb = [ ''type=sdl,xauthority=/home/bozo/.Xauthority,display=:1'' ] +# +# To create one using the VNC backend and sensible defaults: +# +# vfb = [ ''type=vnc'' ] +# +# The backend listens on 127.0.0.1 port 5900+N by default, where N is +# the domain ID. You can override both address and N: +# +# vfb = [ ''type=vnc,vnclisten=127.0.0.1,vncdisplay=%d'' % vmid ] +# +# Or you can bind the first unused port above 5900: +# +# vfb = [ ''type=vnc,vnclisten=0.0.0.0,vnunused=1'' ] +# +# You can override the password: +# +# vfb = [ ''type=vnc,vncpasswd=MYPASSWD'' ] +# +# Empty password disables authentication. Defaults to the vncpasswd +# configured in xend-config.sxp. #---------------------------------------------------------------------------- # Define to which TPM instance the user domain should communicate. diff -r 9865145e53eb tools/examples/xmexample3 --- a/tools/examples/xmexample3 Fri Jan 05 18:17:36 2007 +0000 +++ b/tools/examples/xmexample3 Wed Jan 10 09:49:04 2007 +0100 @@ -85,6 +85,40 @@ vif = [ ''ip=192.168.%d.1/24'' % (vmid)] # to all domains as sda1. # All domains get sda6 read-only (to use for /usr, see below). disk = [ ''phy:hda%d,hda1,w'' % (vmid)] + +#---------------------------------------------------------------------------- +# Define frame buffer device. +# +# By default, no frame buffer device is configured. +# +# To create one using the SDL backend and sensible defaults: +# +# vfb = [ ''type=sdl'' ] +# +# This uses environment variables XAUTHORITY and DISPLAY. You +# can override that: +# +# vfb = [ ''type=sdl,xauthority=/home/bozo/.Xauthority,display=:1'' ] +# +# To create one using the VNC backend and sensible defaults: +# +# vfb = [ ''type=vnc'' ] +# +# The backend listens on 127.0.0.1 port 5900+N by default, where N is +# the domain ID. You can override both address and N: +# +# vfb = [ ''type=vnc,vnclisten=127.0.0.1,vncdisplay=%d'' % vmid ] +# +# Or you can bind the first unused port above 5900: +# +# vfb = [ ''type=vnc,vnclisten=0.0.0.0,vnunused=1'' ] +# +# You can override the password: +# +# vfb = [ ''type=vnc,vncpasswd=MYPASSWD'' ] +# +# Empty password disables authentication. Defaults to the vncpasswd +# configured in xend-config.sxp. #---------------------------------------------------------------------------- # Define to which TPM instance the user domain should communicate. diff -r 9865145e53eb tools/python/xen/xm/create.py --- a/tools/python/xen/xm/create.py Fri Jan 05 18:17:36 2007 +0000 +++ b/tools/python/xen/xm/create.py Wed Jan 10 09:46:02 2007 +0100 @@ -291,7 +291,8 @@ gopts.var(''vfb'', val="type={vnc,sdl},vnc For type=vnc, connect an external vncviewer. The server will listen on ADDR (default 127.0.0.1) on port N+5900. N defaults to the domain id. If vncunused=1, the server will try to find an arbitrary - unused port above 5900. + unused port above 5900. vncpasswd overrides the XenD configured + default password. For type=sdl, a viewer will be started automatically using the given DISPLAY and XAUTHORITY, which default to the current user''s ones.""") _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel