arm64 smmu 驱动笔记 (4.19)

设备StreamID:sid

struct iommu_fwspec {
	const struct iommu_ops	*ops;
	struct fwnode_handle	*iommu_fwnode;
	void			*iommu_priv;
	unsigned int		num_ids;
	u32			ids[1];  //ids保存的某device使用的streamID
};

通过iommu_fwspec_add_id函数设置:

struct iommu_fwspec {
	const struct iommu_ops	*ops;
	struct fwnode_handle	*iommu_fwnode;
	void			*iommu_priv;
	unsigned int		num_ids;  //和该设备关联的总共ids/streamID
	u32			ids[1];//sid内容数组
};


int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
{
        struct iommu_fwspec *fwspec = dev->iommu_fwspec;
        size_t size;
        int i;

        if (!fwspec)
                return -EINVAL;

        size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);
        if (size > sizeof(*fwspec)) {
                fwspec = krealloc(dev->iommu_fwspec, size, GFP_KERNEL);
                if (!fwspec)
                        return -ENOMEM;

                dev->iommu_fwspec = fwspec;
        }

        for (i = 0; i < num_ids; i++)
                fwspec->ids[fwspec->num_ids + i] = ids[i];

        fwspec->num_ids += num_ids;
        return 0;
}

设置流程dump_stack信息: 


[   79.499624] ===iommu_fwspec_add_ids for dev 0000:0a:00.0 num_ids 1
[   79.505862] CPU: 29 PID: 1 Comm: swapper/0 Not tainted 4.19.0l+ #20
[   79.512098] Hardware name: PHYTIUM LTD Phytium S2500/64/Phytium S2500/64, BIOS V2.2 Feb  9 2021
[   79.520754] Call trace:
[   79.523192]  dump_backtrace+0x0/0x1c0
[   79.526838]  show_stack+0x24/0x30
[   79.530138]  dump_stack+0x9c/0xbc
[   79.533439]  iommu_fwspec_add_ids+0x44/0xd0
[   79.537602]  iort_iommu_xlate+0x144/0x170
[   79.541592]  iort_pci_iommu_init+0x58/0x80
[   79.545670]  pci_for_each_dma_alias+0x44/0x188
[   79.550093]  iort_iommu_configure+0xac/0x1c8
[   79.554343]  acpi_dma_configure+0x68/0xbc
[   79.558333]  pci_dma_configure+0xa8/0xc8
[   79.562236]  dma_configure+0x4c/0x64
[   79.565795]  really_probe+0x98/0x3b8
[   79.569354]  driver_probe_device+0x6c/0x138
[   79.573516]  __driver_attach+0x118/0x150
[   79.577419]  bus_for_each_dev+0x84/0xd8
[   79.581237]  driver_attach+0x30/0x40
[   79.584795]  bus_add_driver+0x20c/0x250
[   79.588613]  driver_register+0x64/0x110
[   79.592430]  __pci_register_driver+0x58/0x68
[   79.596681]  xhci_pci_init+0x58/0x64
[   79.600240]  do_one_initcall+0x68/0x1e4
[   79.604059]  kernel_init_freeable+0x2d0/0x388
[   79.608396]  kernel_init+0x18/0x108
[   79.611867]  ret_from_fork+0x10/0x1c
 

iort_iommu_configure函数:

//drivers/acpi/arm64/iort.c
const struct iommu_ops *iort_iommu_configure(struct device *dev)
{
	struct acpi_iort_node *node, *parent;
	const struct iommu_ops *ops;
	u32 streamid = 0;
	int err = -ENODEV;

	/*
	 * If we already translated the fwspec there
	 * is nothing left to do, return the iommu_ops.
	 */
	ops = iort_fwspec_iommu_ops(dev->iommu_fwspec);
	if (ops)
		return ops;

	if (dev_is_pci(dev)) { //PCI设备的sid 使用的是BDF号。
		struct pci_bus *bus = to_pci_dev(dev)->bus;
		struct iort_pci_alias_info info = { .dev = dev };

		node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
				      iort_match_node_callback, &bus->dev);
		if (!node)
			return NULL;

		info.node = node;
//计算sid
		err = pci_for_each_dma_alias(to_pci_dev(dev),
					     iort_pci_iommu_init, &info); 
	} else {
		int i = 0;

		node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
				      iort_match_node_callback, dev);
		if (!node)
			return NULL;

		do {
			parent = iort_node_map_platform_id(node, &streamid,
							   IORT_IOMMU_TYPE,
							   i++);

			if (parent)
				err = iort_iommu_xlate(dev, parent, streamid);
		} while (parent && !err);
	}

	/*
	 * If we have reason to believe the IOMMU driver missed the initial
	 * add_device callback for dev, replay it to get things in order.
	 */
	if (!err) {
		ops = iort_fwspec_iommu_ops(dev->iommu_fwspec);
		err = iort_add_device_replay(ops, dev);
	}

	/* Ignore all other errors apart from EPROBE_DEFER */
	if (err == -EPROBE_DEFER) {
		ops = ERR_PTR(err);
	} else if (err) {
		dev_dbg(dev, "Adding to IOMMU failed: %d\n", err);
		ops = NULL;
	}

	return ops;
}



 */
