QEMU源码全解析 —— virtio(15)

88 篇文章 19 订阅
本文详细介绍了QEMU virtio_pci_device_plugged函数的实现,包括virtio_pci_modern_regions_init初始化MemoryRegion和virtio_pci_modern_mem_region_map映射内存区域的过程,探讨了在虚拟机中如何处理MMIO操作。
摘要由CSDN通过智能技术生成

接前一篇文章:

上一回讲解了virtio_pci_device_plugged函数的前两部分,本回继续讲解virtio_pci_device_plugged函数的其余部分。为了便于理解,再次贴出virtio_pci_device_plugged函数源码,在hw/ virtio /virtio-pci.c中,如下:

  1. /* This is called by virtio-bus just after the device is plugged. */
  2. static void virtio_pci_device_plugged(DeviceState *d, Error **errp)
  3. {
  4. VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
  5. VirtioBusState *bus = &proxy->bus;
  6. bool legacy = virtio_pci_legacy(proxy);
  7. bool modern;
  8. bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY;
  9. uint8_t *config;
  10. uint32_t size;
  11. VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
  12. /*
  13. * Virtio capabilities present without
  14. * VIRTIO_F_VERSION_1 confuses guests
  15. */
  16. if (!proxy->ignore_backend_features &&
  17. !virtio_has_feature(vdev->host_features, VIRTIO_F_VERSION_1)) {
  18. virtio_pci_disable_modern(proxy);
  19. if (!legacy) {
  20. error_setg(errp, "Device doesn't support modern mode, and legacy"
  21. " mode is disabled");
  22. error_append_hint(errp, "Set disable-legacy to off\n");
  23. return;
  24. }
  25. }
  26. modern = virtio_pci_modern(proxy);
  27. config = proxy->pci_dev.config;
  28. if (proxy->class_code) {
  29. pci_config_set_class(config, proxy->class_code);
  30. }
  31. if (legacy) {
  32. if (!virtio_legacy_allowed(vdev)) {
  33. /*
  34. * To avoid migration issues, we allow legacy mode when legacy
  35. * check is disabled in the old machine types (< 5.1).
  36. */
  37. if (virtio_legacy_check_disabled(vdev)) {
  38. warn_report("device is modern-only, but for backward "
  39. "compatibility legacy is allowed");
  40. } else {
  41. error_setg(errp,
  42. "device is modern-only, use disable-legacy=on");
  43. return;
  44. }
  45. }
  46. if (virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) {
  47. error_setg(errp, "VIRTIO_F_IOMMU_PLATFORM was supported by"
  48. " neither legacy nor transitional device");
  49. return;
  50. }
  51. /*
  52. * Legacy and transitional devices use specific subsystem IDs.
  53. * Note that the subsystem vendor ID (config + PCI_SUBSYSTEM_VENDOR_ID)
  54. * is set to PCI_SUBVENDOR_ID_REDHAT_QUMRANET by default.
  55. */
  56. pci_set_word(config + PCI_SUBSYSTEM_ID, virtio_bus_get_vdev_id(bus));
  57. if (proxy->trans_devid) {
  58. pci_config_set_device_id(config, proxy->trans_devid);
  59. }
  60. } else {
  61. /* pure virtio-1.0 */
  62. pci_set_word(config + PCI_VENDOR_ID,
  63. PCI_VENDOR_ID_REDHAT_QUMRANET);
  64. pci_set_word(config + PCI_DEVICE_ID,
  65. PCI_DEVICE_ID_VIRTIO_10_BASE + virtio_bus_get_vdev_id(bus));
  66. pci_config_set_revision(config, 1);
  67. }
  68. config[PCI_INTERRUPT_PIN] = 1;
  69. if (modern) {
  70. struct virtio_pci_cap cap = {
  71. .cap_len = sizeof cap,
  72. };
  73. struct virtio_pci_notify_cap notify = {
  74. .cap.cap_len = sizeof notify,
  75. .notify_off_multiplier =
  76. cpu_to_le32(virtio_pci_queue_mem_mult(proxy)),
  77. };
  78. struct virtio_pci_cfg_cap cfg = {
  79. .cap.cap_len = sizeof cfg,
  80. .cap.cfg_type = VIRTIO_PCI_CAP_PCI_CFG,
  81. };
  82. struct virtio_pci_notify_cap notify_pio = {
  83. .cap.cap_len = sizeof notify,
  84. .notify_off_multiplier = cpu_to_le32(0x0),
  85. };
  86. struct virtio_pci_cfg_cap *cfg_mask;
  87. virtio_pci_modern_regions_init(proxy, vdev->name);
  88. virtio_pci_modern_mem_region_map(proxy, &proxy->common, &cap);
  89. virtio_pci_modern_mem_region_map(proxy, &proxy->isr, &cap);
  90. virtio_pci_modern_mem_region_map(proxy, &proxy->device, &cap);
  91. virtio_pci_modern_mem_region_map(proxy, &proxy->notify, &notify.cap);
  92. if (modern_pio) {
  93. memory_region_init(&proxy->io_bar, OBJECT(proxy),
  94. "virtio-pci-io", 0x4);
  95. pci_register_bar(&proxy->pci_dev, proxy->modern_io_bar_idx,
  96. PCI_BASE_ADDRESS_SPACE_IO, &proxy->io_bar);
  97. virtio_pci_modern_io_region_map(proxy, &proxy->notify_pio,
  98. &notify_pio.cap);
  99. }
  100. pci_register_bar(&proxy->pci_dev, proxy->modern_mem_bar_idx,
  101. PCI_BASE_ADDRESS_SPACE_MEMORY |
  102. PCI_BASE_ADDRESS_MEM_PREFETCH |
  103. PCI_BASE_ADDRESS_MEM_TYPE_64,
  104. &proxy->modern_bar);
  105. proxy->config_cap = virtio_pci_add_mem_cap(proxy, &cfg.cap);
  106. cfg_mask = (void *)(proxy->pci_dev.wmask + proxy->config_cap);
  107. pci_set_byte(&cfg_mask->cap.bar, ~0x0);
  108. pci_set_long((uint8_t *)&cfg_mask->cap.offset, ~0x0);
  109. pci_set_long((uint8_t *)&cfg_mask->cap.length, ~0x0);
  110. pci_set_long(cfg_mask->pci_cfg_data, ~0x0);
  111. }
  112. if (proxy->nvectors) {
  113. int err = msix_init_exclusive_bar(&proxy->pci_dev, proxy->nvectors,
  114. proxy->msix_bar_idx, NULL);
  115. if (err) {
  116. /* Notice when a system that supports MSIx can't initialize it */
  117. if (err != -ENOTSUP) {
  118. warn_report("unable to init msix vectors to %" PRIu32,
  119. proxy->nvectors);
  120. }
  121. proxy->nvectors = 0;
  122. }
  123. }
  124. proxy->pci_dev.config_write = virtio_write_config;
  125. proxy->pci_dev.config_read = virtio_read_config;
  126. if (legacy) {
  127. size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev)
  128. + virtio_bus_get_vdev_config_len(bus);
  129. size = pow2ceil(size);
  130. memory_region_init_io(&proxy->bar, OBJECT(proxy),
  131. &virtio_pci_config_ops,
  132. proxy, "virtio-pci", size);
  133. pci_register_bar(&proxy->pci_dev, proxy->legacy_io_bar_idx,
  134. PCI_BASE_ADDRESS_SPACE_IO, &proxy->bar);
  135. }
  136. }

