先说下iommu几个名词
iommu_group:代表共享同一个streamid的一组device,也就是多个device可以在同个group
domain :代表一个具体的设备使用iommu的详细spec
Kernel has DMA mapping API fromorigin. ARM defines IOMMU which can be used to connect scattered physicalmemory as a continuous region for devices which needs continue address towork(e.g: DMA). So IOMMU implementations & CMA should work behind kernelDMA mapping API. E.g: dma_alloc_from_contiguous can be implemented by CMA;dma_alloc_coherent can be implemented by IOMMU or by the normal case(just call__get_free_pages). So for device drivers need dma buffers, we should use dmamapping APIs, not call iommu api directly
说明cma可以实现函数dma_alloc_from_contiguous,iommu可以实现dma_alloc_coherent
iommu是实现在dma mapping api下层的驱动,所以我们只需要使用dma mapping的相关api,不需要直接调用iommu接口。
IOMMU,Input-Output Memory Management Unit
网上有些关于使用iommu的好处。
但是我感觉最终要的是用于将物理上分散的内存页映射成 cif、isp可见的连续内存,如果没有iommu需要在kernel预留比较大的cma内存
在rk芯片,所有模块的iommu公用一个驱动
kernel\drivers\iommu\rockchip-iommu.c
kernel\drivers\iommu\rk-iommu.h
定义iommu的结构:
struct rk_iommu_domain {
struct list_head iommus;
struct platform_device *pdev;
u32 *dt; /* page directory table */
dma_addr_t dt_dma;
struct mutex iommus_lock; /* lock for iommus list */
struct mutex dt_lock; /* lock for modifying page directory table */
struct iommu_domain domain;
};
struct rk_iommu {
struct device *dev;
void __iomem **bases;
int num_mmu;
int *irq;
int num_irq;
bool reset_disabled; /* isp iommu reset operation would failed */
bool skip_read; /* rk3126/rk3128 can't read vop iommu registers */
struct list_head node; /* entry in rk_iommu_domain.iommus */
struct iommu_domain *domain; /* domain to which iommu is attached */
struct clk *aclk; /* aclock belong to master */
struct clk *hclk; /* hclock belong to master */
struct clk *sclk; /* sclock belong to master */
struct list_head dev_node;
};
在模块加载的时候调用
static int __init rk_iommu_init(void)
{
struct device_node *np;
int ret;
np = of_find_matching_node(NULL, rk_iommu_dt_ids);
if (!np)
return 0;
of_node_put(np);
//初始化iommu bus
ret = bus_set_iommu(&platform_bus_type, &rk_iommu_ops);
if (ret)
return ret;
ret = platform_driver_register(&rk_iommu_domain_driver);
if (ret)
return ret;
//注册两个驱动
ret = platform_driver_register(&rk_iommu_driver);
if (ret)
platform_driver_unregister(&rk_iommu_domain_driver);
return ret;
}
其中
bus_set_iommu
iommu_bus_init
err = bus_for_each_dev(bus, NULL, &cb, add_iommu_group);
添加到group后的回调
add_iommu_group
ops->add_device
调用了.add_device = rk_iommu_add_device
两个probe函数
1.rk_iommu_domain_probe
调用
/* Set dma_ops for dev, otherwise it would be dummy_dma_ops */
arch_setup_dma_ops(dev, 0, DMA_BIT_MASK(32), NULL, false);
这样设置dma_ops的操作函数
common_iommu_setup_dma_ops
do_iommu_attach
arch_set_dma_ops(dev, &iommu_dma_ops);
设置dma操作函数为iommu_dma_ops,这里面有使用iommu分配内存
思想是,分配许多页,可能连续,也可能不连续。然后申请iova,最后用iova去匹配物理page,这样就生成了table。(在dts中没有定义关键字,不走买这个过程在后面的do_iommu_attach执行)
2.rockchip_iommu_probe
里面先获取寄存器资源,ioremap过来,获取中断,并申请中断
之后调用
在rockchip-iommu.c里有
static const struct iommu_ops rk_iommu_ops = {
.domain_alloc = rk_iommu_domain_alloc,
.domain_free = rk_iommu_domain_free,
.attach_dev = rk_iommu_attach_device,
.detach_dev = rk_iommu_detach_device,
.map = rk_iommu_map,
.unmap = rk_iommu_unmap,
.map_sg = rk_iommu_map_sg,
.add_device = rk_iommu_add_device,
.remove_device = rk_iommu_remove_device,
.iova_to_phys = rk_iommu_iova_to_phys,
.pgsize_bitmap = RK_IOMMU_PGSIZE_BITMAP,
};
rk_iommu_domain_alloc是初始化rk_iommu_domain结构,返回的是该结构下的iommu_domain结构体。
结合isp驱动看iommu是如何使用的
在dev.c有
if (is_iommu_enable(dev)) {
rkisp1_iommu_init(isp_dev);
}
static int rkisp1_iommu_init(struct rkisp1_device *rkisp1_dev)
{
……//最终会调用到domain_alloc,申请domain
rkisp1_dev->domain = iommu_domain_alloc(&platform_bus_type);//1
……//分配iova_domain结构保存在domain->iova_cookie
iommu_get_dma_cookie(rkisp1_dev->domain);
……//获取group,为了保证多个device绑定iommu不至于混乱
group = iommu_group_get(rkisp1_dev->dev);
……//isp设备绑定domain
ret = iommu_attach_device(domain, dev);//2
……
//设置dma相关操作函数, iommu_dma_ops,以及地址空间,
// iommu_dma_ops应该是在开启iommu的时候,dma相关操作函数就是执行iommu的相关函数,如果没开启,dma应该是其他函数。也就是说dma的对外操作函数是一致的,只是执行到dma函数的时候调用其中的ops是iommu的。这样dma就可以执行分段操作。后续操作dma的时候会调用到iommu的map和ova_to_phys函数
0x10000000:IOVA可映射地址空间的起始位置
SZ_2G:IOVA空间大小
do_iommu_attach调用domain->ops = ops;和arch_set_dma_ops(dev, &iommu_dma_ops);绑定两组ops
if (!common_iommu_setup_dma_ops(dev, 0x10000000, SZ_2G, domain->ops)) {
……
}
1.static struct iommu_domain *rk_iommu_domain_alloc(unsigned type)
{
……//alloc rk_domain结构
rk_domain = devm_kzalloc(&pdev->dev, sizeof(*rk_domain), GFP_KERNEL);
……//申请内存页来保存dt
rk_domain->dt = (u32 *)get_zeroed_page(GFP_KERNEL | GFP_DMA32);
……//dt做dma映射
rk_domain->dt_dma = dma_map_single(iommu_dev, rk_domain->dt, SPAGE_SIZE, DMA_TO_DEVICE);
……//初始化参数,和iommu的ops
rk_domain->domain.geometry.aperture_start = 0;
rk_domain->domain.geometry.aperture_end = DMA_BIT_MASK(32);
rk_domain->domain.geometry.force_aperture = true;
rk_domain->domain.ops = &rk_iommu_ops;
}
- iommu_attach_device 先获取group,然后调用
__iommu_attach_group
iommu_group_do_attach_device
__iommu_attach_device
domain->ops->attach_dev(domain, dev);
这样就跑到.attach_dev = rk_iommu_attach_device
函数里
static int rk_iommu_attach_device(struct iommu_domain *domain,
struct device *dev)
{
……//获取设备iommu
iommu = rk_iommu_from_dev(dev);
……//其实是设置clk
rk_iommu_power_on(iommu);
……//中间是打开stall模式和复位
iommu->domain = domain; //绑定之后
……//申请中断
ret = devm_request_irq(iommu->dev, iommu->irq[i], rk_iommu_irq,
IRQF_SHARED, dev_name(dev), iommu);
……设置mmu的寄存器和mask中断等
for (i = 0; i < iommu->num_mmu; i++) {
rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR,rk_domain->dt_dma);
rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_ZAP_CACHE);
rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK, RK_MMU_IRQ_MASK);
}
ret = rk_iommu_enable_paging(iommu);
…...
}
Dma map函数会调用iommu的map函数,用申请好的地址,设置iommu的映射表
static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
phys_addr_t paddr, size_t size, int prot)
{
……//为pt申请一个page
page_table = rk_dte_get_page_table(rk_domain, iova);
……
dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
pte_index = rk_iova_pte_index(iova);
pte_addr = &page_table[pte_index];//pte表的首地址
pte_dma = rk_dte_pt_address(dte_index) + pte_index * sizeof(u32);
//物理地址存放到iommu的映射表中
ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
paddr, size, prot);
}
static size_t rk_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
struct scatterlist *sg, unsigned int nents, int prot)
{
……//应该是找到最小的size 为4k
min_pagesz = 1 << __ffs(domain->ops->pgsize_bitmap);
//
for_each_sg(sg, s, nents, i) {
phys_addr_t phys = page_to_phys(sg_page(s)) + s->offset;
//这个函数应该是可以把不同几段物理地址映射到连续地址,
//里面调用domain->ops->map,其实就是做多次map
ret = iommu_map(domain, iova + mapped, phys, s->length,
prot | IOMMU_INV_TLB_ENTIRE);
mapped += s->length;
}
rk_iommu_zap_tlb(domain);
}
这个结构描述的是分散的内存
struct scatterlist {
#ifdef CONFIG_DEBUG_SG
unsigned long sg_magic;
#endif
unsigned long page_link;// 指示该内存块所在的页面。要求page最低4字节对齐
unsigned int offset;// 指示该内存块在页面中的偏移
unsigned int length;// 该内存块的长度
dma_addr_t dma_address;// 该内存块实际的起始地址
#ifdef CONFIG_NEED_SG_DMA_LENGTH
unsigned int dma_length;//相应信息长度
#endif
};
看这个要求,所有的地址和长度都有4k对齐要求,在iommu_map里面有体现。
rk_iommu_iova_to_phys这个函数其实就是在操作dma里的dma_map_ops函数时候会调用。关于前面调用情况,仔细去了解dma
看看
static phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain,
dma_addr_t iova)
{
struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
phys_addr_t pt_phys, phys = 0;
u32 dte, pte;
u32 *page_table;
mutex_lock(&rk_domain->dt_lock);
dte = rk_domain->dt[rk_iova_dte_index(iova)];// //找到对应的目录
if (!rk_dte_is_pt_valid(dte))
goto out;
//(iova & RK_IOVA_PTE_MASK) >> RK_IOVA_PTE_SHIFT;
pt_phys = rk_dte_pt_address(dte);// //找到页码表对应的物理地址
page_table = (u32 *)phys_to_virt(pt_phys);// //找到页码表虚拟地址
pte = page_table[rk_iova_pte_index(iova)];// 找页也码的虚拟地址
if (!rk_pte_is_page_valid(pte))
goto out;
//页码的物理地址+偏移量
phys = rk_pte_page_address(pte) + rk_iova_page_offset(iova);
mutex_unlock(&rk_domain->dt_lock);
return phys;
}
这样看函数的意思是一个dt对应申请一个1024个pt,一组pt为1024*4k,就是4M
如果看不动注释,得懂这个函数就要弄懂地址结构
下面简述rk iommu存储结构
第一个寄存器MMU_DTE_ADDR存放的是DTE表的首地址,初始化驱动代码会调用get_zeroed_page申请一个4k页作为DTE表,DTE表有1024个单位,每个占4Byte.
然后调用dma_map_single将这个地址虚拟地址映射到总线地址(后面有总线地址介绍)。然后把这个地址放到MMU_DTE_ADDR寄存器中。其中每个DTE指向一个PTE表的首地址(注意是PTE表的物理地址)。PTE表的页是在rk_iommu_map中申请,先将这个物理地址虚化(我认为一般是线性的吧)。这地址里面的内容指向实际的页表的物理地址,然后再加上偏移量就是实际页的物理地址。(内存映射都是以页为单位的,找物理地址实际是找的页的物理地址)。
所以这样指地址,那么iova连续的情况,实际的物理地址可以不连续,dma搬运在有iommu的时候传入的是iova(总线地址)。Iommu可以实现转换。
iommu总线地址如下
所以,可以看的出来mmu的地址是由dte,pte,po组成。然后申请了专门的目录表 pte和po的内存,每个有对下一级的指向,就知道物理地址。
小结:仅个人观点
其实iommu都是由dma去申请内存,可能连续,也可能几段离散的,但是地址总线是连续的,申请好之后,需调用dma的map函数,map函数会调用到iommu函数里面分配页表,这样,dma就可以操作分段内存,因为有iommu的映射。