Hello,

It seems that intel_iommu_iova_to_phys() doesn't return a correct
physical address for a given iova. This is the latest kernel code for
the function.

static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
                                            dma_addr_t iova)
{
        struct dmar_domain *dmar_domain = to_dmar_domain(domain);
        struct dma_pte *pte;
        int level = 0;
        u64 phys = 0;

        if (dmar_domain->flags & DOMAIN_FLAG_LOSE_CHILDREN)
                return 0;

        pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, &level);
        if (pte)
                phys = dma_pte_addr(pte);

        return phys;
}

As it can be seen above, pfn_to_dma_pte() returns PTE for (iova >>
VTD_PAGE_SHIFT). Therefore, I think (iova & (VTD_PAGE_SIZE - 1)) needs
to be added to phys. In addition, I think we need to take into account
level, if it is greater than 1. What do you think about the following
code?

        const unsigned long pfn = iova >> VTD_PAGE_SHIFT;
        pte = pfn_to_dma_pte(dmar_domain, pfn, &level);
        if (pte) {
                phys = dma_pte_addr(pte);
                if (level > 1)
                        phys += (pfn & ((level_to_offset_bits(level) << 1) - 1))
                                << VTD_PAGE_SHIFT;

                phys += (iova & (VTD_PAGE_SIZE - 1));
        }

Can this be a fix to make intel_iommu_iova_to_phys() work?

Thank you,
Yonghyun
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to