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> (refactoring and selpriv feature)
11 * Copyright (c) 2012, Jack Lange <jacklange@cs.pitt.edu>
12 * Copyright (c) 2012, The V3VEE Project <http://www.v3vee.org>
13 * All rights reserved.
15 * Authors: Jack Lange <jacklange@cs.pitt.edu>
16 * Peter Dinda <pdinda@northwestern.edu> (selective privilege and refactor)
18 * This is free software. You are permitted to use,
19 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
24 This is the generic passthrough PCI virtual device which allows
25 access to the underlying device to be dynamically provided and
26 revoked. It is a variant of the host_pci.c device, which is
27 "always on" (access permitted).
31 * The basic idea is that we do not change the hardware PCI
32 * configuration Instead we modify the guest environment to map onto
33 * the physical configuration
35 * The pci subsystem handles most of the configuration space, except
36 * for the bar registers. We handle them here, by either letting them
37 * go directly to hardware or remapping through virtual hooks
39 * Memory Bars are remapped via the shadow map on privileged operation
40 * IO Bars are selectively remapped through hooks if the guest changes
43 * By default, this device is fully privileged at all times, meaning
44 * that passthrough to the underlying host side implementation is
45 * always active. In this mode, it should behave the same has the
48 * The device can also be instantiated with the options
49 * selective_privilege=yes and privilege_extension=name In this
50 * "selective privilege" mode of operation, the device starts without
51 * privilege, and the extension can dynamically switch between
52 * privileged and unprivileged operation. In unprivileged operation,
53 * the bars are remapped to stubs, and interrupt delivery is disabled.
57 #include <palacios/vmm.h>
58 #include <palacios/vmm_dev_mgr.h>
59 #include <palacios/vmm_sprintf.h>
60 #include <palacios/vmm_lowlevel.h>
61 #include <palacios/vm_guest.h> // must include this to avoid dependency issue
62 #include <palacios/vmm_symspy.h>
64 #include <devices/pci.h>
65 #include <devices/pci_types.h>
66 #include <interfaces/host_pci.h>
68 #include <palacios/vmm_types.h>
70 #include <gears/privilege.h>
73 #ifndef V3_CONFIG_DEBUG_HOST_PCI_SELPRIV
75 #define PrintDebug(fmt, args...)
79 // set this to one if you want to test the device
80 // in "always on" mode for compatability with host_pci.c
81 #define TEST_NOSELPRIV 0
85 #define PCI_DEV_MAX 32
88 #define PCI_DEVICE 0x0
89 #define PCI_PCI_BRIDGE 0x1
90 #define PCI_CARDBUS_BRIDGE 0x2
92 #define PCI_HDR_SIZE 256
95 typedef enum { UNPRIVILEGED=0, PRIVILEGED} priv_state_t;
96 typedef enum { UNMAPPED=0, STUB, PHYSICAL} bar_state_t;
98 struct host_pci_state {
99 // This holds the description of the host PCI device configuration
100 struct v3_host_pci_dev * host_dev;
103 struct v3_host_pci_bar virt_bars[6];
104 struct v3_host_pci_bar virt_exp_rom;
106 struct vm_device * pci_bus;
107 struct pci_device * pci_dev;
111 // The current privilege level of the device
112 priv_state_t priv_state;
113 // how each bar is currently mapped
114 bar_state_t bar_state[6];
120 static int pci_exp_rom_init(struct vm_device * dev, struct host_pci_state * state) {
121 struct pci_device * pci_dev = state->pci_dev;
122 struct v3_host_pci_bar * hrom = &(state->host_dev->exp_rom);
126 PrintDebug(info->vm_info, info, "Adding 32 bit PCI mem region: start=%p, end=%p\n",
127 (void *)(addr_t)hrom->addr,
128 (void *)(addr_t)(hrom->addr + hrom->size));
130 if (hrom->exp_rom_enabled) {
131 // only map shadow memory if the ROM is enabled
133 v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY,
135 hrom->addr + hrom->size - 1,
138 // Initially the virtual location matches the physical ones
139 memcpy(&(state->virt_exp_rom), hrom, sizeof(struct v3_host_pci_bar));
142 PrintDebug(info->vm_info, info, "phys exp_rom: addr=%p, size=%u\n",
143 (void *)(addr_t)hrom->addr,
147 // Update the pci subsystem versions
148 pci_dev->config_header.expansion_rom_address = PCI_EXP_ROM_VAL(hrom->addr, hrom->exp_rom_enabled);
158 static int pt_io_read(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * priv_data) {
159 struct v3_host_pci_bar * pbar = (struct v3_host_pci_bar *)priv_data;
160 int port_offset = port % pbar->size;
163 *(uint8_t *)dst = v3_inb(pbar->addr + port_offset);
164 } else if (length == 2) {
165 *(uint16_t *)dst = v3_inw(pbar->addr + port_offset);
166 } else if (length == 4) {
167 *(uint32_t *)dst = v3_indw(pbar->addr + port_offset);
169 PrintError(core->vm_info, core, "Invalid PCI passthrough IO Redirection size read\n");
177 static int pt_io_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
178 struct v3_host_pci_bar * pbar = (struct v3_host_pci_bar *)priv_data;
179 int port_offset = port % pbar->size;
182 v3_outb(pbar->addr + port_offset, *(uint8_t *)src);
183 } else if (length == 2) {
184 v3_outw(pbar->addr + port_offset, *(uint16_t *)src);
185 } else if (length == 4) {
186 v3_outdw(pbar->addr + port_offset, *(uint32_t *)src);
188 PrintError(core->vm_info, core, "Invalid PCI passthrough IO Redirection size write\n");
197 static int pt_io_read_stub(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * priv_data) {
198 PrintDebug(core->vm_info, core, "Ignoring read of %u bytes at port %d\n",length,port);
199 memset(dst,0,length);
203 static int pt_io_write_stub(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
204 PrintDebug(core->vm_info, core, "Ignoring read of %u bytes at port %d\n",length,port);
209 static int pt_mem_read_stub(struct guest_info * core,
215 PrintDebug(core->vm_info, core, "Ignoring read of %u bytes at %p\n",length,(void*)guest_addr);
217 memset(dst,0,length);
221 static int pt_mem_write_stub(struct guest_info * core,
227 PrintDebug(core->vm_info, core, "Ignoring write of %u bytes at %p\n",length,(void*)guest_addr);
232 static int pci_unmap_bar(struct vm_device *dev,
233 struct host_pci_state *state,
236 // struct v3_host_pci_bar *hbar = &(state->host_dev->bars[bar_num]);
237 struct v3_host_pci_bar *vbar = &(state->virt_bars[bar_num]);
239 if (state->bar_state[bar_num] == UNMAPPED) {
240 // this is idempotent, so not an error
244 if (state->bar_state[bar_num] != PHYSICAL &&
245 state->bar_state[bar_num] != STUB) {
246 PrintError(dev->vm, VCORE_NONE, "Unknown bar state %d\n", state->bar_state[bar_num]);
250 switch (vbar->type) {
257 // unhook old ports - same for both physical and stub
258 for (i = 0; i < vbar->size; i++) {
259 if (v3_unhook_io_port(dev->vm, vbar->addr + i) == -1) {
260 PrintError(dev->vm, VCORE_NONE, "Could not unhook previously hooked port.... %d (0x%x)\n",
261 (uint32_t)vbar->addr + i, (uint32_t)vbar->addr + i);
270 if (state->bar_state[bar_num] == PHYSICAL ) {
271 // remove old mapping
272 struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vbar->addr);
274 if (old_reg == NULL) {
276 PrintError(dev->vm, VCORE_NONE, "Could not find PCI Passthrough memory redirection region (addr=0x%x)\n", (uint32_t)vbar->addr);
280 v3_delete_mem_region(dev->vm, old_reg); // void return?!
283 if (v3_unhook_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr)==-1) {
284 PrintError(dev->vm, VCORE_NONE, "Failed to unhook memory\n");
292 case PT_BAR_MEM64_LO:
294 // the unmapping happens on the HI BAR
298 case PT_BAR_MEM64_HI: {
299 //struct v3_host_pci_bar * lo_vbar = &(state->virt_bars[bar_num - 1]);
300 struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vbar->addr);
302 if (state->bar_state[bar_num] == PHYSICAL) {
303 if (old_reg == NULL) {
305 PrintError(dev->vm, VCORE_NONE, "Could not find PCI Passthrough memory redirection region (addr=%p)\n",
306 (void *)(addr_t)vbar->addr);
310 // remove old mapping
311 v3_delete_mem_region(dev->vm, old_reg); // void return?!
314 if (v3_unhook_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr)==-1) {
315 PrintError(dev->vm, VCORE_NONE, "Failed to unhook memory\n");
323 PrintError(dev->vm, VCORE_NONE, "Unhandled Pasthrough PCI Bar type %d\n", vbar->type);
328 state->bar_state[bar_num] = UNMAPPED;
336 static int pci_map_bar(struct vm_device *dev,
337 struct host_pci_state *state,
341 struct v3_host_pci_bar *hbar = &(state->host_dev->bars[bar_num]);
342 struct v3_host_pci_bar *vbar = &(state->virt_bars[bar_num]);
344 if (state->bar_state[bar_num] != UNMAPPED) {
345 PrintError(dev->vm, VCORE_NONE, "Attempt to map bar that is already mapped\n");
349 if (target != STUB && target != PHYSICAL) {
350 PrintError(dev->vm, VCORE_NONE, "Attempt to map to unknown target %d\n",target);
355 switch (vbar->type) {
363 if (target == PHYSICAL) {
364 if (vbar->addr == hbar->addr) {
365 // Map the io ports as passthrough
366 PrintDebug(dev->vm,VCORE_NONE,"Adding io port direct mappings\n");
367 for (i = 0; i < hbar->size; i++) {
368 if (v3_hook_io_port(dev->vm, hbar->addr + i, NULL, NULL, NULL) == -1) {
369 PrintError(dev->vm, VCORE_NONE, "Failed to hook ioport %llu direct\n",hbar->addr+i);
374 // We have to manually handle the io redirection
375 PrintDebug(dev->vm,VCORE_NONE,"Adding io port indirect mappings\n");
376 for (i = 0; i < vbar->size; i++) {
377 if (v3_hook_io_port(dev->vm, vbar->addr + i, pt_io_read, pt_io_write, hbar) == -1) {
378 PrintError(dev->vm,VCORE_NONE,"Failed to hook ioport %llu indirect\n", vbar->addr+i);
385 PrintDebug(dev->vm,VCORE_NONE,"Adding io port indirect mappings to stubs\n");
386 for (i = 0; i < vbar->size; i++) {
387 if (v3_hook_io_port(dev->vm, vbar->addr + i, pt_io_read_stub, pt_io_write_stub, dev)==-1) {
388 PrintError(dev->vm, VCORE_NONE, "Failed to hook ioport %llu indirect to stub\n",vbar->addr+i);
398 if (target == PHYSICAL) {
399 PrintDebug(dev->vm, VCORE_NONE,
400 "Adding pci Passthrough mapping: start=0x%x, size=%d, end=0x%x (hpa=%p)\n",
401 (uint32_t)vbar->addr, vbar->size, (uint32_t)vbar->addr + vbar->size, (void *)hbar->addr);
403 if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY,
405 vbar->addr + vbar->size - 1,
406 hbar->addr) == -1 ) {
407 PrintError(dev->vm, VCORE_NONE, "Failed to add region\n");
411 // we will hook the memory instead with the stubs
412 PrintDebug(dev->vm, VCORE_NONE,
413 "Adding pci Passthrough mapping to stubs: start=0x%x, size=%d, end=0x%x (hpa=%p)\n",
414 (uint32_t)vbar->addr, vbar->size, (uint32_t)vbar->addr + vbar->size, (void *)hbar->addr);
416 if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY,
417 vbar->addr, vbar->addr+vbar->size-1,
418 pt_mem_read_stub, pt_mem_write_stub, dev) == -1) {
419 PrintError(dev->vm, VCORE_NONE, "Cannot hook memory for unprivileged access\n");
426 case PT_BAR_MEM64_LO:
428 // the mapping happens on the HI BAR
432 case PT_BAR_MEM64_HI: {
434 if (target == PHYSICAL) {
435 PrintDebug(dev->vm, VCORE_NONE,
436 "Adding pci Passthrough remapping: start=%p, size=%p, end=%p\n",
437 (void *)(addr_t)vbar->addr, (void *)(addr_t)vbar->size,
438 (void *)(addr_t)(vbar->addr + vbar->size));
440 if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr,
441 vbar->addr + vbar->size - 1, hbar->addr) == -1) {
443 PrintDebug(dev->vm, VCORE_NONE, "Fail to insert shadow region (%p, %p) -> %p\n",
444 (void *)(addr_t)vbar->addr,
445 (void *)(addr_t)(vbar->addr + vbar->size - 1),
446 (void *)(addr_t)hbar->addr);
450 // we will hook the memory instead, using the stubs
451 PrintDebug(dev->vm, VCORE_NONE,
452 "Adding pci Passthrough mapping to stubs: start=0x%x, size=%d, end=0x%x (hpa=%p)\n",
453 (uint32_t)vbar->addr, vbar->size, (uint32_t)vbar->addr + vbar->size, (void *)hbar->addr);
455 if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY,
456 vbar->addr, vbar->addr+vbar->size-1,
457 pt_mem_read_stub, pt_mem_write_stub, dev) == -1) {
458 PrintError(dev->vm, VCORE_NONE, "Cannot hook memory for unprivileged access\n");
466 PrintError(dev->vm, VCORE_NONE, "Unhandled Pasthrough PCI Bar type %d\n", vbar->type);
471 state->bar_state[bar_num]=target;
479 // This unmaps the bar no matter what state it's in
481 static int deactivate_bar(struct vm_device *dev,
484 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
486 if (pci_unmap_bar(dev, state, bar_num)) {
487 PrintError(dev->vm, VCORE_NONE, "Cannot unmap bar %d\n", bar_num);
494 // This does nothing if the bar is already physical
495 // if it's not, it unmaps it and then maps it physical
497 static int activate_bar_physical(struct vm_device *dev,
500 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
502 if (state->bar_state[bar_num] != PHYSICAL) {
503 if (pci_unmap_bar(dev, state, bar_num)) {
504 PrintError(dev->vm, VCORE_NONE, "Cannot unmap bar %d\n", bar_num);
507 if (pci_map_bar(dev,state,bar_num,PHYSICAL)) {
508 PrintError(dev->vm, VCORE_NONE, "Cannot map bar %d\n", bar_num);
516 // This does nothing if the bar is already stub
517 // if it's not, it unmaps it and then maps it stub
519 static int activate_bar_stub(struct vm_device *dev,
522 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
524 if (state->bar_state[bar_num] != STUB) {
525 if (pci_unmap_bar(dev, state, bar_num)) {
526 PrintError(dev->vm, VCORE_NONE, "Cannot unmap bar %d\n", bar_num);
529 if (pci_map_bar(dev,state,bar_num,STUB)) {
530 PrintError(dev->vm, VCORE_NONE, "Cannot map bar %d\n", bar_num);
537 static int activate_bar(struct vm_device *dev,
540 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
542 if (state->priv_state == PRIVILEGED) {
543 return activate_bar_physical(dev,bar_num);
545 return activate_bar_stub(dev,bar_num);
551 static int reactivate_device_privileged(struct vm_device *dev)
554 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
556 PrintDebug(dev->vm, VCORE_NONE, "Switch device to privileged operation\n");
559 if (activate_bar_physical(dev,i)) {
560 PrintError(dev->vm, VCORE_NONE, "Cannot activate bar %d as physical\n",i);
564 state->priv_state=PRIVILEGED;
568 static int reactivate_device_unprivileged(struct vm_device *dev)
571 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
573 PrintDebug(dev->vm, VCORE_NONE, "Switch device to unprivileged operation\n");
576 if (activate_bar_stub(dev,i)) {
577 PrintError(dev->vm, VCORE_NONE, "Cannot activate bar %d as stub\n",i);
581 state->priv_state=UNPRIVILEGED;
586 static int pci_bar_init(int bar_num, uint32_t * dst, void * private_data) {
587 struct vm_device * dev = (struct vm_device *)private_data;
588 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
589 struct v3_host_pci_bar * hbar = &(state->host_dev->bars[bar_num]);
590 uint32_t bar_val = 0;
592 if (hbar->type == PT_BAR_IO) {
593 bar_val = PCI_IO_BAR_VAL(hbar->addr);
594 } else if (hbar->type == PT_BAR_MEM32) {
595 bar_val = PCI_MEM32_BAR_VAL(hbar->addr, hbar->prefetchable);
596 } else if (hbar->type == PT_BAR_MEM24) {
597 bar_val = PCI_MEM24_BAR_VAL(hbar->addr, hbar->prefetchable);
598 } else if (hbar->type == PT_BAR_MEM64_LO) {
599 struct v3_host_pci_bar * hi_hbar = &(state->host_dev->bars[bar_num + 1]);
600 bar_val = PCI_MEM64_LO_BAR_VAL(hi_hbar->addr, hbar->prefetchable);
601 } else if (hbar->type == PT_BAR_MEM64_HI) {
602 bar_val = PCI_MEM64_HI_BAR_VAL(hbar->addr, hbar->prefetchable);
604 PrintDebug(dev->vm, VCORE_NONE, "Unknown bar type %d\n",hbar->type);
607 memcpy(&(state->virt_bars[bar_num]), hbar, sizeof(struct v3_host_pci_bar));
611 if (activate_bar(dev,bar_num)) {
612 PrintError(dev->vm, VCORE_NONE,"Cannot activate bar %d\n",bar_num);
622 static int pci_bar_write(int bar_num, uint32_t * src, void * private_data) {
623 struct vm_device * dev = (struct vm_device *)private_data;
624 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
626 struct v3_host_pci_bar * hbar = &(state->host_dev->bars[bar_num]);
627 struct v3_host_pci_bar * vbar = &(state->virt_bars[bar_num]);
630 if (deactivate_bar(dev,bar_num)) {
631 PrintError(dev->vm, VCORE_NONE, "Cannot deactivate bar %d\n", bar_num);
635 switch (vbar->type) {
642 // clear the low bits to match the size
643 vbar->addr = *src & ~(hbar->size - 1);
645 // udpate source version
646 *src = PCI_IO_BAR_VAL(vbar->addr);
652 // clear the low bits to match the size
653 vbar->addr = *src & ~(hbar->size - 1);
656 *src = PCI_MEM32_BAR_VAL(vbar->addr, hbar->prefetchable);
660 case PT_BAR_MEM64_LO:
662 // We only store the written values here, the actual reconfig comes when the high BAR is updated
664 vbar->addr = *src & ~(hbar->size - 1);
666 *src = PCI_MEM64_LO_BAR_VAL(vbar->addr, hbar->prefetchable);
670 case PT_BAR_MEM64_HI: {
672 struct v3_host_pci_bar * lo_vbar = &(state->virt_bars[bar_num - 1]);
674 vbar->addr = (((uint64_t)*src) << 32) + lo_vbar->addr;
676 // We don't set size, because we assume region is less than 4GB
677 // src does not change, because there are no reserved bits
683 PrintError(dev->vm, VCORE_NONE, "Unhandled Pasthrough PCI Bar type %d\n", vbar->type);
689 if (activate_bar(dev,bar_num)) {
690 PrintError(dev->vm, VCORE_NONE, "Cannot activate bar %d\n", bar_num);
699 static int pt_config_write(struct pci_device * pci_dev, uint32_t reg_num, void * src, uint_t length, void * private_data) {
700 struct vm_device * dev = (struct vm_device *)private_data;
701 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
703 // V3_PrintStubStub(dev->vm, VCORE_NONE, "Writing host PCI config space update\n");
705 // We will mask all operations to the config header itself,
706 // and only allow direct access to the device specific config space
711 if (TEST_NOSELPRIV || state->priv_state==PRIVILEGED) {
712 return v3_host_pci_config_write(state->host_dev, reg_num, src, length);
714 PrintError(dev->vm, VCORE_NONE, "configuration write while unprivileged - CURRENTLY IGNORED\n");
721 static int pt_config_read(struct pci_device * pci_dev, uint32_t reg_num, void * dst, uint_t length, void * private_data) {
722 struct vm_device * dev = (struct vm_device *)private_data;
723 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
725 // V3_PrintStubStub(dev->vm, VCORE_NONE, "Reading host PCI config space update\n");
727 if (TEST_NOSELPRIV || state->priv_state==PRIVILEGED) {
728 return v3_host_pci_config_read(state->host_dev, reg_num, dst, length);
730 PrintError(dev->vm, VCORE_NONE, "configuration read while unprivileged - CURRENTLY IGNORED\n");
731 memset(dst,0,length);
738 /* This is really iffy....
739 * It was totally broken before, but it's _not_ totally fixed now
740 * The Expansion rom can be enabled/disabled via software using the low order bit
741 * We should probably handle that somehow here...
743 static int pt_exp_rom_write(struct pci_device * pci_dev, uint32_t * src, void * priv_data) {
744 struct vm_device * dev = (struct vm_device *)(priv_data);
745 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
747 struct v3_host_pci_bar * hrom = &(state->host_dev->exp_rom);
748 struct v3_host_pci_bar * vrom = &(state->virt_exp_rom);
750 PrintDebug(dev->vm, VCORE_NONE, "exp_rom update: src=0x%x\n", *src);
751 PrintDebug(dev->vm, VCORE_NONE, "vrom is size=%u, addr=0x%x\n", vrom->size, (uint32_t)vrom->addr);
752 PrintDebug(dev->vm, VCORE_NONE, "hrom is size=%u, addr=0x%x\n", hrom->size, (uint32_t)hrom->addr);
755 if (!TEST_NOSELPRIV && state->priv_state != PRIVILEGED) {
756 PrintError(dev->vm, VCORE_NONE, "Attempt to do expansion rom write when not privileged is IGNORED\n");
757 //PrintError(dev->vm, VCORE_NONE,"\tKCH: letting it through though\n");
758 /* KCH: let it through for now, commenting below */
762 if (hrom->exp_rom_enabled) {
763 // only remove old mapping if present, I.E. if the rom was enabled previously
764 if (vrom->exp_rom_enabled) {
765 struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vrom->addr);
767 if (old_reg == NULL) {
769 PrintError(dev->vm, VCORE_NONE, "Could not find PCI Passthrough exp_rom_base redirection region (addr=0x%x)\n", (uint32_t)vrom->addr);
773 v3_delete_mem_region(dev->vm, old_reg);
777 vrom->addr = *src & ~(hrom->size - 1);
779 // Set flags in actual register value
780 *src = PCI_EXP_ROM_VAL(vrom->addr, (*src & 0x00000001));
782 PrintDebug(dev->vm, VCORE_NONE, "Cooked src=0x%x\n", *src);
785 PrintDebug(dev->vm, VCORE_NONE, "Adding pci Passthrough exp_rom_base remapping: start=0x%x, size=%u, end=0x%x\n",
786 (uint32_t)vrom->addr, vrom->size, (uint32_t)vrom->addr + vrom->size);
788 if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vrom->addr,
789 vrom->addr + vrom->size - 1, hrom->addr) == -1) {
790 PrintError(dev->vm, VCORE_NONE, "Failed to remap pci exp_rom: start=0x%x, size=%u, end=0x%x\n",
791 (uint32_t)vrom->addr, vrom->size, (uint32_t)vrom->addr + vrom->size);
800 static int pt_cmd_update(struct pci_device * pci, pci_cmd_t cmd, uint64_t arg, void * priv_data) {
801 struct vm_device * dev = (struct vm_device *)(priv_data);
802 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
804 V3_Print(dev->vm, VCORE_NONE, "Host PCI Device: CMD update (%d)(arg=%llu)\n", cmd, arg);
807 if (TEST_NOSELPRIV || state->priv_state == PRIVILEGED) {
808 v3_host_pci_cmd_update(state->host_dev, cmd, arg);
810 PrintError(dev->vm, VCORE_NONE, "Attempt to do cmd update when not in privileged mode is IGNORED\n");
818 static int setup_virt_pci_dev(struct v3_vm_info * vm_info, struct vm_device * dev) {
819 struct host_pci_state * state = (struct host_pci_state *)dev->private_data;
820 struct pci_device * pci_dev = NULL;
821 struct v3_pci_bar bars[6];
825 for (i = 0; i < 6; i++) {
826 bars[i].type = PCI_BAR_PASSTHROUGH;
827 bars[i].private_data = dev;
828 bars[i].bar_init = pci_bar_init;
829 bars[i].bar_write = pci_bar_write;
832 pci_dev = v3_pci_register_device(state->pci_bus,
843 state->pci_dev = pci_dev;
845 // pci_exp_rom_init(dev, state);
846 pci_dev->config_header.expansion_rom_address = 0;
848 v3_pci_enable_capability(pci_dev, PCI_CAP_MSI);
849 // v3_pci_enable_capability(pci_dev, PCI_CAP_MSIX);
850 v3_pci_enable_capability(pci_dev, PCI_CAP_PCIE);
851 v3_pci_enable_capability(pci_dev, PCI_CAP_PM);
855 if (state->host_dev->iface == SYMBIOTIC) {
856 #ifdef V3_CONFIG_SYMBIOTIC
857 v3_sym_map_pci_passthrough(vm_info, pci_dev->bus_num, pci_dev->dev_num, pci_dev->fn_num);
859 PrintError(vm_info, VCORE_NONE, "ERROR Symbiotic Passthrough is not enabled\n");
869 static int init_priv(struct guest_info * core, void ** private_data)
871 PrintDebug(core->vm_info, core, "Initializing host_pci device selective privilege operation\n");
875 static int lower_priv(struct guest_info * core, void * private_data)
877 struct vm_device * dev = (struct vm_device *)private_data;
879 PrintDebug(core->vm_info, core, "Lowering privilege for device %s\n", dev->name);
881 return reactivate_device_unprivileged(dev);
884 static int raise_priv(struct guest_info * core, void * private_data)
886 struct vm_device * dev = (struct vm_device *)private_data;
888 PrintDebug(core->vm_info, core, "Raising privilege for device %s\n", dev->name);
890 return reactivate_device_privileged(dev);
893 static int deinit_priv(struct guest_info * core, void * private_data)
895 PrintDebug(core->vm_info, core, "Deinitializing host_pci device selective privilege operation\n");
900 static struct v3_device_ops dev_ops = {
905 static int irq_ack(struct guest_info * core, uint32_t irq, void * private_data) {
906 struct host_pci_state * state = (struct host_pci_state *)private_data;
909 PrintDebug(core->vm_info,core,"GM HPCI: IRQ ACK of irq %d\n", irq);
910 // V3_PrintStubStub(core->vm_info, core, "Acking IRQ %d\n", irq);
911 v3_host_pci_ack_irq(state->host_dev, irq);
917 static int irq_handler(void * private_data, uint32_t vec_index) {
918 struct host_pci_state * state = (struct host_pci_state *)private_data;
923 vec.private_data = state;
925 // For selective privilege, the interrupt is always delivered
926 // identically to the regular case, regardless of the current
927 // privilege level. The idea here is that delivery of the
928 // interrupt will result in a dispatch to the privileged guest code
929 // and the entry to that code will enable privilege before any
930 // mapped bar is read or written.
932 PrintDebug(0, 0,"Host PCI / Selective Privilege: Delivering vec irq %d, state %d\n", vec.irq, (int)(state->pci_dev->irq_type));
933 if (state->pci_dev->irq_type == IRQ_NONE) {
935 } else if (state->pci_dev->irq_type == IRQ_INTX) {
936 v3_pci_raise_acked_irq(state->pci_bus, state->pci_dev, vec);
938 v3_pci_raise_irq(state->pci_bus, state->pci_dev, vec_index);
945 static int host_pci_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
946 struct host_pci_state * state = V3_Malloc(sizeof(struct host_pci_state));
947 struct vm_device * dev = NULL;
948 struct vm_device * pci = v3_find_dev(vm, v3_cfg_val(cfg, "bus"));
949 char * dev_id = v3_cfg_val(cfg, "ID");
950 char * url = v3_cfg_val(cfg, "url");
952 memset(state, 0, sizeof(struct host_pci_state));
955 PrintError(vm, VCORE_NONE, "PCI bus not specified in config file\n");
959 state->pci_bus = pci;
960 strncpy(state->name, dev_id, 32);
963 dev = v3_add_device(vm, dev_id, &dev_ops, state);
966 PrintError(vm, VCORE_NONE, "Could not attach device %s\n", dev_id);
971 state->host_dev = v3_host_pci_get_dev(vm, url, state);
973 if (state->host_dev == NULL) {
974 PrintError(vm, VCORE_NONE, "Could not connect to host pci device (%s)\n", url);
979 state->host_dev->irq_handler = irq_handler;
981 char * sel_priv = v3_cfg_val(cfg, "selective_privilege");
983 if (!sel_priv || !strncasecmp(sel_priv,"off",3) || !strncasecmp(sel_priv,"0",1)) {
985 state->priv_state=PRIVILEGED;
987 state->priv_state=UNPRIVILEGED;
989 PrintDebug(vm, VCORE_NONE, "Selective privilege functionality disabled\n");
992 char * ext_priv = v3_cfg_val(cfg, "privilege_extension");
995 PrintError(vm, VCORE_NONE, "Selective privilege functionality requested, but not privilege extension given\n");
998 if (v3_bind_privilege(&(vm->cores[0]),
1005 PrintError(vm, VCORE_NONE, "Cannot bind to privilege %s\n",ext_priv);
1009 state->priv_state=UNPRIVILEGED;
1011 PrintDebug(vm, VCORE_NONE, "Selective privilege functionality enabled (initially unprivileged)\n");
1015 if (setup_virt_pci_dev(vm, dev) == -1) {
1016 PrintError(vm, VCORE_NONE, "Could not setup virtual host PCI device\n");
1026 device_register("HOST_PCI_SELPRIV", host_pci_init)