From: Jack Lange Date: Tue, 17 Apr 2012 18:46:24 +0000 (-0400) Subject: reworked PCI bus implmentation -- moved to mask based implementation -- added capabil... X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=commitdiff_plain;h=3938dc8f325981eab29bda77f43fa1be1d91c54f;p=palacios.git reworked PCI bus implmentation -- moved to mask based implementation -- added capability masking updated PCI interrupt interface --- diff --git a/palacios/include/devices/pci.h b/palacios/include/devices/pci.h index ff2e27d..2154ade 100644 --- a/palacios/include/devices/pci.h +++ b/palacios/include/devices/pci.h @@ -27,13 +27,22 @@ #include #include +#include #include - 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 diff --git a/palacios/include/devices/pci_types.h b/palacios/include/devices/pci_types.h index c3cd4cb..f6a98ce 100644 --- a/palacios/include/devices/pci_types.h +++ b/palacios/include/devices/pci_types.h @@ -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 diff --git a/palacios/src/devices/8259a.c b/palacios/src/devices/8259a.c index 0d55349..7e4b7a4 100644 --- a/palacios/src/devices/8259a.c +++ b/palacios/src/devices/8259a.c @@ -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 diff --git a/palacios/src/devices/apic.c b/palacios/src/devices/apic.c index 4823cb6..44ca08d 100644 --- a/palacios/src/devices/apic.c +++ b/palacios/src/devices/apic.c @@ -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; diff --git a/palacios/src/devices/cirrus_gfx_card.c b/palacios/src/devices/cirrus_gfx_card.c index e9d27be..ce25023 100644 --- a/palacios/src/devices/cirrus_gfx_card.c +++ b/palacios/src/devices/cirrus_gfx_card.c @@ -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); diff --git a/palacios/src/devices/i440fx.c b/palacios/src/devices/i440fx.c index 6c587d3..01e8088 100644 --- a/palacios/src/devices/i440fx.c +++ b/palacios/src/devices/i440fx.c @@ -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); diff --git a/palacios/src/devices/ide.c b/palacios/src/devices/ide.c index 285e534..e949fcb 100644 --- a/palacios/src/devices/ide.c +++ b/palacios/src/devices/ide.c @@ -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); diff --git a/palacios/src/devices/lnx_virtio_balloon.c b/palacios/src/devices/lnx_virtio_balloon.c index e936f9e..af3a903 100644 --- a/palacios/src/devices/lnx_virtio_balloon.c +++ b/palacios/src/devices/lnx_virtio_balloon.c @@ -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"); diff --git a/palacios/src/devices/lnx_virtio_blk.c b/palacios/src/devices/lnx_virtio_blk.c index fd9ec61..6cdfa06 100644 --- a/palacios/src/devices/lnx_virtio_blk.c +++ b/palacios/src/devices/lnx_virtio_blk.c @@ -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"); diff --git a/palacios/src/devices/lnx_virtio_console.c b/palacios/src/devices/lnx_virtio_console.c index f7f2a11..0afefb8 100644 --- a/palacios/src/devices/lnx_virtio_console.c +++ b/palacios/src/devices/lnx_virtio_console.c @@ -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"); diff --git a/palacios/src/devices/lnx_virtio_nic.c b/palacios/src/devices/lnx_virtio_nic.c index 400cb0c..9263988 100644 --- a/palacios/src/devices/lnx_virtio_nic.c +++ b/palacios/src/devices/lnx_virtio_nic.c @@ -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"); diff --git a/palacios/src/devices/lnx_virtio_sym.c b/palacios/src/devices/lnx_virtio_sym.c index f5b6501..010415f 100644 --- a/palacios/src/devices/lnx_virtio_sym.c +++ b/palacios/src/devices/lnx_virtio_sym.c @@ -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"); diff --git a/palacios/src/devices/lnx_virtio_symmod.c b/palacios/src/devices/lnx_virtio_symmod.c index 993dcb6..d8172df 100644 --- a/palacios/src/devices/lnx_virtio_symmod.c +++ b/palacios/src/devices/lnx_virtio_symmod.c @@ -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"); diff --git a/palacios/src/devices/lnx_virtio_vnet.c b/palacios/src/devices/lnx_virtio_vnet.c index 7b6d75c..5bf8ae0 100644 --- a/palacios/src/devices/lnx_virtio_vnet.c +++ b/palacios/src/devices/lnx_virtio_vnet.c @@ -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"); diff --git a/palacios/src/devices/ne2k.c b/palacios/src/devices/ne2k.c index b4796f7..3c8fa06 100644 --- a/palacios/src/devices/ne2k.c +++ b/palacios/src/devices/ne2k.c @@ -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) { diff --git a/palacios/src/devices/pci.c b/palacios/src/devices/pci.c index 05272a8..acac197 100644 --- a/palacios/src/devices/pci.c +++ b/palacios/src/devices/pci.c @@ -34,8 +34,11 @@ #include #include +#include +#include + #ifndef V3_CONFIG_DEBUG_PCI #undef PrintDebug @@ -53,6 +56,10 @@ // 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; -} diff --git a/palacios/src/devices/pci_passthrough.c b/palacios/src/devices/pci_passthrough.c index b8f921b..e8d72db 100644 --- a/palacios/src/devices/pci_passthrough.c +++ b/palacios/src/devices/pci_passthrough.c @@ -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); diff --git a/palacios/src/devices/piix3.c b/palacios/src/devices/piix3.c index b482b48..35925b5 100644 --- a/palacios/src/devices/piix3.c +++ b/palacios/src/devices/piix3.c @@ -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; diff --git a/palacios/src/devices/rtl8139.c b/palacios/src/devices/rtl8139.c index 33a5cfa..fde3ef9 100644 --- a/palacios/src/devices/rtl8139.c +++ b/palacios/src/devices/rtl8139.c @@ -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) {