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.


Context-based output infrastructure (V3_Print, etc) and modifications to use it
[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 x, uint8_t y, uint8_t buttons)
273 {
274   /*
275     struct v3_mouse_event e;
276     e.data[0]=x;
277     e.data[1]=y;
278     e.data[2]=buttons;   // These three are completely wrong, of course - ignoring mouse for now
279   */
280     // mouse delivery is broken, so don't do it.
281     // v3_deliver_mouse_event(guest->v3_ctx,&e);
282
283     return 0;
284 }
285
286 static struct v3_graphics_console_hooks palacios_graphics_console_hooks = 
287 {
288     .open  = g_open,
289     .close = g_close,
290
291     .get_data_read = g_get_data_read,
292     .release_data_read = g_release_data_read,
293     .get_data_rw = g_get_data_rw,
294     .release_data_rw = g_release_data_rw,
295
296     .changed = g_changed,
297     .register_render_request = g_register_render_request,
298     .register_update_inquire = g_register_update_inquire,
299 };
300
301
302 static int graphics_console_init( void ) {
303
304     INIT_LIST_HEAD(&(global_gcons));
305     
306     V3_Init_Graphics_Console(&palacios_graphics_console_hooks);
307     
308     return 0;
309 }
310
311
312 static int graphics_console_deinit( void ) {
313     struct palacios_graphics_console * gc  = NULL;
314     struct palacios_graphics_console * tmp = NULL;
315
316     list_for_each_entry_safe(gc, tmp, &(global_gcons), gcons_node) {
317         list_del(&(gc->gcons_node));
318
319         if (gc->data) 
320             vfree(gc->data);
321
322         palacios_free(gc);
323     }
324     
325     return 0;
326 }
327
328 static int fb_query(struct v3_guest * guest, unsigned int cmd, unsigned long arg, 
329                     void * priv_data) {
330     
331     struct palacios_graphics_console * cons = priv_data;
332     struct v3_fb_query_response q;
333     
334     
335     if (copy_from_user(&q, (void __user *) arg, sizeof(struct v3_fb_query_response))) { 
336         ERROR("palacios: copy from user in getting query in fb\n");
337         return -EFAULT;
338     }
339     
340     switch (q.request_type) { 
341         case V3_FB_SPEC:
342             //INFO("palacios: request for db spec from Userland\n");
343             // returns only the spec for the FB
344             q.spec = cons->spec;
345
346             break;
347
348         case V3_FB_UPDATE: 
349             //DEBUG("palacios: test for fb updatei from Userland\n");
350             // returns whether an update is available for the region
351             if (cons->update_inquire) {
352               q.updated = cons->update_inquire(cons,cons->update_data);
353             } else {
354               q.updated = 1;
355             }
356             //DEBUG("palacios: update=%d\n",q.updated);
357
358             // request a render, since a FB_DATA will probably soon come
359             cons->change_request = 1;
360
361             break;
362
363         case V3_FB_DATA_BOX: {
364             // Not curently implemented
365             ERROR("palacios: request for data in bounding box unsupported currently\n");
366             return -EFAULT;
367
368         }
369
370             break;
371             
372         case V3_FB_DATA_ALL: {
373             //DEBUG("palacios: got FrameBuffer Request from Userland\n");
374             // First let's sanity check to see if they are requesting the same
375             // spec that we have
376             if (memcmp(&(q.spec),&(cons->spec),sizeof(struct v3_frame_buffer_spec))) { 
377                 ERROR("palacios: request for data with non-matching fb spec \n");
378                 return -EFAULT;
379             }
380             // Now we will force a render if we can
381             if (cons->render_request) {
382                  //DEBUG("palacios: making rendering request\n");
383                  cons->render_request(cons,cons->render_data);
384             }
385
386             // Now let's indicate an update is in the pointer and copy across the data
387             if (copy_to_user(q.data,cons->data,cons->spec.width*cons->spec.height*cons->spec.bytes_per_pixel)) { 
388                 ERROR("palacios: unable to copy fb content to user\n");
389                 return -EFAULT;
390             }
391             //DEBUG("palacios: FrameBuffer copy out done\n");
392             q.updated = 1;
393             // Now we don't need to request a render
394             cons->change_request = 0;
395         }
396             break;
397             
398         default:
399             return -EFAULT;
400     }
401
402     // now we'll copy back any changes we made to the query/response structure
403     if (copy_to_user((void __user *) arg, (void*)&q, sizeof(struct v3_fb_query_response))) { 
404         ERROR("palacios: unable to copy fb response to user\n");
405         return -EFAULT;
406     }
407
408     return 0;
409
410 }
411
412 static int fb_input(struct v3_guest * guest, 
413                     unsigned int cmd, 
414                     unsigned long arg, 
415                     void * priv_data) {
416
417     struct palacios_graphics_console * cons = priv_data;
418     struct v3_fb_input inp;
419     int rc = 0;
420
421
422     if (copy_from_user(&inp, (void __user *) arg, sizeof(struct v3_fb_input))) { 
423         ERROR("palacios: copy from user in getting input in fb\n");
424         return -EFAULT;
425     }
426
427     //DEBUG("palacios: input from Userland\n");   
428         
429     if ((inp.data_type == V3_FB_KEY) || (inp.data_type == V3_FB_BOTH)) { 
430         rc = palacios_graphics_console_key(guest, cons, inp.scan_code);
431         //DEBUG("palacios: key delivered to palacios\n");
432     }
433
434     if ((inp.data_type == V3_FB_MOUSE) || (inp.data_type == V3_FB_BOTH)) { 
435         rc |= palacios_graphics_console_mouse(guest, cons, inp.mouse_data[0],
436                                               inp.mouse_data[1], inp.mouse_data[2]);
437        //DEBUG("palacios: mouse delivered to palacios\n");
438     }
439
440     if (rc) { 
441         return -EFAULT;
442     } else {
443         cons->change_request=1;
444         return 0;
445     }
446 }
447
448
449 static int graphics_console_guest_init(struct v3_guest * guest, void ** vm_data) {
450     struct palacios_graphics_console * graphics_cons = palacios_alloc(sizeof(struct palacios_graphics_console));
451
452     if (!graphics_cons) { 
453         ERROR("palacios: filed to do guest_init for graphics console\n");
454         return -1;
455     }
456
457     memset(graphics_cons, 0, sizeof(struct palacios_graphics_console));
458
459     *vm_data = graphics_cons;
460
461     add_guest_ctrl(guest, V3_VM_FB_INPUT, fb_input, graphics_cons);
462     add_guest_ctrl(guest, V3_VM_FB_QUERY, fb_query, graphics_cons);
463
464     list_add(&(graphics_cons->gcons_node),&global_gcons);
465
466     return 0;
467 }
468
469
470
471 static int graphics_console_guest_deinit(struct v3_guest * guest, void * vm_data) {
472     struct palacios_graphics_console * graphics_cons = (struct palacios_graphics_console *)vm_data;
473
474     list_del(&(graphics_cons->gcons_node));
475
476     if (graphics_cons->data) { 
477         vfree(graphics_cons->data);
478     }
479
480     palacios_free(graphics_cons);
481
482     return 0;
483 }
484
485
486 static struct linux_ext graphics_cons_ext = {
487     .name = "GRAPHICS_CONSOLE_INTERFACE",
488     .init = graphics_console_init,
489     .deinit = graphics_console_deinit,
490     .guest_init = graphics_console_guest_init,
491     .guest_deinit = graphics_console_guest_deinit,
492 };
493
494
495 register_extension(&graphics_cons_ext);