arm64 smmu 驱动笔记

article/2025/9/18 10:30:19

设备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 


http://chatgpt.dhexx.cn/article/53EIeh8t.shtml

相关文章

ARM_SMMU_下

SMMU驱动代码分析 本文主要分析linux kernel中SMMUv3的代码(drivers/iommu/arm-smmu-v3.c) linux kernel版本是linux 5.7, 体系结构是aarch64 SMMU的作用是把CPU提交给设备的VA地址&#xff0c;直接作为设备发出的地址&#xff0c;变成正确的物理地址&#xff0c;访问到物理内…

arm SMMU

相当于x86下面的IOMMU&#xff0c;是不同架构的不同实现。统一在linux中的IOMMU框架内部&#xff0c;都由结构体 iommu_device 管理。 SMMU的作用是将设备的DMA请求地址翻译成可用的物理地址&#xff0c;当不开启SMMU时也可以直接使用DMA来搬运内存。SMMU是为了安全考虑中间加…

smmu梳理

smmu 用途 用途&#xff1a; &#xff08;参考引用见 参考资料章节&#xff09; 访问非连续的地址 现在系统中很少再预留连续的memory&#xff0c;如果Master需要很多memory&#xff0c;可以通过SMMU把一些非连续的PA映射到连续的VA&#xff0c;例如给DMA&#xff0c;VPU&…

Android 底层知识-SMMU

1.首先了解下MMU MMU是Memory Management Unit的缩写&#xff0c;中文名是内存管理单元。它是一种负责处理中央处理器&#xff08;CPU&#xff09;的内存访问请求的计算机硬件。它的功能包括虚拟地址到物理地址的转换&#xff08;即虚拟内存管理&#xff09;、内存保护、中央处…

理解SMMU基本原理和基本概念

&#xff08;阅读本文前假设您已经熟悉了MMU工作原理&#xff09; 什么是SMMU/IOMMU, 有什么作用&#xff1a; SMMU 有MMU完全一样的能力&#xff0c;当设备以DMA方式工作时&#xff0c;连接在它上面的IOMMU/SMMU可以将设备所使用的地址进行进一步的转换&#xff0c;完全和MM…

SMMU 介绍

SMMU在系统中位置和作用 SMMU功能与MMU功能类似&#xff0c;将IO设备的DMA地址请求(IOVA)转化为系统总线地址(PA)&#xff0c;实现地址映射、属性转换、权限检查等功能&#xff0c;实现不同设备的DMA地址空间隔离。 SMMU软硬件交互过程 硬件结构如下图所示&#xff1a; SMMU处…

MMU和SMMU/IOMMU使用场景和区别,SMMU技术与cache

目录 1.各种MMU 2.各种MMU的使用场景 2.1 各自的使用场景 2.1.1 地址转换 2.1.2 内存保护 3.MMU和SMMU的区别 4. MMU/SMMU/IOMMU与cache 5.扩展阅读 1.各种MMU MMU是memory manage unit 内存管理单元&#xff1b; SMMU是system memory manage unit 系统内存管理单元&a…

junit runwith

junit的runwith是给单元测试中增加一些附属特性&#xff0c;今天在看flink源码的时候&#xff0c;发现其使用了一个注解RunWith(Parameterized.class)&#xff0c;有点好奇&#xff0c;于是研究了一下这个注解的使用。 这个注解产生的背景是这样的&#xff1a;flink中的每个功能…

springmvc--@runwith(springrunner.class)报错

目录 解决方法  1)代码 这是一个文件上传的测试类 不重要,重要的是 RunWith(SpringRunner.class)报错! import com.github.tobato.fastdfs.domain.StorePath; import com.github.tobato.fastdfs.domain.ThumbImageConfig; import com.github.tobato.fastdfs.service.FastF…

run()和start()方法区别

