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 没有啥意思.
而这个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 没有啥意思.