ARM SMMUv3 architecture

ARM SMMUv3 architecture

-v0.1 2017.3.12 Sherlock init
-v0.2 2017.4.17 Sherlock add usage part
-v0.3 2020.4.19 Sherlock 增加smmu的全局描述

本文梳理IOMMU相關的整體軟硬件設計的全貌。具體的硬件以ARM SMMUv3作爲例子。

IOMMU是外設的MMU。原來的外設主動發起的DMA的操作使用的都是系統的物理地址,直接
使用物理地址有很多不方便的地方,在外設和內存之間引入一個新的IOMMU硬件,完成一些
諸如地址翻譯的功能,這樣又有很多新的玩法可以加進來。

加入IOMMU上的這個地址翻譯,就可以引入翻譯時候的權限管理,這樣保證了外設發出的
訪問系統內存的地址是安全的。加上地址翻譯,還可以把一片連續的虛擬地址空間映射到
諸多離散的物理地址上,這樣滿足一部分設備要訪問連續大地址的需要。IOMMU把外設
變得更像CPU,CPU和外設在使用內存方面都只看到虛擬地址, 如果虛擬地址到物理地址的
映射在CPU MMU和IOMMU上是一樣的,CPU和外設將可以看到相同的虛擬地址空間。CPU MMU
上的七七八八的功能上都可以在IOMMU上都加上。

CPU core和MMU是綁在一起的,而IOMMU和外設是獨立的兩個設備, IOMMU一般是不做在
外設裏的,不然帶IOMMU的外設有了地址管理的功能,對系統不安全。MMU上地址翻譯的
頁表,不同進程切換的時候都要換一套, 外設並不能被進程獨佔,不同的進程可以同時
給外設發請求, 廣義上作爲外設狀態一部分的IOMMU自然也用有別MMU的方法描述不同外設
在IOMMU的地址翻譯配置。

爲了說明白整個IOMMU/SMMU的大體架構, 大概要說清楚一下幾個方面:

  1. IOMMU(以下都用SMMU)是一個什麼設備,它在系統中的位置和作用是什麼。

  2. 固件(UEFI)裏如何描述SMMU和系統其他部件的關係(PCI, GIC), 如何描述SMMU和它
    管理的外設的關係。系統軟件(一下都用Linux內核)如何解析,進而構建這種關係。

  3. SMMU硬件都提供怎麼樣的功能, SMMU使用怎麼樣的軟硬件結構來支持這樣的功能。
    Linux內核如何構建SMMU硬件需要的執行環境。SMMU驅動運行時如何工作。

  4. SMMU驅動怎麼對外提供功能,外界訪問IOMMU/SMMU的接口有哪些。IOMMU這一層如何支持
    IOMMU的對外接口。

  5. SMMU的虛擬化(S1 + S2)是怎麼用起來的。

下面來一一介紹下上面的內容。

  1. SMMU的固件描述和Linux解析

參考[2][3], ACPI(先不關注DT裏的描述方法)在IORT表格裏描述SMMU和系統裏其他部件
的關係。

  1. SMMU功能簡介

SMMU爲外設提供和地址翻譯相關的諸多功能。硬件上, SMMU用三個隊列和兩個表格支持
其基本功能。

               +----------+
               | CPU core |
               | +-----+  |
               | | MMU |  |
               +-+--+--+--+       +------------------------------------------+
   system bus       v             |                                          |
          ----------------------->|  DDR     +-------+                       |
                   ^              |          |pa     |                       |
             pa    |              |          +-------+                       |
                   |              |                                          |
               +---+-----+        | +---+    +---+    +----------+           |
               |  SMMU   |------->| |STE|--->|CD |--->|Page table|           |
               |         |        | |   |    +---+    | va -> ipa| (s1)      |
               |         |        | |   |    |.. |    +----------+           |
               |         |        | |   |    +---+                           |
               |  TLB    |        | |   |    |CD |                           |
               |         |        | |   |    +---+                           |
               |STE cache|<-------| |   |    +-----------+                   |
               |         |        | |   |--->|Page table | (s2)              |
               |CD cache |        | |   |    | ipa -> pa |                   |
               |         |        | +---+    +-----------+                   |
               |         |        | |.. |                                    |
               |         |        | +---+                                    |
               |         |        | |STE|                                    |
               |         |        | +---+                                    |
               |         |        |                                          |
               |         |        | +------------------+                     |
               |         |<-------| |command queue     |                     |
               |         |        | +------------------+                     |
               |         |        | +------------------+                     |
               |         |------->| |event queue       |                     |
               |         |        | +------------------+                     |
               |         |        | +------------------+                     |
               |         |------->| |pri queue         |                     |
               +---------+        | +------------------+                     |
                   ^              +------------------------------------------+
