aboutsummaryrefslogtreecommitdiff
path: root/hw/pci
diff options
context:
space:
mode:
Diffstat (limited to 'hw/pci')
-rw-r--r--hw/pci/pci-internal.h1
-rw-r--r--hw/pci/pci.c57
-rw-r--r--hw/pci/pcie_aer.c14
-rw-r--r--hw/pci/pcie_port.c46
4 files changed, 116 insertions, 2 deletions
diff --git a/hw/pci/pci-internal.h b/hw/pci/pci-internal.h
index 2ea356b..a7d6d8a 100644
--- a/hw/pci/pci-internal.h
+++ b/hw/pci/pci-internal.h
@@ -20,6 +20,5 @@ void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent);
int pcie_aer_parse_error_string(const char *error_name,
uint32_t *status, bool *correctable);
-int pcie_aer_inject_error(PCIDevice *dev, const PCIEAERErr *err);
#endif
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index 034fe49..def5000 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -95,6 +95,21 @@ static const VMStateDescription vmstate_pcibus = {
}
};
+static gint g_cmp_uint32(gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ return a - b;
+}
+
+static GSequence *pci_acpi_index_list(void)
+{
+ static GSequence *used_acpi_index_list;
+
+ if (!used_acpi_index_list) {
+ used_acpi_index_list = g_sequence_new(NULL);
+ }
+ return used_acpi_index_list;
+}
+
static void pci_init_bus_master(PCIDevice *pci_dev)
{
AddressSpace *dma_as = pci_device_iommu_address_space(pci_dev);
@@ -1246,6 +1261,17 @@ static void pci_qdev_unrealize(DeviceState *dev)
do_pci_unregister_device(pci_dev);
pci_dev->msi_trigger = NULL;
+
+ /*
+ * clean up acpi-index so it could reused by another device
+ */
+ if (pci_dev->acpi_index) {
+ GSequence *used_indexes = pci_acpi_index_list();
+
+ g_sequence_remove(g_sequence_lookup(used_indexes,
+ GINT_TO_POINTER(pci_dev->acpi_index),
+ g_cmp_uint32, NULL));
+ }
}
void pci_register_bar(PCIDevice *pci_dev, int region_num,
@@ -2005,6 +2031,8 @@ PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn)
return bus->devices[devfn];
}
+#define ONBOARD_INDEX_MAX (16 * 1024 - 1)
+
static void pci_qdev_realize(DeviceState *qdev, Error **errp)
{
PCIDevice *pci_dev = (PCIDevice *)qdev;
@@ -2014,6 +2042,35 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp)
bool is_default_rom;
uint16_t class_id;
+ /*
+ * capped by systemd (see: udev-builtin-net_id.c)
+ * as it's the only known user honor it to avoid users
+ * misconfigure QEMU and then wonder why acpi-index doesn't work
+ */
+ if (pci_dev->acpi_index > ONBOARD_INDEX_MAX) {
+ error_setg(errp, "acpi-index should be less or equal to %u",
+ ONBOARD_INDEX_MAX);
+ return;
+ }
+
+ /*
+ * make sure that acpi-index is unique across all present PCI devices
+ */
+ if (pci_dev->acpi_index) {
+ GSequence *used_indexes = pci_acpi_index_list();
+
+ if (g_sequence_lookup(used_indexes,
+ GINT_TO_POINTER(pci_dev->acpi_index),
+ g_cmp_uint32, NULL)) {
+ error_setg(errp, "a PCI device with acpi-index = %" PRIu32
+ " already exist", pci_dev->acpi_index);
+ return;
+ }
+ g_sequence_insert_sorted(used_indexes,
+ GINT_TO_POINTER(pci_dev->acpi_index),
+ g_cmp_uint32, NULL);
+ }
+
if (pci_dev->romsize != -1 && !is_power_of_2(pci_dev->romsize)) {
error_setg(errp, "ROM size %u is not a power of two", pci_dev->romsize);
return;
diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c
index 9a19be4..103667c 100644
--- a/hw/pci/pcie_aer.c
+++ b/hw/pci/pcie_aer.c
@@ -112,6 +112,10 @@ int pcie_aer_init(PCIDevice *dev, uint8_t cap_ver, uint16_t offset,
pci_set_long(dev->w1cmask + offset + PCI_ERR_UNCOR_STATUS,
PCI_ERR_UNC_SUPPORTED);
+ pci_set_long(dev->config + offset + PCI_ERR_UNCOR_MASK,
+ PCI_ERR_UNC_MASK_DEFAULT);
+ pci_set_long(dev->wmask + offset + PCI_ERR_UNCOR_MASK,
+ PCI_ERR_UNC_SUPPORTED);
pci_set_long(dev->config + offset + PCI_ERR_UNCOR_SEVER,
PCI_ERR_UNC_SEVERITY_DEFAULT);
@@ -188,8 +192,16 @@ static void pcie_aer_update_uncor_status(PCIDevice *dev)
static bool
pcie_aer_msg_alldev(PCIDevice *dev, const PCIEAERMsg *msg)
{
+ uint16_t devctl = pci_get_word(dev->config + dev->exp.exp_cap +
+ PCI_EXP_DEVCTL);
if (!(pcie_aer_msg_is_uncor(msg) &&
- (pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_SERR))) {
+ (pci_get_word(dev->config + PCI_COMMAND) & PCI_COMMAND_SERR)) &&
+ !((msg->severity == PCI_ERR_ROOT_CMD_NONFATAL_EN) &&
+ (devctl & PCI_EXP_DEVCTL_NFERE)) &&
+ !((msg->severity == PCI_ERR_ROOT_CMD_COR_EN) &&
+ (devctl & PCI_EXP_DEVCTL_CERE)) &&
+ !((msg->severity == PCI_ERR_ROOT_CMD_FATAL_EN) &&
+ (devctl & PCI_EXP_DEVCTL_FERE))) {
return false;
}
diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c
index 65a397a..20ff2b3 100644
--- a/hw/pci/pcie_port.c
+++ b/hw/pci/pcie_port.c
@@ -161,6 +161,51 @@ PCIDevice *pcie_find_port_by_pn(PCIBus *bus, uint8_t pn)
return NULL;
}
+/* Find first port in devfn number order */
+PCIDevice *pcie_find_port_first(PCIBus *bus)
+{
+ int devfn;
+
+ for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+ PCIDevice *d = bus->devices[devfn];
+
+ if (!d || !pci_is_express(d) || !d->exp.exp_cap) {
+ continue;
+ }
+
+ if (object_dynamic_cast(OBJECT(d), TYPE_PCIE_PORT)) {
+ return d;
+ }
+ }
+
+ return NULL;
+}
+
+int pcie_count_ds_ports(PCIBus *bus)
+{
+ int dsp_count = 0;
+ int devfn;
+
+ for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+ PCIDevice *d = bus->devices[devfn];
+
+ if (!d || !pci_is_express(d) || !d->exp.exp_cap) {
+ continue;
+ }
+ if (object_dynamic_cast(OBJECT(d), TYPE_PCIE_PORT)) {
+ dsp_count++;
+ }
+ }
+ return dsp_count;
+}
+
+static bool pcie_slot_is_hotpluggbale_bus(HotplugHandler *plug_handler,
+ BusState *bus)
+{
+ PCIESlot *s = PCIE_SLOT(bus->parent);
+ return s->hotplug;
+}
+
static const TypeInfo pcie_port_type_info = {
.name = TYPE_PCIE_PORT,
.parent = TYPE_PCI_BRIDGE,
@@ -188,6 +233,7 @@ static void pcie_slot_class_init(ObjectClass *oc, void *data)
hc->plug = pcie_cap_slot_plug_cb;
hc->unplug = pcie_cap_slot_unplug_cb;
hc->unplug_request = pcie_cap_slot_unplug_request_cb;
+ hc->is_hotpluggable_bus = pcie_slot_is_hotpluggbale_bus;
}
static const TypeInfo pcie_slot_type_info = {