QEMU源码全解析 —— PCI设备模拟(3)

88 篇文章 19 订阅
本文继续上篇,深入解析QEMU如何模拟PCI设备。主要讨论pci_qdev_realize函数的第二部分,包括调用PCI设备类的realize函数,并逐步剖析DECLARE_OBJ_CHECKERS、PCI_DEVICE_GET_CLASS等相关宏的实现细节。
摘要由CSDN通过智能技术生成

接前一篇文章:

2. PCI 设备的模拟

QEMU 模拟的设备很多都是PCI设备,本节介绍PCI设备的模拟。与所有设备类似,PCI设备的父设备也是TYPE_DEVICE,其定义在QEMU源码根目录/hw/pci/pci.c中,代码如下:

  1. static const TypeInfo pci_device_type_info = {
  2. .name = TYPE_PCI_DEVICE,
  3. .parent = TYPE_DEVICE,
  4. .instance_size = sizeof(PCIDevice),
  5. .abstract = true,
  6. .class_size = sizeof(PCIDeviceClass),
  7. .class_init = pci_device_class_init,
  8. .class_base_init = pci_device_class_base_init,
  9. };
  10. static void pci_register_types(void)
  11. {
  12. type_register_static(&pci_bus_info);
  13. type_register_static(&pcie_bus_info);
  14. type_register_static(&cxl_bus_info);
  15. type_register_static(&conventional_pci_interface_info);
  16. type_register_static(&cxl_interface_info);
  17. type_register_static(&pcie_interface_info);
  18. type_register_static(&pci_device_type_info);
  19. }
  20. type_init(pci_register_types)