多线程原理&#xff1a;相当于玩游戏机&#xff0c;只有一个游戏机&#xff08;cpu&#xff09;&#xff0c;可是有很多人要玩&#xff0c;于是&#xff0c;start是排队&#xff01;等CPU选中你就是轮到你&#xff0c;你就run&#xff08;&#xff09;&#xff0c;当CPU的运行的…

关于@RunWith注解的一点问题

问题引入 今天在用IDEA写一个springboot测试的时候&#xff0c;碰到了一个问题。 SpringBootTest public class ServiceTest {Autowiredprivate IUserService userService;Testpublic void testSelectById(){User byId userService.getById(1);System.out.println(byId);} }…

Java学习笔记-@RunWith(SpringRunner.class)

我们在进行Test测试时&#xff0c;除了要保证测试的包和启动包是同一个路径下&#xff0c;并且需要在Test测试类上加上RunWith(SpringRunner.class)注解&#xff0c;否则的话会直接报一个NullPointerExecption的错误&#xff01; RunWith(SpringRunner.class)的作用&#xff1…

@RunWith和 SpringJUnit4ClassRunner ----junit4和Spring一起使用

今天在看Spring的Demo的时候&#xff0c;看到了如此单元测试的写法 如下: RunWIth(SpringJunit4ClassRunner.class) ContextConfiguration(locations {"classpath:applicationContext.xml"} public class MyTest { Test public void hehe() { //....... }…

@RunWith(SpringRunner.class)测试SpringRunner.class爆红

SpringRunner报红无法添加类&#xff0c;上pom.xml文件中去掉scope标签内容即可。如下图所示 然后再点击右边侧面的Reload All Maven Projects即可 方法2 如图所示&#xff0c;直接altenter强制 以上两种方法都不可用的时候&#xff0c;这个时候就得手动导入jar包了&#xf…

Java学习笔记-@RunWith(SpringRunner.class)的作用

建议阅读&#xff1a;https://blog.csdn.net/u011835956/article/details/113950577 注意&#xff1a;Test都是基于Junit写的case&#xff0c;并不是基于TestNg框架&#xff0c;如果是TestNg框架的话&#xff0c;应该要继承AbstractTestNGSpringContextTests&#xff0c;这样b…

SpringBoot在使用测试的时候是否需要@RunWith?

我们在使用SpringBoot进行测试的时候一般是需要加两个注解&#xff1a; SpringBootTest 目的是加载ApplicationContext&#xff0c;启动spring容器。 RunWith 是一个测试启动器&#xff0c;可以加载SpringBoot测试注解让测试在Spring容器环境下执行。如测试类中无此注解&#…

SpringBootTest单元测试—加入@Test无法启动测试方法,什么情况下需要加@RunWith(SpringRunner.class)?

spring环境下单元测试: SpringBoot环境下单元测试: 一、SpringBoot2.4.0之后 二、2.2.0 < SpringBoot < 2.4. 三、SpringBoot2.2.0之前 最近写SpringBootTest单元测试时&#xff0c;加入Test无法启动测试方法&#xff08;我用的是SpringBoot2.2.0之前的版本&#xff09;…

Run Run Run

学习了 2022 年集训队论文 《浅谈与 Lyndon 理论有关的字符串组合问题》 写得很好&#xff0c;像我这样的字符串小白也能看懂 Lyndon 分解 若字符串 w w w 小于它的每一个真后缀&#xff0c;则称 w w w 是 Lyndon 串。若字符串 w w w 是 Lyndon 串&#xff0c;则 w k w ′…

关于springboot在启动测试类时报错的问题和@RunWith的使用

【1】最重要的一点&#xff1a;测试类启动依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupI…

天呐!惊人的Springboot测试.Springboot测试类之@RunWith注解

Springboot测试类之@RunWith注解 Springboot测试类之@RunWith注解Springboot测试类之@RunWith注解 @runWith注解作用: @RunWith就是一个运行器@RunWith(JUnit4.class)就是指用JUnit4来运行@RunWith(SpringJUnit4ClassRunner.class),让测试运行于Spring测试环境,以便在测试开…