#include "hw/loader.h"
#include "qemu/range.h"
#include "qmp-commands.h"
+#include "trace.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "exec/address-spaces.h"
+#include "hw/hotplug.h"
//#define DEBUG_PCI
#ifdef DEBUG_PCI
static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent);
static char *pcibus_get_dev_path(DeviceState *dev);
static char *pcibus_get_fw_dev_path(DeviceState *dev);
-static int pcibus_reset(BusState *qbus);
-static void pci_bus_finalize(Object *obj);
+static void pcibus_reset(BusState *qbus);
static Property pci_props[] = {
DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1),
DEFINE_PROP_END_OF_LIST()
};
+static const VMStateDescription vmstate_pcibus = {
+ .name = "PCIBUS",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32_EQUAL(nirq, PCIBus),
+ VMSTATE_VARRAY_INT32(irq_count, PCIBus,
+ nirq, 0, vmstate_info_int32,
+ int32_t),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void pci_bus_realize(BusState *qbus, Error **errp)
+{
+ PCIBus *bus = PCI_BUS(qbus);
+
+ vmstate_register(NULL, -1, &vmstate_pcibus, bus);
+}
+
+static void pci_bus_unrealize(BusState *qbus, Error **errp)
+{
+ PCIBus *bus = PCI_BUS(qbus);
+
+ vmstate_unregister(NULL, &vmstate_pcibus, bus);
+}
+
static void pci_bus_class_init(ObjectClass *klass, void *data)
{
BusClass *k = BUS_CLASS(klass);
k->print_dev = pcibus_dev_print;
k->get_dev_path = pcibus_get_dev_path;
k->get_fw_dev_path = pcibus_get_fw_dev_path;
+ k->realize = pci_bus_realize;
+ k->unrealize = pci_bus_unrealize;
k->reset = pcibus_reset;
}
.name = TYPE_PCI_BUS,
.parent = TYPE_BUS,
.instance_size = sizeof(PCIBus),
- .instance_finalize = pci_bus_finalize,
.class_init = pci_bus_class_init,
};
static QLIST_HEAD(, PCIHostState) pci_host_bridges;
-static const VMStateDescription vmstate_pcibus = {
- .name = "PCIBUS",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField []) {
- VMSTATE_INT32_EQUAL(nirq, PCIBus),
- VMSTATE_VARRAY_INT32(irq_count, PCIBus, nirq, 0, vmstate_info_int32, int32_t),
- VMSTATE_END_OF_LIST()
- }
-};
static int pci_bar(PCIDevice *d, int reg)
{
uint8_t type;
}
}
-/*
- * This function is called on #RST and FLR.
- * FLR if PCI_EXP_DEVCTL_BCR_FLR is set
- */
-void pci_device_reset(PCIDevice *dev)
+static void pci_do_device_reset(PCIDevice *dev)
{
int r;
- qdev_reset_all(&dev->qdev);
-
- dev->irq_state = 0;
- pci_update_irq_status(dev);
pci_device_deassert_intx(dev);
+ assert(dev->irq_state == 0);
+
/* Clear all writable bits */
pci_word_test_and_clear_mask(dev->config + PCI_COMMAND,
pci_get_word(dev->wmask + PCI_COMMAND) |
msix_reset(dev);
}
+/*
+ * This function is called on #RST and FLR.
+ * FLR if PCI_EXP_DEVCTL_BCR_FLR is set
+ */
+void pci_device_reset(PCIDevice *dev)
+{
+ qdev_reset_all(&dev->qdev);
+ pci_do_device_reset(dev);
+}
+
/*
* Trigger pci bus reset under a given bus.
- * To be called on RST# assert.
+ * Called via qbus_reset_all on RST# assert, after the devices
+ * have been reset qdev_reset_all-ed already.
*/
-void pci_bus_reset(PCIBus *bus)
+static void pcibus_reset(BusState *qbus)
{
+ PCIBus *bus = DO_UPCAST(PCIBus, qbus, qbus);
int i;
- for (i = 0; i < bus->nirq; i++) {
- bus->irq_count[i] = 0;
- }
for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
if (bus->devices[i]) {
- pci_device_reset(bus->devices[i]);
+ pci_do_device_reset(bus->devices[i]);
}
}
-}
-
-static int pcibus_reset(BusState *qbus)
-{
- pci_bus_reset(DO_UPCAST(PCIBus, qbus, qbus));
- /* topology traverse is done by pci_bus_reset().
- Tell qbus/qdev walker not to traverse the tree */
- return 1;
+ for (i = 0; i < bus->nirq; i++) {
+ assert(bus->irq_count[i] == 0);
+ }
}
static void pci_host_bus_register(PCIBus *bus, DeviceState *parent)
QLIST_INIT(&bus->child);
pci_host_bus_register(bus, parent);
-
- vmstate_register(NULL, -1, &vmstate_pcibus, bus);
}
bool pci_bus_is_express(PCIBus *bus)
bus->irq_count = g_malloc0(nirq * sizeof(bus->irq_count[0]));
}
-void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *qdev)
-{
- bus->qbus.allow_hotplug = 1;
- bus->hotplug = hotplug;
- bus->hotplug_qdev = qdev;
-}
-
PCIBus *pci_register_bus(DeviceState *parent, const char *name,
pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
void *irq_opaque,
return s->parent_dev->config[PCI_SECONDARY_BUS];
}
-static void pci_bus_finalize(Object *obj)
-{
- PCIBus *bus = PCI_BUS(obj);
- vmstate_unregister(NULL, &vmstate_pcibus, bus);
-}
-
static int get_pci_config_device(QEMUFile *f, void *pv, size_t size)
{
PCIDevice *s = container_of(pv, PCIDevice, config);
.name = "PCIDevice",
.version_id = 2,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField []) {
- VMSTATE_INT32_LE(version_id, PCIDevice),
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32_POSITIVE_LE(version_id, PCIDevice),
VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0,
vmstate_info_pci_config,
PCI_CONFIG_SPACE_SIZE),
.name = "PCIEDevice",
.version_id = 2,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField []) {
- VMSTATE_INT32_LE(version_id, PCIDevice),
+ .fields = (VMStateField[]) {
+ VMSTATE_INT32_POSITIVE_LE(version_id, PCIDevice),
VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0,
vmstate_info_pci_config,
PCIE_CONFIG_SPACE_SIZE),
* This makes us compatible with old devices
* which never set or clear this bit. */
s->config[PCI_STATUS] &= ~PCI_STATUS_INTERRUPT;
- vmstate_save_state(f, pci_get_vmstate(s), s);
+ vmstate_save_state(f, pci_get_vmstate(s), s, NULL);
/* Restore the interrupt status bit. */
pci_update_irq_status(s);
}
int dom, bus;
unsigned slot;
- assert(!root->parent_dev);
-
if (!root) {
fprintf(stderr, "No primary PCI bus\n");
return NULL;
}
+ assert(!root->parent_dev);
+
if (!devaddr) {
*devfnp = -1;
return pci_find_bus_nr(root, 0);
g_free(pci_dev->used);
}
+static void do_pci_unregister_device(PCIDevice *pci_dev)
+{
+ pci_dev->bus->devices[pci_dev->devfn] = NULL;
+ pci_config_free(pci_dev);
+
+ address_space_destroy(&pci_dev->bus_master_as);
+}
+
/* -1 for devfn means auto assign */
static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus,
const char *name, int devfn)
}
pci_dev->bus = bus;
+ pci_dev->devfn = devfn;
dma_as = pci_device_iommu_address_space(pci_dev);
memory_region_init_alias(&pci_dev->bus_master_enable_region,
address_space_init(&pci_dev->bus_master_as, &pci_dev->bus_master_enable_region,
name);
- pci_dev->devfn = devfn;
pstrcpy(pci_dev->name, sizeof(pci_dev->name), name);
pci_dev->irq_state = 0;
pci_config_alloc(pci_dev);
pci_init_mask_bridge(pci_dev);
}
if (pci_init_multifunction(bus, pci_dev)) {
- pci_config_free(pci_dev);
+ do_pci_unregister_device(pci_dev);
return NULL;
}
return pci_dev;
}
-static void do_pci_unregister_device(PCIDevice *pci_dev)
-{
- pci_dev->bus->devices[pci_dev->devfn] = NULL;
- pci_config_free(pci_dev);
-
- address_space_destroy(&pci_dev->bus_master_as);
- memory_region_destroy(&pci_dev->bus_master_enable_region);
-}
-
static void pci_unregister_io_regions(PCIDevice *pci_dev)
{
PCIIORegion *r;
/* now do the real mapping */
if (r->addr != PCI_BAR_UNMAPPED) {
+ trace_pci_update_mappings_del(d, pci_bus_num(d->bus),
+ PCI_FUNC(d->devfn),
+ PCI_SLOT(d->devfn),
+ i, r->addr, r->size);
memory_region_del_subregion(r->address_space, r->memory);
}
r->addr = new_addr;
if (r->addr != PCI_BAR_UNMAPPED) {
+ trace_pci_update_mappings_add(d, pci_bus_num(d->bus),
+ PCI_FUNC(d->devfn),
+ PCI_SLOT(d->devfn),
+ i, r->addr, r->size);
memory_region_add_subregion_overlap(r->address_space,
r->addr, r->memory, 1);
}
return le32_to_cpu(val);
}
-void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
+void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int l)
{
int i, was_irq_disabled = pci_irq_disabled(d);
+ uint32_t val = val_in;
for (i = 0; i < l; val >>= 8, ++i) {
uint8_t wmask = d->wmask[addr + i];
& PCI_COMMAND_MASTER);
}
- msi_write_config(d, addr, val, l);
- msix_write_config(d, addr, val, l);
+ msi_write_config(d, addr, val_in, l);
+ msix_write_config(d, addr, val_in, l);
}
/***********************************************************/
return NULL;
}
+void pci_for_each_bus_depth_first(PCIBus *bus,
+ void *(*begin)(PCIBus *bus, void *parent_state),
+ void (*end)(PCIBus *bus, void *state),
+ void *parent_state)
+{
+ PCIBus *sec;
+ void *state;
+
+ if (!bus) {
+ return;
+ }
+
+ if (begin) {
+ state = begin(bus, parent_state);
+ } else {
+ state = parent_state;
+ }
+
+ QLIST_FOREACH(sec, &bus->child, sibling) {
+ pci_for_each_bus_depth_first(sec, begin, end, state);
+ }
+
+ if (end) {
+ end(bus, state);
+ }
+}
+
+
PCIDevice *pci_find_device(PCIBus *bus, int bus_num, uint8_t devfn)
{
bus = pci_find_bus_nr(bus, bus_num);
pci_dev->devfn);
if (pci_dev == NULL)
return -1;
- if (qdev->hotplugged && pc->no_hotplug) {
- qerror_report(QERR_DEVICE_NO_HOTPLUG, object_get_typename(OBJECT(pci_dev)));
- do_pci_unregister_device(pci_dev);
- return -1;
- }
+
if (pc->init) {
rc = pc->init(pci_dev);
if (rc != 0) {
pci_dev->romfile = g_strdup(pc->romfile);
is_default_rom = true;
}
- pci_add_option_rom(pci_dev, is_default_rom);
- if (bus->hotplug) {
- /* Let buses differentiate between hotplug and when device is
- * enabled during qemu machine creation. */
- rc = bus->hotplug(bus->hotplug_qdev, pci_dev,
- qdev->hotplugged ? PCI_HOTPLUG_ENABLED:
- PCI_COLDPLUG_ENABLED);
- if (rc != 0) {
- int r = pci_unregister_device(&pci_dev->qdev);
- assert(!r);
- return rc;
- }
+ rc = pci_add_option_rom(pci_dev, is_default_rom);
+ if (rc != 0) {
+ pci_unregister_device(DEVICE(pci_dev));
+ return rc;
}
- return 0;
-}
-static int pci_unplug_device(DeviceState *qdev)
-{
- PCIDevice *dev = PCI_DEVICE(qdev);
- PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev);
-
- if (pc->no_hotplug) {
- qerror_report(QERR_DEVICE_NO_HOTPLUG, object_get_typename(OBJECT(dev)));
- return -1;
- }
- return dev->bus->hotplug(dev->bus->hotplug_qdev, dev,
- PCI_HOTPLUG_DISABLED);
+ return 0;
}
PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction,
* for 0.11 compatibility.
*/
int class = pci_get_word(pdev->config + PCI_CLASS_DEVICE);
+
+ /*
+ * Hot-plugged devices can't use the option ROM
+ * if the rom bar is disabled.
+ */
+ if (DEVICE(pdev)->hotplugged) {
+ return -1;
+ }
+
if (class == 0x0300) {
rom_add_vga(pdev->romfile);
} else {
snprintf(name, sizeof(name), "%s.rom", object_get_typename(OBJECT(pdev)));
}
pdev->has_rom = true;
- memory_region_init_ram(&pdev->rom, OBJECT(pdev), name, size);
+ memory_region_init_ram(&pdev->rom, OBJECT(pdev), name, size, &error_abort);
vmstate_register_ram(&pdev->rom, &pdev->qdev);
ptr = memory_region_get_ram_ptr(&pdev->rom);
load_image(path, ptr);
return;
vmstate_unregister_ram(&pdev->rom, &pdev->qdev);
- memory_region_destroy(&pdev->rom);
pdev->has_rom = false;
}
* in pci config space */
int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
uint8_t offset, uint8_t size)
+{
+ int ret;
+ Error *local_err = NULL;
+
+ ret = pci_add_capability2(pdev, cap_id, offset, size, &local_err);
+ if (local_err) {
+ assert(ret < 0);
+ error_report_err(local_err);
+ } else {
+ /* success implies a positive offset in config space */
+ assert(ret > 0);
+ }
+ return ret;
+}
+
+int pci_add_capability2(PCIDevice *pdev, uint8_t cap_id,
+ uint8_t offset, uint8_t size,
+ Error **errp)
{
uint8_t *config;
int i, overlapping_cap;
if (!offset) {
offset = pci_find_space(pdev, size);
if (!offset) {
+ error_setg(errp, "out of PCI config space");
return -ENOSPC;
}
} else {
for (i = offset; i < offset + size; i++) {
overlapping_cap = pci_find_capability_at_offset(pdev, i);
if (overlapping_cap) {
- fprintf(stderr, "ERROR: %s:%02x:%02x.%x "
- "Attempt to add PCI capability %x at offset "
- "%x overlaps existing capability %x at offset %x\n",
- pci_root_bus_path(pdev), pci_bus_num(pdev->bus),
- PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
- cap_id, offset, overlapping_cap, i);
+ error_setg(errp, "%s:%02x:%02x.%x "
+ "Attempt to add PCI capability %x at offset "
+ "%x overlaps existing capability %x at offset %x",
+ pci_root_bus_path(pdev), pci_bus_num(pdev->bus),
+ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
+ cap_id, offset, overlapping_cap, i);
return -EINVAL;
}
}
{
DeviceClass *k = DEVICE_CLASS(klass);
k->init = pci_qdev_init;
- k->unplug = pci_unplug_device;
k->exit = pci_unregister_device;
k->bus_type = TYPE_PCI_BUS;
k->props = pci_props;