文章linux 4.14 代码分析smmu 流程

linux 在驱动找到对应的设备后会执行driver_probe_device 函数,具体dev和drv 匹配参考文章链接

driver_probe_device->really_probe->dma_configure->of_dma_configure

int of_dma_configure(struct device *dev, struct device_node *np)
{const struct iommu_ops *iommu;省略部分代码iommu = of_iommu_configure(dev, np);if (IS_ERR(iommu) && PTR_ERR(iommu) == -EPROBE_DEFER)return -EPROBE_DEFER;dev_dbg(dev, "device is%sbehind an iommu\n",iommu ? " " : " not ");arch_setup_dma_ops(dev, dma_addr, size, iommu, coherent);return 0;
}

通过of_iommu_configure 获取到了iommu 的操作函数集,arch_setup_dma_ops 设置了dev->dma_ops  决定了dma api 执行的函数。

of_iommu_configure 函数

const struct iommu_ops *of_iommu_configure(struct device *dev,struct device_node *master_np)
{const struct iommu_ops *ops = NULL;struct iommu_fwspec *fwspec = dev->iommu_fwspec;......if (fwspec) {if (fwspec->ops)return fwspec->ops;/* In the deferred case, start again from scratch */iommu_fwspec_free(dev);}if (dev_is_pci(dev)) {struct of_pci_iommu_alias_info info = {.dev = dev,.np = master_np,};err = pci_for_each_dma_alias(to_pci_dev(dev),of_pci_iommu_init, &info);} else {struct of_phandle_args iommu_spec;int idx = 0;while (!of_parse_phandle_with_args(master_np, "iommus","#iommu-cells",idx, &iommu_spec)) {err = of_iommu_xlate(dev, &iommu_spec);of_node_put(iommu_spec.np);idx++;if (err)break;}}if (!err)ops = dev->iommu_fwspec->ops;.......if (ops && ops->add_device && dev->bus && !dev->iommu_group)err = ops->add_device(dev);return ops;
}

从函数中可以看出,区分了是不是pci设备,看不走pci设备的代码,调用了of_iommu_xlate 函数。首先通过of_parse_phandle_with_args函数找到了iommu-spec 。 这里具体iomms,iommu-cell 的含义可以参考

Documentation\devicetree\bindings\iommu 中的iommu.txt 文档,从文档中部分解释摘抄如下:

- #iommu-cells: The number of cells in an IOMMU specifier needed to encode an address.
The meaning of the IOMMU specifier is defined by the device tree binding of the specific IOMMU. Below are a few examples of typical use-cases:
- #iommu-cells = <0>: Single master IOMMU devices are not configurable and therefore no additional information needs to be encoded in the specifier. This may also apply to multiple master IOMMU devices that do not allow the association of masters to be configured. Note that an IOMMU can by design be multi-master yet only expose a single master in a given configuration. In such cases the number of cells will usually be 1 as in the next case.
- #iommu-cells = <1>: Multiple master IOMMU devices may need to be configured in order to enable translation for a given master. In such cases the single address cell corresponds to the master device's ID. In some cases more than one cell can be required to represent a single master ID.
- #iommu-cells = <4>: Some IOMMU devices allow the DMA window for masters to be configured. The first cell of the address in this may contain the master device's ID for example, while the second cell could contain the start of the DMA window for the given device. The length of the DMA window is given by the third and fourth cells.

根据文档中的例子看当使用cells 为 1 时需要和 stream id 匹配, cells 为4 的时候需要dma windows。上面的while 循环不断遍历

获得了iommus 描述符对应的地址。然后调用了of_iommu_xlate 函数