int pci_for_each_dma_alias(struct pci_dev *pdev,
			   int (*fn)(struct pci_dev *pdev,
				     u16 alias, void *data), void *data)
{
    .....
	struct pci_bus *bus;
	int ret;

	ret = fn(pdev, PCI_DEVID(pdev->bus->number, pdev->devfn), data);
    .....
}


static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
{
	struct iort_pci_alias_info *info = data;
	struct acpi_iort_node *parent;
	u32 streamid;

	parent = iort_node_map_id(info->node, alias, &streamid,
				  IORT_IOMMU_TYPE);
	return iort_iommu_xlate(info->dev, parent, streamid);
}

最终:
static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
			       struct fwnode_handle *fwnode,
			       const struct iommu_ops *ops)
{
	int ret = iommu_fwspec_init(dev, fwnode, ops);

	if (!ret)
		ret = iommu_fwspec_add_ids(dev, &streamid, 1);  //添加streamid

	return ret;
}

arm_smmu_add_device

在drivers/iommu/arm-smmu.c的 arm_smmu_add_device函数中:

#ifdef CONFIG_ARCH_PHYTIUM
#define FWID_READ(id) (((u16)(id) >> 3) | (((id) >> SMR_MASK_SHIFT | 0x7000) << SMR_MASK_SHIFT))
#endif
//默认streamID会使用pci的bdf,但ft2000+ 硬件实现上不同,该值会通过FWID_READ函数进行调整。iommu_fwspec_add_ids加入到fwspec中。因此一个设备会有两个sid。

static int arm_smmu_add_device(struct device *dev)
{
        struct arm_smmu_device *smmu;
        struct arm_smmu_master_cfg *cfg;
        struct iommu_fwspec *fwspec = dev->iommu_fwspec;
        int i, ret;

#ifdef CONFIG_ARCH_PHYTIUM
        /* FT2000PLUS workaround patch */
        if ((read_cpuid_id() & MIDR_CPU_MODEL_MASK) == MIDR_PHYTIUM_FT2000PLUS) {
                printk("===arm_smmu_add_device ft2000+ \n");
                int num = fwspec->num_ids;
                for (i = 0; i < num; i++) {
                        u32 fwid = FWID_READ(fwspec->ids[i]);
                        printk("===arm_smmu_add_device ft2000+ ids %llx to fwid %llx \n",fwspec->ids[i],fwid);
                        iommu_fwspec_add_ids(dev, &fwid, 1);
                }
        }
#endif

..............
打印一下设备的sid
       for (i = 0; i < fwspec->num_ids; i++) {
                u16 sid = fwspec->ids[i];
                u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT;
                printk("===arm_smmu_add_device for dev %s num_ids %d of %d sid %llx  \n",dev_name(dev),fwspec->num_ids,i,sid);
                if (sid & ~smmu->streamid_mask) {
                        dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
                                sid, smmu->streamid_mask);
                        goto out_free;
                }
                if (mask & ~smmu->smr_mask_mask) {
                        dev_err(dev, "SMR mask 0x%x out of range for SMMU (0x%x)\n",
                                mask, smmu->smr_mask_mask);
                        goto out_free;
                }
        }
...................

        ret = arm_smmu_master_alloc_smes(dev); //分配stream mapping entry 表项

}

ft2000+日志:0000:04:00.x  都有一个-x80的sid。

[   11.775255] ===arm_smmu_add_device for dev 0000:41:00.0 num_ids 2 of 0 sid 4100  
[   11.782698] ===arm_smmu_add_device for dev 0000:41:00.0 num_ids 2 of 1 sid 820  
[   12.576919] ===arm_smmu_add_device for dev 0000:03:00.0 num_ids 2 of 0 sid 300  
[   12.576922] ===arm_smmu_add_device for dev 0000:03:00.0 num_ids 2 of 1 sid 60  
[   15.638439] ===arm_smmu_add_device for dev
0000:04:00.0 num_ids 2 of 0 sid 400  
[   15.645802] ===arm_smmu_add_device for dev 0000:04:00.0 num_ids 2 of 1 sid 80  

