Commit e7151f83e2316a7f2618b07793a71136165a760e

Authored by aliguori
1 parent e57dd20b

xen: add framebuffer backend driver (Gerd Hoffmann)

This patch adds a frsamebuffer (and kbd+mouse) backend driver.  It
it based on current xen-unstable code.  It has been changed to make
use of the common backend driver code.  It also has been changed to
compile with xen headers older than release 3.3

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7222 c046a42c-6fe2-441c-8c8c-71466251a162
Makefile.target
... ... @@ -562,7 +562,7 @@ endif
562 562  
563 563 # xen backend driver support
564 564 XEN_OBJS := xen_machine_pv.o xen_backend.o
565   -XEN_OBJS += xen_console.o
  565 +XEN_OBJS += xen_console.o xenfb.o
566 566 ifeq ($(CONFIG_XEN), yes)
567 567 OBJS += $(XEN_OBJS)
568 568 LIBS += $(XEN_LIBS)
... ...
hw/xen_backend.h
... ... @@ -85,5 +85,9 @@ void xen_be_printf(struct XenDevice *xendev, int msg_level, const char *fmt, ...
85 85  
86 86 /* actual backend drivers */
87 87 extern struct XenDevOps xen_console_ops; /* xen_console.c */
  88 +extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */
  89 +extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */
  90 +
  91 +void xen_init_display(int domid);
88 92  
89 93 #endif /* QEMU_HW_XEN_BACKEND_H */
... ...
hw/xen_machine_pv.c
... ... @@ -57,6 +57,11 @@ static void xen_init_pv(ram_addr_t ram_size, int vga_ram_size,
57 57 exit(1);
58 58 }
59 59 xen_be_register("console", &xen_console_ops);
  60 + xen_be_register("vkbd", &xen_kbdmouse_ops);
  61 + xen_be_register("vfb", &xen_framebuffer_ops);
  62 +
  63 + /* setup framebuffer */
  64 + xen_init_display(xen_domid);
60 65 }
61 66  
62 67 QEMUMachine xenpv_machine = {
... ...
hw/xenfb.c 0 → 100644
  1 +/*
  2 + * xen paravirt framebuffer backend
  3 + *
  4 + * Copyright IBM, Corp. 2005-2006
  5 + * Copyright Red Hat, Inc. 2006-2008
  6 + *
  7 + * Authors:
  8 + * Anthony Liguori <aliguori@us.ibm.com>,
  9 + * Markus Armbruster <armbru@redhat.com>,
  10 + * Daniel P. Berrange <berrange@redhat.com>,
  11 + * Pat Campbell <plc@novell.com>,
  12 + * Gerd Hoffmann <kraxel@redhat.com>
  13 + *
  14 + * This program is free software; you can redistribute it and/or modify
  15 + * it under the terms of the GNU General Public License as published by
  16 + * the Free Software Foundation; under version 2 of the License.
  17 + *
  18 + * This program is distributed in the hope that it will be useful,
  19 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21 + * GNU General Public License for more details.
  22 + *
  23 + * You should have received a copy of the GNU General Public License along
  24 + * with this program; if not, write to the Free Software Foundation, Inc.,
  25 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  26 + */
  27 +
  28 +#include <stdarg.h>
  29 +#include <stdlib.h>
  30 +#include <sys/types.h>
  31 +#include <fcntl.h>
  32 +#include <unistd.h>
  33 +#include <stdbool.h>
  34 +#include <sys/mman.h>
  35 +#include <errno.h>
  36 +#include <stdio.h>
  37 +#include <string.h>
  38 +#include <time.h>
  39 +
  40 +#include <xs.h>
  41 +#include <xenctrl.h>
  42 +#include <xen/event_channel.h>
  43 +#include <xen/io/xenbus.h>
  44 +#include <xen/io/fbif.h>
  45 +#include <xen/io/kbdif.h>
  46 +#include <xen/io/protocols.h>
  47 +
  48 +#include "hw.h"
  49 +#include "sysemu.h"
  50 +#include "console.h"
  51 +#include "qemu-char.h"
  52 +#include "xen_backend.h"
  53 +
  54 +#ifndef BTN_LEFT
  55 +#define BTN_LEFT 0x110 /* from <linux/input.h> */
  56 +#endif
  57 +
  58 +/* -------------------------------------------------------------------- */
  59 +
  60 +struct common {
  61 + struct XenDevice xendev; /* must be first */
  62 + void *page;
  63 + DisplayState *ds;
  64 +};
  65 +
  66 +struct XenInput {
  67 + struct common c;
  68 + int abs_pointer_wanted; /* Whether guest supports absolute pointer */
  69 + int button_state; /* Last seen pointer button state */
  70 + int extended;
  71 + QEMUPutMouseEntry *qmouse;
  72 +};
  73 +
  74 +#define UP_QUEUE 8
  75 +
  76 +struct XenFB {
  77 + struct common c;
  78 + size_t fb_len;
  79 + int row_stride;
  80 + int depth;
  81 + int width;
  82 + int height;
  83 + int offset;
  84 + void *pixels;
  85 + int fbpages;
  86 + int feature_update;
  87 + int refresh_period;
  88 + int bug_trigger;
  89 + int have_console;
  90 + int do_resize;
  91 +
  92 + struct {
  93 + int x,y,w,h;
  94 + } up_rects[UP_QUEUE];
  95 + int up_count;
  96 + int up_fullscreen;
  97 +};
  98 +
  99 +/* -------------------------------------------------------------------- */
  100 +
  101 +static int common_bind(struct common *c)
  102 +{
  103 + int mfn;
  104 +
  105 + if (xenstore_read_fe_int(&c->xendev, "page-ref", &mfn) == -1)
  106 + return -1;
  107 + if (xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port) == -1)
  108 + return -1;
  109 +
  110 + c->page = xc_map_foreign_range(xen_xc, c->xendev.dom,
  111 + XC_PAGE_SIZE,
  112 + PROT_READ | PROT_WRITE, mfn);
  113 + if (c->page == NULL)
  114 + return -1;
  115 +
  116 + xen_be_bind_evtchn(&c->xendev);
  117 + xen_be_printf(&c->xendev, 1, "ring mfn %d, remote-port %d, local-port %d\n",
  118 + mfn, c->xendev.remote_port, c->xendev.local_port);
  119 +
  120 + return 0;
  121 +}
  122 +
  123 +static void common_unbind(struct common *c)
  124 +{
  125 + xen_be_unbind_evtchn(&c->xendev);
  126 + if (c->page) {
  127 + munmap(c->page, XC_PAGE_SIZE);
  128 + c->page = NULL;
  129 + }
  130 +}
  131 +
  132 +/* -------------------------------------------------------------------- */
  133 +
  134 +#if 0
  135 +/*
  136 + * These two tables are not needed any more, but left in here
  137 + * intentionally as documentation, to show how scancode2linux[]
  138 + * was generated.
  139 + *
  140 + * Tables to map from scancode to Linux input layer keycode.
  141 + * Scancodes are hardware-specific. These maps assumes a
  142 + * standard AT or PS/2 keyboard which is what QEMU feeds us.
  143 + */
  144 +const unsigned char atkbd_set2_keycode[512] = {
  145 +
  146 + 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117,
  147 + 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0,
  148 + 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183,
  149 + 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185,
  150 + 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0,
  151 + 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85,
  152 + 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0,
  153 + 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
  154 +
  155 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  156 + 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125,
  157 + 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127,
  158 + 159, 0,115, 0,164, 0, 0,116,158, 0,150,166, 0, 0, 0,142,
  159 + 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0,
  160 + 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0,
  161 + 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112,
  162 + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0,
  163 +
  164 +};
  165 +
  166 +const unsigned char atkbd_unxlate_table[128] = {
  167 +
  168 + 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
  169 + 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
  170 + 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
  171 + 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3,
  172 + 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105,
  173 + 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63,
  174 + 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
  175 + 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
  176 +
  177 +};
  178 +#endif
  179 +
  180 +/*
  181 + * for (i = 0; i < 128; i++) {
  182 + * scancode2linux[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]];
  183 + * scancode2linux[i | 0x80] = atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80];
  184 + * }
  185 + */
  186 +static const unsigned char scancode2linux[512] = {
  187 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
  188 + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
  189 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  190 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
  191 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
  192 + 80, 81, 82, 83, 99, 0, 86, 87, 88,117, 0, 0, 95,183,184,185,
  193 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  194 + 93, 0, 0, 89, 0, 0, 85, 91, 90, 92, 0, 94, 0,124,121, 0,
  195 +
  196 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  197 + 165, 0, 0, 0, 0, 0, 0, 0, 0,163, 0, 0, 96, 97, 0, 0,
  198 + 113,140,164, 0,166, 0, 0, 0, 0, 0,255, 0, 0, 0,114, 0,
  199 + 115, 0,150, 0, 0, 98,255, 99,100, 0, 0, 0, 0, 0, 0, 0,
  200 + 0, 0, 0, 0, 0,119,119,102,103,104, 0,105,112,106,118,107,
  201 + 108,109,110,111, 0, 0, 0, 0, 0, 0, 0,125,126,127,116,142,
  202 + 0, 0, 0,143, 0,217,156,173,128,159,158,157,155,226, 0,112,
  203 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  204 +};
  205 +
  206 +/* Send an event to the keyboard frontend driver */
  207 +static int xenfb_kbd_event(struct XenInput *xenfb,
  208 + union xenkbd_in_event *event)
  209 +{
  210 + struct xenkbd_page *page = xenfb->c.page;
  211 + uint32_t prod;
  212 +
  213 + if (xenfb->c.xendev.be_state != XenbusStateConnected)
  214 + return 0;
  215 + if (!page)
  216 + return 0;
  217 +
  218 + prod = page->in_prod;
  219 + if (prod - page->in_cons == XENKBD_IN_RING_LEN) {
  220 + errno = EAGAIN;
  221 + return -1;
  222 + }
  223 +
  224 + xen_mb(); /* ensure ring space available */
  225 + XENKBD_IN_RING_REF(page, prod) = *event;
  226 + xen_wmb(); /* ensure ring contents visible */
  227 + page->in_prod = prod + 1;
  228 + return xen_be_send_notify(&xenfb->c.xendev);
  229 +}
  230 +
  231 +/* Send a keyboard (or mouse button) event */
  232 +static int xenfb_send_key(struct XenInput *xenfb, bool down, int keycode)
  233 +{
  234 + union xenkbd_in_event event;
  235 +
  236 + memset(&event, 0, XENKBD_IN_EVENT_SIZE);
  237 + event.type = XENKBD_TYPE_KEY;
  238 + event.key.pressed = down ? 1 : 0;
  239 + event.key.keycode = keycode;
  240 +
  241 + return xenfb_kbd_event(xenfb, &event);
  242 +}
  243 +
  244 +/* Send a relative mouse movement event */
  245 +static int xenfb_send_motion(struct XenInput *xenfb,
  246 + int rel_x, int rel_y, int rel_z)
  247 +{
  248 + union xenkbd_in_event event;
  249 +
  250 + memset(&event, 0, XENKBD_IN_EVENT_SIZE);
  251 + event.type = XENKBD_TYPE_MOTION;
  252 + event.motion.rel_x = rel_x;
  253 + event.motion.rel_y = rel_y;
  254 +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030207
  255 + event.motion.rel_z = rel_z;
  256 +#endif
  257 +
  258 + return xenfb_kbd_event(xenfb, &event);
  259 +}
  260 +
  261 +/* Send an absolute mouse movement event */
  262 +static int xenfb_send_position(struct XenInput *xenfb,
  263 + int abs_x, int abs_y, int z)
  264 +{
  265 + union xenkbd_in_event event;
  266 +
  267 + memset(&event, 0, XENKBD_IN_EVENT_SIZE);
  268 + event.type = XENKBD_TYPE_POS;
  269 + event.pos.abs_x = abs_x;
  270 + event.pos.abs_y = abs_y;
  271 +#if __XEN_LATEST_INTERFACE_VERSION__ == 0x00030207
  272 + event.pos.abs_z = z;
  273 +#endif
  274 +#if __XEN_LATEST_INTERFACE_VERSION__ >= 0x00030208
  275 + event.pos.rel_z = z;
  276 +#endif
  277 +
  278 + return xenfb_kbd_event(xenfb, &event);
  279 +}
  280 +
  281 +/*
  282 + * Send a key event from the client to the guest OS
  283 + * QEMU gives us a raw scancode from an AT / PS/2 style keyboard.
  284 + * We have to turn this into a Linux Input layer keycode.
  285 + *
  286 + * Extra complexity from the fact that with extended scancodes
  287 + * (like those produced by arrow keys) this method gets called
  288 + * twice, but we only want to send a single event. So we have to
  289 + * track the '0xe0' scancode state & collapse the extended keys
  290 + * as needed.
  291 + *
  292 + * Wish we could just send scancodes straight to the guest which
  293 + * already has code for dealing with this...
  294 + */
  295 +static void xenfb_key_event(void *opaque, int scancode)
  296 +{
  297 + struct XenInput *xenfb = opaque;
  298 + int down = 1;
  299 +
  300 + if (scancode == 0xe0) {
  301 + xenfb->extended = 1;
  302 + return;
  303 + } else if (scancode & 0x80) {
  304 + scancode &= 0x7f;
  305 + down = 0;
  306 + }
  307 + if (xenfb->extended) {
  308 + scancode |= 0x80;
  309 + xenfb->extended = 0;
  310 + }
  311 + xenfb_send_key(xenfb, down, scancode2linux[scancode]);
  312 +}
  313 +
  314 +/*
  315 + * Send a mouse event from the client to the guest OS
  316 + *
  317 + * The QEMU mouse can be in either relative, or absolute mode.
  318 + * Movement is sent separately from button state, which has to
  319 + * be encoded as virtual key events. We also don't actually get
  320 + * given any button up/down events, so have to track changes in
  321 + * the button state.
  322 + */
  323 +static void xenfb_mouse_event(void *opaque,
  324 + int dx, int dy, int dz, int button_state)
  325 +{
  326 + struct XenInput *xenfb = opaque;
  327 + int dw = ds_get_width(xenfb->c.ds);
  328 + int dh = ds_get_height(xenfb->c.ds);
  329 + int i;
  330 +
  331 + if (xenfb->abs_pointer_wanted)
  332 + xenfb_send_position(xenfb,
  333 + dx * (dw - 1) / 0x7fff,
  334 + dy * (dh - 1) / 0x7fff,
  335 + dz);
  336 + else
  337 + xenfb_send_motion(xenfb, dx, dy, dz);
  338 +
  339 + for (i = 0 ; i < 8 ; i++) {
  340 + int lastDown = xenfb->button_state & (1 << i);
  341 + int down = button_state & (1 << i);
  342 + if (down == lastDown)
  343 + continue;
  344 +
  345 + if (xenfb_send_key(xenfb, down, BTN_LEFT+i) < 0)
  346 + return;
  347 + }
  348 + xenfb->button_state = button_state;
  349 +}
  350 +
  351 +static int input_init(struct XenDevice *xendev)
  352 +{
  353 + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
  354 +
  355 + if (!in->c.ds) {
  356 + xen_be_printf(xendev, 1, "ds not set (yet)\n");
  357 + return -1;
  358 + }
  359 +
  360 + xenstore_write_be_int(xendev, "feature-abs-pointer", 1);
  361 + return 0;
  362 +}
  363 +
  364 +static int input_connect(struct XenDevice *xendev)
  365 +{
  366 + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
  367 + int rc;
  368 +
  369 + if (xenstore_read_fe_int(xendev, "request-abs-pointer",
  370 + &in->abs_pointer_wanted) == -1)
  371 + in->abs_pointer_wanted = 0;
  372 +
  373 + rc = common_bind(&in->c);
  374 + if (rc != 0)
  375 + return rc;
  376 +
  377 + qemu_add_kbd_event_handler(xenfb_key_event, in);
  378 + in->qmouse = qemu_add_mouse_event_handler(xenfb_mouse_event, in,
  379 + in->abs_pointer_wanted,
  380 + "Xen PVFB Mouse");
  381 + return 0;
  382 +}
  383 +
  384 +static void input_disconnect(struct XenDevice *xendev)
  385 +{
  386 + struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
  387 +
  388 + if (in->qmouse) {
  389 + qemu_remove_mouse_event_handler(in->qmouse);
  390 + in->qmouse = NULL;
  391 + }
  392 + qemu_add_kbd_event_handler(NULL, NULL);
  393 + common_unbind(&in->c);
  394 +}
  395 +
  396 +static void input_event(struct XenDevice *xendev)
  397 +{
  398 + struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev);
  399 + struct xenkbd_page *page = xenfb->c.page;
  400 +
  401 + /* We don't understand any keyboard events, so just ignore them. */
  402 + if (page->out_prod == page->out_cons)
  403 + return;
  404 + page->out_cons = page->out_prod;
  405 + xen_be_send_notify(&xenfb->c.xendev);
  406 +}
  407 +
  408 +/* -------------------------------------------------------------------- */
  409 +
  410 +static void xenfb_copy_mfns(int mode, int count, unsigned long *dst, void *src)
  411 +{
  412 + uint32_t *src32 = src;
  413 + uint64_t *src64 = src;
  414 + int i;
  415 +
  416 + for (i = 0; i < count; i++)
  417 + dst[i] = (mode == 32) ? src32[i] : src64[i];
  418 +}
  419 +
  420 +static int xenfb_map_fb(struct XenFB *xenfb)
  421 +{
  422 + struct xenfb_page *page = xenfb->c.page;
  423 + char *protocol = xenfb->c.xendev.protocol;
  424 + int n_fbdirs;
  425 + unsigned long *pgmfns = NULL;
  426 + unsigned long *fbmfns = NULL;
  427 + void *map, *pd;
  428 + int mode, ret = -1;
  429 +
  430 + /* default to native */
  431 + pd = page->pd;
  432 + mode = sizeof(unsigned long) * 8;
  433 +
  434 + if (!protocol) {
  435 + /*
  436 + * Undefined protocol, some guesswork needed.
  437 + *
  438 + * Old frontends which don't set the protocol use
  439 + * one page directory only, thus pd[1] must be zero.
  440 + * pd[1] of the 32bit struct layout and the lower
  441 + * 32 bits of pd[0] of the 64bit struct layout have
  442 + * the same location, so we can check that ...
  443 + */
  444 + uint32_t *ptr32 = NULL;
  445 + uint32_t *ptr64 = NULL;
  446 +#if defined(__i386__)
  447 + ptr32 = (void*)page->pd;
  448 + ptr64 = ((void*)page->pd) + 4;
  449 +#elif defined(__x86_64__)
  450 + ptr32 = ((void*)page->pd) - 4;
  451 + ptr64 = (void*)page->pd;
  452 +#endif
  453 + if (ptr32) {
  454 + if (ptr32[1] == 0) {
  455 + mode = 32;
  456 + pd = ptr32;
  457 + } else {
  458 + mode = 64;
  459 + pd = ptr64;
  460 + }
  461 + }
  462 +#if defined(__x86_64__)
  463 + } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_32) == 0) {
  464 + /* 64bit dom0, 32bit domU */
  465 + mode = 32;
  466 + pd = ((void*)page->pd) - 4;
  467 +#elif defined(__i386__)
  468 + } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_64) == 0) {
  469 + /* 32bit dom0, 64bit domU */
  470 + mode = 64;
  471 + pd = ((void*)page->pd) + 4;
  472 +#endif
  473 + }
  474 +
  475 + if (xenfb->pixels) {
  476 + munmap(xenfb->pixels, xenfb->fbpages * XC_PAGE_SIZE);
  477 + xenfb->pixels = NULL;
  478 + }
  479 +
  480 + xenfb->fbpages = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
  481 + n_fbdirs = xenfb->fbpages * mode / 8;
  482 + n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
  483 +
  484 + pgmfns = qemu_mallocz(sizeof(unsigned long) * n_fbdirs);
  485 + fbmfns = qemu_mallocz(sizeof(unsigned long) * xenfb->fbpages);
  486 +
  487 + xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd);
  488 + map = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom,
  489 + PROT_READ, pgmfns, n_fbdirs);
  490 + if (map == NULL)
  491 + goto out;
  492 + xenfb_copy_mfns(mode, xenfb->fbpages, fbmfns, map);
  493 + munmap(map, n_fbdirs * XC_PAGE_SIZE);
  494 +
  495 + xenfb->pixels = xc_map_foreign_pages(xen_xc, xenfb->c.xendev.dom,
  496 + PROT_READ | PROT_WRITE, fbmfns, xenfb->fbpages);
  497 + if (xenfb->pixels == NULL)
  498 + goto out;
  499 +
  500 + ret = 0; /* all is fine */
  501 +
  502 +out:
  503 + qemu_free(pgmfns);
  504 + qemu_free(fbmfns);
  505 + return ret;
  506 +}
  507 +
  508 +static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim,
  509 + int width, int height, int depth,
  510 + size_t fb_len, int offset, int row_stride)
  511 +{
  512 + size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd);
  513 + size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz;
  514 + size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz;
  515 + size_t fb_len_max = fb_pages * XC_PAGE_SIZE;
  516 + int max_width, max_height;
  517 +
  518 + if (fb_len_lim > fb_len_max) {
  519 + xen_be_printf(&xenfb->c.xendev, 0, "fb size limit %zu exceeds %zu, corrected\n",
  520 + fb_len_lim, fb_len_max);
  521 + fb_len_lim = fb_len_max;
  522 + }
  523 + if (fb_len_lim && fb_len > fb_len_lim) {
  524 + xen_be_printf(&xenfb->c.xendev, 0, "frontend fb size %zu limited to %zu\n",
  525 + fb_len, fb_len_lim);
  526 + fb_len = fb_len_lim;
  527 + }
  528 + if (depth != 8 && depth != 16 && depth != 24 && depth != 32) {
  529 + xen_be_printf(&xenfb->c.xendev, 0, "can't handle frontend fb depth %d\n",
  530 + depth);
  531 + return -1;
  532 + }
  533 + if (row_stride <= 0 || row_stride > fb_len) {
  534 + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n", row_stride);
  535 + return -1;
  536 + }
  537 + max_width = row_stride / (depth / 8);
  538 + if (width < 0 || width > max_width) {
  539 + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend width %d limited to %d\n",
  540 + width, max_width);
  541 + width = max_width;
  542 + }
  543 + if (offset < 0 || offset >= fb_len) {
  544 + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend offset %d (max %zu)\n",
  545 + offset, fb_len - 1);
  546 + return -1;
  547 + }
  548 + max_height = (fb_len - offset) / row_stride;
  549 + if (height < 0 || height > max_height) {
  550 + xen_be_printf(&xenfb->c.xendev, 0, "invalid frontend height %d limited to %d\n",
  551 + height, max_height);
  552 + height = max_height;
  553 + }
  554 + xenfb->fb_len = fb_len;
  555 + xenfb->row_stride = row_stride;
  556 + xenfb->depth = depth;
  557 + xenfb->width = width;
  558 + xenfb->height = height;
  559 + xenfb->offset = offset;
  560 + xenfb->up_fullscreen = 1;
  561 + xenfb->do_resize = 1;
  562 + xen_be_printf(&xenfb->c.xendev, 1, "framebuffer %dx%dx%d offset %d stride %d\n",
  563 + width, height, depth, offset, row_stride);
  564 + return 0;
  565 +}
  566 +
  567 +/* A convenient function for munging pixels between different depths */
  568 +#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \
  569 + for (line = y ; line < (y+h) ; line++) { \
  570 + SRC_T *src = (SRC_T *)(xenfb->pixels \
  571 + + xenfb->offset \
  572 + + (line * xenfb->row_stride) \
  573 + + (x * xenfb->depth / 8)); \
  574 + DST_T *dst = (DST_T *)(data \
  575 + + (line * linesize) \
  576 + + (x * bpp / 8)); \
  577 + int col; \
  578 + const int RSS = 32 - (RSB + GSB + BSB); \
  579 + const int GSS = 32 - (GSB + BSB); \
  580 + const int BSS = 32 - (BSB); \
  581 + const uint32_t RSM = (~0U) << (32 - RSB); \
  582 + const uint32_t GSM = (~0U) << (32 - GSB); \
  583 + const uint32_t BSM = (~0U) << (32 - BSB); \
  584 + const int RDS = 32 - (RDB + GDB + BDB); \
  585 + const int GDS = 32 - (GDB + BDB); \
  586 + const int BDS = 32 - (BDB); \
  587 + const uint32_t RDM = (~0U) << (32 - RDB); \
  588 + const uint32_t GDM = (~0U) << (32 - GDB); \
  589 + const uint32_t BDM = (~0U) << (32 - BDB); \
  590 + for (col = x ; col < (x+w) ; col++) { \
  591 + uint32_t spix = *src; \
  592 + *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \
  593 + (((spix << GSS) & GSM & GDM) >> GDS) | \
  594 + (((spix << BSS) & BSM & BDM) >> BDS); \
  595 + src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \
  596 + dst = (DST_T *) ((unsigned long) dst + bpp / 8); \
  597 + } \
  598 + }
  599 +
  600 +
  601 +/*
  602 + * This copies data from the guest framebuffer region, into QEMU's
  603 + * displaysurface. qemu uses 16 or 32 bpp. In case the pv framebuffer
  604 + * uses something else we must convert and copy, otherwise we can
  605 + * supply the buffer directly and no thing here.
  606 + */
  607 +static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h)
  608 +{
  609 + int line, oops = 0;
  610 + int bpp = ds_get_bits_per_pixel(xenfb->c.ds);
  611 + int linesize = ds_get_linesize(xenfb->c.ds);
  612 + uint8_t *data = ds_get_data(xenfb->c.ds);
  613 +
  614 + if (!is_buffer_shared(xenfb->c.ds->surface)) {
  615 + switch (xenfb->depth) {
  616 + case 8:
  617 + if (bpp == 16) {
  618 + BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5);
  619 + } else if (bpp == 32) {
  620 + BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8);
  621 + } else {
  622 + oops = 1;
  623 + }
  624 + break;
  625 + case 24:
  626 + if (bpp == 16) {
  627 + BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5);
  628 + } else if (bpp == 32) {
  629 + BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8);
  630 + } else {
  631 + oops = 1;
  632 + }
  633 + break;
  634 + default:
  635 + oops = 1;
  636 + }
  637 + }
  638 + if (oops) /* should not happen */
  639 + xen_be_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n",
  640 + __FUNCTION__, xenfb->depth, bpp);
  641 +
  642 + dpy_update(xenfb->c.ds, x, y, w, h);
  643 +}
  644 +
  645 +#ifdef XENFB_TYPE_REFRESH_PERIOD
  646 +static int xenfb_queue_full(struct XenFB *xenfb)
  647 +{
  648 + struct xenfb_page *page = xenfb->c.page;
  649 + uint32_t cons, prod;
  650 +
  651 + if (!page)
  652 + return 1;
  653 +
  654 + prod = page->in_prod;
  655 + cons = page->in_cons;
  656 + return prod - cons == XENFB_IN_RING_LEN;
  657 +}
  658 +
  659 +static void xenfb_send_event(struct XenFB *xenfb, union xenfb_in_event *event)
  660 +{
  661 + uint32_t prod;
  662 + struct xenfb_page *page = xenfb->c.page;
  663 +
  664 + prod = page->in_prod;
  665 + /* caller ensures !xenfb_queue_full() */
  666 + xen_mb(); /* ensure ring space available */
  667 + XENFB_IN_RING_REF(page, prod) = *event;
  668 + xen_wmb(); /* ensure ring contents visible */
  669 + page->in_prod = prod + 1;
  670 +
  671 + xen_be_send_notify(&xenfb->c.xendev);
  672 +}
  673 +
  674 +static void xenfb_send_refresh_period(struct XenFB *xenfb, int period)
  675 +{
  676 + union xenfb_in_event event;
  677 +
  678 + memset(&event, 0, sizeof(event));
  679 + event.type = XENFB_TYPE_REFRESH_PERIOD;
  680 + event.refresh_period.period = period;
  681 + xenfb_send_event(xenfb, &event);
  682 +}
  683 +#endif
  684 +
  685 +/*
  686 + * Periodic update of display.
  687 + * Also transmit the refresh interval to the frontend.
  688 + *
  689 + * Never ever do any qemu display operations
  690 + * (resize, screen update) outside this function.
  691 + * Our screen might be inactive. When asked for
  692 + * an update we know it is active.
  693 + */
  694 +static void xenfb_update(void *opaque)
  695 +{
  696 + struct XenFB *xenfb = opaque;
  697 + struct DisplayChangeListener *l;
  698 + int i;
  699 +
  700 + if (xenfb->c.xendev.be_state != XenbusStateConnected)
  701 + return;
  702 +
  703 + if (xenfb->feature_update) {
  704 +#ifdef XENFB_TYPE_REFRESH_PERIOD
  705 + int period = 99999999;
  706 + int idle = 1;
  707 +
  708 + if (xenfb_queue_full(xenfb))
  709 + return;
  710 +
  711 + for (l = xenfb->c.ds->listeners; l != NULL; l = l->next) {
  712 + if (l->idle)
  713 + continue;
  714 + idle = 0;
  715 + if (!l->gui_timer_interval) {
  716 + if (period > GUI_REFRESH_INTERVAL)
  717 + period = GUI_REFRESH_INTERVAL;
  718 + } else {
  719 + if (period > l->gui_timer_interval)
  720 + period = l->gui_timer_interval;
  721 + }
  722 + }
  723 + if (idle)
  724 + period = XENFB_NO_REFRESH;
  725 +
  726 + if (xenfb->refresh_period != period) {
  727 + xenfb_send_refresh_period(xenfb, period);
  728 + xenfb->refresh_period = period;
  729 + xen_be_printf(&xenfb->c.xendev, 1, "refresh period: %d\n", period);
  730 + }
  731 +#else
  732 + ; /* nothing */
  733 +#endif
  734 + } else {
  735 + /* we don't get update notifications, thus use the
  736 + * sledge hammer approach ... */
  737 + xenfb->up_fullscreen = 1;
  738 + }
  739 +
  740 + /* resize if needed */
  741 + if (xenfb->do_resize) {
  742 + xenfb->do_resize = 0;
  743 + switch (xenfb->depth) {
  744 + case 16:
  745 + case 32:
  746 + /* console.c supported depth -> buffer can be used directly */
  747 + qemu_free_displaysurface(xenfb->c.ds);
  748 + xenfb->c.ds->surface = qemu_create_displaysurface_from
  749 + (xenfb->width, xenfb->height, xenfb->depth,
  750 + xenfb->row_stride, xenfb->pixels + xenfb->offset);
  751 + break;
  752 + default:
  753 + /* we must convert stuff */
  754 + qemu_resize_displaysurface(xenfb->c.ds, xenfb->width, xenfb->height);
  755 + break;
  756 + }
  757 + xen_be_printf(&xenfb->c.xendev, 1, "update: resizing: %dx%d @ %d bpp%s\n",
  758 + xenfb->width, xenfb->height, xenfb->depth,
  759 + is_buffer_shared(xenfb->c.ds->surface) ? " (shared)" : "");
  760 + dpy_resize(xenfb->c.ds);
  761 + xenfb->up_fullscreen = 1;
  762 + }
  763 +
  764 + /* run queued updates */
  765 + if (xenfb->up_fullscreen) {
  766 + xen_be_printf(&xenfb->c.xendev, 3, "update: fullscreen\n");
  767 + xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height);
  768 + } else if (xenfb->up_count) {
  769 + xen_be_printf(&xenfb->c.xendev, 3, "update: %d rects\n", xenfb->up_count);
  770 + for (i = 0; i < xenfb->up_count; i++)
  771 + xenfb_guest_copy(xenfb,
  772 + xenfb->up_rects[i].x,
  773 + xenfb->up_rects[i].y,
  774 + xenfb->up_rects[i].w,
  775 + xenfb->up_rects[i].h);
  776 + } else {
  777 + xen_be_printf(&xenfb->c.xendev, 3, "update: nothing\n");
  778 + }
  779 + xenfb->up_count = 0;
  780 + xenfb->up_fullscreen = 0;
  781 +}
  782 +
  783 +/* QEMU display state changed, so refresh the framebuffer copy */
  784 +static void xenfb_invalidate(void *opaque)
  785 +{
  786 + struct XenFB *xenfb = opaque;
  787 + xenfb->up_fullscreen = 1;
  788 +}
  789 +
  790 +static void xenfb_handle_events(struct XenFB *xenfb)
  791 +{
  792 + uint32_t prod, cons;
  793 + struct xenfb_page *page = xenfb->c.page;
  794 +
  795 + prod = page->out_prod;
  796 + if (prod == page->out_cons)
  797 + return;
  798 + xen_rmb(); /* ensure we see ring contents up to prod */
  799 + for (cons = page->out_cons; cons != prod; cons++) {
  800 + union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
  801 + int x, y, w, h;
  802 +
  803 + switch (event->type) {
  804 + case XENFB_TYPE_UPDATE:
  805 + if (xenfb->up_count == UP_QUEUE)
  806 + xenfb->up_fullscreen = 1;
  807 + if (xenfb->up_fullscreen)
  808 + break;
  809 + x = MAX(event->update.x, 0);
  810 + y = MAX(event->update.y, 0);
  811 + w = MIN(event->update.width, xenfb->width - x);
  812 + h = MIN(event->update.height, xenfb->height - y);
  813 + if (w < 0 || h < 0) {
  814 + xen_be_printf(&xenfb->c.xendev, 1, "bogus update ignored\n");
  815 + break;
  816 + }
  817 + if (x != event->update.x ||
  818 + y != event->update.y ||
  819 + w != event->update.width ||
  820 + h != event->update.height) {
  821 + xen_be_printf(&xenfb->c.xendev, 1, "bogus update clipped\n");
  822 + }
  823 + if (w == xenfb->width && h > xenfb->height / 2) {
  824 + /* scroll detector: updated more than 50% of the lines,
  825 + * don't bother keeping track of the rectangles then */
  826 + xenfb->up_fullscreen = 1;
  827 + } else {
  828 + xenfb->up_rects[xenfb->up_count].x = x;
  829 + xenfb->up_rects[xenfb->up_count].y = y;
  830 + xenfb->up_rects[xenfb->up_count].w = w;
  831 + xenfb->up_rects[xenfb->up_count].h = h;
  832 + xenfb->up_count++;
  833 + }
  834 + break;
  835 +#ifdef XENFB_TYPE_RESIZE
  836 + case XENFB_TYPE_RESIZE:
  837 + if (xenfb_configure_fb(xenfb, xenfb->fb_len,
  838 + event->resize.width,
  839 + event->resize.height,
  840 + event->resize.depth,
  841 + xenfb->fb_len,
  842 + event->resize.offset,
  843 + event->resize.stride) < 0)
  844 + break;
  845 + xenfb_invalidate(xenfb);
  846 + break;
  847 +#endif
  848 + }
  849 + }
  850 + xen_mb(); /* ensure we're done with ring contents */
  851 + page->out_cons = cons;
  852 +}
  853 +
  854 +static int fb_init(struct XenDevice *xendev)
  855 +{
  856 + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
  857 +
  858 + fb->refresh_period = -1;
  859 +
  860 +#ifdef XENFB_TYPE_RESIZE
  861 + xenstore_write_be_int(xendev, "feature-resize", 1);
  862 +#endif
  863 + return 0;
  864 +}
  865 +
  866 +static int fb_connect(struct XenDevice *xendev)
  867 +{
  868 + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
  869 + struct xenfb_page *fb_page;
  870 + int videoram;
  871 + int rc;
  872 +
  873 + if (xenstore_read_fe_int(xendev, "videoram", &videoram) == -1)
  874 + videoram = 0;
  875 +
  876 + rc = common_bind(&fb->c);
  877 + if (rc != 0)
  878 + return rc;
  879 +
  880 + fb_page = fb->c.page;
  881 + rc = xenfb_configure_fb(fb, videoram * 1024 * 1024U,
  882 + fb_page->width, fb_page->height, fb_page->depth,
  883 + fb_page->mem_length, 0, fb_page->line_length);
  884 + if (rc != 0)
  885 + return rc;
  886 +
  887 + rc = xenfb_map_fb(fb);
  888 + if (rc != 0)
  889 + return rc;
  890 +
  891 +#if 0 /* handled in xen_init_display() for now */
  892 + if (!fb->have_console) {
  893 + fb->c.ds = graphic_console_init(xenfb_update,
  894 + xenfb_invalidate,
  895 + NULL,
  896 + NULL,
  897 + fb);
  898 + fb->have_console = 1;
  899 + }
  900 +#endif
  901 +
  902 + if (xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update) == -1)
  903 + fb->feature_update = 0;
  904 + if (fb->feature_update)
  905 + xenstore_write_be_int(xendev, "request-update", 1);
  906 +
  907 + xen_be_printf(xendev, 1, "feature-update=%d, videoram=%d\n",
  908 + fb->feature_update, videoram);
  909 + return 0;
  910 +}
  911 +
  912 +static void fb_disconnect(struct XenDevice *xendev)
  913 +{
  914 + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
  915 +
  916 + /*
  917 + * FIXME: qemu can't un-init gfx display (yet?).
  918 + * Replacing the framebuffer with anonymous shared memory
  919 + * instead. This releases the guest pages and keeps qemu happy.
  920 + */
  921 + fb->pixels = mmap(fb->pixels, fb->fbpages * XC_PAGE_SIZE,
  922 + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON,
  923 + -1, 0);
  924 + common_unbind(&fb->c);
  925 + fb->feature_update = 0;
  926 + fb->bug_trigger = 0;
  927 +}
  928 +
  929 +static void fb_frontend_changed(struct XenDevice *xendev, const char *node)
  930 +{
  931 + struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
  932 +
  933 + /*
  934 + * Set state to Connected *again* once the frontend switched
  935 + * to connected. We must trigger the watch a second time to
  936 + * workaround a frontend bug.
  937 + */
  938 + if (fb->bug_trigger == 0 && strcmp(node, "state") == 0 &&
  939 + xendev->fe_state == XenbusStateConnected &&
  940 + xendev->be_state == XenbusStateConnected) {
  941 + xen_be_printf(xendev, 2, "re-trigger connected (frontend bug)\n");
  942 + xen_be_set_state(xendev, XenbusStateConnected);
  943 + fb->bug_trigger = 1; /* only once */
  944 + }
  945 +}
  946 +
  947 +static void fb_event(struct XenDevice *xendev)
  948 +{
  949 + struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev);
  950 +
  951 + xenfb_handle_events(xenfb);
  952 + xen_be_send_notify(&xenfb->c.xendev);
  953 +}
  954 +
  955 +/* -------------------------------------------------------------------- */
  956 +
  957 +struct XenDevOps xen_kbdmouse_ops = {
  958 + .size = sizeof(struct XenInput),
  959 + .init = input_init,
  960 + .connect = input_connect,
  961 + .disconnect = input_disconnect,
  962 + .event = input_event,
  963 +};
  964 +
  965 +struct XenDevOps xen_framebuffer_ops = {
  966 + .size = sizeof(struct XenFB),
  967 + .init = fb_init,
  968 + .connect = fb_connect,
  969 + .disconnect = fb_disconnect,
  970 + .event = fb_event,
  971 + .frontend_changed = fb_frontend_changed,
  972 +};
  973 +
  974 +/*
  975 + * FIXME/TODO: Kill this.
  976 + * Temporary needed while DisplayState reorganization is in flight.
  977 + */
  978 +void xen_init_display(int domid)
  979 +{
  980 + struct XenDevice *xfb, *xin;
  981 + struct XenFB *fb;
  982 + struct XenInput *in;
  983 + int i = 0;
  984 +
  985 +wait_more:
  986 + i++;
  987 + main_loop_wait(10); /* miliseconds */
  988 + xfb = xen_be_find_xendev("vfb", domid, 0);
  989 + xin = xen_be_find_xendev("vkbd", domid, 0);
  990 + if (!xfb || !xin) {
  991 + if (i < 256)
  992 + goto wait_more;
  993 + xen_be_printf(NULL, 1, "displaystate setup failed\n");
  994 + return;
  995 + }
  996 +
  997 + /* vfb */
  998 + fb = container_of(xfb, struct XenFB, c.xendev);
  999 + fb->c.ds = graphic_console_init(xenfb_update,
  1000 + xenfb_invalidate,
  1001 + NULL,
  1002 + NULL,
  1003 + fb);
  1004 + fb->have_console = 1;
  1005 +
  1006 + /* vkbd */
  1007 + in = container_of(xin, struct XenInput, c.xendev);
  1008 + in->c.ds = fb->c.ds;
  1009 +
  1010 + /* retry ->init() */
  1011 + xen_be_check_state(xin);
  1012 + xen_be_check_state(xfb);
  1013 +}
... ...