设备StreamID:sid

struct iommu_fwspec {const struct iommu_ops  *ops;struct fwnode_handle   *iommu_fwnode;void          *iommu_priv;unsigned int        num_ids;u32         ids[1];  //ids保存的某device使用的streamID
};

通过iommu_fwspec_add_id函数设置:

struct iommu_fwspec {const struct iommu_ops  *ops;struct fwnode_handle   *iommu_fwnode;void          *iommu_priv;unsigned int        num_ids;  //和该设备关联的总共ids/streamIDu32            ids[1];//sid内容数组
};int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
{struct iommu_fwspec *fwspec = dev->iommu_fwspec;size_t size;int i;if (!fwspec)return -EINVAL;size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]);if (size > sizeof(*fwspec)) {fwspec = krealloc(dev->iommu_fwspec, size, GFP_KERNEL);if (!fwspec)return -ENOMEM;dev->iommu_fwspec = fwspec;}for (i = 0; i < num_ids; i++)fwspec->ids[fwspec->num_ids + i] = ids[i];fwspec->num_ids += num_ids;return 0;
}

设置流程dump_stack信息:

[   79.499624] ===iommu_fwspec_add_ids for dev 0000:0a:00.0 num_ids 1
[   79.505862] CPU: 29 PID: 1 Comm: swapper/0 Not tainted 4.19.0l+ #20
[   79.512098] Hardware name: PHYTIUM LTD Phytium S2500/64/Phytium S2500/64, BIOS V2.2 Feb  9 2021
[   79.520754] Call trace:
[   79.523192]  dump_backtrace+0x0/0x1c0
[   79.526838]  show_stack+0x24/0x30
[   79.530138]  dump_stack+0x9c/0xbc
[   79.533439]  iommu_fwspec_add_ids+0x44/0xd0
[   79.537602]  iort_iommu_xlate+0x144/0x170
[   79.541592]  iort_pci_iommu_init+0x58/0x80
[   79.545670]  pci_for_each_dma_alias+0x44/0x188
[   79.550093]  iort_iommu_configure+0xac/0x1c8
[   79.554343]  acpi_dma_configure+0x68/0xbc
[   79.558333]  pci_dma_configure+0xa8/0xc8
[   79.562236]  dma_configure+0x4c/0x64
[   79.565795]  really_probe+0x98/0x3b8
[   79.569354]  driver_probe_device+0x6c/0x138
[   79.573516]  __driver_attach+0x118/0x150
[   79.577419]  bus_for_each_dev+0x84/0xd8
[   79.581237]  driver_attach+0x30/0x40
[   79.584795]  bus_add_driver+0x20c/0x250
[   79.588613]  driver_register+0x64/0x110
[   79.592430]  __pci_register_driver+0x58/0x68
[   79.596681]  xhci_pci_init+0x58/0x64
[   79.600240]  do_one_initcall+0x68/0x1e4
[   79.604059]  kernel_init_freeable+0x2d0/0x388
[   79.608396]  kernel_init+0x18/0x108
[   79.611867]  ret_from_fork+0x10/0x1c

iort_iommu_configure函数:

//drivers/acpi/arm64/iort.c
const struct iommu_ops *iort_iommu_configure(struct device *dev)
{struct acpi_iort_node *node, *parent;const struct iommu_ops *ops;u32 streamid = 0;int err = -ENODEV;/** If we already translated the fwspec there* is nothing left to do, return the iommu_ops.*/ops = iort_fwspec_iommu_ops(dev->iommu_fwspec);if (ops)return ops;if (dev_is_pci(dev)) { //PCI设备的sid 使用的是BDF号。struct pci_bus *bus = to_pci_dev(dev)->bus;struct iort_pci_alias_info info = { .dev = dev };node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,iort_match_node_callback, &bus->dev);if (!node)return NULL;info.node = node;
//计算siderr = pci_for_each_dma_alias(to_pci_dev(dev),iort_pci_iommu_init, &info); } else {int i = 0;node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,iort_match_node_callback, dev);if (!node)return NULL;do {parent = iort_node_map_platform_id(node, &streamid,IORT_IOMMU_TYPE,i++);if (parent)err = iort_iommu_xlate(dev, parent, streamid);} while (parent && !err);}/** If we have reason to believe the IOMMU driver missed the initial* add_device callback for dev, replay it to get things in order.*/if (!err) {ops = iort_fwspec_iommu_ops(dev->iommu_fwspec);err = iort_add_device_replay(ops, dev);}/* Ignore all other errors apart from EPROBE_DEFER */if (err == -EPROBE_DEFER) {ops = ERR_PTR(err);} else if (err) {dev_dbg(dev, "Adding to IOMMU failed: %d\n", err);ops = NULL;}return ops;
}*/
int pci_for_each_dma_alias(struct pci_dev *pdev,int (*fn)(struct pci_dev *pdev,u16 alias, void *data), void *data)
{.....struct pci_bus *bus;int ret;ret = fn(pdev, PCI_DEVID(pdev->bus->number, pdev->devfn), data);.....
}static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
{struct iort_pci_alias_info *info = data;struct acpi_iort_node *parent;u32 streamid;parent = iort_node_map_id(info->node, alias, &streamid,IORT_IOMMU_TYPE);return iort_iommu_xlate(info->dev, parent, streamid);
}最终:
static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,struct fwnode_handle *fwnode,const struct iommu_ops *ops)
{int ret = iommu_fwspec_init(dev, fwnode, ops);if (!ret)ret = iommu_fwspec_add_ids(dev, &streamid, 1);  //添加streamidreturn ret;
}