[   16.494888] ===arm_smmu_add_device for dev 0000:51:00.0 num_ids 2 of 0 sid 5100  
[   16.502947] ===arm_smmu_add_device for dev 0000:51:00.0 num_ids 2 of 1 sid a20  
[   17.105745] ===arm_smmu_add_device for dev
0000:04:00.1 num_ids 2 of 0 sid 401  
[   17.119853] ===arm_smmu_add_device for dev 0000:04:00.1 num_ids 2 of 1 sid 80  
[   18.338789] ===arm_smmu_add_device for dev 0000:04:00.2 num_ids 2 of 0 sid 402  
[   18.346151] ===arm_smmu_add_device for dev 0000:04:00.2 num_ids 2 of 1 sid 80  
[   19.099235] ===arm_smmu_add_device for dev 0000:04:00.3 num_ids 2 of 0 sid 403  
[   19.099237] ===arm_smmu_add_device for dev 0000:04:00.3 num_ids 2 of 1 sid 80 

 上述中发现一个dev可能有多个sid,如iommu_fwspec_add_ids函数关联了多个sid的话,最终往iommu_fwspec 中填充sid数组值。

 在arm_smmu_add_device函数中建立一个设备下的sid和其它数据的关系:

static int arm_smmu_add_device(struct device *dev)
{

#ifdef CONFIG_ARCH_PHYTIUM
#define FWID_READ(id) (((u16)(id) >> 3) | (((id) >> SMR_MASK_SHIFT | 0x7000) << SMR_MASK_SHIFT))
        /* FT2000PLUS workaround patch */
        if ((read_cpuid_id() & MIDR_CPU_MODEL_MASK) == MIDR_PHYTIUM_FT2000PLUS) {
                int num = fwspec->num_ids;
                for (i = 0; i < num; i++) {
                        u32 fwid = FWID_READ(fwspec->ids[i]);
                        iommu_fwspec_add_ids(dev, &fwid, 1); //这里对2000+进行特殊处理,再一次添加了处理后的sid值,应该是2000+处理器的smmu识别sid的方式不一样,
                }
        }
#endif


        ret = -EINVAL;
        for (i = 0; i < fwspec->num_ids; i++) {
                u16 sid = fwspec->ids[i];
                u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT;

                if (sid & ~smmu->streamid_mask) {
                        dev_err(dev, "stream ID 0x%x out of range for SMMU (0x%x)\n",
                                sid, smmu->streamid_mask);
                        goto out_free;
                }
                if (mask & ~smmu->smr_mask_mask) {
                        dev_err(dev, "SMR mask 0x%x out of range for SMMU (0x%x)\n",
                                mask, smmu->smr_mask_mask);
                        goto out_free;
                }
        }

        ret = -ENOMEM;
//cfg的smendx数组大小刚好和fwspec的num_ids一致,后面要保存数据。
        cfg = kzalloc(offsetof(struct arm_smmu_master_cfg, smendx[i]),
                      GFP_KERNEL);
        if (!cfg)
                goto out_free;

        cfg->smmu = smmu;
        fwspec->iommu_priv = cfg;
        while (i--)
                cfg->smendx[i] = INVALID_SMENDX;

        ret = arm_smmu_master_alloc_smes(dev);

}

arm_smmu_attach_dev函数:

arm_smmu_init_domain_context初始化domain,是arm_smmu_add_device一路调用到arm_smmu_attach_dev:

static int arm_smmu_init_domain_context(struct iommu_domain *domain,
					struct arm_smmu_device *smmu)
{
    ............

	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;

    ret = __arm_smmu_alloc_bitmap(smmu->context_map, start,
				      smmu->num_context_banks);

    cfg->cbndx = ret;  //将上述获取的全局唯一context bank 编号 保存在cfg->cbndx
    

	arm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg); //设置好cb等参数。其中包括lape获取的页表等内容。
	arm_smmu_write_context_bank(smmu, cfg->cbndx); //编号作为参数
    ............

}


static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx)
{
............
cb_base = ARM_SMMU_CB(smmu, idx); //根据编号获取cb_base地址

下面是一些写寄存器的操作
	/* TTBRs */
	if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
		writel_relaxed(cfg->asid, cb_base + ARM_SMMU_CB_CONTEXTIDR);
		writel_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0);
		writel_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1);
	} else {
		writeq_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0); //将ttbr[0]写入,其中保存的是页表的pgd基地址。
		if (stage1)
			writeq_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1);
	}

