Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Revised mouse-related host events, graphics console, and userland support; mouse...
[palacios.git] / linux_module / iface-graphics-console.c
1 /*
2  * Palacios VM Graphics Console Interface (shared framebuffer between palacios and host)
3  * Copyright (c) 2011 Peter Dinda <pdinda@northwestern.edu>
4  */
5
6 #include <linux/device.h>
7 #include <linux/cdev.h>
8 #include <linux/errno.h>
9 #include <linux/fs.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>
15
16 #include <interfaces/vmm_console.h>
17 #include <palacios/vmm_host_events.h>
18 #include "iface-graphics-console.h"
19
20
21 #include "palacios.h"
22 #include "linux-exts.h"
23 #include "vm.h"
24
25 #include <linux/vmalloc.h>
26
27 /*
28
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.  
32
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.
40
41 */
42
43
44 static struct list_head global_gcons;
45
46 struct palacios_graphics_console {
47     // descriptor for the data in the shared frame buffer
48     struct v3_frame_buffer_spec spec;
49
50     // the actual shared frame buffer
51     // Note that "shared" here means shared between palacios and us
52     // This data could of course also be shared with userland
53     void * data;
54
55     int cons_refcount;
56     int data_refcount;
57
58     uint32_t num_updates;
59
60     int change_request;
61   
62     int (*render_request)(v3_graphics_console_t cons, 
63                           void *priv_data);
64     void *render_data;
65
66     int (*update_inquire)(v3_graphics_console_t cons,
67                           void *priv_data);
68     
69     void *update_data;
70
71     struct list_head gcons_node;
72
73 };
74
75
76 static v3_graphics_console_t g_open(void * priv_data, 
77                                     struct v3_frame_buffer_spec *desired_spec,
78                                     struct v3_frame_buffer_spec *actual_spec)
79 {
80     struct v3_guest * guest = (struct v3_guest *)priv_data;
81     struct palacios_graphics_console * gc = NULL;
82     uint32_t mem;
83
84     if (guest == NULL) {
85         return 0;
86     }
87
88     gc = get_vm_ext_data(guest, "GRAPHICS_CONSOLE_INTERFACE");
89     
90     if (gc == NULL) {
91         ERROR("palacios: Could not locate graphics console data for extension GRAPHICS_CONSOLE_INTERFACE\n");
92         return 0;
93     }
94
95     if (gc->data != NULL) { 
96         DEBUG("palacios: framebuffer already allocated - returning it\n");
97
98         *actual_spec = gc->spec;
99         gc->cons_refcount++;
100         gc->data_refcount++;
101
102         return gc;
103     }
104
105     mem = desired_spec->width * desired_spec->height * desired_spec->bytes_per_pixel;
106
107     DEBUG("palacios: allocating %u bytes for %u by %u by %u buffer\n",
108            mem, desired_spec->width, desired_spec->height, desired_spec->bytes_per_pixel);
109
110     gc->data = vmalloc(mem);
111
112     if (!(gc->data)) { 
113         ERROR("palacios: unable to allocate memory for frame buffer\n");
114         return 0;
115     }
116
117     gc->spec = *desired_spec;
118
119     *actual_spec = gc->spec;
120
121     
122     gc->cons_refcount++;
123     gc->data_refcount++;
124
125     INFO("palacios: allocated frame buffer\n");
126
127     return gc;
128 }
129
130 static  void g_close(v3_graphics_console_t cons)
131 {
132     struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
133
134     gc->cons_refcount--;
135     gc->data_refcount--;
136
137     if (gc->data_refcount < gc->cons_refcount) { 
138         ERROR("palacios: error!   data refcount is less than console refcount for graphics console\n");
139     }
140
141     if (gc->cons_refcount > 0) { 
142         return;
143     } else {
144         if (gc->cons_refcount < 0) { 
145             ERROR("palacios: error!  refcount for graphics console is negative on close!\n");
146         }
147         if (gc->data_refcount > 0) { 
148             ERROR("palacios: error!  refcount for graphics console data is positive on close - LEAKING MEMORY\n");
149             return;
150         }
151         if (gc->data) { 
152             vfree(gc->data);
153             gc->data=0;
154         }
155     }
156 }
157
158 static void * g_get_data_read(v3_graphics_console_t cons, 
159                               struct v3_frame_buffer_spec *cur_spec)
160 {
161     struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
162     
163     if (gc->data_refcount<=0) { 
164         ERROR("palacios: error!  data refcount is <= 0 in get_data_read\n");
165     }
166
167     gc->data_refcount++;
168     
169     *cur_spec=gc->spec;
170     
171     return gc->data;
172 }
173
174 static void g_release_data_read(v3_graphics_console_t cons)
175 {
176     struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
177     
178     gc->data_refcount--;
179
180     if (gc->data_refcount<=0) { 
181         ERROR("palacios: error!  data refcount is <= zero in release_data_read\n");
182     }
183     
184 }
185
186 static void *g_get_data_rw(v3_graphics_console_t cons, 
187                            struct v3_frame_buffer_spec *cur_spec)
188 {
189     return g_get_data_read(cons,cur_spec);
190 }
191
192
193 static void g_release_data_rw(v3_graphics_console_t cons)
194 {
195     return g_release_data_read(cons);
196 }
197
198
199 static int g_changed(v3_graphics_console_t cons)
200 {
201
202     struct palacios_graphics_console *gc = 
203         (struct palacios_graphics_console *) cons;
204     int cr;
205   
206     cr = gc->change_request;
207
208     gc->change_request=0;
209
210     gc->num_updates++;
211     
212     return cr;
213
214 }
215
216
217 static int g_register_render_request(
218                                      v3_graphics_console_t cons,
219                                      int (*render_request)(v3_graphics_console_t,
220                                                            void *),
221                                      void *priv_data)
222 {
223    struct palacios_graphics_console *gc =
224      (struct palacios_graphics_console *) cons;
225    
226    gc->render_data = priv_data;
227    gc->render_request = render_request;
228
229    INFO("palacios: installed rendering callback function for graphics console\n");
230    
231    return 0;
232
233 }
234
235 static int g_register_update_inquire(
236                                      v3_graphics_console_t cons,
237                                      int (*update_inquire)(v3_graphics_console_t,
238                                                            void *),
239                                      void *priv_data)
240 {
241    struct palacios_graphics_console *gc =
242      (struct palacios_graphics_console *) cons;
243    
244    gc->update_data = priv_data;
245    gc->update_inquire = update_inquire;
246
247    INFO("palacios: installed update inquiry callback function for graphics console\n");
248    
249    return 0;
250
251 }
252
253 static int palacios_graphics_console_key(struct v3_guest * guest, 
254                                          struct palacios_graphics_console *cons, 
255                                          uint8_t scancode)
256 {
257      struct v3_keyboard_event e;
258     e.status = 0;
259     e.scan_code = scancode;
260
261     //DEBUG("palacios: start key delivery\n");
262
263     v3_deliver_keyboard_event(guest->v3_ctx, &e);
264
265     //DEBUG("palacios: end key delivery\n");
266     
267     return 0;
268 }
269
270 static int palacios_graphics_console_mouse(struct v3_guest * guest, 
271                                            struct palacios_graphics_console *cons, 
272                                            uint8_t sx, uint8_t dx,
273                                            uint8_t sy, uint8_t dy,
274                                            uint8_t buttons)
275 {
276
277     struct v3_mouse_event e;
278
279     e.sx=sx;
280     e.dx=dx;
281     e.sy=sy;
282     e.dy=dy;
283     e.buttons=buttons;
284
285     v3_deliver_mouse_event(guest->v3_ctx,&e);
286
287     return 0;
288 }
289
290 static struct v3_graphics_console_hooks palacios_graphics_console_hooks = 
291 {
292     .open  = g_open,
293     .close = g_close,
294
295     .get_data_read = g_get_data_read,
296     .release_data_read = g_release_data_read,
297     .get_data_rw = g_get_data_rw,
298     .release_data_rw = g_release_data_rw,
299
300     .changed = g_changed,
301     .register_render_request = g_register_render_request,
302     .register_update_inquire = g_register_update_inquire,
303 };
304
305
306 static int graphics_console_init( void ) {
307
308     INIT_LIST_HEAD(&(global_gcons));
309     
310     V3_Init_Graphics_Console(&palacios_graphics_console_hooks);
311     
312     return 0;
313 }
314
315
316 static int graphics_console_deinit( void ) {
317     struct palacios_graphics_console * gc  = NULL;
318     struct palacios_graphics_console * tmp = NULL;
319
320     list_for_each_entry_safe(gc, tmp, &(global_gcons), gcons_node) {
321         list_del(&(gc->gcons_node));
322
323         if (gc->data) 
324             vfree(gc->data);
325
326         palacios_free(gc);
327     }
328     
329     return 0;
330 }
331
332 static int fb_query(struct v3_guest * guest, unsigned int cmd, unsigned long arg, 
333                     void * priv_data) {
334     
335     struct palacios_graphics_console * cons = priv_data;
336     struct v3_fb_query_response q;
337     
338     
339     if (copy_from_user(&q, (void __user *) arg, sizeof(struct v3_fb_query_response))) { 
340         ERROR("palacios: copy from user in getting query in fb\n");
341         return -EFAULT;
342     }
343     
344     switch (q.request_type) { 
345         case V3_FB_SPEC:
346             //INFO("palacios: request for db spec from Userland\n");
347             // returns only the spec for the FB
348             q.spec = cons->spec;
349
350             break;
351
352         case V3_FB_UPDATE: 
353             //DEBUG("palacios: test for fb updatei from Userland\n");
354             // returns whether an update is available for the region
355             if (cons->update_inquire) {
356               q.updated = cons->update_inquire(cons,cons->update_data);
357             } else {
358               q.updated = 1;
359             }
360             //DEBUG("palacios: update=%d\n",q.updated);
361
362             // request a render, since a FB_DATA will probably soon come
363             cons->change_request = 1;
364
365             break;
366
367         case V3_FB_DATA_BOX: {
368             // Not curently implemented
369             ERROR("palacios: request for data in bounding box unsupported currently\n");
370             return -EFAULT;
371
372         }
373
374             break;
375             
376         case V3_FB_DATA_ALL: {
377             //DEBUG("palacios: got FrameBuffer Request from Userland\n");
378             // First let's sanity check to see if they are requesting the same
379             // spec that we have
380             if (memcmp(&(q.spec),&(cons->spec),sizeof(struct v3_frame_buffer_spec))) { 
381                 ERROR("palacios: request for data with non-matching fb spec \n");
382                 return -EFAULT;
383             }
384             // Now we will force a render if we can
385             if (cons->render_request) {
386                  //DEBUG("palacios: making rendering request\n");
387                  cons->render_request(cons,cons->render_data);
388             }
389
390             // Now let's indicate an update is in the pointer and copy across the data
391             if (copy_to_user(q.data,cons->data,cons->spec.width*cons->spec.height*cons->spec.bytes_per_pixel)) { 
392                 ERROR("palacios: unable to copy fb content to user\n");
393                 return -EFAULT;
394             }
395             //DEBUG("palacios: FrameBuffer copy out done\n");
396             q.updated = 1;
397             // Now we don't need to request a render
398             cons->change_request = 0;
399         }
400             break;
401             
402         default:
403             return -EFAULT;
404     }
405
406     // now we'll copy back any changes we made to the query/response structure
407     if (copy_to_user((void __user *) arg, (void*)&q, sizeof(struct v3_fb_query_response))) { 
408         ERROR("palacios: unable to copy fb response to user\n");
409         return -EFAULT;
410     }
411
412     return 0;
413
414 }
415
416 static int fb_input(struct v3_guest * guest, 
417                     unsigned int cmd, 
418                     unsigned long arg, 
419                     void * priv_data) {
420
421     struct palacios_graphics_console * cons = priv_data;
422     struct v3_fb_input inp;
423     int rc = 0;
424
425
426     if (copy_from_user(&inp, (void __user *) arg, sizeof(struct v3_fb_input))) { 
427         ERROR("palacios: copy from user in getting input in fb\n");
428         return -EFAULT;
429     }
430
431     //DEBUG("palacios: input from Userland\n");   
432         
433     if ((inp.data_type == V3_FB_KEY) || (inp.data_type == V3_FB_BOTH)) { 
434         rc = palacios_graphics_console_key(guest, cons, inp.scan_code);
435         //DEBUG("palacios: key delivered to palacios\n");
436     }
437
438     if ((inp.data_type == V3_FB_MOUSE) || (inp.data_type == V3_FB_BOTH)) { 
439         rc |= palacios_graphics_console_mouse(guest, cons, inp.sx, inp.dx, inp.sy, inp.dy, inp.buttons);
440        //DEBUG("palacios: mouse delivered to palacios\n");
441     }
442
443     if (rc) { 
444         return -EFAULT;
445     } else {
446         cons->change_request=1;
447         return 0;
448     }
449 }
450
451
452 static int graphics_console_guest_init(struct v3_guest * guest, void ** vm_data) {
453     struct palacios_graphics_console * graphics_cons = palacios_alloc(sizeof(struct palacios_graphics_console));
454
455     if (!graphics_cons) { 
456         ERROR("palacios: filed to do guest_init for graphics console\n");
457         return -1;
458     }
459
460     memset(graphics_cons, 0, sizeof(struct palacios_graphics_console));
461
462     *vm_data = graphics_cons;
463
464     add_guest_ctrl(guest, V3_VM_FB_INPUT, fb_input, graphics_cons);
465     add_guest_ctrl(guest, V3_VM_FB_QUERY, fb_query, graphics_cons);
466
467     list_add(&(graphics_cons->gcons_node),&global_gcons);
468
469     return 0;
470 }
471
472
473
474 static int graphics_console_guest_deinit(struct v3_guest * guest, void * vm_data) {
475     struct palacios_graphics_console * graphics_cons = (struct palacios_graphics_console *)vm_data;
476
477     list_del(&(graphics_cons->gcons_node));
478
479     if (graphics_cons->data) { 
480         vfree(graphics_cons->data);
481     }
482
483     palacios_free(graphics_cons);
484
485     return 0;
486 }
487
488
489 static struct linux_ext graphics_cons_ext = {
490     .name = "GRAPHICS_CONSOLE_INTERFACE",
491     .init = graphics_console_init,
492     .deinit = graphics_console_deinit,
493     .guest_init = graphics_console_guest_init,
494     .guest_deinit = graphics_console_guest_deinit,
495 };
496
497
498 register_extension(&graphics_cons_ext);