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 "palacios-graphics-console.h"
22 #include "linux-exts.h"
23 #include "palacios-vm.h"
25 #include <linux/vmalloc.h>
29 This is an implementation of the Palacios Graphics Console interface that
30 is designed to interact with a vnc server running in user space,
31 typically something based on x0vncserver.
33 The basic idea is that we manage a frame buffer that we share with
34 palacios. Palacios draws whatever it likes on it.
35 The user-land vncserver will send us requests for fb updates, which
36 we implement by copying the FB to it. When the user-land sends us characters, etc,
37 we deliver those immediately to palacios via the deliver_key and deliver_mouse
38 event interfaces. The end-result is that whatever the graphics system
39 in palacios renders is visible via vnc.
45 struct palacios_graphics_console {
46 // descriptor for the data in the shared frame buffer
47 struct v3_frame_buffer_spec spec;
48 // the actual shared frame buffer
49 // Note that "shared" here means shared between palacios and us
50 // This data could of course also be shared with userland
58 // Currently keystrokes and mouse movements are ignored
60 // currently, we will not worry about locking this
65 static v3_graphics_console_t g_open(void * priv_data,
66 struct v3_frame_buffer_spec *desired_spec,
67 struct v3_frame_buffer_spec *actual_spec)
69 struct v3_guest * guest = (struct v3_guest *)priv_data;
70 struct palacios_graphics_console * gc = NULL;
77 gc = get_vm_ext_data(guest, "GFX_CONSOLE_INTERFACE");
80 printk("ERROR: Could not locate gfx console data for extension GFX_CONSOLE_INTERFACE\n");
84 if (gc->data != NULL) {
85 printk("palacios: framebuffer already allocated - returning it\n");
87 *actual_spec = gc->spec;
94 mem = desired_spec->width * desired_spec->height * desired_spec->bytes_per_pixel;
96 printk("palacios: allocating %u bytes for %u by %u by %u buffer\n",
97 mem, desired_spec->width, desired_spec->height, desired_spec->bytes_per_pixel);
99 gc->data = vmalloc(mem);
102 printk("palacios: unable to allocate memory for frame buffer\n");
106 gc->spec = *desired_spec;
108 *actual_spec = gc->spec;
114 printk("palacios: allocated frame buffer\n");
119 static void g_close(v3_graphics_console_t cons)
121 struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
126 if (gc->data_refcount<gc->cons_refcount) {
127 printk("palacios: error! data refcount is less than console refcount for graphics console\n");
130 if (gc->cons_refcount>0) {
133 if (gc->cons_refcount<0) {
134 printk("palacios: error! refcount for graphics console is negative on close!\n");
136 if (gc->data_refcount>0) {
137 printk("palacios: error! refcount for graphics console data is positive on close - LEAKING MEMORY\n");
147 static void * g_get_data_read(v3_graphics_console_t cons,
148 struct v3_frame_buffer_spec *cur_spec)
150 struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
152 if (gc->data_refcount<=0) {
153 printk("palacios: error! data refcount is <= 0 in get_data_read\n");
163 static void g_release_data_read(v3_graphics_console_t cons)
165 struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
169 if (gc->data_refcount<=0) {
170 printk("palacios: error! data refcount is <= zero in release_data_read\n");
175 static void *g_get_data_rw(v3_graphics_console_t cons,
176 struct v3_frame_buffer_spec *cur_spec)
178 return g_get_data_read(cons,cur_spec);
182 static void g_release_data_rw(v3_graphics_console_t cons)
184 return g_release_data_read(cons);
188 static int g_changed(v3_graphics_console_t cons)
192 struct palacios_graphics_console *gc =
193 (struct palacios_graphics_console *) cons;
195 int rc = !(gc->num_updates % 1000);
207 static int palacios_graphics_console_key(struct v3_guest * guest,
208 struct palacios_graphics_console *cons,
211 struct v3_keyboard_event e;
213 e.scan_code = scancode;
215 v3_deliver_keyboard_event(guest->v3_ctx, &e);
220 static int palacios_graphics_console_mouse(struct v3_guest * guest,
221 struct palacios_graphics_console *cons,
222 uint8_t x, uint8_t y, uint8_t buttons)
224 struct v3_mouse_event e;
227 e.data[2]=buttons; // These three are completely wrong, of course - ignoring mouse for now
229 v3_deliver_mouse_event(guest->v3_ctx,&e);
234 static struct v3_graphics_console_hooks palacios_graphics_console_hooks =
239 .get_data_read = g_get_data_read,
240 .release_data_read = g_release_data_read,
241 .get_data_rw = g_get_data_rw,
242 .release_data_rw = g_release_data_rw,
244 .changed = g_changed,
248 static int gfx_console_init( void ) {
250 V3_Init_Graphics_Console(&palacios_graphics_console_hooks);
256 static int fb_query(struct v3_guest * guest, unsigned int cmd, unsigned long arg,
259 struct v3_fb_query_response __user * u = (struct v3_fb_query_response __user *)arg;
260 struct palacios_graphics_console * cons = priv_data;
261 struct v3_fb_query_response q;
264 if (copy_from_user(&q,(void __user *) u, sizeof(struct v3_fb_query_response))) {
265 printk("palacios: copy from user in getting query in fb\n");
269 switch (q.request_type) {
271 // returns only the spec for the FB
277 // returns whether an update is available for the region
278 // currently always true
283 case V3_FB_DATA_BOX: {
284 // Not curently implemented
285 printk("palacios: request for data in bounding box unsupported currently\n");
292 case V3_FB_DATA_ALL: {
293 // First let's sanity check to see if they are requesting the same
295 if (memcmp(&(q.spec),&(cons->spec),sizeof(struct v3_frame_buffer_spec))) {
296 printk("palacios: request for data with non-matching fb spec \n");
299 // Now let's indicate an update is in the pointer and copy across the data
300 if (copy_to_user(q.data,cons->data,cons->spec.width*cons->spec.height*cons->spec.bytes_per_pixel)) {
301 printk("palacios: unable to copy fb content to user\n");
312 // now we'll copy back any changes we made to the query/response structure
313 if (copy_to_user((void __user *) u, (void*)&q, sizeof(struct v3_fb_query_response))) {
314 printk("palacios: unable to copy fb response to user\n");
322 static int fb_input(struct v3_guest * guest,
327 struct v3_fb_input __user * u = (struct v3_fb_input __user *)arg;
328 struct palacios_graphics_console * cons = priv_data;
329 struct v3_fb_input inp;
333 if (copy_from_user(&inp,(void __user *) u, sizeof(struct v3_fb_input))) {
334 printk("palacios: copy from user in getting input in fb\n");
338 if ((inp.data_type == V3_FB_KEY) || (inp.data_type == V3_FB_BOTH)) {
339 rc = palacios_graphics_console_key(guest, cons, inp.scan_code);
342 if ((inp.data_type == V3_FB_MOUSE) || (inp.data_type == V3_FB_BOTH)) {
343 rc |= palacios_graphics_console_mouse(guest, cons, inp.mouse_data[0],
344 inp.mouse_data[1], inp.mouse_data[2]);
355 static int gfx_console_guest_init(struct v3_guest * guest, void ** vm_data) {
356 struct palacios_graphics_console * gfx_cons = kmalloc(sizeof(struct palacios_graphics_console), GFP_KERNEL);
358 memset(gfx_cons, 0, sizeof(struct palacios_graphics_console));
361 add_guest_ctrl(guest, V3_VM_FB_INPUT, fb_input, gfx_cons);
362 add_guest_ctrl(guest, V3_VM_FB_QUERY, fb_query, gfx_cons);
369 static struct linux_ext gfx_cons_ext = {
370 .name = "GFX_CONSOLE_INTERFACE",
371 .init = gfx_console_init,
373 .guest_init = gfx_console_guest_init,
378 register_extension(&gfx_cons_ext);