#include "net.h"
#include "sysemu.h"
#include "loader.h"
+#include "qemu-objects.h"
//#define DEBUG_PCI
#ifdef DEBUG_PCI
pci_set_irq_fn set_irq;
pci_map_irq_fn map_irq;
pci_hotplug_fn hotplug;
- uint32_t config_reg; /* XXX: suppress */
+ DeviceState *hotplug_qdev;
void *irq_opaque;
PCIDevice *devices[256];
PCIDevice *parent_dev;
.props = (Property[]) {
DEFINE_PROP_PCI_DEVFN("addr", PCIDevice, devfn, -1),
DEFINE_PROP_STRING("romfile", PCIDevice, romfile),
+ DEFINE_PROP_UINT32("rombar", PCIDevice, rom_bar, 1),
DEFINE_PROP_END_OF_LIST()
}
};
return NULL;
}
+int pci_find_domain(const PCIBus *bus)
+{
+ PCIDevice *d;
+ struct PCIHostBus *host;
+
+ /* obtain root bus */
+ while ((d = bus->parent_dev) != NULL) {
+ bus = d->bus;
+ }
+
+ QLIST_FOREACH(host, &host_buses, next) {
+ if (host->bus == bus) {
+ return host->domain;
+ }
+ }
+
+ abort(); /* should not be reached */
+ return -1;
+}
+
void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent,
const char *name, int devfn_min)
{
bus->irq_count = qemu_mallocz(nirq * sizeof(bus->irq_count[0]));
}
-void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug)
+void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *qdev)
{
bus->qbus.allow_hotplug = 1;
bus->hotplug = hotplug;
+ bus->hotplug_qdev = qdev;
}
void pci_bus_set_mem_base(PCIBus *bus, target_phys_addr_t base)
static int get_pci_irq_state(QEMUFile *f, void *pv, size_t size)
{
- PCIDevice *s = container_of(pv, PCIDevice, config);
+ PCIDevice *s = container_of(pv, PCIDevice, irq_state);
uint32_t irq_state[PCI_NUM_PINS];
int i;
for (i = 0; i < PCI_NUM_PINS; ++i) {
static void put_pci_irq_state(QEMUFile *f, void *pv, size_t size)
{
int i;
- PCIDevice *s = container_of(pv, PCIDevice, config);
+ PCIDevice *s = container_of(pv, PCIDevice, irq_state);
for (i = 0; i < PCI_NUM_PINS; ++i) {
qemu_put_be32(f, pci_irq_state(s, i));
return ret;
}
-static int pci_set_default_subsystem_id(PCIDevice *pci_dev)
+static void pci_set_default_subsystem_id(PCIDevice *pci_dev)
{
- uint16_t *id;
-
- id = (void*)(&pci_dev->config[PCI_SUBVENDOR_ID]);
- id[0] = cpu_to_le16(pci_default_sub_vendor_id);
- id[1] = cpu_to_le16(pci_default_sub_device_id);
- return 0;
+ pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID,
+ pci_default_sub_vendor_id);
+ pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID,
+ pci_default_sub_device_id);
}
/*
}
*devfnp = slot << 3;
- return pci_find_bus(pci_find_root_bus(0), bus);
+ return pci_find_bus(pci_find_root_bus(dom), bus);
}
static void pci_init_cmask(PCIDevice *dev)
dev->wmask[PCI_CACHE_LINE_SIZE] = 0xff;
dev->wmask[PCI_INTERRUPT_LINE] = 0xff;
pci_set_word(dev->wmask + PCI_COMMAND,
- PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+ PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+ PCI_COMMAND_INTX_DISABLE);
memset(dev->wmask + PCI_CONFIG_HEADER_SIZE, 0xff,
config_size - PCI_CONFIG_HEADER_SIZE);
if (!bus->devices[devfn])
goto found;
}
- qemu_error("PCI: no devfn available for %s, all in use\n", name);
+ error_report("PCI: no slot/function available for %s, all in use", name);
return NULL;
found: ;
} else if (bus->devices[devfn]) {
- qemu_error("PCI: devfn %d not available for %s, in use by %s\n", devfn,
- name, bus->devices[devfn]->name);
+ error_report("PCI: slot %d function %d not available for %s, in use by %s",
+ PCI_SLOT(devfn), PCI_FUNC(devfn), name, bus->devices[devfn]->name);
return NULL;
}
pci_dev->bus = bus;
return pci_dev;
}
+static void do_pci_unregister_device(PCIDevice *pci_dev)
+{
+ qemu_free_irqs(pci_dev->irq);
+ pci_dev->bus->devices[pci_dev->devfn] = NULL;
+ pci_config_free(pci_dev);
+}
+
PCIDevice *pci_register_device(PCIBus *bus, const char *name,
int instance_size, int devfn,
PCIConfigReadFunc *config_read,
return ret;
pci_unregister_io_regions(pci_dev);
-
- qemu_free_irqs(pci_dev->irq);
- pci_dev->bus->devices[pci_dev->devfn] = NULL;
- pci_config_free(pci_dev);
+ do_pci_unregister_device(pci_dev);
return 0;
}
* Teach them such cases, such that filtered_size < size and
* addr & (size - 1) != 0.
*/
- r->map_func(d, i, r->addr, r->filtered_size, r->type);
+ if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
+ r->map_func(d, i, r->addr, r->filtered_size, r->type);
+ } else {
+ r->map_func(d, i, pci_to_cpu_addr(d->bus, r->addr),
+ r->filtered_size, r->type);
+ }
}
}
}
+static inline int pci_irq_disabled(PCIDevice *d)
+{
+ return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE;
+}
+
+/* Called after interrupt disabled field update in config space,
+ * assert/deassert interrupts if necessary.
+ * Gets original interrupt disable bit value (before update). */
+static void pci_update_irq_disabled(PCIDevice *d, int was_irq_disabled)
+{
+ int i, disabled = pci_irq_disabled(d);
+ if (disabled == was_irq_disabled)
+ return;
+ for (i = 0; i < PCI_NUM_PINS; ++i) {
+ int state = pci_irq_state(d, i);
+ pci_change_irq_level(d, i, disabled ? -state : state);
+ }
+}
+
uint32_t pci_default_read_config(PCIDevice *d,
uint32_t address, int len)
{
void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l)
{
- int i;
+ int i, was_irq_disabled = pci_irq_disabled(d);
uint32_t config_size = pci_config_size(d);
for (i = 0; i < l && addr + i < config_size; val >>= 8, ++i) {
ranges_overlap(addr, l, PCI_ROM_ADDRESS1, 4) ||
range_covers_byte(addr, l, PCI_COMMAND))
pci_update_mappings(d);
+
+ if (range_covers_byte(addr, l, PCI_COMMAND))
+ pci_update_irq_disabled(d, was_irq_disabled);
}
/***********************************************************/
pci_set_irq_state(pci_dev, irq_num, level);
pci_update_irq_status(pci_dev);
+ if (pci_irq_disabled(pci_dev))
+ return;
pci_change_irq_level(pci_dev, irq_num, change);
}
{ 0, NULL}
};
-static void pci_info_device(PCIBus *bus, PCIDevice *d)
+static void pci_for_each_device_under_bus(PCIBus *bus,
+ void (*fn)(PCIBus *b, PCIDevice *d))
{
- Monitor *mon = cur_mon;
- int i, class;
- PCIIORegion *r;
- const pci_class_desc *desc;
+ PCIDevice *d;
+ int devfn;
- monitor_printf(mon, " Bus %2d, device %3d, function %d:\n",
- pci_bus_num(d->bus),
- PCI_SLOT(d->devfn), PCI_FUNC(d->devfn));
- class = pci_get_word(d->config + PCI_CLASS_DEVICE);
+ for(devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+ d = bus->devices[devfn];
+ if (d) {
+ fn(bus, d);
+ }
+ }
+}
+
+void pci_for_each_device(PCIBus *bus, int bus_num,
+ void (*fn)(PCIBus *b, PCIDevice *d))
+{
+ bus = pci_find_bus(bus, bus_num);
+
+ if (bus) {
+ pci_for_each_device_under_bus(bus, fn);
+ }
+}
+
+static void pci_device_print(Monitor *mon, QDict *device)
+{
+ QDict *qdict;
+ QListEntry *entry;
+ uint64_t addr, size;
+
+ monitor_printf(mon, " Bus %2" PRId64 ", ", qdict_get_int(device, "bus"));
+ monitor_printf(mon, "device %3" PRId64 ", function %" PRId64 ":\n",
+ qdict_get_int(device, "slot"),
+ qdict_get_int(device, "function"));
monitor_printf(mon, " ");
- desc = pci_class_descriptions;
- while (desc->desc && class != desc->class)
- desc++;
- if (desc->desc) {
- monitor_printf(mon, "%s", desc->desc);
+
+ qdict = qdict_get_qdict(device, "class_info");
+ if (qdict_haskey(qdict, "desc")) {
+ monitor_printf(mon, "%s", qdict_get_str(qdict, "desc"));
} else {
- monitor_printf(mon, "Class %04x", class);
+ monitor_printf(mon, "Class %04" PRId64, qdict_get_int(qdict, "class"));
}
- monitor_printf(mon, ": PCI device %04x:%04x\n",
- pci_get_word(d->config + PCI_VENDOR_ID),
- pci_get_word(d->config + PCI_DEVICE_ID));
- if (d->config[PCI_INTERRUPT_PIN] != 0) {
- monitor_printf(mon, " IRQ %d.\n",
- d->config[PCI_INTERRUPT_LINE]);
+ qdict = qdict_get_qdict(device, "id");
+ monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n",
+ qdict_get_int(qdict, "device"),
+ qdict_get_int(qdict, "vendor"));
+
+ if (qdict_haskey(device, "irq")) {
+ monitor_printf(mon, " IRQ %" PRId64 ".\n",
+ qdict_get_int(device, "irq"));
}
- if (class == 0x0604) {
- uint64_t base;
- uint64_t limit;
- monitor_printf(mon, " BUS %d.\n", d->config[0x19]);
- monitor_printf(mon, " secondary bus %d.\n",
- d->config[PCI_SECONDARY_BUS]);
- monitor_printf(mon, " subordinate bus %d.\n",
- d->config[PCI_SUBORDINATE_BUS]);
+ if (qdict_haskey(device, "pci_bridge")) {
+ QDict *info;
+
+ qdict = qdict_get_qdict(device, "pci_bridge");
+
+ info = qdict_get_qdict(qdict, "bus");
+ monitor_printf(mon, " BUS %" PRId64 ".\n",
+ qdict_get_int(info, "number"));
+ monitor_printf(mon, " secondary bus %" PRId64 ".\n",
+ qdict_get_int(info, "secondary"));
+ monitor_printf(mon, " subordinate bus %" PRId64 ".\n",
+ qdict_get_int(info, "subordinate"));
- base = pci_bridge_get_base(d, PCI_BASE_ADDRESS_SPACE_IO);
- limit = pci_bridge_get_limit(d, PCI_BASE_ADDRESS_SPACE_IO);
+ info = qdict_get_qdict(qdict, "io_range");
monitor_printf(mon, " IO range [0x%04"PRIx64", 0x%04"PRIx64"]\n",
- base, limit);
+ qdict_get_int(info, "base"),
+ qdict_get_int(info, "limit"));
- base = pci_bridge_get_base(d, PCI_BASE_ADDRESS_SPACE_MEMORY);
- limit= pci_bridge_get_limit(d, PCI_BASE_ADDRESS_SPACE_MEMORY);
+ info = qdict_get_qdict(qdict, "memory_range");
monitor_printf(mon,
" memory range [0x%08"PRIx64", 0x%08"PRIx64"]\n",
- base, limit);
+ qdict_get_int(info, "base"),
+ qdict_get_int(info, "limit"));
- base = pci_bridge_get_base(d, PCI_BASE_ADDRESS_SPACE_MEMORY |
- PCI_BASE_ADDRESS_MEM_PREFETCH);
- limit = pci_bridge_get_limit(d, PCI_BASE_ADDRESS_SPACE_MEMORY |
- PCI_BASE_ADDRESS_MEM_PREFETCH);
+ info = qdict_get_qdict(qdict, "prefetchable_range");
monitor_printf(mon, " prefetchable memory range "
- "[0x%08"PRIx64", 0x%08"PRIx64"]\n", base, limit);
+ "[0x%08"PRIx64", 0x%08"PRIx64"]\n",
+ qdict_get_int(info, "base"),
+ qdict_get_int(info, "limit"));
}
- for(i = 0;i < PCI_NUM_REGIONS; i++) {
- r = &d->io_regions[i];
- if (r->size != 0) {
- monitor_printf(mon, " BAR%d: ", i);
- if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
- monitor_printf(mon, "I/O at 0x%04"FMT_PCIBUS
- " [0x%04"FMT_PCIBUS"].\n",
- r->addr, r->addr + r->size - 1);
- } else {
- const char *type = r->type & PCI_BASE_ADDRESS_MEM_TYPE_64 ?
- "64 bit" : "32 bit";
- const char *prefetch =
- r->type & PCI_BASE_ADDRESS_MEM_PREFETCH ?
- " prefetchable" : "";
- monitor_printf(mon, "%s%s memory at 0x%08"FMT_PCIBUS
+ QLIST_FOREACH_ENTRY(qdict_get_qlist(device, "regions"), entry) {
+ qdict = qobject_to_qdict(qlist_entry_obj(entry));
+ monitor_printf(mon, " BAR%d: ", (int) qdict_get_int(qdict, "bar"));
+
+ addr = qdict_get_int(qdict, "address");
+ size = qdict_get_int(qdict, "size");
+
+ if (!strcmp(qdict_get_str(qdict, "type"), "io")) {
+ monitor_printf(mon, "I/O at 0x%04"FMT_PCIBUS
+ " [0x%04"FMT_PCIBUS"].\n",
+ addr, addr + size - 1);
+ } else {
+ monitor_printf(mon, "%d bit%s memory at 0x%08"FMT_PCIBUS
" [0x%08"FMT_PCIBUS"].\n",
- type, prefetch,
- r->addr, r->addr + r->size - 1);
+ qdict_get_bool(qdict, "mem_type_64") ? 64 : 32,
+ qdict_get_bool(qdict, "prefetch") ?
+ " prefetchable" : "", addr, addr + size - 1);
+ }
+ }
+
+ monitor_printf(mon, " id \"%s\"\n", qdict_get_str(device, "qdev_id"));
+
+ if (qdict_haskey(device, "pci_bridge")) {
+ qdict = qdict_get_qdict(device, "pci_bridge");
+ if (qdict_haskey(qdict, "devices")) {
+ QListEntry *dev;
+ QLIST_FOREACH_ENTRY(qdict_get_qlist(qdict, "devices"), dev) {
+ pci_device_print(mon, qobject_to_qdict(qlist_entry_obj(dev)));
}
}
}
- monitor_printf(mon, " id \"%s\"\n", d->qdev.id ? d->qdev.id : "");
- if (class == 0x0604 && d->config[0x19] != 0) {
- pci_for_each_device(bus, d->config[0x19], pci_info_device);
+}
+
+void do_pci_info_print(Monitor *mon, const QObject *data)
+{
+ QListEntry *bus, *dev;
+
+ QLIST_FOREACH_ENTRY(qobject_to_qlist(data), bus) {
+ QDict *qdict = qobject_to_qdict(qlist_entry_obj(bus));
+ QLIST_FOREACH_ENTRY(qdict_get_qlist(qdict, "devices"), dev) {
+ pci_device_print(mon, qobject_to_qdict(qlist_entry_obj(dev)));
+ }
}
}
-static void pci_for_each_device_under_bus(PCIBus *bus,
- void (*fn)(PCIBus *b, PCIDevice *d))
+static QObject *pci_get_dev_class(const PCIDevice *dev)
+{
+ int class;
+ const pci_class_desc *desc;
+
+ class = pci_get_word(dev->config + PCI_CLASS_DEVICE);
+ desc = pci_class_descriptions;
+ while (desc->desc && class != desc->class)
+ desc++;
+
+ if (desc->desc) {
+ return qobject_from_jsonf("{ 'desc': %s, 'class': %d }",
+ desc->desc, class);
+ } else {
+ return qobject_from_jsonf("{ 'class': %d }", class);
+ }
+}
+
+static QObject *pci_get_dev_id(const PCIDevice *dev)
+{
+ return qobject_from_jsonf("{ 'device': %d, 'vendor': %d }",
+ pci_get_word(dev->config + PCI_VENDOR_ID),
+ pci_get_word(dev->config + PCI_DEVICE_ID));
+}
+
+static QObject *pci_get_regions_list(const PCIDevice *dev)
+{
+ int i;
+ QList *regions_list;
+
+ regions_list = qlist_new();
+
+ for (i = 0; i < PCI_NUM_REGIONS; i++) {
+ QObject *obj;
+ const PCIIORegion *r = &dev->io_regions[i];
+
+ if (!r->size) {
+ continue;
+ }
+
+ if (r->type & PCI_BASE_ADDRESS_SPACE_IO) {
+ obj = qobject_from_jsonf("{ 'bar': %d, 'type': 'io', "
+ "'address': %" PRId64 ", "
+ "'size': %" PRId64 " }",
+ i, r->addr, r->size);
+ } else {
+ int mem_type_64 = r->type & PCI_BASE_ADDRESS_MEM_TYPE_64;
+
+ obj = qobject_from_jsonf("{ 'bar': %d, 'type': 'memory', "
+ "'mem_type_64': %i, 'prefetch': %i, "
+ "'address': %" PRId64 ", "
+ "'size': %" PRId64 " }",
+ i, mem_type_64,
+ r->type & PCI_BASE_ADDRESS_MEM_PREFETCH,
+ r->addr, r->size);
+ }
+
+ qlist_append_obj(regions_list, obj);
+ }
+
+ return QOBJECT(regions_list);
+}
+
+static QObject *pci_get_devices_list(PCIBus *bus, int bus_num);
+
+static QObject *pci_get_dev_dict(PCIDevice *dev, PCIBus *bus, int bus_num)
+{
+ uint8_t type;
+ QObject *obj;
+
+ obj = qobject_from_jsonf("{ 'bus': %d, 'slot': %d, 'function': %d," "'class_info': %p, 'id': %p, 'regions': %p,"
+ " 'qdev_id': %s }",
+ bus_num,
+ PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
+ pci_get_dev_class(dev), pci_get_dev_id(dev),
+ pci_get_regions_list(dev),
+ dev->qdev.id ? dev->qdev.id : "");
+
+ if (dev->config[PCI_INTERRUPT_PIN] != 0) {
+ QDict *qdict = qobject_to_qdict(obj);
+ qdict_put(qdict, "irq", qint_from_int(dev->config[PCI_INTERRUPT_LINE]));
+ }
+
+ type = dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUNCTION;
+ if (type == PCI_HEADER_TYPE_BRIDGE) {
+ QDict *qdict;
+ QObject *pci_bridge;
+
+ pci_bridge = qobject_from_jsonf("{ 'bus': "
+ "{ 'number': %d, 'secondary': %d, 'subordinate': %d }, "
+ "'io_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "}, "
+ "'memory_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "}, "
+ "'prefetchable_range': { 'base': %" PRId64 ", 'limit': %" PRId64 "} }",
+ dev->config[PCI_PRIMARY_BUS], dev->config[PCI_SECONDARY_BUS],
+ dev->config[PCI_SUBORDINATE_BUS],
+ pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO),
+ pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO),
+ pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY),
+ pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY),
+ pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_PREFETCH),
+ pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY |
+ PCI_BASE_ADDRESS_MEM_PREFETCH));
+
+ if (dev->config[PCI_SECONDARY_BUS] != 0) {
+ PCIBus *child_bus = pci_find_bus(bus, dev->config[PCI_SECONDARY_BUS]);
+
+ if (child_bus) {
+ qdict = qobject_to_qdict(pci_bridge);
+ qdict_put_obj(qdict, "devices",
+ pci_get_devices_list(child_bus,
+ dev->config[PCI_SECONDARY_BUS]));
+ }
+ }
+ qdict = qobject_to_qdict(obj);
+ qdict_put_obj(qdict, "pci_bridge", pci_bridge);
+ }
+
+ return obj;
+}
+
+static QObject *pci_get_devices_list(PCIBus *bus, int bus_num)
{
- PCIDevice *d;
int devfn;
+ PCIDevice *dev;
+ QList *dev_list;
- for(devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
- d = bus->devices[devfn];
- if (d)
- fn(bus, d);
+ dev_list = qlist_new();
+
+ for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) {
+ dev = bus->devices[devfn];
+ if (dev) {
+ qlist_append_obj(dev_list, pci_get_dev_dict(dev, bus, bus_num));
+ }
}
+
+ return QOBJECT(dev_list);
}
-void pci_for_each_device(PCIBus *bus, int bus_num,
- void (*fn)(PCIBus *b, PCIDevice *d))
+static QObject *pci_get_bus_dict(PCIBus *bus, int bus_num)
{
bus = pci_find_bus(bus, bus_num);
-
if (bus) {
- pci_for_each_device_under_bus(bus, fn);
+ return qobject_from_jsonf("{ 'bus': %d, 'devices': %p }",
+ bus_num, pci_get_devices_list(bus, bus_num));
}
+
+ return NULL;
}
-void pci_info(Monitor *mon)
+void do_pci_info(Monitor *mon, QObject **ret_data)
{
+ QList *bus_list;
struct PCIHostBus *host;
+
+ bus_list = qlist_new();
+
QLIST_FOREACH(host, &host_buses, next) {
- pci_for_each_device(host->bus, 0, pci_info_device);
+ QObject *obj = pci_get_bus_dict(host->bus, 0);
+ if (obj) {
+ qlist_append_obj(bus_list, obj);
+ }
}
+
+ *ret_data = QOBJECT(bus_list);
}
static const char * const pci_nic_models[] = {
bus = pci_get_bus_devfn(&devfn, devaddr);
if (!bus) {
- qemu_error("Invalid PCI device address %s for device %s\n",
- devaddr, pci_nic_names[i]);
+ error_report("Invalid PCI device address %s for device %s",
+ devaddr, pci_nic_names[i]);
return NULL;
}
pci_dev = pci_create(bus, devfn, pci_nic_names[i]);
dev = &pci_dev->qdev;
- if (nd->name)
- dev->id = qemu_strdup(nd->name);
qdev_set_nic_properties(dev, nd);
if (qdev_init(dev) < 0)
return NULL;
{
PCIBus *sec;
- if (!bus)
+ if (!bus) {
return NULL;
+ }
if (pci_bus_num(bus) == bus_num) {
return bus;
}
/* try child bus */
- QLIST_FOREACH(sec, &bus->child, sibling) {
-
- if (!bus->parent_dev /* pci host bridge */
- || (pci_bus_num(sec) <= bus_num &&
- bus->parent_dev->config[PCI_SUBORDINATE_BUS])) {
- return pci_find_bus(sec, bus_num);
+ if (!bus->parent_dev /* host pci bridge */ ||
+ (bus->parent_dev->config[PCI_SECONDARY_BUS] < bus_num &&
+ bus_num <= bus->parent_dev->config[PCI_SUBORDINATE_BUS])) {
+ for (; bus; bus = sec) {
+ QLIST_FOREACH(sec, &bus->child, sibling) {
+ assert(sec->parent_dev);
+ if (sec->parent_dev->config[PCI_SECONDARY_BUS] == bus_num) {
+ return sec;
+ }
+ if (sec->parent_dev->config[PCI_SECONDARY_BUS] < bus_num &&
+ bus_num <= sec->parent_dev->config[PCI_SUBORDINATE_BUS]) {
+ break;
+ }
+ }
}
}
if (pci_dev == NULL)
return -1;
rc = info->init(pci_dev);
- if (rc != 0)
+ if (rc != 0) {
+ do_pci_unregister_device(pci_dev);
return rc;
+ }
/* rom loading */
if (pci_dev->romfile == NULL && info->romfile != NULL)
pci_add_option_rom(pci_dev);
if (qdev->hotplugged)
- bus->hotplug(pci_dev, 1);
+ bus->hotplug(bus->hotplug_qdev, pci_dev, 1);
return 0;
}
{
PCIDevice *dev = DO_UPCAST(PCIDevice, qdev, qdev);
- dev->bus->hotplug(dev, 0);
+ dev->bus->hotplug(dev->bus->hotplug_qdev, dev, 0);
return 0;
}
if (strlen(pdev->romfile) == 0)
return 0;
+ if (!pdev->rom_bar) {
+ /*
+ * Load rom via fw_cfg instead of creating a rom bar,
+ * for 0.11 compatibility.
+ */
+ int class = pci_get_word(pdev->config + PCI_CLASS_DEVICE);
+ if (class == 0x0300) {
+ rom_add_vga(pdev->romfile);
+ } else {
+ rom_add_option(pdev->romfile);
+ }
+ return 0;
+ }
+
path = qemu_find_file(QEMU_FILE_TYPE_BIOS, pdev->romfile);
if (path == NULL) {
path = qemu_strdup(pdev->romfile);
size = get_image_size(path);
if (size < 0) {
- qemu_error("%s: failed to find romfile \"%s\"\n", __FUNCTION__,
- pdev->romfile);
+ error_report("%s: failed to find romfile \"%s\"",
+ __FUNCTION__, pdev->romfile);
return -1;
}
if (size & (size - 1)) {
}
/* Reserve space and add capability to the linked list in pci config space */
-int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
+int pci_add_capability_at_offset(PCIDevice *pdev, uint8_t cap_id,
+ uint8_t offset, uint8_t size)
{
- uint8_t offset = pci_find_space(pdev, size);
uint8_t *config = pdev->config + offset;
- if (!offset)
- return -ENOSPC;
config[PCI_CAP_LIST_ID] = cap_id;
config[PCI_CAP_LIST_NEXT] = pdev->config[PCI_CAPABILITY_LIST];
pdev->config[PCI_CAPABILITY_LIST] = offset;
return offset;
}
+/* Find and reserve space and add capability to the linked list
+ * in pci config space */
+int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
+{
+ uint8_t offset = pci_find_space(pdev, size);
+ if (!offset) {
+ return -ENOSPC;
+ }
+ return pci_add_capability_at_offset(pdev, cap_id, offset, size);
+}
+
/* Unlink capability from the pci config space. */
void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size)
{
.init = pci_bridge_initfn,
.exit = pci_bridge_exitfn,
.config_write = pci_bridge_write_config,
+ .header_type = PCI_HEADER_TYPE_BRIDGE,
.qdev.props = (Property[]) {
DEFINE_PROP_HEX32("vendorid", PCIBridge, vid, 0),
DEFINE_PROP_HEX32("deviceid", PCIBridge, did, 0),