arch.x86.v86¶
Virtual 8086 (V86) mode monitor for executing real-mode BIOS interrupts from protected mode.
Overview¶
This unit implements a Virtual 8086 mode monitor that allows the kernel to call BIOS interrupt handlers while running in 32-bit protected mode. The caller sets up a TV86Regs record with the desired input register values, calls v86_int, and receives the BIOS output register values back in the same record.
The implementation works by writing a two-instruction thunk (INT n / INT 0xFF) into low physical memory at address 0x7C00, building an IRET frame with VM=1 set in EFLAGS to switch the CPU into V86 mode, and executing the thunk. Every privileged instruction executed by the BIOS handler in V86 mode causes a General Protection Fault. The unit installs a custom naked GPF handler (v86_gpf_isr) that inspects the faulting instruction and emulates it: INT, IRET, CLI, STI, PUSHF/PUSHFD, POPF/POPFD, all IN/OUT variants (with and without operand-size prefix), and HLT. When the sentinel INT 0xFF is encountered, results are copied out of the saved register frame and control returns to the Pascal caller.
A minimal TSS is installed in GDT gate 5 so the CPU can locate the ring-0 stack pointer (ESP0) when transitioning from V86 ring 3 to ring 0 on a GPF.
Dependencies¶
core.utilarch.x86.utilcore.panicio.syslogdebug.tracerarch.x86.memory.virtualarch.x86.gdtarch.x86.idtarch.x86.isr.types
Constants¶
| Constant | Value | Description |
|---|---|---|
| V86_EFLAGS | $23000 |
EFLAGS for V86 entry: VM=1, IOPL=3, IF=0 |
| V86_THUNK_ADDR | $7C00 |
Physical address of the two-instruction thunk |
| V86_STACK_SEG | $0000 |
Real-mode stack segment (SS) |
| V86_STACK_PTR | $8000 |
Real-mode stack pointer (SP), grows downward |
| V86_EXIT_INT | $FF |
Sentinel interrupt number signalling V86 exit |
| V86_R0_STACK_SIZE | 4096 |
Size in bytes of the dedicated ring-0 stack used during V86 GPF handling |
| V86_TSS_GDT_GATE | 5 |
GDT gate index for the TSS descriptor |
| V86_TSS_SELECTOR | 40 |
TSS segment selector (gate 5 * 8) |
| V86_TSS_SIZE | 104 |
Minimum x86 TSS size in bytes |
Types¶
TV86Regs / PV86Regs¶
TV86Regs = record
EAX, EBX, ECX, EDX : uint32;
ESI, EDI, EBP : uint32;
DS, ES, FS, GS : uint16;
end;
Input and output register state for a V86 BIOS call. Populate before calling v86_int; read results after it returns.
TV86GPFFrame / PV86GPFFrame¶
TV86GPFFrame = packed record
ErrorCode : uint32;
EIP : uint32;
CS : uint32;
EFLAGS : uint32;
ESP : uint32;
SS : uint32;
ES : uint32;
DS : uint32;
FS : uint32;
GS : uint32;
end;
The CPU exception frame pushed on the ring-0 stack when a GPF occurs from V86 mode, preceded by the PUSHAD frame. The emulator modifies fields directly to redirect V86 execution.
Functions and Procedures¶
init¶
Initialises the V86 subsystem. Sets up a minimal TSS in V86TSS, installs a GDT descriptor for it at gate 5 (access $89, present, DPL 0, TSS available), reloads the GDT, loads the task register (LTR), identity-maps the first 4 MiB with the user bit set (so V86 code can access the IVT, BDA, and VGA memory), and replaces IDT gate 13 (#GP) with the custom naked handler v86_gpf_isr. Must be called once during kernel initialisation before any BIOS calls are made.
v86_int¶
Executes a real-mode BIOS interrupt from protected mode.
intNo— The BIOS interrupt number to call (e.g.,$10for video services).regs— On entry, contains the register values to pass to the BIOS handler. On return, contains the register values returned by the BIOS handler.
Returns true if the call completed successfully (sentinel INT 0xFF was reached), false if the emulator encountered an unhandled opcode and aborted.
The function disables interrupts for the duration of the call and re-enables them on return.
Notes¶
- Real-mode memory addresses are accessed by adding
KERNEL_VIRTUAL_BASE($C0000000) to the physical linear address, since the first 4 MiB of physical memory is identity-mapped at the higher-half kernel base. - The BIOS thunk is written to physical address 0x7C00 (the standard MBR load address), which is free after boot.
- Segment override prefixes (
$26,$2E,$36,$3E,$64,$65) and LOCK/REP prefixes ($F0,$F2,$F3) are recognised and skipped during opcode scanning but are otherwise not emulated. - The operand-size prefix (
$66) is handled forIRETD,PUSHFD,POPFD, and 32-bitIN/OUTvariants. - Non-V86 GPFs that arrive at the patched gate 13 handler are forwarded to
core.panic.panicviav86_chain_gpf. - Only one V86 call may be in progress at a time; the global state variables are not reentrant.