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.


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