SMMUv3(2)

流的編號

做一次轉換需要地址、size以及相關屬性如讀/寫/安全域/非安全域/可共享性/可緩存性;如果超過1個client設備使用SMMU流量,那麼他們還要有StreamID來區分;StreamID在系統裏的構建傳送是具體實現決定的,邏輯上講,一個StreamID就關聯到一個發起轉換的設備。物理設備到StreamID的映射必須描述給系統軟件,ARM推薦StreamID用密集命名空間,從0開始;每個SMMU都是獨立的StreamID命名空間;一個設備可以不止使用一個StreamID觸發流量,可以用多個StreamID來區分設備不同的狀態。

StreamID用於從Stream table選出STE(Stream Table Entry),這個STE包含這個設備的配置;Stream table最大包含2的StreamIDSize條位於內存的配置數據結構。

另一個屬性,SubstreamID可能選擇性的提供給SMMU做一階段頁錶轉換;SubstreamID可以是0-20bit,用於區分組織自同一邏輯塊但去往不同的程序地址空間,比如說一個有8context的計算加速器,每個context可能映射到不同的用戶進程,但是隻有一個設備且通用的配置,因此必須將其分配給整個VM;SubstreamID等同於PCIE裏的PASID,因爲也用於非PCIE系統,所以給了一個更通用的名字;SubstreamID最大值20bit也與PCIE的PASID一致。

這些屬性和大小都可以通過SMMU_IDR1寄存器檢測到。

StreamID是識別一個transaction所有配置的關鍵,一個StreamID可以配置成bypass或要轉換的項目,而這就決定了要做1或2階段頁錶轉換。SubstreamID提供了一個修飾符,這個修飾符在StreamID指示的一組1階段轉換間做選擇,但對有StreamID選擇的2階段轉換沒有影響。僅實現2階段的SMMU不能接受SubstreamID,1階段的實現不需要支持子流,因此不需要Substream輸入。另外SMMU可以選擇是否實現安全和非安全兩個域,如果實現了,那麼SEC_SID標誌就是StreamID所屬的域的標誌;安全域的StreamID從安全域的Stream table中找STE,非安全域的StreamID從非安全域Stream table找STE。

ARM希望對於PCI,StreamID從PCI RequestID生成,這樣StreamID[15:0]就是RequestID的[15:0],如果一個SMMU後有超過1個RootComplex,ARM推薦將這16bit的RequestID命名空間編入更大的StreamID命名空間來管理,高bit位來區分連續的RequestID命名空間,這樣StreamID[N:16]就能指示哪個RootComplex是stream的源;在PCIE系統裏,SubstreamID應該直接從PASID一對一的對應而來。因此,實現與PCI client一起用的SMMU需要支持StreamID最少16bit。

 

數據結構和轉換過程

SMMU使用一組內存中的數據結構來定位翻譯數據。寄存器持有初始根數據結構的基地址,也就是Stream Table。一個包含2階段轉換表基指針的STE,同樣能定位1階段配置數據結構,而這數據結構就包含轉換表基地址。一個CD(Context Descriptor)就代表1階段翻譯,一個STE就代表2階段翻譯。因此SMMU使用兩個不同組的數據結構:配置結構,從一次轉換的StreamID映射到轉換表基指針、配置和翻譯表訪問的context;轉換表結構,用於表示VA-IPA和IPA-PA翻譯。

一次轉換的過程首先會定位這次轉換的配置,根據StreamID,SubstreamID;然後用這個配置來定位這個地址要用的翻譯。第一步是處理轉換的STE,STE可以告訴SMMU這次轉換需要其他什麼配置;概念上,一個STE描述一個client設備的配置,是依據它是1階段、2階段還是兩階段轉換;多個設備可以關聯到一個VM,因此多個STE可以共享同一個2階段翻譯表;同樣的多個設備(streams)可能共享同一個1階段配置,因此多個STEs可能共享同一個CD。

Stream Table查找

根據StreamID可以定位到一個STE,Stream table支持兩種格式,由Stream table基寄存器控制;當一次轉換髮起,StreamID會被檢查是否符合設置的表長,如果StreamID超出配置好的Stream table範圍,或是超出2級Stream表的跨度,則轉換中止;另外安全域相關的SEC_SID標誌也會在SMMU_S_IDR1.SECURE_IMPL==1時被檢查。