static int of_iommu_xlate(struct device *dev,struct of_phandle_args *iommu_spec)
{const struct iommu_ops *ops;struct fwnode_handle *fwnode = &iommu_spec->np->fwnode;int err;ops = iommu_ops_from_fwnode(fwnode); //通过fwnode 去查找iommu驱动注册的opsif ((ops && !ops->of_xlate) ||!of_device_is_available(iommu_spec->np) ||(!ops && !of_iommu_driver_present(iommu_spec->np)))return NO_IOMMU;err = iommu_fwspec_init(dev, &iommu_spec->np->fwnode, ops);if (err)return err;/** The otherwise-empty fwspec handily serves to indicate the specific* IOMMU device we're waiting for, which will be useful if we ever get* a proper probe-ordering dependency mechanism in future.*/if (!ops)return -EPROBE_DEFER;return ops->of_xlate(dev, iommu_spec);
}
const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)
{const struct iommu_ops *ops = NULL;struct iommu_device *iommu;spin_lock(&iommu_device_lock);list_for_each_entry(iommu, &iommu_device_list, list)if (iommu->fwnode == fwnode) {ops = iommu->ops;break;}spin_unlock(&iommu_device_lock);return ops;
}

时从fwnode 的链表中查找对应的iommu device ,fwnode 往链表中添加时在iommu 驱动注册的时候。后面会介绍,在此获得了ops后,会执行iommu_fwspec_init函数

int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,const struct iommu_ops *ops)
{struct iommu_fwspec *fwspec = dev->iommu_fwspec;if (fwspec)return ops == fwspec->ops ? 0 : -EINVAL;fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);if (!fwspec)return -ENOMEM;of_node_get(to_of_node(iommu_fwnode));fwspec->iommu_fwnode = iommu_fwnode;fwspec->ops = ops;dev->iommu_fwspec = fwspec;return 0;

函数此函数主要是把ops 赋值给另外iommu_fwspec.。回到上面的of_iommu_configure 函数

if (!err)
        ops = dev->iommu_fwspec->ops;

这句代码拿到了需要的ops。

总结下获得iommu ops 代码执行流程:

driver_probe_device->really_probe->dma_configure->of_dma_configure->of_iommu_configure->of_iommu_xlate->iommu_ops_from_fwnode

以上可以看出获得ops 主要是根据fwnode 找到的。

---------------------------------------为fwnode 设置smmu ops-----------------------------------------------------------------------------------------------

kernel\msm-4.14\drivers\iommu\arm-smmu.c 文件中可以看到arm smmu 的驱动

static struct platform_driver arm_smmu_driver = {
    .driver    = {
        .name        = "arm-smmu",
        .of_match_table    = of_match_ptr(arm_smmu_of_match),
        .pm        = &arm_smmu_pm_ops,
        .suppress_bind_attrs = true,
    },
    .probe    = arm_smmu_device_dt_probe,
    .remove    = arm_smmu_device_remove,
};

arm_smmu_of_match 此匹配表中有多种设备的驱动

static const struct of_device_id arm_smmu_of_match[] = {
    { .compatible = "arm,smmu-v1", .data = &smmu_generic_v1 },
    { .compatible = "arm,smmu-v2", .data = &smmu_generic_v2 },
    { .compatible = "arm,mmu-400", .data = &smmu_generic_v1 },
    { .compatible = "arm,mmu-401", .data = &arm_mmu401 },
    { .compatible = "arm,mmu-500", .data = &arm_mmu500 },
    { .compatible = "cavium,smmu-v2", .data = &cavium_smmuv2 },
    { .compatible = "qcom,smmu-v2", .data = &qcom_smmuv2 },
    { .compatible = "qcom,qsmmu-v500", .data = &qcom_smmuv500 },
    { },
};

qcom,qsmmu-v500 是目前高通使用的,是高通基于mmu500

arm_smmu_device_dt_probe函数比较大 前面是获取资源的一些函数调用

static int arm_smmu_device_dt_probe(struct platform_device *pdev)
{iommu_device_set_ops(&smmu->iommu, &arm_smmu_ops);iommu_device_set_fwnode(&smmu->iommu, dev->fwnode);err = iommu_device_register(&smmu->iommu);if (!using_legacy_binding)arm_smmu_bus_init();}

这三个函数主要是用来设置arm smmu ops 的,arm smmu ops 函数还是挺多的。

