smmu 有oas和ias 分别表示输出和输入smmu的地址size,其中ias表示input address size 这个对smmu来说代表IPA,oas代表PA。
其中的oas是通过读取IDR5 中的bit 0,且这个寄存器是只读的.具体是在arm_smmu_device_hw_probe
/* IDR5 */
reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5);
/* Output address size */
switch (reg & IDR5_OAS_MASK << IDR5_OAS_SHIFT) {
case IDR5_OAS_32_BIT:
smmu->oas = 32;
break;
case IDR5_OAS_36_BIT:
smmu->oas = 36;
break;
case IDR5_OAS_40_BIT:
smmu->oas = 40;
break;
case IDR5_OAS_42_BIT:
smmu->oas = 42;
break;
case IDR5_OAS_44_BIT:
smmu->oas = 44;
break;
default:
dev_info(smmu->dev,
"unknown output address size. Truncating to 48-bit\n");
/* Fallthrough */
case IDR5_OAS_48_BIT:
smmu->oas = 48;
}
而ias 也是在arm_smmu_device_hw_probe 中赋值.
/* We only support the AArch64 table format at present */
switch (reg & IDR0_TTF_MASK << IDR0_TTF_SHIFT) {
case IDR0_TTF_AARCH32_64:
smmu->ias = 40;
/* Fallthrough */
case IDR0_TTF_AARCH64:
break;
default:
dev_err(smmu->dev, "AArch64 table format not supported!\n");
return -ENXIO;
}
但是最终的ias是取ias和oas的最大值
smmu->ias = max(smmu->ias, smmu->oas);
从当前arm64 的code看ias和oas中的最大值就是oas,从打印的log也可以看出ias等于oas
dev_info(smmu->dev, "ias %lu-bit, oas %lu-bit (features 0x%08x)\n",
smmu->ias, smmu->oas, smmu->features);
[ 5.914163] arm-smmu-v3 arm-smmu-v3.0.auto: ias 48-bit, oas 48-bit (features 0x00000f0d)
[ 5.923877] arm-smmu-v3 arm-smmu-v3.1.auto: ias 48-bit, oas 48-bit (features 0x00000f0d)
但是这个ias和oas 仅仅表示输入和输出的最大的size,但并不能保证设备DMA拿到的虚拟地址的size,这个是在申请iova的时候通过设备自身的dma_mask来决定的.code 如下:
dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, int prot)
{
dma_addr_t dma_addr;
struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
struct iova_domain *iovad = cookie_iovad(domain);
phys_addr_t phys = page_to_phys(page) + offset;
size_t iova_off = iova_offset(iovad, phys);
size_t len = iova_align(iovad, size + iova_off);
struct iova *iova = __alloc_iova(domain, len, dma_get_mask(dev));
if (!iova)
return DMA_ERROR_CODE;
dma_addr = iova_dma_addr(iovad, iova);
if (iommu_map(domain, dma_addr, phys - iova_off, len, prot)) {
__free_iova(iovad, iova);
return DMA_ERROR_CODE;
}
return dma_addr + iova_off;
}
其中的oas是通过读取IDR5 中的bit 0,且这个寄存器是只读的.具体是在arm_smmu_device_hw_probe
/* IDR5 */
reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5);
/* Output address size */
switch (reg & IDR5_OAS_MASK << IDR5_OAS_SHIFT) {
case IDR5_OAS_32_BIT:
smmu->oas = 32;
break;
case IDR5_OAS_36_BIT:
smmu->oas = 36;
break;
case IDR5_OAS_40_BIT:
smmu->oas = 40;
break;
case IDR5_OAS_42_BIT:
smmu->oas = 42;
break;
case IDR5_OAS_44_BIT:
smmu->oas = 44;
break;
default:
dev_info(smmu->dev,
"unknown output address size. Truncating to 48-bit\n");
/* Fallthrough */
case IDR5_OAS_48_BIT:
smmu->oas = 48;
}
而ias 也是在arm_smmu_device_hw_probe 中赋值.
/* We only support the AArch64 table format at present */
switch (reg & IDR0_TTF_MASK << IDR0_TTF_SHIFT) {
case IDR0_TTF_AARCH32_64:
smmu->ias = 40;
/* Fallthrough */
case IDR0_TTF_AARCH64:
break;
default:
dev_err(smmu->dev, "AArch64 table format not supported!\n");
return -ENXIO;
}
但是最终的ias是取ias和oas的最大值
smmu->ias = max(smmu->ias, smmu->oas);
从当前arm64 的code看ias和oas中的最大值就是oas,从打印的log也可以看出ias等于oas
dev_info(smmu->dev, "ias %lu-bit, oas %lu-bit (features 0x%08x)\n",
smmu->ias, smmu->oas, smmu->features);
[ 5.914163] arm-smmu-v3 arm-smmu-v3.0.auto: ias 48-bit, oas 48-bit (features 0x00000f0d)
[ 5.923877] arm-smmu-v3 arm-smmu-v3.1.auto: ias 48-bit, oas 48-bit (features 0x00000f0d)
但是这个ias和oas 仅仅表示输入和输出的最大的size,但并不能保证设备DMA拿到的虚拟地址的size,这个是在申请iova的时候通过设备自身的dma_mask来决定的.code 如下:
dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, int prot)
{
dma_addr_t dma_addr;
struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
struct iova_domain *iovad = cookie_iovad(domain);
phys_addr_t phys = page_to_phys(page) + offset;
size_t iova_off = iova_offset(iovad, phys);
size_t len = iova_align(iovad, size + iova_off);
struct iova *iova = __alloc_iova(domain, len, dma_get_mask(dev));
if (!iova)
return DMA_ERROR_CODE;
dma_addr = iova_dma_addr(iovad, iova);
if (iommu_map(domain, dma_addr, phys - iova_off, len, prot)) {
__free_iova(iovad, iova);
return DMA_ERROR_CODE;
}
return dma_addr + iova_off;
}
可以看到这里iova的范围是通过dma_get_mask(dev)来限定的,并不是通过ias和oas。
if (dma_set_mask_and_coherent(smmu->dev, DMA_BIT_MASK(smmu->oas)))
dev_warn(smmu->dev,
"failed to set DMA mask for table walker\n");
而smmu这个设备的dma_mask 最终也是由oas决定的.