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.


reworked PCI bus implmentation -- moved to mask based implementation -- added capabil...
Jack Lange [Tue, 17 Apr 2012 18:46:24 +0000 (14:46 -0400)]
19 files changed:
palacios/include/devices/pci.h
palacios/include/devices/pci_types.h
palacios/src/devices/8259a.c
palacios/src/devices/apic.c
palacios/src/devices/cirrus_gfx_card.c
palacios/src/devices/i440fx.c
palacios/src/devices/ide.c
palacios/src/devices/lnx_virtio_balloon.c
palacios/src/devices/lnx_virtio_blk.c
palacios/src/devices/lnx_virtio_console.c
palacios/src/devices/lnx_virtio_nic.c
palacios/src/devices/lnx_virtio_sym.c
palacios/src/devices/lnx_virtio_symmod.c
palacios/src/devices/lnx_virtio_vnet.c
palacios/src/devices/ne2k.c
palacios/src/devices/pci.c
palacios/src/devices/pci_passthrough.c
palacios/src/devices/piix3.c
palacios/src/devices/rtl8139.c

index ff2e27d..2154ade 100644 (file)
 
 #include <palacios/vmm_types.h>
 #include <palacios/vmm_rbtree.h>
+#include <palacios/vmm_intr.h>
 
 #include <devices/pci_types.h>
 
-
 struct vm_device;
 
 
