Linux: 如何确定ioremap返回的对应的DMA地址

Linux: how to determine the DMA address corresponding to what's returned from ioremap

假设我有一些物理地址(这是我的 DMA 源外设的总线位置):

phys_addr = 0xffff0000;

我也是:

virt_addr = ioremap(phys_addr, PAGE_SIZE);

这样做之后,我不再可以直接访问 phys_addr(在我的情况下,我没有访问权限,因为我正在编写的驱动程序仅通过 virt_addr)。

我第一次失败的尝试:

wrong_phys_addr = virt_to_dma(dev,virt_addr);

顺便说一句,我不知道为什么设备 dev 与此查找相关...

这当然是行不通的,因为 virt_addr 不是直接映射的内核内存。 DMA-API 似乎暗示所有类似 virt_to_bus 的翻译都已弃用,每个人都应该使用 dma_map_single 等来获取 DMA 总线地址。所以我接下来尝试了这个:

wrong_phys_addr = dma_map_single(dev, virt_addr, size, DMA_FROM_DEVICE);

还是不行。当然,如果我直接使用 0xffff0000 作为 device_prep_dma_memcpy 的源 DMA 地址,一切都很好——但我还是不能这样做,因为我无法直接访问它。我能想到的获得它的唯一可能方法是遍历内核的页表……我真的不想手动执行此操作。有没有更好的方法?

更新

struct page *kmap_to_page(void * vaddr)

看起来很有希望。我不确定从 ioremap 返回的虚拟地址是否适用于此。另一种可能是

struct page* vmalloc_to_page(void* vmalloc_addr)

我需要进一步调查。

看起来 vmalloc_to_page 正是我要找的。如果我这样做:

phys_addr = page_to_phys(vmalloc_to_page(virt_addr)))

这会产生,phys_addr == 0xffff0000

按照 CL. 的建议,我还尝试执行以下所有操作:

wrong_phy_addr1 = dma_map_single(dev, 0xffff0000, size, DMA_FROM_DEVICE);
wrong_phy_addr2 = dma_map_single(dev, virt_addr, size, DMA_FROM_DEVICE);
correct_phy_addr = phys_to_dma(dev, 0xffff0000);

在 运行 之后我得到:

wrong_phy_addr1 == 7fff0000
wrong_phy_addr2 == 4091e000
correct_phy_addr == ffff0000

因此,看起来 phys_to_dma() 在这里获胜。使用这种方法比使用原始物理地址更可取,因为(如 CL. 建议的那样)物理地址不一定是 DMA 地址——在我的板上它们恰好是相同的。

总之,这是我原来问题的答案:

dma_addr = phys_to_dma(dev, page_to_phys(vmalloc_to_page(virt_addr));

话虽这么说,但如果我的驱动程序可以使用 phys_addr (0xffff0000),则可以避免这种额外的查找 - 我会考虑更改接口。

在接受我自己的回答之前,我会给其他人一些时间来提供他们自己的(更好的?)答案。