cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

xenfb.c (29758B)


      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};