searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

qemu的pci设备id和引用设备bus号对应关系简析

2023-11-28 03:07:18
142
0

比如,对于这样的配置:

-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);
}
0条评论
0 / 1000
李****锋
5文章数
0粉丝数
李****锋
5 文章 | 0 粉丝
原创

qemu的pci设备id和引用设备bus号对应关系简析

2023-11-28 03:07:18
142
0

比如,对于这样的配置:

-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);
}
文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0