............

}

[   78.770289] ===arm_smmu_init_domain_context for dev arm-smmu.0.auto domain ffff8139415a1b50 smmu ffff834941ef0018  smmu_doamin->stage 0 fmt 2 cfg->cbndx 0
[   78.784048] CPU: 28 PID: 1 Comm: swapper/0 Not tainted 4.19.0l+ #40
[   78.790284] Hardware name: PHYTIUM LTD Phytium S2500/64/Phytium S2500/64, BIOS V2.2 Feb  9 2021
[   78.798938] Call trace:
[   78.801373]  dump_backtrace+0x0/0x1c0
[   78.805017]  show_stack+0x24/0x30
[   78.808316]  dump_stack+0x9c/0xbc
[   78.811615]  arm_smmu_attach_dev+0x44c/0x788
[   78.815864]  __iommu_attach_device+0x54/0xf8 
[   78.820113]  iommu_group_add_device+0x17c/0x478
[   78.824621]  iommu_group_get_for_dev+0x74/0x178
[   78.829129]  arm_smmu_add_device+0x218/0x6b8
[   78.833379]  iort_iommu_configure+0x144/0x1c8
[   78.837715]  acpi_dma_configure+0x68/0xbc
[   78.841705]  pci_dma_configure+0xa8/0xc8
[   78.845608]  dma_configure+0x4c/0x64
[   78.849166]  really_probe+0x98/0x3b8
[   78.852725]  driver_probe_device+0x6c/0x138
[   78.856887]  __driver_attach+0x118/0x150
[   78.860790]  bus_for_each_dev+0x84/0xd8
[   78.864608]  driver_attach+0x30/0x40
[   78.868166]  bus_add_driver+0x20c/0x250
[   78.871984]  driver_register+0x64/0x110
[   78.875802]  __pci_register_driver+0x58/0x68
[   78.880051]  ahci_pci_driver_init+0x28/0x30
[   78.884213]  do_one_initcall+0x68/0x1e4
[   78.888031]  kernel_init_freeable+0x2d0/0x388
[   78.892367]  kernel_init+0x18/0x108
[   78.895839]  ret_from_fork+0x10/0x1c
 

设备创建group和doamin流程 

device:加组流程
really_probe
	dma_configure
		pci_dma_configure
			acpi_dma_configure
				iort_iommu_configure
					arm_smmu_add_device
						arm_smmu_master_alloc_smes
							iommu_group_get_for_dev
								arm_smmu_device_group //创建iommu_group
								group->default_domain = dom; //创建domain
								group->domain = dom;
								iommu_group_add_device //将deive加入到group链表,将iommu_grouop 赋值dev->iommu_group指针
									__iommu_attach_device
										arm_smmu_attach_dev
							for_each_cfg_sme:smmu->s2crs[idx].group = group; //循环将该device下的第idxg个s2crs数组分配group
	


arm_smmu_device_group
	pci_device_group
		alloc_group
	generic_device_group

关联smmu下的SMMU_SMRn寄存器。其中写入streamID。 

相关寄存器参考:(66条消息) Smmu硬件寄存器—V2_一墨一飞花的博客-CSDN博客_smmu context bank

SMMU_SMRn
streamID匹配寄存器。

        EXMASK, bits[31:16] 屏蔽无关位,如果EXMASK[i]==1,则EXID[i]被忽略;

                相反,EXMASK[i]==0,则EXID[i]有效,用于匹配

        EXID, bits[15:0] 用于匹配的streamID