static struct iommu_ops arm_smmu_ops = {.capable        = arm_smmu_capable,.domain_alloc       = arm_smmu_domain_alloc,.domain_free       = arm_smmu_domain_free,.attach_dev     = arm_smmu_attach_dev,.detach_dev      = arm_smmu_detach_dev,.map         = arm_smmu_map,.unmap          = arm_smmu_unmap,.map_sg           = arm_smmu_map_sg,.iova_to_phys        = arm_smmu_iova_to_phys,.iova_to_phys_hard = arm_smmu_iova_to_phys_hard,.add_device       = arm_smmu_add_device,.remove_device       = arm_smmu_remove_device,.device_group     = arm_smmu_device_group,.domain_get_attr   = arm_smmu_domain_get_attr,.domain_set_attr    = arm_smmu_domain_set_attr,.of_xlate       = arm_smmu_of_xlate,.get_resv_regions  = arm_smmu_get_resv_regions,.put_resv_regions  = arm_smmu_put_resv_regions,.pgsize_bitmap     = -1UL, /* Restricted during device attach */.trigger_fault        = arm_smmu_trigger_fault,.reg_read     = arm_smmu_reg_read,.reg_write     = arm_smmu_reg_write,.tlbi_domain      = arm_smmu_tlbi_domain,.enable_config_clocks   = arm_smmu_enable_config_clocks,.disable_config_clocks = arm_smmu_disable_config_clocks,.is_iova_coherent = arm_smmu_is_iova_coherent,.iova_to_pte = arm_smmu_iova_to_pte,
};

iommu_device_set_ops 函数很简单就是赋值操作

static inline void iommu_device_set_ops(struct iommu_device *iommu,const struct iommu_ops *ops)
{iommu->ops = ops;
}

iommu_device_set_fwnode 也是简单的赋值

static inline void iommu_device_set_fwnode(struct iommu_device *iommu,struct fwnode_handle *fwnode)
{iommu->fwnode = fwnode;
}

两个函数分别把ops 和dev 的fwnode 赋值给了smmu->iommu,然后执行iommu_device_register

int iommu_device_register(struct iommu_device *iommu)
{spin_lock(&iommu_device_lock);list_add_tail(&iommu->list, &iommu_device_list);spin_unlock(&iommu_device_lock);return 0;
}

iommu_device_register 将iommu 加入到了iommu_device_list 链表中。在获取ops 时会通过iommu_ops_from_fwnode 来获取ops。

arm_smmu_device_dt_probe->iommu_device_set_ops

                                             ->iommu_device_set_fwnode

                                              ->iommu_device_register

------------------------------------给设备添加group 并且获得domain-----------------------

在arm_smmu_device_dt_probe 执行完iommu_device_register 函数后会执行arm_smmu_bus_init函数

arm_smmu_bus_init 函数 将从总线上遍历设备

static void arm_smmu_bus_init(void)
{/* Oh, for a proper bus abstraction */if (!iommu_present(&platform_bus_type))bus_set_iommu(&platform_bus_type, &arm_smmu_ops);
#ifdef CONFIG_ARM_AMBAif (!iommu_present(&amba_bustype))bus_set_iommu(&amba_bustype, &arm_smmu_ops);
#endif
#ifdef CONFIG_PCIif (!iommu_present(&pci_bus_type)) {pci_request_acs();bus_set_iommu(&pci_bus_type, &arm_smmu_ops);}
#endif
}

根据不同的总线类型调用bus_set_iommu函数

int bus_set_iommu(struct bus_type *bus, const struct iommu_ops *ops)
{int err;if (bus->iommu_ops != NULL)return -EBUSY;bus->iommu_ops = ops;/* Do IOMMU specific setup for this bus-type */err = iommu_bus_init(bus, ops);if (err)bus->iommu_ops = NULL;return err;
}

在bus_set_iommu函数中将 iommu ops 赋值给了 bus 的iommu ops 这里的iommu ops 就是前面提到的arm_smmu_ops

static int iommu_bus_init(struct bus_type *bus, const struct iommu_ops *ops)
{int err;struct notifier_block *nb;struct iommu_callback_data cb = {.ops = ops,};nb = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);if (!nb)return -ENOMEM;nb->notifier_call = iommu_bus_notifier;err = bus_register_notifier(bus, nb);if (err)goto out_free;err = bus_for_each_dev(bus, NULL, &cb, add_iommu_group);if (err)goto out_err;return 0;....
}

iommu_bus_init 初始化时候注册了一个通知回调函数。bus_for_each_dev 函数根据函数名也可以看出时遍历设备。这里还有个add_iommu_group 函数通过bus_for_each_dev 传递下去。