arm_smmu_add_device

在drivers/iommu/arm-smmu.c的 arm_smmu_add_device函数中:

#ifdef CONFIG_ARCH_PHYTIUM
#define FWID_READ(id) (((u16)(id) >> 3) | (((id) >> SMR_MASK_SHIFT | 0x7000) << SMR_MASK_SHIFT))
#endif
//默认streamID会使用pci的bdf,但ft2000+ 硬件实现上不同,该值会通过FWID_READ函数进行调整。iommu_fwspec_add_ids加入到fwspec中。因此一个设备会有两个sid。static int arm_smmu_add_device(struct device *dev)
{struct arm_smmu_device *smmu;struct arm_smmu_master_cfg *cfg;struct iommu_fwspec *fwspec = dev->iommu_fwspec;int i, ret;#ifdef CONFIG_ARCH_PHYTIUM/* FT2000PLUS workaround patch */if ((read_cpuid_id() & MIDR_CPU_MODEL_MASK) == MIDR_PHYTIUM_FT2000PLUS) {printk("===arm_smmu_add_device ft2000+ \n");int num = fwspec->num_ids;for (i = 0; i < num; i++) {u32 fwid = FWID_READ(fwspec->ids[i]);printk("===arm_smmu_add_device ft2000+ ids %llx to fwid %llx \n",fwspec->ids[i],fwid);iommu_fwspec_add_ids(dev, &fwid, 1);}}
#endif..............
打印一下设备的sidfor (i = 0; i < fwspec->num_ids; i++) {u16 sid = fwspec->ids[i];u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT;printk("===arm_smmu_add_device for dev %s num_ids %d of %d sid %llx  \n",dev_name(dev),fwspec->num_ids,i,sid);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_free;}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_free;}}
...................ret = arm_smmu_master_alloc_smes(dev); //分配stream mapping entry 表项}

ft2000+日志:0000:04:00.x  都有一个-x80的sid。

[   11.775255] ===arm_smmu_add_device for dev 0000:41:00.0 num_ids 2 of 0 sid 4100  
[   11.782698] ===arm_smmu_add_device for dev 0000:41:00.0 num_ids 2 of 1 sid 820  
[   12.576919] ===arm_smmu_add_device for dev 0000:03:00.0 num_ids 2 of 0 sid 300  
[   12.576922] ===arm_smmu_add_device for dev 0000:03:00.0 num_ids 2 of 1 sid 60  
[   15.638439] ===arm_smmu_add_device for dev 0000:04:00.0 num_ids 2 of 0 sid 400  
[   15.645802] ===arm_smmu_add_device for dev 0000:04:00.0 num_ids 2 of 1 sid 80  
[   16.494888] ===arm_smmu_add_device for dev 0000:51:00.0 num_ids 2 of 0 sid 5100  
[   16.502947] ===arm_smmu_add_device for dev 0000:51:00.0 num_ids 2 of 1 sid a20  
[   17.105745] ===arm_smmu_add_device for dev 0000:04:00.1 num_ids 2 of 0 sid 401  
[   17.119853] ===arm_smmu_add_device for dev 0000:04:00.1 num_ids 2 of 1 sid 80  
[   18.338789] ===arm_smmu_add_device for dev 0000:04:00.2 num_ids 2 of 0 sid 402  
[   18.346151] ===arm_smmu_add_device for dev 0000:04:00.2 num_ids 2 of 1 sid 80  
[   19.099235] ===arm_smmu_add_device for dev 0000:04:00.3 num_ids 2 of 0 sid 403  
[   19.099237] ===arm_smmu_add_device for dev 0000:04:00.3 num_ids 2 of 1 sid 80 

上述中发现一个dev可能有多个sid,如iommu_fwspec_add_ids函数关联了多个sid的话,最终往iommu_fwspec 中填充sid数组值。

在arm_smmu_add_device函数中建立一个设备下的sid和其它数据的关系:

static int arm_smmu_add_device(struct device *dev)
{#ifdef CONFIG_ARCH_PHYTIUM
#define FWID_READ(id) (((u16)(id) >> 3) | (((id) >> SMR_MASK_SHIFT | 0x7000) << SMR_MASK_SHIFT))/* FT2000PLUS workaround patch */if ((read_cpuid_id() & MIDR_CPU_MODEL_MASK) == MIDR_PHYTIUM_FT2000PLUS) {int num = fwspec->num_ids;for (i = 0; i < num; i++) {u32 fwid = FWID_READ(fwspec->ids[i]);iommu_fwspec_add_ids(dev, &fwid, 1); //这里对2000+进行特殊处理,再一次添加了处理后的sid值,应该是2000+处理器的smmu识别sid的方式不一样,}}
#endifret = -EINVAL;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_free;}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_free;}}ret = -ENOMEM;
//cfg的smendx数组大小刚好和fwspec的num_ids一致,后面要保存数据。cfg = kzalloc(offsetof(struct arm_smmu_master_cfg, smendx[i]),GFP_KERNEL);if (!cfg)goto out_free;cfg->smmu = smmu;fwspec->iommu_priv = cfg;while (i--)cfg->smendx[i] = INVALID_SMENDX;ret = arm_smmu_master_alloc_smes(dev);}

arm_smmu_attach_dev函数:

arm_smmu_init_domain_context初始化domain,是arm_smmu_add_device一路调用到arm_smmu_attach_dev:

static int arm_smmu_init_domain_context(struct iommu_domain *domain,struct arm_smmu_device *smmu)
{............struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);struct arm_smmu_cfg *cfg = &smmu_domain->cfg;ret = __arm_smmu_alloc_bitmap(smmu->context_map, start,smmu->num_context_banks);cfg->cbndx = ret;  //将上述获取的全局唯一context bank 编号 保存在cfg->cbndxarm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg); //设置好cb等参数。其中包括lape获取的页表等内容。arm_smmu_write_context_bank(smmu, cfg->cbndx); //编号作为参数............}static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx)
{
............
cb_base = ARM_SMMU_CB(smmu, idx); //根据编号获取cb_base地址下面是一些写寄存器的操作/* TTBRs */if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {writel_relaxed(cfg->asid, cb_base + ARM_SMMU_CB_CONTEXTIDR);writel_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0);writel_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1);} else {writeq_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0); //将ttbr[0]写入,其中保存的是页表的pgd基地址。if (stage1)writeq_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1);}............}

[   78.770289] ===arm_smmu_init_domain_context for dev arm-smmu.0.auto domain ffff8139415a1b50 smmu ffff834941ef0018  smmu_doamin->stage 0 fmt 2 cfg->cbndx 0
[   78.784048] CPU: 28 PID: 1 Comm: swapper/0 Not tainted 4.19.0l+ #40
[   78.790284] Hardware name: PHYTIUM LTD Phytium S2500/64/Phytium S2500/64, BIOS V2.2 Feb  9 2021
[   78.798938] Call trace:
[   78.801373]  dump_backtrace+0x0/0x1c0
[   78.805017]  show_stack+0x24/0x30
[   78.808316]  dump_stack+0x9c/0xbc
[   78.811615]  arm_smmu_attach_dev+0x44c/0x788
[   78.815864]  __iommu_attach_device+0x54/0xf8 
[   78.820113]  iommu_group_add_device+0x17c/0x478
[   78.824621]  iommu_group_get_for_dev+0x74/0x178
[   78.829129]  arm_smmu_add_device+0x218/0x6b8
[   78.833379]  iort_iommu_configure+0x144/0x1c8
[   78.837715]  acpi_dma_configure+0x68/0xbc
[   78.841705]  pci_dma_configure+0xa8/0xc8
[   78.845608]  dma_configure+0x4c/0x64
[   78.849166]  really_probe+0x98/0x3b8
[   78.852725]  driver_probe_device+0x6c/0x138
[   78.856887]  __driver_attach+0x118/0x150
[   78.860790]  bus_for_each_dev+0x84/0xd8
[   78.864608]  driver_attach+0x30/0x40
[   78.868166]  bus_add_driver+0x20c/0x250
[   78.871984]  driver_register+0x64/0x110
[   78.875802]  __pci_register_driver+0x58/0x68
[   78.880051]  ahci_pci_driver_init+0x28/0x30
[   78.884213]  do_one_initcall+0x68/0x1e4
[   78.888031]  kernel_init_freeable+0x2d0/0x388
[   78.892367]  kernel_init+0x18/0x108
[   78.895839]  ret_from_fork+0x10/0x1c

设备创建group和doamin流程

device:加组流程
really_probedma_configurepci_dma_configureacpi_dma_configureiort_iommu_configurearm_smmu_add_devicearm_smmu_master_alloc_smesiommu_group_get_for_devarm_smmu_device_group //创建iommu_groupgroup->default_domain = dom; //创建domaingroup->domain = dom;iommu_group_add_device //将deive加入到group链表,将iommu_grouop 赋值dev->iommu_group指针__iommu_attach_devicearm_smmu_attach_devfor_each_cfg_sme:smmu->s2crs[idx].group = group; //循环将该device下的第idxg个s2crs数组分配grouparm_smmu_device_grouppci_device_groupalloc_groupgeneric_device_group

关联smmu下的SMMU_SMRn寄存器。其中写入streamID。

相关寄存器参考:(66条消息) Smmu硬件寄存器—V2_一墨一飞花的博客-CSDN博客_smmu context bank

SMMU_SMRn
streamID匹配寄存器。

EXMASK, bits[31:16] 屏蔽无关位,如果EXMASK[i]==1,则EXID[i]被忽略;

相反,EXMASK[i]==0,则EXID[i]有效,用于匹配

EXID, bits[15:0] 用于匹配的streamID

static int arm_smmu_master_alloc_smes(struct device *dev)
{struct iommu_fwspec *fwspec = dev->iommu_fwspec;struct arm_smmu_master_cfg *cfg = fwspec->iommu_priv;struct arm_smmu_device *smmu = cfg->smmu;struct arm_smmu_smr *smrs = smmu->smrs;struct iommu_group *group;int i, idx, ret;mutex_lock(&smmu->stream_map_mutex);/* Figure out a viable stream map entry allocation */for_each_cfg_sme(fwspec, i, idx) {u16 sid = fwspec->ids[i];u16 mask = fwspec->ids[i] >> SMR_MASK_SHIFT;if (idx != INVALID_SMENDX) {ret = -EEXIST;goto out_err;}ret = arm_smmu_find_sme(smmu, sid, mask); //分配一个smmu下唯一的idx给当前设备的arm_smmu_master_cfg ,如果sid一致,则返回同样的idx。这个在ft2000+上就是这种情况,bus_dev_fn忽略fn则会导致很多设备相同。if (ret < 0)goto out_err;idx = ret;if (smrs && smmu->s2crs[idx].count == 0) {smrs[idx].id = sid; //将streamid保存在全局smrs数组下,后续写入寄存器中,一个设备有多个sid的情况下也会分配多个全局唯一的idx。smrs[idx].mask = mask;smrs[idx].valid = true;}smmu->s2crs[idx].count++;cfg->smendx[i] = (s16)idx;  //设置当前设备arm_smmu_master_cfg 数组smendx为smmu全局分配的idx值。}group = iommu_group_get_for_dev(dev);if (!group)group = ERR_PTR(-ENOMEM);if (IS_ERR(group)) {ret = PTR_ERR(group);goto out_err;}iommu_group_put(group);/* It worked! Now, poke the actual hardware */for_each_cfg_sme(fwspec, i, idx) {arm_smmu_write_sme(smmu, idx);  //将smrs下的sid写入寄存器中。根据idx来定位寄存器地址。smmu->s2crs[idx].group = group;}mutex_unlock(&smmu->stream_map_mutex);return 0;out_err:while (i--) {arm_smmu_free_sme(smmu, cfg->smendx[i]);cfg->smendx[i] = INVALID_SMENDX;}mutex_unlock(&smmu->stream_map_mutex);return ret;
}

关联smmu下的s2cr寄存器。

static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,struct iommu_fwspec *fwspec)
{struct arm_smmu_device *smmu = smmu_domain->smmu;struct arm_smmu_s2cr *s2cr = smmu->s2crs;u8 cbndx = smmu_domain->cfg.cbndx;   //init_context_bank 中分配的,从bitmap中查找的唯一编号,参考arm_smmu_init_domain_context中分配cfg->cbndx的过程。enum arm_smmu_s2cr_type type;int i, idx;if (smmu_domain->stage == ARM_SMMU_DOMAIN_BYPASS)type = S2CR_TYPE_BYPASS;elsetype = S2CR_TYPE_TRANS;for_each_cfg_sme(fwspec, i, idx) {if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)continue;s2cr[idx].type = type;s2cr[idx].privcfg = S2CR_PRIVCFG_DEFAULT;s2cr[idx].cbndx = cbndx;arm_smmu_write_s2cr(smmu, idx); //根据设备在全局的idx号,定位到s2cr寄存器地址。}return 0;
}

相关写寄存器的函数:通过idx关联。

//写入streamID。可以推断idx值
static void arm_smmu_write_smr(struct arm_smmu_device *smmu, int idx)
{struct arm_smmu_smr *smr = smmu->smrs + idx;u32 reg = smr->id << SMR_ID_SHIFT | smr->mask << SMR_MASK_SHIFT;if (!(smmu->features & ARM_SMMU_FEAT_EXIDS) && smr->valid)reg |= SMR_VALID;writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_SMR(idx));
}//根据idx 找到s2cr寄存器,可以找到cbndx值,该值可以定位到io-pgtable页表的pgd地址。
static void arm_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx)
{struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx;u32 reg = (s2cr->type & S2CR_TYPE_MASK) << S2CR_TYPE_SHIFT |  //16-17bit为TYPE位(s2cr->cbndx & S2CR_CBNDX_MASK) << S2CR_CBNDX_SHIFT |   //0-7bit为CBNDX位。(s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT;if (smmu->features & ARM_SMMU_FEAT_EXIDS && smmu->smrs &&smmu->smrs[idx].valid)reg |= S2CR_EXIDVALID;writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_S2CR(idx));
}static void arm_smmu_write_sme(struct arm_smmu_device *smmu, int idx)
{arm_smmu_write_s2cr(smmu, idx);if (smmu->smrs)arm_smmu_write_smr(smmu, idx);
}

