首页 理论教育PCIExpress存储器地址转换攻略

PCIExpress存储器地址转换攻略

【摘要】:pci_map_single函数的主要作用是通过ptr参数,获得与之对应的dma_addr,即进行存储器域虚拟地址到PCI总线域物理地址的转换。值得注意的是存储器域物理地址与PCI总线域物理地址的区别。起初在x86处理器中,存储器域物理地址到PCI总线域物理地址的转换非常简单,是直接相等的关系。在PowerPC处理器中,存在一组Inbound寄存器,通过该组寄存器可以将PCI总线地址转换为PowePC处理器规定的存储器地址,详见第2.2节。

在Linux系统中,支持一系列API实现存储器地址到PCI总线地址的转换,这些API的详细定义见./Documentation/DMA-API.txt文件。本节仅以pci_map_single函数为例说明这种地址转换的工作原理。pci_map_single函数在./include/asm-generic/pci-dma-compat.h文件中,如源代码12-10所示。

源代码12-10pci_map_single函数

该函数共有4个输入参数,其中hwdev参数与PCI设备的pci_dev对应,ptr参数对应存储器域的虚拟地址,size字段对应数据区域的大小。而direction参数与数据区域的使用方法对应,PCI_DMA_NONE用于调试,较少使用;PCI_DMA_TODEVICE表示这段数据的传递方向是从存储器到PCI设备;PCI_DMA_FROMDEVICE表示这段数据的传递方向是从PCI设备到存储器;PCI_DMA_BIDIRECTIONAL表示方向未知。该函数的返回值为dma_addr,即PCI总线域的物理地址。

pci_map_single函数的主要作用是通过ptr参数,获得与之对应的dma_addr,即进行存储器域虚拟地址到PCI总线域物理地址的转换。值得注意的是存储器域物理地址与PCI总线域物理地址的区别。

在Linux系统中,使用virt_to_phys函数将存储器域的虚拟地址转换为存储器域的物理地址,但是通过该函数仅能获得存储器域的物理地址,因此该地址不能填写到PCI设备中进行DMA操作。值得注意的是,进行DMA操作的地址是由PCI设备使用的,而且这个地址只能是PCI总线域的物理地址,尽管在许多处理器中,virt_to_phys函数和pci_map_single函数的返回值相同。

不同的处理器使用不同的方式实现pci_map_single函数。起初在x86处理器中,存储器域物理地址到PCI总线域物理地址的转换非常简单,是直接相等的关系。但是x86处理器为了支持虚拟化技术,使用了VT-d/IOMMU[99]技术,使得该函数的实现略微复杂。

同样是基于x86架构,AMD处理器使用的IOMMU技术与Intel有所区别,AMD的x86处理器使用./arch/x86/kernel/amd_iommu.c文件中的map_single函数,进行存储器域地址空间到PCI总线域地址空间的转换;而Intel的x86处理器使用./drivers/pci/intel-iommu.c文件中的intel_map_single函数实现存储器地址空间到PCI域地址空间的转换。IOMMU技术略微有些复杂,在第13.1节中将专门描述这部分内容。(www.chuimin.cn)

在PowerPC处理器中,存在一组Inbound寄存器,通过该组寄存器可以将PCI总线地址转换为PowePC处理器规定的存储器地址,详见第2.2节。这组Inbound寄存器也可以看作一种IOMMU,只是该IOMMU机制仅支持段式映射而不支持页式映射。

Linux PowerPC使用dma_direct_map_page函数实现这个地址转换,该函数的定义详见./arch/powerpc/kernel/dma.c。在Linux PowerPC中,PCI总线域的物理地址也与存储器域的物理地址相等。

Linux PowerPC还需要设置Inbound寄存器组,这段代码在./arch/power/sysdev/fsl_pci.c文件的setup_pci_atmu函数中,如源代码12-11所示。目前这段代码对Inbound寄存器组的Entry 2进行设置,允许PCIe设备访问0~0x7FFF-FFFF(2GB)这段存储器域物理地址空间,而且PCI总线地址与存储器地址一一对应而且相等。

源代码12-11 setup_pci_atmu函数

这段代码源于Linux 2.6.30,在这个版本中,PCIe设备不能访问PowerPC处理器2GB之上的物理内存。而在Linux 2.6.31.6中,该函数被大规模修改,以支持超过2GB的存储器系统,本节对Linux内核的这些改动不做进一步描述,对此有兴趣的读者可以参考Linux2.6.31.6内核中setup_pci_atmu函数的最新实现。

有些支持IOMMU机制的PowerPC处理器,如IBM的PowerPC处理器系列,可以使用dma_iommu_map_page或者ibme bus_map_page函数实现pci_map_single函数,而cell处理器使用dma_fixed_map_page函数实现该功能。pci_map_single函数函数在IBM的PowerPC处理器上已经移植完毕,但是Freescale除了P4080处理器之外,还没有支持IOMMU的处理器。目前对P4080处理器的支持并没有加入到Linux PowerPC中。