內核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
英文地址,請注明出處:http://en.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;用于描述多字節數據…