From cbc19837c8d5a679d59b95ac4208b85edd0742e9 Mon Sep 17 00:00:00 2001 From: Alexander Kudryavtsev Date: Fri, 23 Sep 2011 23:15:45 +0400 Subject: [PATCH 06/32] Update for v3_pci_raise_irq, v3_raise_irq, router_ops interfaces to support quialification of IRQ. It allows smoother integration of MSI router and provides possibility to implement deferred acknowledge of passed through interrupt. --- palacios/include/devices/pci.h | 27 +++++++++- palacios/include/devices/pci_msi_router.h | 6 -- palacios/include/palacios/vmm_intr.h | 17 ++++++- palacios/src/devices/8259a.c | 7 ++- palacios/src/devices/io_apic.c | 7 ++- palacios/src/devices/pci.c | 21 ++++++- palacios/src/devices/pci_msi_router.c | 58 ++++++--------------- palacios/src/devices/pci_passthrough.c | 11 ++-- palacios/src/devices/piix3.c | 82 ++++++++++++++++++++-------- palacios/src/palacios/vmm_intr.c | 21 +++++++- 10 files changed, 172 insertions(+), 85 deletions(-) diff --git a/palacios/include/devices/pci.h b/palacios/include/devices/pci.h index edc3e36..92a20d6 100644 --- a/palacios/include/devices/pci.h +++ b/palacios/include/devices/pci.h @@ -26,6 +26,7 @@ #include +#include #include #include @@ -148,14 +149,27 @@ struct pci_device { int is_passthrough; // true if this device is real device passed through into guest + enum {PCI_IRQ_INTX, PCI_IRQ_MSI, PCI_IRQ_MSIX} irq_type; int msi_handle; // controlled by MSI router. 0 - MSI not available for device; > 0 - MSI available, index used by MSI router void * priv_data; }; +struct irq_qualifier { + union irq_qualifier_flags { + uint8_t val; + struct { + uint8_t valid: 1; + uint8_t passed_through: 1; + }; + } flags; + + uint8_t vector; +}; + 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 (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct irq_qualifier iq), int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data), void * dev_data); @@ -163,6 +177,17 @@ int v3_pci_set_irq_bridge(struct vm_device * pci_bus, int bus_num, 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); + +/** + * Raise IRQ via PCI bus with clarification. + * + * \param iq Interrupt qualifier. It is processed only if flags.valid is set. + * If flags.passed_through is set, vector field contains host IRQ which was + * passed through. Else vector may contain target vector to raise if device + * own multiple vectors. + */ +int v3_pci_raise_qualified_irq(struct vm_device * pci_bus, int bus_num, struct pci_device * dev, struct irq_qualifier iq); + void *v3_pci_get_msi_router(struct vm_device *pci_bus); struct pci_device * diff --git a/palacios/include/devices/pci_msi_router.h b/palacios/include/devices/pci_msi_router.h index 53a2833..bc6f0ed 100644 --- a/palacios/include/devices/pci_msi_router.h +++ b/palacios/include/devices/pci_msi_router.h @@ -43,12 +43,6 @@ struct msi_info { int v3_msi_register_device(struct pci_device *dev, int msi_cap, void *private_data); int v3_msi_capability_write(struct pci_device *dev, uint_t reg_num, uint_t length, void *private_data); -// return: -1 - error, 0 - no MSI for this device, 1 - MSI is available, *vector contains vector. -int v3_msi_get_vector(struct pci_device *dev, int *vector, void *private_data); - -// when multiple vectors are assigned, this is the way for device to tell which vector to raise -int v3_msi_set_vector_hint(struct pci_device *dev, int raise_vector_hint, void *private_data); - int v3_msi_get_info(struct pci_device *dev, struct msi_info *info, void *private_data); diff --git a/palacios/include/palacios/vmm_intr.h b/palacios/include/palacios/vmm_intr.h index adf2aa4..f3c7253 100644 --- a/palacios/include/palacios/vmm_intr.h +++ b/palacios/include/palacios/vmm_intr.h @@ -82,6 +82,20 @@ int v3_lower_virq(struct guest_info * info, int irq); int v3_raise_irq(struct v3_vm_info * vm, int irq); int v3_lower_irq(struct v3_vm_info * vm, int irq); +struct irq_data { + uint8_t irq; + union irq_data_flags { + uint8_t val; + struct { + uint8_t valid: 1; + uint8_t passed_through: 1; + uint8_t uses_irqline: 1; // IRQs which do not ise IRQ line (MSI and MSI-X) are not processed by MSI router + }; + } flags; + uint8_t host_vector; +}; + +int v3_raise_qualified_irq(struct v3_vm_info * vm, struct irq_data irq_data); int v3_raise_swintr(struct guest_info * core, uint8_t vector); @@ -92,8 +106,9 @@ struct intr_ctrl_ops { int (*begin_irq)(struct guest_info * info, void * private_data, int irq); }; + struct intr_router_ops { - int (*raise_intr)(struct v3_vm_info * vm, void * private_data, int irq); + int (*raise_intr)(struct v3_vm_info * vm, void * private_data, struct irq_data irq); int (*lower_intr)(struct v3_vm_info * vm, void * private_data, int irq); }; diff --git a/palacios/src/devices/8259a.c b/palacios/src/devices/8259a.c index c24402d..80bfb7e 100644 --- a/palacios/src/devices/8259a.c +++ b/palacios/src/devices/8259a.c @@ -197,8 +197,13 @@ static void DumpPICState(struct pic_internal *p) } -static int pic_raise_intr(struct v3_vm_info * vm, void * private_data, int irq) { +static int pic_raise_intr(struct v3_vm_info * vm, void * private_data, struct irq_data irq_data) { struct pic_internal * state = (struct pic_internal*)private_data; + int irq = irq_data.irq; + + // check whether this IRQ can be processed by 8259 + if(irq_data.flags.valid && !irq_data.flags.uses_irqline) + return -1; if (irq == 2) { irq = 9; diff --git a/palacios/src/devices/io_apic.c b/palacios/src/devices/io_apic.c index 3579fde..ce48ac8 100644 --- a/palacios/src/devices/io_apic.c +++ b/palacios/src/devices/io_apic.c @@ -263,9 +263,14 @@ static int ioapic_write(struct guest_info * core, addr_t guest_addr, void * src, } -static int ioapic_raise_irq(struct v3_vm_info * vm, void * private_data, int irq) { +static int ioapic_raise_irq(struct v3_vm_info * vm, void * private_data, struct irq_data irq_data) { struct io_apic_state * ioapic = (struct io_apic_state *)(private_data); struct redir_tbl_entry * irq_entry = NULL; + int irq = irq_data.irq; + + // check whether this IRQ can be processed by IOAPIC + if(irq_data.flags.valid && !irq_data.flags.uses_irqline) + return -1; if (irq > 24) { PrintDebug("ioapic %u: IRQ out of range of IO APIC\n", ioapic->ioapic_id.id); diff --git a/palacios/src/devices/pci.c b/palacios/src/devices/pci.c index 9ddbe5d..355ea09 100644 --- a/palacios/src/devices/pci.c +++ b/palacios/src/devices/pci.c @@ -82,7 +82,7 @@ struct pci_bus { uint8_t dev_map[MAX_BUS_DEVICES / 8]; - int (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data); + int (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct irq_qualifier iq); int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data); void * irq_dev_data; }; @@ -801,7 +801,7 @@ 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 (*raise_pci_irq)(struct pci_device * pci_dev, void * dev_data, struct irq_qualifier iq), int (*lower_pci_irq)(struct pci_device * pci_dev, void * dev_data), void * priv_data) { struct pci_internal * pci_state = (struct pci_internal *)pci_bus->private_data; @@ -818,9 +818,22 @@ int v3_pci_raise_irq(struct vm_device * pci_bus, int bus_num, struct pci_device struct pci_internal * pci_state = (struct pci_internal *)pci_bus->private_data; struct pci_bus * bus = &(pci_state->bus_list[bus_num]); - return bus->raise_pci_irq(dev, bus->irq_dev_data); + // set irq qualifier to invalid + struct irq_qualifier iq; + iq.flags.val = 0; + iq.vector = 0; + + return bus->raise_pci_irq(dev, bus->irq_dev_data, iq); } +int v3_pci_raise_qualified_irq(struct vm_device * pci_bus, int bus_num, struct pci_device * dev, struct irq_qualifier iq) { + struct pci_internal * pci_state = (struct pci_internal *)pci_bus->private_data; + struct pci_bus * bus = &(pci_state->bus_list[bus_num]); + + return bus->raise_pci_irq(dev, bus->irq_dev_data, iq); +} + + int v3_pci_lower_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]); @@ -912,6 +925,7 @@ struct pci_device * v3_pci_register_device(struct vm_device * pci, pci_dev->cmd_update = cmd_update; pci_dev->exp_rom_update = exp_rom_update; + pci_dev->irq_type = PCI_IRQ_INTX; //copy bars for (i = 0; i < 6; i ++) { @@ -1043,6 +1057,7 @@ struct pci_device * v3_pci_register_passthrough_device(struct vm_device * pci, pci_dev->config_read = config_read; pci_dev->exp_rom_update = exp_rom_update; + pci_dev->irq_type = PCI_IRQ_INTX; //copy bars for (i = 0; i < 6; i ++) { diff --git a/palacios/src/devices/pci_msi_router.c b/palacios/src/devices/pci_msi_router.c index cd049f7..7616e49 100644 --- a/palacios/src/devices/pci_msi_router.c +++ b/palacios/src/devices/pci_msi_router.c @@ -56,7 +56,7 @@ struct msi_desc { struct pci_device *dev; }; -static int msi_raise_intr(struct v3_vm_info *vm, void *private_data, int irq); +static int msi_raise_intr(struct v3_vm_info *vm, void *private_data, struct irq_data irq_data); struct msi_router_state { char *name; @@ -179,55 +179,18 @@ static int msi_update_mode(struct msi_router_state *state, int msi_dev_index) { } PrintDebug(" enabled %d vectors starting at %x, device index %d\n", md->vec_count, md->vector, msi_dev_index); + md->dev->irq_type = PCI_IRQ_MSI; } else { if (md->vector) for(i = md->vector; i < md->vector + md->vec_count; ++i) { state->vector_to_index[i] = -1; } PrintDebug(" disabled %d vectors starting at %x, device index %d\n", md->vec_count, md->vector, msi_dev_index); + md->dev->irq_type = PCI_IRQ_INTX; } return 0; } -int v3_msi_get_vector(struct pci_device *dev, int *vector, void *private_data) { - if(!private_data || !dev->msi_handle) return 0; - - struct msi_router_state *state = (struct msi_router_state*)private_data; - - V3_ASSERT(dev->msi_handle > 0 && dev->msi_handle <= MSI_DEV_COUNT && state->msi[dev->msi_handle - 1].dev == dev); - - int index = dev->msi_handle - 1; - struct msi_desc *md = &state->msi[index]; - - if(!md->control->enable) { - return 0; - } - - *vector = md->raise_vector_hint; - return 1; -} - -int v3_msi_set_vector_hint(struct pci_device *dev, int raise_vector_hint, void *private_data) { - if(!private_data || !dev->msi_handle) return 0; - - struct msi_router_state *state = (struct msi_router_state*)private_data; - - V3_ASSERT(dev->msi_handle > 0 && dev->msi_handle <= MSI_DEV_COUNT && state->msi[dev->msi_handle - 1].dev == dev); - - int index = dev->msi_handle - 1; - struct msi_desc *md = &state->msi[index]; - - if(!md->control->enable) { - return 0; - } - - md->raise_vector_hint = raise_vector_hint; - V3_ASSERT(md->raise_vector_hint >= md->vector && md->raise_vector_hint <= md->vector + md->vec_count); - - //PrintDebug("MSI router set vector hint (0x%x) for device %s\n", raise_vector_hint, dev->name); - return 0; -} - int v3_msi_get_info(struct pci_device *dev, struct msi_info *info, void *private_data) { V3_ASSERT(private_data && info); struct msi_router_state *state = (struct msi_router_state*)private_data; @@ -351,7 +314,13 @@ int v3_msi_capability_write(struct pci_device *dev, uint_t reg_num, uint_t lengt m = 1 << i; if(!(*md->mask_bits & m) && (*md->pending_bits & m)) { PrintDebug("Vector %x is unmasked and pending; raising it.\n", i + md->vector); - return msi_raise_intr(state->vm, state, i + md->vector); + struct irq_data id; + id.flags.val = 0; + id.flags.passed_through = 0; + id.flags.uses_irqline = 0; + id.flags.valid = 1; + id.irq = i + md->vector; + return msi_raise_intr(state->vm, state, id); } } } @@ -364,8 +333,13 @@ int v3_msi_capability_write(struct pci_device *dev, uint_t reg_num, uint_t lengt } -static int msi_raise_intr(struct v3_vm_info *vm, void *private_data, int irq) { +static int msi_raise_intr(struct v3_vm_info *vm, void *private_data, struct irq_data irq_data) { struct msi_router_state *state = (struct msi_router_state*)private_data; + int irq = irq_data.irq; + + // check whether this IRQ can be processed by MSI router + if(!irq_data.flags.valid || irq_data.flags.uses_irqline) + return -1; V3_ASSERT(irq < MAX_IRQ && irq >= 0); diff --git a/palacios/src/devices/pci_passthrough.c b/palacios/src/devices/pci_passthrough.c index ba12e34..f4c207b 100644 --- a/palacios/src/devices/pci_passthrough.c +++ b/palacios/src/devices/pci_passthrough.c @@ -674,15 +674,14 @@ static int pci_bar_write(int bar_num, uint32_t * src, void * private_data) { static int irq_handler(struct v3_vm_info * vm, struct v3_interrupt * intr, void * private_data) { struct vm_device * dev = (struct vm_device *)private_data; struct pt_dev_state * state = (struct pt_dev_state *)dev->private_data; + struct irq_qualifier iq; + iq.vector = intr->irq; + iq.flags.valid = iq.flags.passed_through = 1; - - //PrintError("passthrough host irq %d\n", intr->irq); - if(v3_msi_set_vector_hint(state->pci_dev, intr->irq, state->msi.router_private)) return -1; - if(v3_pci_raise_irq(state->pci_bus, 0, state->pci_dev)) return -1; + if(v3_pci_raise_qualified_irq(state->pci_bus, 0, state->pci_dev, iq)) return -1; V3_ACK_IRQ(intr->irq); - return 0; } @@ -1001,6 +1000,8 @@ static int setup_msi_passthrough(struct v3_vm_info * vm_info, struct pt_dev_stat struct pci_device *dev = state->pci_dev; uint8_t unused; + dev->msi_handle = 0; + // search for PCI MSI capability and remember its offset. int msi_cap = pci_find_capability_and_pointer(dev, PCI_CAP_ID_MSI, &unused); if(!msi_cap) diff --git a/palacios/src/devices/piix3.c b/palacios/src/devices/piix3.c index 40085dc..1200fa3 100644 --- a/palacios/src/devices/piix3.c +++ b/palacios/src/devices/piix3.c @@ -371,37 +371,71 @@ 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 irq_qualifier iq) { 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; - - int guest_irq = -1, msi_vector; - - int is_msi = v3_msi_get_vector(pci_dev, &msi_vector, v3_pci_get_msi_router(piix3->pci_bus)); - if(is_msi < 0) return -1; - - if(is_msi == 1) - guest_irq = msi_vector; - else if(pci_dev->is_passthrough) - guest_irq = pci_dev->config_header.intr_line; - else if (piix3_cfg->pirq_rc[irq_index] < 16) - guest_irq = piix3_cfg->pirq_rc[irq_index] & 0xf; - else { - PrintError("Tried to raise interrupt on disabled PIRQ entry (%d)\n", irq_index); - PrintError("\tpirq_rc values: 0=0x%x, 1=0x%x, 2=0x%x, 3=0x%x\n", - piix3_cfg->pirq_rc[0], piix3_cfg->pirq_rc[1], - piix3_cfg->pirq_rc[2], piix3_cfg->pirq_rc[3]); - return -1; + int guest_irq = -1; + + if(iq.flags.valid) { + struct irq_data irq; + irq.flags.val = 0; + irq.flags.valid = 1; + irq.flags.passed_through = iq.flags.passed_through; + irq.flags.uses_irqline = pci_dev->irq_type == PCI_IRQ_INTX; + + if(iq.flags.passed_through) { + // passed through device + irq.host_vector = iq.vector; + if(pci_dev->irq_type == PCI_IRQ_INTX) { + guest_irq = pci_dev->config_header.intr_line; + } else { + // MSI or MSI-X + guest_irq = iq.vector; + } + } else { + // virtual device + if(pci_dev->irq_type == PCI_IRQ_INTX) { + if (piix3_cfg->pirq_rc[irq_index] < 16) + guest_irq = piix3_cfg->pirq_rc[irq_index] & 0xf; + else { + PrintError("Tried to raise interrupt on disabled PIRQ entry (%d)\n", irq_index); + PrintError("\tpirq_rc values: 0=0x%x, 1=0x%x, 2=0x%x, 3=0x%x\n", + piix3_cfg->pirq_rc[0], piix3_cfg->pirq_rc[1], + piix3_cfg->pirq_rc[2], piix3_cfg->pirq_rc[3]); + return -1; + } + } else { + // MSI or MSI-X + guest_irq = iq.vector; + } + } + irq.irq = guest_irq; + + return v3_raise_qualified_irq(piix3->vm, irq); + } else { + // virtual INTx device + if(pci_dev->irq_type == PCI_IRQ_INTX) { + if (piix3_cfg->pirq_rc[irq_index] < 16) + guest_irq = piix3_cfg->pirq_rc[irq_index] & 0xf; + else { + PrintError("Tried to raise interrupt on disabled PIRQ entry (%d)\n", irq_index); + PrintError("\tpirq_rc values: 0=0x%x, 1=0x%x, 2=0x%x, 3=0x%x\n", + piix3_cfg->pirq_rc[0], piix3_cfg->pirq_rc[1], + piix3_cfg->pirq_rc[2], piix3_cfg->pirq_rc[3]); + return -1; + } + } else { + PrintError("Don't know how to raise interrupt type %d without additional information\n", pci_dev->irq_type); + return -1; + } + + return v3_raise_irq(piix3->vm, guest_irq); } - //PrintError("Raising PCI %sIRQ %d, %p\n", is_msi ? "MSI ":"", guest_irq, piix3->vm); - v3_raise_irq(piix3->vm, guest_irq); - - return 0; + return -1; } diff --git a/palacios/src/palacios/vmm_intr.c b/palacios/src/palacios/vmm_intr.c index 9010a4c..a921da2 100644 --- a/palacios/src/palacios/vmm_intr.c +++ b/palacios/src/palacios/vmm_intr.c @@ -292,12 +292,16 @@ int v3_lower_irq(struct v3_vm_info * vm, int irq) { int v3_raise_irq(struct v3_vm_info * vm, int irq) { struct intr_router * router = NULL; struct v3_intr_routers * routers = &(vm->intr_routers); + struct irq_data irq_data; + irq_data.irq = irq; + irq_data.flags.val = 0; + irq_data.host_vector = 0; // PrintDebug("[v3_raise_irq (%d)]\n", irq); addr_t irq_state = v3_lock_irqsave(routers->irq_lock); list_for_each_entry(router, &(routers->router_list), router_node) { - router->router_ops->raise_intr(vm, router->priv_data, irq); + router->router_ops->raise_intr(vm, router->priv_data, irq_data); } v3_unlock_irqrestore(routers->irq_lock, irq_state); @@ -305,6 +309,21 @@ int v3_raise_irq(struct v3_vm_info * vm, int irq) { return 0; } +int v3_raise_qualified_irq(struct v3_vm_info * vm, struct irq_data irq_data) { + struct intr_router * router = NULL; + struct v3_intr_routers * routers = &(vm->intr_routers); + + // PrintDebug("[v3_raise_irq (%d)]\n", irq); + addr_t irq_state = v3_lock_irqsave(routers->irq_lock); + + list_for_each_entry(router, &(routers->router_list), router_node) { + router->router_ops->raise_intr(vm, router->priv_data, irq_data); + } + + v3_unlock_irqrestore(routers->irq_lock, irq_state); + + return 0; +} -- 1.7.5.4