内核smmu学习

思考

  1. smmu对外提供功能,设备驱动调用smmu 提供的api来配置页表,那其他设备是如何和smmu交互的?
  2. iommu 作为将不同smmu硬件的一个抽象封装,其它设备应该只能看到iommu这个封装层,那么iommu这个子系统是如何进行抽象的,抽象概念 domain group 等
  3. 实际开发smmu驱动是做什么?基于arm-smmu-v3.c 实现具体的硬件驱动?

应该从iommu抽象子系统入手,从上往下去分析

iommu

抽象概念与框架设计

硬件层
软件抽象层
属于
绑定到
实现
控制
通过
通过
物理设备
(PCIe/USB/GPU等)
物理 IOMMU
(ARM SMMU/Intel VT-d/AMD-Vi)
IOMMU Group
(物理隔离的最小单元)
IOMMU Domain
(地址翻译上下文)
IOMMU Operations
(硬件操作接口)
IOMMU 硬件驱动
  • iommu_group:共享同一个IOMMU地址空间的一组设备
  • iommu_domain:地址翻译的上下文(包含页表、地址空间标识符ASID/PASID等),一个group可以绑定到一个domain上,一个domain可以被多个group绑定
  • iommu_ops:一组抽象接口,用于适配不同硬件厂商的smmu设备,如arm-smmu-v3.c中的iommu_ops实例

iommu_group

struct iommu_group {struct kobject kobj;struct kobject *devices_kobj;struct list_head devices; // 组内设备链表struct mutex mutex;struct blocking_notifier_head notifier; // 允许其它组件注册对组变化的监听,当设备被添加、移除或组状态改变时通知监听者void *iommu_data; // 私有字段void (*iommu_data_release)(void *iommu_data); // 组被销毁时释放私有字段,被销毁时会调用char *name; // 显示在sysfs中int id;struct iommu_domain *default_domain;struct iommu_domain *domain;
};
// 组内的一个设备
struct group_device {struct list_head list;    // 链表节点struct device *dev;       // 指向设备的指针char *name;              // 设备在组中的名称
};
struct kobject kobj;           // Sysfs对象,用于在/sys/kernel/iommu_groups/下创建目录
struct kobject *devices_kobj;  // 设备子目录对象,用于/sys/kernel/iommu_groups/X/devices/

功能:
在sysfs中创建IOMMU组的表示
提供用户空间接口来查看和管理IOMMU组
支持组属性和设备列表的显示

struct list_head devices;      // 组内设备链表
struct mutex mutex;           // 保护组内数据结构的互斥锁

功能:

  • devices:维护组内所有设备的链表
  • mutex:确保对组内数据的并发访问安全
  • 防止在遍历设备时设备被添加或移除
struct blocking_notifier_head notifier;  // 阻塞通知器头

功能:

  • 允许其他组件注册对组变化的监听
  • 当设备被添加、移除或组状态改变时通知监听者
  • 支持异步事件处理
struct iommu_domain *default_domain;  // 默认域
struct iommu_domain *domain;          // 当前附加的域

默认域(default_domain)

  • 定义:系统为IOMMU组自动创建的基础域
  • 目的:提供基本的IOMMU功能,确保设备始终有可用的域
  • 类型:通常是IOMMU_DOMAIN_DMA或IOMMU_DOMAIN_IDENTITY
  • 生命周期:与组同生共死,组销毁时域也销毁

当前附加域(domain)

  • 定义:组当前实际使用的IOMMU域
  • 目的:可以是默认域,也可以是用户指定的自定义域
  • 类型:可以是任何类型的IOMMU域
  • 生命周期:可以动态切换,独立于默认域

iommu_domain

struct iommu_domain {unsigned type; // 域类型const struct iommu_ops *ops; unsigned long pgsize_bitmap;	/* Bitmap of page sizes in use */iommu_fault_handler_t handler; // 故障处理函数指针,当IOMMU检测到访问违规时调用void *handler_token; // 上面函数的参数,故障处理相关的上下文struct iommu_domain_geometry geometry; // 定义域的地址空间几何信息struct iommu_domain_geometry {dma_addr_t aperture_start;  // 可映射地址范围的起始地址dma_addr_t aperture_end;    // 可映射地址范围的结束地址bool force_aperture;        // 是否强制在指定范围内进行DMA};void *iova_cookie; // IOVA私有数据指针,存储IOVA管理相关的上下文信息,如地址分配器状态等
};
域类型地址转换用途典型应用
IOMMU_DOMAIN_DMA标准DMA操作设备驱动DMA
IOMMU_DOMAIN_IDENTITY否,可以访问直通模式高性能场景
IOMMU_DOMAIN_UNMANAGED手动自定义管理特殊需求
IOMMU_DOMAIN_BLOCKED拒绝访问安全隔离安全敏感设备

iommu_ops