trasaction with va |              
           +---+       +---+      
           |dev|  ...  |dev|      
           +---+       +---+      

如上是一個SMMU硬件的簡單示意圖, 爲了把SMMU相關的一些內存裏的數據結構也畫下,
上圖把SMMU畫的很大。一般的,一個SMMU物理硬件會同時服務幾個外設,而每個外設又
有可能可以獨立的發出多個內存訪問,這些內存訪問需要靠SMMU相互區分,又要靠SMMU
做地址翻譯。SMMU硬件靠STE(stream table entry)和CD(context descriptor), 去區分
不同硬件以及相同硬件上的不用內存訪問流。如上STE和CD內存裏的表格,SMMU硬件可以
認知這些表格,一個外設相關的內存訪問信息放在一個STE裏,一個外設上的一部分資源
的內存訪問信息放在一個CD裏。外設和STE的對應關係需要SID(stream id)建立聯繫,
對於PCI設備,他的SID一般就是BDF,外設硬件在發出的內存訪問請求中帶上BDF信息,
內存訪問請求被SMMU解析,SMMU通過其中的SID找見對應的STE。外設的可以獨立發內存
訪問請求的單位(比如外設的一個隊列)和CD的關係需要SSID(substream id)建立聯繫,
在PCI設備上,SSID對應的就是PCI協議裏說的PASID,這個PASID一般從系統軟件中申請
得到,然後分別配置到外設的內存訪問單元,和STE一樣,設備發出內存訪問的時候會
帶上這個PASID,SMMU根據PASID找見對應的CD。可以SMMU驅動需要先爲對應的設備或者
設備的獨立內存訪問單元建立STE或者CD,以及填充STE和CD中的域段以支持隨後設備的
內存訪問。

STE和CD裏包含頁表,和MMU一樣,爲了加快翻譯速度,SMMU也做了TLB。爲了加快STE和
CD查找的速度,SMMU裏也可能放STE和CD的cache。

SMMU的軟硬件控制接口,包括SMMU的基本的MMIO寄存器,三個硬件隊列。如上,這是三個
硬件隊列中command queue用於軟件向SMMU發送命令,event queue用於SMMU向軟件報異常
事件(包括缺頁),pri queue是和PCI設備配合一起用的,用於硬件向軟件上報外設的
page request請求。軟件可以通過command queue向硬件發命令,SMMU的命令基本上可以
分爲,配置無效化命令,比如無效掉SMMU cache的STE和CD;TLB無效化命令,缺頁相關
的命令,比如用於繼續stall請求的RESUME命令;prefetch命令;SYNC命令。

  1. SMMU驅動分析

SMMUv3相關的驅動的文件包括arm-smmu-v3.c, io-pgtable-arm.c,
drivers/perf/arm_smmuv3_pmu.c。第一個文件是smmu驅動的主體,第二個文件是和
頁表相關的操作,第三個文件是和SMMU PMU(PMCG)相關的東西。第一二個文件編譯出來
SMMU驅動,第三個文件編譯出SMMU PMCG驅動。

SMMU驅動是一個普通的平臺設備驅動。這驅動的probe函數裏初始化SMMU硬件,包括:
ACPI/DTS解析,中斷初始化,硬件特性解析,SMMU STE初始化,probe裏還把SMMU向
iommu子系統註冊,以及把iommu_ops回調函數註冊給SMMU結構和總線。

這其中涉及的SMMU驅動相關的一些數據結構包括:

  struct arm_smmu_device 描述一個物理的SMMU設備。
  struct fwnode_handle 描述struct iommu_device的固件描述。
  struct arm_smmu_master 描述SMMU物理設備所管理的一個外設, 這個外設可以對應一組
                         stream id, 但是一般是一個外設一個stream id。
  struct device
    +-> struct dev_iommu 一個外設device裏和iommu相關的東西
      +-> struct iommu_fwspec 一個外設device和iommu硬件相關的東西
        +-> struct fwnode_handle iommu_device的固件描述

