Protected Mode Virtual
Interrupts (PVI)
on Pentium and
SL-enhanced i486 Intel processors
by Maciej W. Rozycki
Disclaimer
The following information was obtained by reverse engineering,
using my own test application written some years ago for
debugging protected mode programs. The tests were performed on a
WB-enhanced i486 DX2 processor (model = 3, stepping = 6)
that returned the following value for CPUID feature
flags: CPUID(1).EDX = 0000000B. No confidential sources
of knowledge were used. The following publicly available
documents proved to be useful: [1] and [2].
1. Introduction
Sensitive instructions (in the protected mode) are opcodes
related to I/O operations. They can be dangerous to the system,
but there may exist a necessity of their execution at the
privilege level lower than zero. To ensure protection and
accessibility at the same time, a special two-bit field called IOPL
was introduced into the EFLAGS register (IOPL
stands for Input/Output Privilege Level). The sensitive
instructions can be executed only if CPL <= IOPL
(where CPL or Current Privilege Level is PL of the CS
selector). In this way, disabling of access to these instructions
can be achieved. Additionally, to prevent user-level code from
changing the IOPL field, POPF or IRET do not
modify it when executed at CPL > 0.
Here is the list of sensitive instructions:
- IN - input,
- INS - input a string,
- OUT - output,
- OUTS - output a string,
- CLI - clear the interrupt enable
flag (IF),
- STI - set the interrupt enable flag
(IF).
Trying to execute any of these instructions when CPL >
IOPL will cause a General Protection Fault (#GP),
that is exception 13 (for I/O instructions, the I/O permission
bitmap may prevent the fault from occuring for selected I/O
addresses). However, an implicit attempt to modify the IF
bit, using POPF or IRET instructions will not
cause any error -- simply the new value of the IF bit
will be discarded.
2. Virtual Interrupts (traditional approach)
Of all sensitive instructions, only CLI and STI
are discussed here as only these instructions are related to the
mechanism of interrupts.
It may happen, that some application programs would like to
serve hardware interrupts, originated from non-standard devices
that were not known at the time the operating system (OS) was
created. Additionally, such a program might not want to receive
these interrupts during some special operations. In this
situation the OS may detect executing of the CLI or STI
instruction and set appropriate internal variables that would
decide whether to let the application serve the interrupt or not.
It has to be noted, that there may exist many concurrent tasks in
the system that may be cyclically switched using, for example, a
timer interrupt. Moreover, it may be necessary to serve some
other peripherals, such as keyboard. In such case, disabling of
interrupts may lead to a system crash. However, it is possible to
use the mechanism of so called virtual interrupts, in which
disabling of interrupts performed by one of tasks, causes only
excluding the task from serving these interrupts.
Another example of using CLI and STI
instructions is defining of application level programs' critical
sections. Executing of the CLI instruction would
guarantee uninterrupted execution of the following section, until
the STI instruction is met. Also in this situation, it
is necessary to preserve system's vitality -- a malicious
application could execute the CLI/JMP $ sequence causing
the system halt. To avoid this, the OS may treat the CLI
instruction only as a request to ensure a mutual exclusion, which
could be achieved, for example, using a temporary blockade of the
user-level task switching.
Meeting of the above conditions may be realized in a #GP
exception serving routine. Unfortunately, generating of a #GP
exception on every CLI and STI occurence is
very time-consuming. The routine has to be called through a gate,
then it has to found out the exception's reason, then it has to
perform appropriate actions and, finally, it has to return to the
interrupted program. A typical action performed by this procedure
in case of CLI and STI instructions, is to
modify a flag defined somewhere in the task state area to inform
of the actual state of task's local interrupt subsystem.
3. Pentium Processor's Virtual Interrupts Improvement
Just for improving processor's performance, in Pentium and
SL-enhanced i486s, Intel introduced new mechanisms and some bits
that are related to them. These bits are: the PVI bit of
CR4 register (Fig.1) and VIF
and VIP bits of EFLAGS register (Fig.2). The effect of these bits is as follows:
- For PVI = 0:
- If CPL <= IOPL, then CLI and
STI operate on the IF flag.
- If CPL > IOPL, then CLI and STI
cause a #GP exception.
- For PVI = 1 and CPL < 3:
- If CPL <= IOPL, then CLI and
STI operate on the IF flag.
- If CPL > IOPL, then CLI and STI
cause a #GP exception.
- For PVI = 1 and CPL = 3:
- If IOPL = 3, then CLI and STI
operate on the IF flag.
- If IOPL < 3 and VIP = 0 (no
pending interrupts present), then CLI/STI
resets/sets the VIF flag.
- If IOPL < 3 and VIP = 1 (a
pending virtual interrupt is present), then an
attempt to enable virtual interrupts (by setting VIF)
using STI will cause a #GP
exception.
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |M|-|P|D|T|P|V|
| R E S E R V E D |C|-|S|E|S|V|M|
| |E|-|E| |D|I|E|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* ^
^ -- these bits are referenced
* -- these bits are reserved
Fig.1. The CR4 Register (as defined for Pentium)
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |I|V|V|A|V|R| |N|I O|O|D|I|T|S|Z| |A| |P| |C|
| R E S E R V E D |D|I|I|C|M|F|0|T|P L|F|F|F|F|F|F|0|F|0|F|1|F|
| | |P|F| | | | | | | | | | | | | | | | | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
^ ^ ^ * ^ ^ * * *
^ -- these bits are referenced
* -- these bits are reserved
Fig.2. The EFLAGS Register (as defined for Pentium)
This mechanism significantly simplifies user-level interrupt
serving routines. Before giving control the OS just checks the VIF
flag and depending on its value takes an appropriate further
action. In particular, using this technique, it is possible to
execute some system level programs or procedures (designed for
execution at CPL = 0) at the application level (at CPL
= 3).
Additionally, the VIP flag makes it easier to monitor
enabling of virtual interrupts. This bit may be used by the OS
for marking, that having VIF = 0 (e.g. when serving a
virtual interrupt), another virtual interrupt was signaled. In
this case the OS stores the interrupt reason and sets the VIP
flag, which means a virtual interrupt is currently pending. At
the moment of enabling of virtual interrupts and having CPL =
3 and IOPL < 3, a #GP exception is
generated. A handler that serves this situation may now reset the
VIP flag and give control to the right procedure that
will serve the previously pending interrupt. Certainly, incoming
interrupts may be queued. In this situation resetting of VIP
would occur only after emptying the queue.
Here is the summary of the flags that are used by the
mechanism of virtual interrupts and which are subject to
protection:
- VIP -- It is modified by IRETD,
provided that CPL = 0.
- VIF -- It is modified by IRETD,
provided that CPL = 0; by CLI and STI
-- as described above.
- IOPL -- It is modified by IRET(D)
and POPF(D), provided that CPL = 0.
- IF -- It is modified by IRET(D)
and POPF(D), provided that CPL <= IOPL;
by CLI and STI -- as described above.
Certainly, all flags are modified at the time of a task
switch. Note that, unlike the IF flag, VIF is
not modified at the time of transition through interrupt or trap
gates.
In the case of an (E)FLAGS related protection
violation when executing IRET(D) or POPF(D), no
exception is generated and the protected bit remains unchanged.
On the other hand, a #GP exception is generated when
trying to execute a program having both VIP and VIF
set, provided that PVI = 1 and CPL = 3 (IOPL
may have any value). That happens, for instance, after executing
the IRETD instruction that changes the CPL from
0 to 3 and writes ones to VIP and VIF.
The instruction finishes successfully and then, before the next
opcode, a #GP exception is generated. Testing of the EFLAGS
image on the stack or in the Task State Segment (depending on the
#GP exception gate type) returns both VIP and VIF
bits set.
4. Detection of Mechanism of Virtual Interrupts Presence
The mechanism of protected mode virtual interrupts is
implemented along with virtual mode extensions and is identified
by the VME bit returned by the CPUID instruction.
Processors that do not implement the CPUID instruction,
do not support virtual interrupts. To get the value of the VME
bit the following sequence has to be performed:
- Check the presence of EFLAGS' bit ID (Fig.2) trying to negate it.
- Execute the CPUID instruction with the EAX
argument value of 0 and ensure the chip was
manufactured by Intel (GenuineIntel: ECX=6C65746E,
EDX=49656E69, EBX=756E6547) and
supports executing of CPUID having the argument
in EAX equal to 1,
- Execute the CPUID instruction with the EAX
argument value of 0 and obtain the value of
feature flags from the EDX register (Fig.3).
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |A|C|M|-|M|T|P|D|V|F|
| R E S E R V E D |P|X|C|-|S|S|S|E|M|P|
| |I|8|E|-|R|C|E| |E|U|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+C+-+-+-+-+-+-+-+-+-+
* ^
^ these bits are referenced
* these bits are reserved
Fig.3. The Feature Flags (as defined for Pentium)
5. Source Code Example
The following example proved to be useful for reverse
engineering the PVI feature of the Pentium processor. The source
code can be assembled in several ways leading to different
executables (see notes at the beginning of the program).
View the source code of PVI.ASM:
ftp://ftp.x86.org/source/pvi1/pvi.asm
Download the source code with a sample executable:
ftp://ftp.x86.org/dloads/PVI1.ZIP
6. Literature
- Pentium Family User's Manual,
Volume 3: Architecture and Programming Manual, Intel
Corporation, 1994, order number 241430-003
- AP-485, Intel Processor
Identification with the CPUID Instruction, Intel
Corporation, 1994, order number 241618-003
Back to Books and Articles
home page
|