首页 > 学院 > 开发设计 > 正文

smmu 学习笔记之attach_dev 2

2019-11-07 23:34:53
字体:
来源:转载
供稿:网友
static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev){    int ret = 0;    struct arm_smmu_device *smmu;//从iommu_domain 中得到arm_smmu_domain    struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);    struct arm_smmu_master_data *master;    struct arm_smmu_strtab_ent *ste;    if (!dev->iommu_fwspec)        return -ENOENT;    master = dev->iommu_fwspec->iommu_PRiv;    smmu = master->smmu;    ste = &master->ste;    /* Already attached to a different domain? *///ste->bypass 不为null的话,说明arm_smmu_attach_dev 已经被调用过了,这个值是在arm_smmu_attach_dev 后面赋值的    if (!ste->bypass)        arm_smmu_detach_dev(dev);    mutex_lock(&smmu_domain->init_mutex);//一般情况下smmu_domain->smmu 也就是arm_smmu_device 是等于null的    if (!smmu_domain->smmu) {        smmu_domain->smmu = smmu;        ret = arm_smmu_domain_finalise(domain);        if (ret) {            smmu_domain->smmu = NULL;            goto out_unlock;        }    } else if (smmu_domain->smmu != smmu) {        dev_err(dev,            "cannot attach to SMMU %s (upstream of %s)/n",            dev_name(smmu_domain->smmu->dev),            dev_name(smmu->dev));        ret = -ENXIO;        goto out_unlock;    }//给ste->bypass 赋值,前面已经判断了    ste->bypass = false;    ste->valid = true;//smmu的stage分为两部分,s1和s2,s1是从va->ipa,而s2是从ipa->pa.va 表示虚拟地址,ipa表示中间物理地址,pa表示物理地址.//如果stage 是s1的话,就说明是虚拟化应用,需要配置s1,    if (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) {        ste->s1_cfg = &smmu_domain->s1_cfg;        ste->s2_cfg = NULL;        arm_smmu_write_ctx_desc(smmu, ste->s1_cfg);    } else {        ste->s1_cfg = NULL;        ste->s2_cfg = &smmu_domain->s2_cfg;    }//从这段code看每个domain中只能包含s1或者s2,不能同时包含.也就是说如果要从va->ipa->pa的话,就必须要两个domain.//初始化ste table    ret = arm_smmu_install_ste_for_dev(dev->iommu_fwspec);    if (ret < 0)        ste->valid = false;out_unlock:    mutex_unlock(&smmu_domain->init_mutex);    return ret;}继续看看arm_smmu_domain_finalisestatic int arm_smmu_domain_finalise(struct iommu_domain *domain){    int ret;    unsigned long ias, oas;    enum io_pgtable_fmt fmt;    struct io_pgtable_cfg pgtbl_cfg;    struct io_pgtable_ops *pgtbl_ops;    int (*finalise_stage_fn)(struct arm_smmu_domain *,                 struct io_pgtable_cfg *);    struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);    struct arm_smmu_device *smmu = smmu_domain->smmu;    /* Restrict the stage to what we can actually support *///根据当前硬件feature来决定是smmu_domain->stage,这里也可看出domain只能有一个stage.要么是s1要么是s2    if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S1))        smmu_domain->stage = ARM_SMMU_DOMAIN_S2;    if (!(smmu->features & ARM_SMMU_FEAT_TRANS_S2))        smmu_domain->stage = ARM_SMMU_DOMAIN_S1;    switch (smmu_domain->stage) {    case ARM_SMMU_DOMAIN_S1:        ias = VA_BITS;        oas = smmu->ias;        fmt = ARM_64_LPAE_S1;        finalise_stage_fn = arm_smmu_domain_finalise_s1;        break;    case ARM_SMMU_DOMAIN_NESTED:    case ARM_SMMU_DOMAIN_S2:        ias = smmu->ias;        oas = smmu->oas;        fmt = ARM_64_LPAE_S2;        finalise_stage_fn = arm_smmu_domain_finalise_s2;        break;    default:        return -EINVAL;    }    pgtbl_cfg = (struct io_pgtable_cfg) {        .pgsize_bitmap    = smmu->pgsize_bitmap,        .ias        = ias,        .oas        = oas,        .tlb        = &arm_smmu_gather_ops,        .iommu_dev    = smmu->dev,    };//这个地方很重要,注意一下    pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);    if (!pgtbl_ops)        return -ENOMEM;    domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;    domain->geometry.aperture_end = (1UL << ias) - 1;    domain->geometry.force_aperture = true;    smmu_domain->pgtbl_ops = pgtbl_ops;//简单的赋值    ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg);    if (ret < 0)        free_io_pgtable_ops(pgtbl_ops);    return ret;}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;//根据fmt来决定fns。fmt 有四种//    [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,    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;}我们以ARM_64_LPAE_S2为例,也就是调用io_pgtable_arm_64_lpae_s2_init_fns的alloc函数struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns = {    .alloc    = arm_64_lpae_alloc_pgtable_s2,    .free    = arm_lpae_free_pgtable,};在arm_64_lpae_alloc_pgtable_s2 中最终要就是调用arm_lpae_alloc_pgtable。而arm_lpae_alloc_pgtable中最重要的就是    data->iop.ops = (struct io_pgtable_ops) {        .map        = arm_lpae_map,        .unmap        = arm_lpae_unmap,        .iova_to_phys    = arm_lpae_iova_to_phys,    };这段赋值,后面arm_smmu_ops 中调用的map/unmap/iova_to_phys 最终都是调用到这里来
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表