struct iommu_ops {// 1. 能力查询bool (*capable)(enum iommu_cap);                    // 查询IOMMU是否支持特定功能// 2. 域管理struct iommu_domain *(*domain_alloc)(unsigned iommu_domain_type);  // 分配IOMMU域void (*domain_free)(struct iommu_domain *);                        // 释放IOMMU域// 3. 设备管理int (*attach_dev)(struct iommu_domain *domain, struct device *dev);  // 将设备附加到域void (*detach_dev)(struct iommu_domain *domain, struct device *dev); // 从域分离设备int (*add_device)(struct device *dev);                               // 添加设备到IOMMUvoid (*remove_device)(struct device *dev);                           // 从IOMMU移除设备struct iommu_group *(*device_group)(struct device *dev);             // 获取设备的group 直接创建一个group// 4. 地址映射int (*map)(struct iommu_domain *domain, unsigned long iova,         // 映射IOVA到物理地址phys_addr_t paddr, size_t size, int prot);size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,    // 取消地址映射size_t size, struct iommu_iotlb_gather *iotlb_gather);phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova); // IOVA转物理地址// 5. TLB管理void (*flush_iotlb_all)(struct iommu_domain *domain);               // 刷新所有TLB条目void (*iotlb_sync_map)(struct iommu_domain *domain);                // 同步映射TLBvoid (*iotlb_sync)(struct iommu_domain *domain,                     // 同步TLBstruct iommu_iotlb_gather *iotlb_gather);// 6. 域属性管理int (*domain_get_attr)(struct iommu_domain *domain,                 // 获取域属性enum iommu_attr attr, void *data);int (*domain_set_attr)(struct iommu_domain *domain,                 // 设置域属性enum iommu_attr attr, void *data);// 7. 保留区域管理void (*get_resv_regions)(struct device *dev, struct list_head *list);     // 获取设备保留区域void (*put_resv_regions)(struct device *dev, struct list_head *list);     // 释放保留区域void (*apply_resv_region)(struct device *dev,                              // 应用保留区域struct iommu_domain *domain,struct iommu_resv_region *region);// 8. 窗口管理int (*domain_window_enable)(struct iommu_domain *domain, u32 wnd_nr,      // 启用域窗口phys_addr_t paddr, u64 size, int prot);void (*domain_window_disable)(struct iommu_domain *domain, u32 wnd_nr);   // 禁用域窗口// 9. 设备树支持int (*of_xlate)(struct device *dev, struct of_phandle_args *args);        // 设备树解析// 10. 延迟附加bool (*is_attach_deferred)(struct iommu_domain *domain, struct device *dev); // 检查是否延迟附加// 11. 设备特性管理bool (*dev_has_feat)(struct device *dev, enum iommu_dev_features f);      // 检查设备是否支持特性bool (*dev_feat_enabled)(struct device *dev, enum iommu_dev_features f);  // 检查特性是否启用int (*dev_enable_feat)(struct device *dev, enum iommu_dev_features f);    // 启用设备特性int (*dev_disable_feat)(struct device *dev, enum iommu_dev_features f);   // 禁用设备特性// 12. 辅助域管理int (*aux_attach_dev)(struct iommu_domain *domain, struct device *dev);   // 附加设备到辅助域void (*aux_detach_dev)(struct iommu_domain *domain, struct device *dev);  // 从辅助域分离设备int (*aux_get_pasid)(struct iommu_domain *domain, struct device *dev);    // 获取辅助域PASID// 13. SVA (Shared Virtual Addressing) 支持struct iommu_sva *(*sva_bind)(struct device *dev, struct mm_struct *mm,   // 绑定设备到进程地址空间void *drvdata);void (*sva_unbind)(struct iommu_sva *handle);                             // 解绑SVAint (*sva_get_pasid)(struct iommu_sva *handle);                           // 获取SVA的PASID// 14. 页面响应int (*page_response)(struct device *dev,                                   // 页面请求响应struct iommu_fault_event *evt,struct iommu_page_response *msg);// 15. 支持的页面大小unsigned long pgsize_bitmap;                                              // 支持的页面大小位图
};

iommu 子系统初始化

系统启动
iommu_init
iommu_subsys_init
等待IOMMU驱动
bus_set_iommu
iommu_bus_init
注册总线通知器
设备添加到总线
iommu_bus_notifier
iommu_probe_device
iommu_group_get_for_dev
iommu_group_alloc
iommu_group_add_device
IOMMU子系统就绪
ARM SMMU v3 Domain 创建时机详解
1. Domain创建的触发时机

Domain的创建主要有以下几个时机:

