From: Ian Jackson Date: Wed, 8 Apr 2009 16:54:45 +0000 (+0100) Subject: passthrough: Fix error handling when interrupt hypercall fails. X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=290b6d18585c887ff2cfa30b239aa2fecfcd0308;p=xenclient%2Fioemu.git passthrough: Fix error handling when interrupt hypercall fails. This patch fixes error handling when interrupt hypercall fails. This patch makes Interrupt Disable bit emulate type. The policy of this patch is [ in a comment at the top of pass-through.c ] Signed-off-by: Yuji Shimada --- diff --git a/hw/pass-through.c b/hw/pass-through.c index 95b4a472..11382fd4 100644 --- a/hw/pass-through.c +++ b/hw/pass-through.c @@ -22,6 +22,67 @@ * This file implements direct PCI assignment to a HVM guest */ +/* + * Interrupt Disable policy: + * + * INTx interrupt: + * Initialize(register_real_device) + * Map INTx(xc_physdev_map_pirq): + * + * - Set real Interrupt Disable bit to '1'. + * - Set machine_irq and assigned_device->machine_irq to '0'. + * * Don't bind INTx. + * + * Bind INTx(xc_domain_bind_pt_pci_irq): + * + * - Set real Interrupt Disable bit to '1'. + * - Unmap INTx. + * - Decrement mapped_machine_irq[machine_irq] + * - Set assigned_device->machine_irq to '0'. + * + * Write to Interrupt Disable bit by guest software(pt_cmd_reg_write) + * Write '0' + * msi_trans_en is false> + * - Set real bit to '0' if assigned_device->machine_irq isn't '0'. + * + * Write '1' + * msi_trans_en is false> + * - Set real bit to '1'. + * + * MSI-INTx translation. + * Initialize(xc_physdev_map_pirq_msi/pt_msi_setup) + * Bind MSI-INTx(xc_domain_bind_pt_irq) + * + * - Unmap MSI. + * + * - Set dev->msi->pirq to '-1'. + * + * - Do nothing. + * + * Write to Interrupt Disable bit by guest software(pt_cmd_reg_write) + * Write '0' + * msi_trans_en is true> + * - Set MSI Enable bit to '1'. + * + * Write '1' + * msi_trans_en is true> + * - Set MSI Enable bit to '0'. + * + * MSI interrupt: + * Initialize MSI register(pt_msi_setup, pt_msi_update) + * Bind MSI(xc_domain_update_msi_irq) + * + * - Unmap MSI. + * - Set dev->msi->pirq to '-1'. + * + * MSI-X interrupt: + * Initialize MSI-X register(pt_msix_update_one) + * Bind MSI-X(xc_domain_update_msi_irq) + * + * - Unmap MSI-X. + * - Set entry->pirq to '-1'. + */ + #include "pass-through.h" #include "pci/header.h" #include "pci/pci.h" @@ -208,7 +269,7 @@ static struct pt_reg_info_tbl pt_emu_reg_header0_tbl[] = { .size = 2, .init_val = 0x0000, .ro_mask = 0xF880, - .emu_mask = 0x0340, + .emu_mask = 0x0740, .init = pt_common_reg_init, .u.w.read = pt_cmd_reg_read, .u.w.write = pt_cmd_reg_write, @@ -2945,6 +3006,23 @@ static int pt_cmd_reg_write(struct pt_dev *ptdev, /* create value for writing to I/O device register */ throughable_mask = ~emu_mask & valid_mask; + + if (*value & PCI_COMMAND_DISABLE_INTx) + { + if (ptdev->msi_trans_en) + msi_set_enable(ptdev, 0); + else + throughable_mask |= PCI_COMMAND_DISABLE_INTx; + } + else + { + if (ptdev->msi_trans_en) + msi_set_enable(ptdev, 1); + else + if (ptdev->machine_irq) + throughable_mask |= PCI_COMMAND_DISABLE_INTx; + } + *value = PT_MERGE_VALUE(*value, dev_value, throughable_mask); /* mapping BAR */ @@ -3312,8 +3390,12 @@ static int pt_msgctrl_reg_write(struct pt_dev *ptdev, return 0; } } - pt_msi_update(ptdev); - + if (pt_msi_update(ptdev)) + { + *value &= ~PCI_MSI_FLAGS_ENABLE; + PT_LOG("Warning: Can not bind MSI for dev %x\n", pd->devfn); + return 0; + } ptdev->msi->flags &= ~MSI_FLAG_UNINIT; ptdev->msi->flags |= PT_MSI_MAPPED; } @@ -3543,6 +3625,11 @@ static int pt_cmd_reg_restore(struct pt_dev *ptdev, restorable_mask = reg->emu_mask & ~PCI_COMMAND_FAST_BACK; *value = PT_MERGE_VALUE(*value, dev_value, restorable_mask); + if (!ptdev->machine_irq) + *value |= PCI_COMMAND_DISABLE_INTx; + else + *value &= ~PCI_COMMAND_DISABLE_INTx; + return 0; } @@ -3756,8 +3843,14 @@ static struct pt_dev * register_real_device(PCIBus *e_bus, if ( rc ) { - /* TBD: unregister device in case of an error */ PT_LOG("Error: Mapping irq failed, rc = %d\n", rc); + + /* Disable PCI intx assertion (turn on bit10 of devctl) */ + pci_write_word(pci_dev, PCI_COMMAND, + *(uint16_t *)(&assigned_device->dev.config[PCI_COMMAND]) + | PCI_COMMAND_DISABLE_INTx); + machine_irq = 0; + assigned_device->machine_irq = 0; } else { @@ -3781,16 +3874,23 @@ static struct pt_dev * register_real_device(PCIBus *e_bus, e_device, e_intx); if ( rc < 0 ) { - /* TBD: unregister device in case of an error */ PT_LOG("Error: Binding of interrupt failed! rc=%d\n", rc); + + /* Disable PCI intx assertion (turn on bit10 of devctl) */ + pci_write_word(pci_dev, PCI_COMMAND, + *(uint16_t *)(&assigned_device->dev.config[PCI_COMMAND]) + | PCI_COMMAND_DISABLE_INTx); + mapped_machine_irq[machine_irq]--; + + if (mapped_machine_irq[machine_irq] == 0) + { + if (xc_physdev_unmap_pirq(xc_handle, domid, machine_irq)) + PT_LOG("Error: Unmapping of interrupt failed! rc=%d\n", + rc); + } + assigned_device->machine_irq = 0; } } - else { - /* Disable PCI intx assertion (turn on bit10 of devctl) */ - assigned_device->dev.config[0x05] |= 0x04; - pci_write_word(pci_dev, 0x04, - *(uint16_t *)(&assigned_device->dev.config[0x04])); - } out: PT_LOG("Real physical device %02x:%02x.%x registered successfuly!\n" diff --git a/hw/pass-through.h b/hw/pass-through.h index 3132387f..a503e809 100644 --- a/hw/pass-through.h +++ b/hw/pass-through.h @@ -51,6 +51,11 @@ /* because the current version of libpci (2.2.0) doesn't define these ID, * so we define Capability ID here. */ +#ifndef PCI_COMMAND_DISABLE_INTx +/* Disable INTx interrupts */ +#define PCI_COMMAND_DISABLE_INTx 0x400 +#endif + #ifndef PCI_CAP_ID_HOTPLUG /* SHPC Capability List Item reg group */ #define PCI_CAP_ID_HOTPLUG 0x0C diff --git a/hw/pt-msi.c b/hw/pt-msi.c index d28038a1..4a54ba33 100644 --- a/hw/pt-msi.c +++ b/hw/pt-msi.c @@ -22,7 +22,7 @@ #include "pt-msi.h" #include -static void msi_set_enable(struct pt_dev *dev, int en) +void msi_set_enable(struct pt_dev *dev, int en) { uint16_t val = 0; uint32_t address = 0; @@ -119,6 +119,7 @@ int pt_msi_update(struct pt_dev *d) uint8_t gvec = 0; uint32_t gflags = 0; uint64_t addr = 0; + int ret = 0; /* get vector, address, flags info, etc. */ gvec = d->msi->data & 0xFF; @@ -126,8 +127,20 @@ int pt_msi_update(struct pt_dev *d) gflags = __get_msi_gflags(d->msi->data, addr); PT_LOG("Update msi with pirq %x gvec %x\n", d->msi->pirq, gvec); - return xc_domain_update_msi_irq(xc_handle, domid, gvec, + + ret = xc_domain_update_msi_irq(xc_handle, domid, gvec, d->msi->pirq, gflags, 0); + + if (ret) + { + PT_LOG("Error: Binding of MSI failed.\n"); + + if (xc_physdev_unmap_pirq(xc_handle, domid, d->msi->pirq)) + PT_LOG("Error: Unmapping of MSI failed.\n"); + d->msi->pirq = -1; + return ret; + } + return 0; } void pt_msi_disable(struct pt_dev *dev) @@ -222,6 +235,10 @@ int pt_enable_msi_translate(struct pt_dev* dev) e_device, e_intx, 0)) { PT_LOG("Error: MSI-INTx translation bind failed, fallback\n"); + + if (xc_physdev_unmap_pirq(xc_handle, domid, dev->msi->pirq)) + PT_LOG("Error: Unmapping of MSI failed.\n"); + dev->msi->pirq = -1; return -1; } @@ -302,6 +319,10 @@ static int pt_msix_update_one(struct pt_dev *dev, int entry_nr) if ( ret ) { PT_LOG("Error: Updating msix irq info for entry %d\n", entry_nr); + + if (xc_physdev_unmap_pirq(xc_handle, domid, entry->pirq)) + PT_LOG("Error: Unmapping of MSI-X failed.\n"); + entry->pirq = -1; return ret; } diff --git a/hw/pt-msi.h b/hw/pt-msi.h index 585f6072..9664f890 100644 --- a/hw/pt-msi.h +++ b/hw/pt-msi.h @@ -76,6 +76,9 @@ #define GLFAGS_SHIFT_DELIV_MODE 12 #define GLFAGS_SHIFT_TRG_MODE 15 +void +msi_set_enable(struct pt_dev *dev, int en); + int pt_msi_setup(struct pt_dev *dev);