上一回讲到了PCI设备的具现化函数pci_qdev_realize(),讲解了pci_qdev_realize函数所完成的三个任务中的第一个,本回接着往下讲。为了便于理解和回顾,再次贴出该函数源码,在hw/pci/pci.c中,如下:

  1. static void pci_qdev_realize(DeviceState *qdev, Error **errp)
  2. {
  3. PCIDevice *pci_dev = (PCIDevice *)qdev;
  4. PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev);
  5. ObjectClass *klass = OBJECT_CLASS(pc);
  6. Error *local_err = NULL;
  7. bool is_default_rom;
  8. uint16_t class_id;
  9. /*
  10. * capped by systemd (see: udev-builtin-net_id.c)
  11. * as it's the only known user honor it to avoid users
  12. * misconfigure QEMU and then wonder why acpi-index doesn't work
  13. */
  14. if (pci_dev->acpi_index > ONBOARD_INDEX_MAX) {
  15. error_setg(errp, "acpi-index should be less or equal to %u",
  16. ONBOARD_INDEX_MAX);
  17. return;
  18. }
  19. /*
  20. * make sure that acpi-index is unique across all present PCI devices
  21. */
  22. if (pci_dev->acpi_index) {
  23. GSequence *used_indexes = pci_acpi_index_list();
  24. if (g_sequence_lookup(used_indexes,
  25. GINT_TO_POINTER(pci_dev->acpi_index),
  26. g_cmp_uint32, NULL)) {
  27. error_setg(errp, "a PCI device with acpi-index = %" PRIu32
  28. " already exist", pci_dev->acpi_index);
  29. return;
  30. }
  31. g_sequence_insert_sorted(used_indexes,
  32. GINT_TO_POINTER(pci_dev->acpi_index),
  33. g_cmp_uint32, NULL);
  34. }
  35. if (pci_dev->romsize != -1 && !is_power_of_2(pci_dev->romsize)) {
  36. error_setg(errp, "ROM size %u is not a power of two", pci_dev->romsize);
  37. return;
  38. }
  39. /* initialize cap_present for pci_is_express() and pci_config_size(),
  40. * Note that hybrid PCIs are not set automatically and need to manage
  41. * QEMU_PCI_CAP_EXPRESS manually */
  42. if (object_class_dynamic_cast(klass, INTERFACE_PCIE_DEVICE) &&
  43. !object_class_dynamic_cast(klass, INTERFACE_CONVENTIONAL_PCI_DEVICE)) {
  44. pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
  45. }
  46. if (object_class_dynamic_cast(klass, INTERFACE_CXL_DEVICE)) {
  47. pci_dev->cap_present |= QEMU_PCIE_CAP_CXL;
  48. }
  49. pci_dev = do_pci_register_device(pci_dev,
  50. object_get_typename(OBJECT(qdev)),
  51. pci_dev->devfn, errp);
  52. if (pci_dev == NULL)
  53. return;
  54. if (pc->realize) {
  55. pc->realize(pci_dev, &local_err);
  56. if (local_err) {
  57. error_propagate(errp, local_err);
  58. do_pci_unregister_device(pci_dev);
  59. return;
  60. }
  61. }
  62. /*
  63. * A PCIe Downstream Port that do not have ARI Forwarding enabled must
  64. * associate only Device 0 with the device attached to the bus
  65. * representing the Link from the Port (PCIe base spec rev 4.0 ver 0.3,
  66. * sec 7.3.1).
  67. * With ARI, PCI_SLOT() can return non-zero value as the traditional
  68. * 5-bit Device Number and 3-bit Function Number fields in its associated
  69. * Routing IDs, Requester IDs and Completer IDs are interpreted as a
  70. * single 8-bit Function Number. Hence, ignore ARI capable devices.
  71. */
  72. if (pci_is_express(pci_dev) &&
  73. !pcie_find_capability(pci_dev, PCI_EXT_CAP_ID_ARI) &&
  74. pcie_has_upstream_port(pci_dev) &&
  75. PCI_SLOT(pci_dev->devfn)) {
  76. warn_report("PCI: slot %d is not valid for %s,"
  77. " parent device only allows plugging into slot 0.",
  78. PCI_SLOT(pci_dev->devfn), pci_dev->name);
  79. }
  80. if (pci_dev->failover_pair_id) {
  81. if (!pci_bus_is_express(pci_get_bus(pci_dev))) {
  82. error_setg(errp, "failover primary device must be on "
  83. "PCIExpress bus");
  84. pci_qdev_unrealize(DEVICE(pci_dev));
  85. return;
  86. }
  87. class_id = pci_get_word(pci_dev->config + PCI_CLASS_DEVICE);
  88. if (class_id != PCI_CLASS_NETWORK_ETHERNET) {
  89. error_setg(errp, "failover primary device is not an "
  90. "Ethernet device");
  91. pci_qdev_unrealize(DEVICE(pci_dev));
  92. return;
  93. }
  94. if ((pci_dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION)
  95. || (PCI_FUNC(pci_dev->devfn) != 0)) {
  96. error_setg(errp, "failover: primary device must be in its own "
  97. "PCI slot");
  98. pci_qdev_unrealize(DEVICE(pci_dev));
  99. return;
  100. }
  101. qdev->allow_unplug_during_migration = true;
  102. }
  103. /* rom loading */
  104. is_default_rom = false;
  105. if (pci_dev->romfile == NULL && pc->romfile != NULL) {
  106. pci_dev->romfile = g_strdup(pc->romfile);
  107. is_default_rom = true;
  108. }
  109. pci_add_option_rom(pci_dev, is_default_rom, &local_err);
  110. if (local_err) {
  111. error_propagate(errp, local_err);
  112. pci_qdev_unrealize(DEVICE(pci_dev));
  113. return;
  114. }
  115. pci_set_power(pci_dev, true);
  116. pci_dev->msi_trigger = pci_msi_trigger;
  117. }

(2)其次,pci_qdev_realize函数调用PCI设备所属的class的realize函数,即pc->realize函数。

代码片段如下:

  1. if (pc->realize) {
  2. pc->realize(pci_dev, &local_err);
  3. if (local_err) {
  4. error_propagate(errp, local_err);
  5. do_pci_unregister_device(pci_dev);
  6. return;
  7. }
  8. }

pc是PCIDeviceClass结构类型的指针,PCIDeviceClass的定义在include/hw/pci/pci_device.h中,代码如下:

  1. #define TYPE_PCI_DEVICE "pci-device"
  2. typedef struct PCIDeviceClass PCIDeviceClass;
  3. DECLARE_OBJ_CHECKERS(PCIDevice, PCIDeviceClass,
  4. PCI_DEVICE, TYPE_PCI_DEVICE)

而struct PCIDeviceClass的定义也在include/hw/pci/pci_device.h中,代码如下:

  1. struct PCIDeviceClass {
  2. DeviceClass parent_class;
  3. void (*realize)(PCIDevice *dev, Error **errp);
  4. PCIUnregisterFunc *exit;
  5. PCIConfigReadFunc *config_read;
  6. PCIConfigWriteFunc *config_write;
  7. uint16_t vendor_id;
  8. uint16_t device_id;
  9. uint8_t revision;
  10. uint16_t class_id;
  11. uint16_t subsystem_vendor_id; /* only for header type = 0 */
  12. uint16_t subsystem_id; /* only for header type = 0 */
  13. const char *romfile; /* rom bar */
  14. };

