1. 程式人生 > >Intel VT-d(2)- DMA重定向

Intel VT-d(2)- DMA重定向

DMA重定向硬體一般位於Root Complex中,Root-Complex是PCIe系統中引入的概念,它將CPU、記憶體子系統和PCIe子系連線起來。如下圖所示:

而Root Complex則經常被整合到CPU晶片上、MCH(Memory Controller Hub)上或者是IOH(I/O hub)上。

DMA重定向硬體將來自於I/O子系統的記憶體訪問請求分為兩類:

  1. 不帶地址空間ID的請求(Request without Process Address Space Identifier,即Request-without_PASID),相當於GPA(Guest Physical Address),這是一般Endpoint裝置發出的記憶體訪問請求,這類請求通常會表明該請求的型別(讀、寫或原子操作),DMA目標的地址、大小和發起請求的源裝置的ID。
  2. 帶有地址空間ID的請求(Request with Process Address Space Identifier,即Request-with-PASID),相當於GVA(Guest Virtual Address),能夠發出這類請求的源PCI裝置需要擁有virtual address capability,該請求帶有額外的資訊用於定位目標地址空間和一些其他資訊。

不同虛擬機器之間的隔離是通過防止分配到其他虛擬機器的資源(CPU、I/O裝置)訪問到本虛擬機器的實體地址。每個虛擬機器都會有自己獨立的實體地址空間,即GPA(Guest Physical Address)空間,該空間不同於主機實體地址空間,即HPA(Host Physical Address)空間。DMA重定向硬體將從I/O裝置發過來的訪問請求中包含的地址看做是DMA地址,根據不同的使用配置,該DMA地址可能是GPA;可能是跟PASID(Process Address Space ID)相關的VA(application Virtual Address);也可能是由軟體定義的I/O虛擬地址(IOVA)。不管怎樣,DMA重定向硬體將DMA地址最終轉化為HPA(Host Physical Address)實現最終主機實體地址的訪問。

如下圖所示,系統中存在兩個Domain,1和2,也可以理解為存在兩個虛擬機器,這兩個虛擬機發出的記憶體請求通過在CPU上的記憶體管理單元(MMU),在x86 CPU上可以理解為分頁機制和EPT(Extended Page Table)的組合,將發出的記憶體請求地址最終轉化為主機的實體記憶體地址,即HPA,對應到主機實體記憶體上。而Device 1和2,則可以理解為分別分配給Domain 1和2的I/O裝置,雖然它們發出訪問請求的地址數值一樣,但是由於它們所屬的Domain不一樣,導致DMA Memory Management將會使用不同的地址轉換頁表,將其分別轉換到不同Domain所對應的HPA。VMM/Hypervisor負責對DMA Memory Management所使用的I/O地址轉換頁表進行建立和維護,同時需要對DMA重定向和I/O裝置進行配置,協商好使用什麼型別的地址,GPA或者GVA。

每個DMA重定向硬體的實現可以是一個硬體單元包含整個PCI Segment,也可以是多個硬體單元,每個硬體單元各自包含PCI Segment中的部分PCI裝置。系統的BIOS或者UEFI負責在系統啟動的時候對VT-d硬體進行檢測,並分配相應的地址空間,讓系統軟體能夠訪問到VT-d硬體及其配置暫存器。BIOS/ACPI以ACPI表的子表(DMAR:DMA Remapping Reporting ACPT Table)的形式將VT-d硬體資源描述出來,這樣VMM值需要找到DMAR表,就可以對相應的VT-d硬體進行訪問或配置了。DMAR表的組織形式以後再詳細講。

每個從I/O裝置往上傳輸經過重定向硬體的資料包都會包含source-id用於定位源I/O裝置,不同的I/O裝置其source-id的實現可能不一樣,對於PCI裝置而言,其source-id是傳輸層頭部中包含的requester Identifier,由Bus、Device、Function組成,其格式如下所示:

所謂的重定向就是對目標地址進行轉換或更改,DMA重定硬體向利用分層頁表結構對地址進行轉化。屬於不同Domain的I/O裝置請求需要被分配到不同的轉換頁表中,該索引、分配過程以I/O裝置請求的source-id為輸入源,對於PCI裝置發出的Request-without-PASID而言,它將會使用請求包中包含的PCI Bus、Device和Function號作為索引值。在進行這樣的索引之前,VMM需要在記憶體中建立好一個4KB的Root-table,該table包含256個entry,每個entry對應一個PCI Bus,每個Root-entry中包含一個指標,該指標指向一個4KB的Context-table,該Context-table中包含了256個entry,對應到該PCI Bus下的所有Device和Function。每個Context-entry都包含一個指向該PCI Function所對應的Domain的地址轉換頁表的指標。其結構如下圖所示:

當找到相應的地址轉換頁表後,硬體才開始正常的地址分級頁錶轉換,即所謂的page-walk,將請求中的GPA地址轉化為HPA,實現最終的實體記憶體訪問,該轉化過程即稱為Second-Level-Translation。Root-table的地址需要VMM在啟動VT-d硬體的時候,將Root-table的地址寫到VT-d硬體相應的暫存器(Root Table Address Register)上,為VT-d硬體提供一個入口。

對於Request-with-PASID而言,其請求中包含的地址轉換頁表入口索引,即(PCI、Bus、Device和Function值)是一樣的,但是其包含的請求地址型別不一樣,是GVA,而不是GPA,故需要現將GVA轉化為GPA(即為First-Level-Translation),然後再將得到的GPA轉化為HPA,即Second-Level-Translation,才能實現最終的實體記憶體訪問。

該方法需要有兩次索引地址轉換頁表的入口地址,所以需要用到Extended-root-table,該4KB的table每個Ext-root-entry中,包含兩部分,分別包含Upper-context-table的指標和Lower-contex-table的指標,Upper-context-table指標指向的是PASID-Table,PASID-Table則根據PASID來對該表進行索引,每個PASID-entry都包含一個指向分級頁表的入口,該分級頁表用於完成First-Level-Translation,即將請求中包含的GVA轉化為GPA。Sencond-Level-Translation分級頁表的所以和前面Request-without-PASID一樣。當Frist-Level-Translation完成後得到的GPA,將作為Second-Level-Translation作為輸入地址,最終得到HPA,完成主機實體記憶體的訪問。

不管是first-level translation (requests-with-PASID)還是second-level translation (request-without-PASID)。對於request-with-PASID而言,會先使用first-level translation table將DMA地址轉換為一個不帶PASID的地址,然後再將這個不帶PASID的地址作為second-level-translation table的輸入進行轉換得到最終的實體地址。查詢頁表完成地址轉換的方式就根普通的分頁機制完全一致。頁表的層數會根據具體使用的頁框大小而變化,頁框大小可以是4KB,2MB和1GB,下圖以4KB為例。

由此可見要完成一次DMA重定向訪問到真正的主機實體地址,中間會有很多的記憶體訪問,為了加快這些記憶體訪問,VT-d硬體中,會引入各種各樣的Cache加快這些實體地址的轉換,或者是主機實體記憶體的訪問。

歡迎關注同名微信公眾號“河馬虛擬化”第一時間獲取最新文章。