arm_smmu_device_group:

static struct iommu_group *arm_smmu_device_group(struct device *dev)
{struct iommu_fwspec *fwspec = dev->iommu_fwspec;struct arm_smmu_device *smmu = fwspec_smmu(fwspec);struct iommu_group *group = NULL;int i, idx;for_each_cfg_sme(fwspec, i, idx) {if (group && smmu->s2crs[idx].group &&group != smmu->s2crs[idx].group)return ERR_PTR(-EINVAL);printk("===arm_smmu_deivce_group for %s idx %d group %llx \n",dev_name(dev),idx,group);group = smmu->s2crs[idx].group; //对于ft2000+,由于idx存在一致,因此会分在一个组。}printk("==arm_smmu_device_group=== is_pci %d group = %llx name %s \n",dev_is_pci(dev),group,dev_name(dev));if (group)return iommu_group_ref_get(group);printk("group is empty \n");if (dev_is_pci(dev))group = pci_device_group(dev);elsegroup = generic_device_group(dev);return group;
}
arm_smmu_attach_dev->
arm_smmu_domain_add_master
{
....struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);arm_smmu_domain_add_master(smmu_domain, cfg, fwspec);
.....
}/*
配置设备下不同idx组寄存器,其中cbndx使用的是domain唯一的值。
不同设备如果使用同一个domain。则根据唯一的cbndx值(arm_smmu_init_domain_context中分配)
因此设备的dma发送sid找到smr的idx组。对应s2cr中查找cbndx。进而找到ttbr ?
*/
static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain, //设备归属的domainstruct arm_smmu_master_cfg *cfg,   //dev下的struct iommu_fwspec *fwspec)   //dev下的
{struct arm_smmu_device *smmu = smmu_domain->smmu;struct arm_smmu_s2cr *s2cr = smmu->s2crs;u8 cbndx = smmu_domain->cfg.cbndx; //一个domain唯一的cbndx。enum arm_smmu_s2cr_type type;int i, idx;if (smmu_domain->stage == ARM_SMMU_DOMAIN_BYPASS)type = S2CR_TYPE_BYPASS;elsetype = S2CR_TYPE_TRANS;for_each_cfg_sme(cfg, fwspec, i, idx) {if (type == s2cr[idx].type && cbndx == s2cr[idx].cbndx)continue;s2cr[idx].type = type;s2cr[idx].privcfg = S2CR_PRIVCFG_DEFAULT;s2cr[idx].cbndx = cbndx;arm_smmu_write_s2cr(smmu, idx); //写入第idx组s2cr寄存器}return 0;
}

