背景
-
--By 魯迅Read the fucking source code!
-
--By 高爾基A picture is worth a thousand words.
說明:
- KVM版本:5.9.1
- QEMU版本:5.0.0
- 工具:Source Insight 3.5, Visio
1. 概述

- KVM虛拟化離不開底層硬體的支援,本文将介紹ARMv8架構處理器對虛拟化的支援,包括記憶體虛拟化、中斷虛拟化、I/O虛拟化等内容;
- ARM處理器主要用于移動終端領域,近年也逐漸往伺服器領域靠攏,對虛拟化也有了較為完善的支援;
-
軟體,涵蓋的功能包括:記憶體管理、裝置模拟、裝置配置設定、異常處理、指令捕獲、虛拟異常管理、中斷控制器管理、排程、上下文切換、記憶體轉換、多個虛拟位址空間管理等;Hypervisor
- 本文描述的ARMv8虛拟化支援,對于了解
下的代碼很重要,脫離硬體去看Architecture-Specific代碼,那是耍流氓;arch/arm64/kvm
開始旅程!
2. ARMv8虛拟化
2.1 Exception Level
- ARMv7之前的架構,定義了一個處理器的異常處理模式,比如
等,各個異常模式所處的特權級不一樣,比如USR, FIQ, IRQ, SVC, ABT, UND, SYS, HYP, MON
模式的特權級就為USR
,對應為使用者态程式運作;PL0
- 處理器的異常模式可以在特權級軟體控制下進行主動切換,比如修改
寄存器,也可以被動進行異常模式切換,典型的比如中斷來臨時切換到CPSR
;IRQ模式
ARMv7處理器的異常模式如下表所示:
然鵝,到了ARMv8,
Exception Level(EL)
取代了特權級,其中處理器的異常模式與
Exception Level
的映射關系如下圖:
- 當異常發生時,處理器将改變
(相當于ARMv7中的處理器模式切換),來處理異常類型;Exception Level
- 圖中可以看出
運作在Hypervisor
,而EL2
Guest OS
,可以通過EL1
指令向HVC (Hypervisor Call)
請求服務,響應虛拟化請求時就涉及到了Hypervisor
的切換;Exception Level
2.2 Stage 2 translation
Stage 2轉換
與記憶體虛拟化息息相關,這部分内容不僅包括正常的記憶體映射通路,還包含了基于記憶體映射的I/O(
MMIO
)通路,以及系統記憶體管理單元(
SMMUs
)控制下的記憶體通路。
2.2.1 記憶體映射
OS在通路實體記憶體前,需要先建立頁表來維護虛拟位址到實體位址的映射關系,看過之前記憶體管理分析的同學應該熟悉下邊這張圖,這個可以認為是
Stage 1轉換
:
- 當有了虛拟機時,情況就不太一樣了,比如Qemu運作在Linux系統之上時,它隻是Linux系統的一個使用者程序,
所認為自己通路的實體位址,其實是Linux的使用者程序虛拟位址,到最終的實體位址還需要進一步的映射;Guest OS
-
可以通過Hypervisor
來控制虛拟機的記憶體視圖,控制虛拟機是否可以通路某塊實體記憶體,進而達到隔離的目的;Stage 2轉換
- 整個位址的映射分成了兩個階段:
-
,作業系統控制Stage 1: VA(Virutal Address) -> IPA(Intermediate Physical Address)
;Stage 1轉換
-
,Stage 2: IPA(Intermediate Physical Address) -> PA(Physical Address)
控制Hypervisor
Stage 2轉換
-
-
與Stage 2轉換
轉換機制很類似,不同點在于Stage 1
時判斷記憶體類型是normal還是device時,是存放進頁表資訊裡了,而不是通過Stage 2轉換
寄存器來判斷;MAIR_ELx
- 每個虛拟機(VM,Virtual Machine)都會配置設定一個
,用于辨別VMID
所屬的VM,允許在TLB中同時存在多個不同VM的轉換;TLB entry
- 作業系統會給應用程式配置設定一個
,也可以用于辨別ASID(Address Space Identifier)
,屬于同一個應用程式的TLB entry
都有相同的TLB entry
,不同的應用程式可以共享同一塊ASID
。每個VM都有自己的TLB緩存
空間,通常會結合ASID
和VMID
來同時使用;ASID
-
Stage 1
的轉換頁表中,都包含了屬性的相關裝置,比如通路權限,存儲類型等,在兩級轉換的過程中,Stage 2
會整合成一個最終的也有效值,選擇限制更嚴格的屬性,如下圖:MMU
- 圖中的
屬性限制更嚴格,則選擇Device
類型;Device
-
如果想要改變預設整合行為,可以通過寄存器Hypervisor
來配置,比如設定HCR_EL2(Hypervisor Configuration Register)
Non-cacheable
等特性;Write-Back Cacheable
2.2.2 MMIO(Memory-Mapped Input/Output)
MMIO(Memory-Mapped Input/Output)
Guest OS
認為的實體位址空間,實際是
IPA
位址空間,就像真實實體機中一樣,
IPA
的位址空間,也分成記憶體位址空間和
I/O
位址空間:
- 通路外設有兩種情況:1)直通通路真實的外設;2)觸發
fault
通過軟體來模拟;Hypervisor
-
VTTBR_EL2
,虛拟轉換表基位址寄存器,存放Virtualization Translation Table Base Register
的頁表;Stage 2轉換
- 為了模拟外設,
需要知道通路的是哪個外設以及通路的寄存器,讀通路還是寫通路,通路長度是多少,使用哪些寄存器來傳送資料等。Hypervisor
有一個專門的Stage 2轉換
寄存器,用于捕獲Hypervisor IPA Fault Address Register, EL2(HPFAR_EL2)
過程中的fault;Stage 2轉換
軟體模拟外設的示例流程如下:
- 1)虛拟機VM中的軟體嘗試通路序列槽裝置;
- 2)通路時
被block住,并觸發abort異常路由到Stage 2轉換
。異常處理程式查詢EL2
寄存器關于異常的資訊,如通路長度、目标寄存器,Load/Store操作等,異常處理程式還會查詢ESR_EL2(Exception Syndrome Register)
寄存器,擷取abort的IPA位址;HPFAR_EL2
- 3)
通過Hypervisor
ESR_EL2
裡的相關資訊對相關虛拟外圍裝置進行模拟,完成後通過HPFAR_EL2
指令傳回給ERET
,從發生異常的下一條指令繼續運作;vCPU
2.2.3 SMMUs(System Memory Management Units)
SMMUs(System Memory Management Units)
通路記憶體的另外一種case就是DMA控制器。
非虛拟化下DMA控制器的工作情況如下:
- DMA控制器由核心的驅動程式來控制,能確定作業系統層面的記憶體的保護不會被破壞,使用者程式無法通過DMA去通路被限制的區域;
虛拟化下DMA控制器,VM中的驅動直接與DMA控制器互動會出現什麼問題呢?如下圖:
- DMA控制器不受
的限制,會破壞VM的隔離性;Stage 2轉換
- Guest OS以為的實體位址是IPA位址,而DMA看到的位址是真實的實體位址,兩者的視角不一緻,為了解決這個問題,需要捕獲每次VM與DMA控制器的互動,并提供轉換,當記憶體出現碎片化時,這個處理低效且容易引入問題;
SMMUs
可以用于解決這個問題:
-
也叫SMMU
,對IO部件提供MMU功能,虛拟化隻是SMMU的一個應用;IOMMU
-
可以負責對Hypervisor
進行程式設計,以便讓上層的控制器和虛拟機VM以同一個視角對待記憶體,同時也保持了隔離性;SMMU
2.3 Trapping and emulation of Instructions
Hypervisor
也需要具備捕獲(
trap
)和模拟指令的能力,比如當VM中的軟體需要配置底層處理器來進行功耗管理或者緩存一緻性操作時,為了不破壞隔離性,
Hypervisor
就需要捕獲操作并進行模拟,以便不影響其他的VM。如果設定了捕獲某個操作時,當該操作被執行時會向更高一級的
Exception Level
觸發異常(比如
Hypervisor
為EL2),進而在相應的異常進行中完成模拟。
例子來了:
- 在ARM處理器中執行
指令,可以讓CPU處于一個低功耗的狀态;WFI(wait for interrupt)
-
,當該寄存器的HCR_EL2(Hypervisor Control Register)
時,vCPU執行TWI==1
指令會觸發EL2異常,進而WFI
可以對其進行模拟,将任務排程到另外一個vCPU即可;Hypervisor
捕獲(
traps
)的另一個作用是可以用于向Guest OS呈現寄存器的虛拟值,如下:
-
寄存器用于查詢處理器對記憶體系統相關特性的支援,系統可能在啟動階段會讀取該寄存器,ID_AA64MMFR0_EL1
可以向Guest OS呈現一個不同的虛拟值;Hypervisor
- 當vCPU讀取該寄存器時,觸發異常,
在Hypervisor
中進行處理,設定一個虛拟值,并最終傳回給vCPU;trap_handler
-
來虛拟化一個操作需要大量的計算,包括觸發異常、捕獲,模拟、傳回等一系列操作,像trap
寄存器通路并不頻繁,這種方式問題不大。但是當需要頻繁通路的寄存器,比如ID_AA64MMFR0_EL1
MIDR_EL1
等,出于性能的考慮,應該避免陷入到MPIDR_EL1
中進行模拟處理,可以通過其他機制,比如提供Hypervisor
VPIDR_EL2
寄存器,在進入VM前就設定好該值,當讀取VMIDR_EL2
MIDR_EL1
時,硬體就傳回MPIDR_EL1
VPIDR_EL2
的值,避免了陷入處理;VMIDR_EL2
2.4 Virtualizing exceptions
-
對虛拟中斷的處理比較複雜,Hypervisor
本身需要機制來在EL2進行中斷,還需要機制來将外設的中斷信号發送到目标虛拟機VM(或vCPU)上,為了使能這些機制,ARM體系架構包含了對虛拟中斷的支援(vIRQs,vFIQs,vSErrors);Hypervisor
- 處理器隻有在EL0/EL1執行狀态下,才能收到虛拟中斷,在EL2/EL3狀态下不能收到虛拟中斷;
-
通過設定Hypervisor
寄存器來控制向EL0/EL1發送虛拟中斷,比如為了使能vIRQ,需要設定HCR_EL2
,設定後便會将實體中斷發送至EL2,然後使能将虛拟中斷發送至EL1;HCR_EL2.IMO
有兩種方式可以産生虛拟中斷:1)在處理器内部控制
HCR_EL2
寄存器;2)通過GIC中斷控制器(v2版本以上);其中方式一使用比較簡單,但是它隻提供了産生中斷的方式,需要
Hypervisor
來模拟VM中的中斷控制器,通過捕獲然後模拟的方式,會帶來overhead,當然不是一個最優解。
讓我們來看看
GIC
吧,看過之前中斷子系統系列文章的同學,應該見過下圖:
-
可以将GIC中的Hypervisor
映射到VM中,進而允許VM中的軟體直接與GIC進行通信,Virtual CPU Interface
隻需要進行配置即可,這樣可以減少虛拟中斷的overhead;Hypervisor
來個虛拟中斷的例子吧:
- 外設觸發中斷信号到GIC;
- GIC産生實體中斷
或者IRQ
信号,如果設定了FIQ
,中斷信号将被路由到HCR_EL2.IMO/FMO
Hypervisor
會檢查中斷信号轉發給哪個Hypervisor
vCPU
-
設定GIC,将該實體中斷信号以虛拟中斷的形式發送給某個Hypervisor
,如果此時處理器運作在EL2,中斷信号會被忽略;vCPU
-
将控制權傳回給Hypervisor
vCPU
- 處理器運作在EL0/EL1時,虛拟中斷會被接受和處理
- ARMv8處理器中斷屏蔽由
中的比特位來控制(比如PSTATE
),虛拟化時比特位的作用有些不一樣,比如設定PSTATE.I
時,表明實體IRQ路由到EL2,并且對EL0/EL1開啟HCR_EL2.IMO
,是以,當運作在EL0/EL1時,vIRQs
比特位針對的是虛拟PSTATE.I
而不是實體的vIRQs
。pIRQs
2.5 Virtualizing the Generic Timers
先來看一下SoC的内部:
簡化之後是這樣的:
- ARM體系架構每個處理器都包含了一組通用定時器,從圖中可以看到兩個子產品:
Comparators
,當Counter Module
的值小于等于系統的count值時便會産生中斷,我們都知道在作業系統中Comparators
的中斷就是系統的脈搏了;timer
下圖展示虛拟化系統中運作的
vCPU
的時序:
- 實體時間4ms,每個
運作2ms,如果設定vCPU
vCPU0
之後的3ms後産生中斷,那希望是實體時間的3ms後(也就是T=0
的虛拟時間2ms)産生中斷,還是虛拟時間3ms後産生中斷?ARM體系結構支援這兩種設定;vCPU0
-
上的軟體可以同時通路兩種時鐘:vCPU
EL1實體時鐘
EL1虛拟時鐘
EL1實體時鐘
EL1虛拟時鐘
-
與系統計數器子產品直接比較,使用的是EL1實體時鐘
時間;wall-clock
-
與虛拟計數器比較,而虛拟計數器是在實體計數器上減去一個偏移;EL1虛拟時鐘
-
負責為目前排程運作的Hypervisor
指定對應的偏移,這種方式使得虛拟時間隻會覆寫vCPU
實際運作的那部分時間;vCPU
來一張示例圖:
- 6ms的時間段裡,每個
運作3ms,vCPU
可以使用偏移寄存器來将Hypervisor
的時間調整為其實際的運作時間;vCPU
2.6 Virtualization Host Extensions(VHE)
- 先抛出一個問題:通常
的核心都運作在EL1,而控制虛拟化的代碼運作在EL2,這就意味着傳統的上下文切換,這個顯然是比較低效的;Host OS
-
用于支援VHE
的type-2
,這種擴充可以讓核心直接跑在EL2,減少host和guest之間共享的系統寄存器數量,同時也減少虛拟化的overhead;Hypervisor
VHE
由系統寄存器
HCR_EL2
E2H
TGE
兩個比特位來控制,如下圖:
VHE
的引入,需要考慮虛拟位址空間的問題,如下圖:
- 我們在記憶體子系統分析時提到過虛拟位址空間的問題,分為使用者位址空間(
)和核心位址空間(EL0
),兩者的區域不一緻,而在EL1
隻有一個虛拟位址空間區域,這是因為EL2
不支援應用程式,是以也就不需要分成核心空間和使用者空間了;Hypervisor
-
虛拟位址空間也同時支援EL0/EL1
ASID(Address Space Identifiers)
不支援,原因也是EL2
不需要支援應用程式;Hypervisor
從上兩點可以看出,為了支援
Host OS
能運作在
EL2
,需要添加一個位址空間區域,以及支援
ASID
,設定
HCR_EL2.E2H
的寄存器位可以解決這個問題,如下圖:
Host OS
EL2
需要解決的另一個問題就是寄存器通路重定向,在核心中需要通路
EL1
的寄存器,比如
TTBR0_EL1
,而當核心運作在
EL2
時,不需要修改核心代碼,可以通過寄存器的設定來控制通路流,如下圖:
- 重定向通路寄存器引入一個新的問題,
在某些情況下需要通路真正的Hypervisor
寄存器,ARM架構引入了一套新的别名機制,以EL1
結尾,如下圖,可以在_EL12/_EL02
ECH==1
通路EL2
TTBR0_EL1
Host OS
EL2
還需要考慮異常處理的問題,前邊提到過
HCR_EL2.IMO/FMO/AMO
的比特位可以用來控制實體異常路由到
EL1/EL2
。當運作在
EL0
且
TGE==1
時,所有實體異常都會被路由到
EL2
(除了SCR_EL3控制的),這是因為
Host Apps
EL0
Host OS
EL2
2.7 總結
- 本文涉及到記憶體虛拟化(stage 2轉換),I/O虛拟化(包含了SMMU,中斷等),中斷虛拟化,以及指令
等内容;trap and emulation
- 基本的套路就是請求虛拟化服務時,路由到
去處理,如果有硬體支援的則硬體負責處理,否則可以通過軟體進行模拟;EL2
- 盡管本文還沒涉及到代碼分析,但是已經大概掃了一遍了,大體的輪廓已經了然于胸了,說了可能不信,我現在都有點小興奮了;
參考
《ArmV8-A virtualization.pdf》
《vm-support-ARM-may6-2019.pdf》
《aarch64_virtualization_100942_0100_en.pdf》
《ARM Cortex-A Series Programmer's Guide for ARMv8-A》
arm64: Virtualization Host Extension support
歡迎關注個人公衆号,不定期更新技術文章。
作者:LoyenWang
出處:https://www.cnblogs.com/LoyenWang/
公衆号:
LoyenWang版權:本文版權歸作者和部落格園共有
轉載:歡迎轉載,但未經作者同意,必須保留此段聲明;必須在文章中給出原文連接配接;否則必究法律責任