From: Peter Dinda Date: Thu, 5 May 2011 18:10:36 +0000 (-0500) Subject: Ported graphics console support from monolithic version X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=commitdiff_plain;h=4f99aec507c593a9c7d7fdfe86f3a45175e4a3c0 Ported graphics console support from monolithic version --- diff --git a/linux_module/palacios-graphics-console.c b/linux_module/palacios-graphics-console.c new file mode 100644 index 0000000..0b03b62 --- /dev/null +++ b/linux_module/palacios-graphics-console.c @@ -0,0 +1,302 @@ +/* + * Palacios VM Graphics Console Interface (shared framebuffer between palacios and host) + * Copyright (c) 2011 Peter Dinda + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "palacios.h" +#include "palacios-graphics-console.h" + + +/* + + This is an implementation of the Palacios Graphics Console interface that + is designed to interact with a vnc server running in user space, + typically something based on x0vncserver. + + The basic idea is that we manage a frame buffer that we share with + palacios. Palacios draws whatever it likes on it. + The user-land vncserver will send us requests for fb updates, which + we implement by copying the FB to it. When the user-land sends us characters, etc, + we deliver those immediately to palacios via the deliver_key and deliver_mouse + event interfaces. The end-result is that whatever the graphics system + in palacios renders is visible via vnc. + +*/ + +static v3_graphics_console_t g_open(void * priv_data, + struct v3_frame_buffer_spec *desired_spec, + struct v3_frame_buffer_spec *actual_spec) +{ + struct v3_guest * guest = (struct v3_guest *)priv_data; + struct palacios_graphics_console *gc = (struct palacios_graphics_console *) &(guest->graphics_console); + uint32_t mem; + + if(gc->data) { + printk("palacios: framebuffer already allocated - returning it\n"); + *actual_spec=gc->spec; + gc->cons_refcount++; + gc->data_refcount++; + return gc; + } + + mem = desired_spec->width * desired_spec->height * desired_spec->bytes_per_pixel; + + printk("palacios: allocating %u bytes for %u by %u by %u buffer\n", + mem, desired_spec->width, desired_spec->height, desired_spec->bytes_per_pixel); + + gc->data = kmalloc(mem,GFP_KERNEL); + + if (!(gc->data)) { + printk("palacios: unable to allocate memory for frame buffer\n"); + return 0; + } + + gc->spec = *desired_spec; + + *actual_spec = gc->spec; + + gc->guest=guest; + + gc->cons_refcount++; + gc->data_refcount++; + + printk("palacios: allocated frame buffer\n"); + + return gc; +} + +static void g_close(v3_graphics_console_t cons) +{ + struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons; + + gc->cons_refcount--; + gc->data_refcount--; + + if (gc->data_refcountcons_refcount) { + printk("palacios: error! data refcount is less than console refcount for graphics console\n"); + } + + if (gc->cons_refcount>0) { + return; + } else { + if (gc->cons_refcount<0) { + printk("palacios: error! refcount for graphics console is negative on close!\n"); + } + if (gc->data_refcount>0) { + printk("palacios: error! refcount for graphics console data is positive on close - LEAKING MEMORY\n"); + return; + } + if (gc->data) { + kfree(gc->data); + gc->data=0; + } + } +} + +static void * g_get_data_read(v3_graphics_console_t cons, + struct v3_frame_buffer_spec *cur_spec) +{ + struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons; + + if (gc->data_refcount<=0) { + printk("palacios: error! data refcount is <= 0 in get_data_read\n"); + } + + gc->data_refcount++; + + *cur_spec=gc->spec; + + return gc->data; +} + +static void g_release_data_read(v3_graphics_console_t cons) +{ + struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons; + + gc->data_refcount--; + + if (gc->data_refcount<=0) { + printk("palacios: error! data refcount is <= zero in release_data_read\n"); + } + +} + +static void *g_get_data_rw(v3_graphics_console_t cons, + struct v3_frame_buffer_spec *cur_spec) +{ + return g_get_data_read(cons,cur_spec); +} + + +static void g_release_data_rw(v3_graphics_console_t cons) +{ + return g_release_data_read(cons); +} + + +static int g_changed(v3_graphics_console_t cons) +{ + struct palacios_graphics_console *gc = + (struct palacios_graphics_console *) cons; + +#if 0 + int rc = !(gc->num_updates % 1000); + + gc->num_updates++; + + return rc; +#else + return 1; +#endif +} + + + +static int palacios_graphics_console_key(struct palacios_graphics_console *cons, uint8_t scancode) +{ + struct v3_keyboard_event e; + e.status=0; + e.scan_code=scancode; + + v3_deliver_keyboard_event(cons->guest->v3_ctx,&e); + + return 0; +} + +static int palacios_graphics_console_mouse(struct palacios_graphics_console *cons, uint8_t x, uint8_t y, uint8_t buttons) +{ + struct v3_mouse_event e; + e.data[0]=x; + e.data[1]=y; + e.data[2]=buttons; // These three are completely wrong, of course - ignoring mouse for now + + v3_deliver_mouse_event(cons->guest->v3_ctx,&e); + + return 0; +} + +static struct v3_graphics_console_hooks palacios_graphics_console_hooks = +{ + .open = g_open, + .close = g_close, + + .get_data_read = g_get_data_read, + .release_data_read = g_release_data_read, + .get_data_rw = g_get_data_rw, + .release_data_rw = g_release_data_rw, + + .changed = g_changed, +}; + + +int palacios_init_graphics_console( void ) { + + V3_Init_Graphics_Console(&palacios_graphics_console_hooks); + + return 0; +} + + +int palacios_graphics_console_user_query(struct palacios_graphics_console *cons, + struct v3_fb_query_response __user *u) +{ + struct v3_fb_query_response q; + + + if (copy_from_user(&q,(void __user *) u, sizeof(struct v3_fb_query_response))) { + printk("palacios: copy from user in getting query in fb\n"); + return -EFAULT; + } + + switch (q.request_type) { + case V3_FB_SPEC: + // returns only the spec for the FB + q.spec = cons->spec; + + break; + + case V3_FB_UPDATE: + // returns whether an update is available for the region + // currently always true + q.updated = 1; + + break; + + case V3_FB_DATA_BOX: { + // Not curently implemented + printk("palacios: request for data in bounding box unsupported currently\n"); + return -EFAULT; + + } + + break; + + case V3_FB_DATA_ALL: { + // First let's sanity check to see if they are requesting the same + // spec that we have + if (memcmp(&(q.spec),&(cons->spec),sizeof(struct v3_frame_buffer_spec))) { + printk("palacios: request for data with non-matching fb spec \n"); + return -EFAULT; + } + // Now let's indicate an update is in the pointer and copy across the data + if (copy_to_user(q.data,cons->data,cons->spec.width*cons->spec.height*cons->spec.bytes_per_pixel)) { + printk("palacios: unable to copy fb content to user\n"); + return -EFAULT; + } + q.updated=1; + } + break; + + default: + return -EFAULT; + } + + // now we'll copy back any changes we made to the query/response structure + if (copy_to_user((void __user *) u, (void*)&q, sizeof(struct v3_fb_query_response))) { + printk("palacios: unable to copy fb response to user\n"); + return -EFAULT; + } + + return 0; + +} + +int palacios_graphics_console_user_input(struct palacios_graphics_console *cons, + struct v3_fb_input __user *u) +{ + struct v3_fb_input inp; + int rc=0; + + + if (copy_from_user(&inp,(void __user *) u, sizeof(struct v3_fb_input))) { + printk("palacios: copy from user in getting input in fb\n"); + return -EFAULT; + } + + if (inp.data_type==V3_FB_KEY || inp.data_type==V3_FB_BOTH) { + rc = palacios_graphics_console_key(cons,inp.scan_code); + } + + if (inp.data_type==V3_FB_MOUSE || inp.data_type==V3_FB_BOTH) { + rc |= palacios_graphics_console_mouse(cons,inp.mouse_data[0],inp.mouse_data[1],inp.mouse_data[2]); + } + + if (rc) { + return -EFAULT; + } else { + return 0; + } +} diff --git a/linux_module/palacios-graphics-console.h b/linux_module/palacios-graphics-console.h new file mode 100644 index 0000000..417a26b --- /dev/null +++ b/linux_module/palacios-graphics-console.h @@ -0,0 +1,60 @@ +/* + * Palacios VM Graphics Console Interface (shared framebuffer between palacios and host) + * Copyright (c) 2011 Peter Dinda + */ + +#ifndef __PALACIOS_GRAPHICS_CONSOLE_H__ +#define __PALACIOS_GRAPHICS_CONSOLE_H__ + +#include + +struct palacios_graphics_console { + // descriptor for the data in the shared frame buffer + struct v3_frame_buffer_spec spec; + // the actual shared frame buffer + // Note that "shared" here means shared between palacios and us + // This data could of course also be shared with userland + void *data; + + struct v3_guest * guest; + + int cons_refcount; + int data_refcount; + + uint32_t num_updates; + + // Currently keystrokes and mouse movements are ignored + + // currently, we will not worry about locking this + // lock_t ... +}; + + +// This is the data structure that is passed back and forth with user-land +// ioctl +struct v3_fb_query_response { + enum { V3_FB_DATA_ALL, V3_FB_DATA_BOX, V3_FB_UPDATE, V3_FB_SPEC } request_type; + struct v3_frame_buffer_spec spec; // in: desired spec; out: actual spec + uint32_t x, y, w, h; // region to copy (0s = all) in/out args + int updated; // whether this region has been updated or not + void __user *data; // user space pointer to copy data to +}; + +// This is what userland sends down for input events +struct v3_fb_input { + enum { V3_FB_KEY, V3_FB_MOUSE, V3_FB_BOTH} data_type; + uint8_t scan_code; + uint8_t mouse_data[3]; +}; + + +int palacios_init_graphics_console(void); + +int palacios_graphics_console_user_query(struct palacios_graphics_console *cons, + struct v3_fb_query_response __user *fb); + +int palacios_graphics_console_user_input(struct palacios_graphics_console *cons, + struct v3_fb_input __user *in); + + +#endif