smmu学习笔记之streamtable

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_2lvl
static 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_strtab
static 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 没有啥意思.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值