Introduce boot.mgr - a self-registering, dependency-ordered boot initialization framework with barrier-based phasing and glob pattern matching. Units now declare their own init procedures and dependencies via registerBoot()/registerBarrier() in their initialization sections, boot.mgr.run() topologically sorts and executes them. - Add src/boot/boot.mgr.pas with topoSort, glob matching, barrier two-pass algorithm, and boot tree printer - Gut src/asuro.pas (kmain): now just stores multiboot info, calls System.init(), boot.mgr.run, and yield() - Add initialization sections with registerBoot() to 53 existing units - Add new units: core.pas, test.pas, driver.storage.boot.pas, app.wasm.pas, app.bsod.pas (factored out of asuro.pas) - Define 9 barrier phases: immediate -> early -> middle -> storage -> bus -> device -> bus.late -> late -> final - Fix xHCI ISR: exclude PRC/WRC from PORTSC change bit clearing to prevent race with port reset busy-wait - Fix driver.bus.usb.core fire_completion_hooks: guard against uint32 underflow when CompletionHookCount = 0 - Move auto_mount_volumes from device barrier to late barrier so it runs after PCI scan discovers storage controllers - Add toolchain/compile_initcalls.sh for boot entry extraction - Add Boot Registration sections to all 51 doc files; create 4 new doc files; rewrite boot.mgr.md; update mkdocs.yml nav
5.1 KiB
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
Boot Registration
Registered with boot.mgr as arch.x86.v86 at the early barrier.
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
procedure 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
function v86_int(intNo : uint8; var regs : TV86Regs) : boolean;
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.