On 08/19/2014 10:21 AM, Michael Roth wrote: > This enables hotplug for PHB bridges. Upon hotplug we generate the > OF-nodes required by PAPR specification and IEEE 1275-1994 > "PCI Bus Binding to Open Firmware" for the device. > > We associate the corresponding FDT for these nodes with the DrcEntry > corresponding to the slot, which will be fetched via > ibm,configure-connector RTAS calls by the guest as described by PAPR > specification. The FDT is cleaned up in the case of unplug. > > Signed-off-by: Michael Roth <mdr...@linux.vnet.ibm.com> > --- > hw/ppc/spapr_pci.c | 235 > +++++++++++++++++++++++++++++++++++++++++++++++-- > include/hw/ppc/spapr.h | 1 + > 2 files changed, 228 insertions(+), 8 deletions(-) > > diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c > index 96a57be..23864ab 100644 > --- a/hw/ppc/spapr_pci.c > +++ b/hw/ppc/spapr_pci.c > @@ -87,6 +87,17 @@ > #define ENCODE_DRC_STATE(val, m, s) \ > (((uint32_t)(val) << (s)) & (uint32_t)(m)) > > +#define FDT_MAX_SIZE 0x10000 > +#define _FDT(exp) \ > + do { \ > + int ret = (exp); \ > + if (ret < 0) { \ > + return ret; \ > + } \ > + } while (0) > + > +static void spapr_drc_state_reset(sPAPRDrcEntry *drc_entry); > + > static sPAPRPHBState *find_phb(sPAPREnvironment *spapr, uint64_t buid) > { > sPAPRPHBState *sphb; > @@ -476,6 +487,22 @@ static void rtas_set_indicator(PowerPCCPU *cpu, > sPAPREnvironment *spapr, > /* encode the new value into the correct bit field */ > shift = INDICATOR_ISOLATION_SHIFT; > mask = INDICATOR_ISOLATION_MASK; > + if (drc_entry) { > + /* transition from unisolated to isolated for a hotplug slot > + * entails completion of guest-side device unplug/cleanup, so > + * we can now safely remove the device if qemu is waiting for > + * it to be released > + */ > + if (DECODE_DRC_STATE(*pind, mask, shift) != indicator_state) { > + if (indicator_state == 0 && drc_entry->awaiting_release) { > + /* device_del has been called and host is waiting > + * for guest to release/isolate device, go ahead > + * and remove it now > + */ > + spapr_drc_state_reset(drc_entry); > + } > + } > + } > break; > case 9002: /* DR */ > shift = INDICATOR_DR_SHIFT; > @@ -816,6 +843,198 @@ static AddressSpace *spapr_pci_dma_iommu(PCIBus *bus, > void *opaque, int devfn) > return &phb->iommu_as; > } > > +static int spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset, > + int phb_index) > +{ > + int slot = PCI_SLOT(dev->devfn); > + char slotname[16]; > + bool is_bridge = 1; > + sPAPRDrcEntry *drc_entry, *drc_entry_slot; > + uint32_t reg[RESOURCE_CELLS_TOTAL * 8] = { 0 }; > + uint32_t assigned[RESOURCE_CELLS_TOTAL * 8] = { 0 }; > + int reg_size, assigned_size; > + > + drc_entry = spapr_phb_to_drc_entry(phb_index + SPAPR_PCI_BASE_BUID); > + g_assert(drc_entry); > + drc_entry_slot = &drc_entry->child_entries[slot]; > + > + if (pci_default_read_config(dev, PCI_HEADER_TYPE, 1) == > + PCI_HEADER_TYPE_NORMAL) { > + is_bridge = 0; > + } > + > + _FDT(fdt_setprop_cell(fdt, offset, "vendor-id", > + pci_default_read_config(dev, PCI_VENDOR_ID, 2))); > + _FDT(fdt_setprop_cell(fdt, offset, "device-id", > + pci_default_read_config(dev, PCI_DEVICE_ID, 2))); > + _FDT(fdt_setprop_cell(fdt, offset, "revision-id", > + pci_default_read_config(dev, PCI_REVISION_ID, 1))); > + _FDT(fdt_setprop_cell(fdt, offset, "class-code", > + pci_default_read_config(dev, PCI_CLASS_DEVICE, 2) > << 8)); > + > + _FDT(fdt_setprop_cell(fdt, offset, "interrupts", > + pci_default_read_config(dev, PCI_INTERRUPT_PIN, > 1))); > + > + /* if this device is NOT a bridge */ > + if (!is_bridge) { > + _FDT(fdt_setprop_cell(fdt, offset, "min-grant", > + pci_default_read_config(dev, PCI_MIN_GNT, 1))); > + _FDT(fdt_setprop_cell(fdt, offset, "max-latency", > + pci_default_read_config(dev, PCI_MAX_LAT, 1))); > + _FDT(fdt_setprop_cell(fdt, offset, "subsystem-id", > + pci_default_read_config(dev, PCI_SUBSYSTEM_ID, 2))); > + _FDT(fdt_setprop_cell(fdt, offset, "subsystem-vendor-id", > + pci_default_read_config(dev, PCI_SUBSYSTEM_VENDOR_ID, 2))); > + } > + > + _FDT(fdt_setprop_cell(fdt, offset, "cache-line-size", > + pci_default_read_config(dev, PCI_CACHE_LINE_SIZE, 1))); > + > + /* the following fdt cells are masked off the pci status register */ > + int pci_status = pci_default_read_config(dev, PCI_STATUS, 2); > + _FDT(fdt_setprop_cell(fdt, offset, "devsel-speed", > + PCI_STATUS_DEVSEL_MASK & pci_status)); > + _FDT(fdt_setprop_cell(fdt, offset, "fast-back-to-back", > + PCI_STATUS_FAST_BACK & pci_status)); > + _FDT(fdt_setprop_cell(fdt, offset, "66mhz-capable", > + PCI_STATUS_66MHZ & pci_status)); > + _FDT(fdt_setprop_cell(fdt, offset, "udf-supported", > + PCI_STATUS_UDF & pci_status)); > + > + _FDT(fdt_setprop_string(fdt, offset, "name", "pci")); > + sprintf(slotname, "Slot %d", slot + phb_index * 32); > + _FDT(fdt_setprop(fdt, offset, "ibm,loc-code", slotname, > strlen(slotname))); > + _FDT(fdt_setprop_cell(fdt, offset, "ibm,my-drc-index", > + drc_entry_slot->drc_index)); > + > + _FDT(fdt_setprop_cell(fdt, offset, "#address-cells", > + RESOURCE_CELLS_ADDRESS)); > + _FDT(fdt_setprop_cell(fdt, offset, "#size-cells", > + RESOURCE_CELLS_SIZE)); > + _FDT(fdt_setprop_cell(fdt, offset, "ibm,req#msi-x", > + RESOURCE_CELLS_SIZE)); > + fill_resource_props(dev, phb_index, reg, ®_size, > + assigned, &assigned_size); > + _FDT(fdt_setprop(fdt, offset, "reg", reg, reg_size)); > + _FDT(fdt_setprop(fdt, offset, "assigned-addresses", > + assigned, assigned_size)); > + > + return 0; > +} > + > +static int spapr_device_hotplug_add(DeviceState *qdev, PCIDevice *dev)
Minor comment here too :) Will this function ever support anything but sPAPRPHBState as a bus device? It is not a callback and could receive what it actually needs - sPAPRPHBState. > +{ > + sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(qdev); > + sPAPRDrcEntry *drc_entry, *drc_entry_slot; > + sPAPRConfigureConnectorState *ccs; > + int slot = PCI_SLOT(dev->devfn); > + int offset, ret; > + void *fdt_orig, *fdt; > + char nodename[512]; > + uint32_t encoded = ENCODE_DRC_STATE(INDICATOR_ENTITY_SENSE_PRESENT, > + INDICATOR_ENTITY_SENSE_MASK, > + INDICATOR_ENTITY_SENSE_SHIFT); > + > + drc_entry = spapr_phb_to_drc_entry(phb->buid); > + g_assert(drc_entry); > + drc_entry_slot = &drc_entry->child_entries[slot]; > + > + drc_entry->state &= ~(uint32_t)INDICATOR_ENTITY_SENSE_MASK; > + drc_entry->state |= encoded; /* DR entity present */ > + drc_entry_slot->state &= ~(uint32_t)INDICATOR_ENTITY_SENSE_MASK; > + drc_entry_slot->state |= encoded; /* and the slot */ > + > + /* add OF node for pci device and required OF DT properties */ > + fdt_orig = g_malloc0(FDT_MAX_SIZE); > + offset = fdt_create(fdt_orig, FDT_MAX_SIZE); > + fdt_begin_node(fdt_orig, ""); > + fdt_end_node(fdt_orig); > + fdt_finish(fdt_orig); > + > + fdt = g_malloc0(FDT_MAX_SIZE); > + fdt_open_into(fdt_orig, fdt, FDT_MAX_SIZE); > + sprintf(nodename, "pci@%d", slot); > + offset = fdt_add_subnode(fdt, 0, nodename); > + ret = spapr_populate_pci_child_dt(dev, fdt, offset, phb->index); > + g_assert(!ret); > + g_free(fdt_orig); > + > + /* hold on to node, configure_connector will pass it to the guest later > */ > + ccs = &drc_entry_slot->cc_state; > + ccs->fdt = fdt; > + ccs->offset_start = offset; > + ccs->state = CC_STATE_PENDING; > + ccs->dev = dev; > + > + return 0; > +} > + > +/* check whether guest has released/isolated device */ > +static bool spapr_drc_state_is_releasable(sPAPRDrcEntry *drc_entry) > +{ > + return !DECODE_DRC_STATE(drc_entry->state, > + INDICATOR_ISOLATION_MASK, > + INDICATOR_ISOLATION_SHIFT); > +} > + > +/* finalize device unplug/deletion */ > +static void spapr_drc_state_reset(sPAPRDrcEntry *drc_entry) > +{ > + sPAPRConfigureConnectorState *ccs = &drc_entry->cc_state; > + uint32_t sense_empty = ENCODE_DRC_STATE(INDICATOR_ENTITY_SENSE_EMPTY, > + INDICATOR_ENTITY_SENSE_MASK, > + INDICATOR_ENTITY_SENSE_SHIFT); > + > + g_free(ccs->fdt); > + ccs->fdt = NULL; > + object_unparent(OBJECT(ccs->dev)); > + ccs->dev = NULL; > + ccs->state = CC_STATE_IDLE; > + drc_entry->state &= ~INDICATOR_ENTITY_SENSE_MASK; > + drc_entry->state |= sense_empty; > + drc_entry->awaiting_release = false; > +} > + > +static void spapr_device_hotplug_remove(DeviceState *qdev, PCIDevice *dev) > +{ > + sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(qdev); > + sPAPRDrcEntry *drc_entry, *drc_entry_slot; > + sPAPRConfigureConnectorState *ccs; > + int slot = PCI_SLOT(dev->devfn); > + > + drc_entry = spapr_phb_to_drc_entry(phb->buid); > + g_assert(drc_entry); > + drc_entry_slot = &drc_entry->child_entries[slot]; > + ccs = &drc_entry_slot->cc_state; > + /* shouldn't be removing devices we haven't created an fdt for */ > + g_assert(ccs->state != CC_STATE_IDLE); > + /* if the device has already been released/isolated by guest, go ahead > + * and remove it now. Otherwise, flag it as pending guest release so it > + * can be removed later > + */ > + if (spapr_drc_state_is_releasable(drc_entry_slot)) { > + spapr_drc_state_reset(drc_entry_slot); > + } else { > + if (drc_entry_slot->awaiting_release) { > + fprintf(stderr, "waiting for guest to release the device"); > + } else { > + drc_entry_slot->awaiting_release = true; > + } > + } > +} > + > +static void spapr_phb_hot_plug(HotplugHandler *plug_handler, > + DeviceState *plugged_dev, Error **errp) > +{ > + spapr_device_hotplug_add(DEVICE(plug_handler), PCI_DEVICE(plugged_dev)); > +} > + > +static void spapr_phb_hot_unplug(HotplugHandler *plug_handler, > + DeviceState *plugged_dev, Error **errp) > +{ > + spapr_device_hotplug_remove(DEVICE(plug_handler), > PCI_DEVICE(plugged_dev)); > +} > + > static void spapr_phb_realize(DeviceState *dev, Error **errp) > { > SysBusDevice *s = SYS_BUS_DEVICE(dev); > @@ -903,6 +1122,7 @@ static void spapr_phb_realize(DeviceState *dev, Error > **errp) > &sphb->memspace, &sphb->iospace, > PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS); > phb->bus = bus; > + qbus_set_hotplug_handler(BUS(phb->bus), DEVICE(sphb), NULL); > > /* > * Initialize PHB address space. > @@ -1108,6 +1328,7 @@ static void spapr_phb_class_init(ObjectClass *klass, > void *data) > PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); > DeviceClass *dc = DEVICE_CLASS(klass); > sPAPRPHBClass *spc = SPAPR_PCI_HOST_BRIDGE_CLASS(klass); > + HotplugHandlerClass *hp = HOTPLUG_HANDLER_CLASS(klass); > > hc->root_bus_path = spapr_phb_root_bus_path; > dc->realize = spapr_phb_realize; > @@ -1117,6 +1338,8 @@ static void spapr_phb_class_init(ObjectClass *klass, > void *data) > set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); > dc->cannot_instantiate_with_device_add_yet = false; > spc->finish_realize = spapr_phb_finish_realize; > + hp->plug = spapr_phb_hot_plug; > + hp->unplug = spapr_phb_hot_unplug; > } > > static const TypeInfo spapr_phb_info = { > @@ -1125,6 +1348,10 @@ static const TypeInfo spapr_phb_info = { > .instance_size = sizeof(sPAPRPHBState), > .class_init = spapr_phb_class_init, > .class_size = sizeof(sPAPRPHBClass), > + .interfaces = (InterfaceInfo[]) { > + { TYPE_HOTPLUG_HANDLER }, > + { } > + } > }; > > PCIHostState *spapr_create_phb(sPAPREnvironment *spapr, int index) > @@ -1304,14 +1531,6 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, > return bus_off; > } > > -#define _FDT(exp) \ > - do { \ > - int ret = (exp); \ > - if (ret < 0) { \ > - return ret; \ > - } \ > - } while (0) > - > /* Write PHB properties */ > _FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci")); > _FDT(fdt_setprop_string(fdt, bus_off, "compatible", "IBM,Logical_PHB")); > diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h > index fac85f8..e08dd2f 100644 > --- a/include/hw/ppc/spapr.h > +++ b/include/hw/ppc/spapr.h > @@ -36,6 +36,7 @@ struct sPAPRDrcEntry { > void *fdt; > int fdt_offset; > uint32_t state; > + bool awaiting_release; > sPAPRConfigureConnectorState cc_state; > sPAPRDrcEntry *child_entries; > }; > -- Alexey