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