1.1 设备探测时自动创建
dom = __iommu_domain_alloc(dev->bus, iommu_def_domain_type);
if (!dom && iommu_def_domain_type != IOMMU_DOMAIN_DMA) {dom = __iommu_domain_alloc(dev->bus, IOMMU_DOMAIN_DMA);
  • 触发条件: 当设备被探测时,如果设备组没有默认domain
  • 创建类型: 根据系统配置创建默认domain类型
  • 回退机制: 如果默认类型创建失败,回退到DMA domain
1.2 手动创建
struct iommu_domain *iommu_domain_alloc(struct bus_type *bus)
{return __iommu_domain_alloc(bus, IOMMU_DOMAIN_UNMANAGED);
}
  • 触发条件: 应用程序或驱动主动调用
  • 创建类型: 通常为UNMANAGED类型
2. Domain创建流程
2.1 IOMMU核心层调用
static struct iommu_domain *__iommu_domain_alloc(struct bus_type *bus,unsigned type)
{struct iommu_domain *domain;if (bus == NULL || bus->iommu_ops == NULL)return NULL;domain = bus->iommu_ops->domain_alloc(type);if (!domain)return NULL;domain->ops  = bus->iommu_ops;domain->type = type;domain->pgsize_bitmap  = bus->iommu_ops->pgsize_bitmap;return domain;
}
2.2 ARM SMMU v3驱动实现
static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
{struct arm_smmu_domain *smmu_domain;if (type != IOMMU_DOMAIN_UNMANAGED &&type != IOMMU_DOMAIN_DMA &&type != IOMMU_DOMAIN_IDENTITY)return NULL;smmu_domain = kzalloc(sizeof(*smmu_domain), GFP_KERNEL);if (!smmu_domain)return NULL;if (type == IOMMU_DOMAIN_DMA &&iommu_get_dma_cookie(&smmu_domain->domain)) {kfree(smmu_domain);return NULL;}mutex_init(&smmu_domain->init_mutex);INIT_LIST_HEAD(&smmu_domain->devices);spin_lock_init(&smmu_domain->devices_lock);return &smmu_domain->domain;
}
3. Domain创建时机总结
设备探测
设备组有默认domain?
创建默认domain
使用现有domain
调用__iommu_domain_alloc
调用arm_smmu_domain_alloc
分配smmu_domain结构
初始化互斥锁和列表
返回domain
手动创建
调用iommu_domain_alloc
设备绑定
调用arm_smmu_attach_dev
调用arm_smmu_domain_finalise
初始化页表
分配ASID/VMID
配置STE
4. 关键时间点
4.1 Domain结构创建
  • 时机: 设备探测或手动创建时
  • 内容: 分配内存,初始化基本结构
  • 状态: 此时还没有关联SMMU设备
4.2 Domain最终化
  • 时机: 设备绑定到domain时
  • 内容: 初始化页表,分配ASID/VMID
  • 状态: 此时才真正配置硬件
4.3 页表初始化
  • 时机: arm_smmu_domain_finalise 调用时
  • 内容: 创建IOVA到PA的转换页表
  • 状态: 此时才支持地址转换

arm-smmu-v3实例化

ARM SMMU v3的实例化流程体现了Linux内核中抽象框架的设计模式:平台驱动注册 → 2. 设备探测 → 3. 硬件初始化 → 4. 抽象层注册 → 5. 总线集成。平台驱动注册,然后内核启动时根据设备数创建platform_device,然后device_initcall阶段会注册platform_driver,然后会触发driver_attach最终触发probe函数来进行设备探测和初始化
在这里插入图片描述

设备探测和初始化

内存分配和设备结构初始化

分配 arm_smmu_device 结构体
设置设备指针
根据设备树或ACPI进行探测

硬件资源获取

获取MMIO寄存器区域
映射寄存器地址空间
获取中断资源(combined、eventq、priq、gerror)

硬件探测和初始化 arm_smmu_device_hw_probe

在这里插入图片描述

arm_smmu_init_structures(): 初始化内存中的数据结构

arm_smmu_init_queues 队列初始化
  • CMDQ:接收用户命令的队列
  • EVTQ:接收SMMU硬件的事件队列
  • PRIQ:优先级队列,处理页面请求接口事件队列
    arm smmu v3 队列机制
arm_smmu_init_strtab 流表初始化 设备到domain的映射
支持二级流表
不支持二级流表
失败
失败
失败
arm_smmu_init_strtab开始
检查硬件特性
arm_smmu_init_strtab_2lvl
arm_smmu_init_strtab_linear
计算流表大小
分配DMA内存
配置线性流表格式
初始化bypass STE
设置流表基地址
计算L1表大小
分配L1表DMA内存
配置二级流表格式
初始化L1描述符
分配VMID 0
初始化完成
返回-ENOMEM
2-Level Stream Table
Linear Stream Table
ARM SMMU Device
L1 Desc 0
L1 Table
L1 Desc 1
L1 Desc 2
...
L1 Desc N-1
L2 Table 0
L2 Table 1
L2 Table 2
...
L2 Table N-1
STE 0-255
STE 256-511
STE 512-767
STE N-255-N
STE 0
Linear Table
STE 1
STE 2
...
STE N-1
Stream Table Config
SMMU Device
strtab_base
strtab_dma
strtab_base_cfg
2-Level Stream Table Memory Layout
Linear Stream Table Memory Layout
L1 Desc 0: 16 bytes
L1 Table Base
L1 Desc 1: 16 bytes
L1 Desc 2: 16 bytes
...
L1 Desc N-1: 16 bytes
L2 Table 0: 16KB
L2 Table 1: 16KB
L2 Table 2: 16KB
...
L2 Table N-1: 16KB
STE 0-255
STE 256-511
STE 512-767
STE N-255-N
STE 0: 64 bytes
Linear Table Base
STE 1: 64 bytes
STE 2: 64 bytes
...
STE N-1: 64 bytes
STE Structure (64 bytes)
Word 0: Config
STE Entry
Word 1: Config
Word 2: S1 Context Descriptor
Word 3: S1 Context Descriptor
Word 4: S2 Context Descriptor
Word 5: S2 Context Descriptor
Word 6: S2 Context Descriptor
Word 7: S2 Context Descriptor
Valid Bit
Config Type
Stage 1 Config
Stage 2 Config
TTBR0
TTBR1
VTTBR
VTCR
STE Structure (64 bytes)
Word 0: Config
STE Entry
Word 1: Config
Word 2: S1 Context Descriptor
Word 3: S1 Context Descriptor
Word 4: S2 Context Descriptor
Word 5: S2 Context Descriptor
Word 6: S2 Context Descriptor
Word 7: S2 Context Descriptor
Valid Bit
Config Type
Stage 1 Config
Stage 2 Config
TTBR0
TTBR1
VTTBR
VTCR
Page Tables
Domain Layer
Stream Table
Device Layer
IO Page Tables
IO Page Tables
Stage 2 Page Tables
S1 Domain
S1 Domain
S2 Domain
Domain: S1_CFG
STE 0x100
Domain: S1_CFG
STE 0x200
Domain: S2_CFG
STE 0x300
SID: 0x100
PCI Device
SID: 0x200
Network Device
SID: 0x300
Storage Device

注册到IOMMU核心框架

iommu_device_sysfs_add(): 添加sysfs接口
iommu_device_set_ops(): 设置操作函数
iommu_device_register(): 注册IOMMU设备

总线设备初始化

为PCI、AMBA、Platform总线设置IOMMU操作
将所有总线对应的iommu_ops 都设置为arm_smmu_ops,然后调用iommu_bus_init->bus_for_each_dev 为bus上的每个设备进行add_iommu_group->iommu_probe_device->ops->add_device

最后调用arm_smmu_add_device进行添加设备到group中

  1. 设备初始化
    验证设备: 检查设备是否支持IOMMU操作
    创建master结构: 为设备分配 arm_smmu_master 结构体
    建立关联: 将设备与SMMU设备关联起来
  2. Stream ID管理
    SID验证: 检查设备的Stream ID是否在SMMU支持的范围内
    二级表初始化: 如果支持二级流表,确保相应的二级表已初始化
  3. IOMMU组管理
    获取IOMMU组: 调用 iommu_group_get_for_dev() 为设备分配IOMMU组
    设备链接: 将设备链接到IOMMU设备

arm_smmu_device和arm_smmu_master

关系建立过程
设备发现: 系统发现一个需要IOMMU保护的设备 (dev)
SMMU查找: 通过设备树或ACPI找到对应的SMMU硬件 (smmu)
Master创建: 为设备创建一个master结构,建立设备和SMMU的关联
配置初始化: 在master中设置设备的SID、ATS等配置
实际应用场景
一个SMMU: 管理多个设备,每个设备有自己的master一个设备: 对应一个master,连接到特定的SMMU地址转换: 设备发起DMA请求时,SMMU根据master中的配置进行地址转换这种设计实现了一对多的关系:一个SMMU可以管理多个设备,每个设备通过自己的master与SMMU交互。

dev smmudev domain

多个dev组成一个group ,一个group对应有其domain,表示其地址转换上下文
smmudev和domain关联
dev绑定到domain时机

设备绑定时机
手动绑定
自动绑定
应用程序调用
驱动调用
内核模块调用
设备探测时
系统启动时
DMA分配时
创建默认domain
绑定到默认domain
调用iommu_attach_device
获取设备组
遍历组内所有设备
调用arm_smmu_attach_dev
配置STE
绑定完成

ARM SMMU v3 页表初始化详解

1. 页表初始化的位置

static int arm_smmu_domain_finalise(struct iommu_domain *domain)
{int ret;unsigned long ias, oas;enum io_pgtable_fmt fmt;struct io_pgtable_cfg pgtbl_cfg;struct io_pgtable_ops *pgtbl_ops;int (*finalise_stage_fn)(struct arm_smmu_domain *,struct io_pgtable_cfg *);struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);struct arm_smmu_device *smmu = smmu_domain->smmu;// ... 配置页表格式和参数 ...pgtbl_ops = alloc_io_pgtable_ops(fmt, &pgtbl_cfg, smmu_domain);if (!pgtbl_ops)return -ENOMEM;// ... 调用具体的stage初始化函数 ...smmu_domain->pgtbl_ops = pgtbl_ops;return 0;
}