StreamID写入smmu寄存器:

arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
    arm_smmu_init_domain_context(domain, smmu);
        arm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg);
        arm_smmu_write_context_bank(smmu, cfg->cbndx);

地址映射使用io_pgtable_ops

//drivers/iommu/io-pgtable.h
struct io_pgtable_ops {int (*map)(struct io_pgtable_ops *ops, unsigned long iova,phys_addr_t paddr, size_t size, int prot);size_t (*unmap)(struct io_pgtable_ops *ops, unsigned long iova,size_t size);phys_addr_t (*iova_to_phys)(struct io_pgtable_ops *ops,unsigned long iova);
};

iommu_ops的map函数arm_smmu_map

static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,phys_addr_t paddr, size_t size, int prot)
{struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;if (!ops)return -ENODEV;return ops->map(ops, iova, paddr, size, prot);
}

arm_smmu_init_domain_context中对domain进行初始化的时候分配io_pgtable_ops:

static int arm_smmu_init_domain_context(struct iommu_domain *domain,struct arm_smmu_device *smmu)
{................pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);................}//drivers/iommu/io-pgtable.cstatic const struct io_pgtable_init_fns *
io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
#ifdef CONFIG_IOMMU_IO_PGTABLE_LPAE[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,
#endif
#ifdef CONFIG_IOMMU_IO_PGTABLE_ARMV7S[ARM_V7S] = &io_pgtable_arm_v7s_init_fns,
#endif
};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;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;
}