PCI_DEVICE_GET_CLASS是一个宏定义,但是在QEMU源码中搜素怎么都搜不到。笔者开始以为是在 Linux 内核源码中的定义,后来在Linux源码中搜索却也并没有搜到。后来几经周折,终于弄清楚了其具体细节。

要弄清楚PCI_DEVICE_GET_CLASS,得先从DECLARE_OBJ_CHECKERS讲起。DECLARE_OBJ_CHECKERS的相关代码在include/hw/pci/pci_device.h中:

  1. #define TYPE_PCI_DEVICE "pci-device"
  2. typedef struct PCIDeviceClass PCIDeviceClass;
  3. DECLARE_OBJ_CHECKERS(PCIDevice, PCIDeviceClass,
  4. PCI_DEVICE, TYPE_PCI_DEVICE)

DECLARE_OBJ_CHECKERS的定义在include/qom/object.h中,代码如下:

  1. /**
  2. * DECLARE_OBJ_CHECKERS:
  3. * @InstanceType: instance struct name
  4. * @ClassType: class struct name
  5. * @OBJ_NAME: the object name in uppercase with underscore separators
  6. * @TYPENAME: type name
  7. *
  8. * Direct usage of this macro should be avoided, and the complete
  9. * OBJECT_DECLARE_TYPE macro is recommended instead.
  10. *
  11. * This macro will provide the three standard type cast functions for a
  12. * QOM type.
  13. */
  14. #define DECLARE_OBJ_CHECKERS(InstanceType, ClassType, OBJ_NAME, TYPENAME) \
  15. DECLARE_INSTANCE_CHECKER(InstanceType, OBJ_NAME, TYPENAME) \
  16. \
  17. DECLARE_CLASS_CHECKERS(ClassType, OBJ_NAME, TYPENAME)

代入实际值并展开,得到:

  1. DECLARE_INSTANCE_CHECKER(PCIDevice, PCI_DEVICE, TYPE_PCI_DEVICE)
  2. DECLARE_CLASS_CHECKERS(PCIDeviceClass, PCI_DEVICE, TYPE_PCI_DEVICE)

DECLARE_INSTANCE_CHECKER宏定义也在include/qom/object.h中,代码如下:

  1. /**
  2. * DECLARE_INSTANCE_CHECKER:
  3. * @InstanceType: instance struct name
  4. * @OBJ_NAME: the object name in uppercase with underscore separators
  5. * @TYPENAME: type name
  6. *
  7. * Direct usage of this macro should be avoided, and the complete
  8. * OBJECT_DECLARE_TYPE macro is recommended instead.
  9. *
  10. * This macro will provide the instance type cast functions for a
  11. * QOM type.
  12. */
  13. #define DECLARE_INSTANCE_CHECKER(InstanceType, OBJ_NAME, TYPENAME) \
  14. static inline G_GNUC_UNUSED InstanceType * \
  15. OBJ_NAME(const void *obj) \
  16. { return OBJECT_CHECK(InstanceType, obj, TYPENAME); }

代入实际值并展开得:

  1. static inline G_GNUC_UNUSED PCIDevice* PCI_DEVICE(const void *obj)
  2. {
  3. return OBJECT_CHECK(PCIDevice, obj, TYPE_PCI_DEVICE);
  4. }

DECLARE_CLASS_CHECKERS宏定义也在include/qom/object.h中,代码如下:

  1. /**
  2. * DECLARE_CLASS_CHECKERS:
  3. * @ClassType: class struct name
  4. * @OBJ_NAME: the object name in uppercase with underscore separators
  5. * @TYPENAME: type name
  6. *
  7. * Direct usage of this macro should be avoided, and the complete
  8. * OBJECT_DECLARE_TYPE macro is recommended instead.
  9. *
  10. * This macro will provide the class type cast functions for a
  11. * QOM type.
  12. */
  13. #define DECLARE_CLASS_CHECKERS(ClassType, OBJ_NAME, TYPENAME) \
  14. static inline G_GNUC_UNUSED ClassType * \
  15. OBJ_NAME##_GET_CLASS(const void *obj) \
  16. { return OBJECT_GET_CLASS(ClassType, obj, TYPENAME); } \
  17. \
  18. static inline G_GNUC_UNUSED ClassType * \
  19. OBJ_NAME##_CLASS(const void *klass) \
  20. { return OBJECT_CLASS_CHECK(ClassType, klass, TYPENAME); }