2. 页表操作结构

struct io_pgtable_ops		*pgtbl_ops;

这个结构包含了所有页表操作函数:

  • map: IOVA到PA的映射
  • unmap: 解除映射
  • iova_to_phys: IOVA到PA的转换

3. 页表格式选择

根据domain类型选择不同的页表格式:

	switch (smmu_domain->stage) {case ARM_SMMU_DOMAIN_S1:ias = (smmu->features & ARM_SMMU_FEAT_VAX) ? 52 : 48;ias = min_t(unsigned long, ias, VA_BITS);oas = smmu->ias;fmt = ARM_64_LPAE_S1;  // Stage-1页表格式finalise_stage_fn = arm_smmu_domain_finalise_s1;break;case ARM_SMMU_DOMAIN_NESTED:case ARM_SMMU_DOMAIN_S2:ias = smmu->ias;oas = smmu->oas;fmt = ARM_64_LPAE_S2;  // Stage-2页表格式finalise_stage_fn = arm_smmu_domain_finalise_s2;break;default:return -EINVAL;}

4. Stage-1页表初始化

static int arm_smmu_domain_finalise_s1(struct arm_smmu_domain *smmu_domain,struct io_pgtable_cfg *pgtbl_cfg)
{int ret;int asid;struct arm_smmu_device *smmu = smmu_domain->smmu;struct arm_smmu_s1_cfg *cfg = &smmu_domain->s1_cfg;// 分配ASIDasid = arm_smmu_bitmap_alloc(smmu->asid_map, smmu->asid_bits);if (asid < 0)return asid;// 分配上下文描述符cfg->cdptr = dmam_alloc_coherent(smmu->dev, CTXDESC_CD_DWORDS << 3,&cfg->cdptr_dma,GFP_KERNEL | __GFP_ZERO);if (!cfg->cdptr) {dev_warn(smmu->dev, "failed to allocate context descriptor\n");ret = -ENOMEM;goto out_free_asid;}// 配置上下文描述符cfg->cd.asid	= (u16)asid;cfg->cd.ttbr	= pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];  // 页表基地址cfg->cd.tcr	= pgtbl_cfg->arm_lpae_s1_cfg.tcr;      // 转换控制寄存器cfg->cd.mair	= pgtbl_cfg->arm_lpae_s1_cfg.mair[0];  // 内存属性return 0;
}

5. Stage-2页表初始化

static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,struct io_pgtable_cfg *pgtbl_cfg)
{int vmid;struct arm_smmu_device *smmu = smmu_domain->smmu;struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;// 分配VMIDvmid = arm_smmu_bitmap_alloc(smmu->vmid_map, smmu->vmid_bits);if (vmid < 0)return vmid;// 配置Stage-2参数cfg->vmid	= (u16)vmid;cfg->vttbr	= pgtbl_cfg->arm_lpae_s2_cfg.vttbr;  // Stage-2页表基地址cfg->vtcr	= pgtbl_cfg->arm_lpae_s2_cfg.vtcr;   // Stage-2转换控制return 0;
}