static int arm_smmu_master_alloc_smes(struct device *dev)
{
	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
	struct arm_smmu_master_cfg *cfg = fwspec->iommu_priv;
	struct arm_smmu_device *smmu = cfg->smmu;
	struct arm_smmu_smr *smrs = smmu->smrs;
	struct iommu_group *group;
	int i, idx, ret;

	mutex_lock(&smmu->stream_map_mutex);
	/* Figure out a viable stream map entry allocation */
	for_each_cfg_sme(fwspec, i, idx) {
		u16 sid = fwspec->ids[i];
		u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT;

		if (idx != INVALID_SMENDX) {
			ret = -EEXIST;
			goto out_err;
		}

		ret = arm_smmu_find_sme(smmu, sid, mask); //分配一个smmu下唯一的idx给当前设备的arm_smmu_master_cfg ,如果sid一致,则返回同样的idx。这个在ft2000+上就是这种情况,bus_dev_fn忽略fn则会导致很多设备相同。
		if (ret < 0)
			goto out_err;

		idx = ret;
		if (smrs && smmu->s2crs[idx].count == 0) {
			smrs[idx].id = sid; //将streamid保存在全局smrs数组下,后续写入寄存器中,一个设备有多个sid的情况下也会分配多个全局唯一的idx。
			smrs[idx].mask = mask;
			smrs[idx].valid = true;
		}
		smmu->s2crs[idx].count++;
		cfg->smendx[i] = (s16)idx;  //设置当前设备arm_smmu_master_cfg 数组smendx为smmu全局分配的idx值。
	}

	group = iommu_group_get_for_dev(dev);
	if (!group)
		group = ERR_PTR(-ENOMEM);
	if (IS_ERR(group)) {
		ret = PTR_ERR(group);
		goto out_err;
	}
	iommu_group_put(group);

	/* It worked! Now, poke the actual hardware */
	for_each_cfg_sme(fwspec, i, idx) {
		arm_smmu_write_sme(smmu, idx);  //将smrs下的sid写入寄存器中。根据idx来定位寄存器地址。
		smmu->s2crs[idx].group = group;
	}

	mutex_unlock(&smmu->stream_map_mutex);
	return 0;

out_err:
	while (i--) {
		arm_smmu_free_sme(smmu, cfg->smendx[i]);
		cfg->smendx[i] = INVALID_SMENDX;
	}
	mutex_unlock(&smmu->stream_map_mutex);
	return ret;
}

 关联smmu下的s2cr寄存器。

static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
				      struct iommu_fwspec *fwspec)
{
	struct arm_smmu_device *smmu = smmu_domain->smmu;
	struct arm_smmu_s2cr *s2cr = smmu->s2crs;
	u8 cbndx = smmu_domain->cfg.cbndx;   //init_context_bank 中分配的,从bitmap中查找的唯一编号,参考arm_smmu_init_domain_context中分配cfg->cbndx的过程。
	enum arm_smmu_s2cr_type type;
	int i, idx;

	if (smmu_domain->stage == ARM_SMMU_DOMAIN_BYPASS)
		type = S2CR_TYPE_BYPASS;
	else
		type = S2CR_TYPE_TRANS;

	for_each_cfg_sme(fwspec, i, idx) {
		if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
			continue;

		s2cr[idx].type = type;
		s2cr[idx].privcfg = S2CR_PRIVCFG_DEFAULT;
		s2cr[idx].cbndx = cbndx;
		arm_smmu_write_s2cr(smmu, idx); //根据设备在全局的idx号,定位到s2cr寄存器地址。
	}
	return 0;
}

相关写寄存器的函数:通过idx关联。 

//写入streamID。可以推断idx值
static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
{
	struct arm_smmu_smr *smr = smmu->smrs + idx;
	u32 reg = smr->id << SMR_ID_SHIFT | smr->mask << SMR_MASK_SHIFT;

	if (!(smmu->features & ARM_SMMU_FEAT_EXIDS) && smr->valid)
		reg |= SMR_VALID;
	writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx));
}

//根据idx 找到s2cr寄存器,可以找到cbndx值,该值可以定位到io-pgtable页表的pgd地址。
static void arm_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx)
{
	struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx;
	u32 reg = (s2cr->type & S2CR_TYPE_MASK) << S2CR_TYPE_SHIFT |  //16-17bit为TYPE位
		  (s2cr->cbndx & S2CR_CBNDX_MASK) << S2CR_CBNDX_SHIFT |   //0-7bit为CBNDX位。
		  (s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT;

	if (smmu->features & ARM_SMMU_FEAT_EXIDS && smmu->smrs &&
	    smmu->smrs[idx].valid)
		reg |= S2CR_EXIDVALID;
	writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_S2CR(idx));
}

static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx)
{
	arm_smmu_write_s2cr(smmu, idx);
	if (smmu->smrs)
		arm_smmu_write_smr(smmu, idx);
}

arm_smmu_device_group:

static struct iommu_group *arm_smmu_device_group(struct device *dev)
{
        struct iommu_fwspec *fwspec = dev->iommu_fwspec;
        struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
        struct iommu_group *group = NULL;
        int i, idx;

        for_each_cfg_sme(fwspec, i, idx) {
                if (group && smmu->s2crs[idx].group &&
                    group != smmu->s2crs[idx].group)
                        return ERR_PTR(-EINVAL);
                printk("===arm_smmu_deivce_group for %s idx %d group %llx \n",dev_name(dev),idx,group);
                group = smmu->s2crs[idx].group; //对于ft2000+,由于idx存在一致,因此会分在一个组。
        }
        printk("==arm_smmu_device_group=== is_pci %d group = %llx name %s \n",dev_is_pci(dev),group,dev_name(dev));
        if (group)
                return iommu_group_ref_get(group);
        printk("group is empty \n");
        if (dev_is_pci(dev))
                group = pci_device_group(dev);
        else
                group = generic_device_group(dev);

