Nested Execution

Often, a virtual device (VxD) handles an interrupt or fault by calling code in a virtual machine (VM). The code might be an MS-DOS routine or a BIOS interrupt handler, for example. When the virtual device calls into the virtual machine, it must do so within the context of a nested execution block, which is a block of code delimited by calls to Begin_Nest_Exec (or Begin_Nest_V86_Exec) and End_Nest_Exec.

Begin_Nest_Exec changes the execution mode of the virtual machine to the mode of the application running in the VM. For example, if the virtual machine is running a protected-mode program but happens to be in V86 mode at the moment, Begin_Nest_Exec changes the virtual machine to protected-mode. Begin_Nest_Exec is typically used to call callback procedures registered by an application.

Begin_Nest_V86_Exec is like Begin_Nest_Exec, except that it forces the mode of the virtual machine to V86-mode, even if the program running in the virtual machine is a protected-mode program. Begin_Nest_V86_Exec is typically used to call the BIOS, a TSR, or other V86-mode code.

End_Nest_Exec restores the virtual machine to its original mode, regardless of how nested execution was entered.

The code in the virtual machine can modify the virtual machines registers. It is rarely desirable that changes to the registers resulting from nested execution be propagated back to the virtual machine when nested execution is complete. To be able to restore the state of the virtual machine after the code executes, a virtual device must save the state of the virtual machine's registers before entering a nested execution block. To save the registers, a virtual device can use the Save_Client_State service, which stores the registers in a buffer, or the Push_Client_State macro, which pushes the registers onto the virtual device's stack. After completing the nested execution block, the virtual device can restore the registers by calling the Restore_Client_State service or using the Pop_Client_State macro.

Once inside a nested execution block, a virtual device uses simulation services, followed by the Resume_Exec service to call into a virtual machine. Alternatively, a virtual machine can use the Exec_Int service, which combines the functionality of Simulate_Int and Resume_Exec. Multiple calls to Resume_Exec can occur while inside a single nested execution block; you don't need to exit and re-enter nested execution if you need to simulate multiple operations.

For the curious: So how does your virtual device manage to regain control after a Resume_Exec? When Begin_Nest_Exec or Begin_Nest_V86_Exec is called, the virtual machine manager changes the mode of the virtual machine, then points the client CS:EIP registers at a pre-allocated breakpoint address. When far calls or interrupts are simulated, this breakpoint gets pushed onto the client's stack as the return address. Then when the simulated call or interrupt returns, it returns to this breakpoint address, at which point the virtual machine manager regains control, does some bookkeeping, and returns control to your virtual device. When End_Nest_Exec is called, the mode of the virtual machine is restored, and the original CS:EIP is replaced. This information is provided to aid in understanding nested execution; Microsoft reserves the right to alter the details of the implementation in future versions of Windows, so you shouldn't rely on it.