2.1)virtio_pci_modern_regions_init函数 初始化 5个MemoryRegion,分别是virtio-pci-common、virtio-pci-isr、virtio-pci-device、virtio-pci-notify和virtio-pci-notify-pio。代码片段如下:

    virtio_pci_modern_regions_init(proxy, vdev->name);

virtio_pci_modern_regions_init函数在hw/virtio/virtio- pci .c中,代码如下:

  1. static void virtio_pci_modern_regions_init(VirtIOPCIProxy *proxy,
  2. const char *vdev_name)
  3. {
  4. static const MemoryRegionOps common_ops = {
  5. .read = virtio_pci_common_read,
  6. .write = virtio_pci_common_write,
  7. .impl = {
  8. .min_access_size = 1,
  9. .max_access_size = 4,
  10. },
  11. .endianness = DEVICE_LITTLE_ENDIAN,
  12. };
  13. static const MemoryRegionOps isr_ops = {
  14. .read = virtio_pci_isr_read,
  15. .write = virtio_pci_isr_write,
  16. .impl = {
  17. .min_access_size = 1,
  18. .max_access_size = 4,
  19. },
  20. .endianness = DEVICE_LITTLE_ENDIAN,
  21. };
  22. static const MemoryRegionOps device_ops = {
  23. .read = virtio_pci_device_read,
  24. .write = virtio_pci_device_write,
  25. .impl = {
  26. .min_access_size = 1,
  27. .max_access_size = 4,
  28. },
  29. .endianness = DEVICE_LITTLE_ENDIAN,
  30. };
  31. static const MemoryRegionOps notify_ops = {
  32. .read = virtio_pci_notify_read,
  33. .write = virtio_pci_notify_write,
  34. .impl = {
  35. .min_access_size = 1,
  36. .max_access_size = 4,
  37. },
  38. .endianness = DEVICE_LITTLE_ENDIAN,
  39. };
  40. static const MemoryRegionOps notify_pio_ops = {
  41. .read = virtio_pci_notify_read,
  42. .write = virtio_pci_notify_write_pio,
  43. .impl = {
  44. .min_access_size = 1,
  45. .max_access_size = 4,
  46. },
  47. .endianness = DEVICE_LITTLE_ENDIAN,
  48. };
  49. g_autoptr(GString) name = g_string_new(NULL);
  50. g_string_printf(name, "virtio-pci-common-%s", vdev_name);
  51. memory_region_init_io(&proxy->common.mr, OBJECT(proxy),
  52. &common_ops,
  53. proxy,
  54. name->str,
  55. proxy->common.size);
  56. g_string_printf(name, "virtio-pci-isr-%s", vdev_name);
  57. memory_region_init_io(&proxy->isr.mr, OBJECT(proxy),
  58. &isr_ops,
  59. proxy,
  60. name->str,
  61. proxy->isr.size);
  62. g_string_printf(name, "virtio-pci-device-%s", vdev_name);
  63. memory_region_init_io(&proxy->device.mr, OBJECT(proxy),
  64. &device_ops,
  65. proxy,
  66. name->str,
  67. proxy->device.size);
  68. g_string_printf(name, "virtio-pci-notify-%s", vdev_name);
  69. memory_region_init_io(&proxy->notify.mr, OBJECT(proxy),
  70. &notify_ops,
  71. proxy,
  72. name->str,
  73. proxy->notify.size);
  74. g_string_printf(name, "virtio-pci-notify-pio-%s", vdev_name);
  75. memory_region_init_io(&proxy->notify_pio.mr, OBJECT(proxy),
  76. &notify_pio_ops,
  77. proxy,
  78. name->str,
  79. proxy->notify_pio.size);
  80. }

