1 Abstract
设备之间通过总线互联通信,如果将所有设备都挂在到sysbus(即所谓的内部总线:连接cpu、内存、pic总线控制器等设备的总线)将会增加主板的布线难度,而且cpu并不需要实时与外围设备通信。因此没必要每个设备都直接与cpu连接。
现代cpu通过sysbus直接连接一个控制芯片,这个控制芯片再管理外围设备。控制芯片将数据汇总后再与cpu通信。
这个过程对应到QEMU中的就是:
- cpu通过sysbus找到pcihost
- pcihost通过pcibus找到pci设备完成cpu的指令
本文主要说明QEMU的怎么模拟实现这个过程的
2 Preface
PCI设备有如下三种不同内存:
- MMIO
- PCI IO space
- PCI configuration space
其中pci configuration space 是用来配置pci设备的,其中也包含了关于pci设备的特定信息。而config空间中的BAR: Base address register可以用来确定设备需要使用的内存或I/O空间的大小,也可以用来存放设备寄存器的地址。
与pci设备通信,先会通过pcihost找到对应的pci设备,修改其config空间、传递指令信息。之后会调用pci_update_mappings更新pci设备的内存映射,因为可能接下来就要通过mmio/pio与pci设备通信了。如: gdb追踪pci_host_config_write、pci_host_data_write、pci_update_mappings的执行。
会发现pci_host_config_write传递指令信息保存到config_reg,pci_host_data_write根据config_reg修改config空间,之后会调用pci_update_mappings。pci_update_mappings会根据修改的config空间的信息更新内存映射。
下文将对其进行详细说明
3 模拟方法
pcihost模拟是主要功能是:根据cpu的指令找到pci设备,然后”转发”指令给pci设备
pcibus模拟的主要功能是:维护pci设备信息,为pcihost索引目标设备提供依据
pci设备模拟的主要功能是:维护自己的config空间和操作对应的回调函数,设备的内存空间映射将会根据config空间的内容完成
要了解pci总线模拟应该搞清楚以下问题:
- 如何给设备编号:初始化程序扫描总线上每个插槽并编号
- 对应规范文档:
- 设备编号:pci-pci-bridge specifiation: 13.2. Device Number and Slot Number Assignment Rules,约138页
- 对应qemu: qemu/hw/pci/pci.c:do_pci_register_device,当指定devfn=-1时自动扫描可用设备号
- 对应规范文档:
- cpu如何知道设备编号:初始化时初始化程序从IRQ Routing Table获取设备号信息
- 对应规范文档:
- 设备号信息初始化:pci-pci-bridge specifiation: 13.6. Run-Time Algorithm for Determining Chassis and Slot Number。约145页
- 对应qemu: TODO,没有找到,可能是操作系统中完成的?
- 对应规范文档:
- pcihost如何找到pci设备
- 对应规范文档:
- 3.2.2.3.2. Software Generation of Configuration Transactions。约33页
- 对应本文章节:
- 对应qemu:
- qemu/hw/pci/pci_host.c:pci_dev_find_by_addr()
- 对应规范文档:
- pci总线规范中几个比较重要的协议内容
- 对应规范文档:
- config space的编码规范:Figure 6-1,约191页
- 设备控制:6.2.2. Device Control,约193页
- 设备状态控制:6.2.3. Device Status,约196页
- BAR寄存器:6.2.5. Base Addresses,约201页
- 对应本文章节:
- 设备控制(对应Figure 6-1的command register)和状态控制(对应Figure 6-1的status register)等:bus transaction中举例的接口根据addr参数读写各个寄存器
- BAR寄存器功能:设备IO空间
- 对应qemu:
- 设备控制和状态控制等:qemu/pci/pci.c:pci_default_write_config,根据addr参数修改对应寄存器内容
- 对应规范文档:
- 如何给设备分配IO空间
- 对应规范文档:
- BAR寄存器:6.2.5. Base Addresses,约201页
- 对应本文章节:
- 对应qemu:
- 设备BAR:qemu/pci/pci.c:pci_register_bar
- 根据BAR进程内存空间映射:qemu/pci/pci.c:pci_update_mappings
- 对应规范文档:
- cpu与pci设备通信的流程:中途经过什么设备,做了什么操作,如何读写设备等
- 对应规范文档:
- 整体流程概述:3.2.2.3.2. Software Generation of Configuration Transactions。约33页
- 对应本文章节:
- 中途做了什么操作:控制流程
- 如何读写设备:bus transaction
- 对应qemu:
- 修改config空间的接口:qemu/pci/pci.c:pci_default_write_config
- CONFIG_ADDRESS阶段:qemu/pci/pci_host.c:pci_host_config_read,qemu/pci/pci_host.c:pci_host_config_write
- CONFIG_DATA阶段:qemu/pci/pci_host.c:pci_host_data_read,qemu/pci/pci_host.c:pci_host_data_write
- 对应规范文档:
3.1 pcihost
3.1.1 控制流程
pcihost对pci设备控制流程可以分为两阶段
- CONFIG_ADDRESS
- cpu访问CONFIG_ADDRESS对应的端口时触发,将命令保存到pcihost的config寄存器中
- CONFIG_DATA
- cpu访问CONFIG_DATA对应的端口时触发,根据config寄存器中内容进行相应的操作
3.1.1.1 CONFIG_ADDRESS
以i440fx北桥芯片这个pcihost为例,他使用两个pio来接收,cpu的指令信息。CF8h处的地址空间称为CONFIG_ADDRESS,CFCh处的地址空间称为CONFIG_DATA。
参考i440fx规范文档,其pci相关的核心内容如下:
- i440fx设备的IO端口:3.1. I/O Mapped Registers,约17页
- 主要内容:CONFIG_ADDRESS寄存器在CF8h端口,CONFIG_DATA寄存器在CFCh端口
- 对应qemu的模拟:qemu/hw/pci-host/i440fx.c:i440fx_pcihost_realize
- pci config空间与各寄存器的功能:3.2. PCI Configuration Space Mapped Registers
- 主要内容:说明了config空格各个字段的内容,访问config空间的格式(TYPE0和TYPE1)
- 对应qemu的模拟:qemu/hw/pci-host/i440fx.c:pci_host_conf_le_ops和qemu/hw/pci-host/i440fx.c:pci_host_data_le_ops
static void i440fx_pcihost_realize(DeviceState *dev, Error **errp)
{
PCIHostState *s = PCI_HOST_BRIDGE(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
sysbus_add_io(sbd, 0xcf8, &s->conf_mem);
sysbus_init_ioports(sbd, 0xcf8, 4);
sysbus_add_io(sbd, 0xcfc, &s->data_mem);
sysbus_init_ioports(sbd, 0xcfc, 4);
/* register i440fx 0xcf8 port as coalesced pio */
memory_region_set_flush_coalesced(&s->data_mem);
memory_region_add_coalescing(&s->conf_mem, 0, 4);
}
当对CH8h端口写时,pcihost会将数据用锁存器(latch)保存起来。当对CH8h端口读时,pcihost就会返回CONFIG_ADDRESS中的数据。pci规范文档中说明如下(约在32页, 3.2.2.3.2. Software Generation of Configuration Transactions):
qemu中的模拟如下,qemu中用s->config_reg做锁存器,向对CONFIG_ADDRESS写入的数据。读时就直接返回s->config_reg
stati