比如,对于这样的配置:
-device pcie-root-port,port=0x8,chassis=1,id=pci.1,bus=pcie.0,multifunction=on,addr=0x1 \
-device pcie-root-port,port=0x9,chassis=2,id=pci.2,bus=pcie.0,addr=0x1.0x1 \
-device pcie-root-port,port=0xa,chassis=3,id=pci.3,bus=pcie.0,addr=0x1.0x2 \
-device pcie-root-port,port=0xb,chassis=4,id=pci.4,bus=pcie.0,addr=0x1.0x3 \
-device virtio-blk-pci,scsi=off,bus=pci.4,addr=0x0,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1
其中,对于-device virtio-blk-pci,scsi=off,bus=pci.4,addr=0x0,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1
这个设备的bus=pci.4
是怎么找到的关联的pcie-root-port
设备的呢
首先,我们看下virtio-blk-pci
这个设备的初始化过程,如下:
main
qemu_init
qmp_x_exit_preconfig
qemu_create_cli_devices
device_init_func
qdev_device_add
qdev_device_add_from_qdict
找bus是在如下片段进行的:
/* find bus */
path = qdict_get_try_str(opts, "bus");
if (path != NULL) {
bus = qbus_find(path, errp);
if (!bus) {
return NULL;
}
if (!object_dynamic_cast(OBJECT(bus), dc->bus_type)) {
error_setg(errp, "Device '%s' can't go on %s bus",
driver, object_get_typename(OBJECT(bus)));
return NULL;
}
} else if (dc->bus_type != NULL) {
bus = qbus_find_recursive(sysbus_get_default(), NULL, dc->bus_type);
if (!bus || qbus_is_full(bus)) {
error_setg(errp, "No '%s' bus found for device '%s'",
dc->bus_type, driver);
return NULL;
}
}
而qbus_find
的实现没有什么特殊的,基本上就是字符串匹配,那现在关键就是找到pcie-root-port
创建bus的地方,而要找到这点却确颇为不易!很明显,pcie-root-port
设备是通过id
属性创建的bus,而id
属性只在pcie-root-port
这个流程被拿出来用了一下
/*
* set dev's parent and register its id.
* If it fails it means the id is already taken.
*/
id = g_strdup(qdict_get_try_str(opts, "id"));
if (!qdev_set_id(dev, id, errp)) {
goto err_del_dev;
}
而qdev_set_id
实现也很简单:
/* Takes ownership of @id, will be freed when deleting the device */
const char *qdev_set_id(DeviceState *dev, char *id, Error **errp)
{
ObjectProperty *prop;
assert(!dev->id && !dev->realized);
/*
* object_property_[try_]add_child() below will assert the device
* has no parent
*/
if (id) {
prop = object_property_try_add_child(qdev_get_peripheral(), id,
OBJECT(dev), NULL);
if (prop) {
dev->id = id;
} else {
error_setg(errp, "Duplicate device ID '%s'", id);
g_free(id);
return NULL;
}
} else {
static int anon_count;
gchar *name = g_strdup_printf("device[%d]", anon_count++);
prop = object_property_add_child(qdev_get_peripheral_anon(), name,
OBJECT(dev));
g_free(name);
}
return prop->name;
}
高亮的感觉是比较有关的一行,这时,关键是找到在哪里用的这个id
,经过苦苦搜寻,终于找到
void pci_bridge_initfn(PCIDevice *dev, const char *typename)
{
PCIBus *parent = pci_get_bus(dev);
PCIBridge *br = PCI_BRIDGE(dev);
PCIBus *sec_bus = &br->sec_bus;
pci_word_test_and_set_mask(dev->config + PCI_STATUS,
PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
/*
* TODO: We implement VGA Enable in the Bridge Control Register
* therefore per the PCI to PCI bridge spec we must also implement
* VGA Palette Snooping. When done, set this bit writable:
*
* pci_word_test_and_set_mask(dev->wmask + PCI_COMMAND,
* PCI_COMMAND_VGA_PALETTE);
*/
pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI);
dev->config[PCI_HEADER_TYPE] =
(dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) |
PCI_HEADER_TYPE_BRIDGE;
pci_set_word(dev->config + PCI_SEC_STATUS,
PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK);
/*
* If we don't specify the name, the bus will be addressed as <id>.0, where
* id is the device id.
* Since PCI Bridge devices have a single bus each, we don't need the index:
* let users address the bus using the device name.
*/
if (!br->bus_name && dev->qdev.id && *dev->qdev.id) {
br->bus_name = dev->qdev.id;
}
qbus_init(sec_bus, sizeof(br->sec_bus), typename, DEVICE(dev),
br->bus_name);
sec_bus->parent_dev = dev;
sec_bus->map_irq = br->map_irq ? br->map_irq : pci_swizzle_map_irq_fn;
sec_bus->address_space_mem = &br->address_space_mem;
memory_region_init(&br->address_space_mem, OBJECT(br), "pci_bridge_pci", UINT64_MAX);
sec_bus->address_space_io = &br->address_space_io;
memory_region_init(&br->address_space_io, OBJECT(br), "pci_bridge_io",
4 * GiB);
br->windows = pci_bridge_region_init(br);
QLIST_INIT(&sec_bus->child);
QLIST_INSERT_HEAD(&parent->child, sec_bus, sibling);
}