        return group;
}

arm_smmu_attach_dev->
arm_smmu_domain_add_master
{
....
    struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
    struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
    arm_smmu_domain_add_master(smmu_domain, cfg, fwspec);
.....
}

/*
配置设备下不同idx组寄存器,其中cbndx使用的是domain唯一的值。
不同设备如果使用同一个domain。则根据唯一的cbndx值(arm_smmu_init_domain_context中分配)
因此设备的dma发送sid找到smr的idx组。对应s2cr中查找cbndx。进而找到ttbr ?
*/
static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain, //设备归属的domain
                                      struct arm_smmu_master_cfg *cfg,   //dev下的
                                      struct iommu_fwspec *fwspec)   //dev下的
{
        struct arm_smmu_device *smmu = smmu_domain->smmu;
        struct arm_smmu_s2cr *s2cr = smmu->s2crs;
        u8 cbndx = smmu_domain->cfg.cbndx; //一个domain唯一的cbndx。
        enum arm_smmu_s2cr_type type;
        int i, idx;

        if (smmu_domain->stage == ARM_SMMU_DOMAIN_BYPASS)
                type = S2CR_TYPE_BYPASS;
        else
                type = S2CR_TYPE_TRANS;
        for_each_cfg_sme(cfg, fwspec, i, idx) {
                if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)
                        continue;

                s2cr[idx].type = type;
                s2cr[idx].privcfg = S2CR_PRIVCFG_DEFAULT;
                s2cr[idx].cbndx = cbndx;
                arm_smmu_write_s2cr(smmu, idx); //写入第idx组s2cr寄存器
        }
        return 0;
}

arm64 smmu 驱动笔记 (4.19)_第1张图片

arm64 smmu 驱动笔记 (4.19)_第2张图片

StreamID写入smmu寄存器:

 arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
    arm_smmu_init_domain_context(domain, smmu);
        arm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg);
        arm_smmu_write_context_bank(smmu, cfg->cbndx);

地址映射使用io_pgtable_ops

//drivers/iommu/io-pgtable.h
struct io_pgtable_ops {
	int (*map)(struct io_pgtable_ops *ops, unsigned long iova,
		   phys_addr_t paddr, size_t size, int prot);
	size_t (*unmap)(struct io_pgtable_ops *ops, unsigned long iova,
			size_t size);
	phys_addr_t (*iova_to_phys)(struct io_pgtable_ops *ops,
				    unsigned long iova);
};


 iommu_ops的map函数arm_smmu_map

static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
                        phys_addr_t paddr, size_t size, int prot)
{
        struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;

        if (!ops)
                return -ENODEV;

        return ops->map(ops, iova, paddr, size, prot);
}

arm_smmu_init_domain_context中对domain进行初始化的时候分配io_pgtable_ops:
 

static int arm_smmu_init_domain_context(struct iommu_domain *domain,
                                        struct arm_smmu_device *smmu)
{
    ................
    pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);
    ................

}


//drivers/iommu/io-pgtable.c

static const struct io_pgtable_init_fns *
io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
#ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE
        [ARM_32_LPAE_S1] = &io_pgtable_arm_32_lpae_s1_init_fns,
        [ARM_32_LPAE_S2] = &io_pgtable_arm_32_lpae_s2_init_fns,
        [ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns,
        [ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns,
#endif
#ifdef CONFIG_IOMMU_IO_PGTABLE_ARMV7S
        [ARM_V7S] = &io_pgtable_arm_v7s_init_fns,
#endif
};

struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt,
                                            struct io_pgtable_cfg *cfg,
                                            void *cookie)
{
        struct io_pgtable *iop;
        const struct io_pgtable_init_fns *fns;

        if (fmt >= IO_PGTABLE_NUM_FMTS)
                return NULL;

        fns = io_pgtable_init_table[fmt];
        if (!fns)
                return NULL;

        iop = fns->alloc(cfg, cookie);
        if (!iop)
                return NULL;

        iop->fmt        = fmt;
        iop->cookie     = cookie;
        iop->cfg        = *cfg;

        return &iop->ops;
}

io_pgtable_arm_64_lpae_s1_init_fns: driver/iommu/io-pgtable-arm.c

struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
        .alloc  = arm_64_lpae_alloc_pgtable_s1,
        .free   = arm_lpae_free_pgtable,
};




static struct io_pgtable *
arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie)
{
..................

    struct arm_lpae_io_pgtable *data;
    data = arm_lpae_alloc_pgtable(cfg); //分配
    data->pgd = __arm_lpae_alloc_pages(data->pgd_size, GFP_KERNEL, cfg);
...................
}



static struct arm_lpae_io_pgtable *
arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg)
{
        unsigned long va_bits, pgd_bits;
        struct arm_lpae_io_pgtable *data;
。。。。。。。。。。。

        data = kmalloc(sizeof(*data), GFP_KERNEL);
        if (!data)
                return NULL;

        data->pg_shift = __ffs(cfg->pgsize_bitmap);
        data->bits_per_level = data->pg_shift - ilog2(sizeof(arm_lpae_iopte));

        va_bits = cfg->ias - data->pg_shift;
        data->levels = DIV_ROUND_UP(va_bits, data->bits_per_level);

        /* Calculate the actual size of our pgd (without concatenation) */
        pgd_bits = va_bits - (data->bits_per_level * (data->levels - 1));
        data->pgd_size = 1UL << (pgd_bits + ilog2(sizeof(arm_lpae_iopte)));

        data->iop.ops = (struct io_pgtable_ops) {
                .map            = arm_lpae_map, //map函数
                .unmap          = arm_lpae_unmap,

                .iova_to_phys   = arm_lpae_iova_to_phys,
        };

        return data;
}

案例:

ft2000+/5.10内核 

分组树:

[root@localhost ~]# tree /sys/kernel/iommu_groups/
/sys/kernel/iommu_groups/
├── 0
│   ├── devices
│   │   └── 0000:41:00.0 -> ../../../../devices/pci0000:40/0000:40:00.0/0000:41:00.0
│   ├── reserved_regions
│   └── type
├── 1
│   ├── devices
│   │   └── 0000:03:00.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:01.0/0000:03:00.0
│   ├── reserved_regions
│   └── type
├── 10
│   ├── devices
│   │   └── 0000:40:00.0 -> ../../../../devices/pci0000:40/0000:40:00.0
│   ├── reserved_regions
│   └── type
├── 11
│   ├── devices
│   │   └── 0000:50:00.0 -> ../../../../devices/pci0000:50/0000:50:00.0
│   ├── reserved_regions
│   └── type
├── 12
│   ├── devices
│   │   └── 0000:70:00.0 -> ../../../../devices/pci0000:70/0000:70:00.0
│   ├── reserved_regions
│   └── type
├── 13
│   ├── devices
│   │   ├── 0000:04:00.1 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:02.0/0000:04:00.1
│   │   ├── 0000:04:00.2 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:02.0/0000:04:00.2
│   │   └── 0000:04:00.3 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:02.0/0000:04:00.3
│   ├── reserved_regions
│   └── type
├── 14
│   ├── devices
│   │   └── 0000:21:00.0 -> ../../../../devices/pci0000:20/0000:20:00.0/0000:21:00.0
│   ├── reserved_regions
│   └── type
├── 2
│   ├── devices
│   │   └── 0000:00:00.0 -> ../../../../devices/pci0000:00/0000:00:00.0
│   ├── reserved_regions
│   └── type
├── 3
│   ├── devices
│   │   └── 0000:01:00.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0
│   ├── reserved_regions
│   └── type
├── 4
│   ├── devices
│   │   └── 0000:02:01.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:01.0
│   ├── reserved_regions
│   └── type
├── 5
│   ├── devices
│   │   └── 0000:02:02.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:02.0
│   ├── reserved_regions
│   └── type
├── 6
│   ├── devices
│   │   └── 0000:02:04.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:04.0
│   ├── reserved_regions
│   └── type
├── 7
│   ├── devices
│   │   └── 0000:02:05.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:05.0
│   ├── reserved_regions
│   └── type
├── 8
│   ├── devices
│   │   └── 0000:02:06.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:06.0
│   ├── reserved_regions
│   └── type
└── 9
    ├── devices
    │   ├── 0000:04:00.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:02.0/0000:04:00.0
    │   └── 0000:20:00.0 -> ../../../../devices/pci0000:20/0000:20:00.0
    ├── reserved_regions
    └── type

48 directories, 30 files

 设备下最终sid:看下上述9组。包含04:00.0和20:00.0。 看下面的日志:恰好这两个共同的0x400的sid;