VirtIOPCIProxy结构的定义在include/hw/virtio/virtio-pci.h中,如下:

  1. struct VirtIOPCIProxy {
  2. PCIDevice pci_dev;
  3. MemoryRegion bar;
  4. union {
  5. struct {
  6. VirtIOPCIRegion common;
  7. VirtIOPCIRegion isr;
  8. VirtIOPCIRegion device;
  9. VirtIOPCIRegion notify;
  10. VirtIOPCIRegion notify_pio;
  11. };
  12. VirtIOPCIRegion regs[5];
  13. };
  14. MemoryRegion modern_bar;
  15. MemoryRegion io_bar;
  16. uint32_t legacy_io_bar_idx;
  17. uint32_t msix_bar_idx;
  18. uint32_t modern_io_bar_idx;
  19. uint32_t modern_mem_bar_idx;
  20. int config_cap;
  21. uint32_t flags;
  22. bool disable_modern;
  23. bool ignore_backend_features;
  24. OnOffAuto disable_legacy;
  25. /* Transitional device id */
  26. uint16_t trans_devid;
  27. uint32_t class_code;
  28. uint32_t nvectors;
  29. uint32_t dfselect;
  30. uint32_t gfselect;
  31. uint32_t guest_features[2];
  32. VirtIOPCIQueue vqs[VIRTIO_QUEUE_MAX];
  33. VirtIOIRQFD *vector_irqfd;
  34. int nvqs_with_notifiers;
  35. VirtioBusState bus;
  36. };

