8086 emulation Using Virtual-8086 mode to execute real-mode procedures in a protected-mode environment

advertisement
8086 emulation
Using Virtual-8086 mode to
execute real-mode procedures in
a protected-mode environment
Features of real-mode
• At power-up the Pentium begins executing
in real-address mode (memory addressing
does not require use of descriptor tables)
• CPU privilege-restrictions are not imposed
• Memory addresses are limited to 20-bits
• Interrupt-routing is handled using the IVT
• Multitasking and paging are unsupported
• Lots of ‘legacy’ software exists for 8086
Rationale for 8086 emulation
• It is desirable to run multiple 8086 tasks in
an environment that ‘protects’ each task
from interference by other tasks, yet offers
each task the illusion of being in control of
the system (as in ‘real-mode’ environment)
• Duplicate the environment of an 8086 cpu
• Synchronize access to shared resources,
(such as files and peripheral i/o devices)
The VM-bit in EFLAGS
• The CPU executes in ‘Virtual-8086’ mode
when the VM-bit (bit #17) in EFLAGS is 1
• POPFL instruction cannot modify VM-bit
• Two methods for entering VM86-mode:
1) use the IRET instruction (.code32)
2) use a task-switch to a new 386 TSS
• The only way to leave VM86-mode is with
an interrupt (either hardware or software)
or by resetting the processor (i.e., reboot)
Entering a VM86-mode procedure
Ring-0 Stack-Frame
GS-image
FS-image
DS-image
Execute IRET instruction
from 32-bit code-segment
while in protected-mode
at privilege-level 0
ES-image
SS-image
SP-image
EFLAGS ( VM=1, NT=0 )
CS-image
SS:ESP
IP-image
I/O-sensitive Instructions
• While in VM86-mode, certain instructions are
‘sensitive’ to the current value of the IOPL-field
in EFLAGS:
–
–
–
–
–
The CLI and STI instructions
The PUSHF and POPF instructions
The PUSHFL and POPFL instructions
The IRET and IRETL instructions
The INT-nn instruction
• The above instructions will generate a General
Protection Exception (INT-13) unless IOPL==3
The EFLAGS register
31
17
13 12
VV
I
AVR N
0 0 0 0 0 0 0 0 0 0
I I
0
D
CMF T
PF
Legend:
I
O
P
L
0
OD I T S Z A P C
0 0 1
FFFFFF F F F
VM = Virtual-8086 Mode (1=yes, 0=no)
IOPL = I/O Privilege-Level (0,1,2,3)
VIF = Virtual Interrupt-Flag (if CR4.0 = 1)
VIP = Virtual Interrupt Pending (if CR4.0 = 1)
ID = CPUID-supported (1=yes, 0=no)
CF = Carry-Flag
PF = Parity-Flag
AF = Auxilliary-Flag
ZF = Zero-Flag
SF = Sign-Flag
OF = Overflow-Flag
TF = Trap-Flag
IF = Interrupt-Flag
DF = Direction-Flag
RF = Resume-Flag
NT = Nested Task
AC = Alignment Check
Emulating I/O-sensitive instructions
• Suppose a task executing in VM86-mode tries to
disable device-interrupts, using a ‘cli’ instruction
• If IOPL<3, this instruction will cause a GP-fault
(exception 0x0D) with an error-code equal to 0
• The exception-handler can examine the opcode
(using the saved CS:EIP address on its stack)
• If the opcode equals 0xFA (i.e., ‘cli’), then the
handler can clear bit #9 in the saved EFLAGS
image (i.e., the IF-bit), increment the saved EIP,
then execute IRET to resume the VM86 task
When IOPL == 3
• A VM86-task executes at privilege-level 3
• If IOPL==3, then the VM86 task is allowed
to execute all the IO-sensitive instructions
(except INT-nn) without generating a fault
• If the VME-bit (bit #0) in Control Register 4
is set to 1, Virtual Mode Extensions will be
enabled, and then INT-nn instructions can
also be executed without triggering a fault
(provided a bitmap in the TSS permits it)
How to leave VM-8086 mode?
• In VM86-mode, certain instructions trigger
a General Protection Fault regardless of
the current value in EFLAGS’ IOPL-field
• One of these is the halt-instruction (‘hlt’)
• The GP fault-handler can examine the
opcode that triggered the fault (using the
saved CS:EIP address on its ring0 stack)
and, if it is 0xF4 (i.e., ‘hlt’), can terminate
the VM86 task, if that is what is desired
IO-permission Bitmap
• For tasks that execute in VM86-mode, the
ability to execute IN/OUT instructions can
be controlled on a port-by-port basis, using
a bitmap data-structure within the TSS
• The bitmap can be up to 8192 bytes long
(one bit for each of the 65536 i/o ports)
• The CPU finds this bitmap by using the
value at offset 0x66 within the TSS, which
holds the bitmap’s starting TSS offset
Layout of the Task-State Segment
I/O Permission
Bitmap
Software Interrupt
Redirection Bitmap
(if DR4.0 = 1)
IOMAP
0x66
TSS Base-Address
Trapping I/O
• If you do not want a VM86 task to directly
perform I/O operations on a specific port,
you can set that port’s bit within the bitmap
• For example, to prevent a VM86 task from
reading mouse-data (io-port 0x60), just set
bit $0x60 within that task’s io-permission
bitmap: this will causes a GP-fault if the
instruction ‘in $0x60, %al’ is encountered
Demo-program: ‘tryvm86.s’
• This demo illustrates entering and leaving
a Virtual-8086 procedure within a 386 task
that is executing in protected-mode
• The procedure draws directly to video ram,
changing all the characters’ attribute-bytes
to a blue-colored background (‘turn_blue’)
• It executes with device-interrupts disabled
• It includes no ‘io-sensitive’ instructions
• It uses ‘hlt’ to exit from Virtual-8086 mode
In-class exercise #1
• Try modifying the ‘tryvm86.s’ demo -- to do
something that’s much more interesting
• Replace the ‘turn_blue’ routine with code
that would call ROM-BIOS services (via
interrupt 0x10) to print a message at the
current cursor location
• You will need to add code to the GP-fault
handler that ‘emulates’ an ‘int-nn’ opcode
Steps for ‘int-nn’ emulation
• Determine the interrupt’s ID-number
• Advance the saved IP-value by 2 bytes (to
skip the emulated interrupt-instruction)
• Simulate the ‘push’ of FLAGS, CS, and IP
onto the VM86 task’s ring3 stack
• Copy vector from IVT onto ring0 stack
• Clear IF and TF bits in the saved EFLAGS
• NOTE: You may need to block all device
interrupts (by setting PIC mask registers)
Emulating ‘int-nn’
Ring-0 Stack
Ring-3
Stack
GS
FS
DS
FLAGS
ES
CS
SS
IP
SP
EFLAGS
SS:ESP
Real-Mode IVT
CS
CS
IP
IP
Other emulations if IOPL < 3
• If you try executing code in Virtual-8086
mode without IOPL==3, then you’re likely
to need to emulate the other io-sensitive
instructions (iret, cli, sti, pushf, popf)
• The CLI and STI instructions are easy
• The PUSHF/POPF are a little harder
• The IRET is the most complex of these
Emulating ‘cli’ or ‘sti’
Ring-0 Stack
GS
FS
DS
ES
SS
SP
EFLAGS
CS
IP
SS:ESP
Simply adjust bit number 9
in the saved image
of the EFLAGS register
on the ring0 stack
Emulating ‘popf’
Ring-0 Stack
Ring-3
Stack
GS
FS
DS
ES
SS
FLAGS
SP
EFLAGS
CS
IP
SS:ESP
Copy the topmost word from the ring3 stack
to the low-half of the saved EFLAGS-image
on the ring0 stack;
Add 2 to the saved SP-value;
Add 1 to the saved IP-value;
then execute IRET to resume
Emulating ‘iret’
Ring-0 Stack
Ring-3
Stack
GS
FS
DS
FLAGS
ES
CS
SS
IP
SP
EFLAGS
CS
IP
SS:ESP
Copy topmost 3 words from the
ring3 stack to the low-halves
of the topmost 3 double-words
on the ring0 stack;
Add 6 to the saved SP-value;
then execute IRET to resume
In-class exercise #2
• Include a counter in your GP-fault handler
• Display the counter-value when finishing
• Then enable Virtual-Mode Extensions (by
setting bit #0 in Control Register 4)
• Re-execute your demo – notice the new
value of the GP-fault handler’s counter!
Download