Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Host PCI with selective privilege (can dynamically permit or disallow PCI passthrough...
Peter Dinda [Thu, 25 Apr 2013 00:29:15 +0000 (19:29 -0500)]
Also minor tweak to user space Makefile

linux_usr/Makefile
palacios/src/devices/Kconfig
palacios/src/devices/Makefile
palacios/src/devices/host_pci_selpriv.c [new file with mode: 0644]

index b53732f..cf5649c 100644 (file)
@@ -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 $@
 
index 6583334..d5d8c1d 100644 (file)
@@ -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"
index 32a6498..0dbc07e 100644 (file)
@@ -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 (file)
index 0000000..1af5e86
--- /dev/null
@@ -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 <pdinda@northwestern.edu> (refactoring and selpriv feature)
+ * Copyright (c) 2012, Jack Lange <jacklange@cs.pitt.edu>
+ * Copyright (c) 2012, The V3VEE Project <http://www.v3vee.org> 
+ * All rights reserved.
+ *
+ * Authors: Jack Lange <jacklange@cs.pitt.edu>
+ *          Peter Dinda <pdinda@northwestern.edu> (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 <palacios/vmm.h>
+#include <palacios/vmm_dev_mgr.h>
+#include <palacios/vmm_sprintf.h>
+#include <palacios/vmm_lowlevel.h>
+#include <palacios/vm_guest.h> // must include this to avoid dependency issue
+#include <palacios/vmm_symspy.h>
+
+#include <devices/pci.h>
+#include <devices/pci_types.h>
+#include <interfaces/host_pci.h>
+
+#include <palacios/vmm_types.h>
+
+#include <gears/privilege.h>
+
+
+#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)
+