6. IOVA到PA转换的页表操作

6.1 映射操作
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;return ops->map(ops, iova, paddr, size, prot);
}
6.2 解除映射操作
static size_t arm_smmu_unmap(struct iommu_domain *domain, unsigned long iova,size_t size, struct iommu_iotlb_gather *gather)
{struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);struct io_pgtable_ops *ops = smmu_domain->pgtbl_ops;return ops->unmap(ops, iova, size, gather);
}
6.3 IOVA到PA转换
static phys_addr_t
arm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
{struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;if (!ops)return 0;return ops->iova_to_phys(ops, iova);
}
pgtbl_ops
map函数
unmap函数
iova_to_phys函数
map_sg函数
unmap_sg函数
IOVA到PA映射
IOVA到PA解映射
IOVA到PA转换查询
散列表映射
散列表解映射

ARM SMMU v3 页表架构图

ARM SMMU v3 Page Table Architecture
Stream Table Entry
IOVA
Context Descriptor
TTBR - Page Table Base
L1 Page Table
L2 Page Table
L3 Page Table
Physical Address
VA to IPA
Stage-1 Translation
IPA to PA
Stage-2 Translation

页表初始化调用链

iommu_domain_alloc
arm_smmu_domain_alloc
arm_smmu_domain_finalise
alloc_io_pgtable_ops
ARM_64_LPAE_S1/S2
arm_smmu_domain_finalise_s1/s2
分配ASID/VMID
配置页表基地址
页表初始化完成

ASID和VMID

ASID 是 Address Space ID 的缩写,用于在ARM SMMU v3的Stage-1 (S1) 转换中标识不同的地址空间。

