1.設備樹
cp0_pcie0是一個PCIe RC控制器,使用SMMU將PCIe設備的IOVA轉換成物理地址,使用iommu-map-mask
和iommu-map
定義PCIe設備使用的Stream ID。設備樹定義如下所示。
[arch/arm64/boot/dts/marvell/armada-ap80x.dtsi]
smmu: iommu@100000 {compatible = "marvell,ap806-smmu-500", "arm,mmu-500";reg = <0x100000 0x100000>;dma-coherent;#iommu-cells = <1>;#global-interrupts = <1>;interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,<GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;status = "disabled";
};[arch/arm64/boot/dts/marvell]
&cp0_pcie0 {// iommu-map四個元素分別為BDF、引用的SMMUphandle_node、Base StreamID、數量iommu-map =<0x0 &smmu 0x480 0x20>,<0x100 &smmu 0x4a0 0x20>,<0x200 &smmu 0x4c0 0x20>;iommu-map-mask = <0x031f>;
};
2.dma_configure
設備總系類型數據結構bus_type中定義了dma_configure和dma_cleanup兩個回調函數,前者用于建立該總線上設備的DMA配置,后者用于清理該總線上設備的DMA配置。
[include/linux/device/bus.h]
struct bus_type {......int (*dma_configure)(struct device *dev);void (*dma_cleanup)(struct device *dev);......
};
當設備或者驅動初始化的時候都會調用到really_probe函數。若設備所屬總線定義了dma_configure函數,則會調用該回調函數建立設備的DMA配置。
[drivers/pci/pci-driver.c]
const struct bus_type pci_bus_type = {.name = "pci",.match = pci_bus_match,.uevent = pci_uevent,.probe = pci_device_probe,.remove = pci_device_remove,.shutdown = pci_device_shutdown,.dev_groups = pci_dev_groups,.bus_groups = pci_bus_groups,.drv_groups = pci_drv_groups,.pm = PCI_PM_OPS_PTR,.num_vf = pci_bus_num_vf,.dma_configure = pci_dma_configure,.dma_cleanup = pci_dma_cleanup,
};
[drivers/base/platform.c]
const struct bus_type platform_bus_type = {.name = "platform",.dev_groups = platform_dev_groups,.match = platform_match,.uevent = platform_uevent,.probe = platform_probe,.remove = platform_remove,.shutdown = platform_shutdown,.dma_configure = platform_dma_configure,.dma_cleanup = platform_dma_cleanup,.pm = &platform_dev_pm_ops,
};
3. DMA配置過程
下面以PCIe設備為例,介紹PCIe設備初始化的時候,DMA配置流程。具體工作如下:
platform_bus_type
定義了pci_dma_configure
函數,因此在really_probe
函數里面會調用該函數設置DMA相關配置。- 首先解析
"dma-range"
屬性,"dma-range"
屬性定義了PCI地址到CPU地址的轉換關系(inbound memory)。 - 使能ACS。
- 解析
"iommu-map"
和"iommu-map-mask"
屬性,然后將PCIe設備的BDF轉換成對應的StreamID。 - 調用SMMU驅動提供的
arm_smmu_of_xlate
函數,將轉換后的StreamID保存到iommu_fwspec
數據結構中。 - 調用
iommu_probe_device
初始化設備,這部分內容在ARM SMMUv3控制器注冊過程分析(八)介紹過,這里不多贅述。
of_map_id
函數首先解析"iommu-map"
和"iommu-map-mask"
屬性,然后將設備的BDF轉換成StreamID。流程如代碼所示。
int of_map_id(struct device_node *np, u32 id,const char *map_name, const char *map_mask_name,struct device_node **target, u32 *id_out)
{// 讀取"iommu-map"屬性map = of_get_property(np, map_name, &map_len);....../* The default is to select all bits. */map_mask = 0xffffffff;// 讀取"iommu-map-mask"屬性if (map_mask_name)of_property_read_u32(np, map_mask_name, &map_mask);// iommu-map-mask & BDF,通常情況下,只屏蔽function三位masked_id = map_mask & id;// 遍歷所有iommu-map定義的數據for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) {struct device_node *phandle_node;u32 id_base = be32_to_cpup(map + 0); // 解析BDFu32 phandle = be32_to_cpup(map + 1); // 解析引用的SMMU的phandle_nodeu32 out_base = be32_to_cpup(map + 2); // Base StreamIDu32 id_len = be32_to_cpup(map + 3); // 長度......// *id_ou = iommu-map-mask & BDF - BDF + Base StreamIDif (id_out)*id_out = masked_id - id_base + out_base;return 0;}/* Bypasses translation */if (id_out)*id_out = id;return 0;
}
參考資料
- linux 6.12.35 source code.