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.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
10 * Copyright (c) 2013, Peter Dinda <pdinda@northwestern.edu>
11 * Copyright (c) 2013, The V3VEE Project <http://www.v3vee.org>
12 * All rights reserved.
14 * Author: Peter Dinda <pdinda@northwestern.edu>
16 * This is free software. You are permitted to use,
17 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
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>
31 #ifndef V3_CONFIG_DEBUG_PARAGRAPH
33 #define PrintDebug(fmts, args...)
37 A paravirtualized graphics device is represented in a configuration file as:
39 <device class="PARAGRAPH" id="pgraph">
41 <mode>mem|gcons_mem|gcons_direct</mode>
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.
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
54 the default mode is mem.
67 #define MAX_REGION_SIZE (MAXX*MAXY*MAXBPP)
69 #define DEFAULT_REGION_ADDR 0x80000000 // Right at 2 GB
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
81 void *my_paddr; // me as mapped into the guest (page aligned GPA)
82 uint64_t my_size_pages; // mapped region in pages
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;
90 // If the local memory is used, this stores it and its
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
97 static uint64_t ceil_pages(uint64_t size)
99 return (size / PAGE_SIZE) + !!(size % PAGE_SIZE);
102 static uint64_t next_pow2(uint64_t size)
106 for (test=1; test < size; test<<=1) {}
112 static int pci_bar_init(int bar_num, uint32_t * dst, void * private_data)
114 struct paragraph_state * state = (struct paragraph_state *)private_data;
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
120 PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Bar init %d 0x%x\n",bar_num, *dst);
123 PrintError(VM_NONE, VCORE_NONE, "paragraph: Strange - Bar Init for bar %d - ignored\n",bar_num);
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);
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");
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");
149 *dst = PCI_MEM32_BAR_VAL((addr_t)(state->my_paddr), 0);
151 PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Bar init done %d is now 0x%x\n",bar_num, *dst);
156 static int pci_bar_write(int bar_num, uint32_t * src, void * private_data)
158 struct paragraph_state * state = (struct paragraph_state *)private_data;
159 struct v3_mem_region *old_reg;
161 PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Bar write %d 0x%x\n",bar_num, *src);
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);
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");
176 PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Bar write is a remapping\n");
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");
183 PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Removing old region at 0x%p\n", (void*)(state->my_paddr));
185 v3_delete_mem_region(state->vm, old_reg);
187 state->my_paddr = (void*)(*src & ~(state->my_size_pages*PAGE_SIZE - 1));
189 *src = PCI_MEM32_BAR_VAL((addr_t)(state->my_paddr), 0);
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);
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");
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");
216 PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Bar write done %d 0x%x\n",bar_num, *src);
225 static int register_dev(struct paragraph_state *state)
228 struct v3_pci_bar bars[6];
229 uint64_t target_size;
231 if (!(state->pci_bus)) {
232 PrintError(state->vm, VCORE_NONE, "paragraph: no pci bus!\n");
236 memset(bars,0,sizeof(struct v3_pci_bar)*6);
238 for (i = 0; i < 6; i++) {
239 bars[i].type = PCI_BAR_NONE;
242 // I will map my memory as a single MEM32 bar
244 switch (state->mode) {
247 target_size = state->mem_size;
250 target_size = state->target_spec.height*state->target_spec.width*state->target_spec.bytes_per_pixel;
253 PrintError(state->vm, VCORE_NONE, "paragraph: Unknown mode\n");
257 if (target_size%PAGE_SIZE) {
258 PrintError(VM_NONE, VCORE_NONE, "paragraph: strange, target_size is not an integral number of pages\n");
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");
265 target_size = next_pow2(target_size);
267 target_size /= PAGE_SIZE; // floor(target_size/page_size)
269 state->my_paddr = (void*)DEFAULT_REGION_ADDR;
270 state->my_size_pages = target_size;
272 PrintDebug(VM_NONE, VCORE_NONE, "paragraph: Requesting passthrough bar at %p (%llu pages)\n", state->my_paddr, state->my_size_pages);
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;
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
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
294 if (!(state->pci_dev)) {
295 PrintError(state->vm, VCORE_NONE, "paragraph: Could not register PCI Device\n");
299 // Now lets set up my configuration space
300 // to identify as the kind of pci device I am
302 state->pci_dev->config_header.vendor_id = VENDOR;
303 state->pci_dev->config_header.device_id = DEVICE;
309 static int paragraph_free_internal(struct paragraph_state *state)
311 if (state->host_cons) {
312 v3_graphics_console_close(state->host_cons);
315 if (state->mem_paddr) {
316 V3_FreePages(state->mem_paddr,ceil_pages(state->mem_size));
324 static int paragraph_free(void * private_data)
326 struct paragraph_state *state = (struct paragraph_state *)private_data;
327 return paragraph_free_internal(state);
331 static int render_callback(v3_graphics_console_t cons,
334 struct paragraph_state *state = (struct paragraph_state *) priv;
336 PrintDebug(VM_NONE, VCORE_NONE, "paragraph: render due to callback\n");
338 switch (state->mode) {
340 PrintError(state->vm, VCORE_NONE, "paragraph: Huh? render callback when in mem mode?\n");
344 PrintDebug(state->vm, VCORE_NONE, "paragraph: render callback GCONS_MEM\n");
346 void *fb = v3_graphics_console_get_frame_buffer_data_rw(state->host_cons,&(state->target_spec));
347 uint64_t target_size = state->target_spec.height*state->target_spec.width*state->target_spec.bytes_per_pixel;
349 // must be smaller than the memory we have allocated
350 target_size = target_size<state->mem_size ? target_size : state->mem_size;
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);
355 memcpy(fb,state->mem_vaddr,target_size);
357 v3_graphics_console_release_frame_buffer_data_rw(state->host_cons);
363 PrintDebug(state->vm, VCORE_NONE, "paragraph: render callback GCONS_DIRECT\n");
368 PrintError(state->vm, VCORE_NONE, "paragraph: Huh? render callback when in unknown mode\n");
376 static int update_callback(v3_graphics_console_t cons,
379 // Yes, Virginia, there is an update clause
384 static struct v3_device_ops dev_ops = {
385 .free = paragraph_free,
391 static int paragraph_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg)
393 struct vm_device *bus;
394 struct paragraph_state *state;
400 if (!(id = v3_cfg_val(cfg,"id"))) {
401 PrintError(vm, VCORE_NONE, "paragraph: gnothi seauton!\n");
405 if (!(bus_id = v3_cfg_val(cfg,"bus"))) {
406 PrintError(vm, VCORE_NONE, "paragraph: failed because there is no pci bus named\n");
410 if (!(bus = v3_find_dev(vm,bus_id))) {
411 PrintError(vm, VCORE_NONE, "paragraph: failed because there is no pci bus given\n");
416 PrintError(vm, VCORE_NONE, "paragraph: failed because there is no bus named %s\n",bus_id);
420 state = (struct paragraph_state *) V3_Malloc(sizeof(struct paragraph_state));
423 PrintError(vm, VCORE_NONE, "paragraph: cannot allocate state\n");
427 memset(state, 0, sizeof(struct paragraph_state));
429 if (!(mode=v3_cfg_val(cfg,"mode"))) {
430 V3_Print(vm, VCORE_NONE, "paragraph: no mode given, assuming you mean mem\n");
433 if (!strncasecmp(mode,"mem",3)) {
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");
443 PrintError(state->vm, VCORE_NONE, "paragraph: Unknown mode %s\n",mode);
444 paragraph_free_internal(state);
450 state->pci_bus = bus;
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_AllocShadowSafePages(vm,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);
461 // the following assumes virtual address continuity
462 state->mem_vaddr = V3_VAddr(state->mem_paddr);
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));
469 if (state->mode==GCONS_MEM || state->mode==GCONS_DIRECT) {
470 struct v3_frame_buffer_spec req;
472 PrintDebug(vm, VCORE_NONE, "paragraph: enabling host frame buffer console (GRAPHICS_CONSOLE)\n");
473 memset(&req,0,sizeof(struct v3_frame_buffer_spec));
477 req.bytes_per_pixel=MAXBPP;
478 req.bits_per_channel=8;
483 state->host_cons = v3_graphics_console_open(vm,&req,&(state->target_spec));
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);
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);
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);
505 v3_graphics_console_release_frame_buffer_data_rw(state->host_cons);
506 // we now assume the host FB will not move...
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);
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);
520 state->dev = v3_add_device(vm, id, &dev_ops, state);
523 PrintError(state->vm, VCORE_NONE, "paragraph: could not attach device %s\n", id);
524 paragraph_free_internal(state);
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);
534 V3_Print(state->vm, VCORE_NONE, "paragraph: added device id %s\n",id);
539 device_register("PARAGRAPH", paragraph_init)