summaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/ppc/spapr_pci.c115
1 files changed, 102 insertions, 13 deletions
diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c
index 9d99bc0b4c..c8d7838385 100644
--- a/hw/ppc/spapr_pci.c
+++ b/hw/ppc/spapr_pci.c
@@ -1257,30 +1257,53 @@ static SpaprDrc *drc_from_dev(SpaprPhbState *phb, PCIDevice *dev)
return drc_from_devfn(phb, chassis, dev->devfn);
}
-static void add_drcs(SpaprPhbState *phb)
+static void add_drcs(SpaprPhbState *phb, PCIBus *bus, Error **errp)
{
+ Object *owner;
int i;
+ uint8_t chassis;
+ Error *local_err = NULL;
if (!phb->dr_enabled) {
return;
}
+ chassis = chassis_from_bus(bus, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (pci_bus_is_root(bus)) {
+ owner = OBJECT(phb);
+ } else {
+ owner = OBJECT(pci_bridge_get_device(bus));
+ }
+
for (i = 0; i < PCI_SLOT_MAX * PCI_FUNC_MAX; i++) {
- spapr_dr_connector_new(OBJECT(phb), TYPE_SPAPR_DRC_PCI,
- drc_id_from_devfn(phb, 0, i));
+ spapr_dr_connector_new(owner, TYPE_SPAPR_DRC_PCI,
+ drc_id_from_devfn(phb, chassis, i));
}
}
-static void remove_drcs(SpaprPhbState *phb)
+static void remove_drcs(SpaprPhbState *phb, PCIBus *bus, Error **errp)
{
int i;
+ uint8_t chassis;
+ Error *local_err = NULL;
if (!phb->dr_enabled) {
return;
}
+ chassis = chassis_from_bus(bus, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
for (i = PCI_SLOT_MAX * PCI_FUNC_MAX - 1; i >= 0; i--) {
- SpaprDrc *drc = drc_from_devfn(phb, 0, i);
+ SpaprDrc *drc = drc_from_devfn(phb, chassis, i);
if (drc) {
object_unparent(OBJECT(drc));
@@ -1325,6 +1348,7 @@ static int spapr_dt_pci_bus(SpaprPhbState *sphb, PCIBus *bus,
.sphb = sphb,
.err = 0,
};
+ int ret;
_FDT(fdt_setprop_cell(fdt, offset, "#address-cells",
RESOURCE_CELLS_ADDRESS));
@@ -1339,6 +1363,12 @@ static int spapr_dt_pci_bus(SpaprPhbState *sphb, PCIBus *bus,
}
}
+ ret = spapr_dt_drc(fdt, offset, OBJECT(bus->parent_dev),
+ SPAPR_DR_CONNECTOR_TYPE_PCI);
+ if (ret) {
+ return ret;
+ }
+
return offset;
}
@@ -1483,11 +1513,26 @@ int spapr_pci_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr,
return 0;
}
+static void spapr_pci_bridge_plug(SpaprPhbState *phb,
+ PCIBridge *bridge,
+ Error **errp)
+{
+ Error *local_err = NULL;
+ PCIBus *bus = pci_bridge_get_sec_bus(bridge);
+
+ add_drcs(phb, bus, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+}
+
static void spapr_pci_plug(HotplugHandler *plug_handler,
DeviceState *plugged_dev, Error **errp)
{
SpaprPhbState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler));
PCIDevice *pdev = PCI_DEVICE(plugged_dev);
+ PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(plugged_dev);
SpaprDrc *drc = drc_from_dev(phb, pdev);
Error *local_err = NULL;
PCIBus *bus = PCI_BUS(qdev_get_parent_bus(DEVICE(pdev)));
@@ -1509,6 +1554,14 @@ static void spapr_pci_plug(HotplugHandler *plug_handler,
g_assert(drc);
+ if (pc->is_bridge) {
+ spapr_pci_bridge_plug(phb, PCI_BRIDGE(plugged_dev), &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+
/* Following the QEMU convention used for PCIe multifunction
* hotplug, we do not allow functions to be hotplugged to a
* slot that already has function 0 present
@@ -1559,9 +1612,26 @@ out:
error_propagate(errp, local_err);
}
+static void spapr_pci_bridge_unplug(SpaprPhbState *phb,
+ PCIBridge *bridge,
+ Error **errp)
+{
+ Error *local_err = NULL;
+ PCIBus *bus = pci_bridge_get_sec_bus(bridge);
+
+ remove_drcs(phb, bus, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+}
+
static void spapr_pci_unplug(HotplugHandler *plug_handler,
DeviceState *plugged_dev, Error **errp)
{
+ PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(plugged_dev);
+ SpaprPhbState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler));
+
/* some version guests do not wait for completion of a device
* cleanup (generally done asynchronously by the kernel) before
* signaling to QEMU that the device is safe, but instead sleep
@@ -1573,6 +1643,16 @@ static void spapr_pci_unplug(HotplugHandler *plug_handler,
* an 'idle' state, as the device cleanup code expects.
*/
pci_device_reset(PCI_DEVICE(plugged_dev));
+
+ if (pc->is_bridge) {
+ Error *local_err = NULL;
+ spapr_pci_bridge_unplug(phb, PCI_BRIDGE(plugged_dev), &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ }
+ return;
+ }
+
object_property_set_bool(OBJECT(plugged_dev), false, "realized", NULL);
}
@@ -1593,6 +1673,7 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler,
g_assert(drc->dev == plugged_dev);
if (!spapr_drc_unplug_requested(drc)) {
+ PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(plugged_dev);
uint32_t slotnr = PCI_SLOT(pdev->devfn);
SpaprDrc *func_drc;
SpaprDrcClass *func_drck;
@@ -1606,6 +1687,10 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler,
return;
}
+ if (pc->is_bridge) {
+ error_setg(errp, "PCI: Hot unplug of PCI bridges not supported");
+ }
+
/* ensure any other present functions are pending unplug */
if (PCI_FUNC(pdev->devfn) == 0) {
for (i = 1; i < 8; i++) {
@@ -1658,6 +1743,7 @@ static void spapr_phb_unrealize(DeviceState *dev, Error **errp)
SpaprTceTable *tcet;
int i;
const unsigned windows_supported = spapr_phb_windows_supported(sphb);
+ Error *local_err = NULL;
spapr_phb_nvgpu_free(sphb);
@@ -1678,7 +1764,11 @@ static void spapr_phb_unrealize(DeviceState *dev, Error **errp)
}
}
- remove_drcs(sphb);
+ remove_drcs(sphb, phb->bus, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
for (i = PCI_NUM_PINS - 1; i >= 0; i--) {
if (sphb->lsi_table[i].irq) {
@@ -1721,6 +1811,7 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
uint64_t msi_window_size = 4096;
SpaprTceTable *tcet;
const unsigned windows_supported = spapr_phb_windows_supported(sphb);
+ Error *local_err = NULL;
if (!spapr) {
error_setg(errp, TYPE_SPAPR_PCI_HOST_BRIDGE " needs a pseries machine");
@@ -1873,7 +1964,6 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
/* Initialize the LSI table */
for (i = 0; i < PCI_NUM_PINS; i++) {
uint32_t irq = SPAPR_IRQ_PCI_LSI + sphb->index * PCI_NUM_PINS + i;
- Error *local_err = NULL;
if (smc->legacy_irq_allocation) {
irq = spapr_irq_findone(spapr, &local_err);
@@ -1898,7 +1988,11 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
}
/* allocate connectors for child PCI devices */
- add_drcs(sphb);
+ add_drcs(sphb, phb->bus, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ goto unrealize;
+ }
/* DMA setup */
for (i = 0; i < windows_supported; ++i) {
@@ -2314,11 +2408,6 @@ int spapr_dt_phb(SpaprPhbState *phb, uint32_t intc_phandle, void *fdt,
return ret;
}
- ret = spapr_dt_drc(fdt, bus_off, OBJECT(phb), SPAPR_DR_CONNECTOR_TYPE_PCI);
- if (ret) {
- return ret;
- }
-
spapr_phb_nvgpu_populate_dt(phb, fdt, bus_off, &errp);
if (errp) {
error_report_err(errp);