2 * xen paravirt framebuffer backend
4 * Copyright IBM, Corp. 2005-2006
5 * Copyright Red Hat, Inc. 2006-2008
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>
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.
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.
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/>.
27 #include "qemu/osdep.h"
28 #include "qemu/units.h"
31 #include "ui/console.h"
32 #include "sysemu/sysemu.h"
33 #include "hw/xen/xen-legacy-backend.h"
35 #include "hw/xen/interface/io/fbif.h"
36 #include "hw/xen/interface/io/kbdif.h"
37 #include "hw/xen/interface/io/protocols.h"
42 #define BTN_LEFT 0x110 /* from <linux/input.h> */
45 /* -------------------------------------------------------------------- */
48 struct XenLegacyDevice xendev
; /* must be first */
54 int abs_pointer_wanted
; /* Whether guest supports absolute pointer */
55 int raw_pointer_wanted
; /* Whether guest supports raw (unscaled) pointer */
56 QemuInputHandlerState
*qkbd
;
57 QemuInputHandlerState
*qmou
;
58 int axis
[INPUT_AXIS__MAX
];
85 static const GraphicHwOps xenfb_ops
;
87 /* -------------------------------------------------------------------- */
89 static int common_bind(struct common
*c
)
94 if (xenstore_read_fe_uint64(&c
->xendev
, "page-ref", &val
) == -1)
99 if (xenstore_read_fe_int(&c
->xendev
, "event-channel", &c
->xendev
.remote_port
) == -1)
102 c
->page
= qemu_xen_foreignmem_map(c
->xendev
.dom
, NULL
,
103 PROT_READ
| PROT_WRITE
, 1, &mfn
,
108 xen_be_bind_evtchn(&c
->xendev
);
109 xen_pv_printf(&c
->xendev
, 1,
110 "ring mfn %"PRI_xen_pfn
", remote-port %d, local-port %d\n",
111 mfn
, c
->xendev
.remote_port
, c
->xendev
.local_port
);
116 static void common_unbind(struct common
*c
)
118 xen_pv_unbind_evtchn(&c
->xendev
);
120 qemu_xen_foreignmem_unmap(c
->page
, 1);
125 /* -------------------------------------------------------------------- */
126 /* Send an event to the keyboard frontend driver */
127 static int xenfb_kbd_event(struct XenInput
*xenfb
,
128 union xenkbd_in_event
*event
)
130 struct xenkbd_page
*page
= xenfb
->c
.page
;
133 if (xenfb
->c
.xendev
.be_state
!= XenbusStateConnected
)
138 prod
= page
->in_prod
;
139 if (prod
- page
->in_cons
== XENKBD_IN_RING_LEN
) {
144 xen_mb(); /* ensure ring space available */
145 XENKBD_IN_RING_REF(page
, prod
) = *event
;
146 xen_wmb(); /* ensure ring contents visible */
147 page
->in_prod
= prod
+ 1;
148 return xen_pv_send_notify(&xenfb
->c
.xendev
);
151 /* Send a keyboard (or mouse button) event */
152 static int xenfb_send_key(struct XenInput
*xenfb
, bool down
, int keycode
)
154 union xenkbd_in_event event
;
156 memset(&event
, 0, XENKBD_IN_EVENT_SIZE
);
157 event
.type
= XENKBD_TYPE_KEY
;
158 event
.key
.pressed
= down
? 1 : 0;
159 event
.key
.keycode
= keycode
;
161 return xenfb_kbd_event(xenfb
, &event
);
164 /* Send a relative mouse movement event */
165 static int xenfb_send_motion(struct XenInput
*xenfb
,
166 int rel_x
, int rel_y
, int rel_z
)
168 union xenkbd_in_event event
;
170 memset(&event
, 0, XENKBD_IN_EVENT_SIZE
);
171 event
.type
= XENKBD_TYPE_MOTION
;
172 event
.motion
.rel_x
= rel_x
;
173 event
.motion
.rel_y
= rel_y
;
174 event
.motion
.rel_z
= rel_z
;
176 return xenfb_kbd_event(xenfb
, &event
);
179 /* Send an absolute mouse movement event */
180 static int xenfb_send_position(struct XenInput
*xenfb
,
181 int abs_x
, int abs_y
, int z
)
183 union xenkbd_in_event event
;
185 memset(&event
, 0, XENKBD_IN_EVENT_SIZE
);
186 event
.type
= XENKBD_TYPE_POS
;
187 event
.pos
.abs_x
= abs_x
;
188 event
.pos
.abs_y
= abs_y
;
191 return xenfb_kbd_event(xenfb
, &event
);
195 * Send a key event from the client to the guest OS
196 * QEMU gives us a QCode.
197 * We have to turn this into a Linux Input layer keycode.
199 * Wish we could just send scancodes straight to the guest which
200 * already has code for dealing with this...
202 static void xenfb_key_event(DeviceState
*dev
, QemuConsole
*src
,
205 struct XenInput
*xenfb
= (struct XenInput
*)dev
;
206 InputKeyEvent
*key
= evt
->u
.key
.data
;
207 int qcode
= qemu_input_key_value_to_qcode(key
->key
);
210 if (qcode
< qemu_input_map_qcode_to_linux_len
) {
211 lnx
= qemu_input_map_qcode_to_linux
[qcode
];
214 trace_xenfb_key_event(xenfb
, lnx
, key
->down
);
215 xenfb_send_key(xenfb
, key
->down
, lnx
);
221 * Send a mouse event from the client to the guest OS
223 * The QEMU mouse can be in either relative, or absolute mode.
224 * Movement is sent separately from button state, which has to
225 * be encoded as virtual key events. We also don't actually get
226 * given any button up/down events, so have to track changes in
229 static void xenfb_mouse_event(DeviceState
*dev
, QemuConsole
*src
,
232 struct XenInput
*xenfb
= (struct XenInput
*)dev
;
234 InputMoveEvent
*move
;
236 DisplaySurface
*surface
;
240 case INPUT_EVENT_KIND_BTN
:
241 btn
= evt
->u
.btn
.data
;
242 switch (btn
->button
) {
243 case INPUT_BUTTON_LEFT
:
244 xenfb_send_key(xenfb
, btn
->down
, BTN_LEFT
);
246 case INPUT_BUTTON_RIGHT
:
247 xenfb_send_key(xenfb
, btn
->down
, BTN_LEFT
+ 1);
249 case INPUT_BUTTON_MIDDLE
:
250 xenfb_send_key(xenfb
, btn
->down
, BTN_LEFT
+ 2);
252 case INPUT_BUTTON_WHEEL_UP
:
257 case INPUT_BUTTON_WHEEL_DOWN
:
267 case INPUT_EVENT_KIND_ABS
:
268 move
= evt
->u
.abs
.data
;
269 if (xenfb
->raw_pointer_wanted
) {
270 xenfb
->axis
[move
->axis
] = move
->value
;
272 con
= qemu_console_lookup_by_index(0);
274 xen_pv_printf(&xenfb
->c
.xendev
, 0, "No QEMU console available");
277 surface
= qemu_console_surface(con
);
278 switch (move
->axis
) {
280 scale
= surface_width(surface
) - 1;
283 scale
= surface_height(surface
) - 1;
289 xenfb
->axis
[move
->axis
] = move
->value
* scale
/ 0x7fff;
293 case INPUT_EVENT_KIND_REL
:
294 move
= evt
->u
.rel
.data
;
295 xenfb
->axis
[move
->axis
] += move
->value
;
303 static void xenfb_mouse_sync(DeviceState
*dev
)
305 struct XenInput
*xenfb
= (struct XenInput
*)dev
;
307 trace_xenfb_mouse_event(xenfb
, xenfb
->axis
[INPUT_AXIS_X
],
308 xenfb
->axis
[INPUT_AXIS_Y
],
310 xenfb
->abs_pointer_wanted
);
311 if (xenfb
->abs_pointer_wanted
) {
312 xenfb_send_position(xenfb
, xenfb
->axis
[INPUT_AXIS_X
],
313 xenfb
->axis
[INPUT_AXIS_Y
],
316 xenfb_send_motion(xenfb
, xenfb
->axis
[INPUT_AXIS_X
],
317 xenfb
->axis
[INPUT_AXIS_Y
],
319 xenfb
->axis
[INPUT_AXIS_X
] = 0;
320 xenfb
->axis
[INPUT_AXIS_Y
] = 0;
325 static const QemuInputHandler xenfb_keyboard
= {
326 .name
= "Xen PV Keyboard",
327 .mask
= INPUT_EVENT_MASK_KEY
,
328 .event
= xenfb_key_event
,
331 static const QemuInputHandler xenfb_abs_mouse
= {
332 .name
= "Xen PV Mouse",
333 .mask
= INPUT_EVENT_MASK_BTN
| INPUT_EVENT_MASK_ABS
,
334 .event
= xenfb_mouse_event
,
335 .sync
= xenfb_mouse_sync
,
338 static const QemuInputHandler xenfb_rel_mouse
= {
339 .name
= "Xen PV Mouse",
340 .mask
= INPUT_EVENT_MASK_BTN
| INPUT_EVENT_MASK_REL
,
341 .event
= xenfb_mouse_event
,
342 .sync
= xenfb_mouse_sync
,
345 static int input_init(struct XenLegacyDevice
*xendev
)
347 xenstore_write_be_int(xendev
, "feature-abs-pointer", 1);
348 xenstore_write_be_int(xendev
, "feature-raw-pointer", 1);
352 static int input_initialise(struct XenLegacyDevice
*xendev
)
354 struct XenInput
*in
= container_of(xendev
, struct XenInput
, c
.xendev
);
357 rc
= common_bind(&in
->c
);
364 static void input_connected(struct XenLegacyDevice
*xendev
)
366 struct XenInput
*in
= container_of(xendev
, struct XenInput
, c
.xendev
);
368 if (xenstore_read_fe_int(xendev
, "request-abs-pointer",
369 &in
->abs_pointer_wanted
) == -1) {
370 in
->abs_pointer_wanted
= 0;
372 if (xenstore_read_fe_int(xendev
, "request-raw-pointer",
373 &in
->raw_pointer_wanted
) == -1) {
374 in
->raw_pointer_wanted
= 0;
376 if (in
->raw_pointer_wanted
&& in
->abs_pointer_wanted
== 0) {
377 xen_pv_printf(xendev
, 0, "raw pointer set without abs pointer");
381 qemu_input_handler_unregister(in
->qkbd
);
384 qemu_input_handler_unregister(in
->qmou
);
386 trace_xenfb_input_connected(xendev
, in
->abs_pointer_wanted
);
388 in
->qkbd
= qemu_input_handler_register((DeviceState
*)in
, &xenfb_keyboard
);
389 in
->qmou
= qemu_input_handler_register((DeviceState
*)in
,
390 in
->abs_pointer_wanted
? &xenfb_abs_mouse
: &xenfb_rel_mouse
);
392 if (in
->raw_pointer_wanted
) {
393 qemu_input_handler_activate(in
->qkbd
);
394 qemu_input_handler_activate(in
->qmou
);
398 static void input_disconnect(struct XenLegacyDevice
*xendev
)
400 struct XenInput
*in
= container_of(xendev
, struct XenInput
, c
.xendev
);
403 qemu_input_handler_unregister(in
->qkbd
);
407 qemu_input_handler_unregister(in
->qmou
);
410 common_unbind(&in
->c
);
413 static void input_event(struct XenLegacyDevice
*xendev
)
415 struct XenInput
*xenfb
= container_of(xendev
, struct XenInput
, c
.xendev
);
416 struct xenkbd_page
*page
= xenfb
->c
.page
;
418 /* We don't understand any keyboard events, so just ignore them. */
419 if (page
->out_prod
== page
->out_cons
)
421 page
->out_cons
= page
->out_prod
;
422 xen_pv_send_notify(&xenfb
->c
.xendev
);
425 /* -------------------------------------------------------------------- */
427 static void xenfb_copy_mfns(int mode
, int count
, xen_pfn_t
*dst
, void *src
)
429 uint32_t *src32
= src
;
430 uint64_t *src64
= src
;
433 for (i
= 0; i
< count
; i
++)
434 dst
[i
] = (mode
== 32) ? src32
[i
] : src64
[i
];
437 static int xenfb_map_fb(struct XenFB
*xenfb
)
439 struct xenfb_page
*page
= xenfb
->c
.page
;
440 char *protocol
= xenfb
->c
.xendev
.protocol
;
442 xen_pfn_t
*pgmfns
= NULL
;
443 xen_pfn_t
*fbmfns
= NULL
;
447 /* default to native */
449 mode
= sizeof(unsigned long) * 8;
453 * Undefined protocol, some guesswork needed.
455 * Old frontends which don't set the protocol use
456 * one page directory only, thus pd[1] must be zero.
457 * pd[1] of the 32bit struct layout and the lower
458 * 32 bits of pd[0] of the 64bit struct layout have
459 * the same location, so we can check that ...
461 uint32_t *ptr32
= NULL
;
462 uint32_t *ptr64
= NULL
;
463 #if defined(__i386__)
464 ptr32
= (void*)page
->pd
;
465 ptr64
= ((void*)page
->pd
) + 4;
466 #elif defined(__x86_64__)
467 ptr32
= ((void*)page
->pd
) - 4;
468 ptr64
= (void*)page
->pd
;
479 #if defined(__x86_64__)
480 } else if (strcmp(protocol
, XEN_IO_PROTO_ABI_X86_32
) == 0) {
481 /* 64bit dom0, 32bit domU */
483 pd
= ((void*)page
->pd
) - 4;
484 #elif defined(__i386__)
485 } else if (strcmp(protocol
, XEN_IO_PROTO_ABI_X86_64
) == 0) {
486 /* 32bit dom0, 64bit domU */
488 pd
= ((void*)page
->pd
) + 4;
493 munmap(xenfb
->pixels
, xenfb
->fbpages
* XEN_PAGE_SIZE
);
494 xenfb
->pixels
= NULL
;
497 xenfb
->fbpages
= DIV_ROUND_UP(xenfb
->fb_len
, XEN_PAGE_SIZE
);
498 n_fbdirs
= xenfb
->fbpages
* mode
/ 8;
499 n_fbdirs
= DIV_ROUND_UP(n_fbdirs
, XEN_PAGE_SIZE
);
501 pgmfns
= g_new0(xen_pfn_t
, n_fbdirs
);
502 fbmfns
= g_new0(xen_pfn_t
, xenfb
->fbpages
);
504 xenfb_copy_mfns(mode
, n_fbdirs
, pgmfns
, pd
);
505 map
= qemu_xen_foreignmem_map(xenfb
->c
.xendev
.dom
, NULL
, PROT_READ
,
506 n_fbdirs
, pgmfns
, NULL
);
509 xenfb_copy_mfns(mode
, xenfb
->fbpages
, fbmfns
, map
);
510 qemu_xen_foreignmem_unmap(map
, n_fbdirs
);
512 xenfb
->pixels
= qemu_xen_foreignmem_map(xenfb
->c
.xendev
.dom
, NULL
,
513 PROT_READ
, xenfb
->fbpages
,
515 if (xenfb
->pixels
== NULL
)
518 ret
= 0; /* all is fine */
526 static int xenfb_configure_fb(struct XenFB
*xenfb
, size_t fb_len_lim
,
527 int width
, int height
, int depth
,
528 size_t fb_len
, int offset
, int row_stride
)
530 size_t mfn_sz
= sizeof_field(struct xenfb_page
, pd
[0]);
531 size_t pd_len
= sizeof_field(struct xenfb_page
, pd
) / mfn_sz
;
532 size_t fb_pages
= pd_len
* XEN_PAGE_SIZE
/ mfn_sz
;
533 size_t fb_len_max
= fb_pages
* XEN_PAGE_SIZE
;
534 int max_width
, max_height
;
536 if (fb_len_lim
> fb_len_max
) {
537 xen_pv_printf(&xenfb
->c
.xendev
, 0,
538 "fb size limit %zu exceeds %zu, corrected\n",
539 fb_len_lim
, fb_len_max
);
540 fb_len_lim
= fb_len_max
;
542 if (fb_len_lim
&& fb_len
> fb_len_lim
) {
543 xen_pv_printf(&xenfb
->c
.xendev
, 0,
544 "frontend fb size %zu limited to %zu\n",
548 if (depth
!= 8 && depth
!= 16 && depth
!= 24 && depth
!= 32) {
549 xen_pv_printf(&xenfb
->c
.xendev
, 0,
550 "can't handle frontend fb depth %d\n",
554 if (row_stride
<= 0 || row_stride
> fb_len
) {
555 xen_pv_printf(&xenfb
->c
.xendev
, 0, "invalid frontend stride %d\n",
559 max_width
= row_stride
/ (depth
/ 8);
560 if (width
< 0 || width
> max_width
) {
561 xen_pv_printf(&xenfb
->c
.xendev
, 0,
562 "invalid frontend width %d limited to %d\n",
566 if (offset
< 0 || offset
>= fb_len
) {
567 xen_pv_printf(&xenfb
->c
.xendev
, 0,
568 "invalid frontend offset %d (max %zu)\n",
572 max_height
= (fb_len
- offset
) / row_stride
;
573 if (height
< 0 || height
> max_height
) {
574 xen_pv_printf(&xenfb
->c
.xendev
, 0,
575 "invalid frontend height %d limited to %d\n",
579 xenfb
->fb_len
= fb_len
;
580 xenfb
->row_stride
= row_stride
;
581 xenfb
->depth
= depth
;
582 xenfb
->width
= width
;
583 xenfb
->height
= height
;
584 xenfb
->offset
= offset
;
585 xenfb
->up_fullscreen
= 1;
586 xenfb
->do_resize
= 1;
587 xen_pv_printf(&xenfb
->c
.xendev
, 1,
588 "framebuffer %dx%dx%d offset %d stride %d\n",
589 width
, height
, depth
, offset
, row_stride
);
593 /* A convenient function for munging pixels between different depths */
594 #define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \
595 for (line = y ; line < (y+h) ; line++) { \
596 SRC_T *src = (SRC_T *)(xenfb->pixels \
598 + (line * xenfb->row_stride) \
599 + (x * xenfb->depth / 8)); \
600 DST_T *dst = (DST_T *)(data \
601 + (line * linesize) \
604 const int RSS = 32 - (RSB + GSB + BSB); \
605 const int GSS = 32 - (GSB + BSB); \
606 const int BSS = 32 - (BSB); \
607 const uint32_t RSM = (~0U) << (32 - RSB); \
608 const uint32_t GSM = (~0U) << (32 - GSB); \
609 const uint32_t BSM = (~0U) << (32 - BSB); \
610 const int RDS = 32 - (RDB + GDB + BDB); \
611 const int GDS = 32 - (GDB + BDB); \
612 const int BDS = 32 - (BDB); \
613 const uint32_t RDM = (~0U) << (32 - RDB); \
614 const uint32_t GDM = (~0U) << (32 - GDB); \
615 const uint32_t BDM = (~0U) << (32 - BDB); \
616 for (col = x ; col < (x+w) ; col++) { \
617 uint32_t spix = *src; \
618 *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \
619 (((spix << GSS) & GSM & GDM) >> GDS) | \
620 (((spix << BSS) & BSM & BDM) >> BDS); \
621 src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \
622 dst = (DST_T *) ((unsigned long) dst + bpp / 8); \
628 * This copies data from the guest framebuffer region, into QEMU's
629 * displaysurface. qemu uses 16 or 32 bpp. In case the pv framebuffer
630 * uses something else we must convert and copy, otherwise we can
631 * supply the buffer directly and no thing here.
633 static void xenfb_guest_copy(struct XenFB
*xenfb
, int x
, int y
, int w
, int h
)
635 DisplaySurface
*surface
= qemu_console_surface(xenfb
->con
);
637 int bpp
= surface_bits_per_pixel(surface
);
638 int linesize
= surface_stride(surface
);
639 uint8_t *data
= surface_data(surface
);
641 if (surface_is_allocated(surface
)) {
642 switch (xenfb
->depth
) {
645 BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5);
646 } else if (bpp
== 32) {
647 BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8);
654 BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5);
655 } else if (bpp
== 32) {
656 BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8);
665 if (oops
) /* should not happen */
666 xen_pv_printf(&xenfb
->c
.xendev
, 0, "%s: oops: convert %d -> %d bpp?\n",
667 __func__
, xenfb
->depth
, bpp
);
669 dpy_gfx_update(xenfb
->con
, x
, y
, w
, h
);
672 #ifdef XENFB_TYPE_REFRESH_PERIOD
673 static int xenfb_queue_full(struct XenFB
*xenfb
)
675 struct xenfb_page
*page
= xenfb
->c
.page
;
681 prod
= page
->in_prod
;
682 cons
= page
->in_cons
;
683 return prod
- cons
== XENFB_IN_RING_LEN
;
686 static void xenfb_send_event(struct XenFB
*xenfb
, union xenfb_in_event
*event
)
689 struct xenfb_page
*page
= xenfb
->c
.page
;
691 prod
= page
->in_prod
;
692 /* caller ensures !xenfb_queue_full() */
693 xen_mb(); /* ensure ring space available */
694 XENFB_IN_RING_REF(page
, prod
) = *event
;
695 xen_wmb(); /* ensure ring contents visible */
696 page
->in_prod
= prod
+ 1;
698 xen_pv_send_notify(&xenfb
->c
.xendev
);
701 static void xenfb_send_refresh_period(struct XenFB
*xenfb
, int period
)
703 union xenfb_in_event event
;
705 memset(&event
, 0, sizeof(event
));
706 event
.type
= XENFB_TYPE_REFRESH_PERIOD
;
707 event
.refresh_period
.period
= period
;
708 xenfb_send_event(xenfb
, &event
);
713 * Periodic update of display.
714 * Also transmit the refresh interval to the frontend.
716 * Never ever do any qemu display operations
717 * (resize, screen update) outside this function.
718 * Our screen might be inactive. When asked for
719 * an update we know it is active.
721 static void xenfb_update(void *opaque
)
723 struct XenFB
*xenfb
= opaque
;
724 DisplaySurface
*surface
;
727 if (xenfb
->c
.xendev
.be_state
!= XenbusStateConnected
)
730 if (!xenfb
->feature_update
) {
731 /* we don't get update notifications, thus use the
732 * sledge hammer approach ... */
733 xenfb
->up_fullscreen
= 1;
736 /* resize if needed */
737 if (xenfb
->do_resize
) {
738 pixman_format_code_t format
;
740 xenfb
->do_resize
= 0;
741 switch (xenfb
->depth
) {
744 /* console.c supported depth -> buffer can be used directly */
745 format
= qemu_default_pixman_format(xenfb
->depth
, true);
746 surface
= qemu_create_displaysurface_from
747 (xenfb
->width
, xenfb
->height
, format
,
748 xenfb
->row_stride
, xenfb
->pixels
+ xenfb
->offset
);
751 /* we must convert stuff */
752 surface
= qemu_create_displaysurface(xenfb
->width
, xenfb
->height
);
755 dpy_gfx_replace_surface(xenfb
->con
, surface
);
756 xen_pv_printf(&xenfb
->c
.xendev
, 1,
757 "update: resizing: %dx%d @ %d bpp%s\n",
758 xenfb
->width
, xenfb
->height
, xenfb
->depth
,
759 surface_is_allocated(surface
)
760 ? " (allocated)" : " (borrowed)");
761 xenfb
->up_fullscreen
= 1;
764 /* run queued updates */
765 if (xenfb
->up_fullscreen
) {
766 xen_pv_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_pv_printf(&xenfb
->c
.xendev
, 3, "update: %d rects\n",
771 for (i
= 0; i
< xenfb
->up_count
; i
++)
772 xenfb_guest_copy(xenfb
,
773 xenfb
->up_rects
[i
].x
,
774 xenfb
->up_rects
[i
].y
,
775 xenfb
->up_rects
[i
].w
,
776 xenfb
->up_rects
[i
].h
);
778 xen_pv_printf(&xenfb
->c
.xendev
, 3, "update: nothing\n");
781 xenfb
->up_fullscreen
= 0;
784 static void xenfb_ui_info(void *opaque
, uint32_t idx
, QemuUIInfo
*info
)
786 struct XenFB
*xenfb
= opaque
;
787 uint32_t refresh_rate
;
789 if (xenfb
->feature_update
) {
790 #ifdef XENFB_TYPE_REFRESH_PERIOD
791 if (xenfb_queue_full(xenfb
)) {
795 refresh_rate
= info
->refresh_rate
;
800 /* T = 1 / f = 1 [s*Hz] / f = 1000*1000 [ms*mHz] / f */
801 xenfb_send_refresh_period(xenfb
, 1000 * 1000 / refresh_rate
);
806 /* QEMU display state changed, so refresh the framebuffer copy */
807 static void xenfb_invalidate(void *opaque
)
809 struct XenFB
*xenfb
= opaque
;
810 xenfb
->up_fullscreen
= 1;
813 static void xenfb_handle_events(struct XenFB
*xenfb
)
815 uint32_t prod
, cons
, out_cons
;
816 struct xenfb_page
*page
= xenfb
->c
.page
;
818 prod
= page
->out_prod
;
819 out_cons
= page
->out_cons
;
820 if (prod
- out_cons
> XENFB_OUT_RING_LEN
) {
823 xen_rmb(); /* ensure we see ring contents up to prod */
824 for (cons
= out_cons
; cons
!= prod
; cons
++) {
825 union xenfb_out_event
*event
= &XENFB_OUT_RING_REF(page
, cons
);
826 uint8_t type
= event
->type
;
830 case XENFB_TYPE_UPDATE
:
831 if (xenfb
->up_count
== UP_QUEUE
)
832 xenfb
->up_fullscreen
= 1;
833 if (xenfb
->up_fullscreen
)
835 x
= MAX(event
->update
.x
, 0);
836 y
= MAX(event
->update
.y
, 0);
837 w
= MIN(event
->update
.width
, xenfb
->width
- x
);
838 h
= MIN(event
->update
.height
, xenfb
->height
- y
);
839 if (w
< 0 || h
< 0) {
840 xen_pv_printf(&xenfb
->c
.xendev
, 1, "bogus update ignored\n");
843 if (x
!= event
->update
.x
||
844 y
!= event
->update
.y
||
845 w
!= event
->update
.width
||
846 h
!= event
->update
.height
) {
847 xen_pv_printf(&xenfb
->c
.xendev
, 1, "bogus update clipped\n");
849 if (w
== xenfb
->width
&& h
> xenfb
->height
/ 2) {
850 /* scroll detector: updated more than 50% of the lines,
851 * don't bother keeping track of the rectangles then */
852 xenfb
->up_fullscreen
= 1;
854 xenfb
->up_rects
[xenfb
->up_count
].x
= x
;
855 xenfb
->up_rects
[xenfb
->up_count
].y
= y
;
856 xenfb
->up_rects
[xenfb
->up_count
].w
= w
;
857 xenfb
->up_rects
[xenfb
->up_count
].h
= h
;
861 #ifdef XENFB_TYPE_RESIZE
862 case XENFB_TYPE_RESIZE
:
863 if (xenfb_configure_fb(xenfb
, xenfb
->fb_len
,
865 event
->resize
.height
,
868 event
->resize
.offset
,
869 event
->resize
.stride
) < 0)
871 xenfb_invalidate(xenfb
);
876 xen_mb(); /* ensure we're done with ring contents */
877 page
->out_cons
= cons
;
880 static int fb_init(struct XenLegacyDevice
*xendev
)
882 #ifdef XENFB_TYPE_RESIZE
883 xenstore_write_be_int(xendev
, "feature-resize", 1);
888 static int fb_initialise(struct XenLegacyDevice
*xendev
)
890 struct XenFB
*fb
= container_of(xendev
, struct XenFB
, c
.xendev
);
891 struct xenfb_page
*fb_page
;
895 if (xenstore_read_fe_int(xendev
, "videoram", &videoram
) == -1)
898 rc
= common_bind(&fb
->c
);
902 fb_page
= fb
->c
.page
;
903 rc
= xenfb_configure_fb(fb
, videoram
* MiB
,
904 fb_page
->width
, fb_page
->height
, fb_page
->depth
,
905 fb_page
->mem_length
, 0, fb_page
->line_length
);
909 rc
= xenfb_map_fb(fb
);
913 fb
->con
= graphic_console_init(NULL
, 0, &xenfb_ops
, fb
);
915 if (xenstore_read_fe_int(xendev
, "feature-update", &fb
->feature_update
) == -1)
916 fb
->feature_update
= 0;
917 if (fb
->feature_update
)
918 xenstore_write_be_int(xendev
, "request-update", 1);
920 xen_pv_printf(xendev
, 1, "feature-update=%d, videoram=%d\n",
921 fb
->feature_update
, videoram
);
925 static void fb_disconnect(struct XenLegacyDevice
*xendev
)
927 struct XenFB
*fb
= container_of(xendev
, struct XenFB
, c
.xendev
);
930 * FIXME: qemu can't un-init gfx display (yet?).
931 * Replacing the framebuffer with anonymous shared memory
932 * instead. This releases the guest pages and keeps qemu happy.
934 qemu_xen_foreignmem_unmap(fb
->pixels
, fb
->fbpages
);
935 fb
->pixels
= mmap(fb
->pixels
, fb
->fbpages
* XEN_PAGE_SIZE
,
936 PROT_READ
| PROT_WRITE
, MAP_SHARED
| MAP_ANON
,
938 if (fb
->pixels
== MAP_FAILED
) {
939 xen_pv_printf(xendev
, 0,
940 "Couldn't replace the framebuffer with anonymous memory errno=%d\n",
943 common_unbind(&fb
->c
);
944 fb
->feature_update
= 0;
948 static void fb_frontend_changed(struct XenLegacyDevice
*xendev
,
951 struct XenFB
*fb
= container_of(xendev
, struct XenFB
, c
.xendev
);
954 * Set state to Connected *again* once the frontend switched
955 * to connected. We must trigger the watch a second time to
956 * workaround a frontend bug.
958 if (fb
->bug_trigger
== 0 && strcmp(node
, "state") == 0 &&
959 xendev
->fe_state
== XenbusStateConnected
&&
960 xendev
->be_state
== XenbusStateConnected
) {
961 xen_pv_printf(xendev
, 2, "re-trigger connected (frontend bug)\n");
962 xen_be_set_state(xendev
, XenbusStateConnected
);
963 fb
->bug_trigger
= 1; /* only once */
967 static void fb_event(struct XenLegacyDevice
*xendev
)
969 struct XenFB
*xenfb
= container_of(xendev
, struct XenFB
, c
.xendev
);
971 xenfb_handle_events(xenfb
);
972 xen_pv_send_notify(&xenfb
->c
.xendev
);
975 /* -------------------------------------------------------------------- */
977 static const struct XenDevOps xen_kbdmouse_ops
= {
978 .size
= sizeof(struct XenInput
),
980 .initialise
= input_initialise
,
981 .connected
= input_connected
,
982 .disconnect
= input_disconnect
,
983 .event
= input_event
,
986 const struct XenDevOps xen_framebuffer_ops
= {
987 .size
= sizeof(struct XenFB
),
989 .initialise
= fb_initialise
,
990 .disconnect
= fb_disconnect
,
992 .frontend_changed
= fb_frontend_changed
,
995 static const GraphicHwOps xenfb_ops
= {
996 .invalidate
= xenfb_invalidate
,
997 .gfx_update
= xenfb_update
,
998 .ui_info
= xenfb_ui_info
,
1001 static void xen_ui_register_backend(void)
1003 xen_be_register("vkbd", &xen_kbdmouse_ops
);
1005 if (vga_interface_type
== VGA_XENFB
) {
1006 xen_be_register("vfb", &xen_framebuffer_ops
);
1009 xen_backend_init(xen_ui_register_backend
);