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

smmu学习笔记之streamtable

2019-11-06 09:45:50
字体:
来源:转载
供稿:网友
streamtable有两种组织方式,根据ARM_SMMU_FEAT_2_LVL_STRTAB来判断而这个flag是在arm_smmu_device_hw_PRobe 中通过读取ARM_SMMU_IDR0 这个寄存器来判断的static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu){    u32 reg;    bool coherent = smmu->features & ARM_SMMU_FEAT_COHERENCY;    /* IDR0 */    reg = readl_relaxed(smmu->base + ARM_SMMU_IDR0);    /* 2-level structures */    if ((reg & IDR0_ST_LVL_MASK << IDR0_ST_LVL_SHIFT) == IDR0_ST_LVL_2LVL)        smmu->features |= ARM_SMMU_FEAT_2_LVL_STRTAB;}所以在arm_smmu_init_strtab 中就根据这个flag 将streamtable分成两种组织方式static int arm_smmu_init_strtab(struct arm_smmu_device *smmu){    u64 reg;    int ret;    if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB)        ret = arm_smmu_init_strtab_2lvl(smmu);    else        ret = arm_smmu_init_strtab_linear(smmu);    if (ret)        return ret;    /* Set the strtab base address */    reg  = smmu->strtab_cfg.strtab_dma &           STRTAB_BASE_ADDR_MASK << STRTAB_BASE_ADDR_SHIFT;    reg |= STRTAB_BASE_RA;    smmu->strtab_cfg.strtab_base = reg;    /* Allocate the first VMID for stage-2 bypass STEs */    set_bit(0, smmu->vmid_map);    return 0;}不管采用哪种streamtable,都会对smmu->strtab_cfg.strtab_dma赋值,最后将smmu->strtab_cfg.strtab_dma 保存到smmu->strtab_cfg.strtab_base 中。我们先来看arm_smmu_init_strtab_2lvlstatic int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu){    void *strtab;    u64 reg;    u32 size, l1size;//让cfg 指针指向smmu->strtab_cfg    struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;    /*     * If we can resolve everything with a single L2 table, then we     * just need a single L1 descriptor. Otherwise, calculate the L1     * size, capped to the SIDSIZE.     */    if (smmu->sid_bits < STRTAB_SPLIT) {        size = 0;    } else {        size = STRTAB_L1_SZ_SHIFT - (ilog2(STRTAB_L1_DESC_DWordS) + 3);        size = min(size, smmu->sid_bits - STRTAB_SPLIT);    }#计算l1 entry 的size    cfg->num_l1_ents = 1 << size;    size += STRTAB_SPLIT;    if (size < smmu->sid_bits)        dev_warn(smmu->dev,             "2-level strtab only covers %u/%u bits of SID/n",             size, smmu->sid_bits);    l1size = cfg->num_l1_ents * (STRTAB_L1_DESC_DWORDS << 3);#为strtab 申请memory    strtab = dmam_alloc_coherent(smmu->dev, l1size, &cfg->strtab_dma,                     GFP_KERNEL | __GFP_ZERO);    if (!strtab) {        dev_err(smmu->dev,            "failed to allocate l1 stream table (%u bytes)/n",            size);        return -ENOMEM;    }    cfg->strtab = strtab;    /* Configure strtab_base_cfg for 2 levels */    reg  = STRTAB_BASE_CFG_FMT_2LVL;    reg |= (size & STRTAB_BASE_CFG_LOG2SIZE_MASK)        << STRTAB_BASE_CFG_LOG2SIZE_SHIFT;    reg |= (STRTAB_SPLIT & STRTAB_BASE_CFG_SPLIT_MASK)        << STRTAB_BASE_CFG_SPLIT_SHIFT;#这里就给cfg->strtab_base_cfg 赋值    cfg->strtab_base_cfg = reg;    return arm_smmu_init_l1_strtab(smmu);}总结一下arm_smmu_init_strtab_2lvl 主要是对arm_smmu_strtab_cfg来进行初始化static int arm_smmu_init_l1_strtab(struct arm_smmu_device *smmu){    unsigned int i;    struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;    size_t size = sizeof(*cfg->l1_desc) * cfg->num_l1_ents;    void *strtab = smmu->strtab_cfg.strtab;    cfg->l1_desc = devm_kzalloc(smmu->dev, size, GFP_KERNEL);    if (!cfg->l1_desc) {        dev_err(smmu->dev, "failed to allocate l1 stream table desc/n");        return -ENOMEM;    }    for (i = 0; i < cfg->num_l1_ents; ++i) {    #这里将cfg->l1_desc[i] 里面SPAN和L2PTR的值写到strtab 里面。        arm_smmu_write_strtab_l1_desc(strtab, &cfg->l1_desc[i]);        strtab += STRTAB_L1_DESC_DWORDS << 3;    }    return 0;}总结一下arm_smmu_init_l1_strtab 主要是对strtab_cfg.strtab 赋值,我比较疑惑的是这里的cfg->l1_desc 也是刚申请的,理论上cfg->l1_desc 里面是空的才对,不知道为什么要把这里的值写到strtab中.这个疑惑在arm_smmu_add_device 中可以得到解答    /* Check the SIDs are in range of the SMMU and our stream table */    for (i = 0; i < fwspec->num_ids; i++) {        u32 sid = fwspec->ids[i];        if (!arm_smmu_sid_in_range(smmu, sid))            return -ERANGE;        /* Ensure l2 strtab is initialised */        if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) {            ret = arm_smmu_init_l2_strtab(smmu, sid);            if (ret)                return ret;        }    }从上面在这段code中可以看到如果置了ARM_SMMU_FEAT_2_LVL_STRTAB 这个flag,会调用arm_smmu_init_l2_strtabstatic int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid){    size_t size;    void *strtab;    struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg;    struct arm_smmu_strtab_l1_desc *desc = &cfg->l1_desc[sid >> STRTAB_SPLIT];    if (desc->l2ptr)        return 0;    size = 1 << (STRTAB_SPLIT + ilog2(STRTAB_STE_DWORDS) + 3);    strtab = &cfg->strtab[(sid >> STRTAB_SPLIT) * STRTAB_L1_DESC_DWORDS];    desc->span = STRTAB_SPLIT + 1;    desc->l2ptr = dmam_alloc_coherent(smmu->dev, size, &desc->l2ptr_dma,                      GFP_KERNEL | __GFP_ZERO);    if (!desc->l2ptr) {        dev_err(smmu->dev,            "failed to allocate l2 stream table for SID %u/n",            sid);        return -ENOMEM;    }    arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT);    arm_smmu_write_strtab_l1_desc(strtab, desc);    return 0;}在arm_smmu_init_l2_strtab 中可以看到会对desc->l2ptr和desc->span 赋值,然后在调用arm_smmu_write_strtab_l1_desc 来写streamtable。这感觉在arm_smmu_init_l1_strtab中调用arm_smmu_write_strtab_l1_desc 没有啥意思.
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表