int bus_for_each_dev(struct bus_type *bus, struct device *start,void *data, int (*fn)(struct device *, void *))
{struct klist_iter i;struct device *dev;int error = 0;if (!bus || !bus->p)return -EINVAL;klist_iter_init_node(&bus->p->klist_devices, &i,(start ? &start->p->knode_bus : NULL));while ((dev = next_device(&i)) && !error)error = fn(dev, data);klist_iter_exit(&i);return error;
}

从总线的设备上不断的遍历,找到设备后执行fn函数,也就是执行add_iommu_group函数。fn函数有两个参数一个是dev,另一个是 data。这个data 就是iommu_bus_init中的

struct iommu_callback_data cb = {
        .ops = ops,
    };

所以在add_iommu_group 中通过

static int add_iommu_group(struct device *dev, void *data)
{struct iommu_callback_data *cb = data;const struct iommu_ops *ops = cb->ops;int ret;if (!ops->add_device)return 0;WARN_ON(dev->iommu_group);ret = ops->add_device(dev);/** We ignore -ENODEV errors for now, as they just mean that the* device is not translated by an IOMMU. We still care about* other errors and fail to initialize when they happen.*/if (ret == -ENODEV)ret = 0;return ret;
}

ops->add_device 实际上就是调用了arm_smmu_ops->add_device 函数。从前面的函数集可以看到arm_smmu_ops中的add_device 是指向arm_smmu_add_device 函数

static int arm_smmu_add_device(struct device *dev)
{
....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_pwr_off;}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_pwr_off;}}
......
cfg->smmu = smmu;fwspec->iommu_priv = cfg;while (i--)cfg->smendx[i] = INVALID_SMENDX;ret = arm_smmu_master_alloc_smes(dev);if (ret)goto out_cfg_free;}

arm_smmu_master_alloc_smes  会为没有group 的设置创建group,关于smmu的group 和stream id 概念 参考文章

https://zhuanlan.zhihu.com/p/27026590

https://blog.csdn.net/tiantao2012/article/details/62892281

stream id 的概念可以参考 arm mmu500 文档

这里的TBU 代表的是转换缓冲单元,包含了存储页表的TLB。TCU 是转换控制单元,用于控制和管理地址转换。

在高通的qsmuu-v500 中 也是很多个TBU 链接 一个TCU(实现了单个TCU)。

arm_smmu_master_alloc_smes 函数回得到group

static int arm_smmu_master_alloc_smes(struct device *dev)
{
const struct iommu_ops *ops = dev->bus->iommu_ops;......group = iommu_group_get_for_dev(dev);if (!group)group = ERR_PTR(-ENOMEM);if (IS_ERR(group)) {ret = PTR_ERR(group);goto iommu_group_err;}iommu_group_put(group);/* It worked! Don't poke the actual hardware until we've attached */for_each_cfg_sme(fwspec, i, idx)smmu->s2crs[idx].group = group;}

iommu_group_get_for_dev 主要是分配group 设置domian

struct iommu_group *iommu_group_get_for_dev(struct device *dev)
{
const struct iommu_ops *ops = dev->bus->iommu_ops;.....group = ops->device_group(dev);if (WARN_ON_ONCE(group == NULL))return ERR_PTR(-EINVAL);if (IS_ERR(group))return group;/** Try to allocate a default domain - needs support from the* IOMMU driver.*/if (!group->default_domain) {struct iommu_domain *dom;dom = __iommu_domain_alloc(dev->bus, iommu_def_domain_type);if (!dom && iommu_def_domain_type != IOMMU_DOMAIN_DMA) {dev_warn(dev,"failed to allocate default IOMMU domain of type %u; falling back to IOMMU_DOMAIN_DMA",iommu_def_domain_type);dom = __iommu_domain_alloc(dev->bus, IOMMU_DOMAIN_DMA);}group->default_domain = dom;if (!group->domain)group->domain = dom;}ret = iommu_group_add_device(group, dev);......
}

ops->device_group 实际上调用arm_smmu_device_group 函数