Stream table-線性Stream table

一個線性Stream table是一個連續的STE數組,從0開始,由StreamID索引,佔據空間大小可配置位2的n次方再乘上STE的size,最大爲SMMU在硬件中支持的StreamID爲的最大數目。n就是StreamID[n:0],如下圖:

兩級Stream table

就像是MMU的頁表一般,包含兩級,可以不用連續內存空間,並且節省內存空間,具體結構如下圖:

1級表有StreamID[n:SPT]索引,n是StreamID的位數,SPT是可配置的切分點,️由SMMU_(S_)STRTAB_BASE_CFG.SPLIT,2級表有StreamID[SPT-1:0]索引,依賴於各個2級表跨度。兩級Stream table的支持通過SMMU_IDR0.ST_LEVEL查到,如果支持,SPT(Split Point)可以用6,8,10;StreamID超過6bit的,也就是超過64個StreamID的,必須支持兩級Stream table。1級Stream表項包含一個指向2級Stream表的指針,同步的還有這個StreamID下這個表的跨度。每個Stream table項都可以標記爲invalid。

上圖是一個Split point設爲8的1級表項,StreamID有0-1023,4x8bit;StreamID的0-255由STE數組配置在0x1000;StreamID 256-259由STE數組配置在0x2f20;StreamID 512-767都是失效的;Stream_ID 768的STE位於0x4000。

一個split point爲8的兩級Stream table,可以比與PCIE同時使用的線性Stream table減少內存使用量;如果支持完整的256個PCIE總線號,則RequestID或StreamID空間爲16位,但是由於通常每個物理鏈路只有一條PCIE總線,而每條總線可能只有一個設備,因此在最壞的情況下,有效的StreamID可能沒256個StreamID僅出現一次。另外,Split point爲6則可提供64個2級STE,從而爲每個2級表使用4KB頁面。

StreamID到CD(Context Descriptors)

STE包含每個流的配置:來自設備的流量是否使能;是否爲1階段轉換;是否爲2階段轉換,以及相關的翻譯表;哪個數據結構定位1階段翻譯表。如果使用1階段,STE會指示1到多個內存中的地址。CD將StreamID與1階段翻譯表的基指針、此流的配置、ASID聯繫起來;如果使用了Substream,就會由多個CD指示多個1階段翻譯,每個CD指向1個substream。提供有SubstreamID的轉換在1階段翻譯關閉的情況下會被終止。

如果使用了2階段轉換,STE會包含2階段翻譯表的基指針和VMID;如果多個設備聯繫到了一個VM,這就意味着他們共享2階段翻譯表,多個STE可能映射到一個2階段翻譯表。ARM期望在有hypervisor時,Stream table和2階段轉換表應該有hypervisor管理,在Guest控制下的與設備相關的CD和1階段翻譯表應該由GuestOS管理,另外,hypervisor可以根據自己的要求使用分離的hypervisor 1階段翻譯。如果沒有hypervisor,裸金屬OS管理Stream table和CD。

當轉換時帶了SubstreamID,並且配置也使能了substream,SubstreamID就負責索引1階段翻譯context的CD,在這種配置裏,如果轉換時沒有帶SubstreamID,則其轉換行爲就會根據STE.S1DSS標誌位的值:0xb00表示所有流量都應有SubstreamID,沒有就會提錯誤,aborted,然後記錄事件;0b01表示沒有SubstreamID則bypass掉1階段翻譯;0b10表示沒有SubstreamID則用Substream 0的CD,但是當SubstreamID爲0時會abort並記錄事件。

CD和STE數據結構提供的ASID和VMID值標記TLB條目,這個TLB條目是從CD和STE的配置執行轉化查找並創建的;這些標記被用於查找識別不同流的地址空間,或者在接收到TLB維護操作的廣播時需要去匹配並失效掉相關條目;具體的實現可能同樣使用這些標記來允許識別不同流的翻譯表。

上圖給出了一個示例配置,StreamID從線性Stream table選出一個STE,這個STE指向一個2階段的翻譯表和一個1階段配置的CD,然後這個CD只想一個1階段轉換表。下圖給出了一個STE只想一個多CD數組的配置,根據輸入SubstreamID纔會選擇一個CD,也就是SubstreamID決定所使用的1階段翻譯表的情況。

