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) 2011, Peter Dinda <pdinda@northwestern.edu>
11 * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
12 * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
13 * All rights reserved.
16 * Peter Dinda <pdinda@northwestern.edu> (PCI front device forwarding to host dev interface)
17 * Jack Lange <jarusl@cs.northwestern.edu> (original PCI passthrough to physical hardware)
19 * This is free software. You are permitted to use,
20 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
25 This is front-end PCI device intended to be used together with the
26 host device interface and a *virtual* PCI device implementation in
27 the host OS. It makes it possible to project such a virtual device
28 into the guest as a PCI device. It's based on the PCI passthrough
29 device, which projects *physical* PCI devices into the guest.
31 If you need to project a non-PCI host-based virtual or physical
32 device into the guest, you should use the generic device.
37 * The basic idea is that we do not change the hardware PCI configuration
38 * Instead we modify the guest environment to map onto the physical configuration
40 * The pci subsystem handles most of the configuration space, except for the bar registers.
41 * We handle them here, by either letting them go directly to hardware or remapping through virtual hooks
43 * Memory Bars are always remapped via the shadow map,
44 * IO Bars are selectively remapped through hooks if the guest changes them
47 #include <palacios/vmm.h>
48 #include <palacios/vmm_dev_mgr.h>
49 #include <palacios/vmm_sprintf.h>
50 #include <palacios/vmm_lowlevel.h>
51 #include <palacios/vm_guest.h>
52 #include <palacios/vmm_symspy.h>
54 #include <devices/pci.h>
55 #include <devices/pci_types.h>
57 #include <interfaces/vmm_host_dev.h>
60 #ifndef V3_CONFIG_DEBUG_PCI_FRONT
62 #define PrintDebug(fmt, args...)
66 // Our own address in PCI-land
77 } __attribute__((packed));
78 } __attribute__((packed));
81 // identical to PCI passthrough device
82 typedef enum { PT_BAR_NONE,
88 PT_EXP_ROM } pt_bar_type_t;
90 // identical to PCI passthrough device
95 /* We store 64 bit memory bar addresses in the high BAR
96 * because they are the last to be updated
97 * This means that the addr field must be 64 bits
108 struct pci_front_internal {
109 // this is our local cache of what the host device has
111 uint8_t config_space[256];
112 struct pci_config_header real_hdr;
113 } __attribute__((packed));
115 // We do need a representation of the bars
116 // since we need to be made aware when they are written
117 // so that we can change the hooks.
119 // We assume here that the PCI subsystem, on a bar write
120 // will first send us a config_update, which we forward to
121 // the host dev. Then it will send us a bar update
122 // which we will use to rehook the device
124 struct pt_bar bars[6]; // our bars (for update purposes)
126 // Currently unsupported
128 //struct pt_bar exp_rom; // and exp ram areas of the config space, above
130 struct vm_device *pci_bus; // what bus we are attached to
131 struct pci_device *pci_dev; // our representation as a registered PCI device
133 union pci_addr_reg pci_addr; // our pci address
137 v3_host_dev_t host_dev; // the actual implementation
139 void *rom_hpa; // where our copy of the rom is in the host (physically cont.)
140 uint64_t rom_size; // bytes
141 void *rom_gpa; // where we are mapping it into the guest
148 static int push_config(struct pci_front_internal *state, uint8_t *config)
150 if (v3_host_dev_config_write(state->host_dev, 0, config, 256) != 256) {
158 static int pull_config(struct pci_front_internal *state, uint8_t *config)
160 if (v3_host_dev_read_config(state->host_dev, 0, config, 256) != 256) {
168 static int pci_front_read_mem(struct guest_info * core,
176 struct vm_device *dev = (struct vm_device *) priv;
177 struct pci_front_internal *state = (struct pci_front_internal *) dev->private_data;
179 PrintDebug(core->vm_info, core, "pci_front (%s): reading 0x%x bytes from gpa 0x%p from host dev 0x%p ...",
180 state->name, len, (void*)gpa, state->host_dev);
182 rc = v3_host_dev_read_mem(state->host_dev, gpa, dst, len);
184 PrintDebug(core->vm_info, core, " done ... read %d bytes: 0x", rc);
186 for (i = 0; i < rc; i++) {
187 PrintDebug(core->vm_info, core, "%x", ((uint8_t *)dst)[i]);
190 PrintDebug(core->vm_info, core, "\n");
195 static int pci_front_write_mem(struct guest_info * core,
203 struct vm_device *dev = (struct vm_device *) priv;
204 struct pci_front_internal *state = (struct pci_front_internal *) dev->private_data;
206 PrintDebug(core->vm_info, core, "pci_front (%s): writing 0x%x bytes to gpa 0x%p to host dev 0x%p bytes=0x",
207 state->name, len, (void*)gpa, state->host_dev);
209 for (i = 0; i < len; i++) {
210 PrintDebug(core->vm_info, core, "%x", ((uint8_t *)src)[i]);
213 rc = v3_host_dev_write_mem(state->host_dev, gpa, src, len);
215 PrintDebug(core->vm_info, core, " %d bytes written\n",rc);
221 static int pci_front_read_port(struct guest_info * core,
228 struct pci_front_internal *state = (struct pci_front_internal *) priv_data;
230 PrintDebug(core->vm_info, core, "pci_front (%s): reading 0x%x bytes from port 0x%x from host dev 0x%p ...",
231 state->name, len, port, state->host_dev);
233 int rc = v3_host_dev_read_io(state->host_dev, port, dst, len);
235 PrintDebug(core->vm_info, core, " done ... read %d bytes: 0x", rc);
237 for (i = 0; i < rc; i++) {
238 PrintDebug(core->vm_info, core, "%x", ((uint8_t *)dst)[i]);
241 PrintDebug(core->vm_info, core, "\n");
247 static int pci_front_write_port(struct guest_info * core,
254 struct pci_front_internal *state = (struct pci_front_internal *) priv_data;
257 PrintDebug(core->vm_info, core, "pci_front (%s): writing 0x%x bytes to port 0x%x to host dev 0x%p bytes=0x",
258 state->name, len, port, state->host_dev);
260 for (i = 0; i < len; i++) {
261 PrintDebug(core->vm_info, core, "%x", ((uint8_t *)src)[i]);
264 int rc = v3_host_dev_write_io(state->host_dev, port, src, len);
266 PrintDebug(core->vm_info, core, " %d bytes written\n",rc);
274 // This is called at registration time for the device
276 // We assume that someone has called pull_config to get a local
277 // copy of the config data from the host device by this point
279 // It might be smarter to do the pull config here since
280 // in init we may not yet have the host device running...
282 // KCH: we need to make sure that we don't hook I/O BARs and Mem BARs that
283 // haven't been configured by anyone yet (e.g. QEMU)
284 static int pci_bar_init(int bar_num, uint32_t * dst, void * private_data) {
285 struct vm_device * dev = (struct vm_device *)private_data;
286 struct pci_front_internal * state = (struct pci_front_internal *)(dev->private_data);
289 if (pull_config(state,state->config_space)) {
290 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): cannot initially configure device\n",state->name);
291 v3_remove_device(dev);
296 const uint32_t bar_base_reg = 4; // offset in 32bit words to skip to the first bar
298 union pci_addr_reg pci_addr = {state->pci_addr.value}; // my address
300 uint32_t bar_val = 0;
301 uint32_t max_val = 0;
303 struct pt_bar * pbar = &(state->bars[bar_num]);
305 pci_addr.reg = bar_base_reg + bar_num;
307 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): pci_bar_init: PCI Address = 0x%x\n", state->name, pci_addr.value);
309 // This assumees that pull_config() has been previously called and
310 // we have a local copy of the host device's configuration space
311 //bar_val = *((uint32_t*)(&(state->config_space[(bar_base_reg+bar_num)*4])));
312 v3_host_dev_read_config(state->host_dev, bar_num*4 + 16, &bar_val, 4);
314 // Now let's set our copy of the relevant bar accordingly
317 // Now we will configure the hooks relevant to this bar
319 // We preset this type when we encounter a MEM64 Low BAR
320 // This is a 64 bit memory region that we turn into a memory hook
321 if (pbar->type == PT_BAR_MEM64_HI) {
322 struct pt_bar * lo_pbar = &(state->bars[bar_num - 1]);
324 max_val = PCI_MEM64_MASK_HI;
326 v3_host_dev_write_config(state->host_dev, bar_num*4 + 16, &max_val, 4);
327 v3_host_dev_read_config(state->host_dev, bar_num*4 + 16, &max_val, 4);
328 v3_host_dev_write_config(state->host_dev, bar_num*4 + 16, &bar_val, 4);
330 pbar->size += lo_pbar->size;
332 /* this BAR hasn't been mapped yet */
336 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): pci_bar_init: Adding 64 bit PCI mem region: start=0x%p, end=0x%p as a full hook\n",
338 (void *)(addr_t)pbar->addr,
339 (void *)(addr_t)(pbar->addr + pbar->size));
341 if (v3_hook_full_mem(dev->vm,
344 pbar->addr+pbar->size-1,
349 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): pci_bar_init: failed to hook 64 bit region (0x%p, 0x%p)\n",
351 (void *)(addr_t)pbar->addr,
352 (void *)(addr_t)(pbar->addr + pbar->size - 1));
357 } else if ((bar_val & 0x3) == 0x1) {
358 // This an I/O port region which we will turn into a range of hooks
362 pbar->type = PT_BAR_IO;
363 pbar->addr = PCI_IO_BASE(bar_val);
365 max_val = bar_val | PCI_IO_MASK;
368 uint64_t v3_host_dev_write_config(v3_host_dev_t hdev,
375 v3_host_dev_write_config(state->host_dev, bar_num*4 + 16, &max_val, 4);
376 v3_host_dev_read_config(state->host_dev, bar_num*4 + 16, &max_val, 4);
377 v3_host_dev_write_config(state->host_dev, bar_num*4 + 16, &bar_val, 4);
379 pbar->size = (uint16_t)~PCI_IO_BASE(max_val) + 1;
380 // ~((bar_val | PCI_IO_MASK) & PCI_IO_MASK) + 1 == ~(PCI_IO_MASK) + 1 == ~(0xfffffffc) + 1 == 0x3 + 1 == 0x4
385 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): pci_bar_init: hooking ports 0x%x through 0x%x\n",
386 state->name, (uint32_t)pbar->addr, (uint32_t)pbar->addr + pbar->size - 1);
388 for (i = 0; i < pbar->size; i++) {
389 if (v3_dev_hook_io(dev,
392 pci_front_write_port)<0) {
393 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): pci_bar_init: unabled to hook I/O port 0x%x\n",state->name, (unsigned)(pbar->addr+i));
401 // might be a 32 bit memory region or an empty bar
403 max_val = bar_val | PCI_MEM_MASK;
404 v3_host_dev_write_config(state->host_dev, bar_num*4 + 16, &max_val, 4);
405 v3_host_dev_read_config(state->host_dev, bar_num*4 + 16, &max_val, 4);
406 v3_host_dev_write_config(state->host_dev, bar_num*4 + 16, &bar_val, 4);
410 // nothing, so just ignore it
411 pbar->type = PT_BAR_NONE;
414 // memory region - hook it
416 if ((bar_val & 0x6) == 0x0) {
417 // 32 bit memory region
419 pbar->type = PT_BAR_MEM32;
420 pbar->addr = PCI_MEM32_BASE(bar_val);
421 pbar->size = ~PCI_MEM32_BASE(max_val) + 1;
426 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): pci_init_bar: adding 32 bit PCI mem region: start=0x%p, end=0x%p\n",
428 (void *)(addr_t)pbar->addr,
429 (void *)(addr_t)(pbar->addr + pbar->size));
431 if (v3_hook_full_mem(dev->vm,
434 pbar->addr+pbar->size-1,
438 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): pci_init_bar: unable to hook 32 bit memory region 0x%p to 0x%p\n",
439 state->name, (void*)(pbar->addr), (void*)(pbar->addr+pbar->size-1));
444 } else if ((bar_val & 0x6) == 0x2) {
446 // 24 bit memory region
448 pbar->type = PT_BAR_MEM24;
449 pbar->addr = PCI_MEM24_BASE(bar_val);
450 pbar->size = ~PCI_MEM24_BASE(max_val) + 1;
456 if (v3_hook_full_mem(dev->vm,
459 pbar->addr+pbar->size-1,
463 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): pci_init_bar: unable to hook 24 bit memory region 0x%p to 0x%p\n",
464 state->name, (void*)(pbar->addr), (void*)(pbar->addr+pbar->size-1));
469 } else if ((bar_val & 0x6) == 0x4) {
471 // partial update of a 64 bit region, no hook done yet
473 struct pt_bar * hi_pbar = &(state->bars[bar_num + 1]);
475 pbar->type = PT_BAR_MEM64_LO;
476 hi_pbar->type = PT_BAR_MEM64_HI;
478 // Set the low bits, only for temporary storage until we calculate the high BAR
479 pbar->addr = PCI_MEM64_BASE_LO(bar_val);
480 pbar->size = ~PCI_MEM64_BASE_LO(max_val) + 1;
482 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): pci_bar_init: partial 64 bit update\n",state->name);
485 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): pci_bar_init: invalid memory bar type\n",state->name);
494 // Update the pci subsystem versions
502 // If the guest modifies a BAR, we expect that pci.c will do the following,
505 // 1. notify us via the config_update callback, which we will feed back
506 // to the host device
507 // 2. notify us of the bar change via the following callback
509 // This callback will unhook as needed for the old bar value and rehook
510 // as needed for the new bar value
512 static int pci_bar_write(int bar_num, uint32_t * src, void * private_data) {
513 struct vm_device * dev = (struct vm_device *)private_data;
514 struct pci_front_internal * state = (struct pci_front_internal *)dev->private_data;
516 struct pt_bar * pbar = &(state->bars[bar_num]);
518 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): bar update: bar_num=%d, src=0x%x\n", state->name, bar_num, *src);
519 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): the current bar has size=%u, type=%d, addr=%p, val=0x%x\n",
520 state->name, pbar->size, pbar->type, (void *)(addr_t)pbar->addr, pbar->val);
522 v3_host_dev_write_config(state->host_dev, bar_num*4 + 16, src, 4);
523 v3_host_dev_read_config(state->host_dev, bar_num*4 + 16, &state->config_space[bar_num*4 + 16], 4);
524 if (*src == 0xffffffff || *src == 0) {
525 PrintDebug(dev->vm, VCORE_NONE, "Ignoring BAR write for bar#%d, val=0x%x\n", bar_num, *src);
531 if (pbar->type == PT_BAR_NONE) {
532 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): bar update is to empty bar - ignored\n",state->name);
534 } else if (pbar->type == PT_BAR_IO) {
539 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): unhooking I/O ports 0x%x through 0x%x\n",
541 (unsigned)(pbar->addr), (unsigned)(pbar->addr+pbar->size-1));
542 for (i = 0; i < pbar->size; i++) {
543 if (v3_dev_unhook_io(dev, pbar->addr + i) == -1) {
544 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): could not unhook previously hooked port.... 0x%x\n",
546 (uint32_t)pbar->addr + i);
552 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): setting I/O Port range size=%d\n", state->name, pbar->size);
555 // Not clear if this cooking is needed... why not trust
556 // the write? Who cares if it wants to suddenly hook more ports?
559 // clear the low bits to match the size
560 *src &= ~(pbar->size - 1);
563 *src |= (pbar->val & ~PCI_IO_MASK);
565 pbar->addr = PCI_IO_BASE(*src);
567 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): cooked src=0x%x\n", state->name, *src);
569 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): rehooking I/O ports 0x%x through 0x%x\n",
570 state->name, (unsigned)(pbar->addr), (unsigned)(pbar->addr+pbar->size-1));
573 for (i = 0; i < pbar->size; i++) {
574 if (v3_dev_hook_io(dev,
577 pci_front_write_port)<0) {
578 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): unable to rehook port 0x%x\n",state->name, (unsigned)(pbar->addr+i));
584 } else if (pbar->type == PT_BAR_MEM32) {
587 if (v3_unhook_mem(dev->vm,V3_MEM_CORE_ANY,pbar->addr)<0) {
588 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): unable to unhook 32 bit memory region starting at 0x%p\n",
589 state->name, (void*)(pbar->addr));
594 // Again, not sure I need to do this cooking...
596 // clear the low bits to match the size
597 *src &= ~(pbar->size - 1);
600 *src |= (pbar->val & ~PCI_MEM_MASK);
602 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): cooked src=0x%x\n", state->name, *src);
604 pbar->addr = PCI_MEM32_BASE(*src);
606 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): rehooking 32 bit memory region 0x%p through 0x%p\n",
607 state->name, (void*)(pbar->addr), (void*)(pbar->addr + pbar->size - 1));
610 if (v3_hook_full_mem(dev->vm,
613 pbar->addr+pbar->size-1,
617 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): unable to rehook 32 bit memory region 0x%p through 0x%p\n",
618 state->name, (void*)(pbar->addr), (void*)(pbar->addr + pbar->size - 1));
623 } else if (pbar->type == PT_BAR_MEM64_LO) {
624 // We only store the written values here, the actual reconfig comes when the high BAR is updated
626 // clear the low bits to match the size
627 *src &= ~(pbar->size - 1);
630 *src |= (pbar->val & ~PCI_MEM_MASK);
632 // Temp storage, used when hi bar is written
633 pbar->addr = PCI_MEM64_BASE_LO(*src);
635 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): handled partial update for 64 bit memory region\n",state->name);
637 } else if (pbar->type == PT_BAR_MEM64_HI) {
638 struct pt_bar * lo_vbar = &(state->bars[bar_num - 1]);
641 if (v3_unhook_mem(dev->vm,V3_MEM_CORE_ANY,pbar->addr)<0) {
642 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): unable to unhook 64 bit memory region starting at 0x%p\n",
643 state->name, (void*)(pbar->addr));
649 // We don't set size, because we assume region is less than 4GB
652 *src |= (pbar->val & ~PCI_MEM64_MASK_HI);
654 pbar->addr = PCI_MEM64_BASE_HI(*src);
656 pbar->addr += lo_vbar->addr;
658 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): rehooking 64 bit memory region 0x%p through 0x%p\n",
659 state->name, (void*)(pbar->addr), (void*)(pbar->addr + pbar->size - 1));
661 if (v3_hook_full_mem(dev->vm,
664 pbar->addr+pbar->size-1,
668 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): unable to rehook 64 bit memory region 0x%p through 0x%p\n",
669 state->name, (void*)(pbar->addr), (void*)(pbar->addr + pbar->size - 1));
674 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): unhandled PCI bar type %d\n", state->name, pbar->type);
683 static int pci_front_cmd_update(struct pci_device *pci_dev, pci_cmd_t cmd, uint64_t arg, void * priv_data)
685 #ifdef V3_CONFIG_DEBUG_PCI_FRONT
686 struct vm_device * dev = (struct vm_device *)priv_data;
687 struct pci_front_internal * state = (struct pci_front_internal *)dev->private_data;
689 uint16_t command = (uint16_t)arg;
692 PrintDebug(VM_NONE, VCORE_NONE, "pci_front (%s): command update 0x%x\n", state->name,command);
694 // Note that the config_update corresponding to this callback
695 // has occurred before this request, and so has already been delivered
696 // to the host_dev interface.
698 // We do not need to handle the special semantics of cmd_update here
706 static void remap_rom(struct v3_vm_info *vm, struct pci_front_internal *state, void *new_gpa, int enable)
708 if (state->rom_gpa) {
710 struct v3_mem_region *old_reg = v3_get_mem_region(vm,V3_MEM_CORE_ANY,(addr_t)state->rom_gpa);
712 PrintDebug(vm,VCORE_NONE,"pci_front: removing old memory region\n");
713 v3_delete_mem_region(vm,old_reg);
719 if (v3_add_shadow_mem(vm,
723 new_gpa+state->rom_size-1,
724 (addr_t)state->rom_hpa)) {
725 PrintError(vm,VCORE_NONE,"pci_front: cannot add new shadow mapping\n");
728 state->rom_gpa = new_gpa;
731 PrintDebug(vm,VCORE_NONE,"pci_front: remapped rom to %p (enable=%u)\n",state->rom_gpa,enable) ;
734 #define ROM_ADDR_MASK 0xfffff800
735 #define ROM_ENABLE 0x1
736 #define ROM_BAR_OFFSET 0x30
738 static int pci_front_rom_update(struct pci_device * pci_dev, uint32_t * src, void * priv_data)
740 struct vm_device * dev = (struct vm_device *)priv_data;
741 struct pci_front_internal * state = (struct pci_front_internal *)dev->private_data;
743 PrintDebug(dev->vm,VCORE_NONE,"pci_front: rom update with value 0x%x\n",*src);
745 // the assumption is that the config_write happened before this
746 if (((*src) & ROM_ADDR_MASK) == ROM_ADDR_MASK) {
747 // this is a write to get the size
748 // we will ignore it and assume the that
749 // preceding config_write occured, now we just need to do a pull to make
750 // sure we have the right size
751 v3_host_dev_read_config(state->host_dev, 0x30, &state->config_space[0x30], 4);
753 // actually mapping device, address now valid
754 void *addr = (void*)(uint64_t)((*src) & ROM_ADDR_MASK);
755 remap_rom(dev->vm,state,addr,(*src) & ROM_ENABLE);
762 static int pci_front_config_read(struct pci_device *pci_dev, uint_t reg_num, void * dst, uint_t length, void * priv_data)
764 struct vm_device * dev = (struct vm_device *)priv_data;
765 struct pci_front_internal * state = (struct pci_front_internal *)dev->private_data;
768 memcpy(dst, (void*)&(state->config_space[reg_num]), length);
771 PrintDebug(VM_NONE, VCORE_NONE, "read callback (%d bytes starting at reg num %x)\n", length, reg_num);
772 for (i = 0; i < length; i++) {
773 PrintDebug(VM_NONE, VCORE_NONE, "byte %d: %x\n", i, state->config_space[reg_num+i]);
780 static int pci_front_config_update(struct pci_device *pci_dev, uint_t reg_num, void * src, uint_t length, void * private_data)
783 struct vm_device * dev = (struct vm_device *)private_data;
784 struct pci_front_internal * state = (struct pci_front_internal *)dev->private_data;
785 union pci_addr_reg pci_addr = {state->pci_addr.value};
787 pci_addr.reg = reg_num >> 2;
789 PrintDebug(dev->vm, VCORE_NONE, "pci_front (%s): configuration update: writing 0x%x bytes at offset 0x%x to host device 0x%p, bytes=0x",
790 state->name, length, pci_addr.value, state->host_dev);
792 for (i = 0; i < length; i++) {
793 PrintDebug(dev->vm, VCORE_NONE, "%x", ((uint8_t *)src)[i]);
796 PrintDebug(dev->vm, VCORE_NONE, "\n");
798 /* first, keep our local copy in sync */
799 memcpy((void*)&state->config_space[pci_addr.value], src, length);
801 /* propagate back to the host side */
802 if (v3_host_dev_write_config(state->host_dev,
806 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): configuration update: unable to write all bytes\n",state->name);
814 static int unhook_all_mem(struct pci_front_internal *state)
817 struct vm_device *bus = state->pci_bus;
820 for (bar_num=0;bar_num<6;bar_num++) {
821 struct pt_bar * pbar = &(state->bars[bar_num]);
823 PrintDebug(bus->vm, VCORE_NONE, "pci_front (%s): unhooking for bar %d\n", state->name, bar_num);
825 if (pbar->type == PT_BAR_MEM32) {
826 if (v3_unhook_mem(bus->vm,V3_MEM_CORE_ANY,pbar->addr)<0) {
827 PrintError(bus->vm, VCORE_NONE, "pci_front (%s): unable to unhook 32 bit memory region starting at 0x%p\n",
828 state->name, (void*)(pbar->addr));
831 } else if (pbar->type == PT_BAR_MEM64_HI) {
833 if (v3_unhook_mem(bus->vm,V3_MEM_CORE_ANY,pbar->addr)<0) {
834 PrintError(bus->vm, VCORE_NONE, "pci_front (%s): unable to unhook 64 bit memory region starting at 0x%p\n",
835 state->name, (void*)(pbar->addr));
846 static int setup_virt_pci_dev(struct v3_vm_info * vm_info, struct vm_device * dev)
848 struct pci_front_internal * state = (struct pci_front_internal *)dev->private_data;
849 struct pci_device * pci_dev = NULL;
850 struct v3_pci_bar bars[6];
854 for (i = 0; i < 6; i++) {
855 bars[i].type = PCI_BAR_PASSTHROUGH;
856 bars[i].private_data = dev;
857 bars[i].bar_init = pci_bar_init;
858 bars[i].bar_write = pci_bar_write;
861 pci_dev = v3_pci_register_device(state->pci_bus,
865 pci_front_config_update,
866 pci_front_config_read,
867 pci_front_cmd_update,
868 pci_front_rom_update,
872 state->pci_dev = pci_dev;
875 // COMMANDS CURRENTLY UNSUPPORTED (ignored)
883 // Note: potential bug: not clear what pointer I get here
885 static int pci_front_free(struct pci_front_internal *state)
888 if (unhook_all_mem(state)<0) {
892 // the device manager will unhook the i/o ports for us
894 if (state->host_dev) {
895 v3_host_dev_close(state->host_dev);
899 if (state->rom_hpa) {
900 V3_FreePages(state->rom_hpa,state->rom_size/PAGE_SIZE + 1);
905 PrintDebug(state->pci_bus->vm, VCORE_NONE, "pci_front (%s): freed\n",state->name);
910 #ifdef V3_CONFIG_HOST_DEVICE
911 static void pci_front_intr_update_callback(v3_host_dev_t hdev, v3_guest_dev_t gdev, uint8_t irq, int raise)
915 struct vm_device *dev = (struct vm_device *) gdev;
916 struct pci_front_internal *state = (struct pci_front_internal *) dev->private_data;
918 // We expect the host device will raise and lower irqs as needed, so we
919 // don't need an "acked" irq. Also, we expect the host is using INTX, not
920 // MSI. It's doubtful that MSI will work.
921 // expect: state->pci_dev->irq_type==IRQ_INTX
922 PrintDebug(VM_NONE, VCORE_NONE, "Palacios raising PCI IRQ %x\n", irq);
924 v3_pci_raise_irq(state->pci_bus, state->pci_dev, irq);
926 v3_pci_lower_irq(state->pci_bus, state->pci_dev, irq);
933 static struct v3_device_ops dev_ops = {
935 // Note: potential bug: not clear what pointer I get here
937 .free = (int (*)(void*))pci_front_free,
946 static int pci_front_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg)
948 struct vm_device * dev;
949 struct vm_device * bus;
950 struct pci_front_internal *state;
955 struct v3_cfg_file *rom_file;
962 if (!(dev_id = v3_cfg_val(cfg, "ID"))) {
963 PrintError(vm, VCORE_NONE, "pci_front: no id given!\n");
968 if (!(bus_id = v3_cfg_val(cfg, "bus"))) {
969 PrintError(vm, VCORE_NONE, "pci_front (%s): no bus given!\n",dev_id);
973 if (!(url = v3_cfg_val(cfg, "hostdev"))) {
974 PrintError(vm, VCORE_NONE, "pci_front (%s): no host device url given!\n",dev_id);
978 if (!(rom=v3_cfg_subtree(cfg,"rom"))) {
979 PrintDebug(vm, VCORE_NONE, "pci_front (%s): no expansion block\n",dev_id);
983 rom_id = v3_cfg_val(rom,"file");
985 PrintError(vm, VCORE_NONE, "pci_front (%s): no rom id given\n",dev_id);
988 rom_file = v3_cfg_get_file(vm,rom_id);
990 PrintError(vm,VCORE_NONE, "pci_front (%s): cannot find expansion rom %s\n",dev_id,rom_id);
993 rom_size = rom_file->size;
994 rom_hpa = V3_AllocPages(rom_size/PAGE_SIZE + 1 );
996 PrintError(vm,VCORE_NONE, "pci_front %s) : cannot allocate space for expansion rom %s\n",dev_id,rom_id);
999 memcpy(V3_VAddr(rom_hpa),rom_file->data,rom_size);
1002 PrintDebug(vm,VCORE_NONE,"pci_front (%s): rom %s tag %s size 0x%llx hpa %p\n",
1003 dev_id,rom_id,rom_file->tag,rom_file->size,rom_hpa);
1007 if (!(bus = v3_find_dev(vm,bus_id))) {
1008 PrintError(vm, VCORE_NONE, "pci_front (%s): cannot attach to bus %s\n",dev_id,bus_id);
1012 if (!(state = V3_Malloc(sizeof(struct pci_front_internal)))) {
1013 PrintError(vm, VCORE_NONE, "pci_front (%s): cannot allocate state for device\n",dev_id);
1018 memset(state, 0, sizeof(struct pci_front_internal));
1020 state->pci_bus = bus;
1021 strncpy(state->name, dev_id, 32);
1023 if (!(dev = v3_add_device(vm, dev_id, &dev_ops, state))) {
1024 PrintError(vm, VCORE_NONE, "pci_front (%s): unable to add device\n",state->name);
1028 if (!(state->host_dev=v3_host_dev_open(url,V3_BUS_CLASS_PCI,dev,pci_front_intr_update_callback,vm))) {
1029 PrintError(vm, VCORE_NONE, "pci_front (%s): unable to attach to host device %s\n",state->name, url);
1030 v3_remove_device(dev);
1034 state->rom_gpa = 0 ; // this will be set later by the pull
1035 state->rom_hpa = rom_hpa;
1036 state->rom_size = rom_size;
1038 if (pull_config(state,state->config_space)) {
1039 PrintError(dev->vm, VCORE_NONE, "pci_front (%s): cannot initially configure device\n",state->name);
1040 v3_remove_device(dev);
1044 if (state->rom_hpa) {
1045 // have rom - add it to address space, initially disabled
1046 remap_rom(vm,state,(void*)(uint64_t)*((uint32_t*)&state->config_space[0x30]),0);
1049 // setup virtual device for now
1050 if (setup_virt_pci_dev(vm,dev)<0) {
1051 PrintError(vm, VCORE_NONE, "pci_front (%s): cannot set up virtual pci device\n", state->name);
1052 v3_remove_device(dev);
1056 // We do not need to hook anything here since pci will call
1057 // us back via the bar_init functions
1059 PrintDebug(vm, VCORE_NONE, "pci_front (%s): inited and ready to be Potemkinized\n",state->name);
1066 device_register("PCI_FRONT", pci_front_init)