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

88 篇文章 19 订阅
本文深入探讨QEMU中PCI设备的模拟,解析SeaBIOS如何配置PCI设备与中断控制器的连接,介绍了pci_bios_init_device和pci_irqs[]数组的作用,并简述了ACPI在设备中断路由中的角色,为后续分析QEMU中断流程奠定基础。
摘要由CSDN通过智能技术生成

接前一篇文章:

上一回重点讲了 PCI 设备中断的相关概念。本回开始讲解代码实现细节。

PCI链接设备到 中断控制器 上的路由信息是通过SeaBIOS配置完成的。SeaBIOS中的pci_bios_init_device函数会初始化piix3/4设备。pci_bios_init_device函数在roms/seabios/src/fw/pciinit.c中,代码如下:

  1. static void pci_bios_init_device(struct pci_device *pci)
  2. {
  3. dprintf(1, "PCI: init bdf=%pP id=%04x:%04x\n"
  4. , pci, pci->vendor, pci->device);
  5. /* map the interrupt */
  6. u16 bdf = pci->bdf;
  7. int pin = pci_config_readb(bdf, PCI_INTERRUPT_PIN);
  8. if (pin != 0)
  9. pci_config_writeb(bdf, PCI_INTERRUPT_LINE, pci_slot_get_irq(pci, pin));
  10. pci_init_device(pci_device_tbl, pci, NULL);
  11. /* enable memory mappings */
  12. pci_config_maskw(bdf, PCI_COMMAND, 0,
  13. PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_SERR);
  14. /* enable SERR# for forwarding */
  15. if (pci->header_type & PCI_HEADER_TYPE_BRIDGE)
  16. pci_config_maskw(bdf, PCI_BRIDGE_CONTROL, 0,
  17. PCI_BRIDGE_CTL_SERR);
  18. }

其中的pci_init_device函数也在同文件(roms/seabios/src/fw/pciinit.c)中,代码如下:

  1. int pci_init_device(const struct pci_device_id *ids
  2. , struct pci_device *pci, void *arg)
  3. {
  4. while (ids->vendid || ids->class_mask) {
  5. if ((ids->vendid == PCI_ANY_ID || ids->vendid == pci->vendor) &&
  6. (ids->devid == PCI_ANY_ID || ids->devid == pci->device) &&
  7. !((ids->class ^ pci->class) & ids->class_mask)) {
  8. if (ids->func)
  9. ids->func(pci, arg);
  10. return 0;
  11. }
  12. ids++;
  13. }
  14. return -1;
  15. }

传给pci_init_device函数的实参为:

pci_init_device(pci_device_tbl, pci, NULL);

其中,第一个参数pci_device_tbl的定义也在同文件(roms/seabios/src/fw/pciinit.c)中,代码如下:

  1. static const struct pci_device_id pci_device_tbl[] = {
  2. /* PIIX3/PIIX4 PCI to ISA bridge */
  3. PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0,
  4. piix_isa_bridge_setup),
  5. PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0,
  6. piix_isa_bridge_setup),
  7. PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC,
  8. mch_isa_bridge_setup),
  9. /* STORAGE IDE */
  10. PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1,
  11. PCI_CLASS_STORAGE_IDE, piix_ide_setup),
  12. PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB,
  13. PCI_CLASS_STORAGE_IDE, piix_ide_setup),
  14. PCI_DEVICE_CLASS(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE,
  15. storage_ide_setup),
  16. /* PIC, IBM, MPIC & MPIC2 */
  17. PCI_DEVICE_CLASS(PCI_VENDOR_ID_IBM, 0x0046, PCI_CLASS_SYSTEM_PIC,
  18. pic_ibm_setup),
  19. PCI_DEVICE_CLASS(PCI_VENDOR_ID_IBM, 0xFFFF, PCI_CLASS_SYSTEM_PIC,
  20. pic_ibm_setup),
  21. /* PIIX4 Power Management device (for ACPI) */
  22. PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3,
  23. piix4_pm_setup),
  24. PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_SMBUS,
  25. ich9_smbus_setup),
  26. /* 0xff00 */
  27. PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0017, 0xff00, apple_macio_setup),
  28. PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0022, 0xff00, apple_macio_setup),
  29. /* Intel IGD OpRegion setup */
  30. PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA,
  31. intel_igd_setup),
  32. PCI_DEVICE_END,
  33. };

由此就引出了piix_isa_bridge_setup函数。它也在同文件(roms/seabios/src/fw/pciinit.c)中,代码如下:

  1. /* PIIX3/PIIX4 PCI to ISA bridge */
  2. static void piix_isa_bridge_setup(struct pci_device *pci, void *arg)
  3. {
  4. int i, irq;
  5. u8 elcr[2];
  6. elcr[0] = 0x00;
  7. elcr[1] = 0x00;
  8. for (i = 0; i < 4; i++) {
  9. irq = pci_irqs[i];
  10. /* set to trigger level */
  11. elcr[irq >> 3] |= (1 << (irq & 7));
  12. /* activate irq remapping in PIIX */
  13. pci_config_writeb(pci->bdf, 0x60 + i, irq);
  14. }
  15. outb(elcr[0], PIIX_PORT_ELCR1);
  16. outb(elcr[1], PIIX_PORT_ELCR2);
  17. dprintf(1, "PIIX3/PIIX4 init: elcr=%02x %02x\n", elcr[0], elcr[1]);
  18. }

由代码可见,在piix3/4设备的配置空间0x60开始的地方,写入了PCI链接设备到中断线的路由关系。代码片段为:

  1. /* activate irq remapping in PIIX */
  2. pci_config_writeb(pci->bdf, 0x60 + i, irq);

LNKA、LNKB、LNKC、LNKD分别对应10、10、11、11这4条中断线。何以见得?pci_irqs[]在同文件(roms/seabios/src/fw/pciinit.c)中,定义如下:

  1. /* host irqs corresponding to PCI irqs A-D */
  2. const u8 pci_irqs[4] = {
  3. 10, 10, 11, 11
  4. };

PCI设备到PCI链接设备的路由信息是写在ACPI表中的 ,在此不深入ACPI的具体细节,仅简单介绍一下基本概念。

ACPI的英文全称为 Advanced Configuration and Power Interface ,中文译为 高级配置电源管理接口 ACPI提供了处理器硬件和操作系统之间的一组接口,用来对处理器以及设备的电源进行管理,并且可以配置外部设备使用的系统资源 。ACPI使用 一系列描述符表来 管理处理器和设备资源。PCI设备的中断路由是通过build_prt函数完成的,该函数会构造包含128项数组的数据,每一项表示一个设备的路由信息,说明该设备路由到哪个PCI链接设备。映射关系按照如下公式完成:

(slot+pin)&3   ->   "LNK[D|A|B|C]"

下一回开始解析QEMU中PCI设备触发中断的流程。

举报

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