static struct iommu_group *arm_smmu_device_group(struct device *dev)
{....if (group)iommu_group_ref_get(group);else {if (dev_is_pci(dev))group = pci_device_group(dev);elsegroup = generic_device_group(dev);if (IS_ERR(group))return NULL;}if (arm_smmu_arch_device_group(dev, group)) {iommu_group_put(group);return ERR_PTR(-EINVAL);}return group;
}

如果不是pci设备 执行generic_device_group 函数,然后调用iommu_group_alloc 此函数回分配一个group 并且进行一些kobject操作。

group domain 参考https://blog.csdn.net/tiantao2012/article/details/62892281 文章,

在获得到group后 iommu_group_get_for_dev 执行__iommu_domain_alloc 函数

static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,unsigned type)
{struct iommu_domain *domain;if (bus == NULL || bus->iommu_ops == NULL)return NULL;domain = bus->iommu_ops->domain_alloc(type);if (!domain)return NULL;domain->ops  = bus->iommu_ops;domain->type = type;/* Assume all sizes by default; the driver may override this later */domain->pgsize_bitmap  = bus->iommu_ops->pgsize_bitmap;domain->is_debug_domain = false;memset(domain->name, 0, IOMMU_DOMAIN_NAME_LEN);return domain;
}

上边函数中为domain的ops 设置了bus->iommu_ops ,bus->iommu_ops 在前面已经被设置为了arm_smmu_ops

所以设备使用smmu的ops 只需要通过获得group 在获得domain。并且设置了domain的ops

总结设置group过程:

arm_smmu_device_dt_probe->arm_smmu_bus_init->bus_set_iommu->iommu_bus_init->bus_for_each_dev->add_iommu_group->arm_smmu_add_device_arm_smmu_master_alloc_smes->iommu_group_get_for_dev->arm_smmu_device_group->generic_device_group

-------------------------------------------配置group 的iommu data----------------------------------------------------------------------

回到arm_smmu_device_group函数在获得group后执行arm_smmu_arch_device_group 函数,在此函数中调用了smmu->arch_ops->device_group(dev, group);, arch_ops 的赋值时在arm smmu 的probe 函数。

static struct platform_driver arm_smmu_driver = {.driver    = {.name       = "arm-smmu",.of_match_table = of_match_ptr(arm_smmu_of_match),.pm      = &arm_smmu_pm_ops,.suppress_bind_attrs = true,},.probe   = arm_smmu_device_dt_probe,.remove = arm_smmu_device_remove,
};
static int arm_smmu_device_dt_probe(struct platform_device *pdev)
{......arm_smmu_arch_init();.....data = of_device_get_match_data(dev);smmu->version = data->version;smmu->model = data->model;smmu->arch_ops = data->arch_ops;....
}

可以看到smmu->arch_ops = data->arch_ops; 这句代码。这里还有个arm_smmu_arch_init 函数,会进行一些初始化。后边会用到。

查看of_device_get_match_data 函数的实现,回发现data 来自of_match_stable

onst void *of_device_get_match_data(const struct device *dev)
{const struct of_device_id *match;match = of_match_device(dev->driver->of_match_table, dev);if (!match)return NULL;return match->data;
}

实际上就是来自arm_smmu.c文件中的

ARM_SMMU_MATCH_DATA(smmu_generic_v1, ARM_SMMU_V1, GENERIC_SMMU, NULL);
ARM_SMMU_MATCH_DATA(smmu_generic_v2, ARM_SMMU_V2, GENERIC_SMMU, NULL);
ARM_SMMU_MATCH_DATA(arm_mmu401, ARM_SMMU_V1_64K, GENERIC_SMMU, NULL);
ARM_SMMU_MATCH_DATA(arm_mmu500, ARM_SMMU_V2, ARM_MMU500, NULL);
ARM_SMMU_MATCH_DATA(cavium_smmuv2, ARM_SMMU_V2, CAVIUM_SMMUV2, NULL);
ARM_SMMU_MATCH_DATA(qcom_smmuv2, ARM_SMMU_V2, QCOM_SMMUV2, &qsmmuv2_arch_ops);
ARM_SMMU_MATCH_DATA(qcom_smmuv500, ARM_SMMU_V2, QCOM_SMMUV500,&qsmmuv500_arch_ops);static const struct of_device_id arm_smmu_of_match[] = {{ .compatible = "arm,smmu-v1", .data = &smmu_generic_v1 },{ .compatible = "arm,smmu-v2", .data = &smmu_generic_v2 },{ .compatible = "arm,mmu-400", .data = &smmu_generic_v1 },{ .compatible = "arm,mmu-401", .data = &arm_mmu401 },{ .compatible = "arm,mmu-500", .data = &arm_mmu500 },{ .compatible = "cavium,smmu-v2", .data = &cavium_smmuv2 },{ .compatible = "qcom,smmu-v2", .data = &qcom_smmuv2 },{ .compatible = "qcom,qsmmu-v500", .data = &qcom_smmuv500 },{ },
};