下圖展示了一個更復雜的層次結構,用上了多級Stream table,其中一個STE指向了CD數組,第二個STE指向了單CD,第三個STE只想了一個多級CD表,基於此多級CD表,可以支持很多流和很多Substream而不需要物理連續的表內存。

一次轉換的處理需要一系列邏輯上的步驟:

1. 如果SMMU全局性的關閉了(如SMMU_CR0.SMMUEN==0),轉換就會對地址不做處理直通SMMU;全局屬性設置如內存類型、共享性可能還用SMMU的SMMU_GBPA寄存器的設置,當然可能SMMU_GBPA寄存器被配置成所有轉換都abort;

2. 如果SMMU沒關,配置就會由一下情況確定:

  a. STE被定位到

  b. STE是否使能2階段翻譯,STE還包含2階段轉換表基地址

  c. 如果STE使能1階段翻譯,定位到CD,如果STE裏2階段翻譯也使能,CD由IPA空間使用2階段翻譯得到,否則CD會從PA地址空間得到。

3. 有轉換表,且不是失效的

  a. 如果1階段翻譯被配置了,那麼CD就有翻譯表的基地址,然後去翻譯一遍;如果2階段翻譯也在STE裏使能了,這可能還需要2階段翻譯。如果1階段翻譯沒使能,那就直接bypass掉,然後輸入地址直接做2階段翻譯。

  b. 如果2階段翻譯被使能了,並且STE使能了1階段翻譯,那麼STE就包含一個1階段轉換表地址,然後就會嵌入一個1階段翻譯,然後得到IPA;如果2階段翻譯沒使能,2階段被bypass掉,那麼2階段轉換的輸入地址就被直接作爲輸出

4. 一個有效轉換意味着沒有翻譯失敗,並且會應用並轉發輸出地址以及相應的內存屬性。

具體的實現可能在上面這些步驟的任意位置做緩存;並且在這些步驟中的很多位置也可能產生轉換失敗,然後轉換就會終止;如果一次轉換沒有定位到有效的配置或是類型不支持轉換都會終止,然後報告事件;根據CD和STE配置,轉換在失敗時決定終止還是卡住並拋出軟件失敗結果。

 

配置和翻譯搜索:

下圖給出了配置搜索和翻譯搜索的概念:

一次轉換首先要搜索配置,然後SMMU決定如何開始翻譯此次轉換,這包含定位到STE,然後如果需要的話,在定位到CD;配置搜索階段與輸入地址無關,與SMMU全局寄存器配置,與要轉換的StreamID,與要轉換的SubstreamID(如果有的話)相關;配置的搜索結果是特定的stream或substream的配置,能定位翻譯相關配置,包含1階段轉換表基指針,ASID和與轉換表的解釋相關的屬性(如翻譯粒度);還包含2階段轉換表基指針,VMID和與解釋轉換表相關屬性;流相關的屬性,如異常等級、與PE相關翻譯域,還是要翻譯到PE的內存空間裏嘛。翻譯搜索階段與PE的差不多,最後就是輸出個物理地址,與輸入地址、Stream的安全域一場等級,ASID,VMID等。上圖就給出了一個類似PE的使用TLB的翻譯搜索步驟,ARM希望SMMU使用TLB來緩存翻譯結果,而不是每次轉換都要TTW,但是這不是固定的。

一次翻譯有StreamWorld屬性,表示轉換方式,直接等效於PE的一場等級;所有翻譯caches都標記了一個StreamWord,好在查找和失效的時候匹配;StreamWorld由插入和查找翻譯的配置定義,由STE的安全態:STE.Config、STE.STRW、SMMU_CR2.E2H組合定義;除了插入和查找,StreamWorld/異常等級/翻譯方式還會影響不同類型的TLB無效範圍。

寫到這裏,反正我自己已經認爲自己對SMMUv3的代碼會怎麼寫有了大致的感覺,本來研究SMMUv3就是要去解掉一個顯卡相關的BUG,雖然東西寫的亂七八糟,但我又自認爲總結完了,是時候去直面這個BUG了!

發佈了37 篇原創文章 · 獲贊 4 · 訪問量 3367
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章