By Robert R. Collins
In the first two installments of this series on Intels Virtual Mode Extensions (VME), I examined why VME is needed. More specifically, in the January column I explained the performance problems associated with Intels original v86 mode implementation. I went on to discuss Intels new Enhanced v86 Mode implementation, and specifically how Ev86 mode fixed many of the performance deficiencies in the first v86-mode implementation.
In the March installment, I went under the hood of VME, examining all of the various components that are used by the VME, and explained how they work. At the end of that column, I presented source code that can be used to model your own Ev86 mode tasks.
Subsequent to those columns being published, Intel released the Intel Architecture Software Developers Manual, Volume 3. Although six months late, the manual couldnt have been released at a better time because it gave me the opportunity to determine whether Intel had finally documented the missing details of VME or described its many caveats.
Alas, Intel has done neither. The missing details are still missing, and the caveats of VME have never been described. Notwithstanding these omissions, Intel has actually removed some vital VME information.
This month, among other things, Ill show how many references to VMEs remained in the Pentium manuals after Intels lawyers had forced their removal. These oversights allowed the Appendix H liberation movement to reverse engineer the VME details and release the information six months before Intel.
After the creation of the notorious Appendix H, you would have thought Intel had removed every reference to the "secret" VMEs (see my November 1997 column for a description of Appendix H). However, Intel wasnt careful enough. In fact, I found many references to VMEs scattered throughout the Pentium manuals.
When preparing to help reverse engineer the VME details, I started with the premise that the Pentium manual was virtually silent on the subject of VME. In my research, I found many more references than expected. I extracted all the references and began categorizing them. As I rearranged the quotes into categories, I found the Pentium manual told its own VME story. This story starts by listing some of the problems with the existing v86 mode. Next the story establishes that new VMEs were implemented to enhance performance. By the end of the story, the manual tells you virtually every aspect of VME. From this information, any capably-equipped engineer can write code to figure out the various implementation details.
Letting Intel's Pentium manuals tell their own VME story Heres the Pentium manuals own story of why VME was needed, and how to use it. Unless otherwise noted, all of the following quotes are taken from the Pentium Processor Family Developers Manual, Volume 3: Architecture and Programming Manual (Intel part number 241430).
As I've already mentioned, this information was publicly available at the time Intel was trying to keep it secret. Anybody with the necessary skills could use this information to reverse engineer the remaining details of Intels VME implementation. In the long run, keeping this information secret hurt the engineering community more than it helped Intel maintain any competitive advantage. |
When the Pentium Pro manuals were finally released in April 1996, Intel released its own description of VME. By this time, my article "Pentiums Virtual Mode Extensions Revealed" (co-authored with Jim Brooks, EE Times, November 11, 1995) describing VME had been publicly available for five months. Upon reading Intels VME description, I noticed that some of it is fatally flawed. Consider the following excerpt from the Pentium Pro Family Developers Manual, Volume3, section 12.3.5:
If the processor receives an interrupt or exception and the VIF flag is clear (maskable hardware interrupts enabled), the processor performs the same operation as it does for method 5 interrupt and exception handling (that is, it redirects handling to the 8080 programs interrupt and exception handlers). The processor also handles interrupts and exceptions in this manner if the VIF flag is set, and the processor receives either an NMI interrupt or an exception (interrupt vectors 0 through 18).
If the processor receives a maskable hardware interrupt (interrupt vector 32 through 255) when the VIF flag is set, processor performs and the interrupt handler software must perform the following operations:
In some way or another, almost every sentence in this quoted text needs correction. The passage should read as follows:
If the processor receives an INT-n instruction, the processor performs the same operation as it does for method 5 interrupt and exception handling (that is, it redirects handling to the 8086 programs interrupt and exception handlers). The processor never handles maskable hardware interrupts, exceptions, or an NMI interrupts in this manner.
If the processor receives a maskable hardware interrupt, exception, or an NMI interrupt when the VIF flag is set, the processor causes a #GP(0). Otherwise, when the VIF flag is clear the processor and interrupt handler software must perform the following operations:
I was so confused by Intels documentation that Im still not sure what it was trying to say. Therefore I made my best guess, while making the technical corrections along the way. To determine if Intel has corrected these errors, I checked this same text in the newly released Intel Architecture Software Developers Manual, Volume 3, discovering that Intel has completely removed this entire section of text, and the detailed explanation that followed.
The following text was removed from Intels manuals. The text is the continuation of the passage just listed. The text describes the steps which the microprocessor and Ev86 monitor must perform when a maskable hardware interrupt, exception, or an NMI interrupt occurs when EFLAGS.VIF= 0.
- The processor makes a call to a protected mode interrupt handler as described in the following steps. These steps are al most identical to those described for method 1 interrupt and exception handling in "Handling a Virtual8086 Mode Interrupt or Exception Through a Protected-Mode Trap or Interrupt Gate" on page 1217:
- Switches to 32-bit protected mode and privilege level 0.
- Saves the state of the processor on the privilege-level 0 stack. The states of the EIP, CS, EFLAGS, ESP, SS, ES, DS, FS, and GS registers are saved (see Figure 125 on page 1218). In the EFLAGS image on the stack, the IOPL field is set to 3 and the VIF flag is copied to the IF flag.
- Clears the segment registers.
- Clears the VM flag in the EFLAGS register.
- Begins executing selected the protected-mode interrupt handler.
- The recommended action of the protected-mode interrupt handler is to read the VM flag from the EFLAGS image on the stack. If this flag is set, the handler makes a call to the virtual-8086 monitor.
- The virtual-8086 monitor reads the VIF flag in the EFLAGS register. If the flag is set, the virtual-8086 monitor sets the VIP flag in the EFLAGS register to indicate that there is an interrupt pending and returns to the protected mode handler.
- The protected mode handler executes a return to virtual-8086 mode.
- Upon returning to virtual-8086 mode, the processor continues execution of the 8086 program without handling the interrupt.
When the 8086 program executes the STI instruction to clear the VIF flag, the processor does the following:
- Checks the VIP flag.
- If the VIP flag is clear, the processor clears the VIF flag.
- If the VIP flag is set, the processor generates a general-protection exception (#GP)
- The recommended action of the protected-mode general-protection exception handler is to then call the virtual 8086 monitor and let it handle the pending interrupt.
A typical action of the virtual-8086 monitor is to clear the VIF and VIP flags in the EFLAGS image on the stack and execute a return to the virtual-8086 mode (through the protected-mode exception handler). The next time the processor receives a maskable hardware interrupt, (providing the VIF flag is still clear) it will handle it in the same manner as with method 5 interrupt and exception handling. Note that the states of the VIF and VIP flags are not modified in real-address mode or during transitions between real-address and protected modes.
Intel has never released the algorithms used by the various CPU instructions needed to support VME. Perusal of Intels Pentium manuals shows that CLI, STI, PUSHF, POPF, INT-n, and IRET have all been modified to support VME. This support is necessary, as all of these instructions have the ability to modify the Interrupt Flag in the EFLAGS register (EFLAGS.IF). With VME enabled, the processor must now act on the Virtual Interrupt Flag (EFLAGS.VIF), instead of the real IF-flag. To the best of my ability, Ive made Listing One representative of the algorithms Intel uses in its Pentium VME implementation. Ive only included the algorithms for CLI, STI, PUSHF, POPF, and IRET. I never attempted to reverse engineer the INT-n instruction. The INT-n instruction should closely resemble the PUSHF instruction, with the additional support needed for the Interrupt Redirection Bitmap. The code is written in pseudo-C format, but is intended to be human readable.
VME is wonderful for reducing the complexity of the operating system, but has many caveats. Some of the quirks are natural extensions of VME or the underlying architecture, while others make little sense.
I have described everything I know about VME in this series, but Im sure that even so, not everything is known and documented. For example, since I wrote my last VME column, I discovered a few more caveats. As time goes on, Im sure more VME details will be discovered. Yet in these three articles, Ive presented many times more information than is contained in Intels manuals. In the release of its newest architecture manual, Intel has demonstrated that it wishes to say less about VME, thereby removing vital information from the engineers who need it most. Therefore, consider these articles a blessing, and save the source code contained within. You never know when youll need it and youll never know when Intel will release any more VME details.
CLI { /* v86 mode */ if (EFLAGS.IOPL == 3) { then EFLAGS.IF = 0; else if (CR4.VME == 0) { /* IOPL < 3 */ then #GP(0); /* ERROR CODE = 0 */ else EFLAGS.VIF = 0; } } } STI { /* v86 mode */ if (EFLAGS.IOPL == 3) { then EFLAGS.IF = 1; else if (CR4.VME == 0) { /* IOPL < 3 */ #GP(0); /* ERROR CODE = 0 */ else { if (EFLAGS.VIP == 1) { /* if VIP already=1, #GP(?) */ then #GP(0); /* ERROR CODE = 0 */ /* Processor never set VIF */ /* before #GP() */ else EFLAGS.VIF = 1; } } } } } PUSHF { /* v86 mode */ if (EFLAGS.IOPL == 3) { then if (OperandSize == 32) { then push(EFLAGS & 0xFCFFFF); /* Clear VM & RF */ else push(FLAGS); } /* IOPL < 3 */ else if ((CR4.VME == 0) || (OperandSize == 32)) { /* IOPL < 3 */ then #GP(0); /* ERROR CODE = 0 */ else { TEMP = FLAGS; TEMP = TEMP OR 0x3000; /* Set IOPL=3 on stack (dumb?) */ TEMP.IF = EFLAGS.VIF; push(TEMP); } } } } POPF { /* v86 mode */ if (EFLAGS.IOPL == 3) { /* IOPL = 3 */ then if (OperandSize == 32) { then { TEMP = pop(); /* Clear these fields from EFLAGS stack image: */ TEMP = TEMP AND NOT 0x1BB02A; /* VIP VIF VM RF IOPL */ /* Keep these fields from current EFLAGS register: */ EFLAGS = EFLAGS AND 0x1B3002; /* VIP VIF VM RF IOPL */ EFLAGS = EFLAGS OR TEMP; } else { TEMP = pop(); /* Clear these fields from EFLAGS stack image: */ TEMP = TEMP AND NOT 0xB02A; /* IOPL */ /* Keep these fields from current EFLAGS register: */ FLAGS = FLAGS AND 0x3002; /* IOPL */ FLAGS = FLAGS OR TEMP; } else if ((CR4.VME == 0) || (OperandSize == 32)) { /* IOPL < 3 */ then #GP(0); else if ([SP].TF) { /* If stack image TF=1, then #GP*/ then #GP(0); else if ((VIP == 1) && [SP.IF]) { then #GP(0); else { TEMP = pop(); EFLAGS.VIF = TEMP.IF; /* Clear these fields from EFLAGS stack image: */ TEMP = AND NOT 0xB22A; /* IOPL, IF */ /* Keep these fields from current EFLAGS reg */ FLAGS = FLAGS AND 0x3202;/* IOPL, IF */ FLAGS = FLAGS OR TEMP; } } } } } } IRET { /* v86 mode */ if (IOPL == 3) { /* IOPL = 3 */ then if (OperandSize == 32) { then { EIP = pop(); CS = pop(); TEMP = pop(); /* Clear these fields from EFLAGS stack image: */ TEMP = TEMP AND NOT 0x1BB02A; /* VIP VIF VM RF IOPL */ /* Keep these fields from current EFLAGS register: */ EFLAGS = EFLAGS AND 0x1B3002; /* VIP VIF VM RF IOPL */ EFLAGS = EFLAGS OR TEMP; } else { IP = pop(); CS = pop(); TEMP = pop(); /* Clear these fields from EFLAGS stack image: */ TEMP = TEMP AND NOT 0xB02A; /* IOPL */ /* Keep these fields from current EFLAGS register: */ FLAGS = FLAGS AND 0x3002; /* IOPL */ FLAGS = FLAGS OR TEMP; } } else if ((CR4.VME == 0) || (OperandSize == 32)) { /* IOPL < 3 */ then #GP(0); else if ([SP].TF) { /* If stack image TF=1, then #GP*/ then #GP(0); else { IP = pop(); CS = pop(); TEMP = pop(); /* Clear these fields from EFLAGS stack image: */ TEMP = AND NOT 0xB02A; /* IOPL */ /****************************************************/ /* ** NOTE ** The following treatment of TF flag */ /* ********** *MAY BE A BUG* in the Pentium. */ /* POPF uses the FLAGS mask of 0x3202, but IRET */ /* uses 0x3302. Consider that POPF and IRET */ /* #GP on a condition involving TF. So is it */ /* just coincidence that IRET has further */ /* special treatment of TF on IRET, where POPF */ /* does not? */ /****************************************************/ /* Keep these fields from current EFLAGS reg */ FLAGS = FLAGS AND 0x3102;/* IOPL, TF */ FLAGS = FLAGS OR TEMP; if (EFLAGS.VIP && EFLAGS.IF) then #GP(0); /* GP if VIP set */ } } } } } |