2 * Palacios VM Graphics Console Interface (shared framebuffer between palacios and host)
3 * Copyright (c) 2011 Peter Dinda <pdinda@northwestern.edu>
6 #include <linux/device.h>
7 #include <linux/cdev.h>
8 #include <linux/errno.h>
10 #include <linux/uaccess.h>
11 #include <linux/poll.h>
12 #include <linux/anon_inodes.h>
13 #include <linux/file.h>
14 #include <linux/sched.h>
16 #include <interfaces/vmm_console.h>
17 #include <palacios/vmm_host_events.h>
18 #include "iface-graphics-console.h"
22 #include "linux-exts.h"
28 This is an implementation of the Palacios Graphics Console interface that
29 is designed to interact with a vnc server running in user space,
30 typically something based on x0vncserver.
32 The basic idea is that we manage a frame buffer that we share with
33 palacios. Palacios draws whatever it likes on it.
34 The user-land vncserver will send us requests for fb updates, which
35 we implement by copying the FB to it. When the user-land sends us characters, etc,
36 we deliver those immediately to palacios via the deliver_key and deliver_mouse
37 event interfaces. The end-result is that whatever the graphics system
38 in palacios renders is visible via vnc.
43 static struct list_head global_gcons;
45 struct palacios_graphics_console {
46 // descriptor for the data in the shared frame buffer
47 struct v3_frame_buffer_spec spec;
49 // the actual shared frame buffer
50 // Note that "shared" here means shared between palacios and us
51 // This data could of course also be shared with userland
61 int (*render_request)(v3_graphics_console_t cons,
65 int (*update_inquire)(v3_graphics_console_t cons,
70 struct list_head gcons_node;
75 static v3_graphics_console_t g_open(void * priv_data,
76 struct v3_frame_buffer_spec *desired_spec,
77 struct v3_frame_buffer_spec *actual_spec)
79 struct v3_guest * guest = (struct v3_guest *)priv_data;
80 struct palacios_graphics_console * gc = NULL;
87 gc = get_vm_ext_data(guest, "GRAPHICS_CONSOLE_INTERFACE");
90 ERROR("palacios: Could not locate graphics console data for extension GRAPHICS_CONSOLE_INTERFACE\n");
94 if (gc->data != NULL) {
95 DEBUG("palacios: framebuffer already allocated - returning it\n");
97 *actual_spec = gc->spec;
104 mem = desired_spec->width * desired_spec->height * desired_spec->bytes_per_pixel;
106 DEBUG("palacios: allocating %u bytes for %u by %u by %u buffer\n",
107 mem, desired_spec->width, desired_spec->height, desired_spec->bytes_per_pixel);
109 gc->data = palacios_valloc(mem);
112 ERROR("palacios: unable to allocate memory for frame buffer\n");
116 gc->spec = *desired_spec;
118 *actual_spec = gc->spec;
124 INFO("palacios: allocated frame buffer\n");
129 static void g_close(v3_graphics_console_t cons)
131 struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
136 if (gc->data_refcount < gc->cons_refcount) {
137 ERROR("palacios: error! data refcount is less than console refcount for graphics console\n");
140 if (gc->cons_refcount > 0) {
143 if (gc->cons_refcount < 0) {
144 ERROR("palacios: error! refcount for graphics console is negative on close!\n");
146 if (gc->data_refcount > 0) {
147 ERROR("palacios: error! refcount for graphics console data is positive on close - LEAKING MEMORY\n");
151 palacios_vfree(gc->data);
157 static void * g_get_data_read(v3_graphics_console_t cons,
158 struct v3_frame_buffer_spec *cur_spec)
160 struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
162 if (gc->data_refcount<=0) {
163 ERROR("palacios: error! data refcount is <= 0 in get_data_read\n");
173 static void g_release_data_read(v3_graphics_console_t cons)
175 struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
179 if (gc->data_refcount<=0) {
180 ERROR("palacios: error! data refcount is <= zero in release_data_read\n");
185 static void *g_get_data_rw(v3_graphics_console_t cons,
186 struct v3_frame_buffer_spec *cur_spec)
188 return g_get_data_read(cons,cur_spec);
192 static void g_release_data_rw(v3_graphics_console_t cons)
194 return g_release_data_read(cons);
198 static int g_changed(v3_graphics_console_t cons)
201 struct palacios_graphics_console *gc =
202 (struct palacios_graphics_console *) cons;
205 cr = gc->change_request;
207 gc->change_request=0;
216 static int g_register_render_request(
217 v3_graphics_console_t cons,
218 int (*render_request)(v3_graphics_console_t,
222 struct palacios_graphics_console *gc =
223 (struct palacios_graphics_console *) cons;
225 gc->render_data = priv_data;
226 gc->render_request = render_request;
228 INFO("palacios: installed rendering callback function for graphics console\n");
234 static int g_register_update_inquire(
235 v3_graphics_console_t cons,
236 int (*update_inquire)(v3_graphics_console_t,
240 struct palacios_graphics_console *gc =
241 (struct palacios_graphics_console *) cons;
243 gc->update_data = priv_data;
244 gc->update_inquire = update_inquire;
246 INFO("palacios: installed update inquiry callback function for graphics console\n");
252 static int palacios_graphics_console_key(struct v3_guest * guest,
253 struct palacios_graphics_console *cons,
256 struct v3_keyboard_event e;
258 e.scan_code = scancode;
260 //DEBUG("palacios: start key delivery\n");
262 v3_deliver_keyboard_event(guest->v3_ctx, &e);
264 //DEBUG("palacios: end key delivery\n");
269 static int palacios_graphics_console_mouse(struct v3_guest * guest,
270 struct palacios_graphics_console *cons,
271 uint8_t sx, uint8_t dx,
272 uint8_t sy, uint8_t dy,
276 struct v3_mouse_event e;
284 v3_deliver_mouse_event(guest->v3_ctx,&e);
289 static struct v3_graphics_console_hooks palacios_graphics_console_hooks =
294 .get_data_read = g_get_data_read,
295 .release_data_read = g_release_data_read,
296 .get_data_rw = g_get_data_rw,
297 .release_data_rw = g_release_data_rw,
299 .changed = g_changed,
300 .register_render_request = g_register_render_request,
301 .register_update_inquire = g_register_update_inquire,
305 static int graphics_console_init( void ) {
307 INIT_LIST_HEAD(&(global_gcons));
309 V3_Init_Graphics_Console(&palacios_graphics_console_hooks);
315 static int graphics_console_deinit( void ) {
316 struct palacios_graphics_console * gc = NULL;
317 struct palacios_graphics_console * tmp = NULL;
319 list_for_each_entry_safe(gc, tmp, &(global_gcons), gcons_node) {
320 list_del(&(gc->gcons_node));
323 palacios_vfree(gc->data);
331 static int fb_query(struct v3_guest * guest, unsigned int cmd, unsigned long arg,
334 struct palacios_graphics_console * cons = priv_data;
335 struct v3_fb_query_response q;
338 if (copy_from_user(&q, (void __user *) arg, sizeof(struct v3_fb_query_response))) {
339 ERROR("palacios: copy from user in getting query in fb\n");
343 switch (q.request_type) {
345 //INFO("palacios: request for db spec from Userland\n");
346 // returns only the spec for the FB
352 //DEBUG("palacios: test for fb updatei from Userland\n");
353 // returns whether an update is available for the region
354 if (cons->update_inquire) {
355 q.updated = cons->update_inquire(cons,cons->update_data);
359 //DEBUG("palacios: update=%d\n",q.updated);
361 // request a render, since a FB_DATA will probably soon come
362 cons->change_request = 1;
366 case V3_FB_DATA_BOX: {
367 // Not curently implemented
368 ERROR("palacios: request for data in bounding box unsupported currently\n");
375 case V3_FB_DATA_ALL: {
376 //DEBUG("palacios: got FrameBuffer Request from Userland\n");
377 // First let's sanity check to see if they are requesting the same
379 if (memcmp(&(q.spec),&(cons->spec),sizeof(struct v3_frame_buffer_spec))) {
380 ERROR("palacios: request for data with non-matching fb spec \n");
383 // Now we will force a render if we can
384 if (cons->render_request) {
385 //DEBUG("palacios: making rendering request\n");
386 cons->render_request(cons,cons->render_data);
389 // Now let's indicate an update is in the pointer and copy across the data
390 if (copy_to_user(q.data,cons->data,cons->spec.width*cons->spec.height*cons->spec.bytes_per_pixel)) {
391 ERROR("palacios: unable to copy fb content to user\n");
394 //DEBUG("palacios: FrameBuffer copy out done\n");
396 // Now we don't need to request a render
397 cons->change_request = 0;
405 // now we'll copy back any changes we made to the query/response structure
406 if (copy_to_user((void __user *) arg, (void*)&q, sizeof(struct v3_fb_query_response))) {
407 ERROR("palacios: unable to copy fb response to user\n");
415 static int fb_input(struct v3_guest * guest,
420 struct palacios_graphics_console * cons = priv_data;
421 struct v3_fb_input inp;
425 if (copy_from_user(&inp, (void __user *) arg, sizeof(struct v3_fb_input))) {
426 ERROR("palacios: copy from user in getting input in fb\n");
430 //DEBUG("palacios: input from Userland\n");
432 if ((inp.data_type == V3_FB_KEY) || (inp.data_type == V3_FB_BOTH)) {
433 rc = palacios_graphics_console_key(guest, cons, inp.scan_code);
434 //DEBUG("palacios: key delivered to palacios\n");
437 if ((inp.data_type == V3_FB_MOUSE) || (inp.data_type == V3_FB_BOTH)) {
438 rc |= palacios_graphics_console_mouse(guest, cons, inp.sx, inp.dx, inp.sy, inp.dy, inp.buttons);
439 //DEBUG("palacios: mouse delivered to palacios\n");
445 cons->change_request=1;
451 static int graphics_console_guest_init(struct v3_guest * guest, void ** vm_data) {
452 struct palacios_graphics_console * graphics_cons = palacios_alloc(sizeof(struct palacios_graphics_console));
454 if (!graphics_cons) {
455 ERROR("palacios: filed to do guest_init for graphics console\n");
459 memset(graphics_cons, 0, sizeof(struct palacios_graphics_console));
461 *vm_data = graphics_cons;
463 add_guest_ctrl(guest, V3_VM_FB_INPUT, fb_input, graphics_cons);
464 add_guest_ctrl(guest, V3_VM_FB_QUERY, fb_query, graphics_cons);
466 list_add(&(graphics_cons->gcons_node),&global_gcons);
473 static int graphics_console_guest_deinit(struct v3_guest * guest, void * vm_data) {
474 struct palacios_graphics_console * graphics_cons = (struct palacios_graphics_console *)vm_data;
476 list_del(&(graphics_cons->gcons_node));
478 remove_guest_ctrl(guest, V3_VM_FB_INPUT);
479 remove_guest_ctrl(guest, V3_VM_FB_QUERY);
481 if (graphics_cons->data) {
482 palacios_vfree(graphics_cons->data);
486 palacios_free(graphics_cons);
492 static struct linux_ext graphics_cons_ext = {
493 .name = "GRAPHICS_CONSOLE_INTERFACE",
494 .init = graphics_console_init,
495 .deinit = graphics_console_deinit,
496 .guest_init = graphics_console_guest_init,
497 .guest_deinit = graphics_console_guest_deinit,
501 register_extension(&graphics_cons_ext);