高通平台最新使用的时qcom,qsmmu-v500,所以使用的data 是qcom_smmuv500,对应的操作函数如下

struct arm_smmu_arch_ops qsmmuv500_arch_ops = {.init = qsmmuv500_arch_init,.iova_to_phys_hard = qsmmuv500_iova_to_phys_hard,.init_context_bank = qsmmuv500_init_cb,.device_group = qsmmuv500_device_group,
};

所以arch_ops->device_group 函数执行的是 qsmmuv500_device_group

static int qsmmuv500_device_group(struct device *dev,struct iommu_group *group)
{struct iommu_fwspec *fwspec = dev->iommu_fwspec;struct arm_smmu_device *smmu = fwspec_smmu(fwspec);struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);struct qsmmuv500_group_iommudata *iommudata;u32 actlr, i;struct arm_smmu_smr *smr;iommudata = to_qsmmuv500_group_iommudata(group);if (!iommudata) {iommudata = kzalloc(sizeof(*iommudata), GFP_KERNEL);if (!iommudata)return -ENOMEM;iommu_group_set_iommudata(group, iommudata,qsmmuv500_release_group_iommudata);}for (i = 0; i < data->actlr_tbl_size; i++) {smr = &data->actlrs[i].smr;actlr = data->actlrs[i].actlr;if (!arm_smmu_fwspec_match_smr(fwspec, smr))continue;if (!iommudata->has_actlr) {iommudata->actlr = actlr;iommudata->has_actlr = true;} else if (iommudata->actlr != actlr) {return -EINVAL;}}return 0;
}

此函数创建了一个iommudata 将get_qsmmuv500_archdata (实际调用smmu->archdata)获取的data 一些值赋值给iommudta。

smmu->archdata 获得是从前面提到的arm_smmu_arch_init函数。

static int arm_smmu_arch_init(struct arm_smmu_device *smmu)
{if (!smmu->arch_ops)return 0;if (!smmu->arch_ops->init)return 0;return smmu->arch_ops->init(smmu);
}

init函数实际上执行的是qsmmuv500_arch_init

static int qsmmuv500_arch_init(struct arm_smmu_device *smmu)
{struct resource *res;struct device *dev = smmu->dev;struct qsmmuv500_archdata *data;struct platform_device *pdev;int ret;u32 val;void __iomem *reg;data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);if (!data)return -ENOMEM;INIT_LIST_HEAD(&data->tbus);pdev = container_of(dev, struct platform_device, dev);res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tcu-base");if (!res) {dev_err(dev, "Unable to get the tcu-base\n");return -EINVAL;}data->tcu_base = devm_ioremap(dev, res->start, resource_size(res));if (IS_ERR(data->tcu_base))return PTR_ERR(data->tcu_base);data->version = readl_relaxed(data->tcu_base + TCU_HW_VERSION_HLOS1);smmu->archdata = data;if (arm_smmu_is_static_cb(smmu))return 0;ret = qsmmuv500_read_actlr_tbl(smmu);if (ret)return ret;.....}

回到qsmmuv500_device_group 函数

void iommu_group_set_iommudata(struct iommu_group *group, void *iommu_data,void (*release)(void *iommu_data))
{group->iommu_data = iommu_data;group->iommu_data_release = release;
}

将iommu_data  赋值给group 组中,从高通的代看iommu_data保存着应该是硬件的一些信息。

总结下给dev 设置group增加iommu data流程:

arm_smmu_device_dt_probe->arm_smmu_bus_init->bus_set_iommu->iommu_bus_init->bus_for_each_dev->add_iommu_group->arm_smmu_add_device_arm_smmu_master_alloc_smes->iommu_group_get_for_dev->arm_smmu_device_group->arm_smmu_arch_device_group->qsmmuv500_device_group->get_qsmmuv500_archdata

-------------------------------------------------------------------设置dma ops-----------------------------

回到 of_dma_configure 函数前面

driver_probe_device->really_probe->dma_configure->of_dma_configure->of_iommu_configure->of_iommu_xlate->iommu_ops_from_fwnode
拿到 iommu ops 后执行

driver_probe_device->really_probe->dma_configure->of_dma_configure->arch_setup_dma_ops

void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,const struct iommu_ops *iommu, bool coherent)
{if (!dev->dma_ops)dev->dma_ops = &swiotlb_dma_ops;dev->archdata.dma_coherent = coherent;__iommu_setup_dma_ops(dev, dma_base, size, iommu);#ifdef CONFIG_XENif (xen_initial_domain()) {dev->archdata.dev_dma_ops = dev->dma_ops;dev->dma_ops = xen_dma_ops;}
#end

调用了__iommu_setup_dma_ops 函数

static void __iommu_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,const struct iommu_ops *ops)
{struct iommu_domain *domain;if (!ops)return;/** The IOMMU core code allocates the default DMA domain, which the* underlying IOMMU driver needs to support via the dma-iommu layer.*/domain = iommu_get_domain_for_dev(dev);if (!domain)goto out_err;if (domain->type == IOMMU_DOMAIN_DMA) {if (iommu_dma_init_domain(domain, dma_base, size, dev))goto out_err;dev->dma_ops = &iommu_dma_ops;}return;out_err:pr_debug("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",dev_name(dev));
}

iommu_get_domain_for_dev 通过设备的group 获取到domain,之前设置的type 是IOMMU_DOMAIN_DMA 执行iommu_dma_init_domain函数,此函数代码没看懂,看官方注释是根据dma base addr 和size 去配置空间。

addr 是of_dma_configure 函数中通过of_dma_get_range函数获得的,它在通过dtsi 获得。

看下dev->dma_ops 绑定的函数 集iommu_dma_ops

static const struct dma_map_ops iommu_dma_ops = {.alloc = __iommu_alloc_attrs,.free = __iommu_free_attrs,.mmap = __iommu_mmap_attrs,.get_sgtable = __iommu_get_sgtable,.map_page = __iommu_map_page,.unmap_page = __iommu_unmap_page,.map_sg = __iommu_map_sg_attrs,.unmap_sg = __iommu_unmap_sg_attrs,.sync_single_for_cpu = __iommu_sync_single_for_cpu,.sync_single_for_device = __iommu_sync_single_for_device,.sync_sg_for_cpu = __iommu_sync_sg_for_cpu,.sync_sg_for_device = __iommu_sync_sg_for_device,.map_resource = iommu_dma_map_resource,.unmap_resource = iommu_dma_unmap_resource,.mapping_error = iommu_dma_mapping_error,
};

到这里dev 的dma_ops 和dev 通过group,domain ops 已经全部设置完成了

