/* * This file is part of the Palacios Virtual Machine Monitor developed * by the V3VEE Project with funding from the United States National * Science Foundation and the Department of Energy. * * The V3VEE Project is a joint project between Northwestern University * and the University of New Mexico. You can find out more at * http://www.v3vee.org * * Copyright (c) 2013, Peter Dinda (refactoring and selpriv feature) * Copyright (c) 2012, Jack Lange * Copyright (c) 2012, The V3VEE Project * All rights reserved. * * Authors: Jack Lange * Peter Dinda (selective privilege and refactor) * * This is free software. You are permitted to use, * redistribute, and modify it as specified in the file "V3VEE_LICENSE". */ /* This is the generic passthrough PCI virtual device which allows access to the underlying device to be dynamically provided and revoked. It is a variant of the host_pci.c device, which is "always on" (access permitted). */ /* * The basic idea is that we do not change the hardware PCI * configuration Instead we modify the guest environment to map onto * the physical configuration * * The pci subsystem handles most of the configuration space, except * for the bar registers. We handle them here, by either letting them * go directly to hardware or remapping through virtual hooks * * Memory Bars are remapped via the shadow map on privileged operation * IO Bars are selectively remapped through hooks if the guest changes * them * * By default, this device is fully privileged at all times, meaning * that passthrough to the underlying host side implementation is * always active. In this mode, it should behave the same has the * host_pci.c device. * * The device can also be instantiated with the options * selective_privilege=yes and privilege_extension=name In this * "selective privilege" mode of operation, the device starts without * privilege, and the extension can dynamically switch between * privileged and unprivileged operation. In unprivileged operation, * the bars are remapped to stubs, and interrupt delivery is disabled. */ #include #include #include #include #include // must include this to avoid dependency issue #include #include #include #include #include #include #ifndef V3_CONFIG_DEBUG_HOST_PCI_SELPRIV #undef PrintDebug #define PrintDebug(fmt, args...) #endif // set this to one if you want to test the device // in "always on" mode for compatability with host_pci.c #define TEST_NOSELPRIV 0 #define PCI_BUS_MAX 7 #define PCI_DEV_MAX 32 #define PCI_FN_MAX 7 #define PCI_DEVICE 0x0 #define PCI_PCI_BRIDGE 0x1 #define PCI_CARDBUS_BRIDGE 0x2 #define PCI_HDR_SIZE 256 typedef enum { UNPRIVILEGED=0, PRIVILEGED} priv_state_t; typedef enum { UNMAPPED=0, STUB, PHYSICAL} bar_state_t; struct host_pci_state { // This holds the description of the host PCI device configuration struct v3_host_pci_dev * host_dev; struct v3_host_pci_bar virt_bars[6]; struct v3_host_pci_bar virt_exp_rom; struct vm_device * pci_bus; struct pci_device * pci_dev; char name[32]; // The current privilege level of the device priv_state_t priv_state; // how each bar is currently mapped bar_state_t bar_state[6]; }; /* static int pci_exp_rom_init(struct vm_device * dev, struct host_pci_state * state) { struct pci_device * pci_dev = state->pci_dev; struct v3_host_pci_bar * hrom = &(state->host_dev->exp_rom); PrintDebug(info->vm_info, info, "Adding 32 bit PCI mem region: start=%p, end=%p\n", (void *)(addr_t)hrom->addr, (void *)(addr_t)(hrom->addr + hrom->size)); if (hrom->exp_rom_enabled) { // only map shadow memory if the ROM is enabled v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, hrom->addr, hrom->addr + hrom->size - 1, hrom->addr); // Initially the virtual location matches the physical ones memcpy(&(state->virt_exp_rom), hrom, sizeof(struct v3_host_pci_bar)); PrintDebug(info->vm_info, info, "phys exp_rom: addr=%p, size=%u\n", (void *)(addr_t)hrom->addr, hrom->size); // Update the pci subsystem versions pci_dev->config_header.expansion_rom_address = PCI_EXP_ROM_VAL(hrom->addr, hrom->exp_rom_enabled); } return 0; } */ static int pt_io_read(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * priv_data) { struct v3_host_pci_bar * pbar = (struct v3_host_pci_bar *)priv_data; int port_offset = port % pbar->size; if (length == 1) { *(uint8_t *)dst = v3_inb(pbar->addr + port_offset); } else if (length == 2) { *(uint16_t *)dst = v3_inw(pbar->addr + port_offset); } else if (length == 4) { *(uint32_t *)dst = v3_indw(pbar->addr + port_offset); } else { PrintError(core->vm_info, core, "Invalid PCI passthrough IO Redirection size read\n"); return -1; } return length; } static int pt_io_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) { struct v3_host_pci_bar * pbar = (struct v3_host_pci_bar *)priv_data; int port_offset = port % pbar->size; if (length == 1) { v3_outb(pbar->addr + port_offset, *(uint8_t *)src); } else if (length == 2) { v3_outw(pbar->addr + port_offset, *(uint16_t *)src); } else if (length == 4) { v3_outdw(pbar->addr + port_offset, *(uint32_t *)src); } else { PrintError(core->vm_info, core, "Invalid PCI passthrough IO Redirection size write\n"); return -1; } return length; } static int pt_io_read_stub(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * priv_data) { PrintDebug(core->vm_info, core, "Ignoring read of %u bytes at port %d\n",length,port); memset(dst,0,length); return length; } static int pt_io_write_stub(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) { PrintDebug(core->vm_info, core, "Ignoring read of %u bytes at port %d\n",length,port); return length; } static int pt_mem_read_stub(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data) { PrintDebug(core->vm_info, core, "Ignoring read of %u bytes at %p\n",length,(void*)guest_addr); memset(dst,0,length); return length; } static int pt_mem_write_stub(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data) { PrintDebug(core->vm_info, core, "Ignoring write of %u bytes at %p\n",length,(void*)guest_addr); return length; } static int pci_unmap_bar(struct vm_device *dev, struct host_pci_state *state, int bar_num) { // struct v3_host_pci_bar *hbar = &(state->host_dev->bars[bar_num]); struct v3_host_pci_bar *vbar = &(state->virt_bars[bar_num]); if (state->bar_state[bar_num] == UNMAPPED) { // this is idempotent, so not an error return 0; } if (state->bar_state[bar_num] != PHYSICAL && state->bar_state[bar_num] != STUB) { PrintError(dev->vm, VCORE_NONE, "Unknown bar state %d\n", state->bar_state[bar_num]); return -1; } switch (vbar->type) { case PT_BAR_NONE: break; case PT_BAR_IO: { int i; // unhook old ports - same for both physical and stub for (i = 0; i < vbar->size; i++) { if (v3_unhook_io_port(dev->vm, vbar->addr + i) == -1) { PrintError(dev->vm, VCORE_NONE, "Could not unhook previously hooked port.... %d (0x%x)\n", (uint32_t)vbar->addr + i, (uint32_t)vbar->addr + i); return -1; } } } break; case PT_BAR_MEM32: { if (state->bar_state[bar_num] == PHYSICAL ) { // remove old mapping struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vbar->addr); if (old_reg == NULL) { // uh oh... PrintError(dev->vm, VCORE_NONE, "Could not find PCI Passthrough memory redirection region (addr=0x%x)\n", (uint32_t)vbar->addr); return -1; } v3_delete_mem_region(dev->vm, old_reg); // void return?! } else { // STUB if (v3_unhook_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr)==-1) { PrintError(dev->vm, VCORE_NONE, "Failed to unhook memory\n"); return -1; } } } break; case PT_BAR_MEM64_LO: // the unmapping happens on the HI BAR break; case PT_BAR_MEM64_HI: { //struct v3_host_pci_bar * lo_vbar = &(state->virt_bars[bar_num - 1]); struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vbar->addr); if (state->bar_state[bar_num] == PHYSICAL) { if (old_reg == NULL) { // uh oh... PrintError(dev->vm, VCORE_NONE, "Could not find PCI Passthrough memory redirection region (addr=%p)\n", (void *)(addr_t)vbar->addr); return -1; } // remove old mapping v3_delete_mem_region(dev->vm, old_reg); // void return?! } else { // STUB if (v3_unhook_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr)==-1) { PrintError(dev->vm, VCORE_NONE, "Failed to unhook memory\n"); return -1; } } } break; default: PrintError(dev->vm, VCORE_NONE, "Unhandled Pasthrough PCI Bar type %d\n", vbar->type); return -1; break; } state->bar_state[bar_num] = UNMAPPED; return 0; } static int pci_map_bar(struct vm_device *dev, struct host_pci_state *state, int bar_num, bar_state_t target) { struct v3_host_pci_bar *hbar = &(state->host_dev->bars[bar_num]); struct v3_host_pci_bar *vbar = &(state->virt_bars[bar_num]); if (state->bar_state[bar_num] != UNMAPPED) { PrintError(dev->vm, VCORE_NONE, "Attempt to map bar that is already mapped\n"); return -1; } if (target != STUB && target != PHYSICAL) { PrintError(dev->vm, VCORE_NONE, "Attempt to map to unknown target %d\n",target); return -1; } switch (vbar->type) { case PT_BAR_NONE: break; case PT_BAR_IO: { int i; if (target == PHYSICAL) { if (vbar->addr == hbar->addr) { // Map the io ports as passthrough PrintDebug(dev->vm,VCORE_NONE,"Adding io port direct mappings\n"); for (i = 0; i < hbar->size; i++) { if (v3_hook_io_port(dev->vm, hbar->addr + i, NULL, NULL, NULL) == -1) { PrintError(dev->vm, VCORE_NONE, "Failed to hook ioport %llu direct\n",hbar->addr+i); return -1; } } } else { // We have to manually handle the io redirection PrintDebug(dev->vm,VCORE_NONE,"Adding io port indirect mappings\n"); for (i = 0; i < vbar->size; i++) { if (v3_hook_io_port(dev->vm, vbar->addr + i, pt_io_read, pt_io_write, hbar) == -1) { PrintError(dev->vm,VCORE_NONE,"Failed to hook ioport %llu indirect\n", vbar->addr+i); return -1; } } } } else { // STUB // hook to stubs PrintDebug(dev->vm,VCORE_NONE,"Adding io port indirect mappings to stubs\n"); for (i = 0; i < vbar->size; i++) { if (v3_hook_io_port(dev->vm, vbar->addr + i, pt_io_read_stub, pt_io_write_stub, dev)==-1) { PrintError(dev->vm, VCORE_NONE, "Failed to hook ioport %llu indirect to stub\n",vbar->addr+i); return -1; } } } } break; case PT_BAR_MEM32: { if (target == PHYSICAL) { PrintDebug(dev->vm, VCORE_NONE, "Adding pci Passthrough mapping: start=0x%x, size=%d, end=0x%x (hpa=%p)\n", (uint32_t)vbar->addr, vbar->size, (uint32_t)vbar->addr + vbar->size, (void *)hbar->addr); if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr, vbar->addr + vbar->size - 1, hbar->addr) == -1 ) { PrintError(dev->vm, VCORE_NONE, "Failed to add region\n"); return -1; } } else { // STUB // we will hook the memory instead with the stubs PrintDebug(dev->vm, VCORE_NONE, "Adding pci Passthrough mapping to stubs: start=0x%x, size=%d, end=0x%x (hpa=%p)\n", (uint32_t)vbar->addr, vbar->size, (uint32_t)vbar->addr + vbar->size, (void *)hbar->addr); if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr, vbar->addr+vbar->size-1, pt_mem_read_stub, pt_mem_write_stub, dev) == -1) { PrintError(dev->vm, VCORE_NONE, "Cannot hook memory for unprivileged access\n"); return -1; } } } break; case PT_BAR_MEM64_LO: // the mapping happens on the HI BAR break; case PT_BAR_MEM64_HI: { if (target == PHYSICAL) { PrintDebug(dev->vm, VCORE_NONE, "Adding pci Passthrough remapping: start=%p, size=%p, end=%p\n", (void *)(addr_t)vbar->addr, (void *)(addr_t)vbar->size, (void *)(addr_t)(vbar->addr + vbar->size)); if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr, vbar->addr + vbar->size - 1, hbar->addr) == -1) { PrintDebug(dev->vm, VCORE_NONE, "Fail to insert shadow region (%p, %p) -> %p\n", (void *)(addr_t)vbar->addr, (void *)(addr_t)(vbar->addr + vbar->size - 1), (void *)(addr_t)hbar->addr); return -1; } } else { // STUB // we will hook the memory instead, using the stubs PrintDebug(dev->vm, VCORE_NONE, "Adding pci Passthrough mapping to stubs: start=0x%x, size=%d, end=0x%x (hpa=%p)\n", (uint32_t)vbar->addr, vbar->size, (uint32_t)vbar->addr + vbar->size, (void *)hbar->addr); if (v3_hook_full_mem(dev->vm, V3_MEM_CORE_ANY, vbar->addr, vbar->addr+vbar->size-1, pt_mem_read_stub, pt_mem_write_stub, dev) == -1) { PrintError(dev->vm, VCORE_NONE, "Cannot hook memory for unprivileged access\n"); return -1; } } } break; default: PrintError(dev->vm, VCORE_NONE, "Unhandled Pasthrough PCI Bar type %d\n", vbar->type); return -1; break; } state->bar_state[bar_num]=target; return 0; } // // This unmaps the bar no matter what state it's in // static int deactivate_bar(struct vm_device *dev, int bar_num) { struct host_pci_state * state = (struct host_pci_state *)dev->private_data; if (pci_unmap_bar(dev, state, bar_num)) { PrintError(dev->vm, VCORE_NONE, "Cannot unmap bar %d\n", bar_num); return -1; } return 0; } // // This does nothing if the bar is already physical // if it's not, it unmaps it and then maps it physical // static int activate_bar_physical(struct vm_device *dev, int bar_num) { struct host_pci_state * state = (struct host_pci_state *)dev->private_data; if (state->bar_state[bar_num] != PHYSICAL) { if (pci_unmap_bar(dev, state, bar_num)) { PrintError(dev->vm, VCORE_NONE, "Cannot unmap bar %d\n", bar_num); return -1; } if (pci_map_bar(dev,state,bar_num,PHYSICAL)) { PrintError(dev->vm, VCORE_NONE, "Cannot map bar %d\n", bar_num); return -1; } } return 0; } // // This does nothing if the bar is already stub // if it's not, it unmaps it and then maps it stub // static int activate_bar_stub(struct vm_device *dev, int bar_num) { struct host_pci_state * state = (struct host_pci_state *)dev->private_data; if (state->bar_state[bar_num] != STUB) { if (pci_unmap_bar(dev, state, bar_num)) { PrintError(dev->vm, VCORE_NONE, "Cannot unmap bar %d\n", bar_num); return -1; } if (pci_map_bar(dev,state,bar_num,STUB)) { PrintError(dev->vm, VCORE_NONE, "Cannot map bar %d\n", bar_num); return -1; } } return 0; } static int activate_bar(struct vm_device *dev, int bar_num) { struct host_pci_state * state = (struct host_pci_state *)dev->private_data; if (state->priv_state == PRIVILEGED) { return activate_bar_physical(dev,bar_num); } else { return activate_bar_stub(dev,bar_num); } } static int reactivate_device_privileged(struct vm_device *dev) { int i; struct host_pci_state * state = (struct host_pci_state *)dev->private_data; PrintDebug(dev->vm, VCORE_NONE, "Switch device to privileged operation\n"); for (i=0;i<6;i++) { if (activate_bar_physical(dev,i)) { PrintError(dev->vm, VCORE_NONE, "Cannot activate bar %d as physical\n",i); return -1; } } state->priv_state=PRIVILEGED; return 0; } static int reactivate_device_unprivileged(struct vm_device *dev) { int i; struct host_pci_state * state = (struct host_pci_state *)dev->private_data; PrintDebug(dev->vm, VCORE_NONE, "Switch device to unprivileged operation\n"); for (i=0;i<6;i++) { if (activate_bar_stub(dev,i)) { PrintError(dev->vm, VCORE_NONE, "Cannot activate bar %d as stub\n",i); return -1; } } state->priv_state=UNPRIVILEGED; return 0; } static int pci_bar_init(int bar_num, uint32_t * dst, void * private_data) { struct vm_device * dev = (struct vm_device *)private_data; struct host_pci_state * state = (struct host_pci_state *)dev->private_data; struct v3_host_pci_bar * hbar = &(state->host_dev->bars[bar_num]); uint32_t bar_val = 0; if (hbar->type == PT_BAR_IO) { bar_val = PCI_IO_BAR_VAL(hbar->addr); } else if (hbar->type == PT_BAR_MEM32) { bar_val = PCI_MEM32_BAR_VAL(hbar->addr, hbar->prefetchable); } else if (hbar->type == PT_BAR_MEM24) { bar_val = PCI_MEM24_BAR_VAL(hbar->addr, hbar->prefetchable); } else if (hbar->type == PT_BAR_MEM64_LO) { struct v3_host_pci_bar * hi_hbar = &(state->host_dev->bars[bar_num + 1]); bar_val = PCI_MEM64_LO_BAR_VAL(hi_hbar->addr, hbar->prefetchable); } else if (hbar->type == PT_BAR_MEM64_HI) { bar_val = PCI_MEM64_HI_BAR_VAL(hbar->addr, hbar->prefetchable); } else { PrintDebug(dev->vm, VCORE_NONE, "Unknown bar type %d\n",hbar->type); } memcpy(&(state->virt_bars[bar_num]), hbar, sizeof(struct v3_host_pci_bar)); *dst = bar_val; if (activate_bar(dev,bar_num)) { PrintError(dev->vm, VCORE_NONE,"Cannot activate bar %d\n",bar_num); return -1; } return 0; } static int pci_bar_write(int bar_num, uint32_t * src, void * private_data) { struct vm_device * dev = (struct vm_device *)private_data; struct host_pci_state * state = (struct host_pci_state *)dev->private_data; struct v3_host_pci_bar * hbar = &(state->host_dev->bars[bar_num]); struct v3_host_pci_bar * vbar = &(state->virt_bars[bar_num]); if (deactivate_bar(dev,bar_num)) { PrintError(dev->vm, VCORE_NONE, "Cannot deactivate bar %d\n", bar_num); return -1; } switch (vbar->type) { case PT_BAR_NONE: // nothing to do break; case PT_BAR_IO: // clear the low bits to match the size vbar->addr = *src & ~(hbar->size - 1); // udpate source version *src = PCI_IO_BAR_VAL(vbar->addr); break; case PT_BAR_MEM32: // clear the low bits to match the size vbar->addr = *src & ~(hbar->size - 1); // Set reserved bits *src = PCI_MEM32_BAR_VAL(vbar->addr, hbar->prefetchable); break; case PT_BAR_MEM64_LO: // We only store the written values here, the actual reconfig comes when the high BAR is updated vbar->addr = *src & ~(hbar->size - 1); *src = PCI_MEM64_LO_BAR_VAL(vbar->addr, hbar->prefetchable); break; case PT_BAR_MEM64_HI: { struct v3_host_pci_bar * lo_vbar = &(state->virt_bars[bar_num - 1]); vbar->addr = (((uint64_t)*src) << 32) + lo_vbar->addr; // We don't set size, because we assume region is less than 4GB // src does not change, because there are no reserved bits } break; default: PrintError(dev->vm, VCORE_NONE, "Unhandled Pasthrough PCI Bar type %d\n", vbar->type); return -1; break; } if (activate_bar(dev,bar_num)) { PrintError(dev->vm, VCORE_NONE, "Cannot activate bar %d\n", bar_num); return -1; } return 0; } static int pt_config_write(struct pci_device * pci_dev, uint32_t reg_num, void * src, uint_t length, void * private_data) { struct vm_device * dev = (struct vm_device *)private_data; struct host_pci_state * state = (struct host_pci_state *)dev->private_data; // V3_PrintStubStub(dev->vm, VCORE_NONE, "Writing host PCI config space update\n"); // We will mask all operations to the config header itself, // and only allow direct access to the device specific config space if (reg_num < 64) { return 0; } if (TEST_NOSELPRIV || state->priv_state==PRIVILEGED) { return v3_host_pci_config_write(state->host_dev, reg_num, src, length); } else { PrintError(dev->vm, VCORE_NONE, "configuration write while unprivileged - CURRENTLY IGNORED\n"); return length; } } static int pt_config_read(struct pci_device * pci_dev, uint32_t reg_num, void * dst, uint_t length, void * private_data) { struct vm_device * dev = (struct vm_device *)private_data; struct host_pci_state * state = (struct host_pci_state *)dev->private_data; // V3_PrintStubStub(dev->vm, VCORE_NONE, "Reading host PCI config space update\n"); if (TEST_NOSELPRIV || state->priv_state==PRIVILEGED) { return v3_host_pci_config_read(state->host_dev, reg_num, dst, length); } else { PrintError(dev->vm, VCORE_NONE, "configuration read while unprivileged - CURRENTLY IGNORED\n"); memset(dst,0,length); return length; } } /* This is really iffy.... * It was totally broken before, but it's _not_ totally fixed now * The Expansion rom can be enabled/disabled via software using the low order bit * We should probably handle that somehow here... */ static int pt_exp_rom_write(struct pci_device * pci_dev, uint32_t * src, void * priv_data) { struct vm_device * dev = (struct vm_device *)(priv_data); struct host_pci_state * state = (struct host_pci_state *)dev->private_data; struct v3_host_pci_bar * hrom = &(state->host_dev->exp_rom); struct v3_host_pci_bar * vrom = &(state->virt_exp_rom); PrintDebug(dev->vm, VCORE_NONE, "exp_rom update: src=0x%x\n", *src); PrintDebug(dev->vm, VCORE_NONE, "vrom is size=%u, addr=0x%x\n", vrom->size, (uint32_t)vrom->addr); PrintDebug(dev->vm, VCORE_NONE, "hrom is size=%u, addr=0x%x\n", hrom->size, (uint32_t)hrom->addr); if (!TEST_NOSELPRIV && state->priv_state != PRIVILEGED) { PrintError(dev->vm, VCORE_NONE, "Attempt to do expansion rom write when not privileged is IGNORED\n"); //PrintError(dev->vm, VCORE_NONE,"\tKCH: letting it through though\n"); /* KCH: let it through for now, commenting below */ return 0; } if (hrom->exp_rom_enabled) { // only remove old mapping if present, I.E. if the rom was enabled previously if (vrom->exp_rom_enabled) { struct v3_mem_region * old_reg = v3_get_mem_region(dev->vm, V3_MEM_CORE_ANY, vrom->addr); if (old_reg == NULL) { // uh oh... PrintError(dev->vm, VCORE_NONE, "Could not find PCI Passthrough exp_rom_base redirection region (addr=0x%x)\n", (uint32_t)vrom->addr); return -1; } v3_delete_mem_region(dev->vm, old_reg); } vrom->addr = *src & ~(hrom->size - 1); // Set flags in actual register value *src = PCI_EXP_ROM_VAL(vrom->addr, (*src & 0x00000001)); PrintDebug(dev->vm, VCORE_NONE, "Cooked src=0x%x\n", *src); PrintDebug(dev->vm, VCORE_NONE, "Adding pci Passthrough exp_rom_base remapping: start=0x%x, size=%u, end=0x%x\n", (uint32_t)vrom->addr, vrom->size, (uint32_t)vrom->addr + vrom->size); if (v3_add_shadow_mem(dev->vm, V3_MEM_CORE_ANY, vrom->addr, vrom->addr + vrom->size - 1, hrom->addr) == -1) { PrintError(dev->vm, VCORE_NONE, "Failed to remap pci exp_rom: start=0x%x, size=%u, end=0x%x\n", (uint32_t)vrom->addr, vrom->size, (uint32_t)vrom->addr + vrom->size); return -1; } } return 0; } static int pt_cmd_update(struct pci_device * pci, pci_cmd_t cmd, uint64_t arg, void * priv_data) { struct vm_device * dev = (struct vm_device *)(priv_data); struct host_pci_state * state = (struct host_pci_state *)dev->private_data; V3_Print(dev->vm, VCORE_NONE, "Host PCI Device: CMD update (%d)(arg=%llu)\n", cmd, arg); if (TEST_NOSELPRIV || state->priv_state == PRIVILEGED) { v3_host_pci_cmd_update(state->host_dev, cmd, arg); } else { PrintError(dev->vm, VCORE_NONE, "Attempt to do cmd update when not in privileged mode is IGNORED\n"); return 0; } return 0; } static int setup_virt_pci_dev(struct v3_vm_info * vm_info, struct vm_device * dev) { struct host_pci_state * state = (struct host_pci_state *)dev->private_data; struct pci_device * pci_dev = NULL; struct v3_pci_bar bars[6]; int bus_num = 0; int i; for (i = 0; i < 6; i++) { bars[i].type = PCI_BAR_PASSTHROUGH; bars[i].private_data = dev; bars[i].bar_init = pci_bar_init; bars[i].bar_write = pci_bar_write; } pci_dev = v3_pci_register_device(state->pci_bus, PCI_STD_DEVICE, bus_num, -1, 0, state->name, bars, pt_config_write, pt_config_read, pt_cmd_update, pt_exp_rom_write, dev); state->pci_dev = pci_dev; // pci_exp_rom_init(dev, state); pci_dev->config_header.expansion_rom_address = 0; v3_pci_enable_capability(pci_dev, PCI_CAP_MSI); // v3_pci_enable_capability(pci_dev, PCI_CAP_MSIX); v3_pci_enable_capability(pci_dev, PCI_CAP_PCIE); v3_pci_enable_capability(pci_dev, PCI_CAP_PM); if (state->host_dev->iface == SYMBIOTIC) { #ifdef V3_CONFIG_SYMBIOTIC v3_sym_map_pci_passthrough(vm_info, pci_dev->bus_num, pci_dev->dev_num, pci_dev->fn_num); #else PrintError(vm_info, VCORE_NONE, "ERROR Symbiotic Passthrough is not enabled\n"); return -1; #endif } return 0; } static int init_priv(struct guest_info * core, void ** private_data) { PrintDebug(core->vm_info, core, "Initializing host_pci device selective privilege operation\n"); return 0; } static int lower_priv(struct guest_info * core, void * private_data) { struct vm_device * dev = (struct vm_device *)private_data; PrintDebug(core->vm_info, core, "Lowering privilege for device %s\n", dev->name); return reactivate_device_unprivileged(dev); } static int raise_priv(struct guest_info * core, void * private_data) { struct vm_device * dev = (struct vm_device *)private_data; PrintDebug(core->vm_info, core, "Raising privilege for device %s\n", dev->name); return reactivate_device_privileged(dev); } static int deinit_priv(struct guest_info * core, void * private_data) { PrintDebug(core->vm_info, core, "Deinitializing host_pci device selective privilege operation\n"); return 0; } static struct v3_device_ops dev_ops = { .free = NULL, }; static int irq_ack(struct guest_info * core, uint32_t irq, void * private_data) { struct host_pci_state * state = (struct host_pci_state *)private_data; PrintDebug(core->vm_info,core,"GM HPCI: IRQ ACK of irq %d\n", irq); // V3_PrintStubStub(core->vm_info, core, "Acking IRQ %d\n", irq); v3_host_pci_ack_irq(state->host_dev, irq); return 0; } static int irq_handler(void * private_data, uint32_t vec_index) { struct host_pci_state * state = (struct host_pci_state *)private_data; struct v3_irq vec; vec.irq = vec_index; vec.ack = irq_ack; vec.private_data = state; // For selective privilege, the interrupt is always delivered // identically to the regular case, regardless of the current // privilege level. The idea here is that delivery of the // interrupt will result in a dispatch to the privileged guest code // and the entry to that code will enable privilege before any // mapped bar is read or written. PrintDebug(0, 0,"Host PCI / Selective Privilege: Delivering vec irq %d, state %d\n", vec.irq, (int)(state->pci_dev->irq_type)); if (state->pci_dev->irq_type == IRQ_NONE) { return 0; } else if (state->pci_dev->irq_type == IRQ_INTX) { v3_pci_raise_acked_irq(state->pci_bus, state->pci_dev, vec); } else { v3_pci_raise_irq(state->pci_bus, state->pci_dev, vec_index); } return 0; } static int host_pci_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) { struct host_pci_state * state = V3_Malloc(sizeof(struct host_pci_state)); struct vm_device * dev = NULL; struct vm_device * pci = v3_find_dev(vm, v3_cfg_val(cfg, "bus")); char * dev_id = v3_cfg_val(cfg, "ID"); char * url = v3_cfg_val(cfg, "url"); memset(state, 0, sizeof(struct host_pci_state)); if (!pci) { PrintError(vm, VCORE_NONE, "PCI bus not specified in config file\n"); return -1; } state->pci_bus = pci; strncpy(state->name, dev_id, 32); dev = v3_add_device(vm, dev_id, &dev_ops, state); if (dev == NULL) { PrintError(vm, VCORE_NONE, "Could not attach device %s\n", dev_id); V3_Free(state); return -1; } state->host_dev = v3_host_pci_get_dev(vm, url, state); if (state->host_dev == NULL) { PrintError(vm, VCORE_NONE, "Could not connect to host pci device (%s)\n", url); return -1; } state->host_dev->irq_handler = irq_handler; char * sel_priv = v3_cfg_val(cfg, "selective_privilege"); if (!sel_priv || !strncasecmp(sel_priv,"off",3) || !strncasecmp(sel_priv,"0",1)) { #if TEST_NOSELPRIV state->priv_state=PRIVILEGED; #else state->priv_state=UNPRIVILEGED; #endif PrintDebug(vm, VCORE_NONE, "Selective privilege functionality disabled\n"); } else { char * ext_priv = v3_cfg_val(cfg, "privilege_extension"); if (!ext_priv) { PrintError(vm, VCORE_NONE, "Selective privilege functionality requested, but not privilege extension given\n"); return -1; } if (v3_bind_privilege(&(vm->cores[0]), ext_priv, init_priv, lower_priv, raise_priv, deinit_priv, dev)) { PrintError(vm, VCORE_NONE, "Cannot bind to privilege %s\n",ext_priv); return -1; } state->priv_state=UNPRIVILEGED; PrintDebug(vm, VCORE_NONE, "Selective privilege functionality enabled (initially unprivileged)\n"); } if (setup_virt_pci_dev(vm, dev) == -1) { PrintError(vm, VCORE_NONE, "Could not setup virtual host PCI device\n"); return -1; } return 0; } device_register("HOST_PCI_SELPRIV", host_pci_init)