Pentium處理器的虛擬模式


  • 尋找VME
  •   在Pentium處理器的使用者手冊(附錄H)裡,對VME(Virtual Mode Extension)並未詳述,但在Pentium處理器家族的Developer's Manual(第三冊)對VME卻有頗多的敘述(至少有27項參考資料)。除此之外,Intel在英國的VME專利權申請中也可以查到VME的相關資料。如果對虛擬86模式(v86 mode)有足夠的了解,讀者可以從上述的資料中查到關於VME的細節部份。

  • 對VME的需要
  •   當80286問世時,Intel應用了一種可以多功處理各種應用程式的方法。不過80286的多功能力被限制,而且不被主流的作業系統所採用(如微軟)。80286缺乏管理虛擬微處理器的必須硬體相容性,所以80286並不適合作為VDM(Virtual DOS Machine)的平台。

      Intel的80386也有這個缺點。80386被賦予一些多功相容性,而重要的是,80386有新的作業模式--虛擬86模式(v86 mode)--如同它的名稱,v86 mode就是要用來模擬8086的。為造出虛擬的8086,Intel加入了允許從v86 mode轉換到作業系統核心的機制,但是作業系統核心的支援情況並非是軟體開發者所喜歡的樣子......。

      當多重v86工作同時在執行時,作業系統核心必須調配並控制電腦的資源。某些時候,核心必須使特定的中斷指向特定的v86工作(如鍵盤中斷);在其它情況下,核心會想要把中斷指向所有的v86工作(如timer click)。因此,微處理器的指令會限制一個工作接受中斷的能力(如CLI和STI指令),這些指令是 "IOPL-sensitive",代表它們的成功執行有賴於目前執行的v86工作的 I/O Privilege Level 。指令 CLI, STI, PUSHF, POPF, INT , IRET 都對旗標暫存器有影響,尤其是中斷旗標IF。中斷服務的調配與這些指令的關係會隨著VME的解釋一起漸漸明朗化。

      當v86 mode剛開始被應用時,軟體設計者找到兩種v86 mode的用途 :

    1. DOS記憶體管理者
    2. 在多功作業系統下的DOS區(如Windows下的DOS box)

      在記憶體管理者下,DOS仍是一個單功作業系統(所有作業依序單獨發生),所以硬體資源不需要被調配(以從 I/O permission位址表來的 I/O protection為條件的 I/O埠<<如DMA埠>>除外)。IOPL-sensitive指令完全不須被限制。在Windows下執行DOS就不同了,幾乎所有的資源都要被限制,DOS不 需要為了規劃所有裝置去直接存取硬體,也不需要直接控制 IF和 EFLAGS暫存器。v86 mode支援這種存取的限制,經由IOPL的使用可以限制改變IF的IOPL-sensitive的指令(在保護模式中為IOPL-sensitive的I/O埠指令,到了v86 mode下並非IOPL-sensitive);經由I/O permission位址表可以限制對I/O埠的存取。然而,標準的v86 mode有一些缺點:

    1. 把IOPL設為3會比小於3有更好的工作表現(performance),這樣的設定可以減少過度的trapping,但會讓VDM使中斷禁能(disable),使得整個系統出現了潛在的整合問題。
    2. 當作業系統要虛擬中斷旗標的時候,IOPL的設定值要小於3,當虛擬驅動程式要在VDM中模擬硬體中斷時,它必須能夠偵測 VDM是否可接受中斷。因此,IOPL要小於3,這樣IF才能夠被虛擬化。不過performance會因為企圖執行 IF-sensitive指令(對v86 monitor是錯誤的)而大為下降。
    3. 作業系統會允許VDM去接受真正的(外部)中斷或虛擬的中斷,這點由作業系統應用者決定。如果v86工作只接受虛擬的中斷,它會被要求分頁(demand-paged),由此,當真正的記憶體被其他目的所需要時,它就會被swap到磁碟去;如果v86工作接受真正的(外部)中斷,它就不能被要求分頁,以防中斷發生時,中斷handler被分頁出去(page out)。作業系統可能沒有足夠的時間在中斷發生時帶入整個工作,而如果僅帶入包含中斷服務常式的工作,又會太複雜、太花時間。
    4. 所有的INT-n指令都會使v86 mode被切換出去(switch out)。當IOPL<3,INT-n指令對v86 monitor來說是錯誤的;當IOPL=3,INT-n企圖呼叫跟一個特別中斷有關的保護模式中斷handler(這有賴於被使用的gate的DPL)。monitor或中斷handler必須趕上中斷的作用,不然就要讓v86工作重新開始,讓它能自己執行中斷。DOS核心常式由軟體中斷來存取,因此每呼叫一次中斷就會引發一次v86 mode的切換,這使得v86工作和同一個程式在DOS下執行的performance相比有了顯著的缺點。
  • VME修正v86的問題
  •   Ev86(Enhanced v86)mode 被設計用來消除以上的問題,而且有效地加強在各個IOPL值下v86工作的performance。在Ev86 mode下,IOPL=3時,CLI和STI仍會改變IF;在IOPL<3時,CLI,STI和其它所有IF-sensitive指令對Ev86 monitor來說不再是錯誤的。相反地,IF-sensitive指令是明確的,而且在EFLAGS暫存器裡設了一個叫做VIF的中斷旗標(當EFLAGS.VIP=1時,STI是錯誤的)。清除VIF不會阻隔外部的中斷(清除IF就會),而且,VIF是Ev86工作是否可接受中斷的指標,如果一個DOS程式會使中斷禁能,然後困在無窮迴圈之中,這會造成電腦當機。然而,把這種程式當成Ev86工作來執行,電腦就不會被這類的bug傷害到。作業系統對這種錯誤的程式可以一直取得控制權,不必去設定IF。這個特點有顯著的好處:performance加強了,CLI和STI不會引起耗時的錯誤。其次,監視程式的複雜性降低了,而且它不需要維持自己的須擬中斷旗標。當舊式的v86 monitor虛擬IF時,它必須趕上IF所有的改變(由CLI, STI, PUSHF, POPF, INT, IRET所引起的),使用Ev86 MODE消除了這個複雜性,因為CPU會自動虛擬IF。

      當外部中斷發生時(鍵盤按下或是timer tick),在CPL-0執行的主作業系統會去攔截這些中斷。當某些中斷發生時,目前的工作可能不是v86工作,它可能被swap到磁碟上,可能在一個無法中斷的狀態下。當這種情況發生時,主作業系統可能會延遲傳送中斷訊息到v86工作的時間直到它開始執行並準備可以接受中斷。其它的中斷可能是針對某些VDM而非所有的VDM(如鍵盤按下),在這種情況下,v86 monitor需要把這個特定的中斷傳達給特定的VDM--忽略其它的VDM。這種延遲和過濾鐘斷的方法叫作中斷的虛擬化(interrupt virtualization)。當尚不能接受虛擬中斷的VDM變成可以接受中斷時,作業系統會將中斷反應給VDM,如同真正的中斷發生一樣。

      在Ev86 mode之前,v86 monitor需要在軟體裡維持一個虛擬中斷旗標。v86 monitor被強迫去處理許多不必要的例外,舉例來說,當一個虛擬中斷尚未被接受時,較遠的IOPL-sensitive指令企圖清除IF,引起不可預期的錯誤,接著使得monitor去清除不必清除的VIF。這個問題在Ev86 mode中並不存在。,所以那些清除軟體裡的VIF的source code可以被移除。事實上,在使用Ev86模式時,所有用來維持軟體的VIF的code都要被移除,因為CPU可以自己維持VIF。

      在Ev86 mode之前,軟體中斷(INT-n指令)會使v86 mode被切換出去,若IOPL=3,轉換透過跟中斷有關的gate發生(假設所有的保護屬性允許轉換發生);當IOPL<3,轉換的發生對monitor來說是"一般性的保護錯誤"(general protection fault)。當IOPL=3時,monitor必須去判斷中斷是軟體中斷、外部中斷或是CPU產生的例外所引發;當IOPL<3,軟體中斷不會經由在IDT中相關的gate轉換(從#GP gate轉換)。在軟體中斷的情況下,monitor必須去翻譯opcode來決定哪一號中斷需要服務(service),然後monitor必須趕上中斷,或是將中斷訊息反應回v86工作。外部中斷和CPU產生的例外仍然經由它們在IDT中相關的gate轉換,在這些情況下,monitor仍然要判斷中斷的來源(外部或是CPU產生的),然後採取適當的行動。使用Ev86 mode可以簡化這個過程,並加強處理軟體中斷的 performance。

      軟體中斷的執行被一個在TSS中叫作中斷向量表的新結構所控制,在這新結構裡的每一個bit都控制著一個軟體中斷該被以和Intel 386相容的方式呼叫或是單純地在Ev86工作中被呼叫。在Ev86 mode中,這些中斷可以在不離開Ev86 mode的情況下被呼叫和執行,使用這個新技術可以減低monitor內的複雜度。從前對monitor來說是錯誤的中斷現已不再被當成錯誤,原先經由IDT轉換的中斷也不再經過IDT。

  • VME元件概觀
  •   設定VME bit裡的CR4(bit0)可以決定是否要VME的支援(enabled/disabled)。在enabled,IOPL=3時,所有的INT-n指令都被TSS裡的中斷向量表控制(中斷指向在IOPL的設定中是沒有條件性的);當IOPL<3,加上INT-n的屬性,IF-sensitive指令在Ev86 monitor中是被允許執行的。

      為了包含一個32-byte的中斷向量表,TSS已被延伸。32-bytes精準地說是256 bits,每一個bit對應一個可以經由INT-n指令呼叫的軟體中斷。(見Fig.1) TSS中的I/O Base field因此被延伸且有雙重目的,I/O Base field不僅指向 I/O permission位址表的基部,同時也指向中斷向量表的尾端,這個結構除了是控制軟體中斷的之外,其餘都與I/O permission位址表非常相像,當其相對應的bit被設定時,中斷對Ev86 monitor來說是錯誤的,當它的bit被清除時,中斷不必離開Ev86 mode就會被service。

    Fig.1 -- TSS中的中斷向量表

  • VIF和VIP
  •   EFLAGS暫存器加入了兩個新旗標,這兩個旗標是在Ev86 mode,IOPL<3的時候使用,它們可以被CPL-0 Ev86 monitor或中斷服務常式刻意改變。

      VIF是標準中斷旗標IF的"虛擬版",當Ev86工作在執行時,任和CLI和STI指令都不會改變真正的IF,它們會改變VIF(當EFLAGS.VIP=1時,STI是錯誤的)。這個特性被隱藏在Ev86工作中,因為PUSHF, POPF, INT-n, IRET也被改變了。

      VIF旗標(Virtual Interrupt Pending Flag)可以輔助多功作業系統傳送中斷訊息到Ev86工作中。最容易了解VIP的方法就是先了解它在8086下執行程式時的用法:當8086在不能接受中斷的狀態下,外部中斷尚未被service,在IF被設定(因為有STI, POPF或IRET)之後,中斷就會被CPU service。假設一個Ev86工作在不能接受中斷的狀態,此時一個timer-tick中斷發生,多功作業系統要來service這個中斷,在這個中斷服務常式中,多功作業系統決定Ev86工作必須service這個timer-tick,所以VIP被設定,中斷結束後,Ev86工作又回到了不能接受中斷的狀態(VIF=0)。過一段時間之後,Ev86工作會去設定IF(STI, POPF, 或IERT),此時Ev86工作變成可以接受中斷的狀態,而對monitor來說,general protection fault立刻發生(#GP(0))。

  • IF-sensitive指令
  •   為了支援新的VIF和VIP旗標,讀寫EFLAGS暫存器中斷旗標的指令必須改變。CLI, STI, PUSHF, POPF, INT-n, IRET也都必須為了支援Ev86 mode改變。

      當IOPL<3時執行Ev86工作,CLI和STI會改變VIF旗標,而非對Ev86 monitor造成錯誤,或是影響IF旗標(EFLAGS.VIP=1時STI為錯誤)。

      PUSHF把FLAGS push到stack裡,把VIF的內容copy到IF的位置,這讓Ev86工作看起來像是STI和CLI真的去改變IF,萬一軟體要檢查IF的時候,這樣的"偽裝"是必需的。這種檢查IF的code demo在List 1裡。

    List 1-- 軟體測試IF旗標的code

      POPF跟PUSHF相似,它把FLAGS 的image pop到stack裡,把IF的內容copy到VIF的位置。Pentuim小心地確保"假的"IF和IOPL會在POPF的動作中意外地被copy到真的IOPL裡,在FLAGS的image合併到EFLAGS暫存器前,IF的image會先copy到VIF的槽中,IF和IOPL的image被清除。真正的FLAGS暫存器被清除,IF和IOPL除外。最後,被過濾的FLAGS image跟真正的EFLAS暫存器合併在一起。有個"副作用"發生在POPF處理stack image裡的TF時,當TF在stack image裡被設定時,POPF會在任何FLAGS值改變前引起general protection fault(#GP(0))。

      IRET指令就像POPF對IF, VIF, IOPL所作的一樣,其差別在於對TF的處理:IRET不會使 #GP發生。

      INT-n指令是IOPL-sensitive指令裡最複雜的,就像PUSHF處理IF, VIF, IOPL一樣(假設中斷是由Ev86 monitor根據中斷向量表指向Ev86工作的)。然而,Ev86優越的一點是軟體中斷不必離開Ev86 mode就可以執行,這可歸因為TSS裡中斷向量表的幫助。當相對應的IR bit被設定,中斷就會像在一般v86 mode下一樣被呼叫;當相對應的bit被清除,中斷會像在8086處理器上執行一般被呼叫。換言之,對monitor而言的錯誤不再產生,也不會對保護模式中斷handler轉換,中斷的轉換與返回完全都在Ev86工作裡完成,中斷向量表的影響列在List 2裡。

    List 2 -- 在Ev86 mode下處理中斷
  • 結論
  •   延伸虛擬模式對於記憶體管理者和多功作業系統非常有用,記憶體管理者可以由利用中斷向量表來減少切換保護模式的次數;同時也減低了中斷服務常式的複雜度,不再將中斷反應到v86工作。

      多功作業系統可從很多方面得利,從中斷指向得利,也從虛擬中斷的支援得利。多功作業系統會在可用虛擬模式延伸的情況下(enabled)執行,Ev86工作在IOPL<3時執行。虛擬中斷給了多功作業系統很多好處,當它希望傳遞一個中斷(如虛擬的timer tick)給不能接受中斷情況下的Ev86工作時,它可借由設定VIP=1來完成;當工作變成可以接受中斷,general protection fault發生,多功作業系統會傳遞一個中斷給Ev86工作,這會給有時效性的程式(如game)在performance上有很顯著的優點。加上CPU的虛擬化特色,更多Ev86 monitor的複雜性可以被移除。這都是因為使用了Ev86 mode--不但比非Ev86 mode更容義易應用,也使執行速度變得更快。


    所有與本文相關的測試程式可以從以下位址download:

    ftp://ftp.x86.org/dloads/VME1.ZIP
    http://www.x86.org/ftp/dloads/V86MON.ZIP

      這篇文章是由Robert R.Collins 和 Jim Brooks所發表的 Virtual Mode Extensions on the Pentium ProcessorThe Pentium's Enhanced v86 Mode 整理、翻譯而成,在翻譯之前,我在 www.x86.org 找了很多相關的article來看,不過由於其它的文章內容比較艱深,而且有時原著者用了很多比較主觀的語句,作為翻譯文章並不恰當,所以我選擇大家應該比較能看懂的兩篇整理起來翻譯。

      有些平常習慣用英文描述的單字,硬生生翻成中文念起來反而怪怪的,所以在這篇文章裡還是出現了一些英文,感覺上比較流暢。

      在找資料與翻譯的過程中,我學到了很多(不僅是英文程度與查字典的速度大增,對於自己所作的topic也從完全沒概念到了有一定程度的了解),希望看完這篇文章的人都能從以上的文字中得到一些東西。  ^_^