#include "monitor.h"
#include "net.h"
#include "sysemu.h"
+#include "msix.h"
//#define DEBUG_PCI
#ifdef DEBUG_PCI
int devfn_min;
pci_set_irq_fn set_irq;
pci_map_irq_fn map_irq;
+ pci_hotplug_fn hotplug;
uint32_t config_reg; /* XXX: suppress */
void *irq_opaque;
PCIDevice *devices[256];
bus->irq_count = qemu_mallocz(nirq * sizeof(bus->irq_count[0]));
}
+void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug)
+{
+ bus->qbus.allow_hotplug = 1;
+ bus->hotplug = hotplug;
+}
+
PCIBus *pci_register_bus(DeviceState *parent, const char *name,
pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
void *irq_opaque, int devfn_min, int nirq)
return bus;
}
-static PCIBus *pci_register_secondary_bus(PCIDevice *dev,
- pci_map_irq_fn map_irq,
- const char *name)
+static void pci_register_secondary_bus(PCIBus *bus,
+ PCIDevice *dev,
+ pci_map_irq_fn map_irq,
+ const char *name)
{
- PCIBus *bus;
-
- bus = FROM_QBUS(PCIBus, qbus_create(&pci_bus_info, &dev->qdev, name));
+ qbus_create_inplace(&bus->qbus, &pci_bus_info, &dev->qdev, name);
bus->map_irq = map_irq;
bus->parent_dev = dev;
bus->next = dev->bus->next;
dev->bus->next = bus;
- return bus;
}
int pci_bus_num(PCIBus *s)
}
}
-int pci_unregister_device(PCIDevice *pci_dev)
+static int pci_unregister_device(DeviceState *dev)
{
+ PCIDevice *pci_dev = DO_UPCAST(PCIDevice, qdev, dev);
+ PCIDeviceInfo *info = DO_UPCAST(PCIDeviceInfo, qdev, dev->info);
int ret = 0;
- if (pci_dev->unregister)
- ret = pci_dev->unregister(pci_dev);
+ if (info->exit)
+ ret = info->exit(pci_dev);
if (ret)
return ret;
+ msix_uninit(pci_dev);
pci_unregister_io_regions(pci_dev);
qemu_free_irqs(pci_dev->irq);
pci_dev->bus->devices[pci_dev->devfn] = NULL;
- qdev_free(&pci_dev->qdev);
return 0;
}
typedef struct {
PCIDevice dev;
- PCIBus *bus;
+ PCIBus bus;
+ uint32_t vid;
+ uint32_t did;
} PCIBridge;
static void pci_bridge_write_config(PCIDevice *d,
PCIBridge *s = (PCIBridge *)d;
pci_default_write_config(d, address, val, len);
- s->bus->bus_num = d->config[PCI_SECONDARY_BUS];
+ s->bus.bus_num = d->config[PCI_SECONDARY_BUS];
}
PCIBus *pci_find_bus(int bus_num)
return bus->devices[PCI_DEVFN(slot, function)];
}
-PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did,
- pci_map_irq_fn map_irq, const char *name)
+static int pci_bridge_initfn(PCIDevice *dev)
{
- PCIBridge *s;
- s = (PCIBridge *)pci_register_device(bus, name, sizeof(PCIBridge),
- devfn, NULL, pci_bridge_write_config);
+ PCIBridge *s = DO_UPCAST(PCIBridge, dev, dev);
- pci_config_set_vendor_id(s->dev.config, vid);
- pci_config_set_device_id(s->dev.config, did);
+ pci_config_set_vendor_id(s->dev.config, s->vid);
+ pci_config_set_device_id(s->dev.config, s->did);
s->dev.config[0x04] = 0x06; // command = bus master, pci mem
s->dev.config[0x05] = 0x00;
s->dev.config[PCI_HEADER_TYPE] =
PCI_HEADER_TYPE_MULTI_FUNCTION | PCI_HEADER_TYPE_BRIDGE; // header_type
s->dev.config[0x1E] = 0xa0; // secondary status
+ return 0;
+}
+
+PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did,
+ pci_map_irq_fn map_irq, const char *name)
+{
+ PCIDevice *dev;
+ PCIBridge *s;
- s->bus = pci_register_secondary_bus(&s->dev, map_irq, name);
- return s->bus;
+ dev = pci_create_noinit(bus, devfn, "pci-bridge");
+ qdev_prop_set_uint32(&dev->qdev, "vendorid", vid);
+ qdev_prop_set_uint32(&dev->qdev, "deviceid", did);
+ qdev_init(&dev->qdev);
+
+ s = DO_UPCAST(PCIBridge, dev, dev);
+ pci_register_secondary_bus(&s->bus, &s->dev, map_irq, name);
+ return &s->bus;
}
static int pci_qdev_init(DeviceState *qdev, DeviceInfo *base)
PCIDevice *pci_dev = (PCIDevice *)qdev;
PCIDeviceInfo *info = container_of(base, PCIDeviceInfo, qdev);
PCIBus *bus;
- int devfn;
+ int devfn, rc;
bus = FROM_QBUS(PCIBus, qdev_get_parent_bus(qdev));
devfn = pci_dev->devfn;
pci_dev = do_pci_register_device(pci_dev, bus, base->name, devfn,
info->config_read, info->config_write);
assert(pci_dev);
- return info->init(pci_dev);
+ rc = info->init(pci_dev);
+ if (rc != 0)
+ return rc;
+ if (qdev->hotplugged)
+ bus->hotplug(pci_dev, 1);
+ return 0;
+}
+
+static int pci_unplug_device(DeviceState *qdev)
+{
+ PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev);
+
+ dev->bus->hotplug(dev, 0);
+ return 0;
}
void pci_qdev_register(PCIDeviceInfo *info)
{
info->qdev.init = pci_qdev_init;
+ info->qdev.unplug = pci_unplug_device;
+ info->qdev.exit = pci_unregister_device;
info->qdev.bus_info = &pci_bus_info;
qdev_register(&info->qdev);
}
r->addr, r->addr + r->size - 1);
}
}
+
+static PCIDeviceInfo bridge_info = {
+ .qdev.name = "pci-bridge",
+ .qdev.size = sizeof(PCIBridge),
+ .init = pci_bridge_initfn,
+ .config_write = pci_bridge_write_config,
+ .qdev.props = (Property[]) {
+ DEFINE_PROP_HEX32("vendorid", PCIBridge, vid, 0),
+ DEFINE_PROP_HEX32("deviceid", PCIBridge, did, 0),
+ DEFINE_PROP_END_OF_LIST(),
+ }
+};
+
+static void pci_register_devices(void)
+{
+ pci_qdev_register(&bridge_info);
+}
+
+device_init(pci_register_devices)