[   10.503563] ===arm_smmu_add_device dev 0000:41:00.0 num_ids 4 of 0 sid 4100 
[   10.510581] ===arm_smmu_add_device dev 0000:41:00.0 num_ids 4 of 1 sid 4101 
[   10.517598] ===arm_smmu_add_device dev 0000:41:00.0 num_ids 4 of 2 sid 820 
[   10.524522] ===arm_smmu_add_device dev 0000:41:00.0 num_ids 4 of 3 sid 820 
[   10.677266] ===arm_smmu_add_device dev 0000:03:00.0 num_ids 2 of 0 sid 300 
[   10.684191] ===arm_smmu_add_device dev 0000:03:00.0 num_ids 2 of 1 sid 60 
[   11.108133] ===arm_smmu_add_device dev 0000:00:00.0 num_ids 2 of 0 sid 0 
[   11.121369] ===arm_smmu_add_device dev 0000:00:00.0 num_ids 2 of 1 sid 0 
[   11.155433] ===arm_smmu_add_device dev 0000:01:00.0 num_ids 2 of 0 sid 100 
[   11.162367] ===arm_smmu_add_device dev 0000:01:00.0 num_ids 2 of 1 sid 20 
[   11.215796] ===arm_smmu_add_device dev 0000:02:01.0 num_ids 2 of 0 sid 208 
[   11.215799] ===arm_smmu_add_device dev 0000:02:01.0 num_ids 2 of 1 sid 41 
[   11.268019] ===arm_smmu_add_device dev 0000:02:02.0 num_ids 2 of 0 sid 210 
[   11.283765] ===arm_smmu_add_device dev 0000:02:02.0 num_ids 2 of 1 sid 42 
[   11.387949] ===arm_smmu_add_device dev 0000:02:04.0 num_ids 2 of 0 sid 220 
[   11.387950] ===arm_smmu_add_device dev 0000:02:04.0 num_ids 2 of 1 sid 44 
[   11.442892] ===arm_smmu_add_device dev 0000:02:05.0 num_ids 2 of 0 sid 228 
[   11.449820] ===arm_smmu_add_device dev 0000:02:05.0 num_ids 2 of 1 sid 45 
[   11.484502] ===arm_smmu_add_device dev 0000:02:06.0 num_ids 2 of 0 sid 230 
[   11.491435] ===arm_smmu_add_device dev 0000:02:06.0 num_ids 2 of 1 sid 46 
[   11.526207] ===arm_smmu_add_device dev 0000:20:00.0 num_ids 2 of 0 sid 2000 
[   11.533218] ===arm_smmu_add_device dev 0000:20:00.0 num_ids 2 of 1 sid 400   //0x400 
[   11.568096] ===arm_smmu_add_device dev 0000:40:00.0 num_ids 2 of 0 sid 4000 
[   11.575107] ===arm_smmu_add_device dev 0000:40:00.0 num_ids 2 of 1 sid 800 
[   11.610065] ===arm_smmu_add_device dev 0000:50:00.0 num_ids 2 of 0 sid 5000 
[   11.617080] ===arm_smmu_add_device dev 0000:50:00.0 num_ids 2 of 1 sid a00 
[   11.652088] ===arm_smmu_add_device dev 0000:70:00.0 num_ids 2 of 0 sid 7000 
[   11.665674] ===arm_smmu_add_device dev 0000:70:00.0 num_ids 2 of 1 sid e00 
[   13.530809] ===arm_smmu_add_device dev 0000:04:00.0 num_ids 2 of 0 sid 400  //x0400
[   13.537743] ===arm_smmu_add_device dev 0000:04:00.0 num_ids 2 of 1 sid 80 
[   13.848325] ===arm_smmu_add_device dev 0000:04:00.1 num_ids 2 of 0 sid 401 
[   13.855250] ===arm_smmu_add_device dev 0000:04:00.1 num_ids 2 of 1 sid 80 
[   14.171899] ===arm_smmu_add_device dev 0000:04:00.2 num_ids 2 of 0 sid 402 
[   14.178831] ===arm_smmu_add_device dev 0000:04:00.2 num_ids 2 of 1 sid 80 
[   14.489847] ===arm_smmu_add_device dev 0000:04:00.3 num_ids 2 of 0 sid 403 
[   14.496779] ===arm_smmu_add_device dev 0000:04:00.3 num_ids 2 of 1 sid 80 
[   21.292421] ===arm_smmu_add_device dev 0000:21:00.0 num_ids 2 of 0 sid 2100 
[   21.299443] ===arm_smmu_add_device dev 0000:21:00.0 num_ids 2 of 1 sid 420 

你可能感兴趣的:(linux)