probe裏的流程比較直白:

  arm_smmu_device_probe
    ...
    +-> arm_smmu_device_hw_probe 探測各種硬件配置信息
    +-> arm_smmu_init_structures 初始化cmd, event, pri隊列的內存, 初始化STE表,
                                 如果是兩級STE表,只初始化L1 STE
    +-> iommu_device_register 向iommu子系統註冊SMMU
    +-> arm_smmu_set_bus_ops 向pci,amba或者platform總線註冊iommu_ops

struct iommu_ops和具體iommu設備相關的回調函數需要通過上層的接口使用。
±> arm_smmu_add_device:
創建外設對應的arm_smmu_master結構, 創建外設對應的iommu_group。
外設和SMMU的關係怎麼傳給這個函數的?很明顯在這個函數調用之前外設device
之中的iommu_fwspece已經被賦值。這個賦值的地方在[1]中已經提到,就是
pci_device_add(這裏只看PCI設備的情況),基本邏輯是PCI設備在被加入系統中
的時候調用acpi_dma_configure找見它自己的RC,從而找見對應的SMMU。

但是,內核代碼後來修改了這部分,現在的內核代碼(v5.7-rc1),把xxx_dma_configure
移到了really_probe裏。已PCI總線爲例,這裏調用的是pci_dma_configure:

pci_dma_configure
  +-> acpi_dma_configure
    +-> iort_iommu_configure 這個函數裏創建device的iommu_fwspec
    +-> arch_setup_dma_ops 調到比如arm64的回調中
      +-> iommu_setup_dma_ops 在有iommu的情況下給device->dma_ops掛上iommu_dma_ops
      (諸如dma_alloc_coherent的dma接口在做dma相關的操作時,在有iommu的
       情況下,調用的就是這裏掛在device->dma_ops裏的各種回調函數)

上面說的是爲啥arm_smmu_add_device這個函數調用到的時候,device入參裏已經
有device->iommu_fwspec的域段,下面說arm_smmu_add_device這個函數怎麼調用到:

arm_smmu_add_device這個函數在iommu層裏被封裝, 然後註冊成總線的一個notifier:
smmu probe -> arm_smmu_set_bus_ops -> bus_set_iommu
    +-> iommu_bus_init
      +-> iommu_bus_notifier  BUS_NOTIFY_ADD_DEVICE會觸發
        +-> iommu_probe_device
	  +-> ops->add_device

如上pci_device_add的最後會調用devcie_add向bus添加外設device, 這個過程
會觸發以上notifier回調函數,最終調用到arm_smmu_add_device。注意,這裏
really_probe在後,觸發notifier執行在前。觸發notifier在設備加載的時候,
而really_probe在驅動加載的時候。

下面看add_device都做了什麼:
add_device
  +-> blocking_notifier_call_chain
  +-> bus_probe_device
        +-> device_initial_probe
      +-> __device_attach
        +-> __device_attch_driver 注意:如果驅動沒有加載不會再繼續下面的調用
	...
	  +-> really_probe

也就是說,一般先枚舉設備,再insmod驅動的場景,只有在驅動加載的時候纔會
生成device結構裏的iommu_fwspec。

但是,實際跟蹤執行流程,你會發現對於一個外設的arm_smmu_add_device調用是
發生在對應的設備驅動加載的時候。這是因爲smmu驅動加載比pci驅動晚,add_device
想要觸發notifier時,因爲smmu驅動沒有加載,notifier都還沒有註冊,自然也
不會在add_device這個流程中觸發arm_smmu_add_device這個函數。那麼really_probe
爲啥會最終調用到arm_smmu_add_device? 這是因爲在iort_iommu_configure裏
調用了iort_add_device_replay->iommu_probe_device。綜上struct device裏的
和iommu有關的struct dev_iommu,以及iommu_fwspec都是在iort_iommu_configure
裏創建的。

如最開始所述,這個arm_smmu_add_device創建特定外設在smmu處的各種描述信息。
其中包括,創建arm_smmu_master, 建立設備對應的STE entry,初始化設備的
pasid,pri功能,建立這個設備相關的iommu_group和iommu_domain, 在這個過程
中會調用smmu ops裏的device_group, domain_alloc以及attach_dev。

    架構設計上,iommu_group描述共享一個地址空間的設備的集合, iommu_domain
