diff options
author | Michael S. Tsirkin <mst@redhat.com> | 2010-12-08 17:46:25 +0900 |
---|---|---|
committer | Michael S. Tsirkin <mst@redhat.com> | 2010-12-09 12:48:26 +0200 |
commit | c3f33667a64a6de0b92106c862247d97d81490ef (patch) | |
tree | 508fe4cb02ac8d22f93551d13d50c85e1ca5c1d4 /hw/pcie_aer.c | |
parent | 624c716cc576ec5e6bed50da3deb45aa9537cacd (diff) |
pci/aer: fix error injection
Fix the injection logic upon aer message to follow 6.2.4.1.2 more
closely: specifically only send an msi interrupt when the logical or of
the enabled bits changed, not when a bit which was previously clear
becomes set.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
Diffstat (limited to 'hw/pcie_aer.c')
-rw-r--r-- | hw/pcie_aer.c | 51 |
1 files changed, 35 insertions, 16 deletions
diff --git a/hw/pcie_aer.c b/hw/pcie_aer.c index dac27c39fe..9505c93343 100644 --- a/hw/pcie_aer.c +++ b/hw/pcie_aer.c @@ -257,6 +257,22 @@ static unsigned int pcie_aer_root_get_vector(PCIDevice *dev) return (root_status & PCI_ERR_ROOT_IRQ) >> PCI_ERR_ROOT_IRQ_SHIFT; } +/* Given a status register, get corresponding bits in the command register */ +static uint32_t pcie_aer_status_to_cmd(uint32_t status) +{ + uint32_t cmd = 0; + if (status & PCI_ERR_ROOT_COR_RCV) { + cmd |= PCI_ERR_ROOT_CMD_COR_EN; + } + if (status & PCI_ERR_ROOT_NONFATAL_RCV) { + cmd |= PCI_ERR_ROOT_CMD_NONFATAL_EN; + } + if (status & PCI_ERR_ROOT_FATAL_RCV) { + cmd |= PCI_ERR_ROOT_CMD_FATAL_EN; + } + return cmd; +} + /* * return value: * true: error message is sent up @@ -272,14 +288,14 @@ static bool pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) uint16_t cmd; uint8_t *aer_cap; uint32_t root_cmd; - uint32_t root_status; + uint32_t root_status, prev_status; bool msi_trigger; msg_sent = false; cmd = pci_get_word(dev->config + PCI_COMMAND); aer_cap = dev->config + dev->exp.aer_cap; root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); - root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); + prev_status = root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); msi_trigger = false; if (cmd & PCI_COMMAND_SERR) { @@ -337,20 +353,23 @@ static bool pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) } pci_set_long(aer_cap + PCI_ERR_ROOT_STATUS, root_status); - if (root_cmd & msg->severity) { - /* 6.2.4.1.2 Interrupt Generation */ - if (msix_enabled(dev)) { - if (msi_trigger) { - msix_notify(dev, pcie_aer_root_get_vector(dev)); - } - } else if (msi_enabled(dev)) { - if (msi_trigger) { - msi_notify(dev, pcie_aer_root_get_vector(dev)); - } - } else { - qemu_set_irq(dev->irq[dev->exp.aer_intx], 1); - } - msg_sent = true; + /* 6.2.4.1.2 Interrupt Generation */ + /* All the above did was set some bits in the status register. + * Specifically these that match message severity. + * The below code relies on this fact. */ + if (!(root_cmd & msg->severity) || + (pcie_aer_status_to_cmd(prev_status) & root_cmd)) { + /* Condition is not being set or was already true so nothing to do. */ + return msg_sent; + } + + msg_sent = true; + if (msix_enabled(dev)) { + msix_notify(dev, pcie_aer_root_get_vector(dev)); + } else if (msi_enabled(dev)) { + msi_notify(dev, pcie_aer_root_get_vector(dev)); + } else { + qemu_set_irq(dev->irq[dev->exp.aer_intx], 1); } return msg_sent; } |