arm smmu 学习(1)相关推荐

  1. [architecture]-ARM SMMU学习概念总结一篇就够了

    引流关键词: SMMU,mmu500,mmu600,mmu700,system mmu,Non-cacheable,Cacheable, non-shareable,inner-shareable,o ...

  2. ARM SMMU学习笔记

    1. 什么是SMMU? SMMU(system mmu),是I/O device与总线之间的地址转换桥. 它在系统的位置如下图: 它与mmu的功能类似,可以实现地址转换,内存属性转换,权限检查等功能. ...

  3. linux中断响应时间太慢_linux+arm系统学习与基础学习

    先说说arm吧.先前学习linux+arm老是觉得arm次要的,学习arm和学习单片机一样,了解了中断,UART,AD,定时器就差不多了,工作重心主要在linux上面.随着时间的推移,学习的深入,自我 ...

  4. 嵌入式知识-ARM裸机-学习笔记(2):利用GPIO来控制LED(附mkv210_image.c文件解析)

    嵌入式知识-ARM裸机-学习笔记(2):利用GPIO来控制LED(附mkv210_image.c文件解析) 首先声明该博客是针对朱有鹏老师的嵌入式课程进行笔记的总结. 一.通过GPIO控制点亮LED( ...

  5. 嵌入式知识-ARM裸机-学习笔记(9):SD卡启动详解(S5PV210)

    嵌入式知识-ARM裸机-学习笔记(9):SD卡启动详解(S5PV210) 一.SD卡介绍 1. SD卡背景知识和特点 SD卡.MMC卡.MicroSD.TF卡:这些卡其实内部就是Flash存储颗粒,比 ...

  6. ARM SMMU的原理与IOMMU[转载]

    ARM SMMU的原理与IOMMU 首先放一个社区iommu patch的网址:https://lore.kernel.org/linux-iommu/ arm smmu的原理 smmu 基本知识 如 ...

  7. I.MX6ULL ARM Linux学习笔记

    I.MX6ULL ARM Linux学习笔记 写在前面 S1:U-Boot S2:正点原子U-Boot编译 S3:U-Boot基本命令第1讲:help.bdinfo和环境变量命令 S4:U-Boot基 ...

  8. 最右android工资,Android逆向之旅—最右App的签名算法解析(ARM指令学习不舍篇)

    一.逆向分析 本文继续来看最后一篇介绍ARM指令学习,之前的两篇文章已经详细介绍了ARM指令的基础知识,本文继续把剩下来的所有都介绍完了,首先一定要去看前面的基础篇和进阶篇,不然我都很难保证你看这篇文 ...

  9. 嵌入式知识-ARM裸机-学习笔记(8):SoC定时器与看门狗(WDT)

    嵌入式知识-ARM裸机-学习笔记(8):SoC定时器与看门狗(WDT) 一.定时器 1. 定时器简介 定时器是SoC中最常见的外设 (1)定时器与计数器.计数器是用来计数的(每隔一个固定时间会计一个数 ...

最新文章

  1. IIS7.5 HTTP 错误 500.19 - Internal Server Error 问题的解决方案
  2. 骑士周游算法 c语言_C语言经典算法04--骑士走棋盘(骑士旅游:Knight tour)
  3. navicat怎么安装mysql数据库_【20170825】从零开始学SQL数据库 安装mysql与navicat,开始练习...
  4. fanuc roboguide_ROBOGUIDE软件:机器人产线输送带输送物料虚拟仿真操作
  5. 【数字逻辑设计】组合电路
  6. log4j + flume 1.6 集成
  7. 压力校准仪器开发之日--2017-10-31
  8. 大数据可视化的意义在哪
  9. html中怎么给网页添加音乐播放器,怎么给网站或网页添加音乐
  10. matlab如何生存对称矩阵,如何使用Matlab产生对称矩阵
  11. 20世纪50年代到60年代 电子计算机,计算机基础知识习题标准答案
  12. ads pspice 导入_ADS中使用pspice模型
  13. Profinet Commander下载方法
  14. mysql查询姓名第二个字_第二篇:MySQL中SELECT查询语句总结
  15. 【埋点】是什么埋点?简述埋点的操作流程
  16. IOS使用高德地图获取当前位置信息
  17. Containers feature is disabled. Enable it using the PowerShell script (in an administrative PowerShe
  18. LLM__llama-7B模型试验
  19. Unity 颜色查找表富文本颜色
  20. cenntos系统配置dns地址_localhost

热门文章

  1. iMindMap如何新建导图
  2. 搞懂分布式技术14:Spring Boot使用注解集成Redis缓存
  3. Laravel框架中如何使用Service模式?
  4. Windows Media Player控件属性及方法(c#)以duration为什么总是“0”解决方法
  5. 盐城北大青鸟“致最爱的你”感恩母亲节特别活动
  6. 基于Java毕业设计养老机构服务信息管理源码+系统+mysql+lw文档+部署软件
  7. PIR 传感器入侵检测算法和分析研究(Matlab代码实现)
  8. 【STM32学习】(29)STM32实现595驱动三个数码管(标准库和HAL库实现)
  9. [Swift]Alamofire使用嵌套参数进行网络请求遇到的问题
  10. 面试中常被问到的(16)进程间通信方式