代入实际值并展开得:

  1. static inline G_GNUC_UNUSED PCIDeviceClass* PCI_DEVICE_GET_CLASS(const void *obj)
  2. {
  3. return OBJECT_GET_CLASS(PCIDeviceClass, obj, TYPE_PCI_DEVICE);
  4. }
  5. static inline G_GNUC_UNUSED PCIDeviceClass* PCI_DEVICE_CLASS(const void *klass)
  6. {
  7. return OBJECT_CLASS_CHECK(PCIDeviceClass, klass, TYPE_PCI_DEVICE);
  8. }

综合两式,最终得到:

  1. static inline G_GNUC_UNUSED PCIDevice* PCI_DEVICE(const void *obj)
  2. {
  3. return OBJECT_CHECK(PCIDevice, obj, TYPE_PCI_DEVICE);
  4. }
  5. static inline G_GNUC_UNUSED PCIDeviceClass* PCI_DEVICE_GET_CLASS(const void *obj)
  6. {
  7. return OBJECT_GET_CLASS(PCIDeviceClass, obj, TYPE_PCI_DEVICE);
  8. }
  9. static inline G_GNUC_UNUSED PCIDeviceClass* PCI_DEVICE_CLASS(const void *klass)
  10. {
  11. return OBJECT_CLASS_CHECK(PCIDeviceClass, klass, TYPE_PCI_DEVICE);
  12. }

这样,最终就得到了也可以说生成出了PCI_DEVICE_GET_CLASS。

再深入跟进一点,OBJECT_CHECK、OBJECT_GET_CLASS、OBJECT_CLASS_CHECK三个宏的定义都在include/qom/object.h中,代码如下:

  1. /**
  2. * OBJECT_CHECK:
  3. * @type: The C type to use for the return value.
  4. * @obj: A derivative of @type to cast.
  5. * @name: The QOM typename of @type
  6. *
  7. * A type safe version of @object_dynamic_cast_assert. Typically each class
  8. * will define a macro based on this type to perform type safe dynamic_casts to
  9. * this object type.
  10. *
  11. * If an invalid object is passed to this function, a run time assert will be
  12. * generated.
  13. */
  14. #define OBJECT_CHECK(type, obj, name) \
  15. ((type *)object_dynamic_cast_assert(OBJECT(obj), (name), \
  16. __FILE__, __LINE__, __func__))
  17. /**
  18. * OBJECT_CLASS_CHECK:
  19. * @class_type: The C type to use for the return value.
  20. * @class: A derivative class of @class_type to cast.
  21. * @name: the QOM typename of @class_type.
  22. *
  23. * A type safe version of @object_class_dynamic_cast_assert. This macro is
  24. * typically wrapped by each type to perform type safe casts of a class to a
  25. * specific class type.
  26. */
  27. #define OBJECT_CLASS_CHECK(class_type, class, name) \
  28. ((class_type *)object_class_dynamic_cast_assert(OBJECT_CLASS(class), (name), \
  29. __FILE__, __LINE__, __func__))
  30. /**
  31. * OBJECT_GET_CLASS:
  32. * @class: The C type to use for the return value.
  33. * @obj: The object to obtain the class for.
  34. * @name: The QOM typename of @obj.
  35. *
  36. * This function will return a specific class for a given object. Its generally
  37. * used by each type to provide a type safe macro to get a specific class type
  38. * from an object.
  39. */
  40. #define OBJECT_GET_CLASS(class, obj, name) \
  41. OBJECT_CLASS_CHECK(class, object_get_class(OBJECT(obj)), name)

至此,pci_qdev_realize函数所做的第二方面工作即所调用的第2个函数也就是PCI设备所属的class的realize函数,即pc->realize函数就解析完了。

欲知后事如何,且看下回分解。

举报

选择你想要举报的内容(必选)
  • 内容涉黄
  • 政治相关
  • 内容抄袭
  • 涉嫌广告
  • 内容侵权
  • 侮辱谩骂
  • 样式问题
  • 其他