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.


Merge branch 'devel' of newskysaw.cs.northwestern.edu:/home/palacios/palacios into...
[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
45 struct palacios_graphics_console {
46     // descriptor for the data in the shared frame buffer
47     struct v3_frame_buffer_spec spec;
48
49     // the actual shared frame buffer
50     // Note that "shared" here means shared between palacios and us
51     // This data could of course also be shared with userland
52     void * data;
53
54     int cons_refcount;
55     int data_refcount;
56
57     uint32_t num_updates;
58
59     // Currently keystrokes and mouse movements are ignored
60
61     // currently, we will not worry about locking this
62     // lock_t ...
63 };
64
65
66 static v3_graphics_console_t g_open(void * priv_data, 
67                                     struct v3_frame_buffer_spec *desired_spec,
68                                     struct v3_frame_buffer_spec *actual_spec)
69 {
70     struct v3_guest * guest = (struct v3_guest *)priv_data;
71     struct palacios_graphics_console * gc = NULL;
72     uint32_t mem;
73
74     if (guest == NULL) {
75         return 0;
76     }
77
78     gc = get_vm_ext_data(guest, "GRAPHICS_CONSOLE_INTERFACE");
79     
80     if (gc == NULL) {
81         printk("palacios: Could not locate graphics console data for extension GRAPHICS_CONSOLE_INTERFACE\n");
82         return 0;
83     }
84
85     if (gc->data != NULL) { 
86         printk("palacios: framebuffer already allocated - returning it\n");
87
88         *actual_spec = gc->spec;
89         gc->cons_refcount++;
90         gc->data_refcount++;
91
92         return gc;
93     }
94
95     mem = desired_spec->width * desired_spec->height * desired_spec->bytes_per_pixel;
96
97     printk("palacios: allocating %u bytes for %u by %u by %u buffer\n",
98            mem, desired_spec->width, desired_spec->height, desired_spec->bytes_per_pixel);
99
100     gc->data = vmalloc(mem);
101
102     if (!(gc->data)) { 
103         printk("palacios: unable to allocate memory for frame buffer\n");
104         return 0;
105     }
106
107     gc->spec = *desired_spec;
108
109     *actual_spec = gc->spec;
110
111     
112     gc->cons_refcount++;
113     gc->data_refcount++;
114
115     printk("palacios: allocated frame buffer\n");
116
117     return gc;
118 }
119
120 static  void g_close(v3_graphics_console_t cons)
121 {
122     struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
123
124     gc->cons_refcount--;
125     gc->data_refcount--;
126
127     if (gc->data_refcount < gc->cons_refcount) { 
128         printk("palacios: error!   data refcount is less than console refcount for graphics console\n");
129     }
130
131     if (gc->cons_refcount > 0) { 
132         return;
133     } else {
134         if (gc->cons_refcount < 0) { 
135             printk("palacios: error!  refcount for graphics console is negative on close!\n");
136         }
137         if (gc->data_refcount > 0) { 
138             printk("palacios: error!  refcount for graphics console data is positive on close - LEAKING MEMORY\n");
139             return;
140         }
141         if (gc->data) { 
142             kfree(gc->data);
143             gc->data=0;
144         }
145     }
146 }
147
148 static void * g_get_data_read(v3_graphics_console_t cons, 
149                               struct v3_frame_buffer_spec *cur_spec)
150 {
151     struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
152     
153     if (gc->data_refcount<=0) { 
154         printk("palacios: error!  data refcount is <= 0 in get_data_read\n");
155     }
156
157     gc->data_refcount++;
158     
159     *cur_spec=gc->spec;
160     
161     return gc->data;
162 }
163
164 static void g_release_data_read(v3_graphics_console_t cons)
165 {
166     struct palacios_graphics_console *gc = (struct palacios_graphics_console *) cons;
167     
168     gc->data_refcount--;
169
170     if (gc->data_refcount<=0) { 
171         printk("palacios: error!  data refcount is <= zero in release_data_read\n");
172     }
173     
174 }
175
176 static void *g_get_data_rw(v3_graphics_console_t cons, 
177                            struct v3_frame_buffer_spec *cur_spec)
178 {
179     return g_get_data_read(cons,cur_spec);
180 }
181
182
183 static void g_release_data_rw(v3_graphics_console_t cons)
184 {
185     return g_release_data_read(cons);
186 }
187
188
189 static int g_changed(v3_graphics_console_t cons)
190 {
191
192 #if 0
193     struct palacios_graphics_console *gc = 
194         (struct palacios_graphics_console *) cons;
195
196     int rc =  !(gc->num_updates % 1000);
197     
198     gc->num_updates++;
199     
200     return rc;
201 #else
202     return 1;
203 #endif
204 }
205
206
207
208 static int palacios_graphics_console_key(struct v3_guest * guest, 
209                                          struct palacios_graphics_console *cons, 
210                                          uint8_t scancode)
211 {
212     struct v3_keyboard_event e;
213     e.status = 0;
214     e.scan_code = scancode;
215
216     v3_deliver_keyboard_event(guest->v3_ctx, &e);
217     
218     return 0;
219 }
220
221 static int palacios_graphics_console_mouse(struct v3_guest * guest, 
222                                            struct palacios_graphics_console *cons, 
223                                            uint8_t x, uint8_t y, uint8_t buttons)
224 {
225     struct v3_mouse_event e;
226     e.data[0]=x;
227     e.data[1]=y;
228     e.data[2]=buttons;   // These three are completely wrong, of course - ignoring mouse for now
229
230     v3_deliver_mouse_event(guest->v3_ctx,&e);
231
232     return 0;
233 }
234
235 static struct v3_graphics_console_hooks palacios_graphics_console_hooks = 
236 {
237     .open  = g_open,
238     .close = g_close,
239
240     .get_data_read = g_get_data_read,
241     .release_data_read = g_release_data_read,
242     .get_data_rw = g_get_data_rw,
243     .release_data_rw = g_release_data_rw,
244
245     .changed = g_changed,
246 };
247
248
249 static int graphics_console_init( void ) {
250
251     V3_Init_Graphics_Console(&palacios_graphics_console_hooks);
252     
253     return 0;
254 }
255
256
257 static int fb_query(struct v3_guest * guest, unsigned int cmd, unsigned long arg, 
258                     void * priv_data) {
259     
260     struct palacios_graphics_console * cons = priv_data;
261     struct v3_fb_query_response q;
262     
263     
264     if (copy_from_user(&q, (void __user *) arg, sizeof(struct v3_fb_query_response))) { 
265         printk("palacios: copy from user in getting query in fb\n");
266         return -EFAULT;
267     }
268     
269     switch (q.request_type) { 
270         case V3_FB_SPEC:
271             // returns only the spec for the FB
272             q.spec = cons->spec;
273
274             break;
275
276         case V3_FB_UPDATE: 
277             // returns whether an update is available for the region
278             // currently always true
279             q.updated = 1;
280
281             break;
282
283         case V3_FB_DATA_BOX: {
284             // Not curently implemented
285             printk("palacios: request for data in bounding box unsupported currently\n");
286             return -EFAULT;
287
288         }
289
290             break;
291             
292         case V3_FB_DATA_ALL: {
293             // First let's sanity check to see if they are requesting the same
294             // spec that we have
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");
297                 return -EFAULT;
298             }
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");
302                 return -EFAULT;
303             }
304             q.updated = 1;
305         }
306             break;
307             
308         default:
309             return -EFAULT;
310     }
311
312     // now we'll copy back any changes we made to the query/response structure
313     if (copy_to_user((void __user *) arg, (void*)&q, sizeof(struct v3_fb_query_response))) { 
314         printk("palacios: unable to copy fb response to user\n");
315         return -EFAULT;
316     }
317
318     return 0;
319
320 }
321
322 static int fb_input(struct v3_guest * guest, 
323                     unsigned int cmd, 
324                     unsigned long arg, 
325                     void * priv_data) {
326
327     struct palacios_graphics_console * cons = priv_data;
328     struct v3_fb_input inp;
329     int rc = 0;
330
331
332     if (copy_from_user(&inp, (void __user *) arg, sizeof(struct v3_fb_input))) { 
333         printk("palacios: copy from user in getting input in fb\n");
334         return -EFAULT;
335     }
336         
337     if ((inp.data_type == V3_FB_KEY) || (inp.data_type == V3_FB_BOTH)) { 
338         rc = palacios_graphics_console_key(guest, cons, inp.scan_code);
339     }
340
341     if ((inp.data_type == V3_FB_MOUSE) || (inp.data_type == V3_FB_BOTH)) { 
342         rc |= palacios_graphics_console_mouse(guest, cons, inp.mouse_data[0],
343                                               inp.mouse_data[1], inp.mouse_data[2]);
344     }
345
346     if (rc) { 
347         return -EFAULT;
348     } else {
349         return 0;
350     }
351 }
352
353
354 static int graphics_console_guest_init(struct v3_guest * guest, void ** vm_data) {
355     struct palacios_graphics_console * graphics_cons = kmalloc(sizeof(struct palacios_graphics_console), GFP_KERNEL);
356
357     if (!graphics_cons) { 
358         printk("palacios: filed to do guest_init for graphics console\n");
359         return -1;
360     }
361
362     memset(graphics_cons, 0, sizeof(struct palacios_graphics_console));
363
364     *vm_data = graphics_cons;
365
366     add_guest_ctrl(guest, V3_VM_FB_INPUT, fb_input, graphics_cons);
367     add_guest_ctrl(guest, V3_VM_FB_QUERY, fb_query, graphics_cons);
368
369     return 0;
370 }
371
372
373
374 static struct linux_ext graphics_cons_ext = {
375     .name = "GRAPHICS_CONSOLE_INTERFACE",
376     .init = graphics_console_init,
377     .deinit = NULL,
378     .guest_init = graphics_console_guest_init,
379     .guest_deinit = NULL
380 };
381
382
383 register_extension(&graphics_cons_ext);