进程A
ASID 1
进程B
ASID 2
虚拟机1
ASID 3
S1页表A
S1页表B
S1页表C
IOVA转换
IOVA转换
IOVA转换

VMID 是 Virtual Machine ID 的缩写,用于在ARM SMMU v3的Stage-2 (S2) 转换中标识不同的虚拟机。

虚拟机A
VMID 1
虚拟机B
VMID 2
虚拟机C
VMID 3
S2页表A
S2页表B
S2页表C
IPA转换
IPA转换
IPA转换

对比

特性ASIDVMID
转换阶段Stage-1 (S1)Stage-2 (S2)
转换类型IOVA → IPAIPA → PA
标识对象进程/地址空间虚拟机
使用场景进程隔离虚拟机隔离

我来详细解释 alloc_io_pgtable_ops 分配的 pgtbl_ops 中函数定义的位置:

Ran tool

Read file: drivers/iommu/io-pgtable.c
现在让我查看ARM 64位LPAE页表的初始化函数:

Ran tool

Read file: drivers/iommu/io-pgtable-arm.c
现在让我查看具体的页表操作函数定义:

Read file: drivers/iommu/io-pgtable-arm.c
现在让我查看具体的页表操作函数实现:

Read file: drivers/iommu/io-pgtable-arm.c
现在让我查看unmap和iova_to_phys函数的实现:

Read file: drivers/iommu/io-pgtable-arm.c
现在我来详细解释 alloc_io_pgtable_ops 分配的 pgtbl_ops 中函数定义的位置:

alloc_io_pgtable_ops 分配的 pgtbl_ops 函数定义位置

1. 分配流程

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;
}

2. 初始化表映射

static 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,[ARM_MALI_LPAE] = &io_pgtable_arm_mali_lpae_init_fns,
#endif
#ifdef CONFIG_IOMMU_IO_PGTABLE_ARMV7S[ARM_V7S] = &io_pgtable_arm_v7s_init_fns,
#endif
};

3. ARM 64位LPAE初始化函数

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,
};

4. 页表操作函数赋值

	data->iop.ops = (struct io_pgtable_ops) {.map		= arm_lpae_map,.unmap		= arm_lpae_unmap,.iova_to_phys	= arm_lpae_iova_to_phys,};

5. 具体函数定义位置

5.1 arm_lpae_map 函数
static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova,phys_addr_t paddr, size_t size, int iommu_prot)
{struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);arm_lpae_iopte *ptep = data->pgd;int ret, lvl = ARM_LPAE_START_LVL(data);arm_lpae_iopte prot;/* If no access, then nothing to do */if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE)))return 0;if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias) ||paddr >= (1ULL << data->iop.cfg.oas)))return -ERANGE;prot = arm_lpae_prot_to_pte(data, iommu_prot);ret = __arm_lpae_map(data, iova, paddr, size, prot, lvl, ptep);/** Synchronise all PTE updates for the new mapping before there's* a chance for anything to kick off a table walk for the new iova.*/wmb();return ret;
}
5.2 arm_lpae_unmap 函数
static size_t arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova,size_t size, struct iommu_iotlb_gather *gather)
{struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);arm_lpae_iopte *ptep = data->pgd;int lvl = ARM_LPAE_START_LVL(data);if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias)))return 0;return __arm_lpae_unmap(data, gather, iova, size, lvl, ptep);
}
5.3 arm_lpae_iova_to_phys 函数
static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,unsigned long iova)
{struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);arm_lpae_iopte pte, *ptep = data->pgd;int lvl = ARM_LPAE_START_LVL(data);do {/* Valid IOPTE pointer? */if (!ptep)return 0;/* Grab the IOPTE we're interested in */ptep += ARM_LPAE_LVL_IDX(iova, lvl, data);pte = READ_ONCE(*ptep);/* Valid entry? */if (!pte)return 0;/* Leaf entry? */if (iopte_leaf(pte, lvl, data->iop.fmt))goto found_translation;/* Take it to the next level */ptep = iopte_deref(pte, data);} while (++lvl < ARM_LPAE_MAX_LEVELS);/* Ran out of page tables to walk */return 0;found_translation:iova &= (ARM_LPAE_BLOCK_SIZE(lvl, data) - 1);return iopte_to_paddr(pte, data) | iova;
}

6. 函数调用链

alloc_io_pgtable_ops
io_pgtable_init_table
io_pgtable_arm_64_lpae_s1_init_fns
arm_64_lpae_alloc_pgtable_s1
arm_lpae_alloc_pgtable
data->iop.ops赋值
arm_lpae_map
arm_lpae_unmap
arm_lpae_iova_to_phys

7. 文件位置总结

