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) 2012, Jack Lange <jacklange@cs.pitt.edu>
11 * Copyright (c) 2012, The V3VEE Project <http://www.v3vee.org>
12 * All rights reserved.
14 * Author: Jack Lange <jacklange@cs.pitt.edu>
16 * This is free software. You are permitted to use,
17 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
21 /* This is the generic passthrough PCI virtual device */
24 * The basic idea is that we do not change the hardware PCI configuration
25 * Instead we modify the guest environment to map onto the physical configuration
27 * The pci subsystem handles most of the configuration space, except for the bar registers.
28 * We handle them here, by either letting them go directly to hardware or remapping through virtual hooks
30 * Memory Bars are always remapped via the shadow map,
31 * IO Bars are selectively remapped through hooks if the guest changes them
34 #include <palacios/vmm.h>
35 #include <palacios/vmm_dev_mgr.h>
36 #include <palacios/vmm_sprintf.h>
37 #include <palacios/vmm_lowlevel.h>
38 #include <palacios/vm_guest.h> // must include this to avoid dependency issue
39 #include <palacios/vmm_symspy.h>
41 #include <devices/pci.h>
42 #include <devices/pci_types.h>
43 #include <interfaces/host_pci.h>
46 #define PCI_DEV_MAX 32
49 #define PCI_DEVICE 0x0
50 #define PCI_PCI_BRIDGE 0x1
51 #define PCI_CARDBUS_BRIDGE 0x2
53 #define PCI_HDR_SIZE 256
58 struct host_pci_state {
59 // This holds the description of the host PCI device configuration
60 struct v3_host_pci_dev * host_dev;
63 struct v3_host_pci_bar virt_bars[6];
64 struct v3_host_pci_bar virt_exp_rom;
66 struct vm_device * pci_bus;
67 struct pci_device * pci_dev;
75 static int pci_exp_rom_init(struct vm_device * dev, struct host_pci_state * state) {
76 struct pci_device * pci_dev = state->pci_dev;
77 struct v3_host_pci_bar * hrom = &(state->host_dev->exp_rom);
81 PrintDebug(info->vm_info, info, "Adding 32 bit PCI mem region: start=%p, end=%p\n",
82 (void *)(addr_t)hrom->addr,
83 (void *)(addr_t)(hrom->addr + hrom->size));
85 if (hrom->exp_rom_enabled) {
86 // only map shadow memory if the ROM is enabled
88 v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY,
90 hrom->addr + hrom->size - 1,
93 // Initially the virtual location matches the physical ones
94 memcpy(&(state->virt_exp_rom), hrom, sizeof(struct v3_host_pci_bar));
97 PrintDebug(info->vm_info, info, "phys exp_rom: addr=%p, size=%u\n",
98 (void *)(addr_t)hrom->addr,
102 // Update the pci subsystem versions
103 pci_dev->config_header.expansion_rom_address = PCI_EXP_ROM_VAL(hrom->addr, hrom->exp_rom_enabled);
113 static int pt_io_read(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * priv_data) {
114 struct v3_host_pci_bar * pbar = (struct v3_host_pci_bar *)priv_data;
115 int port_offset = port % pbar->size;
118 *(uint8_t *)dst = v3_inb(pbar->addr + port_offset);
119 } else if (length == 2) {
120 *(uint16_t *)dst = v3_inw(pbar->addr + port_offset);
121 } else if (length == 4) {
122 *(uint32_t *)dst = v3_indw(pbar->addr + port_offset);
124 PrintError(core->vm_info, core, "Invalid PCI passthrough IO Redirection size read\n");
132 static int pt_io_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
133 struct v3_host_pci_bar * pbar = (struct v3_host_pci_bar *)priv_data;
134 int port_offset = port % pbar->size;
137 v3_outb(pbar->addr + port_offset, *(uint8_t *)src);
138 } else if (length == 2) {
139 v3_outw(pbar->addr + port_offset, *(uint16_t *)src);
140 } else if (length == 4) {
141 v3_outdw(pbar->addr + port_offset, *(uint32_t *)src);
143 PrintError(core->vm_info, core, "Invalid PCI passthrough IO Redirection size write\n");
153 static int pci_bar_init(int bar_num, uint32_t * dst, void * private_data) {
154 struct vm_device * dev = (struct vm_device *)private_data;
155 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
156 struct v3_host_pci_bar * hbar = &(state->host_dev->bars[bar_num]);
157 uint32_t bar_val = 0;
159 if (hbar->type == PT_BAR_IO) {
162 bar_val = PCI_IO_BAR_VAL(hbar->addr);
164 for (i = 0; i < hbar->size; i++) {
165 v3_hook_io_port(dev->vm, hbar->addr + i, NULL, NULL, NULL);
167 } else if (hbar->type == PT_BAR_MEM32) {
168 bar_val = PCI_MEM32_BAR_VAL(hbar->addr, hbar->prefetchable);
170 v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY,
171 hbar->addr, hbar->addr + hbar->size - 1,
174 } else if (hbar->type == PT_BAR_MEM24) {
175 bar_val = PCI_MEM24_BAR_VAL(hbar->addr, hbar->prefetchable);
177 v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY,
178 hbar->addr, hbar->addr + hbar->size - 1,
180 } else if (hbar->type == PT_BAR_MEM64_LO) {
181 struct v3_host_pci_bar * hi_hbar = &(state->host_dev->bars[bar_num + 1]);
182 bar_val = PCI_MEM64_LO_BAR_VAL(hi_hbar->addr, hbar->prefetchable);
183 } else if (hbar->type == PT_BAR_MEM64_HI) {
184 bar_val = PCI_MEM64_HI_BAR_VAL(hbar->addr, hbar->prefetchable);
186 v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY,
187 hbar->addr, hbar->addr + hbar->size - 1,
192 memcpy(&(state->virt_bars[bar_num]), hbar, sizeof(struct v3_host_pci_bar));
201 static int pci_bar_write(int bar_num, uint32_t * src, void * private_data) {
202 struct vm_device * dev = (struct vm_device *)private_data;
203 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
205 struct v3_host_pci_bar * hbar = &(state->host_dev->bars[bar_num]);
206 struct v3_host_pci_bar * vbar = &(state->virt_bars[bar_num]);
210 if (vbar->type == PT_BAR_NONE) {
212 } else if (vbar->type == PT_BAR_IO) {
216 for (i = 0; i < vbar->size; i++) {
217 if (v3_unhook_io_port(dev->vm, vbar->addr + i) == -1) {
218 PrintError(VM_NONE, VCORE_NONE, "Could not unhook previously hooked port.... %d (0x%x)\n",
219 (uint32_t)vbar->addr + i, (uint32_t)vbar->addr + i);
224 // clear the low bits to match the size
225 vbar->addr = *src & ~(hbar->size - 1);
227 // udpate source version
228 *src = PCI_IO_BAR_VAL(vbar->addr);
230 PrintDebug(VM_NONE, VCORE_NONE, "Rehooking passthrough IO ports starting at %d (0x%x)\n",
231 (uint32_t)vbar->addr, (uint32_t)vbar->addr);
233 if (vbar->addr == hbar->addr) {
234 // Map the io ports as passthrough
235 for (i = 0; i < hbar->size; i++) {
236 v3_hook_io_port(dev->vm, hbar->addr + i, NULL, NULL, NULL);
239 // We have to manually handle the io redirection
240 for (i = 0; i < vbar->size; i++) {
241 v3_hook_io_port(dev->vm, vbar->addr + i, pt_io_read, pt_io_write, hbar);
244 } else if (vbar->type == PT_BAR_MEM32) {
245 // remove old mapping
246 struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vbar->addr);
248 if (old_reg == NULL) {
250 PrintError(VM_NONE, VCORE_NONE, "Could not find PCI Passthrough memory redirection region (addr=0x%x)\n", (uint32_t)vbar->addr);
254 v3_delete_mem_region(dev->vm, old_reg);
256 // clear the low bits to match the size
257 vbar->addr = *src & ~(hbar->size - 1);
260 *src = PCI_MEM32_BAR_VAL(vbar->addr, hbar->prefetchable);
262 PrintDebug(VM_NONE, VCORE_NONE, "Adding pci Passthrough remapping: start=0x%x, size=%d, end=0x%x (hpa=%p)\n",
263 (uint32_t)vbar->addr, vbar->size, (uint32_t)vbar->addr + vbar->size, (void *)hbar->addr);
265 v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY,
267 vbar->addr + vbar->size - 1,
270 } else if (vbar->type == PT_BAR_MEM64_LO) {
271 // We only store the written values here, the actual reconfig comes when the high BAR is updated
273 vbar->addr = *src & ~(hbar->size - 1);
275 *src = PCI_MEM64_LO_BAR_VAL(vbar->addr, hbar->prefetchable);
278 } else if (vbar->type == PT_BAR_MEM64_HI) {
279 struct v3_host_pci_bar * lo_vbar = &(state->virt_bars[bar_num - 1]);
280 struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vbar->addr);
282 if (old_reg == NULL) {
284 PrintError(VM_NONE, VCORE_NONE, "Could not find PCI Passthrough memory redirection region (addr=%p)\n",
285 (void *)(addr_t)vbar->addr);
289 // remove old mapping
290 v3_delete_mem_region(dev->vm, old_reg);
292 vbar->addr = (((uint64_t)*src) << 32) + lo_vbar->addr;
294 // We don't set size, because we assume region is less than 4GB
295 // src does not change, because there are no reserved bits
298 PrintDebug(VM_NONE, VCORE_NONE, "Adding pci Passthrough remapping: start=%p, size=%p, end=%p\n",
299 (void *)(addr_t)vbar->addr, (void *)(addr_t)vbar->size,
300 (void *)(addr_t)(vbar->addr + vbar->size));
302 if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr,
303 vbar->addr + vbar->size - 1, hbar->addr) == -1) {
305 PrintDebug(VM_NONE, VCORE_NONE, "Fail to insert shadow region (%p, %p) -> %p\n",
306 (void *)(addr_t)vbar->addr,
307 (void *)(addr_t)(vbar->addr + vbar->size - 1),
308 (void *)(addr_t)hbar->addr);
313 PrintError(VM_NONE, VCORE_NONE, "Unhandled Pasthrough PCI Bar type %d\n", vbar->type);
322 static int pt_config_write(struct pci_device * pci_dev, uint32_t reg_num, void * src, uint_t length, void * private_data) {
323 struct vm_device * dev = (struct vm_device *)private_data;
324 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
326 // V3_Print(VM_NONE, VCORE_NONE, "Writing host PCI config space update\n");
328 // We will mask all operations to the config header itself,
329 // and only allow direct access to the device specific config space
334 return v3_host_pci_config_write(state->host_dev, reg_num, src, length);
339 static int pt_config_read(struct pci_device * pci_dev, uint32_t reg_num, void * dst, uint_t length, void * private_data) {
340 struct vm_device * dev = (struct vm_device *)private_data;
341 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
343 // V3_Print(VM_NONE, VCORE_NONE, "Reading host PCI config space update\n");
345 return v3_host_pci_config_read(state->host_dev, reg_num, dst, length);
351 /* This is really iffy....
352 * It was totally broken before, but it's _not_ totally fixed now
353 * The Expansion rom can be enabled/disabled via software using the low order bit
354 * We should probably handle that somehow here...
356 static int pt_exp_rom_write(struct pci_device * pci_dev, uint32_t * src, void * priv_data) {
357 struct vm_device * dev = (struct vm_device *)(priv_data);
358 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
360 struct v3_host_pci_bar * hrom = &(state->host_dev->exp_rom);
361 struct v3_host_pci_bar * vrom = &(state->virt_exp_rom);
363 PrintDebug(VM_NONE, VCORE_NONE, "exp_rom update: src=0x%x\n", *src);
364 PrintDebug(VM_NONE, VCORE_NONE, "vrom is size=%u, addr=0x%x\n", vrom->size, (uint32_t)vrom->addr);
365 PrintDebug(VM_NONE, VCORE_NONE, "hrom is size=%u, addr=0x%x\n", hrom->size, (uint32_t)hrom->addr);
367 if (hrom->exp_rom_enabled) {
368 // only remove old mapping if present, I.E. if the rom was enabled previously
369 if (vrom->exp_rom_enabled) {
370 struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vrom->addr);
372 if (old_reg == NULL) {
374 PrintError(VM_NONE, VCORE_NONE, "Could not find PCI Passthrough exp_rom_base redirection region (addr=0x%x)\n", (uint32_t)vrom->addr);
378 v3_delete_mem_region(dev->vm, old_reg);
382 vrom->addr = *src & ~(hrom->size - 1);
384 // Set flags in actual register value
385 *src = PCI_EXP_ROM_VAL(vrom->addr, (*src & 0x00000001));
387 PrintDebug(VM_NONE, VCORE_NONE, "Cooked src=0x%x\n", *src);
390 PrintDebug(VM_NONE, VCORE_NONE, "Adding pci Passthrough exp_rom_base remapping: start=0x%x, size=%u, end=0x%x\n",
391 (uint32_t)vrom->addr, vrom->size, (uint32_t)vrom->addr + vrom->size);
393 if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vrom->addr,
394 vrom->addr + vrom->size - 1, hrom->addr) == -1) {
395 PrintError(VM_NONE, VCORE_NONE, "Failed to remap pci exp_rom: start=0x%x, size=%u, end=0x%x\n",
396 (uint32_t)vrom->addr, vrom->size, (uint32_t)vrom->addr + vrom->size);
405 static int pt_cmd_update(struct pci_device * pci, pci_cmd_t cmd, uint64_t arg, void * priv_data) {
406 struct vm_device * dev = (struct vm_device *)(priv_data);
407 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
409 V3_Print(VM_NONE, VCORE_NONE, "Host PCI Device: CMD update (%d)(arg=%llu)\n", cmd, arg);
411 v3_host_pci_cmd_update(state->host_dev, cmd, arg);
417 static int setup_virt_pci_dev(struct v3_vm_info * vm_info, struct vm_device * dev) {
418 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
419 struct pci_device * pci_dev = NULL;
420 struct v3_pci_bar bars[6];
424 for (i = 0; i < 6; i++) {
425 bars[i].type = PCI_BAR_PASSTHROUGH;
426 bars[i].private_data = dev;
427 bars[i].bar_init = pci_bar_init;
428 bars[i].bar_write = pci_bar_write;
431 pci_dev = v3_pci_register_device(state->pci_bus,
442 state->pci_dev = pci_dev;
444 // pci_exp_rom_init(dev, state);
445 pci_dev->config_header.expansion_rom_address = 0;
447 v3_pci_enable_capability(pci_dev, PCI_CAP_MSI);
448 // v3_pci_enable_capability(pci_dev, PCI_CAP_MSIX);
449 v3_pci_enable_capability(pci_dev, PCI_CAP_PCIE);
450 v3_pci_enable_capability(pci_dev, PCI_CAP_PM);
454 if (state->host_dev->iface == SYMBIOTIC) {
455 #ifdef V3_CONFIG_SYMBIOTIC
456 v3_sym_map_pci_passthrough(vm_info, pci_dev->bus_num, pci_dev->dev_num, pci_dev->fn_num);
458 PrintError(VM_NONE, VCORE_NONE, "ERROR Symbiotic Passthrough is not enabled\n");
467 static struct v3_device_ops dev_ops = {
472 static int irq_ack(struct guest_info * core, uint32_t irq, void * private_data) {
473 struct host_pci_state * state = (struct host_pci_state *)private_data;
476 // V3_Print(core->vm_info, core, "Acking IRQ %d\n", irq);
477 v3_host_pci_ack_irq(state->host_dev, irq);
483 static int irq_handler(void * private_data, uint32_t vec_index) {
484 struct host_pci_state * state = (struct host_pci_state *)private_data;
489 vec.private_data = state;
492 // V3_Print(VM_NONE, VCORE_NONE, "Raising host PCI IRQ %d\n", vec_index);
494 if (state->pci_dev->irq_type == IRQ_NONE) {
496 } else if (state->pci_dev->irq_type == IRQ_INTX) {
497 v3_pci_raise_acked_irq(state->pci_bus, state->pci_dev, vec);
499 v3_pci_raise_irq(state->pci_bus, state->pci_dev, vec_index);
506 static int host_pci_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
507 struct host_pci_state * state = V3_Malloc(sizeof(struct host_pci_state));
508 struct vm_device * dev = NULL;
509 struct vm_device * pci = v3_find_dev(vm, v3_cfg_val(cfg, "bus"));
510 char * dev_id = v3_cfg_val(cfg, "ID");
511 char * url = v3_cfg_val(cfg, "url");
513 memset(state, 0, sizeof(struct host_pci_state));
516 PrintError(vm, VCORE_NONE, "PCI bus not specified in config file\n");
520 state->pci_bus = pci;
521 strncpy(state->name, dev_id, 32);
524 dev = v3_add_device(vm, dev_id, &dev_ops, state);
527 PrintError(vm, VCORE_NONE, "Could not attach device %s\n", dev_id);
532 state->host_dev = v3_host_pci_get_dev(vm, url, state);
534 if (state->host_dev == NULL) {
535 PrintError(vm, VCORE_NONE, "Could not connect to host pci device (%s)\n", url);
540 state->host_dev->irq_handler = irq_handler;
542 if (setup_virt_pci_dev(vm, dev) == -1) {
543 PrintError(vm, VCORE_NONE, "Could not setup virtual host PCI device\n");
553 device_register("HOST_PCI", host_pci_init)