我已经浏览了Linux IOMMU代码很长一段时间了,但没有找到直接创建IOMMU条目的简单方法。
我想指定物理地址(也可能是虚拟地址,但这是不必要的)和设备。该范围应插入IOMMU,并通过printk
打印virt地址。
我正在寻找一个函数,让我很容易做到这一点。
感谢我最终得到了一个相当粗糙的解决方案,不是最优的,但它适用于我的用例。将dma-iommu.c
中的iommu_dma_map_page
函数调整如下并导出
(香草5.18除了这个修改)
dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction dir,
unsigned long attrs)
{
bool coherent = dev_is_dma_coherent(dev);
int prot = dma_info_to_prot(dir, coherent, attrs);
struct iommu_domain *domain = iommu_get_dma_domain(dev);
struct iommu_dma_cookie *cookie = domain->iova_cookie;
struct iova_domain *iovad = &cookie->iovad;
dma_addr_t iova, dma_mask = dma_get_mask(dev);
phys_addr_t phys;
if (page->flags == 0xF0F0F0F0F0F0F) {
phys = page->dma_addr;
} else {
phys = page_to_phys(page) + offset;
}
/*
* If both the physical buffer start address and size are
* page aligned, we don't need to use a bounce page.
*/
if (dev_use_swiotlb(dev) && iova_offset(iovad, phys | size)) {
void *padding_start;
size_t padding_size, aligned_size;
aligned_size = iova_align(iovad, size);
phys = swiotlb_tbl_map_single(dev, phys, size, aligned_size,
iova_mask(iovad), dir, attrs);
if (phys == DMA_MAPPING_ERROR)
return DMA_MAPPING_ERROR;
/* Cleanup the padding area. */
padding_start = phys_to_virt(phys);
padding_size = aligned_size;
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)) {
padding_start += size;
padding_size -= size;
}
memset(padding_start, 0, padding_size);
}
if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
arch_sync_dma_for_device(phys, size, dir);
iova = __iommu_dma_map(dev, phys, size, prot, dma_mask);
if (iova == DMA_MAPPING_ERROR && is_swiotlb_buffer(dev, phys))
swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs);
return iova;
}
EXPORT_SYMBOL(iommu_dma_map_page);
然后使用下面的内核模块对条目进行编程。这也可以以更可用的方式进行扩展和编程,但对于原型来说,这应该足够了。
#include <linux/init.h>
#include <asm/io.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
extern dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size, enum dma_data_direction dir,
unsigned long attrs);
int magic_value = 0xF0F0F0F0F0F0F;
struct page page_ = {
.flags = 0xF0F0F0F0F0F0F,
.dma_addr = 0x0000002f000f0000,
};
static int my_init(void)
{
dma_addr_t dma_addr;
struct pci_dev *dummy = pci_get_device(0x10EE, 0x0666, NULL);
if (dummy != NULL)
{
printk(KERN_INFO "module loaded.n");
dma_addr = iommu_dma_map_page(&(dummy->dev), &page_, 0, 4096, DMA_BIDIRECTIONAL, DMA_ATTR_SKIP_CPU_SYNC);
printk(KERN_INFO "DMA_addr: %llx", dma_addr);
}
else
{
printk("Error getting device");
}
return 0;
}
static void my_exit(void)
{
printk(KERN_INFO "iommu_alloc unloaded.n");
return;
}
module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("benedict.schlueter@inf.ethz.ch");
MODULE_DESCRIPTION("Alloc IOMMU entry");