中断处理的那些事儿
繼續(xù)“那些事兒”系列,這次的主題是Intel的中斷處理。參考的資料主要來自Intel文檔第三冊的第六、第十和第二十九章節(jié),以及這篇文章。其中,有一部分的內(nèi)容來自于上面提到的那篇文章。
以下內(nèi)容主要圍繞下面五個問題來展開:
- 第一,中斷是什么,種類有哪些?
- 第二,中斷是如何被發(fā)送給CPU的?
- 第三,CPU是如何對接收到的中斷進行處理的?
- 第四,中斷的優(yōu)先級問題?
- 第五,虛擬化環(huán)境對中斷提供了哪些支持?
接下來會對它們一一進行解答。
中斷是什么,種類有哪些?
我們經(jīng)常會將中斷(Interrupt)和異常(Exception)混在一起進行說明,可以說,它們有相似的地方,也有不同的地方。相似的地方在于,它們都是因為系統(tǒng)發(fā)生了某些事件,使得處理器需要暫停當前的執(zhí)行流,從而抽出精力(進到某個預先設定好的路徑中)來處理這些事件。不同的地方在于它們的來源,以及從事件發(fā)生到最終被處理的整條路徑都是不一樣的。這篇博文主要關注中斷,所以對異常就不去詳談了。
一般來說,中斷主要是由一些硬件設備產(chǎn)生的,表示這些硬件有一些重要的事件需要通知處理器,比如某些從外部設備請求的數(shù)據(jù)準備好了,需要通知處理器對其進行讀取等。當然這里所謂的“一般來說”是指也可以通過軟件的方式來觸發(fā)中斷,比如調(diào)用INT n指令,當然這種方式產(chǎn)生的中斷和通過意見產(chǎn)生的中斷最終的處理方式會有很大的不同。 因此從種類來分,可以將中斷分為通過硬件產(chǎn)生的外部中斷(External interrupt)和通過軟件產(chǎn)生的軟件中斷(Software interrupt)。不管是外部中斷還是軟件中斷,每個中斷都有一個中斷號與之對應,對于外部中斷來說,可使用的中斷號范圍從16到255(0到15)為系統(tǒng)預留的中斷號,而對于軟件中斷來說,可使用的中斷號為0到255。除此之外,16到255范圍內(nèi)的中斷是可以通過EFLAGS中的IF flag進行disable的,如果EFLAGS中的IF flag被清零,則表示當前CPU不接受這個范圍內(nèi)的中斷,如果其被置為1,則表示當前CPU可以正常處理這個范圍內(nèi)的中斷。
中斷是如何被發(fā)送給CPU的?
中斷在進入CPU之前,首先會進入一個被稱為Advanced Programmable Interrupt Controller(APIC)的控制器中,可以說,每個CPU都有一個APIC,被稱為該CPU的Local APIC(LAPIC)。每個LAPIC由一系列的寄存器組成,這些寄存器控制了LAPIC如何將中斷送到處理器中。而根據(jù)實現(xiàn)的不同,對這些寄存器的訪問方式也不一樣,比如,對于傳統(tǒng)的APIC和xAPIC來說,這些寄存器都是被映射在內(nèi)存中的,可以直接通過內(nèi)存讀寫的方式對其進行訪問,而對于x2APIC來說,需要通過訪問MSR的方式來訪問這些寄存器,具體的地址和訪問方法可以查看Intel文檔。
下圖展示了一個LAPIC的架構,里面包含了各種各樣的寄存器,大部分的功能都可以通過查詢Intel文檔獲得,其中有幾個特別重要的寄存器:In-Service Register (ISR),Interrupt Request Register (IRR),EOI Register,Task Priority Register (TPR),Processor Priority Register (PPR),Interrupt Command Register (ICR),Local Vector Table (LVT)。這些會在接下來的篇幅中一一進行介紹。
對于目前的LAPIC來說,它可能從以下幾個來源接收到中斷:
- Locally connected I/O devices:這個主要是指通過local interrupt pins (LINT0 and LINT1)直接和處理器相連的I/O設備;
- APIC timer generated interrupts:LAPIC可以通過編程設置某個counter,在固定時間內(nèi)向處理器發(fā)送中斷;
- Performance monitoring counter interrupts:這個是指處理器中的性能計數(shù)器在發(fā)生overflow事件的時候向處理器發(fā)送中斷進行通知;
- Thermal Sensor interrupts:這個是由溫度傳感器觸發(fā)的中斷;
- APIC internal error interrupts:這個是LAPIC內(nèi)部錯誤觸發(fā)的中斷;
- Externally connected I/O devices:這個是指外部設備通過IOAPIC和LAPIC進行相連;
- Inter-processor interrupts (IPIs):這個是指處理器之間通過IPI的方式進行中斷的發(fā)送和接收。
其中,前面五種中斷來源被稱為本地中斷源(local interrupt sources),LAPIC會預先在Local Vector Table (LVT)表中設置好相應的中斷遞送(delivery)方案,在接收到這些本地中斷源的時候根據(jù)LVT中的方案對相關中斷進行遞送。
除此之外,對于從IOAPIC中發(fā)送過來的外部中斷,以及從其它處理器中發(fā)過來的IPI中斷,LAPIC會直接將該中斷交給本地的處理器進行處理。而如果需要向其它處理器發(fā)送IPI,則可以通過寫LAPIC中的ICR寄存器完成。這部分這里就不詳述,直接看文檔就可以了。
那么我們現(xiàn)在就來看看當一個外部設備產(chǎn)生中斷,到這個中斷被發(fā)送給相應的CPU,這中間都會經(jīng)歷些什么過程。
在IOAPIC內(nèi)部,有一個非常重要的數(shù)據(jù)結構,叫做可編程重定向表(Programmable Redirection Table,PRT),在PRT表中,包含了若干個重定向表項(Redirection Table Entry,RTE),每個RTE對應一個中斷管腳,比如,典型的IOAPIC可能包含24個中斷管腳,相應的PTR表中就有24個與之相對應的RTE。 通常情況下,每個外部設備都會通過特定的管腳和IOAPIC相連,中斷產(chǎn)生之后,會通過該管腳進入IOAPIC,而當IOAPIC的某個管腳接收到中斷信號后,會根據(jù)該管腳對應的一個RTE,格式化出一條中斷消息,發(fā)送給某個(或多個)處理器的LAPIC。下表列出了RTE的格式:
| 63:56 | Destination Field,目的字段,R/W(可讀寫)。根據(jù) Destination Filed(見下)值的不同,該字段值的意義不同,它有兩個意義:Physical Mode(Destination Mode 為 0 時 ): 其值為 APIC ID,用于標識一個唯一的 APIC。Logical Mode(Destination Mode 為 1 時):其值根據(jù) LAPIC 的不同配置,代表一組CPU。 |
| 55:17 | Reserved,預留未用。 |
| 16 | Interrupt Mask,中斷屏蔽位,R/W。置一時,對應的中斷管腳被屏蔽,這時產(chǎn)生的中斷將被忽略。清零時,對應管腳產(chǎn)生的中斷被發(fā)送至LAPIC。 |
| 15 | Trigger Mode,觸發(fā)模式,R/W。指明該管腳的的中斷由什么方式觸發(fā)。1:Level,電平觸發(fā);2:Edge,邊沿觸發(fā)。 |
| 14 | Remote IRR,遠程 IRR,RO(只讀)。只對level觸發(fā)的中斷有效,當該中斷是edge觸發(fā)時,該值代表的意義未定義。當中斷是level觸發(fā)時,LAPIC接收了該中斷,該位置一,LAPIC寫EOI 時,該位清零。 |
| 13 | Interrupt Input Pin Polarity(INTPOL),中斷管腳的極性,R/W。指定該管腳的有效電平是高電平還是低電平。0:高電平;1:低電平。 |
| 12 | Delivery Status,傳送狀態(tài),RO。0:IDEL,當前沒有中斷;1:Send Pending,IOAPIC 已經(jīng)收到該中斷,但由于某種原因該中斷還未發(fā)送給LAPIC |
| 11 | Destination Mode,目的地模式,R/W。0:Physical Mode,解釋見 Destination Field;1:Logical Mode,同上。 |
| 10:8 | Delivery Mode,傳送模式,R/W。用于指定該中斷以何種方式發(fā)送給目的 APIC,各種模式需要和相應的觸發(fā)方式配合。詳見Intel文檔。 |
| 7:0 | Interrupt Vector,中斷向量,R/W。指定該中斷對應的vector,范圍從10h到FEh。 |
從上表我們可以看出,該消息包含了一個中斷的所有信息。其中Destination field和Destination mode定義了該中斷將被遞送的目標處理器。 從IOAPIC到LAPIC有兩種可能的路徑,如下圖所示:第一種是通過系統(tǒng)總線(System bus),該種路徑實現(xiàn)在Pentium 4和Intel Xeon系列的處理器上;第二種是通過APIC bus,這種路徑實現(xiàn)在Pentium and P6家族的處理器上。至于它們有什么區(qū)別,還是去看文檔的解釋吧。
總之,外部設備產(chǎn)生的中斷最終通過IOAPIC被遞送到了某個(或者多個)處理器中的LAPIC中。接下來,就要看LAPIC是如何將這些中斷遞送給處理器進行處理了。
CPU是如何對接收到的中斷進行處理的?
LAPIC無論是接收到來自IOAPIC的中斷,來自本地中斷源的中斷,還是來自其他處理器發(fā)送的IPI中斷,都會將其交由CPU進行處理,但是由于CPU這個時候可能正在處理其它中斷,所以需要一套機制來保證中斷處理的安全性。
首先需要注意的是,在RTE格式那張表中,中斷的delivery mode可能有好幾種,其中NMI、SMI、INIT、ExtINT和SIPI這幾種delivery mode的中斷將會直接交由CPU進行處理,如果當前CPU正在處理這些delivery mode的中斷,則會禁止相同的中斷被遞送進來。除此之外,還有一種被稱為fixed的delivery mode,也就是普通的中斷,它們的遞送機制是通過IRR和ISR寄存器完成的。在X86平臺上,這兩個都是256bits的寄存器(其實是由8個64bits的寄存器組成的),每個bit代表一個中斷的vector,其中第0到第16個bit是reserve的。IRR和ISR每個bit代表的意思分別如下:
- IRR:如果第n位的bit被置上,則代表LAPIC已接收vector為n的中斷,但還未交CPU處理。
- ISR:如果第n位的bit被置上,則代表CPU已開始處理vector為n的中斷,但還未完成。
需要注意的是,當CPU正在處理某中斷時,如果又被遞送過來一個相同vector的中斷,則相應的IRR bit會再次置一; 如果某中斷被pending在IRR中,同類型的被再次遞送過來,則ISR中相應的bit被置一。 這說明在APIC系統(tǒng)中,同一類型中斷最多可以被計數(shù)兩次。
另外,當某個中斷被處理完之后,LAPIC需要通過軟件寫EOI寄存器來告知。
因此,根據(jù)處理器的不同,一個典型的LAPIC中斷處理流程是這樣的:
對于Pentium4和Xeon系列:
- 通過中斷消息的destination field字段,確定該中斷是否是發(fā)送給自己的;
- 如果該中斷的delivery mode為 NMI、SMI、INIT、ExtINT、SIPI,直接交由CPU處理;
- 如果不為以上所列舉的中斷,則將IRR中相應的bit置一;
- 當中斷被pending到IRR寄存器中后,根據(jù)TPR和PPR寄存器,判斷當前最高優(yōu)先級的中斷是否能發(fā)送給CPU進行處理,并將ISR寄存器中相應的bit置一;
- 軟件寫EOI寄存器通知中斷處理完成。如果中斷為level觸發(fā),該EOI廣播到所有IOAPIC,NMI、SMI、INIT、ExtINT、SIPI類型中斷無需寫EOI。
對于Pentium系列和P6架構:
- 確定該中斷是否由自己接收。如果是一個IPI,且delivery mode為lowest priority,LAPIC與其它LAPIC一起仲裁該IPI由誰接收。
- 若該中斷由自己接收,且類型為NMI、SMI、INIT、ExtINIT、INIT-deassert、或MP協(xié)議中的IPI中斷(BIPI、FIPI、SIPI),直接交由CPU處理。
- 將中斷pending到IRR或ISR寄存器,若已有相同的的中斷pending在IRR和ISR寄存器上,拒絕該中斷消息,并通知sender “retry”。
- 同Pentium4、Xeon系列流程。
- 同Pentium4、Xeon系列流程。
在上面的這兩套流程中,涉及到幾個關鍵的寄存器(TPR,PRR)和delivery mode(lowest priority),這就涉及到中斷的優(yōu)先級問題了,會在“中斷的優(yōu)先級問題”中進行解釋。
當CPU開始處理中斷的時候,會查詢一個被稱為中斷描述符表(Interrupt Descriptor Table,IDT)的數(shù)據(jù)結構,該數(shù)據(jù)結構的每一項都被預先填上了一個門描述符(gate descriptor),其中有三種門描述符:task, interrupt和trap,這里我們主要關注的是interrupt-gate descriptor。下圖顯示了interrupt gate的相關信息:
通過它,就可以找到相應vector的中斷的處理函數(shù)了。在進入處理函數(shù)之前,一般會對棧進行一個切換,并且將相應的寄存器信息(包括RFLAGS, CS, RIP等)壓入棧中,從而保證在中斷處理結束之后可以恢復相關信息。切換棧和保存相關信息的過程如下圖所示:
主要包括兩種情況,第一種情況是被中斷的進程不是內(nèi)核進程,則需要有一個權限級別的切換,因此需要換一個棧;第二種情況是被中斷的進程是一個內(nèi)核進程,因此不需要切換棧,只需要在原來的棧中保存信息就可以了。整個流程還是比較清楚的,因此這里也不詳述了。
中斷的優(yōu)先級問題?
就像之前提到的,中斷是有優(yōu)先級概念的,具體體現(xiàn)在優(yōu)先級高的中斷會被先遞送給CPU進行處理,而優(yōu)先級低的中斷往往需要在優(yōu)先級高的中斷被處理完之后才會被處理。為了簡單起見,中斷的優(yōu)先級是由中斷本身的vector信息來得到的。
我們知道每個中斷都有一個vector與之對應,而中斷的優(yōu)先級別由下列公式得到:
優(yōu)先級別 = vector / 16因此,16~255號vector的中斷構成了1~15共15個優(yōu)先級別。而對于同一個級別的中斷,vector號越大的優(yōu)先級越高。例如vector33、34都屬于級別2,34的優(yōu)先級就比33 高。所以,對于8bit的vector,又可以劃分成兩部分,高4bit表示中斷優(yōu)先級別,低4bit表示該中斷在這一級別中的位置。
除此之外,LAPIC中還有兩個寄存器是和優(yōu)先級相關的,它們分別是任務優(yōu)先級寄存器(task priority register, TPR)和處理器優(yōu)先級寄存器(processor priority register, PPR)。
其中,TPR確定當前CPU可處理什么優(yōu)先級別范圍內(nèi)的中斷。具有如下的格式:
TPR寄存器接收0~15共16個值,對應16個CPU規(guī)定的中斷優(yōu)先級級別,值越大優(yōu)先級越高。CPU只處理比TPR中值優(yōu)先級別更高的中斷。例如TPR中值為8,則級別小于等于8的中斷被屏蔽(注意,屏蔽不代表拒絕,LAPIC 接收它們,把它們pending到IRR中,但不交CPU處理)。值15表示屏蔽所有中斷;值0表示接收所有中斷,這也是Linux為TPR設置的默認值。注意,TPR是由軟件讀/寫的,硬 件不更改它。因此,TPR的值增加 1,將會屏蔽16個vector對應的中斷。當然,NMI、SMI、ExtINT、INIT、start-up delivery的中斷不受TPR約束。
而PPR決定當前CPU正在處理的中斷的優(yōu)先級級別,以確定一個pending在IRR上的中斷是否發(fā)送給CPU。具體格式如下圖所示:
與TPR不同,它的值由CPU寫而不是軟件寫。PPR取值范圍為[0,15],計算方式由下列偽代碼描述:
| 1 2 3 4 5 | If TPR[7:4] >= ISR[7:4] THEN PPR[7:0] = TPR[7:0] ELSE PPR[7:4] = ISRV[7:4] PPR[3:0] = 0 |
這里,ISRV[7:4]標識當前ISR中,最高優(yōu)先級中斷對應vector的高4bit,如前面所說,這代表了該中斷的優(yōu)先級級別。簡而言之,取TPR和正在服務的最高優(yōu)先級中斷中,優(yōu)先級級別高的。所以說,IRR中pending的中斷,優(yōu)先級級別必須高于PPR中值才會被發(fā)送給CPU處理,否則,繼續(xù)等…
最后一個概念是lowest priority。RTE的delivery mode有一中模式為lowest priority,即最低優(yōu)先級。需要注意的是,這里的最低優(yōu)先級不是指中斷的優(yōu)先級,而是指將中斷發(fā)送給destination field列出的CPU中,優(yōu)先級最低的一個。而如何選擇所有CPU中優(yōu)先級最低的一個呢,答案應該是通過每個CPU所對應的TPR來決定的。
這里舉一個例子:假設有CPU1、CPU2、CPU3三個CPU,相應的TPR值為:TPR1=5、TPR2=6、TPR3=10,IOAPIC以lowest priority模式發(fā)送一條中斷消息,該中斷對應的優(yōu)先級級別為3。則CPU1具有最低優(yōu)先級,接收該中斷。此時,該中斷被pending到CPU1的IRR中,但不會交給CPU1處理,因為其優(yōu)先級級別低于TPR值。
虛擬化環(huán)境對中斷提供了哪些支持?
最后我們來談談硬件虛擬化對中斷提供了哪些支持。該部分主要參考Intel文檔第三冊的第二十九章節(jié)。
中斷的虛擬化主要分為兩個部分:第一,需要模擬虛擬機對APIC控制寄存器的讀寫操作;第二,需要虛擬化中斷的delivery步驟,換句話說,當虛擬機正在運行的時候來了一個中斷,虛擬化層需要判斷該中斷是否應該遞送給虛擬機,以及如何遞送。
APIC虛擬化
在虛擬機中,不可避免地會對APIC中的寄存器進行訪問,而虛擬化層有兩種方式可以對其進行模擬:
- 第一,通過設置EPT中的權限位,使得虛擬機在訪問APIC對應的頁的時候產(chǎn)生EPT violation的下陷,從而在虛擬化層對其進行模擬。
- 第二,通過設置VMCS里面Secondary Processor-Based VM-Execution Controls域中的virtualize APIC accesses?bit。在這種情況下,通過設置特定的VM-execution controls的位,使得虛擬機在訪問APIC對應的頁的時候可能產(chǎn)生APIC-access VM exit的下陷,或者不產(chǎn)生下陷。
我們主要考慮第二種方式。
第二種方式的前提是virtualize APIC accesses bit被置一。在這個前提下,如果non-root中的虛擬機通過linear address對APIC page進行訪問,則需要對相關操作進行虛擬化。這里有兩個比較重要的VMCS域:APIC-access address和Virtual-APIC address。其中,APIC-access address表示當虛擬機訪問該地址,將會觸發(fā)之后APIC的虛擬化步驟,也就是說,它是真實的APIC在內(nèi)存中映射的地址;而virtual-APIC address表示一個virtual-APIC page的物理地址,而這個virtual-APIC page是在APIC虛擬化過程中,實際被訪問的頁,所以它是一個被虛擬化的APIC頁,但是是被實際訪問的,之后會進行詳細描述。這里需要注意的是,這兩個address存放的都是真實主機的物理地址。
接下來,我們通過對APIC的讀和寫操作分別進行APIC虛擬化步驟的闡述。在介紹之前,需要先解釋一下,以下對memory mapped的內(nèi)存頁的讀寫是基于xAPIC環(huán)境下的,而在x2APIC環(huán)境下,都是通過RDMSR和WRMSR來對相應APIC的寄存器進行讀寫的,這里就略過了。
- APIC-access頁的讀操作虛擬化
當non-root環(huán)境下虛擬機對APIC-access address進行了一個讀操作,當滿足下列任何一個條件時,會發(fā)生VMExit:
否則,這個對APIC-access address的讀操作會觸發(fā)以下虛擬化過程:
| local APIC ID | 020H–023H |
| local APIC version | 030H–033H |
| task priority | 080H–083H |
| end of interrupt | 0B0H–0B3H |
| logical destination | 0D0H–0D3H |
| destination format | 0E0H–0E3H |
| spurious-interrupt vector | 0F0H–0F3H |
| in-service | 100H–103H, 110H–113H, 120H–123H, 130H–133H, 140H–143H, 150H–153H, 160H–163H, 170H–173H |
| trigger mode | 180H–183H, 190H–193H, 1A0H–1A3H, 1B0H–1B3H, 1C0H–1C3H, 1D0H–1D3H, 1E0H–1E3H, 1F0H–1F3H |
| interrupt request | 200H–203H, 210H–213H, 220H–223H, 230H–233H, 240H–243H, 250H–253H, 260H–263H, 270H–273H |
| error status | 280H–283H |
| interrupt command | 300H–303H, 310H–313H |
| LVT entries | 320H–323H, 330H–333H, 340H–343H, 350H–353H, 360H–363H, 370H–373H |
| initial count | 380H–383H |
| divide configuration | 3E0H–3E3H |
除此之外,其它offset的讀訪問都會造成VMExit。而對于這些offset的讀訪問,數(shù)據(jù)可以直接從virtual-APIC page中相應的offset中獲得。
- APIC-access頁的寫操作虛擬化
對APIC的寫比讀操作復雜一些。首先,和讀操作類似,我們得先確定什么時候會觸發(fā)APIC寫操作的虛擬化過程。
當non-root環(huán)境下虛擬機對APIC-access address進行了一個寫操作,當滿足下列任何一個條件時,會發(fā)生VMExit:
否則,是否對APIC-access address的寫操作進行虛擬化由以下條件決定:
| local APIC ID | 020H–023H |
| task priority | 080H–083H |
| end of interrupt | 0B0H–0B3H |
| logical destination | 0D0H–0D3H |
| destination format | 0E0H–0E3H |
| spurious-interrupt vector | 0F0H–0F3H |
| error status | 280H–283H |
| interrupt command | 300H–303H, 310H–313H |
| LVT entries | 320H–323H, 330H–333H, 340H–343H, 350H–353H, 360H–363H, 370H–373H |
| initial count | 380H–383H |
| divide configuration | 3E0H–3E3H |
除此之外,其它offset的寫操作都會造成VMExit。而對于這些offset的寫操作,數(shù)據(jù)直接被寫到virtual-APIC page相應的offset中。但是,由于對APIC某些寄存器的寫會產(chǎn)生一些side-effect,因此需要進行一些所謂的APIC-write emulation,具體的emulation操作由APIC page offset來決定(參考Intel手冊第三冊的29.4.3.2),這里就不詳述了。
- 幾個比較重要的APIC寄存器的虛擬化過程
接下來列舉幾個在virtual-APIC page中比較重要的寄存器:
Virtual task-priority register (VTPR),?Virtual processor-priority register (VPPR),?Virtual end-of-interrupt register (VEOI),?Virtual interrupt-service register (VISR),?Virtual interrupt-request register (VIRR),?Virtual interrupt-command register (VICR_LO),?Virtual interrupt-command register (VICR_HI)。
以及它們的虛擬化過程:
TPR Virtualization: 發(fā)生在以下三個場景中:(1)對MOV to CR8指令的虛擬化;(2)對APIC-access page的offset為080H進行訪問的虛擬化(xAPIC);(3)對WRMSR指令中ECX = 808H的虛擬化(x2APIC)。虛擬化的過程偽代碼如下:
| 1 2 3 4 5 6 7 8 9 | IF “virtual-interrupt delivery” is 0 THEN IF VTPR[7:4] < TPR threshold THEN cause VM exit due to TPR below threshold; FI; ELSE perform PPR virtualization; evaluate pending virtual interrupts (see Section 29.2.1); FI; |
PPR Virtualization: 發(fā)生在以下三個場景中:(1)VM entry;(2)TPR virtualization;(3)EOI virtualization。虛擬化的過程偽代碼如下:
| 1 2 3 4 5 6 | IF VTPR[7:4] ≥ SVI[7:4] THEN VPPR ← VTPR & FFH; ELSE VPPR ← SVI & F0H; FI; |
EOI Virtualization: 發(fā)生在以下兩個場景中:(1)對APIC-access page的offset為0B0H進行訪問的虛擬化(xAPIC);(2)對WRMSR指令中ECX = 80BH的虛擬化(x2APIC)。EOI的虛擬化會使用和更新VMCS中的guest interrupt status域中的SVI。虛擬化的過程偽代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 | Vector ← SVI; VISR[Vector] ← 0; IF any bits set in VISR THEN SVI ← highest index of bit set in VISR ELSE SVI ← 0; FI; perform PPR virtualiation; IF EOI_exit_bitmap[Vector] = 1 THEN cause EOI-induced VM exit with Vector as exit qualification; ELSE evaluate pending virtual interrupts; FI; |
Self-IPI Virtualization: 發(fā)生在以下兩個場景中:(1)對APIC-access page的offset為300H進行訪問的虛擬化(xAPIC);(2)對WRMSR指令中ECX = 83FH的虛擬化(x2APIC)。self-IPI的虛擬化會更新VMCS中的guest interrupt status域中的RVI,相應的偽代碼如下:
| 1 2 3 | VIRR[Vector] ← 1; RVI ← max{RVI,Vector}; evaluate pending virtual interrupts; |
虛擬化環(huán)境中對中斷的evaluation和delivery
首先,在VMCS中的Pin-Based VM-Execution Controls域中,有一個bit用于控制External-interrupt exiting,如果該bit置一,則表示所有的外部中斷都會產(chǎn)生VMExit,否則,所有的外部中斷不會產(chǎn)生VMExit,這就意味著,如果當前CPU處于non-root模式,那么中斷就直接由虛擬機進行處理了。
當然這種將所有中斷都直接讓虛擬機自身來處理的做法很不安全,所以,一般情況下發(fā)生中斷還是會引起下陷的,而在虛擬化層處理完返回虛擬機(VMEntry)時,就需要做中斷的evaluation和delivery了。
所謂的evaluation,其實就是判斷當前是否有中斷需要交給虛擬機進行處理,而delivery就是將evaluation好的中斷交由虛擬機內(nèi)核中的相應的IDT進行處理。
當Secondary Processor-Based VM-Execution Controls中的virtual-interrupt delivery bit為1時,以下場景會觸發(fā)處理器evaluate pending的中斷:(1)VM entry;(2)TPR virtualization;(3)EOI virtualization;(4)self-IPI virtualization;(5)posted-interrupt processing。對pending virtual interrupts的evaluation會使用guest interrupt status中的RVI,相應的偽代碼如下:
| 1 2 3 4 | IF “interrupt-window exiting” is 0 AND RVI[7:4] > VPPR[7:4] THEN recognize a pending virtual interrupt; ELSE do not recognize a pending virtual interrupt; FI; |
當該中斷被recognized了,并且滿足以下四個條件,就會觸發(fā)該虛擬中斷的delivery:(1)RFLAGS.IF = 1;(2)沒有因為STI產(chǎn)生的blocking;(3)沒有因為MOV SS或者POP SS產(chǎn)生的blocking;(4)Primary Processor-Based VM-Execution Controls中的interrupt-window exiting bit為0。
虛擬中斷的delivery會更新guest interrupt status中的RVI和SVI,并且在non-root環(huán)境下產(chǎn)生一個中斷事件,相應的偽代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 | Vector ← RVI; VISR[Vector] ← 1; SVI ← Vector; VPPR ← Vector & F0H; VIRR[Vector] ← 0; IF any bits set in VIRR THEN RVI ← highest index of bit set in VIRR ELSE RVI ← 0; FI; deliver interrupt with Vector through IDT; cease recognition of any pending virtual interrupt; |
至此,對“中斷處理的那些事兒”的介紹就結束了,對于這一塊的內(nèi)容,我也還在學習中,很多細節(jié)上的東西之后也會慢慢再補充進去吧。
原文地址:?http://ytliu.info/blog/2016/12/24/zhong-duan-chu-li-de-na-xie-shi-er/
總結
- 上一篇: Linux内存初始化(C语言部分)
- 下一篇: Android平台监听系统截屏方案预研及