函数定义位置文件
alloc_io_pgtable_opsdrivers/iommu/io-pgtable.c:28通用页表分配器
io_pgtable_arm_64_lpae_s1_init_fnsdrivers/iommu/io-pgtable-arm.c:1072ARM LPAE初始化函数
arm_64_lpae_alloc_pgtable_s1drivers/iommu/io-pgtable-arm.c:789S1页表分配
arm_lpae_alloc_pgtabledrivers/iommu/io-pgtable-arm.c:744LPAE页表分配
arm_lpae_mapdrivers/iommu/io-pgtable-arm.c:478地址映射函数
arm_lpae_unmapdrivers/iommu/io-pgtable-arm.c:651地址解映射函数
arm_lpae_iova_to_physdrivers/iommu/io-pgtable-arm.c:664地址转换查询函数

8. 核心实现特点

  1. 模块化设计: 不同页表格式有不同的初始化函数
  2. 函数指针赋值: 在页表分配时动态赋值操作函数
  3. ARM LPAE实现: 所有ARM LPAE相关的页表操作都在 io-pgtable-arm.c
  4. 通用接口: 通过 io_pgtable_ops 结构体提供统一的页表操作接口

这种设计使得ARM SMMU v3可以使用标准的ARM LPAE页表格式,同时保持了良好的模块化和可扩展性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/93922.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/93922.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Slices:让应用功能在系统级交互中触手可及

引言 在当今移动应用生态中&#xff0c;用户每天要面对数十个甚至上百个应用的选择&#xff0c;如何让自己的应用在关键时刻触达用户&#xff0c;成为开发者面临的重要挑战。Google在Android 9 Pie中引入的Slices技术&#xff0c;正是为了解决这一痛点而生。本文将全面介绍And…

python学智能算法(三十))|SVM-KKT条件的数学理解

【1】引言 前序学习进程中&#xff0c;通过类比力的平衡对KKT条件进行了初步的理解。 今天我们更进一步&#xff0c;常使用数学语言进一步解释KKT条件。 【2】带约束的最小优化问题 首先定义一个即将求解的优化问题&#xff1a; 目标函数&#xff1a;最小化f(x)(x∈Rn)f(x)(…

华为云Flexus+DeepSeek征文|Linux命令实现两种部署的性能捕获+(硅基+Maas)模型添加教学

前引&#xff1a;“在数字化浪潮汹涌澎湃的今天&#xff0c;企业对云计算服务的需求已从基础架构支撑&#xff0c;逐步转向更深层次的AI赋能与业务创新驱动。面对复杂多变的市场环境&#xff0c;选择一个强大、可靠且具备前瞻性的云服务伙伴&#xff0c;无疑是企业实现高速增长…

langchain--1--prompt、output格式、LCEL示例

环境&#xff1a;本地使用ollama部署的deepseek-r1:1.5b模型 本文示例包含: [1] 非LCEL的调用方法[2] LCEL的调用方法[3] prompt template的简单使用&#xff0c;除了PromptTemplate模板&#xff0c;还有一些其它模板&#xff0c;可去查看官网[4] 输出&#xff1a;json格式、py…

【算法】指数滑动滤波器

指数滑动滤波器作用原理特点公式代码优化升级作用 首先这个滤波器能够将一些突变的信号对系统的影响降低&#xff0c;能够平滑输入信号&#xff0c;滤除噪声&#xff0c;减少测量数据的瞬间波动和干扰&#xff0c;就是实现输入信号不能不变&#xff0c;数值不会突然变大&#…

STM32F4—电源管理器

Power supply schemesPower supply supervisorInternal reset ON有PDR_ON pin的MCU&#xff0c;PDR_ON pin被拉高的时候电源监视器被使能。没有PDR_ON pin的MCU默认一直使能。内部集成了power-on reset (POR) / power-down reset (PDR)POR&#xff08;上电复位&#xff09;&…

MySQL锁的分类 MVCC和S/X锁的互补关系

各位看官&#xff0c;大家早安午安晚安呀~~~如果您觉得这篇文章对您有帮助的话欢迎您一键三连&#xff0c;小编尽全力做到更好 欢迎您分享给更多人哦今天我们来学习&#xff1a;MySQL锁的分类 && MVCC和S/X锁的互补关系1.锁分类1.按锁粒度分类&#xff1a;全局锁&#…

第五届智能通信与计算国际学术会议(ICICC 2025)

重要信息 官网&#xff1a;www.ic-icc.org 时间&#xff1a;2025年8月15-16日 地点&#xff1a;中国 南京 第五届智能通信与计算国际学术会议(ICICC 2025&#xff09;定于2025年8月15-16日在中国 南京举行。随着信息技术的飞速发展&#xff0c;智能通信与计算领域的研究与…

基于C#和NModbus4库实现的Modbus RTU串口通信

基于C#和NModbus4库实现的Modbus RTU串口通信&#xff0c;包含完整的界面设计和功能实现&#xff1a;一、项目依赖配置NuGet包安装&#xff1a; Install-Package NModbus4 Install-Package System.IO.Ports窗体控件布局&#xff1a; <!-- 基础控件配置 --> <ComboBox …

