qemu/hw/display/xenfb.c
<<
>>
Prefs
   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, see <http://www.gnu.org/licenses/>.
  25 */
  26
  27#include "qemu/osdep.h"
  28#include "qemu/units.h"
  29
  30#include "ui/input.h"
  31#include "ui/console.h"
  32#include "hw/xen/xen-legacy-backend.h"
  33
  34#include "hw/xen/interface/io/fbif.h"
  35#include "hw/xen/interface/io/kbdif.h"
  36#include "hw/xen/interface/io/protocols.h"
  37
  38#include "trace.h"
  39
  40#ifndef BTN_LEFT
  41#define BTN_LEFT 0x110 /* from <linux/input.h> */
  42#endif
  43
  44/* -------------------------------------------------------------------- */
  45
  46struct common {
  47    struct XenLegacyDevice  xendev;  /* must be first */
  48    void              *page;
  49};
  50
  51struct XenInput {
  52    struct common c;
  53    int abs_pointer_wanted; /* Whether guest supports absolute pointer */
  54    int raw_pointer_wanted; /* Whether guest supports raw (unscaled) pointer */
  55    QemuInputHandlerState *qkbd;
  56    QemuInputHandlerState *qmou;
  57    int axis[INPUT_AXIS__MAX];
  58    int wheel;
  59};
  60
  61#define UP_QUEUE 8
  62
  63struct XenFB {
  64    struct common     c;
  65    QemuConsole       *con;
  66    size_t            fb_len;
  67    int               row_stride;
  68    int               depth;
  69    int               width;
  70    int               height;
  71    int               offset;
  72    void              *pixels;
  73    int               fbpages;
  74    int               feature_update;
  75    int               bug_trigger;
  76    int               do_resize;
  77
  78    struct {
  79        int x,y,w,h;
  80    } up_rects[UP_QUEUE];
  81    int               up_count;
  82    int               up_fullscreen;
  83};
  84static const GraphicHwOps xenfb_ops;
  85
  86/* -------------------------------------------------------------------- */
  87
  88static int common_bind(struct common *c)
  89{
  90    uint64_t val;
  91    xen_pfn_t mfn;
  92
  93    if (xenstore_read_fe_uint64(&c->xendev, "page-ref", &val) == -1)
  94        return -1;
  95    mfn = (xen_pfn_t)val;
  96    assert(val == mfn);
  97
  98    if (xenstore_read_fe_int(&c->xendev, "event-channel", &c->xendev.remote_port) == -1)
  99        return -1;
 100
 101    c->page = xenforeignmemory_map(xen_fmem, c->xendev.dom,
 102                                   PROT_READ | PROT_WRITE, 1, &mfn, NULL);
 103    if (c->page == NULL)
 104        return -1;
 105
 106    xen_be_bind_evtchn(&c->xendev);
 107    xen_pv_printf(&c->xendev, 1,
 108                  "ring mfn %"PRI_xen_pfn", remote-port %d, local-port %d\n",
 109                  mfn, c->xendev.remote_port, c->xendev.local_port);
 110
 111    return 0;
 112}
 113
 114static void common_unbind(struct common *c)
 115{
 116    xen_pv_unbind_evtchn(&c->xendev);
 117    if (c->page) {
 118        xenforeignmemory_unmap(xen_fmem, c->page, 1);
 119        c->page = NULL;
 120    }
 121}
 122
 123/* -------------------------------------------------------------------- */
 124/* Send an event to the keyboard frontend driver */
 125static int xenfb_kbd_event(struct XenInput *xenfb,
 126                           union xenkbd_in_event *event)
 127{
 128    struct xenkbd_page *page = xenfb->c.page;
 129    uint32_t prod;
 130
 131    if (xenfb->c.xendev.be_state != XenbusStateConnected)
 132        return 0;
 133    if (!page)
 134        return 0;
 135
 136    prod = page->in_prod;
 137    if (prod - page->in_cons == XENKBD_IN_RING_LEN) {
 138        errno = EAGAIN;
 139        return -1;
 140    }
 141
 142    xen_mb();           /* ensure ring space available */
 143    XENKBD_IN_RING_REF(page, prod) = *event;
 144    xen_wmb();          /* ensure ring contents visible */
 145    page->in_prod = prod + 1;
 146    return xen_pv_send_notify(&xenfb->c.xendev);
 147}
 148
 149/* Send a keyboard (or mouse button) event */
 150static int xenfb_send_key(struct XenInput *xenfb, bool down, int keycode)
 151{
 152    union xenkbd_in_event event;
 153
 154    memset(&event, 0, XENKBD_IN_EVENT_SIZE);
 155    event.type = XENKBD_TYPE_KEY;
 156    event.key.pressed = down ? 1 : 0;
 157    event.key.keycode = keycode;
 158
 159    return xenfb_kbd_event(xenfb, &event);
 160}
 161
 162/* Send a relative mouse movement event */
 163static int xenfb_send_motion(struct XenInput *xenfb,
 164                             int rel_x, int rel_y, int rel_z)
 165{
 166    union xenkbd_in_event event;
 167
 168    memset(&event, 0, XENKBD_IN_EVENT_SIZE);
 169    event.type = XENKBD_TYPE_MOTION;
 170    event.motion.rel_x = rel_x;
 171    event.motion.rel_y = rel_y;
 172    event.motion.rel_z = rel_z;
 173
 174    return xenfb_kbd_event(xenfb, &event);
 175}
 176
 177/* Send an absolute mouse movement event */
 178static int xenfb_send_position(struct XenInput *xenfb,
 179                               int abs_x, int abs_y, int z)
 180{
 181    union xenkbd_in_event event;
 182
 183    memset(&event, 0, XENKBD_IN_EVENT_SIZE);
 184    event.type = XENKBD_TYPE_POS;
 185    event.pos.abs_x = abs_x;
 186    event.pos.abs_y = abs_y;
 187    event.pos.rel_z = z;
 188
 189    return xenfb_kbd_event(xenfb, &event);
 190}
 191
 192/*
 193 * Send a key event from the client to the guest OS
 194 * QEMU gives us a QCode.
 195 * We have to turn this into a Linux Input layer keycode.
 196 *
 197 * Wish we could just send scancodes straight to the guest which
 198 * already has code for dealing with this...
 199 */
 200static void xenfb_key_event(DeviceState *dev, QemuConsole *src,
 201                            InputEvent *evt)
 202{
 203    struct XenInput *xenfb = (struct XenInput *)dev;
 204    InputKeyEvent *key = evt->u.key.data;
 205    int qcode = qemu_input_key_value_to_qcode(key->key);
 206    int lnx;
 207
 208    if (qcode < qemu_input_map_qcode_to_linux_len) {
 209        lnx = qemu_input_map_qcode_to_linux[qcode];
 210
 211        if (lnx) {
 212            trace_xenfb_key_event(xenfb, lnx, key->down);
 213            xenfb_send_key(xenfb, key->down, lnx);
 214        }
 215    }
 216}
 217
 218/*
 219 * Send a mouse event from the client to the guest OS
 220 *
 221 * The QEMU mouse can be in either relative, or absolute mode.
 222 * Movement is sent separately from button state, which has to
 223 * be encoded as virtual key events. We also don't actually get
 224 * given any button up/down events, so have to track changes in
 225 * the button state.
 226 */
 227static void xenfb_mouse_event(DeviceState *dev, QemuConsole *src,
 228                              InputEvent *evt)
 229{
 230    struct XenInput *xenfb = (struct XenInput *)dev;
 231    InputBtnEvent *btn;
 232    InputMoveEvent *move;
 233    QemuConsole *con;
 234    DisplaySurface *surface;
 235    int scale;
 236
 237    switch (evt->type) {
 238    case INPUT_EVENT_KIND_BTN:
 239        btn = evt->u.btn.data;
 240        switch (btn->button) {
 241        case INPUT_BUTTON_LEFT:
 242            xenfb_send_key(xenfb, btn->down, BTN_LEFT);
 243            break;
 244        case INPUT_BUTTON_RIGHT:
 245            xenfb_send_key(xenfb, btn->down, BTN_LEFT + 1);
 246            break;
 247        case INPUT_BUTTON_MIDDLE:
 248            xenfb_send_key(xenfb, btn->down, BTN_LEFT + 2);
 249            break;
 250        case INPUT_BUTTON_WHEEL_UP:
 251            if (btn->down) {
 252                xenfb->wheel--;
 253            }
 254            break;
 255        case INPUT_BUTTON_WHEEL_DOWN:
 256            if (btn->down) {
 257                xenfb->wheel++;
 258            }
 259            break;
 260        default:
 261            break;
 262        }
 263        break;
 264
 265    case INPUT_EVENT_KIND_ABS:
 266        move = evt->u.abs.data;
 267        if (xenfb->raw_pointer_wanted) {
 268            xenfb->axis[move->axis] = move->value;
 269        } else {
 270            con = qemu_console_lookup_by_index(0);
 271            if (!con) {
 272                xen_pv_printf(&xenfb->c.xendev, 0, "No QEMU console available");
 273                return;
 274            }
 275            surface = qemu_console_surface(con);
 276            switch (move->axis) {
 277            case INPUT_AXIS_X:
 278                scale = surface_width(surface) - 1;
 279                break;
 280            case INPUT_AXIS_Y:
 281                scale = surface_height(surface) - 1;
 282                break;
 283            default:
 284                scale = 0x8000;
 285                break;
 286            }
 287            xenfb->axis[move->axis] = move->value * scale / 0x7fff;
 288        }
 289        break;
 290
 291    case INPUT_EVENT_KIND_REL:
 292        move = evt->u.rel.data;
 293        xenfb->axis[move->axis] += move->value;
 294        break;
 295
 296    default:
 297        break;
 298    }
 299}
 300
 301static void xenfb_mouse_sync(DeviceState *dev)
 302{
 303    struct XenInput *xenfb = (struct XenInput *)dev;
 304
 305    trace_xenfb_mouse_event(xenfb, xenfb->axis[INPUT_AXIS_X],
 306                            xenfb->axis[INPUT_AXIS_Y],
 307                            xenfb->wheel, 0,
 308                            xenfb->abs_pointer_wanted);
 309    if (xenfb->abs_pointer_wanted) {
 310        xenfb_send_position(xenfb, xenfb->axis[INPUT_AXIS_X],
 311                            xenfb->axis[INPUT_AXIS_Y],
 312                            xenfb->wheel);
 313    } else {
 314        xenfb_send_motion(xenfb, xenfb->axis[INPUT_AXIS_X],
 315                          xenfb->axis[INPUT_AXIS_Y],
 316                          xenfb->wheel);
 317        xenfb->axis[INPUT_AXIS_X] = 0;
 318        xenfb->axis[INPUT_AXIS_Y] = 0;
 319    }
 320    xenfb->wheel = 0;
 321}
 322
 323static QemuInputHandler xenfb_keyboard = {
 324    .name  = "Xen PV Keyboard",
 325    .mask  = INPUT_EVENT_MASK_KEY,
 326    .event = xenfb_key_event,
 327};
 328
 329static QemuInputHandler xenfb_abs_mouse = {
 330    .name  = "Xen PV Mouse",
 331    .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
 332    .event = xenfb_mouse_event,
 333    .sync  = xenfb_mouse_sync,
 334};
 335
 336static QemuInputHandler xenfb_rel_mouse = {
 337    .name  = "Xen PV Mouse",
 338    .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_REL,
 339    .event = xenfb_mouse_event,
 340    .sync  = xenfb_mouse_sync,
 341};
 342
 343static int input_init(struct XenLegacyDevice *xendev)
 344{
 345    xenstore_write_be_int(xendev, "feature-abs-pointer", 1);
 346    xenstore_write_be_int(xendev, "feature-raw-pointer", 1);
 347    return 0;
 348}
 349
 350static int input_initialise(struct XenLegacyDevice *xendev)
 351{
 352    struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
 353    int rc;
 354
 355    rc = common_bind(&in->c);
 356    if (rc != 0)
 357        return rc;
 358
 359    return 0;
 360}
 361
 362static void input_connected(struct XenLegacyDevice *xendev)
 363{
 364    struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
 365
 366    if (xenstore_read_fe_int(xendev, "request-abs-pointer",
 367                             &in->abs_pointer_wanted) == -1) {
 368        in->abs_pointer_wanted = 0;
 369    }
 370    if (xenstore_read_fe_int(xendev, "request-raw-pointer",
 371                             &in->raw_pointer_wanted) == -1) {
 372        in->raw_pointer_wanted = 0;
 373    }
 374    if (in->raw_pointer_wanted && in->abs_pointer_wanted == 0) {
 375        xen_pv_printf(xendev, 0, "raw pointer set without abs pointer");
 376    }
 377
 378    if (in->qkbd) {
 379        qemu_input_handler_unregister(in->qkbd);
 380    }
 381    if (in->qmou) {
 382        qemu_input_handler_unregister(in->qmou);
 383    }
 384    trace_xenfb_input_connected(xendev, in->abs_pointer_wanted);
 385
 386    in->qkbd = qemu_input_handler_register((DeviceState *)in, &xenfb_keyboard);
 387    in->qmou = qemu_input_handler_register((DeviceState *)in,
 388               in->abs_pointer_wanted ? &xenfb_abs_mouse : &xenfb_rel_mouse);
 389
 390    if (in->raw_pointer_wanted) {
 391        qemu_input_handler_activate(in->qkbd);
 392        qemu_input_handler_activate(in->qmou);
 393    }
 394}
 395
 396static void input_disconnect(struct XenLegacyDevice *xendev)
 397{
 398    struct XenInput *in = container_of(xendev, struct XenInput, c.xendev);
 399
 400    if (in->qkbd) {
 401        qemu_input_handler_unregister(in->qkbd);
 402        in->qkbd = NULL;
 403    }
 404    if (in->qmou) {
 405        qemu_input_handler_unregister(in->qmou);
 406        in->qmou = NULL;
 407    }
 408    common_unbind(&in->c);
 409}
 410
 411static void input_event(struct XenLegacyDevice *xendev)
 412{
 413    struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev);
 414    struct xenkbd_page *page = xenfb->c.page;
 415
 416    /* We don't understand any keyboard events, so just ignore them. */
 417    if (page->out_prod == page->out_cons)
 418        return;
 419    page->out_cons = page->out_prod;
 420    xen_pv_send_notify(&xenfb->c.xendev);
 421}
 422
 423/* -------------------------------------------------------------------- */
 424
 425static void xenfb_copy_mfns(int mode, int count, xen_pfn_t *dst, void *src)
 426{
 427    uint32_t *src32 = src;
 428    uint64_t *src64 = src;
 429    int i;
 430
 431    for (i = 0; i < count; i++)
 432        dst[i] = (mode == 32) ? src32[i] : src64[i];
 433}
 434
 435static int xenfb_map_fb(struct XenFB *xenfb)
 436{
 437    struct xenfb_page *page = xenfb->c.page;
 438    char *protocol = xenfb->c.xendev.protocol;
 439    int n_fbdirs;
 440    xen_pfn_t *pgmfns = NULL;
 441    xen_pfn_t *fbmfns = NULL;
 442    void *map, *pd;
 443    int mode, ret = -1;
 444
 445    /* default to native */
 446    pd = page->pd;
 447    mode = sizeof(unsigned long) * 8;
 448
 449    if (!protocol) {
 450        /*
 451         * Undefined protocol, some guesswork needed.
 452         *
 453         * Old frontends which don't set the protocol use
 454         * one page directory only, thus pd[1] must be zero.
 455         * pd[1] of the 32bit struct layout and the lower
 456         * 32 bits of pd[0] of the 64bit struct layout have
 457         * the same location, so we can check that ...
 458         */
 459        uint32_t *ptr32 = NULL;
 460        uint32_t *ptr64 = NULL;
 461#if defined(__i386__)
 462        ptr32 = (void*)page->pd;
 463        ptr64 = ((void*)page->pd) + 4;
 464#elif defined(__x86_64__)
 465        ptr32 = ((void*)page->pd) - 4;
 466        ptr64 = (void*)page->pd;
 467#endif
 468        if (ptr32) {
 469            if (ptr32[1] == 0) {
 470                mode = 32;
 471                pd   = ptr32;
 472            } else {
 473                mode = 64;
 474                pd   = ptr64;
 475            }
 476        }
 477#if defined(__x86_64__)
 478    } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_32) == 0) {
 479        /* 64bit dom0, 32bit domU */
 480        mode = 32;
 481        pd   = ((void*)page->pd) - 4;
 482#elif defined(__i386__)
 483    } else if (strcmp(protocol, XEN_IO_PROTO_ABI_X86_64) == 0) {
 484        /* 32bit dom0, 64bit domU */
 485        mode = 64;
 486        pd   = ((void*)page->pd) + 4;
 487#endif
 488    }
 489
 490    if (xenfb->pixels) {
 491        munmap(xenfb->pixels, xenfb->fbpages * XC_PAGE_SIZE);
 492        xenfb->pixels = NULL;
 493    }
 494
 495    xenfb->fbpages = DIV_ROUND_UP(xenfb->fb_len, XC_PAGE_SIZE);
 496    n_fbdirs = xenfb->fbpages * mode / 8;
 497    n_fbdirs = DIV_ROUND_UP(n_fbdirs, XC_PAGE_SIZE);
 498
 499    pgmfns = g_malloc0(sizeof(xen_pfn_t) * n_fbdirs);
 500    fbmfns = g_malloc0(sizeof(xen_pfn_t) * xenfb->fbpages);
 501
 502    xenfb_copy_mfns(mode, n_fbdirs, pgmfns, pd);
 503    map = xenforeignmemory_map(xen_fmem, xenfb->c.xendev.dom,
 504                               PROT_READ, n_fbdirs, pgmfns, NULL);
 505    if (map == NULL)
 506        goto out;
 507    xenfb_copy_mfns(mode, xenfb->fbpages, fbmfns, map);
 508    xenforeignmemory_unmap(xen_fmem, map, n_fbdirs);
 509
 510    xenfb->pixels = xenforeignmemory_map(xen_fmem, xenfb->c.xendev.dom,
 511            PROT_READ, xenfb->fbpages, fbmfns, NULL);
 512    if (xenfb->pixels == NULL)
 513        goto out;
 514
 515    ret = 0; /* all is fine */
 516
 517out:
 518    g_free(pgmfns);
 519    g_free(fbmfns);
 520    return ret;
 521}
 522
 523static int xenfb_configure_fb(struct XenFB *xenfb, size_t fb_len_lim,
 524                              int width, int height, int depth,
 525                              size_t fb_len, int offset, int row_stride)
 526{
 527    size_t mfn_sz = sizeof_field(struct xenfb_page, pd[0]);
 528    size_t pd_len = sizeof_field(struct xenfb_page, pd) / mfn_sz;
 529    size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz;
 530    size_t fb_len_max = fb_pages * XC_PAGE_SIZE;
 531    int max_width, max_height;
 532
 533    if (fb_len_lim > fb_len_max) {
 534        xen_pv_printf(&xenfb->c.xendev, 0,
 535                      "fb size limit %zu exceeds %zu, corrected\n",
 536                      fb_len_lim, fb_len_max);
 537        fb_len_lim = fb_len_max;
 538    }
 539    if (fb_len_lim && fb_len > fb_len_lim) {
 540        xen_pv_printf(&xenfb->c.xendev, 0,
 541                      "frontend fb size %zu limited to %zu\n",
 542                      fb_len, fb_len_lim);
 543        fb_len = fb_len_lim;
 544    }
 545    if (depth != 8 && depth != 16 && depth != 24 && depth != 32) {
 546        xen_pv_printf(&xenfb->c.xendev, 0,
 547                      "can't handle frontend fb depth %d\n",
 548                      depth);
 549        return -1;
 550    }
 551    if (row_stride <= 0 || row_stride > fb_len) {
 552        xen_pv_printf(&xenfb->c.xendev, 0, "invalid frontend stride %d\n",
 553                      row_stride);
 554        return -1;
 555    }
 556    max_width = row_stride / (depth / 8);
 557    if (width < 0 || width > max_width) {
 558        xen_pv_printf(&xenfb->c.xendev, 0,
 559                      "invalid frontend width %d limited to %d\n",
 560                      width, max_width);
 561        width = max_width;
 562    }
 563    if (offset < 0 || offset >= fb_len) {
 564        xen_pv_printf(&xenfb->c.xendev, 0,
 565                      "invalid frontend offset %d (max %zu)\n",
 566                      offset, fb_len - 1);
 567        return -1;
 568    }
 569    max_height = (fb_len - offset) / row_stride;
 570    if (height < 0 || height > max_height) {
 571        xen_pv_printf(&xenfb->c.xendev, 0,
 572                      "invalid frontend height %d limited to %d\n",
 573                      height, max_height);
 574        height = max_height;
 575    }
 576    xenfb->fb_len = fb_len;
 577    xenfb->row_stride = row_stride;
 578    xenfb->depth = depth;
 579    xenfb->width = width;
 580    xenfb->height = height;
 581    xenfb->offset = offset;
 582    xenfb->up_fullscreen = 1;
 583    xenfb->do_resize = 1;
 584    xen_pv_printf(&xenfb->c.xendev, 1,
 585                  "framebuffer %dx%dx%d offset %d stride %d\n",
 586                  width, height, depth, offset, row_stride);
 587    return 0;
 588}
 589
 590/* A convenient function for munging pixels between different depths */
 591#define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB)                        \
 592    for (line = y ; line < (y+h) ; line++) {                            \
 593        SRC_T *src = (SRC_T *)(xenfb->pixels                            \
 594                               + xenfb->offset                          \
 595                               + (line * xenfb->row_stride)             \
 596                               + (x * xenfb->depth / 8));               \
 597        DST_T *dst = (DST_T *)(data                                     \
 598                               + (line * linesize)                      \
 599                               + (x * bpp / 8));                        \
 600        int col;                                                        \
 601        const int RSS = 32 - (RSB + GSB + BSB);                         \
 602        const int GSS = 32 - (GSB + BSB);                               \
 603        const int BSS = 32 - (BSB);                                     \
 604        const uint32_t RSM = (~0U) << (32 - RSB);                       \
 605        const uint32_t GSM = (~0U) << (32 - GSB);                       \
 606        const uint32_t BSM = (~0U) << (32 - BSB);                       \
 607        const int RDS = 32 - (RDB + GDB + BDB);                         \
 608        const int GDS = 32 - (GDB + BDB);                               \
 609        const int BDS = 32 - (BDB);                                     \
 610        const uint32_t RDM = (~0U) << (32 - RDB);                       \
 611        const uint32_t GDM = (~0U) << (32 - GDB);                       \
 612        const uint32_t BDM = (~0U) << (32 - BDB);                       \
 613        for (col = x ; col < (x+w) ; col++) {                           \
 614            uint32_t spix = *src;                                       \
 615            *dst = (((spix << RSS) & RSM & RDM) >> RDS) |               \
 616                (((spix << GSS) & GSM & GDM) >> GDS) |                  \
 617                (((spix << BSS) & BSM & BDM) >> BDS);                   \
 618            src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8);   \
 619            dst = (DST_T *) ((unsigned long) dst + bpp / 8);            \
 620        }                                                               \
 621    }
 622
 623
 624/*
 625 * This copies data from the guest framebuffer region, into QEMU's
 626 * displaysurface. qemu uses 16 or 32 bpp.  In case the pv framebuffer
 627 * uses something else we must convert and copy, otherwise we can
 628 * supply the buffer directly and no thing here.
 629 */
 630static void xenfb_guest_copy(struct XenFB *xenfb, int x, int y, int w, int h)
 631{
 632    DisplaySurface *surface = qemu_console_surface(xenfb->con);
 633    int line, oops = 0;
 634    int bpp = surface_bits_per_pixel(surface);
 635    int linesize = surface_stride(surface);
 636    uint8_t *data = surface_data(surface);
 637
 638    if (!is_buffer_shared(surface)) {
 639        switch (xenfb->depth) {
 640        case 8:
 641            if (bpp == 16) {
 642                BLT(uint8_t, uint16_t,   3, 3, 2,   5, 6, 5);
 643            } else if (bpp == 32) {
 644                BLT(uint8_t, uint32_t,   3, 3, 2,   8, 8, 8);
 645            } else {
 646                oops = 1;
 647            }
 648            break;
 649        case 24:
 650            if (bpp == 16) {
 651                BLT(uint32_t, uint16_t,  8, 8, 8,   5, 6, 5);
 652            } else if (bpp == 32) {
 653                BLT(uint32_t, uint32_t,  8, 8, 8,   8, 8, 8);
 654            } else {
 655                oops = 1;
 656            }
 657            break;
 658        default:
 659            oops = 1;
 660        }
 661    }
 662    if (oops) /* should not happen */
 663        xen_pv_printf(&xenfb->c.xendev, 0, "%s: oops: convert %d -> %d bpp?\n",
 664                      __func__, xenfb->depth, bpp);
 665
 666    dpy_gfx_update(xenfb->con, x, y, w, h);
 667}
 668
 669#ifdef XENFB_TYPE_REFRESH_PERIOD
 670static int xenfb_queue_full(struct XenFB *xenfb)
 671{
 672    struct xenfb_page *page = xenfb->c.page;
 673    uint32_t cons, prod;
 674
 675    if (!page)
 676        return 1;
 677
 678    prod = page->in_prod;
 679    cons = page->in_cons;
 680    return prod - cons == XENFB_IN_RING_LEN;
 681}
 682
 683static void xenfb_send_event(struct XenFB *xenfb, union xenfb_in_event *event)
 684{
 685    uint32_t prod;
 686    struct xenfb_page *page = xenfb->c.page;
 687
 688    prod = page->in_prod;
 689    /* caller ensures !xenfb_queue_full() */
 690    xen_mb();                   /* ensure ring space available */
 691    XENFB_IN_RING_REF(page, prod) = *event;
 692    xen_wmb();                  /* ensure ring contents visible */
 693    page->in_prod = prod + 1;
 694
 695    xen_pv_send_notify(&xenfb->c.xendev);
 696}
 697
 698static void xenfb_send_refresh_period(struct XenFB *xenfb, int period)
 699{
 700    union xenfb_in_event event;
 701
 702    memset(&event, 0, sizeof(event));
 703    event.type = XENFB_TYPE_REFRESH_PERIOD;
 704    event.refresh_period.period = period;
 705    xenfb_send_event(xenfb, &event);
 706}
 707#endif
 708
 709/*
 710 * Periodic update of display.
 711 * Also transmit the refresh interval to the frontend.
 712 *
 713 * Never ever do any qemu display operations
 714 * (resize, screen update) outside this function.
 715 * Our screen might be inactive.  When asked for
 716 * an update we know it is active.
 717 */
 718static void xenfb_update(void *opaque)
 719{
 720    struct XenFB *xenfb = opaque;
 721    DisplaySurface *surface;
 722    int i;
 723
 724    if (xenfb->c.xendev.be_state != XenbusStateConnected)
 725        return;
 726
 727    if (!xenfb->feature_update) {
 728        /* we don't get update notifications, thus use the
 729         * sledge hammer approach ... */
 730        xenfb->up_fullscreen = 1;
 731    }
 732
 733    /* resize if needed */
 734    if (xenfb->do_resize) {
 735        pixman_format_code_t format;
 736
 737        xenfb->do_resize = 0;
 738        switch (xenfb->depth) {
 739        case 16:
 740        case 32:
 741            /* console.c supported depth -> buffer can be used directly */
 742            format = qemu_default_pixman_format(xenfb->depth, true);
 743            surface = qemu_create_displaysurface_from
 744                (xenfb->width, xenfb->height, format,
 745                 xenfb->row_stride, xenfb->pixels + xenfb->offset);
 746            break;
 747        default:
 748            /* we must convert stuff */
 749            surface = qemu_create_displaysurface(xenfb->width, xenfb->height);
 750            break;
 751        }
 752        dpy_gfx_replace_surface(xenfb->con, surface);
 753        xen_pv_printf(&xenfb->c.xendev, 1,
 754                      "update: resizing: %dx%d @ %d bpp%s\n",
 755                      xenfb->width, xenfb->height, xenfb->depth,
 756                      is_buffer_shared(surface) ? " (shared)" : "");
 757        xenfb->up_fullscreen = 1;
 758    }
 759
 760    /* run queued updates */
 761    if (xenfb->up_fullscreen) {
 762        xen_pv_printf(&xenfb->c.xendev, 3, "update: fullscreen\n");
 763        xenfb_guest_copy(xenfb, 0, 0, xenfb->width, xenfb->height);
 764    } else if (xenfb->up_count) {
 765        xen_pv_printf(&xenfb->c.xendev, 3, "update: %d rects\n",
 766                      xenfb->up_count);
 767        for (i = 0; i < xenfb->up_count; i++)
 768            xenfb_guest_copy(xenfb,
 769                             xenfb->up_rects[i].x,
 770                             xenfb->up_rects[i].y,
 771                             xenfb->up_rects[i].w,
 772                             xenfb->up_rects[i].h);
 773    } else {
 774        xen_pv_printf(&xenfb->c.xendev, 3, "update: nothing\n");
 775    }
 776    xenfb->up_count = 0;
 777    xenfb->up_fullscreen = 0;
 778}
 779
 780static void xenfb_update_interval(void *opaque, uint64_t interval)
 781{
 782    struct XenFB *xenfb = opaque;
 783
 784    if (xenfb->feature_update) {
 785#ifdef XENFB_TYPE_REFRESH_PERIOD
 786        if (xenfb_queue_full(xenfb)) {
 787            return;
 788        }
 789        xenfb_send_refresh_period(xenfb, interval);
 790#endif
 791    }
 792}
 793
 794/* QEMU display state changed, so refresh the framebuffer copy */
 795static void xenfb_invalidate(void *opaque)
 796{
 797    struct XenFB *xenfb = opaque;
 798    xenfb->up_fullscreen = 1;
 799}
 800
 801static void xenfb_handle_events(struct XenFB *xenfb)
 802{
 803    uint32_t prod, cons, out_cons;
 804    struct xenfb_page *page = xenfb->c.page;
 805
 806    prod = page->out_prod;
 807    out_cons = page->out_cons;
 808    if (prod - out_cons > XENFB_OUT_RING_LEN) {
 809        return;
 810    }
 811    xen_rmb();          /* ensure we see ring contents up to prod */
 812    for (cons = out_cons; cons != prod; cons++) {
 813        union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
 814        uint8_t type = event->type;
 815        int x, y, w, h;
 816
 817        switch (type) {
 818        case XENFB_TYPE_UPDATE:
 819            if (xenfb->up_count == UP_QUEUE)
 820                xenfb->up_fullscreen = 1;
 821            if (xenfb->up_fullscreen)
 822                break;
 823            x = MAX(event->update.x, 0);
 824            y = MAX(event->update.y, 0);
 825            w = MIN(event->update.width, xenfb->width - x);
 826            h = MIN(event->update.height, xenfb->height - y);
 827            if (w < 0 || h < 0) {
 828                xen_pv_printf(&xenfb->c.xendev, 1, "bogus update ignored\n");
 829                break;
 830            }
 831            if (x != event->update.x ||
 832                y != event->update.y ||
 833                w != event->update.width ||
 834                h != event->update.height) {
 835                xen_pv_printf(&xenfb->c.xendev, 1, "bogus update clipped\n");
 836            }
 837            if (w == xenfb->width && h > xenfb->height / 2) {
 838                /* scroll detector: updated more than 50% of the lines,
 839                 * don't bother keeping track of the rectangles then */
 840                xenfb->up_fullscreen = 1;
 841            } else {
 842                xenfb->up_rects[xenfb->up_count].x = x;
 843                xenfb->up_rects[xenfb->up_count].y = y;
 844                xenfb->up_rects[xenfb->up_count].w = w;
 845                xenfb->up_rects[xenfb->up_count].h = h;
 846                xenfb->up_count++;
 847            }
 848            break;
 849#ifdef XENFB_TYPE_RESIZE
 850        case XENFB_TYPE_RESIZE:
 851            if (xenfb_configure_fb(xenfb, xenfb->fb_len,
 852                                   event->resize.width,
 853                                   event->resize.height,
 854                                   event->resize.depth,
 855                                   xenfb->fb_len,
 856                                   event->resize.offset,
 857                                   event->resize.stride) < 0)
 858                break;
 859            xenfb_invalidate(xenfb);
 860            break;
 861#endif
 862        }
 863    }
 864    xen_mb();           /* ensure we're done with ring contents */
 865    page->out_cons = cons;
 866}
 867
 868static int fb_init(struct XenLegacyDevice *xendev)
 869{
 870#ifdef XENFB_TYPE_RESIZE
 871    xenstore_write_be_int(xendev, "feature-resize", 1);
 872#endif
 873    return 0;
 874}
 875
 876static int fb_initialise(struct XenLegacyDevice *xendev)
 877{
 878    struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
 879    struct xenfb_page *fb_page;
 880    int videoram;
 881    int rc;
 882
 883    if (xenstore_read_fe_int(xendev, "videoram", &videoram) == -1)
 884        videoram = 0;
 885
 886    rc = common_bind(&fb->c);
 887    if (rc != 0)
 888        return rc;
 889
 890    fb_page = fb->c.page;
 891    rc = xenfb_configure_fb(fb, videoram * MiB,
 892                            fb_page->width, fb_page->height, fb_page->depth,
 893                            fb_page->mem_length, 0, fb_page->line_length);
 894    if (rc != 0)
 895        return rc;
 896
 897    rc = xenfb_map_fb(fb);
 898    if (rc != 0)
 899        return rc;
 900
 901    fb->con = graphic_console_init(NULL, 0, &xenfb_ops, fb);
 902
 903    if (xenstore_read_fe_int(xendev, "feature-update", &fb->feature_update) == -1)
 904        fb->feature_update = 0;
 905    if (fb->feature_update)
 906        xenstore_write_be_int(xendev, "request-update", 1);
 907
 908    xen_pv_printf(xendev, 1, "feature-update=%d, videoram=%d\n",
 909                  fb->feature_update, videoram);
 910    return 0;
 911}
 912
 913static void fb_disconnect(struct XenLegacyDevice *xendev)
 914{
 915    struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
 916
 917    /*
 918     * FIXME: qemu can't un-init gfx display (yet?).
 919     *   Replacing the framebuffer with anonymous shared memory
 920     *   instead.  This releases the guest pages and keeps qemu happy.
 921     */
 922    xenforeignmemory_unmap(xen_fmem, fb->pixels, fb->fbpages);
 923    fb->pixels = mmap(fb->pixels, fb->fbpages * XC_PAGE_SIZE,
 924                      PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON,
 925                      -1, 0);
 926    if (fb->pixels == MAP_FAILED) {
 927        xen_pv_printf(xendev, 0,
 928                "Couldn't replace the framebuffer with anonymous memory errno=%d\n",
 929                errno);
 930    }
 931    common_unbind(&fb->c);
 932    fb->feature_update = 0;
 933    fb->bug_trigger    = 0;
 934}
 935
 936static void fb_frontend_changed(struct XenLegacyDevice *xendev,
 937                                const char *node)
 938{
 939    struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev);
 940
 941    /*
 942     * Set state to Connected *again* once the frontend switched
 943     * to connected.  We must trigger the watch a second time to
 944     * workaround a frontend bug.
 945     */
 946    if (fb->bug_trigger == 0 && strcmp(node, "state") == 0 &&
 947        xendev->fe_state == XenbusStateConnected &&
 948        xendev->be_state == XenbusStateConnected) {
 949        xen_pv_printf(xendev, 2, "re-trigger connected (frontend bug)\n");
 950        xen_be_set_state(xendev, XenbusStateConnected);
 951        fb->bug_trigger = 1; /* only once */
 952    }
 953}
 954
 955static void fb_event(struct XenLegacyDevice *xendev)
 956{
 957    struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev);
 958
 959    xenfb_handle_events(xenfb);
 960    xen_pv_send_notify(&xenfb->c.xendev);
 961}
 962
 963/* -------------------------------------------------------------------- */
 964
 965struct XenDevOps xen_kbdmouse_ops = {
 966    .size       = sizeof(struct XenInput),
 967    .init       = input_init,
 968    .initialise = input_initialise,
 969    .connected  = input_connected,
 970    .disconnect = input_disconnect,
 971    .event      = input_event,
 972};
 973
 974struct XenDevOps xen_framebuffer_ops = {
 975    .size       = sizeof(struct XenFB),
 976    .init       = fb_init,
 977    .initialise = fb_initialise,
 978    .disconnect = fb_disconnect,
 979    .event      = fb_event,
 980    .frontend_changed = fb_frontend_changed,
 981};
 982
 983static const GraphicHwOps xenfb_ops = {
 984    .invalidate  = xenfb_invalidate,
 985    .gfx_update  = xenfb_update,
 986    .update_interval = xenfb_update_interval,
 987};
 988