打包地址翻譯的類型以及爲具體的iomm_domain實現提供橋樑,比如arm_smmu_domain。
iommu_ops裏的有些操作,比如,map會最終調用到arm_smmu_domain->io_pagetable_ops->map

arm_smmu_add_device
  +-> iommu_group_get_for_dev
    +-> iommu_group_add_device
      +-> __iommu_attach_device
        +-> arm_smmu_attach_dev
裏創建iommu_group, iommu_domain, 並最終調用到arm_smmu_attach_dev。可以看到
先按全局變量iommu_def_domain_type創建domain, 再用iommu_domain_set_attr
補充設置domain的attr。一般domain的type有IDENTITY, UNMANAGED, DMA。IDENTITY
    是IOMMU不做處理,DMA是在內核裏使用IOMMU,UNMANAGED是把IOMMU的能力暴露出去
給其他模塊使用,現在一般是VFIO和SVA再用UNMANAGED domain。attr用來區分
domain裏更細一步的配置,比如,arm_smmu_domain_set_attr裏,對於DMA domain,
用attr區分non_strict mode; 對於UNMANAGED domain, 用attr區分Nested的使用
方式。可以看到UNMANAGED domain時, 如果只用一個stage,用的是stage1; 對於
內核的DMA流程,一般順着這個調用關係下來,但是,對於UNMANAGED domain和
Nested,iommu還有其他對外接口,提供配置的方法。

下面看arm_smmu_attach_dev裏具體幹了什麼。

+-> arm_smmu_attach_dev:
    這個函數的入參是,struct iommu_domain, struct device。如上,這裏的domain
表示的是一種使用方式。也就是說這裏是把一個device和一種使用方式建立起聯繫。
注意,上面的add_device是把一個外設加入到smmu這個系統裏來。

如上,我們可以看下iommu_domain的各種不同類型,以及內核裏的不同子系統是
怎麼調用這個函數的,以及這個函數到底做了什麼。

這個函數對於特定的設備,根據傳進來的domain類型, 初始化具體domain的相關
操作函數,然後配置STE生效:
arm_smmu_attach_dev
  +-> arm_smmu_domain_finalise
    +-> arm_smmu_domain_finalise_xxx[cd, s1, s2]
    +-> smmu_domain->pgtbl_ops = alloc_io_pgtable_ops
  +-> arm_smmu_install_ste_for_dev
可見arm_smmu_domain_finalise裏首先根絕smmu_domain裏stage的配置,去配置
smmu的CD,或者STE和s2相關的內容。然後,把對應的頁表操作函數賦值給smmu_domain
裏的回調函數: smmu_domain->pgtbl_ops。以後,iommu_ops裏的mmap,unmap函數
直接會調用到這裏。

iommu_attach_group, iommu_attach_device, iommu_group_add_device,
iommu_request_dm_for_dev, iommu_request_dma_domain_for_dev都會最終調用到
attach_dev這個回調。在vfio裏會使用到iommu_attach_group, iommu_attach_device,
vfio會用UNMANAGED domain。(具體邏輯: todo)
  1. IOMMU接口分析

DMA接口和IOMMU的關係在上面已給出。

目前社區正在上傳SVA的相關補丁,SVA的接口也是IOMMU子系統對外接口的一部分, 這
一部分的分析可以參見[5]。目前的SVA接口使用的是DMA domain, 通過SVA的接口觸發
在STE表裏建立SSID非零的CD表,從而可以實現一個外設,一部分在內核態使用(使用CD0),
一部風在用戶態使用(使用其他CD)。

VFIO重度使用IOMMU接口, 從這個角度看IOMMU子系統的對外接口參見[6]

  1. SMMU虛擬化分析

Nested SMMU的分析參見[4]。

Reference

[1] http://blog.csdn.net/scarecrow_byr/article/details/53844162
[2] https://blog.csdn.net/scarecrow_byr/article/details/52348234
[3] https://blog.csdn.net/scarecrow_byr/article/details/53844162
[4] https://blog.csdn.net/scarecrow_byr/article/details/104606571
[5] https://blog.csdn.net/scarecrow_byr/article/details/100983619
[6] https://blog.csdn.net/scarecrow_byr/article/details/51494401

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章