+typedef enum { PCI_CMD_DMA_DISABLE  = 1,
+              PCI_CMD_DMA_ENABLE   = 2,
+              PCI_CMD_INTX_DISABLE = 3, 
+              PCI_CMD_INTX_ENABLE  = 4,
+              PCI_CMD_MSI_DISABLE  = 5,
+              PCI_CMD_MSI_ENABLE   = 6,
+              PCI_CMD_MSIX_DISABLE = 7,
+              PCI_CMD_MSIX_ENABLE  = 8 } pci_cmd_t;
+
 typedef enum { PCI_BAR_IO, 
               PCI_BAR_MEM24, 
               PCI_BAR_MEM32, 
@@ -104,6 +113,13 @@ struct v3_pci_bar {
 #define PCI_MEM64_BASE_LO(bar_val) (bar_val & PCI_MEM64_MASK_LO)
 #define PCI_EXP_ROM_BASE(rom_val) (rom_val & PCI_EXP_ROM_MASK)
 
+#define PCI_IO_BAR_VAL(addr) ((addr & PCI_IO_MASK) | 0x1)
+#define PCI_MEM24_BAR_VAL(addr, prefetch) (((addr & PCI_MEM24_MASK) | 0x2) | ((prefetch) != 0) << 3)
+#define PCI_MEM32_BAR_VAL(addr, prefetch) (((addr & PCI_MEM_MASK) | ((prefetch) != 0) << 3))
+#define PCI_MEM64_HI_BAR_VAL(addr, prefetch) (addr & PCI_MEM64_MASK_HI)
+#define PCI_MEM64_LO_BAR_VAL(addr, prefetch) ((((addr) & PCI_MEM64_MASK_LO) | 0x4) | ((prefetch) != 0) << 3)
+#define PCI_EXP_ROM_VAL(addr, enable) (((addr) & PCI_EXP_ROM_MASK) | ((enable) != 0))
+
 
 struct pci_device {
 
@@ -134,30 +150,39 @@ struct pci_device {
 
     char name[64];
 
-    int (*config_update)(uint_t reg_num, void * src, uint_t length, void * priv_data);
-
-    int (*cmd_update)(struct pci_device * pci_dev, uchar_t io_enabled, uchar_t mem_enabled);
+    int (*config_write)(struct pci_device * pci_dev, uint32_t reg_num, void * src, 
+                       uint_t length, void * priv_data);
+    int (*config_read)(struct pci_device * pci_dev, uint32_t reg_num, void * dst, 
+                      uint_t length, void * priv_data);
+    int (*cmd_update)(struct pci_device * pci_dev, pci_cmd_t cmd, uint64_t arg, void * priv_data);
     int (*exp_rom_update)(struct pci_device * pci_dev, uint32_t * src, void * private_data);
 
-    int (*config_write)(uint_t reg_num, void * src, uint_t length, void * private_data);
-    int (*config_read)(uint_t reg_num, void * dst, uint_t length, void * private_data);
+    struct v3_vm_info * vm;
 
+    struct list_head cfg_hooks;
+    struct list_head capabilities; 
 
-    int exp_rom_update_flag;
-    int bar_update_flag;
+    struct msi_msg_ctrl * msi_cap;
+    struct msix_cap * msix_cap;
+    struct vm_device * apic_dev;
+
+    enum {IRQ_NONE, IRQ_INTX, IRQ_MSI, IRQ_MSIX} irq_type;
 
     void * priv_data;
 };
 
 
 int v3_pci_set_irq_bridge(struct vm_device * pci_bus, int bus_num,
-                         int (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data), 
-                         int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data), 
+                         int (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec), 
+                         int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec), 
                          void * dev_data);
 
 
-int v3_pci_raise_irq(struct vm_device * pci_bus, int bus_num, struct pci_device * dev);
-int v3_pci_lower_irq(struct vm_device * pci_bus, int bus_num, struct pci_device * dev);
+int v3_pci_raise_irq(struct vm_device * pci_bus, struct pci_device * dev, uint32_t vec_index);
+int v3_pci_lower_irq(struct vm_device * pci_bus, struct pci_device * dev, uint32_t vec_index);
+
+int v3_pci_raise_acked_irq(struct vm_device * pci_bus, struct pci_device * dev, struct v3_irq vec);
+int v3_pci_lower_acked_irq(struct vm_device * pci_bus, struct pci_device * dev, struct v3_irq vec);
 
 struct pci_device * 
 v3_pci_register_device(struct vm_device * pci,
@@ -167,21 +192,34 @@ v3_pci_register_device(struct vm_device * pci,
                       int fn_num,
                       const char * name,
                       struct v3_pci_bar * bars,
-                      int (*config_update)(uint_t reg_num, void * src, uint_t length, void * private_data),
-                      int (*cmd_update)(struct pci_device *pci_dev, uchar_t io_enabled, uchar_t mem_enabled),
+                      int (*config_write)(struct pci_device * pci_dev, uint32_t reg_num, void * src, 
+                                          uint_t length, void * private_data),
+                      int (*config_read)(struct pci_device * pci_dev, uint32_t reg_num, void * dst, 
+                                         uint_t length, void * private_data),
+                      int (*cmd_update)(struct pci_device *pci_dev, pci_cmd_t cmd, uint64_t arg, void * priv_data),
                       int (*exp_rom_update)(struct pci_device * pci_dev, uint32_t * src, void * private_data),
                       void * priv_data);
 
 
-struct pci_device * 
-v3_pci_register_passthrough_device(struct vm_device * pci,
-                                  int bus_num,
-                                  int dev_num,
-                                  int fn_num,
-                                  const char * name,
-                                  int (*config_write)(uint_t reg_num, void * src, uint_t length, void * private_data),
-                                  int (*config_read)(uint_t reg_num, void * dst, uint_t length, void * private_data),
-                                  void * private_data);
+
+int v3_pci_hook_config_range(struct pci_device * pci, 
+                            uint32_t start, uint32_t length, 
+                            int (*write)(struct pci_device * pci_dev, uint32_t offset, 
+                                                void * src, uint_t length, void * private_data), 
+                            int (*read)(struct pci_device * pci_dev, uint32_t offset, 
+                                                void * src, uint_t length, void * private_data), 
+                            void * private_data);
+
+
+
+
+typedef enum { PCI_CAP_INVALID = 0, 
+              PCI_CAP_PM = 0x1,
+              PCI_CAP_MSI = 0x5,
+              PCI_CAP_MSIX = 0x11,
+               PCI_CAP_PCIE = 0x10 } pci_cap_type_t;
+
+int v3_pci_enable_capability(struct pci_device * pci, pci_cap_type_t cap_type, int mask);
 
 
 #endif
index c3cd4cb..f6a98ce 100644 (file)
@@ -63,6 +63,28 @@ struct pci_config_header {
 } __attribute__((packed));
 
 
+struct pci_cmd_reg {
+    union {
+       uint16_t val;
+       struct {
+           uint16_t io_enable         : 1;
+           uint16_t mem_enable        : 1;
+           uint16_t dma_enable        : 1;
+           uint16_t special_cycles    : 1;
+           uint16_t mem_wr_inv_enable : 1;
+           uint16_t vga_snoop         : 1;
+           uint16_t parity_err_resp   : 1;
+           uint16_t rsvd1             : 1;
+           uint16_t serr_enable       : 1;
+           uint16_t fast_b2b_enable   : 1;
+           uint16_t intx_disable      : 1;
+           uint16_t rsvd2             : 5;
+       } __attribute__((packed));
+    } __attribute__((packed));
+
+} __attribute__((packed));
+
+
 typedef enum { PCI_CLASS_PRE2 = 0x00, 
               PCI_CLASS_STORAGE = 0x01, 
               PCI_CLASS_NETWORK = 0x02,
@@ -165,5 +187,357 @@ typedef enum { PCI_BRIDGE_SUBCLASS_HOST_PCI = 0x00,
   };
 */
 
+
+
+struct msi_addr {
+    union {
+       uint32_t val;
+       struct {
+           uint32_t rsvd1        : 2;
+           uint32_t dst_mode     : 1;
+           uint32_t redir_hint   : 1;
+           uint32_t rsvd2        : 8;
+           uint32_t dst_id       : 8;
+           uint32_t fixaddr      : 12;
+       } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+
+struct msi_data {
+    union {
+       uint16_t val;
+       struct {
+           uint16_t vector       : 8;
+           uint16_t del_mode     : 3;
+           uint16_t rsvd1        : 3;
+           uint16_t level        : 1;
+           uint16_t trig_mode    : 1;
+       } __attribute__((packed));;
+    } __attribute__((packed));
+} __attribute__((packed));
+
+
+struct msi_msg_ctrl {
+    union {
+       uint16_t val;
+       struct {
+           uint16_t msi_enable       : 1;
+           uint16_t mult_msg_capable : 3;
+           uint16_t mult_msg_enable  : 3;
+           uint16_t cap_64bit        : 1;
+           uint16_t per_vect_mask    : 1;
+           uint16_t rsvd             : 7;
+       } __attribute__((packed));
+    } __attribute__((packed));
+} __attribute__((packed));
+
+
+struct msi32_msg_addr {
+    struct msi_msg_ctrl msg_ctrl;
+    struct msi_addr addr;
+    struct msi_data data;
+} __attribute__((packed));
+
+
+struct msi64_msg_addr {
+    struct msi_msg_ctrl msg_ctrl;
+    struct msi_addr addr;
+    uint32_t hi_addr;
+    struct msi_data data;
+} __attribute__((packed));
+
+
+struct msi64_pervec_msg_addr {
+    struct msi_msg_ctrl msg_ctrl;
+    struct msi_addr addr;
+    uint32_t hi_addr;
+    struct msi_data data;
+    uint16_t rsvd;
+    uint32_t mask;
+    uint32_t pending;
+} __attribute__((packed));
+
+
+struct msix_msg_ctrl {
+    uint16_t table_size   : 11;
+    uint16_t rsvd         : 4;
+    uint16_t msix_enable  : 1;
+} __attribute__((packed));
+
+struct msix_cap {
+    struct msix_msg_ctrl msg_ctrl;
+    uint32_t hi_addr;
+    uint32_t bir      : 3;
+    uint32_t table_offset : 29;
+} __attribute__((packed));
+
+
+struct msix_table {
+    struct {
+       struct msi_data data;
+       struct msi_addr addr;
+    } __attribute__((packed)) entries[0];
+} __attribute__((packed));
+
+
+
+// See PCI power management specification (1.2)
+struct pmc_cap {
+    struct {
+       uint16_t version        : 3;
+       uint16_t pme_clock      : 1;
+       uint16_t rsvd1          : 1;
+       uint16_t dsi            : 1;
+       uint16_t aux_current    : 3;
+       uint16_t d1_support     : 1;
+       uint16_t d2_support     : 1;
+       uint16_t pme_support    : 5;
+    } __attribute__((packed)) pmc;
+
+    struct {
+       uint16_t power_state    : 2;
+       uint16_t rsvd1          : 1;
+       uint16_t no_soft_reset  : 1;
+       uint16_t rsvd2          : 4;
+       uint16_t pme_en         : 1;
+       uint16_t data_select    : 4;
+       uint16_t data_scale     : 2;
+       uint16_t pme_status     : 1;
+    } __attribute__((packed)) pmcsr;
+
+    struct {
+       uint8_t rsvd1           : 6;
+       uint8_t b2_b3           : 1;
+       uint8_t bpcc_en         : 1;
+    } __attribute__((packed)) pmcsr_bse;
+
+    uint8_t data;
+} __attribute__((packed));
+
+
+struct pcie_cap_reg {
+    uint16_t version     : 4;
+    uint16_t type        : 4;
+    uint16_t slot_impl   : 1;
+    uint16_t irq_msg_num : 5;
+    uint16_t rsvd        : 2;
+} __attribute__((packed));
+
+struct pcie_cap_v1 {
+    struct pcie_cap_reg pcie_cap;
+
+    union {
+       uint32_t val;
+       struct {
+           uint32_t max_payload          : 3;
+           uint32_t phantom_fns          : 2;
+           uint32_t ext_tag_support      : 1;
+           uint32_t endpt_L0_latency     : 3;
+           uint32_t endpt_L1_latency     : 3;
+           uint32_t maybe_rsvd1          : 3;
+           uint32_t role_err_reporting   : 1;
+           uint32_t rsvd2                : 2;
+           uint32_t slot_pwr_lim_val     : 8;
+           uint32_t slot_pwr_lim_scale   : 2;
+           uint32_t rsvd3                : 4;
+       } __attribute__((packed));
+    } __attribute__((packed)) dev_cap;
+
+    union {
+       uint16_t val;
+       struct {
+           uint16_t correctable_err_enable  : 1;
+           uint16_t non_fatal_err_enable    : 1;
+           uint16_t fatal_err_enable        : 1;
+           uint16_t unsupp_req_enable       : 1;
+           uint16_t relaxed_order_enable    : 1;
+           uint16_t max_payload_size        : 3;
+           uint16_t ext_tag_field_enable    : 1;
+           uint16_t phantom_fn_enable       : 1;
+           uint16_t aux_pwr_enable          : 1;
+           uint16_t no_snoop_enable         : 1;
+           uint16_t max_read_req_size       : 3;
+           uint16_t bridge_cfg_retry_enable : 1;
+       } __attribute__((packed));
+    } __attribute__((packed)) dev_ctrl;
+
+    union {
+       uint16_t val;
+       struct {
+           uint16_t correctable_err         : 1;
+           uint16_t non_fatal_err           : 1;
+           uint16_t fatal_err               : 1;
+           uint16_t unsupp_req              : 1;
+           uint16_t aux_pwr                 : 1;
+           uint16_t transaction_pending     : 1;
+           uint16_t rsvd                    : 10;
+       } __attribute__((packed));
+    } __attribute__((packed)) dev_status;
+
+    union {
+       uint32_t val;
+       struct {
+           uint32_t max_link_speed            : 4;
+           uint32_t max_link_width            : 6;
+           uint32_t aspm_support              : 2; /* Active State Power Management Support */
+           uint32_t L0_exit_latency           : 3;
+           uint32_t L1_exit_latency           : 3;
+           uint32_t clk_pwr_mngmt             : 1;
+           uint32_t surprise_pwr_down_capable : 1;
+           uint32_t data_link_active_capable  : 1;
+           uint32_t rsvd1                     : 3;
+           uint32_t port_number               : 8;
+       } __attribute__((packed));
+    } __attribute__((packed)) link_cap;
+
+
+    union {
+       uint16_t val;
+       struct {
+           uint16_t aspm_ctrl                 : 2;
+           uint16_t rsvd1                     : 1;
+           uint16_t rd_cmpl_bndry             : 1;
+           uint16_t link_disable              : 1;
+           uint16_t retrain_link              : 1;
+           uint16_t common_clk_cfg            : 1;
+           uint16_t ext_synch                 : 1;
+           uint16_t clk_pwr_mngmt_enable      : 1;
+           uint16_t rsvd2                     : 7;
+       } __attribute__((packed));
+    } __attribute__((packed)) link_ctrl;
+
+
+    union {
+       uint16_t val;
+       struct {
+           uint16_t link_speed               : 4;
+           uint16_t negotiate_link_width     : 6;
+           uint16_t undef                    : 1;
+           uint16_t link_training            : 1;
+           uint16_t slot_clk_cfg             : 1;
+           uint16_t data_link_layer_active   : 1;
+           uint16_t rsvd                     : 2;
+       } __attribute__((packed));
+    } __attribute__((packed)) link_status;
+
+} __attribute__((packed));
+
+
+
+
+struct pcie_cap_v2 {
+   struct pcie_cap_reg pcie_cap;
+
+    union {
+       uint32_t val;
+       struct {
+           uint32_t max_payload          : 3;
+           uint32_t phantom_fns          : 2;
+           uint32_t ext_tag_support      : 1;
+           uint32_t endpt_L0_latency     : 3;
+           uint32_t endpt_L1_latency     : 3;
+           uint32_t maybe_rsvd1          : 3;
+           uint32_t role_err_reporting   : 1;
+           uint32_t rsvd2                : 2;
+           uint32_t slot_pwr_lim_val     : 8;
+           uint32_t slot_pwr_lim_scale   : 2;
+           uint32_t fn_level_reset       : 1;
+           uint32_t rsvd3                : 3;
+       } __attribute__((packed));
+    } __attribute__((packed)) dev_cap;
+
+    union {
+       uint16_t val;
+       struct {
+           uint16_t correctable_err_enable  : 1;
+           uint16_t non_fatal_err_enable    : 1;
+           uint16_t fatal_err_enable        : 1;
+           uint16_t unsupp_req_enable       : 1;
+           uint16_t relaxed_order_enable    : 1;
+           uint16_t max_payload_size        : 3;
+           uint16_t ext_tag_field_enable    : 1;
+           uint16_t phantom_fn_enable       : 1;
+           uint16_t aux_pwr_enable          : 1;
+           uint16_t no_snoop_enable         : 1;
+           uint16_t max_read_req_size       : 3;
+           uint16_t bridge_cfg_retry_enable : 1;
+       } __attribute__((packed));
+    } __attribute__((packed)) dev_ctrl;
+
+    union {
+       uint16_t val;
+       struct {
+           uint16_t correctable_err         : 1;
+           uint16_t non_fatal_err           : 1;
+           uint16_t fatal_err               : 1;
+           uint16_t unsupp_req              : 1;
+           uint16_t aux_pwr                 : 1;
+           uint16_t transaction_pending     : 1;
+           uint16_t rsvd                    : 10;
+       } __attribute__((packed));
+    } __attribute__((packed)) dev_status;
+
+    union {
+       uint32_t val;
+       struct {
+           uint32_t max_link_speed            : 4;
+           uint32_t max_link_width            : 6;
+           uint32_t aspm_support              : 2; /* Active State Power Management Support */
+           uint32_t L0_exit_latency           : 3;
+           uint32_t L1_exit_latency           : 3;
+           uint32_t clk_pwr_mngmt             : 1;
+           uint32_t surprise_pwr_down_capable : 1;
+           uint32_t data_link_active_capable  : 1;
+           uint32_t rsvd1                     : 3;
+           uint32_t port_number               : 8;
+       } __attribute__((packed));
+    } __attribute__((packed)) link_cap;
+
+
+    union {
+       uint16_t val;
+       struct {
+           uint16_t aspm_ctrl                 : 2;
+           uint16_t rsvd1                     : 1;
+           uint16_t rd_cmpl_bndry             : 1;
+           uint16_t link_disable              : 1;
+           uint16_t retrain_link              : 1;
+           uint16_t common_clk_cfg            : 1;
+           uint16_t ext_synch                 : 1;
+           uint16_t clk_pwr_mngmt_enable      : 1;
+           uint16_t rsvd2                     : 7;
+       } __attribute__((packed));
+    } __attribute__((packed)) link_ctrl;
+
+
+    union {
+       uint16_t val;
+       struct {
+           uint16_t link_speed               : 4;
+           uint16_t negotiate_link_width     : 6;
+           uint16_t undef                    : 1;
+           uint16_t link_training            : 1;
+           uint16_t slot_clk_cfg             : 1;
+           uint16_t data_link_layer_active   : 1;
+           uint16_t rsvd                     : 2;
+       } __attribute__((packed));
+    } __attribute__((packed)) link_status;
+
+
+    /* Some crap whose format we don't know because the PCI-SIG sucks */
+    
+    uint32_t slot_cap;
+    uint16_t slot_ctrl;
+    uint16_t slot_status;
+
+    uint16_t root_ctrl;
+    uint16_t root_cap;
+    uint32_t root_status;
+
+} __attribute__((packed));
+
+
+
 #endif
 
index 0d55349..7e4b7a4 100644 (file)
@@ -159,6 +159,11 @@ struct pic_internal {
 
     struct guest_info * core;
 
+    struct {
+       int (*ack)(struct guest_info * core, uint32_t irq, void * private_data);
+       void * private_data;
+    } irq_ack_cbs[15];
+
 
     void * router_handle;
     void * controller_handle;
@@ -217,6 +222,9 @@ static int pic_raise_intr(struct v3_vm_info * vm, void * private_data, struct v3
        return -1;
     }
 
+    state->irq_ack_cbs[irq_num].ack = irq->ack;
+    state->irq_ack_cbs[irq_num].private_data = irq->private_data;
+
     if (V3_Get_CPU() != vm->cores[0].pcpu_id) {
        // guest is running on another core, interrupt it to deliver irq
        v3_interrupt_cpu(vm, 0, 0);
@@ -329,7 +337,7 @@ static int pic_begin_irq(struct guest_info * info, void * private_data, int irq)
                state->master_irr &= ~(0x1 << irq);
            }
        } else {
-          PrintDebug("8259 PIC: (master) Ignoring begin_irq for %d since I don't own it\n",irq);
+          PrintDebug("8259 PIC: (master) Ignoring begin_irq for %d since I don't own it\n", irq);
        }
 
     } else {
@@ -341,11 +349,12 @@ static int pic_begin_irq(struct guest_info * info, void * private_data, int irq)
               state->slave_irr &= ~(0x1 << (irq - 8));
           }
        } else {
-          PrintDebug("8259 PIC: (slave) Ignoring begin_irq for %d since I don't own it\n",irq);
+          PrintDebug("8259 PIC: (slave) Ignoring begin_irq for %d since I don't own it\n", irq);
        }
-
     }
 
+
+
     return 0;
 }
 
@@ -466,6 +475,15 @@ static int write_master_port1(struct guest_info * core, ushort_t port, void * sr
             if ((cw2->EOI) && (!cw2->R) && (cw2->SL)) {
                 // specific EOI;
                 state->master_isr &= ~(0x01 << cw2->level);
+
+
+               /*
+               // ack the irq if requested
+               if (state->irq_ack_cbs[irq].ack) {
+                   state->irq_ack_cbs[irq].ack(info, irq, state->irq_ack_cbs[irq].private_data);
+               }
+               */
+
             } else if ((cw2->EOI) & (!cw2->R) && (!cw2->SL)) {
                 int i;
                 // Non-specific EOI
index 4823cb6..44ca08d 100644 (file)
@@ -472,7 +472,6 @@ static int add_apic_irq_entry(struct apic_state * apic, uint32_t irq_num,
 
 static void drain_irq_entries(struct apic_state * apic) {
 
-
     while (1) {
        unsigned int flags = 0;
        struct irq_queue_entry * entry = NULL;
index e9d27be..ce25023 100644 (file)
@@ -539,8 +539,8 @@ static int cirrus_gfx_card_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg){
        //                                                     Not sure if STD
        pci_dev = v3_pci_register_device(video_state->pci_bus, PCI_STD_DEVICE, 0,
                                         //or0  1st null could be pci_config_update
-                                        -1, 0, "CIRRUS_GFX_CARD", bars, NULL, NULL,
-                                        NULL, dev);
+                                        -1, 0, "CIRRUS_GFX_CARD", bars, NULL, NULL, 
+                                        NULL, NULL, dev);
 
        if (pci_dev == NULL) {
            PrintError("Failed to register VIDEO %d with PCI\n", i);
index 6c587d3..01e8088 100644 (file)
@@ -107,7 +107,7 @@ static int i440_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
 
     pci_dev = v3_pci_register_device(state->pci, PCI_STD_DEVICE, 
                                     0, 0, 0, "i440FX", bars,
-                                    NULL, NULL, NULL, state);
+                                    NULL, NULL, NULL, NULL, state);
 
     if (!pci_dev) {
        v3_remove_device(dev);
index 285e534..e949fcb 100644 (file)
@@ -206,9 +206,17 @@ struct ide_channel {
     // Control Registers
     struct ide_ctrl_reg ctrl_reg; // [write] 0x3f6,0x376
 
-    struct ide_dma_cmd_reg dma_cmd;
-    struct ide_dma_status_reg dma_status;
-    uint32_t dma_prd_addr;
+    union {
+       uint8_t dma_ports[8];
+       struct {
+           struct ide_dma_cmd_reg dma_cmd;
+           uint8_t rsvd1;
+           struct ide_dma_status_reg dma_status;
+           uint8_t rsvd2;
+           uint32_t dma_prd_addr;
+       } __attribute__((packed));
+    } __attribute__((packed));
+
     uint32_t dma_tbl_index;
 };
 
@@ -283,7 +291,9 @@ static inline int is_lba_enabled(struct ide_channel * channel) {
 /* Drive Commands */
 static void ide_raise_irq(struct ide_internal * ide, struct ide_channel * channel) {
     if (channel->ctrl_reg.irq_disable == 0) {
-       //        PrintError("Raising IDE Interrupt %d\n", channel->irq);
+
+       //PrintError("Raising IDE Interrupt %d\n", channel->irq);
+
         channel->dma_status.int_gen = 1;
         v3_raise_irq(ide->vm, channel->irq);
     }
@@ -456,15 +466,18 @@ static int dma_read(struct guest_info * core, struct ide_internal * ide, struct
                    }
                } else {
                    /*
-                   PrintError("DMA of command packet\n");
                    PrintError("How does this work (ATAPI CMD=%x)???\n", drive->cd_state.atapi_cmd);
                    return -1;
                    */
                    int cmd_ret = 0;
 
+                   //V3_Print("DMA of command packet\n");
+
                    bytes_to_write = (prd_bytes_left > bytes_left) ? bytes_left : prd_bytes_left;
                    prd_bytes_left = bytes_to_write;
 
+
+                   // V3_Print("Writing ATAPI cmd OP DMA (cmd=%x) (len=%d)\n", drive->cd_state.atapi_cmd, prd_bytes_left);
                    cmd_ret = v3_write_gpa_memory(core, prd_entry.base_addr + prd_offset, 
                                                  bytes_to_write, drive->data_buf); 
 
@@ -745,7 +758,7 @@ static int write_dma_port(struct guest_info * core, ushort_t port, void * src, u
 }
 
 
-static int read_dma_port(struct guest_info * core, ushort_t port, void * dst, uint_t length, void * private_data) {
+static int read_dma_port(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * private_data) {
     struct ide_internal * ide = (struct ide_internal *)private_data;
     uint16_t port_offset = port & (DMA_CHANNEL_FLAG - 1);
     uint_t channel_flag = (port & DMA_CHANNEL_FLAG) >> 3;
@@ -753,44 +766,13 @@ static int read_dma_port(struct guest_info * core, ushort_t port, void * dst, ui
 
     PrintDebug("Reading DMA port %d (%x) (channel=%d)\n", port, port, channel_flag);
 
-    switch (port_offset) {
-       case DMA_CMD_PORT:
-           *(uint8_t *)dst = channel->dma_cmd.val;
-           break;
-
-       case DMA_STATUS_PORT:
-           if (length != 1) {
-               PrintError("Invalid read length for DMA status port\n");
-               return -1;
-           }
-
-           *(uint8_t *)dst = channel->dma_status.val;
-           break;
-
-       case DMA_PRD_PORT0:
-       case DMA_PRD_PORT1:
-       case DMA_PRD_PORT2:
-       case DMA_PRD_PORT3: {
-           uint_t addr_index = port_offset & 0x3;
-           uint8_t * addr_buf = (uint8_t *)&(channel->dma_prd_addr);
-           int i = 0;
-
-           if (addr_index + length > 4) {
-               PrintError("DMA Port space overrun port=%x len=%d\n", port_offset, length);
-               return -1;
-           }
-
-           for (i = 0; i < length; i++) {
-               *((uint8_t *)dst + i) = addr_buf[addr_index + i];
-           }
-
-           break;
-       }
-       default:
-           PrintError("IDE: Invalid DMA Port (%d) (%s)\n", port, dma_port_to_str(port_offset));
-           break;
+    if (port_offset + length > 16) {
+       PrintError("DMA Port Read: Port overrun (port_offset=%d, length=%d)\n", port_offset, length);
+       return -1;
     }
 
+    memcpy(dst, channel->dma_ports + port_offset, length);
+    
     PrintDebug("\tval=%x (len=%d)\n", *(uint32_t *)dst, length);
 
     return length;
@@ -1453,7 +1435,6 @@ static void init_channel(struct ide_channel * channel) {
     channel->cmd_reg = 0x00;
     channel->ctrl_reg.val = 0x08;
 
-
     channel->dma_cmd.val = 0;
     channel->dma_status.val = 0;
     channel->dma_prd_addr = 0;
@@ -1466,7 +1447,7 @@ static void init_channel(struct ide_channel * channel) {
 }
 
 
-static int pci_config_update(uint_t reg_num, void * src, uint_t length, void * private_data) {
+static int pci_config_update(struct pci_device * pci_dev, uint32_t reg_num, void * src, uint_t length, void * private_data) {
     PrintDebug("PCI Config Update\n");
     /*
     struct ide_internal * ide = (struct ide_internal *)(private_data);
@@ -1855,7 +1836,7 @@ static int ide_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
 
        pci_dev = v3_pci_register_device(ide->pci_bus, PCI_STD_DEVICE, 0, sb_pci->dev_num, 1, 
                                         "PIIX3_IDE", bars,
-                                        pci_config_update, NULL, NULL, ide);
+                                        pci_config_update, NULL, NULL, NULL, ide);
 
        if (pci_dev == NULL) {
            PrintError("Failed to register IDE BUS %d with PCI\n", i); 
index e936f9e..af3a903 100644 (file)
@@ -172,7 +172,7 @@ static int handle_kick(struct guest_info * core, struct virtio_balloon_state * v
 
     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
        PrintDebug("Raising IRQ %d\n",  virtio->pci_dev->config_header.intr_line);
-       v3_pci_raise_irq(virtio->pci_bus, 0, virtio->pci_dev);
+       v3_pci_raise_irq(virtio->pci_bus, virtio->pci_dev, 0);
        virtio->virtio_cfg.pci_isr = VIRTIO_ISR_ACTIVE;
     }
 
@@ -336,7 +336,7 @@ static int virtio_io_read(struct guest_info * core, uint16_t port, void * dst, u
        case VIRTIO_ISR_PORT:
            *(uint8_t *)dst = virtio->virtio_cfg.pci_isr;
            virtio->virtio_cfg.pci_isr = 0;
-           v3_pci_lower_irq(virtio->pci_bus, 0, virtio->pci_dev);
+           v3_pci_lower_irq(virtio->pci_bus, virtio->pci_dev, 0);
            break;
 
        default:
@@ -379,7 +379,7 @@ static int set_size(struct virtio_balloon_state * virtio, addr_t size) {
 
     PrintDebug("Requesting %d pages\n", virtio->balloon_cfg.requested_pages);
 
-    v3_pci_raise_irq(virtio->pci_bus, 0, virtio->pci_dev);
+    v3_pci_raise_irq(virtio->pci_bus, virtio->pci_dev, 0);
     virtio->virtio_cfg.pci_isr = VIRTIO_ISR_ACTIVE | VIRTIO_ISR_CFG_CHANGED;
     
     return 0;
@@ -476,7 +476,7 @@ static int virtio_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
        pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE, 
                                         0, PCI_AUTO_DEV_NUM, 0,
                                         "LNX_VIRTIO_BALLOON", bars,
-                                        NULL, NULL, NULL, virtio_state);
+                                        NULL, NULL, NULL, NULL, virtio_state);
 
        if (!pci_dev) {
            PrintError("Could not register PCI Device\n");
index fd9ec61..6cdfa06 100644 (file)
@@ -285,7 +285,7 @@ static int handle_kick(struct guest_info * core, struct virtio_blk_state * blk_s
 
     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
        PrintDebug("Raising IRQ %d\n",  blk_state->pci_dev->config_header.intr_line);
-       v3_pci_raise_irq(blk_state->virtio_dev->pci_bus, 0, blk_state->pci_dev);
+       v3_pci_raise_irq(blk_state->virtio_dev->pci_bus, blk_state->pci_dev, 0);
        blk_state->virtio_cfg.pci_isr = 1;
     }
 
@@ -454,7 +454,7 @@ static int virtio_io_read(struct guest_info * core, uint16_t port, void * dst, u
        case VIRTIO_ISR_PORT:
            *(uint8_t *)dst = blk_state->virtio_cfg.pci_isr;
            blk_state->virtio_cfg.pci_isr = 0;
-           v3_pci_lower_irq(blk_state->virtio_dev->pci_bus, 0, blk_state->pci_dev);
+           v3_pci_lower_irq(blk_state->virtio_dev->pci_bus, blk_state->pci_dev, 0);
            break;
 
        default:
@@ -548,7 +548,7 @@ static int register_dev(struct virtio_dev_state * virtio, struct virtio_blk_stat
     pci_dev = v3_pci_register_device(virtio->pci_bus, PCI_STD_DEVICE, 
                                     0, PCI_AUTO_DEV_NUM, 0,
                                     "LNX_VIRTIO_BLK", bars,
-                                    NULL, NULL, NULL, blk_state);
+                                    NULL, NULL, NULL, NULL, blk_state);
     
     if (!pci_dev) {
        PrintError("Could not register PCI Device\n");
index f7f2a11..0afefb8 100644 (file)
@@ -147,7 +147,7 @@ static int handle_kick(struct guest_info * core, struct virtio_console_state * v
 
     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
        PrintDebug("Raising IRQ %d\n",  virtio->pci_dev->config_header.intr_line);
-       v3_pci_raise_irq(virtio->pci_bus, 0, virtio->pci_dev);
+       v3_pci_raise_irq(virtio->pci_bus, virtio->pci_dev, 0);
        virtio->virtio_cfg.pci_isr = VIRTIO_ISR_ACTIVE;
     }
 
@@ -194,7 +194,7 @@ static uint64_t virtio_input(struct v3_vm_info * vm, uint8_t * buf, uint64_t len
 
     // say hello
     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
-           v3_pci_raise_irq(cons_state->pci_bus, 0, cons_state->pci_dev);
+       v3_pci_raise_irq(cons_state->pci_bus, cons_state->pci_dev, 0);
            cons_state->virtio_cfg.pci_isr = 0x1;
     }
 
@@ -359,7 +359,7 @@ static int virtio_io_read(struct guest_info * core, uint16_t port, void * dst, u
        case VIRTIO_ISR_PORT:
            *(uint8_t *)dst = virtio->virtio_cfg.pci_isr;
            virtio->virtio_cfg.pci_isr = 0;
-           v3_pci_lower_irq(virtio->pci_bus, 0, virtio->pci_dev);
+           v3_pci_lower_irq(virtio->pci_bus, virtio->pci_dev, 0);
            break;
 
        default:
@@ -489,7 +489,7 @@ static int virtio_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
        pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE, 
                                         0, PCI_AUTO_DEV_NUM, 0,
                                         "LNX_VIRTIO_CONSOLE", bars,
-                                        NULL, NULL, NULL, virtio_state);
+                                        NULL, NULL, NULL, NULL, virtio_state);
 
        if (!pci_dev) {
            PrintError("Could not register PCI Device\n");
index 400cb0c..9263988 100644 (file)
@@ -326,7 +326,7 @@ static int handle_pkt_tx(struct guest_info * core,
     
     if (txed && !(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
        v3_pci_raise_irq(virtio_state->virtio_dev->pci_bus, 
-                        0, virtio_state->pci_dev);
+                        virtio_state->pci_dev, 0);
        virtio_state->virtio_cfg.pci_isr = 0x1;
        virtio_state->stats.rx_interrupts ++;
     }
@@ -557,7 +557,7 @@ static int virtio_io_read(struct guest_info *core,
            *(uint8_t *)dst = virtio->virtio_cfg.pci_isr;
            virtio->virtio_cfg.pci_isr = 0;
            v3_pci_lower_irq(virtio->virtio_dev->pci_bus, 
-                            0, virtio->pci_dev);
+                            virtio->pci_dev, 0);
            break;
 
        case VIRTIO_NET_CONFIG ... VIRTIO_NET_CONFIG + ETH_ALEN:
@@ -712,7 +712,7 @@ static int virtio_rx(uint8_t * buf, uint32_t size, void * private_data) {
                     virtio->pci_dev->config_header.intr_line);
 
        virtio->virtio_cfg.pci_isr = 0x1;       
-       v3_pci_raise_irq(virtio->virtio_dev->pci_bus, 0, virtio->pci_dev);
+       v3_pci_raise_irq(virtio->virtio_dev->pci_bus, virtio->pci_dev, 0);
        virtio->stats.rx_interrupts ++;
     }
 
@@ -807,7 +807,7 @@ static int register_dev(struct virtio_dev_state * virtio,
     pci_dev = v3_pci_register_device(virtio->pci_bus, PCI_STD_DEVICE, 
                                     0, PCI_AUTO_DEV_NUM, 0,
                                     "LNX_VIRTIO_NIC", bars,
-                                    NULL, NULL, NULL, net_state);
+                                    NULL, NULL, NULL, NULL, net_state);
     
     if (!pci_dev) {
        PrintError("Virtio NIC: Could not register PCI Device\n");
index f5b6501..010415f 100644 (file)
@@ -147,7 +147,7 @@ static int handle_kick(struct guest_info * core, struct virtio_sym_state * sym_s
 
     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
        PrintDebug("Raising IRQ %d\n",  sym_state->pci_dev->config_header.intr_line);
-       v3_pci_raise_irq(sym_state->pci_bus, 0, sym_state->pci_dev);
+       v3_pci_raise_irq(sym_state->pci_bus, sym_state->pci_dev, 0);
        sym_state->virtio_cfg.pci_isr = VIRTIO_ISR_ACTIVE;
     }
 
@@ -314,7 +314,7 @@ static int virtio_io_read(struct guest_info * core, uint16_t port, void * dst, u
        case VIRTIO_ISR_PORT:
            *(uint8_t *)dst = sym_state->virtio_cfg.pci_isr;
            sym_state->virtio_cfg.pci_isr = 0;
-           v3_pci_lower_irq(sym_state->pci_bus, 0, sym_state->pci_dev);
+           v3_pci_lower_irq(sym_state->pci_bus, sym_state->pci_dev, 0);
            break;
 
        default:
@@ -420,7 +420,7 @@ static int virtio_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
        pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE, 
                                         0, PCI_AUTO_DEV_NUM, 0,
                                         "LNX_VIRTIO_SYM", bars,
-                                        NULL, NULL, NULL, virtio_state);
+                                        NULL, NULL, NULL, NULL, virtio_state);
 
        if (!pci_dev) {
            PrintError("Could not register PCI Device\n");
index 993dcb6..d8172df 100644 (file)
@@ -238,7 +238,7 @@ static int handle_xfer_kick(struct guest_info * core, struct virtio_sym_state *
 
     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
        PrintDebug("Raising IRQ %d\n",  sym_state->pci_dev->config_header.intr_line);
-       v3_pci_raise_irq(sym_state->pci_bus, 0, sym_state->pci_dev);
+       v3_pci_raise_irq(sym_state->pci_bus, sym_state->pci_dev, 0);
        sym_state->virtio_cfg.pci_isr = 1;
     }
 
@@ -307,7 +307,7 @@ static int handle_notification_kick(struct guest_info * core, struct virtio_sym_
 
     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
        PrintDebug("Raising IRQ %d\n",  sym_state->pci_dev->config_header.intr_line);
-       v3_pci_raise_irq(sym_state->pci_bus, 0, sym_state->pci_dev);
+       v3_pci_raise_irq(sym_state->pci_bus, sym_state->pci_dev, 0);
        sym_state->virtio_cfg.pci_isr = 1;
     }
 
@@ -491,7 +491,7 @@ static int virtio_io_read(struct guest_info * core, uint16_t port, void * dst, u
        case VIRTIO_ISR_PORT:
            *(uint8_t *)dst = sym_state->virtio_cfg.pci_isr;
            sym_state->virtio_cfg.pci_isr = 0;
-           v3_pci_lower_irq(sym_state->pci_bus, 0, sym_state->pci_dev);
+           v3_pci_lower_irq(sym_state->pci_bus, sym_state->pci_dev, 0);
            break;
 
        default:
@@ -579,7 +579,7 @@ static int virtio_load_capsule(struct v3_vm_info * vm, struct v3_sym_capsule * m
 
     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
        PrintDebug("SYMMOD: Raising IRQ %d\n",  virtio->pci_dev->config_header.intr_line);
-       v3_pci_raise_irq(virtio->pci_bus, 0, virtio->pci_dev);
+       v3_pci_raise_irq(virtio->pci_bus, virtio->pci_dev, 0);
        virtio->virtio_cfg.pci_isr = 0x1;
     }
 
@@ -677,7 +677,7 @@ static int virtio_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
        pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE, 
                                         0, PCI_AUTO_DEV_NUM, 0,
                                         "LNX_VIRTIO_SYMMOD", bars,
-                                        NULL, NULL, NULL, virtio_state);
+                                        NULL, NULL, NULL, NULL, virtio_state);
 
        if (!pci_dev) {
            PrintError("Could not register PCI Device\n");
index 7b6d75c..5bf8ae0 100644 (file)
@@ -204,7 +204,7 @@ static int handle_cmd_kick(struct guest_info * core,
 
     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
        PrintDebug("Raising IRQ %d\n",  vnet_state->pci_dev->config_header.intr_line);
-       v3_pci_raise_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
+       v3_pci_raise_irq(vnet_state->pci_bus, vnet_state->pci_dev, 0);
        vnet_state->virtio_cfg.pci_isr = 1;
     }
 
@@ -258,7 +258,7 @@ static int vnet_pkt_input_cb(struct v3_vm_info * vm,
     }
 
     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
-       v3_pci_raise_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
+       v3_pci_raise_irq(vnet_state->pci_bus, vnet_state->pci_dev, 0);
        vnet_state->virtio_cfg.pci_isr = 0x1;
        PrintDebug("Raising IRQ %d\n",  vnet_state->pci_dev->config_header.intr_line);
     }
@@ -320,8 +320,8 @@ static int do_tx_pkts(struct guest_info * core,
     }
 
     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
-           v3_pci_raise_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
-           vnet_state->virtio_cfg.pci_isr = 0x1;
+       v3_pci_raise_irq(vnet_state->pci_bus,  vnet_state->pci_dev, 0);
+       vnet_state->virtio_cfg.pci_isr = 0x1;
     }
        
     return 0;
@@ -525,7 +525,7 @@ static int vnet_virtio_io_read(struct guest_info * core,
        case VIRTIO_ISR_PORT:
            *(uint8_t *)dst = vnet_state->virtio_cfg.pci_isr;
            vnet_state->virtio_cfg.pci_isr = 0;
-           v3_pci_lower_irq(vnet_state->pci_bus, 0, vnet_state->pci_dev);
+           v3_pci_lower_irq(vnet_state->pci_bus, vnet_state->pci_dev, 0);
            break;
 
        default:
@@ -623,7 +623,7 @@ static int dev_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
        pci_dev = v3_pci_register_device(pci_bus, PCI_STD_DEVICE, 
                                         0, 5 /*PCI_AUTO_DEV_NUM*/, 0,
                                         "LNX_VIRTIO_VNET", bars,
-                                        NULL, NULL, NULL, vnet_state);
+                                        NULL, NULL, NULL, NULL, vnet_state);
 
        if (!pci_dev) {
            PrintError("Could not register PCI Device\n");
index b4796f7..3c8fa06 100644 (file)
@@ -334,7 +334,7 @@ static int ne2k_update_irq(struct ne2k_state * nic_state) {
        if (pci_dev == NULL){
            v3_raise_virq(&(nic_state->vm->cores[0]), NE2K_DEFAULT_IRQ);
        } else {            
-           v3_pci_raise_irq(nic_state->pci_bus, 0, nic_state->pci_dev);
+          v3_pci_raise_irq(nic_state->pci_bus, nic_state->pci_dev, 0);
        }
 
        nic_state->statistics.rx_interrupts ++;
@@ -1124,7 +1124,7 @@ static int register_dev(struct ne2k_state * nic_state)
 
        pci_dev = v3_pci_register_device(nic_state->pci_bus, PCI_STD_DEVICE, 0, -1, 0, 
                                         "NE2000", bars,
-                                        pci_config_update, NULL, NULL, nic_state);
+                                        pci_config_update, NULL, NULL, NULL, nic_state);
 
 
        if (pci_dev == NULL) {
index 05272a8..acac197 100644 (file)
 #include <devices/pci_types.h>
 
 #include <palacios/vm_guest.h>
+#include <palacios/vm_guest_mem.h>
 
 
+#include <devices/apic.h>
+
 
 #ifndef V3_CONFIG_DEBUG_PCI
 #undef PrintDebug
 // This must always be a multiple of 8
 #define MAX_BUS_DEVICES 32
 
+#define PCI_CAP_ID_MSI 0x05
+#define PCI_CAP_ID_MSIX 0x11
+
+
 struct pci_addr_reg {
     union {
        uint32_t val;
@@ -62,7 +69,8 @@ struct pci_addr_reg {
            uint_t fn_num     : 3;
            uint_t dev_num    : 5;
            uint_t bus_num    : 8;
-           uint_t rsvd2      : 7;
+           uint_t hi_reg_num : 4;
+           uint_t rsvd2      : 3;
            uint_t enable     : 1;
        } __attribute__((packed));
     } __attribute__((packed));
@@ -82,8 +90,8 @@ struct pci_bus {
     uint8_t dev_map[MAX_BUS_DEVICES / 8];
 
 
-    int (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data);
-    int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data);
+    int (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec);
+    int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec);
     void * irq_dev_data;
 };
 
@@ -102,6 +110,58 @@ struct pci_internal {
 
 
 
+struct cfg_range_hook {
+    uint32_t start;
+    uint32_t length;
+
+    int (*write)(struct pci_device * pci_dev, uint32_t offset, 
+                void * src, uint_t length, void * private_data);
+
+    int (*read)(struct pci_device * pci_dev, uint32_t offset, 
+               void * dst, uint_t length, void * private_data);
+
+    void * private_data;
+
+    struct list_head list_node;
+};
+
+
+
+struct pci_cap {
+    uint8_t id;
+    uint32_t offset;
+    uint8_t enabled;
+
+    struct list_head cap_node;
+};
+
+
+// These mark read only fields in the pci config header. 
+// If a bit is 1, then the field is writable in the header
+/* Notes: 
+ * BIST is disabled by default (All writes to it will be dropped 
+ * Cardbus CIS is disabled (All writes are dropped)
+ * Writes to capability pointer are disabled
+ */
+static uint8_t pci_hdr_write_mask_00[64] = { 0x00, 0x00, 0x00, 0x00, /* Device ID, Vendor ID */
+                                            0xbf, 0xff, 0x00, 0xf9, /* Command, status */
+                                            0x00, 0x00, 0x00, 0x00, /* Revision ID, Class code */
+                                            0x00, 0xff, 0x00, 0x00, /* CacheLine Size, Latency Timer, Header Type, BIST */
+                                            0xff, 0xff, 0xff, 0xff, /* BAR 0 */
+                                            0xff, 0xff, 0xff, 0xff, /* BAR 1 */
+                                            0xff, 0xff, 0xff, 0xff, /* BAR 2 */
+                                            0xff, 0xff, 0xff, 0xff, /* BAR 3 */
+                                            0xff, 0xff, 0xff, 0xff, /* BAR 4 */
+                                            0xff, 0xff, 0xff, 0xff, /* BAR 5 */
+                                            0x00, 0x00, 0x00, 0x00, /* CardBus CIS Ptr */
+                                            0xff, 0xff, 0xff, 0xff, /* SubSystem Vendor ID, SubSystem ID */
+                                            0xff, 0xff, 0xff, 0xff, /* ExpRom BAR */
+                                            0x00, 0x00, 0x00, 0x00, /* CAP ptr (0xfc to enable), RSVD */
+                                            0x00, 0x00, 0x00, 0x00, /* Reserved */
+                                            0xff, 0x00, 0x00, 0x00 /* INTR Line, INTR Pin, MIN_GNT, MAX_LAT */
+}; 
+
+
 
 
 #ifdef V3_CONFIG_DEBUG_PCI
@@ -225,181 +285,565 @@ static struct pci_device * get_device(struct pci_bus * bus, uint8_t dev_num, uin
 
 
 
+// There won't be many hooks at all, so unordered lists are acceptible for now 
+static struct cfg_range_hook * find_cfg_range_hook(struct pci_device * pci, uint32_t start, uint32_t length) {
+    uint32_t end = start + length - 1; // end is inclusive
+    struct cfg_range_hook * hook = NULL;
 
+    list_for_each_entry(hook, &(pci->cfg_hooks), list_node) {
+       uint32_t hook_end = hook->start + hook->length - 1;
+       if (!((hook->start > end) || (hook_end < start))) {
+           return hook;
+       }
+    }
+    
+    return NULL;
+}
 
 
-static int addr_port_read(struct guest_info * core, ushort_t port, void * dst, uint_t length, void * priv_data) {
-    struct pci_internal * pci_state = priv_data;
-    int reg_offset = port & 0x3;
-    uint8_t * reg_addr = ((uint8_t *)&(pci_state->addr_reg.val)) + reg_offset;
+int v3_pci_hook_config_range(struct pci_device * pci, 
+                            uint32_t start, uint32_t length, 
+                            int (*write)(struct pci_device * pci_dev, uint32_t offset, 
+                                                void * src, uint_t length, void * private_data), 
+                            int (*read)(struct pci_device * pci_dev, uint32_t offset, 
+                                               void * dst, uint_t length, void * private_data), 
+                            void * private_data) {
+    struct cfg_range_hook * hook = NULL;    
+    
 
-    PrintDebug("Reading PCI Address Port (%x): %x len=%d\n", port, pci_state->addr_reg.val, length);
+    if (find_cfg_range_hook(pci, start, length)) {
+       PrintError("Tried to hook an already hooked config region\n");
+       return -1;
+    }
+    
+    hook = V3_Malloc(sizeof(struct cfg_range_hook));
 
-    if (length == 4) {
-       if (reg_offset != 0) {
-           PrintError("Invalid Address Port Read\n");
-           return -1;
+    if (!hook) {
+       PrintError("Could not allocate range hook\n");
+       return -1;
+    }
+
+    memset(hook, 0, sizeof(struct cfg_range_hook));
+
+    hook->start = start;
+    hook->length = length;
+    hook->private_data = private_data;
+    hook->write = write;
+    hook->read = read;
+
+    list_add(&(hook->list_node), &(pci->cfg_hooks));
+
+    return 0;
+
+}
+
+
+
+
+// Note byte ordering: LSB -> MSB
+static uint8_t msi_32_rw_bitmask[10] = { 0x00, 0x00,                     /* ID, next ptr */
+                                        0x71, 0x00,                     /* MSG CTRL */
+                                        0xfc, 0xff, 0xff, 0xff,         /* MSG ADDR */
+                                        0xff, 0xff};                    /* MSG DATA */
+
+static uint8_t msi_64_rw_bitmask[14] = { 0x00, 0x00,                     /* ID, next ptr */
+                                        0x71, 0x00,                     /* MSG CTRL */
+                                        0xfc, 0xff, 0xff, 0xff,         /* MSG LO ADDR */
+                                        0xff, 0xff, 0xff, 0xff,         /* MSG HI ADDR */
+                                        0xff, 0xff};                    /* MSG DATA */
+
+static uint8_t msi_64pervect_rw_bitmask[24] = { 0x00, 0x00,              /* ID, next ptr */
+                                               0x71, 0x00,              /* MSG CTRL */
+                                               0xfc, 0xff, 0xff, 0xff,  /* MSG LO CTRL */
+                                               0xff, 0xff, 0xff, 0xff,  /* MSG HI ADDR */
+                                               0xff, 0xff,              /* MSG DATA */
+                                               0x00, 0x00,              /* RSVD */
+                                               0xff, 0xff, 0xff, 0xff,  
+                                               0x00, 0x00, 0x00, 0x00}; 
+
+static uint8_t msix_rw_bitmask[12] = { 0x00, 0x00,                       /* ID, next ptr */
+                                      0x00, 0x80, 
+                                      0xff, 0xff, 0xff, 0xff,
+                                      0x08, 0xff, 0xff, 0xff};
+
+
+/* I am completely guessing what the format is here. 
+   I only have version 1 of the PCIe spec and cannot download version 2 or 3 
+   without paying the PCI-SIG $3000 a year for membership. 
+   So this is just cobbled together from the version 1 spec and KVM. 
+*/ 
+
+
+static uint8_t pciev1_rw_bitmask[20] = { 0x00, 0x00, /* ID, next ptr */
+                                        0x00, 0x00, /* PCIE CAP register */
+                                        0x00, 0x00, 0x00, 0x00, /* DEV CAP */
+                                        0xff, 0xff, /* DEV CTRL */
+                                        0x0f, 0x00, /* DEV STATUS */
+                                        0x00, 0x00, 0x00, 0x00, /* LINK CAP */
+                                        0xfb, 0x01, /* LINK CTRL */
+                                        0x00, 0x00  /* LINK STATUS */ 
+};
+
+
+static uint8_t pciev2_rw_bitmask[60] = { 0x00, 0x00, /* ID, next ptr */
+                                        0x00, 0x00, /* PCIE CAP register */
+                                        0x00, 0x00, 0x00, 0x00, /* DEV CAP */
+                                        0xff, 0xff, /* DEV CTRL */
+                                        0x0f, 0x00, /* DEV STATUS */
+                                        0x00, 0x00, 0x00, 0x00, /* LINK CAP */
+                                        0xfb, 0x01, /* LINK CTRL */
+                                        0x00, 0x00, /* LINK STATUS */ 
+                                        0x00, 0x00, 0x00, 0x00, /* SLOT CAP ?? */
+                                        0x00, 0x00, /* SLOT CTRL ?? */
+                                        0x00, 0x00, /* SLOT STATUS */
+                                        0x00, 0x00, /* ROOT CTRL */
+                                        0x00, 0x00, /* ROOT CAP */
+                                        0x00, 0x00, 0x00, 0x00, /* ROOT STATUS */
+                                        0x00, 0x00, 0x00, 0x00, /* WHO THE FUCK KNOWS */
+                                        0x00, 0x00, 0x00, 0x00, 
+                                        0x00, 0x00, 0x00, 0x00, 
+                                        0x00, 0x00, 0x00, 0x00, 
+                                        0x00, 0x00, 0x00, 0x00 
+};
+
+static uint8_t pm_rw_bitmask[] = { 0x00, 0x00, /* ID, next ptr */
+                                  0x00, 0x00, /* PWR MGMT CAPS */
+                                  0x03, 0x9f, /* PWR MGMT CTRL */
+                                  0x00, 0x00  /* PMCSR_BSE, Data */
+};
+
+
+
+int cap_write(struct pci_device * pci, uint32_t offset, void * src, uint_t length, void * private_data) {
+    struct pci_cap * cap = private_data;
+    uint32_t cap_offset = cap->offset;
+    pci_cap_type_t cap_type = cap->id;
+
+    uint32_t write_offset = offset - cap_offset;
+    void * cap_ptr = &(pci->config_space[cap_offset + 2]);    
+    int i = 0;
+
+    int msi_was_enabled = 0;
+    int msix_was_enabled = 0;
+
+
+    V3_Print("CAP write trapped (val=%x, cfg_offset=%d, write_offset=%d)\n", *(uint32_t *)src, offset, write_offset);
+
+    if (cap_type == PCI_CAP_MSI) {
+       struct msi_msg_ctrl * msg_ctrl = cap_ptr;
+
+       if (msg_ctrl->msi_enable == 1) {
+           msi_was_enabled = 1;
+       }
+    } else if (cap_type == PCI_CAP_MSIX) {
+       struct msix_cap * msix_cap = cap_ptr;
+
+       if (msix_cap->msg_ctrl.msix_enable == 1) {
+           msix_was_enabled = 1;
+       }
+    }
+
+    for (i = 0; i < length; i++) {
+       uint8_t mask = 0;
+
+       if (cap_type == PCI_CAP_MSI) {
+           struct msi_msg_ctrl * msg_ctrl = cap_ptr;
+
+           V3_Print("MSI Cap Ctrl=%x\n", *(uint16_t *)pci->msi_cap);
+           V3_Print("MSI ADDR=%x\n", *(uint32_t *)(cap_ptr + 2));
+           V3_Print("MSI HI ADDR=%x\n", *(uint32_t *)(cap_ptr + 6));
+           V3_Print("MSI Data=%x\n", *(uint16_t *)(cap_ptr + 10));
+
+           if (msg_ctrl->cap_64bit) {
+               if (msg_ctrl->per_vect_mask) {
+                   mask = msi_64pervect_rw_bitmask[write_offset];
+               } else {
+                   mask = msi_64_rw_bitmask[write_offset];
+               }
+           } else {
+               mask = msi_32_rw_bitmask[write_offset];
+           }
+       } else if (cap_type == PCI_CAP_MSIX) {
+           mask = msix_rw_bitmask[write_offset];
+       } else if (cap_type == PCI_CAP_PCIE) {
+           struct pcie_cap_reg * pcie_cap = cap_ptr;
+
+           if (pcie_cap->version == 1) {
+               mask = pciev1_rw_bitmask[write_offset];
+           } else if (pcie_cap->version == 2) {
+               mask = pciev2_rw_bitmask[write_offset];
+           } else {
+               return 0;
+           }
+       } else if (cap_type == PCI_CAP_PM) {
+           mask = pm_rw_bitmask[write_offset];
+       }
+
+       pci->config_space[offset + i] &= ~mask;
+       pci->config_space[offset + i] |= ((*(uint8_t *)(src + i)) & mask);
+
+       write_offset++;
+    }
+
+
+    if (pci->cmd_update) {
+
+       /* Detect changes to interrupt types for cmd updates */
+       if (cap_type == PCI_CAP_MSI) {
+           struct msi_msg_ctrl * msg_ctrl = cap_ptr;
+           
+           V3_Print("msi_was_enabled=%d, msi_is_enabled=%d\n", msi_was_enabled,  msg_ctrl->msi_enable);
+
+           if ((msg_ctrl->msi_enable == 1) && (msi_was_enabled == 0)) {
+               pci->irq_type = IRQ_MSI;
+               pci->cmd_update(pci, PCI_CMD_MSI_ENABLE, msg_ctrl->mult_msg_enable, pci->priv_data);
+           } else if ((msg_ctrl->msi_enable == 0) && (msi_was_enabled == 1)) {
+               pci->irq_type = IRQ_NONE;
+               pci->cmd_update(pci, PCI_CMD_MSI_DISABLE, 0, pci->priv_data);
+           }
+       } else if (cap_type == PCI_CAP_MSIX) {
+           struct msix_cap * msix_cap = cap_ptr;
+
+           if ((msix_cap->msg_ctrl.msix_enable == 1) && (msix_was_enabled == 0)) {
+               pci->irq_type = IRQ_MSIX;
+               pci->cmd_update(pci, PCI_CMD_MSIX_ENABLE, msix_cap->msg_ctrl.table_size, pci->priv_data);
+           } else if ((msix_cap->msg_ctrl.msix_enable == 0) && (msix_was_enabled == 1)) {
+               pci->irq_type = IRQ_NONE;
+               pci->cmd_update(pci, PCI_CMD_MSIX_DISABLE, msix_cap->msg_ctrl.table_size, pci->priv_data);
+           }
+       }
+    }
+
+    return 0;
+}
+
+
+static int init_pci_cap(struct pci_device * pci, pci_cap_type_t cap_type, uint_t cap_offset) {
+    void * cap_ptr = &(pci->config_space[cap_offset + 2]);
+
+    if (cap_type == PCI_CAP_MSI) {
+       struct msi32_msg_addr * msi = cap_ptr;
+
+       // We only expose a basic 32 bit MSI interface
+       msi->msg_ctrl.msi_enable = 0;
+       msi->msg_ctrl.mult_msg_enable = 0;
+       msi->msg_ctrl.cap_64bit = 0;
+       msi->msg_ctrl.per_vect_mask = 0;
+       
+       msi->addr.val = 0; 
+       msi->data.val = 0;
+
+    } else if (cap_type == PCI_CAP_MSIX) {
+       
+       
+
+    } else if (cap_type == PCI_CAP_PCIE) {
+       struct pcie_cap_v2 * pcie = cap_ptr;
+       
+       // The v1 and v2 formats are identical for the first X bytes
+       // So we use the v2 struct, and only modify extended fields if v2 is detected
+       
+       pcie->dev_cap.fn_level_reset = 0;
+       
+       pcie->dev_ctrl.val &= 0x70e0; // only preserve max_payload_size and max_read_req_size untouched
+       pcie->dev_ctrl.relaxed_order_enable = 1;
+       pcie->dev_ctrl.no_snoop_enable = 1;
+
+       pcie->dev_status.val = 0;
+
+       pcie->link_cap.val &= 0x0003ffff;
+
+       pcie->link_status.val &= 0x03ff;
+       
+       if (pcie->pcie_cap.version >= 2) {
+           pcie->slot_cap = 0;
+           pcie->slot_ctrl = 0;
+           pcie->slot_status = 0;
+
+           pcie->root_ctrl = 0;
+           pcie->root_cap = 0;
+           pcie->root_status = 0;
        }
-       *(uint32_t *)dst = *(uint32_t *)reg_addr;
-    } else if (length == 2) {
-       if (reg_offset > 2) {
-           PrintError("Invalid Address Port Read\n");
+    } else if (cap_type == PCI_CAP_PM) {
+
+    }
+
+
+    return 0;
+}
+
+
+// enumerate all capabilities and disable them.
+static int scan_pci_caps(struct pci_device * pci) {
+    uint_t cap_offset = pci->config_header.cap_ptr;
+        
+    V3_Print("Scanning for Capabilities (cap_offset=%d)\n", cap_offset);
+
+    while (cap_offset != 0) {
+       uint8_t id = pci->config_space[cap_offset];
+       uint8_t next = pci->config_space[cap_offset + 1];
+
+       V3_Print("Found Capability 0x%x at offset %d (0x%x)\n", 
+                id, cap_offset, cap_offset);
+
+       struct pci_cap * cap = V3_Malloc(sizeof(struct pci_cap));
+
+       if (!cap) {
+           PrintError("Error allocating PCI CAP info\n");
            return -1;
        }
-       *(uint16_t *)dst = *(uint16_t *)reg_addr;
-    } else if (length == 1) {
-       *(uint8_t *)dst = *(uint8_t *)reg_addr;
-    } else {
-       PrintError("Invalid read length (%d) for PCI address register\n", length);
+       memset(cap, 0, sizeof(struct pci_cap));
+       
+       cap->id = id;
+       cap->offset = cap_offset;
+
+       list_add(&(cap->cap_node), &(pci->capabilities));
+
+       // set correct init values 
+       init_pci_cap(pci, id, cap_offset);
+
+
+       // set to the next pointer
+       cap_offset = next;
+    }
+
+    // Disable Capabilities
+    pci->config_header.cap_ptr = 0;
+
+    // Hook Cap pointer to return cached config space value
+    if (v3_pci_hook_config_range(pci, 0x34, 1, 
+                                NULL, NULL, NULL) == -1) {
+       PrintError("Could not hook cap pointer\n");
        return -1;
     }
     
 
-    return length;
+
+/*
+    // Disable all PCIE extended capabilities for now
+    pci->config_space[0x100] = 0;
+    pci->config_space[0x101] = 0;
+    pci->config_space[0x102] = 0;
+    pci->config_space[0x103] = 0;
+*/  
+
+
+    return 0;
+
 }
 
+int v3_pci_enable_capability(struct pci_device * pci, pci_cap_type_t cap_type, int mask) {
+    uint32_t size = 0;
+    struct pci_cap * tmp_cap = NULL;
+    struct pci_cap * cap = NULL;
+    void * cap_ptr = NULL;
 
-static int addr_port_write(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
-    struct pci_internal * pci_state = priv_data;
-    int reg_offset = port & 0x3; 
-    uint8_t * reg_addr = ((uint8_t *)&(pci_state->addr_reg.val)) + reg_offset;
+
+    list_for_each_entry(tmp_cap, &(pci->capabilities), cap_node) {
+       if (tmp_cap->id == cap_type) {
+           cap = tmp_cap;
+           break;
+       }
+    }
+
+    if ((cap == NULL) || (cap->enabled)) {
+       return -1;
+    }
 
 
-    if (length == 4) {
-       if (reg_offset != 0) {
-           PrintError("Invalid Address Port Write\n");
+    V3_Print("Found Capability %x at %x (%d)\n", cap_type, cap->offset, cap->offset);
+
+    // found the capability
+
+    // mark it as enabled
+    cap->enabled = 1;
+
+    cap_ptr = &(pci->config_space[cap->offset + 2]);
+
+    if (cap_type == PCI_CAP_MSI) {
+       pci->msi_cap = cap_ptr;
+       
+       if (pci->msi_cap->cap_64bit) {
+           if (pci->msi_cap->per_vect_mask) {
+               // 64 bit MSI w/ per vector masking
+               size = 22;
+           } else {
+               // 64 bit MSI
+               size = 12;
+           }
+       } else {
+           // 32 bit MSI
+           size = 8;
+       }
+    } else if (cap_type == PCI_CAP_MSIX) {
+       pci->msix_cap = cap_ptr;
+       
+       // disable passthrough for MSIX BAR
+
+       pci->bar[pci->msix_cap->bir].type = PCI_BAR_MEM32;
+
+       size = 10;
+    } else if (cap_type == PCI_CAP_PCIE) {
+       struct pcie_cap_reg * pcie_cap = (struct pcie_cap_reg *)&(pci->config_space[cap->offset + 2]);
+
+       if (pcie_cap->version == 1) {
+           size = 20;
+       } else if (pcie_cap->version == 2) {
+           size = 60;
+       } else {
            return -1;
        }
+    } else if (cap_type == PCI_CAP_PM) {
+       size = 8;
+    }
 
-       PrintDebug("Writing PCI 4 bytes Val=%x\n",  *(uint32_t *)src);
+    if (mask) {
+       V3_Print("Hooking capability range (offset=%d, size=%d)\n", cap->offset, size);
 
-       *(uint32_t *)reg_addr = *(uint32_t *)src;
-    } else if (length == 2) {
-       if (reg_offset > 2) {
-           PrintError("Invalid Address Port Write\n");
+       if (v3_pci_hook_config_range(pci, cap->offset, size + 2, 
+                                    cap_write, NULL, cap) == -1) {
+           PrintError("Could not hook config range (start=%d, size=%d)\n", 
+                      cap->offset + 2, size);
            return -1;
        }
+    }
 
-       PrintDebug("Writing PCI 2 byte Val=%x\n",  *(uint16_t *)src);
 
-       *(uint16_t *)reg_addr = *(uint16_t *)src;
-    } else if (length == 1) {
-       PrintDebug("Writing PCI 1 byte Val=%x\n",  *(uint8_t *)src);
-       *(uint8_t *)reg_addr = *(uint8_t *)src;
-    } else {
-       PrintError("Invalid write length (%d) for PCI address register\n", length);
+    // link it to the active capabilities list
+    pci->config_space[cap->offset + 1] = pci->config_header.cap_ptr;
+    pci->config_header.cap_ptr = cap->offset; // add to the head of the list
+
+    return 0;
+}
+
+
+
+
+static int addr_port_read(struct guest_info * core, ushort_t port, void * dst, uint_t length, void * priv_data) {
+    struct pci_internal * pci_state = priv_data;
+    int reg_offset = port & 0x3;
+    uint8_t * reg_addr = ((uint8_t *)&(pci_state->addr_reg.val)) + reg_offset;
+
+    PrintDebug("Reading PCI Address Port (%x): %x len=%d\n", port, pci_state->addr_reg.val, length);
+
+    if (reg_offset + length > 4) {
+       PrintError("Invalid Address port write\n");
        return -1;
     }
 
-    PrintDebug("Writing PCI Address Port(%x): %x\n", port, pci_state->addr_reg.val);
+    memcpy(dst, reg_addr, length);    
 
     return length;
 }
 
 
-static int data_port_read(struct guest_info * core, ushort_t port, void * dst, uint_t length, void * priv_data) {
+static int addr_port_write(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
+    struct pci_internal * pci_state = priv_data;
+    int reg_offset = port & 0x3; 
+    uint8_t * reg_addr = ((uint8_t *)&(pci_state->addr_reg.val)) + reg_offset;
+
+    if (reg_offset + length > 4) {
+       PrintError("Invalid Address port write\n");
+       return -1;
+    }
+
+    // Set address register
+    memcpy(reg_addr, src, length);
+
+    PrintDebug("Writing PCI Address Port(%x): AddrReg=%x (op_val = %x, len=%d) \n", port, pci_state->addr_reg.val, *(uint32_t *)src, length);
+
+    return length;
+}
+
+
+static int data_port_read(struct guest_info * core, uint16_t port, void * dst, uint_t length, void * priv_data) {
     struct pci_internal * pci_state =  priv_data;
     struct pci_device * pci_dev = NULL;
-    uint_t reg_num = (pci_state->addr_reg.reg_num << 2) + (port & 0x3);
-    int i;
+    uint_t reg_num =  (pci_state->addr_reg.hi_reg_num << 16) +(pci_state->addr_reg.reg_num << 2) + (port & 0x3);
+    int i = 0;
+    int bytes_left = length;
 
     if (pci_state->addr_reg.bus_num != 0) {
-       int i = 0;
-       for (i = 0; i < length; i++) {
-           *((uint8_t *)dst + i) = 0xff;
-       }
-
+       memset(dst, 0xff, length);
        return length;
     }
 
-    PrintDebug("Reading PCI Data register. bus = %d, dev = %d, reg = %d (%x), cfg_reg = %x\n", 
+
+    pci_dev = get_device(&(pci_state->bus_list[0]), pci_state->addr_reg.dev_num, pci_state->addr_reg.fn_num);
+    
+    if (pci_dev == NULL) {
+       memset(dst, 0xff, length);
+       return length;
+    } 
+
+    PrintDebug("Reading PCI Data register. bus = %d, dev = %d, fn = %d, reg = %d (%x), cfg_reg = %x\n", 
               pci_state->addr_reg.bus_num, 
               pci_state->addr_reg.dev_num, 
+              pci_state->addr_reg.fn_num,
               reg_num, reg_num, 
               pci_state->addr_reg.val);
 
-    pci_dev = get_device(&(pci_state->bus_list[0]), pci_state->addr_reg.dev_num, pci_state->addr_reg.fn_num);
-    
-    if (pci_dev == NULL) {
-       for (i = 0; i < length; i++) {
-           *(uint8_t *)((uint8_t *)dst + i) = 0xff;
-       }
 
-       return length;
-    }
+    while (bytes_left > 0) {
+       struct cfg_range_hook * cfg_hook  = find_cfg_range_hook(pci_dev, reg_num + i, 1);
+       void * cfg_dst =  &(pci_dev->config_space[reg_num + i]);
 
-    if (pci_dev->type == PCI_PASSTHROUGH) {
-       if (pci_dev->config_read(reg_num, dst, length, pci_dev->priv_data) == -1) {
-           PrintError("Failed to handle configuration update for passthrough pci_device\n");
-           return -1;
-       }
-       
-       return 0;
-    }
+       if (cfg_hook) {
+           uint_t range_len = cfg_hook->length - ((reg_num + i) - cfg_hook->start);
+           range_len = (range_len > bytes_left) ? bytes_left : range_len;
 
-    for (i = 0; i < length; i++) {
-       *(uint8_t *)((uint8_t *)dst + i) = pci_dev->config_space[reg_num + i];
-    }
+           if (cfg_hook->read) {
+               cfg_hook->read(pci_dev, reg_num + i, cfg_dst, range_len, cfg_hook->private_data);
+           }
+           
+           bytes_left -= range_len;
+           i += range_len;
+       } else {
+           if (pci_dev->config_read) {
+               if (pci_dev->config_read(pci_dev, reg_num + i, cfg_dst, 1, pci_dev->priv_data) != 0) {
+                   PrintError("Error in config_read from PCI device (%s)\n", pci_dev->name);
+               }
+           }
+
+           bytes_left--;
+           i++;
+       } 
+    }      
 
+    memcpy(dst, &(pci_dev->config_space[reg_num]), length);
+           
     PrintDebug("\tVal=%x, len=%d\n", *(uint32_t *)dst, length);
 
     return length;
 }
 
 
-static inline int is_cfg_reg_writable(uchar_t header_type, int reg_num) {
-    if (header_type == 0x00) {
-       switch (reg_num) {
-           case 0x00:
-           case 0x01:
-           case 0x02:
-           case 0x03:
-           case 0x08:
-           case 0x09:
-           case 0x0a:
-           case 0x0b:
-           case 0x0e:
-           case 0x3d:
-               return 0;
-                           
-           default:
-               return 1;
-       }
-    } else if (header_type == 0x80) {
-       switch (reg_num) {
-           case 0x00:
-           case 0x01:
-           case 0x02:
-           case 0x03:
-           case 0x08:
-           case 0x09:
-           case 0x0a:
-           case 0x0b:
-           case 0x0e:
-           case 0x3d:
-               return 0;
-                           
-           default:
-               return 1;
-       }
-    } else {
-       // PCI to PCI Bridge = 0x01
-       // CardBus Bridge = 0x02
 
-       // huh?
-       PrintError("Invalid PCI Header type (0x%.2x)\n", header_type);
+static int bar_update(struct pci_device * pci_dev, uint32_t offset, 
+                     void * src, uint_t length, void * private_data) {
+    struct v3_pci_bar * bar = (struct v3_pci_bar *)private_data;
+    int bar_offset = offset & ~0x03;
+    int bar_num = (bar_offset - 0x10) / 4;
+    uint32_t new_val = *(uint32_t *)src;
+    
+    PrintDebug("Updating BAR Register  (Dev=%s) (bar=%d) (old_val=0x%x) (new_val=0x%x)\n", 
+              pci_dev->name, bar_num, bar->val, new_val);
 
-       return -1;
-    }
-}
+    // Cache the changes locally
+    memcpy(&(pci_dev->config_space[offset]), src, length);
 
-static int bar_update(struct guest_info * info, struct pci_device * pci, int bar_num, uint32_t new_val) {
-    struct v3_pci_bar * bar = &(pci->bar[bar_num]);
+    if (bar->type == PCI_BAR_PASSTHROUGH) {
+        if (bar->bar_write(bar_num, (void *)(pci_dev->config_space + bar_offset), bar->private_data) == -1) {
+           PrintError("Error in Passthrough bar write operation\n");
+           return -1;
+       }
+       
+       return 0;
+    }
+   
+    // Else we are a virtualized BAR
 
-    PrintDebug("Updating BAR Register  (Dev=%s) (bar=%d) (old_val=0x%x) (new_val=0x%x)\n", 
-              pci->name, bar_num, bar->val, new_val);
+    *(uint32_t *)(pci_dev->config_space + offset) &= bar->mask;
 
     switch (bar->type) {
        case PCI_BAR_IO: {
@@ -410,7 +854,7 @@ static int bar_update(struct guest_info * info, struct pci_device * pci, int bar
                       bar->num_ports);
                
            // only do this if pci device is enabled....
-           if (!(pci->config_header.status & 0x1)) {
+           if (!(pci_dev->config_header.status & 0x1)) {
                PrintError("PCI Device IO space not enabled\n");
            }
 
@@ -419,15 +863,15 @@ static int bar_update(struct guest_info * info, struct pci_device * pci, int bar
                PrintDebug("Rehooking PCI IO port (old port=%u) (new port=%u)\n",  
                           PCI_IO_BASE(bar->val) + i, PCI_IO_BASE(new_val) + i);
 
-               v3_unhook_io_port(info->vm_info, PCI_IO_BASE(bar->val) + i);
+               v3_unhook_io_port(pci_dev->vm, PCI_IO_BASE(bar->val) + i);
 
-               if (v3_hook_io_port(info->vm_info, PCI_IO_BASE(new_val) + i, 
+               if (v3_hook_io_port(pci_dev->vm, PCI_IO_BASE(new_val) + i, 
                                    bar->io_read, bar->io_write, 
                                    bar->private_data) == -1) {
 
                    PrintError("Could not hook PCI IO port (old port=%u) (new port=%u)\n",  
                               PCI_IO_BASE(bar->val) + i, PCI_IO_BASE(new_val) + i);
-                   v3_print_io_map(info->vm_info);
+                   v3_print_io_map(pci_dev->vm);
                    return -1;
                }
            }
@@ -437,12 +881,12 @@ static int bar_update(struct guest_info * info, struct pci_device * pci, int bar
            break;
        }
        case PCI_BAR_MEM32: {
-           v3_unhook_mem(info->vm_info, V3_MEM_CORE_ANY, (addr_t)(bar->val));
+           v3_unhook_mem(pci_dev->vm, V3_MEM_CORE_ANY, (addr_t)(bar->val));
            
            if (bar->mem_read) {
-               v3_hook_full_mem(info->vm_info, V3_MEM_CORE_ANY, PCI_MEM32_BASE(new_val), 
+               v3_hook_full_mem(pci_dev->vm, V3_MEM_CORE_ANY, PCI_MEM32_BASE(new_val), 
                                 PCI_MEM32_BASE(new_val) + (bar->num_pages * PAGE_SIZE_4KB),
-                                bar->mem_read, bar->mem_write, pci->priv_data);
+                                bar->mem_read, bar->mem_write, pci_dev->priv_data);
            } else {
                PrintError("Write hooks not supported for PCI\n");
                return -1;
@@ -454,7 +898,7 @@ static int bar_update(struct guest_info * info, struct pci_device * pci, int bar
        }
        case PCI_BAR_NONE: {
            PrintDebug("Reprogramming an unsupported BAR register (Dev=%s) (bar=%d) (val=%x)\n", 
-                      pci->name, bar_num, new_val);
+                      pci_dev->name, bar_num, new_val);
            break;
        }
        default:
@@ -466,12 +910,12 @@ static int bar_update(struct guest_info * info, struct pci_device * pci, int bar
 }
 
 
-static int data_port_write(struct guest_info * core, ushort_t port, void * src, uint_t length, void * priv_data) {
+static int data_port_write(struct guest_info * core, uint16_t port, void * src, uint_t length, void * priv_data) {
     struct pci_internal * pci_state = priv_data;
     struct pci_device * pci_dev = NULL;
-    uint_t reg_num = (pci_state->addr_reg.reg_num << 2) + (port & 0x3);
-    int i;
-
+    uint_t reg_num = (pci_state->addr_reg.hi_reg_num << 16) +(pci_state->addr_reg.reg_num << 2) + (port & 0x3);
+    int i = 0;
+    int ret = length;
 
     if (pci_state->addr_reg.bus_num != 0) {
        return length;
@@ -493,110 +937,107 @@ static int data_port_write(struct guest_info * core, ushort_t port, void * src,
                   pci_state->addr_reg.dev_num); 
        return -1;
     }
-    
-    if (pci_dev->type == PCI_PASSTHROUGH) {
-       if (pci_dev->config_write(reg_num, src, length, pci_dev->priv_data) == -1) {
-           PrintError("Failed to handle configuration update for passthrough pci_device\n");
-           return -1;
-       }
-       
-       return 0;
-    }
 
+    /* update the config space
+       If a hook has been registered for a given region, call the hook with the max write length
+    */ 
+    while (length > 0) {
+       struct cfg_range_hook * cfg_hook  = find_cfg_range_hook(pci_dev, reg_num + i, 1);
 
-    for (i = 0; i < length; i++) {
-       uint_t cur_reg = reg_num + i;
-       int writable = is_cfg_reg_writable(pci_dev->config_header.header_type, cur_reg);
-       
-       if (writable == -1) {
-           PrintError("Invalid PCI configuration space\n");
-           return -1;
-       }
-
-       if (writable) {
-           pci_dev->config_space[cur_reg] = *(uint8_t *)((uint8_t *)src + i);
+       if (cfg_hook) {
+           uint_t range_len = cfg_hook->length - ((reg_num + i) - cfg_hook->start);
+           range_len = (range_len > length) ? length : range_len;
+           
+           if (cfg_hook->write) {
+               cfg_hook->write(pci_dev, reg_num + i, (void *)(src + i), range_len, cfg_hook->private_data);
+           }
 
-           if ((cur_reg >= 0x10) && (cur_reg < 0x28)) {
-               // BAR Register Update
-               int bar_reg = ((cur_reg & ~0x3) - 0x10) / 4;
-               
-               pci_dev->bar_update_flag = 1;
-               pci_dev->bar[bar_reg].updated = 1;
-               
-               // PrintDebug("Updating BAR register %d\n", bar_reg);
+           length -= range_len;
+           i += range_len;
+       } else {
+           // send the writes to the cached config space, and to the generic callback if present
+           uint8_t mask = 0xff;
 
-           } else if ((cur_reg >= 0x30) && (cur_reg < 0x34)) {
-               // Extension ROM update
+           if (reg_num < 64) {
+               mask = pci_hdr_write_mask_00[reg_num + i];
+           }
+           
+           if (mask != 0) {
+               uint8_t new_val = *(uint8_t *)(src + i);
+               uint8_t old_val = pci_dev->config_space[reg_num + i];
 
-               pci_dev->exp_rom_update_flag = 1;
-           } else if (cur_reg == 0x04) {
-               // COMMAND update            
-               uint8_t command = *((uint8_t *)src + i);
+               pci_dev->config_space[reg_num + i] = ((new_val & mask) | (old_val & ~mask));
                
-               PrintError("command update for %s old=%x new=%x\n",
-                          pci_dev->name, 
-                          pci_dev->config_space[cur_reg],command);
-
-               pci_dev->config_space[cur_reg] = command;             
-
-               if (pci_dev->cmd_update) {
-                   pci_dev->cmd_update(pci_dev, (command & 0x01), (command & 0x02));
+               if (pci_dev->config_write) {
+                   pci_dev->config_write(pci_dev, reg_num + i, &(pci_dev->config_space[reg_num + i]), 1, pci_dev->priv_data);
                }
-               
-           } else if (cur_reg == 0x0f) {
-               // BIST update
-               pci_dev->config_header.BIST = 0x00;
-           }
-       } else {
-           PrintError("PCI Write to read only register %d\n", cur_reg);
+           }       
+
+           length--;
+           i++;
        }
     }
 
-    if (pci_dev->config_update) {
-       pci_dev->config_update(reg_num, src, length, pci_dev->priv_data);
+    return ret;
+}
+
+
+
+static int exp_rom_write(struct pci_device * pci_dev, uint32_t offset, 
+                        void * src, uint_t length, void * private_data) {
+    int bar_offset = offset & ~0x03;
+
+    if (pci_dev->exp_rom_update) {
+       pci_dev->exp_rom_update(pci_dev, (void *)(pci_dev->config_space + bar_offset), pci_dev->priv_data);
+       
+       return 0;
     }
 
-    // Scan for BAR updated
-    if (pci_dev->bar_update_flag) {
-       for (i = 0; i < 6; i++) {
-           if (pci_dev->bar[i].updated) {
-               int bar_offset = 0x10 + 4 * i;
+    PrintError("Expansion ROM update not handled. Will appear to not Exist\n");
 
+    return 0;
+}
 
-               if (pci_dev->bar[i].type == PCI_BAR_PASSTHROUGH) {
-                   if (pci_dev->bar[i].bar_write(i, (uint32_t *)(pci_dev->config_space + bar_offset), pci_dev->bar[i].private_data) == -1) {
-                       PrintError("Error in passthrough bar write operation\n");
-                       return -1;
-                   }
-               } else {
 
-                   *(uint32_t *)(pci_dev->config_space + bar_offset) &= pci_dev->bar[i].mask;
-                   // check special flags....
+static int cmd_write(struct pci_device * pci_dev, uint32_t offset, 
+                    void * src, uint_t length, void * private_data) {
 
-                   // bar_update
-                   if (bar_update(core, pci_dev, i, *(uint32_t *)(pci_dev->config_space + bar_offset)) == -1) {
-                       PrintError("PCI Device %s: Bar update Error Bar=%d\n", pci_dev->name, i);
-                       return -1;
-                   }
-               }
+    int i = 0;
 
-               pci_dev->bar[i].updated = 0;
-           }
-       }
-       pci_dev->bar_update_flag = 0;
-    }
+    struct pci_cmd_reg old_cmd;
+    struct pci_cmd_reg new_cmd;
+    old_cmd.val = pci_dev->config_header.command;
+
+    for (i = 0; i < length; i++) {
+        uint8_t mask = pci_hdr_write_mask_00[offset + i];
+       uint8_t new_val = *(uint8_t *)(src + i);
+       uint8_t old_val = pci_dev->config_space[offset + i];
 
-    if ((pci_dev->exp_rom_update_flag) && (pci_dev->exp_rom_update)) {
-       pci_dev->exp_rom_update(pci_dev, &(pci_dev->config_header.expansion_rom_address), pci_dev->priv_data);
-       pci_dev->exp_rom_update_flag = 0;
+       pci_dev->config_space[offset + i] = ((new_val & mask) | (old_val & ~mask));
     }
 
+    new_cmd.val = pci_dev->config_header.command;
 
-    return length;
-}
+
+    if (pci_dev->cmd_update) {
+       if ((new_cmd.intx_disable == 1) && (old_cmd.intx_disable == 0)) {
+           pci_dev->irq_type = IRQ_NONE;
+           pci_dev->cmd_update(pci_dev, PCI_CMD_INTX_DISABLE, 0, pci_dev->priv_data);
+       } else if ((new_cmd.intx_disable == 0) && (old_cmd.intx_disable == 1)) {
+           pci_dev->irq_type = IRQ_INTX;
+           pci_dev->cmd_update(pci_dev, PCI_CMD_INTX_ENABLE, 0, pci_dev->priv_data);
+       }
 
 
+       if ((new_cmd.dma_enable == 1) && (old_cmd.dma_enable == 0)) {
+           pci_dev->cmd_update(pci_dev, PCI_CMD_DMA_ENABLE, 0, pci_dev->priv_data);
+       } else if ((new_cmd.dma_enable == 0) && (old_cmd.dma_enable == 1)) {
+           pci_dev->cmd_update(pci_dev, PCI_CMD_DMA_DISABLE, 0, pci_dev->priv_data);
+       }
+    }
 
+    return 0;
+}
 
 
 static void init_pci_busses(struct pci_internal * pci_state) {
@@ -625,6 +1066,27 @@ static int pci_free(struct pci_internal * pci_state) {
            node = v3_rb_next(node);
            
            v3_rb_erase(&(dev->dev_tree_node), &(bus->devices));
+           
+           // Free config range hooks
+           { 
+               struct cfg_range_hook * hook = NULL;
+               struct cfg_range_hook * tmp = NULL;
+               list_for_each_entry_safe(hook, tmp, &(dev->cfg_hooks), list_node) {
+                   list_del(&(hook->list_node));
+                   V3_Free(hook);
+               }
+           }
+
+           // Free caps
+           {
+               struct pci_cap * cap = NULL;
+               struct pci_cap * tmp = NULL;
+               list_for_each_entry_safe(cap, tmp, &(dev->cfg_hooks), cap_node) {
+                   list_del(&(cap->cap_node));
+                   V3_Free(cap);
+               }
+           }
+
            V3_Free(dev);
        }
 
@@ -785,82 +1247,85 @@ static inline int init_bars(struct v3_vm_info * vm, struct pci_device * pci_dev)
 
     for (i = 0; i < 6; i++) {
        int bar_offset = 0x10 + (4 * i);
+       struct v3_pci_bar * bar = &(pci_dev->bar[i]);
 
-       if (pci_dev->bar[i].type == PCI_BAR_IO) {
+       if (bar->type == PCI_BAR_IO) {
            int j = 0;
-           pci_dev->bar[i].mask = (~((pci_dev->bar[i].num_ports) - 1)) | 0x01;
+           bar->mask = (~((bar->num_ports) - 1)) | 0x01;
 
-           if (pci_dev->bar[i].default_base_port != 0xffff) {
-               pci_dev->bar[i].val = pci_dev->bar[i].default_base_port & pci_dev->bar[i].mask;
+           if (bar->default_base_port != 0xffff) {
+               bar->val = bar->default_base_port & bar->mask;
            } else {
-               pci_dev->bar[i].val = 0;
+               bar->val = 0;
            }
 
-           pci_dev->bar[i].val |= 0x00000001;
+           bar->val |= 0x00000001;
 
-           for (j = 0; j < pci_dev->bar[i].num_ports; j++) {
+           for (j = 0; j < bar->num_ports; j++) {
                // hook IO
-               if (pci_dev->bar[i].default_base_port != 0xffff) {
-                   if (v3_hook_io_port(vm, pci_dev->bar[i].default_base_port + j,
-                                       pci_dev->bar[i].io_read, pci_dev->bar[i].io_write, 
-                                       pci_dev->bar[i].private_data) == -1) {
-                       PrintError("Could not hook default io port %x\n", pci_dev->bar[i].default_base_port + j);
+               if (bar->default_base_port != 0xffff) {
+                   if (v3_hook_io_port(vm, bar->default_base_port + j,
+                                       bar->io_read, bar->io_write, 
+                                       bar->private_data) == -1) {
+                       PrintError("Could not hook default io port %x\n", bar->default_base_port + j);
                        return -1;
                    }
                }
            }
 
-           *(uint32_t *)(pci_dev->config_space + bar_offset) = pci_dev->bar[i].val;
+           *(uint32_t *)(pci_dev->config_space + bar_offset) = bar->val;
 
-       } else if (pci_dev->bar[i].type == PCI_BAR_MEM32) {
-           pci_dev->bar[i].mask = ~((pci_dev->bar[i].num_pages << 12) - 1);
-           pci_dev->bar[i].mask |= 0xf; // preserve the configuration flags
+       } else if (bar->type == PCI_BAR_MEM32) {
+           bar->mask = ~((bar->num_pages << 12) - 1);
+           bar->mask |= 0xf; // preserve the configuration flags
 
-           if (pci_dev->bar[i].default_base_addr != 0xffffffff) {
-               pci_dev->bar[i].val = pci_dev->bar[i].default_base_addr & pci_dev->bar[i].mask;
+           if (bar->default_base_addr != 0xffffffff) {
+               bar->val = bar->default_base_addr & bar->mask;
            } else {
-               pci_dev->bar[i].val = 0;
+               bar->val = 0;
            }
 
            // hook memory
-           if (pci_dev->bar[i].mem_read) {
+           if (bar->mem_read) {
                // full hook
-               v3_hook_full_mem(vm, V3_MEM_CORE_ANY, pci_dev->bar[i].default_base_addr,
-                                pci_dev->bar[i].default_base_addr + (pci_dev->bar[i].num_pages * PAGE_SIZE_4KB),
-                                pci_dev->bar[i].mem_read, pci_dev->bar[i].mem_write, pci_dev->priv_data);
-           } else if (pci_dev->bar[i].mem_write) {
+               v3_hook_full_mem(vm, V3_MEM_CORE_ANY, bar->default_base_addr,
+                                bar->default_base_addr + (bar->num_pages * PAGE_SIZE_4KB),
+                                bar->mem_read, bar->mem_write, pci_dev->priv_data);
+           } else if (bar->mem_write) {
                // write hook
                PrintError("Write hooks not supported for PCI devices\n");
                return -1;
                /*
-                 v3_hook_write_mem(pci_dev->vm_dev->vm, pci_dev->bar[i].default_base_addr, 
-                 pci_dev->bar[i].default_base_addr + (pci_dev->bar[i].num_pages * PAGE_SIZE_4KB),
-                 pci_dev->bar[i].mem_write, pci_dev->vm_dev);
+                 v3_hook_write_mem(pci_dev->vm_dev->vm, bar->default_base_addr, 
+                 bar->default_base_addr + (bar->num_pages * PAGE_SIZE_4KB),
+                 bar->mem_write, pci_dev->vm_dev);
                */
            } else {
                // set the prefetchable flag...
-               pci_dev->bar[i].val |= 0x00000008;
+               bar->val |= 0x00000008;
            }
 
 
-           *(uint32_t *)(pci_dev->config_space + bar_offset) = pci_dev->bar[i].val;
+           *(uint32_t *)(pci_dev->config_space + bar_offset) = bar->val;
 
-       } else if (pci_dev->bar[i].type == PCI_BAR_MEM24) {
+       } else if (bar->type == PCI_BAR_MEM24) {
            PrintError("16 Bit memory ranges not supported (reg: %d)\n", i);
            return -1;
-       } else if (pci_dev->bar[i].type == PCI_BAR_NONE) {
-           pci_dev->bar[i].val = 0x00000000;
-           pci_dev->bar[i].mask = 0x00000000; // This ensures that all updates will be dropped
-           *(uint32_t *)(pci_dev->config_space + bar_offset) = pci_dev->bar[i].val;
-       } else if (pci_dev->bar[i].type == PCI_BAR_PASSTHROUGH) {
+       } else if (bar->type == PCI_BAR_NONE) {
+           bar->val = 0x00000000;
+           bar->mask = 0x00000000; // This ensures that all updates will be dropped
+           *(uint32_t *)(pci_dev->config_space + bar_offset) = bar->val;
+       } else if (bar->type == PCI_BAR_PASSTHROUGH) {
 
            // Call the bar init function to get the local cached value
-           pci_dev->bar[i].bar_init(i, &(pci_dev->bar[i].val), pci_dev->bar[i].private_data);
+           bar->bar_init(i, &(bar->val), bar->private_data);
 
        } else {
            PrintError("Invalid BAR type for bar #%d\n", i);
            return -1;
        }
+
+       v3_pci_hook_config_range(pci_dev, bar_offset, 4, bar_update, NULL, bar);
     }
 
     return 0;
@@ -868,8 +1333,8 @@ static inline int init_bars(struct v3_vm_info * vm, struct pci_device * pci_dev)
 
 
 int v3_pci_set_irq_bridge(struct  vm_device * pci_bus, int bus_num, 
-                         int (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data),
-                         int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data),
+                         int (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec),
+                         int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec),
                          void * priv_data) {
     struct pci_internal * pci_state = (struct pci_internal *)pci_bus->private_data;
 
@@ -881,20 +1346,129 @@ int v3_pci_set_irq_bridge(struct  vm_device * pci_bus, int bus_num,
     return 0;
 }
 
-int v3_pci_raise_irq(struct vm_device * pci_bus, int bus_num, struct pci_device * dev) {
-   struct pci_internal * pci_state = (struct pci_internal *)pci_bus->private_data;
-   struct pci_bus * bus = &(pci_state->bus_list[bus_num]);
+int v3_pci_raise_irq(struct vm_device * pci_bus, struct pci_device * dev, uint32_t vec_index) {
+   struct v3_irq vec;
+
+   vec.ack = NULL;
+   vec.private_data = NULL;
+   vec.irq = vec_index;
 
-   return bus->raise_pci_irq(dev, bus->irq_dev_data);
+   return v3_pci_raise_acked_irq(pci_bus, dev, vec);
 }
 
-int v3_pci_lower_irq(struct vm_device * pci_bus, int bus_num, struct pci_device * dev) {
+int v3_pci_lower_irq(struct vm_device * pci_bus, struct pci_device * dev, uint32_t vec_index) {
+    struct v3_irq vec;
+
+    vec.irq = vec_index;
+    vec.ack = NULL;
+    vec.private_data = NULL;
+    
+    return v3_pci_lower_acked_irq(pci_bus, dev, vec);
+}
+
+int v3_pci_raise_acked_irq(struct vm_device * pci_bus, struct pci_device * dev, struct v3_irq vec) {
    struct pci_internal * pci_state = (struct pci_internal *)pci_bus->private_data;
-   struct pci_bus * bus = &(pci_state->bus_list[bus_num]);
+   struct pci_bus * bus = &(pci_state->bus_list[dev->bus_num]);
+
+
+   if (dev->irq_type == IRQ_INTX) {
+       return bus->raise_pci_irq(dev, bus->irq_dev_data, &vec);
+   } else if (dev->irq_type == IRQ_MSI) {
+       struct v3_gen_ipi ipi;
+       struct msi_addr * addr = NULL;
+       struct msi_data * data = NULL;       
+       
+       if (dev->msi_cap->cap_64bit) {
+          if (dev->msi_cap->per_vect_mask) {
+              struct msi64_pervec_msg_addr * msi = (void *)dev->msi_cap;
+              addr = &(msi->addr);
+              data = &(msi->data);
+          } else {
+              struct msi64_msg_addr * msi = (void *)dev->msi_cap;
+              addr = &(msi->addr);
+              data = &(msi->data);
+          }
+       } else {
+          struct msi32_msg_addr * msi = (void *)dev->msi_cap;
+          addr = &(msi->addr);
+          data = &(msi->data);
+       }
+
+       memset(&ipi, 0, sizeof(struct v3_gen_ipi));
+
+       ipi.vector = data->vector + vec.irq;
+       ipi.mode = data->del_mode;
+       ipi.logical = addr->dst_mode;
+       ipi.trigger_mode = data->trig_mode;
+       ipi.dst_shorthand = 0;
+       ipi.dst = addr->dst_id;
+       
+       // decode MSI fields into IPI
+
+       V3_Print("Decode MSI\n");
+
+       v3_apic_send_ipi(dev->vm, &ipi, dev->apic_dev);
+
+       return 0;       
+   } else if (dev->irq_type == IRQ_MSIX) {
+       addr_t msix_table_gpa = 0;
+       struct msix_table * msix_table = NULL;
+       uint_t bar_idx = dev->msix_cap->bir;
+       struct v3_gen_ipi ipi;
+       struct msi_addr * addr = NULL;
+       struct msi_data * data = NULL;   
+       
+       if (dev->bar[bar_idx].type != PCI_BAR_MEM32) {
+          PrintError("Non 32bit MSIX BAR registers are not supported\n");
+          return -1;
+       }
+
+       msix_table_gpa = dev->bar[bar_idx].val;
+       msix_table_gpa += dev->msix_cap->table_offset;
+
+       if (v3_gpa_to_hva(&(dev->vm->cores[0]), msix_table_gpa, (void *)&(msix_table)) != 0) {
+          PrintError("Could not translate MSIX Table GPA (%p)\n", (void *)msix_table_gpa);
+          return -1;
+       }
+       
+       memset(&ipi, 0, sizeof(struct v3_gen_ipi));
+
+       data = &(msix_table->entries[vec.irq].data);
+       addr = &(msix_table->entries[vec.irq].addr);;
+       
+       ipi.vector = data->vector + vec.irq;
+       ipi.mode = data->del_mode;
+       ipi.logical = addr->dst_mode;
+       ipi.trigger_mode = data->trig_mode;
+       ipi.dst_shorthand = 0;
+       ipi.dst = addr->dst_id;
+       
+       // decode MSIX fields into IPI
+
+       V3_Print("Decode MSIX\n");
+
+       v3_apic_send_ipi(dev->vm, &ipi, dev->apic_dev);
+
+       return 0;
+   } 
+   
+   // Should never get here
+   return -1;
 
-   return bus->lower_pci_irq(dev, bus->irq_dev_data);
 }
 
+int v3_pci_lower_acked_irq(struct vm_device * pci_bus, struct pci_device * dev, struct v3_irq vec) {
+    if (dev->irq_type == IRQ_INTX) {
+       struct pci_internal * pci_state = (struct pci_internal *)pci_bus->private_data;
+       struct pci_bus * bus = &(pci_state->bus_list[dev->bus_num]);
+       
+       return bus->lower_pci_irq(dev, bus->irq_dev_data, &vec);
+    } else {
+       return -1;
+    }
+}
+
+
 // if dev_num == -1, auto assign 
 struct pci_device * v3_pci_register_device(struct vm_device * pci,
                                           pci_device_type_t dev_type, 
@@ -903,8 +1477,11 @@ struct pci_device * v3_pci_register_device(struct vm_device * pci,
                                           int fn_num,
                                           const char * name,
                                           struct v3_pci_bar * bars,
-                                          int (*config_update)(uint_t reg_num, void * src, uint_t length, void * priv_data),
-                                          int (*cmd_update)(struct pci_device * pci_dev, uchar_t io_enabled, uchar_t mem_enabled),
+                                          int (*config_write)(struct pci_device * pci_dev, uint32_t reg_num, void * src, 
+                                                              uint_t length, void * priv_data),
+                                          int (*config_read)(struct pci_device * pci_dev, uint32_t reg_num, void * dst, 
+                                                             uint_t length, void * priv_data),
+                                          int (*cmd_update)(struct pci_device * pci_dev, pci_cmd_t cmd, uint64_t arg, void * priv_data),
                                           int (*exp_rom_update)(struct pci_device * pci_dev, uint32_t * src, void * priv_data),
                                           void * priv_data) {
 
@@ -966,14 +1543,53 @@ struct pci_device * v3_pci_register_device(struct vm_device * pci,
     pci_dev->fn_num = fn_num;
 
     strncpy(pci_dev->name, name, sizeof(pci_dev->name));
+    pci_dev->vm = pci->vm;
     pci_dev->priv_data = priv_data;
 
+    INIT_LIST_HEAD(&(pci_dev->cfg_hooks));
+    INIT_LIST_HEAD(&(pci_dev->capabilities));
+
+    
+    {
+       // locate APIC for MSI/MSI-X
+       pci_dev->apic_dev = v3_find_dev(pci->vm, "apic");
+    }
+
     // register update callbacks
-    pci_dev->config_update = config_update;
+    pci_dev->config_write = config_write;
+    pci_dev->config_read = config_read;
     pci_dev->cmd_update = cmd_update;
     pci_dev->exp_rom_update = exp_rom_update;
 
 
+
+    if (config_read) {
+       int i = 0;
+
+       // Only 256 bytes for now, should expand it in the future
+       for (i = 0; i < 256; i++) {
+           config_read(pci_dev, i, &(pci_dev->config_space[i]), 1, pci_dev->priv_data);
+       }
+    }
+
+    V3_Print("Scanning for Capabilities\n");
+
+    // scan for caps
+    scan_pci_caps(pci_dev);
+
+    pci_dev->irq_type = IRQ_INTX;
+
+    V3_Print("Caps scanned\n");
+
+    // hook important regions
+    v3_pci_hook_config_range(pci_dev, 0x30, 4, exp_rom_write, NULL, NULL);  // ExpRom
+    v3_pci_hook_config_range(pci_dev, 0x04, 2, cmd_write, NULL, NULL);      // CMD Reg
+    // * Status resets
+    // * Drop BIST
+    // 
+
+    
+
     //copy bars
     for (i = 0; i < 6; i ++) {
        pci_dev->bar[i].type = bars[i].type;
@@ -1024,70 +1640,3 @@ struct pci_device * v3_pci_register_device(struct vm_device * pci,
     return pci_dev;
 }
 
-
-
-// if dev_num == -1, auto assign 
-struct pci_device * v3_pci_register_passthrough_device(struct vm_device * pci,
-                                                      int bus_num,
-                                                      int dev_num,
-                                                      int fn_num,
-                                                      const char * name,
-                                                      int (*config_write)(uint_t reg_num, void * src, uint_t length, void * private_data),
-                                                      int (*config_read)(uint_t reg_num, void * dst, uint_t length, void * private_data),
-                                                      void * private_data) {
-
-    struct pci_internal * pci_state = (struct pci_internal *)pci->private_data;
-    struct pci_bus * bus = &(pci_state->bus_list[bus_num]);
-    struct pci_device * pci_dev = NULL;
-
-    if (dev_num > MAX_BUS_DEVICES) {
-       PrintError("Requested Invalid device number (%d)\n", dev_num);
-       return NULL;
-    }
-
-    if (dev_num == PCI_AUTO_DEV_NUM) {
-       PrintDebug("Searching for free device number\n");
-       if ((dev_num = get_free_dev_num(bus)) == -1) {
-           PrintError("No more available PCI slots on bus %d\n", bus->bus_num);
-           return NULL;
-       }
-    }
-    
-    PrintDebug("Checking for PCI Device\n");
-
-    if (get_device(bus, dev_num, fn_num) != NULL) {
-       PrintError("PCI Device already registered at slot %d on bus %d\n", 
-                  dev_num, bus->bus_num);
-       return NULL;
-    }
-
-    
-    pci_dev = (struct pci_device *)V3_Malloc(sizeof(struct pci_device));
-
-    if (pci_dev == NULL) {
-       PrintError("Could not allocate pci device\n");
-       return NULL;
-    }
-
-    memset(pci_dev, 0, sizeof(struct pci_device));
-    
-    pci_dev->bus_num = bus_num;
-    pci_dev->dev_num = dev_num;
-    pci_dev->fn_num = fn_num;
-
-    strncpy(pci_dev->name, name, sizeof(pci_dev->name));
-    pci_dev->priv_data = private_data;
-
-    // register update callbacks
-    pci_dev->config_write = config_write;
-    pci_dev->config_read = config_read;
-
-    // add the device
-    add_device_to_bus(bus, pci_dev);
-
-#ifdef V3_CONFIG_DEBUG_PCI
-    pci_dump_state(pci_state);
-#endif
-
-    return pci_dev;
-}
index b8f921b..e8d72db 100644 (file)
@@ -588,7 +588,7 @@ static int pci_bar_write(int bar_num, uint32_t * src, void * private_data) {
 }
 
 
-static int pt_config_update(uint_t reg_num, void * src, uint_t length, void * private_data) {
+static int pt_config_update(struct pci_device * pci_dev, uint_t reg_num, void * src, uint_t length, void * private_data) {
     struct vm_device * dev = (struct vm_device *)private_data;
     struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data;
     union pci_addr_reg pci_addr = {state->phys_pci_addr.value};
@@ -732,6 +732,7 @@ static int setup_virt_pci_dev(struct v3_vm_info * vm_info, struct vm_device * de
                                     bus_num, -1, 0, 
                                     state->name, bars,
                                     pt_config_update,
+                                    NULL, 
                                     NULL,
                                     pt_exp_rom_write,               
                                     dev);
@@ -762,7 +763,7 @@ static int irq_handler(struct v3_vm_info * vm, struct v3_interrupt * intr, void
     struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data;
 
 
-    v3_pci_raise_irq(state->pci_bus, 0, state->pci_dev);
+    v3_pci_raise_irq(state->pci_bus, state->pci_dev, 0);
 
     V3_ACK_IRQ(intr->irq);
 
index b482b48..35925b5 100644 (file)
@@ -371,12 +371,16 @@ struct pirq_rc_reg {
 */
 
 
-static int raise_pci_irq(struct pci_device * pci_dev, void * dev_data) {
+static int raise_pci_irq(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec) {
     struct v3_southbridge * piix3 = dev_data;
     struct pci_device * piix3_pci = piix3->southbridge_pci;
     struct piix3_config_space * piix3_cfg = (struct piix3_config_space *)(piix3_pci->config_data);
     int intr_pin = pci_dev->config_header.intr_pin - 1;
     int irq_index = (intr_pin + pci_dev->dev_num - 1) & 0x3;
+    struct v3_irq irq; // Make a copy of the irq state because we will switch the irq number
+
+    irq.ack = vec->ack;
+    irq.private_data = vec->private_data;
 
     /*
     PrintError("Raising PCI dev %d intr %d via IOAPIC as IRQ %d and via PIRQ as IRQ %d on VM %p\n", 
@@ -388,7 +392,10 @@ static int raise_pci_irq(struct pci_device * pci_dev, void * dev_data) {
     // deliver first by PIRQ, if it exists
     //
     if (piix3_cfg->pirq_rc[irq_index] < 16) {
-       v3_raise_irq(piix3->vm, piix3_cfg->pirq_rc[irq_index] & 0xf);
+       irq.irq = piix3_cfg->pirq_rc[irq_index] & 0xf;
+
+       //      V3_Print("Raising PIIX IRQ %d\n", irq.irq);
+       v3_raise_acked_irq(piix3->vm, irq);
     } else {
       // not an error
     }
@@ -396,7 +403,9 @@ static int raise_pci_irq(struct pci_device * pci_dev, void * dev_data) {
     // deliver next via the PCI0 to ioapic mapping defined in the 
     // mptable (ioapic, pins 16->19 are used for PCI0)
     // ideally this would check to verify that an ioapic is actually available
-    v3_raise_irq(piix3->vm, 16+irq_index);
+    irq.irq = (irq_index + 1) + 16;
+    //    V3_Print("Raising PIIX IRQ (#2) %d\n", irq.irq);
+    v3_raise_acked_irq(piix3->vm, irq);
     
 
     return 0;
@@ -404,21 +413,26 @@ static int raise_pci_irq(struct pci_device * pci_dev, void * dev_data) {
 
 
 
-static int lower_pci_irq(struct pci_device * pci_dev, void * dev_data) {
+static int lower_pci_irq(struct pci_device * pci_dev, void * dev_data, struct v3_irq * vec) {
     struct v3_southbridge * piix3 = dev_data;
     struct pci_device * piix3_pci = piix3->southbridge_pci;
     struct piix3_config_space * piix3_cfg = (struct piix3_config_space *)(piix3_pci->config_data);
     int intr_pin = pci_dev->config_header.intr_pin - 1;
     int irq_index = (intr_pin + pci_dev->dev_num - 1) & 0x3;
-    
+    struct v3_irq irq; // Make a copy of the irq state because we will switch the irq number
+
+    irq.ack = vec->ack;
+    irq.private_data = vec->private_data;
     //    PrintError("Lowering PCI IRQ %d\n", piix3_cfg->pirq_rc[irq_index]);
 
     // First, lower the pin on the ioapic
-    v3_lower_irq(piix3->vm, irq_index+16);
+    irq.irq = (irq_index + 1) + 16;
+    v3_lower_acked_irq(piix3->vm, irq);
     
     // Next, lower whatever we asserted by the PIRQs
     if (piix3_cfg->pirq_rc[irq_index] < 16) {
-       v3_lower_irq(piix3->vm, piix3_cfg->pirq_rc[irq_index] & 0xf);
+       irq.irq = piix3_cfg->pirq_rc[irq_index] & 0xf;
+       v3_lower_acked_irq(piix3->vm, irq);
     } else {
       // not an error
     }
@@ -457,7 +471,7 @@ static int setup_pci(struct v3_southbridge * piix3) {
     pci_dev = v3_pci_register_device(piix3->pci_bus, PCI_MULTIFUNCTION, 
                                     bus_num, -1, 0, 
                                     "PIIX3", bars, 
-                                    NULL, NULL, NULL, piix3);
+                                    NULL, NULL, NULL, NULL, piix3);
     if (pci_dev == NULL) {
        PrintError("Could not register PCI Device for PIIX3\n");
        return -1;
index 33a5cfa..fde3ef9 100644 (file)
@@ -582,7 +582,7 @@ static inline void rtl8139_update_irq(struct rtl8139_state * nic_state) {
     int isr = ((nic_state->regs.isr & nic_state->regs.imr) & 0xffff);
 
     if(isr & 0xffff){
-       v3_pci_raise_irq(nic_state->pci_bus, 0, nic_state->pci_dev);
+       v3_pci_raise_irq(nic_state->pci_bus, nic_state->pci_dev, 0);
        nic_state->statistic.tx_interrupts ++;
     }
 }
@@ -1732,7 +1732,7 @@ static int register_dev(struct rtl8139_state * nic_state)  {
 
     pci_dev = v3_pci_register_device(nic_state->pci_bus, PCI_STD_DEVICE, 0, -1, 0, 
                                         "RTL8139", bars,
-                                        NULL, NULL, NULL, nic_state);
+                                    NULL, NULL, NULL, NULL, nic_state);
 
 
     if (pci_dev == NULL) {