From: Peter Dinda Date: Thu, 25 Apr 2013 00:29:15 +0000 (-0500) Subject: Host PCI with selective privilege (can dynamically permit or disallow PCI passthrough... X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=commitdiff_plain;h=2377d33e71ba625a547b414916949181db2a49da Host PCI with selective privilege (can dynamically permit or disallow PCI passthrough access) Also minor tweak to user space Makefile --- diff --git a/linux_usr/Makefile b/linux_usr/Makefile index b53732f..cf5649c 100644 --- a/linux_usr/Makefile +++ b/linux_usr/Makefile @@ -90,6 +90,9 @@ libv3_ctrl.a : v3_ctrl.c v3_ctrl.h $(AR) ruv libv3_ctrl.a v3_ctrl.o rm -rf v3_ctrl.o +# +# JSON library for use in some tools +# libjson.a : cJSON.c cJSON.h $(CC) $(CFLAGS) -c cJSON.c $(AR) ruv libjson.a cJSON.o @@ -137,6 +140,9 @@ v3_user_host_dev_example : v3_user_host_dev_example.c libv3_user_host_dev.a v3_os_debug : v3_os_debug.c libv3_user_host_dev.a $(CC) $(CFLAGS) $< -I../linux_module -L. -lv3_user_host_dev -o $@ +# +# Guarded module registration for GEARS +# v3_register_gm: v3_register_gm.c libjson.a libv3_ctrl.a v3_ctrl.h $(CC) $(CFLAGS) $< -L. -lm -lv3_ctrl -ljson -o $@ diff --git a/palacios/src/devices/Kconfig b/palacios/src/devices/Kconfig index 6583334..d5d8c1d 100644 --- a/palacios/src/devices/Kconfig +++ b/palacios/src/devices/Kconfig @@ -340,6 +340,25 @@ config DEBUG_PCI_FRONT Enable debugging for the PCI front-end device +config HOST_PCI_SELPRIV + bool "Host PCI selective privilege" + depends on HOST_PCI + depends on EXT_PRIV + default n + help + This device enables selective direct access to hardware for + passthrough PCI devices. The privilege extension under + GEARS must be enabled for this option. This device + is a variant of the "always on" "host_pci" device. + +config DEBUG_HOST_PCI_SELPRIV + bool "Host PCI Debugging" + depends on HOST_PCI_SELPRIV + default n + help + This enables debugging output for the host_pci_selpriv device + + config PIC bool "8259A PIC" diff --git a/palacios/src/devices/Makefile b/palacios/src/devices/Makefile index 32a6498..0dbc07e 100644 --- a/palacios/src/devices/Makefile +++ b/palacios/src/devices/Makefile @@ -21,6 +21,7 @@ obj-$(V3_CONFIG_NVRAM) += nvram.o obj-$(V3_CONFIG_OS_DEBUG) += os_debug.o obj-$(V3_CONFIG_PCI) += pci.o obj-$(V3_CONFIG_HOST_PCI) += host_pci.o +obj-$(V3_CONFIG_HOST_PCI_SELPRIV) += host_pci_selpriv.o obj-$(V3_CONFIG_PIIX3) += piix3.o obj-$(V3_CONFIG_SWAPBYPASS_DISK_CACHE) += swapbypass_cache.o obj-$(V3_CONFIG_SWAPBYPASS_DISK_CACHE2) += swapbypass_cache2.o diff --git a/palacios/src/devices/host_pci_selpriv.c b/palacios/src/devices/host_pci_selpriv.c new file mode 100644 index 0000000..1af5e86 --- /dev/null +++ b/palacios/src/devices/host_pci_selpriv.c @@ -0,0 +1,1027 @@ +/* + * 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) +