想要批量提取视频背景音乐?FFmpeg 和转换器都安排上

你是否遇到过这样的情况&#xff1f;看到一个超赞的短视频&#xff0c;里面的背景音乐特别好听&#xff0c;想单独保存下来当手机铃声或收藏&#xff0c;却不知道怎么把音乐从视频里“抠”出来&#xff1f;别担心&#xff01;今天就为大家分享两种简单易行的方法&#xff0c;无…

为什么MCP协议是AI集成的未来API

一、企业AI应用的核心挑战与架构演进 当前企业AI落地面临三大核心痛点&#xff1a; ​​系统集成困境​​&#xff1a;需对接企业内部业务系统&#xff08;CRM/ERP等&#xff09;​​异构环境兼容​​&#xff1a;需整合第三方AI服务与传统API​​数据孤岛突破​​&#xff1…

Apache Tomcat样例目录session操纵漏洞解读

【漏洞名称】&#xff1a;Apache Tomcat样例目录session操纵漏洞 &#xff08;Apache Tomcat示例目录漏洞&#xff09;【漏洞等级】&#xff1a;中危&#xff0c;5.9分。【漏洞描述】Apache Tomcat默认安装页面中存在examples样例目录&#xff0c;里面存放着Servlets、JSP、Web…

Go语言实战案例:实现HTTP客户端请求并解析响应

本文是 Go 网络与并发实战系列的第2篇&#xff0c;聚焦于如何使用 Go 实现一个 HTTP 客户端&#xff0c;完成请求发送、响应解析、错误处理、Header与Body提取等完整流程。一、前言&#xff1a;为什么学习HTTP客户端&#xff1f;在日常开发中&#xff0c;无论是调用 RESTful AP…

java的冒泡排序算法

冒泡排序是一种简单的排序算法&#xff0c;通过重复遍历待排序序列&#xff0c;比较相邻元素并在必要时交换位置&#xff0c;最终实现排序。以下是Java实现的详细说明&#xff1a;核心原理‌比较相邻元素‌&#xff1a;从序列第一个元素开始&#xff0c;逐对比较相邻元素的大小…

玻尔兹曼分布与玻尔兹曼探索

目录 玻尔兹曼分布定义 玻尔兹曼探索&#xff1a; 1. 玻尔兹曼分布公式 2. 温度 T 如何影响采样结果&#xff1f; (1) 高温 (T→∞)&#xff1a; (2) 低温 (T→0)&#xff1a; (3) 中等温度 (T∈(0,∞))&#xff1a; 3. 直观示例 4. 实际应用中的意义 5.核心误区澄清…

【工具】jsDelivr CDN完全指南:免费高速的开源项目CDN服务

前言 在现代Web开发中&#xff0c;内容分发网络&#xff08;CDN&#xff09;已经成为提升网站性能的重要工具。jsDelivr作为一个免费、快速、可靠的开源CDN服务&#xff0c;为全球开发者提供了优质的静态资源分发服务。无论是加速GitHub仓库访问、分发npm包&#xff0c;还是为…

OSPF笔记整理

一、OSPF 基础特性1. 技术背景&#xff08;对比 RIP&#xff09;RIP 的缺陷&#xff1a;最大跳数 15 限制、周期性发送全路由表&#xff08;占用带宽&#xff09;、收敛慢、以跳数为度量值、易产生环路、30 秒更新间隔。OSPF 的改进&#xff1a;无跳数限制&#xff08;支持大规…

sqLite 数据库 (3):以编程方式使用 sqLite,4 个函数,以及 sqLite 移植,合并编译

&#xff08;22&#xff09; 只有四个函数 &#xff1a;以及 &#xff1a;&#xff08;23&#xff09;以及 &#xff1a;&#xff08;24&#xff09;&#xff08;25&#xff09; sqLite 的源代码很少 &#xff1a;&#xff08;26&#xff09;&#xff08;27&#xff09;&#x…

Nginx跨域问题与 MIME 类型错误深度排错指南:解决 MIME type of “application/octet-stream“ 报错

前言&#xff1a;在 Web 开发中&#xff0c;跨域请求和资源加载错误是前端工程师和运维人员经常遇到的棘手问题。本文将详细解析 Nginx 环境下跨域配置的多种方案、gzip 类型参数的优化要点&#xff0c;以及.mjs 文件 MIME 类型错误的解决方法&#xff0c;并结合排错思路和原理…

什么是大端?什么是小端?如何验证?

什么是大端&#xff1f;什么是小端&#xff1f;如何验证&#xff1f; 在计算机系统中&#xff0c;大端&#xff08;Big-Endian&#xff09; 和小端&#xff08;Little-Endian&#xff09; 是两种不同的字节序&#xff08;Byte Order&#xff09;&#xff0c;用于描述多字节数据…