io_pgtable_arm_64_lpae_s1_init_fns: driver/iommu/io-pgtable-arm.c

struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {.alloc  = arm_64_lpae_alloc_pgtable_s1,.free   = arm_lpae_free_pgtable,
};static struct io_pgtable *
arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie)
{
..................struct arm_lpae_io_pgtable *data;data = arm_lpae_alloc_pgtable(cfg); //分配data->pgd = __arm_lpae_alloc_pages(data->pgd_size, GFP_KERNEL, cfg);
...................
}static struct arm_lpae_io_pgtable *
arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg)
{unsigned long va_bits, pgd_bits;struct arm_lpae_io_pgtable *data;
。。。。。。。。。。。data = kmalloc(sizeof(*data), GFP_KERNEL);if (!data)return NULL;data->pg_shift = __ffs(cfg->pgsize_bitmap);data->bits_per_level = data->pg_shift - ilog2(sizeof(arm_lpae_iopte));va_bits = cfg->ias - data->pg_shift;data->levels = DIV_ROUND_UP(va_bits, data->bits_per_level);/* Calculate the actual size of our pgd (without concatenation) */pgd_bits = va_bits - (data->bits_per_level * (data->levels - 1));data->pgd_size = 1UL << (pgd_bits + ilog2(sizeof(arm_lpae_iopte)));data->iop.ops = (struct io_pgtable_ops) {.map            = arm_lpae_map, //map函数.unmap          = arm_lpae_unmap,.iova_to_phys   = arm_lpae_iova_to_phys,};return data;
}

案例:

ft2000+/5.10内核

分组树:

[root@localhost ~]# tree /sys/kernel/iommu_groups/
/sys/kernel/iommu_groups/
├── 0
│   ├── devices
│   │   └── 0000:41:00.0 -> ../../../../devices/pci0000:40/0000:40:00.0/0000:41:00.0
│   ├── reserved_regions
│   └── type
├── 1
│   ├── devices
│   │   └── 0000:03:00.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:01.0/0000:03:00.0
│   ├── reserved_regions
│   └── type
├── 10
│   ├── devices
│   │   └── 0000:40:00.0 -> ../../../../devices/pci0000:40/0000:40:00.0
│   ├── reserved_regions
│   └── type
├── 11
│   ├── devices
│   │   └── 0000:50:00.0 -> ../../../../devices/pci0000:50/0000:50:00.0
│   ├── reserved_regions
│   └── type
├── 12
│   ├── devices
│   │   └── 0000:70:00.0 -> ../../../../devices/pci0000:70/0000:70:00.0
│   ├── reserved_regions
│   └── type
├── 13
│   ├── devices
│   │   ├── 0000:04:00.1 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:02.0/0000:04:00.1
│   │   ├── 0000:04:00.2 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:02.0/0000:04:00.2
│   │   └── 0000:04:00.3 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:02.0/0000:04:00.3
│   ├── reserved_regions
│   └── type
├── 14
│   ├── devices
│   │   └── 0000:21:00.0 -> ../../../../devices/pci0000:20/0000:20:00.0/0000:21:00.0
│   ├── reserved_regions
│   └── type
├── 2
│   ├── devices
│   │   └── 0000:00:00.0 -> ../../../../devices/pci0000:00/0000:00:00.0
│   ├── reserved_regions
│   └── type
├── 3
│   ├── devices
│   │   └── 0000:01:00.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0
│   ├── reserved_regions
│   └── type
├── 4
│   ├── devices
│   │   └── 0000:02:01.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:01.0
│   ├── reserved_regions
│   └── type
├── 5
│   ├── devices
│   │   └── 0000:02:02.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:02.0
│   ├── reserved_regions
│   └── type
├── 6
│   ├── devices
│   │   └── 0000:02:04.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:04.0
│   ├── reserved_regions
│   └── type
├── 7
│   ├── devices
│   │   └── 0000:02:05.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:05.0
│   ├── reserved_regions
│   └── type
├── 8
│   ├── devices
│   │   └── 0000:02:06.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:06.0
│   ├── reserved_regions
│   └── type
└── 9├── devices│   ├── 0000:04:00.0 -> ../../../../devices/pci0000:00/0000:00:00.0/0000:01:00.0/0000:02:02.0/0000:04:00.0│   └── 0000:20:00.0 -> ../../../../devices/pci0000:20/0000:20:00.0├── reserved_regions└── type48 directories, 30 files

设备下最终sid:看下上述9组。包含04:00.0和20:00.0。 看下面的日志:恰好这两个共同的0x400的sid;

[   10.503563] ===arm_smmu_add_device dev 0000:41:00.0 num_ids 4 of 0 sid 4100
[   10.510581] ===arm_smmu_add_device dev 0000:41:00.0 num_ids 4 of 1 sid 4101
[   10.517598] ===arm_smmu_add_device dev 0000:41:00.0 num_ids 4 of 2 sid 820
[   10.524522] ===arm_smmu_add_device dev 0000:41:00.0 num_ids 4 of 3 sid 820
[   10.677266] ===arm_smmu_add_device dev 0000:03:00.0 num_ids 2 of 0 sid 300
[   10.684191] ===arm_smmu_add_device dev 0000:03:00.0 num_ids 2 of 1 sid 60
[   11.108133] ===arm_smmu_add_device dev 0000:00:00.0 num_ids 2 of 0 sid 0
[   11.121369] ===arm_smmu_add_device dev 0000:00:00.0 num_ids 2 of 1 sid 0
[   11.155433] ===arm_smmu_add_device dev 0000:01:00.0 num_ids 2 of 0 sid 100
[   11.162367] ===arm_smmu_add_device dev 0000:01:00.0 num_ids 2 of 1 sid 20
[   11.215796] ===arm_smmu_add_device dev 0000:02:01.0 num_ids 2 of 0 sid 208
[   11.215799] ===arm_smmu_add_device dev 0000:02:01.0 num_ids 2 of 1 sid 41
[   11.268019] ===arm_smmu_add_device dev 0000:02:02.0 num_ids 2 of 0 sid 210
[   11.283765] ===arm_smmu_add_device dev 0000:02:02.0 num_ids 2 of 1 sid 42
[   11.387949] ===arm_smmu_add_device dev 0000:02:04.0 num_ids 2 of 0 sid 220
[   11.387950] ===arm_smmu_add_device dev 0000:02:04.0 num_ids 2 of 1 sid 44
[   11.442892] ===arm_smmu_add_device dev 0000:02:05.0 num_ids 2 of 0 sid 228
[   11.449820] ===arm_smmu_add_device dev 0000:02:05.0 num_ids 2 of 1 sid 45
[   11.484502] ===arm_smmu_add_device dev 0000:02:06.0 num_ids 2 of 0 sid 230
[   11.491435] ===arm_smmu_add_device dev 0000:02:06.0 num_ids 2 of 1 sid 46
[   11.526207] ===arm_smmu_add_device dev 0000:20:00.0 num_ids 2 of 0 sid 2000
[   11.533218] ===arm_smmu_add_device dev 0000:20:00.0 num_ids 2 of 1 sid 400   //0x400
[   11.568096] ===arm_smmu_add_device dev 0000:40:00.0 num_ids 2 of 0 sid 4000
[   11.575107] ===arm_smmu_add_device dev 0000:40:00.0 num_ids 2 of 1 sid 800
[   11.610065] ===arm_smmu_add_device dev 0000:50:00.0 num_ids 2 of 0 sid 5000
[   11.617080] ===arm_smmu_add_device dev 0000:50:00.0 num_ids 2 of 1 sid a00
[   11.652088] ===arm_smmu_add_device dev 0000:70:00.0 num_ids 2 of 0 sid 7000
[   11.665674] ===arm_smmu_add_device dev 0000:70:00.0 num_ids 2 of 1 sid e00
[   13.530809] ===arm_smmu_add_device dev 0000:04:00.0 num_ids 2 of 0 sid 400  //x0400
[   13.537743] ===arm_smmu_add_device dev 0000:04:00.0 num_ids 2 of 1 sid 80
[   13.848325] ===arm_smmu_add_device dev 0000:04:00.1 num_ids 2 of 0 sid 401
[   13.855250] ===arm_smmu_add_device dev 0000:04:00.1 num_ids 2 of 1 sid 80
[   14.171899] ===arm_smmu_add_device dev 0000:04:00.2 num_ids 2 of 0 sid 402
[   14.178831] ===arm_smmu_add_device dev 0000:04:00.2 num_ids 2 of 1 sid 80
[   14.489847] ===arm_smmu_add_device dev 0000:04:00.3 num_ids 2 of 0 sid 403
[   14.496779] ===arm_smmu_add_device dev 0000:04:00.3 num_ids 2 of 1 sid 80
[   21.292421] ===arm_smmu_add_device dev 0000:21:00.0 num_ids 2 of 0 sid 2100
[   21.299443] ===arm_smmu_add_device dev 0000:21:00.0 num_ids 2 of 1 sid 420

arm64 smmu 驱动笔记相关推荐

  1. 嵌入式Linux驱动笔记(十八)------浅析V4L2框架之ioctl【转】

    转自:https://blog.csdn.net/Guet_Kite/article/details/78574781 权声明:本文为 风筝 博主原创文章,未经博主允许不得转载!!!!!!谢谢合作 h ...

  2. 嵌入式Linux驱动笔记--转自风筝丶

    为了阅读学习方便,将系列博客的网址进行粘贴,感谢原博客的分享. 嵌入式Linux驱动笔记(一)------第一个LED驱动程序 嵌入式Linux驱动笔记(二)------定时器 嵌入式Linux驱动笔 ...

  3. charging hw bq25601充电驱动笔记

    charging hw bq25601充电驱动笔记 #include "../bq25601.h"/* ====================================== ...

  4. 嵌入式Linux驱动笔记(十六)------设备驱动模型(kobject、kset、ktype)

    ###你好!这里是风筝的博客, ###欢迎和我一起交流. 前几天去面试,被问到Linux设备驱动模型这个问题,没答好,回来后恶补知识,找了些资料,希望下次能答出个满意答案. Linux早期时候,一个驱 ...

  5. 嵌入式Linux驱动笔记(五)------学习platform设备驱动

    你好!这里是风筝的博客, 欢迎和我一起交流. 设备是设备,驱动是驱动. 如果把两个糅合写一起,当设备发生变化时,势必要改写整个文件,这是非常愚蠢的做法.如果把他们分开来,当设备发生变化时,只要改写设备 ...

  6. Linux-USB驱动笔记(五)--主机控制器驱动框架

    Linux-USB驱动笔记(五)--主机控制器驱动框架 1.前言 2.主机控制器框架 3.重要结构体 3.1.usb_hcd -- 主机控制器驱动 3.2.hc_driver -- 控制器操作函数 3 ...

  7. 嵌入式Linux驱动笔记(二十四)------framebuffer之使用spi-tft屏幕(上)

    你好!这里是风筝的博客, 欢迎和我一起交流. 最近入手了一块spi接口的tft彩屏,想着在我的h3板子上使用framebuffer驱动起来. 我们知道: Linux抽象出FrameBuffer这个设备 ...

  8. mtk+android+之mt6577驱动笔记,MTK6577+Android之音频(audio)移植

    MTK6577+Android之音频(audio)移植 备注:audio PA音频功放(power amplifier) 先借用<Y1MT6577 design notice V0.1>关 ...

  9. M62429驱动笔记

    M62429驱动笔记 M62429是一款串行数据控制的双声道电子音量控制器芯片,它的规格书(中文),三菱(英文), RENESAS(英文) 1.功能框图 2.引脚说明 Pin no. Symbol F ...

最新文章

  1. PHP获取当前时间差8小时的问题
  2. CBAC(基于内容的访问控制)
  3. 计算机毕业论文多久,计算机毕业论文
  4. IP地址不是唯一的吗?为什么路由器的IP地址都是这样的呢?
  5. 程序员,你可以更优秀些!加油!
  6. 安装nokogiri前提
  7. 存量客户管理之提额降息
  8. 十种常见排序算法欢聚一堂
  9. AcWing 1952. 金发姑娘和 N 头牛(离散化+差分)
  10. 想要给视频去色有什么方法?原来用这个就可以了
  11. win7 计算机刷新dns,刷新dns,教您怎么刷新DNS
  12. android手机获取qq闪照的方法,QQ闪照怎么保存 闪照保存到手机的方法教程
  13. 浅谈电动自行车智能充电管理系统的设计及应用
  14. win10开始菜单卡住没反应,其他都没问题 看视频也没问题。
  15. 还原文件打开方式为未知应用程序
  16. linux下Nerdtree安装方法
  17. Configure Database Mirroring
  18. LeetCode 到底怎么刷?GitHub 上多位大厂程序员亲测的高效刷题方式
  19. Charles抓包工具测试实战
  20. 超越“双十一”—— ebay百万TPS支付账务系统的设计与实现

热门文章

  1. 日记本编辑器(控制台程序)
  2. elasticjob spring xml 代码分析
  3. 128*160屏幕的手机(如5200等)专用的QQ2007 下载地址
  4. 【igb驱动交叉编译】igb_main.c:10044:7: error: implicit declaration of function ‘isdigit’
  5. 华为查看存储正在计算机,华为手机怎么看内存使用情况
  6. 十进制转换为八进制或十六进制
  7. 增加网站访问量的29个方法
  8. itools驱动组件安装失败怎么办
  9. profibus总线和profibus dp的区别
  10. Mobizen免帐号版