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.


Cleanup and sanity-checking of unintentional integer overflow, unsigned/zero comparis...
[palacios.git] / palacios / src / devices / paragraph.c
1 /*
2  * This file is part of the Palacios Virtual Machine Monitor developed
3  * by the V3VEE Project with funding from the United States National 
4  * Science Foundation and the Department of Energy.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
10  * Copyright (c) 2013, Peter Dinda <pdinda@northwestern.edu> 
11  * Copyright (c) 2013, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Peter Dinda <pdinda@northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 #include <devices/pci.h>
21 #include <palacios/vmm.h>
22 #include <palacios/vmm_types.h>
23 #include <palacios/vmm_io.h>
24 #include <palacios/vmm_debug.h>
25 #include <palacios/vm_guest_mem.h>
26 #include <palacios/vmm_sprintf.h>
27 #include <palacios/vmm_paging.h>
28 #include <interfaces/vmm_graphics_console.h>
29
30
31 #ifndef V3_CONFIG_DEBUG_PARAGRAPH
32 #undef PrintDebug
33 #define PrintDebug(fmts, args...)
34 #endif
35
36 /*
37   A paravirtualized graphics device is represented in a configuration file as:
38
39   <device class="PARAGRAPH" id="pgraph">
40      <bus>pci0</bus>
41      <mode>mem|gcons_mem|gcons_direct</mode>
42   </device>
43
44    
45   The purpose of this device is to project a graphics console
46   to the guest as a PCI device with a single BAR.  The BAR maps
47   the memory of the frame buffer.
48
49   The mode spec means the following:
50      mem = create a backing memory within the device  (dummy device)
51      gcons_mem = like mem, but render to graphics console (memcpy)
52      gcons_direct = use the graphics console as the memory
53
54   the default mode is mem.
55
56 */
57
58
59 #define VENDOR 0xf00f    
60 #define DEVICE 0xd00f   
61
62 #define MAXX   1024
63 #define MAXY   1024
64 #define MAXBPP    4
65
66
67 #define MAX_REGION_SIZE (MAXX*MAXY*MAXBPP)
68
69 #define DEFAULT_REGION_ADDR 0x80000000 // Right at 2 GB
70
71
72
73 struct paragraph_state {
74   enum {MEM, GCONS_MEM, GCONS_DIRECT} mode;
75   //  v3_lock_t          lock;      // my lock
76   struct v3_vm_info *vm;        // my VM
77   struct vm_device  *pci_bus;   // my PCI bus
78   struct vm_device  *dev;       // me as a registered device
79   struct pci_device *pci_dev;   // me as a registered PCI device
80
81   void              *my_paddr;  // me as mapped into the guest (page aligned GPA)
82   uint64_t           my_size_pages;   // mapped region in pages
83
84   // If the graphics console is used, these store it and
85   // the frame buffer spec it is using
86   struct v3_frame_buffer_spec  target_spec;
87   v3_graphics_console_t        host_cons;
88   void                        *host_fb_vaddr;
89
90   // If the local memory is used, this stores it and its
91   // size
92   void              *mem_paddr;     // my memory address, paddr
93   void              *mem_vaddr;    // my memory address, vaddr
94   uint64_t           mem_size;     // my memory size in bytes
95 };
96
97 static uint64_t ceil_pages(uint64_t size)
98 {
99   return (size / PAGE_SIZE) + !!(size % PAGE_SIZE);
100 }
101
102 static uint64_t next_pow2(uint64_t size)
103 {
104   uint64_t test;
105
106   for (test=1; test < size; test<<=1) {}
107
108   return test;
109 }
110
111
112 static int pci_bar_init(int bar_num, uint32_t * dst, void * private_data) 
113 {
114     struct paragraph_state * state = (struct paragraph_state *)private_data;
115
116     // I am going to show up as a PCI_BAR_MEM32 at my_paddr 
117     // and going for my_size_pages pages and not prefetchable
118
119
120     PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Bar init %d 0x%x\n",bar_num, *dst);
121
122     if (bar_num!=0) {
123       PrintError(VM_NONE, VCORE_NONE, "paragraph: Strange - Bar Init for bar %d - ignored\n",bar_num);
124       return 0;
125     }
126
127     if (state->mode==MEM || state->mode==GCONS_MEM) { 
128       PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Adding shadow memory for 0x%p to 0x%p -> 0x%p\n", (void*)(state->my_paddr), (void*)(state->my_paddr+state->my_size_pages*PAGE_SIZE-1), state->mem_paddr);
129
130       if (v3_add_shadow_mem(state->vm, V3_MEM_CORE_ANY,
131                             (addr_t)(state->my_paddr), 
132                             (addr_t)(state->my_paddr+state->my_size_pages*PAGE_SIZE-1), 
133                             (addr_t) (state->mem_paddr))) {
134         PrintError(VM_NONE, VCORE_NONE, "paragraph: Cannot add shadow memory for staging\n");
135         return -1;
136       }
137     } else if (state->mode==GCONS_DIRECT) { 
138       PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Adding shadow memory for 0x%p to 0x%p -> 0x%p\n", (void*)(state->my_paddr), (void*)(state->my_paddr+state->my_size_pages*PAGE_SIZE-1), (void*)V3_PAddr(state->host_fb_vaddr));
139       // Note the physical contiguous assumption here.... 
140       if (v3_add_shadow_mem(state->vm, V3_MEM_CORE_ANY,
141                             (addr_t)(state->my_paddr), 
142                             (addr_t)(state->my_paddr+state->my_size_pages*PAGE_SIZE-1), 
143                             (addr_t)(V3_PAddr(state->host_fb_vaddr)))) {
144         PrintError(VM_NONE, VCORE_NONE, "paragraph: Cannot add shadow memory for host fb\n");
145         return -1;
146       }
147     }
148     
149     *dst = PCI_MEM32_BAR_VAL((addr_t)(state->my_paddr), 0);
150
151     PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Bar init done %d is now 0x%x\n",bar_num, *dst);
152     
153     return 0;
154 }
155         
156 static int pci_bar_write(int bar_num, uint32_t * src, void * private_data) 
157 {
158     struct paragraph_state * state = (struct paragraph_state *)private_data;
159     struct v3_mem_region *old_reg;
160
161     PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Bar write %d 0x%x\n",bar_num, *src);
162
163     if (*src==(uint32_t)0xffffffff) {
164       PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Bar write is a size request - complying\n");
165       *src = ~(state->my_size_pages*PAGE_SIZE-1);
166       PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Returning size mask as %x\n",*src);
167       return 0;
168     }
169
170     // This whacky cast should be ok - my_paddr will be <2^32 since this is a mem32 bar
171     if (*src==(uint32_t)(uint64_t)(state->my_paddr)) { 
172       PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Bar write maps to currently mapped address - done.\n");
173       return 0;
174     } 
175
176     PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Bar write is a remapping\n");
177
178     if (!(old_reg=v3_get_mem_region(state->vm, V3_MEM_CORE_ANY, (addr_t)(state->my_paddr)))) {
179       PrintError(VM_NONE,VCORE_NONE, "paragraph: cannot find old region in bar write...\n");
180       return -1;
181     }
182
183     PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Removing old region at 0x%p\n", (void*)(state->my_paddr));
184
185     v3_delete_mem_region(state->vm, old_reg);
186
187     state->my_paddr = (void*)(*src & ~(state->my_size_pages*PAGE_SIZE - 1));
188
189     *src = PCI_MEM32_BAR_VAL((addr_t)(state->my_paddr), 0);
190
191     PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Moving paragraph to: start=0x%p, size=%llu\n",
192                state->my_paddr, state->my_size_pages*PAGE_SIZE);
193
194
195     if (state->mode==MEM || state->mode==GCONS_MEM) { 
196       PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Adding shadow memory for 0x%p to 0x%p -> 0x%p\n", (void*)(state->my_paddr), (void*)(state->my_paddr+state->my_size_pages*PAGE_SIZE-1), state->mem_paddr);
197       if (v3_add_shadow_mem(state->vm, V3_MEM_CORE_ANY,
198                             (addr_t)(state->my_paddr),
199                             (addr_t)(state->my_paddr+state->my_size_pages*PAGE_SIZE-1),
200                             (addr_t) (state->mem_paddr))) {
201         PrintError(VM_NONE, VCORE_NONE, "paragraph: Cannot add shadow memory for staging\n");
202         return -1;
203       }
204     } else if (state->mode==GCONS_DIRECT) { 
205       // Note the physical contiguous assumption here.... 
206       PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Adding shadow memory for 0x%p to 0x%p -> 0x%p\n", (void*)(state->my_paddr), (void*)(state->my_paddr+state->my_size_pages*PAGE_SIZE-1), (void*)V3_PAddr(state->host_fb_vaddr));
207       if (v3_add_shadow_mem(state->vm, V3_MEM_CORE_ANY,
208                             (addr_t)(state->my_paddr),
209                             (addr_t)(state->my_paddr+state->my_size_pages*PAGE_SIZE-1),
210                             (addr_t)(V3_PAddr(state->host_fb_vaddr)))) {
211         PrintError(VM_NONE, VCORE_NONE, "paragraph: Cannot add shadow memory for host fb\n");
212         return -1;
213       }
214     }
215
216     PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Bar write done %d 0x%x\n",bar_num, *src);
217
218     return 0;
219 }
220
221       
222     
223
224
225 static int register_dev(struct paragraph_state *state)  
226 {
227   int i;
228   struct v3_pci_bar bars[6];
229   uint64_t target_size;
230   
231   if (!(state->pci_bus)) { 
232     PrintError(state->vm, VCORE_NONE, "paragraph: no pci bus!\n");
233     return -1;
234   }
235   
236   memset(bars,0,sizeof(struct v3_pci_bar)*6);
237
238   for (i = 0; i < 6; i++) {
239     bars[i].type = PCI_BAR_NONE;
240   }
241
242   // I will map my memory as a single MEM32 bar
243
244   switch (state->mode) { 
245   case MEM:
246   case GCONS_MEM:
247     target_size = state->mem_size;
248     break;
249   case GCONS_DIRECT:
250       target_size = (uint64_t)state->target_spec.height*state->target_spec.width*state->target_spec.bytes_per_pixel;
251     break;
252   default:
253     PrintError(state->vm, VCORE_NONE, "paragraph: Unknown mode\n");
254     return -1;
255   }
256   
257   if (target_size%PAGE_SIZE) { 
258     PrintError(VM_NONE, VCORE_NONE, "paragraph: strange, target_size is not an integral number of pages\n");
259   }
260   
261   if (target_size != next_pow2(target_size)) { 
262     PrintError(VM_NONE, VCORE_NONE, "paragraph: strange, target_size is not a power of two\n");
263   }
264
265   target_size = next_pow2(target_size);
266
267   target_size /= PAGE_SIZE;  // floor(target_size/page_size)
268
269   state->my_paddr = (void*)DEFAULT_REGION_ADDR;
270   state->my_size_pages = target_size;
271
272   PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Requesting passthrough bar at %p (%llu pages)\n", state->my_paddr, state->my_size_pages);
273
274   bars[0].type = PCI_BAR_PASSTHROUGH;
275   bars[0].private_data = state;
276   bars[0].bar_init = pci_bar_init;
277   bars[0].bar_write = pci_bar_write;
278
279   state->pci_dev =
280     v3_pci_register_device(state->pci_bus, // my bus interace
281                            PCI_STD_DEVICE, // I'm a typical device
282                            0,              // put me in bus zero
283                            -1,             // put me in any slot you want
284                            0,              // I am function 0 in that slot
285                            "PARAGRAPH",    // My name 
286                            bars,           // My bars
287                            NULL,           // I don't care about config writes
288                            NULL,           // I don't care about config reads
289                            NULL,           // I don't care about cmd updates
290                            NULL,           // I don't care about rom updates
291                            state);         // this is my internal state
292
293
294   if (!(state->pci_dev)) { 
295     PrintError(state->vm, VCORE_NONE, "paragraph: Could not register PCI Device\n");
296     return -1;
297   }
298
299   // Now lets set up my configuration space
300   // to identify as the kind of pci device I am
301         
302   state->pci_dev->config_header.vendor_id = VENDOR;
303   state->pci_dev->config_header.device_id = DEVICE;
304
305   return 0;
306 }
307
308
309 static int paragraph_free_internal(struct paragraph_state *state)
310 {
311   if (state->host_cons) { 
312     v3_graphics_console_close(state->host_cons);
313   }
314
315   if (state->mem_paddr) { 
316     V3_FreePages(state->mem_paddr,ceil_pages(state->mem_size));
317   }
318
319   V3_Free(state);
320
321   return 0;
322 }
323
324 static int paragraph_free(void * private_data) 
325 {
326     struct paragraph_state *state = (struct paragraph_state *)private_data;
327     return paragraph_free_internal(state);
328 }
329
330
331 static int render_callback(v3_graphics_console_t cons,
332                            void *priv)
333 {
334   struct paragraph_state *state = (struct paragraph_state *) priv;
335   
336   PrintDebug(VM_NONE, VCORE_NONE, "paragraph: render due to callback\n");
337
338   switch (state->mode) {
339   case MEM:
340     PrintError(state->vm, VCORE_NONE, "paragraph: Huh?  render callback when in mem mode?\n");
341     return -1;
342     break;
343   case GCONS_MEM: {
344     PrintDebug(state->vm, VCORE_NONE, "paragraph: render callback GCONS_MEM\n");
345
346     void *fb = v3_graphics_console_get_frame_buffer_data_rw(state->host_cons,&(state->target_spec));
347     uint64_t target_size = (uint64_t)state->target_spec.height*state->target_spec.width*state->target_spec.bytes_per_pixel;
348     
349     // must be smaller than the memory we have allocated
350     target_size = target_size<state->mem_size ? target_size : state->mem_size;
351     
352     PrintDebug(state->vm, VCORE_NONE, "paragraph: render - copying %llu bytes from our vaddr 0x%p to fb vaddr 0x%p\n", target_size, state->mem_vaddr, fb);
353
354     
355     memcpy(fb,state->mem_vaddr,target_size);
356
357     v3_graphics_console_release_frame_buffer_data_rw(state->host_cons);
358     
359     return 0;
360   }                                         
361     break;
362   case GCONS_DIRECT:
363     PrintDebug(state->vm, VCORE_NONE, "paragraph: render callback GCONS_DIRECT\n");
364     // nothing to do;
365     return 0;
366     break;
367   default:
368     PrintError(state->vm, VCORE_NONE, "paragraph: Huh?  render callback when in unknown mode\n");
369     return -1;
370     break;
371   }
372
373   return 0;
374 }
375
376 static int update_callback(v3_graphics_console_t cons,
377                            void *priv)
378 {
379   // Yes, Virginia, there is an update clause
380   return 1;
381 }
382
383
384 static struct v3_device_ops dev_ops = {
385     .free = paragraph_free,
386 };
387
388
389
390
391 static int paragraph_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) 
392 {
393   struct vm_device         *bus;
394   struct paragraph_state  *state;
395   char *id;
396   char *bus_id;
397   char *mode;
398
399   
400   if (!(id = v3_cfg_val(cfg,"id"))) {
401     PrintError(vm, VCORE_NONE, "paragraph: gnothi seauton!\n");  
402     return -1;
403   }
404   
405   if (!(bus_id = v3_cfg_val(cfg,"bus"))) { 
406     PrintError(vm, VCORE_NONE, "paragraph: failed because there is no pci bus named\n");
407     return -1;
408   }
409   
410   if (!(bus = v3_find_dev(vm,bus_id))) { 
411     PrintError(vm, VCORE_NONE, "paragraph: failed because there is no pci bus given\n");
412     return -1;
413   }
414   
415   if (!bus) { 
416     PrintError(vm, VCORE_NONE, "paragraph: failed because there is no bus named %s\n",bus_id);
417     return -1;
418   }
419  
420   state = (struct paragraph_state *) V3_Malloc(sizeof(struct paragraph_state));
421
422   if (!state) {
423     PrintError(vm, VCORE_NONE, "paragraph: cannot allocate state\n");
424     return -1;
425   }
426
427   memset(state, 0, sizeof(struct paragraph_state));
428
429   if (!(mode=v3_cfg_val(cfg,"mode"))) { 
430     V3_Print(vm, VCORE_NONE, "paragraph: no mode given, assuming you mean mem\n");
431     state->mode=MEM;
432   } else {
433     if (!strncasecmp(mode,"mem",3)) { 
434       state->mode=MEM;
435       V3_Print(state->vm, VCORE_NONE, "paragraph: mode set to mem\n");
436     } else if (!strncasecmp(mode,"gcons_mem",9)) { 
437       state->mode=GCONS_MEM;
438       V3_Print(state->vm, VCORE_NONE, "paragraph: mode set to gcons_mem\n");
439     } else if (!strncasecmp(mode,"gcons_direct",12)) { 
440       state->mode=GCONS_DIRECT;
441       V3_Print(state->vm, VCORE_NONE, "paragraph: mode set to gcons_direct\n");
442     } else {
443       PrintError(state->vm, VCORE_NONE, "paragraph: Unknown mode %s\n",mode);
444       paragraph_free_internal(state);
445       return -1;
446     }
447   }
448                                                 
449   state->vm = vm;
450   state->pci_bus = bus;
451
452   if (state->mode==MEM || state->mode==GCONS_MEM) { 
453     state->mem_size=MAXX*MAXY*MAXBPP;
454     PrintDebug(vm, VCORE_NONE, "paragraph: allocating %llu bytes for local framebuffer\n", state->mem_size);
455     state->mem_paddr = V3_AllocPages(ceil_pages(state->mem_size));
456     if (!state->mem_paddr) { 
457       PrintError(state->vm, VCORE_NONE, "paragraph: Cannot allocate memory for framebuffer\n");
458       paragraph_free_internal(state);
459       return -1;
460     }
461     // the following assumes virtual address continuity
462     state->mem_vaddr = V3_VAddr(state->mem_paddr);
463    
464     PrintDebug(vm, VCORE_NONE, "paragraph: staging memory (state->mem) at paddr 0x%p and vaddr 0x%p size=%llu bytes (%llu pages)\n",
465                state->mem_paddr, state->mem_vaddr,
466                state->mem_size, ceil_pages(state->mem_size));
467   }
468   
469   if (state->mode==GCONS_MEM || state->mode==GCONS_DIRECT) { 
470     struct v3_frame_buffer_spec req;
471
472     PrintDebug(vm, VCORE_NONE, "paragraph: enabling host frame buffer console (GRAPHICS_CONSOLE)\n");
473     memset(&req,0,sizeof(struct v3_frame_buffer_spec));
474
475     req.height=MAXY;
476     req.width=MAXX;
477     req.bytes_per_pixel=MAXBPP;
478     req.bits_per_channel=8;
479     req.red_offset=0;
480     req.green_offset=1;
481     req.blue_offset=2;
482     
483     state->host_cons = v3_graphics_console_open(vm,&req,&(state->target_spec));
484     
485     if (!state->host_cons) { 
486       PrintError(vm, VCORE_NONE, "paragraph: unable to open host OS's graphics console\n");
487       paragraph_free_internal(state);
488       return -1;
489     }
490     
491     if (memcmp(&req,&(state->target_spec),sizeof(req))) {
492       PrintDebug(vm, VCORE_NONE, "paragraph: warning: target spec differs from requested spec\n");
493       PrintDebug(vm, VCORE_NONE, "paragraph: request: %u by %u by %u with %u bpc and r,g,b at %u, %u, %u\n", req.width, req.height, req.bytes_per_pixel, req.bits_per_channel, req.red_offset, req.green_offset, req.blue_offset);
494       PrintDebug(vm, VCORE_NONE, "paragraph: response: %u by %u by %u with %u bpc and r,g,b at %u, %u, %u\n", state->target_spec.width, state->target_spec.height, state->target_spec.bytes_per_pixel, state->target_spec.bits_per_channel, state->target_spec.red_offset, state->target_spec.green_offset, state->target_spec.blue_offset);
495     }
496
497     if (state->mode==GCONS_DIRECT) { 
498       PrintDebug(state->vm, VCORE_NONE, "paragraph: grabbing host console address\n");
499       state->host_fb_vaddr = v3_graphics_console_get_frame_buffer_data_rw(state->host_cons,&(state->target_spec));
500       if (!state->host_fb_vaddr) { 
501         PrintError(state->vm, VCORE_NONE, "paragraph: Unable to acquire host's framebuffer address\n");
502         paragraph_free_internal(state);
503         return -1;
504       }
505       v3_graphics_console_release_frame_buffer_data_rw(state->host_cons);
506       // we now assume the host FB will not move...
507     }
508
509     if (v3_graphics_console_register_render_request(state->host_cons, render_callback, state)!=0) {      PrintError(vm, VCORE_NONE, "paragraph: cannot install render callback\n");
510       paragraph_free_internal(state);
511       return -1;
512     }
513     if (v3_graphics_console_register_update_inquire(state->host_cons, update_callback, state)!=0) {
514       PrintError(vm, VCORE_NONE, "paragraph: cannot install update inquire callback\n");
515       paragraph_free_internal(state);
516       return -1;
517     }
518   }
519   
520   state->dev = v3_add_device(vm, id, &dev_ops, state);
521
522   if (!(state->dev)) {
523     PrintError(state->vm, VCORE_NONE, "paragraph: could not attach device %s\n", id);
524     paragraph_free_internal(state);
525     return -1;
526   }
527
528   if (register_dev(state) !=0 ) { 
529     PrintError(state->vm, VCORE_NONE, "paragraph: could not set up device for pci\n");
530     paragraph_free_internal(state);
531     return -1;
532   }
533
534   V3_Print(state->vm, VCORE_NONE, "paragraph: added device id %s\n",id);
535
536   return 0;
537 }
538
539 device_register("PARAGRAPH", paragraph_init)