virtio-pci-common、virtio-pci-isr、virtio-pci-device、virtio-pci-notify和virtio-pci-notify-pio这5个MemoryRegion的相关信息存放在VirtIOPCIProxy结构中的几个VirtIOPCIRegion成员中。

2.2)virtio_pci_device_plugged接下来调用virtio_pci_modern_mem_region_map函。代码片段如下:

  1. virtio_pci_modern_mem_region_map(proxy, &proxy->common, &cap);
  2. virtio_pci_modern_mem_region_map(proxy, &proxy->isr, &cap);
  3. virtio_pci_modern_mem_region_map(proxy, &proxy->device, &cap);
  4. virtio_pci_modern_mem_region_map(proxy, &proxy->notify, &notify.cap);

virtio_pci_modern_mem_region_map函数在hw/virtio/virtio-pci.c中,代码如下:

  1. static void virtio_pci_modern_mem_region_map(VirtIOPCIProxy *proxy,
  2. VirtIOPCIRegion *region,
  3. struct virtio_pci_cap *cap)
  4. {
  5. virtio_pci_modern_region_map(proxy, region, cap,
  6. &proxy->modern_bar, proxy->modern_mem_bar_idx);
  7. }

virtio_pci_modern_mem_region_map函数仅仅调用了virtio_pci_modern_region_map函数。该函数也在hw/virtio/virtio-pci.c中(就在上边),代码如下:

  1. static void virtio_pci_modern_region_map(VirtIOPCIProxy *proxy,
  2. VirtIOPCIRegion *region,
  3. struct virtio_pci_cap *cap,
  4. MemoryRegion *mr,
  5. uint8_t bar)
  6. {
  7. memory_region_add_subregion(mr, region->offset, &region->mr);
  8. cap->cfg_type = region->type;
  9. cap->bar = bar;
  10. cap->offset = cpu_to_le32(region->offset);
  11. cap->length = cpu_to_le32(region->size);
  12. virtio_pci_add_mem_cap(proxy, cap);
  13. }

virtio_pci_modern_region_map函数完成了两个功能:

  • 将VirtIOPCIRegion的mr成员virtio-pci-***作为子MemoryRegion加入到VirtIOProxy的modern_bar成员中去。所以当在虚拟机内部写virtio PCI proxy的MMIO时会落入这几个virtio设备的MemoryRegion的回调函数。
  • 调用virtio_pci_add_mem_cap函数将这些寄存器信息加入到virtio PCI代理设备的pci capability上去。virtio_pci_add_mem_cap函数同样在hw/virtio/virtio-pci.c中,代码如下:
  1. static int virtio_pci_add_mem_cap(VirtIOPCIProxy *proxy,
  2. struct virtio_pci_cap *cap)
  3. {
  4. PCIDevice *dev = &proxy->pci_dev;
  5. int offset;
  6. offset = pci_add_capability(dev, PCI_CAP_ID_VNDR, 0,
  7. cap->cap_len, &error_abort);
  8. assert(cap->cap_len >= sizeof *cap);
  9. memcpy(dev->config + offset + PCI_CAP_FLAGS, &cap->cap_len,
  10. cap->cap_len - PCI_CAP_FLAGS);
  11. return offset;
  12. }

virtio_pci_device_plugged函数的其余部分,将在下一回继续解析。

举报

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